Coverage Report

Created: 2026-10-16 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}