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

Commit 737a292

Browse files
committed
tableam: VACUUM and ANALYZE support.
This is a relatively straightforward move of the current implementation to sit below tableam. As the current analyze sampling implementation is pretty inherently block based, the tableam analyze interface is as well. It might make sense to generalize that at some point, but that seems like a larger project that shouldn't be undertaken at the same time as the introduction of tableam. Author: Andres Freund Discussion: https://postgr.es/m/20180703070645.wchpu5muyto5n647@alap3.anarazel.de
1 parent 0f5493f commit 737a292

File tree

4 files changed

+324
-169
lines changed

4 files changed

+324
-169
lines changed

src/backend/access/heap/heapam_handler.c

+170
Original file line numberDiff line numberDiff line change
@@ -921,6 +921,173 @@ heapam_relation_copy_for_cluster(Relation OldHeap, Relation NewHeap,
921921
pfree(isnull);
922922
}
923923

924+
static bool
925+
heapam_scan_analyze_next_block(TableScanDesc sscan, BlockNumber blockno,
926+
BufferAccessStrategy bstrategy)
927+
{
928+
HeapScanDesc scan = (HeapScanDesc) sscan;
929+
930+
/*
931+
* We must maintain a pin on the target page's buffer to ensure that
932+
* concurrent activity - e.g. HOT pruning - doesn't delete tuples out from
933+
* under us. Hence, pin the page until we are done looking at it. We
934+
* also choose to hold sharelock on the buffer throughout --- we could
935+
* release and re-acquire sharelock for each tuple, but since we aren't
936+
* doing much work per tuple, the extra lock traffic is probably better
937+
* avoided.
938+
*/
939+
scan->rs_cblock = blockno;
940+
scan->rs_cindex = FirstOffsetNumber;
941+
scan->rs_cbuf = ReadBufferExtended(scan->rs_base.rs_rd, MAIN_FORKNUM,
942+
blockno, RBM_NORMAL, bstrategy);
943+
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
944+
945+
/* in heap all blocks can contain tuples, so always return true */
946+
return true;
947+
}
948+
949+
static bool
950+
heapam_scan_analyze_next_tuple(TableScanDesc sscan, TransactionId OldestXmin,
951+
double *liverows, double *deadrows,
952+
TupleTableSlot *slot)
953+
{
954+
HeapScanDesc scan = (HeapScanDesc) sscan;
955+
Page targpage;
956+
OffsetNumber maxoffset;
957+
BufferHeapTupleTableSlot *hslot;
958+
959+
Assert(TTS_IS_BUFFERTUPLE(slot));
960+
961+
hslot = (BufferHeapTupleTableSlot *) slot;
962+
targpage = BufferGetPage(scan->rs_cbuf);
963+
maxoffset = PageGetMaxOffsetNumber(targpage);
964+
965+
/* Inner loop over all tuples on the selected page */
966+
for (; scan->rs_cindex <= maxoffset; scan->rs_cindex++)
967+
{
968+
ItemId itemid;
969+
HeapTuple targtuple = &hslot->base.tupdata;
970+
bool sample_it = false;
971+
972+
itemid = PageGetItemId(targpage, scan->rs_cindex);
973+
974+
/*
975+
* We ignore unused and redirect line pointers. DEAD line pointers
976+
* should be counted as dead, because we need vacuum to run to get rid
977+
* of them. Note that this rule agrees with the way that
978+
* heap_page_prune() counts things.
979+
*/
980+
if (!ItemIdIsNormal(itemid))
981+
{
982+
if (ItemIdIsDead(itemid))
983+
*deadrows += 1;
984+
continue;
985+
}
986+
987+
ItemPointerSet(&targtuple->t_self, scan->rs_cblock, scan->rs_cindex);
988+
989+
targtuple->t_tableOid = RelationGetRelid(scan->rs_base.rs_rd);
990+
targtuple->t_data = (HeapTupleHeader) PageGetItem(targpage, itemid);
991+
targtuple->t_len = ItemIdGetLength(itemid);
992+
993+
switch (HeapTupleSatisfiesVacuum(targtuple, OldestXmin, scan->rs_cbuf))
994+
{
995+
case HEAPTUPLE_LIVE:
996+
sample_it = true;
997+
*liverows += 1;
998+
break;
999+
1000+
case HEAPTUPLE_DEAD:
1001+
case HEAPTUPLE_RECENTLY_DEAD:
1002+
/* Count dead and recently-dead rows */
1003+
*deadrows += 1;
1004+
break;
1005+
1006+
case HEAPTUPLE_INSERT_IN_PROGRESS:
1007+
1008+
/*
1009+
* Insert-in-progress rows are not counted. We assume that
1010+
* when the inserting transaction commits or aborts, it will
1011+
* send a stats message to increment the proper count. This
1012+
* works right only if that transaction ends after we finish
1013+
* analyzing the table; if things happen in the other order,
1014+
* its stats update will be overwritten by ours. However, the
1015+
* error will be large only if the other transaction runs long
1016+
* enough to insert many tuples, so assuming it will finish
1017+
* after us is the safer option.
1018+
*
1019+
* A special case is that the inserting transaction might be
1020+
* our own. In this case we should count and sample the row,
1021+
* to accommodate users who load a table and analyze it in one
1022+
* transaction. (pgstat_report_analyze has to adjust the
1023+
* numbers we send to the stats collector to make this come
1024+
* out right.)
1025+
*/
1026+
if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(targtuple->t_data)))
1027+
{
1028+
sample_it = true;
1029+
*liverows += 1;
1030+
}
1031+
break;
1032+
1033+
case HEAPTUPLE_DELETE_IN_PROGRESS:
1034+
1035+
/*
1036+
* We count and sample delete-in-progress rows the same as
1037+
* live ones, so that the stats counters come out right if the
1038+
* deleting transaction commits after us, per the same
1039+
* reasoning given above.
1040+
*
1041+
* If the delete was done by our own transaction, however, we
1042+
* must count the row as dead to make pgstat_report_analyze's
1043+
* stats adjustments come out right. (Note: this works out
1044+
* properly when the row was both inserted and deleted in our
1045+
* xact.)
1046+
*
1047+
* The net effect of these choices is that we act as though an
1048+
* IN_PROGRESS transaction hasn't happened yet, except if it
1049+
* is our own transaction, which we assume has happened.
1050+
*
1051+
* This approach ensures that we behave sanely if we see both
1052+
* the pre-image and post-image rows for a row being updated
1053+
* by a concurrent transaction: we will sample the pre-image
1054+
* but not the post-image. We also get sane results if the
1055+
* concurrent transaction never commits.
1056+
*/
1057+
if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetUpdateXid(targtuple->t_data)))
1058+
deadrows += 1;
1059+
else
1060+
{
1061+
sample_it = true;
1062+
liverows += 1;
1063+
}
1064+
break;
1065+
1066+
default:
1067+
elog(ERROR, "unexpected HeapTupleSatisfiesVacuum result");
1068+
break;
1069+
}
1070+
1071+
if (sample_it)
1072+
{
1073+
ExecStoreBufferHeapTuple(targtuple, slot, scan->rs_cbuf);
1074+
scan->rs_cindex++;
1075+
1076+
/* note that we leave the buffer locked here! */
1077+
return true;
1078+
}
1079+
}
1080+
1081+
/* Now release the lock and pin on the page */
1082+
UnlockReleaseBuffer(scan->rs_cbuf);
1083+
scan->rs_cbuf = InvalidBuffer;
1084+
1085+
/* also prevent old slot contents from having pin on page */
1086+
ExecClearTuple(slot);
1087+
1088+
return false;
1089+
}
1090+
9241091
static double
9251092
heapam_index_build_range_scan(Relation heapRelation,
9261093
Relation indexRelation,
@@ -1743,6 +1910,9 @@ static const TableAmRoutine heapam_methods = {
17431910
.relation_nontransactional_truncate = heapam_relation_nontransactional_truncate,
17441911
.relation_copy_data = heapam_relation_copy_data,
17451912
.relation_copy_for_cluster = heapam_relation_copy_for_cluster,
1913+
.relation_vacuum = heap_vacuum_rel,
1914+
.scan_analyze_next_block = heapam_scan_analyze_next_block,
1915+
.scan_analyze_next_tuple = heapam_scan_analyze_next_tuple,
17461916
.index_build_range_scan = heapam_index_build_range_scan,
17471917
.index_validate_scan = heapam_index_validate_scan,
17481918
};

0 commit comments

Comments
 (0)