search for standard sites pub-search.waow.tech
search zig blog atproto

fix: full sync on startup with stale-doc cleanup

fullSync now tracks synced URIs in a temp table and deletes local docs
that no longer exist in Turso. Startup always does a full sync instead
of incremental, so deleted docs get cleaned up on every deploy.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

+22 -3
+3 -3
backend/src/db/mod.zig
··· 84 } 85 86 fn syncLoop(turso: *Client, local: *LocalDb) void { 87 - // incremental sync on startup (falls back to full sync if no last_sync found) 88 - sync.incrementalSync(turso, local) catch |err| { 89 - std.debug.print("sync: initial sync failed: {}\n", .{err}); 90 }; 91 92 // get sync interval from env (default 5 minutes)
··· 84 } 85 86 fn syncLoop(turso: *Client, local: *LocalDb) void { 87 + // full sync on startup — reconciles with Turso and cleans up stale docs 88 + sync.fullSync(turso, local) catch |err| { 89 + std.debug.print("sync: initial full sync failed: {}\n", .{err}); 90 }; 91 92 // get sync interval from env (default 5 minutes)
+19
backend/src/db/sync.zig
··· 47 std.debug.print("sync: local has data, keeping ready during re-sync\n", .{}); 48 } 49 50 // sync documents in batches — fetch from Turso unlocked, write to local with brief lock 51 var doc_count: usize = 0; 52 var offset: usize = 0; ··· 79 insertDocumentLocal(conn, row) catch |err| { 80 std.debug.print("sync: insert doc failed: {}\n", .{err}); 81 }; 82 doc_count += 1; 83 } 84 conn.exec("COMMIT", .{}) catch {}; ··· 163 popular_count += 1; 164 } 165 conn.exec("COMMIT", .{}) catch {}; 166 } 167 168 // record sync time (brief lock)
··· 47 std.debug.print("sync: local has data, keeping ready during re-sync\n", .{}); 48 } 49 50 + // create temp table to track synced URIs (for stale-doc cleanup) 51 + { 52 + local.lock(); 53 + defer local.unlock(); 54 + conn.exec("DROP TABLE IF EXISTS _synced_uris", .{}) catch {}; 55 + conn.exec("CREATE TEMP TABLE _synced_uris (uri TEXT PRIMARY KEY)", .{}) catch {}; 56 + } 57 + 58 // sync documents in batches — fetch from Turso unlocked, write to local with brief lock 59 var doc_count: usize = 0; 60 var offset: usize = 0; ··· 87 insertDocumentLocal(conn, row) catch |err| { 88 std.debug.print("sync: insert doc failed: {}\n", .{err}); 89 }; 90 + conn.exec("INSERT OR IGNORE INTO _synced_uris (uri) VALUES (?)", .{row.text(0)}) catch {}; 91 doc_count += 1; 92 } 93 conn.exec("COMMIT", .{}) catch {}; ··· 172 popular_count += 1; 173 } 174 conn.exec("COMMIT", .{}) catch {}; 175 + } 176 + 177 + // clean up stale docs that were deleted from Turso (brief lock) 178 + { 179 + local.lock(); 180 + defer local.unlock(); 181 + conn.exec("DELETE FROM documents_fts WHERE uri NOT IN (SELECT uri FROM _synced_uris)", .{}) catch {}; 182 + conn.exec("DELETE FROM documents WHERE uri NOT IN (SELECT uri FROM _synced_uris)", .{}) catch {}; 183 + conn.exec("DELETE FROM document_tags WHERE document_uri NOT IN (SELECT uri FROM _synced_uris)", .{}) catch {}; 184 + conn.exec("DROP TABLE IF EXISTS _synced_uris", .{}) catch {}; 185 } 186 187 // record sync time (brief lock)