Skip to content

Move temporary objects to the catalog#37811

Draft
SangJunBak wants to merge 6 commits into
MaterializeInc:mainfrom
SangJunBak:jun/move-temp-to-catalog
Draft

Move temporary objects to the catalog#37811
SangJunBak wants to merge 6 commits into
MaterializeInc:mainfrom
SangJunBak:jun/move-temp-to-catalog

Conversation

@SangJunBak

@SangJunBak SangJunBak commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Batch session benchmark https://buildkite.com/materialize/nightly/builds/17518#019f904d-099f-4f89-889d-be35a5fb3861

Motivation

Why does this change exist? Link to a GitHub issue, design doc, Slack
thread, or explain the problem in a sentence or two. A reviewer who has
no context should understand why after reading this section.

If this implements or addresses an existing issue, it's enough to link to that:
Closes
Fixes
etc.

Description

What does this PR actually do? Focus on the approach and any non-obvious
decisions. The diff shows the code --- use this space to explain what the
diff can't tell a reviewer.

Verification

How do you know this change is correct? Describe new or existing automated
tests, or manual steps you took.

@SangJunBak
SangJunBak force-pushed the jun/move-temp-to-catalog branch from ce3ca26 to f99c06a Compare July 23, 2026 17:01
Initial plan generated off the design doc. To be removed.
@SangJunBak
SangJunBak force-pushed the jun/move-temp-to-catalog branch from f99c06a to bc91280 Compare July 23, 2026 17:12
…(SQL-150)

Adds StateUpdateKind::Session to the durable catalog, written on session
connect and removed on graceful close. All session records are reclaimed
when a catalog is opened with write intent, since opening fences out
every previous owner (this also covers same-generation restarts).

Converts mz_sessions from a coordinator-written builtin table into a
BuiltinMaterializedView over the session records in mz_catalog_raw. The
old shard is released through the usual builtin schema migration
replacement step and finalized by the storage layer.

Also adds ephemeral_owner_session: Option<Uuid> to ItemValue and
SchemaValue (catalog version 91, backfilled as None), the durable
foundation for temporary objects per the durable temporary objects
design (doc/developer/design/20260706_sql_150_durable_temporary_objects.md).
Temporary items now write real durable Item rows, marked with
ephemeral_owner_session = the creating session's UUID and parented to
the temporary schema sentinel id (u0). The in-memory TemporaryItem
side-channel is deleted: temp items flow through the normal durable
update pipeline. On apply, an ephemeral item is routed into the owning
connection's in-memory temporary schema, resolved through a new
session-UUID-to-connection map derived from durable session records.
Item name uniqueness is scoped by owning session, since every session's
temporary schema shares the sentinel schema id.

Session close drops the session's temporary items and its session
record in one catalog transaction. Opening the catalog with write
intent reclaims all ephemeral items alongside stale session records,
covering crashes and restarts. Orphaned temp-table shards are finalized
by the existing storage metadata reconciliation.

BootstrapStateUpdateKind is deleted: it existed only to exclude the
TemporaryItem variant, and memory StateUpdateKind now serves both uses.

Verified manually: temp create/insert/query/rename/cascade-drop, same
temp table name in two concurrent sessions, graceful-close cleanup,
kill -9 + restart reclamation, DISCARD TEMP, temporary_objects.slt.
With temporary items durable in the catalog shard, mz_tables and
mz_views can be derived from mz_internal.mz_catalog_raw instead of
being written by the coordinator. Both show every item including
temporary ones, matching the previous builtin tables. Temporary rows
keep the temporary schema sentinel "0" in schema_id.

Builtin tables and views are reported through two new generated
constant views, mz_internal.mz_builtin_tables and
mz_internal.mz_builtin_views, following the mz_builtin_materialized_views
pattern. mz_builtin_views lists every builtin view except itself, since
its definition cannot contain itself, so that one (new) view is absent
from mz_views and relations derived from it.

parse_catalog_create_sql now exposes 'definition' for views and
'source_id' for tables created from sources.

The old mz_tables and mz_views shards are released through builtin
schema migration replacement steps and finalized by the storage layer,
like the other builtin table conversions.

Verified manually: boot, user/temp tables and views appear with correct
schema ids and definitions, pg_tables/pg_views joins, builtin arms
populated (191 builtin views = all but mz_builtin_views). SLT:
information_schema_tables, oid, temporary_objects.
Session connects and disconnects each write the durable catalog, and
previously did so with one catalog transaction per session, serialized
on the coordinator loop: one timestamp oracle round-trip plus one
compare-and-append against the catalog shard per connection. A
reconnect storm would queue real DDL behind thousands of such commits.

Batch them instead. Session ops land in a pending buffer and a
FlushSessionOps message, scheduled only when the buffer transitions
from empty, commits everything pending in a single catalog
transaction. Because the coordinator loop is single-threaded, ops that
arrive while a flush is in flight accumulate for the next one, so
batch size self-tunes to the commit latency.

Startup completion is two-phase: handle_startup enqueues the
CreateSession op together with everything needed to finish connection
setup, and the flush completes each startup (active_conns insertion,
metrics, StartupResponse) after the shared commit lands. Awaiting the
commit inline would deadlock the loop on its own flush message. The
response is still only sent after the session record is durable and
applied, so temporary object creation can never race its own session
record. Session close enqueues a fire-and-forget DropSession, except
when the session owns temporary items, which keep dropping atomically
with the session record in one inline transaction.

Also fixes an active_sessions metric leak: the gauge was incremented
before the session write and never decremented if the write failed.

Verified manually: 50-connection burst all durable and reclaimed;
debug logs show 60 session ops coalescing into 9 commits with batches
up to 15; solo connects commit alone. SLT: temporary_objects,
transactions.
@SangJunBak
SangJunBak force-pushed the jun/move-temp-to-catalog branch 2 times, most recently from f0bae71 to d47a349 Compare July 23, 2026 18:43
Batched session commits previously flushed as soon as the coordinator
got to them, so under sustained connection churn the commit rate
converged to one catalog compare-and-append per commit latency, on the
order of 100-200 per second.

Bound it with the session_op_flush_interval dyncfg (default 50ms): a
flush scheduled within the interval of the previous one is delayed
until the interval has passed, capping catalog-shard writes from
connection churn at one commit per interval. The delay applies only
under churn: the first op after an idle period still flushes
immediately, so isolated connects keep their latency. Larger batches
also stay well under persist's inline-write threshold, so these
commits continue to skip blob storage.

Verified manually: with the 50ms floor a 30-connection burst commits
61 session ops in 5 catalog transactions (batches up to 28, down from
9 transactions with batches up to 15); solo connects flush
immediately; the interval is tunable via ALTER SYSTEM. SLT:
temporary_objects.
@SangJunBak
SangJunBak force-pushed the jun/move-temp-to-catalog branch from d47a349 to 20a5da3 Compare July 23, 2026 20: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.

1 participant