|
19 | 19 | #include "funcapi.h"
|
20 | 20 | #include "miscadmin.h"
|
21 | 21 | #include "storage/bufmgr.h"
|
| 22 | +#include "storage/proc.h" |
22 | 23 | #include "storage/procarray.h"
|
23 | 24 | #include "storage/smgr.h"
|
24 | 25 | #include "utils/rel.h"
|
@@ -532,6 +533,63 @@ collect_visibility_data(Oid relid, bool include_pd)
|
532 | 533 | return info;
|
533 | 534 | }
|
534 | 535 |
|
| 536 | +/* |
| 537 | + * The "strict" version of GetOldestNonRemovableTransactionId(). The |
| 538 | + * pg_visibility check can tolerate false positives (don't report some of the |
| 539 | + * errors), but can't tolerate false negatives (report false errors). Normally, |
| 540 | + * horizons move forwards, but there are cases when it could move backward |
| 541 | + * (see comment for ComputeXidHorizons()). |
| 542 | + * |
| 543 | + * This is why we have to implement our own function for xid horizon, which |
| 544 | + * would be guaranteed to be newer or equal to any xid horizon computed before. |
| 545 | + * We have to do the following to achieve this. |
| 546 | + * |
| 547 | + * 1. Ignore processes xmin's, because they consider connection to other |
| 548 | + * databases that were ignored before. |
| 549 | + * 2. Ignore KnownAssignedXids, because they are not database-aware. At the |
| 550 | + * same time, the primary could compute its horizons database-aware. |
| 551 | + * 3. Ignore walsender xmin, because it could go backward if some replication |
| 552 | + * connections don't use replication slots. |
| 553 | + * |
| 554 | + * As a result, we're using only currently running xids to compute the horizon. |
| 555 | + * Surely these would significantly sacrifice accuracy. But we have to do so |
| 556 | + * to avoid reporting false errors. |
| 557 | + */ |
| 558 | +static TransactionId |
| 559 | +GetStrictOldestNonRemovableTransactionId(Relation rel) |
| 560 | +{ |
| 561 | + RunningTransactions runningTransactions; |
| 562 | + |
| 563 | + if (rel == NULL || rel->rd_rel->relisshared || RecoveryInProgress()) |
| 564 | + { |
| 565 | + /* Shared relation: take into account all running xids */ |
| 566 | + runningTransactions = GetRunningTransactionData(); |
| 567 | + LWLockRelease(ProcArrayLock); |
| 568 | + LWLockRelease(XidGenLock); |
| 569 | + return runningTransactions->oldestRunningXid; |
| 570 | + } |
| 571 | + else if (!RELATION_IS_LOCAL(rel)) |
| 572 | + { |
| 573 | + /* |
| 574 | + * Normal relation: take into account xids running within the current |
| 575 | + * database |
| 576 | + */ |
| 577 | + runningTransactions = GetRunningTransactionData(); |
| 578 | + LWLockRelease(ProcArrayLock); |
| 579 | + LWLockRelease(XidGenLock); |
| 580 | + return runningTransactions->oldestDatabaseRunningXid; |
| 581 | + } |
| 582 | + else |
| 583 | + { |
| 584 | + /* |
| 585 | + * For temporary relations, ComputeXidHorizons() uses only |
| 586 | + * TransamVariables->latestCompletedXid and MyProc->xid. These two |
| 587 | + * shouldn't go backwards. So we're fine with this horizon. |
| 588 | + */ |
| 589 | + return GetOldestNonRemovableTransactionId(rel); |
| 590 | + } |
| 591 | +} |
| 592 | + |
535 | 593 | /*
|
536 | 594 | * Returns a list of items whose visibility map information does not match
|
537 | 595 | * the status of the tuples on the page.
|
@@ -563,7 +621,7 @@ collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen)
|
563 | 621 | check_relation_relkind(rel);
|
564 | 622 |
|
565 | 623 | if (all_visible)
|
566 |
| - OldestXmin = GetOldestNonRemovableTransactionId(rel); |
| 624 | + OldestXmin = GetStrictOldestNonRemovableTransactionId(rel); |
567 | 625 |
|
568 | 626 | nblocks = RelationGetNumberOfBlocks(rel);
|
569 | 627 |
|
@@ -671,11 +729,11 @@ collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen)
|
671 | 729 | * retake ProcArrayLock here while we're holding the buffer
|
672 | 730 | * exclusively locked, but it should be safe against
|
673 | 731 | * deadlocks, because surely
|
674 |
| - * GetOldestNonRemovableTransactionId() should never take a |
675 |
| - * buffer lock. And this shouldn't happen often, so it's worth |
676 |
| - * being careful so as to avoid false positives. |
| 732 | + * GetStrictOldestNonRemovableTransactionId() should never |
| 733 | + * take a buffer lock. And this shouldn't happen often, so |
| 734 | + * it's worth being careful so as to avoid false positives. |
677 | 735 | */
|
678 |
| - RecomputedOldestXmin = GetOldestNonRemovableTransactionId(rel); |
| 736 | + RecomputedOldestXmin = GetStrictOldestNonRemovableTransactionId(rel); |
679 | 737 |
|
680 | 738 | if (!TransactionIdPrecedes(OldestXmin, RecomputedOldestXmin))
|
681 | 739 | record_corrupt_item(items, &tuple.t_self);
|
|
0 commit comments