@@ -78,10 +78,13 @@ typedef struct OldSnapshotControlData
7878 * Variables for old snapshot handling are shared among processes and are
7979 * only allowed to move forward.
8080 */
81- slock_t mutex_current ; /* protect current timestamp */
81+ slock_t mutex_current ; /* protect current_timestamp */
8282 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+ */
8486 TransactionId latest_xmin ; /* latest snapshot xmin */
87+ int64 next_map_update ; /* latest snapshot valid up to */
8588 slock_t mutex_threshold ; /* protect threshold fields */
8689 int64 threshold_timestamp ; /* earlier snapshot is old */
8790 TransactionId threshold_xid ; /* earlier xid may be gone */
@@ -95,7 +98,10 @@ typedef struct OldSnapshotControlData
9598 * count_used value of OLD_SNAPSHOT_TIME_MAP_ENTRIES means that the buffer
9699 * is full and the head must be advanced to add new entries. Use
97100 * 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.
99105 *
100106 * It is OK if the xid for a given time slot is from earlier than
101107 * calculated by adding the number of minutes corresponding to the
@@ -269,6 +275,7 @@ SnapMgrInit(void)
269275 oldSnapshotControl -> current_timestamp = 0 ;
270276 SpinLockInit (& oldSnapshotControl -> mutex_latest_xmin );
271277 oldSnapshotControl -> latest_xmin = InvalidTransactionId ;
278+ oldSnapshotControl -> next_map_update = 0 ;
272279 SpinLockInit (& oldSnapshotControl -> mutex_threshold );
273280 oldSnapshotControl -> threshold_timestamp = 0 ;
274281 oldSnapshotControl -> threshold_xid = InvalidTransactionId ;
@@ -1595,9 +1602,15 @@ TransactionIdLimitedForOldSnapshots(TransactionId recentXmin,
15951602 {
15961603 int64 ts = GetSnapshotCurrentTimestamp ();
15971604 TransactionId xlimit = recentXmin ;
1598- TransactionId latest_xmin = oldSnapshotControl -> latest_xmin ;
1605+ TransactionId latest_xmin ;
1606+ int64 update_ts ;
15991607 bool same_ts_as_threshold = false;
16001608
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+
16011614 /*
16021615 * Zero threshold always overrides to latest xmin, if valid. Without
16031616 * some heuristic it will find its own snapshot too old on, for
@@ -1632,26 +1645,35 @@ TransactionIdLimitedForOldSnapshots(TransactionId recentXmin,
16321645
16331646 if (!same_ts_as_threshold )
16341647 {
1635- LWLockAcquire (OldSnapshotTimeMapLock , LW_SHARED );
1636-
1637- if (oldSnapshotControl -> count_used > 0
1638- && ts >= oldSnapshotControl -> head_timestamp )
1648+ if (ts == update_ts )
16391649 {
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 ;
16501651 if (NormalTransactionIdFollows (xlimit , recentXmin ))
16511652 SetOldSnapshotThresholdTimestamp (ts , xlimit );
16521653 }
1654+ else
1655+ {
1656+ LWLockAcquire (OldSnapshotTimeMapLock , LW_SHARED );
16531657
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+ }
16551677 }
16561678
16571679 /*
@@ -1681,16 +1703,35 @@ void
16811703MaintainOldSnapshotTimeMapping (int64 whenTaken , TransactionId xmin )
16821704{
16831705 int64 ts ;
1706+ TransactionId latest_xmin ;
1707+ int64 update_ts ;
1708+ bool map_update_required = false;
16841709
16851710 /* Never call this function when old snapshot checking is disabled. */
16861711 Assert (old_snapshot_threshold >= 0 );
16871712
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+ */
16891719 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 ))
16911728 oldSnapshotControl -> latest_xmin = xmin ;
16921729 SpinLockRelease (& oldSnapshotControl -> mutex_latest_xmin );
16931730
1731+ /* We only needed to update the most recent xmin value. */
1732+ if (!map_update_required )
1733+ return ;
1734+
16941735 /* No further tracking needed for 0 (used for testing). */
16951736 if (old_snapshot_threshold == 0 )
16961737 return ;
@@ -1716,8 +1757,6 @@ MaintainOldSnapshotTimeMapping(int64 whenTaken, TransactionId xmin)
17161757 return ;
17171758 }
17181759
1719- ts = AlignTimestampToMinuteBoundary (whenTaken );
1720-
17211760 LWLockAcquire (OldSnapshotTimeMapLock , LW_EXCLUSIVE );
17221761
17231762 Assert (oldSnapshotControl -> head_offset >= 0 );
0 commit comments