Skip to content

fix(mysql): align JSON representation between snapshot and CDC#4535

Draft
dtunikov wants to merge 5 commits into
mainfrom
fix/dbi-823/json-representation-snapshot-cdc-mismatch
Draft

fix(mysql): align JSON representation between snapshot and CDC#4535
dtunikov wants to merge 5 commits into
mainfrom
fix/dbi-823/json-representation-snapshot-cdc-mismatch

Conversation

@dtunikov

@dtunikov dtunikov commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Problem (DBI-823)

MySQL JSON columns reached PeerDB through two different renderers that disagreed on representation:

  • Snapshot (text protocol): MySQL server-rendered JSON — type-faithful (DOUBLE 1.0 stays 1.0, JSONB key order preserved) but with a space after : and , (e.g. {"a": 1.0}).
  • CDC (binlog JSONB decoder): historically lossy — 1.0 collapsed to 1, object keys reordered by Go map iteration.

The same logical value therefore landed in the destination with different text depending on whether it arrived via initial load or CDC — a real mismatch when JSON is stored as a String (the default in ClickHouse).

Fix

  • Set RenderJSONAsMySQLText: true on the binlog syncer (startSyncer). The pinned go-mysql fork's decoder then emits type-faithful, key-order-preserving, compact JSON.
  • json.Compact the snapshot text-protocol form so it matches the compact CDC output byte for byte. json.Compact only elides structural whitespace — number literals (1.0), string bytes, and key order are preserved; invalid JSON falls back to the raw text.

MariaDB is unaffected: its JSON is LONGTEXT-backed, so values map to QValueKindString/Bytes and never hit the JSON compaction path, and RenderJSONAsMySQLText only affects MySQL JSONB decoding.

Residual (documented) corners

Full byte-identity still can't hold for a couple of go-mysql-documented cases: float exponent form (1.5e-05 vs MySQL's 1.5e-5) and the NEWDECIMAL opaque type tag. The numeric values round-trip; only the text differs.

Tests

  • TestCompactMySQLJSON — unit test for the compaction helper (whitespace elision, 1.0 preservation, key-order preservation, in-string whitespace, invalid-JSON fallback).
  • Test_MySQL_JSON_SnapshotCDCConsistency — e2e test inserting JSON variants during both snapshot and CDC, asserting the destination representations match (and, for MySQL, equal the expected compact type-faithful form).

🤖 Generated with Claude Code

dtunikov and others added 2 commits July 3, 2026 17:04
MySQL JSON columns reached PeerDB through two different renderers that
disagreed on representation:

- Snapshot (text protocol): MySQL server-rendered JSON, type-faithful
  (DOUBLE 1.0 stays "1.0", JSONB key order preserved) but with a space
  after ':' and ','.
- CDC (binlog JSONB decoder): historically lossy -- 1.0 collapsed to 1,
  keys reordered by Go map iteration.

Enable RenderJSONAsMySQLText on the binlog syncer so CDC emits
type-faithful, key-order-preserving, compact JSON, and json.Compact the
snapshot text form so both paths are byte-consistent. MariaDB JSON is
LONGTEXT-backed (QValueKindString), so it never hits the compaction path
and is unaffected.

Adds a unit test for the compaction helper and an e2e test asserting the
snapshot and CDC representations of the same JSON value match.

DBI-823

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
DisableRetrySync: true,
UseDecimal: true,
ParseTime: true,
RenderJSONAsMySQLText: true,

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this one was added (really weird git diff because of go fmt)
we can also put it behind mirror version gate to ensure that the old mirrors continue working the same way (in case someone relies on default parsing logic..)

@codecov

codecov Bot commented Jul 3, 2026

Copy link
Copy Markdown

❌ 4 Tests Failed:

