📄 freespace.c
字号:
FSMRelation *fsmrel; /* Limit nPages to something sane */ if (nPages < 0) nPages = 0; else if (nPages > MaxFSMPages) nPages = MaxFSMPages; LWLockAcquire(FreeSpaceLock, LW_EXCLUSIVE); /* * Note we don't record info about a relation unless there's already an * FSM entry for it, implying someone has done GetFreeIndexPage for it. * Inactive rels thus will not clutter the map simply by being vacuumed. */ fsmrel = lookup_fsm_rel(rel); if (fsmrel) { int curAlloc; int curAllocPages; int i; IndexFSMPageData *newLocation; curAlloc = realloc_fsm_rel(fsmrel, interestingPages, true); curAllocPages = curAlloc * INDEXCHUNKPAGES; /* * If the data fits in our current allocation, just copy it; otherwise * must compress. But compression is easy: we merely forget extra * pages. */ newLocation = (IndexFSMPageData *) (FreeSpaceMap->arena + fsmrel->firstChunk * CHUNKBYTES); if (nPages > curAllocPages) nPages = curAllocPages; for (i = 0; i < nPages; i++) { BlockNumber page = pages[i]; /* Check caller provides sorted data */ if (i > 0 && page <= pages[i - 1]) elog(ERROR, "free-space data is not in page order"); IndexFSMPageSetPageNum(newLocation, page); newLocation++; } fsmrel->storedPages = nPages; } LWLockRelease(FreeSpaceLock);}/* * FreeSpaceMapTruncateRel - adjust for truncation of a relation. * * We need to delete any stored data past the new relation length, so that * we don't bogusly return removed block numbers. */voidFreeSpaceMapTruncateRel(RelFileNode *rel, BlockNumber nblocks){ FSMRelation *fsmrel; LWLockAcquire(FreeSpaceLock, LW_EXCLUSIVE); fsmrel = lookup_fsm_rel(rel); if (fsmrel) { int pageIndex; /* Use lookup to locate first entry >= nblocks */ (void) lookup_fsm_page_entry(fsmrel, nblocks, &pageIndex); /* Delete all such entries */ fsmrel->storedPages = pageIndex; /* XXX should we adjust rel's interestingPages and sumRequests? */ } LWLockRelease(FreeSpaceLock);}/* * FreeSpaceMapForgetRel - forget all about a relation. * * This is called when a relation is deleted. Although we could just let * the rel age out of the map, it's better to reclaim and reuse the space * sooner. */voidFreeSpaceMapForgetRel(RelFileNode *rel){ FSMRelation *fsmrel; LWLockAcquire(FreeSpaceLock, LW_EXCLUSIVE); fsmrel = lookup_fsm_rel(rel); if (fsmrel) delete_fsm_rel(fsmrel); LWLockRelease(FreeSpaceLock);}/* * FreeSpaceMapForgetDatabase - forget all relations of a database. * * This is called during DROP DATABASE. As above, might as well reclaim * map space sooner instead of later. */voidFreeSpaceMapForgetDatabase(Oid dbid){ FSMRelation *fsmrel, *nextrel; LWLockAcquire(FreeSpaceLock, LW_EXCLUSIVE); for (fsmrel = FreeSpaceMap->usageList; fsmrel; fsmrel = nextrel) { nextrel = fsmrel->nextUsage; /* in case we delete it */ if (fsmrel->key.dbNode == dbid) delete_fsm_rel(fsmrel); } LWLockRelease(FreeSpaceLock);}/* * PrintFreeSpaceMapStatistics - print statistics about FSM contents * * The info is sent to ereport() with the specified message level. This is * intended for use during VACUUM. */voidPrintFreeSpaceMapStatistics(int elevel){ FSMRelation *fsmrel; int storedPages = 0; double sumRequests = 0; int numRels; double needed; LWLockAcquire(FreeSpaceLock, LW_EXCLUSIVE); /* * Count total space actually used, as well as the unclamped request total */ for (fsmrel = FreeSpaceMap->firstRel; fsmrel != NULL; fsmrel = fsmrel->nextPhysical) { storedPages += fsmrel->storedPages; sumRequests += fsm_calc_request_unclamped(fsmrel); } /* Copy other stats before dropping lock */ numRels = FreeSpaceMap->numRels; LWLockRelease(FreeSpaceLock); /* Convert stats to actual number of page slots needed */ needed = (sumRequests + numRels) * CHUNKPAGES; ereport(elevel, (errmsg("free space map contains %d pages in %d relations", storedPages, numRels), errdetail("A total of %.0f page slots are in use (including overhead).\n" "%.0f page slots are required to track all free space.\n" "Current limits are: %d page slots, %d relations, using %.0f kB.", Min(needed, MaxFSMPages), needed, MaxFSMPages, MaxFSMRelations, (double) FreeSpaceShmemSize() / 1024.0))); CheckFreeSpaceMapStatistics(NOTICE, numRels, needed); /* Print to server logs too because is deals with a config variable. */ CheckFreeSpaceMapStatistics(LOG, numRels, needed);}static voidCheckFreeSpaceMapStatistics(int elevel, int numRels, double needed){ if (numRels == MaxFSMRelations) ereport(elevel, (errmsg("max_fsm_relations(%d) equals the number of relations checked", MaxFSMRelations), errhint("You have at least %d relations. " "Consider increasing the configuration parameter \"max_fsm_relations\".", numRels))); else if (needed > MaxFSMPages) ereport(elevel, (errmsg("number of page slots needed (%.0f) exceeds max_fsm_pages (%d)", needed, MaxFSMPages), errhint("Consider increasing the configuration parameter \"max_fsm_pages\" " "to a value over %.0f.", needed)));}/* * DumpFreeSpaceMap - dump contents of FSM into a disk file for later reload * * This is expected to be called during database shutdown, after updates to * the FSM have stopped. We lock the FreeSpaceLock but that's purely pro * forma --- if anyone else is still accessing FSM, there's a problem. */voidDumpFreeSpaceMap(int code, Datum arg){ FILE *fp; FsmCacheFileHeader header; FSMRelation *fsmrel; /* Try to create file */ unlink(FSM_CACHE_FILENAME); /* in case it exists w/wrong permissions */ fp = AllocateFile(FSM_CACHE_FILENAME, PG_BINARY_W); if (fp == NULL) { elog(LOG, "could not write \"%s\": %m", FSM_CACHE_FILENAME); return; } LWLockAcquire(FreeSpaceLock, LW_EXCLUSIVE); /* Write file header */ MemSet(&header, 0, sizeof(header)); strcpy(header.label, FSM_CACHE_LABEL); header.endian = FSM_CACHE_ENDIAN; header.version = FSM_CACHE_VERSION; header.numRels = FreeSpaceMap->numRels; if (fwrite(&header, 1, sizeof(header), fp) != sizeof(header)) goto write_failed; /* For each relation, in order from least to most recently used... */ for (fsmrel = FreeSpaceMap->usageListTail; fsmrel != NULL; fsmrel = fsmrel->priorUsage) { FsmCacheRelHeader relheader; int nPages; /* Write relation header */ MemSet(&relheader, 0, sizeof(relheader)); relheader.key = fsmrel->key; relheader.isIndex = fsmrel->isIndex; relheader.avgRequest = fsmrel->avgRequest; relheader.interestingPages = fsmrel->interestingPages; relheader.storedPages = fsmrel->storedPages; if (fwrite(&relheader, 1, sizeof(relheader), fp) != sizeof(relheader)) goto write_failed; /* Write the per-page data directly from the arena */ nPages = fsmrel->storedPages; if (nPages > 0) { Size len; char *data; if (fsmrel->isIndex) len = nPages * sizeof(IndexFSMPageData); else len = nPages * sizeof(FSMPageData); data = (char *) (FreeSpaceMap->arena + fsmrel->firstChunk * CHUNKBYTES); if (fwrite(data, 1, len, fp) != len) goto write_failed; } } /* Clean up */ LWLockRelease(FreeSpaceLock); if (FreeFile(fp)) { elog(LOG, "could not write \"%s\": %m", FSM_CACHE_FILENAME); /* Remove busted cache file */ unlink(FSM_CACHE_FILENAME); } return;write_failed: elog(LOG, "could not write \"%s\": %m", FSM_CACHE_FILENAME); /* Clean up */ LWLockRelease(FreeSpaceLock); FreeFile(fp); /* Remove busted cache file */ unlink(FSM_CACHE_FILENAME);}/* * LoadFreeSpaceMap - load contents of FSM from a disk file * * This is expected to be called during database startup, before any FSM * updates begin. We lock the FreeSpaceLock but that's purely pro * forma --- if anyone else is accessing FSM yet, there's a problem. * * Notes: no complaint is issued if no cache file is found. If the file is * found, it is deleted after reading. Thus, if we crash without a clean * shutdown, the next cycle of life starts with no FSM data. To do otherwise, * we'd need to do significantly more validation in this routine, because of * the likelihood that what is in the dump file would be out-of-date, eg * there might be entries for deleted or truncated rels. */voidLoadFreeSpaceMap(void){ FILE *fp; FsmCacheFileHeader header; int relno; /* Try to open file */ fp = AllocateFile(FSM_CACHE_FILENAME, PG_BINARY_R); if (fp == NULL) { if (errno != ENOENT) elog(LOG, "could not read \"%s\": %m", FSM_CACHE_FILENAME); return; } LWLockAcquire(FreeSpaceLock, LW_EXCLUSIVE); /* Read and verify file header */ if (fread(&header, 1, sizeof(header), fp) != sizeof(header) || strcmp(header.label, FSM_CACHE_LABEL) != 0 || header.endian != FSM_CACHE_ENDIAN || header.version != FSM_CACHE_VERSION || header.numRels < 0) { elog(LOG, "bogus file header in \"%s\"", FSM_CACHE_FILENAME); goto read_failed; } /* For each relation, in order from least to most recently used... */ for (relno = 0; relno < header.numRels; relno++) { FsmCacheRelHeader relheader; Size len; char *data; FSMRelation *fsmrel; int nPages; int curAlloc; int curAllocPages; /* Read and verify relation header, as best we can */ if (fread(&relheader, 1, sizeof(relheader), fp) != sizeof(relheader) || (relheader.isIndex != false && relheader.isIndex != true) || relheader.avgRequest >= BLCKSZ || relheader.storedPages < 0) { elog(LOG, "bogus rel header in \"%s\"", FSM_CACHE_FILENAME); goto read_failed; } /* Read the per-page data */ nPages = relheader.storedPages; if (relheader.isIndex) len = nPages * sizeof(IndexFSMPageData); else len = nPages * sizeof(FSMPageData); data = (char *) palloc(len); if (fread(data, 1, len, fp) != len) { elog(LOG, "premature EOF in \"%s\"", FSM_CACHE_FILENAME); pfree(data); goto read_failed; } /* * Okay, create the FSM entry and insert data into it. Since the rels * were stored in reverse usage order, at the end of the loop they * will be correctly usage-ordered in memory; and if MaxFSMRelations * is less than it used to be, we will correctly drop the least * recently used ones. */ fsmrel = create_fsm_rel(&relheader.key); fsmrel->avgRequest = relheader.avgRequest; curAlloc = realloc_fsm_rel(fsmrel, relheader.interestingPages, relheader.isIndex); if (relheader.isIndex) { IndexFSMPageData *newLocation; curAllocPages = curAlloc * INDEXCHUNKPAGES; /* * If the data fits in our current allocation, just copy it; * otherwise must compress. But compression is easy: we merely * forget extra pages. */ newLocation = (IndexFSMPageData *) (FreeSpaceMap->arena + fsmrel->firstChunk * CHUNKBYTES); if (nPages > curAllocPages) nPages = curAllocPages; memcpy(newLocation, data, nPages * sizeof(IndexFSMPageData)); fsmrel->storedPages = nPages; } else { FSMPageData *newLocation; curAllocPages = curAlloc * CHUNKPAGES; /* * If the data fits in our current allocation, just copy it; * otherwise must compress. */ newLocation = (FSMPageData *) (FreeSpaceMap->arena + fsmrel->firstChunk * CHUNKBYTES); if (nPages <= curAllocPages) { memcpy(newLocation, data, nPages * sizeof(FSMPageData)); fsmrel->storedPages = nPages; } else { pack_existing_pages(newLocation, curAllocPages, (FSMPageData *) data, nPages); fsmrel->storedPages = curAllocPages; } } pfree(data); }read_failed: /* Clean up */ LWLockRelease(FreeSpaceLock); FreeFile(fp); /* Remove cache file before it can become stale; see notes above */ unlink(FSM_CACHE_FILENAME);}/* * Internal routines. These all assume the caller holds the FreeSpaceLock. *//* * Lookup a relation in the hash table. If not present, return NULL. * * The relation's position in the LRU list is not changed. */static FSMRelation *lookup_fsm_rel(RelFileNode *rel){ FSMRelation *fsmrel; fsmrel = (FSMRelation *) hash_search(FreeSpaceMapRelHash, (void *) rel, HASH_FIND, NULL); if (!fsmrel) return NULL; return fsmrel;}/* * Lookup a relation in the hash table, creating an entry if not present. * * On successful lookup, the relation is moved to the front of the LRU list. */static FSMRelation *create_fsm_rel(RelFileNode *rel){ FSMRelation *fsmrel; bool found; fsmrel = (FSMRelation *) hash_search(FreeSpaceMapRelHash, (void *) rel, HASH_ENTER, &found);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -