sqlguard

package
v10.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 15, 2026 License: AGPL-3.0 Imports: 7 Imported by: 0

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) 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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL