Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions test/native_backend_crash.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ pgc_setup "${1:-/usr/local/pg17/bin/pg_config}"
# The postmaster must reinitialize a crashed backend rather than shut the cluster
# down; that is the default, set here so the suite does not depend on it.
psql_run "ALTER SYSTEM SET restart_after_crash = on;"
# THE WAL WRITER IS HELD OFF (its 10s maximum), as a busy runner can leave it, so
# a kill always loses the killed write's unflushed WAL. See the post-flush
# scenario below for why that decides the order of the scenarios.
psql_run "ALTER SYSTEM SET wal_writer_delay = '10s';"
psql_run "SELECT pg_reload_conf();" >/dev/null

# Heap mirror and columnar table, same committed baseline in both.
Expand Down Expand Up @@ -187,14 +191,21 @@ assert_intact() { # label
check "$1: the killed insert left no partial row group" "$(rowgroups)" "$base_groups"
}

# --- kill while a large insert is mid-write ----------------------------------
crash_one_backend 3000000 crashA insert
assert_intact "mid-write"

# --- kill after the stripes have flushed, before the commit that never comes -
# FIRST, because its write is the small one. A killed write's stripe reservation
# (the metapage highwater) is WAL-logged but unflushed, while its pages already
# extend the file. Redo then restores the old highwater over blocks that exist,
# and the next write fills them before it extends anything: after crashA, all
# 100000 rows of this one fit, the relation never grew, and its premise failed
# (nightly 40376459928, PG 17). Here it follows only the committed baseline.
# crashA and crashC write 3000000 rows, which outgrow any such leftover.
crash_one_backend 100000 crashB sleep
assert_intact "post-flush"

# --- kill while a large insert is mid-write ----------------------------------
crash_one_backend 3000000 crashA insert
assert_intact "mid-write"

# --- kill with a concurrent reader holding a connection ----------------------
# The reinit drops every session, not only the one that died. This scenario adds
# a second parked reader on top of the sentinel and asserts it too was dropped,
Expand Down
18 changes: 13 additions & 5 deletions test/pytest/test_native_backend_crash.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ def nbc(pgc_cluster):
c.execute("CREATE EXTENSION IF NOT EXISTS pgcolumnar")
# the postmaster must reinitialize after a backend crash, not shut down
c.execute("ALTER SYSTEM SET restart_after_crash = on")
# the WAL writer held off, as the shell does: a kill always loses the
# killed write's unflushed WAL, which is what orders the scenarios below
c.execute("ALTER SYSTEM SET wal_writer_delay = '10s'")
c.execute("SELECT pg_reload_conf()")
c.execute("CREATE TABLE h (id int, v text, x float8)")
c.execute("CREATE TABLE c (id int, v text, x float8) USING pgcolumnar")
Expand Down Expand Up @@ -213,16 +216,21 @@ def base(nbc):
"mirror": _rows(nbc, "SELECT id, v, x FROM h"), "pm": _postmaster_pid(nbc)}


def test_a_kill_mid_write(nbc, base, expect):
dropped = _crash_one_backend(nbc, expect, 3000000, "crashA", "insert")
_assert_intact(nbc, expect, "mid-write", dropped, base)


def test_a_kill_after_the_stripes_flushed(nbc, base, expect):
"""FIRST, because its write is the small one. A killed write's stripe reservation
is WAL-logged but unflushed while its pages already extend the file, so redo
restores the old highwater over blocks that exist and the next write fills them
first: after crashA, all 100000 rows fit, the relation never grew, and the premise
failed (nightly 40376459928, PG 17). crashA and crashC outgrow any leftover."""
dropped = _crash_one_backend(nbc, expect, 100000, "crashB", "sleep")
_assert_intact(nbc, expect, "post-flush", dropped, base)


def test_a_kill_mid_write(nbc, base, expect):
dropped = _crash_one_backend(nbc, expect, 3000000, "crashA", "insert")
_assert_intact(nbc, expect, "mid-write", dropped, base)


def test_a_kill_with_a_second_reader(nbc, base, expect):
"""The reinit drops every session, so a second parked reader goes too."""
reader = _Bystander(nbc, 90)
Expand Down
Loading