Skip to content

Commit aecf5ee

Browse files
committed
Add new 'old_snapshot' contrib module.
You can use this to view the contents of the time to XID mapping which the server maintains when old_snapshot_threshold != -1. Being able to view that information may be interesting for users, and it's definitely useful for figuring out whether the mapping is being maintained correctly. It isn't, so that will need to be fixed in a subsequent commit. Patch by me, reviewed by Thomas Munro, Dilip Kumar, Hamid Akhtar. Discussion: http://postgr.es/m/CA+TgmoY=aqf0zjTD+3dUWYkgMiNDegDLFjo+6ze=Wtpik+3XqA@mail.gmail.com
1 parent f5ea92e commit aecf5ee

File tree

8 files changed

+236
-0
lines changed

8 files changed

+236
-0
lines changed

contrib/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ SUBDIRS = \
2727
lo \
2828
ltree \
2929
oid2name \
30+
old_snapshot \
3031
pageinspect \
3132
passwordcheck \
3233
pg_buffercache \

contrib/old_snapshot/Makefile

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# contrib/old_snapshot/Makefile
2+
3+
MODULE_big = old_snapshot
4+
OBJS = \
5+
$(WIN32RES) \
6+
time_mapping.o
7+
PG_CPPFLAGS = -I$(libpq_srcdir)
8+
9+
EXTENSION = old_snapshot
10+
DATA = old_snapshot--1.0.sql
11+
PGFILEDESC = "old_snapshot - utilities in support of old_snapshot_threshold"
12+
13+
ifdef USE_PGXS
14+
PG_CONFIG = pg_config
15+
PGXS := $(shell $(PG_CONFIG) --pgxs)
16+
include $(PGXS)
17+
else
18+
subdir = contrib/old_snapshot
19+
top_builddir = ../..
20+
include $(top_builddir)/src/Makefile.global
21+
include $(top_srcdir)/contrib/contrib-global.mk
22+
endif
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/* contrib/old_snapshot/old_snapshot--1.0.sql */
2+
3+
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
4+
\echo Use "CREATE EXTENSION old_snapshot" to load this file. \quit
5+
6+
-- Show visibility map and page-level visibility information for each block.
7+
CREATE FUNCTION pg_old_snapshot_time_mapping(array_offset OUT int4,
8+
end_timestamp OUT timestamptz,
9+
newest_xmin OUT xid)
10+
RETURNS SETOF record
11+
AS 'MODULE_PATHNAME', 'pg_old_snapshot_time_mapping'
12+
LANGUAGE C STRICT;
13+
14+
-- XXX. Do we want REVOKE commands here?
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# old_snapshot extension
2+
comment = 'utilities in support of old_snapshot_threshold'
3+
default_version = '1.0'
4+
module_pathname = '$libdir/old_snapshot'
5+
relocatable = true

contrib/old_snapshot/time_mapping.c

+159
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/*-------------------------------------------------------------------------
2+
*
3+
* time_mapping.c
4+
* time to XID mapping information
5+
*
6+
* Copyright (c) 2020, PostgreSQL Global Development Group
7+
*
8+
* contrib/old_snapshot/time_mapping.c
9+
*-------------------------------------------------------------------------
10+
*/
11+
#include "postgres.h"
12+
13+
#include "funcapi.h"
14+
#include "storage/lwlock.h"
15+
#include "utils/old_snapshot.h"
16+
#include "utils/snapmgr.h"
17+
#include "utils/timestamp.h"
18+
19+
/*
20+
* Backend-private copy of the information from oldSnapshotControl which relates
21+
* to the time to XID mapping, plus an index so that we can iterate.
22+
*
23+
* Note that the length of the xid_by_minute array is given by
24+
* OLD_SNAPSHOT_TIME_MAP_ENTRIES (which is not a compile-time constant).
25+
*/
26+
typedef struct
27+
{
28+
int current_index;
29+
int head_offset;
30+
TimestampTz head_timestamp;
31+
int count_used;
32+
TransactionId xid_by_minute[FLEXIBLE_ARRAY_MEMBER];
33+
} OldSnapshotTimeMapping;
34+
35+
#define NUM_TIME_MAPPING_COLUMNS 3
36+
37+
PG_MODULE_MAGIC;
38+
PG_FUNCTION_INFO_V1(pg_old_snapshot_time_mapping);
39+
40+
static OldSnapshotTimeMapping *GetOldSnapshotTimeMapping(void);
41+
static TupleDesc MakeOldSnapshotTimeMappingTupleDesc(void);
42+
static HeapTuple MakeOldSnapshotTimeMappingTuple(TupleDesc tupdesc,
43+
OldSnapshotTimeMapping *mapping);
44+
45+
/*
46+
* SQL-callable set-returning function.
47+
*/
48+
Datum
49+
pg_old_snapshot_time_mapping(PG_FUNCTION_ARGS)
50+
{
51+
FuncCallContext *funcctx;
52+
OldSnapshotTimeMapping *mapping;
53+
54+
if (SRF_IS_FIRSTCALL())
55+
{
56+
MemoryContext oldcontext;
57+
58+
funcctx = SRF_FIRSTCALL_INIT();
59+
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
60+
mapping = GetOldSnapshotTimeMapping();
61+
funcctx->user_fctx = mapping;
62+
funcctx->tuple_desc = MakeOldSnapshotTimeMappingTupleDesc();
63+
MemoryContextSwitchTo(oldcontext);
64+
}
65+
66+
funcctx = SRF_PERCALL_SETUP();
67+
mapping = (OldSnapshotTimeMapping *) funcctx->user_fctx;
68+
69+
while (mapping->current_index < mapping->count_used)
70+
{
71+
HeapTuple tuple;
72+
73+
tuple = MakeOldSnapshotTimeMappingTuple(funcctx->tuple_desc, mapping);
74+
++mapping->current_index;
75+
SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
76+
}
77+
78+
SRF_RETURN_DONE(funcctx);
79+
}
80+
81+
/*
82+
* Get the old snapshot time mapping data from shared memory.
83+
*/
84+
static OldSnapshotTimeMapping *
85+
GetOldSnapshotTimeMapping(void)
86+
{
87+
OldSnapshotTimeMapping *mapping;
88+
89+
mapping = palloc(offsetof(OldSnapshotTimeMapping, xid_by_minute)
90+
+ sizeof(TransactionId) * OLD_SNAPSHOT_TIME_MAP_ENTRIES);
91+
mapping->current_index = 0;
92+
93+
LWLockAcquire(OldSnapshotTimeMapLock, LW_SHARED);
94+
mapping->head_offset = oldSnapshotControl->head_offset;
95+
mapping->head_timestamp = oldSnapshotControl->head_timestamp;
96+
mapping->count_used = oldSnapshotControl->count_used;
97+
for (int i = 0; i < OLD_SNAPSHOT_TIME_MAP_ENTRIES; ++i)
98+
mapping->xid_by_minute[i] = oldSnapshotControl->xid_by_minute[i];
99+
LWLockRelease(OldSnapshotTimeMapLock);
100+
101+
return mapping;
102+
}
103+
104+
/*
105+
* Build a tuple descriptor for the pg_old_snapshot_time_mapping() SRF.
106+
*/
107+
static TupleDesc
108+
MakeOldSnapshotTimeMappingTupleDesc(void)
109+
{
110+
TupleDesc tupdesc;
111+
112+
tupdesc = CreateTemplateTupleDesc(NUM_TIME_MAPPING_COLUMNS);
113+
114+
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "array_offset",
115+
INT4OID, -1, 0);
116+
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "end_timestamp",
117+
TIMESTAMPTZOID, -1, 0);
118+
TupleDescInitEntry(tupdesc, (AttrNumber) 3, "newest_xmin",
119+
XIDOID, -1, 0);
120+
121+
return BlessTupleDesc(tupdesc);
122+
}
123+
124+
/*
125+
* Convert one entry from the old snapshot time mapping to a HeapTuple.
126+
*/
127+
static HeapTuple
128+
MakeOldSnapshotTimeMappingTuple(TupleDesc tupdesc, OldSnapshotTimeMapping *mapping)
129+
{
130+
Datum values[NUM_TIME_MAPPING_COLUMNS];
131+
bool nulls[NUM_TIME_MAPPING_COLUMNS];
132+
int array_position;
133+
TimestampTz timestamp;
134+
135+
/*
136+
* Figure out the array position corresponding to the current index.
137+
*
138+
* Index 0 means the oldest entry in the mapping, which is stored at
139+
* mapping->head_offset. Index 1 means the next-oldest entry, which is a the
140+
* following index, and so on. We wrap around when we reach the end of the array.
141+
*/
142+
array_position = (mapping->head_offset + mapping->current_index)
143+
% OLD_SNAPSHOT_TIME_MAP_ENTRIES;
144+
145+
/*
146+
* No explicit timestamp is stored for any entry other than the oldest one,
147+
* but each entry corresponds to 1-minute period, so we can just add.
148+
*/
149+
timestamp = TimestampTzPlusMilliseconds(mapping->head_timestamp,
150+
mapping->current_index * 60000);
151+
152+
/* Initialize nulls and values arrays. */
153+
memset(nulls, 0, sizeof(nulls));
154+
values[0] = Int32GetDatum(array_position);
155+
values[1] = TimestampTzGetDatum(timestamp);
156+
values[2] = TransactionIdGetDatum(mapping->xid_by_minute[array_position]);
157+
158+
return heap_form_tuple(tupdesc, values, nulls);
159+
}

doc/src/sgml/contrib.sgml

+1
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ CREATE EXTENSION <replaceable>module_name</replaceable>;
116116
&isn;
117117
&lo;
118118
&ltree;
119+
&oldsnapshot;
119120
&pageinspect;
120121
&passwordcheck;
121122
&pgbuffercache;

doc/src/sgml/filelist.sgml

+1
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@
129129
<!ENTITY lo SYSTEM "lo.sgml">
130130
<!ENTITY ltree SYSTEM "ltree.sgml">
131131
<!ENTITY oid2name SYSTEM "oid2name.sgml">
132+
<!ENTITY oldsnapshot SYSTEM "oldsnapshot.sgml">
132133
<!ENTITY pageinspect SYSTEM "pageinspect.sgml">
133134
<!ENTITY passwordcheck SYSTEM "passwordcheck.sgml">
134135
<!ENTITY pgbuffercache SYSTEM "pgbuffercache.sgml">

doc/src/sgml/oldsnapshot.sgml

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<!-- doc/src/sgml/oldsnapshot.sgml -->
2+
3+
<sect1 id="oldsnapshot" xreflabel="old_snapshot">
4+
<title>old_snapshot</title>
5+
6+
<indexterm zone="oldsnapshot">
7+
<primary>old_snapshot</primary>
8+
</indexterm>
9+
10+
<para>
11+
The <filename>old_snapshot</filename> module allows inspection
12+
of the server state that is used to implement
13+
<xref linkend="guc-old-snapshot-threshold" />.
14+
</para>
15+
16+
<sect2>
17+
<title>Functions</title>
18+
19+
<variablelist>
20+
<varlistentry>
21+
<term><function>pg_old_snapshot_time_mapping(array_offset OUT int4, end_timestamp OUT timestamptz, newest_xmin OUT xid) returns setof record</function></term>
22+
<listitem>
23+
<para>
24+
Returns all of the entries in the server's timestamp to XID mapping.
25+
Each entry represents the newest xmin of any snapshot taken in the
26+
corresponding minute.
27+
</para>
28+
</listitem>
29+
</varlistentry>
30+
</variablelist>
31+
</sect2>
32+
33+
</sect1>

0 commit comments

Comments
 (0)