Skip to content

Commit fdbfc0e

Browse files
committed
test: cover 0dt preflight behavior for temporary items
Ensure creating temporary items during a 0dt upgrade doesn't cause a halt. This is already the case because even though these temporary objects are public to all envds, we clear them from the snapshot whenever we open the catalog not in readonly mode.
1 parent 074bb68 commit fdbfc0e

1 file changed

Lines changed: 116 additions & 0 deletions

File tree

test/0dt/mzcompose.py

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3105,3 +3105,119 @@ def workflow_ddl_detection_with_id_pool(c: Composition) -> None:
31053105
> SELECT * FROM pool_mv;
31063106
1
31073107
"""))
3108+
3109+
3110+
def workflow_ddl_detection_ephemeral_items(c: Composition) -> None:
3111+
"""Verify that temporary items do not count as reactable DDL in preflight.
3112+
3113+
Temporary items are durable catalog items tagged with their owning
3114+
session's UUID and draw ids from the normal user-id allocator.
3115+
Ensure that creation of them during 0dt preflight does not halt the
3116+
read-only environment.
3117+
"""
3118+
c.down(destroy_volumes=True)
3119+
c.up("mz_old")
3120+
3121+
PREFLIGHT_STARTED = "waiting for deployment to be caught up"
3122+
3123+
def count_preflight_starts() -> int:
3124+
"""Count mz_new boots via the preflight start line in its log."""
3125+
logs = c.invoke("logs", "mz_new", capture=True).stdout
3126+
return sum(PREFLIGHT_STARTED in line for line in logs.splitlines())
3127+
3128+
def await_preflight_start() -> None:
3129+
deadline = time.time() + 120
3130+
while time.time() < deadline:
3131+
if count_preflight_starts() >= 1:
3132+
return
3133+
time.sleep(0.5)
3134+
raise RuntimeError("timed out waiting for mz_new preflight to start")
3135+
3136+
# The DDL check defaults to every 5 minutes plus once right before
3137+
# ready-to-promote. Tighten it so the temporary items below sit through
3138+
# many checks. Read at mz_new's boot from the catalog.
3139+
c.sql(
3140+
"""
3141+
ALTER SYSTEM SET with_0dt_deployment_ddl_check_interval = '1s';
3142+
ALTER SYSTEM SET cluster = quickstart;
3143+
""",
3144+
service="mz_old",
3145+
port=6877,
3146+
user="mz_system",
3147+
)
3148+
3149+
# Start mz_new in read-only mode (deploy_generation=1) and wait for it
3150+
# to start the preflight process.
3151+
c.up("mz_new")
3152+
await_preflight_start()
3153+
3154+
# A session on the leader creates the temporary items. The connection
3155+
# stays open so the items stay durable.
3156+
conn = c.sql_connection(service="mz_old")
3157+
cur = conn.cursor()
3158+
cur.execute("CREATE TEMPORARY TABLE temp_t (a int)")
3159+
cur.execute("CREATE TEMPORARY VIEW temp_v AS SELECT * FROM temp_t")
3160+
cur.execute("INSERT INTO temp_t VALUES (1)")
3161+
3162+
# Prove the temporary items are durable catalog rows on the leader while
3163+
# mz_new's checks tick
3164+
ephemeral = c.sql_query(
3165+
"""SELECT count(*) FROM mz_internal.mz_catalog_raw
3166+
WHERE data->>'kind' = 'Item'
3167+
AND data->'value'->>'ephemeral_owner_session' IS NOT NULL""",
3168+
service="mz_old",
3169+
port=6877,
3170+
user="mz_system",
3171+
)
3172+
assert ephemeral == [(2,)], f"temporary items are not durable: {ephemeral}"
3173+
3174+
# the temporary items must exist while mz_new is still checking for DDL
3175+
# i.e. before it announces ready. if we're caught up before we've created
3176+
# temporary items, fail loudly.
3177+
deadline = time.time() + 120
3178+
status = None
3179+
while time.time() < deadline:
3180+
try:
3181+
status = _leader_status(c, "mz_new")
3182+
break
3183+
except Exception:
3184+
time.sleep(1)
3185+
assert (
3186+
status == DeploymentStatus.INITIALIZING.value
3187+
), f"mz_new reached status {status} before the temporary items were created"
3188+
3189+
# Sit through several 1s-interval DDL checks with the temporary items in
3190+
# the catalog, then let mz_new run the final check on its way to
3191+
# ready-to-promote. Assert we only see one preflight start throughout
3192+
# promotion which means we never halted.
3193+
time.sleep(5)
3194+
assert (
3195+
count_preflight_starts() == 1
3196+
), "mz_new rebooted with only temporary items created"
3197+
c.await_mz_deployment_status(DeploymentStatus.READY_TO_PROMOTE, "mz_new")
3198+
assert (
3199+
count_preflight_starts() == 1
3200+
), "mz_new rebooted on the final DDL check with only temporary items created"
3201+
3202+
c.promote_mz("mz_new")
3203+
c.await_mz_deployment_status(DeploymentStatus.IS_LEADER, "mz_new", sleep_time=None)
3204+
3205+
# The takeover opened the catalog with write intent, which fences the old
3206+
# leader (killing the session that owned the temporary items) and
3207+
# reclaims every ephemeral item. Only mz_catalog_raw shows whether the
3208+
# durable rows themselves are gone.
3209+
ephemeral = c.sql_query(
3210+
"""SELECT count(*) FROM mz_internal.mz_catalog_raw
3211+
WHERE data->>'kind' = 'Item'
3212+
AND data->'value'->>'ephemeral_owner_session' IS NOT NULL""",
3213+
service="mz_new",
3214+
port=6877,
3215+
user="mz_system",
3216+
)
3217+
assert ephemeral == [(0,)], f"ephemeral items survived promotion: {ephemeral}"
3218+
3219+
# The old leader died with the session's socket; closing is bookkeeping.
3220+
try:
3221+
conn.close()
3222+
except Exception:
3223+
pass

0 commit comments

Comments
 (0)