mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-01-23 02:36:05 +00:00
fix(sync): prevent SYNC_IMPORT for fresh clients syncing to empty server
The server migration check was incorrectly creating a SYNC_IMPORT when a fresh client (with local data but no sync history) synced to an empty server. This caused operations from other clients to be filtered out as "invalidated by SYNC_IMPORT" because they were CONCURRENT with it. Now _checkAndHandleServerMigration() checks for previously synced ops before triggering migration, correctly distinguishing between: - Fresh client (only local ops) → uploads ops normally - Server migration (has sync history) → creates SYNC_IMPORT Also adds npm scripts for debugging supersync E2E tests.
This commit is contained in:
parent
5cb9219cdd
commit
02d80249af
3 changed files with 36 additions and 3 deletions
|
|
@ -60,6 +60,8 @@
|
|||
"e2e:report": "PLAYWRIGHT_HTML_REPORT=1 npx playwright test --config e2e/playwright.config.ts",
|
||||
"e2e:webdav": "docker compose up -d webdav && ./scripts/wait-for-webdav.sh && npm run e2e -- --grep webdav; docker compose down",
|
||||
"e2e:supersync": "docker compose up -d --build supersync && echo 'Waiting for SuperSync server...' && until curl -s http://localhost:1900/health > /dev/null 2>&1; do sleep 1; done && echo 'Server ready!' && npm run e2e -- --grep @supersync; docker compose down supersync",
|
||||
"e2e:supersync:file": "docker compose up -d --build supersync && echo 'Waiting for SuperSync server...' && until curl -s http://localhost:1900/health > /dev/null 2>&1; do sleep 1; done && echo 'Server ready!' && E2E_VERBOSE=true npx playwright test --config e2e/playwright.config.ts --reporter=list",
|
||||
"e2e:supersync:down": "docker compose down supersync",
|
||||
"electron": "NODE_ENV=PROD electron .",
|
||||
"electron:build": "tsc -p electron/tsconfig.electron.json",
|
||||
"electron:watch": "tsc -p electron/tsconfig.electron.json --watch",
|
||||
|
|
|
|||
|
|
@ -424,6 +424,22 @@ export class OperationLogStoreService {
|
|||
return cursor ? (cursor.key as number) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there are any operations that have been synced to the server.
|
||||
* Used to distinguish between:
|
||||
* - Fresh client (only local ops, never synced) → NOT a server migration
|
||||
* - Client that previously synced (has synced ops) → Server migration scenario
|
||||
*/
|
||||
async hasSyncedOps(): Promise<boolean> {
|
||||
await this._ensureInit();
|
||||
// Use the bySyncedAt index to efficiently check for any synced ops
|
||||
const cursor = await this.db
|
||||
.transaction('ops')
|
||||
.store.index('bySyncedAt')
|
||||
.openCursor();
|
||||
return cursor !== null;
|
||||
}
|
||||
|
||||
async saveStateCache(snapshot: {
|
||||
state: unknown;
|
||||
lastAppliedOpSeq: number;
|
||||
|
|
|
|||
|
|
@ -382,11 +382,14 @@ export class OperationLogSyncService {
|
|||
* Check if we're connecting to a new/empty server and need to upload full state.
|
||||
*
|
||||
* This handles the server migration scenario:
|
||||
* - Client has history (not fresh)
|
||||
* - Client has PREVIOUSLY SYNCED operations (not just local ops)
|
||||
* - lastServerSeq is 0 for this server (first time connecting)
|
||||
* - Server is empty (latestSeq = 0)
|
||||
*
|
||||
* When detected, creates a SYNC_IMPORT with full state before regular ops are uploaded.
|
||||
*
|
||||
* IMPORTANT: A fresh client with only local (unsynced) ops is NOT a migration scenario.
|
||||
* Fresh clients should just upload their ops normally without creating a SYNC_IMPORT.
|
||||
*/
|
||||
private async _checkAndHandleServerMigration(
|
||||
syncProvider: SyncProviderServiceInterface<SyncProviderId>,
|
||||
|
|
@ -411,11 +414,23 @@ export class OperationLogSyncService {
|
|||
return;
|
||||
}
|
||||
|
||||
// Server is empty AND we have history (not fresh) AND lastServerSeq is 0
|
||||
// CRITICAL: Check if this client has PREVIOUSLY synced operations.
|
||||
// A client that has never synced (only local ops) is NOT a migration case.
|
||||
// It's just a fresh client that should upload its ops normally.
|
||||
const hasSyncedOps = await this.opLogStore.hasSyncedOps();
|
||||
if (!hasSyncedOps) {
|
||||
OpLog.normal(
|
||||
'OperationLogSyncService: Empty server detected, but no previously synced ops. ' +
|
||||
'This is a fresh client, not a server migration. Proceeding with normal upload.',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Server is empty AND we have PREVIOUSLY SYNCED ops AND lastServerSeq is 0
|
||||
// This is a server migration - create SYNC_IMPORT with full state
|
||||
OpLog.warn(
|
||||
'OperationLogSyncService: Server migration detected during upload check. ' +
|
||||
'Empty server detected, creating full state SYNC_IMPORT.',
|
||||
'Empty server with previously synced ops. Creating full state SYNC_IMPORT.',
|
||||
);
|
||||
await this._handleServerMigration();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue