On Sat, Jan 21, 2012 at 1:57 PM, Robert Haas <[email protected]> wrote:
> On Fri, Jan 20, 2012 at 10:44 AM, Robert Haas <[email protected]> wrote:
>>>> D'oh. You're right. Looks like I accidentally tried to apply this to
>>>> the 9.1 sources. Sigh...
>>>
>>> No worries. It's Friday.
>
> Server passed 'make check' with this patch, but when I tried to fire
> it up for some test runs, it fell over with:
>
> FATAL: no more LWLockIds available
>
> I assume that it must be dependent on the config settings used. Here are
> mine:
>
> shared_buffers = 8GB
> maintenance_work_mem = 1GB
> synchronous_commit = off
> checkpoint_segments = 300
> checkpoint_timeout = 15min
> checkpoint_completion_target = 0.9
> wal_writer_delay = 20ms
Yes, it was. Sorry about that. New version attached, retesting while
you read this.
--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
diff --git a/src/backend/access/transam/clog.c b/src/backend/access/transam/clog.c
index 69b6ef3..6ff6894 100644
--- a/src/backend/access/transam/clog.c
+++ b/src/backend/access/transam/clog.c
@@ -37,6 +37,7 @@
#include "access/transam.h"
#include "miscadmin.h"
#include "pg_trace.h"
+#include "utils/snapmgr.h"
/*
* Defines for CLOG page sizes. A page is the same BLCKSZ as is used
@@ -70,10 +71,17 @@
/*
* Link to shared-memory data structures for CLOG control
+ *
+ * As of 9.2, we have 2 structures for commit log data.
+ * ClogCtl manages the main read/write part of the commit log, while
+ * the ClogHistoryCtl manages the now read-only, older part. ClogHistory
+ * removes contention from the path of transaction commits.
*/
static SlruCtlData ClogCtlData;
+static SlruCtlData ClogHistoryCtlData;
-#define ClogCtl (&ClogCtlData)
+#define ClogCtl (&ClogCtlData)
+#define ClogHistoryCtl (&ClogHistoryCtlData)
static int ZeroCLOGPage(int pageno, bool writeXlog);
@@ -296,6 +304,10 @@ TransactionIdSetPageStatus(TransactionId xid, int nsubxids,
/* ... then the main transaction */
TransactionIdSetStatusBit(xid, status, lsn, slotno);
+
+ /* When we commit advance ClogCtl's shared RecentXminPageno if needed */
+ if (ClogCtl->shared->RecentXminPageno < TransactionIdToPage(RecentXmin))
+ ClogCtl->shared->RecentXminPageno = TransactionIdToPage(RecentXmin);
}
/* Set the subtransactions */
@@ -387,6 +399,8 @@ TransactionIdSetStatusBit(TransactionId xid, XidStatus status, XLogRecPtr lsn, i
XidStatus
TransactionIdGetStatus(TransactionId xid, XLogRecPtr *lsn)
{
+ SlruCtl clog = ClogCtl;
+ bool useClogHistory = true;
int pageno = TransactionIdToPage(xid);
int byteno = TransactionIdToByte(xid);
int bshift = TransactionIdToBIndex(xid) * CLOG_BITS_PER_XACT;
@@ -397,15 +411,35 @@ TransactionIdGetStatus(TransactionId xid, XLogRecPtr *lsn)
/* lock is acquired by SimpleLruReadPage_ReadOnly */
- slotno = SimpleLruReadPage_ReadOnly(ClogCtl, pageno, xid);
- byteptr = ClogCtl->shared->page_buffer[slotno] + byteno;
+ /*
+ * Decide whether to use main Clog or read-only ClogHistory.
+ *
+ * Our knowledge of the boundary between the two may be a little out
+ * of date, so if we try Clog and can't find it we need to try again
+ * against ClogHistory.
+ */
+ if (pageno >= ClogCtl->recent_oldest_active_page_number)
+ {
+ slotno = SimpleLruReadPage_ReadOnly(clog, pageno, xid);
+ if (slotno >= 0)
+ useClogHistory = false;
+ }
+
+ if (useClogHistory)
+ {
+ clog = ClogHistoryCtl;
+ slotno = SimpleLruReadPage_ReadOnly(clog, pageno, xid);
+ Assert(slotno >= 0);
+ }
+
+ byteptr = clog->shared->page_buffer[slotno] + byteno;
status = (*byteptr >> bshift) & CLOG_XACT_BITMASK;
lsnindex = GetLSNIndex(slotno, xid);
- *lsn = ClogCtl->shared->group_lsn[lsnindex];
+ *lsn = clog->shared->group_lsn[lsnindex];
- LWLockRelease(CLogControlLock);
+ LWLockRelease(clog->shared->ControlLock);
return status;
}
@@ -445,15 +479,19 @@ CLOGShmemBuffers(void)
Size
CLOGShmemSize(void)
{
- return SimpleLruShmemSize(CLOGShmemBuffers(), CLOG_LSNS_PER_PAGE);
+ /* Reserve shmem for both ClogCtl and ClogHistoryCtl */
+ return SimpleLruShmemSize(2 * CLOGShmemBuffers(), CLOG_LSNS_PER_PAGE);
}
void
CLOGShmemInit(void)
{
ClogCtl->PagePrecedes = CLOGPagePrecedes;
+ ClogHistoryCtl->PagePrecedes = CLOGPagePrecedes;
SimpleLruInit(ClogCtl, "CLOG Ctl", CLOGShmemBuffers(), CLOG_LSNS_PER_PAGE,
CLogControlLock, "pg_clog");
+ SimpleLruInit(ClogHistoryCtl, "CLOG History Ctl", CLOGShmemBuffers(), CLOG_LSNS_PER_PAGE,
+ CLogHistoryControlLock, "pg_clog");
}
/*
@@ -592,6 +630,16 @@ CheckPointCLOG(void)
TRACE_POSTGRESQL_CLOG_CHECKPOINT_START(true);
SimpleLruFlush(ClogCtl, true);
TRACE_POSTGRESQL_CLOG_CHECKPOINT_DONE(true);
+
+ /*
+ * Now that we've written out all dirty buffers the only pages that
+ * will get dirty again will be pages with active transactions on them.
+ * So we can move forward the oldest_active_page_number and allow
+ * read only operations via ClogHistoryCtl.
+ */
+ LWLockAcquire(CLogControlLock, LW_EXCLUSIVE);
+ ClogCtl->shared->oldest_active_page_number = ClogCtl->shared->RecentXminPageno;
+ LWLockRelease(CLogControlLock);
}
diff --git a/src/backend/access/transam/slru.c b/src/backend/access/transam/slru.c
index 30538ff..2cebdf9 100644
--- a/src/backend/access/transam/slru.c
+++ b/src/backend/access/transam/slru.c
@@ -188,6 +188,9 @@ SimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns,
shared->cur_lru_count = 0;
+ shared->oldest_active_page_number = -1;
+ shared->RecentXminPageno = -1;
+
/* shared->latest_page_number will be set later */
ptr = (char *) shared;
@@ -476,6 +479,16 @@ SimpleLruReadPage_ReadOnly(SlruCtl ctl, int pageno, TransactionId xid)
LWLockRelease(shared->ControlLock);
LWLockAcquire(shared->ControlLock, LW_EXCLUSIVE);
+ /* update local state while we have the lock */
+ ctl->recent_oldest_active_page_number = shared->oldest_active_page_number;
+
+ /* Check if our cached boundary information was out of date */
+ if (pageno < ctl->recent_oldest_active_page_number)
+ {
+ LWLockRelease(shared->ControlLock);
+ return -1;
+ }
+
return SimpleLruReadPage(ctl, pageno, true, xid);
}
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index cc41568..353f101 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -170,8 +170,8 @@ NumLWLocks(void)
/* proc.c needs one for each backend or auxiliary process */
numLocks += MaxBackends + NUM_AUXILIARY_PROCS;
- /* clog.c needs one per CLOG buffer */
- numLocks += CLOGShmemBuffers();
+ /* clog.c needs two per CLOG buffer */
+ numLocks += 2 * CLOGShmemBuffers();
/* subtrans.c needs one per SubTrans buffer */
numLocks += NUM_SUBTRANS_BUFFERS;
diff --git a/src/include/access/slru.h b/src/include/access/slru.h
index 41cd484..f7b0d87 100644
--- a/src/include/access/slru.h
+++ b/src/include/access/slru.h
@@ -99,6 +99,15 @@ typedef struct SlruSharedData
* the latest page.
*/
int latest_page_number;
+
+ /*
+ * RecentXminPageno is the oldest page that any active
+ * transaction would ever wish to write to.
+ * oldest_active_page_number is the oldest dirty page, or the
+ * RecentXminPageno, whichever is lower. We advance oldest at checkpoint.
+ */
+ int oldest_active_page_number;
+ int RecentXminPageno;
} SlruSharedData;
typedef SlruSharedData *SlruShared;
@@ -125,6 +134,11 @@ typedef struct SlruCtlData
bool (*PagePrecedes) (int, int);
/*
+ * Local cached value of oldest_active_page_number.
+ */
+ int recent_oldest_active_page_number;
+
+ /*
* Dir is set during SimpleLruInit and does not change thereafter. Since
* it's always the same, it doesn't need to be in shared memory.
*/
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index df3df29..3d8838f 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -79,6 +79,7 @@ typedef enum LWLockId
SerializablePredicateLockListLock,
OldSerXidLock,
SyncRepLock,
+ CLogHistoryControlLock,
/* Individual lock IDs end here */
FirstBufMappingLock,
FirstLockMgrLock = FirstBufMappingLock + NUM_BUFFER_PARTITIONS,
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers