-
Notifications
You must be signed in to change notification settings - Fork 0
refactor(sync): drop batch_id protocol fields #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,10 +15,8 @@ import { getServerIdentity } from '../services/serverIdentity' | |
| import vaultRoutes from './sync.vaults' | ||
| import { broadcastToSpace } from './ws' | ||
|
|
||
| import { validateBatches } from '../utils/syncUtils' | ||
|
|
||
| // Re-export sync utilities | ||
| export { validateBatches, type SyncChange, type BatchValidationError } from '../utils/syncUtils' | ||
| export { type SyncChange } from '../utils/syncUtils' | ||
|
|
||
| const sync = new Hono() | ||
|
|
||
|
|
@@ -38,9 +36,13 @@ function getCallerDid(c: any): string | null { | |
|
|
||
| /** | ||
| * POST /sync/push | ||
| * Push CRDT changes to server with unencrypted metadata for deduplication | ||
| * Uses INSERT ... ON CONFLICT DO UPDATE to keep only latest value per cell | ||
| * Validates batch completeness if batchId/batchSeq/batchTotal are provided | ||
| * Push CRDT changes to server with unencrypted metadata for deduplication. | ||
| * Uses INSERT ... ON CONFLICT DO UPDATE to keep only latest value per cell. | ||
| * | ||
| * HLC atomicity: every change in a push shares its sender-side transaction HLC, | ||
| * and all changes in one request are persisted in a single db.transaction() — | ||
| * so an HLC-group is never split across partial inserts. The client chunks | ||
| * at HLC boundaries, the server never splits what the client sends. | ||
| */ | ||
| sync.post('/push', zValidator('json', pushChangesSchema), async (c) => { | ||
| const { spaceId, changes: rawChanges } = c.req.valid('json') | ||
|
|
@@ -121,20 +123,10 @@ sync.post('/push', zValidator('json', pushChangesSchema), async (c) => { | |
| spaceAuthenticatedPublicKey = authenticatedPublicKey | ||
| } | ||
|
|
||
| // Validate batch completeness + duplicate-sequence detection. | ||
| // | ||
| // Delegates to validateBatches() in utils/syncUtils.ts — the previous | ||
| // inline duplicate of this logic checked completeness before duplicates, | ||
| // so a batch like seq=[1,1,3] with batchTotal=3 reported "Incomplete | ||
| // batch (missing 2)" instead of "Duplicate sequence numbers", masking | ||
| // the real client bug. The shared util has the correct check order. | ||
| const batchError = validateBatches(changes) | ||
| if (batchError) { | ||
| return c.json(batchError, 400) | ||
| } | ||
|
|
||
| // All batches are complete - apply changes atomically in a transaction | ||
| // This ensures either ALL changes are applied or NONE (on error/constraint violation) | ||
| // Apply changes atomically in a transaction so either ALL changes are | ||
| // applied or NONE. HLC-group atomicity is guaranteed by the client | ||
| // chunking at HLC boundaries before calling this endpoint — the server | ||
| // trusts the sender's grouping and does no completeness verification. | ||
|
Comment on lines
+126
to
+129
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Enforce the single-HLC request invariant server-side. Line 126-Line 129 explicitly trusts client chunking, but there is no guard against mixed Suggested guard before transaction const changes = rawChanges as PushChange[]
+ const uniqueHlc = new Set(changes.map((c) => c.hlcTimestamp))
+ if (uniqueHlc.size > 1) {
+ return c.json({ error: 'All changes in one push request must share the same hlcTimestamp' }, 400)
+ }
try {🤖 Prompt for AI Agents |
||
| // | ||
| // PostgreSQL has a limit of 65534 parameters per query. | ||
| // Each change has ~9 parameters, so we can safely insert ~5000 changes per query. | ||
|
|
||
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Validate
hlcTimestampstructurally instead of accepting any string.On Line 45,
hlcTimestamp: z.string()is too permissive now that HLC is the primary transaction/ordering key. Malformed values can break ordering assumptions in push conflict handling.Suggested hardening
export const pushChangeSchema = z.object({ tableName: sqlIdentifier, rowPks: z.string(), // JSON string columnName: sqlIdentifier.nullable(), - hlcTimestamp: z.string(), // Transaction-scope HLC; shared by every change from one sender-side tx + hlcTimestamp: z + .string() + .min(1) + // Replace with your canonical HLC regex/parser if available. + .regex(/^[^\\s]+$/, 'invalid hlcTimestamp'), deviceId: z.string().optional(),📝 Committable suggestion
🤖 Prompt for AI Agents