@@ -78,10 +78,13 @@ typedef struct OldSnapshotControlData
78
78
* Variables for old snapshot handling are shared among processes and are
79
79
* only allowed to move forward.
80
80
*/
81
- slock_t mutex_current ; /* protect current timestamp */
81
+ slock_t mutex_current ; /* protect current_timestamp */
82
82
int64 current_timestamp ; /* latest snapshot timestamp */
83
- slock_t mutex_latest_xmin ; /* protect latest snapshot xmin */
83
+ slock_t mutex_latest_xmin ; /* protect latest_xmin
84
+ * and next_map_update
85
+ */
84
86
TransactionId latest_xmin ; /* latest snapshot xmin */
87
+ int64 next_map_update ; /* latest snapshot valid up to */
85
88
slock_t mutex_threshold ; /* protect threshold fields */
86
89
int64 threshold_timestamp ; /* earlier snapshot is old */
87
90
TransactionId threshold_xid ; /* earlier xid may be gone */
@@ -95,7 +98,10 @@ typedef struct OldSnapshotControlData
95
98
* count_used value of OLD_SNAPSHOT_TIME_MAP_ENTRIES means that the buffer
96
99
* is full and the head must be advanced to add new entries. Use
97
100
* timestamps aligned to minute boundaries, since that seems less
98
- * surprising than aligning based on the first usage timestamp.
101
+ * surprising than aligning based on the first usage timestamp. The
102
+ * latest bucket is effectively stored within latest_xmin. The circular
103
+ * buffer is updated when we get a new xmin value that doesn't fall into
104
+ * the same interval.
99
105
*
100
106
* It is OK if the xid for a given time slot is from earlier than
101
107
* calculated by adding the number of minutes corresponding to the
@@ -269,6 +275,7 @@ SnapMgrInit(void)
269
275
oldSnapshotControl -> current_timestamp = 0 ;
270
276
SpinLockInit (& oldSnapshotControl -> mutex_latest_xmin );
271
277
oldSnapshotControl -> latest_xmin = InvalidTransactionId ;
278
+ oldSnapshotControl -> next_map_update = 0 ;
272
279
SpinLockInit (& oldSnapshotControl -> mutex_threshold );
273
280
oldSnapshotControl -> threshold_timestamp = 0 ;
274
281
oldSnapshotControl -> threshold_xid = InvalidTransactionId ;
@@ -1595,9 +1602,15 @@ TransactionIdLimitedForOldSnapshots(TransactionId recentXmin,
1595
1602
{
1596
1603
int64 ts = GetSnapshotCurrentTimestamp ();
1597
1604
TransactionId xlimit = recentXmin ;
1598
- TransactionId latest_xmin = oldSnapshotControl -> latest_xmin ;
1605
+ TransactionId latest_xmin ;
1606
+ int64 update_ts ;
1599
1607
bool same_ts_as_threshold = false;
1600
1608
1609
+ SpinLockAcquire (& oldSnapshotControl -> mutex_latest_xmin );
1610
+ latest_xmin = oldSnapshotControl -> latest_xmin ;
1611
+ update_ts = oldSnapshotControl -> next_map_update ;
1612
+ SpinLockRelease (& oldSnapshotControl -> mutex_latest_xmin );
1613
+
1601
1614
/*
1602
1615
* Zero threshold always overrides to latest xmin, if valid. Without
1603
1616
* some heuristic it will find its own snapshot too old on, for
@@ -1632,26 +1645,35 @@ TransactionIdLimitedForOldSnapshots(TransactionId recentXmin,
1632
1645
1633
1646
if (!same_ts_as_threshold )
1634
1647
{
1635
- LWLockAcquire (OldSnapshotTimeMapLock , LW_SHARED );
1636
-
1637
- if (oldSnapshotControl -> count_used > 0
1638
- && ts >= oldSnapshotControl -> head_timestamp )
1648
+ if (ts == update_ts )
1639
1649
{
1640
- int offset ;
1641
-
1642
- offset = ((ts - oldSnapshotControl -> head_timestamp )
1643
- / USECS_PER_MINUTE );
1644
- if (offset > oldSnapshotControl -> count_used - 1 )
1645
- offset = oldSnapshotControl -> count_used - 1 ;
1646
- offset = (oldSnapshotControl -> head_offset + offset )
1647
- % OLD_SNAPSHOT_TIME_MAP_ENTRIES ;
1648
- xlimit = oldSnapshotControl -> xid_by_minute [offset ];
1649
-
1650
+ xlimit = latest_xmin ;
1650
1651
if (NormalTransactionIdFollows (xlimit , recentXmin ))
1651
1652
SetOldSnapshotThresholdTimestamp (ts , xlimit );
1652
1653
}
1654
+ else
1655
+ {
1656
+ LWLockAcquire (OldSnapshotTimeMapLock , LW_SHARED );
1653
1657
1654
- LWLockRelease (OldSnapshotTimeMapLock );
1658
+ if (oldSnapshotControl -> count_used > 0
1659
+ && ts >= oldSnapshotControl -> head_timestamp )
1660
+ {
1661
+ int offset ;
1662
+
1663
+ offset = ((ts - oldSnapshotControl -> head_timestamp )
1664
+ / USECS_PER_MINUTE );
1665
+ if (offset > oldSnapshotControl -> count_used - 1 )
1666
+ offset = oldSnapshotControl -> count_used - 1 ;
1667
+ offset = (oldSnapshotControl -> head_offset + offset )
1668
+ % OLD_SNAPSHOT_TIME_MAP_ENTRIES ;
1669
+ xlimit = oldSnapshotControl -> xid_by_minute [offset ];
1670
+
1671
+ if (NormalTransactionIdFollows (xlimit , recentXmin ))
1672
+ SetOldSnapshotThresholdTimestamp (ts , xlimit );
1673
+ }
1674
+
1675
+ LWLockRelease (OldSnapshotTimeMapLock );
1676
+ }
1655
1677
}
1656
1678
1657
1679
/*
@@ -1681,16 +1703,35 @@ void
1681
1703
MaintainOldSnapshotTimeMapping (int64 whenTaken , TransactionId xmin )
1682
1704
{
1683
1705
int64 ts ;
1706
+ TransactionId latest_xmin ;
1707
+ int64 update_ts ;
1708
+ bool map_update_required = false;
1684
1709
1685
1710
/* Never call this function when old snapshot checking is disabled. */
1686
1711
Assert (old_snapshot_threshold >= 0 );
1687
1712
1688
- /* Keep track of the latest xmin seen by any process. */
1713
+ ts = AlignTimestampToMinuteBoundary (whenTaken );
1714
+
1715
+ /*
1716
+ * Keep track of the latest xmin seen by any process. Update mapping
1717
+ * with a new value when we have crossed a bucket boundary.
1718
+ */
1689
1719
SpinLockAcquire (& oldSnapshotControl -> mutex_latest_xmin );
1690
- if (TransactionIdFollows (xmin , oldSnapshotControl -> latest_xmin ))
1720
+ latest_xmin = oldSnapshotControl -> latest_xmin ;
1721
+ update_ts = oldSnapshotControl -> next_map_update ;
1722
+ if (ts > update_ts )
1723
+ {
1724
+ oldSnapshotControl -> next_map_update = ts ;
1725
+ map_update_required = true;
1726
+ }
1727
+ if (TransactionIdFollows (xmin , latest_xmin ))
1691
1728
oldSnapshotControl -> latest_xmin = xmin ;
1692
1729
SpinLockRelease (& oldSnapshotControl -> mutex_latest_xmin );
1693
1730
1731
+ /* We only needed to update the most recent xmin value. */
1732
+ if (!map_update_required )
1733
+ return ;
1734
+
1694
1735
/* No further tracking needed for 0 (used for testing). */
1695
1736
if (old_snapshot_threshold == 0 )
1696
1737
return ;
@@ -1716,8 +1757,6 @@ MaintainOldSnapshotTimeMapping(int64 whenTaken, TransactionId xmin)
1716
1757
return ;
1717
1758
}
1718
1759
1719
- ts = AlignTimestampToMinuteBoundary (whenTaken );
1720
-
1721
1760
LWLockAcquire (OldSnapshotTimeMapLock , LW_EXCLUSIVE );
1722
1761
1723
1762
Assert (oldSnapshotControl -> head_offset >= 0 );
0 commit comments