mirror of
https://github.com/postgres/postgres.git
synced 2025-07-27 12:41:57 +03:00
Remove the "snapshot too old" feature.
Remove the old_snapshot_threshold setting and mechanism for producing
the error "snapshot too old", originally added by commit 848ef42b
.
Unfortunately it had a number of known problems in terms of correctness
and performance, mostly reported by Andres in the course of his work on
snapshot scalability. We agreed to remove it, after a long period
without an active plan to fix it.
This is certainly a desirable feature, and someone might propose a new
or improved implementation in the future.
Reported-by: Andres Freund <andres@anarazel.de>
Discussion: https://postgr.es/m/CACG%3DezYV%2BEvO135fLRdVn-ZusfVsTY6cH1OZqWtezuEYH6ciQA%40mail.gmail.com
Discussion: https://postgr.es/m/20200401064008.qob7bfnnbu4w5cw4%40alap3.anarazel.de
Discussion: https://postgr.es/m/CA%2BTgmoY%3Daqf0zjTD%2B3dUWYkgMiNDegDLFjo%2B6ze%3DWtpik%2B3XqA%40mail.gmail.com
This commit is contained in:
@ -29,7 +29,6 @@ SUBDIRS = \
|
||||
lo \
|
||||
ltree \
|
||||
oid2name \
|
||||
old_snapshot \
|
||||
pageinspect \
|
||||
passwordcheck \
|
||||
pg_buffercache \
|
||||
|
@ -132,7 +132,6 @@ blgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
|
||||
|
||||
LockBuffer(buffer, BUFFER_LOCK_SHARE);
|
||||
page = BufferGetPage(buffer);
|
||||
TestForOldSnapshot(scan->xs_snapshot, scan->indexRelation, page);
|
||||
|
||||
if (!PageIsNew(page) && !BloomPageIsDeleted(page))
|
||||
{
|
||||
|
@ -37,7 +37,6 @@ subdir('lo')
|
||||
subdir('ltree')
|
||||
subdir('ltree_plpython')
|
||||
subdir('oid2name')
|
||||
subdir('old_snapshot')
|
||||
subdir('pageinspect')
|
||||
subdir('passwordcheck')
|
||||
subdir('pg_buffercache')
|
||||
|
@ -1,21 +0,0 @@
|
||||
# contrib/old_snapshot/Makefile
|
||||
|
||||
MODULE_big = old_snapshot
|
||||
OBJS = \
|
||||
$(WIN32RES) \
|
||||
time_mapping.o
|
||||
|
||||
EXTENSION = old_snapshot
|
||||
DATA = old_snapshot--1.0.sql
|
||||
PGFILEDESC = "old_snapshot - utilities in support of old_snapshot_threshold"
|
||||
|
||||
ifdef USE_PGXS
|
||||
PG_CONFIG = pg_config
|
||||
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
||||
include $(PGXS)
|
||||
else
|
||||
subdir = contrib/old_snapshot
|
||||
top_builddir = ../..
|
||||
include $(top_builddir)/src/Makefile.global
|
||||
include $(top_srcdir)/contrib/contrib-global.mk
|
||||
endif
|
@ -1,23 +0,0 @@
|
||||
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
|
||||
|
||||
old_snapshot_sources = files(
|
||||
'time_mapping.c',
|
||||
)
|
||||
|
||||
if host_system == 'windows'
|
||||
old_snapshot_sources += rc_lib_gen.process(win32ver_rc, extra_args: [
|
||||
'--NAME', 'old_snapshot',
|
||||
'--FILEDESC', 'old_snapshot - utilities in support of old_snapshot_threshold',])
|
||||
endif
|
||||
|
||||
old_snapshot = shared_module('old_snapshot',
|
||||
old_snapshot_sources,
|
||||
kwargs: contrib_mod_args,
|
||||
)
|
||||
contrib_targets += old_snapshot
|
||||
|
||||
install_data(
|
||||
'old_snapshot.control',
|
||||
'old_snapshot--1.0.sql',
|
||||
kwargs: contrib_data_args,
|
||||
)
|
@ -1,14 +0,0 @@
|
||||
/* contrib/old_snapshot/old_snapshot--1.0.sql */
|
||||
|
||||
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||
\echo Use "CREATE EXTENSION old_snapshot" to load this file. \quit
|
||||
|
||||
-- Show visibility map and page-level visibility information for each block.
|
||||
CREATE FUNCTION pg_old_snapshot_time_mapping(array_offset OUT int4,
|
||||
end_timestamp OUT timestamptz,
|
||||
newest_xmin OUT xid)
|
||||
RETURNS SETOF record
|
||||
AS 'MODULE_PATHNAME', 'pg_old_snapshot_time_mapping'
|
||||
LANGUAGE C STRICT;
|
||||
|
||||
-- XXX. Do we want REVOKE commands here?
|
@ -1,5 +0,0 @@
|
||||
# old_snapshot extension
|
||||
comment = 'utilities in support of old_snapshot_threshold'
|
||||
default_version = '1.0'
|
||||
module_pathname = '$libdir/old_snapshot'
|
||||
relocatable = true
|
@ -1,142 +0,0 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* time_mapping.c
|
||||
* time to XID mapping information
|
||||
*
|
||||
* Copyright (c) 2020-2023, PostgreSQL Global Development Group
|
||||
*
|
||||
* contrib/old_snapshot/time_mapping.c
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "funcapi.h"
|
||||
#include "storage/lwlock.h"
|
||||
#include "utils/old_snapshot.h"
|
||||
#include "utils/snapmgr.h"
|
||||
#include "utils/timestamp.h"
|
||||
|
||||
/*
|
||||
* Backend-private copy of the information from oldSnapshotControl which relates
|
||||
* to the time to XID mapping, plus an index so that we can iterate.
|
||||
*
|
||||
* Note that the length of the xid_by_minute array is given by
|
||||
* OLD_SNAPSHOT_TIME_MAP_ENTRIES (which is not a compile-time constant).
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int current_index;
|
||||
int head_offset;
|
||||
TimestampTz head_timestamp;
|
||||
int count_used;
|
||||
TransactionId xid_by_minute[FLEXIBLE_ARRAY_MEMBER];
|
||||
} OldSnapshotTimeMapping;
|
||||
|
||||
#define NUM_TIME_MAPPING_COLUMNS 3
|
||||
|
||||
PG_MODULE_MAGIC;
|
||||
PG_FUNCTION_INFO_V1(pg_old_snapshot_time_mapping);
|
||||
|
||||
static OldSnapshotTimeMapping *GetOldSnapshotTimeMapping(void);
|
||||
static HeapTuple MakeOldSnapshotTimeMappingTuple(TupleDesc tupdesc,
|
||||
OldSnapshotTimeMapping *mapping);
|
||||
|
||||
/*
|
||||
* SQL-callable set-returning function.
|
||||
*/
|
||||
Datum
|
||||
pg_old_snapshot_time_mapping(PG_FUNCTION_ARGS)
|
||||
{
|
||||
FuncCallContext *funcctx;
|
||||
OldSnapshotTimeMapping *mapping;
|
||||
|
||||
if (SRF_IS_FIRSTCALL())
|
||||
{
|
||||
MemoryContext oldcontext;
|
||||
TupleDesc tupdesc;
|
||||
|
||||
funcctx = SRF_FIRSTCALL_INIT();
|
||||
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
|
||||
mapping = GetOldSnapshotTimeMapping();
|
||||
funcctx->user_fctx = mapping;
|
||||
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
|
||||
elog(ERROR, "return type must be a row type");
|
||||
funcctx->tuple_desc = tupdesc;
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
}
|
||||
|
||||
funcctx = SRF_PERCALL_SETUP();
|
||||
mapping = (OldSnapshotTimeMapping *) funcctx->user_fctx;
|
||||
|
||||
while (mapping->current_index < mapping->count_used)
|
||||
{
|
||||
HeapTuple tuple;
|
||||
|
||||
tuple = MakeOldSnapshotTimeMappingTuple(funcctx->tuple_desc, mapping);
|
||||
++mapping->current_index;
|
||||
SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
|
||||
}
|
||||
|
||||
SRF_RETURN_DONE(funcctx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the old snapshot time mapping data from shared memory.
|
||||
*/
|
||||
static OldSnapshotTimeMapping *
|
||||
GetOldSnapshotTimeMapping(void)
|
||||
{
|
||||
OldSnapshotTimeMapping *mapping;
|
||||
|
||||
mapping = palloc(offsetof(OldSnapshotTimeMapping, xid_by_minute)
|
||||
+ sizeof(TransactionId) * OLD_SNAPSHOT_TIME_MAP_ENTRIES);
|
||||
mapping->current_index = 0;
|
||||
|
||||
LWLockAcquire(OldSnapshotTimeMapLock, LW_SHARED);
|
||||
mapping->head_offset = oldSnapshotControl->head_offset;
|
||||
mapping->head_timestamp = oldSnapshotControl->head_timestamp;
|
||||
mapping->count_used = oldSnapshotControl->count_used;
|
||||
for (int i = 0; i < OLD_SNAPSHOT_TIME_MAP_ENTRIES; ++i)
|
||||
mapping->xid_by_minute[i] = oldSnapshotControl->xid_by_minute[i];
|
||||
LWLockRelease(OldSnapshotTimeMapLock);
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert one entry from the old snapshot time mapping to a HeapTuple.
|
||||
*/
|
||||
static HeapTuple
|
||||
MakeOldSnapshotTimeMappingTuple(TupleDesc tupdesc, OldSnapshotTimeMapping *mapping)
|
||||
{
|
||||
Datum values[NUM_TIME_MAPPING_COLUMNS];
|
||||
bool nulls[NUM_TIME_MAPPING_COLUMNS];
|
||||
int array_position;
|
||||
TimestampTz timestamp;
|
||||
|
||||
/*
|
||||
* Figure out the array position corresponding to the current index.
|
||||
*
|
||||
* Index 0 means the oldest entry in the mapping, which is stored at
|
||||
* mapping->head_offset. Index 1 means the next-oldest entry, which is a
|
||||
* the following index, and so on. We wrap around when we reach the end of
|
||||
* the array.
|
||||
*/
|
||||
array_position = (mapping->head_offset + mapping->current_index)
|
||||
% OLD_SNAPSHOT_TIME_MAP_ENTRIES;
|
||||
|
||||
/*
|
||||
* No explicit timestamp is stored for any entry other than the oldest
|
||||
* one, but each entry corresponds to 1-minute period, so we can just add.
|
||||
*/
|
||||
timestamp = TimestampTzPlusMilliseconds(mapping->head_timestamp,
|
||||
mapping->current_index * 60000);
|
||||
|
||||
/* Initialize nulls and values arrays. */
|
||||
memset(nulls, 0, sizeof(nulls));
|
||||
values[0] = Int32GetDatum(array_position);
|
||||
values[1] = TimestampTzGetDatum(timestamp);
|
||||
values[2] = TransactionIdGetDatum(mapping->xid_by_minute[array_position]);
|
||||
|
||||
return heap_form_tuple(tupdesc, values, nulls);
|
||||
}
|
Reference in New Issue
Block a user