📄 freespace.c
字号:
/* * Note we don't record info about a relation unless there's already * an FSM entry for it, implying someone has done GetPageWithFreeSpace * 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; FSMPageData *newLocation; curAlloc = realloc_fsm_rel(fsmrel, nPages, false); 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) { int i; for (i = 0; i < nPages; i++) { BlockNumber page = pageSpaces[i].blkno; Size avail = pageSpaces[i].avail; /* Check caller provides sorted data */ if (i > 0 && page <= pageSpaces[i - 1].blkno) elog(ERROR, "free-space data is not in page order"); FSMPageSetPageNum(newLocation, page); FSMPageSetSpace(newLocation, avail); newLocation++; } fsmrel->storedPages = nPages; } else { pack_incoming_pages(newLocation, curAllocPages, pageSpaces, nPages); fsmrel->storedPages = curAllocPages; } } LWLockRelease(FreeSpaceLock);}/* * GetFreeIndexPage - like GetPageWithFreeSpace, but for indexes */BlockNumberGetFreeIndexPage(RelFileNode *rel){ FSMRelation *fsmrel; BlockNumber freepage; LWLockAcquire(FreeSpaceLock, LW_EXCLUSIVE); /* * We always add a rel to the hashtable when it is inquired about. */ fsmrel = create_fsm_rel(rel); freepage = find_index_free_space(fsmrel); LWLockRelease(FreeSpaceLock); return freepage;}/* * RecordIndexFreeSpace - like RecordRelationFreeSpace, but for indexes */voidRecordIndexFreeSpace(RelFileNode *rel, int nPages, BlockNumber *pages){ 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, nPages, 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 lastPageCount 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. * * XXX when we implement tablespaces, target Oid will need to be tablespace * ID not database ID. */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.tblNode == 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; int numRels; double sumRequests; double needed; LWLockAcquire(FreeSpaceLock, LW_EXCLUSIVE); /* Count total space used --- tedious, but seems useful */ for (fsmrel = FreeSpaceMap->firstRel; fsmrel != NULL; fsmrel = fsmrel->nextPhysical) storedPages += fsmrel->storedPages; /* Copy other stats before dropping lock */ numRels = FreeSpaceMap->numRels; sumRequests = FreeSpaceMap->sumRequests; LWLockRelease(FreeSpaceLock); /* Convert stats to actual number of page slots needed */ needed = (sumRequests + numRels) * CHUNKPAGES; ereport(elevel, (errmsg("free space map: %d relations, %d pages stored; %.0f total pages needed", numRels, storedPages, needed), errdetail("Allocated FSM size: %d relations + %d pages = %.0f kB shared memory.", MaxFSMRelations, MaxFSMPages, (double) FreeSpaceShmemSize() / 1024.0)));}/* * 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(void){ FILE *fp; char cachefilename[MAXPGPATH]; FsmCacheFileHeader header; FSMRelation *fsmrel; /* Try to create file */ snprintf(cachefilename, sizeof(cachefilename), "%s/%s", DataDir, FSM_CACHE_FILENAME); unlink(cachefilename); /* in case it exists w/wrong permissions */ fp = AllocateFile(cachefilename, PG_BINARY_W); if (fp == NULL) { elog(LOG, "could not write \"%s\": %m", cachefilename); 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.lastPageCount = fsmrel->lastPageCount; 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); FreeFile(fp); return;write_failed: elog(LOG, "could not write \"%s\": %m", cachefilename); /* Clean up */ LWLockRelease(FreeSpaceLock); FreeFile(fp); /* Remove busted cache file */ unlink(cachefilename);}/* * 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; char cachefilename[MAXPGPATH]; FsmCacheFileHeader header; int relno; /* Try to open file */ snprintf(cachefilename, sizeof(cachefilename), "%s/%s", DataDir, FSM_CACHE_FILENAME); fp = AllocateFile(cachefilename, PG_BINARY_R); if (fp == NULL) { if (errno != ENOENT) elog(LOG, "could not read \"%s\": %m", cachefilename); 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\"", cachefilename); 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.lastPageCount < 0 || relheader.storedPages < 0) { elog(LOG, "bogus rel header in \"%s\"", cachefilename); goto read_failed; } /* Make sure lastPageCount doesn't exceed current MaxFSMPages */ if (relheader.lastPageCount > MaxFSMPages) relheader.lastPageCount = MaxFSMPages; /* Read the per-page data */ nPages = relheader.storedPages; if (relheader.isIndex) len = nPages * sizeof(IndexFSMPageData); else len = nPages * sizeof(FSMPageData); data = (char *) palloc(len + 1); /* +1 to avoid palloc(0) */ if (fread(data, 1, len, fp) != len) { elog(LOG, "premature EOF in \"%s\"", cachefilename); 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.lastPageCount, 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -