mirror of
https://github.com/neondatabase/neon.git
synced 2026-08-12 09:19:39 +00:00
Preserve HEAP_COMBOCID when restoring t_cid from WAL (#8503)
## Problem See https://github.com/neondatabase/neon/issues/8499 ## Summary of changes Save HEAP_COMBOCID flag in WAL and do not clear it in redo handlers. Related Postgres PRs: https://github.com/neondatabase/postgres/pull/457 https://github.com/neondatabase/postgres/pull/458 https://github.com/neondatabase/postgres/pull/459 ## Checklist before requesting a review - [ ] I have performed a self-review of my code. - [ ] If it is a core feature, I have added thorough tests. - [ ] Do we need to implement analytics? if so did you add the relevant metrics to the dashboard? - [ ] If this PR requires public announcement, mark it with /release-notes label and add several sentences in this section. ## Checklist before merging - [ ] Do not forget to reformat commit message to not include the above checklist --------- Co-authored-by: Konstantin Knizhnik <knizhnik@neon.tech> Co-authored-by: Heikki Linnakangas <heikki@neon.tech>
This commit is contained in:
committed by
GitHub
parent
c624317b0e
commit
7a1736ddcf
@@ -0,0 +1,139 @@
|
||||
from fixtures.neon_fixtures import NeonEnvBuilder
|
||||
|
||||
|
||||
def do_combocid_op(neon_env_builder: NeonEnvBuilder, op):
|
||||
env = neon_env_builder.init_start()
|
||||
endpoint = env.endpoints.create_start(
|
||||
"main",
|
||||
config_lines=[
|
||||
"shared_buffers='1MB'",
|
||||
],
|
||||
)
|
||||
|
||||
conn = endpoint.connect()
|
||||
cur = conn.cursor()
|
||||
n_records = 1000
|
||||
|
||||
cur.execute("CREATE EXTENSION neon_test_utils")
|
||||
|
||||
cur.execute("create table t(id integer, val integer)")
|
||||
|
||||
cur.execute("begin")
|
||||
cur.execute("insert into t values (1, 0)")
|
||||
cur.execute("insert into t values (2, 0)")
|
||||
cur.execute(f"insert into t select g, 0 from generate_series(3,{n_records}) g")
|
||||
|
||||
# Open a cursor that scroll it halfway through
|
||||
cur.execute("DECLARE c1 NO SCROLL CURSOR WITHOUT HOLD FOR SELECT * FROM t")
|
||||
cur.execute("fetch 500 from c1")
|
||||
rows = cur.fetchall()
|
||||
assert len(rows) == 500
|
||||
|
||||
# Perform specified operation
|
||||
cur.execute(op)
|
||||
|
||||
# Clear the cache, so that we exercise reconstructing the pages
|
||||
# from WAL
|
||||
cur.execute("SELECT clear_buffer_cache()")
|
||||
|
||||
# Check that the cursor opened earlier still works. If the
|
||||
# combocids are not restored correctly, it won't.
|
||||
cur.execute("fetch all from c1")
|
||||
rows = cur.fetchall()
|
||||
assert len(rows) == 500
|
||||
|
||||
cur.execute("rollback")
|
||||
|
||||
|
||||
def test_combocid_delete(neon_env_builder: NeonEnvBuilder):
|
||||
do_combocid_op(neon_env_builder, "delete from t")
|
||||
|
||||
|
||||
def test_combocid_update(neon_env_builder: NeonEnvBuilder):
|
||||
do_combocid_op(neon_env_builder, "update t set val=val+1")
|
||||
|
||||
|
||||
def test_combocid_lock(neon_env_builder: NeonEnvBuilder):
|
||||
do_combocid_op(neon_env_builder, "select * from t for update")
|
||||
|
||||
|
||||
def test_combocid_multi_insert(neon_env_builder: NeonEnvBuilder):
|
||||
env = neon_env_builder.init_start()
|
||||
endpoint = env.endpoints.create_start(
|
||||
"main",
|
||||
config_lines=[
|
||||
"shared_buffers='1MB'",
|
||||
],
|
||||
)
|
||||
|
||||
conn = endpoint.connect()
|
||||
cur = conn.cursor()
|
||||
n_records = 1000
|
||||
|
||||
cur.execute("CREATE EXTENSION neon_test_utils")
|
||||
|
||||
cur.execute("create table t(id integer, val integer)")
|
||||
file_path = f"{endpoint.pg_data_dir_path()}/t.csv"
|
||||
cur.execute(f"insert into t select g, 0 from generate_series(1,{n_records}) g")
|
||||
cur.execute(f"copy t to '{file_path}'")
|
||||
cur.execute("truncate table t")
|
||||
|
||||
cur.execute("begin")
|
||||
cur.execute(f"copy t from '{file_path}'")
|
||||
|
||||
# Open a cursor that scroll it halfway through
|
||||
cur.execute("DECLARE c1 NO SCROLL CURSOR WITHOUT HOLD FOR SELECT * FROM t")
|
||||
cur.execute("fetch 500 from c1")
|
||||
rows = cur.fetchall()
|
||||
assert len(rows) == 500
|
||||
|
||||
# Delete all the rows. Because all of the rows were inserted earlier in the
|
||||
# same transaction, all the rows will get a combocid.
|
||||
cur.execute("delete from t")
|
||||
# Clear the cache, so that we exercise reconstructing the pages
|
||||
# from WAL
|
||||
cur.execute("SELECT clear_buffer_cache()")
|
||||
|
||||
# Check that the cursor opened earlier still works. If the
|
||||
# combocids are not restored correctly, it won't.
|
||||
cur.execute("fetch all from c1")
|
||||
rows = cur.fetchall()
|
||||
assert len(rows) == 500
|
||||
|
||||
cur.execute("rollback")
|
||||
|
||||
|
||||
def test_combocid(neon_env_builder: NeonEnvBuilder):
|
||||
env = neon_env_builder.init_start()
|
||||
endpoint = env.endpoints.create_start("main")
|
||||
|
||||
conn = endpoint.connect()
|
||||
cur = conn.cursor()
|
||||
n_records = 100000
|
||||
|
||||
cur.execute("create table t(id integer, val integer)")
|
||||
cur.execute(f"insert into t values (generate_series(1,{n_records}), 0)")
|
||||
|
||||
cur.execute("begin")
|
||||
|
||||
cur.execute("update t set val=val+1")
|
||||
assert cur.rowcount == n_records
|
||||
cur.execute("update t set val=val+1")
|
||||
assert cur.rowcount == n_records
|
||||
cur.execute("update t set val=val+1")
|
||||
assert cur.rowcount == n_records
|
||||
|
||||
cur.execute("delete from t")
|
||||
assert cur.rowcount == n_records
|
||||
cur.execute("delete from t")
|
||||
assert cur.rowcount == 0
|
||||
|
||||
cur.execute(f"insert into t values (generate_series(1,{n_records}), 0)")
|
||||
cur.execute("update t set val=val+1")
|
||||
assert cur.rowcount == n_records
|
||||
cur.execute("update t set val=val+1")
|
||||
assert cur.rowcount == n_records
|
||||
cur.execute("update t set val=val+1")
|
||||
assert cur.rowcount == n_records
|
||||
|
||||
cur.execute("rollback")
|
||||
Reference in New Issue
Block a user