diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 47b1192245..6bfb7bbc11 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -5911,6 +5911,27 @@ SET XML OPTION { DOCUMENT | CONTENT };
+
+ pending_list_cleanup_size (integer)
+
+ pending_list_cleanup_size> configuration parameter
+
+
+
+
+ Sets the maximum size of the GIN pending list which is used
+ when fastupdate> is enabled. If the list grows
+ larger than this maximum size, it is cleaned up by moving
+ the entries in it to the main GIN data structure in bulk.
+ The default is four megabytes (4MB>). This setting
+ can be overridden for individual GIN indexes by changing
+ storage parameters.
+ See and
+ for more information.
+
+
+
+
diff --git a/doc/src/sgml/gin.sgml b/doc/src/sgml/gin.sgml
index 8443c01f6f..94d2d5c19d 100644
--- a/doc/src/sgml/gin.sgml
+++ b/doc/src/sgml/gin.sgml
@@ -728,8 +728,8 @@
from the indexed item). As of PostgreSQL 8.4,
GIN> is capable of postponing much of this work by inserting
new tuples into a temporary, unsorted list of pending entries.
- When the table is vacuumed, or if the pending list becomes too large
- (larger than ), the entries are moved to the
+ When the table is vacuumed, or if the pending list becomes larger than
+ , the entries are moved to the
main GIN data structure using the same bulk insert
techniques used during initial index creation. This greatly improves
GIN index update speed, even counting the additional
@@ -750,7 +750,7 @@
If consistent response time is more important than update speed,
use of pending entries can be disabled by turning off the
- FASTUPDATE storage parameter for a
+ fastupdate storage parameter for a
GIN index. See
for details.
@@ -812,18 +812,26 @@
-
+
During a series of insertions into an existing GIN
- index that has FASTUPDATE> enabled, the system will clean up
+ index that has fastupdate> enabled, the system will clean up
the pending-entry list whenever the list grows larger than
- work_mem>. To avoid fluctuations in observed response time,
- it's desirable to have pending-list cleanup occur in the background
- (i.e., via autovacuum). Foreground cleanup operations can be avoided by
- increasing work_mem> or making autovacuum more aggressive.
- However, enlarging work_mem> means that if a foreground
- cleanup does occur, it will take even longer.
+ pending_list_cleanup_size>. To avoid fluctuations in observed
+ response time, it's desirable to have pending-list cleanup occur in the
+ background (i.e., via autovacuum). Foreground cleanup operations
+ can be avoided by increasing pending_list_cleanup_size>
+ or making autovacuum more aggressive.
+ However, enlarging the threshold of the cleanup operation means that
+ if a foreground cleanup does occur, it will take even longer.
+
+
+ pending_list_cleanup_size> can be overridden for individual
+ GIN indexes by changing storage parameters, and which allows each
+ GIN index to have its own cleanup threshold.
+ For example, it's possible to increase the threshold only for the GIN
+ index which can be updated heavily, and decrease it otherwise.
diff --git a/doc/src/sgml/gist.sgml b/doc/src/sgml/gist.sgml
index 0158b1759e..5de282b294 100644
--- a/doc/src/sgml/gist.sgml
+++ b/doc/src/sgml/gist.sgml
@@ -861,7 +861,7 @@ my_distance(PG_FUNCTION_ARGS)
By default, a GiST index build switches to the buffering method when the
index size reaches . It can
- be manually turned on or off by the BUFFERING parameter
+ be manually turned on or off by the buffering parameter
to the CREATE INDEX command. The default behavior is good for most cases,
but turning buffering off might speed up the build somewhat if the input
data is ordered.
diff --git a/doc/src/sgml/ref/cluster.sgml b/doc/src/sgml/ref/cluster.sgml
index 2ab090d03e..e6a77095ec 100644
--- a/doc/src/sgml/ref/cluster.sgml
+++ b/doc/src/sgml/ref/cluster.sgml
@@ -46,7 +46,7 @@ CLUSTER [VERBOSE]
not clustered. That is, no attempt is made to store new or
updated rows according to their index order. (If one wishes, one can
periodically recluster by issuing the command again. Also, setting
- the table's FILLFACTOR storage parameter to less than
+ the table's fillfactor storage parameter to less than
100% can aid in preserving cluster ordering during updates, since updated
rows are kept on the same page if enough space is available there.)
diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml
index 18bd0d3370..21f7604ac0 100644
--- a/doc/src/sgml/ref/create_index.sgml
+++ b/doc/src/sgml/ref/create_index.sgml
@@ -300,7 +300,7 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ]
- FILLFACTOR>
+ fillfactor>
The fillfactor for an index is a percentage that determines how full
@@ -327,7 +327,7 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ]
- BUFFERING>
+ buffering>
Determines whether the buffering build technique described in
@@ -341,12 +341,12 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ]
- GIN indexes accept a different parameter:
+ GIN indexes accept different parameters:
- FASTUPDATE>
+ fastupdate>
This setting controls usage of the fast update technique described in
@@ -359,7 +359,7 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ]
- Turning FASTUPDATE> off via ALTER INDEX> prevents
+ Turning fastupdate> off via ALTER INDEX> prevents
future insertions from going into the list of pending index entries,
but does not in itself flush previous entries. You might want to
VACUUM> the table afterward to ensure the pending list is
@@ -369,6 +369,17 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ]
+
+
+ pending_list_cleanup_size>
+
+
+ Custom parameter.
+ This value is specified in kilobytes.
+
+
+
+
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index c55a775827..86d918fafb 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -216,6 +216,14 @@ static relopt_int intRelOpts[] =
RELOPT_KIND_BRIN
}, 128, 1, 131072
},
+ {
+ {
+ "pending_list_cleanup_size",
+ "Maximum size of the pending list for this GIN index, in kilobytes.",
+ RELOPT_KIND_GIN
+ },
+ -1, 64, MAX_KILOBYTES
+ },
/* list terminator */
{{NULL}}
diff --git a/src/backend/access/gin/ginfast.c b/src/backend/access/gin/ginfast.c
index ed581977f5..96255104ac 100644
--- a/src/backend/access/gin/ginfast.c
+++ b/src/backend/access/gin/ginfast.c
@@ -25,6 +25,8 @@
#include "utils/memutils.h"
#include "utils/rel.h"
+/* GUC parameter */
+int pending_list_cleanup_size = 0;
#define GIN_PAGE_FREESIZE \
( BLCKSZ - MAXALIGN(SizeOfPageHeaderData) - MAXALIGN(sizeof(GinPageOpaqueData)) )
@@ -228,6 +230,7 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector)
ginxlogUpdateMeta data;
bool separateList = false;
bool needCleanup = false;
+ int cleanupSize;
if (collector->ntuples == 0)
return;
@@ -422,11 +425,13 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector)
* ginInsertCleanup could take significant amount of time, so we prefer to
* call it when it can do all the work in a single collection cycle. In
* non-vacuum mode, it shouldn't require maintenance_work_mem, so fire it
- * while pending list is still small enough to fit into work_mem.
+ * while pending list is still small enough to fit into
+ * pending_list_cleanup_size.
*
* ginInsertCleanup() should not be called inside our CRIT_SECTION.
*/
- if (metadata->nPendingPages * GIN_PAGE_FREESIZE > work_mem * 1024L)
+ cleanupSize = GinGetPendingListCleanupSize(index);
+ if (metadata->nPendingPages * GIN_PAGE_FREESIZE > cleanupSize * 1024L)
needCleanup = true;
UnlockReleaseBuffer(metabuffer);
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index 1f8db9de6d..ff1dd7ee51 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -525,7 +525,9 @@ ginoptions(PG_FUNCTION_ARGS)
GinOptions *rdopts;
int numoptions;
static const relopt_parse_elt tab[] = {
- {"fastupdate", RELOPT_TYPE_BOOL, offsetof(GinOptions, useFastUpdate)}
+ {"fastupdate", RELOPT_TYPE_BOOL, offsetof(GinOptions, useFastUpdate)},
+ {"pending_list_cleanup_size", RELOPT_TYPE_INT, offsetof(GinOptions,
+ pendingListCleanupSize)}
};
options = parseRelOptions(reloptions, validate, RELOPT_KIND_GIN,
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index aca42436d3..6547dfab69 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -96,14 +96,6 @@
#define CONFIG_EXEC_PARAMS_NEW "global/config_exec_params.new"
#endif
-/* upper limit for GUC variables measured in kilobytes of memory */
-/* note that various places assume the byte size fits in a "long" variable */
-#if SIZEOF_SIZE_T > 4 && SIZEOF_LONG > 4
-#define MAX_KILOBYTES INT_MAX
-#else
-#define MAX_KILOBYTES (INT_MAX / 1024)
-#endif
-
#define KB_PER_MB (1024)
#define KB_PER_GB (1024*1024)
#define KB_PER_TB (1024*1024*1024)
@@ -2550,6 +2542,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"pending_list_cleanup_size", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Sets the maximum size of the pending list for GIN index."),
+ NULL,
+ GUC_UNIT_KB
+ },
+ &pending_list_cleanup_size,
+ 4096, 64, MAX_KILOBYTES,
+ NULL, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index dac67769f1..6e8ea1e481 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -519,6 +519,7 @@
#bytea_output = 'hex' # hex, escape
#xmlbinary = 'base64'
#xmloption = 'content'
+#pending_list_cleanup_size = 4MB
# - Locale and Formatting -
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 56dc688fcb..be6ad7ea64 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1172,7 +1172,7 @@ psql_completion(const char *text, int start, int end)
pg_strcasecmp(prev_wd, "(") == 0)
{
static const char *const list_INDEXOPTIONS[] =
- {"fillfactor", "fastupdate", NULL};
+ {"fillfactor", "fastupdate", "pending_list_cleanup_size", NULL};
COMPLETE_WITH_LIST(list_INDEXOPTIONS);
}
diff --git a/src/include/access/gin.h b/src/include/access/gin.h
index 80826b843b..a0d4da84b0 100644
--- a/src/include/access/gin.h
+++ b/src/include/access/gin.h
@@ -65,8 +65,9 @@ typedef char GinTernaryValue;
#define GinTernaryValueGetDatum(X) ((Datum)(X))
#define PG_RETURN_GIN_TERNARY_VALUE(x) return GinTernaryValueGetDatum(x)
-/* GUC parameter */
+/* GUC parameters */
extern PGDLLIMPORT int GinFuzzySearchLimit;
+extern int pending_list_cleanup_size;
/* ginutil.c */
extern void ginGetStats(Relation index, GinStatsData *stats);
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index 6a09dc990e..4a8db5a500 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -315,12 +315,18 @@ typedef struct GinOptions
{
int32 vl_len_; /* varlena header (do not touch directly!) */
bool useFastUpdate; /* use fast updates? */
+ int pendingListCleanupSize; /* maximum size of pending list */
} GinOptions;
#define GIN_DEFAULT_USE_FASTUPDATE true
#define GinGetUseFastUpdate(relation) \
((relation)->rd_options ? \
((GinOptions *) (relation)->rd_options)->useFastUpdate : GIN_DEFAULT_USE_FASTUPDATE)
+#define GinGetPendingListCleanupSize(relation) \
+ ((relation)->rd_options && \
+ ((GinOptions *) (relation)->rd_options)->pendingListCleanupSize != -1 ? \
+ ((GinOptions *) (relation)->rd_options)->pendingListCleanupSize : \
+ pending_list_cleanup_size)
/* Macros for buffer lock/unlock operations */
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index 66b5cd36c5..2b2aaf4ac2 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -18,6 +18,14 @@
#include "utils/array.h"
+/* upper limit for GUC variables measured in kilobytes of memory */
+/* note that various places assume the byte size fits in a "long" variable */
+#if SIZEOF_SIZE_T > 4 && SIZEOF_LONG > 4
+#define MAX_KILOBYTES INT_MAX
+#else
+#define MAX_KILOBYTES (INT_MAX / 1024)
+#endif
+
/*
* Certain options can only be set at certain times. The rules are
* like this:
diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out
index 26d883c447..45689d9950 100644
--- a/src/test/regress/expected/create_index.out
+++ b/src/test/regress/expected/create_index.out
@@ -2240,6 +2240,19 @@ SELECT COUNT(*) FROM array_gin_test WHERE a @> '{2}';
(1 row)
DROP TABLE array_gin_test;
+--
+-- Test GIN index's reloptions
+--
+CREATE INDEX gin_relopts_test ON array_index_op_test USING gin (i)
+ WITH (FASTUPDATE=on, PENDING_LIST_CLEANUP_SIZE=128);
+\d+ gin_relopts_test
+ Index "public.gin_relopts_test"
+ Column | Type | Definition | Storage
+--------+---------+------------+---------
+ i | integer | i | plain
+gin, for table "public.array_index_op_test"
+Options: fastupdate=on, pending_list_cleanup_size=128
+
--
-- HASH
--
diff --git a/src/test/regress/sql/create_index.sql b/src/test/regress/sql/create_index.sql
index e08f35ebcf..619558551e 100644
--- a/src/test/regress/sql/create_index.sql
+++ b/src/test/regress/sql/create_index.sql
@@ -654,6 +654,13 @@ SELECT COUNT(*) FROM array_gin_test WHERE a @> '{2}';
DROP TABLE array_gin_test;
+--
+-- Test GIN index's reloptions
+--
+CREATE INDEX gin_relopts_test ON array_index_op_test USING gin (i)
+ WITH (FASTUPDATE=on, PENDING_LIST_CLEANUP_SIZE=128);
+\d+ gin_relopts_test
+
--
-- HASH
--