Skip to content

meta: let an operator stop, and forget, a whole namespace - #123

Merged
bjmeetsfo merged 2 commits into
mainfrom
oss/namespace-lifecycle
Aug 24, 2026
Merged

meta: let an operator stop, and forget, a whole namespace#123
bjmeetsfo merged 2 commits into
mainfrom
oss/namespace-lifecycle

Conversation

@bjmeetsfo

Copy link
Copy Markdown
Collaborator

A namespace has a state, and nothing ever reads it

MetaEntityState on a namespace is set to Normal when the namespace is created, carried through every snapshot, and reported by GET /namespaces. Nothing consults it. There is no call that changes it.

Two consequences:

There is no tenant-level lever. You can freeze one shard, one table, one server, one proxy. The unit an operator actually reaches for during an incident — stop everything for this customer — takes one call per table, and races with any table created while you are working through the list.

A namespace is permanent. Once created, including implicitly by add_table, it is in the meta state for the life of the cluster. Retention collects servers, proxies and tables; namespaces have nothing to collect them by.

What this adds

freeze_namespace, unfreeze_namespace, drop_namespace, and POST /namespaces/freeze, /namespaces/unfreeze, /namespaces/delete. No new wire type — a namespace state change carries the same body as creating one.

The part that matters: the freeze has to actually hold

A lever nothing respects is worse than no lever, so:

  • Topology returns resource_frozen for every table in a frozen namespace, and table_not_found for a dropped one — checked against the namespace, not the tables, so a table created after the freeze is covered too.
  • add_table is refused into a frozen or dropped namespace. Without this, a table created a second after the freeze serves straight through it.
  • drop_namespace is refused while the namespace still holds a table that is not itself dropped (namespace_not_empty). Dropping the namespace out from under a live table would leave that table addressable by name but unreachable through its namespace.
  • The mute guard applies, like every other recorded change.

Dropping is a tombstone, not an erasure: unfreeze_namespace revives a dropped namespace, so the drop stays recoverable up until retention forgets it. The drop is stamped into dropped_since_ms under a namespace: key, which is what a later retention round will age against — collecting dropped namespaces is a separate change, so this one does not touch retention.

Recorded as a mutation, so it replays and reaches raft peers; the raft backend gets the three matching operations.

Tests

7 new: freezing a namespace stopping both its tables and unfreezing restoring them, a table refused into a frozen namespace, drop refused while a table is live and permitted once they are all dropped, a dropped namespace coming back, the unknown/unchanged rejections, the mute guard, and state surviving both a snapshot round trip and mutation-log replay.

Verification:

  • cargo test -p temporalstore-rust --lib meta -- --test-threads=1259 passed, 0 failed.
  • --bin metaserver, --bin matrixark_rust_proxy, --lib client — all green.
  • cargo build -p temporalstore-rust --bin metaserver — clean.

No gate and no new configuration: namespaces are created Normal and stay that way until someone asks otherwise.

@bjmeetsfo
bjmeetsfo requested a review from superhaiou as a code owner August 22, 2026 05:39
@bjmeetsfo
bjmeetsfo force-pushed the oss/namespace-lifecycle branch from f1a9382 to ad1dfdc Compare August 23, 2026 17:50
supermengm and others added 2 commits August 23, 2026 21:08
MetaEntityState on a namespace is set to Normal when the namespace is created,
carried through every snapshot, and reported by GET /namespaces. Nothing
consults it, and no call changes it.

So there is no tenant-level lever. You can freeze one shard, one table, one
server, one proxy; the unit an operator actually reaches for during an incident,
stop everything for this customer, takes one call per table and races with any
table created while you are working through the list. And a namespace is
permanent: once created, including implicitly by add_table, it is in the meta
state for the life of the cluster, with nothing for retention to collect it by.

This adds freeze, unfreeze and drop, over POST /namespaces/{freeze,unfreeze,
delete}. No new wire type - a namespace state change carries the same body as
creating one.

A lever nothing respects is worse than no lever, so the freeze has to hold.
Topology returns resource_frozen for every table in a frozen namespace and
table_not_found for a dropped one, checked against the namespace rather than
the tables, so a table created after the freeze is covered too. add_table is
refused into a namespace that is not serving, because a table created a second
after the freeze would otherwise serve straight through it. drop_namespace is
refused while the namespace still holds a table that is not itself dropped,
because dropping it out from under a live table would leave that table
addressable by name but unreachable through its namespace. The mute guard
applies, like every other recorded change.

Dropping is a tombstone, not an erasure: unfreeze revives a dropped namespace,
so the drop stays recoverable. The drop is stamped into dropped_since_ms under a
namespace key, which is what a later retention round will age against;
collecting dropped namespaces is a separate change, so this one leaves retention
alone.

Recorded as a mutation, so it replays and reaches raft peers; the raft backend
gets the three matching operations.

7 tests, including a table refused into a frozen namespace, drop refused while a
table is live, and state surviving a snapshot round trip and mutation-log replay.
Rebasing crossed changes this branch predates: the metadata counters became
atomics, server registration gained a hardware shape, and a topology request
gained a client location. None is a conflict of intent -- the counter is
bumped either way, and a caller has nothing to say about a field that did not
exist when it was written, so each gets the empty value every helper written
after the change already passes.
@bjmeetsfo
bjmeetsfo force-pushed the oss/namespace-lifecycle branch from ad1dfdc to 8c4f460 Compare August 24, 2026 04:11
@bjmeetsfo
bjmeetsfo merged commit d1bb641 into main Aug 24, 2026
6 of 7 checks passed
@bjmeetsfo
bjmeetsfo deleted the oss/namespace-lifecycle branch August 24, 2026 04:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants