Add GUCs for predicate lock promotion thresholds.

Defaults match the fixed behavior of prior releases, but now DBAs
have better options to tune serializable workloads.

It might be nice to be able to set this per relation, but that part
will need to wait for another release.

Author: Dagfinn Ilmari Mannsåker
This commit is contained in:
Kevin Grittner 2017-04-07 21:38:05 -05:00
parent 9c7f5229ad
commit c63172d60f
6 changed files with 98 additions and 18 deletions

View File

@ -7337,7 +7337,43 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir'
tables in a single serializable transaction. This parameter can tables in a single serializable transaction. This parameter can
only be set at server start. only be set at server start.
</para> </para>
</listitem>
</varlistentry>
<varlistentry id="guc-max-pred-locks-per-relation" xreflabel="max_pred_locks_per_relation">
<term><varname>max_pred_locks_per_relation</varname> (<type>integer</type>)
<indexterm>
<primary><varname>max_pred_locks_per_relation</> configuration parameter</primary>
</indexterm>
</term>
<listitem>
<para>
This controls how many pages or tuples of a single relation can be
predicate-locked before the lock is promoted to covering the whole
relation. Values greater than or equal to zero mean an absolute
limit, while negative values
mean <xref linkend="guc-max-pred-locks-per-transaction"> divided by
the absolute value of this setting. The default is -2, which keeps
the behaviour from previous versions of <productname>PostgreSQL</>.
This parameter can only be set in the <filename>postgresql.conf</>
file or on the server command line.
</para>
</listitem>
</varlistentry>
<varlistentry id="guc-max-pred-locks-per-page" xreflabel="max_pred_locks_per_page">
<term><varname>max_pred_locks_per_page</varname> (<type>integer</type>)
<indexterm>
<primary><varname>max_pred_locks_per_page</> configuration parameter</primary>
</indexterm>
</term>
<listitem>
<para>
This controls how many rows on a single page can be predicate-locked
before the lock is promoted to covering the whole page. The default
is 2. This parameter can only be set in
the <filename>postgresql.conf</> file or on the server command line.
</para>
</listitem> </listitem>
</varlistentry> </varlistentry>

View File

@ -765,7 +765,9 @@ ERROR: could not serialize access due to read/write dependencies among transact
locks into a single relation-level predicate lock because the predicate locks into a single relation-level predicate lock because the predicate
lock table is short of memory, an increase in the rate of serialization lock table is short of memory, an increase in the rate of serialization
failures may occur. You can avoid this by increasing failures may occur. You can avoid this by increasing
<xref linkend="guc-max-pred-locks-per-transaction">. <xref linkend="guc-max-pred-locks-per-transaction">,
<xref linkend="guc-max-pred-locks-per-relation">, and/or
<xref linkend="guc-max-pred-locks-per-page">.
</para> </para>
</listitem> </listitem>
<listitem> <listitem>

View File

@ -352,8 +352,15 @@ static OldSerXidControl oldSerXidControl;
static SERIALIZABLEXACT *OldCommittedSxact; static SERIALIZABLEXACT *OldCommittedSxact;
/* This configuration variable is used to set the predicate lock table size */ /*
* These configuration variables are used to set the predicate lock table size
* and to control promotion of predicate locks to coarser granularity in an
* attempt to degrade performance (mostly as false positive serialization
* failure) gracefully in the face of memory pressurel
*/
int max_predicate_locks_per_xact; /* set by guc.c */ int max_predicate_locks_per_xact; /* set by guc.c */
int max_predicate_locks_per_relation; /* set by guc.c */
int max_predicate_locks_per_page; /* set by guc.c */
/* /*
* This provides a list of objects in order to track transactions * This provides a list of objects in order to track transactions
@ -437,7 +444,7 @@ static void RestoreScratchTarget(bool lockheld);
static void RemoveTargetIfNoLongerUsed(PREDICATELOCKTARGET *target, static void RemoveTargetIfNoLongerUsed(PREDICATELOCKTARGET *target,
uint32 targettaghash); uint32 targettaghash);
static void DeleteChildTargetLocks(const PREDICATELOCKTARGETTAG *newtargettag); static void DeleteChildTargetLocks(const PREDICATELOCKTARGETTAG *newtargettag);
static int PredicateLockPromotionThreshold(const PREDICATELOCKTARGETTAG *tag); static int MaxPredicateChildLocks(const PREDICATELOCKTARGETTAG *tag);
static bool CheckAndPromotePredicateLockRequest(const PREDICATELOCKTARGETTAG *reqtag); static bool CheckAndPromotePredicateLockRequest(const PREDICATELOCKTARGETTAG *reqtag);
static void DecrementParentLocks(const PREDICATELOCKTARGETTAG *targettag); static void DecrementParentLocks(const PREDICATELOCKTARGETTAG *targettag);
static void CreatePredicateLock(const PREDICATELOCKTARGETTAG *targettag, static void CreatePredicateLock(const PREDICATELOCKTARGETTAG *targettag,
@ -2118,28 +2125,35 @@ DeleteChildTargetLocks(const PREDICATELOCKTARGETTAG *newtargettag)
} }
/* /*
* Returns the promotion threshold for a given predicate lock * Returns the promotion limit for a given predicate lock target. This is the
* target. This is the number of descendant locks required to promote * max number of descendant locks allowed before promoting to the specified
* to the specified tag. Note that the threshold includes non-direct * tag. Note that the limit includes non-direct descendants (e.g., both tuples
* descendants, e.g. both tuples and pages for a relation lock. * and pages for a relation lock).
* *
* TODO SSI: We should do something more intelligent about what the * Currently the default limit is 2 for a page lock, and half of the value of
* thresholds are, either making it proportional to the number of * max_pred_locks_per_transaction - 1 for a relation lock, to match behavior
* tuples in a page & pages in a relation, or at least making it a * of earlier releases when upgrading.
* GUC. Currently the threshold is 3 for a page lock, and *
* max_pred_locks_per_transaction/2 for a relation lock, chosen * TODO SSI: We should probably add additional GUCs to allow a maximum ratio
* entirely arbitrarily (and without benchmarking). * of page and tuple locks based on the pages in a relation, and the maximum
* ratio of tuple locks to tuples in a page. This would provide more
* generally "balanced" allocation of locks to where they are most useful,
* while still allowing the absolute numbers to prevent one relation from
* tying up all predicate lock resources.
*/ */
static int static int
PredicateLockPromotionThreshold(const PREDICATELOCKTARGETTAG *tag) MaxPredicateChildLocks(const PREDICATELOCKTARGETTAG *tag)
{ {
switch (GET_PREDICATELOCKTARGETTAG_TYPE(*tag)) switch (GET_PREDICATELOCKTARGETTAG_TYPE(*tag))
{ {
case PREDLOCKTAG_RELATION: case PREDLOCKTAG_RELATION:
return max_predicate_locks_per_xact / 2; return max_predicate_locks_per_relation < 0
? (max_predicate_locks_per_xact
/ (-max_predicate_locks_per_relation)) - 1
: max_predicate_locks_per_relation;
case PREDLOCKTAG_PAGE: case PREDLOCKTAG_PAGE:
return 3; return max_predicate_locks_per_page;
case PREDLOCKTAG_TUPLE: case PREDLOCKTAG_TUPLE:
@ -2194,8 +2208,8 @@ CheckAndPromotePredicateLockRequest(const PREDICATELOCKTARGETTAG *reqtag)
else else
parentlock->childLocks++; parentlock->childLocks++;
if (parentlock->childLocks >= if (parentlock->childLocks >
PredicateLockPromotionThreshold(&targettag)) MaxPredicateChildLocks(&targettag))
{ {
/* /*
* We should promote to this parent lock. Continue to check its * We should promote to this parent lock. Continue to check its

View File

@ -2199,6 +2199,28 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL NULL, NULL, NULL
}, },
{
{"max_pred_locks_per_relation", PGC_SIGHUP, LOCK_MANAGEMENT,
gettext_noop("Sets the maximum number of predicate-locked pages and tuples per relation."),
gettext_noop("If more than this total of pages and tuples in the same relation are locked "
"by a connection, those locks are replaced by a relation level lock.")
},
&max_predicate_locks_per_relation,
-2, INT_MIN, INT_MAX,
NULL, NULL, NULL
},
{
{"max_pred_locks_per_page", PGC_SIGHUP, LOCK_MANAGEMENT,
gettext_noop("Sets the maximum number of predicate-locked tuples per page."),
gettext_noop("If more than this number of tuples on the same page are locked "
"by a connection, those locks are replaced by a page level lock.")
},
&max_predicate_locks_per_page,
2, 0, INT_MAX,
NULL, NULL, NULL
},
{ {
{"authentication_timeout", PGC_SIGHUP, CONN_AUTH_SECURITY, {"authentication_timeout", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Sets the maximum allowed time to complete client authentication."), gettext_noop("Sets the maximum allowed time to complete client authentication."),

View File

@ -593,6 +593,10 @@
# (change requires restart) # (change requires restart)
#max_pred_locks_per_transaction = 64 # min 10 #max_pred_locks_per_transaction = 64 # min 10
# (change requires restart) # (change requires restart)
#max_pred_locks_per_relation = -2 # negative values mean
# (max_pred_locks_per_transaction
# / -max_pred_locks_per_relation) - 1
#max_pred_locks_per_page = 2 # min 0
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------

View File

@ -22,6 +22,8 @@
* GUC variables * GUC variables
*/ */
extern int max_predicate_locks_per_xact; extern int max_predicate_locks_per_xact;
extern int max_predicate_locks_per_relation;
extern int max_predicate_locks_per_page;
/* Number of SLRU buffers to use for predicate locking */ /* Number of SLRU buffers to use for predicate locking */