How it works
The explanation, with examples you can run.
How a registry works
The common setup, with Confluent's Schema Registry:
- The producer's serialiser registers the record's schema under a subject (by default
<topic>-valueor<topic>-key) and gets back a numeric id. - Each record starts with a version byte (0) and the 4-byte schema id, then the encoded data.
- The consumer's deserialiser reads the id, fetches and caches the schema, and decodes.
Registering a new version triggers a compatibility check against earlier versions of the same subject. An incompatible change is rejected at the producer, before any bad record reaches the topic.
Evolving a schema
Version 1 of an Avro schema:
{ "type": "record", "name": "OrderPaid", "namespace": "shop", "fields": [ { "name": "orderId", "type": "string" }, { "name": "amountCents", "type": "long" } ] }Version 2 adds a field with a default:
{ "type": "record", "name": "OrderPaid", "namespace": "shop", "fields": [ { "name": "orderId", "type": "string" }, { "name": "amountCents", "type": "long" }, { "name": "discountCents", "type": "long", "default": 0 } ] }A consumer on version 2 reading a version 1 record gets discountCents: 0. That makes version 2 backward compatible. Checking it against the registry's REST API (not run here; host and topic are examples):
curl -s -X POST -H "Content-Type: application/vnd.schemaregistry.v1+json" \ --data '{"schema": "<version 2 as an escaped JSON string>"}' \ http://registry:8081/compatibility/subjects/orders-paid-value/versions/latestCompatibility modes
| Mode | Guarantees | Safe changes (Avro) | Upgrade first |
|---|---|---|---|
| BACKWARD (default) | New schema reads data written with the previous one | Delete fields, add fields with defaults | Consumers |
| FORWARD | Previous schema reads data written with the new one | Add fields, delete fields with defaults | Producers |
| FULL | Both | Add or delete fields that have defaults | Either |
*_TRANSITIVE | The same, against all earlier versions, not just the last | ||
| NONE | No checks | Anything | All at once |
Plain BACKWARD only compares with the latest version, so a consumer replaying a topic from the beginning may meet records written with much older versions; BACKWARD_TRANSITIVE protects that case. For a change that no mode allows (changing a field's type from number to string), create a new topic or a new record type and migrate consumers.
Without a registry
JSON without a schema works until someone renames a field. If you go without a registry, at least version your records (a schemaVersion field or a header), write consumers that ignore unknown fields, and never remove or repurpose a field in place. The same rules apply to events in any log or queue.
Check yourself
3 questions. Get them all right to finish this topic.
0 of 3 right so far. Get all three to finish this topic.