Skip to content

Commit d172b71

Browse files
committed
Use atomic access for SlruShared->latest_page_number
The new concurrency model proposed for slru.c to improve performance does not include any single lock that would coordinate processes doing concurrent reads/writes on SlruShared->latest_page_number. We can instead use atomic reads and writes for that variable. Author: Dilip Kumar <dilipbalaut@gmail.com> Reviewed-by: Andrey M. Borodin <x4mmm@yandex-team.ru> Discussion: https://postgr.es/m/CAFiTN-vzDvNz=ExGXz6gdyjtzGixKSqs0mKHMmaQ8sOSEFZ33A@mail.gmail.com
1 parent b83033c commit d172b71

File tree

5 files changed

+53
-32
lines changed

5 files changed

+53
-32
lines changed

src/backend/access/transam/clog.c

+1-5
Original file line numberDiff line numberDiff line change
@@ -766,14 +766,10 @@ StartupCLOG(void)
766766
TransactionId xid = XidFromFullTransactionId(TransamVariables->nextXid);
767767
int64 pageno = TransactionIdToPage(xid);
768768

769-
LWLockAcquire(XactSLRULock, LW_EXCLUSIVE);
770-
771769
/*
772770
* Initialize our idea of the latest page number.
773771
*/
774-
XactCtl->shared->latest_page_number = pageno;
775-
776-
LWLockRelease(XactSLRULock);
772+
pg_atomic_write_u64(&XactCtl->shared->latest_page_number, pageno);
777773
}
778774

779775
/*

src/backend/access/transam/commit_ts.c

+3-4
Original file line numberDiff line numberDiff line change
@@ -689,9 +689,7 @@ ActivateCommitTs(void)
689689
/*
690690
* Re-Initialize our idea of the latest page number.
691691
*/
692-
LWLockAcquire(CommitTsSLRULock, LW_EXCLUSIVE);
693-
CommitTsCtl->shared->latest_page_number = pageno;
694-
LWLockRelease(CommitTsSLRULock);
692+
pg_atomic_write_u64(&CommitTsCtl->shared->latest_page_number, pageno);
695693

696694
/*
697695
* If CommitTs is enabled, but it wasn't in the previous server run, we
@@ -1006,7 +1004,8 @@ commit_ts_redo(XLogReaderState *record)
10061004
* During XLOG replay, latest_page_number isn't set up yet; insert a
10071005
* suitable value to bypass the sanity test in SimpleLruTruncate.
10081006
*/
1009-
CommitTsCtl->shared->latest_page_number = trunc->pageno;
1007+
pg_atomic_write_u64(&CommitTsCtl->shared->latest_page_number,
1008+
trunc->pageno);
10101009

10111010
SimpleLruTruncate(CommitTsCtl, trunc->pageno);
10121011
}

src/backend/access/transam/multixact.c

+17-11
Original file line numberDiff line numberDiff line change
@@ -2017,13 +2017,15 @@ StartupMultiXact(void)
20172017
* Initialize offset's idea of the latest page number.
20182018
*/
20192019
pageno = MultiXactIdToOffsetPage(multi);
2020-
MultiXactOffsetCtl->shared->latest_page_number = pageno;
2020+
pg_atomic_write_u64(&MultiXactOffsetCtl->shared->latest_page_number,
2021+
pageno);
20212022

20222023
/*
20232024
* Initialize member's idea of the latest page number.
20242025
*/
20252026
pageno = MXOffsetToMemberPage(offset);
2026-
MultiXactMemberCtl->shared->latest_page_number = pageno;
2027+
pg_atomic_write_u64(&MultiXactMemberCtl->shared->latest_page_number,
2028+
pageno);
20272029
}
20282030

20292031
/*
@@ -2047,14 +2049,15 @@ TrimMultiXact(void)
20472049
oldestMXactDB = MultiXactState->oldestMultiXactDB;
20482050
LWLockRelease(MultiXactGenLock);
20492051

2050-
/* Clean up offsets state */
2051-
LWLockAcquire(MultiXactOffsetSLRULock, LW_EXCLUSIVE);
2052-
20532052
/*
20542053
* (Re-)Initialize our idea of the latest page number for offsets.
20552054
*/
20562055
pageno = MultiXactIdToOffsetPage(nextMXact);
2057-
MultiXactOffsetCtl->shared->latest_page_number = pageno;
2056+
pg_atomic_write_u64(&MultiXactOffsetCtl->shared->latest_page_number,
2057+
pageno);
2058+
2059+
/* Clean up offsets state */
2060+
LWLockAcquire(MultiXactOffsetSLRULock, LW_EXCLUSIVE);
20582061

20592062
/*
20602063
* Zero out the remainder of the current offsets page. See notes in
@@ -2081,14 +2084,16 @@ TrimMultiXact(void)
20812084

20822085
LWLockRelease(MultiXactOffsetSLRULock);
20832086

2084-
/* And the same for members */
2085-
LWLockAcquire(MultiXactMemberSLRULock, LW_EXCLUSIVE);
2086-
20872087
/*
2088+
* And the same for members.
2089+
*
20882090
* (Re-)Initialize our idea of the latest page number for members.
20892091
*/
20902092
pageno = MXOffsetToMemberPage(offset);
2091-
MultiXactMemberCtl->shared->latest_page_number = pageno;
2093+
pg_atomic_write_u64(&MultiXactMemberCtl->shared->latest_page_number,
2094+
pageno);
2095+
2096+
LWLockAcquire(MultiXactMemberSLRULock, LW_EXCLUSIVE);
20922097

20932098
/*
20942099
* Zero out the remainder of the current members page. See notes in
@@ -3333,7 +3338,8 @@ multixact_redo(XLogReaderState *record)
33333338
* SimpleLruTruncate.
33343339
*/
33353340
pageno = MultiXactIdToOffsetPage(xlrec.endTruncOff);
3336-
MultiXactOffsetCtl->shared->latest_page_number = pageno;
3341+
pg_atomic_write_u64(&MultiXactOffsetCtl->shared->latest_page_number,
3342+
pageno);
33373343
PerformOffsetsTruncation(xlrec.startTruncOff, xlrec.endTruncOff);
33383344

33393345
LWLockRelease(MultiXactTruncationLock);

src/backend/access/transam/slru.c

+28-11
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
* per-buffer LWLocks that synchronize I/O for each buffer. The control lock
1818
* must be held to examine or modify any shared state. A process that is
1919
* reading in or writing out a page buffer does not hold the control lock,
20-
* only the per-buffer lock for the buffer it is working on.
20+
* only the per-buffer lock for the buffer it is working on. One exception
21+
* is latest_page_number, which is read and written using atomic ops.
2122
*
2223
* "Holding the control lock" means exclusive lock in all cases except for
2324
* SimpleLruReadPage_ReadOnly(); see comments for SlruRecentlyUsed() for
@@ -239,8 +240,7 @@ SimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns,
239240
shared->lsn_groups_per_page = nlsns;
240241

241242
shared->cur_lru_count = 0;
242-
243-
/* shared->latest_page_number will be set later */
243+
pg_atomic_write_u64(&shared->latest_page_number, 0);
244244

245245
shared->slru_stats_idx = pgstat_get_slru_index(name);
246246

@@ -329,8 +329,15 @@ SimpleLruZeroPage(SlruCtl ctl, int64 pageno)
329329
/* Set the LSNs for this new page to zero */
330330
SimpleLruZeroLSNs(ctl, slotno);
331331

332-
/* Assume this page is now the latest active page */
333-
shared->latest_page_number = pageno;
332+
/*
333+
* Assume this page is now the latest active page.
334+
*
335+
* Note that because both this routine and SlruSelectLRUPage run with
336+
* ControlLock held, it is not possible for this to be zeroing a page that
337+
* SlruSelectLRUPage is going to evict simultaneously. Therefore, there's
338+
* no memory barrier here.
339+
*/
340+
pg_atomic_write_u64(&shared->latest_page_number, pageno);
334341

335342
/* update the stats counter of zeroed pages */
336343
pgstat_count_slru_page_zeroed(shared->slru_stats_idx);
@@ -1113,9 +1120,17 @@ SlruSelectLRUPage(SlruCtl ctl, int64 pageno)
11131120
shared->page_lru_count[slotno] = cur_count;
11141121
this_delta = 0;
11151122
}
1123+
1124+
/*
1125+
* If this page is the one most recently zeroed, don't consider it
1126+
* an eviction candidate. See comments in SimpleLruZeroPage for an
1127+
* explanation about the lack of a memory barrier here.
1128+
*/
11161129
this_page_number = shared->page_number[slotno];
1117-
if (this_page_number == shared->latest_page_number)
1130+
if (this_page_number ==
1131+
pg_atomic_read_u64(&shared->latest_page_number))
11181132
continue;
1133+
11191134
if (shared->page_status[slotno] == SLRU_PAGE_VALID)
11201135
{
11211136
if (this_delta > best_valid_delta ||
@@ -1254,7 +1269,6 @@ void
12541269
SimpleLruTruncate(SlruCtl ctl, int64 cutoffPage)
12551270
{
12561271
SlruShared shared = ctl->shared;
1257-
int slotno;
12581272

12591273
/* update the stats counter of truncates */
12601274
pgstat_count_slru_truncate(shared->slru_stats_idx);
@@ -1270,10 +1284,13 @@ SimpleLruTruncate(SlruCtl ctl, int64 cutoffPage)
12701284
restart:
12711285

12721286
/*
1273-
* While we are holding the lock, make an important safety check: the
1274-
* current endpoint page must not be eligible for removal.
1287+
* An important safety check: the current endpoint page must not be
1288+
* eligible for removal. This check is just a backstop against wraparound
1289+
* bugs elsewhere in SLRU handling, so we don't care if we read a slightly
1290+
* outdated value; therefore we don't add a memory barrier.
12751291
*/
1276-
if (ctl->PagePrecedes(shared->latest_page_number, cutoffPage))
1292+
if (ctl->PagePrecedes(pg_atomic_read_u64(&shared->latest_page_number),
1293+
cutoffPage))
12771294
{
12781295
LWLockRelease(shared->ControlLock);
12791296
ereport(LOG,
@@ -1282,7 +1299,7 @@ SimpleLruTruncate(SlruCtl ctl, int64 cutoffPage)
12821299
return;
12831300
}
12841301

1285-
for (slotno = 0; slotno < shared->num_slots; slotno++)
1302+
for (int slotno = 0; slotno < shared->num_slots; slotno++)
12861303
{
12871304
if (shared->page_status[slotno] == SLRU_PAGE_EMPTY)
12881305
continue;

src/include/access/slru.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ typedef enum
4949

5050
/*
5151
* Shared-memory state
52+
*
53+
* ControlLock is used to protect access to the other fields, except
54+
* latest_page_number, which uses atomics; see comment in slru.c.
5255
*/
5356
typedef struct SlruSharedData
5457
{
@@ -95,7 +98,7 @@ typedef struct SlruSharedData
9598
* this is not critical data, since we use it only to avoid swapping out
9699
* the latest page.
97100
*/
98-
int64 latest_page_number;
101+
pg_atomic_uint64 latest_page_number;
99102

100103
/* SLRU's index for statistics purposes (might not be unique) */
101104
int slru_stats_idx;

0 commit comments

Comments
 (0)