1
0
mirror of https://github.com/postgres/postgres.git synced 2025-12-21 05:21:08 +03:00

Merge prune, freeze and vacuum WAL record formats

The new combined WAL record is now used for pruning, freezing and 2nd
pass of vacuum. This is in preparation for changing VACUUM to write a
combined prune+freeze record per page, instead of separate two
records. The new WAL record format now supports that, but the code
still always writes separate records for pruning and freezing.

This reserves separate XLOG_HEAP2_* info codes for when the pruning
record is emitted for on-access pruning or VACUUM, per Peter
Geoghegan's suggestion. The record format is identical, but having
separate info codes makes it easier analyze pruning and vacuuming with
pg_waldump.

The function to emit the new WAL record, log_heap_prune_and_freeze(),
is in pruneheap.c. The existing heap_log_freeze_plan() and its
subroutines are moved to pruneheap.c without changes, to keep them
together with log_heap_prune_and_freeze().

Author: Melanie Plageman <melanieplageman@gmail.com>
Discussion: https://www.postgresql.org/message-id/CAAKRu_azf-zH%3DDgVbquZ3tFWjMY1w5pO8m-TXJaMdri8z3933g@mail.gmail.com
Discussion: https://www.postgresql.org/message-id/CAAKRu_b2oE4GL%3Dq4g9mcByS9yT7wTQvEH9OLpabj28e%2BWKFi2A@mail.gmail.com
This commit is contained in:
Heikki Linnakangas
2024-03-25 14:59:58 +02:00
parent d44032d014
commit f83d709760
11 changed files with 799 additions and 594 deletions

View File

@@ -74,7 +74,7 @@ truncate_flags_desc(StringInfo buf, uint8 flags)
static void
plan_elem_desc(StringInfo buf, void *plan, void *data)
{
xl_heap_freeze_plan *new_plan = (xl_heap_freeze_plan *) plan;
xlhp_freeze_plan *new_plan = (xlhp_freeze_plan *) plan;
OffsetNumber **offsets = data;
appendStringInfo(buf, "{ xmax: %u, infomask: %u, infomask2: %u, ntuples: %u",
@@ -91,6 +91,94 @@ plan_elem_desc(StringInfo buf, void *plan, void *data)
appendStringInfoString(buf, " }");
}
/*
* Given a MAXALIGNed buffer returned by XLogRecGetBlockData() and pointed to
* by cursor and any xl_heap_prune flags, deserialize the arrays of
* OffsetNumbers contained in an XLOG_HEAP2_PRUNE_* record.
*
* This is in heapdesc.c so it can be shared between heap2_redo and heap2_desc
* code, the latter of which is used in frontend (pg_waldump) code.
*/
void
heap_xlog_deserialize_prune_and_freeze(char *cursor, uint8 flags,
int *nplans, xlhp_freeze_plan **plans,
OffsetNumber **frz_offsets,
int *nredirected, OffsetNumber **redirected,
int *ndead, OffsetNumber **nowdead,
int *nunused, OffsetNumber **nowunused)
{
if (flags & XLHP_HAS_FREEZE_PLANS)
{
xlhp_freeze_plans *freeze_plans = (xlhp_freeze_plans *) cursor;
*nplans = freeze_plans->nplans;
Assert(*nplans > 0);
*plans = freeze_plans->plans;
cursor += offsetof(xlhp_freeze_plans, plans);
cursor += sizeof(xlhp_freeze_plan) * *nplans;
}
else
{
*nplans = 0;
*plans = NULL;
}
if (flags & XLHP_HAS_REDIRECTIONS)
{
xlhp_prune_items *subrecord = (xlhp_prune_items *) cursor;
*nredirected = subrecord->ntargets;
Assert(*nredirected > 0);
*redirected = &subrecord->data[0];
cursor += offsetof(xlhp_prune_items, data);
cursor += sizeof(OffsetNumber[2]) * *nredirected;
}
else
{
*nredirected = 0;
*redirected = NULL;
}
if (flags & XLHP_HAS_DEAD_ITEMS)
{
xlhp_prune_items *subrecord = (xlhp_prune_items *) cursor;
*ndead = subrecord->ntargets;
Assert(*ndead > 0);
*nowdead = subrecord->data;
cursor += offsetof(xlhp_prune_items, data);
cursor += sizeof(OffsetNumber) * *ndead;
}
else
{
*ndead = 0;
*nowdead = NULL;
}
if (flags & XLHP_HAS_NOW_UNUSED_ITEMS)
{
xlhp_prune_items *subrecord = (xlhp_prune_items *) cursor;
*nunused = subrecord->ntargets;
Assert(*nunused > 0);
*nowunused = subrecord->data;
cursor += offsetof(xlhp_prune_items, data);
cursor += sizeof(OffsetNumber) * *nunused;
}
else
{
*nunused = 0;
*nowunused = NULL;
}
*frz_offsets = (OffsetNumber *) cursor;
}
void
heap_desc(StringInfo buf, XLogReaderState *record)
{
@@ -175,86 +263,76 @@ heap2_desc(StringInfo buf, XLogReaderState *record)
uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
info &= XLOG_HEAP_OPMASK;
if (info == XLOG_HEAP2_PRUNE)
if (info == XLOG_HEAP2_PRUNE_ON_ACCESS ||
info == XLOG_HEAP2_PRUNE_VACUUM_SCAN ||
info == XLOG_HEAP2_PRUNE_VACUUM_CLEANUP)
{
xl_heap_prune *xlrec = (xl_heap_prune *) rec;
appendStringInfo(buf, "snapshotConflictHorizon: %u, nredirected: %u, ndead: %u, isCatalogRel: %c",
xlrec->snapshotConflictHorizon,
xlrec->nredirected,
xlrec->ndead,
xlrec->isCatalogRel ? 'T' : 'F');
if (xlrec->flags & XLHP_HAS_CONFLICT_HORIZON)
{
TransactionId conflict_xid;
memcpy(&conflict_xid, rec + SizeOfHeapPrune, sizeof(TransactionId));
appendStringInfo(buf, "snapshotConflictHorizon: %u",
conflict_xid);
}
appendStringInfo(buf, ", isCatalogRel: %c",
xlrec->flags & XLHP_IS_CATALOG_REL ? 'T' : 'F');
if (XLogRecHasBlockData(record, 0))
{
OffsetNumber *end;
Size datalen;
OffsetNumber *redirected;
OffsetNumber *nowdead;
OffsetNumber *nowunused;
int nredirected;
int nunused;
Size datalen;
int ndead;
int nplans;
xlhp_freeze_plan *plans;
OffsetNumber *frz_offsets;
redirected = (OffsetNumber *) XLogRecGetBlockData(record, 0,
&datalen);
char *cursor = XLogRecGetBlockData(record, 0, &datalen);
nredirected = xlrec->nredirected;
end = (OffsetNumber *) ((char *) redirected + datalen);
nowdead = redirected + (nredirected * 2);
nowunused = nowdead + xlrec->ndead;
nunused = (end - nowunused);
Assert(nunused >= 0);
heap_xlog_deserialize_prune_and_freeze(cursor, xlrec->flags,
&nplans, &plans, &frz_offsets,
&nredirected, &redirected,
&ndead, &nowdead,
&nunused, &nowunused);
appendStringInfo(buf, ", nunused: %d", nunused);
appendStringInfo(buf, ", nplans: %u, nredirected: %u, ndead: %u, nunused: %u",
nplans, nredirected, ndead, nunused);
appendStringInfoString(buf, ", redirected:");
array_desc(buf, redirected, sizeof(OffsetNumber) * 2,
nredirected, &redirect_elem_desc, NULL);
appendStringInfoString(buf, ", dead:");
array_desc(buf, nowdead, sizeof(OffsetNumber), xlrec->ndead,
&offset_elem_desc, NULL);
appendStringInfoString(buf, ", unused:");
array_desc(buf, nowunused, sizeof(OffsetNumber), nunused,
&offset_elem_desc, NULL);
}
}
else if (info == XLOG_HEAP2_VACUUM)
{
xl_heap_vacuum *xlrec = (xl_heap_vacuum *) rec;
if (nplans > 0)
{
appendStringInfoString(buf, ", plans:");
array_desc(buf, plans, sizeof(xlhp_freeze_plan), nplans,
&plan_elem_desc, &frz_offsets);
}
appendStringInfo(buf, "nunused: %u", xlrec->nunused);
if (nredirected > 0)
{
appendStringInfoString(buf, ", redirected:");
array_desc(buf, redirected, sizeof(OffsetNumber) * 2,
nredirected, &redirect_elem_desc, NULL);
}
if (XLogRecHasBlockData(record, 0))
{
OffsetNumber *nowunused;
if (ndead > 0)
{
appendStringInfoString(buf, ", dead:");
array_desc(buf, nowdead, sizeof(OffsetNumber), ndead,
&offset_elem_desc, NULL);
}
nowunused = (OffsetNumber *) XLogRecGetBlockData(record, 0, NULL);
appendStringInfoString(buf, ", unused:");
array_desc(buf, nowunused, sizeof(OffsetNumber), xlrec->nunused,
&offset_elem_desc, NULL);
}
}
else if (info == XLOG_HEAP2_FREEZE_PAGE)
{
xl_heap_freeze_page *xlrec = (xl_heap_freeze_page *) rec;
appendStringInfo(buf, "snapshotConflictHorizon: %u, nplans: %u, isCatalogRel: %c",
xlrec->snapshotConflictHorizon, xlrec->nplans,
xlrec->isCatalogRel ? 'T' : 'F');
if (XLogRecHasBlockData(record, 0))
{
xl_heap_freeze_plan *plans;
OffsetNumber *offsets;
plans = (xl_heap_freeze_plan *) XLogRecGetBlockData(record, 0, NULL);
offsets = (OffsetNumber *) ((char *) plans +
(xlrec->nplans *
sizeof(xl_heap_freeze_plan)));
appendStringInfoString(buf, ", plans:");
array_desc(buf, plans, sizeof(xl_heap_freeze_plan), xlrec->nplans,
&plan_elem_desc, &offsets);
if (nunused > 0)
{
appendStringInfoString(buf, ", unused:");
array_desc(buf, nowunused, sizeof(OffsetNumber), nunused,
&offset_elem_desc, NULL);
}
}
}
else if (info == XLOG_HEAP2_VISIBLE)
@@ -355,14 +433,14 @@ heap2_identify(uint8 info)
switch (info & ~XLR_INFO_MASK)
{
case XLOG_HEAP2_PRUNE:
id = "PRUNE";
case XLOG_HEAP2_PRUNE_ON_ACCESS:
id = "PRUNE_ON_ACCESS";
break;
case XLOG_HEAP2_VACUUM:
id = "VACUUM";
case XLOG_HEAP2_PRUNE_VACUUM_SCAN:
id = "PRUNE_VACUUM_SCAN";
break;
case XLOG_HEAP2_FREEZE_PAGE:
id = "FREEZE_PAGE";
case XLOG_HEAP2_PRUNE_VACUUM_CLEANUP:
id = "PRUNE_VACUUM_CLEANUP";
break;
case XLOG_HEAP2_VISIBLE:
id = "VISIBLE";