2-lock_timeout-v19.patch
text/x-patch
Filename: 2-lock_timeout-v19.patch
Type: text/x-patch
Part: 1
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
Series: patch v19
| File | + | − |
|---|---|---|
| doc/src/sgml/config.sgml | 58 | 1 |
| doc/src/sgml/ref/lock.sgml | 5 | 2 |
| doc/src/sgml/ref/select.sgml | 8 | 0 |
| src/backend/storage/lmgr/proc.c | 2 | 0 |
| src/backend/utils/init/postinit.c | 89 | 0 |
| src/backend/utils/misc/guc.c | 32 | 0 |
| src/backend/utils/misc/postgresql.conf.sample | 5 | 0 |
| src/include/storage/proc.h | 7 | 0 |
| src/include/utils/timeout.h | 1 | 0 |
| src/test/regress/expected/prepared_xacts.out | 21 | 0 |
| src/test/regress/sql/prepared_xacts.sql | 21 | 0 |
diff -durpN postgresql.1/doc/src/sgml/config.sgml postgresql.2/doc/src/sgml/config.sgml
--- postgresql.1/doc/src/sgml/config.sgml 2012-07-05 06:51:30.540155082 +0200
+++ postgresql.2/doc/src/sgml/config.sgml 2012-07-22 21:39:00.856924933 +0200
@@ -4953,7 +4953,10 @@ COPY postgres_log FROM '/full/path/to/lo
milliseconds, starting from the time the command arrives at the server
from the client. If <varname>log_min_error_statement</> is set to
<literal>ERROR</> or lower, the statement that timed out will also be
- logged. A value of zero (the default) turns this off.
+ logged. The timeout may happen any time, i.e. while waiting for locks
+ on database objects or in case of a large result set, during data
+ retrieval from the server after all locks were successfully acquired.
+ A value of zero (the default) turns this off.
</para>
<para>
@@ -4964,6 +4967,60 @@ COPY postgres_log FROM '/full/path/to/lo
</listitem>
</varlistentry>
+ <varlistentry id="guc-lock-timeout" xreflabel="lock_timeout">
+ <term><varname>lock_timeout</varname> (<type>integer</type>)</term>
+ <indexterm>
+ <primary><varname>lock_timeout</> configuration parameter</primary>
+ </indexterm>
+ <listitem>
+ <para>
+ Abort any statement that tries to acquire a heavy-weight lock on rows,
+ pages, tables, indices or other objects and the lock(s) has to wait
+ more than the specified number of milliseconds. As opposed to
+ <varname>statement_timeout</>, this timeout (and the error) may only
+ occur while waiting for locks. If <varname>log_min_error_statement</>
+ is set to <literal>ERROR</> or lower, the statement that timed out will
+ also be logged. A value of zero (the default) turns this off.
+ </para>
+
+ <para>
+ Setting <varname>lock_timeout</> in
+ <filename>postgresql.conf</> is not recommended because it
+ affects all sessions.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="guc-lock-timeout-option" xreflabel="lock_timeout_option">
+ <term><varname>lock_timeout_option</varname> (<type>enum</type>)</term>
+ <indexterm>
+ <primary><varname>lock_timeout_option</> configuration parameter</primary>
+ </indexterm>
+ <listitem>
+ <para>
+ Control the behaviour of <varname>lock_timeout</>. Possible values are
+ 'per_lock' and 'per_statement'.
+ </para>
+
+ <para>
+ With 'per_lock' in effect and if the statement involves more than one
+ lock, the timeout applies to every one of them individually, starting
+ from the time the server attempts to lock an object. This makes the
+ statement possibly wait for up to N * <varname>lock_timeout</> time in
+ the worst case where N is the number of locks attempted. This is the
+ default.
+ </para>
+
+ <para>
+ With 'per_statement' in effect, <varname>lock_timeout</> behaves like
+ <varname>statement_timeout</>: the specified timeout applies to all
+ the locks in a cumulative way, starting from the time the command
+ arrives at the server from the client.
+ </para>
+
+ </listitem>
+ </varlistentry>
+
<varlistentry id="guc-vacuum-freeze-table-age" xreflabel="vacuum_freeze_table_age">
<term><varname>vacuum_freeze_table_age</varname> (<type>integer</type>)</term>
<indexterm>
diff -durpN postgresql.1/doc/src/sgml/ref/lock.sgml postgresql.2/doc/src/sgml/ref/lock.sgml
--- postgresql.1/doc/src/sgml/ref/lock.sgml 2012-04-16 19:57:22.229913063 +0200
+++ postgresql.2/doc/src/sgml/ref/lock.sgml 2012-07-22 21:39:00.857924939 +0200
@@ -39,8 +39,11 @@ LOCK [ TABLE ] [ ONLY ] <replaceable cla
<literal>NOWAIT</literal> is specified, <command>LOCK
TABLE</command> does not wait to acquire the desired lock: if it
cannot be acquired immediately, the command is aborted and an
- error is emitted. Once obtained, the lock is held for the
- remainder of the current transaction. (There is no <command>UNLOCK
+ error is emitted. If <varname>lock_timeout</varname> is set to a value
+ higher than 0, and the lock cannot be acquired under the specified
+ timeout value in milliseconds, the command is aborted and an error
+ is emitted. Once obtained, the lock is held for the remainder of
+ the current transaction. (There is no <command>UNLOCK
TABLE</command> command; locks are always released at transaction
end.)
</para>
diff -durpN postgresql.1/doc/src/sgml/ref/select.sgml postgresql.2/doc/src/sgml/ref/select.sgml
--- postgresql.1/doc/src/sgml/ref/select.sgml 2012-04-16 19:57:22.233913109 +0200
+++ postgresql.2/doc/src/sgml/ref/select.sgml 2012-07-22 21:39:00.859924951 +0200
@@ -1199,6 +1199,14 @@ FOR SHARE [ OF <replaceable class="param
</para>
<para>
+ If <literal>NOWAIT</> option is not specified and <varname>lock_timeout</varname>
+ is set to a value higher than 0, and the lock or statement (depending on
+ <varname>lock_timeout_option</varname>) needs to wait more than
+ the specified value in milliseconds, the command reports an error after
+ timing out, rather than waiting indefinitely.
+ </para>
+
+ <para>
If specific tables are named in <literal>FOR UPDATE</literal>
or <literal>FOR SHARE</literal>,
then only rows coming from those tables are locked; any other
diff -durpN postgresql.1/src/backend/storage/lmgr/proc.c postgresql.2/src/backend/storage/lmgr/proc.c
--- postgresql.1/src/backend/storage/lmgr/proc.c 2012-07-22 21:34:50.478375695 +0200
+++ postgresql.2/src/backend/storage/lmgr/proc.c 2012-07-22 21:39:00.860924957 +0200
@@ -55,6 +55,8 @@
/* GUC variables */
int DeadlockTimeout = 1000;
int StatementTimeout = 0;
+int LockTimeout = 0;
+int LockTimeoutOption = LOCK_TIMEOUT_PER_LOCK;
bool log_lock_waits = false;
/* Pointer to this process's PGPROC and PGXACT structs, if any */
diff -durpN postgresql.1/src/backend/utils/init/postinit.c postgresql.2/src/backend/utils/init/postinit.c
--- postgresql.1/src/backend/utils/init/postinit.c 2012-07-22 16:48:48.525857751 +0200
+++ postgresql.2/src/backend/utils/init/postinit.c 2012-07-22 21:39:00.861924963 +0200
@@ -56,6 +56,7 @@
#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/timeout.h"
+#include "utils/timestamp.h"
#include "utils/tqual.h"
@@ -66,10 +67,21 @@ static void CheckMyDatabase(const char *
static void InitCommunication(void);
static void ShutdownPostgres(int code, Datum arg);
static void StatementTimeoutHandler(void);
+static void LockTimeoutHandler(void);
+static void EnableLockTimeout(void);
+static void DisableLockTimeout(void);
+static bool LockTimeoutCondition(void);
+static void LockTimeoutError(void);
static bool ThereIsAtLeastOneRole(void);
static void process_startup_options(Port *port, bool am_superuser);
static void process_settings(Oid databaseid, Oid roleid);
+static ExtraTimeoutFunctions LockTimeoutFunctions = {
+ NULL,
+ EnableLockTimeout, DisableLockTimeout,
+ LockTimeoutCondition, LockTimeoutError
+};
+
/*** InitPostgres support ***/
@@ -501,6 +513,8 @@ InitPostgres(const char *in_dbname, Oid
if (!bootstrap)
{
RegisterTimeout(DEADLOCK_TIMEOUT, CheckDeadLock);
+ RegisterTimeout(LOCK_TIMEOUT, LockTimeoutHandler);
+ RegisterExtraTimeout(&LockTimeoutFunctions);
RegisterTimeout(STATEMENT_TIMEOUT, StatementTimeoutHandler);
}
@@ -983,6 +997,81 @@ ShutdownPostgres(int code, Datum arg)
}
+/*
+ * LOCK_TIMEOUT handler: disable all subsequent timeouts
+ *
+ * This ensures that the error coming from lock timeout is reported
+ * instead of triggering the query-cancel interrupt (and reporting
+ * the statement timeout error) in case they trigger at the same time.
+ */
+static void
+LockTimeoutHandler(void)
+{
+ disable_all_timeouts(true);
+}
+
+
+/*
+ * Enable LOCK_TIMEOUT
+ */
+static void
+EnableLockTimeout(void)
+{
+ if (LockTimeout > 0)
+ {
+ switch (LockTimeoutOption)
+ {
+ case LOCK_TIMEOUT_PER_LOCK:
+ enable_timeout_after(LOCK_TIMEOUT, LockTimeout);
+ break;
+ case LOCK_TIMEOUT_PER_STMT:
+ enable_timeout_at(LOCK_TIMEOUT,
+ TimestampTzPlusMilliseconds(
+ GetCurrentStatementStartTimestamp(),
+ LockTimeout));
+ break;
+ default:
+ elog(ERROR, "unhandled lock_timeout_option value: \"%s\"",
+ GetConfigOptionByName("lock_timeout_option", NULL));
+ break;
+ }
+ }
+}
+
+
+/*
+ * Disable LOCK_TIMEOUT
+ */
+static void
+DisableLockTimeout(void)
+{
+ disable_timeout(LOCK_TIMEOUT, false);
+}
+
+
+/*
+ * Indicator for LOCK_TIMEOUT.
+ */
+static bool
+LockTimeoutCondition(void)
+{
+ return get_timeout_indicator(LOCK_TIMEOUT);
+}
+
+
+/*
+ * Error reporting for LOCK_TIMEOUT
+ */
+static void
+LockTimeoutError(void)
+{
+ if (get_timeout_indicator(LOCK_TIMEOUT))
+ ereport(ERROR,
+ (errcode(ERRCODE_QUERY_CANCELED),
+ errmsg("canceling statement due to lock timeout")));
+}
+
+
/*
* STATEMENT_TIMEOUT handler: trigger a query-cancel interrupt.
*/
diff -durpN postgresql.1/src/backend/utils/misc/guc.c postgresql.2/src/backend/utils/misc/guc.c
--- postgresql.1/src/backend/utils/misc/guc.c 2012-07-03 13:50:11.725151648 +0200
+++ postgresql.2/src/backend/utils/misc/guc.c 2012-07-22 21:39:00.865924989 +0200
@@ -389,6 +389,17 @@ static const struct config_enum_entry sy
};
/*
+ * Control behaviour of lock_timeout:
+ * - timeout applied per lock from the time the lock is attempted to be taken
+ * - timeout applied per statement from the time the statements has started
+ */
+static const struct config_enum_entry lock_timeout_options[] = {
+ {"per_lock", LOCK_TIMEOUT_PER_LOCK, false},
+ {"per_statement", LOCK_TIMEOUT_PER_STMT, false},
+ {NULL, 0, false}
+};
+
+/*
* Options for enum values stored in other modules
*/
extern const struct config_enum_entry wal_level_options[];
@@ -1861,6 +1872,17 @@ static struct config_int ConfigureNamesI
},
{
+ {"lock_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Sets the maximum allowed timeout for any lock taken by a statement."),
+ gettext_noop("A value of 0 turns off the timeout."),
+ GUC_UNIT_MS
+ },
+ &LockTimeout,
+ 0, 0, INT_MAX,
+ NULL, NULL, NULL
+ },
+
+ {
{"vacuum_freeze_min_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
gettext_noop("Minimum age at which VACUUM should freeze a table row."),
NULL
@@ -3141,6 +3163,16 @@ static struct config_enum ConfigureNames
NULL, NULL, NULL
},
+ {
+ {"lock_timeout_option", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Sets the lock_timeout behaviour."),
+ NULL
+ },
+ &LockTimeoutOption,
+ LOCK_TIMEOUT_PER_LOCK, lock_timeout_options,
+ NULL, NULL, NULL
+ },
+
{
{"log_error_verbosity", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Sets the verbosity of logged messages."),
diff -durpN postgresql.1/src/backend/utils/misc/postgresql.conf.sample postgresql.2/src/backend/utils/misc/postgresql.conf.sample
--- postgresql.1/src/backend/utils/misc/postgresql.conf.sample 2012-05-14 08:20:56.298830662 +0200
+++ postgresql.2/src/backend/utils/misc/postgresql.conf.sample 2012-07-22 21:39:00.866924995 +0200
@@ -528,6 +528,11 @@
#------------------------------------------------------------------------------
#deadlock_timeout = 1s
+#lock_timeout = 0 # timeout value for heavy-weight locks
+ # taken by statements. 0 disables timeout
+ # unit in milliseconds, default is 0
+#lock_timeout_option = 'per_lock' # behaviour of lock_timeout. possible
+ # values are: 'per_lock' or 'per_statement'
#max_locks_per_transaction = 64 # min 10
# (change requires restart)
# Note: Each lock table slot uses ~270 bytes of shared memory, and there are
diff -durpN postgresql.1/src/include/storage/proc.h postgresql.2/src/include/storage/proc.h
--- postgresql.1/src/include/storage/proc.h 2012-07-22 16:48:48.548857900 +0200
+++ postgresql.2/src/include/storage/proc.h 2012-07-22 21:39:00.867925001 +0200
@@ -219,8 +219,15 @@ extern PGPROC *PreparedXactProcs;
/* configurable options */
extern int DeadlockTimeout;
extern int StatementTimeout;
+extern int LockTimeout;
+extern int LockTimeoutOption;
extern bool log_lock_waits;
+typedef enum LockTimeoutOptions {
+ LOCK_TIMEOUT_PER_LOCK,
+ LOCK_TIMEOUT_PER_STMT
+} LockTimeoutOptions;
+
/*
* Function Prototypes
diff -durpN postgresql.1/src/include/utils/timeout.h postgresql.2/src/include/utils/timeout.h
--- postgresql.1/src/include/utils/timeout.h 2012-07-22 21:34:50.480375707 +0200
+++ postgresql.2/src/include/utils/timeout.h 2012-07-22 21:39:00.867925001 +0200
@@ -25,6 +25,7 @@ typedef enum TimeoutId
/* Predefined timeout reasons */
STARTUP_PACKET_TIMEOUT,
DEADLOCK_TIMEOUT,
+ LOCK_TIMEOUT,
STATEMENT_TIMEOUT,
STANDBY_DEADLOCK_TIMEOUT,
STANDBY_TIMEOUT,
diff -durpN postgresql.1/src/test/regress/expected/prepared_xacts.out postgresql.2/src/test/regress/expected/prepared_xacts.out
--- postgresql.1/src/test/regress/expected/prepared_xacts.out 2012-04-16 19:57:22.776919413 +0200
+++ postgresql.2/src/test/regress/expected/prepared_xacts.out 2012-07-22 21:39:00.868925007 +0200
@@ -198,6 +198,22 @@ set statement_timeout to 2000;
SELECT * FROM pxtest3;
ERROR: canceling statement due to statement timeout
reset statement_timeout;
+-- pxtest3 should be locked because of the pending DROP
+set lock_timeout to 2000;
+SELECT * FROM pxtest3;
+ERROR: canceling statement due to lock timeout
+reset lock_timeout;
+-- Test lock_timeout_option = 'per_statement' and see that lock_timeout
+-- triggers instead of statement_timeout if both are set.
+-- pxtest3 should be locked because of the pending DROP
+set statement_timeout to 2000;
+set lock_timeout to 2000;
+set lock_timeout_option to 'per_statement';
+SELECT * FROM pxtest3;
+ERROR: canceling statement due to lock timeout
+reset lock_timeout;
+reset statement_timeout;
+reset lock_timeout_option;
-- Disconnect, we will continue testing in a different backend
\c -
-- There should still be two prepared transactions
@@ -213,6 +229,11 @@ set statement_timeout to 2000;
SELECT * FROM pxtest3;
ERROR: canceling statement due to statement timeout
reset statement_timeout;
+-- pxtest3 should be locked because of the pending DROP
+set lock_timeout to 2000;
+SELECT * FROM pxtest3;
+ERROR: canceling statement due to lock timeout
+reset lock_timeout;
-- Commit table creation
COMMIT PREPARED 'regress-one';
\d pxtest2
diff -durpN postgresql.1/src/test/regress/sql/prepared_xacts.sql postgresql.2/src/test/regress/sql/prepared_xacts.sql
--- postgresql.1/src/test/regress/sql/prepared_xacts.sql 2012-04-16 19:57:22.796919644 +0200
+++ postgresql.2/src/test/regress/sql/prepared_xacts.sql 2012-07-22 21:39:00.868925007 +0200
@@ -126,6 +126,22 @@ set statement_timeout to 2000;
SELECT * FROM pxtest3;
reset statement_timeout;
+-- pxtest3 should be locked because of the pending DROP
+set lock_timeout to 2000;
+SELECT * FROM pxtest3;
+reset lock_timeout;
+
+-- Test lock_timeout_option = 'per_statement' and see that lock_timeout
+-- triggers instead of statement_timeout if both are set.
+-- pxtest3 should be locked because of the pending DROP
+set statement_timeout to 2000;
+set lock_timeout to 2000;
+set lock_timeout_option to 'per_statement';
+SELECT * FROM pxtest3;
+reset lock_timeout;
+reset statement_timeout;
+reset lock_timeout_option;
+
-- Disconnect, we will continue testing in a different backend
\c -
@@ -137,6 +153,11 @@ set statement_timeout to 2000;
SELECT * FROM pxtest3;
reset statement_timeout;
+-- pxtest3 should be locked because of the pending DROP
+set lock_timeout to 2000;
+SELECT * FROM pxtest3;
+reset lock_timeout;
+
-- Commit table creation
COMMIT PREPARED 'regress-one';
\d pxtest2