TLA Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * blvacuum.c
4 : * Bloom VACUUM functions.
5 : *
6 : * Copyright (c) 2016-2023, PostgreSQL Global Development Group
7 : *
8 : * IDENTIFICATION
9 : * contrib/bloom/blvacuum.c
10 : *
11 : *-------------------------------------------------------------------------
12 : */
13 : #include "postgres.h"
14 :
15 : #include "access/genam.h"
16 : #include "bloom.h"
17 : #include "catalog/storage.h"
18 : #include "commands/vacuum.h"
19 : #include "miscadmin.h"
20 : #include "postmaster/autovacuum.h"
21 : #include "storage/bufmgr.h"
22 : #include "storage/indexfsm.h"
23 : #include "storage/lmgr.h"
24 :
25 :
26 : /*
27 : * Bulk deletion of all index entries pointing to a set of heap tuples.
28 : * The set of target tuples is specified via a callback routine that tells
29 : * whether any given heap tuple (identified by ItemPointer) is being deleted.
30 : *
31 : * Result: a palloc'd struct containing statistical info for VACUUM displays.
32 : */
33 : IndexBulkDeleteResult *
34 CBC 11 : blbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
35 : IndexBulkDeleteCallback callback, void *callback_state)
36 : {
37 11 : Relation index = info->index;
38 : BlockNumber blkno,
39 : npages;
40 : FreeBlockNumberArray notFullPage;
41 11 : int countPage = 0;
42 : BloomState state;
43 : Buffer buffer;
44 : Page page;
45 : BloomMetaPageData *metaData;
46 : GenericXLogState *gxlogState;
47 :
48 11 : if (stats == NULL)
49 11 : stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
50 :
51 11 : initBloomState(&state, index);
52 :
53 : /*
54 : * Iterate over the pages. We don't care about concurrently added pages,
55 : * they can't contain tuples to delete.
56 : */
57 11 : npages = RelationGetNumberOfBlocks(index);
58 1800 : for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
59 : {
60 : BloomTuple *itup,
61 : *itupPtr,
62 : *itupEnd;
63 :
64 1789 : vacuum_delay_point();
65 :
66 1789 : buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
67 : RBM_NORMAL, info->strategy);
68 :
69 1789 : LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
70 1789 : gxlogState = GenericXLogStart(index);
71 1789 : page = GenericXLogRegisterBuffer(gxlogState, buffer, 0);
72 :
73 : /* Ignore empty/deleted pages until blvacuumcleanup() */
74 1789 : if (PageIsNew(page) || BloomPageIsDeleted(page))
75 : {
76 4 : UnlockReleaseBuffer(buffer);
77 4 : GenericXLogAbort(gxlogState);
78 4 : continue;
79 : }
80 :
81 : /*
82 : * Iterate over the tuples. itup points to current tuple being
83 : * scanned, itupPtr points to where to save next non-deleted tuple.
84 : */
85 1785 : itup = itupPtr = BloomPageGetTuple(&state, page, FirstOffsetNumber);
86 1785 : itupEnd = BloomPageGetTuple(&state, page,
87 : OffsetNumberNext(BloomPageGetMaxOffset(page)));
88 823785 : while (itup < itupEnd)
89 : {
90 : /* Do we have to delete this tuple? */
91 822000 : if (callback(&itup->heapPtr, callback_state))
92 : {
93 : /* Yes; adjust count of tuples that will be left on page */
94 129621 : BloomPageGetOpaque(page)->maxoff--;
95 129621 : stats->tuples_removed += 1;
96 : }
97 : else
98 : {
99 : /* No; copy it to itupPtr++, but skip copy if not needed */
100 692379 : if (itupPtr != itup)
101 690375 : memmove((Pointer) itupPtr, (Pointer) itup,
102 : state.sizeOfBloomTuple);
103 692379 : itupPtr = BloomPageGetNextTuple(&state, itupPtr);
104 : }
105 :
106 822000 : itup = BloomPageGetNextTuple(&state, itup);
107 : }
108 :
109 : /* Assert that we counted correctly */
110 1785 : Assert(itupPtr == BloomPageGetTuple(&state, page,
111 : OffsetNumberNext(BloomPageGetMaxOffset(page))));
112 :
113 : /*
114 : * Add page to new notFullPage list if we will not mark page as
115 : * deleted and there is free space on it
116 : */
117 1785 : if (BloomPageGetMaxOffset(page) != 0 &&
118 1781 : BloomPageGetFreeSpace(&state, page) >= state.sizeOfBloomTuple &&
119 1778 : countPage < BloomMetaBlockN)
120 1778 : notFullPage[countPage++] = blkno;
121 :
122 : /* Did we delete something? */
123 1785 : if (itupPtr != itup)
124 : {
125 : /* Is it empty page now? */
126 1781 : if (BloomPageGetMaxOffset(page) == 0)
127 4 : BloomPageSetDeleted(page);
128 : /* Adjust pd_lower */
129 1781 : ((PageHeader) page)->pd_lower = (Pointer) itupPtr - page;
130 : /* Finish WAL-logging */
131 1781 : GenericXLogFinish(gxlogState);
132 : }
133 : else
134 : {
135 : /* Didn't change anything: abort WAL-logging */
136 4 : GenericXLogAbort(gxlogState);
137 : }
138 1785 : UnlockReleaseBuffer(buffer);
139 : }
140 :
141 : /*
142 : * Update the metapage's notFullPage list with whatever we found. Our
143 : * info could already be out of date at this point, but blinsert() will
144 : * cope if so.
145 : */
146 11 : buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
147 11 : LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
148 :
149 11 : gxlogState = GenericXLogStart(index);
150 11 : page = GenericXLogRegisterBuffer(gxlogState, buffer, 0);
151 :
152 11 : metaData = BloomPageGetMeta(page);
153 11 : memcpy(metaData->notFullPage, notFullPage, sizeof(BlockNumber) * countPage);
154 11 : metaData->nStart = 0;
155 11 : metaData->nEnd = countPage;
156 :
157 11 : GenericXLogFinish(gxlogState);
158 11 : UnlockReleaseBuffer(buffer);
159 :
160 11 : return stats;
161 : }
162 :
163 : /*
164 : * Post-VACUUM cleanup.
165 : *
166 : * Result: a palloc'd struct containing statistical info for VACUUM displays.
167 : */
168 : IndexBulkDeleteResult *
169 12 : blvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
170 : {
171 12 : Relation index = info->index;
172 : BlockNumber npages,
173 : blkno;
174 :
175 12 : if (info->analyze_only)
176 UBC 0 : return stats;
177 :
178 CBC 12 : if (stats == NULL)
179 1 : stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
180 :
181 : /*
182 : * Iterate over the pages: insert deleted pages into FSM and collect
183 : * statistics.
184 : */
185 12 : npages = RelationGetNumberOfBlocks(index);
186 12 : stats->num_pages = npages;
187 12 : stats->pages_free = 0;
188 12 : stats->num_index_tuples = 0;
189 1998 : for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
190 : {
191 : Buffer buffer;
192 : Page page;
193 :
194 1986 : vacuum_delay_point();
195 :
196 1986 : buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
197 : RBM_NORMAL, info->strategy);
198 1986 : LockBuffer(buffer, BUFFER_LOCK_SHARE);
199 1986 : page = (Page) BufferGetPage(buffer);
200 :
201 1986 : if (PageIsNew(page) || BloomPageIsDeleted(page))
202 : {
203 8 : RecordFreeIndexPage(index, blkno);
204 8 : stats->pages_free++;
205 : }
206 : else
207 : {
208 1978 : stats->num_index_tuples += BloomPageGetMaxOffset(page);
209 : }
210 :
211 1986 : UnlockReleaseBuffer(buffer);
212 : }
213 :
214 12 : IndexFreeSpaceMapVacuum(info->index);
215 :
216 12 : return stats;
217 : }
|