Tests completed Failed Passed Skipped
2951 4 2947 346
View the top 3 failed test(s) by shortest run time
github.com/PeerDB-io/peerdb/flow/e2e::TestPeerFlowE2ETestSuiteMySQL_CH
Stack Traces | 0.01s run time
=== RUN   TestPeerFlowE2ETestSuiteMySQL_CH
=== PAUSE TestPeerFlowE2ETestSuiteMySQL_CH
=== CONT  TestPeerFlowE2ETestSuiteMySQL_CH
--- FAIL: TestPeerFlowE2ETestSuiteMySQL_CH (0.01s)
github.com/PeerDB-io/peerdb/flow/e2e::TestPeerFlowE2ETestSuiteMySQL_CH_Cluster
Stack Traces | 0.01s run time
=== RUN   TestPeerFlowE2ETestSuiteMySQL_CH_Cluster
=== PAUSE TestPeerFlowE2ETestSuiteMySQL_CH_Cluster
=== CONT  TestPeerFlowE2ETestSuiteMySQL_CH_Cluster
--- FAIL: TestPeerFlowE2ETestSuiteMySQL_CH_Cluster (0.01s)
github.com/PeerDB-io/peerdb/flow/e2e::TestPeerFlowE2ETestSuiteMySQL_CH/Test_MySQL_JSON_SnapshotCDCConsistency
Stack Traces | 19.2s run time
=== RUN   TestPeerFlowE2ETestSuiteMySQL_CH/Test_MySQL_JSON_SnapshotCDCConsistency
=== PAUSE TestPeerFlowE2ETestSuiteMySQL_CH/Test_MySQL_JSON_SnapshotCDCConsistency
=== CONT  TestPeerFlowE2ETestSuiteMySQL_CH/Test_MySQL_JSON_SnapshotCDCConsistency
2026/07/03 16:07:40 INFO Received AWS credentials from peer for connector: ci x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN}
2026/07/03 16:07:40 INFO Received AWS credentials from peer for connector: clickhouse x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN}
    clickhouse_mysql_test.go:283: WaitFor waiting for initial snapshot 2026-07-03 16:07:44.183755887 +0000 UTC m=+336.161056381
    clickhouse_mysql_test.go:287: WaitFor waiting for cdc 2026-07-03 16:07:44.194263764 +0000 UTC m=+336.171564258
    clickhouse_mysql_test.go:299: 
        	Error Trace:	.../flow/e2e/clickhouse_mysql_test.go:299
        	Error:      	Not equal: 
        	            	expected: "{\"pi\":3.14,\"big\":1000000.5,\"neg\":-2.5}"
        	            	actual  : "{\"pi\":3.14,\"big\":1.0000005e+06,\"neg\":-2.5}"
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -1 +1 @@
        	            	-{"pi":3.14,"big":1000000.5,"neg":-2.5}
        	            	+{"pi":3.14,"big":1.0000005e+06,"neg":-2.5}
        	Test:       	TestPeerFlowE2ETestSuiteMySQL_CH/Test_MySQL_JSON_SnapshotCDCConsistency
        	Messages:   	snapshot/cdc JSON representation differs for "{\"pi\": 3.14, \"big\": 1000000.5, \"neg\": -2.5}"
--- FAIL: TestPeerFlowE2ETestSuiteMySQL_CH/Test_MySQL_JSON_SnapshotCDCConsistency (19.17s)
github.com/PeerDB-io/peerdb/flow/e2e::TestPeerFlowE2ETestSuiteMySQL_CH_Cluster/Test_MySQL_JSON_SnapshotCDCConsistency
Stack Traces | 20.3s run time
=== RUN   TestPeerFlowE2ETestSuiteMySQL_CH_Cluster/Test_MySQL_JSON_SnapshotCDCConsistency
=== PAUSE TestPeerFlowE2ETestSuiteMySQL_CH_Cluster/Test_MySQL_JSON_SnapshotCDCConsistency
=== CONT  TestPeerFlowE2ETestSuiteMySQL_CH_Cluster/Test_MySQL_JSON_SnapshotCDCConsistency
2026/07/03 16:09:31 INFO Received AWS credentials from peer for connector: ci x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN}
2026/07/03 16:09:31 INFO Received AWS credentials from peer for connector: clickhouse x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN}
2026/07/03 16:09:31 INFO fetched schema x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} table=e2e_test_mychcl_1lahmrm9.test_nullable_mirror
    clickhouse_mysql_test.go:283: WaitFor waiting for initial snapshot 2026-07-03 16:09:36.723081725 +0000 UTC m=+448.700382229
    clickhouse_mysql_test.go:287: WaitFor waiting for cdc 2026-07-03 16:09:36.739412568 +0000 UTC m=+448.716713072
