mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-03-19 20:00:51 +08:00
pg_prewarm, a contrib module for prewarming relationd data.
Patch by me. Review by Álvaro Herrera, Amit Kapila, Jeff Janes, Gurjeet Singh, and others.
This commit is contained in:
parent
6eda3e9c27
commit
c32afe53c2
@ -32,6 +32,7 @@ SUBDIRS = \
|
||||
pg_archivecleanup \
|
||||
pg_buffercache \
|
||||
pg_freespacemap \
|
||||
pg_prewarm \
|
||||
pg_standby \
|
||||
pg_stat_statements \
|
||||
pg_test_fsync \
|
||||
|
18
contrib/pg_prewarm/Makefile
Normal file
18
contrib/pg_prewarm/Makefile
Normal file
@ -0,0 +1,18 @@
|
||||
# contrib/pg_prewarm/Makefile
|
||||
|
||||
MODULE_big = pg_prewarm
|
||||
OBJS = pg_prewarm.o
|
||||
|
||||
EXTENSION = pg_prewarm
|
||||
DATA = pg_prewarm--1.0.sql
|
||||
|
||||
ifdef USE_PGXS
|
||||
PG_CONFIG = pg_config
|
||||
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
||||
include $(PGXS)
|
||||
else
|
||||
subdir = contrib/pg_prewarm
|
||||
top_builddir = ../..
|
||||
include $(top_builddir)/src/Makefile.global
|
||||
include $(top_srcdir)/contrib/contrib-global.mk
|
||||
endif
|
14
contrib/pg_prewarm/pg_prewarm--1.0.sql
Normal file
14
contrib/pg_prewarm/pg_prewarm--1.0.sql
Normal file
@ -0,0 +1,14 @@
|
||||
/* contrib/pg_prewarm/pg_prewarm--1.0.sql */
|
||||
|
||||
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||
\echo Use "CREATE EXTENSION pg_prewarm" to load this file. \quit
|
||||
|
||||
-- Register the function.
|
||||
CREATE FUNCTION pg_prewarm(regclass,
|
||||
mode text default 'buffer',
|
||||
fork text default 'main',
|
||||
first_block int8 default null,
|
||||
last_block int8 default null)
|
||||
RETURNS int8
|
||||
AS 'MODULE_PATHNAME', 'pg_prewarm'
|
||||
LANGUAGE C;
|
205
contrib/pg_prewarm/pg_prewarm.c
Normal file
205
contrib/pg_prewarm/pg_prewarm.c
Normal file
@ -0,0 +1,205 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* pg_prewarm.c
|
||||
* prewarming utilities
|
||||
*
|
||||
* Copyright (c) 2010-2013, PostgreSQL Global Development Group
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* contrib/pg_prewarm/pg_prewarm.c
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "access/heapam.h"
|
||||
#include "catalog/catalog.h"
|
||||
#include "fmgr.h"
|
||||
#include "miscadmin.h"
|
||||
#include "storage/bufmgr.h"
|
||||
#include "storage/smgr.h"
|
||||
#include "utils/acl.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/lsyscache.h"
|
||||
#include "utils/rel.h"
|
||||
|
||||
PG_MODULE_MAGIC;
|
||||
|
||||
extern Datum pg_prewarm(PG_FUNCTION_ARGS);
|
||||
|
||||
PG_FUNCTION_INFO_V1(pg_prewarm);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
PREWARM_PREFETCH,
|
||||
PREWARM_READ,
|
||||
PREWARM_BUFFER
|
||||
} PrewarmType;
|
||||
|
||||
static char blockbuffer[BLCKSZ];
|
||||
|
||||
/*
|
||||
* pg_prewarm(regclass, mode text, fork text,
|
||||
* first_block int8, last_block int8)
|
||||
*
|
||||
* The first argument is the relation to be prewarmed; the second controls
|
||||
* how prewarming is done; legal options are 'prefetch', 'read', and 'buffer'.
|
||||
* The third is the name of the relation fork to be prewarmed. The fourth
|
||||
* and fifth arguments specify the first and last block to be prewarmed.
|
||||
* If the fourth argument is NULL, it will be taken as 0; if the fifth argument
|
||||
* is NULL, it will be taken as the number of blocks in the relation. The
|
||||
* return value is the number of blocks successfully prewarmed.
|
||||
*/
|
||||
Datum
|
||||
pg_prewarm(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Oid relOid;
|
||||
text *forkName;
|
||||
text *type;
|
||||
int64 first_block;
|
||||
int64 last_block;
|
||||
int64 nblocks;
|
||||
int64 blocks_done = 0;
|
||||
int64 block;
|
||||
Relation rel;
|
||||
ForkNumber forkNumber;
|
||||
char *forkString;
|
||||
char *ttype;
|
||||
PrewarmType ptype;
|
||||
AclResult aclresult;
|
||||
|
||||
/* Basic sanity checking. */
|
||||
if (PG_ARGISNULL(0))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("relation cannot be null")));
|
||||
relOid = PG_GETARG_OID(0);
|
||||
if (PG_ARGISNULL(1))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
(errmsg("prewarm type cannot be null"))));
|
||||
type = PG_GETARG_TEXT_P(1);
|
||||
ttype = text_to_cstring(type);
|
||||
if (strcmp(ttype, "prefetch") == 0)
|
||||
ptype = PREWARM_PREFETCH;
|
||||
else if (strcmp(ttype, "read") == 0)
|
||||
ptype = PREWARM_READ;
|
||||
else if (strcmp(ttype, "buffer") == 0)
|
||||
ptype = PREWARM_BUFFER;
|
||||
else
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("invalid prewarm type"),
|
||||
errhint("Valid prewarm types are \"prefetch\", \"read\", and \"buffer\".")));
|
||||
PG_RETURN_INT64(0); /* Placate compiler. */
|
||||
}
|
||||
if (PG_ARGISNULL(2))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
(errmsg("relation fork cannot be null"))));
|
||||
forkName = PG_GETARG_TEXT_P(2);
|
||||
forkString = text_to_cstring(forkName);
|
||||
forkNumber = forkname_to_number(forkString);
|
||||
|
||||
/* Open relation and check privileges. */
|
||||
rel = relation_open(relOid, AccessShareLock);
|
||||
aclresult = pg_class_aclcheck(relOid, GetUserId(), ACL_SELECT);
|
||||
if (aclresult != ACLCHECK_OK)
|
||||
aclcheck_error(aclresult, ACL_KIND_CLASS, get_rel_name(relOid));
|
||||
|
||||
/* Check that the fork exists. */
|
||||
RelationOpenSmgr(rel);
|
||||
if (!smgrexists(rel->rd_smgr, forkNumber))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("fork \"%s\" does not exist for this relation",
|
||||
forkString)));
|
||||
|
||||
/* Validate block numbers, or handle nulls. */
|
||||
nblocks = RelationGetNumberOfBlocksInFork(rel, forkNumber);
|
||||
if (PG_ARGISNULL(3))
|
||||
first_block = 0;
|
||||
else
|
||||
{
|
||||
first_block = PG_GETARG_INT64(3);
|
||||
if (first_block < 0 || first_block >= nblocks)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("starting block number must be between 0 and " INT64_FORMAT,
|
||||
nblocks - 1)));
|
||||
}
|
||||
if (PG_ARGISNULL(4))
|
||||
last_block = nblocks - 1;
|
||||
else
|
||||
{
|
||||
last_block = PG_GETARG_INT64(4);
|
||||
if (last_block < 0 || last_block >= nblocks)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("ending block number must be between 0 and " INT64_FORMAT,
|
||||
nblocks - 1)));
|
||||
}
|
||||
|
||||
/* Now we're ready to do the real work. */
|
||||
if (ptype == PREWARM_PREFETCH)
|
||||
{
|
||||
#ifdef USE_PREFETCH
|
||||
|
||||
/*
|
||||
* In prefetch mode, we just hint the OS to read the blocks, but we
|
||||
* don't know whether it really does it, and we don't wait for it to
|
||||
* finish.
|
||||
*
|
||||
* It would probably be better to pass our prefetch requests in chunks
|
||||
* of a megabyte or maybe even a whole segment at a time, but there's
|
||||
* no practical way to do that at present without a gross modularity
|
||||
* violation, so we just do this.
|
||||
*/
|
||||
for (block = first_block; block <= last_block; ++block)
|
||||
{
|
||||
PrefetchBuffer(rel, forkNumber, block);
|
||||
++blocks_done;
|
||||
}
|
||||
#else
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("prefetch is not supported by this build")));
|
||||
#endif
|
||||
}
|
||||
else if (ptype == PREWARM_READ)
|
||||
{
|
||||
/*
|
||||
* In read mode, we actually read the blocks, but not into shared
|
||||
* buffers. This is more portable than prefetch mode (it works
|
||||
* everywhere) and is synchronous.
|
||||
*/
|
||||
for (block = first_block; block <= last_block; ++block)
|
||||
{
|
||||
smgrread(rel->rd_smgr, forkNumber, block, blockbuffer);
|
||||
++blocks_done;
|
||||
}
|
||||
}
|
||||
else if (ptype == PREWARM_BUFFER)
|
||||
{
|
||||
/*
|
||||
* In buffer mode, we actually pull the data into shared_buffers.
|
||||
*/
|
||||
for (block = first_block; block <= last_block; ++block)
|
||||
{
|
||||
Buffer buf;
|
||||
|
||||
buf = ReadBufferExtended(rel, forkNumber, block, RBM_NORMAL, NULL);
|
||||
ReleaseBuffer(buf);
|
||||
++blocks_done;
|
||||
}
|
||||
}
|
||||
|
||||
/* Close relation, release lock. */
|
||||
relation_close(rel, AccessShareLock);
|
||||
|
||||
PG_RETURN_INT64(blocks_done);
|
||||
}
|
5
contrib/pg_prewarm/pg_prewarm.control
Normal file
5
contrib/pg_prewarm/pg_prewarm.control
Normal file
@ -0,0 +1,5 @@
|
||||
# pg_prewarm extension
|
||||
comment = 'prewarm relation data'
|
||||
default_version = '1.0'
|
||||
module_pathname = '$libdir/pg_prewarm'
|
||||
relocatable = true
|
@ -128,6 +128,7 @@ CREATE EXTENSION <replaceable>module_name</> FROM unpackaged;
|
||||
&pgbuffercache;
|
||||
&pgcrypto;
|
||||
&pgfreespacemap;
|
||||
&pgprewarm;
|
||||
&pgrowlocks;
|
||||
&pgstatstatements;
|
||||
&pgstattuple;
|
||||
|
@ -126,6 +126,7 @@
|
||||
<!ENTITY pgbuffercache SYSTEM "pgbuffercache.sgml">
|
||||
<!ENTITY pgcrypto SYSTEM "pgcrypto.sgml">
|
||||
<!ENTITY pgfreespacemap SYSTEM "pgfreespacemap.sgml">
|
||||
<!ENTITY pgprewarm SYSTEM "pgprewarm.sgml">
|
||||
<!ENTITY pgrowlocks SYSTEM "pgrowlocks.sgml">
|
||||
<!ENTITY pgstandby SYSTEM "pgstandby.sgml">
|
||||
<!ENTITY pgstatstatements SYSTEM "pgstatstatements.sgml">
|
||||
|
68
doc/src/sgml/pgprewarm.sgml
Normal file
68
doc/src/sgml/pgprewarm.sgml
Normal file
@ -0,0 +1,68 @@
|
||||
<!-- doc/src/sgml/pgprewarm.sgml -->
|
||||
|
||||
<sect1 id="pgprewarm" xreflabel="pg_prewarm">
|
||||
<title>pg_prewarm</title>
|
||||
|
||||
<indexterm zone="pgprewarm">
|
||||
<primary>pg_prewarm</primary>
|
||||
</indexterm>
|
||||
|
||||
<para>
|
||||
The <filename>pg_prewarm</filename> module provides a convenient way
|
||||
to load relation data into either the operating system buffer cache
|
||||
or the <productname>PostgreSQL</productname> buffer cache.
|
||||
</para>
|
||||
|
||||
<sect2>
|
||||
<title>Functions</title>
|
||||
|
||||
<synopsis>
|
||||
pg_prewarm(regclass, mode text default 'buffer', fork text default 'main',
|
||||
first_block int8 default null,
|
||||
last_block int8 default null) RETURNS int8
|
||||
</synopsis>
|
||||
|
||||
<para>
|
||||
The first argument is the relation to be prewarmed. The second argument
|
||||
is the prewarming method to be used, as further discussed below; the third
|
||||
is the relation fork to be prewarmed, usually <literal>main</literal>.
|
||||
The fourth argument is the first block number to prewarm
|
||||
(<literal>NULL</literal> is accepted as a synonym for zero). The fifth
|
||||
argument is the last block number to prewarm (<literal>NULL</literal>
|
||||
means prewarm through the last block in the relation). The return value
|
||||
is the number of blocks prewarmed.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
There are three available prewarming methods. <literal>prefetch</literal>
|
||||
issues asynchronous prefetch requests to the operating system, if this is
|
||||
supported, or throws an error otherwise. <literal>read</literal> reads
|
||||
the requested range of blocks; unlike <literal>prefetch</literal>, this is
|
||||
synchronous and supported on all platforms and builds, but may be slower.
|
||||
<literal>buffer</literal> reads the requested range of blocks into the
|
||||
database buffer cache.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Note that with any of these methods, attempting to prewarm more blocks than
|
||||
can be cached — by the OS when using <literal>prefetch</literal> or
|
||||
<literal>read</literal>, or by <productname>PostgreSQL</productname> when
|
||||
using <literal>buffer</literal> — will likely result in lower-numbered
|
||||
blocks being evicted as higher numbered blocks are read in. Prewarmed data
|
||||
also enjoys no special protection from cache evictions, so it is possible
|
||||
for other system activity may evict the newly prewarmed blocks shortly after
|
||||
they are read; conversely, prewarming may also evict other data from cache.
|
||||
For these reasons, prewarming is typically most useful at startup, when
|
||||
caches are largely empty.
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Author</title>
|
||||
|
||||
<para>
|
||||
Robert Haas <email>rhaas@postgresql.org</email>
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
@ -1334,6 +1334,7 @@ PostParseColumnRefHook
|
||||
PostgresPollingStatusType
|
||||
PostingItem
|
||||
PreParseColumnRefHook
|
||||
PrewarmType
|
||||
PredClass
|
||||
PredIterInfo
|
||||
PredIterInfoData
|
||||
|
Loading…
x
Reference in New Issue
Block a user