-
Notifications
You must be signed in to change notification settings - Fork 959
confluent: fix schema_registry_encode rejecting decoder output #4704
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
base: main
Are you sure you want to change the base?
Changes from 1 commit
1c1d1a7
5bbed70
9881d90
b672342
12a2cff
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 |
|---|---|---|
|
|
@@ -177,14 +177,29 @@ func (*schemaRegistryEncoder) newAvroEncoder(avroJSON string) (schemaEncoder, er | |
| return nil, fmt.Errorf("parsing Avro schema: %w", err) | ||
| } | ||
|
|
||
| // Encode accepts both bare values (standard JSON) and tagged union | ||
| // maps (Avro JSON), so both avroRawJSON modes use the same path. | ||
| // Avro JSON is the canonical input: it is what schema_registry_decode | ||
| // emits (EncodeJSON) in both avroRawJSON modes — only union tagging | ||
| // differs, and DecodeJSON accepts tagged and bare unions alike — so one | ||
| // path still serves both. DecodeJSON is also the only reader that | ||
| // implements Avro JSON's bytes and fixed semantics, where a JSON string | ||
| // carries one byte per codepoint. Encode does not: it reads a Go string | ||
| // as UTF-8, mangling any byte above 0x7f, and for a decimal it accepts | ||
| // only numeric text, so every decimal backed by bytes or fixed failed to | ||
| // re-encode from what the decoder emitted. | ||
| // | ||
| // Input that is not Avro JSON falls through to Encode, which is the more | ||
| // permissive of the two: it also takes RFC 3339 strings and time.Time | ||
| // for timestamp fields, a shape CDC sources emit and Avro JSON cannot | ||
| // spell. Its error is therefore the one worth reporting. | ||
| return func(m *service.Message) error { | ||
| data, err := m.AsStructuredMut() | ||
| if err != nil { | ||
| return fmt.Errorf("extracting structured data: %w", err) | ||
| var native any | ||
| b, err := m.AsBytes() | ||
| if err != nil || schema.DecodeJSON(b, &native) != nil { | ||
| if native, err = m.AsStructuredMut(); err != nil { | ||
| return fmt.Errorf("extracting structured data: %w", err) | ||
| } | ||
| } | ||
|
Comment on lines
243
to
251
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. Routing through Previously the closure went straight to Concrete case: a mapping such as Suggested fix: only take the Refs
Contributor
Author
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. Confirmed and fixed in b672342.
Trade-off, since neither reader is a superset of the other: a message holding both forms — a raw payload something upstream read via Tests, in
Both breaks were confirmed failing before I trusted the tests. With One note on "worth a test covering a plain |
||
| binary, err := schema.Encode(data) | ||
| binary, err := schema.Encode(native) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
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.
Test coverage gap: the
fixed-backed decimal half of the fix is never exercised.The commit message and the new code comment both scope the bug to "every decimal backed by bytes or fixed" (
serde_avro.go#L184-L188), buttestSchemaLogicalTypes— the only schema this round-trip test uses — contains just abytesdecimal (processor_schema_registry_decode_test.go#L195-L207). No test ininternal/impl/confluentputs afixedtype through the encode path: the three"type": "fixed"occurrences in the package are inavro_walker_test.goandecs_avro_test.go, which cover schema walking / common-schema conversion, notnewAvroEncoder.fixeddiffers frombyteson the wire (no length prefix, size-checked), so it is a distinct path throughDecodeJSON→Encodeand can regress independently.Suggested fix: add a
fixed-backed decimal field (e.g.{"type":"fixed","name":"Dec","size":16,"logicalType":"decimal","precision":38,"scale":8}) to a schema used by this round-trip test, so both branches named in the fix are pinned.Per CONTRIBUTING.md §1.3.2 — "Tests should cover end-to-end functionality and prove that the connector works across supported configurations."
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.
Right,
fixedwas unexercised. Added in b672342.Rather than change
testSchemaLogicalTypes— its expected wire bytes are asserted verbatim by three other tests — the round trip is now table-driven over schemas, and the second one istestSchemaBytesFixed:{"name": "raw", "type": "bytes"} {"name": "raw_fixed", "type": {"type": "fixed", "name": "Raw4", "size": 4}} {"name": "dec_bytes", "type": {"type": "bytes", "logicalType": "decimal", "precision": 16, "scale": 2}} {"name": "dec_fixed", "type": {"type": "fixed", "name": "Dec", "size": 16, "logicalType": "decimal", "precision": 38, "scale": 8}}That covers both halves of the fix and both wire shapes side by side:
dec_bytescarries a length prefix,dec_fixedis 16 raw bytes with the unscaled0xbc614eleft-padded and no prefix. Plainraw/raw_fixedride along so the codepoint spelling is pinned without a logical type on top, andrawise9 00 7f 21so a byte above 0x7f is in there.Same schema runs through
TestSchemaRegistryEncodeAvroMessageFormandTestSchemaRegistryAvroPreserveLogicalTypesRoundTrip, sofixedis pinned through the structuredEncodepath as well as theDecodeJSONone.Verified failing first: with the encoder reverted to
AsStructuredMut+Encode, bothavro_raw_jsonmodes of thebytes_and_fixedround trip fail.