Skip to content

Commit de0a1f8

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 b94f67a commit de0a1f8

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

0 commit comments

Comments
 (0)