Skip to content

fix: close the final audit findings and add the database mirror - #449

Merged
farhan-syah merged 73 commits into
mainfrom
fix/final-audit
Nov 7, 2026
Merged

farhan-syah merged 73 commits into
mainfrom
fix/final-audit

Conversation

@farhan-syah

Copy link
Copy Markdown
Member

Closes the findings of the final audit of v0.6.2..main, and adds the working database mirror.
This PR also holds the six fixes of #426, which did not reach main.

What changes for a user

Database mirrors follow a source cluster. A mirror keeps a read-only, ordered copy of one database from another cluster.

  • MIRROR DATABASE <name> FROM <cluster>.<db> [MODE = sync|async] starts a mirror.
  • ALTER DATABASE <name> PROMOTE makes it writable, at one final cut.
  • ALTER DATABASE <name> MIRROR RESUME and MIRROR REBOOTSTRAP recover a failed mirror.
  • MODE = sync holds a write until the mirror has it. A timeout answers MIRROR_SYNC_TIMEOUT (55M01), and the write stays committed.
  • Clusters connect over a separate mTLS listener, limited by the [mirror.clients] allowlist.

See the mirror section of the databases guide for config, statuses, refusals, and limits.

These bugs are fixed:

Area What went wrong What happens with this PR
Named databases MATCH ... IN returned no rows It reads its collection
Named databases A committed array transaction stopped a Data Plane core until restart It commits, and bookkeeping errors no longer stop a core
Named databases REFRESH CONTINUOUS AGGREGATE hung, and retention dropped rows before folding them Both work
Named databases crdt_apply was refused, and CRDT UNIQUE and NOT NULL never fired Deltas apply, and constraints hold
Named databases Array writes published no change event, and RESTART IDENTITY did nothing Both work
Replication PURGE TENANT and CONVERT changed one node only Both go through the Raft log to every replica
Replication Each node ran retention on its own clock One decision goes through the log
Sequences Two nodes can issue one value, and a restart rewound the counter Values come from durable reserved ranges
SQL parser Input such as plain INSERT panicked the preprocessor Each input answers a result or a typed error
Sessions A revoke left live sessions open on some protocols A revoke ends the session on every protocol
Audit An audited action can commit with no durable record A durable record comes first, and a failed record refuses the action
Mirrors Some protocols let a write reach a mirror Every protocol refuses them with 25006
Raft A disk error can report a write as durable The group's writer fail-stops
Boot An unreadable catalog booted with empty state Boot refuses, and names the file and the next action
Bounds COPY, array batches, ST_Buffer, and decoders took unbounded or hostile sizes Limits and typed errors apply

The audit also covered lifecycle, planner, and low-severity items. The commit log lists each one.

Breaking changes

  • Data directories: an existing data directory can fail to open, because _system.calvin_applied changes its key type.
  • CRDT in named databases: stored documents keep their old root and do not read correctly. A delta must name the bare collection.
  • GAP_FREE: CREATE SEQUENCE ... GAP_FREE answers 0A000. No deployment ever held that promise.
  • Retention timing: a partition with an interval expires once its whole interval has ended. Rows can stay up to one interval longer.
  • PROMOTE: a database that never was a mirror answers 0A000.
  • KILL USER SESSIONS: it ends stored users. Add OIDC to end an OIDC principal.
  • Database ids: each id carries a per-cluster prefix, so ids are larger. They stay below 2^53.
  • Sequences: a restart or a lost race can leave a gap of one range.

Where to focus review

  • Mirror applier and source: entry stamping, the two-phase cut, and PROMOTE decide data correctness.
  • Listener trust boundary: check the allowlist, the source filter, and the frame bounds.
  • Strict audit paths: each action records before it commits. TLS reload and access removal stay best-effort, so an audit outage cannot block a security response.
  • Log forms: TenantPurge, CollectionConvert, and RetentionDrop must apply the same way on every replica.
  • CI fuzz job: it drops -runs=0, which stopped each target before any mutation.

Known limits

  • A long scan holds its core until its deadline, because no resumable scan task exists.
  • Audit retention reaches arrays only. Other bitemporal=true collections keep their history, and no DDL sets a limit.
  • RESTORE does not carry aggregate state. A refreshed manual aggregate restores empty until its next REFRESH.
  • Sync mode does not cover ILP, CRDT sync sessions, or event-plane writes. The guide states why.
  • nodedb-lite needs a matching update for the new MetaOp fields and sync frame.

A dependent predicate write, a TRUNCATE and a CRDT document delete
settle the balance deltas of a cross-shard materialized sum from reads
of the source rows. Carry those reads into the Calvin transaction as
versioned reads so a concurrent change to a source row aborts and
retries the settlement instead of committing a stale delta.

A TRUNCATE now joins the other tasks of its request in one transaction,
and a zero-net settlement that ships no task still builds a
single-vShard class that holds its reads. A gateway forward applies
tasks one by one and checks no read, so a request that holds settlement
reads commits through the deterministic scheduler.

Add recon::before_submit and stmt::before_route fail gates and wire and
cluster tests that change a source row between the settlement read and
the submit.
Extract the dependent-recon gate into its own module and apply it
consistently across pgwire, HTTP and native paths, including
multi-statement requests, mixed-verb requests and implicit TRUNCATE
transactions. Materialized-sum settlements read their source block in
the same request, and balances over several columns settle together.

Document that best_effort_non_atomic commits a predicate write and the
shipped materialized-sum balance as separate groups.

Add wire tests covering the new request shapes.
A multi-slice Calvin write answered with the first participant's
affected count, so a coordinator hosting one slice reported only that
slice. Each participant's completion ack now carries its slice's plain
answer, and the coordinator sums them. A plain count also joins a
timeseries slice's install counts.

- Add affected_payload to add two write answers in a fixed key order,
  so every node derives the same result.
- Fail the statement when a participant's report does not decode,
  rather than answer a smaller count.
- Fold the summed total once per batch in the response shape, with
  later count-bearing tasks folding their verb alone.
- Refuse a request with several RETURNING tasks before it commits,
  since one transaction answers one set of rows.
- Add wire and cluster tests for per-request counts across hosts.
A mirror takes its rows and schema from its source cluster. Until now
only some entry points refused other writers.

Add a mirror fence, the set of databases that are not promoted mirrors,
kept in step with the catalog by descriptor post-apply and rebuilt on
boot and metadata image install. Every write gate consults it: the write
funnel, the leader proposal gate, the Calvin submit, the authorized
dispatch and CONVERT paths, and the metadata proposer for object DDL.
Writes from the mirror applier pass. Grants, quotas, DROP/ALTER/PROMOTE
DATABASE and consumer offsets stay allowed.

Reads follow the session read consistency. A strong read on a mirror
returns STALE_READ_NOT_LEADER on every protocol. pgwire and native
sessions select eventual or bounded_staleness through
default_read_consistency. HTTP and WebSocket requests carry a
read_consistency field. Delta sync, topic publish, ILP, RESP pub/sub and
CRDT routes report the refusal with its own error class.
A hard revoke used to end its sessions only when the audit bus
consumer handled the event, so a lagging consumer or a dropped
broadcast event left revoked sessions open. The credential store now
kills the user's sessions itself, before it publishes, for create,
update, drop, replicated installs and catalog reloads. A catalog
reload also bumps versions and publishes events for users it drops or
changes.

Tenant moves kill sessions by tenant id through the registry and
record an audit row, instead of publishing the tenant id as a user id
on the user-keyed bus.

The bus consumer now only writes audit rows. It batches ready events
into one WAL sync on a blocking thread, records how many rows a lag
lost, and files a diagnostics report for a lag or an unfinished batch
task. The duplicate top-level session_registry module is removed.
Sessions now hold a binding to the session registry for their whole
life. The binding carries the kill signal each protocol loop races
against its next read, and it rebuilds a session-lifetime identity at
request boundaries when the user, its grants or its API key change.

Kill paths cover every protocol (pgwire, native, HTTP streams, RESP,
ILP, sync):
- API key revoke ends the sessions the key opened, on every node.
- MOVE TENANT cutover ends the tenant's sessions in post-apply, and a
  metadata image that carries a cutover does the same on reload.
- BLACKLIST IP accepts WITH KILL SESSIONS, and user kills match the
  credential-store principal so an OIDC session with an equal numeric
  id stays open.
- Database grant changes bump the user's identity version.

MOVE TENANT drain now returns holds that end the drain when the
statement future is dropped, so a killed connection strands no drain.

The session cap applies per node; the config doc says so.
…al floor

Group disk writer:
- Retry I/O errors with a doubling wait, reopening the log on each
  attempt, so a full disk resumes once freed.
- Fail-stop on an error no retry clears: the replica leads nothing and
  answers no Raft request, and the failure is recorded and reported on
  the health and metrics surfaces. The node keeps serving other groups.
- Publish durable progress only for commits that succeeded.

Ordinal floor and write gate:
- Ask each voter independently during a floor round so a slow or
  returning voter does not hold the others back.
- Record the leadership an admission ran for and recheck it under the
  lock that appends the entry. On a mismatch the entry passes the gate
  again, so no leader appends an entry its gate did not admit at its
  current term.

Calvin completion verdicts that conflict with the stored one keep the
first verdict, reach a diagnostics capture, and notify no scheduler.
Add begin_sync/finish_sync so a caller can run the fsync with no lock
on the WAL. An append during the fsync is not claimed as durable and
syncs next. A segment roll between the steps stays safe, and a failed
fsync latches the active writer even when a roll replaced the file.

Add SegmentScanner, a record reader for a WAL directory that holds no
borrow of the WAL, so appends run during a scan. The directory replay
functions move into their own module and re-export from segmented.
Record a statement's intent in the audit log before its mutation
commits, across DDL, sync, HTTP and native entry points, so a record
that fails refuses the action.

Rework the audit pipeline around the WAL:
- Run the audit WAL fsync with no WAL mutex held, so concurrent
  recorders share a sync and an append never waits for one.
- Bound the in-memory buffer. At the hard bound a recorder with an
  error path refuses, and a best-effort entry stays in the audit WAL
  until a flush commits it.
- Release recorder waiters only after a flush commits to the catalog.
  A failing catalog throttles recorders instead of dropping entries.
- Run the flush executor on its own blocking thread, and hand off the
  Tokio worker before an async recorder waits.
- Verify the hash chain one entry at a time without holding the log.
- Report a batch as durable, staged, or unrecorded to its sender.

Bound the audit rows of repeated bearer refusals per peer, and emit
periodic summary rows for the throttled count. Report OTLP auth errors
through a dedicated type. Stop the JWKS refresh loop at drain, and
reject a credential key that reuses the WAL or backup key. Expose
audit counters in the system metrics.
Add two invariant reports. One fires when the quarantine registry drops a
record at its bound. The other fires when a release of idle descriptor
leases is not proposed.
Charge corrupt time-series partition reads to a quarantine record on every
scan path, and read partitions on the owning core through its io_uring
instead of a scan thread. Release the records of a collection, tenant or
partition when its segments are removed, replaced or merged, so a reused
segment id starts clean. Bound the registry and report evictions. Read
partition cells with a typed error rather than a panic.
An idle group resends its snapshot at the same index, and a replica that
awaits a snapshot neither replicates nor campaigns. Refusing a chunk that
decodes held the replica behind until a write or a restart. A chunk that
decodes now clears its record, while bad bytes stay refused with a stable
error that names the quarantine. Installing a snapshot releases the
records at or below its index.
A drain has no expiry, so a lost drain end leaves its descriptor drained
until a restart. A full queue now hands the end to a backlog that needs no
queue slot. An end whose attempts fail files a report and retries until it
applies, and shutdown gives each waiting end one bounded attempt. Dropped
releases of other kinds file a report too.
A catalog file that exists and does not open, or whose rows do not load,
used to seed every core empty and look like a first boot. Each seed loader
now returns a typed error that names the file, the cause and the next
action, and only an absent catalog file seeds empty. A vector index rebuild
that cannot read the store or insert a row fails boot instead of
publishing an incomplete index.
…register

A register refused after it applied its policy or installed its config now
puts back what it replaced. A failed peer-id change keeps the previous
incarnation.
…olds it

Requeue delivers at least once. The entry stays in the DLQ, claimed and safe
from eviction, until the action consumer durably holds it, and an inbox
that refuses the action gives the claim back. A requeue by another tenant
reports the entry as absent, and requeue writes a durable audit record
first.
Extend the durable ActionIntent record to statements whose first effect
precedes their catalog entry (row copy, tenant move, backup, engine index
change, purge), the JWT key exchange, the session tenant override, a
scheduled backup, a restore of a hard-deleted collection, and DEFINE EVENT
actions. A failed record refuses the action, and a refused trigger action
goes to its retry queue. Actions that remove access stay best-effort so an
audit outage cannot block a security response.
Wall-clock reads in nodedb-types and nodedb-array go through web-time,
which uses the JS clock on wasm32-unknown-unknown where std::time
panics. Add checked_wall_now_ms, which reports a clock before the epoch
as None so callers that stamp stored data can refuse instead of storing
epoch 0. The fuzz workflow runs the 32-bit build check over the array,
graph, FTS, vector and types crates and triggers on the array, graph,
FTS, vector and memory crates.
Array column decoders read lengths through shared bounds-checked frame
helpers, so an oversized or truncated length is a corruption error and
never an out-of-range slice or allocation. The error-details string
array decoder reads through the checked Vec reader instead of
pre-allocating from the claimed length. MBR building no longer relies
on expect and treats an all-null or empty column as AllNull.
ST_BUFFER and GEO_CIRCLE refuse a segment count above the cap with
SpatialError::BufferSegments instead of attempting the allocation.
Export the list of window function names from nodedb-query. The planner
refuses a registered aggregate or timeseries function that has no window
evaluator, and the evaluator returns UnknownFunction instead of
panicking.
A row-format COPY reads the file under a memory governor reservation
and holds one parsed row at a time, reserving that row too. The first
pass checks every row so an unparsable row writes nothing, and the
second pass inserts. Raster loads reserve each block from the reader's
block_bytes, and an array COPY stream reserves its incomplete trailing
record. A refused reservation is SQLSTATE 53200 with the next action.
ArrayPutBatch gets the tenant quota check and max_batch_size cap that
edge batches already apply, counting the longest column so a short
first column cannot hide a long one. The shared request decoding and
size refusal move to one admission module used by both opcodes.
Column redaction does not cover array attributes, so an array read under
a policy is refused whole, and CREATE ARRAY refuses a name a policy
already holds. CREATE ARRAY stamps its creation time from the checked
clock and errors when the clock reads before the epoch.
AS OF NOW(), DEFAULT NOW() and SHOW CHANGES read the checked clock and
return a typed error with the next action instead of using epoch 0.
A lone quote after AS OF SYSTEM TIME is a parse error, not a panic.
LATERAL top-k and loop joins carry the statement's AS OF to their inner
scans and refuse an inner side the by-name scan cannot read: a CTE or
derived table, a non-document engine, a valid-time scope, or AS OF
SYSTEM TIME NULL. CTE inlining recurses into LATERAL plans, and a
search leaf over a CTE or derived table is refused instead of reading a
collection of the same name. A statement that names AS OF SYSTEM TIME
is refused when no live node can pin its cut. SELECT-list aliases bind
in HAVING at any expression depth. Array function dispatch uses typed
kinds, and a LATERAL item with no subquery is a typed error.
The tenant QPS series was rendered with a hard-coded "default" database
label. Count admitted requests per database and tenant in the database
metrics registry, render them as nodedb_tenant_qps_total with the real
database name, and remove a dropped tenant's series from every database.
Document the per-database series and the tenant-wide series, which carry
only the tenant id.
Add a generation counter to the system catalog that advances on every
committed write of a collection or array row. The database metrics
sampler publishes collection and tenant counts only when that generation
or the set of databases changed since the last scan, and keeps nothing
cached after a catalog error so the next tick scans again.
Use the scene array's real dimensions and attributes in the ARRAY_AGG
and GeoTIFF COPY examples, and drop the TIME option. Show concrete bearer
login audit entries, explain the method values, and note that the
provider, sub and reason values are quoted and escaped.
The Apache-2.0 license covers the crate's source, not the EPSG data. Name
the registry versions and generators behind the shipped data files.
Check that the ring buffer push hook runs once per successful push and
never after the consumer drops. Check that a capped graph RAG fusion on
the single-node reference emits the MAX_VISITED truncation notice and an
uncapped one emits none.
Replace the fixed sleep with a wait on each core logging its checkpoint
completion twice in the restarted boot, so the file check runs only
after a cycle in which a stale index would have rewritten its files.
Describe what the Calvin determinism tests cover, and point two module
docs at the files that exist.
… log

