in alloc.go [181:258]
func (a *allocator) fileCommitAlloc(st *allocCommitState) reason {
const op = "txfile/commit-alloc-meta"
if !st.updated {
return nil
}
dataFreed := st.tx.data.freed.Regions()
metaFreed := st.tx.meta.freed.Regions()
// Predict number of meta pages required to store new freelist,
// by iterating all region entries and take the potential encoding size
// into account. As allocation might force a region from the data area
// being moved (or split) into the meta area, we add more dummy region
// with enforced max size. So the allocator can move pages between
// meta and data if required.
// This method over-estimates the number of required pages, as
// we will have to allocate pages from the metaFree lists end
// after the estimator finishes.
prediction := prepareFreelistEncPagePrediction(freePageHeaderSize, a.pageSize)
prediction.AddRegions(dataFreed)
prediction.AddRegions(metaFreed)
prediction.AddRegions(a.data.freelist.regions)
prediction.AddRegions(a.meta.freelist.regions)
if prediction.count > 0 {
// only add extra pages if we need to write the meta page
prediction.AddRegion(region{id: 1, count: math.MaxUint32})
prediction.AddRegion(region{id: 1, count: math.MaxUint32})
}
// alloc regions for writing the new freelist
var allocRegions regionList
if n := prediction.count; n > 0 {
allocRegions = a.MetaAllocator().AllocRegions(st.tx, n)
if allocRegions == nil {
return a.err(op).of(OutOfMemory).
report("not enough space to allocate freelist meta pages")
}
}
// Compute new freelist. As consecutive regions are merged the
// resulting list might require less pages
newDataList := mergeRegionLists(a.data.freelist.regions, dataFreed)
newMetaList := mergeRegionLists(a.meta.freelist.regions, metaFreed)
st.allocRegions = allocRegions
dataEndMarker := a.data.endMarker
metaEndMarker := a.meta.endMarker
// Remove pages from end of overflow area from meta freelist + adjust end marker
st.metaList, st.overflowFreed = releaseOverflowPages(newMetaList, a.maxPages, metaEndMarker)
if st.overflowFreed > 0 {
st.tx.stats.overflow.freed += st.overflowFreed
newEnd := metaEndMarker - PageID(st.overflowFreed)
if metaEndMarker > dataEndMarker { // shrink overflow area, which was allocated from data area
dataEndMarker = newEnd
}
metaEndMarker = newEnd
}
// Remove pages from end of data area. Pages are removed from the data area
// only if the file size has been decreased.
st.dataList, st.dataFreed = releaseOverflowPages(newDataList, a.maxPages, dataEndMarker)
if st.dataFreed > 0 {
dataEndMarker -= PageID(st.dataFreed)
if metaEndMarker >= dataEndMarker {
metaEndMarker = dataEndMarker
}
}
// Update new allocator end markers if regions have been removed from the free lists.
st.dataEndMarker = dataEndMarker
st.metaEndMarker = metaEndMarker
return nil
}