示例#1
0
/*
 * ForwardFsyncRequest
 *		Forward a file-fsync request from a backend to the checkpointer
 *
 * Whenever a backend is compelled to write directly to a relation
 * (which should be seldom, if the background writer is getting its job done),
 * the backend calls this routine to pass over knowledge that the relation
 * is dirty and must be fsync'd before next checkpoint.  We also use this
 * opportunity to count such writes for statistical purposes.
 *
 * This functionality is only supported for regular (not backend-local)
 * relations, so the rnode argument is intentionally RelFileNode not
 * RelFileNodeBackend.
 *
 * segno specifies which segment (not block!) of the relation needs to be
 * fsync'd.  (Since the valid range is much less than BlockNumber, we can
 * use high values for special flags; that's all internal to md.c, which
 * see for details.)
 *
 * To avoid holding the lock for longer than necessary, we normally write
 * to the requests[] queue without checking for duplicates.  The checkpointer
 * will have to eliminate dups internally anyway.  However, if we discover
 * that the queue is full, we make a pass over the entire queue to compact
 * it.  This is somewhat expensive, but the alternative is for the backend
 * to perform its own fsync, which is far more expensive in practice.  It
 * is theoretically possible a backend fsync might still be necessary, if
 * the queue is full and contains no duplicate entries.  In that case, we
 * let the backend know by returning false.
 */
bool
ForwardFsyncRequest(RelFileNode rnode, ForkNumber forknum, BlockNumber segno)
{
	CheckpointerRequest *request;
	bool		too_full;

	if (!IsUnderPostmaster)
		return false;			/* probably shouldn't even get here */

	if (AmCheckpointerProcess())
		elog(ERROR, "ForwardFsyncRequest must not be called in checkpointer");

	LWLockAcquire(CheckpointerCommLock, LW_EXCLUSIVE);

	/* Count all backend writes regardless of if they fit in the queue */
	if (!AmBackgroundWriterProcess())
		CheckpointerShmem->num_backend_writes++;

	/*
	 * If the checkpointer isn't running or the request queue is full, the
	 * backend will have to perform its own fsync request.  But before forcing
	 * that to happen, we can try to compact the request queue.
	 */
	if (CheckpointerShmem->checkpointer_pid == 0 ||
		(CheckpointerShmem->num_requests >= CheckpointerShmem->max_requests &&
		 !CompactCheckpointerRequestQueue()))
	{
		/*
		 * Count the subset of writes where backends have to do their own
		 * fsync
		 */
		if (!AmBackgroundWriterProcess())
			CheckpointerShmem->num_backend_fsync++;
		LWLockRelease(CheckpointerCommLock);
		return false;
	}

	/* OK, insert request */
	request = &CheckpointerShmem->requests[CheckpointerShmem->num_requests++];
	request->rnode = rnode;
	request->forknum = forknum;
	request->segno = segno;

	/* If queue is more than half full, nudge the checkpointer to empty it */
	too_full = (CheckpointerShmem->num_requests >=
				CheckpointerShmem->max_requests / 2);

	LWLockRelease(CheckpointerCommLock);

	/* ... but not till after we release the lock */
	if (too_full && ProcGlobal->checkpointerLatch)
		SetLatch(ProcGlobal->checkpointerLatch);

	return true;
}
示例#2
0
/*
 * ForwardFsyncRequest
 *		Forward a file-fsync request from a backend to the bgwriter
 *
 * Whenever a backend is compelled to write directly to a relation
 * (which should be seldom, if the bgwriter is getting its job done),
 * the backend calls this routine to pass over knowledge that the relation
 * is dirty and must be fsync'd before next checkpoint.  We also use this
 * opportunity to count such writes for statistical purposes.
 *
 * segno specifies which segment (not block!) of the relation needs to be
 * fsync'd.  (Since the valid range is much less than BlockNumber, we can
 * use high values for special flags; that's all internal to md.c, which
 * see for details.)
 *
 * To avoid holding the lock for longer than necessary, we normally write
 * to the requests[] queue without checking for duplicates.  The bgwriter
 * will have to eliminate dups internally anyway.  However, if we discover
 * that the queue is full, we make a pass over the entire queue to compact
 * it.	This is somewhat expensive, but the alternative is for the backend
 * to perform its own fsync, which is far more expensive in practice.  It
 * is theoretically possible a backend fsync might still be necessary, if
 * the queue is full and contains no duplicate entries.  In that case, we
 * let the backend know by returning false.
 */
bool
ForwardFsyncRequest(RelFileNodeBackend rnode, ForkNumber forknum,
					BlockNumber segno)
{
	BgWriterRequest *request;

	if (!IsUnderPostmaster)
		return false;			/* probably shouldn't even get here */

	if (am_checkpointer)
		elog(ERROR, "ForwardFsyncRequest must not be called in bgwriter");

	LWLockAcquire(BgWriterCommLock, LW_EXCLUSIVE);

	/* Count all backend writes regardless of if they fit in the queue */
	BgWriterShmem->num_backend_writes++;

	/*
	 * If the background writer isn't running or the request queue is full,
	 * the backend will have to perform its own fsync request.	But before
	 * forcing that to happen, we can try to compact the background writer
	 * request queue.
	 */
	if (BgWriterShmem->checkpointer_pid == 0 ||
		(BgWriterShmem->num_requests >= BgWriterShmem->max_requests
		 && !CompactCheckpointerRequestQueue()))
	{
		/*
		 * Count the subset of writes where backends have to do their own
		 * fsync
		 */
		BgWriterShmem->num_backend_fsync++;
		LWLockRelease(BgWriterCommLock);
		return false;
	}
	request = &BgWriterShmem->requests[BgWriterShmem->num_requests++];
	request->rnode = rnode;
	request->forknum = forknum;
	request->segno = segno;
	LWLockRelease(BgWriterCommLock);
	return true;
}