diff --git a/.github/workflows/run-tests-tiered.yml b/.github/workflows/run-tests-tiered.yml index bfc1e0614..17f5440bf 100644 --- a/.github/workflows/run-tests-tiered.yml +++ b/.github/workflows/run-tests-tiered.yml @@ -166,6 +166,7 @@ jobs: - follow-defer-validate-fks - follow-sequence-reset - copy-source-reconnect + - resume-vacuum steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 diff --git a/src/bin/pgcopydb/summary.c b/src/bin/pgcopydb/summary.c index 0a8fc8632..172dd5e54 100644 --- a/src/bin/pgcopydb/summary.c +++ b/src/bin/pgcopydb/summary.c @@ -843,7 +843,7 @@ summary_add_vacuum(DatabaseCatalog *catalog, CopyTableDataSpec *tableSpecs) } char *sql = - "insert into vacuum_summary(pid, tableoid, start_time_epoch)" + "insert or replace into vacuum_summary(pid, tableoid, start_time_epoch)" "values($1, $2, $3)"; if (!semaphore_lock(&(catalog->sema))) diff --git a/tests/Makefile b/tests/Makefile index 4610abbd2..f8cf16e70 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -19,7 +19,7 @@ all: pagila pagila-multi-steps blobs unit filtering filtering-standby extensions endpos-in-multi-wal-txn exclude-extension \ blob-snapshot-release follow-defer-indexes fk-not-valid defer-validate-fks \ fk-partition-clone follow-defer-validate-fks follow-sequence-reset \ - copy-source-reconnect; + copy-source-reconnect resume-vacuum; pagila: build $(MAKE) -C $@ @@ -120,6 +120,9 @@ follow-sequence-reset: build copy-source-reconnect: build $(MAKE) -C $@ +resume-vacuum: build + $(MAKE) -C $@ + timescaledb: build $(MAKE) -C $@ @@ -135,4 +138,4 @@ build: .PHONY: endpos-in-multi-wal-txn exclude-extension .PHONY: blob-snapshot-release follow-defer-indexes fk-not-valid defer-validate-fks .PHONY: fk-partition-clone follow-defer-validate-fks follow-sequence-reset -.PHONY: copy-source-reconnect +.PHONY: copy-source-reconnect resume-vacuum diff --git a/tests/resume-vacuum/Dockerfile b/tests/resume-vacuum/Dockerfile new file mode 100644 index 000000000..16875d9fd --- /dev/null +++ b/tests/resume-vacuum/Dockerfile @@ -0,0 +1,10 @@ +FROM pagila + +COPY --from=pgcopydb /usr/local/bin/pgcopydb /usr/local/bin + +WORKDIR /usr/src/pgcopydb +COPY copydb.sh copydb.sh +COPY ddl.sql ddl.sql + +USER docker +CMD ["/usr/src/pgcopydb/copydb.sh"] diff --git a/tests/resume-vacuum/Makefile b/tests/resume-vacuum/Makefile new file mode 100644 index 000000000..d405ef0e4 --- /dev/null +++ b/tests/resume-vacuum/Makefile @@ -0,0 +1,15 @@ +# Copyright (c) 2021 The PostgreSQL Global Development Group. +# Licensed under the PostgreSQL License. + +test: down run down ; + +run: build + $(DOCKER) compose run test + +down: + $(DOCKER) compose down + +build: + $(DOCKER) compose build + +.PHONY: down build test diff --git a/tests/resume-vacuum/compose.yaml b/tests/resume-vacuum/compose.yaml new file mode 100644 index 000000000..5e9fdd783 --- /dev/null +++ b/tests/resume-vacuum/compose.yaml @@ -0,0 +1,29 @@ +services: + source: + image: postgres:${PGVERSION:-18} + expose: + - 5432 + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: h4ckm3 + POSTGRES_HOST_AUTH_METHOD: trust + target: + image: postgres:${PGVERSION:-18} + expose: + - 5432 + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: h4ckm3 + POSTGRES_HOST_AUTH_METHOD: trust + test: + build: + context: . + dockerfile: Dockerfile + environment: + PGCOPYDB_SOURCE_PGURI: postgres://postgres:h4ckm3@source/postgres + PGCOPYDB_TARGET_PGURI: postgres://postgres:h4ckm3@target/postgres + PGCOPYDB_TABLE_JOBS: 2 + PGCOPYDB_INDEX_JOBS: 2 + depends_on: + - source + - target diff --git a/tests/resume-vacuum/copydb.sh b/tests/resume-vacuum/copydb.sh new file mode 100755 index 000000000..abc5e44c5 --- /dev/null +++ b/tests/resume-vacuum/copydb.sh @@ -0,0 +1,62 @@ +#!/bin/bash + +set -x +set -e + +export PAGER=cat + +DIR=/tmp/resume-vacuum + +pgcopydb ping + +psql -v ON_ERROR_STOP=1 -d "${PGCOPYDB_SOURCE_PGURI}" -f /usr/src/pgcopydb/ddl.sql + +psql -v ON_ERROR_STOP=1 -d "${PGCOPYDB_TARGET_PGURI}" <<'EOF' +CREATE FUNCTION block_idx() RETURNS event_trigger LANGUAGE plpgsql AS $$ +BEGIN + IF current_setting('test.block_index', true) = 'on' + AND current_query() ~ 'idx_fail_val' + THEN + RAISE EXCEPTION 'CREATE INDEX idx_fail_val blocked by test'; + END IF; +END +$$; + +CREATE EVENT TRIGGER block_idx ON ddl_command_start + WHEN TAG IN ('CREATE INDEX') + EXECUTE FUNCTION block_idx(); + +ALTER DATABASE postgres SET test.block_index = 'on'; +EOF + +rm -rf "${DIR}" + +if pgcopydb clone --dir "${DIR}" --notice > /tmp/clone.log 2>&1 +then + cat /tmp/clone.log + echo "first clone was expected to fail" + exit 1 +fi + +cat /tmp/clone.log + +grep -q "Failed to create index" /tmp/clone.log +grep -q "VACUUM ANALYZE public.t_ok" /tmp/clone.log + +psql -v ON_ERROR_STOP=1 -d "${PGCOPYDB_TARGET_PGURI}" \ + -c "ALTER DATABASE postgres RESET test.block_index" + +pgcopydb clone --dir "${DIR}" --resume --not-consistent --notice \ + > /tmp/resume.log 2>&1 || { cat /tmp/resume.log; exit 1; } + +cat /tmp/resume.log + +if grep -q "constraint failed" /tmp/resume.log +then + echo "resume hit a SQLite constraint failure" + exit 1 +fi + +COUNT=$(psql -tA -d "${PGCOPYDB_TARGET_PGURI}" \ + -c "SELECT count(*) FROM pg_indexes WHERE indexname = 'idx_fail_val'") +test "${COUNT}" -eq 1 diff --git a/tests/resume-vacuum/ddl.sql b/tests/resume-vacuum/ddl.sql new file mode 100644 index 000000000..5cb48099c --- /dev/null +++ b/tests/resume-vacuum/ddl.sql @@ -0,0 +1,20 @@ +CREATE TABLE t_ok ( + id integer PRIMARY KEY, + val text +); + +CREATE TABLE t_noidx ( + id integer, + val text +); + +CREATE TABLE t_fail ( + id integer PRIMARY KEY, + val text +); + +CREATE INDEX idx_fail_val ON t_fail(val); + +INSERT INTO t_ok SELECT i, 'ok' || i FROM generate_series(1, 1000) AS i; +INSERT INTO t_noidx SELECT i, 'noidx' || i FROM generate_series(1, 1000) AS i; +INSERT INTO t_fail SELECT i, 'fail' || i FROM generate_series(1, 1000) AS i;