2026/07/03 16:09:36 INFO fetched schema x-peerdb-additional-metadata={Operation:FLOW_OPERATION_UNKNOWN} table=e2e_test_mychcl_1lahmrm9.test_nullable_mirror
    clickhouse_mysql_test.go:299: 
        	Error Trace:	.../flow/e2e/clickhouse_mysql_test.go:299
        	Error:      	Not equal: 
        	            	expected: "{\"pi\":3.14,\"big\":1000000.5,\"neg\":-2.5}"
        	            	actual  : "{\"pi\":3.14,\"big\":1.0000005e+06,\"neg\":-2.5}"
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -1 +1 @@
        	            	-{"pi":3.14,"big":1000000.5,"neg":-2.5}
        	            	+{"pi":3.14,"big":1.0000005e+06,"neg":-2.5}
        	Test:       	TestPeerFlowE2ETestSuiteMySQL_CH_Cluster/Test_MySQL_JSON_SnapshotCDCConsistency
        	Messages:   	snapshot/cdc JSON representation differs for "{\"pi\": 3.14, \"big\": 1000000.5, \"neg\": -2.5}"
--- FAIL: TestPeerFlowE2ETestSuiteMySQL_CH_Cluster/Test_MySQL_JSON_SnapshotCDCConsistency (20.27s)

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

❌ Test Failure

Analysis: The PR's own new test Test_MySQL_JSON_SnapshotCDCConsistency deterministically fails with "Not equal: unexpected JSON representation at row 1" on the mysql-pos config (passing on both mysql-gtid configs), indicating the JSON snapshot/CDC mismatch the PR aims to fix is still present on that path — a real, config-dependent bug, not flakiness.
Confidence: 0.9

⚠️ This appears to be a real bug - manual intervention needed

View workflow run

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

❌ Test Failure

Analysis: A deterministic value-equality assertion ("unexpected JSON representation at row 1") fails in the very test (Test_MySQL_JSON_SnapshotCDCConsistency) that validates this PR's own fix for JSON snapshot/CDC alignment, indicating the fix is incomplete rather than a flaky failure.
Confidence: 0.9

⚠️ This appears to be a real bug - manual intervention needed

View workflow run

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

❌ Test Failure

Analysis: The PR's own new test Test_MySQL_JSON_SnapshotCDCConsistency deterministically fails a fixed JSON value assertion (expected "1.0", got "1") in both standalone and cluster variants, a real snapshot/CDC representation-mismatch bug the PR aims to fix — not a timeout, race, or network flake.
Confidence: 0.95

⚠️ This appears to be a real bug - manual intervention needed

View workflow run

dtunikov and others added 2 commits July 3, 2026 17:49
MySQL normalizes whole-number JSON doubles (1.0 -> 1) on storage, so the
previous exact-literal assertion was wrong. Rewrite the e2e test around
the invariant that actually matters for DBI-823: the snapshot and CDC
representations of the same JSON value must be byte-identical. Center the
variants on object key ordering, which is where the old CDC path (Go-map
lexicographic order) diverged from MySQL's (length-then-byte) order.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

❌ Test Failure

Analysis: A deterministic value mismatch in Test_MySQL_JSON_SnapshotCDCConsistency (JSON float serialized as "1.0000005e+06" instead of "1000000.5") that reproduced identically across multiple matrix jobs and directly reflects the unfixed bug this PR targets — not a flaky failure.
Confidence: 0.97

⚠️ This appears to be a real bug - manual intervention needed

View workflow run

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

❌ Test Failure

Analysis: A real, deterministic failure: the PR's own new test Test_MySQL_JSON_SnapshotCDCConsistency fails a JSON float-representation equality assertion identically across both MySQL_CH and MySQL_CH_Cluster variants, indicating the PR's snapshot/CDC JSON-alignment fix is incomplete rather than any flakiness.
Confidence: 0.93

⚠️ This appears to be a real bug - manual intervention needed

View workflow run

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant