diff --git a/src/backend/access/transam/clog.c b/src/backend/access/transam/clog.c index 3a58f1e..f19b5ce 100644 --- a/src/backend/access/transam/clog.c +++ b/src/backend/access/transam/clog.c @@ -262,9 +262,16 @@ TransactionIdSetPageStatus(TransactionId xid, int nsubxids, status == TRANSACTION_STATUS_ABORTED || (status == TRANSACTION_STATUS_SUB_COMMITTED && !TransactionIdIsValid(xid))); - LWLockAcquire(CLogControlLock, LW_EXCLUSIVE); - /* + * Acquire ClogCtl Share lock while we set commit bits, if possible. + * + * This is safe because all callers of these routines always + * check the xid is complete first, either by checking + * TransactionIdIsInProgress() or by waiting for the transaction + * to complete via the lock manager. As a result, + * anybody checking visibility of our xids will never get as far as + * checking the status here at the same time we are setting it. + * * If we're doing an async commit (ie, lsn is valid), then we must wait * for any active write on the page slot to complete. Otherwise our * update could reach disk in that write, which will not do since we @@ -273,7 +280,17 @@ TransactionIdSetPageStatus(TransactionId xid, int nsubxids, * write-busy, since we don't care if the update reaches disk sooner than * we think. */ - slotno = SimpleLruReadPage(ClogCtl, pageno, XLogRecPtrIsInvalid(lsn), xid); + slotno = SimpleLruReadPage_optShared(ClogCtl, pageno, XLogRecPtrIsInvalid(lsn), xid); + + /* + * We might have problems with concurrent writes, but we solve + * that by acquiring an exclusive page lock to serialize commits and ensure + * there is a memory barrier between commit writes. Note that we + * acquire and release the lock for each page, to ensure that + * transactions with huge numbers of subxacts don't hold up everyone else. + */ + if (!InRecovery) + LWLockAcquire(ClogCtl->shared->buffer_locks[slotno], LW_EXCLUSIVE); /* * Set the main transaction id, if any. @@ -311,6 +328,9 @@ TransactionIdSetPageStatus(TransactionId xid, int nsubxids, ClogCtl->shared->page_dirty[slotno] = true; + if (!InRecovery) + LWLockRelease(ClogCtl->shared->buffer_locks[slotno]); + LWLockRelease(CLogControlLock); } diff --git a/src/backend/access/transam/slru.c b/src/backend/access/transam/slru.c index 5fcea11..38440f4 100644 --- a/src/backend/access/transam/slru.c +++ b/src/backend/access/transam/slru.c @@ -449,6 +449,13 @@ SimpleLruReadPage(SlruCtl ctl, int pageno, bool write_ok, int SimpleLruReadPage_ReadOnly(SlruCtl ctl, int pageno, TransactionId xid) { + return SimpleLruReadPage_optShared(ctl, pageno, true, xid); +} + +int +SimpleLruReadPage_optShared(SlruCtl ctl, int pageno, bool write_ok, + TransactionId xid) +{ SlruShared shared = ctl->shared; int slotno; @@ -460,7 +467,9 @@ SimpleLruReadPage_ReadOnly(SlruCtl ctl, int pageno, TransactionId xid) { if (shared->page_number[slotno] == pageno && shared->page_status[slotno] != SLRU_PAGE_EMPTY && - shared->page_status[slotno] != SLRU_PAGE_READ_IN_PROGRESS) + shared->page_status[slotno] != SLRU_PAGE_READ_IN_PROGRESS && + (write_ok || + shared->page_status[slotno] != SLRU_PAGE_WRITE_IN_PROGRESS)) { /* See comments for SlruRecentlyUsed macro */ SlruRecentlyUsed(shared, slotno); @@ -472,7 +481,7 @@ SimpleLruReadPage_ReadOnly(SlruCtl ctl, int pageno, TransactionId xid) LWLockRelease(shared->ControlLock); LWLockAcquire(shared->ControlLock, LW_EXCLUSIVE); - return SimpleLruReadPage(ctl, pageno, true, xid); + return SimpleLruReadPage(ctl, pageno, write_ok, xid); } /* diff --git a/src/include/access/slru.h b/src/include/access/slru.h index 9c7f019..256b03d 100644 --- a/src/include/access/slru.h +++ b/src/include/access/slru.h @@ -142,6 +142,8 @@ extern int SimpleLruReadPage(SlruCtl ctl, int pageno, bool write_ok, TransactionId xid); extern int SimpleLruReadPage_ReadOnly(SlruCtl ctl, int pageno, TransactionId xid); +extern int SimpleLruReadPage_optShared(SlruCtl ctl, int pageno, + bool write_ok, TransactionId xid); extern void SimpleLruWritePage(SlruCtl ctl, int slotno); extern void SimpleLruFlush(SlruCtl ctl, bool checkpoint); extern void SimpleLruTruncate(SlruCtl ctl, int cutoffPage);