Commit a1b395b6 authored by Fujii Masao's avatar Fujii Masao

Add GUC and storage parameter to set the maximum size of GIN pending list.

Previously the maximum size of GIN pending list was controlled only by
work_mem. But the reasonable value of work_mem and the reasonable size
of the list are basically not the same, so it was not appropriate to
control both of them by only one GUC, i.e., work_mem. This commit
separates new GUC, pending_list_cleanup_size, from work_mem to allow
users to control only the size of the list.

Also this commit adds pending_list_cleanup_size as new storage parameter
to allow users to specify the size of the list per index. This is useful,
for example, when users want to increase the size of the list only for
the GIN index which can be updated heavily, and decrease it otherwise.

Reviewed by Etsuro Fujita.
parent ae667f77
...@@ -5911,6 +5911,27 @@ SET XML OPTION { DOCUMENT | CONTENT }; ...@@ -5911,6 +5911,27 @@ SET XML OPTION { DOCUMENT | CONTENT };
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry id="guc-pending-list-cleanup-size" xreflabel="pending_list_cleanup_size">
<term><varname>pending_list_cleanup_size</varname> (<type>integer</type>)
<indexterm>
<primary><varname>pending_list_cleanup_size</> configuration parameter</primary>
</indexterm>
</term>
<listitem>
<para>
Sets the maximum size of the GIN pending list which is used
when <literal>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 (<literal>4MB</>). This setting
can be overridden for individual GIN indexes by changing
storage parameters.
See <xref linkend="gin-fast-update"> and <xref linkend="gin-tips">
for more information.
</para>
</listitem>
</varlistentry>
</variablelist> </variablelist>
</sect2> </sect2>
<sect2 id="runtime-config-client-format"> <sect2 id="runtime-config-client-format">
......
...@@ -728,8 +728,8 @@ ...@@ -728,8 +728,8 @@
from the indexed item). As of <productname>PostgreSQL</productname> 8.4, from the indexed item). As of <productname>PostgreSQL</productname> 8.4,
<acronym>GIN</> is capable of postponing much of this work by inserting <acronym>GIN</> is capable of postponing much of this work by inserting
new tuples into a temporary, unsorted list of pending entries. new tuples into a temporary, unsorted list of pending entries.
When the table is vacuumed, or if the pending list becomes too large When the table is vacuumed, or if the pending list becomes larger than
(larger than <xref linkend="guc-work-mem">), the entries are moved to the <xref linkend="guc-pending-list-cleanup-size">, the entries are moved to the
main <acronym>GIN</acronym> data structure using the same bulk insert main <acronym>GIN</acronym> data structure using the same bulk insert
techniques used during initial index creation. This greatly improves techniques used during initial index creation. This greatly improves
<acronym>GIN</acronym> index update speed, even counting the additional <acronym>GIN</acronym> index update speed, even counting the additional
...@@ -750,7 +750,7 @@ ...@@ -750,7 +750,7 @@
<para> <para>
If consistent response time is more important than update speed, If consistent response time is more important than update speed,
use of pending entries can be disabled by turning off the use of pending entries can be disabled by turning off the
<literal>FASTUPDATE</literal> storage parameter for a <literal>fastupdate</literal> storage parameter for a
<acronym>GIN</acronym> index. See <xref linkend="sql-createindex"> <acronym>GIN</acronym> index. See <xref linkend="sql-createindex">
for details. for details.
</para> </para>
...@@ -812,18 +812,26 @@ ...@@ -812,18 +812,26 @@
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><xref linkend="guc-work-mem"></term> <term><xref linkend="guc-pending-list-cleanup-size"></term>
<listitem> <listitem>
<para> <para>
During a series of insertions into an existing <acronym>GIN</acronym> During a series of insertions into an existing <acronym>GIN</acronym>
index that has <literal>FASTUPDATE</> enabled, the system will clean up index that has <literal>fastupdate</> enabled, the system will clean up
the pending-entry list whenever the list grows larger than the pending-entry list whenever the list grows larger than
<varname>work_mem</>. To avoid fluctuations in observed response time, <varname>pending_list_cleanup_size</>. To avoid fluctuations in observed
it's desirable to have pending-list cleanup occur in the background response time, it's desirable to have pending-list cleanup occur in the
(i.e., via autovacuum). Foreground cleanup operations can be avoided by background (i.e., via autovacuum). Foreground cleanup operations
increasing <varname>work_mem</> or making autovacuum more aggressive. can be avoided by increasing <varname>pending_list_cleanup_size</>
However, enlarging <varname>work_mem</> means that if a foreground or making autovacuum more aggressive.
cleanup does occur, it will take even longer. However, enlarging the threshold of the cleanup operation means that
if a foreground cleanup does occur, it will take even longer.
</para>
<para>
<varname>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.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
......
...@@ -861,7 +861,7 @@ my_distance(PG_FUNCTION_ARGS) ...@@ -861,7 +861,7 @@ my_distance(PG_FUNCTION_ARGS)
<para> <para>
By default, a GiST index build switches to the buffering method when the By default, a GiST index build switches to the buffering method when the
index size reaches <xref linkend="guc-effective-cache-size">. It can index size reaches <xref linkend="guc-effective-cache-size">. It can
be manually turned on or off by the <literal>BUFFERING</literal> parameter be manually turned on or off by the <literal>buffering</literal> parameter
to the CREATE INDEX command. The default behavior is good for most cases, 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 but turning buffering off might speed up the build somewhat if the input
data is ordered. data is ordered.
......
...@@ -46,7 +46,7 @@ CLUSTER [VERBOSE] ...@@ -46,7 +46,7 @@ CLUSTER [VERBOSE]
not clustered. That is, no attempt is made to store new or not clustered. That is, no attempt is made to store new or
updated rows according to their index order. (If one wishes, one can updated rows according to their index order. (If one wishes, one can
periodically recluster by issuing the command again. Also, setting periodically recluster by issuing the command again. Also, setting
the table's <literal>FILLFACTOR</literal> storage parameter to less than the table's <literal>fillfactor</literal> storage parameter to less than
100% can aid in preserving cluster ordering during updates, since updated 100% can aid in preserving cluster ordering during updates, since updated
rows are kept on the same page if enough space is available there.) rows are kept on the same page if enough space is available there.)
</para> </para>
......
...@@ -300,7 +300,7 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= ...@@ -300,7 +300,7 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class=
<variablelist> <variablelist>
<varlistentry> <varlistentry>
<term><literal>FILLFACTOR</></term> <term><literal>fillfactor</></term>
<listitem> <listitem>
<para> <para>
The fillfactor for an index is a percentage that determines how full The fillfactor for an index is a percentage that determines how full
...@@ -327,7 +327,7 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= ...@@ -327,7 +327,7 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class=
<variablelist> <variablelist>
<varlistentry> <varlistentry>
<term><literal>BUFFERING</></term> <term><literal>buffering</></term>
<listitem> <listitem>
<para> <para>
Determines whether the buffering build technique described in Determines whether the buffering build technique described in
...@@ -341,12 +341,12 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= ...@@ -341,12 +341,12 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class=
</variablelist> </variablelist>
<para> <para>
GIN indexes accept a different parameter: GIN indexes accept different parameters:
</para> </para>
<variablelist> <variablelist>
<varlistentry> <varlistentry>
<term><literal>FASTUPDATE</></term> <term><literal>fastupdate</></term>
<listitem> <listitem>
<para> <para>
This setting controls usage of the fast update technique described in This setting controls usage of the fast update technique described in
...@@ -359,7 +359,7 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= ...@@ -359,7 +359,7 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class=
<note> <note>
<para> <para>
Turning <literal>FASTUPDATE</> off via <command>ALTER INDEX</> prevents Turning <literal>fastupdate</> off via <command>ALTER INDEX</> prevents
future insertions from going into the list of pending index entries, future insertions from going into the list of pending index entries,
but does not in itself flush previous entries. You might want to but does not in itself flush previous entries. You might want to
<command>VACUUM</> the table afterward to ensure the pending list is <command>VACUUM</> the table afterward to ensure the pending list is
...@@ -369,6 +369,17 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= ...@@ -369,6 +369,17 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class=
</listitem> </listitem>
</varlistentry> </varlistentry>
</variablelist> </variablelist>
<variablelist>
<varlistentry>
<term><literal>pending_list_cleanup_size</></term>
<listitem>
<para>
Custom <xref linkend="guc-pending-list-cleanup-size"> parameter.
This value is specified in kilobytes.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect2> </refsect2>
<refsect2 id="SQL-CREATEINDEX-CONCURRENTLY"> <refsect2 id="SQL-CREATEINDEX-CONCURRENTLY">
......
...@@ -216,6 +216,14 @@ static relopt_int intRelOpts[] = ...@@ -216,6 +216,14 @@ static relopt_int intRelOpts[] =
RELOPT_KIND_BRIN RELOPT_KIND_BRIN
}, 128, 1, 131072 }, 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 */ /* list terminator */
{{NULL}} {{NULL}}
......
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
#include "utils/memutils.h" #include "utils/memutils.h"
#include "utils/rel.h" #include "utils/rel.h"
/* GUC parameter */
int pending_list_cleanup_size = 0;
#define GIN_PAGE_FREESIZE \ #define GIN_PAGE_FREESIZE \
( BLCKSZ - MAXALIGN(SizeOfPageHeaderData) - MAXALIGN(sizeof(GinPageOpaqueData)) ) ( BLCKSZ - MAXALIGN(SizeOfPageHeaderData) - MAXALIGN(sizeof(GinPageOpaqueData)) )
...@@ -228,6 +230,7 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector) ...@@ -228,6 +230,7 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector)
ginxlogUpdateMeta data; ginxlogUpdateMeta data;
bool separateList = false; bool separateList = false;
bool needCleanup = false; bool needCleanup = false;
int cleanupSize;
if (collector->ntuples == 0) if (collector->ntuples == 0)
return; return;
...@@ -422,11 +425,13 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector) ...@@ -422,11 +425,13 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector)
* ginInsertCleanup could take significant amount of time, so we prefer to * 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 * 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 * 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. * 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; needCleanup = true;
UnlockReleaseBuffer(metabuffer); UnlockReleaseBuffer(metabuffer);
......
...@@ -525,7 +525,9 @@ ginoptions(PG_FUNCTION_ARGS) ...@@ -525,7 +525,9 @@ ginoptions(PG_FUNCTION_ARGS)
GinOptions *rdopts; GinOptions *rdopts;
int numoptions; int numoptions;
static const relopt_parse_elt tab[] = { 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, options = parseRelOptions(reloptions, validate, RELOPT_KIND_GIN,
......
...@@ -96,14 +96,6 @@ ...@@ -96,14 +96,6 @@
#define CONFIG_EXEC_PARAMS_NEW "global/config_exec_params.new" #define CONFIG_EXEC_PARAMS_NEW "global/config_exec_params.new"
#endif #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_MB (1024)
#define KB_PER_GB (1024*1024) #define KB_PER_GB (1024*1024)
#define KB_PER_TB (1024*1024*1024) #define KB_PER_TB (1024*1024*1024)
...@@ -2550,6 +2542,17 @@ static struct config_int ConfigureNamesInt[] = ...@@ -2550,6 +2542,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL 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 */ /* End-of-list marker */
{ {
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL {NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
......
...@@ -519,6 +519,7 @@ ...@@ -519,6 +519,7 @@
#bytea_output = 'hex' # hex, escape #bytea_output = 'hex' # hex, escape
#xmlbinary = 'base64' #xmlbinary = 'base64'
#xmloption = 'content' #xmloption = 'content'
#pending_list_cleanup_size = 4MB
# - Locale and Formatting - # - Locale and Formatting -
......
...@@ -1172,7 +1172,7 @@ psql_completion(const char *text, int start, int end) ...@@ -1172,7 +1172,7 @@ psql_completion(const char *text, int start, int end)
pg_strcasecmp(prev_wd, "(") == 0) pg_strcasecmp(prev_wd, "(") == 0)
{ {
static const char *const list_INDEXOPTIONS[] = static const char *const list_INDEXOPTIONS[] =
{"fillfactor", "fastupdate", NULL}; {"fillfactor", "fastupdate", "pending_list_cleanup_size", NULL};
COMPLETE_WITH_LIST(list_INDEXOPTIONS); COMPLETE_WITH_LIST(list_INDEXOPTIONS);
} }
......
...@@ -65,8 +65,9 @@ typedef char GinTernaryValue; ...@@ -65,8 +65,9 @@ typedef char GinTernaryValue;
#define GinTernaryValueGetDatum(X) ((Datum)(X)) #define GinTernaryValueGetDatum(X) ((Datum)(X))
#define PG_RETURN_GIN_TERNARY_VALUE(x) return GinTernaryValueGetDatum(x) #define PG_RETURN_GIN_TERNARY_VALUE(x) return GinTernaryValueGetDatum(x)
/* GUC parameter */ /* GUC parameters */
extern PGDLLIMPORT int GinFuzzySearchLimit; extern PGDLLIMPORT int GinFuzzySearchLimit;
extern int pending_list_cleanup_size;
/* ginutil.c */ /* ginutil.c */
extern void ginGetStats(Relation index, GinStatsData *stats); extern void ginGetStats(Relation index, GinStatsData *stats);
......
...@@ -315,12 +315,18 @@ typedef struct GinOptions ...@@ -315,12 +315,18 @@ typedef struct GinOptions
{ {
int32 vl_len_; /* varlena header (do not touch directly!) */ int32 vl_len_; /* varlena header (do not touch directly!) */
bool useFastUpdate; /* use fast updates? */ bool useFastUpdate; /* use fast updates? */
int pendingListCleanupSize; /* maximum size of pending list */
} GinOptions; } GinOptions;
#define GIN_DEFAULT_USE_FASTUPDATE true #define GIN_DEFAULT_USE_FASTUPDATE true
#define GinGetUseFastUpdate(relation) \ #define GinGetUseFastUpdate(relation) \
((relation)->rd_options ? \ ((relation)->rd_options ? \
((GinOptions *) (relation)->rd_options)->useFastUpdate : GIN_DEFAULT_USE_FASTUPDATE) ((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 */ /* Macros for buffer lock/unlock operations */
......
...@@ -18,6 +18,14 @@ ...@@ -18,6 +18,14 @@
#include "utils/array.h" #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 * Certain options can only be set at certain times. The rules are
* like this: * like this:
......
...@@ -2240,6 +2240,19 @@ SELECT COUNT(*) FROM array_gin_test WHERE a @> '{2}'; ...@@ -2240,6 +2240,19 @@ SELECT COUNT(*) FROM array_gin_test WHERE a @> '{2}';
(1 row) (1 row)
DROP TABLE array_gin_test; 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 -- HASH
-- --
......
...@@ -654,6 +654,13 @@ SELECT COUNT(*) FROM array_gin_test WHERE a @> '{2}'; ...@@ -654,6 +654,13 @@ SELECT COUNT(*) FROM array_gin_test WHERE a @> '{2}';
DROP TABLE array_gin_test; 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 -- HASH
-- --
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment