Documentation
¶
Overview ¶
Package benchnotify pushes benchmark lifecycle events to an SQS queue so an operator can watch a remote run with a blocking receive loop instead of grepping a remembered log format over SSM. Producer (the bench binaries) and consumer (deploy/benchmark/watch-events.sh) ship in the same commit, so the two cannot drift.
Event schema ¶
One JSON object per SQS message, single line, no envelope:
{
"run_id": "20260817-142233", // results timestamp id: results/<run_id>/
"event": "run_started" | "query_completed" | "run_completed" |
"suite_completed" | "fatal",
"query": "Q01", // query_completed
"try": 1, // query_completed, clickbench only (1 = cold)
"wall_seconds": 12.345, // query_completed
"rows": 4, // query_completed, tpch only
"ok": true, // query_completed
"run_index": 2, // run_completed (1-based)
"total_runs": 3, // run_started, run_completed
"total_seconds": 198.4, // run_completed, suite_completed
"cold_seconds": 256.5, // suite_completed, clickbench only (sum of try 1)
"hot_seconds": 164.4, // suite_completed, clickbench only (sum of min(try2,try3))
"error": "...", // fatal
"ts": "2026-08-17T14:22:33Z" // RFC3339, always present
}
Omitted fields are absent from the JSON, not zero-valued — a consumer should treat a missing field as "not applicable to this event".
Event order for a TPC-H run: one run_started, then per run N query_completed events followed by a run_completed, then one suite_completed. For ClickBench: one run_started, then per query one query_completed per try, then one suite_completed. Either suite can end on a fatal instead — a fatal is always terminal.
Delivery is at-least-once ¶
The queue is a standard SQS queue, so delivery is at-least-once, not exactly-once: a message can arrive more than once, with no ordering guarantee across messages. Observed directly on the first SF100 run using this emitter (2026-08-19, run_id 20260819-112820) — one run_started delivered twice, 30 seconds apart, same run_id, same ts:
07:28:36 {"run_id":"20260819-112820","event":"run_started","total_runs":1,"ts":"2026-08-19T11:28:20Z"}
07:29:06 {"run_id":"20260819-112820","event":"run_started","total_runs":1,"ts":"2026-08-19T11:28:20Z"}
That run also emitted 78 query_completed events for 22 distinct queries across 1 run, so duplicates are not rare enough to ignore by hope.
A consumer that only prints the stream (watch-events.sh) is unaffected. Any consumer that counts events to track progress, accumulates timings, or treats an event as an edge (e.g. run_started resetting state) MUST dedupe on (RunID, Event, Query) before acting — Try additionally distinguishes ClickBench's per-query retries. If a consumer needs ordering as well as exactly-once delivery, use a FIFO queue with content-based deduplication instead of a dedupe key.
Fire-and-forget ¶
Emission never affects the benchmark. Sends happen outside every timed region, carry a short timeout (default 2s), take no retries beyond the SDK default, and log a warning on failure. After a few consecutive failures the notifier disables itself so an unreachable queue cannot add wall time to a long suite. A nil *Notifier is a valid disabled notifier: every method is a no-op, which is what an unset --notify-sqs-url yields.
Index ¶
Constants ¶
const ( EventRunStarted = "run_started" EventQueryCompleted = "query_completed" EventRunCompleted = "run_completed" EventSuiteCompleted = "suite_completed" EventFatal = "fatal" )
Event names.
Variables ¶
This section is empty.
Functions ¶
func OK ¶
OK returns a pointer to b, for Event.OK — a plain bool cannot be distinguished from "not set" under omitempty, and `"ok": false` is the field's most important value.
func RegionFromQueueURL ¶
RegionFromQueueURL extracts the region from an SQS queue URL. Both the current host form (sqs.<region>.amazonaws.com) and the legacy one (<region>.queue.amazonaws.com) are recognized; anything else returns "" and the caller falls back to the ambient AWS config.
func Rows ¶
Rows returns a pointer to n, for Event.Rows (a zero row count is a real observation, not an unset field).
func SecondsFloat ¶
SecondsFloat returns a pointer to v, for callers that already hold seconds (the ClickBench per-try triples are float64 seconds).
Types ¶
type Config ¶
type Config struct {
// QueueURL is the SQS queue URL. Empty disables notifications entirely
// (New returns nil).
QueueURL string
// Region overrides the region derived from QueueURL.
Region string
// RunID identifies the run; defaults to a UTC timestamp in the same
// format the bench result directories use (20060102-150405).
RunID string
// Timeout bounds a single send. Defaults to 2s.
Timeout time.Duration
// Logf receives warnings. Defaults to discarding them.
Logf func(string, ...any)
}
Config configures a Notifier.
type Emitter ¶
Emitter delivers one event to the transport. Implementations are synchronous; the Notifier bounds them with a timeout.
type Event ¶
type Event struct {
RunID string `json:"run_id"`
Event string `json:"event"`
Query string `json:"query,omitempty"`
Try int `json:"try,omitempty"`
WallSeconds *float64 `json:"wall_seconds,omitempty"`
Rows *int64 `json:"rows,omitempty"`
OK *bool `json:"ok,omitempty"`
RunIndex int `json:"run_index,omitempty"`
TotalRuns int `json:"total_runs,omitempty"`
TotalSeconds *float64 `json:"total_seconds,omitempty"`
ColdSeconds *float64 `json:"cold_seconds,omitempty"`
HotSeconds *float64 `json:"hot_seconds,omitempty"`
Error string `json:"error,omitempty"`
TS string `json:"ts"`
}
Event is one lifecycle message. See the package comment for which fields each event carries; every field but RunID/Event/TS is optional.
type Notifier ¶
type Notifier struct {
// contains filtered or unexported fields
}
Notifier stamps events with the run id and timestamp and hands them to an Emitter. The zero value is unusable; a nil *Notifier is a disabled one.
func New ¶
New builds an SQS-backed Notifier. An empty cfg.QueueURL — the default of --notify-sqs-url — returns nil, the disabled notifier, so callers need no enabled/disabled branch. A client that cannot be built logs a warning and also returns nil: notifications are never a gate on the benchmark.
func NewWithEmitter ¶
NewWithEmitter builds a Notifier over an arbitrary emitter. Used by tests and by New once it has an SQS client.
func (*Notifier) Fatal ¶
Fatal sends a terminal fatal event carrying the formatted message. Call it immediately before the process aborts — the send is synchronous, so no flush is needed afterwards.