diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c index eece89aa21f08..44ee84ddb8f19 100644 --- a/src/backend/access/common/reloptions.c +++ b/src/backend/access/common/reloptions.c @@ -348,6 +348,24 @@ static relopt_int intRelOpts[] = }, -1, 0, 1024 }, + { + { + "max_pred_locks_per_relation", + "Maximum number of pages or rows that can be predicate-locked before locking the whole relation.", + RELOPT_KIND_HEAP, + AccessExclusiveLock, + }, + -1, 0, INT_MAX + }, + { + { + "max_pred_locks_per_page", + "Maximum number of rows on a single page that can be predicate-locked before locking the whole page.", + RELOPT_KIND_HEAP, + AccessExclusiveLock, + }, + -1, 0, INT_MAX + }, /* list terminator */ {{NULL}} @@ -1397,7 +1415,11 @@ default_reloptions(Datum reloptions, bool validate, relopt_kind kind) {"parallel_workers", RELOPT_TYPE_INT, offsetof(StdRdOptions, parallel_workers)}, {"vacuum_cleanup_index_scale_factor", RELOPT_TYPE_REAL, - offsetof(StdRdOptions, vacuum_cleanup_index_scale_factor)} + offsetof(StdRdOptions, vacuum_cleanup_index_scale_factor)}, + {"max_pred_locks_per_relation", RELOPT_TYPE_INT, + offsetof(StdRdOptions, max_predicate_locks_per_relation)}, + {"max_pred_locks_per_page", RELOPT_TYPE_INT, + offsetof(StdRdOptions, max_predicate_locks_per_page)} }; options = parseRelOptions(reloptions, validate, kind, &numoptions); diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c index e8390311d03fd..1b49b192ac2dd 100644 --- a/src/backend/storage/lmgr/predicate.c +++ b/src/backend/storage/lmgr/predicate.c @@ -443,8 +443,9 @@ static void RestoreScratchTarget(bool lockheld); static void RemoveTargetIfNoLongerUsed(PREDICATELOCKTARGET *target, uint32 targettaghash); static void DeleteChildTargetLocks(const PREDICATELOCKTARGETTAG *newtargettag); -static int MaxPredicateChildLocks(const PREDICATELOCKTARGETTAG *tag); -static bool CheckAndPromotePredicateLockRequest(const PREDICATELOCKTARGETTAG *reqtag); +static int MaxPredicateChildLocks(const PREDICATELOCKTARGETTAG *tag, Relation relation); +static bool CheckAndPromotePredicateLockRequest(const PREDICATELOCKTARGETTAG *reqtag, + Relation relation); static void DecrementParentLocks(const PREDICATELOCKTARGETTAG *targettag); static void CreatePredicateLock(const PREDICATELOCKTARGETTAG *targettag, uint32 targettaghash, @@ -453,7 +454,8 @@ static void DeleteLockTarget(PREDICATELOCKTARGET *target, uint32 targettaghash); static bool TransferPredicateLocksToNewTarget(PREDICATELOCKTARGETTAG oldtargettag, PREDICATELOCKTARGETTAG newtargettag, bool removeOld); -static void PredicateLockAcquire(const PREDICATELOCKTARGETTAG *targettag); +static void PredicateLockAcquire(const PREDICATELOCKTARGETTAG *targettag, + Relation relation); static void DropAllPredicateLocksFromTable(Relation relation, bool transfer); static void SetNewSxactGlobalXmin(void); @@ -2165,18 +2167,28 @@ DeleteChildTargetLocks(const PREDICATELOCKTARGETTAG *newtargettag) * tying up all predicate lock resources. */ static int -MaxPredicateChildLocks(const PREDICATELOCKTARGETTAG *tag) +MaxPredicateChildLocks(const PREDICATELOCKTARGETTAG *tag, Relation relation) { switch (GET_PREDICATELOCKTARGETTAG_TYPE(*tag)) { case PREDLOCKTAG_RELATION: + { + int rel_max_pred_locks = RelationGetMaxPredicateLocksPerRelation(relation, -1); + if (rel_max_pred_locks != -1) + return rel_max_pred_locks; 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: + { + int rel_max_pred_locks_per_page = RelationGetMaxPredicateLocksPerPage(relation, -1); + if (rel_max_pred_locks_per_page != -1) + return rel_max_pred_locks_per_page; return max_predicate_locks_per_page; + } case PREDLOCKTAG_TUPLE: @@ -2202,7 +2214,8 @@ MaxPredicateChildLocks(const PREDICATELOCKTARGETTAG *tag) * Returns true if a parent lock was acquired and false otherwise. */ static bool -CheckAndPromotePredicateLockRequest(const PREDICATELOCKTARGETTAG *reqtag) +CheckAndPromotePredicateLockRequest(const PREDICATELOCKTARGETTAG *reqtag, + Relation relation) { PREDICATELOCKTARGETTAG targettag, nexttag, @@ -2232,7 +2245,7 @@ CheckAndPromotePredicateLockRequest(const PREDICATELOCKTARGETTAG *reqtag) parentlock->childLocks++; if (parentlock->childLocks > - MaxPredicateChildLocks(&targettag)) + MaxPredicateChildLocks(&targettag, relation)) { /* * We should promote to this parent lock. Continue to check its @@ -2248,7 +2261,7 @@ CheckAndPromotePredicateLockRequest(const PREDICATELOCKTARGETTAG *reqtag) if (promote) { /* acquire coarsest ancestor eligible for promotion */ - PredicateLockAcquire(&promotiontag); + PredicateLockAcquire(&promotiontag, relation); return true; } else @@ -2390,7 +2403,7 @@ CreatePredicateLock(const PREDICATELOCKTARGETTAG *targettag, * any finer-grained locks covered by the new one. */ static void -PredicateLockAcquire(const PREDICATELOCKTARGETTAG *targettag) +PredicateLockAcquire(const PREDICATELOCKTARGETTAG *targettag, Relation relation) { uint32 targettaghash; bool found; @@ -2423,7 +2436,7 @@ PredicateLockAcquire(const PREDICATELOCKTARGETTAG *targettag) * coarser granularity, or whether there are finer-granularity locks to * clean up. */ - if (CheckAndPromotePredicateLockRequest(targettag)) + if (CheckAndPromotePredicateLockRequest(targettag, relation)) { /* * Lock request was promoted to a coarser-granularity lock, and that @@ -2459,7 +2472,7 @@ PredicateLockRelation(Relation relation, Snapshot snapshot) SET_PREDICATELOCKTARGETTAG_RELATION(tag, relation->rd_node.dbNode, relation->rd_id); - PredicateLockAcquire(&tag); + PredicateLockAcquire(&tag, relation); } /* @@ -2483,7 +2496,7 @@ PredicateLockPage(Relation relation, BlockNumber blkno, Snapshot snapshot) relation->rd_node.dbNode, relation->rd_id, blkno); - PredicateLockAcquire(&tag); + PredicateLockAcquire(&tag, relation); } /* @@ -2546,7 +2559,7 @@ PredicateLockTuple(Relation relation, HeapTuple tuple, Snapshot snapshot) relation->rd_id, ItemPointerGetBlockNumber(tid), ItemPointerGetOffsetNumber(tid)); - PredicateLockAcquire(&tag); + PredicateLockAcquire(&tag, relation); } diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index fa44b2820b52e..7d86a3c9d1b81 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -1911,6 +1911,8 @@ psql_completion(const char *text, int start, int end) "fillfactor", "parallel_workers", "log_autovacuum_min_duration", + "max_pred_locks_per_relation", + "max_pred_locks_per_page", "toast_tuple_target", "toast.autovacuum_enabled", "toast.autovacuum_freeze_max_age", diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index 2217081dcc35a..85ea34b5d2398 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -263,6 +263,8 @@ typedef struct StdRdOptions AutoVacOpts autovacuum; /* autovacuum-related options */ bool user_catalog_table; /* use as an additional catalog relation */ int parallel_workers; /* max number of parallel workers */ + int max_predicate_locks_per_relation; /* max number of predicate locks */ + int max_predicate_locks_per_page; /* max number of predicate locks per page */ } StdRdOptions; #define HEAP_MIN_FILLFACTOR 10 @@ -319,6 +321,25 @@ typedef struct StdRdOptions ((StdRdOptions *) (relation)->rd_options)->parallel_workers : (defaultpw)) +/* + * RelationGetMaxPredicateLocksPerRelation + * Returns the relation's max_predicate_locks_per_relation reloption setting. + * Note multiple eval of argument! + */ +#define RelationGetMaxPredicateLocksPerRelation(relation, defaultmpl) \ + ((relation)->rd_options ? \ + ((StdRdOptions *) (relation)->rd_options)->max_predicate_locks_per_relation : (defaultmpl)) + +/* + * RelationGetMaxPredicateLocksPerPage + * Returns the relation's max_predicate_locks_per_page reloption setting. + * Note multiple eval of argument! + */ +#define RelationGetMaxPredicateLocksPerPage(relation, defaultmplpp) \ + ((relation)->rd_options ? \ + ((StdRdOptions *) (relation)->rd_options)->max_predicate_locks_per_page : (defaultmplpp)) + + /* * ViewOptions * Contents of rd_options for views