00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064 #include "postgres.h"
00065
00066 #include "access/multixact.h"
00067 #include "access/slru.h"
00068 #include "access/transam.h"
00069 #include "access/twophase.h"
00070 #include "access/twophase_rmgr.h"
00071 #include "access/xact.h"
00072 #include "catalog/pg_type.h"
00073 #include "commands/dbcommands.h"
00074 #include "funcapi.h"
00075 #include "miscadmin.h"
00076 #include "pg_trace.h"
00077 #include "storage/lmgr.h"
00078 #include "storage/pmsignal.h"
00079 #include "storage/procarray.h"
00080 #include "utils/builtins.h"
00081 #include "utils/memutils.h"
00082 #include "utils/snapmgr.h"
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099 #define MULTIXACT_OFFSETS_PER_PAGE (BLCKSZ / sizeof(MultiXactOffset))
00100
00101 #define MultiXactIdToOffsetPage(xid) \
00102 ((xid) / (MultiXactOffset) MULTIXACT_OFFSETS_PER_PAGE)
00103 #define MultiXactIdToOffsetEntry(xid) \
00104 ((xid) % (MultiXactOffset) MULTIXACT_OFFSETS_PER_PAGE)
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119 #define MXACT_MEMBER_BITS_PER_XACT 8
00120 #define MXACT_MEMBER_FLAGS_PER_BYTE 1
00121 #define MXACT_MEMBER_XACT_BITMASK ((1 << MXACT_MEMBER_BITS_PER_XACT) - 1)
00122
00123
00124 #define MULTIXACT_FLAGBYTES_PER_GROUP 4
00125 #define MULTIXACT_MEMBERS_PER_MEMBERGROUP \
00126 (MULTIXACT_FLAGBYTES_PER_GROUP * MXACT_MEMBER_FLAGS_PER_BYTE)
00127
00128 #define MULTIXACT_MEMBERGROUP_SIZE \
00129 (sizeof(TransactionId) * MULTIXACT_MEMBERS_PER_MEMBERGROUP + MULTIXACT_FLAGBYTES_PER_GROUP)
00130 #define MULTIXACT_MEMBERGROUPS_PER_PAGE (BLCKSZ / MULTIXACT_MEMBERGROUP_SIZE)
00131 #define MULTIXACT_MEMBERS_PER_PAGE \
00132 (MULTIXACT_MEMBERGROUPS_PER_PAGE * MULTIXACT_MEMBERS_PER_MEMBERGROUP)
00133
00134
00135 #define MXOffsetToMemberPage(xid) ((xid) / (TransactionId) MULTIXACT_MEMBERS_PER_PAGE)
00136
00137
00138 #define MXOffsetToFlagsOffset(xid) \
00139 ((((xid) / (TransactionId) MULTIXACT_MEMBERS_PER_MEMBERGROUP) % \
00140 (TransactionId) MULTIXACT_MEMBERGROUPS_PER_PAGE) * \
00141 (TransactionId) MULTIXACT_MEMBERGROUP_SIZE)
00142 #define MXOffsetToFlagsBitShift(xid) \
00143 (((xid) % (TransactionId) MULTIXACT_MEMBERS_PER_MEMBERGROUP) * \
00144 MXACT_MEMBER_BITS_PER_XACT)
00145
00146
00147 #define MXOffsetToMemberOffset(xid) \
00148 (MXOffsetToFlagsOffset(xid) + MULTIXACT_FLAGBYTES_PER_GROUP + \
00149 ((xid) % MULTIXACT_MEMBERS_PER_MEMBERGROUP) * sizeof(TransactionId))
00150
00151
00152
00153
00154
00155 static SlruCtlData MultiXactOffsetCtlData;
00156 static SlruCtlData MultiXactMemberCtlData;
00157
00158 #define MultiXactOffsetCtl (&MultiXactOffsetCtlData)
00159 #define MultiXactMemberCtl (&MultiXactMemberCtlData)
00160
00161
00162
00163
00164
00165
00166
00167
00168 typedef struct MultiXactStateData
00169 {
00170
00171 MultiXactId nextMXact;
00172
00173
00174 MultiXactOffset nextOffset;
00175
00176
00177 MultiXactId lastTruncationPoint;
00178
00179
00180
00181
00182
00183 MultiXactId oldestMultiXactId;
00184 Oid oldestMultiXactDB;
00185
00186
00187 MultiXactId multiVacLimit;
00188 MultiXactId multiWarnLimit;
00189 MultiXactId multiStopLimit;
00190 MultiXactId multiWrapLimit;
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229 MultiXactId perBackendXactIds[1];
00230 } MultiXactStateData;
00231
00232
00233
00234
00235
00236 #define MaxOldestSlot (MaxBackends + max_prepared_xacts)
00237
00238
00239 static MultiXactStateData *MultiXactState;
00240 static MultiXactId *OldestMemberMXactId;
00241 static MultiXactId *OldestVisibleMXactId;
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261 typedef struct mXactCacheEnt
00262 {
00263 struct mXactCacheEnt *next;
00264 MultiXactId multi;
00265 int nmembers;
00266 MultiXactMember members[FLEXIBLE_ARRAY_MEMBER];
00267 } mXactCacheEnt;
00268
00269 static mXactCacheEnt *MXactCache = NULL;
00270 static MemoryContext MXactContext = NULL;
00271
00272 #ifdef MULTIXACT_DEBUG
00273 #define debug_elog2(a,b) elog(a,b)
00274 #define debug_elog3(a,b,c) elog(a,b,c)
00275 #define debug_elog4(a,b,c,d) elog(a,b,c,d)
00276 #define debug_elog5(a,b,c,d,e) elog(a,b,c,d,e)
00277 #define debug_elog6(a,b,c,d,e,f) elog(a,b,c,d,e,f)
00278 #else
00279 #define debug_elog2(a,b)
00280 #define debug_elog3(a,b,c)
00281 #define debug_elog4(a,b,c,d)
00282 #define debug_elog5(a,b,c,d,e)
00283 #define debug_elog6(a,b,c,d,e,f)
00284 #endif
00285
00286
00287 static void MultiXactIdSetOldestVisible(void);
00288 static MultiXactId CreateMultiXactId(int nmembers, MultiXactMember *members);
00289 static void RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
00290 int nmembers, MultiXactMember *members);
00291 static MultiXactId GetNewMultiXactId(int nmembers, MultiXactOffset *offset);
00292
00293
00294 static int mxactMemberComparator(const void *arg1, const void *arg2);
00295 static MultiXactId mXactCacheGetBySet(int nmembers, MultiXactMember *members);
00296 static int mXactCacheGetById(MultiXactId multi, MultiXactMember **members);
00297 static void mXactCachePut(MultiXactId multi, int nmembers,
00298 MultiXactMember *members);
00299
00300 static char *mxstatus_to_string(MultiXactStatus status);
00301
00302
00303 static int ZeroMultiXactOffsetPage(int pageno, bool writeXlog);
00304 static int ZeroMultiXactMemberPage(int pageno, bool writeXlog);
00305 static bool MultiXactOffsetPagePrecedes(int page1, int page2);
00306 static bool MultiXactMemberPagePrecedes(int page1, int page2);
00307 static bool MultiXactOffsetPrecedes(MultiXactOffset offset1,
00308 MultiXactOffset offset2);
00309 static void ExtendMultiXactOffset(MultiXactId multi);
00310 static void ExtendMultiXactMember(MultiXactOffset offset, int nmembers);
00311 static void WriteMZeroPageXlogRec(int pageno, uint8 info);
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323 MultiXactId
00324 MultiXactIdCreate(TransactionId xid1, MultiXactStatus status1,
00325 TransactionId xid2, MultiXactStatus status2)
00326 {
00327 MultiXactId newMulti;
00328 MultiXactMember members[2];
00329
00330 AssertArg(TransactionIdIsValid(xid1));
00331 AssertArg(TransactionIdIsValid(xid2));
00332
00333 Assert(!TransactionIdEquals(xid1, xid2) || (status1 != status2));
00334
00335
00336
00337
00338
00339
00340
00341 members[0].xid = xid1;
00342 members[0].status = status1;
00343 members[1].xid = xid2;
00344 members[1].status = status2;
00345
00346 newMulti = CreateMultiXactId(2, members);
00347
00348 debug_elog3(DEBUG2, "Create: %s",
00349 mxid_to_string(newMulti, 2, members));
00350
00351 return newMulti;
00352 }
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373 MultiXactId
00374 MultiXactIdExpand(MultiXactId multi, TransactionId xid, MultiXactStatus status)
00375 {
00376 MultiXactId newMulti;
00377 MultiXactMember *members;
00378 MultiXactMember *newMembers;
00379 int nmembers;
00380 int i;
00381 int j;
00382
00383 AssertArg(MultiXactIdIsValid(multi));
00384 AssertArg(TransactionIdIsValid(xid));
00385
00386 debug_elog5(DEBUG2, "Expand: received multi %u, xid %u status %s",
00387 multi, xid, mxstatus_to_string(status));
00388
00389
00390
00391
00392
00393
00394 nmembers = GetMultiXactIdMembers(multi, &members, false);
00395
00396 if (nmembers < 0)
00397 {
00398 MultiXactMember member;
00399
00400
00401
00402
00403
00404
00405
00406
00407 member.xid = xid;
00408 member.status = status;
00409 newMulti = CreateMultiXactId(1, &member);
00410
00411 debug_elog4(DEBUG2, "Expand: %u has no members, create singleton %u",
00412 multi, newMulti);
00413 return newMulti;
00414 }
00415
00416
00417
00418
00419
00420 for (i = 0; i < nmembers; i++)
00421 {
00422 if (TransactionIdEquals(members[i].xid, xid) &&
00423 (members[i].status == status))
00424 {
00425 debug_elog4(DEBUG2, "Expand: %u is already a member of %u",
00426 xid, multi);
00427 pfree(members);
00428 return multi;
00429 }
00430 }
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441
00442 newMembers = (MultiXactMember *)
00443 palloc(sizeof(MultiXactMember) * (nmembers + 1));
00444
00445 for (i = 0, j = 0; i < nmembers; i++)
00446 {
00447 if (TransactionIdIsInProgress(members[i].xid) ||
00448 ((members[i].status > MultiXactStatusForUpdate) &&
00449 TransactionIdDidCommit(members[i].xid)))
00450 {
00451 newMembers[j].xid = members[i].xid;
00452 newMembers[j++].status = members[i].status;
00453 }
00454 }
00455
00456 newMembers[j].xid = xid;
00457 newMembers[j++].status = status;
00458 newMulti = CreateMultiXactId(j, newMembers);
00459
00460 pfree(members);
00461 pfree(newMembers);
00462
00463 debug_elog3(DEBUG2, "Expand: returning new multi %u", newMulti);
00464
00465 return newMulti;
00466 }
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476
00477
00478
00479 bool
00480 MultiXactIdIsRunning(MultiXactId multi)
00481 {
00482 MultiXactMember *members;
00483 int nmembers;
00484 int i;
00485
00486 debug_elog3(DEBUG2, "IsRunning %u?", multi);
00487
00488
00489
00490
00491
00492 nmembers = GetMultiXactIdMembers(multi, &members, false);
00493
00494 if (nmembers < 0)
00495 {
00496 debug_elog2(DEBUG2, "IsRunning: no members");
00497 return false;
00498 }
00499
00500
00501
00502
00503
00504
00505
00506
00507 for (i = 0; i < nmembers; i++)
00508 {
00509 if (TransactionIdIsCurrentTransactionId(members[i].xid))
00510 {
00511 debug_elog3(DEBUG2, "IsRunning: I (%d) am running!", i);
00512 pfree(members);
00513 return true;
00514 }
00515 }
00516
00517
00518
00519
00520
00521
00522 for (i = 0; i < nmembers; i++)
00523 {
00524 if (TransactionIdIsInProgress(members[i].xid))
00525 {
00526 debug_elog4(DEBUG2, "IsRunning: member %d (%u) is running",
00527 i, members[i].xid);
00528 pfree(members);
00529 return true;
00530 }
00531 }
00532
00533 pfree(members);
00534
00535 debug_elog3(DEBUG2, "IsRunning: %u is not running", multi);
00536
00537 return false;
00538 }
00539
00540
00541
00542
00543
00544
00545
00546
00547
00548
00549
00550
00551
00552
00553 void
00554 MultiXactIdSetOldestMember(void)
00555 {
00556 if (!MultiXactIdIsValid(OldestMemberMXactId[MyBackendId]))
00557 {
00558 MultiXactId nextMXact;
00559
00560
00561
00562
00563
00564
00565
00566
00567
00568
00569 LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
00570
00571
00572
00573
00574
00575
00576 nextMXact = MultiXactState->nextMXact;
00577 if (nextMXact < FirstMultiXactId)
00578 nextMXact = FirstMultiXactId;
00579
00580 OldestMemberMXactId[MyBackendId] = nextMXact;
00581
00582 LWLockRelease(MultiXactGenLock);
00583
00584 debug_elog4(DEBUG2, "MultiXact: setting OldestMember[%d] = %u",
00585 MyBackendId, nextMXact);
00586 }
00587 }
00588
00589
00590
00591
00592
00593
00594
00595
00596
00597
00598
00599
00600
00601
00602
00603
00604
00605 static void
00606 MultiXactIdSetOldestVisible(void)
00607 {
00608 if (!MultiXactIdIsValid(OldestVisibleMXactId[MyBackendId]))
00609 {
00610 MultiXactId oldestMXact;
00611 int i;
00612
00613 LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
00614
00615
00616
00617
00618
00619
00620 oldestMXact = MultiXactState->nextMXact;
00621 if (oldestMXact < FirstMultiXactId)
00622 oldestMXact = FirstMultiXactId;
00623
00624 for (i = 1; i <= MaxOldestSlot; i++)
00625 {
00626 MultiXactId thisoldest = OldestMemberMXactId[i];
00627
00628 if (MultiXactIdIsValid(thisoldest) &&
00629 MultiXactIdPrecedes(thisoldest, oldestMXact))
00630 oldestMXact = thisoldest;
00631 }
00632
00633 OldestVisibleMXactId[MyBackendId] = oldestMXact;
00634
00635 LWLockRelease(MultiXactGenLock);
00636
00637 debug_elog4(DEBUG2, "MultiXact: setting OldestVisible[%d] = %u",
00638 MyBackendId, oldestMXact);
00639 }
00640 }
00641
00642
00643
00644
00645
00646 MultiXactId
00647 ReadNextMultiXactId(void)
00648 {
00649 MultiXactId mxid;
00650
00651
00652 LWLockAcquire(MultiXactGenLock, LW_SHARED);
00653 mxid = MultiXactState->nextMXact;
00654 LWLockRelease(MultiXactGenLock);
00655
00656 if (mxid < FirstMultiXactId)
00657 mxid = FirstMultiXactId;
00658
00659 return mxid;
00660 }
00661
00662
00663
00664
00665
00666
00667
00668
00669
00670
00671 static MultiXactId
00672 CreateMultiXactId(int nmembers, MultiXactMember *members)
00673 {
00674 MultiXactId multi;
00675 MultiXactOffset offset;
00676 XLogRecData rdata[2];
00677 xl_multixact_create xlrec;
00678
00679 debug_elog3(DEBUG2, "Create: %s",
00680 mxid_to_string(InvalidMultiXactId, nmembers, members));
00681
00682
00683
00684
00685
00686
00687
00688
00689
00690
00691
00692 multi = mXactCacheGetBySet(nmembers, members);
00693 if (MultiXactIdIsValid(multi))
00694 {
00695 debug_elog2(DEBUG2, "Create: in cache!");
00696 return multi;
00697 }
00698
00699
00700
00701
00702
00703
00704 multi = GetNewMultiXactId(nmembers, &offset);
00705
00706
00707
00708
00709
00710
00711
00712
00713
00714
00715
00716
00717
00718
00719 xlrec.mid = multi;
00720 xlrec.moff = offset;
00721 xlrec.nmembers = nmembers;
00722
00723
00724
00725
00726
00727
00728
00729 rdata[0].data = (char *) (&xlrec);
00730 rdata[0].len = SizeOfMultiXactCreate;
00731 rdata[0].buffer = InvalidBuffer;
00732 rdata[0].next = &(rdata[1]);
00733
00734 rdata[1].data = (char *) members;
00735 rdata[1].len = nmembers * sizeof(MultiXactMember);
00736 rdata[1].buffer = InvalidBuffer;
00737 rdata[1].next = NULL;
00738
00739 (void) XLogInsert(RM_MULTIXACT_ID, XLOG_MULTIXACT_CREATE_ID, rdata);
00740
00741
00742 RecordNewMultiXact(multi, offset, nmembers, members);
00743
00744
00745 END_CRIT_SECTION();
00746
00747
00748 mXactCachePut(multi, nmembers, members);
00749
00750 debug_elog2(DEBUG2, "Create: all done");
00751
00752 return multi;
00753 }
00754
00755
00756
00757
00758
00759
00760
00761 static void
00762 RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
00763 int nmembers, MultiXactMember *members)
00764 {
00765 int pageno;
00766 int prev_pageno;
00767 int entryno;
00768 int slotno;
00769 MultiXactOffset *offptr;
00770 int i;
00771
00772 LWLockAcquire(MultiXactOffsetControlLock, LW_EXCLUSIVE);
00773
00774 pageno = MultiXactIdToOffsetPage(multi);
00775 entryno = MultiXactIdToOffsetEntry(multi);
00776
00777
00778
00779
00780
00781
00782
00783
00784 slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, multi);
00785 offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
00786 offptr += entryno;
00787
00788 *offptr = offset;
00789
00790 MultiXactOffsetCtl->shared->page_dirty[slotno] = true;
00791
00792
00793 LWLockRelease(MultiXactOffsetControlLock);
00794
00795 LWLockAcquire(MultiXactMemberControlLock, LW_EXCLUSIVE);
00796
00797 prev_pageno = -1;
00798
00799 for (i = 0; i < nmembers; i++, offset++)
00800 {
00801 TransactionId *memberptr;
00802 uint32 *flagsptr;
00803 uint32 flagsval;
00804 int bshift;
00805 int flagsoff;
00806 int memberoff;
00807
00808 Assert(members[i].status <= MultiXactStatusUpdate);
00809
00810 pageno = MXOffsetToMemberPage(offset);
00811 memberoff = MXOffsetToMemberOffset(offset);
00812 flagsoff = MXOffsetToFlagsOffset(offset);
00813 bshift = MXOffsetToFlagsBitShift(offset);
00814
00815 if (pageno != prev_pageno)
00816 {
00817 slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, multi);
00818 prev_pageno = pageno;
00819 }
00820
00821 memberptr = (TransactionId *)
00822 (MultiXactMemberCtl->shared->page_buffer[slotno] + memberoff);
00823
00824 *memberptr = members[i].xid;
00825
00826 flagsptr = (uint32 *)
00827 (MultiXactMemberCtl->shared->page_buffer[slotno] + flagsoff);
00828
00829 flagsval = *flagsptr;
00830 flagsval &= ~(((1 << MXACT_MEMBER_BITS_PER_XACT) - 1) << bshift);
00831 flagsval |= (members[i].status << bshift);
00832 *flagsptr = flagsval;
00833
00834 MultiXactMemberCtl->shared->page_dirty[slotno] = true;
00835 }
00836
00837 LWLockRelease(MultiXactMemberControlLock);
00838 }
00839
00840
00841
00842
00843
00844
00845
00846
00847
00848
00849
00850
00851
00852
00853
00854
00855 static MultiXactId
00856 GetNewMultiXactId(int nmembers, MultiXactOffset *offset)
00857 {
00858 MultiXactId result;
00859 MultiXactOffset nextOffset;
00860
00861 debug_elog3(DEBUG2, "GetNew: for %d xids", nmembers);
00862
00863
00864 Assert(MultiXactIdIsValid(OldestMemberMXactId[MyBackendId]));
00865
00866
00867 if (RecoveryInProgress())
00868 elog(ERROR, "cannot assign MultiXactIds during recovery");
00869
00870 LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
00871
00872
00873 if (MultiXactState->nextMXact < FirstMultiXactId)
00874 MultiXactState->nextMXact = FirstMultiXactId;
00875
00876
00877 result = MultiXactState->nextMXact;
00878
00879
00880
00881
00882
00883
00884
00885
00886
00887
00888
00889
00890
00891 if (!MultiXactIdPrecedes(result, MultiXactState->multiVacLimit))
00892 {
00893
00894
00895
00896
00897
00898
00899
00900 MultiXactId multiWarnLimit = MultiXactState->multiWarnLimit;
00901 MultiXactId multiStopLimit = MultiXactState->multiStopLimit;
00902 MultiXactId multiWrapLimit = MultiXactState->multiWrapLimit;
00903 Oid oldest_datoid = MultiXactState->oldestMultiXactDB;
00904
00905 LWLockRelease(MultiXactGenLock);
00906
00907
00908
00909
00910
00911
00912 if (IsUnderPostmaster && (result % 65536) == 0)
00913 SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);
00914
00915 if (IsUnderPostmaster &&
00916 !MultiXactIdPrecedes(result, multiStopLimit))
00917 {
00918 char *oldest_datname = get_database_name(oldest_datoid);
00919
00920
00921 if (oldest_datname)
00922 ereport(ERROR,
00923 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
00924 errmsg("database is not accepting commands that generate new MultiXactIds to avoid wraparound data loss in database \"%s\"",
00925 oldest_datname),
00926 errhint("Execute a database-wide VACUUM in that database.\n"
00927 "You might also need to commit or roll back old prepared transactions.")));
00928 else
00929 ereport(ERROR,
00930 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
00931 errmsg("database is not accepting commands that generate new MultiXactIds to avoid wraparound data loss in database with OID %u",
00932 oldest_datoid),
00933 errhint("Execute a database-wide VACUUM in that database.\n"
00934 "You might also need to commit or roll back old prepared transactions.")));
00935 }
00936 else if (!MultiXactIdPrecedes(result, multiWarnLimit))
00937 {
00938 char *oldest_datname = get_database_name(oldest_datoid);
00939
00940
00941 if (oldest_datname)
00942 ereport(WARNING,
00943 (errmsg("database \"%s\" must be vacuumed before %u more MultiXactIds are used",
00944 oldest_datname,
00945 multiWrapLimit - result),
00946 errhint("Execute a database-wide VACUUM in that database.\n"
00947 "You might also need to commit or roll back old prepared transactions.")));
00948 else
00949 ereport(WARNING,
00950 (errmsg("database with OID %u must be vacuumed before %u more MultiXactIds are used",
00951 oldest_datoid,
00952 multiWrapLimit - result),
00953 errhint("Execute a database-wide VACUUM in that database.\n"
00954 "You might also need to commit or roll back old prepared transactions.")));
00955 }
00956
00957
00958 LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
00959 result = MultiXactState->nextMXact;
00960 if (result < FirstMultiXactId)
00961 result = FirstMultiXactId;
00962 }
00963
00964
00965 ExtendMultiXactOffset(result);
00966
00967
00968
00969
00970
00971
00972 nextOffset = MultiXactState->nextOffset;
00973 if (nextOffset == 0)
00974 {
00975 *offset = 1;
00976 nmembers++;
00977 }
00978 else
00979 *offset = nextOffset;
00980
00981 ExtendMultiXactMember(nextOffset, nmembers);
00982
00983
00984
00985
00986
00987
00988
00989
00990 START_CRIT_SECTION();
00991
00992
00993
00994
00995
00996
00997
00998
00999
01000
01001
01002
01003 (MultiXactState->nextMXact)++;
01004
01005 MultiXactState->nextOffset += nmembers;
01006
01007 LWLockRelease(MultiXactGenLock);
01008
01009 debug_elog4(DEBUG2, "GetNew: returning %u offset %u", result, *offset);
01010 return result;
01011 }
01012
01013
01014
01015
01016
01017
01018
01019
01020
01021
01022
01023
01024
01025
01026
01027
01028
01029 int
01030 GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members,
01031 bool allow_old)
01032 {
01033 int pageno;
01034 int prev_pageno;
01035 int entryno;
01036 int slotno;
01037 MultiXactOffset *offptr;
01038 MultiXactOffset offset;
01039 int length;
01040 int truelength;
01041 int i;
01042 MultiXactId oldestMXact;
01043 MultiXactId nextMXact;
01044 MultiXactId tmpMXact;
01045 MultiXactOffset nextOffset;
01046 MultiXactMember *ptr;
01047
01048 debug_elog3(DEBUG2, "GetMembers: asked for %u", multi);
01049
01050 Assert(MultiXactIdIsValid(multi));
01051
01052
01053 length = mXactCacheGetById(multi, members);
01054 if (length >= 0)
01055 {
01056 debug_elog3(DEBUG2, "GetMembers: found %s in the cache",
01057 mxid_to_string(multi, length, *members));
01058 return length;
01059 }
01060
01061
01062 MultiXactIdSetOldestVisible();
01063
01064
01065
01066
01067
01068
01069
01070
01071
01072
01073
01074
01075
01076
01077
01078
01079
01080
01081
01082
01083
01084
01085
01086 LWLockAcquire(MultiXactGenLock, LW_SHARED);
01087
01088 oldestMXact = MultiXactState->oldestMultiXactId;
01089 nextMXact = MultiXactState->nextMXact;
01090 nextOffset = MultiXactState->nextOffset;
01091
01092 LWLockRelease(MultiXactGenLock);
01093
01094 if (MultiXactIdPrecedes(multi, oldestMXact))
01095 {
01096 ereport(allow_old ? DEBUG1 : ERROR,
01097 (errcode(ERRCODE_INTERNAL_ERROR),
01098 errmsg("MultiXactId %u does no longer exist -- apparent wraparound",
01099 multi)));
01100 return -1;
01101 }
01102
01103 if (!MultiXactIdPrecedes(multi, nextMXact))
01104 ereport(ERROR,
01105 (errcode(ERRCODE_INTERNAL_ERROR),
01106 errmsg("MultiXactId %u has not been created yet -- apparent wraparound",
01107 multi)));
01108
01109
01110
01111
01112
01113
01114
01115
01116
01117
01118
01119
01120
01121
01122
01123
01124
01125
01126
01127
01128
01129
01130
01131
01132
01133
01134
01135
01136
01137
01138
01139
01140
01141
01142 retry:
01143 LWLockAcquire(MultiXactOffsetControlLock, LW_EXCLUSIVE);
01144
01145 pageno = MultiXactIdToOffsetPage(multi);
01146 entryno = MultiXactIdToOffsetEntry(multi);
01147
01148 slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, multi);
01149 offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
01150 offptr += entryno;
01151 offset = *offptr;
01152
01153 Assert(offset != 0);
01154
01155
01156
01157
01158
01159 tmpMXact = multi + 1;
01160
01161 if (nextMXact == tmpMXact)
01162 {
01163
01164 length = nextOffset - offset;
01165 }
01166 else
01167 {
01168 MultiXactOffset nextMXOffset;
01169
01170
01171 if (tmpMXact < FirstMultiXactId)
01172 tmpMXact = FirstMultiXactId;
01173
01174 prev_pageno = pageno;
01175
01176 pageno = MultiXactIdToOffsetPage(tmpMXact);
01177 entryno = MultiXactIdToOffsetEntry(tmpMXact);
01178
01179 if (pageno != prev_pageno)
01180 slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, tmpMXact);
01181
01182 offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
01183 offptr += entryno;
01184 nextMXOffset = *offptr;
01185
01186 if (nextMXOffset == 0)
01187 {
01188
01189 LWLockRelease(MultiXactOffsetControlLock);
01190 pg_usleep(1000L);
01191 goto retry;
01192 }
01193
01194 length = nextMXOffset - offset;
01195 }
01196
01197 LWLockRelease(MultiXactOffsetControlLock);
01198
01199 ptr = (MultiXactMember *) palloc(length * sizeof(MultiXactMember));
01200 *members = ptr;
01201
01202
01203 LWLockAcquire(MultiXactMemberControlLock, LW_EXCLUSIVE);
01204
01205 truelength = 0;
01206 prev_pageno = -1;
01207 for (i = 0; i < length; i++, offset++)
01208 {
01209 TransactionId *xactptr;
01210 uint32 *flagsptr;
01211 int flagsoff;
01212 int bshift;
01213 int memberoff;
01214
01215 pageno = MXOffsetToMemberPage(offset);
01216 memberoff = MXOffsetToMemberOffset(offset);
01217
01218 if (pageno != prev_pageno)
01219 {
01220 slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, multi);
01221 prev_pageno = pageno;
01222 }
01223
01224 xactptr = (TransactionId *)
01225 (MultiXactMemberCtl->shared->page_buffer[slotno] + memberoff);
01226
01227 if (!TransactionIdIsValid(*xactptr))
01228 {
01229
01230 Assert(offset == 0);
01231 continue;
01232 }
01233
01234 flagsoff = MXOffsetToFlagsOffset(offset);
01235 bshift = MXOffsetToFlagsBitShift(offset);
01236 flagsptr = (uint32 *) (MultiXactMemberCtl->shared->page_buffer[slotno] + flagsoff);
01237
01238 ptr[truelength].xid = *xactptr;
01239 ptr[truelength].status = (*flagsptr >> bshift) & MXACT_MEMBER_XACT_BITMASK;
01240 truelength++;
01241 }
01242
01243 LWLockRelease(MultiXactMemberControlLock);
01244
01245
01246
01247
01248 mXactCachePut(multi, truelength, ptr);
01249
01250 debug_elog3(DEBUG2, "GetMembers: no cache for %s",
01251 mxid_to_string(multi, truelength, ptr));
01252 return truelength;
01253 }
01254
01255
01256
01257
01258
01259
01260
01261
01262 static int
01263 mxactMemberComparator(const void *arg1, const void *arg2)
01264 {
01265 MultiXactMember member1 = *(const MultiXactMember *) arg1;
01266 MultiXactMember member2 = *(const MultiXactMember *) arg2;
01267
01268 if (member1.xid > member2.xid)
01269 return 1;
01270 if (member1.xid < member2.xid)
01271 return -1;
01272 if (member1.status > member2.status)
01273 return 1;
01274 if (member1.status < member2.status)
01275 return -1;
01276 return 0;
01277 }
01278
01279
01280
01281
01282
01283
01284
01285
01286
01287
01288
01289
01290
01291
01292 static MultiXactId
01293 mXactCacheGetBySet(int nmembers, MultiXactMember *members)
01294 {
01295 mXactCacheEnt *entry;
01296
01297 debug_elog3(DEBUG2, "CacheGet: looking for %s",
01298 mxid_to_string(InvalidMultiXactId, nmembers, members));
01299
01300
01301 qsort(members, nmembers, sizeof(MultiXactMember), mxactMemberComparator);
01302
01303 for (entry = MXactCache; entry != NULL; entry = entry->next)
01304 {
01305 if (entry->nmembers != nmembers)
01306 continue;
01307
01308
01309
01310
01311
01312 if (memcmp(members, entry->members, nmembers * sizeof(MultiXactMember)) == 0)
01313 {
01314 debug_elog3(DEBUG2, "CacheGet: found %u", entry->multi);
01315 return entry->multi;
01316 }
01317 }
01318
01319 debug_elog2(DEBUG2, "CacheGet: not found :-(");
01320 return InvalidMultiXactId;
01321 }
01322
01323
01324
01325
01326
01327
01328
01329
01330
01331 static int
01332 mXactCacheGetById(MultiXactId multi, MultiXactMember **members)
01333 {
01334 mXactCacheEnt *entry;
01335
01336 debug_elog3(DEBUG2, "CacheGet: looking for %u", multi);
01337
01338 for (entry = MXactCache; entry != NULL; entry = entry->next)
01339 {
01340 if (entry->multi == multi)
01341 {
01342 MultiXactMember *ptr;
01343 Size size;
01344
01345 size = sizeof(MultiXactMember) * entry->nmembers;
01346 ptr = (MultiXactMember *) palloc(size);
01347 *members = ptr;
01348
01349 memcpy(ptr, entry->members, size);
01350
01351 debug_elog3(DEBUG2, "CacheGet: found %s",
01352 mxid_to_string(multi, entry->nmembers, entry->members));
01353 return entry->nmembers;
01354 }
01355 }
01356
01357 debug_elog2(DEBUG2, "CacheGet: not found");
01358 return -1;
01359 }
01360
01361
01362
01363
01364
01365 static void
01366 mXactCachePut(MultiXactId multi, int nmembers, MultiXactMember *members)
01367 {
01368 mXactCacheEnt *entry;
01369
01370 debug_elog3(DEBUG2, "CachePut: storing %s",
01371 mxid_to_string(multi, nmembers, members));
01372
01373 if (MXactContext == NULL)
01374 {
01375
01376 debug_elog2(DEBUG2, "CachePut: initializing memory context");
01377 MXactContext = AllocSetContextCreate(TopTransactionContext,
01378 "MultiXact Cache Context",
01379 ALLOCSET_SMALL_MINSIZE,
01380 ALLOCSET_SMALL_INITSIZE,
01381 ALLOCSET_SMALL_MAXSIZE);
01382 }
01383
01384 entry = (mXactCacheEnt *)
01385 MemoryContextAlloc(MXactContext,
01386 offsetof(mXactCacheEnt, members) +
01387 nmembers * sizeof(MultiXactMember));
01388
01389 entry->multi = multi;
01390 entry->nmembers = nmembers;
01391 memcpy(entry->members, members, nmembers * sizeof(MultiXactMember));
01392
01393
01394 qsort(entry->members, nmembers, sizeof(MultiXactMember), mxactMemberComparator);
01395
01396 entry->next = MXactCache;
01397 MXactCache = entry;
01398 }
01399
01400 static char *
01401 mxstatus_to_string(MultiXactStatus status)
01402 {
01403 switch (status)
01404 {
01405 case MultiXactStatusForKeyShare:
01406 return "keysh";
01407 case MultiXactStatusForShare:
01408 return "sh";
01409 case MultiXactStatusForNoKeyUpdate:
01410 return "fornokeyupd";
01411 case MultiXactStatusForUpdate:
01412 return "forupd";
01413 case MultiXactStatusNoKeyUpdate:
01414 return "nokeyupd";
01415 case MultiXactStatusUpdate:
01416 return "upd";
01417 default:
01418 elog(ERROR, "unrecognized multixact status %d", status);
01419 return "";
01420 }
01421 }
01422
01423 char *
01424 mxid_to_string(MultiXactId multi, int nmembers, MultiXactMember *members)
01425 {
01426 static char *str = NULL;
01427 StringInfoData buf;
01428 int i;
01429
01430 if (str != NULL)
01431 pfree(str);
01432
01433 initStringInfo(&buf);
01434
01435 appendStringInfo(&buf, "%u %d[%u (%s)", multi, nmembers, members[0].xid,
01436 mxstatus_to_string(members[0].status));
01437
01438 for (i = 1; i < nmembers; i++)
01439 appendStringInfo(&buf, ", %u (%s)", members[i].xid,
01440 mxstatus_to_string(members[i].status));
01441
01442 appendStringInfoChar(&buf, ']');
01443 str = MemoryContextStrdup(TopMemoryContext, buf.data);
01444 pfree(buf.data);
01445 return str;
01446 }
01447
01448
01449
01450
01451
01452
01453
01454 void
01455 AtEOXact_MultiXact(void)
01456 {
01457
01458
01459
01460
01461
01462
01463
01464 OldestMemberMXactId[MyBackendId] = InvalidMultiXactId;
01465 OldestVisibleMXactId[MyBackendId] = InvalidMultiXactId;
01466
01467
01468
01469
01470
01471 MXactContext = NULL;
01472 MXactCache = NULL;
01473 }
01474
01475
01476
01477
01478
01479
01480
01481
01482 void
01483 AtPrepare_MultiXact(void)
01484 {
01485 MultiXactId myOldestMember = OldestMemberMXactId[MyBackendId];
01486
01487 if (MultiXactIdIsValid(myOldestMember))
01488 RegisterTwoPhaseRecord(TWOPHASE_RM_MULTIXACT_ID, 0,
01489 &myOldestMember, sizeof(MultiXactId));
01490 }
01491
01492
01493
01494
01495
01496 void
01497 PostPrepare_MultiXact(TransactionId xid)
01498 {
01499 MultiXactId myOldestMember;
01500
01501
01502
01503
01504
01505 myOldestMember = OldestMemberMXactId[MyBackendId];
01506 if (MultiXactIdIsValid(myOldestMember))
01507 {
01508 BackendId dummyBackendId = TwoPhaseGetDummyBackendId(xid);
01509
01510
01511
01512
01513
01514
01515
01516 LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
01517
01518 OldestMemberMXactId[dummyBackendId] = myOldestMember;
01519 OldestMemberMXactId[MyBackendId] = InvalidMultiXactId;
01520
01521 LWLockRelease(MultiXactGenLock);
01522 }
01523
01524
01525
01526
01527
01528
01529
01530
01531
01532 OldestVisibleMXactId[MyBackendId] = InvalidMultiXactId;
01533
01534
01535
01536
01537 MXactContext = NULL;
01538 MXactCache = NULL;
01539 }
01540
01541
01542
01543
01544
01545 void
01546 multixact_twophase_recover(TransactionId xid, uint16 info,
01547 void *recdata, uint32 len)
01548 {
01549 BackendId dummyBackendId = TwoPhaseGetDummyBackendId(xid);
01550 MultiXactId oldestMember;
01551
01552
01553
01554
01555
01556 Assert(len == sizeof(MultiXactId));
01557 oldestMember = *((MultiXactId *) recdata);
01558
01559 OldestMemberMXactId[dummyBackendId] = oldestMember;
01560 }
01561
01562
01563
01564
01565
01566 void
01567 multixact_twophase_postcommit(TransactionId xid, uint16 info,
01568 void *recdata, uint32 len)
01569 {
01570 BackendId dummyBackendId = TwoPhaseGetDummyBackendId(xid);
01571
01572 Assert(len == sizeof(MultiXactId));
01573
01574 OldestMemberMXactId[dummyBackendId] = InvalidMultiXactId;
01575 }
01576
01577
01578
01579
01580
01581 void
01582 multixact_twophase_postabort(TransactionId xid, uint16 info,
01583 void *recdata, uint32 len)
01584 {
01585 multixact_twophase_postcommit(xid, info, recdata, len);
01586 }
01587
01588
01589
01590
01591
01592
01593 Size
01594 MultiXactShmemSize(void)
01595 {
01596 Size size;
01597
01598 #define SHARED_MULTIXACT_STATE_SIZE \
01599 add_size(sizeof(MultiXactStateData), \
01600 mul_size(sizeof(MultiXactId) * 2, MaxOldestSlot))
01601
01602 size = SHARED_MULTIXACT_STATE_SIZE;
01603 size = add_size(size, SimpleLruShmemSize(NUM_MXACTOFFSET_BUFFERS, 0));
01604 size = add_size(size, SimpleLruShmemSize(NUM_MXACTMEMBER_BUFFERS, 0));
01605
01606 return size;
01607 }
01608
01609 void
01610 MultiXactShmemInit(void)
01611 {
01612 bool found;
01613
01614 debug_elog2(DEBUG2, "Shared Memory Init for MultiXact");
01615
01616 MultiXactOffsetCtl->PagePrecedes = MultiXactOffsetPagePrecedes;
01617 MultiXactMemberCtl->PagePrecedes = MultiXactMemberPagePrecedes;
01618
01619 SimpleLruInit(MultiXactOffsetCtl,
01620 "MultiXactOffset Ctl", NUM_MXACTOFFSET_BUFFERS, 0,
01621 MultiXactOffsetControlLock, "pg_multixact/offsets");
01622 SimpleLruInit(MultiXactMemberCtl,
01623 "MultiXactMember Ctl", NUM_MXACTMEMBER_BUFFERS, 0,
01624 MultiXactMemberControlLock, "pg_multixact/members");
01625
01626
01627 MultiXactState = ShmemInitStruct("Shared MultiXact State",
01628 SHARED_MULTIXACT_STATE_SIZE,
01629 &found);
01630 if (!IsUnderPostmaster)
01631 {
01632 Assert(!found);
01633
01634
01635 MemSet(MultiXactState, 0, SHARED_MULTIXACT_STATE_SIZE);
01636 }
01637 else
01638 Assert(found);
01639
01640
01641
01642
01643
01644 OldestMemberMXactId = MultiXactState->perBackendXactIds;
01645 OldestVisibleMXactId = OldestMemberMXactId + MaxOldestSlot;
01646 }
01647
01648
01649
01650
01651
01652
01653 void
01654 BootStrapMultiXact(void)
01655 {
01656 int slotno;
01657
01658 LWLockAcquire(MultiXactOffsetControlLock, LW_EXCLUSIVE);
01659
01660
01661 slotno = ZeroMultiXactOffsetPage(0, false);
01662
01663
01664 SimpleLruWritePage(MultiXactOffsetCtl, slotno);
01665 Assert(!MultiXactOffsetCtl->shared->page_dirty[slotno]);
01666
01667 LWLockRelease(MultiXactOffsetControlLock);
01668
01669 LWLockAcquire(MultiXactMemberControlLock, LW_EXCLUSIVE);
01670
01671
01672 slotno = ZeroMultiXactMemberPage(0, false);
01673
01674
01675 SimpleLruWritePage(MultiXactMemberCtl, slotno);
01676 Assert(!MultiXactMemberCtl->shared->page_dirty[slotno]);
01677
01678 LWLockRelease(MultiXactMemberControlLock);
01679 }
01680
01681
01682
01683
01684
01685
01686
01687
01688
01689
01690 static int
01691 ZeroMultiXactOffsetPage(int pageno, bool writeXlog)
01692 {
01693 int slotno;
01694
01695 slotno = SimpleLruZeroPage(MultiXactOffsetCtl, pageno);
01696
01697 if (writeXlog)
01698 WriteMZeroPageXlogRec(pageno, XLOG_MULTIXACT_ZERO_OFF_PAGE);
01699
01700 return slotno;
01701 }
01702
01703
01704
01705
01706 static int
01707 ZeroMultiXactMemberPage(int pageno, bool writeXlog)
01708 {
01709 int slotno;
01710
01711 slotno = SimpleLruZeroPage(MultiXactMemberCtl, pageno);
01712
01713 if (writeXlog)
01714 WriteMZeroPageXlogRec(pageno, XLOG_MULTIXACT_ZERO_MEM_PAGE);
01715
01716 return slotno;
01717 }
01718
01719
01720
01721
01722
01723
01724
01725
01726
01727
01728
01729
01730 void
01731 StartupMultiXact(void)
01732 {
01733 MultiXactId multi = MultiXactState->nextMXact;
01734 MultiXactOffset offset = MultiXactState->nextOffset;
01735 int pageno;
01736 int entryno;
01737 int flagsoff;
01738
01739
01740 LWLockAcquire(MultiXactOffsetControlLock, LW_EXCLUSIVE);
01741
01742
01743
01744
01745 pageno = MultiXactIdToOffsetPage(multi);
01746 MultiXactOffsetCtl->shared->latest_page_number = pageno;
01747
01748
01749
01750
01751
01752 entryno = MultiXactIdToOffsetEntry(multi);
01753 if (entryno != 0)
01754 {
01755 int slotno;
01756 MultiXactOffset *offptr;
01757
01758 slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, multi);
01759 offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
01760 offptr += entryno;
01761
01762 MemSet(offptr, 0, BLCKSZ - (entryno * sizeof(MultiXactOffset)));
01763
01764 MultiXactOffsetCtl->shared->page_dirty[slotno] = true;
01765 }
01766
01767 LWLockRelease(MultiXactOffsetControlLock);
01768
01769
01770 LWLockAcquire(MultiXactMemberControlLock, LW_EXCLUSIVE);
01771
01772
01773
01774
01775 pageno = MXOffsetToMemberPage(offset);
01776 MultiXactMemberCtl->shared->latest_page_number = pageno;
01777
01778
01779
01780
01781
01782 flagsoff = MXOffsetToFlagsOffset(offset);
01783 if (flagsoff != 0)
01784 {
01785 int slotno;
01786 TransactionId *xidptr;
01787 int memberoff;
01788
01789 memberoff = MXOffsetToMemberOffset(offset);
01790 slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, offset);
01791 xidptr = (TransactionId *)
01792 (MultiXactMemberCtl->shared->page_buffer[slotno] + memberoff);
01793
01794 MemSet(xidptr, 0, BLCKSZ - memberoff);
01795
01796
01797
01798
01799
01800
01801
01802 MultiXactMemberCtl->shared->page_dirty[slotno] = true;
01803 }
01804
01805 LWLockRelease(MultiXactMemberControlLock);
01806 }
01807
01808
01809
01810
01811 void
01812 ShutdownMultiXact(void)
01813 {
01814
01815 TRACE_POSTGRESQL_MULTIXACT_CHECKPOINT_START(false);
01816 SimpleLruFlush(MultiXactOffsetCtl, false);
01817 SimpleLruFlush(MultiXactMemberCtl, false);
01818 TRACE_POSTGRESQL_MULTIXACT_CHECKPOINT_DONE(false);
01819 }
01820
01821
01822
01823
01824 void
01825 MultiXactGetCheckptMulti(bool is_shutdown,
01826 MultiXactId *nextMulti,
01827 MultiXactOffset *nextMultiOffset,
01828 MultiXactId *oldestMulti,
01829 Oid *oldestMultiDB)
01830 {
01831 LWLockAcquire(MultiXactGenLock, LW_SHARED);
01832 *nextMulti = MultiXactState->nextMXact;
01833 *nextMultiOffset = MultiXactState->nextOffset;
01834 *oldestMulti = MultiXactState->oldestMultiXactId;
01835 *oldestMultiDB = MultiXactState->oldestMultiXactDB;
01836 LWLockRelease(MultiXactGenLock);
01837
01838 debug_elog6(DEBUG2,
01839 "MultiXact: checkpoint is nextMulti %u, nextOffset %u, oldestMulti %u in DB %u",
01840 *nextMulti, *nextMultiOffset, *oldestMulti, *oldestMultiDB);
01841 }
01842
01843
01844
01845
01846 void
01847 CheckPointMultiXact(void)
01848 {
01849 TRACE_POSTGRESQL_MULTIXACT_CHECKPOINT_START(true);
01850
01851
01852 SimpleLruFlush(MultiXactOffsetCtl, true);
01853 SimpleLruFlush(MultiXactMemberCtl, true);
01854
01855 TRACE_POSTGRESQL_MULTIXACT_CHECKPOINT_DONE(true);
01856 }
01857
01858
01859
01860
01861
01862
01863
01864
01865
01866 void
01867 MultiXactSetNextMXact(MultiXactId nextMulti,
01868 MultiXactOffset nextMultiOffset)
01869 {
01870 debug_elog4(DEBUG2, "MultiXact: setting next multi to %u offset %u",
01871 nextMulti, nextMultiOffset);
01872 LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
01873 MultiXactState->nextMXact = nextMulti;
01874 MultiXactState->nextOffset = nextMultiOffset;
01875 LWLockRelease(MultiXactGenLock);
01876 }
01877
01878
01879
01880
01881
01882
01883 void
01884 SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid)
01885 {
01886 MultiXactId multiVacLimit;
01887 MultiXactId multiWarnLimit;
01888 MultiXactId multiStopLimit;
01889 MultiXactId multiWrapLimit;
01890 MultiXactId curMulti;
01891
01892 Assert(MultiXactIdIsValid(oldest_datminmxid));
01893
01894
01895
01896
01897
01898
01899
01900
01901 multiWrapLimit = oldest_datminmxid + (MaxMultiXactId >> 1);
01902 if (multiWrapLimit < FirstMultiXactId)
01903 multiWrapLimit += FirstMultiXactId;
01904
01905
01906
01907
01908
01909 multiStopLimit = multiWrapLimit - 100;
01910 if (multiStopLimit < FirstMultiXactId)
01911 multiStopLimit -= FirstMultiXactId;
01912
01913
01914
01915
01916
01917
01918
01919
01920
01921
01922
01923 multiWarnLimit = multiStopLimit - 10000000;
01924 if (multiWarnLimit < FirstMultiXactId)
01925 multiWarnLimit -= FirstMultiXactId;
01926
01927
01928
01929
01930
01931 multiVacLimit = oldest_datminmxid + 200000000;
01932 if (multiVacLimit < FirstMultiXactId)
01933 multiVacLimit += FirstMultiXactId;
01934
01935
01936 LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
01937 MultiXactState->oldestMultiXactId = oldest_datminmxid;
01938 MultiXactState->oldestMultiXactDB = oldest_datoid;
01939 MultiXactState->multiVacLimit = multiVacLimit;
01940 MultiXactState->multiWarnLimit = multiWarnLimit;
01941 MultiXactState->multiStopLimit = multiStopLimit;
01942 MultiXactState->multiWrapLimit = multiWrapLimit;
01943 curMulti = MultiXactState->nextMXact;
01944 LWLockRelease(MultiXactGenLock);
01945
01946
01947 ereport(DEBUG1,
01948 (errmsg("MultiXactId wrap limit is %u, limited by database with OID %u",
01949 multiWrapLimit, oldest_datoid)));
01950
01951
01952
01953
01954
01955
01956
01957
01958 if (MultiXactIdPrecedes(multiVacLimit, curMulti) &&
01959 IsUnderPostmaster && !InRecovery)
01960 SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);
01961
01962
01963 if (MultiXactIdPrecedes(multiWarnLimit, curMulti) && !InRecovery)
01964 {
01965 char *oldest_datname;
01966
01967
01968
01969
01970
01971
01972
01973
01974
01975
01976 if (IsTransactionState())
01977 oldest_datname = get_database_name(oldest_datoid);
01978 else
01979 oldest_datname = NULL;
01980
01981 if (oldest_datname)
01982 ereport(WARNING,
01983 (errmsg("database \"%s\" must be vacuumed before %u more MultiXactIds are used",
01984 oldest_datname,
01985 multiWrapLimit - curMulti),
01986 errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
01987 "You might also need to commit or roll back old prepared transactions.")));
01988 else
01989 ereport(WARNING,
01990 (errmsg("database with OID %u must be vacuumed before %u more MultiXactIds are used",
01991 oldest_datoid,
01992 multiWrapLimit - curMulti),
01993 errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
01994 "You might also need to commit or roll back old prepared transactions.")));
01995 }
01996 }
01997
01998
01999
02000
02001
02002
02003
02004
02005
02006
02007 void
02008 MultiXactAdvanceNextMXact(MultiXactId minMulti,
02009 MultiXactOffset minMultiOffset)
02010 {
02011 LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
02012 if (MultiXactIdPrecedes(MultiXactState->nextMXact, minMulti))
02013 {
02014 debug_elog3(DEBUG2, "MultiXact: setting next multi to %u", minMulti);
02015 MultiXactState->nextMXact = minMulti;
02016 }
02017 if (MultiXactOffsetPrecedes(MultiXactState->nextOffset, minMultiOffset))
02018 {
02019 debug_elog3(DEBUG2, "MultiXact: setting next offset to %u",
02020 minMultiOffset);
02021 MultiXactState->nextOffset = minMultiOffset;
02022 }
02023 LWLockRelease(MultiXactGenLock);
02024 }
02025
02026
02027
02028
02029
02030 void
02031 MultiXactAdvanceOldest(MultiXactId oldestMulti, Oid oldestMultiDB)
02032 {
02033 if (MultiXactIdPrecedes(MultiXactState->oldestMultiXactId, oldestMulti))
02034 SetMultiXactIdLimit(oldestMulti, oldestMultiDB);
02035 }
02036
02037
02038
02039
02040
02041
02042
02043
02044
02045 static void
02046 ExtendMultiXactOffset(MultiXactId multi)
02047 {
02048 int pageno;
02049
02050
02051
02052
02053
02054 if (MultiXactIdToOffsetEntry(multi) != 0 &&
02055 multi != FirstMultiXactId)
02056 return;
02057
02058 pageno = MultiXactIdToOffsetPage(multi);
02059
02060 LWLockAcquire(MultiXactOffsetControlLock, LW_EXCLUSIVE);
02061
02062
02063 ZeroMultiXactOffsetPage(pageno, true);
02064
02065 LWLockRelease(MultiXactOffsetControlLock);
02066 }
02067
02068
02069
02070
02071
02072
02073
02074
02075 static void
02076 ExtendMultiXactMember(MultiXactOffset offset, int nmembers)
02077 {
02078
02079
02080
02081
02082
02083
02084 while (nmembers > 0)
02085 {
02086 int flagsoff;
02087 int flagsbit;
02088 int difference;
02089
02090
02091
02092
02093 flagsoff = MXOffsetToFlagsOffset(offset);
02094 flagsbit = MXOffsetToFlagsBitShift(offset);
02095 if (flagsoff == 0 && flagsbit == 0)
02096 {
02097 int pageno;
02098
02099 pageno = MXOffsetToMemberPage(offset);
02100
02101 LWLockAcquire(MultiXactMemberControlLock, LW_EXCLUSIVE);
02102
02103
02104 ZeroMultiXactMemberPage(pageno, true);
02105
02106 LWLockRelease(MultiXactMemberControlLock);
02107 }
02108
02109
02110 difference = MULTIXACT_MEMBERS_PER_PAGE - offset % MULTIXACT_MEMBERS_PER_PAGE;
02111 offset += difference;
02112 nmembers -= difference;
02113 }
02114 }
02115
02116
02117
02118
02119
02120
02121
02122
02123
02124
02125
02126
02127 MultiXactId
02128 GetOldestMultiXactId(void)
02129 {
02130 MultiXactId oldestMXact;
02131 MultiXactId nextMXact;
02132 int i;
02133
02134
02135
02136
02137
02138 LWLockAcquire(MultiXactGenLock, LW_SHARED);
02139
02140
02141
02142
02143
02144
02145 nextMXact = MultiXactState->nextMXact;
02146 if (nextMXact < FirstMultiXactId)
02147 nextMXact = FirstMultiXactId;
02148
02149 oldestMXact = nextMXact;
02150 for (i = 1; i <= MaxOldestSlot; i++)
02151 {
02152 MultiXactId thisoldest;
02153
02154 thisoldest = OldestMemberMXactId[i];
02155 if (MultiXactIdIsValid(thisoldest) &&
02156 MultiXactIdPrecedes(thisoldest, oldestMXact))
02157 oldestMXact = thisoldest;
02158 thisoldest = OldestVisibleMXactId[i];
02159 if (MultiXactIdIsValid(thisoldest) &&
02160 MultiXactIdPrecedes(thisoldest, oldestMXact))
02161 oldestMXact = thisoldest;
02162 }
02163
02164 LWLockRelease(MultiXactGenLock);
02165
02166 return oldestMXact;
02167 }
02168
02169 typedef struct mxtruncinfo
02170 {
02171 int earliestExistingPage;
02172 } mxtruncinfo;
02173
02174
02175
02176
02177
02178 static bool
02179 SlruScanDirCbFindEarliest(SlruCtl ctl, char *filename, int segpage, void *data)
02180 {
02181 mxtruncinfo *trunc = (mxtruncinfo *) data;
02182
02183 if (trunc->earliestExistingPage == -1 ||
02184 ctl->PagePrecedes(segpage, trunc->earliestExistingPage))
02185 {
02186 trunc->earliestExistingPage = segpage;
02187 }
02188
02189 return false;
02190 }
02191
02192
02193
02194
02195
02196
02197
02198
02199
02200 void
02201 TruncateMultiXact(MultiXactId oldestMXact)
02202 {
02203 MultiXactOffset oldestOffset;
02204 mxtruncinfo trunc;
02205 MultiXactId earliest;
02206
02207
02208
02209
02210
02211
02212
02213
02214 trunc.earliestExistingPage = -1;
02215 SlruScanDirectory(MultiXactOffsetCtl, SlruScanDirCbFindEarliest, &trunc);
02216 earliest = trunc.earliestExistingPage * MULTIXACT_OFFSETS_PER_PAGE;
02217
02218
02219 if (MultiXactIdPrecedes(oldestMXact, earliest))
02220 return;
02221
02222
02223
02224
02225
02226
02227 {
02228 int pageno;
02229 int slotno;
02230 int entryno;
02231 MultiXactOffset *offptr;
02232
02233
02234
02235 pageno = MultiXactIdToOffsetPage(oldestMXact);
02236 entryno = MultiXactIdToOffsetEntry(oldestMXact);
02237
02238 slotno = SimpleLruReadPage_ReadOnly(MultiXactOffsetCtl, pageno,
02239 oldestMXact);
02240 offptr = (MultiXactOffset *)
02241 MultiXactOffsetCtl->shared->page_buffer[slotno];
02242 offptr += entryno;
02243 oldestOffset = *offptr;
02244
02245 LWLockRelease(MultiXactOffsetControlLock);
02246 }
02247
02248
02249 SimpleLruTruncate(MultiXactOffsetCtl,
02250 MultiXactIdToOffsetPage(oldestMXact));
02251
02252
02253 SimpleLruTruncate(MultiXactMemberCtl,
02254 MXOffsetToMemberPage(oldestOffset));
02255 }
02256
02257
02258
02259
02260
02261
02262
02263
02264
02265
02266
02267
02268 static bool
02269 MultiXactOffsetPagePrecedes(int page1, int page2)
02270 {
02271 MultiXactId multi1;
02272 MultiXactId multi2;
02273
02274 multi1 = ((MultiXactId) page1) * MULTIXACT_OFFSETS_PER_PAGE;
02275 multi1 += FirstMultiXactId;
02276 multi2 = ((MultiXactId) page2) * MULTIXACT_OFFSETS_PER_PAGE;
02277 multi2 += FirstMultiXactId;
02278
02279 return MultiXactIdPrecedes(multi1, multi2);
02280 }
02281
02282
02283
02284
02285
02286 static bool
02287 MultiXactMemberPagePrecedes(int page1, int page2)
02288 {
02289 MultiXactOffset offset1;
02290 MultiXactOffset offset2;
02291
02292 offset1 = ((MultiXactOffset) page1) * MULTIXACT_MEMBERS_PER_PAGE;
02293 offset2 = ((MultiXactOffset) page2) * MULTIXACT_MEMBERS_PER_PAGE;
02294
02295 return MultiXactOffsetPrecedes(offset1, offset2);
02296 }
02297
02298
02299
02300
02301
02302
02303
02304 bool
02305 MultiXactIdPrecedes(MultiXactId multi1, MultiXactId multi2)
02306 {
02307 int32 diff = (int32) (multi1 - multi2);
02308
02309 return (diff < 0);
02310 }
02311
02312
02313
02314
02315 static bool
02316 MultiXactOffsetPrecedes(MultiXactOffset offset1, MultiXactOffset offset2)
02317 {
02318 int32 diff = (int32) (offset1 - offset2);
02319
02320 return (diff < 0);
02321 }
02322
02323
02324
02325
02326
02327 static void
02328 WriteMZeroPageXlogRec(int pageno, uint8 info)
02329 {
02330 XLogRecData rdata;
02331
02332 rdata.data = (char *) (&pageno);
02333 rdata.len = sizeof(int);
02334 rdata.buffer = InvalidBuffer;
02335 rdata.next = NULL;
02336 (void) XLogInsert(RM_MULTIXACT_ID, info, &rdata);
02337 }
02338
02339
02340
02341
02342 void
02343 multixact_redo(XLogRecPtr lsn, XLogRecord *record)
02344 {
02345 uint8 info = record->xl_info & ~XLR_INFO_MASK;
02346
02347
02348 Assert(!(record->xl_info & XLR_BKP_BLOCK_MASK));
02349
02350 if (info == XLOG_MULTIXACT_ZERO_OFF_PAGE)
02351 {
02352 int pageno;
02353 int slotno;
02354
02355 memcpy(&pageno, XLogRecGetData(record), sizeof(int));
02356
02357 LWLockAcquire(MultiXactOffsetControlLock, LW_EXCLUSIVE);
02358
02359 slotno = ZeroMultiXactOffsetPage(pageno, false);
02360 SimpleLruWritePage(MultiXactOffsetCtl, slotno);
02361 Assert(!MultiXactOffsetCtl->shared->page_dirty[slotno]);
02362
02363 LWLockRelease(MultiXactOffsetControlLock);
02364 }
02365 else if (info == XLOG_MULTIXACT_ZERO_MEM_PAGE)
02366 {
02367 int pageno;
02368 int slotno;
02369
02370 memcpy(&pageno, XLogRecGetData(record), sizeof(int));
02371
02372 LWLockAcquire(MultiXactMemberControlLock, LW_EXCLUSIVE);
02373
02374 slotno = ZeroMultiXactMemberPage(pageno, false);
02375 SimpleLruWritePage(MultiXactMemberCtl, slotno);
02376 Assert(!MultiXactMemberCtl->shared->page_dirty[slotno]);
02377
02378 LWLockRelease(MultiXactMemberControlLock);
02379 }
02380 else if (info == XLOG_MULTIXACT_CREATE_ID)
02381 {
02382 xl_multixact_create *xlrec =
02383 (xl_multixact_create *) XLogRecGetData(record);
02384 TransactionId max_xid;
02385 int i;
02386
02387
02388 RecordNewMultiXact(xlrec->mid, xlrec->moff, xlrec->nmembers,
02389 xlrec->members);
02390
02391
02392 MultiXactAdvanceNextMXact(xlrec->mid + 1,
02393 xlrec->moff + xlrec->nmembers);
02394
02395
02396
02397
02398
02399
02400 max_xid = record->xl_xid;
02401 for (i = 0; i < xlrec->nmembers; i++)
02402 {
02403 if (TransactionIdPrecedes(max_xid, xlrec->members[i].xid))
02404 max_xid = xlrec->members[i].xid;
02405 }
02406
02407
02408
02409
02410
02411
02412 if (TransactionIdFollowsOrEquals(max_xid,
02413 ShmemVariableCache->nextXid))
02414 {
02415 LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
02416 ShmemVariableCache->nextXid = max_xid;
02417 TransactionIdAdvance(ShmemVariableCache->nextXid);
02418 LWLockRelease(XidGenLock);
02419 }
02420 }
02421 else
02422 elog(PANIC, "multixact_redo: unknown op code %u", info);
02423 }
02424
02425 Datum
02426 pg_get_multixact_members(PG_FUNCTION_ARGS)
02427 {
02428 typedef struct
02429 {
02430 MultiXactMember *members;
02431 int nmembers;
02432 int iter;
02433 } mxact;
02434 MultiXactId mxid = PG_GETARG_UINT32(0);
02435 mxact *multi;
02436 FuncCallContext *funccxt;
02437
02438 if (mxid < FirstMultiXactId)
02439 ereport(ERROR,
02440 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
02441 errmsg("invalid MultiXactId: %u", mxid)));
02442
02443 if (SRF_IS_FIRSTCALL())
02444 {
02445 MemoryContext oldcxt;
02446 TupleDesc tupdesc;
02447
02448 funccxt = SRF_FIRSTCALL_INIT();
02449 oldcxt = MemoryContextSwitchTo(funccxt->multi_call_memory_ctx);
02450
02451 multi = palloc(sizeof(mxact));
02452
02453 multi->nmembers = GetMultiXactIdMembers(mxid, &multi->members, false);
02454 multi->iter = 0;
02455
02456 tupdesc = CreateTemplateTupleDesc(2, false);
02457 TupleDescInitEntry(tupdesc, (AttrNumber) 1, "xid",
02458 XIDOID, -1, 0);
02459 TupleDescInitEntry(tupdesc, (AttrNumber) 2, "mode",
02460 TEXTOID, -1, 0);
02461
02462 funccxt->attinmeta = TupleDescGetAttInMetadata(tupdesc);
02463 funccxt->user_fctx = multi;
02464
02465 MemoryContextSwitchTo(oldcxt);
02466 }
02467
02468 funccxt = SRF_PERCALL_SETUP();
02469 multi = (mxact *) funccxt->user_fctx;
02470
02471 while (multi->iter < multi->nmembers)
02472 {
02473 HeapTuple tuple;
02474 char *values[2];
02475
02476 values[0] = palloc(32);
02477 sprintf(values[0], "%u", multi->members[multi->iter].xid);
02478 values[1] = mxstatus_to_string(multi->members[multi->iter].status);
02479
02480 tuple = BuildTupleFromCStrings(funccxt->attinmeta, values);
02481
02482 multi->iter++;
02483 pfree(values[0]);
02484 SRF_RETURN_NEXT(funccxt, HeapTupleGetDatum(tuple));
02485 }
02486
02487 if (multi->nmembers > 0)
02488 pfree(multi->members);
02489 pfree(multi);
02490
02491 SRF_RETURN_DONE(funccxt);
02492 }