diff --git a/bin/pg_repack.c b/bin/pg_repack.c index 6b9cde6..80bbada 100644 --- a/bin/pg_repack.c +++ b/bin/pg_repack.c @@ -252,6 +252,7 @@ static bool moveidx = false; static SimpleStringList r_index = {NULL, NULL}; static bool only_indexes = false; static int wait_timeout = 60; /* in seconds */ +static int max_lock_timeout_msec = 1000; static int jobs = 0; /* number of concurrent worker conns. */ static bool dryrun = false; static unsigned int temp_obj_num = 0; /* temporary objects counter */ @@ -285,6 +286,7 @@ static pgut_option options[] = { 'l', 'i', "index", &r_index }, { 'b', 'x', "only-indexes", &only_indexes }, { 'i', 'T', "wait-timeout", &wait_timeout }, + { 'i', 'm', "max-lock-timeout", &max_lock_timeout_msec }, { 'B', 'Z', "no-analyze", &analyze }, { 'i', 'j', "jobs", &jobs }, { 'b', 'D', "no-kill-backend", &no_kill_backend }, @@ -320,6 +322,10 @@ main(int argc, char *argv[]) if (dryrun) elog(INFO, "Dry run enabled, not executing repack"); + if (max_lock_timeout_msec < 1 || max_lock_timeout_msec > 1000) + ereport(ERROR, (errcode(EINVAL), + errmsg("--max-lock-timeout must be between 1 and 1000"))); + if (r_index.head || only_indexes) { if (r_index.head && table_list.head) @@ -1916,6 +1922,7 @@ lock_exclusive(PGconn *conn, const char *relid, const char *lock_query, bool sta char sql[1024]; PGresult *res; int wait_msec; + int timeout_msec; if (start_xact) pgut_command(conn, "BEGIN ISOLATION LEVEL READ COMMITTED", 0, NULL); @@ -1964,7 +1971,8 @@ lock_exclusive(PGconn *conn, const char *relid, const char *lock_query, bool sta /* wait for a while to lock the table. */ wait_msec = Min(1000, i * 100); - snprintf(sql, lengthof(sql), "SET LOCAL lock_timeout = %d", wait_msec); + timeout_msec = Min(wait_msec, max_lock_timeout_msec); + snprintf(sql, lengthof(sql), "SET LOCAL lock_timeout = %d", timeout_msec); pgut_command(conn, sql, 0, NULL); res = pgut_execute_elevel(conn, lock_query, 0, NULL, DEBUG2); @@ -1981,6 +1989,8 @@ lock_exclusive(PGconn *conn, const char *relid, const char *lock_query, bool sta pgut_rollback(conn); else pgut_command(conn, "ROLLBACK TO SAVEPOINT repack_sp1", 0, NULL); + if (timeout_msec < wait_msec) + usleep(1000 * (wait_msec - timeout_msec)); continue; } else @@ -2415,6 +2425,7 @@ pgut_help(bool details) printf(" -i, --index=INDEX move only the specified index\n"); printf(" -x, --only-indexes move only indexes of the specified table\n"); printf(" -T, --wait-timeout=SECS timeout to cancel other backends on conflict\n"); + printf(" -m, --max-lock-timeout=MS max millisecond timeout for a strong lock attempt\n"); printf(" -D, --no-kill-backend don't kill other backends when timed out\n"); printf(" -Z, --no-analyze don't analyze at end\n"); printf(" -k, --no-superuser-check skip superuser checks in client\n"); diff --git a/doc/pg_repack.rst b/doc/pg_repack.rst index 1a92f5e..7541435 100644 --- a/doc/pg_repack.rst +++ b/doc/pg_repack.rst @@ -121,6 +121,7 @@ Options: -i, --index=INDEX move only the specified index -x, --only-indexes move only indexes of the specified table -T, --wait-timeout=SECS timeout to cancel other backends on conflict + -m, --max-lock-timeout=MS max millisecond timeout for a strong lock attempt -D, --no-kill-backend don't kill other backends when timed out -Z, --no-analyze don't analyze at end -k, --no-superuser-check skip superuser checks in client @@ -212,6 +213,20 @@ Reorg Options backends after twice this timeout has passed. The default is 60 seconds. +``-m MS``, ``--max-lock-timeout=MS`` + When attempting to take an exclusive lock, pg_repack sets a short timeout + for the lock command and makes repeated attempts to get the lock. This + avoids blocking all access to the table for up to ``--wait-timeout`` + seconds if a long-running query is in the way. The lock timeout is + increased with each attempt, up to maximum of ``--max-lock-timeout`` + milliseconds. + The default is 1000, i.e. 1 second. + If you want to avoid undue delay to queries on timescales much less than 1 + second, setting a lower ``--max-lock-timeout`` will help. It comes at the + cost of making each lock attempt less likely to succeed, which may cause + the pg_repack run to take much longer. + Currently, values larger than 1000 are not supported. + ``-D``, ``--no-kill-backend`` Skip to repack table if the lock cannot be taken for duration specified ``--wait-timeout``, instead of cancelling conflicting queries. The default