Track how far a mirror followed its source as a single MirrorCutState
(ready cut, applied cut, cut HLC, system-time cap and per-group resume
positions) instead of per-node lag and collection-map rows. A new
PutMirrorCutState catalog entry carries it, so every node holds the same
row. Apply only raises the row, and writes nothing for a database that
follows no source, so a replay or a late entry is a no-op. PROMOTE and
DROP remove the row in their apply.

Replace the lag record with typed MirrorFailure reasons on the stopped
status, and add the source database name to MirrorOrigin. The observer,
restart and fence paths, the promote and read handlers, and SHOW
DATABASE MIRROR STATUS read the new state. The status output now reports
failure, byte progress, lag, ready cut and applied cut. Tests and docs
follow the new shape.
Compaction ceilings are now named per owner. The log never compacts
past the lowest ceiling, so one owner releasing entries no longer
frees entries another owner still needs. Ceilings set before a group
mounts bind it when it mounts.

Each log tracks the entry bytes it holds in memory. The count shows in
group status and in the cluster raft group listing.
A database id is now (space << 30) | counter, where the space derives
from the cluster id. Two clusters issue disjoint ids, and every id
stays below 2^53 so JSON clients read it exactly.

The id space travels in the DatabaseIdReserve metadata entry, so every
node computes the same id from the log alone. start_raft installs the
space from the saved cluster id before any listener opens. The
registry keeps the high-water mark rising when it already passed the
entry's space.

The native placement test now creates databases until one has a
collection name that splits placement, since the id no longer fixes
that.
…them

A mirror's source cut now travels as a MirrorCut metadata entry. The
data entries of a cut commit in every mirror group and wait in their
lanes. The cut entry installs the cut's schema and raises the ready
cut at one log index, which releases them.

An entry from a former applier's epoch, a promoted mirror or a dropped
database is a no-op on every node. A cut that equals the ready cut
installs nothing and only raises the epoch fence and the applied
fields, so a new applier releases a partial cut a former one left in
the lanes.

Redo records and chunks, the Calvin applied ledger, snapshot capture
and install, the write gates and SHOW output carry the cut state to
match.
…ompaction

A source now answers mirror links. A listener accepts a mirror's
connection, checks the client against an allowlist, and tails the
committed entries of the source's Raft groups over a bounded read. The
mirror side gains a client for the same wire.

Each link holds a replicated pin that records the last acked index per
group. The pin bounds log compaction, and it expires by age or bytes,
or ends when the source database drops. Pin rows replicate through the
metadata log so every node holds the same ceiling.

A source also takes mirror cuts. A cut carries a mark (number, newest
system-time ordinal, covered metadata index) in its Calvin marker and
in every group's barrier, plus the fully applied Calvin epoch of each
vShard, so a mirror installs no redo that its ledger already holds.

Tuning and server config gain the mirror settings, and the old
source handler gives way to the new source service.
A mirror cluster now replays a source's tail into its own Raft groups.
The applier fences its leadership, cuts each source group's tail into
batches, rewrites entries for the mirror's id space, proposes them with
a commit-wait propose, and applies each source cut through the metadata
log as a MirrorCut entry that carries the frozen catalog entries.

Supporting changes:
- Add a log probe RPC so a node can compare entries it proposed with
  what a replica committed, backed by MultiRaft::log_probe_view.
- Expose the link id on CrossClusterLink and install the Raft
  operations the appliers use on MirrorState during Raft start.
- Conclude a mirror cut's barrier as durable so an idle group's floor
  crosses it and compaction runs.
- Run compaction on every hosted group from the pin pass, so a log
  released by an expired pin is freed without a following write.
- Cover the applier with a two-cluster test, extend the source tests
  for idle-group compaction, and raise that test's slow timeout.
Outside the default database the edge store and the CSR hold a
collection under its database-qualified name, while the IN clause names
it bare. The collection filter now resolves the stored name, and a
scoped query with no stored name matches nothing.
An array outside the default database was named bare in its write
note, its row-change resolution and its change events, while every
other collection name there carries the database qualifier. A
transaction writing such an array fail-stopped the core after the redo
record installed, and its change events did not resolve on publish.

Array writes now name the array qualified in the written-collection
list, the CDC metadata and the transaction row changes. A settled redo
record that still names a collection outside its database no longer
halts the core: the note drops the newest-ordinal cache, applies the
LSN and reports the misnamed record.
…ata group

PURGE TENANT and CONVERT COLLECTION ran as one Data Plane op on the
node that received the statement. They now propose a TenantPurge entry
to each data group's log and a CollectionConvert entry to the
collection's group, so every replica applies them at one log position.
Both are exclusive in the apply loop, with a WAL barrier around the
core work, and neither adds storage, so a purge passes at any tenant
size.

A conversion re-encodes rows from inputs shared by every replica. The
catalog row records the conversion (generation, incarnation, source
mode), and each core keeps a per-collection conversion record in the
sparse store. A core resolves the storage mode its rows actually hold
at register and boot, so a replica whose group has not yet applied the
conversion keeps reading in the source mode. Tenant snapshots, snapshot
install and restore carry or drop the records.

The tenant purge also clears graph edges, node labels and chain heads
through one group-scoped path.
A new mirror database now fills itself from its source before it
follows the source's log. The source captures the database at one cut,
including a catalog image and each tenant's conversion records, and
streams it in chunks. The mirror installs the catalog image, re-issues
the rows under their source keys, surrogates and stamps, and takes its
first cut. A mirror status of Bootstrapping admits only the applier's
unstamped follower writes, and every other source stays refused.

MIRROR DATABASE now contacts the source first. The source's hello names
the id the mirror keeps, and the statement refuses a source the config
does not name, this cluster as its own source, the source's default
database, an unmaterialized clone, a second mirror of one source
database, and an id already in use. A per-database applier supervisor
on each node starts following once the descriptor exists.

A capture that cannot install, or stops part way, fails the bootstrap
with a typed error.
A mirror's status now changes through a StepMirrorStatus catalog entry,
so every node reaches one answer when the lag monitor, a seal, and a
promote race. Each step names the status it read and changes nothing
when the row no longer permits it. Only the metadata leader proposes lag
steps.

A read-only mirror now limits its own history and background work:
- version cleanup keeps each mirror's stores at or below its applied cut
- KV expiry, continuous aggregate retention, and constraint
  reconciliation skip mirrored databases, whose changes arrive through
  the log
- reads on a mirror are capped at its applied cut

A tenant purge also clears collections a core's stores hold with no
catalog row, and removes array cells while keeping the array definition.
A mirror now receives every change its source makes on its own clock,
and an operator can recover a mirror whose link stopped.

Retention: the metadata leader reads its clock once and proposes each
retention decision (timeseries partition drops, bitemporal and audit
purges) into every data group's log. Replicas apply the decision at one
log position and a mirror's tail carries it, so a mirror reads no clock.
Local retention loops skip mirrored databases.

Sequences: a node reserves a run of values through a catalog entry that
names the row it read, so every node and every promoted mirror counts
from one durable counter. GAP_FREE is refused, since a rollback or a
node stop leaves a gap.

Continuous aggregates: a bootstrap capture carries each root's
aggregate state and the mirror installs it after the root's rows.

Recovery: ALTER DATABASE ... MIRROR RESUME retries a link from its
replicated positions, and MIRROR REBOOTSTRAP starts over. A mirror
status gains progress, installed, fail, resume and rebootstrap steps,
and a capture records the source's metadata timeline so a restored
source fails the link. MIRROR DATABASE claims its name, id and source
through one catalog entry, so concurrent statements resolve in log
order.
A database mirrored with MODE = sync now holds each committed write
until the mirror's groups commit a cut that contains it. A write that
gets no confirmation within the new sync_timeout_ms tuning returns
MIRROR_SYNC_TIMEOUT (SQLSTATE 55M01). The write is committed on the
source, so a client must not retry it as failed. The wait covers SQL
DML, graph, KV, CRDT, array, COPY, CALL, native, RESP, HTTP CRDT apply
and Prometheus remote write paths, and a transaction block waits at its
COMMIT. ILP, CRDT sync sessions and event-plane writes do not wait.

CRDT: the Loro root of a collection is now its bare name in every
database, so a client delta applies in a named database. HTTP
crdt_apply admits the database-qualified collection, and
QualifiedCollection gains bare_of_stored.

Supporting changes:
- the mirror types module is split into a directory of submodules
- cluster error conversions move out of error_from into their own file
- write admission entry scoping moves to its own module
- mirror bootstrap cluster tests are split into focused files
- mirror docs describe config, status, recovery, promote and sync
  mode, and the retention and array docs are updated to match
A DDL statement that returns rows can attach a notice, but the pgwire
encoder dropped it. Take each notice out of the results and queue it on
the session so the client receives it as a NoticeResponse with the
reply.
Reads refused with 55P03 and writes aborted with 40001 leave nothing
applied, so the materialized-sum and write-admission cases now run them
again instead of panicking or silently skipping the round.

Add try_simple_query and error_sqlstate to the raw pgwire harness so a
case can inspect an ErrorResponse and keep using the connection. The
write-admission round panics on any error other than the two retryable
SQLSTATEs.
Drop the entries for files that no longer use the system door and allow
the replicated collection-convert applier, which runs a committed entry
that carries no user identity.
Handle statements that end right after a backslash escape, after a
bare INSERT or UPSERT verb, or after multi-byte text before a vector
operator. Each case sliced past the end of the input or off a
character boundary.

Find the INTO keyword after the verb with any whitespace and leading
comments, and return a parse error for UPSERT without INTO. Take the
left operand of a vector operator only from a directly adjacent text
segment.
…dule

Session binding in the security module depended on the server's
session_auth module for identity construction. Move the stored-user
and API-key identity builders, and session identity rebuild, into
control::security::session_identity and point callers at it.
Drop -runs=0 so the ASan job fuzzes for its time budget instead of
only replaying the existing corpus.
Refresh Cargo.lock with newer compatible releases of the AWS SDK and
smithy crates and related transitive dependencies. Drops the unused
pin-utils and num-integer entries.
@farhan-syah farhan-syah added the run-ci Opt this PR into the full test suite; re-add to force a re-run label Nov 7, 2026
@farhan-syah
farhan-syah merged commit c075464 into main Nov 7, 2026
16 checks passed
@farhan-syah
farhan-syah deleted the fix/final-audit branch November 7, 2026 09:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

run-ci Opt this PR into the full test suite; re-add to force a re-run

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants