/* * 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; }
/* * 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; }