pg.ddx.io  pgsql-hackers@postgresql.org mailing list archive  
help / color / mirror / Atom feed
From: Andrey V. Lepikhov <a.lepikhov@postgrespro.ru>
To: pgsql-hackers@postgresql.org
Subject: [WIP] [B-Tree] Retail IndexTuple deletion
Date: Mon, 18 Jun 2018 09:39:23 +0500
Message-ID: <425db134-8bba-005c-b59d-56e50de3b41e@postgrespro.ru> (raw)

Hi,
I have written a code for quick indextuple deletion from an relation by 
heap tuple TID. The code relate to "Retail IndexTuple deletion" 
enhancement of btree index on postgresql wiki [1].
Briefly, it includes three steps:
1. Key generation for index tuple searching.
2. Index relation search for tuple with the heap tuple TID.
3. Deletion of the tuple from the index relation.

Now, index relation cleaning performs by vacuum which scan all index 
relation for dead entries sequentially, tuple-by-tuple. This simplistic 
and safe method can be significantly surpasses in the cases, than a 
number of dead entries is not large by retail deletions which uses index 
scans for search dead entries. Also, it can be used by distributed 
systems for reduce cost of a global index vacuum.

Patch '0001-retail-indextuple-deletion' introduce new function 
amtargetdelete() in access method interface. Patch 
'0002-quick-vacuum-strategy' implements this function for an alternative 
strategy of lazy index vacuum, called 'Quick Vacuum'.

The code demands hold DEAD tuple storage until scan key will be created. 
In this version I add 'target_index_deletion_factor' option. If it more 
than 0, heap_page_prune() uses ItemIdMarkDead() instead of 
ItemIdSetDead() function for set DEAD flag and hold the tuple storage.
Next step is developing background worker which will collect pairs (tid, 
scankey) of DEAD tuples from heap_page_prune() function.

Here are test description and some execution time measurements results 
showing the benefit of this patches:

Test:
-----
create table test_range(id serial primary key, value integer);
insert into test_range (value) select random()*1e7/10^N from 
generate_series(1, 1e7);
DELETE FROM test_range WHERE value=1;
VACUUM test_range;

Results:
--------

| n | t1, s  | t2, s  | speedup |
|---|---------------------------|
| 0 | 0.00003| 0.4476 | 1748.4  |
| 1 | 0.00006| 0.5367 | 855.99  |
| 2 | 0.0004 | 0.9804 | 233.99  |
| 3 | 0.0048 | 1.6493 | 34.576  |
| 4 | 0.5600 | 2.4854 | 4.4382  |
| 5 | 3.3300 | 3.9555 | 1.2012  |
| 6 | 17.700 | 5.6000 | 0.3164  |
|---|---------------------------|
in the table, t1 - measured execution time of lazy_vacuum_index() 
function by Quick-Vacuum strategy; t2 - measured execution time of 
lazy_vacuum_index() function by Lazy-Vacuum strategy;

Note, guaranteed allowable time of index scans (used for quick deletion) 
will be achieved by storing equal-key index tuples in physical TID order 
[2] with patch [3].

[1] 
https://wiki.postgresql.org/wiki/Key_normalization#Retail_IndexTuple_deletion
[2] 
https://wiki.postgresql.org/wiki/Key_normalization#Making_all_items_in_the_index_unique_by_treating_...
[3] 
https://www.postgresql.org/message-id/CAH2-WzkVb0Kom%3DR%2B88fDFb%3DJSxZMFvbHVC6Mn9LJ2n%3DX%3DkS-Uw%...

--
Andrey Lepikhov
Postgres Professional:
https://postgrespro.com
The Russian Postgres Company

Attachments: download all (2 files, .tar.gz)

