Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit c32afe5

Browse files
committed
pg_prewarm, a contrib module for prewarming relationd data.
Patch by me. Review by Álvaro Herrera, Amit Kapila, Jeff Janes, Gurjeet Singh, and others.
1 parent 6eda3e9 commit c32afe5

File tree

9 files changed

+314
-0
lines changed

9 files changed

+314
-0
lines changed

contrib/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ SUBDIRS = \
3232
pg_archivecleanup \
3333
pg_buffercache \
3434
pg_freespacemap \
35+
pg_prewarm \
3536
pg_standby \
3637
pg_stat_statements \
3738
pg_test_fsync \

contrib/pg_prewarm/Makefile

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# contrib/pg_prewarm/Makefile
2+
3+
MODULE_big = pg_prewarm
4+
OBJS = pg_prewarm.o
5+
6+
EXTENSION = pg_prewarm
7+
DATA = pg_prewarm--1.0.sql
8+
9+
ifdef USE_PGXS
10+
PG_CONFIG = pg_config
11+
PGXS := $(shell $(PG_CONFIG) --pgxs)
12+
include $(PGXS)
13+
else
14+
subdir = contrib/pg_prewarm
15+
top_builddir = ../..
16+
include $(top_builddir)/src/Makefile.global
17+
include $(top_srcdir)/contrib/contrib-global.mk
18+
endif
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/* contrib/pg_prewarm/pg_prewarm--1.0.sql */
2+
3+
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
4+
\echo Use "CREATE EXTENSION pg_prewarm" to load this file. \quit
5+
6+
-- Register the function.
7+
CREATE FUNCTION pg_prewarm(regclass,
8+
mode text default 'buffer',
9+
fork text default 'main',
10+
first_block int8 default null,
11+
last_block int8 default null)
12+
RETURNS int8
13+
AS 'MODULE_PATHNAME', 'pg_prewarm'
14+
LANGUAGE C;

contrib/pg_prewarm/pg_prewarm.c

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
/*-------------------------------------------------------------------------
2+
*
3+
* pg_prewarm.c
4+
* prewarming utilities
5+
*
6+
* Copyright (c) 2010-2013, PostgreSQL Global Development Group
7+
*
8+
* IDENTIFICATION
9+
* contrib/pg_prewarm/pg_prewarm.c
10+
*
11+
*-------------------------------------------------------------------------
12+
*/
13+
#include "postgres.h"
14+
15+
#include <sys/stat.h>
16+
#include <unistd.h>
17+
18+
#include "access/heapam.h"
19+
#include "catalog/catalog.h"
20+
#include "fmgr.h"
21+
#include "miscadmin.h"
22+
#include "storage/bufmgr.h"
23+
#include "storage/smgr.h"
24+
#include "utils/acl.h"
25+
#include "utils/builtins.h"
26+
#include "utils/lsyscache.h"
27+
#include "utils/rel.h"
28+
29+
PG_MODULE_MAGIC;
30+
31+
extern Datum pg_prewarm(PG_FUNCTION_ARGS);
32+
33+
PG_FUNCTION_INFO_V1(pg_prewarm);
34+
35+
typedef enum
36+
{
37+
PREWARM_PREFETCH,
38+
PREWARM_READ,
39+
PREWARM_BUFFER
40+
} PrewarmType;
41+
42+
static char blockbuffer[BLCKSZ];
43+
44+
/*
45+
* pg_prewarm(regclass, mode text, fork text,
46+
* first_block int8, last_block int8)
47+
*
48+
* The first argument is the relation to be prewarmed; the second controls
49+
* how prewarming is done; legal options are 'prefetch', 'read', and 'buffer'.
50+
* The third is the name of the relation fork to be prewarmed. The fourth
51+
* and fifth arguments specify the first and last block to be prewarmed.
52+
* If the fourth argument is NULL, it will be taken as 0; if the fifth argument
53+
* is NULL, it will be taken as the number of blocks in the relation. The
54+
* return value is the number of blocks successfully prewarmed.
55+
*/
56+
Datum
57+
pg_prewarm(PG_FUNCTION_ARGS)
58+
{
59+
Oid relOid;
60+
text *forkName;
61+
text *type;
62+
int64 first_block;
63+
int64 last_block;
64+
int64 nblocks;
65+
int64 blocks_done = 0;
66+
int64 block;
67+
Relation rel;
68+
ForkNumber forkNumber;
69+
char *forkString;
70+
char *ttype;
71+
PrewarmType ptype;
72+
AclResult aclresult;
73+
74+
/* Basic sanity checking. */
75+
if (PG_ARGISNULL(0))
76+
ereport(ERROR,
77+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
78+
errmsg("relation cannot be null")));
79+
relOid = PG_GETARG_OID(0);
80+
if (PG_ARGISNULL(1))
81+
ereport(ERROR,
82+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
83+
(errmsg("prewarm type cannot be null"))));
84+
type = PG_GETARG_TEXT_P(1);
85+
ttype = text_to_cstring(type);
86+
if (strcmp(ttype, "prefetch") == 0)
87+
ptype = PREWARM_PREFETCH;
88+
else if (strcmp(ttype, "read") == 0)
89+
ptype = PREWARM_READ;
90+
else if (strcmp(ttype, "buffer") == 0)
91+
ptype = PREWARM_BUFFER;
92+
else
93+
{
94+
ereport(ERROR,
95+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
96+
errmsg("invalid prewarm type"),
97+
errhint("Valid prewarm types are \"prefetch\", \"read\", and \"buffer\".")));
98+
PG_RETURN_INT64(0); /* Placate compiler. */
99+
}
100+
if (PG_ARGISNULL(2))
101+
ereport(ERROR,
102+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
103+
(errmsg("relation fork cannot be null"))));
104+
forkName = PG_GETARG_TEXT_P(2);
105+
forkString = text_to_cstring(forkName);
106+
forkNumber = forkname_to_number(forkString);
107+
108+
/* Open relation and check privileges. */
109+
rel = relation_open(relOid, AccessShareLock);
110+
aclresult = pg_class_aclcheck(relOid, GetUserId(), ACL_SELECT);
111+
if (aclresult != ACLCHECK_OK)
112+
aclcheck_error(aclresult, ACL_KIND_CLASS, get_rel_name(relOid));
113+
114+
/* Check that the fork exists. */
115+
RelationOpenSmgr(rel);
116+
if (!smgrexists(rel->rd_smgr, forkNumber))
117+
ereport(ERROR,
118+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
119+
errmsg("fork \"%s\" does not exist for this relation",
120+
forkString)));
121+
122+
/* Validate block numbers, or handle nulls. */
123+
nblocks = RelationGetNumberOfBlocksInFork(rel, forkNumber);
124+
if (PG_ARGISNULL(3))
125+
first_block = 0;
126+
else
127+
{
128+
first_block = PG_GETARG_INT64(3);
129+
if (first_block < 0 || first_block >= nblocks)
130+
ereport(ERROR,
131+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
132+
errmsg("starting block number must be between 0 and " INT64_FORMAT,
133+
nblocks - 1)));
134+
}
135+
if (PG_ARGISNULL(4))
136+
last_block = nblocks - 1;
137+
else
138+
{
139+
last_block = PG_GETARG_INT64(4);
140+
if (last_block < 0 || last_block >= nblocks)
141+
ereport(ERROR,
142+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
143+
errmsg("ending block number must be between 0 and " INT64_FORMAT,
144+
nblocks - 1)));
145+
}
146+
147+
/* Now we're ready to do the real work. */
148+
if (ptype == PREWARM_PREFETCH)
149+
{
150+
#ifdef USE_PREFETCH
151+
152+
/*
153+
* In prefetch mode, we just hint the OS to read the blocks, but we
154+
* don't know whether it really does it, and we don't wait for it to
155+
* finish.
156+
*
157+
* It would probably be better to pass our prefetch requests in chunks
158+
* of a megabyte or maybe even a whole segment at a time, but there's
159+
* no practical way to do that at present without a gross modularity
160+
* violation, so we just do this.
161+
*/
162+
for (block = first_block; block <= last_block; ++block)
163+
{
164+
PrefetchBuffer(rel, forkNumber, block);
165+
++blocks_done;
166+
}
167+
#else
168+
ereport(ERROR,
169+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
170+
errmsg("prefetch is not supported by this build")));
171+
#endif
172+
}
173+
else if (ptype == PREWARM_READ)
174+
{
175+
/*
176+
* In read mode, we actually read the blocks, but not into shared
177+
* buffers. This is more portable than prefetch mode (it works
178+
* everywhere) and is synchronous.
179+
*/
180+
for (block = first_block; block <= last_block; ++block)
181+
{
182+
smgrread(rel->rd_smgr, forkNumber, block, blockbuffer);
183+
++blocks_done;
184+
}
185+
}
186+
else if (ptype == PREWARM_BUFFER)
187+
{
188+
/*
189+
* In buffer mode, we actually pull the data into shared_buffers.
190+
*/
191+
for (block = first_block; block <= last_block; ++block)
192+
{
193+
Buffer buf;
194+
195+
buf = ReadBufferExtended(rel, forkNumber, block, RBM_NORMAL, NULL);
196+
ReleaseBuffer(buf);
197+
++blocks_done;
198+
}
199+
}
200+
201+
/* Close relation, release lock. */
202+
relation_close(rel, AccessShareLock);
203+
204+
PG_RETURN_INT64(blocks_done);
205+
}

contrib/pg_prewarm/pg_prewarm.control

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# pg_prewarm extension
2+
comment = 'prewarm relation data'
3+
default_version = '1.0'
4+
module_pathname = '$libdir/pg_prewarm'
5+
relocatable = true

doc/src/sgml/contrib.sgml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ CREATE EXTENSION <replaceable>module_name</> FROM unpackaged;
128128
&pgbuffercache;
129129
&pgcrypto;
130130
&pgfreespacemap;
131+
&pgprewarm;
131132
&pgrowlocks;
132133
&pgstatstatements;
133134
&pgstattuple;

doc/src/sgml/filelist.sgml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@
126126
<!ENTITY pgbuffercache SYSTEM "pgbuffercache.sgml">
127127
<!ENTITY pgcrypto SYSTEM "pgcrypto.sgml">
128128
<!ENTITY pgfreespacemap SYSTEM "pgfreespacemap.sgml">
129+
<!ENTITY pgprewarm SYSTEM "pgprewarm.sgml">
129130
<!ENTITY pgrowlocks SYSTEM "pgrowlocks.sgml">
130131
<!ENTITY pgstandby SYSTEM "pgstandby.sgml">
131132
<!ENTITY pgstatstatements SYSTEM "pgstatstatements.sgml">

doc/src/sgml/pgprewarm.sgml

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<!-- doc/src/sgml/pgprewarm.sgml -->
2+
3+
<sect1 id="pgprewarm" xreflabel="pg_prewarm">
4+
<title>pg_prewarm</title>
5+
6+
<indexterm zone="pgprewarm">
7+
<primary>pg_prewarm</primary>
8+
</indexterm>
9+
10+
<para>
11+
The <filename>pg_prewarm</filename> module provides a convenient way
12+
to load relation data into either the operating system buffer cache
13+
or the <productname>PostgreSQL</productname> buffer cache.
14+
</para>
15+
16+
<sect2>
17+
<title>Functions</title>
18+
19+
<synopsis>
20+
pg_prewarm(regclass, mode text default 'buffer', fork text default 'main',
21+
first_block int8 default null,
22+
last_block int8 default null) RETURNS int8
23+
</synopsis>
24+
25+
<para>
26+
The first argument is the relation to be prewarmed. The second argument
27+
is the prewarming method to be used, as further discussed below; the third
28+
is the relation fork to be prewarmed, usually <literal>main</literal>.
29+
The fourth argument is the first block number to prewarm
30+
(<literal>NULL</literal> is accepted as a synonym for zero). The fifth
31+
argument is the last block number to prewarm (<literal>NULL</literal>
32+
means prewarm through the last block in the relation). The return value
33+
is the number of blocks prewarmed.
34+
</para>
35+
36+
<para>
37+
There are three available prewarming methods. <literal>prefetch</literal>
38+
issues asynchronous prefetch requests to the operating system, if this is
39+
supported, or throws an error otherwise. <literal>read</literal> reads
40+
the requested range of blocks; unlike <literal>prefetch</literal>, this is
41+
synchronous and supported on all platforms and builds, but may be slower.
42+
<literal>buffer</literal> reads the requested range of blocks into the
43+
database buffer cache.
44+
</para>
45+
46+
<para>
47+
Note that with any of these methods, attempting to prewarm more blocks than
48+
can be cached &mdash; by the OS when using <literal>prefetch</literal> or
49+
<literal>read</literal>, or by <productname>PostgreSQL</productname> when
50+
using <literal>buffer</literal> &mdash; will likely result in lower-numbered
51+
blocks being evicted as higher numbered blocks are read in. Prewarmed data
52+
also enjoys no special protection from cache evictions, so it is possible
53+
for other system activity may evict the newly prewarmed blocks shortly after
54+
they are read; conversely, prewarming may also evict other data from cache.
55+
For these reasons, prewarming is typically most useful at startup, when
56+
caches are largely empty.
57+
</para>
58+
</sect2>
59+
60+
<sect2>
61+
<title>Author</title>
62+
63+
<para>
64+
Robert Haas <email>rhaas@postgresql.org</email>
65+
</para>
66+
</sect2>
67+
68+
</sect1>

src/tools/pgindent/typedefs.list

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1334,6 +1334,7 @@ PostParseColumnRefHook
13341334
PostgresPollingStatusType
13351335
PostingItem
13361336
PreParseColumnRefHook
1337+
PrewarmType
13371338
PredClass
13381339
PredIterInfo
13391340
PredIterInfoData

0 commit comments

Comments
 (0)