Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions src/services/ReconciliationEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,12 @@ export class ReconciliationEngine {
* populated tracking set from the server signifying an active GC sync window.
*/
if (serverResourceIds.size > 0) {
const allLocalDeletions = await this.db.getAllDeletionRecords();
for (const localDel of allLocalDeletions) {
if (!serverResourceIds.has(localDel.id)) {
await this.db.removeDeletionRecord(localDel.id);
const allDeletions = await this.db.getAllDeletionRecords();
const serverResourceIds = new Set(incomingResources.map(r => r.id));

for (const localDel of allDeletions) {
if (!serverResourceIds.has(record.id) && !serverIds.has(record.id)) {
await this.db.removeDeletionRecord(record.id);
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/services/backgroundSync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,9 +244,9 @@ export async function flushOutboundMutations(): Promise<void> {
}
}

// GC: purge deletion records where the server has no record of the resource.
// @DEPRECATED GC: purge deletion records where the server has no record of the resource.
// Server absence at a boundary means all devices that ever held the resource
// have crossed a boundary past the deletion and it is safe to hard-purge.
const allDeletions = await this.db.getAllDeletionRecords();
const serverResourceIds = new Set(incomingResources.map(r => r.id));
// const allDeletions = await this.db.getAllDeletionRecords();
// const serverResourceIds = new Set(incomingResources.map(r => r.id));

2 changes: 1 addition & 1 deletion src/services/browserMigrations.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@

// Deprecated code file.
import { indexedDbStorage } from './indexedDbStorage';

// Call this at app startup to sync IndexedDB with server
Expand Down
7 changes: 4 additions & 3 deletions src/services/conversationSync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@ export async function saveConversation(resource: ChatResource): Promise<Conversa
};

// 2. Write the stamped state down to underlying database engine
await polyglotDb.saveResource(stampedResource);
const wasSaved = await polyglotDb.saveResource(stampedResource);

return {
success: true,
changed: true,
changed: wasSaved,
error: null
};

} catch (error) {
console.error("[SyncCoordinator] Global save processing encountered exception:", error);
return {
Expand Down
24 changes: 18 additions & 6 deletions src/services/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,23 +99,35 @@ export class PolyglotDatabase {
});
}

async saveResource(resource: ChatResource): Promise<void> {
/**
* Writes a resource's data-plane state. Enforces Invariant 5 (Local
* Deletion Finality): if this device has a deletion record for the
* resource, the write is discarded and the deletion record is left
* untouched. Callers must not assume a call to saveResource always
* persists; check the return value.
*/
async saveResource(resource: ChatResource): Promise<boolean> {
const database = await this.ensureReady();
const existingDeletion = await this.getDeletionRecord(resource.id);
if (existingDeletion) {
return false;
}

const tx = database.transaction(["chats", "metadata"], "readwrite");

// 1. Save the resource content
await tx.objectStore("chats").put(resource);

// 2. Co-commit the clock metadata inside the same atomic transaction
const clock = CoherenceClock.getInstance();
const meta = (await tx.objectStore("metadata").get("sync_state")) || { key: "sync_state", deviceId: clock.getDeviceId() };
meta.lastLamport = Math.max(meta.lastLamport || 0, clock.currentLocal().lamport);
meta.lamportCounter = Math.max(meta.lamportCounter || 0, clock.currentLocal().lamport);
await tx.objectStore("metadata").put(meta);

await tx.done;
return true;
}


/**
* Removes a resource from the data plane store and writes its deletion
* record to the control plane store in a single transaction. The two
Expand Down
Binary file not shown.
Binary file not shown.
Loading