Registering and evolving schemas
Register a schema for an event type and the server rejects non-conforming payloads at write time, before they reach the log. See Schema validation.
Register
await pool.RegisterSchemaAsync(new RegisterSchemaRequest
{
ClientId = adminClientId,
SchemaKey = new SchemaKey(orgId, ordersType, eventTypeMajor: 1, eventTypeMinor: 0),
SchemaType = SchemaType.Json, // Json, Avro, or Protobuf
Schema = """
{
"type": "object",
"required": ["sku", "qty"],
"properties": {
"sku": { "type": "string" },
"qty": { "type": "integer", "minimum": 1 }
}
}
""",
});
A schema is keyed to (org, aggregate type, major, minor) and applies to exactly that key. From then on, a write of that event type whose payload does not conform is rejected with SchemaValidationException (2022), and nothing in the request is appended.
What Schema holds depends on the type:
SchemaType | Schema string | Payload checked as |
|---|---|---|
Json | a JSON Schema document | UTF-8 JSON, validated against the schema |
Avro | an Avro schema (JSON) | an Avro binary datum decoded with that schema |
Protobuf | base64(FileDescriptorSet):MessageName | that message, decoded from the payload bytes |
The schema string is capped by --max-schema-size-bytes (default 16384). Format details are in Schema formats.
What validation does not cover
- Unregistered types. An event type with no schema at its exact
(major, minor)is not validated at all. Bump the minor and forget to register, and that version goes in unchecked. - Encrypted events. The server skips validation for any event that carries an
Iv. It cannot read ciphertext, so it does not try. See Encryption. - Events already written. A schema applies to writes after it registers. The log is not re-checked.
Evolve
Register a new schema for the new version rather than mutating the old one. There is no update or delete:
- a minor bump for a backward-compatible change (a new optional field),
- a major bump for a breaking one.
Both versions stay registered and valid, so producers and consumers migrate on their own schedule.
Errors
All come back as SchemaErrorException except 2022, which is its subclass SchemaValidationException.
| Code | Name | Cause |
|---|---|---|
| 2020 | RegisterSchemaAlreadyExists | That (org, type, major, minor) is already registered. |
| 2021 | RegisterSchemaInvalid | The schema does not parse or compile, or exceeds --max-schema-size-bytes. |
| 2022 | WriteSchemaValidationFailed | A write's payload does not conform. |
| 2024 | RegisterSchemaUnsupportedType | Unknown SchemaType. |
| 2029 | RegisterSchemaCoordinationFailed | Not every shard confirmed the registration. |
Registration runs on the leader. It lands on shard 0 first, then fans out to the other shards.
A 2029 means shard 0 has the schema and at least one other shard does not. Retrying the same registration fails fast with 2020 at shard 0 and never reaches the shards that missed it, so writes routed there go unvalidated. Treat a 2029 as an incident, not a retry.