Commit 78c0b6ed authored by Tom Lane's avatar Tom Lane

Re-allow testing of GiST buffered builds.

Commit 16fa9b2b broke the ability to reliably test GiST buffered builds,
because it caused sorted builds to be done instead if sortsupport is
available, regardless of any attempt to override that.  While a would-be
test case could try to work around that by choosing an opclass that has
no sortsupport function, coverage would be silently lost the moment
someone decides it'd be a good idea to add a sortsupport function.

Hence, rearrange the logic in gistbuild() so that if "buffering = on"
is specified in CREATE INDEX, we will use that method, sortsupport or no.

Also document the interaction between sorting and the buffering
parameter, as 16fa9b2b failed to do.

(Note that in fact we still lack any test coverage of buffered builds,
but this is a prerequisite to adding a non-fragile test.)

Discussion: https://postgr.es/m/3249980.1602532990@sss.pgh.pa.us
parent 397ea901
...@@ -1157,23 +1157,38 @@ my_sortsupport(PG_FUNCTION_ARGS) ...@@ -1157,23 +1157,38 @@ my_sortsupport(PG_FUNCTION_ARGS)
<title>Implementation</title> <title>Implementation</title>
<sect2 id="gist-buffering-build"> <sect2 id="gist-buffering-build">
<title>GiST Buffering Build</title> <title>GiST Index Build Methods</title>
<para>
The simplest way to build a GiST index is just to insert all the entries,
one by one. This tends to be slow for large indexes, because if the
index tuples are scattered across the index and the index is large enough
to not fit in cache, a lot of random I/O will be
needed. <productname>PostgreSQL</productname> supports two alternative
methods for initial build of a GiST index: <firstterm>sorted</firstterm>
and <firstterm>buffered</firstterm> modes.
</para>
<para>
The sorted method is only available if each of the opclasses used by the
index provides a <function>sortsupport</function> function, as described
in <xref linkend="gist-extensibility"/>. If they do, this method is
usually the best, so it is used by default.
</para>
<para> <para>
Building large GiST indexes by simply inserting all the tuples tends to be The buffered method works by not inserting tuples directly into the index
slow, because if the index tuples are scattered across the index and the right away. It can dramatically reduce the amount of random I/O needed
index is large enough to not fit in cache, the insertions need to perform for non-ordered data sets. For well-ordered data sets the benefit is
a lot of random I/O. Beginning in version 9.2, PostgreSQL supports a more smaller or non-existent, because only a small number of pages receive new
efficient method to build GiST indexes based on buffering, which can tuples at a time, and those pages fit in cache even if the index as a
dramatically reduce the number of random I/Os needed for non-ordered data whole does not.
sets. For well-ordered data sets the benefit is smaller or non-existent,
because only a small number of pages receive new tuples at a time, and
those pages fit in cache even if the index as whole does not.
</para> </para>
<para> <para>
However, buffering index build needs to call the <function>penalty</function> The buffered method needs to call the <function>penalty</function>
function more often, which consumes some extra CPU resources. Also, the function more often than the simple method does, which consumes some
buffers used in the buffering build need temporary disk space, up to extra CPU resources. Also, the buffers need temporary disk space, up to
the size of the resulting index. Buffering can also influence the quality the size of the resulting index. Buffering can also influence the quality
of the resulting index, in both positive and negative directions. That of the resulting index, in both positive and negative directions. That
influence depends on various factors, like the distribution of the input influence depends on various factors, like the distribution of the input
...@@ -1181,12 +1196,13 @@ my_sortsupport(PG_FUNCTION_ARGS) ...@@ -1181,12 +1196,13 @@ my_sortsupport(PG_FUNCTION_ARGS)
</para> </para>
<para> <para>
By default, a GiST index build switches to the buffering method when the If sorting is not possible, then by default a GiST index build switches
index size reaches <xref linkend="guc-effective-cache-size"/>. It can to the buffering method when the index size reaches
be manually turned on or off by the <literal>buffering</literal> parameter <xref linkend="guc-effective-cache-size"/>. Buffering can be manually
to the CREATE INDEX command. The default behavior is good for most cases, forced or prevented by the <literal>buffering</literal> parameter to the
but turning buffering off might speed up the build somewhat if the input CREATE INDEX command. The default behavior is good for most cases, but
data is ordered. turning buffering off might speed up the build somewhat if the input data
is ordered.
</para> </para>
</sect2> </sect2>
......
...@@ -463,11 +463,15 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class= ...@@ -463,11 +463,15 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class=
</term> </term>
<listitem> <listitem>
<para> <para>
Determines whether the buffering build technique described in Determines whether the buffered build technique described in
<xref linkend="gist-buffering-build"/> is used to build the index. With <xref linkend="gist-buffering-build"/> is used to build the index. With
<literal>OFF</literal> it is disabled, with <literal>ON</literal> it is enabled, and <literal>OFF</literal> buffering is disabled, with <literal>ON</literal>
with <literal>AUTO</literal> it is initially disabled, but turned on it is enabled, and with <literal>AUTO</literal> it is initially disabled,
on-the-fly once the index size reaches <xref linkend="guc-effective-cache-size"/>. The default is <literal>AUTO</literal>. but is turned on on-the-fly once the index size reaches
<xref linkend="guc-effective-cache-size"/>. The default
is <literal>AUTO</literal>.
Note that if sorted build is possible, it will be used instead of
buffered build unless <literal>buffering=ON</literal> is specified.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
......
...@@ -180,9 +180,7 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo) ...@@ -180,9 +180,7 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo)
MemoryContext oldcxt = CurrentMemoryContext; MemoryContext oldcxt = CurrentMemoryContext;
int fillfactor; int fillfactor;
Oid SortSupportFnOids[INDEX_MAX_KEYS]; Oid SortSupportFnOids[INDEX_MAX_KEYS];
bool hasallsortsupports; GiSTOptions *options = (GiSTOptions *) index->rd_options;
int keyscount = IndexRelationGetNumberOfKeyAttributes(index);
GiSTOptions *options = NULL;
/* /*
* We expect to be called exactly once for any index relation. If that's * We expect to be called exactly once for any index relation. If that's
...@@ -192,9 +190,6 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo) ...@@ -192,9 +190,6 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo)
elog(ERROR, "index \"%s\" already contains data", elog(ERROR, "index \"%s\" already contains data",
RelationGetRelationName(index)); RelationGetRelationName(index));
if (index->rd_options)
options = (GiSTOptions *) index->rd_options;
buildstate.indexrel = index; buildstate.indexrel = index;
buildstate.heaprel = heap; buildstate.heaprel = heap;
buildstate.sortstate = NULL; buildstate.sortstate = NULL;
...@@ -208,11 +203,32 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo) ...@@ -208,11 +203,32 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo)
buildstate.giststate->tempCxt = createTempGistContext(); buildstate.giststate->tempCxt = createTempGistContext();
/* /*
* Choose build strategy. If all keys support sorting, do that. Otherwise * Choose build strategy. First check whether the user specified to use
* the default strategy is switch to buffering mode when the index grows * buffering mode. (The use-case for that in the field is somewhat
* too large to fit in cache. * questionable perhaps, but it's important for testing purposes.)
*/ */
hasallsortsupports = true; if (options)
{
if (options->buffering_mode == GIST_OPTION_BUFFERING_ON)
buildstate.buildMode = GIST_BUFFERING_STATS;
else if (options->buffering_mode == GIST_OPTION_BUFFERING_OFF)
buildstate.buildMode = GIST_BUFFERING_DISABLED;
else /* must be "auto" */
buildstate.buildMode = GIST_BUFFERING_AUTO;
}
else
{
buildstate.buildMode = GIST_BUFFERING_AUTO;
}
/*
* Unless buffering mode was forced, see if we can use sorting instead.
*/
if (buildstate.buildMode != GIST_BUFFERING_STATS)
{
bool hasallsortsupports = true;
int keyscount = IndexRelationGetNumberOfKeyAttributes(index);
for (int i = 0; i < keyscount; i++) for (int i = 0; i < keyscount; i++)
{ {
SortSupportFnOids[i] = index_getprocid(index, i + 1, SortSupportFnOids[i] = index_getprocid(index, i + 1,
...@@ -223,24 +239,9 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo) ...@@ -223,24 +239,9 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo)
break; break;
} }
} }
if (hasallsortsupports) if (hasallsortsupports)
{
buildstate.buildMode = GIST_SORTED_BUILD; buildstate.buildMode = GIST_SORTED_BUILD;
} }
else if (options)
{
if (options->buffering_mode == GIST_OPTION_BUFFERING_ON)
buildstate.buildMode = GIST_BUFFERING_STATS;
else if (options->buffering_mode == GIST_OPTION_BUFFERING_OFF)
buildstate.buildMode = GIST_BUFFERING_DISABLED;
else
buildstate.buildMode = GIST_BUFFERING_AUTO;
}
else
{
buildstate.buildMode = GIST_BUFFERING_AUTO;
}
/* /*
* Calculate target amount of free space to leave on pages. * Calculate target amount of free space to leave on pages.
...@@ -852,7 +853,10 @@ gistBuildCallback(Relation index, ...@@ -852,7 +853,10 @@ gistBuildCallback(Relation index,
* and switch to buffering mode if it has. * and switch to buffering mode if it has.
* *
* To avoid excessive calls to smgrnblocks(), only check this every * To avoid excessive calls to smgrnblocks(), only check this every
* BUFFERING_MODE_SWITCH_CHECK_STEP index tuples * BUFFERING_MODE_SWITCH_CHECK_STEP index tuples.
*
* In 'stats' state, switch as soon as we have seen enough tuples to have
* some idea of the average tuple size.
*/ */
if ((buildstate->buildMode == GIST_BUFFERING_AUTO && if ((buildstate->buildMode == GIST_BUFFERING_AUTO &&
buildstate->indtuples % BUFFERING_MODE_SWITCH_CHECK_STEP == 0 && buildstate->indtuples % BUFFERING_MODE_SWITCH_CHECK_STEP == 0 &&
......
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