@@ -172,7 +172,7 @@ GlobalSnapshotShmemInit()
172
172
/*
173
173
* GlobalSnapshotStartup
174
174
*
175
- * Set gsXidMap etnries to oldestActiveXID during startup.
175
+ * Set gsXidMap entries to oldestActiveXID during startup.
176
176
*/
177
177
void
178
178
GlobalSnapshotStartup (TransactionId oldestActiveXID )
@@ -200,24 +200,33 @@ GlobalSnapshotStartup(TransactionId oldestActiveXID)
200
200
* global transaction. Otherwise old versions of tuples that were needed for
201
201
* this transaction can be recycled by other processes (vacuum, HOT, etc).
202
202
*
203
- * Called upon each snapshot creation after ProcArrayLock is released. Such
204
- * usage creates a race condition. It is possible that backend who got
205
- * glabal_csn called GlobalSnapshotMapXmin() only after other backends managed
206
- * to get snapshot and complete GlobalSnapshotMapXmin() call. To address that
207
- * race we do two thigs:
203
+ * Locking here is not trivial. Called upon each snapshot creation after
204
+ * ProcArrayLock is released. Such usage creates several race conditions. It
205
+ * is possible that backend who got global_csn called GlobalSnapshotMapXmin()
206
+ * only after other backends managed to get snapshot and complete
207
+ * GlobalSnapshotMapXmin() call, or even committed. This is safe because
208
208
*
209
- * * snapshot_global_csn is always rounded up to next second. So that is
210
- * okay if call to GlobalSnapshotMapXmin() with later global_csn will
211
- * succeed first -- it anyway will be taken into account for a next
209
+ * * We already hold our xmin in MyPgXact, so our snapshot will not be
210
+ * harmed even though ProcArrayLock is released.
211
+ *
212
+ * * snapshot_global_csn is always pessmistically rounded up to the next
212
213
* second.
213
214
*
215
+ * * For performance reasons, xmin value for particular second is filled
216
+ * only once. Because of that instead of writing to buffer just our
217
+ * xmin (which is enough for our snapshot), we bump oldestXmin there --
218
+ * it mitigates the possibility of damaging someone else's snapshot by
219
+ * writing to the buffer too advanced value in case of slowness of
220
+ * another backend who generated csn earlier, but didn't manage to
221
+ * insert it before us.
222
+ *
214
223
* * if GlobalSnapshotMapXmin() founds a gap in several seconds between
215
224
* current call and latest completed call then it should fill that gap
216
- * with latest known values instead of new one. Otherwise it is possible
217
- * (however highly unlikely) that this gap also happend between taking
218
- * snapshot and call to GlobalSnapshotMapXmin() for some backend. And we
219
- * are at risk to fill circullar buffer with oldestXmin's that are
220
- * bigger then they actually were.
225
+ * with latest known values instead of new one. Otherwise it is
226
+ * possible (however highly unlikely) that this gap also happend
227
+ * between taking snapshot and call to GlobalSnapshotMapXmin() for some
228
+ * backend. And we are at risk to fill circullar buffer with
229
+ * oldestXmin's that are bigger then they actually were.
221
230
*/
222
231
void
223
232
GlobalSnapshotMapXmin (GlobalCSN snapshot_global_csn )
@@ -233,10 +242,7 @@ GlobalSnapshotMapXmin(GlobalCSN snapshot_global_csn)
233
242
Assert (gsXidMap != NULL );
234
243
235
244
/*
236
- * We don't have guarantee that process who called us first for this
237
- * csn_seconds is actually one who took snapshot firt in this second.
238
- * So just round up global_csn to the next second -- snapshots for next
239
- * second would have oldestXmin greater or equal then ours anyway.
245
+ * Round up global_csn to the next second -- pessimistically and safely.
240
246
*/
241
247
csn_seconds = (snapshot_global_csn / NSECS_PER_SEC + 1 );
242
248
@@ -290,16 +296,15 @@ GlobalSnapshotMapXmin(GlobalCSN snapshot_global_csn)
290
296
291
297
/* Fill new entry with current_oldest_xmin */
292
298
gsXidMap -> xmin_by_second [offset ] = current_oldest_xmin ;
293
- offset = (offset + gsXidMap -> size - 1 ) % gsXidMap -> size ;
294
299
295
300
/*
296
301
* If we have gap then fill it with previous_oldest_xmin for reasons
297
302
* outlined in comment above this function.
298
303
*/
299
304
for (i = 1 ; i < gap ; i ++ )
300
305
{
301
- gsXidMap -> xmin_by_second [offset ] = previous_oldest_xmin ;
302
306
offset = (offset + gsXidMap -> size - 1 ) % gsXidMap -> size ;
307
+ gsXidMap -> xmin_by_second [offset ] = previous_oldest_xmin ;
303
308
}
304
309
305
310
oldest_deferred_xmin =
@@ -309,8 +314,11 @@ GlobalSnapshotMapXmin(GlobalCSN snapshot_global_csn)
309
314
310
315
/*
311
316
* Advance procArray->global_snapshot_xmin after we released
312
- * GlobalSnapshotXidMapLock.
317
+ * GlobalSnapshotXidMapLock. Since we gather not xmin but oldestXmin, it
318
+ * never goes backwards regardless of how slow we can do that.
313
319
*/
320
+ Assert (TransactionIdFollowsOrEquals (oldest_deferred_xmin ,
321
+ ProcArrayGetGlobalSnapshotXmin ()));
314
322
ProcArraySetGlobalSnapshotXmin (oldest_deferred_xmin );
315
323
}
316
324
@@ -554,7 +562,7 @@ XidInvisibleInGlobalSnapshot(TransactionId xid, Snapshot snapshot)
554
562
* Functions to handle distributed commit on transaction coordinator:
555
563
* GlobalSnapshotPrepareCurrent() / GlobalSnapshotAssignCsnCurrent().
556
564
* Correspoding functions for remote nodes are defined in twophase.c:
557
- * pg_global_snaphot_prepare/pg_global_snaphot_assign .
565
+ * pg_global_snapshot_prepare/pg_global_snapshot_assign .
558
566
*****************************************************************************/
559
567
560
568
@@ -594,7 +602,7 @@ GlobalSnapshotPrepareCurrent()
594
602
*
595
603
* Asign GlobalCSN for currently active transaction. GlobalCSN is supposedly
596
604
* maximal among of values returned by GlobalSnapshotPrepareCurrent and
597
- * pg_global_snaphot_prepare .
605
+ * pg_global_snapshot_prepare .
598
606
*/
599
607
void
600
608
GlobalSnapshotAssignCsnCurrent (GlobalCSN global_csn )
@@ -609,7 +617,7 @@ GlobalSnapshotAssignCsnCurrent(GlobalCSN global_csn)
609
617
if (!GlobalCSNIsNormal (global_csn ))
610
618
ereport (ERROR ,
611
619
(errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
612
- errmsg ("pg_global_snaphot_assign expects normal global_csn" )));
620
+ errmsg ("pg_global_snapshot_assign expects normal global_csn" )));
613
621
614
622
/* Skip emtpty transactions */
615
623
if (!TransactionIdIsValid (GetCurrentTransactionIdIfAny ()))
@@ -633,7 +641,7 @@ GlobalSnapshotAssignCsnCurrent(GlobalCSN global_csn)
633
641
* proc->assignedGlobalCsn to GlobalCSNLog.
634
642
*
635
643
* Same rules applies to global transaction, except that global_csn is already
636
- * assigned by GlobalSnapshotAssignCsnCurrent/pg_global_snaphot_assign and
644
+ * assigned by GlobalSnapshotAssignCsnCurrent/pg_global_snapshot_assign and
637
645
* GlobalSnapshotPrecommit is basically no-op.
638
646
*
639
647
* GlobalSnapshotAbort is slightly different comparing to commit because abort
0 commit comments