Skip to content

Commit

Permalink
configurable upper bound on locking attempt timeout
Browse files Browse the repository at this point in the history
  • Loading branch information
ncleaton committed Mar 25, 2023
1 parent 7eaa988 commit 4527244
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 1 deletion.
13 changes: 12 additions & 1 deletion bin/pg_repack.c
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,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 */
Expand Down Expand Up @@ -280,6 +281,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 },
Expand Down Expand Up @@ -308,6 +310,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)
Expand Down Expand Up @@ -1883,6 +1889,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);
Expand Down Expand Up @@ -1931,7 +1938,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 statement_timeout = %d", wait_msec);
timeout_msec = Min(wait_msec, max_lock_timeout_msec);
snprintf(sql, lengthof(sql), "SET LOCAL statement_timeout = %d", timeout_msec);
pgut_command(conn, sql, 0, NULL);

res = pgut_execute_elevel(conn, lock_query, 0, NULL, DEBUG2);
Expand All @@ -1948,6 +1956,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
Expand Down Expand Up @@ -2351,6 +2361,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");
Expand Down
15 changes: 15 additions & 0 deletions doc/pg_repack.rst
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,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
Expand Down Expand Up @@ -207,6 +208,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
Expand Down

0 comments on commit 4527244

Please sign in to comment.