diff --git a/packages/core/__e2e__/sync.test.js b/packages/core/__e2e__/sync.test.js index ca694acd5..4bec7bbd0 100644 --- a/packages/core/__e2e__/sync.test.js +++ b/packages/core/__e2e__/sync.test.js @@ -156,6 +156,34 @@ test( TEST_TIMEOUT * 10 ); +test( + "issue: syncing should not affect the items' dateModified", + async (t) => { + const [deviceA] = await Promise.all([initializeDevice("deviceA")]); + + t.onTestFinished(async (r) => { + console.log(`${t.task.name} log out`); + await cleanup(deviceA); + }); + + const noteId = await deviceA.notes.add({ + title: "Test note from device A", + content: { data: "

Hello

", type: "tiptap" } + }); + const noteDateBefore = (await deviceA.notes.note(noteId)).dateModified; + const contentDateBefore = (await deviceA.content.findByNoteId(noteId)) + .dateModified; + await deviceA.sync({ type: "full" }); + const noteDateAfter = (await deviceA.notes.note(noteId)).dateModified; + const contentDateAfter = (await deviceA.content.findByNoteId(noteId)) + .dateModified; + expect(noteDateBefore).toBe(noteDateAfter); + expect(contentDateBefore).toBe(contentDateAfter); + }, + TEST_TIMEOUT +); + + // test( // "case 4: Device A's sync is interrupted halfway and Device B makes some changes afterwards and syncs.", // async () => { diff --git a/packages/core/src/api/sync/collector.ts b/packages/core/src/api/sync/collector.ts index 8aee89c8a..396effbc4 100644 --- a/packages/core/src/api/sync/collector.ts +++ b/packages/core/src/api/sync/collector.ts @@ -57,37 +57,34 @@ class Collector { const collection = this.db[collectionKey].collection; let pushTimestamp = Date.now(); for await (const chunk of collection.unsynced(chunkSize, isForceSync)) { - const items = await this.prepareChunk(chunk, key); + const { ids, items: syncableItems } = filterSyncableItems(chunk); + if (!ids.length) continue; + const ciphers = await this.db + .storage() + .encryptMulti(key, syncableItems); + const items = toPushItem(ids, ciphers); if (!items) continue; yield { items, type: itemType }; - await collection.update( - chunk.map((i) => i.id), - { synced: true }, - { - sendEvent: false, - // EDGE CASE: - // Sometimes an item can get updated while it's being pushed. - // The result is that its `synced` property becomes true even - // though it's modification wasn't yet synced. - // In order to prevent that, we only set the `synced` property - // to true for items that haven't been modified since we last ran - // the push. Everything else will be collected again in the next - // push. - condition: (eb) => eb("dateModified", "<=", pushTimestamp) - } - ); + await this.db + .sql() + .updateTable(collection.type) + .where("id", "in", ids) + // EDGE CASE: + // Sometimes an item can get updated while it's being pushed. + // The result is that its `synced` property becomes true even + // though it's modification wasn't yet synced. + // In order to prevent that, we only set the `synced` property + // to true for items that haven't been modified since we last ran + // the push. Everything else will be collected again in the next + // push. + .where("dateModified", "<=", pushTimestamp) + .set({ synced: true }) + .execute(); pushTimestamp = Date.now(); } } } - - async prepareChunk(chunk: MaybeDeletedItem[], key: SerializedKey) { - const { ids, items } = filterSyncableItems(chunk); - if (!ids.length) return; - const ciphers = await this.db.storage().encryptMulti(key, items); - return toPushItem(ids, ciphers); - } } export default Collector; diff --git a/packages/core/src/database/index.ts b/packages/core/src/database/index.ts index defe263f3..021d2060d 100644 --- a/packages/core/src/database/index.ts +++ b/packages/core/src/database/index.ts @@ -160,6 +160,7 @@ type AsyncOrSyncResult = Async extends true : Response; export interface DatabaseCollection { + type: keyof DatabaseSchema; clear(): Promise; init(): Promise; upsert(item: T): Promise; diff --git a/packages/core/src/database/sql-cached-collection.ts b/packages/core/src/database/sql-cached-collection.ts index 95d8f93ec..a207d834d 100644 --- a/packages/core/src/database/sql-cached-collection.ts +++ b/packages/core/src/database/sql-cached-collection.ts @@ -38,7 +38,7 @@ export class SQLCachedCollection< startTransaction: ( executor: (tr: Kysely) => Promise ) => Promise, - type: TCollectionType, + public type: TCollectionType, eventManager: EventManager, sanitizer: Sanitizer ) { diff --git a/packages/core/src/database/sql-collection.ts b/packages/core/src/database/sql-collection.ts index 02359f644..f4d6f8c9e 100644 --- a/packages/core/src/database/sql-collection.ts +++ b/packages/core/src/database/sql-collection.ts @@ -75,7 +75,7 @@ export class SQLCollection< _startTransaction: ( executor: (tr: Kysely) => Promise ) => Promise, - private readonly type: TCollectionType, + public readonly type: TCollectionType, private readonly eventManager: EventManager, private readonly sanitizer: Sanitizer ) {}