/src/postgres/src/backend/access/transam/multixact.c
Line | Count | Source |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * multixact.c |
4 | | * PostgreSQL multi-transaction-log manager |
5 | | * |
6 | | * The pg_multixact manager is a pg_xact-like manager that stores an array of |
7 | | * MultiXactMember for each MultiXactId. It is a fundamental part of the |
8 | | * shared-row-lock implementation. Each MultiXactMember is comprised of a |
9 | | * TransactionId and a set of flag bits. The name is a bit historical: |
10 | | * originally, a MultiXactId consisted of more than one TransactionId (except |
11 | | * in rare corner cases), hence "multi". Nowadays, however, it's perfectly |
12 | | * legitimate to have MultiXactIds that only include a single Xid. |
13 | | * |
14 | | * The meaning of the flag bits is opaque to this module, but they are mostly |
15 | | * used in heapam.c to identify lock modes that each of the member transactions |
16 | | * is holding on any given tuple. This module just contains support to store |
17 | | * and retrieve the arrays. |
18 | | * |
19 | | * We use two SLRU areas, one for storing the offsets at which the data |
20 | | * starts for each MultiXactId in the other one. This trick allows us to |
21 | | * store variable length arrays of TransactionIds. (We could alternatively |
22 | | * use one area containing counts and TransactionIds, with valid MultiXactId |
23 | | * values pointing at slots containing counts; but that way seems less robust |
24 | | * since it would get completely confused if someone inquired about a bogus |
25 | | * MultiXactId that pointed to an intermediate slot containing an XID.) |
26 | | * |
27 | | * XLOG interactions: this module generates a record whenever a new OFFSETs or |
28 | | * MEMBERs page is initialized to zeroes, as well as an |
29 | | * XLOG_MULTIXACT_CREATE_ID record whenever a new MultiXactId is defined. |
30 | | * This module ignores the WAL rule "write xlog before data," because it |
31 | | * suffices that actions recording a MultiXactId in a heap xmax do follow that |
32 | | * rule. The only way for the MXID to be referenced from any data page is for |
33 | | * heap_lock_tuple() or heap_update() to have put it there, and each generates |
34 | | * an XLOG record that must follow ours. The normal LSN interlock between the |
35 | | * data page and that XLOG record will ensure that our XLOG record reaches |
36 | | * disk first. If the SLRU members/offsets data reaches disk sooner than the |
37 | | * XLOG records, we do not care; after recovery, no xmax will refer to it. On |
38 | | * the flip side, to ensure that all referenced entries _do_ reach disk, this |
39 | | * module's XLOG records completely rebuild the data entered since the last |
40 | | * checkpoint. We flush and sync all dirty OFFSETs and MEMBERs pages to disk |
41 | | * before each checkpoint is considered complete. |
42 | | * |
43 | | * Like clog.c, and unlike subtrans.c, we have to preserve state across |
44 | | * crashes and ensure that MXID and offset numbering increases monotonically |
45 | | * across a crash. We do this in the same way as it's done for transaction |
46 | | * IDs: the WAL record is guaranteed to contain evidence of every MXID we |
47 | | * could need to worry about, and we just make sure that at the end of |
48 | | * replay, the next-MXID and next-offset counters are at least as large as |
49 | | * anything we saw during replay. |
50 | | * |
51 | | * We are able to remove segments no longer necessary by carefully tracking |
52 | | * each table's used values: during vacuum, any multixact older than a certain |
53 | | * value is removed; the cutoff value is stored in pg_class. The minimum value |
54 | | * across all tables in each database is stored in pg_database, and the global |
55 | | * minimum across all databases is part of pg_control and is kept in shared |
56 | | * memory. Whenever that minimum is advanced, the SLRUs are truncated. |
57 | | * |
58 | | * When new multixactid values are to be created, care is taken that the |
59 | | * counter does not fall within the wraparound horizon considering the global |
60 | | * minimum value. |
61 | | * |
62 | | * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group |
63 | | * Portions Copyright (c) 1994, Regents of the University of California |
64 | | * |
65 | | * src/backend/access/transam/multixact.c |
66 | | * |
67 | | *------------------------------------------------------------------------- |
68 | | */ |
69 | | #include "postgres.h" |
70 | | |
71 | | #include "access/multixact.h" |
72 | | #include "access/multixact_internal.h" |
73 | | #include "access/slru.h" |
74 | | #include "access/twophase.h" |
75 | | #include "access/twophase_rmgr.h" |
76 | | #include "access/xlog.h" |
77 | | #include "access/xloginsert.h" |
78 | | #include "access/xlogutils.h" |
79 | | #include "miscadmin.h" |
80 | | #include "pg_trace.h" |
81 | | #include "pgstat.h" |
82 | | #include "postmaster/autovacuum.h" |
83 | | #include "storage/pmsignal.h" |
84 | | #include "storage/proc.h" |
85 | | #include "storage/procarray.h" |
86 | | #include "storage/subsystems.h" |
87 | | #include "utils/guc_hooks.h" |
88 | | #include "utils/injection_point.h" |
89 | | #include "utils/lsyscache.h" |
90 | | #include "utils/memutils.h" |
91 | | |
92 | | |
93 | | /* |
94 | | * Thresholds used to keep members disk usage in check when multixids have a |
95 | | * lot of members. When MULTIXACT_MEMBER_LOW_THRESHOLD is reached, vacuum |
96 | | * starts freezing multixids more aggressively, even if the normal multixid |
97 | | * age limits haven't been reached yet. |
98 | | */ |
99 | 0 | #define MULTIXACT_MEMBER_LOW_THRESHOLD UINT64CONST(2000000000) |
100 | 0 | #define MULTIXACT_MEMBER_HIGH_THRESHOLD UINT64CONST(4000000000) |
101 | | |
102 | | static inline MultiXactId |
103 | | NextMultiXactId(MultiXactId multi) |
104 | 0 | { |
105 | 0 | return multi == MaxMultiXactId ? FirstMultiXactId : multi + 1; |
106 | 0 | } |
107 | | |
108 | | static inline MultiXactId |
109 | | PreviousMultiXactId(MultiXactId multi) |
110 | 0 | { |
111 | 0 | return multi == FirstMultiXactId ? MaxMultiXactId : multi - 1; |
112 | 0 | } |
113 | | |
114 | | /* |
115 | | * Links to shared-memory data structures for MultiXact control |
116 | | */ |
117 | | static bool MultiXactOffsetPagePrecedes(int64 page1, int64 page2); |
118 | | static int MultiXactOffsetIoErrorDetail(const void *opaque_data); |
119 | | static bool MultiXactMemberPagePrecedes(int64 page1, int64 page2); |
120 | | static int MultiXactMemberIoErrorDetail(const void *opaque_data); |
121 | | |
122 | | static SlruDesc MultiXactOffsetSlruDesc; |
123 | | static SlruDesc MultiXactMemberSlruDesc; |
124 | | |
125 | 0 | #define MultiXactOffsetCtl (&MultiXactOffsetSlruDesc) |
126 | 0 | #define MultiXactMemberCtl (&MultiXactMemberSlruDesc) |
127 | | |
128 | | /* |
129 | | * MultiXact state shared across all backends. All this state is protected |
130 | | * by MultiXactGenLock. (We also use SLRU bank's lock of MultiXactOffset and |
131 | | * MultiXactMember to guard accesses to the two sets of SLRU buffers. For |
132 | | * concurrency's sake, we avoid holding more than one of these locks at a |
133 | | * time.) |
134 | | */ |
135 | | typedef struct MultiXactStateData |
136 | | { |
137 | | /* next-to-be-assigned MultiXactId */ |
138 | | MultiXactId nextMXact; |
139 | | |
140 | | /* next-to-be-assigned offset */ |
141 | | MultiXactOffset nextOffset; |
142 | | |
143 | | /* Have we completed multixact startup? */ |
144 | | bool finishedStartup; |
145 | | |
146 | | /* |
147 | | * Oldest multixact that is still potentially referenced by a relation. |
148 | | * Anything older than this should not be consulted. These values are |
149 | | * updated by vacuum. |
150 | | */ |
151 | | MultiXactId oldestMultiXactId; |
152 | | Oid oldestMultiXactDB; |
153 | | |
154 | | /* |
155 | | * Oldest multixact offset that is potentially referenced by a multixact |
156 | | * referenced by a relation. |
157 | | */ |
158 | | MultiXactOffset oldestOffset; |
159 | | |
160 | | /* support for anti-wraparound measures */ |
161 | | MultiXactId multiVacLimit; |
162 | | MultiXactId multiWarnLimit; |
163 | | MultiXactId multiStopLimit; |
164 | | MultiXactId multiWrapLimit; |
165 | | |
166 | | /* |
167 | | * Per-backend data starts here. We have two arrays stored in the area |
168 | | * immediately following the MultiXactStateData struct: |
169 | | * |
170 | | * OldestMemberMXactId[k] is the oldest MultiXactId each backend's current |
171 | | * transaction(s) could possibly be a member of, or InvalidMultiXactId |
172 | | * when the backend has no live transaction that could possibly be a |
173 | | * member of a MultiXact. Each backend sets its entry to the current |
174 | | * nextMXact counter just before first acquiring a shared lock in a given |
175 | | * transaction, and clears it at transaction end. (This works because only |
176 | | * during or after acquiring a shared lock could an XID possibly become a |
177 | | * member of a MultiXact, and that MultiXact would have to be created |
178 | | * during or after the lock acquisition.) |
179 | | * |
180 | | * In the OldestMemberMXactId array, there's a slot for all normal |
181 | | * backends (0..MaxBackends-1) followed by a slot for max_prepared_xacts |
182 | | * prepared transactions. |
183 | | * |
184 | | * OldestVisibleMXactId[k] is the oldest MultiXactId each backend's |
185 | | * current transaction(s) think is potentially live, or InvalidMultiXactId |
186 | | * when not in a transaction or not in a transaction that's paid any |
187 | | * attention to MultiXacts yet. This is computed when first needed in a |
188 | | * given transaction, and cleared at transaction end. We can compute it |
189 | | * as the minimum of the valid OldestMemberMXactId[] entries at the time |
190 | | * we compute it (using nextMXact if none are valid). Each backend is |
191 | | * required not to attempt to access any SLRU data for MultiXactIds older |
192 | | * than its own OldestVisibleMXactId[] setting; this is necessary because |
193 | | * the relevant SLRU data can be concurrently truncated away. |
194 | | * |
195 | | * In the OldestVisibleMXactId array, there's a slot for all normal |
196 | | * backends (0..MaxBackends-1) only. No slots for prepared transactions. |
197 | | * |
198 | | * The oldest valid value among all of the OldestMemberMXactId[] and |
199 | | * OldestVisibleMXactId[] entries is considered by vacuum as the earliest |
200 | | * possible value still having any live member transaction -- OldestMxact. |
201 | | * Any value older than that is typically removed from tuple headers, or |
202 | | * "frozen" via being replaced with a new xmax. VACUUM can sometimes even |
203 | | * remove an individual MultiXact xmax whose value is >= its OldestMxact |
204 | | * cutoff, though typically only when no individual member XID is still |
205 | | * running. See FreezeMultiXactId for full details. |
206 | | * |
207 | | * Whenever VACUUM advances relminmxid, then either its OldestMxact cutoff |
208 | | * or the oldest extant Multi remaining in the table is used as the new |
209 | | * pg_class.relminmxid value (whichever is earlier). The minimum of all |
210 | | * relminmxid values in each database is stored in pg_database.datminmxid. |
211 | | * In turn, the minimum of all of those values is stored in pg_control. |
212 | | * This is used as the truncation point for pg_multixact when unneeded |
213 | | * segments get removed by vac_truncate_clog() during vacuuming. |
214 | | */ |
215 | | MultiXactId perBackendXactIds[FLEXIBLE_ARRAY_MEMBER]; |
216 | | } MultiXactStateData; |
217 | | |
218 | | /* |
219 | | * Sizes of OldestMemberMXactId and OldestVisibleMXactId arrays. |
220 | | */ |
221 | 0 | #define NumMemberSlots (MaxBackends + max_prepared_xacts) |
222 | 0 | #define NumVisibleSlots MaxBackends |
223 | | |
224 | | /* Pointers to the state data in shared memory */ |
225 | | static MultiXactStateData *MultiXactState; |
226 | | static MultiXactId *OldestMemberMXactId; |
227 | | static MultiXactId *OldestVisibleMXactId; |
228 | | |
229 | | static void MultiXactShmemRequest(void *arg); |
230 | | static void MultiXactShmemInit(void *arg); |
231 | | static void MultiXactShmemAttach(void *arg); |
232 | | |
233 | | const ShmemCallbacks MultiXactShmemCallbacks = { |
234 | | .request_fn = MultiXactShmemRequest, |
235 | | .init_fn = MultiXactShmemInit, |
236 | | .attach_fn = MultiXactShmemAttach, |
237 | | }; |
238 | | |
239 | | static inline MultiXactId * |
240 | | MyOldestMemberMXactIdSlot(void) |
241 | 0 | { |
242 | | /* |
243 | | * The first MaxBackends entries in the OldestMemberMXactId array are |
244 | | * reserved for regular backends. MyProcNumber should index into one of |
245 | | * them. |
246 | | */ |
247 | 0 | Assert(MyProcNumber >= 0 && MyProcNumber < MaxBackends); |
248 | 0 | return &OldestMemberMXactId[MyProcNumber]; |
249 | 0 | } |
250 | | |
251 | | static inline MultiXactId * |
252 | | PreparedXactOldestMemberMXactIdSlot(ProcNumber procno) |
253 | 0 | { |
254 | 0 | int prepared_xact_idx; |
255 | |
|
256 | 0 | Assert(procno >= FIRST_PREPARED_XACT_PROC_NUMBER); |
257 | 0 | prepared_xact_idx = procno - FIRST_PREPARED_XACT_PROC_NUMBER; |
258 | | |
259 | | /* |
260 | | * The first MaxBackends entries in the OldestMemberMXactId array are |
261 | | * reserved for regular backends. Prepared xacts come after them. |
262 | | */ |
263 | 0 | Assert(MaxBackends + prepared_xact_idx < NumMemberSlots); |
264 | 0 | return &OldestMemberMXactId[MaxBackends + prepared_xact_idx]; |
265 | 0 | } |
266 | | |
267 | | static inline MultiXactId * |
268 | | MyOldestVisibleMXactIdSlot(void) |
269 | 0 | { |
270 | 0 | Assert(MyProcNumber >= 0 && MyProcNumber < NumVisibleSlots); |
271 | 0 | return &OldestVisibleMXactId[MyProcNumber]; |
272 | 0 | } |
273 | | |
274 | | /* |
275 | | * Definitions for the backend-local MultiXactId cache. |
276 | | * |
277 | | * We use this cache to store known MultiXacts, so we don't need to go to |
278 | | * SLRU areas every time. |
279 | | * |
280 | | * The cache lasts for the duration of a single transaction, the rationale |
281 | | * for this being that most entries will contain our own TransactionId and |
282 | | * so they will be uninteresting by the time our next transaction starts. |
283 | | * (XXX not clear that this is correct --- other members of the MultiXact |
284 | | * could hang around longer than we did. However, it's not clear what a |
285 | | * better policy for flushing old cache entries would be.) FIXME actually |
286 | | * this is plain wrong now that multixact's may contain update Xids. |
287 | | * |
288 | | * We allocate the cache entries in a memory context that is deleted at |
289 | | * transaction end, so we don't need to do retail freeing of entries. |
290 | | */ |
291 | | typedef struct mXactCacheEnt |
292 | | { |
293 | | MultiXactId multi; |
294 | | int nmembers; |
295 | | dlist_node node; |
296 | | MultiXactMember members[FLEXIBLE_ARRAY_MEMBER]; |
297 | | } mXactCacheEnt; |
298 | | |
299 | 0 | #define MAX_CACHE_ENTRIES 256 |
300 | | static dclist_head MXactCache = DCLIST_STATIC_INIT(MXactCache); |
301 | | static MemoryContext MXactContext = NULL; |
302 | | |
303 | | #ifdef MULTIXACT_DEBUG |
304 | | #define debug_elog2(a,b) elog(a,b) |
305 | | #define debug_elog3(a,b,c) elog(a,b,c) |
306 | | #define debug_elog4(a,b,c,d) elog(a,b,c,d) |
307 | | #define debug_elog5(a,b,c,d,e) elog(a,b,c,d,e) |
308 | | #define debug_elog6(a,b,c,d,e,f) elog(a,b,c,d,e,f) |
309 | | #else |
310 | | #define debug_elog2(a,b) |
311 | | #define debug_elog3(a,b,c) |
312 | | #define debug_elog4(a,b,c,d) |
313 | | #define debug_elog5(a,b,c,d,e) |
314 | | #define debug_elog6(a,b,c,d,e,f) |
315 | | #endif |
316 | | |
317 | | /* internal MultiXactId management */ |
318 | | static void MultiXactIdSetOldestVisible(void); |
319 | | static void RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset, |
320 | | int nmembers, MultiXactMember *members); |
321 | | static MultiXactId GetNewMultiXactId(int nmembers, MultiXactOffset *offset); |
322 | | |
323 | | /* MultiXact cache management */ |
324 | | static int mxactMemberComparator(const void *arg1, const void *arg2); |
325 | | static MultiXactId mXactCacheGetBySet(int nmembers, MultiXactMember *members); |
326 | | static int mXactCacheGetById(MultiXactId multi, MultiXactMember **members); |
327 | | static void mXactCachePut(MultiXactId multi, int nmembers, |
328 | | MultiXactMember *members); |
329 | | |
330 | | /* management of SLRU infrastructure */ |
331 | | |
332 | | /* opaque_data type for MultiXactMemberIoErrorDetail */ |
333 | | typedef struct MultiXactMemberSlruReadContext |
334 | | { |
335 | | MultiXactId multi; |
336 | | MultiXactOffset offset; |
337 | | } MultiXactMemberSlruReadContext; |
338 | | |
339 | | static void ExtendMultiXactOffset(MultiXactId multi); |
340 | | static void ExtendMultiXactMember(MultiXactOffset offset, int nmembers); |
341 | | static void SetOldestOffset(void); |
342 | | static bool find_multixact_start(MultiXactId multi, MultiXactOffset *result); |
343 | | static void WriteMTruncateXlogRec(Oid oldestMultiDB, |
344 | | MultiXactId oldestMulti, |
345 | | MultiXactOffset oldestOffset); |
346 | | |
347 | | |
348 | | /* |
349 | | * MultiXactIdCreate |
350 | | * Construct a MultiXactId representing two TransactionIds. |
351 | | * |
352 | | * The two XIDs must be different, or be requesting different statuses. |
353 | | * |
354 | | * NB - we don't worry about our local MultiXactId cache here, because that |
355 | | * is handled by the lower-level routines. |
356 | | */ |
357 | | MultiXactId |
358 | | MultiXactIdCreate(TransactionId xid1, MultiXactStatus status1, |
359 | | TransactionId xid2, MultiXactStatus status2) |
360 | 0 | { |
361 | 0 | MultiXactId newMulti; |
362 | 0 | MultiXactMember members[2]; |
363 | |
|
364 | 0 | Assert(TransactionIdIsValid(xid1)); |
365 | 0 | Assert(TransactionIdIsValid(xid2)); |
366 | |
|
367 | 0 | Assert(!TransactionIdEquals(xid1, xid2) || (status1 != status2)); |
368 | | |
369 | | /* MultiXactIdSetOldestMember() must have been called already. */ |
370 | 0 | Assert(MultiXactIdIsValid(*MyOldestMemberMXactIdSlot())); |
371 | | |
372 | | /* |
373 | | * Note: unlike MultiXactIdExpand, we don't bother to check that both XIDs |
374 | | * are still running. In typical usage, xid2 will be our own XID and the |
375 | | * caller just did a check on xid1, so it'd be wasted effort. |
376 | | */ |
377 | |
|
378 | 0 | members[0].xid = xid1; |
379 | 0 | members[0].status = status1; |
380 | 0 | members[1].xid = xid2; |
381 | 0 | members[1].status = status2; |
382 | |
|
383 | 0 | newMulti = MultiXactIdCreateFromMembers(2, members); |
384 | |
|
385 | 0 | debug_elog3(DEBUG2, "Create: %s", |
386 | 0 | mxid_to_string(newMulti, 2, members)); |
387 | |
|
388 | 0 | return newMulti; |
389 | 0 | } |
390 | | |
391 | | /* |
392 | | * MultiXactIdExpand |
393 | | * Add a TransactionId to a pre-existing MultiXactId. |
394 | | * |
395 | | * If the TransactionId is already a member of the passed MultiXactId with the |
396 | | * same status, just return it as-is. |
397 | | * |
398 | | * Note that we do NOT actually modify the membership of a pre-existing |
399 | | * MultiXactId; instead we create a new one. This is necessary to avoid |
400 | | * a race condition against code trying to wait for one MultiXactId to finish; |
401 | | * see notes in heapam.c. |
402 | | * |
403 | | * NB - we don't worry about our local MultiXactId cache here, because that |
404 | | * is handled by the lower-level routines. |
405 | | * |
406 | | * Note: It is critical that MultiXactIds that come from an old cluster (i.e. |
407 | | * one upgraded by pg_upgrade from a cluster older than this feature) are not |
408 | | * passed in. |
409 | | */ |
410 | | MultiXactId |
411 | | MultiXactIdExpand(MultiXactId multi, TransactionId xid, MultiXactStatus status) |
412 | 0 | { |
413 | 0 | MultiXactId newMulti; |
414 | 0 | MultiXactMember *members; |
415 | 0 | MultiXactMember *newMembers; |
416 | 0 | int nmembers; |
417 | 0 | int i; |
418 | 0 | int j; |
419 | |
|
420 | 0 | Assert(MultiXactIdIsValid(multi)); |
421 | 0 | Assert(TransactionIdIsValid(xid)); |
422 | | |
423 | | /* MultiXactIdSetOldestMember() must have been called already. */ |
424 | 0 | Assert(MultiXactIdIsValid(*MyOldestMemberMXactIdSlot())); |
425 | |
|
426 | 0 | debug_elog5(DEBUG2, "Expand: received multi %u, xid %u status %s", |
427 | 0 | multi, xid, mxstatus_to_string(status)); |
428 | | |
429 | | /* |
430 | | * Note: we don't allow for old multis here. The reason is that the only |
431 | | * caller of this function does a check that the multixact is no longer |
432 | | * running. |
433 | | */ |
434 | 0 | nmembers = GetMultiXactIdMembers(multi, &members, false, false); |
435 | |
|
436 | 0 | if (nmembers < 0) |
437 | 0 | { |
438 | 0 | MultiXactMember member; |
439 | | |
440 | | /* |
441 | | * The MultiXactId is obsolete. This can only happen if all the |
442 | | * MultiXactId members stop running between the caller checking and |
443 | | * passing it to us. It would be better to return that fact to the |
444 | | * caller, but it would complicate the API and it's unlikely to happen |
445 | | * too often, so just deal with it by creating a singleton MultiXact. |
446 | | */ |
447 | 0 | member.xid = xid; |
448 | 0 | member.status = status; |
449 | 0 | newMulti = MultiXactIdCreateFromMembers(1, &member); |
450 | |
|
451 | 0 | debug_elog4(DEBUG2, "Expand: %u has no members, create singleton %u", |
452 | 0 | multi, newMulti); |
453 | 0 | return newMulti; |
454 | 0 | } |
455 | | |
456 | | /* |
457 | | * If the TransactionId is already a member of the MultiXactId with the |
458 | | * same status, just return the existing MultiXactId. |
459 | | */ |
460 | 0 | for (i = 0; i < nmembers; i++) |
461 | 0 | { |
462 | 0 | if (TransactionIdEquals(members[i].xid, xid) && |
463 | 0 | (members[i].status == status)) |
464 | 0 | { |
465 | 0 | debug_elog4(DEBUG2, "Expand: %u is already a member of %u", |
466 | 0 | xid, multi); |
467 | 0 | pfree(members); |
468 | 0 | return multi; |
469 | 0 | } |
470 | 0 | } |
471 | | |
472 | | /* |
473 | | * Determine which of the members of the MultiXactId are still of |
474 | | * interest. This is any running transaction, and also any transaction |
475 | | * that grabbed something stronger than just a lock and was committed. (An |
476 | | * update that aborted is of no interest here; and having more than one |
477 | | * update Xid in a multixact would cause errors elsewhere.) |
478 | | * |
479 | | * Removing dead members is not just an optimization: freezing of tuples |
480 | | * whose Xmax are multis depends on this behavior. |
481 | | * |
482 | | * Note we have the same race condition here as above: j could be 0 at the |
483 | | * end of the loop. |
484 | | */ |
485 | 0 | newMembers = palloc_array(MultiXactMember, nmembers + 1); |
486 | |
|
487 | 0 | for (i = 0, j = 0; i < nmembers; i++) |
488 | 0 | { |
489 | 0 | if (TransactionIdIsInProgress(members[i].xid) || |
490 | 0 | (ISUPDATE_from_mxstatus(members[i].status) && |
491 | 0 | TransactionIdDidCommit(members[i].xid))) |
492 | 0 | { |
493 | 0 | newMembers[j].xid = members[i].xid; |
494 | 0 | newMembers[j++].status = members[i].status; |
495 | 0 | } |
496 | 0 | } |
497 | |
|
498 | 0 | newMembers[j].xid = xid; |
499 | 0 | newMembers[j++].status = status; |
500 | 0 | newMulti = MultiXactIdCreateFromMembers(j, newMembers); |
501 | |
|
502 | 0 | pfree(members); |
503 | 0 | pfree(newMembers); |
504 | |
|
505 | 0 | debug_elog3(DEBUG2, "Expand: returning new multi %u", newMulti); |
506 | |
|
507 | 0 | return newMulti; |
508 | 0 | } |
509 | | |
510 | | /* |
511 | | * MultiXactIdIsRunning |
512 | | * Returns whether a MultiXactId is "running". |
513 | | * |
514 | | * We return true if at least one member of the given MultiXactId is still |
515 | | * running. Note that a "false" result is certain not to change, |
516 | | * because it is not legal to add members to an existing MultiXactId. |
517 | | * |
518 | | * Caller is expected to have verified that the multixact does not come from |
519 | | * a pg_upgraded share-locked tuple. |
520 | | */ |
521 | | bool |
522 | | MultiXactIdIsRunning(MultiXactId multi, bool isLockOnly) |
523 | 0 | { |
524 | 0 | MultiXactMember *members; |
525 | 0 | int nmembers; |
526 | 0 | int i; |
527 | |
|
528 | 0 | debug_elog3(DEBUG2, "IsRunning %u?", multi); |
529 | | |
530 | | /* |
531 | | * "false" here means we assume our callers have checked that the given |
532 | | * multi cannot possibly come from a pg_upgraded database. |
533 | | */ |
534 | 0 | nmembers = GetMultiXactIdMembers(multi, &members, false, isLockOnly); |
535 | |
|
536 | 0 | if (nmembers <= 0) |
537 | 0 | { |
538 | 0 | debug_elog2(DEBUG2, "IsRunning: no members"); |
539 | 0 | return false; |
540 | 0 | } |
541 | | |
542 | | /* |
543 | | * Checking for myself is cheap compared to looking in shared memory; |
544 | | * return true if any live subtransaction of the current top-level |
545 | | * transaction is a member. |
546 | | * |
547 | | * This is not needed for correctness, it's just a fast path. |
548 | | */ |
549 | 0 | for (i = 0; i < nmembers; i++) |
550 | 0 | { |
551 | 0 | if (TransactionIdIsCurrentTransactionId(members[i].xid)) |
552 | 0 | { |
553 | 0 | debug_elog3(DEBUG2, "IsRunning: I (%d) am running!", i); |
554 | 0 | pfree(members); |
555 | 0 | return true; |
556 | 0 | } |
557 | 0 | } |
558 | | |
559 | | /* |
560 | | * This could be made faster by having another entry point in procarray.c, |
561 | | * walking the PGPROC array only once for all the members. But in most |
562 | | * cases nmembers should be small enough that it doesn't much matter. |
563 | | */ |
564 | 0 | for (i = 0; i < nmembers; i++) |
565 | 0 | { |
566 | 0 | if (TransactionIdIsInProgress(members[i].xid)) |
567 | 0 | { |
568 | 0 | debug_elog4(DEBUG2, "IsRunning: member %d (%u) is running", |
569 | 0 | i, members[i].xid); |
570 | 0 | pfree(members); |
571 | 0 | return true; |
572 | 0 | } |
573 | 0 | } |
574 | | |
575 | 0 | pfree(members); |
576 | |
|
577 | 0 | debug_elog3(DEBUG2, "IsRunning: %u is not running", multi); |
578 | |
|
579 | 0 | return false; |
580 | 0 | } |
581 | | |
582 | | /* |
583 | | * MultiXactIdSetOldestMember |
584 | | * Save the oldest MultiXactId this transaction could be a member of. |
585 | | * |
586 | | * We set the OldestMemberMXactId for a given transaction the first time it's |
587 | | * going to do some operation that might require a MultiXactId (tuple lock, |
588 | | * update or delete). We need to do this even if we end up using a |
589 | | * TransactionId instead of a MultiXactId, because there is a chance that |
590 | | * another transaction would add our XID to a MultiXactId. |
591 | | * |
592 | | * The value to set is the next-to-be-assigned MultiXactId, so this is meant to |
593 | | * be called just before doing any such possibly-MultiXactId-able operation. |
594 | | */ |
595 | | void |
596 | | MultiXactIdSetOldestMember(void) |
597 | 0 | { |
598 | 0 | if (!MultiXactIdIsValid(*MyOldestMemberMXactIdSlot())) |
599 | 0 | { |
600 | 0 | MultiXactId nextMXact; |
601 | | |
602 | | /* |
603 | | * You might think we don't need to acquire a lock here, since |
604 | | * fetching and storing of TransactionIds is probably atomic, but in |
605 | | * fact we do: suppose we pick up nextMXact and then lose the CPU for |
606 | | * a long time. Someone else could advance nextMXact, and then |
607 | | * another someone else could compute an OldestVisibleMXactId that |
608 | | * would be after the value we are going to store when we get control |
609 | | * back. Which would be wrong. |
610 | | * |
611 | | * Note that a shared lock is sufficient, because it's enough to stop |
612 | | * someone from advancing nextMXact; and nobody else could be trying |
613 | | * to write to our OldestMember entry, only reading (and we assume |
614 | | * storing it is atomic.) |
615 | | */ |
616 | 0 | LWLockAcquire(MultiXactGenLock, LW_SHARED); |
617 | |
|
618 | 0 | nextMXact = MultiXactState->nextMXact; |
619 | |
|
620 | 0 | *MyOldestMemberMXactIdSlot() = nextMXact; |
621 | |
|
622 | 0 | LWLockRelease(MultiXactGenLock); |
623 | |
|
624 | 0 | debug_elog4(DEBUG2, "MultiXact: setting OldestMember[%d] = %u", |
625 | 0 | MyProcNumber, nextMXact); |
626 | 0 | } |
627 | 0 | } |
628 | | |
629 | | /* |
630 | | * MultiXactIdSetOldestVisible |
631 | | * Save the oldest MultiXactId this transaction considers possibly live. |
632 | | * |
633 | | * We set the OldestVisibleMXactId for a given transaction the first time |
634 | | * it's going to inspect any MultiXactId. Once we have set this, we are |
635 | | * guaranteed that SLRU data for MultiXactIds >= our own OldestVisibleMXactId |
636 | | * won't be truncated away. |
637 | | * |
638 | | * The value to set is the oldest of nextMXact and all the valid per-backend |
639 | | * OldestMemberMXactId[] entries. Because of the locking we do, we can be |
640 | | * certain that no subsequent call to MultiXactIdSetOldestMember can set |
641 | | * an OldestMemberMXactId[] entry older than what we compute here. Therefore |
642 | | * there is no live transaction, now or later, that can be a member of any |
643 | | * MultiXactId older than the OldestVisibleMXactId we compute here. |
644 | | */ |
645 | | static void |
646 | | MultiXactIdSetOldestVisible(void) |
647 | 0 | { |
648 | 0 | if (!MultiXactIdIsValid(*MyOldestVisibleMXactIdSlot())) |
649 | 0 | { |
650 | 0 | MultiXactId oldestMXact; |
651 | 0 | int i; |
652 | |
|
653 | 0 | LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE); |
654 | |
|
655 | 0 | oldestMXact = MultiXactState->nextMXact; |
656 | 0 | for (i = 0; i < NumMemberSlots; i++) |
657 | 0 | { |
658 | 0 | MultiXactId thisoldest = OldestMemberMXactId[i]; |
659 | |
|
660 | 0 | if (MultiXactIdIsValid(thisoldest) && |
661 | 0 | MultiXactIdPrecedes(thisoldest, oldestMXact)) |
662 | 0 | oldestMXact = thisoldest; |
663 | 0 | } |
664 | |
|
665 | 0 | *MyOldestVisibleMXactIdSlot() = oldestMXact; |
666 | |
|
667 | 0 | LWLockRelease(MultiXactGenLock); |
668 | |
|
669 | 0 | debug_elog4(DEBUG2, "MultiXact: setting OldestVisible[%d] = %u", |
670 | 0 | MyProcNumber, oldestMXact); |
671 | 0 | } |
672 | 0 | } |
673 | | |
674 | | /* |
675 | | * ReadNextMultiXactId |
676 | | * Return the next MultiXactId to be assigned, but don't allocate it |
677 | | */ |
678 | | MultiXactId |
679 | | ReadNextMultiXactId(void) |
680 | 0 | { |
681 | 0 | MultiXactId mxid; |
682 | | |
683 | | /* XXX we could presumably do this without a lock. */ |
684 | 0 | LWLockAcquire(MultiXactGenLock, LW_SHARED); |
685 | 0 | mxid = MultiXactState->nextMXact; |
686 | 0 | LWLockRelease(MultiXactGenLock); |
687 | |
|
688 | 0 | return mxid; |
689 | 0 | } |
690 | | |
691 | | /* |
692 | | * ReadMultiXactIdRange |
693 | | * Get the range of IDs that may still be referenced by a relation. |
694 | | */ |
695 | | void |
696 | | ReadMultiXactIdRange(MultiXactId *oldest, MultiXactId *next) |
697 | 0 | { |
698 | 0 | LWLockAcquire(MultiXactGenLock, LW_SHARED); |
699 | 0 | *oldest = MultiXactState->oldestMultiXactId; |
700 | 0 | *next = MultiXactState->nextMXact; |
701 | 0 | LWLockRelease(MultiXactGenLock); |
702 | 0 | } |
703 | | |
704 | | |
705 | | /* |
706 | | * MultiXactIdCreateFromMembers |
707 | | * Make a new MultiXactId from the specified set of members |
708 | | * |
709 | | * Make XLOG, SLRU and cache entries for a new MultiXactId, recording the |
710 | | * given TransactionIds as members. Returns the newly created MultiXactId. |
711 | | * |
712 | | * NB: the passed members[] array will be sorted in-place. |
713 | | */ |
714 | | MultiXactId |
715 | | MultiXactIdCreateFromMembers(int nmembers, MultiXactMember *members) |
716 | 0 | { |
717 | 0 | MultiXactId multi; |
718 | 0 | MultiXactOffset offset; |
719 | 0 | xl_multixact_create xlrec; |
720 | |
|
721 | 0 | debug_elog3(DEBUG2, "Create: %s", |
722 | 0 | mxid_to_string(InvalidMultiXactId, nmembers, members)); |
723 | | |
724 | | /* |
725 | | * See if the same set of members already exists in our cache; if so, just |
726 | | * re-use that MultiXactId. (Note: it might seem that looking in our |
727 | | * cache is insufficient, and we ought to search disk to see if a |
728 | | * duplicate definition already exists. But since we only ever create |
729 | | * MultiXacts containing our own XID, in most cases any such MultiXacts |
730 | | * were in fact created by us, and so will be in our cache. There are |
731 | | * corner cases where someone else added us to a MultiXact without our |
732 | | * knowledge, but it's not worth checking for.) |
733 | | */ |
734 | 0 | multi = mXactCacheGetBySet(nmembers, members); |
735 | 0 | if (MultiXactIdIsValid(multi)) |
736 | 0 | { |
737 | 0 | debug_elog2(DEBUG2, "Create: in cache!"); |
738 | 0 | return multi; |
739 | 0 | } |
740 | | |
741 | | /* Verify that there is a single update Xid among the given members. */ |
742 | 0 | { |
743 | 0 | int i; |
744 | 0 | bool has_update = false; |
745 | |
|
746 | 0 | for (i = 0; i < nmembers; i++) |
747 | 0 | { |
748 | 0 | if (ISUPDATE_from_mxstatus(members[i].status)) |
749 | 0 | { |
750 | 0 | if (has_update) |
751 | 0 | elog(ERROR, "new multixact has more than one updating member: %s", |
752 | 0 | mxid_to_string(InvalidMultiXactId, nmembers, members)); |
753 | 0 | has_update = true; |
754 | 0 | } |
755 | 0 | } |
756 | 0 | } |
757 | | |
758 | | /* Load the injection point before entering the critical section */ |
759 | 0 | INJECTION_POINT_LOAD("multixact-create-from-members"); |
760 | | |
761 | | /* |
762 | | * Assign the MXID and offsets range to use, and make sure there is space |
763 | | * in the OFFSETs and MEMBERs files. NB: this routine does |
764 | | * START_CRIT_SECTION(). |
765 | | * |
766 | | * Note: unlike MultiXactIdCreate and MultiXactIdExpand, we do not check |
767 | | * that we've called MultiXactIdSetOldestMember here. This is because |
768 | | * this routine is used in some places to create new MultiXactIds of which |
769 | | * the current backend is not a member, notably during freezing of multis |
770 | | * in vacuum. During vacuum, in particular, it would be unacceptable to |
771 | | * keep OldestMulti set, in case it runs for long. |
772 | | */ |
773 | 0 | multi = GetNewMultiXactId(nmembers, &offset); |
774 | |
|
775 | 0 | INJECTION_POINT_CACHED("multixact-create-from-members", NULL); |
776 | | |
777 | | /* Make an XLOG entry describing the new MXID. */ |
778 | 0 | xlrec.mid = multi; |
779 | 0 | xlrec.moff = offset; |
780 | 0 | xlrec.nmembers = nmembers; |
781 | | |
782 | | /* |
783 | | * XXX Note: there's a lot of padding space in MultiXactMember. We could |
784 | | * find a more compact representation of this Xlog record -- perhaps all |
785 | | * the status flags in one XLogRecData, then all the xids in another one? |
786 | | * Not clear that it's worth the trouble though. |
787 | | */ |
788 | 0 | XLogBeginInsert(); |
789 | 0 | XLogRegisterData(&xlrec, SizeOfMultiXactCreate); |
790 | 0 | XLogRegisterData(members, nmembers * sizeof(MultiXactMember)); |
791 | |
|
792 | 0 | (void) XLogInsert(RM_MULTIXACT_ID, XLOG_MULTIXACT_CREATE_ID); |
793 | | |
794 | | /* Now enter the information into the OFFSETs and MEMBERs logs */ |
795 | 0 | RecordNewMultiXact(multi, offset, nmembers, members); |
796 | | |
797 | | /* Done with critical section */ |
798 | 0 | END_CRIT_SECTION(); |
799 | | |
800 | | /* Store the new MultiXactId in the local cache, too */ |
801 | 0 | mXactCachePut(multi, nmembers, members); |
802 | |
|
803 | 0 | debug_elog2(DEBUG2, "Create: all done"); |
804 | |
|
805 | 0 | return multi; |
806 | 0 | } |
807 | | |
808 | | /* |
809 | | * RecordNewMultiXact |
810 | | * Write info about a new multixact into the offsets and members files |
811 | | * |
812 | | * This is broken out of MultiXactIdCreateFromMembers so that xlog replay can |
813 | | * use it. |
814 | | */ |
815 | | static void |
816 | | RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset, |
817 | | int nmembers, MultiXactMember *members) |
818 | 0 | { |
819 | 0 | int64 pageno; |
820 | 0 | int64 prev_pageno; |
821 | 0 | int entryno; |
822 | 0 | int slotno; |
823 | 0 | MultiXactOffset *offptr; |
824 | 0 | MultiXactId next; |
825 | 0 | int64 next_pageno; |
826 | 0 | int next_entryno; |
827 | 0 | MultiXactOffset *next_offptr; |
828 | 0 | MultiXactOffset next_offset; |
829 | 0 | LWLock *lock; |
830 | 0 | LWLock *prevlock = NULL; |
831 | | |
832 | | /* position of this multixid in the offsets SLRU area */ |
833 | 0 | pageno = MultiXactIdToOffsetPage(multi); |
834 | 0 | entryno = MultiXactIdToOffsetEntry(multi); |
835 | | |
836 | | /* position of the next multixid */ |
837 | 0 | next = NextMultiXactId(multi); |
838 | 0 | next_pageno = MultiXactIdToOffsetPage(next); |
839 | 0 | next_entryno = MultiXactIdToOffsetEntry(next); |
840 | | |
841 | | /* |
842 | | * Set the starting offset of this multixid's members. |
843 | | * |
844 | | * In the common case, it was already set by the previous |
845 | | * RecordNewMultiXact call, as this was the next multixid of the previous |
846 | | * multixid. But if multiple backends are generating multixids |
847 | | * concurrently, we might race ahead and get called before the previous |
848 | | * multixid. |
849 | | */ |
850 | 0 | lock = SimpleLruGetBankLock(MultiXactOffsetCtl, pageno); |
851 | 0 | LWLockAcquire(lock, LW_EXCLUSIVE); |
852 | |
|
853 | 0 | slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, &multi); |
854 | 0 | offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno]; |
855 | 0 | offptr += entryno; |
856 | |
|
857 | 0 | if (*offptr != offset) |
858 | 0 | { |
859 | | /* should already be set to the correct value, or not at all */ |
860 | 0 | Assert(*offptr == 0); |
861 | 0 | *offptr = offset; |
862 | 0 | MultiXactOffsetCtl->shared->page_dirty[slotno] = true; |
863 | 0 | } |
864 | | |
865 | | /* |
866 | | * Set the next multixid's offset to the end of this multixid's members. |
867 | | */ |
868 | 0 | if (next_pageno == pageno) |
869 | 0 | { |
870 | 0 | next_offptr = offptr + 1; |
871 | 0 | } |
872 | 0 | else |
873 | 0 | { |
874 | | /* must be the first entry on the page */ |
875 | 0 | Assert(next_entryno == 0 || next == FirstMultiXactId); |
876 | | |
877 | | /* Swap the lock for a lock on the next page */ |
878 | 0 | LWLockRelease(lock); |
879 | 0 | lock = SimpleLruGetBankLock(MultiXactOffsetCtl, next_pageno); |
880 | 0 | LWLockAcquire(lock, LW_EXCLUSIVE); |
881 | |
|
882 | 0 | slotno = SimpleLruReadPage(MultiXactOffsetCtl, next_pageno, true, &next); |
883 | 0 | next_offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno]; |
884 | 0 | next_offptr += next_entryno; |
885 | 0 | } |
886 | | |
887 | | /* Like in GetNewMultiXactId(), skip over offset 0 */ |
888 | 0 | next_offset = offset + nmembers; |
889 | 0 | if (next_offset == 0) |
890 | 0 | next_offset = 1; |
891 | 0 | if (*next_offptr != next_offset) |
892 | 0 | { |
893 | | /* should already be set to the correct value, or not at all */ |
894 | 0 | Assert(*next_offptr == 0); |
895 | 0 | *next_offptr = next_offset; |
896 | 0 | MultiXactOffsetCtl->shared->page_dirty[slotno] = true; |
897 | 0 | } |
898 | | |
899 | | /* Release MultiXactOffset SLRU lock. */ |
900 | 0 | LWLockRelease(lock); |
901 | |
|
902 | 0 | prev_pageno = -1; |
903 | |
|
904 | 0 | for (int i = 0; i < nmembers; i++, offset++) |
905 | 0 | { |
906 | 0 | TransactionId *memberptr; |
907 | 0 | uint32 *flagsptr; |
908 | 0 | uint32 flagsval; |
909 | 0 | int bshift; |
910 | 0 | int flagsoff; |
911 | 0 | int memberoff; |
912 | |
|
913 | 0 | Assert(members[i].status <= MultiXactStatusUpdate); |
914 | |
|
915 | 0 | pageno = MXOffsetToMemberPage(offset); |
916 | 0 | memberoff = MXOffsetToMemberOffset(offset); |
917 | 0 | flagsoff = MXOffsetToFlagsOffset(offset); |
918 | 0 | bshift = MXOffsetToFlagsBitShift(offset); |
919 | |
|
920 | 0 | if (pageno != prev_pageno) |
921 | 0 | { |
922 | 0 | MultiXactMemberSlruReadContext slru_read_context = {multi, offset}; |
923 | | |
924 | | /* |
925 | | * MultiXactMember SLRU page is changed so check if this new page |
926 | | * fall into the different SLRU bank then release the old bank's |
927 | | * lock and acquire lock on the new bank. |
928 | | */ |
929 | 0 | lock = SimpleLruGetBankLock(MultiXactMemberCtl, pageno); |
930 | 0 | if (lock != prevlock) |
931 | 0 | { |
932 | 0 | if (prevlock != NULL) |
933 | 0 | LWLockRelease(prevlock); |
934 | |
|
935 | 0 | LWLockAcquire(lock, LW_EXCLUSIVE); |
936 | 0 | prevlock = lock; |
937 | 0 | } |
938 | 0 | slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, |
939 | 0 | &slru_read_context); |
940 | 0 | prev_pageno = pageno; |
941 | 0 | } |
942 | |
|
943 | 0 | memberptr = (TransactionId *) |
944 | 0 | (MultiXactMemberCtl->shared->page_buffer[slotno] + memberoff); |
945 | |
|
946 | 0 | *memberptr = members[i].xid; |
947 | |
|
948 | 0 | flagsptr = (uint32 *) |
949 | 0 | (MultiXactMemberCtl->shared->page_buffer[slotno] + flagsoff); |
950 | |
|
951 | 0 | flagsval = *flagsptr; |
952 | 0 | flagsval &= ~(((1 << MXACT_MEMBER_BITS_PER_XACT) - 1) << bshift); |
953 | 0 | flagsval |= (members[i].status << bshift); |
954 | 0 | *flagsptr = flagsval; |
955 | |
|
956 | 0 | MultiXactMemberCtl->shared->page_dirty[slotno] = true; |
957 | 0 | } |
958 | |
|
959 | 0 | if (prevlock != NULL) |
960 | 0 | LWLockRelease(prevlock); |
961 | 0 | } |
962 | | |
963 | | /* |
964 | | * GetNewMultiXactId |
965 | | * Get the next MultiXactId. |
966 | | * |
967 | | * Also, reserve the needed amount of space in the "members" area. The |
968 | | * starting offset of the reserved space is returned in *offset. |
969 | | * |
970 | | * This may generate XLOG records for expansion of the offsets and/or members |
971 | | * files. Unfortunately, we have to do that while holding MultiXactGenLock |
972 | | * to avoid race conditions --- the XLOG record for zeroing a page must appear |
973 | | * before any backend can possibly try to store data in that page! |
974 | | * |
975 | | * We start a critical section before advancing the shared counters. The |
976 | | * caller must end the critical section after writing SLRU data. |
977 | | */ |
978 | | static MultiXactId |
979 | | GetNewMultiXactId(int nmembers, MultiXactOffset *offset) |
980 | 0 | { |
981 | 0 | MultiXactId result; |
982 | 0 | MultiXactOffset nextOffset; |
983 | |
|
984 | 0 | debug_elog3(DEBUG2, "GetNew: for %d xids", nmembers); |
985 | | |
986 | | /* safety check, we should never get this far in a HS standby */ |
987 | 0 | if (RecoveryInProgress()) |
988 | 0 | elog(ERROR, "cannot assign MultiXactIds during recovery"); |
989 | | |
990 | 0 | LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE); |
991 | | |
992 | | /* Assign the MXID */ |
993 | 0 | result = MultiXactState->nextMXact; |
994 | | |
995 | | /*---------- |
996 | | * Check to see if it's safe to assign another MultiXactId. This protects |
997 | | * against catastrophic data loss due to multixact wraparound. The basic |
998 | | * rules are: |
999 | | * |
1000 | | * If we're past multiVacLimit, start trying to force autovacuum cycles. |
1001 | | * If we're past multiWarnLimit, start issuing warnings. |
1002 | | * If we're past multiStopLimit, refuse to create new MultiXactIds. |
1003 | | * |
1004 | | * Note these are pretty much the same protections in GetNewTransactionId. |
1005 | | *---------- |
1006 | | */ |
1007 | 0 | if (!MultiXactIdPrecedes(result, MultiXactState->multiVacLimit)) |
1008 | 0 | { |
1009 | | /* |
1010 | | * For safety's sake, we release MultiXactGenLock while sending |
1011 | | * signals, warnings, etc. This is not so much because we care about |
1012 | | * preserving concurrency in this situation, as to avoid any |
1013 | | * possibility of deadlock while doing get_database_name(). First, |
1014 | | * copy all the shared values we'll need in this path. |
1015 | | */ |
1016 | 0 | MultiXactId multiWarnLimit = MultiXactState->multiWarnLimit; |
1017 | 0 | MultiXactId multiStopLimit = MultiXactState->multiStopLimit; |
1018 | 0 | MultiXactId multiWrapLimit = MultiXactState->multiWrapLimit; |
1019 | 0 | Oid oldest_datoid = MultiXactState->oldestMultiXactDB; |
1020 | |
|
1021 | 0 | LWLockRelease(MultiXactGenLock); |
1022 | |
|
1023 | 0 | if (IsUnderPostmaster && |
1024 | 0 | !MultiXactIdPrecedes(result, multiStopLimit)) |
1025 | 0 | { |
1026 | 0 | char *oldest_datname = get_database_name(oldest_datoid); |
1027 | | |
1028 | | /* |
1029 | | * Immediately kick autovacuum into action as we're already in |
1030 | | * ERROR territory. |
1031 | | */ |
1032 | 0 | SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER); |
1033 | | |
1034 | | /* complain even if that DB has disappeared */ |
1035 | 0 | if (oldest_datname) |
1036 | 0 | ereport(ERROR, |
1037 | 0 | (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), |
1038 | 0 | errmsg("database is not accepting commands that assign new MultiXactIds to avoid wraparound data loss in database \"%s\"", |
1039 | 0 | oldest_datname), |
1040 | 0 | errhint("Execute a database-wide VACUUM in that database.\n" |
1041 | 0 | "You might also need to commit or roll back old prepared transactions."))); |
1042 | 0 | else |
1043 | 0 | ereport(ERROR, |
1044 | 0 | (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), |
1045 | 0 | errmsg("database is not accepting commands that assign new MultiXactIds to avoid wraparound data loss in database with OID %u", |
1046 | 0 | oldest_datoid), |
1047 | 0 | errhint("Execute a database-wide VACUUM in that database.\n" |
1048 | 0 | "You might also need to commit or roll back old prepared transactions."))); |
1049 | 0 | } |
1050 | | |
1051 | | /* |
1052 | | * To avoid swamping the postmaster with signals, we issue the autovac |
1053 | | * request only once per 64K multis generated. This still gives |
1054 | | * plenty of chances before we get into real trouble. |
1055 | | */ |
1056 | 0 | if (IsUnderPostmaster && ((result % 65536) == 0 || result == FirstMultiXactId)) |
1057 | 0 | SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER); |
1058 | |
|
1059 | 0 | if (!MultiXactIdPrecedes(result, multiWarnLimit)) |
1060 | 0 | { |
1061 | 0 | char *oldest_datname = get_database_name(oldest_datoid); |
1062 | | |
1063 | | /* complain even if that DB has disappeared */ |
1064 | 0 | if (oldest_datname) |
1065 | 0 | ereport(WARNING, |
1066 | 0 | (errmsg_plural("database \"%s\" must be vacuumed before %u more MultiXactId is used", |
1067 | 0 | "database \"%s\" must be vacuumed before %u more MultiXactIds are used", |
1068 | 0 | multiWrapLimit - result, |
1069 | 0 | oldest_datname, |
1070 | 0 | multiWrapLimit - result), |
1071 | 0 | errdetail("Approximately %.2f%% of MultiXactId space remains before wraparound.", |
1072 | 0 | (double) (multiWrapLimit - result) / (MaxMultiXactId / 2) * 100), |
1073 | 0 | errhint("Execute a database-wide VACUUM in that database.\n" |
1074 | 0 | "You might also need to commit or roll back old prepared transactions."))); |
1075 | 0 | else |
1076 | 0 | ereport(WARNING, |
1077 | 0 | (errmsg_plural("database with OID %u must be vacuumed before %u more MultiXactId is used", |
1078 | 0 | "database with OID %u must be vacuumed before %u more MultiXactIds are used", |
1079 | 0 | multiWrapLimit - result, |
1080 | 0 | oldest_datoid, |
1081 | 0 | multiWrapLimit - result), |
1082 | 0 | errdetail("Approximately %.2f%% of MultiXactId space remains before wraparound.", |
1083 | 0 | (double) (multiWrapLimit - result) / (MaxMultiXactId / 2) * 100), |
1084 | 0 | errhint("Execute a database-wide VACUUM in that database.\n" |
1085 | 0 | "You might also need to commit or roll back old prepared transactions."))); |
1086 | 0 | } |
1087 | | |
1088 | | /* Re-acquire lock and start over */ |
1089 | 0 | LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE); |
1090 | 0 | result = MultiXactState->nextMXact; |
1091 | 0 | } |
1092 | | |
1093 | | /* |
1094 | | * Make sure there is room for the next MXID in the file. Assigning this |
1095 | | * MXID sets the next MXID's offset already. |
1096 | | */ |
1097 | 0 | ExtendMultiXactOffset(NextMultiXactId(result)); |
1098 | | |
1099 | | /* |
1100 | | * Reserve the members space, similarly to above. |
1101 | | */ |
1102 | 0 | nextOffset = MultiXactState->nextOffset; |
1103 | | |
1104 | | /* |
1105 | | * Offsets are 64-bit integers and will never wrap around. Firstly, it |
1106 | | * would take an unrealistic amount of time and resources to consume 2^64 |
1107 | | * offsets. Secondly, multixid creation is WAL-logged, so you would run |
1108 | | * out of LSNs before reaching offset wraparound. Nevertheless, check for |
1109 | | * wraparound as a sanity check. |
1110 | | */ |
1111 | 0 | if (nextOffset + nmembers < nextOffset) |
1112 | 0 | ereport(ERROR, |
1113 | 0 | (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), |
1114 | 0 | errmsg("MultiXact members would wrap around"))); |
1115 | 0 | *offset = nextOffset; |
1116 | |
|
1117 | 0 | ExtendMultiXactMember(nextOffset, nmembers); |
1118 | | |
1119 | | /* |
1120 | | * Critical section from here until caller has written the data into the |
1121 | | * just-reserved SLRU space; we don't want to error out with a partly |
1122 | | * written MultiXact structure. (In particular, failing to write our |
1123 | | * start offset after advancing nextMXact would effectively corrupt the |
1124 | | * previous MultiXact.) |
1125 | | */ |
1126 | 0 | START_CRIT_SECTION(); |
1127 | | |
1128 | | /* |
1129 | | * Advance counters. As in GetNewTransactionId(), this must not happen |
1130 | | * until after file extension has succeeded! |
1131 | | */ |
1132 | 0 | MultiXactState->nextMXact = NextMultiXactId(result); |
1133 | 0 | MultiXactState->nextOffset += nmembers; |
1134 | |
|
1135 | 0 | LWLockRelease(MultiXactGenLock); |
1136 | |
|
1137 | 0 | debug_elog4(DEBUG2, "GetNew: returning %u offset %" PRIu64, |
1138 | 0 | result, *offset); |
1139 | 0 | return result; |
1140 | 0 | } |
1141 | | |
1142 | | /* |
1143 | | * GetMultiXactIdMembers |
1144 | | * Return the set of MultiXactMembers that make up a MultiXactId |
1145 | | * |
1146 | | * Return value is the number of members found, or -1 if there are none, |
1147 | | * and *members is set to a newly palloc'ed array of members. It's the |
1148 | | * caller's responsibility to free it when done with it. |
1149 | | * |
1150 | | * from_pgupgrade must be passed as true if and only if only the multixact |
1151 | | * corresponds to a value from a tuple that was locked in a 9.2-or-older |
1152 | | * installation and later pg_upgrade'd (that is, the infomask is |
1153 | | * HEAP_LOCKED_UPGRADED). In this case, we know for certain that no members |
1154 | | * can still be running, so we return -1 just like for an empty multixact |
1155 | | * without any further checking. It would be wrong to try to resolve such a |
1156 | | * multixact: either the multixact is within the current valid multixact |
1157 | | * range, in which case the returned result would be bogus, or outside that |
1158 | | * range, in which case an error would be raised. |
1159 | | * |
1160 | | * In all other cases, the passed multixact must be within the known valid |
1161 | | * range, that is, greater than or equal to oldestMultiXactId, and less than |
1162 | | * nextMXact. Otherwise, an error is raised. |
1163 | | * |
1164 | | * isLockOnly must be set to true if caller is certain that the given multi |
1165 | | * is used only to lock tuples; can be false without loss of correctness, |
1166 | | * but passing a true means we can return quickly without checking for |
1167 | | * old updates. |
1168 | | */ |
1169 | | int |
1170 | | GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members, |
1171 | | bool from_pgupgrade, bool isLockOnly) |
1172 | 0 | { |
1173 | 0 | int64 pageno; |
1174 | 0 | int64 prev_pageno; |
1175 | 0 | int entryno; |
1176 | 0 | int slotno; |
1177 | 0 | MultiXactOffset *offptr; |
1178 | 0 | MultiXactOffset offset; |
1179 | 0 | MultiXactOffset nextMXOffset; |
1180 | 0 | int length; |
1181 | 0 | MultiXactId oldestMXact; |
1182 | 0 | MultiXactId nextMXact; |
1183 | 0 | MultiXactMember *ptr; |
1184 | 0 | LWLock *lock; |
1185 | |
|
1186 | 0 | debug_elog3(DEBUG2, "GetMembers: asked for %u", multi); |
1187 | |
|
1188 | 0 | if (!MultiXactIdIsValid(multi) || from_pgupgrade) |
1189 | 0 | { |
1190 | 0 | *members = NULL; |
1191 | 0 | return -1; |
1192 | 0 | } |
1193 | | |
1194 | | /* See if the MultiXactId is in the local cache */ |
1195 | 0 | length = mXactCacheGetById(multi, members); |
1196 | 0 | if (length >= 0) |
1197 | 0 | { |
1198 | 0 | debug_elog3(DEBUG2, "GetMembers: found %s in the cache", |
1199 | 0 | mxid_to_string(multi, length, *members)); |
1200 | 0 | return length; |
1201 | 0 | } |
1202 | | |
1203 | | /* Set our OldestVisibleMXactId[] entry if we didn't already */ |
1204 | 0 | MultiXactIdSetOldestVisible(); |
1205 | | |
1206 | | /* |
1207 | | * If we know the multi is used only for locking and not for updates, then |
1208 | | * we can skip checking if the value is older than our oldest visible |
1209 | | * multi. It cannot possibly still be running. |
1210 | | */ |
1211 | 0 | if (isLockOnly && |
1212 | 0 | MultiXactIdPrecedes(multi, *MyOldestVisibleMXactIdSlot())) |
1213 | 0 | { |
1214 | 0 | debug_elog2(DEBUG2, "GetMembers: a locker-only multi is too old"); |
1215 | 0 | *members = NULL; |
1216 | 0 | return -1; |
1217 | 0 | } |
1218 | | |
1219 | | /* |
1220 | | * We check known limits on MultiXact before resorting to the SLRU area. |
1221 | | * |
1222 | | * An ID older than MultiXactState->oldestMultiXactId cannot possibly be |
1223 | | * useful; it has already been removed, or will be removed shortly, by |
1224 | | * truncation. If one is passed, an error is raised. |
1225 | | * |
1226 | | * Also, an ID >= nextMXact shouldn't ever be seen here; if it is seen, it |
1227 | | * implies undetected ID wraparound has occurred. This raises a hard |
1228 | | * error. |
1229 | | * |
1230 | | * Shared lock is enough here since we aren't modifying any global state. |
1231 | | * Acquire it just long enough to grab the current counter values. |
1232 | | */ |
1233 | 0 | LWLockAcquire(MultiXactGenLock, LW_SHARED); |
1234 | |
|
1235 | 0 | oldestMXact = MultiXactState->oldestMultiXactId; |
1236 | 0 | nextMXact = MultiXactState->nextMXact; |
1237 | |
|
1238 | 0 | LWLockRelease(MultiXactGenLock); |
1239 | |
|
1240 | 0 | if (MultiXactIdPrecedes(multi, oldestMXact)) |
1241 | 0 | ereport(ERROR, |
1242 | 0 | (errcode(ERRCODE_INTERNAL_ERROR), |
1243 | 0 | errmsg("MultiXactId %u does no longer exist -- apparent wraparound", |
1244 | 0 | multi))); |
1245 | | |
1246 | 0 | if (!MultiXactIdPrecedes(multi, nextMXact)) |
1247 | 0 | ereport(ERROR, |
1248 | 0 | (errcode(ERRCODE_INTERNAL_ERROR), |
1249 | 0 | errmsg("MultiXactId %u has not been created yet -- apparent wraparound", |
1250 | 0 | multi))); |
1251 | | |
1252 | | /* |
1253 | | * Find out the offset at which we need to start reading MultiXactMembers |
1254 | | * and the number of members in the multixact. We determine the latter as |
1255 | | * the difference between this multixact's starting offset and the next |
1256 | | * one's. |
1257 | | */ |
1258 | 0 | pageno = MultiXactIdToOffsetPage(multi); |
1259 | 0 | entryno = MultiXactIdToOffsetEntry(multi); |
1260 | | |
1261 | | /* Acquire the bank lock for the page we need. */ |
1262 | 0 | lock = SimpleLruGetBankLock(MultiXactOffsetCtl, pageno); |
1263 | 0 | LWLockAcquire(lock, LW_EXCLUSIVE); |
1264 | | |
1265 | | /* read this multi's offset */ |
1266 | 0 | slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, &multi); |
1267 | 0 | offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno]; |
1268 | 0 | offptr += entryno; |
1269 | 0 | offset = *offptr; |
1270 | |
|
1271 | 0 | if (offset == 0) |
1272 | 0 | ereport(ERROR, |
1273 | 0 | (errcode(ERRCODE_DATA_CORRUPTED), |
1274 | 0 | errmsg("MultiXact %u has invalid offset", multi))); |
1275 | | |
1276 | | /* read next multi's offset */ |
1277 | 0 | { |
1278 | 0 | MultiXactId tmpMXact; |
1279 | | |
1280 | | /* handle wraparound if needed */ |
1281 | 0 | tmpMXact = NextMultiXactId(multi); |
1282 | |
|
1283 | 0 | prev_pageno = pageno; |
1284 | |
|
1285 | 0 | pageno = MultiXactIdToOffsetPage(tmpMXact); |
1286 | 0 | entryno = MultiXactIdToOffsetEntry(tmpMXact); |
1287 | |
|
1288 | 0 | if (pageno != prev_pageno) |
1289 | 0 | { |
1290 | 0 | LWLock *newlock; |
1291 | | |
1292 | | /* |
1293 | | * Since we're going to access a different SLRU page, if this page |
1294 | | * falls under a different bank, release the old bank's lock and |
1295 | | * acquire the lock of the new bank. |
1296 | | */ |
1297 | 0 | newlock = SimpleLruGetBankLock(MultiXactOffsetCtl, pageno); |
1298 | 0 | if (newlock != lock) |
1299 | 0 | { |
1300 | 0 | LWLockRelease(lock); |
1301 | 0 | LWLockAcquire(newlock, LW_EXCLUSIVE); |
1302 | 0 | lock = newlock; |
1303 | 0 | } |
1304 | 0 | slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, &tmpMXact); |
1305 | 0 | } |
1306 | |
|
1307 | 0 | offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno]; |
1308 | 0 | offptr += entryno; |
1309 | 0 | nextMXOffset = *offptr; |
1310 | 0 | } |
1311 | |
|
1312 | 0 | LWLockRelease(lock); |
1313 | 0 | lock = NULL; |
1314 | | |
1315 | | /* Sanity check the next offset */ |
1316 | 0 | if (nextMXOffset == 0) |
1317 | 0 | ereport(ERROR, |
1318 | 0 | (errcode(ERRCODE_DATA_CORRUPTED), |
1319 | 0 | errmsg("MultiXact %u has invalid next offset", multi))); |
1320 | 0 | if (nextMXOffset == offset) |
1321 | 0 | ereport(ERROR, |
1322 | 0 | (errcode(ERRCODE_DATA_CORRUPTED), |
1323 | 0 | errmsg("MultiXact %u with offset (%" PRIu64 ") has zero members", |
1324 | 0 | multi, offset))); |
1325 | 0 | if (nextMXOffset < offset) |
1326 | 0 | ereport(ERROR, |
1327 | 0 | (errcode(ERRCODE_DATA_CORRUPTED), |
1328 | 0 | errmsg("MultiXact %u has offset (%" PRIu64 ") greater than its next offset (%" PRIu64 ")", |
1329 | 0 | multi, offset, nextMXOffset))); |
1330 | 0 | if (nextMXOffset - offset > INT32_MAX) |
1331 | 0 | ereport(ERROR, |
1332 | 0 | (errcode(ERRCODE_DATA_CORRUPTED), |
1333 | 0 | errmsg("MultiXact %u has too many members (%" PRIu64 ")", |
1334 | 0 | multi, nextMXOffset - offset))); |
1335 | 0 | length = nextMXOffset - offset; |
1336 | | |
1337 | | /* read the members */ |
1338 | 0 | ptr = palloc_array(MultiXactMember, length); |
1339 | 0 | prev_pageno = -1; |
1340 | 0 | for (int i = 0; i < length; i++, offset++) |
1341 | 0 | { |
1342 | 0 | TransactionId *xactptr; |
1343 | 0 | uint32 *flagsptr; |
1344 | 0 | int flagsoff; |
1345 | 0 | int bshift; |
1346 | 0 | int memberoff; |
1347 | |
|
1348 | 0 | pageno = MXOffsetToMemberPage(offset); |
1349 | 0 | memberoff = MXOffsetToMemberOffset(offset); |
1350 | |
|
1351 | 0 | if (pageno != prev_pageno) |
1352 | 0 | { |
1353 | 0 | MultiXactMemberSlruReadContext slru_read_context = {multi, offset}; |
1354 | 0 | LWLock *newlock; |
1355 | | |
1356 | | /* |
1357 | | * Since we're going to access a different SLRU page, if this page |
1358 | | * falls under a different bank, release the old bank's lock and |
1359 | | * acquire the lock of the new bank. |
1360 | | */ |
1361 | 0 | newlock = SimpleLruGetBankLock(MultiXactMemberCtl, pageno); |
1362 | 0 | if (newlock != lock) |
1363 | 0 | { |
1364 | 0 | if (lock) |
1365 | 0 | LWLockRelease(lock); |
1366 | 0 | LWLockAcquire(newlock, LW_EXCLUSIVE); |
1367 | 0 | lock = newlock; |
1368 | 0 | } |
1369 | 0 | slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, |
1370 | 0 | &slru_read_context); |
1371 | 0 | prev_pageno = pageno; |
1372 | 0 | } |
1373 | |
|
1374 | 0 | xactptr = (TransactionId *) |
1375 | 0 | (MultiXactMemberCtl->shared->page_buffer[slotno] + memberoff); |
1376 | 0 | Assert(TransactionIdIsValid(*xactptr)); |
1377 | |
|
1378 | 0 | flagsoff = MXOffsetToFlagsOffset(offset); |
1379 | 0 | bshift = MXOffsetToFlagsBitShift(offset); |
1380 | 0 | flagsptr = (uint32 *) (MultiXactMemberCtl->shared->page_buffer[slotno] + flagsoff); |
1381 | |
|
1382 | 0 | ptr[i].xid = *xactptr; |
1383 | 0 | ptr[i].status = (*flagsptr >> bshift) & MXACT_MEMBER_XACT_BITMASK; |
1384 | 0 | } |
1385 | |
|
1386 | 0 | LWLockRelease(lock); |
1387 | | |
1388 | | /* |
1389 | | * Copy the result into the local cache. |
1390 | | */ |
1391 | 0 | mXactCachePut(multi, length, ptr); |
1392 | |
|
1393 | 0 | debug_elog3(DEBUG2, "GetMembers: no cache for %s", |
1394 | 0 | mxid_to_string(multi, length, ptr)); |
1395 | 0 | *members = ptr; |
1396 | 0 | return length; |
1397 | 0 | } |
1398 | | |
1399 | | /* |
1400 | | * mxactMemberComparator |
1401 | | * qsort comparison function for MultiXactMember |
1402 | | * |
1403 | | * We can't use wraparound comparison for XIDs because that does not respect |
1404 | | * the triangle inequality! Any old sort order will do. |
1405 | | */ |
1406 | | static int |
1407 | | mxactMemberComparator(const void *arg1, const void *arg2) |
1408 | 0 | { |
1409 | 0 | MultiXactMember member1 = *(const MultiXactMember *) arg1; |
1410 | 0 | MultiXactMember member2 = *(const MultiXactMember *) arg2; |
1411 | |
|
1412 | 0 | if (member1.xid > member2.xid) |
1413 | 0 | return 1; |
1414 | 0 | if (member1.xid < member2.xid) |
1415 | 0 | return -1; |
1416 | 0 | if (member1.status > member2.status) |
1417 | 0 | return 1; |
1418 | 0 | if (member1.status < member2.status) |
1419 | 0 | return -1; |
1420 | 0 | return 0; |
1421 | 0 | } |
1422 | | |
1423 | | /* |
1424 | | * mXactCacheGetBySet |
1425 | | * returns a MultiXactId from the cache based on the set of |
1426 | | * TransactionIds that compose it, or InvalidMultiXactId if |
1427 | | * none matches. |
1428 | | * |
1429 | | * This is helpful, for example, if two transactions want to lock a huge |
1430 | | * table. By using the cache, the second will use the same MultiXactId |
1431 | | * for the majority of tuples, thus keeping MultiXactId usage low (saving |
1432 | | * both I/O and wraparound issues). |
1433 | | * |
1434 | | * NB: the passed members array will be sorted in-place. |
1435 | | */ |
1436 | | static MultiXactId |
1437 | | mXactCacheGetBySet(int nmembers, MultiXactMember *members) |
1438 | 0 | { |
1439 | 0 | dlist_iter iter; |
1440 | |
|
1441 | 0 | debug_elog3(DEBUG2, "CacheGet: looking for %s", |
1442 | 0 | mxid_to_string(InvalidMultiXactId, nmembers, members)); |
1443 | | |
1444 | | /* sort the array so comparison is easy */ |
1445 | 0 | qsort(members, nmembers, sizeof(MultiXactMember), mxactMemberComparator); |
1446 | |
|
1447 | 0 | dclist_foreach(iter, &MXactCache) |
1448 | 0 | { |
1449 | 0 | mXactCacheEnt *entry = dclist_container(mXactCacheEnt, node, |
1450 | 0 | iter.cur); |
1451 | |
|
1452 | 0 | if (entry->nmembers != nmembers) |
1453 | 0 | continue; |
1454 | | |
1455 | | /* |
1456 | | * We assume the cache entries are sorted, and that the unused bits in |
1457 | | * "status" are zeroed. |
1458 | | */ |
1459 | 0 | if (memcmp(members, entry->members, nmembers * sizeof(MultiXactMember)) == 0) |
1460 | 0 | { |
1461 | 0 | debug_elog3(DEBUG2, "CacheGet: found %u", entry->multi); |
1462 | 0 | dclist_move_head(&MXactCache, iter.cur); |
1463 | 0 | return entry->multi; |
1464 | 0 | } |
1465 | 0 | } |
1466 | | |
1467 | 0 | debug_elog2(DEBUG2, "CacheGet: not found :-("); |
1468 | 0 | return InvalidMultiXactId; |
1469 | 0 | } |
1470 | | |
1471 | | /* |
1472 | | * mXactCacheGetById |
1473 | | * returns the composing MultiXactMember set from the cache for a |
1474 | | * given MultiXactId, if present. |
1475 | | * |
1476 | | * If successful, *xids is set to the address of a palloc'd copy of the |
1477 | | * MultiXactMember set. Return value is number of members, or -1 on failure. |
1478 | | */ |
1479 | | static int |
1480 | | mXactCacheGetById(MultiXactId multi, MultiXactMember **members) |
1481 | 0 | { |
1482 | 0 | dlist_iter iter; |
1483 | |
|
1484 | 0 | debug_elog3(DEBUG2, "CacheGet: looking for %u", multi); |
1485 | |
|
1486 | 0 | dclist_foreach(iter, &MXactCache) |
1487 | 0 | { |
1488 | 0 | mXactCacheEnt *entry = dclist_container(mXactCacheEnt, node, |
1489 | 0 | iter.cur); |
1490 | |
|
1491 | 0 | if (entry->multi == multi) |
1492 | 0 | { |
1493 | 0 | MultiXactMember *ptr; |
1494 | 0 | Size size; |
1495 | |
|
1496 | 0 | size = sizeof(MultiXactMember) * entry->nmembers; |
1497 | 0 | ptr = (MultiXactMember *) palloc(size); |
1498 | |
|
1499 | 0 | memcpy(ptr, entry->members, size); |
1500 | |
|
1501 | 0 | debug_elog3(DEBUG2, "CacheGet: found %s", |
1502 | 0 | mxid_to_string(multi, |
1503 | 0 | entry->nmembers, |
1504 | 0 | entry->members)); |
1505 | | |
1506 | | /* |
1507 | | * Note we modify the list while not using a modifiable iterator. |
1508 | | * This is acceptable only because we exit the iteration |
1509 | | * immediately afterwards. |
1510 | | */ |
1511 | 0 | dclist_move_head(&MXactCache, iter.cur); |
1512 | |
|
1513 | 0 | *members = ptr; |
1514 | 0 | return entry->nmembers; |
1515 | 0 | } |
1516 | 0 | } |
1517 | | |
1518 | 0 | debug_elog2(DEBUG2, "CacheGet: not found"); |
1519 | 0 | return -1; |
1520 | 0 | } |
1521 | | |
1522 | | /* |
1523 | | * mXactCachePut |
1524 | | * Add a new MultiXactId and its composing set into the local cache. |
1525 | | */ |
1526 | | static void |
1527 | | mXactCachePut(MultiXactId multi, int nmembers, MultiXactMember *members) |
1528 | 0 | { |
1529 | 0 | mXactCacheEnt *entry; |
1530 | |
|
1531 | 0 | debug_elog3(DEBUG2, "CachePut: storing %s", |
1532 | 0 | mxid_to_string(multi, nmembers, members)); |
1533 | |
|
1534 | 0 | if (MXactContext == NULL) |
1535 | 0 | { |
1536 | | /* The cache only lives as long as the current transaction */ |
1537 | 0 | debug_elog2(DEBUG2, "CachePut: initializing memory context"); |
1538 | 0 | MXactContext = AllocSetContextCreate(TopTransactionContext, |
1539 | 0 | "MultiXact cache context", |
1540 | 0 | ALLOCSET_SMALL_SIZES); |
1541 | 0 | } |
1542 | |
|
1543 | 0 | entry = (mXactCacheEnt *) |
1544 | 0 | MemoryContextAlloc(MXactContext, |
1545 | 0 | offsetof(mXactCacheEnt, members) + |
1546 | 0 | nmembers * sizeof(MultiXactMember)); |
1547 | |
|
1548 | 0 | entry->multi = multi; |
1549 | 0 | entry->nmembers = nmembers; |
1550 | 0 | memcpy(entry->members, members, nmembers * sizeof(MultiXactMember)); |
1551 | | |
1552 | | /* mXactCacheGetBySet assumes the entries are sorted, so sort them */ |
1553 | 0 | qsort(entry->members, nmembers, sizeof(MultiXactMember), mxactMemberComparator); |
1554 | |
|
1555 | 0 | dclist_push_head(&MXactCache, &entry->node); |
1556 | 0 | if (dclist_count(&MXactCache) > MAX_CACHE_ENTRIES) |
1557 | 0 | { |
1558 | 0 | dlist_node *node; |
1559 | |
|
1560 | 0 | node = dclist_tail_node(&MXactCache); |
1561 | 0 | dclist_delete_from(&MXactCache, node); |
1562 | |
|
1563 | 0 | entry = dclist_container(mXactCacheEnt, node, node); |
1564 | 0 | debug_elog3(DEBUG2, "CachePut: pruning cached multi %u", |
1565 | 0 | entry->multi); |
1566 | |
|
1567 | 0 | pfree(entry); |
1568 | 0 | } |
1569 | 0 | } |
1570 | | |
1571 | | char * |
1572 | | mxstatus_to_string(MultiXactStatus status) |
1573 | 0 | { |
1574 | 0 | switch (status) |
1575 | 0 | { |
1576 | 0 | case MultiXactStatusForKeyShare: |
1577 | 0 | return "keysh"; |
1578 | 0 | case MultiXactStatusForShare: |
1579 | 0 | return "sh"; |
1580 | 0 | case MultiXactStatusForNoKeyUpdate: |
1581 | 0 | return "fornokeyupd"; |
1582 | 0 | case MultiXactStatusForUpdate: |
1583 | 0 | return "forupd"; |
1584 | 0 | case MultiXactStatusNoKeyUpdate: |
1585 | 0 | return "nokeyupd"; |
1586 | 0 | case MultiXactStatusUpdate: |
1587 | 0 | return "upd"; |
1588 | 0 | default: |
1589 | 0 | elog(ERROR, "unrecognized multixact status %d", status); |
1590 | 0 | return ""; |
1591 | 0 | } |
1592 | 0 | } |
1593 | | |
1594 | | char * |
1595 | | mxid_to_string(MultiXactId multi, int nmembers, MultiXactMember *members) |
1596 | 0 | { |
1597 | 0 | static char *str = NULL; |
1598 | 0 | StringInfoData buf; |
1599 | 0 | int i; |
1600 | |
|
1601 | 0 | if (str != NULL) |
1602 | 0 | pfree(str); |
1603 | |
|
1604 | 0 | initStringInfo(&buf); |
1605 | |
|
1606 | 0 | appendStringInfo(&buf, "%u %d[%u (%s)", multi, nmembers, members[0].xid, |
1607 | 0 | mxstatus_to_string(members[0].status)); |
1608 | |
|
1609 | 0 | for (i = 1; i < nmembers; i++) |
1610 | 0 | appendStringInfo(&buf, ", %u (%s)", members[i].xid, |
1611 | 0 | mxstatus_to_string(members[i].status)); |
1612 | |
|
1613 | 0 | appendStringInfoChar(&buf, ']'); |
1614 | 0 | str = MemoryContextStrdup(TopMemoryContext, buf.data); |
1615 | 0 | pfree(buf.data); |
1616 | 0 | return str; |
1617 | 0 | } |
1618 | | |
1619 | | /* |
1620 | | * AtEOXact_MultiXact |
1621 | | * Handle transaction end for MultiXact |
1622 | | * |
1623 | | * This is called at top transaction commit or abort (we don't care which). |
1624 | | */ |
1625 | | void |
1626 | | AtEOXact_MultiXact(void) |
1627 | 0 | { |
1628 | | /* |
1629 | | * Reset our OldestMemberMXactId and OldestVisibleMXactId values, both of |
1630 | | * which should only be valid while within a transaction. |
1631 | | * |
1632 | | * We assume that storing a MultiXactId is atomic and so we need not take |
1633 | | * MultiXactGenLock to do this. |
1634 | | */ |
1635 | 0 | *MyOldestMemberMXactIdSlot() = InvalidMultiXactId; |
1636 | 0 | *MyOldestVisibleMXactIdSlot() = InvalidMultiXactId; |
1637 | | |
1638 | | /* |
1639 | | * Discard the local MultiXactId cache. Since MXactContext was created as |
1640 | | * a child of TopTransactionContext, we needn't delete it explicitly. |
1641 | | */ |
1642 | 0 | MXactContext = NULL; |
1643 | 0 | dclist_init(&MXactCache); |
1644 | 0 | } |
1645 | | |
1646 | | /* |
1647 | | * AtPrepare_MultiXact |
1648 | | * Save multixact state at 2PC transaction prepare |
1649 | | * |
1650 | | * In this phase, we only store our OldestMemberMXactId value in the two-phase |
1651 | | * state file. |
1652 | | */ |
1653 | | void |
1654 | | AtPrepare_MultiXact(void) |
1655 | 0 | { |
1656 | 0 | MultiXactId myOldestMember = *MyOldestMemberMXactIdSlot(); |
1657 | |
|
1658 | 0 | if (MultiXactIdIsValid(myOldestMember)) |
1659 | 0 | RegisterTwoPhaseRecord(TWOPHASE_RM_MULTIXACT_ID, 0, |
1660 | 0 | &myOldestMember, sizeof(MultiXactId)); |
1661 | 0 | } |
1662 | | |
1663 | | /* |
1664 | | * PostPrepare_MultiXact |
1665 | | * Clean up after successful PREPARE TRANSACTION |
1666 | | */ |
1667 | | void |
1668 | | PostPrepare_MultiXact(FullTransactionId fxid) |
1669 | 0 | { |
1670 | 0 | MultiXactId myOldestMember; |
1671 | | |
1672 | | /* |
1673 | | * Transfer our OldestMemberMXactId value to the slot reserved for the |
1674 | | * prepared transaction. |
1675 | | */ |
1676 | 0 | myOldestMember = *MyOldestMemberMXactIdSlot(); |
1677 | 0 | if (MultiXactIdIsValid(myOldestMember)) |
1678 | 0 | { |
1679 | 0 | ProcNumber dummyProcNumber = TwoPhaseGetDummyProcNumber(fxid, false); |
1680 | | |
1681 | | /* |
1682 | | * Even though storing MultiXactId is atomic, acquire lock to make |
1683 | | * sure others see both changes, not just the reset of the slot of the |
1684 | | * current backend. Using a volatile pointer might suffice, but this |
1685 | | * isn't a hot spot. |
1686 | | */ |
1687 | 0 | LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE); |
1688 | |
|
1689 | 0 | *PreparedXactOldestMemberMXactIdSlot(dummyProcNumber) = myOldestMember; |
1690 | 0 | *MyOldestMemberMXactIdSlot() = InvalidMultiXactId; |
1691 | |
|
1692 | 0 | LWLockRelease(MultiXactGenLock); |
1693 | 0 | } |
1694 | | |
1695 | | /* |
1696 | | * We don't need to transfer OldestVisibleMXactId value, because the |
1697 | | * transaction is not going to be looking at any more multixacts once it's |
1698 | | * prepared. |
1699 | | * |
1700 | | * We assume that storing a MultiXactId is atomic and so we need not take |
1701 | | * MultiXactGenLock to do this. |
1702 | | */ |
1703 | 0 | *MyOldestVisibleMXactIdSlot() = InvalidMultiXactId; |
1704 | | |
1705 | | /* |
1706 | | * Discard the local MultiXactId cache like in AtEOXact_MultiXact. |
1707 | | */ |
1708 | 0 | MXactContext = NULL; |
1709 | 0 | dclist_init(&MXactCache); |
1710 | 0 | } |
1711 | | |
1712 | | /* |
1713 | | * multixact_twophase_recover |
1714 | | * Recover the state of a prepared transaction at startup |
1715 | | */ |
1716 | | void |
1717 | | multixact_twophase_recover(FullTransactionId fxid, uint16 info, |
1718 | | void *recdata, uint32 len) |
1719 | 0 | { |
1720 | 0 | ProcNumber dummyProcNumber = TwoPhaseGetDummyProcNumber(fxid, false); |
1721 | 0 | MultiXactId oldestMember; |
1722 | | |
1723 | | /* |
1724 | | * Get the oldest member XID from the state file record, and set it in the |
1725 | | * OldestMemberMXactId slot reserved for this prepared transaction. |
1726 | | */ |
1727 | 0 | Assert(len == sizeof(MultiXactId)); |
1728 | 0 | oldestMember = *((MultiXactId *) recdata); |
1729 | |
|
1730 | 0 | *PreparedXactOldestMemberMXactIdSlot(dummyProcNumber) = oldestMember; |
1731 | 0 | } |
1732 | | |
1733 | | /* |
1734 | | * multixact_twophase_postcommit |
1735 | | * Similar to AtEOXact_MultiXact but for COMMIT PREPARED |
1736 | | */ |
1737 | | void |
1738 | | multixact_twophase_postcommit(FullTransactionId fxid, uint16 info, |
1739 | | void *recdata, uint32 len) |
1740 | 0 | { |
1741 | 0 | ProcNumber dummyProcNumber = TwoPhaseGetDummyProcNumber(fxid, true); |
1742 | |
|
1743 | 0 | Assert(len == sizeof(MultiXactId)); |
1744 | |
|
1745 | 0 | *PreparedXactOldestMemberMXactIdSlot(dummyProcNumber) = InvalidMultiXactId; |
1746 | 0 | } |
1747 | | |
1748 | | /* |
1749 | | * multixact_twophase_postabort |
1750 | | * This is actually just the same as the COMMIT case. |
1751 | | */ |
1752 | | void |
1753 | | multixact_twophase_postabort(FullTransactionId fxid, uint16 info, |
1754 | | void *recdata, uint32 len) |
1755 | 0 | { |
1756 | 0 | multixact_twophase_postcommit(fxid, info, recdata, len); |
1757 | 0 | } |
1758 | | |
1759 | | |
1760 | | /* |
1761 | | * Register shared memory needs for MultiXact. |
1762 | | */ |
1763 | | static void |
1764 | | MultiXactShmemRequest(void *arg) |
1765 | 0 | { |
1766 | 0 | Size size; |
1767 | | |
1768 | | /* |
1769 | | * Calculate the size of the MultiXactState struct, and the two |
1770 | | * per-backend MultiXactId arrays. They are carved out of the same |
1771 | | * allocation. |
1772 | | */ |
1773 | 0 | size = offsetof(MultiXactStateData, perBackendXactIds); |
1774 | 0 | size = add_size(size, |
1775 | 0 | mul_size(sizeof(MultiXactId), NumMemberSlots)); |
1776 | 0 | size = add_size(size, |
1777 | 0 | mul_size(sizeof(MultiXactId), NumVisibleSlots)); |
1778 | 0 | ShmemRequestStruct(.name = "Shared MultiXact State", |
1779 | 0 | .size = size, |
1780 | 0 | .ptr = (void **) &MultiXactState, |
1781 | 0 | ); |
1782 | |
|
1783 | 0 | SimpleLruRequest(.desc = &MultiXactOffsetSlruDesc, |
1784 | 0 | .name = "multixact_offset", |
1785 | 0 | .Dir = "pg_multixact/offsets", |
1786 | 0 | .long_segment_names = false, |
1787 | |
|
1788 | 0 | .nslots = multixact_offset_buffers, |
1789 | |
|
1790 | 0 | .sync_handler = SYNC_HANDLER_MULTIXACT_OFFSET, |
1791 | 0 | .PagePrecedes = MultiXactOffsetPagePrecedes, |
1792 | 0 | .errdetail_for_io_error = MultiXactOffsetIoErrorDetail, |
1793 | |
|
1794 | 0 | .buffer_tranche_id = LWTRANCHE_MULTIXACTOFFSET_BUFFER, |
1795 | 0 | .bank_tranche_id = LWTRANCHE_MULTIXACTOFFSET_SLRU, |
1796 | 0 | ); |
1797 | |
|
1798 | 0 | SimpleLruRequest(.desc = &MultiXactMemberSlruDesc, |
1799 | 0 | .name = "multixact_member", |
1800 | 0 | .Dir = "pg_multixact/members", |
1801 | 0 | .long_segment_names = true, |
1802 | |
|
1803 | 0 | .nslots = multixact_member_buffers, |
1804 | |
|
1805 | 0 | .sync_handler = SYNC_HANDLER_MULTIXACT_MEMBER, |
1806 | 0 | .PagePrecedes = MultiXactMemberPagePrecedes, |
1807 | 0 | .errdetail_for_io_error = MultiXactMemberIoErrorDetail, |
1808 | |
|
1809 | 0 | .buffer_tranche_id = LWTRANCHE_MULTIXACTMEMBER_BUFFER, |
1810 | 0 | .bank_tranche_id = LWTRANCHE_MULTIXACTMEMBER_SLRU, |
1811 | 0 | ); |
1812 | 0 | } |
1813 | | |
1814 | | static void |
1815 | | MultiXactShmemInit(void *arg) |
1816 | 0 | { |
1817 | 0 | SlruPagePrecedesUnitTests(MultiXactOffsetCtl, MULTIXACT_OFFSETS_PER_PAGE); |
1818 | | |
1819 | | /* |
1820 | | * members SLRU doesn't call SimpleLruTruncate() or meet criteria for unit |
1821 | | * tests |
1822 | | */ |
1823 | | |
1824 | | /* Set up array pointers */ |
1825 | 0 | OldestMemberMXactId = MultiXactState->perBackendXactIds; |
1826 | 0 | OldestVisibleMXactId = OldestMemberMXactId + NumMemberSlots; |
1827 | 0 | } |
1828 | | |
1829 | | static void |
1830 | | MultiXactShmemAttach(void *arg) |
1831 | 0 | { |
1832 | | /* Set up array pointers */ |
1833 | 0 | OldestMemberMXactId = MultiXactState->perBackendXactIds; |
1834 | 0 | OldestVisibleMXactId = OldestMemberMXactId + NumMemberSlots; |
1835 | 0 | } |
1836 | | |
1837 | | /* |
1838 | | * GUC check_hook for multixact_offset_buffers |
1839 | | */ |
1840 | | bool |
1841 | | check_multixact_offset_buffers(int *newval, void **extra, GucSource source) |
1842 | 2 | { |
1843 | 2 | return check_slru_buffers("multixact_offset_buffers", newval); |
1844 | 2 | } |
1845 | | |
1846 | | /* |
1847 | | * GUC check_hook for multixact_member_buffers |
1848 | | */ |
1849 | | bool |
1850 | | check_multixact_member_buffers(int *newval, void **extra, GucSource source) |
1851 | 2 | { |
1852 | 2 | return check_slru_buffers("multixact_member_buffers", newval); |
1853 | 2 | } |
1854 | | |
1855 | | /* |
1856 | | * This func must be called ONCE on system install. It creates the initial |
1857 | | * MultiXact segments. (The MultiXacts directories are assumed to have been |
1858 | | * created by initdb, and MultiXactShmemInit must have been called already.) |
1859 | | */ |
1860 | | void |
1861 | | BootStrapMultiXact(void) |
1862 | 0 | { |
1863 | | /* Zero the initial pages and flush them to disk */ |
1864 | 0 | SimpleLruZeroAndWritePage(MultiXactOffsetCtl, 0); |
1865 | 0 | SimpleLruZeroAndWritePage(MultiXactMemberCtl, 0); |
1866 | 0 | } |
1867 | | |
1868 | | /* |
1869 | | * This must be called ONCE during postmaster or standalone-backend startup. |
1870 | | * |
1871 | | * StartupXLOG has already established nextMXact/nextOffset by calling |
1872 | | * MultiXactSetNextMXact and/or MultiXactAdvanceNextMXact, and the oldestMulti |
1873 | | * info from pg_control and/or MultiXactAdvanceOldest, but we haven't yet |
1874 | | * replayed WAL. |
1875 | | */ |
1876 | | void |
1877 | | StartupMultiXact(void) |
1878 | 0 | { |
1879 | 0 | MultiXactId multi = MultiXactState->nextMXact; |
1880 | 0 | MultiXactOffset offset = MultiXactState->nextOffset; |
1881 | 0 | int64 pageno; |
1882 | | |
1883 | | /* |
1884 | | * Initialize offset's idea of the latest page number. |
1885 | | */ |
1886 | 0 | pageno = MultiXactIdToOffsetPage(multi); |
1887 | 0 | pg_atomic_write_u64(&MultiXactOffsetCtl->shared->latest_page_number, |
1888 | 0 | pageno); |
1889 | | |
1890 | | /* |
1891 | | * Initialize member's idea of the latest page number. |
1892 | | */ |
1893 | 0 | pageno = MXOffsetToMemberPage(offset); |
1894 | 0 | pg_atomic_write_u64(&MultiXactMemberCtl->shared->latest_page_number, |
1895 | 0 | pageno); |
1896 | 0 | } |
1897 | | |
1898 | | /* |
1899 | | * This must be called ONCE at the end of startup/recovery. |
1900 | | */ |
1901 | | void |
1902 | | TrimMultiXact(void) |
1903 | 0 | { |
1904 | 0 | MultiXactId nextMXact; |
1905 | 0 | MultiXactOffset offset; |
1906 | 0 | MultiXactId oldestMXact; |
1907 | 0 | Oid oldestMXactDB; |
1908 | 0 | int64 pageno; |
1909 | 0 | int entryno; |
1910 | 0 | int flagsoff; |
1911 | |
|
1912 | 0 | LWLockAcquire(MultiXactGenLock, LW_SHARED); |
1913 | 0 | nextMXact = MultiXactState->nextMXact; |
1914 | 0 | offset = MultiXactState->nextOffset; |
1915 | 0 | oldestMXact = MultiXactState->oldestMultiXactId; |
1916 | 0 | oldestMXactDB = MultiXactState->oldestMultiXactDB; |
1917 | 0 | LWLockRelease(MultiXactGenLock); |
1918 | | |
1919 | | /* Clean up offsets state */ |
1920 | | |
1921 | | /* |
1922 | | * (Re-)Initialize our idea of the latest page number for offsets. |
1923 | | */ |
1924 | 0 | pageno = MultiXactIdToOffsetPage(nextMXact); |
1925 | 0 | pg_atomic_write_u64(&MultiXactOffsetCtl->shared->latest_page_number, |
1926 | 0 | pageno); |
1927 | | |
1928 | | /* |
1929 | | * Set the offset of nextMXact on the offsets page. This is normally done |
1930 | | * in RecordNewMultiXact() of the previous multixact, but let's be sure |
1931 | | * the next page exists, if the nextMXact was reset with pg_resetwal for |
1932 | | * example. |
1933 | | * |
1934 | | * Zero out the remainder of the page. See notes in TrimCLOG() for |
1935 | | * background. Unlike CLOG, some WAL record covers every pg_multixact |
1936 | | * SLRU mutation. Since, also unlike CLOG, we ignore the WAL rule "write |
1937 | | * xlog before data," nextMXact successors may carry obsolete, nonzero |
1938 | | * offset values. |
1939 | | */ |
1940 | 0 | entryno = MultiXactIdToOffsetEntry(nextMXact); |
1941 | 0 | { |
1942 | 0 | int slotno; |
1943 | 0 | MultiXactOffset *offptr; |
1944 | 0 | LWLock *lock = SimpleLruGetBankLock(MultiXactOffsetCtl, pageno); |
1945 | |
|
1946 | 0 | LWLockAcquire(lock, LW_EXCLUSIVE); |
1947 | 0 | if (entryno == 0 || nextMXact == FirstMultiXactId) |
1948 | 0 | slotno = SimpleLruZeroPage(MultiXactOffsetCtl, pageno); |
1949 | 0 | else |
1950 | 0 | slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, &nextMXact); |
1951 | 0 | offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno]; |
1952 | 0 | offptr += entryno; |
1953 | |
|
1954 | 0 | *offptr = offset; |
1955 | 0 | if (entryno != 0 && (entryno + 1) * sizeof(MultiXactOffset) != BLCKSZ) |
1956 | 0 | MemSet(offptr + 1, 0, BLCKSZ - (entryno + 1) * sizeof(MultiXactOffset)); |
1957 | |
|
1958 | 0 | MultiXactOffsetCtl->shared->page_dirty[slotno] = true; |
1959 | 0 | LWLockRelease(lock); |
1960 | 0 | } |
1961 | | |
1962 | | /* |
1963 | | * And the same for members. |
1964 | | * |
1965 | | * (Re-)Initialize our idea of the latest page number for members. |
1966 | | */ |
1967 | 0 | pageno = MXOffsetToMemberPage(offset); |
1968 | 0 | pg_atomic_write_u64(&MultiXactMemberCtl->shared->latest_page_number, |
1969 | 0 | pageno); |
1970 | | |
1971 | | /* |
1972 | | * Zero out the remainder of the current members page. See notes in |
1973 | | * TrimCLOG() for motivation. |
1974 | | */ |
1975 | 0 | flagsoff = MXOffsetToFlagsOffset(offset); |
1976 | 0 | if (flagsoff != 0) |
1977 | 0 | { |
1978 | 0 | MultiXactMemberSlruReadContext slru_read_context = {InvalidMultiXactId, offset}; |
1979 | 0 | int slotno; |
1980 | 0 | TransactionId *xidptr; |
1981 | 0 | int memberoff; |
1982 | 0 | LWLock *lock = SimpleLruGetBankLock(MultiXactMemberCtl, pageno); |
1983 | |
|
1984 | 0 | LWLockAcquire(lock, LW_EXCLUSIVE); |
1985 | 0 | memberoff = MXOffsetToMemberOffset(offset); |
1986 | 0 | slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, &slru_read_context); |
1987 | 0 | xidptr = (TransactionId *) |
1988 | 0 | (MultiXactMemberCtl->shared->page_buffer[slotno] + memberoff); |
1989 | |
|
1990 | 0 | MemSet(xidptr, 0, BLCKSZ - memberoff); |
1991 | | |
1992 | | /* |
1993 | | * Note: we don't need to zero out the flag bits in the remaining |
1994 | | * members of the current group, because they are always reset before |
1995 | | * writing. |
1996 | | */ |
1997 | |
|
1998 | 0 | MultiXactMemberCtl->shared->page_dirty[slotno] = true; |
1999 | 0 | LWLockRelease(lock); |
2000 | 0 | } |
2001 | | |
2002 | | /* signal that we're officially up */ |
2003 | 0 | LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE); |
2004 | 0 | MultiXactState->finishedStartup = true; |
2005 | 0 | LWLockRelease(MultiXactGenLock); |
2006 | | |
2007 | | /* Now compute how far away the next multixid wraparound is. */ |
2008 | 0 | SetMultiXactIdLimit(oldestMXact, oldestMXactDB); |
2009 | 0 | } |
2010 | | |
2011 | | /* |
2012 | | * Get the MultiXact data to save in a checkpoint record |
2013 | | */ |
2014 | | void |
2015 | | MultiXactGetCheckptMulti(bool is_shutdown, |
2016 | | MultiXactId *nextMulti, |
2017 | | MultiXactOffset *nextMultiOffset, |
2018 | | MultiXactId *oldestMulti, |
2019 | | Oid *oldestMultiDB) |
2020 | 0 | { |
2021 | 0 | LWLockAcquire(MultiXactGenLock, LW_SHARED); |
2022 | 0 | *nextMulti = MultiXactState->nextMXact; |
2023 | 0 | *nextMultiOffset = MultiXactState->nextOffset; |
2024 | 0 | *oldestMulti = MultiXactState->oldestMultiXactId; |
2025 | 0 | *oldestMultiDB = MultiXactState->oldestMultiXactDB; |
2026 | 0 | LWLockRelease(MultiXactGenLock); |
2027 | |
|
2028 | 0 | debug_elog6(DEBUG2, |
2029 | 0 | "MultiXact: checkpoint is nextMulti %u, nextOffset %" PRIu64 ", oldestMulti %u in DB %u", |
2030 | 0 | *nextMulti, *nextMultiOffset, *oldestMulti, *oldestMultiDB); |
2031 | 0 | } |
2032 | | |
2033 | | /* |
2034 | | * Perform a checkpoint --- either during shutdown, or on-the-fly |
2035 | | */ |
2036 | | void |
2037 | | CheckPointMultiXact(void) |
2038 | 0 | { |
2039 | 0 | TRACE_POSTGRESQL_MULTIXACT_CHECKPOINT_START(true); |
2040 | | |
2041 | | /* |
2042 | | * Write dirty MultiXact pages to disk. This may result in sync requests |
2043 | | * queued for later handling by ProcessSyncRequests(), as part of the |
2044 | | * checkpoint. |
2045 | | */ |
2046 | 0 | SimpleLruWriteAll(MultiXactOffsetCtl, true); |
2047 | 0 | SimpleLruWriteAll(MultiXactMemberCtl, true); |
2048 | |
|
2049 | 0 | TRACE_POSTGRESQL_MULTIXACT_CHECKPOINT_DONE(true); |
2050 | 0 | } |
2051 | | |
2052 | | /* |
2053 | | * Set the next-to-be-assigned MultiXactId and offset |
2054 | | * |
2055 | | * This is used when we can determine the correct next ID/offset exactly |
2056 | | * from a checkpoint record. Although this is only called during bootstrap |
2057 | | * and XLog replay, we take the lock in case any hot-standby backends are |
2058 | | * examining the values. |
2059 | | */ |
2060 | | void |
2061 | | MultiXactSetNextMXact(MultiXactId nextMulti, |
2062 | | MultiXactOffset nextMultiOffset) |
2063 | 0 | { |
2064 | 0 | Assert(MultiXactIdIsValid(nextMulti)); |
2065 | 0 | debug_elog4(DEBUG2, "MultiXact: setting next multi to %u offset %" PRIu64, |
2066 | 0 | nextMulti, nextMultiOffset); |
2067 | |
|
2068 | 0 | LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE); |
2069 | 0 | MultiXactState->nextMXact = nextMulti; |
2070 | 0 | MultiXactState->nextOffset = nextMultiOffset; |
2071 | 0 | LWLockRelease(MultiXactGenLock); |
2072 | 0 | } |
2073 | | |
2074 | | /* |
2075 | | * Determine the last safe MultiXactId to allocate given the currently oldest |
2076 | | * datminmxid (ie, the oldest MultiXactId that might exist in any database |
2077 | | * of our cluster), and the OID of the (or a) database with that value. |
2078 | | * |
2079 | | * This also updates MultiXactState->oldestOffset, by looking up the offset of |
2080 | | * MultiXactState->oldestMultiXactId. |
2081 | | */ |
2082 | | void |
2083 | | SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid) |
2084 | | { |
2085 | | MultiXactId multiVacLimit; |
2086 | | MultiXactId multiWarnLimit; |
2087 | | MultiXactId multiStopLimit; |
2088 | | MultiXactId multiWrapLimit; |
2089 | | MultiXactId curMulti; |
2090 | | |
2091 | | Assert(MultiXactIdIsValid(oldest_datminmxid)); |
2092 | | |
2093 | | /* |
2094 | | * We pretend that a wrap will happen halfway through the multixact ID |
2095 | | * space, but that's not really true, because multixacts wrap differently |
2096 | | * from transaction IDs. |
2097 | | */ |
2098 | | multiWrapLimit = oldest_datminmxid + (MaxMultiXactId >> 1); |
2099 | | if (multiWrapLimit < FirstMultiXactId) |
2100 | | multiWrapLimit += FirstMultiXactId; |
2101 | | |
2102 | | /* |
2103 | | * We'll refuse to continue assigning MultiXactIds once we get within 3M |
2104 | | * multi of data loss. See SetTransactionIdLimit. |
2105 | | */ |
2106 | | multiStopLimit = multiWrapLimit - 3000000; |
2107 | | if (multiStopLimit < FirstMultiXactId) |
2108 | | multiStopLimit -= FirstMultiXactId; |
2109 | | |
2110 | | /* |
2111 | | * We'll start complaining loudly when we get within 100M multis of data |
2112 | | * loss. This is kind of arbitrary, but if you let your gas gauge get |
2113 | | * down to 5% of full, would you be looking for the next gas station? We |
2114 | | * need to be fairly liberal about this number because there are lots of |
2115 | | * scenarios where most transactions are done by automatic clients that |
2116 | | * won't pay attention to warnings. (No, we're not gonna make this |
2117 | | * configurable. If you know enough to configure it, you know enough to |
2118 | | * not get in this kind of trouble in the first place.) |
2119 | | */ |
2120 | | multiWarnLimit = multiWrapLimit - 100000000; |
2121 | | if (multiWarnLimit < FirstMultiXactId) |
2122 | | multiWarnLimit -= FirstMultiXactId; |
2123 | | |
2124 | | /* |
2125 | | * We'll start trying to force autovacuums when oldest_datminmxid gets to |
2126 | | * be more than autovacuum_multixact_freeze_max_age mxids old. |
2127 | | * |
2128 | | * Note: autovacuum_multixact_freeze_max_age is a PGC_POSTMASTER parameter |
2129 | | * so that we don't have to worry about dealing with on-the-fly changes in |
2130 | | * its value. See SetTransactionIdLimit. |
2131 | | */ |
2132 | | multiVacLimit = oldest_datminmxid + autovacuum_multixact_freeze_max_age; |
2133 | | if (multiVacLimit < FirstMultiXactId) |
2134 | | multiVacLimit += FirstMultiXactId; |
2135 | | |
2136 | | /* Grab lock for just long enough to set the new limit values */ |
2137 | | LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE); |
2138 | | MultiXactState->oldestMultiXactId = oldest_datminmxid; |
2139 | | MultiXactState->oldestMultiXactDB = oldest_datoid; |
2140 | | MultiXactState->multiVacLimit = multiVacLimit; |
2141 | | MultiXactState->multiWarnLimit = multiWarnLimit; |
2142 | | MultiXactState->multiStopLimit = multiStopLimit; |
2143 | | MultiXactState->multiWrapLimit = multiWrapLimit; |
2144 | | curMulti = MultiXactState->nextMXact; |
2145 | | LWLockRelease(MultiXactGenLock); |
2146 | | |
2147 | | /* Log the info */ |
2148 | | ereport(DEBUG1, |
2149 | | (errmsg_internal("MultiXactId wrap limit is %u, limited by database with OID %u", |
2150 | | multiWrapLimit, oldest_datoid))); |
2151 | | |
2152 | | /* |
2153 | | * Computing the actual limits is only possible once the data directory is |
2154 | | * in a consistent state. There's no need to compute the limits while |
2155 | | * still replaying WAL - no decisions about new multis are made even |
2156 | | * though multixact creations might be replayed. So we'll only do further |
2157 | | * checks after TrimMultiXact() has been called. |
2158 | | */ |
2159 | | if (!MultiXactState->finishedStartup) |
2160 | | return; |
2161 | | |
2162 | | Assert(!InRecovery); |
2163 | | |
2164 | | /* |
2165 | | * Offsets are 64-bits wide and never wrap around, so we don't need to |
2166 | | * consider them for emergency autovacuum purposes. But now that we're in |
2167 | | * a consistent state, determine MultiXactState->oldestOffset. It will be |
2168 | | * used to adjust the freezing cutoff, to keep the members disk usage in |
2169 | | * check. |
2170 | | */ |
2171 | | SetOldestOffset(); |
2172 | | |
2173 | | /* |
2174 | | * If past the autovacuum force point, immediately signal an autovac |
2175 | | * request. The reason for this is that autovac only processes one |
2176 | | * database per invocation. Once it's finished cleaning up the oldest |
2177 | | * database, it'll call here, and we'll signal the postmaster to start |
2178 | | * another iteration immediately if there are still any old databases. |
2179 | | */ |
2180 | | if (MultiXactIdPrecedes(multiVacLimit, curMulti) && IsUnderPostmaster) |
2181 | | SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER); |
2182 | | |
2183 | | /* Give an immediate warning if past the wrap warn point */ |
2184 | | if (MultiXactIdPrecedes(multiWarnLimit, curMulti)) |
2185 | | { |
2186 | | char *oldest_datname; |
2187 | | |
2188 | | /* |
2189 | | * We can be called when not inside a transaction, for example during |
2190 | | * StartupXLOG(). In such a case we cannot do database access, so we |
2191 | | * must just report the oldest DB's OID. |
2192 | | * |
2193 | | * Note: it's also possible that get_database_name fails and returns |
2194 | | * NULL, for example because the database just got dropped. We'll |
2195 | | * still warn, even though the warning might now be unnecessary. |
2196 | | */ |
2197 | | if (IsTransactionState()) |
2198 | | oldest_datname = get_database_name(oldest_datoid); |
2199 | | else |
2200 | | oldest_datname = NULL; |
2201 | | |
2202 | | if (oldest_datname) |
2203 | | ereport(WARNING, |
2204 | | (errmsg_plural("database \"%s\" must be vacuumed before %u more MultiXactId is used", |
2205 | | "database \"%s\" must be vacuumed before %u more MultiXactIds are used", |
2206 | | multiWrapLimit - curMulti, |
2207 | | oldest_datname, |
2208 | | multiWrapLimit - curMulti), |
2209 | | errdetail("Approximately %.2f%% of MultiXactId space remains before wraparound.", |
2210 | | (double) (multiWrapLimit - curMulti) / (MaxMultiXactId / 2) * 100), |
2211 | | errhint("To avoid MultiXactId assignment failures, execute a database-wide VACUUM in that database.\n" |
2212 | | "You might also need to commit or roll back old prepared transactions."))); |
2213 | | else |
2214 | | ereport(WARNING, |
2215 | | (errmsg_plural("database with OID %u must be vacuumed before %u more MultiXactId is used", |
2216 | | "database with OID %u must be vacuumed before %u more MultiXactIds are used", |
2217 | | multiWrapLimit - curMulti, |
2218 | | oldest_datoid, |
2219 | | multiWrapLimit - curMulti), |
2220 | | errdetail("Approximately %.2f%% of MultiXactId space remains before wraparound.", |
2221 | | (double) (multiWrapLimit - curMulti) / (MaxMultiXactId / 2) * 100), |
2222 | | errhint("To avoid MultiXactId assignment failures, execute a database-wide VACUUM in that database.\n" |
2223 | | "You might also need to commit or roll back old prepared transactions."))); |
2224 | | } |
2225 | | } |
2226 | | |
2227 | | /* |
2228 | | * Ensure the next-to-be-assigned MultiXactId is at least minMulti, |
2229 | | * and similarly nextOffset is at least minMultiOffset. |
2230 | | * |
2231 | | * This is used when we can determine minimum safe values from an XLog |
2232 | | * record (either an on-line checkpoint or an mxact creation log entry). |
2233 | | * Although this is only called during XLog replay, we take the lock in case |
2234 | | * any hot-standby backends are examining the values. |
2235 | | */ |
2236 | | void |
2237 | | MultiXactAdvanceNextMXact(MultiXactId minMulti, |
2238 | | MultiXactOffset minMultiOffset) |
2239 | 0 | { |
2240 | 0 | Assert(MultiXactIdIsValid(minMulti)); |
2241 | |
|
2242 | 0 | LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE); |
2243 | 0 | if (MultiXactIdPrecedes(MultiXactState->nextMXact, minMulti)) |
2244 | 0 | { |
2245 | 0 | debug_elog3(DEBUG2, "MultiXact: setting next multi to %u", minMulti); |
2246 | 0 | MultiXactState->nextMXact = minMulti; |
2247 | 0 | } |
2248 | 0 | if (MultiXactState->nextOffset < minMultiOffset) |
2249 | 0 | { |
2250 | 0 | debug_elog3(DEBUG2, "MultiXact: setting next offset to %" PRIu64, |
2251 | 0 | minMultiOffset); |
2252 | 0 | MultiXactState->nextOffset = minMultiOffset; |
2253 | 0 | } |
2254 | 0 | LWLockRelease(MultiXactGenLock); |
2255 | 0 | } |
2256 | | |
2257 | | /* |
2258 | | * Update our oldestMultiXactId value, but only if it's more recent than what |
2259 | | * we had. |
2260 | | * |
2261 | | * This may only be called during WAL replay. |
2262 | | */ |
2263 | | void |
2264 | | MultiXactAdvanceOldest(MultiXactId oldestMulti, Oid oldestMultiDB) |
2265 | 0 | { |
2266 | 0 | Assert(InRecovery); |
2267 | |
|
2268 | 0 | if (MultiXactIdPrecedes(MultiXactState->oldestMultiXactId, oldestMulti)) |
2269 | 0 | SetMultiXactIdLimit(oldestMulti, oldestMultiDB); |
2270 | 0 | } |
2271 | | |
2272 | | /* |
2273 | | * Make sure that MultiXactOffset has room for a newly-allocated MultiXactId. |
2274 | | * |
2275 | | * NB: this is called while holding MultiXactGenLock. We want it to be very |
2276 | | * fast most of the time; even when it's not so fast, no actual I/O need |
2277 | | * happen unless we're forced to write out a dirty log or xlog page to make |
2278 | | * room in shared memory. |
2279 | | */ |
2280 | | static void |
2281 | | ExtendMultiXactOffset(MultiXactId multi) |
2282 | 0 | { |
2283 | 0 | int64 pageno; |
2284 | 0 | LWLock *lock; |
2285 | | |
2286 | | /* |
2287 | | * No work except at first MultiXactId of a page. But beware: just after |
2288 | | * wraparound, the first MultiXactId of page zero is FirstMultiXactId. |
2289 | | */ |
2290 | 0 | if (MultiXactIdToOffsetEntry(multi) != 0 && |
2291 | 0 | multi != FirstMultiXactId) |
2292 | 0 | return; |
2293 | | |
2294 | 0 | pageno = MultiXactIdToOffsetPage(multi); |
2295 | 0 | lock = SimpleLruGetBankLock(MultiXactOffsetCtl, pageno); |
2296 | |
|
2297 | 0 | LWLockAcquire(lock, LW_EXCLUSIVE); |
2298 | | |
2299 | | /* Zero the page and make a WAL entry about it */ |
2300 | 0 | SimpleLruZeroPage(MultiXactOffsetCtl, pageno); |
2301 | 0 | XLogSimpleInsertInt64(RM_MULTIXACT_ID, XLOG_MULTIXACT_ZERO_OFF_PAGE, |
2302 | 0 | pageno); |
2303 | |
|
2304 | 0 | LWLockRelease(lock); |
2305 | 0 | } |
2306 | | |
2307 | | /* |
2308 | | * Make sure that MultiXactMember has room for the members of a newly- |
2309 | | * allocated MultiXactId. |
2310 | | * |
2311 | | * Like the above routine, this is called while holding MultiXactGenLock; |
2312 | | * same comments apply. |
2313 | | */ |
2314 | | static void |
2315 | | ExtendMultiXactMember(MultiXactOffset offset, int nmembers) |
2316 | 0 | { |
2317 | | /* |
2318 | | * It's possible that the members span more than one page of the members |
2319 | | * file, so we loop to ensure we consider each page. The coding is not |
2320 | | * optimal if the members span several pages, but that seems unusual |
2321 | | * enough to not worry much about. |
2322 | | */ |
2323 | 0 | while (nmembers > 0) |
2324 | 0 | { |
2325 | 0 | int flagsoff; |
2326 | 0 | int flagsbit; |
2327 | 0 | uint32 difference; |
2328 | | |
2329 | | /* |
2330 | | * Only zero when at first entry of a page. |
2331 | | */ |
2332 | 0 | flagsoff = MXOffsetToFlagsOffset(offset); |
2333 | 0 | flagsbit = MXOffsetToFlagsBitShift(offset); |
2334 | 0 | if (flagsoff == 0 && flagsbit == 0) |
2335 | 0 | { |
2336 | 0 | int64 pageno; |
2337 | 0 | LWLock *lock; |
2338 | |
|
2339 | 0 | pageno = MXOffsetToMemberPage(offset); |
2340 | 0 | lock = SimpleLruGetBankLock(MultiXactMemberCtl, pageno); |
2341 | |
|
2342 | 0 | LWLockAcquire(lock, LW_EXCLUSIVE); |
2343 | | |
2344 | | /* Zero the page and make a WAL entry about it */ |
2345 | 0 | SimpleLruZeroPage(MultiXactMemberCtl, pageno); |
2346 | 0 | XLogSimpleInsertInt64(RM_MULTIXACT_ID, |
2347 | 0 | XLOG_MULTIXACT_ZERO_MEM_PAGE, pageno); |
2348 | |
|
2349 | 0 | LWLockRelease(lock); |
2350 | 0 | } |
2351 | | |
2352 | | /* Compute the number of items till end of current page. */ |
2353 | 0 | difference = MULTIXACT_MEMBERS_PER_PAGE - offset % MULTIXACT_MEMBERS_PER_PAGE; |
2354 | | |
2355 | | /* |
2356 | | * Advance to next page. OK if nmembers goes negative. |
2357 | | */ |
2358 | 0 | nmembers -= difference; |
2359 | 0 | offset += difference; |
2360 | 0 | } |
2361 | 0 | } |
2362 | | |
2363 | | /* |
2364 | | * GetOldestMultiXactId |
2365 | | * |
2366 | | * Return the oldest MultiXactId that's still possibly still seen as live by |
2367 | | * any running transaction. Older ones might still exist on disk, but they no |
2368 | | * longer have any running member transaction. |
2369 | | * |
2370 | | * It's not safe to truncate MultiXact SLRU segments on the value returned by |
2371 | | * this function; however, it can be set as the new relminmxid for any table |
2372 | | * that VACUUM knows has no remaining MXIDs < the same value. It is only safe |
2373 | | * to truncate SLRUs when no table can possibly still have a referencing MXID. |
2374 | | */ |
2375 | | MultiXactId |
2376 | | GetOldestMultiXactId(void) |
2377 | 0 | { |
2378 | 0 | MultiXactId oldestMXact; |
2379 | | |
2380 | | /* |
2381 | | * This is the oldest valid value among all the OldestMemberMXactId[] and |
2382 | | * OldestVisibleMXactId[] entries, or nextMXact if none are valid. |
2383 | | */ |
2384 | 0 | LWLockAcquire(MultiXactGenLock, LW_SHARED); |
2385 | 0 | oldestMXact = MultiXactState->nextMXact; |
2386 | 0 | for (int i = 0; i < NumMemberSlots; i++) |
2387 | 0 | { |
2388 | 0 | MultiXactId thisoldest; |
2389 | |
|
2390 | 0 | thisoldest = OldestMemberMXactId[i]; |
2391 | 0 | if (MultiXactIdIsValid(thisoldest) && |
2392 | 0 | MultiXactIdPrecedes(thisoldest, oldestMXact)) |
2393 | 0 | oldestMXact = thisoldest; |
2394 | 0 | } |
2395 | 0 | for (int i = 0; i < NumVisibleSlots; i++) |
2396 | 0 | { |
2397 | 0 | MultiXactId thisoldest; |
2398 | |
|
2399 | 0 | thisoldest = OldestVisibleMXactId[i]; |
2400 | 0 | if (MultiXactIdIsValid(thisoldest) && |
2401 | 0 | MultiXactIdPrecedes(thisoldest, oldestMXact)) |
2402 | 0 | oldestMXact = thisoldest; |
2403 | 0 | } |
2404 | |
|
2405 | 0 | LWLockRelease(MultiXactGenLock); |
2406 | |
|
2407 | 0 | return oldestMXact; |
2408 | 0 | } |
2409 | | |
2410 | | /* |
2411 | | * Calculate the oldest member offset and install it in MultiXactState, where |
2412 | | * it can be used to adjust multixid freezing cutoffs. |
2413 | | */ |
2414 | | static void |
2415 | | SetOldestOffset(void) |
2416 | 0 | { |
2417 | 0 | MultiXactId oldestMultiXactId; |
2418 | 0 | MultiXactId nextMXact; |
2419 | 0 | MultiXactOffset oldestOffset = 0; /* placate compiler */ |
2420 | 0 | MultiXactOffset nextOffset; |
2421 | 0 | bool oldestOffsetKnown = false; |
2422 | | |
2423 | | /* |
2424 | | * NB: Have to prevent concurrent truncation, we might otherwise try to |
2425 | | * lookup an oldestMulti that's concurrently getting truncated away. |
2426 | | */ |
2427 | 0 | LWLockAcquire(MultiXactTruncationLock, LW_SHARED); |
2428 | | |
2429 | | /* Read relevant fields from shared memory. */ |
2430 | 0 | LWLockAcquire(MultiXactGenLock, LW_SHARED); |
2431 | 0 | oldestMultiXactId = MultiXactState->oldestMultiXactId; |
2432 | 0 | nextMXact = MultiXactState->nextMXact; |
2433 | 0 | nextOffset = MultiXactState->nextOffset; |
2434 | 0 | Assert(MultiXactState->finishedStartup); |
2435 | 0 | LWLockRelease(MultiXactGenLock); |
2436 | | |
2437 | | /* |
2438 | | * Determine the offset of the oldest multixact. Normally, we can read |
2439 | | * the offset from the multixact itself, but there's an important special |
2440 | | * case: if there are no multixacts in existence at all, oldestMXact |
2441 | | * obviously can't point to one. It will instead point to the multixact |
2442 | | * ID that will be assigned the next time one is needed. |
2443 | | */ |
2444 | 0 | if (oldestMultiXactId == nextMXact) |
2445 | 0 | { |
2446 | | /* |
2447 | | * When the next multixact gets created, it will be stored at the next |
2448 | | * offset. |
2449 | | */ |
2450 | 0 | oldestOffset = nextOffset; |
2451 | 0 | oldestOffsetKnown = true; |
2452 | 0 | } |
2453 | 0 | else |
2454 | 0 | { |
2455 | | /* |
2456 | | * Look up the offset at which the oldest existing multixact's members |
2457 | | * are stored. If we cannot find it, be careful not to fail, and |
2458 | | * leave oldestOffset unchanged. oldestOffset is initialized to zero |
2459 | | * at system startup, which prevents truncating members until a proper |
2460 | | * value is calculated. |
2461 | | * |
2462 | | * (We had bugs in early releases of PostgreSQL 9.3.X and 9.4.X where |
2463 | | * the supposedly-earliest multixact might not really exist. Those |
2464 | | * should be long gone by now, so this should not fail, but let's |
2465 | | * still be defensive.) |
2466 | | */ |
2467 | 0 | oldestOffsetKnown = |
2468 | 0 | find_multixact_start(oldestMultiXactId, &oldestOffset); |
2469 | |
|
2470 | 0 | if (oldestOffsetKnown) |
2471 | 0 | ereport(DEBUG1, |
2472 | 0 | (errmsg_internal("oldest MultiXactId member is at offset %" PRIu64, |
2473 | 0 | oldestOffset))); |
2474 | 0 | else |
2475 | 0 | ereport(LOG, |
2476 | 0 | (errmsg("MultiXact member truncation is disabled because oldest checkpointed MultiXact %u does not exist on disk", |
2477 | 0 | oldestMultiXactId))); |
2478 | 0 | } |
2479 | | |
2480 | 0 | LWLockRelease(MultiXactTruncationLock); |
2481 | | |
2482 | | /* Install the computed value */ |
2483 | 0 | if (oldestOffsetKnown) |
2484 | 0 | { |
2485 | 0 | LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE); |
2486 | 0 | MultiXactState->oldestOffset = oldestOffset; |
2487 | 0 | LWLockRelease(MultiXactGenLock); |
2488 | 0 | } |
2489 | 0 | } |
2490 | | |
2491 | | /* |
2492 | | * Find the starting offset of the given MultiXactId. |
2493 | | * |
2494 | | * Returns false if the file containing the multi does not exist on disk. |
2495 | | * Otherwise, returns true and sets *result to the starting member offset. |
2496 | | * |
2497 | | * This function does not prevent concurrent truncation, so if that's |
2498 | | * required, the caller has to protect against that. |
2499 | | */ |
2500 | | static bool |
2501 | | find_multixact_start(MultiXactId multi, MultiXactOffset *result) |
2502 | 0 | { |
2503 | 0 | MultiXactOffset offset; |
2504 | 0 | int64 pageno; |
2505 | 0 | int entryno; |
2506 | 0 | int slotno; |
2507 | 0 | MultiXactOffset *offptr; |
2508 | |
|
2509 | 0 | Assert(MultiXactState->finishedStartup); |
2510 | |
|
2511 | 0 | pageno = MultiXactIdToOffsetPage(multi); |
2512 | 0 | entryno = MultiXactIdToOffsetEntry(multi); |
2513 | | |
2514 | | /* |
2515 | | * Write out dirty data, so PhysicalPageExists can work correctly. |
2516 | | */ |
2517 | 0 | SimpleLruWriteAll(MultiXactOffsetCtl, true); |
2518 | 0 | SimpleLruWriteAll(MultiXactMemberCtl, true); |
2519 | |
|
2520 | 0 | if (!SimpleLruDoesPhysicalPageExist(MultiXactOffsetCtl, pageno)) |
2521 | 0 | return false; |
2522 | | |
2523 | | /* lock is acquired by SimpleLruReadPage_ReadOnly */ |
2524 | 0 | slotno = SimpleLruReadPage_ReadOnly(MultiXactOffsetCtl, pageno, &multi); |
2525 | 0 | offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno]; |
2526 | 0 | offptr += entryno; |
2527 | 0 | offset = *offptr; |
2528 | 0 | LWLockRelease(SimpleLruGetBankLock(MultiXactOffsetCtl, pageno)); |
2529 | |
|
2530 | 0 | *result = offset; |
2531 | 0 | return true; |
2532 | 0 | } |
2533 | | |
2534 | | /* |
2535 | | * GetMultiXactInfo |
2536 | | * |
2537 | | * Returns information about the current MultiXact state, as of: |
2538 | | * multixacts: Number of MultiXacts (nextMultiXactId - oldestMultiXactId) |
2539 | | * nextOffset: Next-to-be-assigned offset |
2540 | | * oldestMultiXactId: Oldest MultiXact ID still in use |
2541 | | * oldestOffset: Oldest offset still in use |
2542 | | */ |
2543 | | void |
2544 | | GetMultiXactInfo(uint32 *multixacts, MultiXactOffset *nextOffset, |
2545 | | MultiXactId *oldestMultiXactId, MultiXactOffset *oldestOffset) |
2546 | 0 | { |
2547 | 0 | MultiXactId nextMultiXactId; |
2548 | |
|
2549 | 0 | LWLockAcquire(MultiXactGenLock, LW_SHARED); |
2550 | 0 | *nextOffset = MultiXactState->nextOffset; |
2551 | 0 | *oldestMultiXactId = MultiXactState->oldestMultiXactId; |
2552 | 0 | nextMultiXactId = MultiXactState->nextMXact; |
2553 | 0 | *oldestOffset = MultiXactState->oldestOffset; |
2554 | 0 | LWLockRelease(MultiXactGenLock); |
2555 | |
|
2556 | 0 | *multixacts = nextMultiXactId - *oldestMultiXactId; |
2557 | 0 | } |
2558 | | |
2559 | | /* |
2560 | | * Multixact members can be removed once the multixacts that refer to them |
2561 | | * are older than every datminmxid. autovacuum_multixact_freeze_max_age and |
2562 | | * vacuum_multixact_freeze_table_age work together to make sure we never have |
2563 | | * too many multixacts; we hope that, at least under normal circumstances, |
2564 | | * this will also be sufficient to keep us from using too many offsets. |
2565 | | * However, if the average multixact has many members, we might accumulate a |
2566 | | * large amount of members, consuming disk space, while still using few enough |
2567 | | * multixids that the multixid limits fail to trigger relminmxid advancement |
2568 | | * by VACUUM. |
2569 | | * |
2570 | | * To prevent that, if the members space usage exceeds a threshold |
2571 | | * (MULTIXACT_MEMBER_LOW_THRESHOLD), we effectively reduce |
2572 | | * autovacuum_multixact_freeze_max_age to a value just less than the number of |
2573 | | * multixacts in use. We hope that this will quickly trigger autovacuuming on |
2574 | | * the table or tables with the oldest relminmxid, thus allowing datminmxid |
2575 | | * values to advance and removing some members. |
2576 | | * |
2577 | | * As the amount of the member space in use grows, we become more aggressive |
2578 | | * in clamping this value. That not only causes autovacuum to ramp up, but |
2579 | | * also makes any manual vacuums the user issues more aggressive. This |
2580 | | * happens because vacuum_get_cutoffs() will clamp the freeze table and the |
2581 | | * minimum freeze age cutoffs based on the effective |
2582 | | * autovacuum_multixact_freeze_max_age this function returns. At the extreme, |
2583 | | * when the members usage reaches MULTIXACT_MEMBER_HIGH_THRESHOLD, we clamp |
2584 | | * freeze_max_age to zero, and every vacuum of any table will freeze every |
2585 | | * multixact. |
2586 | | */ |
2587 | | int |
2588 | | MultiXactMemberFreezeThreshold(void) |
2589 | 0 | { |
2590 | 0 | uint32 multixacts; |
2591 | 0 | uint32 victim_multixacts; |
2592 | 0 | double fraction; |
2593 | 0 | int result; |
2594 | 0 | MultiXactId oldestMultiXactId; |
2595 | 0 | MultiXactOffset oldestOffset; |
2596 | 0 | MultiXactOffset nextOffset; |
2597 | 0 | uint64 members; |
2598 | | |
2599 | | /* Read the current offsets and multixact usage. */ |
2600 | 0 | GetMultiXactInfo(&multixacts, &nextOffset, &oldestMultiXactId, &oldestOffset); |
2601 | 0 | members = nextOffset - oldestOffset; |
2602 | | |
2603 | | /* If member space utilization is low, no special action is required. */ |
2604 | 0 | if (members <= MULTIXACT_MEMBER_LOW_THRESHOLD) |
2605 | 0 | return autovacuum_multixact_freeze_max_age; |
2606 | | |
2607 | | /* |
2608 | | * Compute a target for relminmxid advancement. The number of multixacts |
2609 | | * we try to eliminate from the system is based on how far we are past |
2610 | | * MULTIXACT_MEMBER_LOW_THRESHOLD. |
2611 | | * |
2612 | | * The way this formula works is that when members is exactly at the low |
2613 | | * threshold, fraction = 0.0, and we set freeze_max_age equal to |
2614 | | * mxid_age(oldestMultiXactId). As members grows further, towards the |
2615 | | * high threshold, fraction grows linearly from 0.0 to 1.0, and the result |
2616 | | * shrinks from mxid_age(oldestMultiXactId) to 0. Beyond the high |
2617 | | * threshold, fraction > 1.0 and the result is clamped to 0. |
2618 | | */ |
2619 | 0 | fraction = (double) (members - MULTIXACT_MEMBER_LOW_THRESHOLD) / |
2620 | 0 | (MULTIXACT_MEMBER_HIGH_THRESHOLD - MULTIXACT_MEMBER_LOW_THRESHOLD); |
2621 | | |
2622 | | /* fraction could be > 1.0, but lowest possible freeze age is zero */ |
2623 | 0 | if (fraction >= 1.0) |
2624 | 0 | return 0; |
2625 | | |
2626 | 0 | victim_multixacts = multixacts * fraction; |
2627 | 0 | result = multixacts - victim_multixacts; |
2628 | | |
2629 | | /* |
2630 | | * Clamp to autovacuum_multixact_freeze_max_age, so that we never make |
2631 | | * autovacuum less aggressive than it would otherwise be. |
2632 | | */ |
2633 | 0 | return Min(result, autovacuum_multixact_freeze_max_age); |
2634 | 0 | } |
2635 | | |
2636 | | |
2637 | | /* |
2638 | | * Delete members segments older than newOldestOffset |
2639 | | */ |
2640 | | static void |
2641 | | PerformMembersTruncation(MultiXactOffset newOldestOffset) |
2642 | 0 | { |
2643 | | /* |
2644 | | * We step back one entry to avoid passing a cutoff page that hasn't been |
2645 | | * created yet in the rare case that oldestOffset would be the first item |
2646 | | * on a page and oldestOffset == nextOffset. In that case, if we didn't |
2647 | | * subtract one, we'd trigger SimpleLruTruncate's wraparound detection. |
2648 | | */ |
2649 | 0 | if (newOldestOffset <= 1) |
2650 | 0 | return; |
2651 | 0 | SimpleLruTruncate(MultiXactMemberCtl, |
2652 | 0 | MXOffsetToMemberPage(newOldestOffset - 1)); |
2653 | 0 | } |
2654 | | |
2655 | | /* |
2656 | | * Delete offsets segments older than newOldestMulti |
2657 | | */ |
2658 | | static void |
2659 | | PerformOffsetsTruncation(MultiXactId newOldestMulti) |
2660 | 0 | { |
2661 | | /* |
2662 | | * Like in PerformMembersTruncation(), step back one entry to avoid |
2663 | | * passing a cutoff page that hasn't been created yet. |
2664 | | */ |
2665 | 0 | SimpleLruTruncate(MultiXactOffsetCtl, |
2666 | 0 | MultiXactIdToOffsetPage(PreviousMultiXactId(newOldestMulti))); |
2667 | 0 | } |
2668 | | |
2669 | | /* |
2670 | | * Remove all MultiXactOffset and MultiXactMember segments before the oldest |
2671 | | * ones still of interest. |
2672 | | * |
2673 | | * This is only called on a primary as part of vacuum (via |
2674 | | * vac_truncate_clog()). During recovery truncation is done by replaying |
2675 | | * truncation WAL records logged here. |
2676 | | * |
2677 | | * newOldestMulti is the oldest currently required multixact, newOldestMultiDB |
2678 | | * is one of the databases preventing newOldestMulti from increasing. |
2679 | | */ |
2680 | | void |
2681 | | TruncateMultiXact(MultiXactId newOldestMulti, Oid newOldestMultiDB) |
2682 | 0 | { |
2683 | 0 | MultiXactId oldestMulti; |
2684 | 0 | MultiXactId nextMulti; |
2685 | 0 | MultiXactOffset newOldestOffset; |
2686 | 0 | MultiXactOffset nextOffset; |
2687 | |
|
2688 | 0 | Assert(!RecoveryInProgress()); |
2689 | 0 | Assert(MultiXactState->finishedStartup); |
2690 | 0 | Assert(MultiXactIdIsValid(newOldestMulti)); |
2691 | | |
2692 | | /* |
2693 | | * We can only allow one truncation to happen at once. Otherwise parts of |
2694 | | * members might vanish while we're doing lookups or similar. There's no |
2695 | | * need to have an interlock with creating new multis or such, since those |
2696 | | * are constrained by the limits (which only grow, never shrink). |
2697 | | */ |
2698 | 0 | LWLockAcquire(MultiXactTruncationLock, LW_EXCLUSIVE); |
2699 | |
|
2700 | 0 | LWLockAcquire(MultiXactGenLock, LW_SHARED); |
2701 | 0 | nextMulti = MultiXactState->nextMXact; |
2702 | 0 | nextOffset = MultiXactState->nextOffset; |
2703 | 0 | oldestMulti = MultiXactState->oldestMultiXactId; |
2704 | 0 | LWLockRelease(MultiXactGenLock); |
2705 | | |
2706 | | /* |
2707 | | * Make sure to only attempt truncation if there's values to truncate |
2708 | | * away. In normal processing values shouldn't go backwards, but there's |
2709 | | * some corner cases (due to bugs) where that's possible. |
2710 | | */ |
2711 | 0 | if (MultiXactIdPrecedesOrEquals(newOldestMulti, oldestMulti)) |
2712 | 0 | { |
2713 | 0 | LWLockRelease(MultiXactTruncationLock); |
2714 | 0 | return; |
2715 | 0 | } |
2716 | | |
2717 | | /* |
2718 | | * Compute up to where to truncate MultiXactMember. Lookup the |
2719 | | * corresponding member offset for newOldestMulti for that. |
2720 | | */ |
2721 | 0 | if (newOldestMulti == nextMulti) |
2722 | 0 | { |
2723 | | /* there are NO MultiXacts */ |
2724 | 0 | newOldestOffset = nextOffset; |
2725 | 0 | } |
2726 | 0 | else if (!find_multixact_start(newOldestMulti, &newOldestOffset)) |
2727 | 0 | { |
2728 | 0 | ereport(LOG, |
2729 | 0 | (errmsg("cannot truncate up to MultiXact %u because it does not exist on disk, skipping truncation", |
2730 | 0 | newOldestMulti))); |
2731 | 0 | LWLockRelease(MultiXactTruncationLock); |
2732 | 0 | return; |
2733 | 0 | } |
2734 | | |
2735 | | /* |
2736 | | * On crash, MultiXactIdCreateFromMembers() can leave behind multixids |
2737 | | * that were not yet written out and hence have zero offset on disk. If |
2738 | | * such a multixid becomes oldestMulti, we won't be able to look up its |
2739 | | * offset. That should be rare, so we don't try to do anything smart about |
2740 | | * it. Just skip the truncation, and hope that by the next truncation |
2741 | | * attempt, oldestMulti has advanced to a valid multixid. |
2742 | | */ |
2743 | 0 | if (newOldestOffset == 0) |
2744 | 0 | { |
2745 | 0 | ereport(LOG, |
2746 | 0 | (errmsg("cannot truncate up to MultiXact %u because it has invalid offset, skipping truncation", |
2747 | 0 | newOldestMulti))); |
2748 | 0 | LWLockRelease(MultiXactTruncationLock); |
2749 | 0 | return; |
2750 | 0 | } |
2751 | | |
2752 | 0 | elog(DEBUG1, "performing multixact truncation: " |
2753 | 0 | "oldestMulti %u (offsets segment %" PRIx64 "), " |
2754 | 0 | "oldestOffset %" PRIu64 " (members segment %" PRIx64 ")", |
2755 | 0 | newOldestMulti, |
2756 | 0 | MultiXactIdToOffsetSegment(newOldestMulti), |
2757 | 0 | newOldestOffset, |
2758 | 0 | MXOffsetToMemberSegment(newOldestOffset)); |
2759 | | |
2760 | | /* |
2761 | | * Do truncation, and the WAL logging of the truncation, in a critical |
2762 | | * section. That way offsets/members cannot get out of sync anymore, i.e. |
2763 | | * once consistent the newOldestMulti will always exist in members, even |
2764 | | * if we crashed in the wrong moment. |
2765 | | */ |
2766 | 0 | START_CRIT_SECTION(); |
2767 | | |
2768 | | /* |
2769 | | * Prevent checkpoints from being scheduled concurrently. This is critical |
2770 | | * because otherwise a truncation record might not be replayed after a |
2771 | | * crash/basebackup, even though the state of the data directory would |
2772 | | * require it. |
2773 | | */ |
2774 | 0 | Assert((MyProc->delayChkptFlags & DELAY_CHKPT_START) == 0); |
2775 | 0 | MyProc->delayChkptFlags |= DELAY_CHKPT_START; |
2776 | | |
2777 | | /* WAL log truncation */ |
2778 | 0 | WriteMTruncateXlogRec(newOldestMultiDB, newOldestMulti, newOldestOffset); |
2779 | | |
2780 | | /* |
2781 | | * Update in-memory limits before performing the truncation, while inside |
2782 | | * the critical section: Have to do it before truncation, to prevent |
2783 | | * concurrent lookups of those values. Has to be inside the critical |
2784 | | * section as otherwise a future call to this function would error out, |
2785 | | * while looking up the oldest member in offsets, if our caller crashes |
2786 | | * before updating the limits. |
2787 | | */ |
2788 | 0 | LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE); |
2789 | 0 | MultiXactState->oldestMultiXactId = newOldestMulti; |
2790 | 0 | MultiXactState->oldestMultiXactDB = newOldestMultiDB; |
2791 | 0 | MultiXactState->oldestOffset = newOldestOffset; |
2792 | 0 | LWLockRelease(MultiXactGenLock); |
2793 | | |
2794 | | /* First truncate members */ |
2795 | 0 | PerformMembersTruncation(newOldestOffset); |
2796 | | |
2797 | | /* Then offsets */ |
2798 | 0 | PerformOffsetsTruncation(newOldestMulti); |
2799 | |
|
2800 | 0 | MyProc->delayChkptFlags &= ~DELAY_CHKPT_START; |
2801 | |
|
2802 | 0 | END_CRIT_SECTION(); |
2803 | 0 | LWLockRelease(MultiXactTruncationLock); |
2804 | 0 | } |
2805 | | |
2806 | | /* |
2807 | | * Decide whether a MultiXactOffset page number is "older" for truncation |
2808 | | * purposes. Analogous to CLOGPagePrecedes(). |
2809 | | * |
2810 | | * Offsetting the values is optional, because MultiXactIdPrecedes() has |
2811 | | * translational symmetry. |
2812 | | */ |
2813 | | static bool |
2814 | | MultiXactOffsetPagePrecedes(int64 page1, int64 page2) |
2815 | 0 | { |
2816 | 0 | MultiXactId multi1; |
2817 | 0 | MultiXactId multi2; |
2818 | |
|
2819 | 0 | multi1 = ((MultiXactId) page1) * MULTIXACT_OFFSETS_PER_PAGE; |
2820 | 0 | multi1 += FirstMultiXactId + 1; |
2821 | 0 | multi2 = ((MultiXactId) page2) * MULTIXACT_OFFSETS_PER_PAGE; |
2822 | 0 | multi2 += FirstMultiXactId + 1; |
2823 | |
|
2824 | 0 | return (MultiXactIdPrecedes(multi1, multi2) && |
2825 | 0 | MultiXactIdPrecedes(multi1, |
2826 | 0 | multi2 + MULTIXACT_OFFSETS_PER_PAGE - 1)); |
2827 | 0 | } |
2828 | | |
2829 | | /* |
2830 | | * Decide whether a MultiXactMember page number is "older" for truncation |
2831 | | * purposes. There is no "invalid offset number" and members never wrap |
2832 | | * around, so use the numbers verbatim. |
2833 | | */ |
2834 | | static bool |
2835 | | MultiXactMemberPagePrecedes(int64 page1, int64 page2) |
2836 | 0 | { |
2837 | 0 | return page1 < page2; |
2838 | 0 | } |
2839 | | |
2840 | | static int |
2841 | | MultiXactOffsetIoErrorDetail(const void *opaque_data) |
2842 | 0 | { |
2843 | 0 | MultiXactId multixid = *(const MultiXactId *) opaque_data; |
2844 | |
|
2845 | 0 | return errdetail("Could not access offset of multixact %u.", multixid); |
2846 | 0 | } |
2847 | | |
2848 | | static int |
2849 | | MultiXactMemberIoErrorDetail(const void *opaque_data) |
2850 | 0 | { |
2851 | 0 | const MultiXactMemberSlruReadContext *context = opaque_data; |
2852 | |
|
2853 | 0 | if (MultiXactIdIsValid(context->multi)) |
2854 | 0 | return errdetail("Could not access member of multixact %u at offset %" PRIu64 ".", |
2855 | 0 | context->multi, context->offset); |
2856 | 0 | else |
2857 | 0 | return errdetail("Could not access multixact member at offset %" PRIu64 ".", |
2858 | 0 | context->offset); |
2859 | 0 | } |
2860 | | |
2861 | | |
2862 | | /* |
2863 | | * Write a TRUNCATE xlog record |
2864 | | * |
2865 | | * We must flush the xlog record to disk before returning --- see notes in |
2866 | | * TruncateCLOG(). |
2867 | | */ |
2868 | | static void |
2869 | | WriteMTruncateXlogRec(Oid oldestMultiDB, |
2870 | | MultiXactId oldestMulti, |
2871 | | MultiXactOffset oldestOffset) |
2872 | 0 | { |
2873 | 0 | XLogRecPtr recptr; |
2874 | 0 | xl_multixact_truncate xlrec; |
2875 | |
|
2876 | 0 | xlrec.oldestMultiDB = oldestMultiDB; |
2877 | 0 | xlrec.oldestMulti = oldestMulti; |
2878 | 0 | xlrec.oldestOffset = oldestOffset; |
2879 | |
|
2880 | 0 | XLogBeginInsert(); |
2881 | 0 | XLogRegisterData(&xlrec, SizeOfMultiXactTruncate); |
2882 | 0 | recptr = XLogInsert(RM_MULTIXACT_ID, XLOG_MULTIXACT_TRUNCATE_ID); |
2883 | 0 | XLogFlush(recptr); |
2884 | 0 | } |
2885 | | |
2886 | | /* |
2887 | | * MULTIXACT resource manager's routines |
2888 | | */ |
2889 | | void |
2890 | | multixact_redo(XLogReaderState *record) |
2891 | 0 | { |
2892 | 0 | uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK; |
2893 | | |
2894 | | /* Backup blocks are not used in multixact records */ |
2895 | 0 | Assert(!XLogRecHasAnyBlockRefs(record)); |
2896 | |
|
2897 | 0 | if (info == XLOG_MULTIXACT_ZERO_OFF_PAGE) |
2898 | 0 | { |
2899 | 0 | int64 pageno; |
2900 | |
|
2901 | 0 | memcpy(&pageno, XLogRecGetData(record), sizeof(pageno)); |
2902 | 0 | SimpleLruZeroAndWritePage(MultiXactOffsetCtl, pageno); |
2903 | 0 | } |
2904 | 0 | else if (info == XLOG_MULTIXACT_ZERO_MEM_PAGE) |
2905 | 0 | { |
2906 | 0 | int64 pageno; |
2907 | |
|
2908 | 0 | memcpy(&pageno, XLogRecGetData(record), sizeof(pageno)); |
2909 | 0 | SimpleLruZeroAndWritePage(MultiXactMemberCtl, pageno); |
2910 | 0 | } |
2911 | 0 | else if (info == XLOG_MULTIXACT_CREATE_ID) |
2912 | 0 | { |
2913 | 0 | xl_multixact_create *xlrec = |
2914 | 0 | (xl_multixact_create *) XLogRecGetData(record); |
2915 | 0 | TransactionId max_xid; |
2916 | 0 | int i; |
2917 | | |
2918 | | /* Store the data back into the SLRU files */ |
2919 | 0 | RecordNewMultiXact(xlrec->mid, xlrec->moff, xlrec->nmembers, |
2920 | 0 | xlrec->members); |
2921 | | |
2922 | | /* Make sure nextMXact/nextOffset are beyond what this record has */ |
2923 | 0 | MultiXactAdvanceNextMXact(NextMultiXactId(xlrec->mid), |
2924 | 0 | xlrec->moff + xlrec->nmembers); |
2925 | | |
2926 | | /* |
2927 | | * Make sure nextXid is beyond any XID mentioned in the record. This |
2928 | | * should be unnecessary, since any XID found here ought to have other |
2929 | | * evidence in the XLOG, but let's be safe. |
2930 | | */ |
2931 | 0 | max_xid = XLogRecGetXid(record); |
2932 | 0 | for (i = 0; i < xlrec->nmembers; i++) |
2933 | 0 | { |
2934 | 0 | if (TransactionIdPrecedes(max_xid, xlrec->members[i].xid)) |
2935 | 0 | max_xid = xlrec->members[i].xid; |
2936 | 0 | } |
2937 | |
|
2938 | 0 | AdvanceNextFullTransactionIdPastXid(max_xid); |
2939 | 0 | } |
2940 | 0 | else if (info == XLOG_MULTIXACT_TRUNCATE_ID) |
2941 | 0 | { |
2942 | 0 | xl_multixact_truncate xlrec; |
2943 | |
|
2944 | 0 | memcpy(&xlrec, XLogRecGetData(record), |
2945 | 0 | SizeOfMultiXactTruncate); |
2946 | |
|
2947 | 0 | elog(DEBUG1, "replaying multixact truncation: " |
2948 | 0 | "oldestMulti %u (offsets segment %" PRIx64 "), " |
2949 | 0 | "oldestOffset %" PRIu64 " (members segment %" PRIx64 ")", |
2950 | 0 | xlrec.oldestMulti, |
2951 | 0 | MultiXactIdToOffsetSegment(xlrec.oldestMulti), |
2952 | 0 | xlrec.oldestOffset, |
2953 | 0 | MXOffsetToMemberSegment(xlrec.oldestOffset)); |
2954 | | |
2955 | | /* should not be required, but more than cheap enough */ |
2956 | 0 | LWLockAcquire(MultiXactTruncationLock, LW_EXCLUSIVE); |
2957 | | |
2958 | | /* |
2959 | | * Advance the horizon values, so they're current at the end of |
2960 | | * recovery. |
2961 | | */ |
2962 | 0 | SetMultiXactIdLimit(xlrec.oldestMulti, xlrec.oldestMultiDB); |
2963 | |
|
2964 | 0 | PerformMembersTruncation(xlrec.oldestOffset); |
2965 | 0 | PerformOffsetsTruncation(xlrec.oldestMulti); |
2966 | |
|
2967 | 0 | LWLockRelease(MultiXactTruncationLock); |
2968 | 0 | } |
2969 | 0 | else |
2970 | 0 | elog(PANIC, "multixact_redo: unknown op code %u", info); |
2971 | 0 | } |
2972 | | |
2973 | | /* |
2974 | | * Entrypoint for sync.c to sync offsets files. |
2975 | | */ |
2976 | | int |
2977 | | multixactoffsetssyncfiletag(const FileTag *ftag, char *path) |
2978 | 0 | { |
2979 | 0 | return SlruSyncFileTag(MultiXactOffsetCtl, ftag, path); |
2980 | 0 | } |
2981 | | |
2982 | | /* |
2983 | | * Entrypoint for sync.c to sync members files. |
2984 | | */ |
2985 | | int |
2986 | | multixactmemberssyncfiletag(const FileTag *ftag, char *path) |
2987 | 0 | { |
2988 | 0 | return SlruSyncFileTag(MultiXactMemberCtl, ftag, path); |
2989 | 0 | } |