/* * Wake up (using latch) the specified logical replication worker. * * Caller must hold lock, else worker->proc could change under us. */ void logicalrep_worker_wakeup_ptr(LogicalRepWorker *worker) { Assert(LWLockHeldByMe(LogicalRepWorkerLock)); SetLatch(&worker->proc->procLatch); }
/* * Return the hash entry for a tablespace. */ static TablespaceDirEntry PersistentTablespace_FindEntryUnderLock( Oid tablespaceOid) { bool found; TablespaceDirEntry tablespaceDirEntry; TablespaceDirEntryKey key; Assert(LWLockHeldByMe(TablespaceHashLock)); if (persistentTablespaceSharedHashTable == NULL) elog(PANIC, "Persistent tablespace information shared-memory not setup"); key.tablespaceOid = tablespaceOid; tablespaceDirEntry = (TablespaceDirEntry) hash_search(persistentTablespaceSharedHashTable, (void *) &key, HASH_FIND, &found); if (!found) return NULL; return tablespaceDirEntry; }
static FilespaceDirEntry PersistentFilespace_CreateDirUnderLock( Oid filespaceOid) { bool found; FilespaceDirEntry filespaceDirEntry; FilespaceDirEntryKey key; Assert(LWLockHeldByMe(FilespaceHashLock)); if (persistentFilespaceSharedHashTable == NULL) elog(PANIC, "Persistent filespace information shared-memory not setup"); key.filespaceOid = filespaceOid; filespaceDirEntry = (FilespaceDirEntry) hash_search(persistentFilespaceSharedHashTable, (void *) &key, HASH_ENTER_NULL, &found); if (filespaceDirEntry == NULL) elog(ERROR, "Out of shared-memory for persistent filespaces"); filespaceDirEntry->state = 0; filespaceDirEntry->persistentSerialNum = 0; MemSet(&filespaceDirEntry->persistentTid, 0, sizeof(ItemPointerData)); return filespaceDirEntry; }
/* * Return the hash entry for a filespace. */ static FilespaceDirEntry PersistentFilespace_FindDirUnderLock( Oid filespaceOid) { bool found; FilespaceDirEntry filespaceDirEntry; FilespaceDirEntryKey key; Assert(LWLockHeldByMe(FilespaceHashLock)); if (persistentFilespaceSharedHashTable == NULL) elog(PANIC, "Persistent filespace information shared-memory not setup"); key.filespaceOid = filespaceOid; filespaceDirEntry = (FilespaceDirEntry) hash_search(persistentFilespaceSharedHashTable, (void *) &key, HASH_FIND, &found); if (!found) return NULL; return filespaceDirEntry; }
void PersistentStore_FreeTuple( PersistentStoreData *storeData, PersistentStoreSharedData *storeSharedData, ItemPointer persistentTid, /* TID of the stored tuple. */ Datum *freeValues, bool flushToXLog) /* When true, the XLOG record for this change will be flushed to disk. */ { Relation persistentRel; XLogRecPtr xlogEndLoc; /* The end location of the UPDATE XLOG record. */ Assert( LWLockHeldByMe(PersistentObjLock) ); #ifdef USE_ASSERT_CHECKING if (storeSharedData == NULL || !PersistentStoreSharedData_EyecatcherIsValid(storeSharedData)) elog(ERROR, "Persistent store shared-memory not valid"); #endif if (Debug_persistent_store_print) elog(PersistentStore_DebugPrintLevel(), "PersistentStore_FreeTuple: Going to free tuple at TID %s ('%s', shared data %p)", ItemPointerToString(persistentTid), storeData->tableName, storeSharedData); Assert(ItemPointerIsValid(persistentTid)); persistentRel = (*storeData->openRel)(); simple_heap_delete_xid(persistentRel, persistentTid, FrozenTransactionId); /* * XLOG location of the UPDATE tuple's XLOG record. */ xlogEndLoc = XLogLastInsertEndLoc(); (*storeData->closeRel)(persistentRel); storeSharedData->inUseCount--; if (flushToXLog) { XLogFlush(xlogEndLoc); XLogRecPtr_Zero(&nowaitXLogEndLoc); } else nowaitXLogEndLoc = xlogEndLoc; }
/* * Count the number of registered (not necessarily running) sync workers * for a subscription. */ int logicalrep_sync_worker_count(Oid subid) { int i; int res = 0; Assert(LWLockHeldByMe(LogicalRepWorkerLock)); /* Search for attached worker for a given subscription id. */ for (i = 0; i < max_logical_replication_workers; i++) { LogicalRepWorker *w = &LogicalRepCtx->workers[i]; if (w->subid == subid && OidIsValid(w->relid)) res++; } return res; }
/* * Similar to logicalrep_worker_find(), but returns list of all workers for * the subscription, instead just one. */ List * logicalrep_workers_find(Oid subid, bool only_running) { int i; List *res = NIL; Assert(LWLockHeldByMe(LogicalRepWorkerLock)); /* Search for attached worker for a given subscription id. */ for (i = 0; i < max_logical_replication_workers; i++) { LogicalRepWorker *w = &LogicalRepCtx->workers[i]; if (w->in_use && w->subid == subid && (!only_running || w->proc)) res = lappend(res, w); } return res; }
static void PersistentTablespace_RemoveEntryUnderLock( TablespaceDirEntry tablespaceDirEntry) { TablespaceDirEntry removeTablespaceDirEntry; Assert(LWLockHeldByMe(TablespaceHashLock)); if (persistentTablespaceSharedHashTable == NULL) elog(PANIC, "Persistent tablespace information shared-memory not setup"); removeTablespaceDirEntry = (TablespaceDirEntry) hash_search(persistentTablespaceSharedHashTable, (void *) &tablespaceDirEntry->key, HASH_REMOVE, NULL); if (removeTablespaceDirEntry == NULL) elog(ERROR, "Trying to delete entry that does not exist"); }
/* * Walks the workers array and searches for one that matches given * subscription id and relid. */ LogicalRepWorker * logicalrep_worker_find(Oid subid, Oid relid, bool only_running) { int i; LogicalRepWorker *res = NULL; Assert(LWLockHeldByMe(LogicalRepWorkerLock)); /* Search for attached worker for a given subscription id. */ for (i = 0; i < max_logical_replication_workers; i++) { LogicalRepWorker *w = &LogicalRepCtx->workers[i]; if (w->in_use && w->subid == subid && w->relid == relid && (!only_running || w->proc)) { res = w; break; } } return res; }
/* * CompactCheckpointerRequestQueue * Remove duplicates from the request queue to avoid backend fsyncs. * Returns "true" if any entries were removed. * * Although a full fsync request queue is not common, it can lead to severe * performance problems when it does happen. So far, this situation has * only been observed to occur when the system is under heavy write load, * and especially during the "sync" phase of a checkpoint. Without this * logic, each backend begins doing an fsync for every block written, which * gets very expensive and can slow down the whole system. * * Trying to do this every time the queue is full could lose if there * aren't any removable entries. But that should be vanishingly rare in * practice: there's one queue entry per shared buffer. */ static bool CompactCheckpointerRequestQueue(void) { struct CheckpointerSlotMapping { CheckpointerRequest request; int slot; }; int n, preserve_count; int num_skipped = 0; HASHCTL ctl; HTAB *htab; bool *skip_slot; /* must hold CheckpointerCommLock in exclusive mode */ Assert(LWLockHeldByMe(CheckpointerCommLock)); /* Initialize skip_slot array */ skip_slot = palloc0(sizeof(bool) * CheckpointerShmem->num_requests); /* Initialize temporary hash table */ MemSet(&ctl, 0, sizeof(ctl)); ctl.keysize = sizeof(CheckpointerRequest); ctl.entrysize = sizeof(struct CheckpointerSlotMapping); ctl.hcxt = CurrentMemoryContext; htab = hash_create("CompactCheckpointerRequestQueue", CheckpointerShmem->num_requests, &ctl, HASH_ELEM | HASH_BLOBS | HASH_CONTEXT); /* * The basic idea here is that a request can be skipped if it's followed * by a later, identical request. It might seem more sensible to work * backwards from the end of the queue and check whether a request is * *preceded* by an earlier, identical request, in the hopes of doing less * copying. But that might change the semantics, if there's an * intervening FORGET_RELATION_FSYNC or FORGET_DATABASE_FSYNC request, so * we do it this way. It would be possible to be even smarter if we made * the code below understand the specific semantics of such requests (it * could blow away preceding entries that would end up being canceled * anyhow), but it's not clear that the extra complexity would buy us * anything. */ for (n = 0; n < CheckpointerShmem->num_requests; n++) { CheckpointerRequest *request; struct CheckpointerSlotMapping *slotmap; bool found; /* * We use the request struct directly as a hashtable key. This * assumes that any padding bytes in the structs are consistently the * same, which should be okay because we zeroed them in * CheckpointerShmemInit. Note also that RelFileNode had better * contain no pad bytes. */ request = &CheckpointerShmem->requests[n]; slotmap = hash_search(htab, request, HASH_ENTER, &found); if (found) { /* Duplicate, so mark the previous occurrence as skippable */ skip_slot[slotmap->slot] = true; num_skipped++; } /* Remember slot containing latest occurrence of this request value */ slotmap->slot = n; } /* Done with the hash table. */ hash_destroy(htab); /* If no duplicates, we're out of luck. */ if (!num_skipped) { pfree(skip_slot); return false; } /* We found some duplicates; remove them. */ preserve_count = 0; for (n = 0; n < CheckpointerShmem->num_requests; n++) { if (skip_slot[n]) continue; CheckpointerShmem->requests[preserve_count++] = CheckpointerShmem->requests[n]; } ereport(DEBUG1, (errmsg("compacted fsync request queue from %d entries to %d entries", CheckpointerShmem->num_requests, preserve_count))); CheckpointerShmem->num_requests = preserve_count; /* Cleanup. */ pfree(skip_slot); return true; }
void PersistentStore_FreeTuple( PersistentStoreData *storeData, PersistentStoreSharedData *storeSharedData, ItemPointer persistentTid, /* TID of the stored tuple. */ Datum *freeValues, bool flushToXLog) /* When true, the XLOG record for this change will be flushed to disk. */ { Relation persistentRel; HeapTuple persistentTuple = NULL; ItemPointerData prevFreeTid; XLogRecPtr xlogEndLoc; /* The end location of the UPDATE XLOG record. */ Assert( LWLockHeldByMe(PersistentObjLock) ); #ifdef USE_ASSERT_CHECKING if (storeSharedData == NULL || !PersistentStoreSharedData_EyecatcherIsValid(storeSharedData)) elog(ERROR, "Persistent store shared-memory not valid"); #endif if (Debug_persistent_store_print) elog(PersistentStore_DebugPrintLevel(), "PersistentStore_FreeTuple: Going to free tuple at TID %s ('%s', shared data %p)", ItemPointerToString(persistentTid), storeData->tableName, storeSharedData); Assert(persistentTid->ip_posid != 0); persistentRel = (*storeData->openRel)(); prevFreeTid = storeSharedData->freeTid; if (validate_previous_free_tid) { /* Let us validate and have sanity check to make sure the prevFreeTid is really free. */ ItemPointerData tmpPrevFreeTid; PersistentStore_ValidateFreeTID( storeData, storeSharedData, &tmpPrevFreeTid); } storeSharedData->maxFreeOrderNum++; if (storeSharedData->maxFreeOrderNum == 1) ItemPointerCopy(persistentTid, &prevFreeTid); /* So non-zero PreviousFreeTid indicates free. */ storeSharedData->freeTid = *persistentTid; PersistentStore_FormTupleSetOurs( storeData, persistentRel->rd_att, freeValues, storeSharedData->maxFreeOrderNum, &prevFreeTid, &persistentTuple); persistentTuple->t_self = *persistentTid; frozen_heap_inplace_update(persistentRel, persistentTuple); /* * XLOG location of the UPDATE tuple's XLOG record. */ xlogEndLoc = XLogLastInsertEndLoc(); heap_freetuple(persistentTuple); (*storeData->closeRel)(persistentRel); storeSharedData->inUseCount--; if (Debug_persistent_store_print) elog(PersistentStore_DebugPrintLevel(), "PersistentStore_FreeTuple: Freed tuple at TID %s. Maximum free order number " INT64_FORMAT ", in use count " INT64_FORMAT " ('%s')", ItemPointerToString(&storeSharedData->freeTid), storeSharedData->maxFreeOrderNum, storeSharedData->inUseCount, storeData->tableName); if (flushToXLog) { XLogFlush(xlogEndLoc); XLogRecPtr_Zero(&nowaitXLogEndLoc); } else nowaitXLogEndLoc = xlogEndLoc; }
/* * CompactBgwriterRequestQueue * Remove duplicates from the request queue to avoid backend fsyncs. * * Although a full fsync request queue is not common, it can lead to severe * performance problems when it does happen. So far, this situation has * only been observed to occur when the system is under heavy write load, * and especially during the "sync" phase of a checkpoint. Without this * logic, each backend begins doing an fsync for every block written, which * gets very expensive and can slow down the whole system. * * Trying to do this every time the queue is full could lose if there * aren't any removable entries. But should be vanishingly rare in * practice: there's one queue entry per shared buffer. */ static bool CompactBgwriterRequestQueue() { struct BgWriterSlotMapping { BgWriterRequest request; int slot; }; int n, preserve_count; int num_skipped = 0; HASHCTL ctl; HTAB *htab; bool *skip_slot; /* must hold BgWriterCommLock in exclusive mode */ Assert(LWLockHeldByMe(BgWriterCommLock)); /* Initialize temporary hash table */ MemSet(&ctl, 0, sizeof(ctl)); ctl.keysize = sizeof(BgWriterRequest); ctl.entrysize = sizeof(struct BgWriterSlotMapping); ctl.hash = tag_hash; htab = hash_create("CompactBgwriterRequestQueue", BgWriterShmem->num_requests, &ctl, HASH_ELEM | HASH_FUNCTION); /* Initialize skip_slot array */ skip_slot = palloc0(sizeof(bool) * BgWriterShmem->num_requests); /* * The basic idea here is that a request can be skipped if it's followed * by a later, identical request. It might seem more sensible to work * backwards from the end of the queue and check whether a request is * *preceded* by an earlier, identical request, in the hopes of doing less * copying. But that might change the semantics, if there's an * intervening FORGET_RELATION_FSYNC or FORGET_DATABASE_FSYNC request, so * we do it this way. It would be possible to be even smarter if we made * the code below understand the specific semantics of such requests (it * could blow away preceding entries that would end up being canceled * anyhow), but it's not clear that the extra complexity would buy us * anything. */ for (n = 0; n < BgWriterShmem->num_requests; ++n) { BgWriterRequest *request; struct BgWriterSlotMapping *slotmap; bool found; request = &BgWriterShmem->requests[n]; slotmap = hash_search(htab, request, HASH_ENTER, &found); if (found) { skip_slot[slotmap->slot] = true; ++num_skipped; } slotmap->slot = n; } /* Done with the hash table. */ hash_destroy(htab); /* If no duplicates, we're out of luck. */ if (!num_skipped) { pfree(skip_slot); return false; } /* We found some duplicates; remove them. */ for (n = 0, preserve_count = 0; n < BgWriterShmem->num_requests; ++n) { if (skip_slot[n]) continue; BgWriterShmem->requests[preserve_count++] = BgWriterShmem->requests[n]; } ereport(DEBUG1, (errmsg("compacted fsync request queue from %d entries to %d entries", BgWriterShmem->num_requests, preserve_count))); BgWriterShmem->num_requests = preserve_count; /* Cleanup. */ pfree(skip_slot); return true; }