Documentation
¶
Overview ¶
Package sqlguard runs the guarded write four durable-state packages in this module are built on, and says what it means when the guard matches nothing.
A guarded write is an UPDATE whose WHERE clause encodes the state its caller believed the row was in — "finish this operation, if it is still running", "advance this saga, if it is still on the step I read". Matching zero rows is not an error the driver reports; it is a success that recorded nothing, and the whole reason the guard is in the statement is to notice. operations, dataprivacy, saga and metering had each written the same seven-step handling of that by hand: exec, read RowsAffected, stamp the span, and on zero mark the span, count the miss, log the identifier, and return a wrapped sentinel.
The copies had already begun to differ — metering logs no identifier and returns an ad-hoc error where its siblings return a package sentinel — which is the argument for one home. What varies between callers is what the row is called and what a missed guard means to that caller, and that is exactly what a Guard carries.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Guard ¶
type Guard struct {
// MissCounter counts guards that matched nothing, labeled by the operation
// that missed. An absent one records nothing.
MissCounter metrics.Int64Counter
// NotFound is the sentinel a missed guard returns, wrapped with Reason so
// the caller keeps a matchable error and a readable message. An absent one
// leaves Reason as the whole error.
NotFound error
// Namespace prefixes the span and metric attributes this writes — the
// owning package's name, as it appears in its other keys: "operations",
// "dataprivacy", "saga", "metering".
Namespace string
// IDKey names the log field carrying the row's identifier. Empty logs no
// identifier, for a guard that is not keyed by one.
IDKey string
// Message is the line logged when the guard matches nothing. It is logged
// rather than merely counted because this one is worth reading: the work
// already ran — the export is written, the charge is posted — and the row
// that should record it has moved on without us.
Message string
// Reason renders the returned error, as a format taking the identifier:
// "saga instance %q is no longer advanceable".
Reason string
}
Guard describes one package's guarded writes: what the row is called, and what it means when the guard matches nothing.
The zero value works and is silent — no counter, no identifier in the line — which is the honest behavior for a caller that has not said what a miss means. Everything it does say is a field rather than a parameter because it is fixed for the lifetime of a store, while the query and the identifier change per call.
func (*Guard) Count ¶
func (g *Guard) Count( ctx context.Context, op observability.Operation, affected int64, err error, id, operation, description string, ) error
Count reports a guarded write that has already run, from the row count and error a generated statement returned.
It is Exec's other half, for a store on the unison tier: the statement went through the generated querier, whose :execrows method has already asked the driver for the affected count, so what is left is everything Exec does after the exec. The arguments after the count are Exec's, and mean the same things.
One narrowing against the hand-written path: a driver that declines to report the count reaches this as an error rather than as an acknowledged unknown, because the generated method has no seam between running the statement and reading the count. None of the three supported drivers declines.
func (*Guard) Exec ¶
func (g *Guard) Exec( ctx context.Context, op observability.Operation, q database.SQLQueryExecutor, query string, args []any, id, operation, description string, ) error
Exec runs a guarded write and reports one that matched nothing.
id identifies the row for the log line and the returned error; operation labels the miss counter — "finish", "advance", "release" — so a guard miss distinguishes a caller cancelling twice from a worker losing a lease race, one of which is routine and one of which wants looking at. description names the write in whatever error the driver returns.
func (*Guard) OpAttr ¶
func (g *Guard) OpAttr(operation string) metric.MeasurementOption
OpAttr labels a measurement with the store operation that produced it, so a guard miss distinguishes a caller cancelling twice from a worker losing a lease race — one is routine and one wants looking at.
It is exported because not every guard is a guarded UPDATE. A caller that reads a row, finds it in the wrong state, and reports the miss itself wants the same attribute on the same counter, and two spellings of it would put half a package's misses in a series nothing groups with the other half.