Skip to content

Commit

Permalink
Merge pull request #400 from MasahikoSawada/fix_concurrent_vacuums
Browse files Browse the repository at this point in the history
Fix possible two vacuums concurrently processing the same relfilenode.
  • Loading branch information
MasahikoSawada authored May 21, 2024
2 parents cfa429a + 8f6d0df commit f3e6353
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 0 deletions.
24 changes: 24 additions & 0 deletions bin/pg_repack.c
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ typedef struct repack_table
Oid target_tidx; /* target: toast index OID */
Oid pkid; /* target: PK OID */
Oid ckid; /* target: CK OID */
Oid temp_oid; /* temp: OID */
const char *create_pktype; /* CREATE TYPE pk */
const char *create_log; /* CREATE TABLE log */
const char *create_trigger; /* CREATE TRIGGER repack_trigger */
Expand Down Expand Up @@ -910,6 +911,7 @@ repack_one_database(const char *orderby, char *errbuf, size_t errsize)
c++; // Skip schemaname
table.pkid = getoid(res, i, c++);
table.ckid = getoid(res, i, c++);
table.temp_oid = InvalidOid; /* filled after creating the temp table */

if (table.pkid == 0) {
ereport(WARNING,
Expand Down Expand Up @@ -1537,6 +1539,14 @@ repack_one_table(repack_table *table, const char *orderby)
command(sql.data, 0, NULL);
command("COMMIT", 0, NULL);

/* Get OID of the temp table */
printfStringInfo(&sql, "SELECT 'repack.table_%u'::regclass::oid",
table->target_oid);
res = execute(sql.data, 0, NULL);
table->temp_oid = getoid(res, 0, 0);
Assert(OidIsValid(table->temp_oid));
CLEARPGRES(res);

/*
* 3. Create indexes on temp table.
*/
Expand Down Expand Up @@ -1612,6 +1622,20 @@ repack_one_table(repack_table *table, const char *orderby)
goto cleanup;
}

/*
* Acquire AccessExclusiveLock on the temp table to prevent concurrent
* operations during swapping relations.
*/
printfStringInfo(&sql, "LOCK TABLE repack.table_%u IN ACCESS EXCLUSIVE MODE",
table->target_oid);
if (!(lock_exclusive(conn2, utoa(table->temp_oid, buffer),
sql.data, false)))
{
elog(WARNING, "lock_exclusive() failed in conn2 for table_%u",
table->target_oid);
goto cleanup;
}

apply_log(conn2, table, 0);
params[0] = utoa(table->target_oid, buffer);
pgut_command(conn2, "SELECT repack.repack_swap($1)", 1, params);
Expand Down
18 changes: 18 additions & 0 deletions lib/repack.c
Original file line number Diff line number Diff line change
Expand Up @@ -901,6 +901,24 @@ repack_swap(PG_FUNCTION_ARGS)
CommandCounterIncrement();
}

/*
* Sanity check if both relations are locked in access exclusive mode
* before swapping these files.
*/
#if PG_VERSION_NUM >= 120000
{
LOCKTAG tag;

SET_LOCKTAG_RELATION(tag, MyDatabaseId, oid);
if (!LockHeldByMe(&tag, AccessExclusiveLock))
elog(ERROR, "must hold access exclusive lock on table \"%s\"", relname);

SET_LOCKTAG_RELATION(tag, MyDatabaseId, oid2);
if (!LockHeldByMe(&tag, AccessExclusiveLock))
elog(ERROR, "must hold access exclusive lock on table \"table_%u\"", oid);
}
#endif

/* swap tables. */
swap_heap_or_index_files(oid, oid2);
CommandCounterIncrement();
Expand Down

0 comments on commit f3e6353

Please sign in to comment.