| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713 |
- /* eslint-disable
- camelcase,
- handle-callback-err,
- no-unused-vars,
- */
- // TODO: This file was created by bulk-decaffeinate.
- // Fix any style issues and re-enable lint.
- /*
- * decaffeinate suggestions:
- * DS101: Remove unnecessary use of Array.from
- * DS102: Remove unnecessary code created because of implicit returns
- * DS205: Consider reworking code to avoid use of IIFEs
- * DS207: Consider shorter variations of null checks
- * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
- */
- let PackManager;
- const async = require("async");
- const _ = require("underscore");
- const {db, ObjectId, BSON} = require("./mongojs");
- const logger = require("logger-sharelatex");
- const LockManager = require("./LockManager");
- const MongoAWS = require("./MongoAWS");
- const Metrics = require("metrics-sharelatex");
- const ProjectIterator = require("./ProjectIterator");
- const Settings = require("settings-sharelatex");
- const keys = Settings.redis.lock.key_schema;
- // Sharejs operations are stored in a 'pack' object
- //
- // e.g. a single sharejs update looks like
- //
- // {
- // "doc_id" : 549dae9e0a2a615c0c7f0c98,
- // "project_id" : 549dae9c0a2a615c0c7f0c8c,
- // "op" : [ {"p" : 6981, "d" : "?" } ],
- // "meta" : { "user_id" : 52933..., "start_ts" : 1422310693931, "end_ts" : 1422310693931 },
- // "v" : 17082
- // }
- //
- // and a pack looks like this
- //
- // {
- // "doc_id" : 549dae9e0a2a615c0c7f0c98,
- // "project_id" : 549dae9c0a2a615c0c7f0c8c,
- // "pack" : [ U1, U2, U3, ...., UN],
- // "meta" : { "user_id" : 52933..., "start_ts" : 1422310693931, "end_ts" : 1422310693931 },
- // "v" : 17082
- // "v_end" : ...
- // }
- //
- // where U1, U2, U3, .... are single updates stripped of their
- // doc_id and project_id fields (which are the same for all the
- // updates in the pack).
- //
- // The pack itself has v and meta fields, this makes it possible to
- // treat packs and single updates in a similar way.
- //
- // The v field of the pack itself is from the first entry U1, the
- // v_end field from UN. The meta.end_ts field of the pack itself is
- // from the last entry UN, the meta.start_ts field from U1.
- const DAYS = 24 * 3600 * 1000; // one day in milliseconds
- module.exports = (PackManager = {
- MAX_SIZE: 1024*1024, // make these configurable parameters
- MAX_COUNT: 1024,
- insertCompressedUpdates(project_id, doc_id, lastUpdate, newUpdates, temporary, callback) {
- if (callback == null) { callback = function(error) {}; }
- if (newUpdates.length === 0) { return callback(); }
- // never append permanent ops to a pack that will expire
- if (((lastUpdate != null ? lastUpdate.expiresAt : undefined) != null) && !temporary) { lastUpdate = null; }
- const updatesToFlush = [];
- const updatesRemaining = newUpdates.slice();
- let n = (lastUpdate != null ? lastUpdate.n : undefined) || 0;
- let sz = (lastUpdate != null ? lastUpdate.sz : undefined) || 0;
- while (updatesRemaining.length && (n < PackManager.MAX_COUNT) && (sz < PackManager.MAX_SIZE)) {
- const nextUpdate = updatesRemaining[0];
- const nextUpdateSize = BSON.calculateObjectSize(nextUpdate);
- if (((nextUpdateSize + sz) > PackManager.MAX_SIZE) && (n > 0)) {
- break;
- }
- n++;
- sz += nextUpdateSize;
- updatesToFlush.push(updatesRemaining.shift());
- }
- return PackManager.flushCompressedUpdates(project_id, doc_id, lastUpdate, updatesToFlush, temporary, function(error) {
- if (error != null) { return callback(error); }
- return PackManager.insertCompressedUpdates(project_id, doc_id, null, updatesRemaining, temporary, callback);
- });
- },
- flushCompressedUpdates(project_id, doc_id, lastUpdate, newUpdates, temporary, callback) {
- if (callback == null) { callback = function(error) {}; }
- if (newUpdates.length === 0) { return callback(); }
- let canAppend = false;
- // check if it is safe to append to an existing pack
- if (lastUpdate != null) {
- if (!temporary && (lastUpdate.expiresAt == null)) {
- // permanent pack appends to permanent pack
- canAppend = true;
- }
- const age = Date.now() - (lastUpdate.meta != null ? lastUpdate.meta.start_ts : undefined);
- if (temporary && (lastUpdate.expiresAt != null) && (age < (1 * DAYS))) {
- // temporary pack appends to temporary pack if same day
- canAppend = true;
- }
- }
- if (canAppend) {
- return PackManager.appendUpdatesToExistingPack(project_id, doc_id, lastUpdate, newUpdates, temporary, callback);
- } else {
- return PackManager.insertUpdatesIntoNewPack(project_id, doc_id, newUpdates, temporary, callback);
- }
- },
- insertUpdatesIntoNewPack(project_id, doc_id, newUpdates, temporary, callback) {
- if (callback == null) { callback = function(error) {}; }
- const first = newUpdates[0];
- const last = newUpdates[newUpdates.length - 1];
- const n = newUpdates.length;
- const sz = BSON.calculateObjectSize(newUpdates);
- const newPack = {
- project_id: ObjectId(project_id.toString()),
- doc_id: ObjectId(doc_id.toString()),
- pack: newUpdates,
- n,
- sz,
- meta: {
- start_ts: first.meta.start_ts,
- end_ts: last.meta.end_ts
- },
- v: first.v,
- v_end: last.v,
- temporary
- };
- if (temporary) {
- newPack.expiresAt = new Date(Date.now() + (7 * DAYS));
- newPack.last_checked = new Date(Date.now() + (30 * DAYS)); // never check temporary packs
- }
- logger.log({project_id, doc_id, newUpdates}, "inserting updates into new pack");
- return db.docHistory.save(newPack, function(err, result) {
- if (err != null) { return callback(err); }
- Metrics.inc(`insert-pack-${temporary ? "temporary" : "permanent"}`);
- if (temporary) {
- return callback();
- } else {
- return PackManager.updateIndex(project_id, doc_id, callback);
- }
- });
- },
- appendUpdatesToExistingPack(project_id, doc_id, lastUpdate, newUpdates, temporary, callback) {
- if (callback == null) { callback = function(error) {}; }
- const first = newUpdates[0];
- const last = newUpdates[newUpdates.length - 1];
- const n = newUpdates.length;
- const sz = BSON.calculateObjectSize(newUpdates);
- const query = {
- _id: lastUpdate._id,
- project_id: ObjectId(project_id.toString()),
- doc_id: ObjectId(doc_id.toString()),
- pack: {$exists: true}
- };
- const update = {
- $push: {
- "pack": {$each: newUpdates}
- },
- $inc: {
- "n": n,
- "sz": sz
- },
- $set: {
- "meta.end_ts": last.meta.end_ts,
- "v_end": last.v
- }
- };
- if (lastUpdate.expiresAt && temporary) {
- update.$set.expiresAt = new Date(Date.now() + (7 * DAYS));
- }
- logger.log({project_id, doc_id, lastUpdate, newUpdates}, "appending updates to existing pack");
- Metrics.inc(`append-pack-${temporary ? "temporary" : "permanent"}`);
- return db.docHistory.findAndModify({query, update, new:true, fields:{meta:1,v_end:1}}, callback);
- },
- // Retrieve all changes for a document
- getOpsByVersionRange(project_id, doc_id, fromVersion, toVersion, callback) {
- if (callback == null) { callback = function(error, updates) {}; }
- return PackManager.loadPacksByVersionRange(project_id, doc_id, fromVersion, toVersion, function(error) {
- const query = {doc_id:ObjectId(doc_id.toString())};
- if (toVersion != null) { query.v = {$lte:toVersion}; }
- if (fromVersion != null) { query.v_end = {$gte:fromVersion}; }
- // console.log "query:", query
- return db.docHistory.find(query).sort({v:-1}, function(err, result) {
- if (err != null) { return callback(err); }
- // console.log "getOpsByVersionRange:", err, result
- const updates = [];
- const opInRange = function(op, from, to) {
- if ((fromVersion != null) && (op.v < fromVersion)) { return false; }
- if ((toVersion != null) && (op.v > toVersion)) { return false; }
- return true;
- };
- for (const docHistory of Array.from(result)) {
- // console.log 'adding', docHistory.pack
- for (const op of Array.from(docHistory.pack.reverse())) {
- if (opInRange(op, fromVersion, toVersion)) {
- op.project_id = docHistory.project_id;
- op.doc_id = docHistory.doc_id;
- // console.log "added op", op.v, fromVersion, toVersion
- updates.push(op);
- }
- }
- }
- return callback(null, updates);
- });
- });
- },
- loadPacksByVersionRange(project_id, doc_id, fromVersion, toVersion, callback) {
- return PackManager.getIndex(doc_id, function(err, indexResult) {
- let pack;
- if (err != null) { return callback(err); }
- const indexPacks = (indexResult != null ? indexResult.packs : undefined) || [];
- const packInRange = function(pack, from, to) {
- if ((fromVersion != null) && (pack.v_end < fromVersion)) { return false; }
- if ((toVersion != null) && (pack.v > toVersion)) { return false; }
- return true;
- };
- const neededIds = ((() => {
- const result = [];
- for (pack of Array.from(indexPacks)) { if (packInRange(pack, fromVersion, toVersion)) {
- result.push(pack._id);
- }
- }
- return result;
- })());
- if (neededIds.length) {
- return PackManager.fetchPacksIfNeeded(project_id, doc_id, neededIds, callback);
- } else {
- return callback();
- }
- });
- },
- fetchPacksIfNeeded(project_id, doc_id, pack_ids, callback) {
- let id;
- return db.docHistory.find({_id: {$in: ((() => {
- const result = [];
- for (id of Array.from(pack_ids)) { result.push(ObjectId(id));
- }
- return result;
- })())}}, {_id:1}, function(err, loadedPacks) {
- if (err != null) { return callback(err); }
- const allPackIds = ((() => {
- const result1 = [];
- for (id of Array.from(pack_ids)) { result1.push(id.toString());
- }
- return result1;
- })());
- const loadedPackIds = (Array.from(loadedPacks).map((pack) => pack._id.toString()));
- const packIdsToFetch = _.difference(allPackIds, loadedPackIds);
- logger.log({project_id, doc_id, loadedPackIds, allPackIds, packIdsToFetch}, "analysed packs");
- if (packIdsToFetch.length === 0) { return callback(); }
- return async.eachLimit(packIdsToFetch, 4, (pack_id, cb) => MongoAWS.unArchivePack(project_id, doc_id, pack_id, cb)
- , function(err) {
- if (err != null) { return callback(err); }
- logger.log({project_id, doc_id}, "done unarchiving");
- return callback();
- });
- });
- },
- // Retrieve all changes across a project
- makeProjectIterator(project_id, before, callback) {
- // get all the docHistory Entries
- return db.docHistory.find({project_id: ObjectId(project_id)},{pack:false}).sort({"meta.end_ts":-1}, function(err, packs) {
- let pack;
- if (err != null) { return callback(err); }
- const allPacks = [];
- const seenIds = {};
- for (pack of Array.from(packs)) {
- allPacks.push(pack);
- seenIds[pack._id] = true;
- }
- return db.docHistoryIndex.find({project_id: ObjectId(project_id)}, function(err, indexes) {
- if (err != null) { return callback(err); }
- for (const index of Array.from(indexes)) {
- for (pack of Array.from(index.packs)) {
- if (!seenIds[pack._id]) {
- pack.project_id = index.project_id;
- pack.doc_id = index._id;
- pack.fromIndex = true;
- allPacks.push(pack);
- seenIds[pack._id] = true;
- }
- }
- }
- return callback(null, new ProjectIterator(allPacks, before, PackManager.getPackById));
- });
- });
- },
- getPackById(project_id, doc_id, pack_id, callback) {
- return db.docHistory.findOne({_id: pack_id}, function(err, pack) {
- if (err != null) { return callback(err); }
- if ((pack == null)) {
- return MongoAWS.unArchivePack(project_id, doc_id, pack_id, callback);
- } else if ((pack.expiresAt != null) && (pack.temporary === false)) {
- // we only need to touch the TTL when listing the changes in the project
- // because diffs on individual documents are always done after that
- return PackManager.increaseTTL(pack, callback);
- // only do this for cached packs, not temporary ones to avoid older packs
- // being kept longer than newer ones (which messes up the last update version)
- } else {
- return callback(null, pack);
- }
- });
- },
- increaseTTL(pack, callback) {
- if (pack.expiresAt < new Date(Date.now() + (6 * DAYS))) {
- // update cache expiry since we are using this pack
- return db.docHistory.findAndModify({
- query: {_id: pack._id},
- update: {$set: {expiresAt: new Date(Date.now() + (7 * DAYS))}}
- }, err => callback(err, pack));
- } else {
- return callback(null, pack);
- }
- },
- // Manage docHistoryIndex collection
- getIndex(doc_id, callback) {
- return db.docHistoryIndex.findOne({_id:ObjectId(doc_id.toString())}, callback);
- },
- getPackFromIndex(doc_id, pack_id, callback) {
- return db.docHistoryIndex.findOne({_id:ObjectId(doc_id.toString()), "packs._id": pack_id}, {"packs.$":1}, callback);
- },
- getLastPackFromIndex(doc_id, callback) {
- return db.docHistoryIndex.findOne({_id: ObjectId(doc_id.toString())}, {packs:{$slice:-1}}, function(err, indexPack) {
- if (err != null) { return callback(err); }
- if ((indexPack == null)) { return callback(); }
- return callback(null,indexPack[0]);
- });
- },
- getIndexWithKeys(doc_id, callback) {
- return PackManager.getIndex(doc_id, function(err, index) {
- if (err != null) { return callback(err); }
- if ((index == null)) { return callback(); }
- for (const pack of Array.from((index != null ? index.packs : undefined) || [])) {
- index[pack._id] = pack;
- }
- return callback(null, index);
- });
- },
- initialiseIndex(project_id, doc_id, callback) {
- return PackManager.findCompletedPacks(project_id, doc_id, function(err, packs) {
- // console.log 'err', err, 'packs', packs, packs?.length
- if (err != null) { return callback(err); }
- if ((packs == null)) { return callback(); }
- return PackManager.insertPacksIntoIndexWithLock(project_id, doc_id, packs, callback);
- });
- },
- updateIndex(project_id, doc_id, callback) {
- // find all packs prior to current pack
- return PackManager.findUnindexedPacks(project_id, doc_id, function(err, newPacks) {
- if (err != null) { return callback(err); }
- if ((newPacks == null) || (newPacks.length === 0)) { return callback(); }
- return PackManager.insertPacksIntoIndexWithLock(project_id, doc_id, newPacks, function(err) {
- if (err != null) { return callback(err); }
- logger.log({project_id, doc_id, newPacks}, "added new packs to index");
- return callback();
- });
- });
- },
- findCompletedPacks(project_id, doc_id, callback) {
- const query = { doc_id: ObjectId(doc_id.toString()), expiresAt: {$exists:false} };
- return db.docHistory.find(query, {pack:false}).sort({v:1}, function(err, packs) {
- if (err != null) { return callback(err); }
- if ((packs == null)) { return callback(); }
- if (!(packs != null ? packs.length : undefined)) { return callback(); }
- const last = packs.pop(); // discard the last pack, if it's still in progress
- if (last.finalised) { packs.push(last); } // it's finalised so we push it back to archive it
- return callback(null, packs);
- });
- },
- findPacks(project_id, doc_id, callback) {
- const query = { doc_id: ObjectId(doc_id.toString()), expiresAt: {$exists:false} };
- return db.docHistory.find(query, {pack:false}).sort({v:1}, function(err, packs) {
- if (err != null) { return callback(err); }
- if ((packs == null)) { return callback(); }
- if (!(packs != null ? packs.length : undefined)) { return callback(); }
- return callback(null, packs);
- });
- },
- findUnindexedPacks(project_id, doc_id, callback) {
- return PackManager.getIndexWithKeys(doc_id, function(err, indexResult) {
- if (err != null) { return callback(err); }
- return PackManager.findCompletedPacks(project_id, doc_id, function(err, historyPacks) {
- let pack;
- if (err != null) { return callback(err); }
- if ((historyPacks == null)) { return callback(); }
- // select only the new packs not already in the index
- let newPacks = ((() => {
- const result = [];
- for (pack of Array.from(historyPacks)) { if (((indexResult != null ? indexResult[pack._id] : undefined) == null)) {
- result.push(pack);
- }
- }
- return result;
- })());
- newPacks = ((() => {
- const result1 = [];
- for (pack of Array.from(newPacks)) { result1.push(_.omit(pack, 'doc_id', 'project_id', 'n', 'sz', 'last_checked', 'finalised'));
- }
- return result1;
- })());
- if (newPacks.length) {
- logger.log({project_id, doc_id, n: newPacks.length}, "found new packs");
- }
- return callback(null, newPacks);
- });
- });
- },
- insertPacksIntoIndexWithLock(project_id, doc_id, newPacks, callback) {
- return LockManager.runWithLock(
- keys.historyIndexLock({doc_id}),
- releaseLock => PackManager._insertPacksIntoIndex(project_id, doc_id, newPacks, releaseLock),
- callback
- );
- },
- _insertPacksIntoIndex(project_id, doc_id, newPacks, callback) {
- return db.docHistoryIndex.findAndModify({
- query: {_id:ObjectId(doc_id.toString())},
- update: {
- $setOnInsert: { project_id: ObjectId(project_id.toString())
- },
- $push: {
- packs: {$each: newPacks, $sort: {v: 1}}
- }
- },
- upsert: true
- }, callback);
- },
- // Archiving packs to S3
- archivePack(project_id, doc_id, pack_id, callback) {
- const clearFlagOnError = function(err, cb) {
- if (err != null) { // clear the inS3 flag on error
- return PackManager.clearPackAsArchiveInProgress(project_id, doc_id, pack_id, function(err2) {
- if (err2 != null) { return cb(err2); }
- return cb(err);
- });
- } else {
- return cb();
- }
- };
- return async.series([
- cb => PackManager.checkArchiveNotInProgress(project_id, doc_id, pack_id, cb),
- cb => PackManager.markPackAsArchiveInProgress(project_id, doc_id, pack_id, cb),
- cb =>
- MongoAWS.archivePack(project_id, doc_id, pack_id, err => clearFlagOnError(err, cb))
- ,
- cb =>
- PackManager.checkArchivedPack(project_id, doc_id, pack_id, err => clearFlagOnError(err, cb))
- ,
- cb => PackManager.markPackAsArchived(project_id, doc_id, pack_id, cb),
- cb => PackManager.setTTLOnArchivedPack(project_id, doc_id, pack_id, callback)
- ], callback);
- },
- checkArchivedPack(project_id, doc_id, pack_id, callback) {
- return db.docHistory.findOne({_id: pack_id}, function(err, pack) {
- if (err != null) { return callback(err); }
- if ((pack == null)) { return callback(new Error("pack not found")); }
- return MongoAWS.readArchivedPack(project_id, doc_id, pack_id, function(err, result) {
- delete result.last_checked;
- delete pack.last_checked;
- // need to compare ids as ObjectIds with .equals()
- for (const key of ['_id', 'project_id', 'doc_id']) {
- if (result[key].equals(pack[key])) { result[key] = pack[key]; }
- }
- for (let i = 0; i < result.pack.length; i++) {
- const op = result.pack[i];
- if ((op._id != null) && op._id.equals(pack.pack[i]._id)) { op._id = pack.pack[i]._id; }
- }
- if (_.isEqual(pack, result)) {
- return callback();
- } else {
- logger.err({pack, result, jsondiff: JSON.stringify(pack) === JSON.stringify(result)}, "difference when comparing packs");
- return callback(new Error("pack retrieved from s3 does not match pack in mongo"));
- }
- });
- });
- },
- // Extra methods to test archive/unarchive for a doc_id
- pushOldPacks(project_id, doc_id, callback) {
- return PackManager.findPacks(project_id, doc_id, function(err, packs) {
- if (err != null) { return callback(err); }
- if (!(packs != null ? packs.length : undefined)) { return callback(); }
- return PackManager.processOldPack(project_id, doc_id, packs[0]._id, callback);
- });
- },
- pullOldPacks(project_id, doc_id, callback) {
- return PackManager.loadPacksByVersionRange(project_id, doc_id, null, null, callback);
- },
- // Processing old packs via worker
- processOldPack(project_id, doc_id, pack_id, callback) {
- const markAsChecked = err =>
- PackManager.markPackAsChecked(project_id, doc_id, pack_id, function(err2) {
- if (err2 != null) { return callback(err2); }
- return callback(err);
- })
- ;
- logger.log({project_id, doc_id}, "processing old packs");
- return db.docHistory.findOne({_id:pack_id}, function(err, pack) {
- if (err != null) { return markAsChecked(err); }
- if ((pack == null)) { return markAsChecked(); }
- if (pack.expiresAt != null) { return callback(); } // return directly
- return PackManager.finaliseIfNeeded(project_id, doc_id, pack._id, pack, function(err) {
- if (err != null) { return markAsChecked(err); }
- return PackManager.updateIndexIfNeeded(project_id, doc_id, function(err) {
- if (err != null) { return markAsChecked(err); }
- return PackManager.findUnarchivedPacks(project_id, doc_id, function(err, unarchivedPacks) {
- if (err != null) { return markAsChecked(err); }
- if (!(unarchivedPacks != null ? unarchivedPacks.length : undefined)) {
- logger.log({project_id, doc_id}, "no packs need archiving");
- return markAsChecked();
- }
- return async.eachSeries(unarchivedPacks, (pack, cb) => PackManager.archivePack(project_id, doc_id, pack._id, cb)
- , function(err) {
- if (err != null) { return markAsChecked(err); }
- logger.log({project_id, doc_id}, "done processing");
- return markAsChecked();
- });
- });
- });
- });
- });
- },
- finaliseIfNeeded(project_id, doc_id, pack_id, pack, callback) {
- const sz = pack.sz / (1024 * 1024); // in fractions of a megabyte
- const n = pack.n / 1024; // in fraction of 1024 ops
- const age = (Date.now() - pack.meta.end_ts) / DAYS;
- if (age < 30) { // always keep if less than 1 month old
- logger.log({project_id, doc_id, pack_id, age}, "less than 30 days old");
- return callback();
- }
- // compute an archiving threshold which decreases for each month of age
- const archive_threshold = 30 / age;
- if ((sz > archive_threshold) || (n > archive_threshold) || (age > 90)) {
- logger.log({project_id, doc_id, pack_id, age, archive_threshold, sz, n}, "meets archive threshold");
- return PackManager.markPackAsFinalisedWithLock(project_id, doc_id, pack_id, callback);
- } else {
- logger.log({project_id, doc_id, pack_id, age, archive_threshold, sz, n}, "does not meet archive threshold");
- return callback();
- }
- },
- markPackAsFinalisedWithLock(project_id, doc_id, pack_id, callback) {
- return LockManager.runWithLock(
- keys.historyLock({doc_id}),
- releaseLock => PackManager._markPackAsFinalised(project_id, doc_id, pack_id, releaseLock),
- callback
- );
- },
- _markPackAsFinalised(project_id, doc_id, pack_id, callback) {
- logger.log({project_id, doc_id, pack_id}, "marking pack as finalised");
- return db.docHistory.findAndModify({
- query: {_id: pack_id},
- update: {$set: {finalised: true}}
- }, callback);
- },
- updateIndexIfNeeded(project_id, doc_id, callback) {
- logger.log({project_id, doc_id}, "archiving old packs");
- return PackManager.getIndexWithKeys(doc_id, function(err, index) {
- if (err != null) { return callback(err); }
- if ((index == null)) {
- return PackManager.initialiseIndex(project_id, doc_id, callback);
- } else {
- return PackManager.updateIndex(project_id, doc_id, callback);
- }
- });
- },
- markPackAsChecked(project_id, doc_id, pack_id, callback) {
- logger.log({project_id, doc_id, pack_id}, "marking pack as checked");
- return db.docHistory.findAndModify({
- query: {_id: pack_id},
- update: {$currentDate: {"last_checked":true}}
- }, callback);
- },
- findUnarchivedPacks(project_id, doc_id, callback) {
- return PackManager.getIndex(doc_id, function(err, indexResult) {
- if (err != null) { return callback(err); }
- const indexPacks = (indexResult != null ? indexResult.packs : undefined) || [];
- const unArchivedPacks = ((() => {
- const result = [];
- for (const pack of Array.from(indexPacks)) { if ((pack.inS3 == null)) {
- result.push(pack);
- }
- }
- return result;
- })());
- if (unArchivedPacks.length) {
- logger.log({project_id, doc_id, n: unArchivedPacks.length}, "find unarchived packs");
- }
- return callback(null, unArchivedPacks);
- });
- },
- // Archive locking flags
- checkArchiveNotInProgress(project_id, doc_id, pack_id, callback) {
- logger.log({project_id, doc_id, pack_id}, "checking if archive in progress");
- return PackManager.getPackFromIndex(doc_id, pack_id, function(err, result) {
- if (err != null) { return callback(err); }
- if ((result == null)) { return callback(new Error("pack not found in index")); }
- if (result.inS3) {
- return callback(new Error("pack archiving already done"));
- } else if (result.inS3 != null) {
- return callback(new Error("pack archiving already in progress"));
- } else {
- return callback();
- }
- });
- },
- markPackAsArchiveInProgress(project_id, doc_id, pack_id, callback) {
- logger.log({project_id, doc_id}, "marking pack as archive in progress status");
- return db.docHistoryIndex.findAndModify({
- query: {_id:ObjectId(doc_id.toString()), packs: {$elemMatch: {"_id": pack_id, inS3: {$exists:false}}}},
- fields: { "packs.$": 1 },
- update: {$set: {"packs.$.inS3":false}}
- }, function(err, result) {
- if (err != null) { return callback(err); }
- if ((result == null)) { return callback(new Error("archive is already in progress")); }
- logger.log({project_id, doc_id, pack_id}, "marked as archive in progress");
- return callback();
- });
- },
- clearPackAsArchiveInProgress(project_id, doc_id, pack_id, callback) {
- logger.log({project_id, doc_id, pack_id}, "clearing as archive in progress");
- return db.docHistoryIndex.findAndModify({
- query: {_id:ObjectId(doc_id.toString()), "packs" : {$elemMatch: {"_id": pack_id, inS3: false}}},
- fields: { "packs.$": 1 },
- update: {$unset: {"packs.$.inS3":true}}
- }, callback);
- },
- markPackAsArchived(project_id, doc_id, pack_id, callback) {
- logger.log({project_id, doc_id, pack_id}, "marking pack as archived");
- return db.docHistoryIndex.findAndModify({
- query: {_id:ObjectId(doc_id.toString()), "packs" : {$elemMatch: {"_id": pack_id, inS3: false}}},
- fields: { "packs.$": 1 },
- update: {$set: {"packs.$.inS3":true}}
- }, function(err, result) {
- if (err != null) { return callback(err); }
- if ((result == null)) { return callback(new Error("archive is not marked as progress")); }
- logger.log({project_id, doc_id, pack_id}, "marked as archived");
- return callback();
- });
- },
- setTTLOnArchivedPack(project_id, doc_id, pack_id, callback) {
- return db.docHistory.findAndModify({
- query: {_id: pack_id},
- update: {$set: {expiresAt: new Date(Date.now() + (1*DAYS))}}
- }, function(err) {
- logger.log({project_id, doc_id, pack_id}, "set expiry on pack");
- return callback();
- });
- }
- });
- // _getOneDayInFutureWithRandomDelay: ->
- // thirtyMins = 1000 * 60 * 30
- // randomThirtyMinMax = Math.ceil(Math.random() * thirtyMins)
- // return new Date(Date.now() + randomThirtyMinMax + 1*DAYS)
|