Alert on unvalidated messages¶
A guard that cannot resolve a schema processes the message anyway and increments a counter. That counter is the load-bearing half of the design, and a deployment that does not export it has taken the fail-open without the "loudly".
Read it directly¶
Or wire a callback¶
unvalidated, err := meter.Int64Counter("schema.unvalidated",
metric.WithDescription("messages processed without validating them"))
if err != nil {
return err
}
guard := schema.NewGuard(resolver, validators,
schema.OnFailOpen(func(ctx context.Context, dataschema string, cause error) {
unvalidated.Add(ctx, 1, metric.WithAttributes(
attribute.String("dataschema", dataschema),
))
logger.WarnContext(ctx, "processed a message unvalidated",
"dataschema", dataschema, "cause", cause)
}),
)
It is a callback rather than a metric instrument so that this module does not put an observability stack into the graph of every service that reads a message. The counter is the requirement; the SDK is your choice.
What to alert on¶
schema.unvalidated should be zero in normal operation. Alert on it being anything else.
That is only true because of what is deliberately not counted:
| Case | Counted? |
|---|---|
An event with no dataschema at all |
No. It claims no schema; nothing failed |
| Payload resolved and did not conform | No. That is a validation failure, and it fails closed |
dataschema is not a URN we recognise |
Yes |
| A URN this service does not hold | Yes |
| The registry is unreachable | Yes |
| No validator registered for the schema's language | Yes |
| The schema resolved but will not compile, or names no root | Yes |
A counter that ticked during healthy running would be a counter nobody could alert on, which is why the first two rows matter as much as the last four.
What it is usually telling you¶
A deleted embedded schema. The commonest cause by a distance: a service stopped emitting v1, so
somebody removed orders.created.v1.json, and the stream still holds a year of v1 messages.
A version skew. A producer deployed v4 before consumers embedded it.
A missing validator. The schema resolved fine; nothing was registered that could read it.
A mis-published schema. A protobuf schema published without its root message, or a JSON Schema that will never compile. Worth calling out because it is the case that used to fail closed: the guard now lets the message through and counts it, so the counter is how you find out rather than a support ticket saying a topic has stopped working.
An actual outage, if the guard is backed by the network resolver.
The first three are fixed by embedding what you consume. Only the last is the registry's fault, and it is the one the fail-open exists for.