1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-07-06 23:56:13 +02:00

All: Fixes #5051: Fixed error that could prevent a revision from being created, and that would prevent the revision service from processing the rest of the notes

This commit is contained in:
Laurent Cozic
2021-06-20 11:19:59 +01:00
parent e79f965e5d
commit 097e49d797
10 changed files with 1099 additions and 155 deletions

View File

@ -9,10 +9,13 @@ import shim from '../shim';
import BaseService from './BaseService';
import { _ } from '../locale';
import { ItemChangeEntity, NoteEntity, RevisionEntity } from './database/types';
import Logger from '../Logger';
const { substrWithEllipsis } = require('../string-utils');
const { sprintf } = require('sprintf-js');
const { wrapError } = require('../errorUtils');
const logger = Logger.create('RevisionService');
export default class RevisionService extends BaseService {
public static instance_: RevisionService;
@ -60,18 +63,7 @@ export default class RevisionService extends BaseService {
return md;
}
isEmptyRevision_(rev: RevisionEntity) {
if (rev.title_diff) return false;
if (rev.body_diff) return false;
const md = JSON.parse(rev.metadata_diff);
if (md.new && Object.keys(md.new).length) return false;
if (md.deleted && Object.keys(md.deleted).length) return false;
return true;
}
async createNoteRevision_(note: NoteEntity, parentRevId: string = null) {
public async createNoteRevision_(note: NoteEntity, parentRevId: string = null): Promise<RevisionEntity> {
try {
const parentRev = parentRevId ? await Revision.load(parentRevId) : await Revision.latestRevision(BaseModel.TYPE_NOTE, note.id);
@ -100,7 +92,7 @@ export default class RevisionService extends BaseService {
output.metadata_diff = Revision.createObjectPatch(merged.metadata, noteMd);
}
if (this.isEmptyRevision_(output)) return null;
if (Revision.isEmptyRevision(output)) return null;
return Revision.save(output);
} catch (error) {
@ -109,7 +101,7 @@ export default class RevisionService extends BaseService {
}
}
async collectRevisions() {
public async collectRevisions() {
if (this.isCollecting_) return;
this.isCollecting_ = true;
@ -153,11 +145,11 @@ export default class RevisionService extends BaseService {
if (oldNote && oldNote.updated_time < this.oldNoteCutOffDate_()) {
// This is where we save the original version of this old note
const rev = await this.createNoteRevision_(oldNote);
if (rev) this.logger().debug(sprintf('RevisionService::collectRevisions: Saved revision %s (old note)', rev.id));
if (rev) logger.debug(sprintf('RevisionService::collectRevisions: Saved revision %s (old note)', rev.id));
}
const rev = await this.createNoteRevision_(note);
if (rev) this.logger().debug(sprintf('RevisionService::collectRevisions: Saved revision %s (Last rev was more than %d ms ago)', rev.id, Setting.value('revisionService.intervalBetweenRevisions')));
if (rev) logger.debug(sprintf('RevisionService::collectRevisions: Saved revision %s (Last rev was more than %d ms ago)', rev.id, Setting.value('revisionService.intervalBetweenRevisions')));
doneNoteIds.push(noteId);
this.isOldNotesCache_[noteId] = false;
}
@ -168,7 +160,7 @@ export default class RevisionService extends BaseService {
const revExists = await Revision.revisionExists(BaseModel.TYPE_NOTE, note.id, note.updated_time);
if (!revExists) {
const rev = await this.createNoteRevision_(note);
if (rev) this.logger().debug(sprintf('RevisionService::collectRevisions: Saved revision %s (for deleted note)', rev.id));
if (rev) logger.debug(sprintf('RevisionService::collectRevisions: Saved revision %s (for deleted note)', rev.id));
}
doneNoteIds.push(noteId);
}
@ -181,9 +173,15 @@ export default class RevisionService extends BaseService {
// One or more revisions are encrypted - stop processing for now
// and these revisions will be processed next time the revision
// collector runs.
this.logger().info('RevisionService::collectRevisions: One or more revision was encrypted. Processing was stopped but will resume later when the revision is decrypted.', error);
logger.info('RevisionService::collectRevisions: One or more revision was encrypted. Processing was stopped but will resume later when the revision is decrypted.', error);
} else {
this.logger().error('RevisionService::collectRevisions:', error);
// Note that, for now, if any revision creation fails, the whole
// process fails. This is on purpose because if we keep on
// processing, whatever caused the error will be in the past
// changes (before revisionService.lastProcessedChangeId) and
// will never be processed again. Now that the diff-match-patch
// issue is fixed, there should be no such error anyway.
logger.error('RevisionService::collectRevisions:', error);
}
}
@ -192,7 +190,7 @@ export default class RevisionService extends BaseService {
this.isCollecting_ = false;
this.logger().info(`RevisionService::collectRevisions: Created revisions for ${doneNoteIds.length} notes`);
logger.info(`RevisionService::collectRevisions: Created revisions for ${doneNoteIds.length} notes`);
}
async deleteOldRevisions(ttl: number) {
@ -266,23 +264,23 @@ export default class RevisionService extends BaseService {
this.maintenanceCalls_.push(true);
try {
const startTime = Date.now();
this.logger().info('RevisionService::maintenance: Starting...');
logger.info('RevisionService::maintenance: Starting...');
if (!Setting.value('revisionService.enabled')) {
this.logger().info('RevisionService::maintenance: Service is disabled');
logger.info('RevisionService::maintenance: Service is disabled');
// We do as if we had processed all the latest changes so that they can be cleaned up
// later on by ItemChangeUtils.deleteProcessedChanges().
Setting.setValue('revisionService.lastProcessedChangeId', await ItemChange.lastChangeId());
await this.deleteOldRevisions(Setting.value('revisionService.ttlDays') * 24 * 60 * 60 * 1000);
} else {
this.logger().info('RevisionService::maintenance: Service is enabled');
logger.info('RevisionService::maintenance: Service is enabled');
await this.collectRevisions();
await this.deleteOldRevisions(Setting.value('revisionService.ttlDays') * 24 * 60 * 60 * 1000);
this.logger().info(`RevisionService::maintenance: Done in ${Date.now() - startTime}ms`);
logger.info(`RevisionService::maintenance: Done in ${Date.now() - startTime}ms`);
}
} catch (error) {
this.logger().error('RevisionService::maintenance:', error);
logger.error('RevisionService::maintenance:', error);
} finally {
this.maintenanceCalls_.pop();
}
@ -294,7 +292,7 @@ export default class RevisionService extends BaseService {
if (collectRevisionInterval === null) collectRevisionInterval = 1000 * 60 * 10;
this.logger().info(`RevisionService::runInBackground: Starting background service with revision collection interval ${collectRevisionInterval}`);
logger.info(`RevisionService::runInBackground: Starting background service with revision collection interval ${collectRevisionInterval}`);
this.maintenanceTimer1_ = shim.setTimeout(() => {
void this.maintenance();