Everything here lives in the PostgreSQL schema queen, created by server/sql/schema.sql. That
file plus exactly 23 files under server/sql/procedures/ (001–023) are embedded in the binary
with include_str! and applied at every boot in lexical order under session advisory lock
778120010. All of it is idempotent, so re-apply is a no-op, and QUEEN_APPLY_SCHEMA=0 skips the
step entirely.
The apply only creates. There are no teardown files, no migration blocks and no upgrade
ALTERs: a deployment starts from an empty database, and schema.sql’s CREATE TABLEs declare
the final shape directly. Pointing this build at a database written by an earlier schema is
unsupported (fresh database, full stop). See Retired objects for the history.
The engine needs CREATE SCHEMA and no extensions, with one floor worth naming: the
cgm_identity_uk index below uses NULLS NOT DISTINCT, which needs PostgreSQL 15 or newer.
The file map: 001_log_schema and 002_streams_schema are the DDL, 003_log_push through
007_log_streams are the engine’s write, read, ack, maintenance and streaming paths,
008/009 are the streams registry calls, 010_log_admin and 011_log_stats are the log-native
admin and stats surface, and 012–023 (minus 020) are the shared management plane: configure,
queue deletion, consumer groups, status, messages, traces, stats readers, metrics, Prometheus.
020_log_partition_counters is a special case: it only attaches triggers, and its position after
both 001 (the table) and 019 (the trigger functions) is the point of it.
Queue identity: queen.queues (schema.sql)
One row per queue, and the single queue identity in the system: queen.log_partitions.queue_id
references queues(id) ON DELETE CASCADE directly, so every partition→config join is by id.
There is no separate log-engine queue table and no (tenant_id, name) bridge join between
configuration and data, which makes the cross-tenant name-join hazard class structurally
unrepresentable (the retention work list joins by id).
Identity is (tenant_id, name), enforced by the unique index queues_tenant_name_uk. The columns
the engine reads directly:
| Column | Type | Notes |
|---|---|---|
id |
UUID |
PK. log_partitions.queue_id, consumer_watermarks.queue_id, consumer_groups_metadata.queue_id, queue_lag_metrics.queue_id and stats.queue_id all reference it ON DELETE CASCADE |
tenant_id |
UUID |
default 00000000-0000-0000-0000-000000000001 |
name |
VARCHAR(255) |
not null; (tenant_id, name) unique |
lease_time |
INTEGER |
default 60; /configure writes an explicit 300 when leaseTime is omitted |
dedup_window_seconds |
INTEGER NOT NULL |
default 3600; 0 disables the push dedup probe |
namespace, task |
VARCHAR(255) |
derived from the dotted name on implicit creation |
created_at |
TIMESTAMPTZ |
The rest of the row is the queue configuration /configure writes: retry limit and delay, DLQ
flags, delayed_processing, window_buffer, the retention policy columns, encryption,
max_wait_time_seconds, max_queue_size, min_pop_wait_time. See
Queue options.
Two things about this table are worth being precise about:
- The
lease_timedefault of 60 is a truth fix, not a behaviour change. An implicitly push-created queue has always leased at 60 (the pop path read that default) while the config display used to show 300 from a column the pop path never read. The display lied; now the one column both paths share tells the one truth./configurestill writes an explicit 300 whenleaseTimeis omitted, so configured queues are unchanged. - There is no
storagecolumn. One engine, nothing to select. The wire still echoesstorage: "segments"as a hard-coded literal (012_configure,010_log_admin,011_log_stats) for compatibility.
A queue row is created by the first push (003_log_push provisioning), by /configure
(012_configure), by the transaction wire (005_log_ack), by a streams sink (007_log_streams),
and by a wildcard pop or subscription registration on a queue that does not exist yet
(004_log_pop). That last one is a deliberate widening: the pop writes a durable queue-scoped
consumer_groups_metadata row, whose FK needs the queue row, and subscribe-before-first-push is
legitimate, so the FK must not turn an early subscriber into an error. The honest flip side: a
consumer typo now creates a queue row, where it previously left only an orphan watermark.
Queue deletion is one call: queen.delete_queue_v1 (013_analytics) resolves the id, deletes
the queue’s log_txns and log_dlq rows explicitly (both FK-less by design, the cascade cannot
reach them), then deletes the queues row and lets the FK cascade take
log_partitions → log_segments/log_consumers, consumer_watermarks, the queue-scoped
consumer_groups_metadata rows, queue_lag_metrics and stats. Previously this was two
non-atomic steps (the SQL delete plus a broker-side log-table teardown).
Log tables
queen.log_partitions (001_log_schema)
One row per partition, and the per-partition write serializer.
| Column | Type | Notes |
|---|---|---|
id |
UUID |
PK; every other log table keys on it |
queue_id |
UUID |
FK to queen.queues(id), cascading |
name |
TEXT |
default Default; UNIQUE (queue_id, name) |
last_offset |
BIGINT |
allocator, default -1; next base is last_offset + 1 |
log_start |
BIGINT |
segment retention watermark, default 0 |
txns_start |
BIGINT |
log_txns purge watermark, default 0 |
last_write_at |
TIMESTAMPTZ |
indexed; quantized to at most one change per second |
created_at |
TIMESTAMPTZ |
Index: idx_log_partitions_queue_write (queue_id, last_write_at), the wildcard candidate scan’s
hot filter. Storage parameters: fillfactor = 70, autovacuum_vacuum_scale_factor = 0,
autovacuum_vacuum_threshold = 500, autovacuum_vacuum_cost_delay = 0, vacuum_truncate = off.
The row is deleted only by the cleanup phase, and only once the partition is empty and both its
timestamps are older than PARTITION_CLEANUP_DAYS. Deleting it cascades to log_segments (none, by
definition) and log_consumers, so the group cursors go with it; a later push recreates the
partition under a new id, allocating from last_offset = -1 again. See
Retention internals.
queen.log_segments (001_log_schema)
The messages.
| Column | Type | Notes |
|---|---|---|
partition_id |
UUID |
FK to log_partitions, cascading; part of PK |
base_offset |
BIGINT |
part of PK |
end_offset |
BIGINT |
inclusive; msg_count = end_offset - base_offset + 1 |
created_at |
TIMESTAMPTZ |
stamped under the partition row lock |
blob |
BYTEA |
packed length-prefixed frames, zstd-compressed |
PRIMARY KEY (partition_id, base_offset) and no secondary indexes: the PK is also the pop path.
blob is SET STORAGE EXTERNAL. Autovacuum: scale factor 0.1, insert scale factor 0.3, cost limit
4000, cost delay 0, with the same values mirrored onto the TOAST table.
queen.log_txns (001_log_schema)
The hash sidecar. Written on every push, whether deduplication is enabled or not, because
ack-by-transactionId resolves through it.
| Column | Type | Notes |
|---|---|---|
partition_id |
UUID |
part of PK; no foreign key, deliberately |
base_offset |
BIGINT |
part of PK |
end_offset |
BIGINT |
inclusive, same range as the segment |
created_at |
TIMESTAMPTZ |
same timestamp as the segment |
hashes |
BYTEA |
16 × msg_count bytes: xxh3_128 big-endian, frame order |
The missing foreign key keeps FK-trigger cost off the purge path, at the price of one obligation:
whatever deletes a partition must delete these rows itself. The purge phase walks live partitions,
so a row orphaned by a partition delete would be unreachable forever. Exactly two things delete
partitions, log_partition_cleanup_step_v1 and delete_queue_v1, and both delete the sidecar
explicitly first.
queen.log_consumers (001_log_schema)
Coordination state per (partition, consumer_group).
| Column | Type | Notes |
|---|---|---|
partition_id |
UUID |
FK to log_partitions, cascading; part of PK |
consumer_group |
TEXT |
part of PK; default __QUEUE_MODE__ |
committed |
BIGINT |
the cursor (last acked offset); default -1 |
batch_end |
BIGINT |
inclusive end of the leased batch; NULL = no lease |
worker_id |
TEXT |
the lease holder, echoed to clients as leaseId |
lease_expires_at |
TIMESTAMPTZ |
|
lease_acquired_at |
TIMESTAMPTZ |
also the queue-detail lastActivity source |
batch_retry_count |
INTEGER |
the retry budget; charged only by an explicit failed ack |
attempt_offset |
BIGINT |
the batch’s first delivered offset |
attempt_count |
INTEGER |
redelivery telemetry; never spends budget |
total_consumed |
BIGINT |
lifetime counter |
created_at |
TIMESTAMPTZ |
Storage parameters: fillfactor = 50, autovacuum_vacuum_scale_factor = 0,
autovacuum_vacuum_threshold = 500, autovacuum_vacuum_cost_delay = 0, vacuum_truncate = off.
queen.log_dlq (005_log_ack)
Dead-lettered frames, as snapshots.
| Column | Type | Notes |
|---|---|---|
id |
UUID |
PK |
partition_id |
UUID |
no FK: the snapshot must outlive segment retention |
consumer_group |
TEXT |
|
"offset" |
BIGINT |
where the poison frame lived; quoted, offset is reserved |
message_id |
UUID |
|
transaction_id |
TEXT |
|
payload |
JSONB |
the snapshot itself |
error |
TEXT |
failure reason from the nack |
retry_count |
INTEGER |
retries consumed when it was dead-lettered |
failed_at |
TIMESTAMPTZ |
default now() |
Index: idx_log_dlq_partition_failed_at (partition_id, failed_at DESC).
Nothing in the retention cycle purges this table. Because these rows have no foreign key either, a
partition that still holds one is never reclaimed by the cleanup phase: dropping the partition would
leave the snapshots unreachable rather than clean them up, since every DLQ reader joins through
log_partitions. (delete_queue_v1 does clear them, explicitly, as part of deleting the queue.)
Shared tables the log engine uses
These are created by schema.sql (and 019_worker_metrics for the metrics tables) and are not
engine-specific. Since the queue-identity merge they key on queen.queues(id) wherever they used
to carry a queue name, so a deleted queue cascades its shared rows away.
| Table | Role under the log engine |
|---|---|
queen.consumer_groups_metadata |
durable subscription registration (mode + timestamp) and the group-first-contact seed marker. queue_id UUID NULL FK to queues(id), cascading, with NULL only for namespace/task discovery rows, which name no queue and keep their own tenant_id for scoping. Uniqueness is cgm_identity_uk (tenant_id, consumer_group, queue_id, partition_name, namespace, task) NULLS NOT DISTINCT (PG ≥ 15, so two discovery rows with NULL queue ids collide instead of duplicating) |
queen.consumer_watermarks |
the per-(queue, group) empty-scan watermark and its verification time. PRIMARY KEY (queue_id, consumer_group), FK cascade; tenant scoping is inherited from the queue row |
queen.stats |
output of the stats reconciler: queue, namespace, task and system rows. queue_id FK to queues(id), cascading; partition_id carries a log_partitions id with no FK |
queen.retention_history |
one audit row per retention step that deleted something, plus one __partition_deleted__ row per partition the cleanup phase reclaimed. The __%__ prefix keeps partition events out of the messages-evicted timeseries. partition_id has no FK: the audit row is written by the very step that deletes the partition |
queen.message_traces, queen.message_trace_names |
tracing; partition_id and message_id carry log-engine ids with no FK, because a trace must outlive the segment it describes |
queen.system_state |
the two maintenance flags, as {"enabled": bool} JSONB rows |
queen.worker_metrics, queen.worker_metrics_summary, queen.system_metrics |
the metrics collector’s output |
queen.queue_lag_metrics |
per-queue ops counters and lifecycle events. queue_id UUID NOT NULL FK to queues(id), cascading; UNIQUE (bucket_time, queue_id). Readers join queues for display names; a deleted queue’s metric rows cascade away |
queen_streams.queries, queen_streams.state |
streaming query registry and state |
Procedures, file by file
Every function below is in the schema queen. Where a signature ends in
p_tenant UUID DEFAULT '00000000-...0001', the parameter was added last with a default so an
unscoped caller lands on the default tenant, and the older signature is explicitly dropped on
re-apply so no ambiguous overload survives.
001_log_schema.sql (tables)
The four log_* tables above (log_dlq lives in 005_log_ack), plus one helper.
| Function | Role |
|---|---|
log_unnest_hashes(p BYTEA) RETURNS TABLE (idx INT, h BYTEA) |
explode a 16-byte-stride blob into zero-based (index, hash) rows. IMMUTABLE pure byte slicing, so PostgreSQL inlines it into the push probe and ack resolution |
003_log_push.sql (push)
| Function | Role |
|---|---|
log_push_one_v1(p_queue TEXT, p_partition TEXT, p_msg_count INT, p_hashes BYTEA, p_verified BIGINT, p_blob BYTEA, p_pid UUID DEFAULT NULL, p_window INT DEFAULT NULL, p_tenant UUID DEFAULT …) RETURNS JSONB |
the single allocator code path. Validates the hash stride, provisions the queue and partition rows on first contact, probes before allocating under the partition row lock, allocates, inserts the segment and the log_txns row. Returns {"status":"queued","baseOffset":B,"createdAt":…} or {"status":"duplicate","dups":[{"i":k,"off":O}]} |
log_push_multi_v1(p_queues TEXT[], p_partitions TEXT[], p_msg_counts INT[], p_hashes BYTEA[], p_verified INT8[], p_blobs BYTEA[], p_tenants UUID[] DEFAULT NULL) RETURNS JSONB |
bundle N disjoint partitions’ segments into one transaction. Alignment guards, provisioning skip, one set-based pre-lock ascending by log_partitions.id, then log_push_one_v1 per segment. Returns a JSON array in input order |
log_segment_at_v1(p_pid UUID, p_off BIGINT) RETURNS TABLE (r_base BIGINT, r_end BIGINT, r_created TIMESTAMPTZ, r_blob BYTEA) |
fetch the segment covering an absolute offset. One descending PK probe with the covering test applied outside the LIMIT, so a miss cannot walk the partition head. Zero rows when retention removed it |
log_push_one_v1 is also called by the transaction wire and the streams cycle, so there is exactly
one place in the system where an offset is assigned.
004_log_pop.sql (pop)
| Function | Role |
|---|---|
log_pop_v1(p_queue TEXT, p_partition TEXT, p_group TEXT, p_budget INTEGER, p_lease_seconds INTEGER, p_worker TEXT, p_auto_ack BOOLEAN DEFAULT FALSE, p_sub_mode TEXT DEFAULT 'all', p_sub_from TEXT DEFAULT '', p_skip_window_debounce BOOLEAN DEFAULT FALSE, p_tenant UUID DEFAULT …) RETURNS TABLE (r_base BIGINT, r_start_idx INTEGER, r_take INTEGER, r_msg_count INTEGER, r_created_at TIMESTAMPTZ, r_blob BYTEA) |
the claim core. Every other pop variant calls it once per partition. Visibility gates, first-contact subscription seeding, claim-first FOR UPDATE SKIP LOCKED, head probe plus forward scan, empty-partition cursor seal, then auto-ack or lease |
log_pop_specific_v1(p_queue, p_partition, p_group, p_budget, p_lease_seconds, p_worker, p_auto_ack, p_sub_mode, p_sub_from, p_tenant) RETURNS JSONB |
the single-partition pop’s wire assembly, VOLATILE plpgsql: one log_pop_v1 claim, then the partitionId resolution in a later command, whose snapshot is at least as new as anything the claim saw. The old shape (the broker’s outer statement resolving partitionId under an older snapshot than the claim’s internals) could deliver a leased batch whose every message carried partitionId "" when the pop raced the partition-creating push commit, and the client’s mandatory-partitionId ack guard then threw. Wire shape unchanged |
log_pop_wildcard_wire_v1(p_queue, p_group, p_budget, p_lease_seconds, p_worker, p_auto_ack, p_max_partitions, p_sub_mode, p_sub_from, p_tenant) RETURNS JSONB |
SQL candidate scan across a queue’s partitions; blobs base64-encoded inside the JSON. Carries the group-first-contact bulk seed, the empty-scan watermark maintenance, and the subscribe-before-first-push queue provisioning described above |
log_pop_wildcard_bin_v1(… same args …) RETURNS TABLE(meta JSONB, blobs BYTEA[]) |
identical claim logic with blobs as a native bytea[] out of band: no encode() on the server, no base64 decode on the broker. The default wildcard path |
log_pop_discover_wire_v1(p_namespace TEXT, p_task TEXT, p_group, p_budget, p_lease_seconds, p_worker, p_auto_ack, p_max_partitions, p_sub_mode, p_sub_from, p_tenant) RETURNS JSONB |
namespace/task discovery pop across matching queues, each partition leased with its own queue’s lease_time |
log_has_pending_v1(p_queue TEXT, p_group TEXT, p_tenant UUID DEFAULT …) RETURNS BOOLEAN |
cheap indexed superset probe: does any partition have last_offset > COALESCE(committed, -1). Coarse on retention gaps by contract; the pop resolves the truth under the row lock |
log_pop_list_v1(p_queue, p_group, p_partitions TEXT[], p_budget, p_lease_seconds, p_worker, p_auto_ack, p_max_partitions, p_sub_mode, p_sub_from, p_skip_window BOOLEAN DEFAULT FALSE, p_tenant) RETURNS TABLE(meta JSONB, blobs BYTEA[], states JSONB) |
the hot-list serve path: claim from a caller-supplied candidate list and return a tri-state verdict per evaluated candidate: took (with lastOff), empty, or leased (with until) |
log_hotlist_reseed_v1(p_queue TEXT, p_group TEXT, p_after_id UUID, p_limit INTEGER, p_tenant UUID DEFAULT …) RETURNS TABLE(r_id UUID, r_name TEXT) |
keyset-paginated enumeration of probably-pending partitions in id order, for ring reseeding. Includes lease-held partitions; pass the nil UUID to start a walk |
log_pop_v1 is DROP-and-CREATE rather than CREATE OR REPLACE, because its OUT row type is
part of the signature and OR REPLACE refuses return-type changes, which would brick the boot
re-apply.
005_log_ack.sql (ack, lease renewal, DLQ, transactions)
Also creates queen.log_dlq (above).
| Function | Role |
|---|---|
log_ack_v1(p_queue TEXT, p_partition TEXT, p_group TEXT, p_worker TEXT, p_upto BIGINT, p_ok BOOLEAN DEFAULT TRUE, p_acked_count INTEGER DEFAULT 0, p_tenant UUID DEFAULT …) RETURNS JSONB |
positional ack addressed by names. Locks the consumer row, validates the lease only when a worker is supplied, clamps to batch_end, advances or releases |
log_ack_at_v1(p_partition_id UUID, p_group TEXT, p_worker TEXT, p_upto BIGINT, p_ok BOOLEAN DEFAULT TRUE, p_acked_count INTEGER DEFAULT 0) RETURNS JSONB |
the same decision procedure addressed by partition id, the ack registry’s fast path |
log_ack_multi_v1(p_pids TEXT[], p_groups TEXT[], p_ends BIGINT[], p_workers TEXT[], p_counts INT[]) RETURNS JSONB |
N full-batch cursor advances in one transaction, for ack fusion. Rows execute ORDER BY (partition_id, group); verdicts are emitted by input ordinal |
log_ack_by_hash_v1(p_partition_id UUID, p_group TEXT, p_worker TEXT, p_hashes BYTEA[], p_statuses TEXT[]) RETURNS JSONB |
the authoritative ack contract. One join resolves hashes to offsets; implements implicit ack, explicit-signal clamping, the retry budget, DLQ hand-off, and the noopHashes / staleHashes / unresolvedHashes honesty lists |
log_renew_lease_v1(p_worker TEXT, p_seconds INTEGER) RETURNS JSONB |
renew every live lease held by a worker. GREATEST so it never shortens; reports the minimum expiry |
log_dlq_head_v1(p_partition_id UUID, p_group TEXT, p_worker TEXT, p_off BIGINT, p_message_id UUID, p_txn TEXT, p_payload JSONB, p_error TEXT) RETURNS JSONB |
file the poison frame’s snapshot, advance the cursor past it, reset attempt and retry state, release the lease. Idempotent on a second call (the lease is gone) |
log_transaction_wire_v1(p JSONB) RETURNS JSONB |
atomic push plus ack. Guards against cross-tenant partition ids, pre-locks partitions ascending by id, then acks ascending by (partition_id, group). Every failure raises, so the whole batch rolls back |
get_dlq_messages_v1 used to be defined here too; it is not any more. 010_log_admin applies
after this file and redefines the same signature, so the copy here was overwritten at every boot
and never ran. 010_log_admin owns it now.
006_log_maintenance.sql (retention steps)
| Function | Role |
|---|---|
log_retention_boundary_v1(p_partition_id UUID, p_from BIGINT, p_cutoff TIMESTAMPTZ) RETURNS BIGINT |
smallest base_offset >= p_from of a segment fresh enough to keep. A single forward PK walk, valid because created_at is monotone. MAX(end_offset) + 1 when everything above the watermark is stale |
log_retention_step_v1(p_pid UUID, p_all_cutoff TIMESTAMPTZ, p_completed_cutoff TIMESTAMPTZ, p_max_rows INT, p_history_type TEXT DEFAULT NULL) RETURNS JSONB |
one bounded retention transaction for one partition. Rule 1 by time, rule 2 capped at MIN(committed) + 1, whole segments only, one ranged DELETE, watermark advance, one retention_history row. Returns {"deleted":N,"new_log_start":X,"done":bool} |
log_txns_purge_step_v1(p_pid UUID, p_cutoff TIMESTAMPTZ, p_max_rows INT) RETURNS JSONB |
the same step pattern over log_txns and txns_start |
log_evict_max_wait_step_v1(p_pid UUID, p_cutoff TIMESTAMPTZ, p_max_rows INT) RETURNS JSONB |
maxWaitTimeSeconds eviction, delegating to log_retention_step_v1 with rule 2 disabled and the history type max_wait_time_eviction |
log_partition_dead_v1(p_pid UUID, p_cutoff TIMESTAMPTZ) RETURNS BOOLEAN |
the empty-partition eligibility predicate, in one place so the candidate scan and the under-lock re-check cannot drift: no segments, no log_dlq row, no queen_streams.state row, no live lease, no consumer activity since the cutoff, and both partition timestamps older than it |
log_partition_cleanup_step_v1(p_cutoff TIMESTAMPTZ, p_max_rows INT) RETURNS JSONB |
deletes up to p_max_rows eligible partitions in one transaction: locks the batch ascending by id with SKIP LOCKED, re-checks the predicate under the locks, purges the FK-less log_txns rows explicitly, writes one __partition_deleted__ history row each, then deletes the rows (cascading to log_consumers). The only step here that is not per-partition |
This file also adds idx_retention_history_executed_at (executed_at DESC), the index the retention
analytics readers scan.
007_log_streams.sql (streaming)
| Function | Role |
|---|---|
log_streams_cycle_v1(p_requests JSONB) RETURNS JSONB |
one atomic streaming-cycle commit per request element: state upserts, sink pushes through log_push_one_v1, and the source ack as a cursor advance over the leased span. A gate partial-ack walks K frames forward and retains the lease on the tail. Sink segments arrive broker-prepacked as hashesB64 plus blobB64 |
010_log_admin.sql (admin and observability)
Ported from the retired seg-era consumer-group, observability and traces files. The rows-engine
legs those redefinitions once carried are gone. There is one engine, so every body here runs
unconditionally, and the join to queen.queues on p.queue_id is itself the queue-existence
guard (it supplies namespace, task, priority and tenant scoping in the same touch).
| Function | Role |
|---|---|
log_delete_consumer_group_v1(p_group TEXT, p_delete_metadata BOOLEAN DEFAULT TRUE, p_tenant UUID DEFAULT …) RETURNS JSONB |
remove every log_consumers cursor for a group across all queues of one tenant, plus its empty-scan watermarks |
log_seek_one_v1(p_partition_id UUID, p_last_offset BIGINT, p_log_start BIGINT, p_group TEXT, p_to_end BOOLEAN, p_timestamp TIMESTAMPTZ) RETURNS VOID |
seek one cursor: to last_offset for end, else just before the first segment at or after the timestamp (segment-granular). Releases any lease and resets retry and attempt state; creates the row if absent |
log_seek_consumer_group_v1(p_group TEXT, p_queue TEXT, p_to_end BOOLEAN DEFAULT FALSE, p_timestamp TIMESTAMPTZ DEFAULT NULL, p_tenant UUID DEFAULT …) RETURNS JSONB |
seek every partition of a queue |
log_seek_partition_v1(p_group TEXT, p_queue TEXT, p_partition TEXT, p_to_end BOOLEAN DEFAULT FALSE, p_timestamp TIMESTAMPTZ DEFAULT NULL, p_tenant UUID DEFAULT …) RETURNS JSONB |
seek one named partition |
get_consumer_groups_v4(p_tenant UUID DEFAULT …) RETURNS JSONB |
consumer-group listing |
list_messages_v1(p_filters JSONB DEFAULT '{}') RETURNS JSONB |
message browsing; a segment seq carries base_offset and frameIdx carries offset - base_offset |
get_dlq_messages_v1(p_filters JSONB DEFAULT '{}') RETURNS JSONB |
DLQ browsing over log_dlq; payload comes from the snapshot, createdAt is failed_at (per-frame enqueue time is not recoverable from an opaque blob), producerSub is null |
record_trace_v1(p_data JSONB) RETURNS JSONB |
trace recording (this file owns the redefinition; 017_traces no longer defines it) |
get_lagging_partitions_v1(p_min_lag_seconds INTEGER DEFAULT 0, p_tenant UUID DEFAULT …) RETURNS JSONB |
lagging-partition report |
get_consumer_group_details_v1(p_consumer_group TEXT, p_tenant UUID DEFAULT …) RETURNS JSONB |
per-group detail |
Three procedures from the retired observability file were deliberately not ported, because they
have no Rust call site: get_queue_messages_v1, has_pending_messages and
get_prometheus_metrics_v1.
011_log_stats.sql (stats and analytics)
| Function | Role |
|---|---|
log_oldest_pending_at_v1(p_pid UUID, p_wanted BIGINT) RETURNS TIMESTAMPTZ |
created_at of the segment holding an offset, or of the next existing segment when it fell into a retention gap. Two bounded PK probes, never a filtered backward scan |
log_refresh_all_stats_v1() RETURNS JSONB |
recompute queen.stats queue rows from the log tables by watermark arithmetic, then reuse the existing namespace, task and system rollups and correct the system partition count. Returns a summary labelled engine: "segments", a literal wire-compat telemetry label, since no table carries a storage column |
get_queue_detail_v2(p_queue_name TEXT, p_tenant UUID DEFAULT …) RETURNS JSONB |
log-native queue detail (this file owns it; 018_stats no longer defines it). cursor.batchesConsumed is 0 and lastActivity is MAX(lease_acquired_at) |
get_queue_v2(p_queue_name TEXT, p_tenant UUID DEFAULT …) RETURNS JSONB |
log-native queue summary, same watermark arithmetic. failed is NULL, not 0: there is no per-partition failure counter |
get_analytics_v1(p_filters JSONB DEFAULT '{}') RETURNS JSONB |
the message-volume series, bucketing log_segments by created_at and attributing each segment’s frame count to its creation bucket. Deliberately O(segments in range), since it is a per-request dashboard read with a time filter, not the refresh loop |
log_queue_message_stats_v1(p_queue TEXT, p_tenant UUID DEFAULT …) RETURNS TABLE (segments BIGINT, messages BIGINT) |
per-queue counters for queue detail: messages by watermark arithmetic, segments by an index-only count |
log_queue_stats_all_v1(p_tenant UUID DEFAULT …) RETURNS TABLE (queue_name TEXT, partitions BIGINT, segments BIGINT, messages BIGINT) |
the same accounting broker-wide, for queue listing (the live view, since queen.stats can lag one refresh cadence). Segment counts are pre-aggregated per partition before the queue join, or the watermark row would fan out once per segment and over-count |
020_log_partition_counters.sql (triggers)
Defines no functions; attaches trg_partitions_created_counter and
trg_partitions_deleted_counter to queen.log_partitions. It is a separate file for two reasons.
It must run after both 001_log_schema (the table) and 019_worker_metrics (the trigger
functions), so a fresh database gets the attachment on its very first boot. And the attachment is
conditional on the trigger being absent: DROP TRIGGER takes ACCESS EXCLUSIVE and
CREATE TRIGGER takes SHARE ROW EXCLUSIVE on the engine’s hottest table, so re-running the pair
every boot would stall every push and pop behind each rolling restart. The trade-off is that
changing a trigger definition needs a manual drop; changing the function body still takes effect
on restart, since CREATE OR REPLACE FUNCTION rebinds it without touching the table.
Advisory locks
| Id | Scope | Held by |
|---|---|---|
778120010 |
session | boot-time schema apply, serialising DDL across replicas |
737001 |
session | the retention cycle leader |
737002 |
transaction | the stats reconciler leader |
| `hashtextextended(partition_id | ‘/’ | |
| `hashtextextended(query_id | ‘:’ |
The one global lock order
Every multi-row locker follows it, which is what makes the write paths deadlock-free by construction:
- provisioning inserts, each
ORDER BYits own unique key; queen.log_partitionsrows, ascending byid;queen.log_consumersrows, ascending by(partition_id, consumer_group).
Consumer-row locks are only ever taken after all partition-row locks, so the two lock spaces cannot form a cross-space cycle. The ack path takes only consumer locks and never a partition lock; the per-partition retention steps take exactly one partition lock each, so they cannot deadlock against a multi-partition locker regardless of iteration order.
log_partition_cleanup_step_v1 is the one maintenance step that holds many partition locks at
once, so it is bound by rule 2 like any other multi-partition writer: it takes them ascending by id
in a single statement, with SKIP LOCKED so it steps over a partition a pusher is holding instead of
queueing in front of the write path.