Skip to main content
Version: 0.6.x
schema_registryCODEC

Schema Registry

The schema_registry codec decodes Confluent wire-format messages by resolving the embedded schema id from a Confluent Schema Registry at runtime. Each schema version (id) is fetched at most once and cached per codec instance, so multi-version schema evolution is supported within the same stream. Both Protobuf and Avro subjects are supported, dispatched on the registry's schemaType response. An optional subject compatibility gate fails the stream fast when a subject's registered compatibility level drops below the configured minimum.

Configuration​

FieldTypeRequiredDefaultDescription
typestringyes—Fixed value "schema_registry"
registry_urlstringyes—Confluent Schema Registry root URL, e.g. http://localhost:8081
message_typestringconditional—Fully qualified Protobuf message type. Required for Protobuf schemas; omit for Avro.
subjectstringno—Registry subject for the compatibility gate.
min_compatibilitystringno—none, backward, forward or full (lowercase only; other values are rejected at config load). Minimum subject compatibility level enforced on the first decoded message. Requires subject.
authobjectno—Registry authentication configuration
auth.typestringyes (if auth)—Authentication method: basic or bearer
auth.usernamestringno—Username for basic mode
auth.passwordstringno—Password for basic mode
auth.tokenstringno—Token for bearer mode

Examples​

Protobuf topic:

codec:
type: schema_registry
registry_url: http://localhost:8081
message_type: com.example.User

Avro topic with a compatibility gate:

codec:
type: schema_registry
registry_url: http://registry:8081
subject: orders-value
min_compatibility: backward

Bearer form:

codec:
type: schema_registry
registry_url: http://registry:8081
message_type: com.example.User
auth:
type: bearer
token: ${SR_TOKEN}

See examples/schema_registry.yaml and examples/schema_registry_avro.yaml.

Semantics​

Wire format​

[0x00 magic][4-byte big-endian schema id][payload]

The codec validates the magic byte, splits out the id and payload, then resolves the schema from the registry using the id.

Workflow​

  1. Parse the Confluent wire format (magic + id + payload).
  2. Resolve the schema by id (GET {registry}/schemas/ids/{id}), caching it per id. The dispatch target comes from the response's schemaType:
    • PROTOBUF → build a MessageDescriptor and decode the payload via the flat Protobuf→Arrow mapping. When schemaType is absent (older registries), the schema text is detected by content: a JSON object decodes as Avro, anything else as Protobuf. If the text parses as neither, the error names both attempts.
    • AVRO → parse the Avro writer schema and decode the payload via the flat Avro→Arrow mapping.
  3. Merge the single-row batches of the request into one columnar batch.

Schema resolution is abstracted behind a pluggable SchemaResolver trait (RestSchemaResolver for production, an in-memory implementation for tests), so the wire format / caching / multi-version logic can be unit-tested without a real registry.

Avro → Arrow mapping​

The flat mapping mirrors the Protobuf one: top-level record fields become columns. Supported types:

Avro typeArrow type
nullNull
booleanBoolean
intInt32
longInt64
floatFloat32
doubleFloat64
bytes, fixedBinary
string, enumUtf8
uuidUtf8
dateDate32
time-millis / time-microsTime32(ms) / Time64(µs)
timestamp-millis / timestamp-microsTimestamp(ms/µs, UTC)
local-timestamp-*Timestamp (no timezone)
decimalDecimal128 (precision ≤ 38)
["null", T] unionnullable T

Nested records, arrays, maps and unions with more than two branches are rejected with an explicit error rather than silently flattened.

Subject compatibility gate​

With subject and min_compatibility configured, the first decoded message triggers a single GET {registry}/config/{subject}?defaultToGlobal=true request. The subject's registered level is ranked (NONE < BACKWARD/FORWARD incl. their _TRANSITIVE variants < FULL incl. FULL_TRANSITIVE); if it is below the configured minimum the stream fails with an error naming the subject, the actual level and the requirement. A pass verdict is cached for the codec lifetime (the endpoint is hit at most once); a failure is retried on later decodes no sooner than every 30 seconds — decodes inside that window return the last error without another request — so a transient registry outage does not brick the codec after the registry recovers. This catches compatibility-policy degradation (e.g. a subject switched to NONE) at the pipeline instead of silently decoding incompatible future versions.

Notes / Non-goals​

  • Decode-side only: it resolves schemas by id; it never registers new schemas, and encoding emits line-delimited JSON (registry-agnostic).
  • schemaType absent from the registry response is resolved by content detection: schema text that is a JSON object decodes as Avro (the original Confluent default — older registries usually omit the field for Avro subjects), any other text as Protobuf. Both old-registry Avro streams and Protobuf streams therefore keep working.
  • Debezium envelope flattening is not performed — payloads are mapped as their registered schema declares; nested envelope structs (source, before) are rejected by the flat mapping.
  • Protobuf schema references (imports) are not supported — only single-file schemas.
  • Avro decoding uses the writer schema only (no reader-schema resolution), matching the Protobuf path.
  • Local schema compatibility derivation (reader/writer schema resolution checks, POST /compatibility) is out of scope; the gate reads the registry's subject configuration, which is the compatibility authority.