meta: let an operator stop, and forget, a whole namespace - #123
Merged
Conversation
bjmeetsfo
force-pushed
the
oss/namespace-lifecycle
branch
from
August 23, 2026 17:50
f1a9382 to
ad1dfdc
Compare
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
force-pushed
the
oss/namespace-lifecycle
branch
from
August 24, 2026 04:11
ad1dfdc to
8c4f460
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A namespace has a state, and nothing ever reads it
MetaEntityStateon a namespace is set toNormalwhen the namespace is created, carried through every snapshot, and reported byGET /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, andPOST /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:
resource_frozenfor every table in a frozen namespace, andtable_not_foundfor a dropped one — checked against the namespace, not the tables, so a table created after the freeze is covered too.add_tableis refused into a frozen or dropped namespace. Without this, a table created a second after the freeze serves straight through it.drop_namespaceis 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.Dropping is a tombstone, not an erasure:
unfreeze_namespacerevives a dropped namespace, so the drop stays recoverable up until retention forgets it. The drop is stamped intodropped_since_msunder anamespace: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=1— 259 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
Normaland stay that way until someone asks otherwise.