Skip to content

bug(sqlserver-cdc): UUID primary-key ordering mismatch can lose changes during backfill #27107

Description

@hzxa21

Describe the bug

SQL Server CDC accepts uniqueidentifier primary keys and maps them to RisingWave varchar, but the CDC backfill event-range check compares the resulting strings. SQL Server orders uniqueidentifier values differently from canonical UUID strings. Consequently, snapshot progress and CDC event filtering can disagree about which keys have already been scanned, potentially losing changes or producing incorrect results during backfill.

This is a correctness issue with UUID primary-key ordering during snapshot/CDC merging, not a lack of UUID column decoding support. It also applies when a UUID component determines the ordering of a composite primary key.

Evidence status: confirmed ordering mismatch and affected code path by source inspection; a deterministic live SQL Server + RisingWave reproduction has not yet been run. The scenario below is a proposed regression test, not a reported executed test.

Affected code

Inspected main at 6666dd6a1af39df23add684dce9d2defa95a252d:

SQL Server's UUID ordering gives the final six bytes higher significance than the leading bytes; see Microsoft's explanation.

Proposed reproduction / regression test

Use deliberately inverted keys rather than random UUIDs:

-- Run in SQL Server to inspect native UUID order versus textual order.
DECLARE @keys TABLE (id uniqueidentifier PRIMARY KEY);
INSERT INTO @keys VALUES
  ('FFFFFFFF-0000-0000-0000-000000000001'), -- A
  ('00000000-0000-0000-0000-000000000002'), -- B
  ('00000000-0000-0000-0000-000000000003'); -- C
SELECT id FROM @keys ORDER BY id; -- A, B, C
SELECT id FROM @keys ORDER BY CONVERT(varchar(36), id); -- B, C, A
  1. Create a CDC-enabled upstream table with these keys and a mutable payload; create the corresponding RisingWave CDC table.
  2. Control snapshot batching/barriers so A and B have been emitted and current_pos = B, while backfill remains unfinished with C still unread.
  3. Update or delete A upstream and ensure its CDC event is processed while that progress boundary is active and passes the CDC offset check.
  4. A is in the already-scanned range according to SQL Server (A < B), so the event must be forwarded. RisingWave's string comparison instead gives A > B, hiding the event. A is not revisited by subsequent native snapshot pages, potentially leaving a stale value or an undeleted row.
  5. Compare complete downstream contents with upstream after backfill and CDC catch-up, not just row counts. Cover the opposite misclassification (an unscanned key considered scanned) as well.

Expected behavior / fix direction

All UUID primary-key comparisons used to merge SQL Server snapshots with CDC must agree with upstream ordering, including composite keys. Preserve upstream type information and use a SQL Server UUID comparator in the actual event-range checks. Until that is implemented and tested, reject unsupported key ordering by default.

Regression coverage should include controlled concurrent updates, deletes, and inserts; composite keys; and recovery during unfinished backfill.

Recovery scope

The ordering mismatch alone does not establish that restarting a static snapshot reader skips rows. get_new_pos takes the last snapshot row rather than a string-order maximum, and the saved key is passed back to SQL Server for native pagination. A dedicated UUID restart test remains useful; static recovery data loss has not been demonstrated here.

History and related work

Error message / deployment

No live reproduction logs or deployment version: this report is based on the pinned main source above. The failure mode can be incorrect data rather than an explicit error.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    type/bugType: Bug. Only for issues.

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions