diff --git a/src/backend/utils/time/snapmgr.c b/src/backend/utils/time/snapmgr.c index 8aa1f49..dc00d91 100644 --- a/src/backend/utils/time/snapmgr.c +++ b/src/backend/utils/time/snapmgr.c @@ -80,8 +80,11 @@ typedef struct OldSnapshotControlData */ slock_t mutex_current; /* protect current timestamp */ int64 current_timestamp; /* latest snapshot timestamp */ - slock_t mutex_latest_xmin; /* protect latest snapshot xmin */ + slock_t mutex_latest_xmin; /* protect latest snapshot xmin + * and next_map_update + */ TransactionId latest_xmin; /* latest snapshot xmin */ + int64 next_map_update; /* latest snapshot valid up to */ slock_t mutex_threshold; /* protect threshold fields */ int64 threshold_timestamp; /* earlier snapshot is old */ TransactionId threshold_xid; /* earlier xid may be gone */ @@ -95,7 +98,9 @@ typedef struct OldSnapshotControlData * count_used value of old_snapshot_threshold means that the buffer is * full and the head must be advanced to add new entries. Use timestamps * aligned to minute boundaries, since that seems less surprising than - * aligning based on the first usage timestamp. + * aligning based on the first usage timestamp. The latest bucket is + * effectively stored within latest_xmin. The circular buffer is updated + * when we get a new xmin value that doesn't fall into the same interval. * * It is OK if the xid for a given time slot is from earlier than * calculated by adding the number of minutes corresponding to the @@ -269,6 +274,7 @@ SnapMgrInit(void) oldSnapshotControl->current_timestamp = 0; SpinLockInit(&oldSnapshotControl->mutex_latest_xmin); oldSnapshotControl->latest_xmin = InvalidTransactionId; + oldSnapshotControl->next_map_update = 0; SpinLockInit(&oldSnapshotControl->mutex_threshold); oldSnapshotControl->threshold_timestamp = 0; oldSnapshotControl->threshold_xid = InvalidTransactionId; @@ -1594,9 +1600,15 @@ TransactionIdLimitedForOldSnapshots(TransactionId recentXmin, { int64 ts = GetSnapshotCurrentTimestamp(); TransactionId xlimit = recentXmin; - TransactionId latest_xmin = oldSnapshotControl->latest_xmin; + TransactionId latest_xmin; + int64 update_ts; bool same_ts_as_threshold = false; + SpinLockAcquire(&oldSnapshotControl->mutex_latest_xmin); + latest_xmin = oldSnapshotControl->latest_xmin; + update_ts = oldSnapshotControl->next_map_update; + SpinLockRelease(&oldSnapshotControl->mutex_latest_xmin); + /* * Zero threshold always overrides to latest xmin, if valid. Without * some heuristic it will find its own snapshot too old on, for @@ -1631,6 +1643,14 @@ TransactionIdLimitedForOldSnapshots(TransactionId recentXmin, if (!same_ts_as_threshold) { + if (ts == update_ts) + { + xlimit = latest_xmin; + if (NormalTransactionIdFollows(xlimit, recentXmin)) + SetOldSnapshotThresholdTimestamp(ts, xlimit); + } + else + { LWLockAcquire(OldSnapshotTimeMapLock, LW_SHARED); if (oldSnapshotControl->count_used > 0 @@ -1651,6 +1671,7 @@ TransactionIdLimitedForOldSnapshots(TransactionId recentXmin, } LWLockRelease(OldSnapshotTimeMapLock); + } } /* @@ -1680,17 +1701,36 @@ void MaintainOldSnapshotTimeMapping(int64 whenTaken, TransactionId xmin) { int64 ts; + TransactionId latest_xmin; + int64 update_ts; + bool map_update_required = false; /* Fast exit when old_snapshot_threshold is not used. */ if (old_snapshot_threshold < 0) return; - /* Keep track of the latest xmin seen by any process. */ + ts = AlignTimestampToMinuteBoundary(whenTaken); + + /* + * Keep track of the latest xmin seen by any process. Update mapping + * with a new value when we have crossed a bucket boundary. + */ SpinLockAcquire(&oldSnapshotControl->mutex_latest_xmin); - if (TransactionIdFollows(xmin, oldSnapshotControl->latest_xmin)) + latest_xmin = oldSnapshotControl->latest_xmin; + update_ts = oldSnapshotControl->next_map_update; + if (ts > update_ts) + { + oldSnapshotControl->next_map_update = ts; + map_update_required = true; + } + if (TransactionIdFollows(xmin, latest_xmin)) oldSnapshotControl->latest_xmin = xmin; SpinLockRelease(&oldSnapshotControl->mutex_latest_xmin); + /* We only needed to update the most recent xmin value. */ + if (!map_update_required) + return; + /* No further tracking needed for 0 (used for testing). */ if (old_snapshot_threshold == 0) return; @@ -1716,8 +1756,6 @@ MaintainOldSnapshotTimeMapping(int64 whenTaken, TransactionId xmin) return; } - ts = AlignTimestampToMinuteBoundary(whenTaken); - LWLockAcquire(OldSnapshotTimeMapLock, LW_EXCLUSIVE); Assert(oldSnapshotControl->head_offset >= 0);