0001-retail-indextuple-deletion.patch (text/x-patch, 12.0K) download diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c index 6b2b9e3..96f1d47 100644 --- a/contrib/bloom/blutils.c +++ b/contrib/bloom/blutils.c @@ -126,6 +126,7 @@ blhandler(PG_FUNCTION_ARGS) amroutine->ambuild = blbuild; amroutine->ambuildempty = blbuildempty; amroutine->aminsert = blinsert; + amroutine->amtargetdelete = NULL; amroutine->ambulkdelete = blbulkdelete; amroutine->amvacuumcleanup = blvacuumcleanup; amroutine->amcanreturn = NULL; diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index e95fbbc..a0e06bd 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -103,6 +103,7 @@ brinhandler(PG_FUNCTION_ARGS) amroutine->ambuild = brinbuild; amroutine->ambuildempty = brinbuildempty; amroutine->aminsert = brininsert; + amroutine->amtargetdelete = NULL; amroutine->ambulkdelete = brinbulkdelete; amroutine->amvacuumcleanup = brinvacuumcleanup; amroutine->amcanreturn = NULL; diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c index 0a32182..acf14e7 100644 --- a/src/backend/access/gin/ginutil.c +++ b/src/backend/access/gin/ginutil.c @@ -58,6 +58,7 @@ ginhandler(PG_FUNCTION_ARGS) amroutine->ambuild = ginbuild; amroutine->ambuildempty = ginbuildempty; amroutine->aminsert = gininsert; + amroutine->amtargetdelete = NULL; amroutine->ambulkdelete = ginbulkdelete; amroutine->amvacuumcleanup = ginvacuumcleanup; amroutine->amcanreturn = NULL; diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c index 8a42eff..d7a53d2 100644 --- a/src/backend/access/gist/gist.c +++ b/src/backend/access/gist/gist.c @@ -80,6 +80,7 @@ gisthandler(PG_FUNCTION_ARGS) amroutine->ambuild = gistbuild; amroutine->ambuildempty = gistbuildempty; amroutine->aminsert = gistinsert; + amroutine->amtargetdelete = NULL; amroutine->ambulkdelete = gistbulkdelete; amroutine->amvacuumcleanup = gistvacuumcleanup; amroutine->amcanreturn = gistcanreturn; diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c index 0002df3..5fb32d6 100644 --- a/src/backend/access/hash/hash.c +++ b/src/backend/access/hash/hash.c @@ -76,6 +76,7 @@ hashhandler(PG_FUNCTION_ARGS) amroutine->ambuild = hashbuild; amroutine->ambuildempty = hashbuildempty; amroutine->aminsert = hashinsert; + amroutine->amtargetdelete = NULL; amroutine->ambulkdelete = hashbulkdelete; amroutine->amvacuumcleanup = hashvacuumcleanup; amroutine->amcanreturn = NULL; diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c index 22b5cc9..a25d844 100644 --- a/src/backend/access/index/indexam.c +++ b/src/backend/access/index/indexam.c @@ -730,6 +730,23 @@ index_getbitmap(IndexScanDesc scan, TIDBitmap *bitmap) return ntids; } +IndexBulkDeleteResult * +index_target_delete(IndexVacuumInfo *info, + IndexBulkDeleteResult *stats, + Relation hrel, + Datum *values, + bool *isnull, + ItemPointer htid) +{ + Relation indexRelation = info->index; + + RELATION_CHECKS; + + CHECK_REL_PROCEDURE(amtargetdelete); + + return indexRelation->rd_amroutine->amtargetdelete(info, stats, hrel, values, isnull, htid); +} + /* ---------------- * index_bulk_delete - do mass deletion of index entries * diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c index 27a3032..dd87a05 100644 --- a/src/backend/access/nbtree/nbtree.c +++ b/src/backend/access/nbtree/nbtree.c @@ -34,7 +34,9 @@ #include "storage/smgr.h" #include "utils/builtins.h" #include "utils/index_selfuncs.h" +#include "utils/lsyscache.h" #include "utils/memutils.h" +#include "utils/syscache.h" /* Working state needed by btvacuumpage */ @@ -127,6 +129,7 @@ bthandler(PG_FUNCTION_ARGS) amroutine->ambuild = btbuild; amroutine->ambuildempty = btbuildempty; amroutine->aminsert = btinsert; + amroutine->amtargetdelete = bttargetdelete; amroutine->ambulkdelete = btbulkdelete; amroutine->amvacuumcleanup = btvacuumcleanup; amroutine->amcanreturn = btcanreturn; @@ -884,6 +887,195 @@ btbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, return stats; } +static void +cleanup_block(IndexVacuumInfo *info, + IndexBulkDeleteResult *stats, BlockNumber blkno) +{ + BTVacState vstate; + + vstate.info = info; + vstate.stats = stats; + vstate.callback = NULL; + vstate.callback_state = NULL; + vstate.cycleid = 0; + vstate.lastBlockVacuumed = BTREE_METAPAGE; /* Initialise at first block */ + vstate.lastBlockLocked = BTREE_METAPAGE; + vstate.totFreePages = 0; + vstate.oldestBtpoXact = InvalidTransactionId; + + /* Create a temporary memory context to run _bt_pagedel in */ + vstate.pagedelcontext = AllocSetContextCreate(CurrentMemoryContext, + "_bt_pagedel", + ALLOCSET_DEFAULT_SIZES); + btvacuumpage(&vstate, blkno, blkno); + + if (vstate.totFreePages > 0) + IndexFreeSpaceMapVacuum(info->index); + + stats->pages_free += vstate.totFreePages; + + MemoryContextDelete(vstate.pagedelcontext); +} + +/* + * Build scan key for an index relation by a heap tuple TID + */ +static int +build_scan_key(ScanKey skey, Relation rel, Relation irel, Datum *values, bool *nulls) +{ + TupleDesc desc = RelationGetDescr(rel); + HeapTuple tuple; + Datum indclassDatum; + int attoff; + oidvector* opclass; + int2vector* indkey = &irel->rd_index->indkey; + bool isnull; + + tuple = heap_form_tuple(desc, values, nulls); + indclassDatum = SysCacheGetAttr(INDEXRELID, irel->rd_indextuple, + Anum_pg_index_indclass, &isnull); + + opclass = (oidvector *) DatumGetPointer(indclassDatum); + for (attoff = 0; attoff < IndexRelationGetNumberOfKeyAttributes(irel); attoff++) + { + Oid operator; + Oid opfamily; + RegProcedure regop; + int pkattno = attoff + 1; + int mainattno = indkey->values[attoff]; + Oid optype = get_opclass_input_type(opclass->values[attoff]); + Datum scanvalue; + StrategyNumber strategy; + + if (mainattno > 0) + scanvalue = heap_getattr(tuple, mainattno, desc, &isnull); + else + return -1; + + /* + * Load the operator info. We need this to get the equality operator + * function for the scan key. + */ + opfamily = get_opclass_family(opclass->values[attoff]); + + if (irel->rd_exclstrats != NULL) + strategy = irel->rd_exclstrats[attoff]; + else + strategy = BTEqualStrategyNumber; + + operator = get_opfamily_member(opfamily, optype, optype, strategy); + if (!OidIsValid(operator)) + return -1; + + regop = get_opcode(operator); + + /* Initialize the scan key. */ + ScanKeyInit(&skey[attoff], + pkattno, + strategy, + regop, + scanvalue); + + /* Check for null value. */ + if (isnull) + skey[attoff].sk_flags |= SK_ISNULL; + + /* Use index collation */ + if (irel->rd_indcollation != NULL) + skey[attoff].sk_collation = irel->rd_indcollation[attoff]; + + /* Use index procedure */ + if (irel->rd_exclprocs != NULL) + fmgr_info(irel->rd_exclprocs[attoff], &(skey[attoff].sk_func)); + } + + return 0; +} + +IndexBulkDeleteResult* +bttargetdelete(IndexVacuumInfo *info, + IndexBulkDeleteResult *stats, + Relation hrel, + Datum *values, + bool *isnull, + ItemPointer htid) +{ + Relation irel = info->index; + ScanKeyData skey[INDEX_MAX_KEYS]; + SnapshotData snap; + IndexScanDesc scan; + ItemPointer tid; + bool found = false; + OffsetNumber offnum; + BlockNumber blkno; + + if (stats == NULL) + { + stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult)); + stats->estimated_count = false; + stats->num_index_tuples = 0; + stats->pages_deleted = 0; + } + + if (build_scan_key(skey, hrel, irel, values, isnull) != 0) + { + pfree(stats); + return NULL; + } + + InitDirtySnapshot(snap); + scan = index_beginscan(hrel, irel, &snap, IndexRelationGetNumberOfKeyAttributes(irel), 0); + index_rescan(scan, skey, IndexRelationGetNumberOfKeyAttributes(irel), NULL, 0); + + /* Pass along index entries array with equal scan key value */ + while ((!found) && ((tid = index_getnext_tid(scan, ForwardScanDirection)) != NULL)) + { + if (ItemPointerEquals(tid, htid)) + { + BTScanPos idx_pos = (BTScanPos) &(((BTScanOpaque) scan->opaque)->currPos); + blkno = idx_pos->currPage; + offnum = idx_pos->items[idx_pos->itemIndex].indexOffset; + found = true; + } + } + index_endscan(scan); + + /* Delete index tuple */ + if (found) + { + Buffer buf; + Page page; + bool needLock; + int npages; + IndexTuple itup; + + needLock = !RELATION_IS_LOCAL(irel); + if (needLock) + LockRelationForExtension(irel, ExclusiveLock); + npages = RelationGetNumberOfBlocks(irel); + if (needLock) + UnlockRelationForExtension(irel, ExclusiveLock); + if (blkno >= npages) + return stats; + + buf = ReadBuffer(irel, blkno); + page = BufferGetPage(buf); + LockBufferForCleanup(buf); + + itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offnum)); + + if (ItemPointerEquals(htid, &(itup->t_tid))) + { + _bt_delitems_vacuum(irel, buf, &offnum, 1, blkno); + stats->tuples_removed++; + stats->num_pages = npages; + } + _bt_relbuf(irel, buf); + cleanup_block(info, stats, blkno); + } + return stats; +} + /* * Post-VACUUM cleanup. * diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c index 4a9b5da..3257281 100644 --- a/src/backend/access/spgist/spgutils.c +++ b/src/backend/access/spgist/spgutils.c @@ -56,6 +56,7 @@ spghandler(PG_FUNCTION_ARGS) amroutine->ambuild = spgbuild; amroutine->ambuildempty = spgbuildempty; amroutine->aminsert = spginsert; + amroutine->amtargetdelete = NULL; amroutine->ambulkdelete = spgbulkdelete; amroutine->amvacuumcleanup = spgvacuumcleanup; amroutine->amcanreturn = spgcanreturn; diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h index 14526a6..411bb43 100644 --- a/src/include/access/amapi.h +++ b/src/include/access/amapi.h @@ -76,6 +76,13 @@ typedef bool (*aminsert_function) (Relation indexRelation, IndexUniqueCheck checkUnique, struct IndexInfo *indexInfo); +/* target delete */ +typedef IndexBulkDeleteResult *(*amtargetdelete_function) (IndexVacuumInfo *info, + IndexBulkDeleteResult *stats, + Relation hrel, + Datum *values, + bool *isnull, + ItemPointer htid); /* bulk delete */ typedef IndexBulkDeleteResult *(*ambulkdelete_function) (IndexVacuumInfo *info, IndexBulkDeleteResult *stats, @@ -207,6 +214,7 @@ typedef struct IndexAmRoutine ambuild_function ambuild; ambuildempty_function ambuildempty; aminsert_function aminsert; + amtargetdelete_function amtargetdelete; ambulkdelete_function ambulkdelete; amvacuumcleanup_function amvacuumcleanup; amcanreturn_function amcanreturn; /* can be NULL */ diff --git a/src/include/access/genam.h b/src/include/access/genam.h index 24c720b..cdcc6db 100644 --- a/src/include/access/genam.h +++ b/src/include/access/genam.h @@ -163,6 +163,12 @@ extern HeapTuple index_fetch_heap(IndexScanDesc scan); extern HeapTuple index_getnext(IndexScanDesc scan, ScanDirection direction); extern int64 index_getbitmap(IndexScanDesc scan, TIDBitmap *bitmap); +extern IndexBulkDeleteResult *index_target_delete(IndexVacuumInfo *info, + IndexBulkDeleteResult *stats, + Relation hrel, + Datum *values, + bool *isnull, + ItemPointer htid); extern IndexBulkDeleteResult *index_bulk_delete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h index 04ecb4c..0de5a24 100644 --- a/src/include/access/nbtree.h +++ b/src/include/access/nbtree.h @@ -508,6 +508,12 @@ extern void btparallelrescan(IndexScanDesc scan); extern void btendscan(IndexScanDesc scan); extern void btmarkpos(IndexScanDesc scan); extern void btrestrpos(IndexScanDesc scan); +extern IndexBulkDeleteResult *bttargetdelete(IndexVacuumInfo *info, + IndexBulkDeleteResult *stats, + Relation hrel, + Datum *values, + bool *isnull, + ItemPointer htid); extern IndexBulkDeleteResult *btbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback,
0002-quick-vacuum-strategy.patch (text/x-patch, 7.4K) download diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c index c2f5343..d394a7a 100644 --- a/src/backend/access/heap/pruneheap.c +++ b/src/backend/access/heap/pruneheap.c @@ -43,6 +43,9 @@ typedef struct bool marked[MaxHeapTuplesPerPage + 1]; } PruneState; +/* Parameter for target deletion strategy in lazy vacuum */ +double target_index_deletion_factor = 0.01; + /* Local functions */ static int heap_prune_chain(Relation relation, Buffer buffer, OffsetNumber rootoffnum, @@ -580,8 +583,10 @@ heap_prune_chain(Relation relation, Buffer buffer, OffsetNumber rootoffnum, */ for (i = 1; (i < nchain) && (chainitems[i - 1] != latestdead); i++) { - heap_prune_record_unused(prstate, chainitems[i]); ndeleted++; + if (chainitems[i] == latestdead) + continue; + heap_prune_record_unused(prstate, chainitems[i]); } /* @@ -598,9 +603,24 @@ heap_prune_chain(Relation relation, Buffer buffer, OffsetNumber rootoffnum, * redirect the root to the correct chain member. */ if (i >= nchain) + { + if (rootoffnum != latestdead) + { + if (ItemIdIsNormal(rootlp)) + heap_prune_record_unused(prstate, latestdead); + else + { + ItemIdSetDeadRedirect(rootlp, latestdead); + heap_prune_record_dead(prstate, latestdead); + } + } heap_prune_record_dead(prstate, rootoffnum); - else + } + else { + if (rootoffnum != latestdead) + heap_prune_record_unused(prstate, latestdead); heap_prune_record_redirect(prstate, rootoffnum, chainitems[i]); + } } else if (nchain < 2 && ItemIdIsRedirected(rootlp)) { @@ -706,7 +726,10 @@ heap_page_prune_execute(Buffer buffer, OffsetNumber off = *offnum++; ItemId lp = PageGetItemId(page, off); - ItemIdSetDead(lp); + if (target_index_deletion_factor > 0) + ItemIdMarkDead(lp); + else + ItemIdSetDead(lp); } /* Update all now-unused line pointers */ diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c index 5649a70..04127a4 100644 --- a/src/backend/commands/vacuumlazy.c +++ b/src/backend/commands/vacuumlazy.c @@ -137,9 +137,9 @@ typedef struct LVRelStats int num_index_scans; TransactionId latestRemovedXid; bool lock_waiter_detected; + Relation rel; } LVRelStats; - /* A few variables that don't seem worth passing around as parameters */ static int elevel = -1; @@ -519,6 +519,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats, vacrelstats->tupcount_pages = 0; vacrelstats->nonempty_pages = 0; vacrelstats->latestRemovedXid = InvalidTransactionId; + vacrelstats->rel = onerel; lazy_space_alloc(vacrelstats, nblocks); frozen = palloc(sizeof(xl_heap_freeze_tuple) * MaxHeapTuplesPerPage); @@ -1671,6 +1672,47 @@ lazy_check_needs_freeze(Buffer buf, bool *hastup) return false; } +/* + * Get tuple from heap for a scan key building. If tuples in HOT chain + * have not a storage, return NULL. + */ +static HeapTuple get_tuple_by_tid(Relation rel, ItemPointer tid) +{ + Buffer buffer; + Page page; + OffsetNumber offnum; + ItemId lp; + HeapTuple tuple; + + buffer = ReadBufferExtended(rel, MAIN_FORKNUM, ItemPointerGetBlockNumber(tid), RBM_NORMAL, NULL); + LockBuffer(buffer, BUFFER_LOCK_SHARE); + + page = (Page) BufferGetPage(buffer); + offnum = ItemPointerGetOffsetNumber(tid); + lp = PageGetItemId(page, offnum); + + while (!ItemIdHasStorage(lp)) + { + offnum = ItemIdGetRedirect(lp); + lp = PageGetItemId(page, offnum); + + if (!ItemIdIsUsed(lp)) + { + UnlockReleaseBuffer(buffer); + return NULL; + } + } + + tuple = palloc(sizeof(HeapTupleData)); + ItemPointerSet(&(tuple->t_self), BufferGetBlockNumber(buffer), offnum); + tuple->t_tableOid = RelationGetRelid(rel); + tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp); + tuple->t_len = ItemIdGetLength(lp); + UnlockReleaseBuffer(buffer); + return tuple; +} + +#include "access/nbtree.h" /* * lazy_vacuum_index() -- vacuum one index relation. @@ -1684,6 +1726,7 @@ lazy_vacuum_index(Relation indrel, LVRelStats *vacrelstats) { IndexVacuumInfo ivinfo; + bool use_target_strategy = true;//(vacrelstats->num_dead_tuples/vacrelstats->old_live_tuples < target_index_deletion_factor); PGRUsage ru0; pg_rusage_init(&ru0); @@ -1696,9 +1739,33 @@ lazy_vacuum_index(Relation indrel, ivinfo.num_heap_tuples = vacrelstats->old_live_tuples; ivinfo.strategy = vac_strategy; - /* Do bulk deletion */ - *stats = index_bulk_delete(&ivinfo, *stats, - lazy_tid_reaped, (void *) vacrelstats); + if ((use_target_strategy) && (indrel->rd_amroutine->amtargetdelete != NULL)) + { + int tnum; + TupleDesc desc = RelationGetDescr(vacrelstats->rel); + Datum* values = (Datum *) palloc(desc->natts * sizeof(Datum)); + bool* isnull = (bool *) palloc(desc->natts * sizeof(bool)); + + for (tnum = 0; tnum < vacrelstats->num_dead_tuples; tnum++) + { + HeapTuple tuple = get_tuple_by_tid(vacrelstats->rel, &(vacrelstats->dead_tuples[tnum])); + + if (tuple == NULL) + continue; + + heap_deform_tuple(tuple, desc, values, isnull); + *stats = index_target_delete(&ivinfo, *stats, vacrelstats->rel, values, isnull, &(vacrelstats->dead_tuples[tnum])); + if (*stats == NULL) + break; + } + pfree(values); + pfree(isnull); + } + + if ((!use_target_strategy) || (*stats == NULL)) + /* Do bulk deletion */ + *stats = index_bulk_delete(&ivinfo, *stats, + lazy_tid_reaped, (void *) vacrelstats); ereport(elevel, (errmsg("scanned index \"%s\" to remove %d row versions", diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index fa3c8a7..3f6c83b 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -3236,7 +3236,15 @@ static struct config_real ConfigureNamesReal[] = 0.1, 0.0, 100.0, NULL, NULL, NULL }, - + { + {"target_index_deletion_factor", PGC_SIGHUP, AUTOVACUUM, + gettext_noop("Maximum number of vacuumed tuples as a fraction of reltuples where we can use target index vacuum strategy."), + NULL + }, + &target_index_deletion_factor, + 0.01, 0.0, 1.0, + NULL, NULL, NULL + }, { {"checkpoint_completion_target", PGC_SIGHUP, WAL_CHECKPOINTS, gettext_noop("Time spent flushing dirty buffers during checkpoint, as fraction of checkpoint interval."), diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h index ca5cad7..c5a7bc8 100644 --- a/src/include/access/heapam.h +++ b/src/include/access/heapam.h @@ -100,6 +100,7 @@ extern Relation heap_openrv_extended(const RangeVar *relation, typedef struct HeapScanDescData *HeapScanDesc; typedef struct ParallelHeapScanDescData *ParallelHeapScanDesc; +extern double target_index_deletion_factor; /* * HeapScanIsValid * True iff the heap scan is valid. diff --git a/src/include/storage/itemid.h b/src/include/storage/itemid.h index 60570cc..9ea9269 100644 --- a/src/include/storage/itemid.h +++ b/src/include/storage/itemid.h @@ -112,6 +112,11 @@ typedef uint16 ItemLength; #define ItemIdIsDead(itemId) \ ((itemId)->lp_flags == LP_DEAD) +#define ItemIdIsDeadRedirection(itemId) \ + ( ((itemId)->lp_flags == 3) && \ + ((itemId)->lp_len == 0) && \ + ((itemId)->lp_off != 0) ) + /* * ItemIdHasStorage * True iff item identifier has associated storage. @@ -155,6 +160,12 @@ typedef uint16 ItemLength; (itemId)->lp_len = 0 \ ) +#define ItemIdSetDeadRedirect(itemId, link) \ +( \ + (itemId)->lp_off = (link), \ + (itemId)->lp_len = 0 \ +) + /* * ItemIdSetDead * Set the item identifier to be DEAD, with no storage.

view thread (47+ messages)  latest in thread

Message-ID: <425db134-8bba-005c-b59d-56e50de3b41e@postgrespro.ru>
Permalink:  ../425db134-8bba-005c-b59d-56e50de3b41e@postgrespro.ru/
Also on:    postgresql.org/message-id/425db134-8bba-005c-b59d-56e50de3b41e@postgrespro.ru

 ·  · 

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: pgsql-hackers@postgresql.org
  Cc: a.lepikhov@postgrespro.ru
  Subject: Re: [WIP] [B-Tree] Retail IndexTuple deletion
  In-Reply-To: <425db134-8bba-005c-b59d-56e50de3b41e@postgrespro.ru>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox