/*
* Copyright (c) 2020 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* $Id: //eng/uds-releases/jasper/src/uds/index.c#15 $
*/
#include "index.h"
#include "hashUtils.h"
#include "indexCheckpoint.h"
#include "indexInternals.h"
#include "logger.h"
static const uint64_t NO_LAST_CHECKPOINT = UINT_MAX;
/**
* Replay an index which was loaded from a checkpoint.
*
* @param index The index to replay
* @param lastCheckpointChapter The number of the chapter where the
* last checkpoint was made
*
* @return UDS_SUCCESS or an error code.
**/
static int replayIndexFromCheckpoint(Index *index,
uint64_t lastCheckpointChapter)
{
// Find the volume chapter boundaries
uint64_t lowestVCN, highestVCN;
bool isEmpty = false;
IndexLookupMode oldLookupMode = index->volume->lookupMode;
index->volume->lookupMode = LOOKUP_FOR_REBUILD;
int result = findVolumeChapterBoundaries(index->volume, &lowestVCN,
&highestVCN, &isEmpty);
index->volume->lookupMode = oldLookupMode;
if (result != UDS_SUCCESS) {
return logFatalWithStringError(result,
"cannot replay index: "
"unknown volume chapter boundaries");
}
if (lowestVCN > highestVCN) {
logFatal("cannot replay index: no valid chapters exist");
return UDS_CORRUPT_COMPONENT;
}
if (isEmpty) {
// The volume is empty, so the index should also be empty
if (index->newestVirtualChapter != 0) {
logFatal("cannot replay index from empty volume");
return UDS_CORRUPT_COMPONENT;
}
return UDS_SUCCESS;
}
unsigned int chaptersPerVolume = index->volume->geometry->chaptersPerVolume;
index->oldestVirtualChapter = lowestVCN;
index->newestVirtualChapter = highestVCN + 1;
if (index->newestVirtualChapter == lowestVCN + chaptersPerVolume) {
// skip the chapter shadowed by the open chapter
index->oldestVirtualChapter++;
}
uint64_t firstReplayChapter = lastCheckpointChapter;
if (firstReplayChapter < index->oldestVirtualChapter) {
firstReplayChapter = index->oldestVirtualChapter;
}
return replayVolume(index, firstReplayChapter);
}
/**********************************************************************/
static int loadIndex(Index *index, bool allowReplay)
{
bool replayRequired = false;
int result = loadIndexState(index->state, &replayRequired);
if (result != UDS_SUCCESS) {
return result;
}
if (replayRequired && !allowReplay) {
return logErrorWithStringError(
UDS_INDEX_NOT_SAVED_CLEANLY,
"index not saved cleanly: open chapter missing");
}
uint64_t lastCheckpointChapter
= ((index->lastCheckpoint != NO_LAST_CHECKPOINT)
? index->lastCheckpoint : 0);
logInfo("loaded index from chapter %llu through chapter %llu",
index->oldestVirtualChapter, lastCheckpointChapter);
if (replayRequired) {
result = replayIndexFromCheckpoint(index, lastCheckpointChapter);
if (result != UDS_SUCCESS) {
return result;
}
}
unsigned int i;
for (i = 0; i < index->zoneCount; i++) {
setActiveChapters(index->zones[i]);
}
index->loadedType = replayRequired ? LOAD_REPLAY : LOAD_LOAD;
return UDS_SUCCESS;
}
/**********************************************************************/
static int rebuildIndex(Index *index)
{
// Find the volume chapter boundaries
uint64_t lowestVCN, highestVCN;
bool isEmpty = false;
IndexLookupMode oldLookupMode = index->volume->lookupMode;
index->volume->lookupMode = LOOKUP_FOR_REBUILD;
int result = findVolumeChapterBoundaries(index->volume, &lowestVCN,
&highestVCN, &isEmpty);
index->volume->lookupMode = oldLookupMode;
if (result != UDS_SUCCESS) {
return logFatalWithStringError(result,
"cannot rebuild index: "
"unknown volume chapter boundaries");
}
if (lowestVCN > highestVCN) {
logFatal("cannot rebuild index: no valid chapters exist");
return UDS_CORRUPT_COMPONENT;
}
if (isEmpty) {
index->newestVirtualChapter = index->oldestVirtualChapter = 0;
} else {
unsigned int numChapters = index->volume->geometry->chaptersPerVolume;
index->newestVirtualChapter = highestVCN + 1;
index->oldestVirtualChapter = lowestVCN;
if (index->newestVirtualChapter
== (index->oldestVirtualChapter + numChapters)) {
// skip the chapter shadowed by the open chapter
index->oldestVirtualChapter++;
}
}
if ((index->newestVirtualChapter - index->oldestVirtualChapter) >
index->volume->geometry->chaptersPerVolume) {
return logFatalWithStringError(UDS_CORRUPT_COMPONENT,
"cannot rebuild index: "
"volume chapter boundaries too large");
}
setMasterIndexOpenChapter(index->masterIndex, 0);
if (isEmpty) {
index->loadedType = LOAD_EMPTY;
return UDS_SUCCESS;
}
result = replayVolume(index, index->oldestVirtualChapter);
if (result != UDS_SUCCESS) {
return result;
}
unsigned int i;
for (i = 0; i < index->zoneCount; i++) {
setActiveChapters(index->zones[i]);
}
index->loadedType = LOAD_REBUILD;
return UDS_SUCCESS;
}
/**********************************************************************/
int makeIndex(IndexLayout *layout,
const Configuration *config,
const struct uds_parameters *userParams,
unsigned int zoneCount,
LoadType loadType,
IndexLoadContext *loadContext,
Index **newIndex)
{
Index *index;
int result = allocateIndex(layout, config, userParams, zoneCount, loadType,
&index);
if (result != UDS_SUCCESS) {
return logErrorWithStringError(result, "could not allocate index");
}
index->loadContext = loadContext;
uint64_t nonce = getVolumeNonce(layout);
result = makeMasterIndex(config, zoneCount, nonce, &index->masterIndex);
if (result != UDS_SUCCESS) {
freeIndex(index);
return logErrorWithStringError(result, "could not make master index");
}
result = addIndexStateComponent(index->state, MASTER_INDEX_INFO, NULL,
index->masterIndex);
if (result != UDS_SUCCESS) {
freeIndex(index);
return result;
}
result = addIndexStateComponent(index->state, &INDEX_PAGE_MAP_INFO,
index->volume->indexPageMap, NULL);
if (result != UDS_SUCCESS) {
freeIndex(index);
return result;
}
result = makeChapterWriter(index, getIndexVersion(layout),
&index->chapterWriter);
if (result != UDS_SUCCESS) {
freeIndex(index);
return result;
}
if ((loadType == LOAD_LOAD) || (loadType == LOAD_REBUILD)) {
if (!index->existed) {
freeIndex(index);
return UDS_NO_INDEX;
}
result = loadIndex(index, loadType == LOAD_REBUILD);
switch (result) {
case UDS_SUCCESS:
break;
case ENOMEM:
// We should not try a rebuild for this error.
logErrorWithStringError(result, "index could not be loaded");
break;
default:
logErrorWithStringError(result, "index could not be loaded");
if (loadType == LOAD_REBUILD) {
result = rebuildIndex(index);
if (result != UDS_SUCCESS) {
logErrorWithStringError(result, "index could not be rebuilt");
}
}
break;
}
} else {
index->loadedType = LOAD_CREATE;
discardIndexStateData(index->state);
}
if (result != UDS_SUCCESS) {
freeIndex(index);
return logUnrecoverable(result, "fatal error in makeIndex");
}
if (index->loadContext != NULL) {
lockMutex(&index->loadContext->mutex);
index->loadContext->status = INDEX_READY;
// If we get here, suspend is meaningless, but notify any thread trying
// to suspend us so it doesn't hang.
broadcastCond(&index->loadContext->cond);
unlockMutex(&index->loadContext->mutex);
}
index->hasSavedOpenChapter = index->loadedType == LOAD_LOAD;
*newIndex = index;
return UDS_SUCCESS;
}
/**********************************************************************/
void freeIndex(Index *index)
{
if (index == NULL) {
return;
}
freeChapterWriter(index->chapterWriter);
if (index->masterIndex != NULL) {
freeMasterIndex(index->masterIndex);
}
releaseIndex(index);
}
/**********************************************************************/
int saveIndex(Index *index)
{
waitForIdleChapterWriter(index->chapterWriter);
int result = finishCheckpointing(index);
if (result != UDS_SUCCESS) {
logInfo("save index failed");
return result;
}
beginSave(index, false, index->newestVirtualChapter);
result = saveIndexState(index->state);
if (result != UDS_SUCCESS) {
logInfo("save index failed");
index->lastCheckpoint = index->prevCheckpoint;
} else {
index->hasSavedOpenChapter = true;
logInfo("finished save (vcn %llu)", index->lastCheckpoint);
}
return result;
}
/**
* Get the zone for a request.
*
* @param index The index
* @param request The request
*
* @return The zone for the request
**/
static IndexZone *getRequestZone(Index *index, Request *request)
{
return index->zones[request->zoneNumber];
}
/**
* Search an index zone. This function is only correct for LRU.
*
* @param zone The index zone to query.
* @param request The request originating the query.
*
* @return UDS_SUCCESS or an error code
**/
static int searchIndexZone(IndexZone *zone, Request *request)
{
MasterIndexRecord record;
int result = getMasterIndexRecord(zone->index->masterIndex,
&request->chunkName, &record);
if (result != UDS_SUCCESS) {
return result;
}
bool found = false;
if (record.isFound) {
result = getRecordFromZone(zone, request, &found, record.virtualChapter);
if (result != UDS_SUCCESS) {
return result;
}
if (found) {
request->location = computeIndexRegion(zone, record.virtualChapter);
}
}
/*
* If a record has overflowed a chapter index in more than one chapter
* (or overflowed in one chapter and collided with an existing record),
* it will exist as a collision record in the master index, but we won't
* find it in the volume. This case needs special handling.
*/
bool overflowRecord = (record.isFound && record.isCollision && !found);
uint64_t chapter = zone->newestVirtualChapter;
if (found || overflowRecord) {
if ((request->action == REQUEST_QUERY)
&& (!request->update || overflowRecord)) {
/* This is a query without update, or with nothing to update */
return UDS_SUCCESS;
}
if (record.virtualChapter != chapter) {
/*
* Update the master index to reference the new chapter for the block.
* If the record had been deleted or dropped from the chapter index, it
* will be back.
*/
result = setMasterIndexRecordChapter(&record, chapter);
} else if (request->action != REQUEST_UPDATE) {
/* The record is already in the open chapter, so we're done */
return UDS_SUCCESS;
}
} else {
// The record wasn't in the master index, so check whether the name
// is in a cached sparse chapter.
if (!isMasterIndexSample(zone->index->masterIndex, &request->chunkName)
&& isSparse(zone->index->volume->geometry)) {
// Passing UINT64_MAX triggers a search of the entire sparse cache.
result = searchSparseCacheInZone(zone, request, UINT64_MAX, &found);
if (result != UDS_SUCCESS) {
return result;
}
if (found) {
request->location = LOC_IN_SPARSE;
}
}
if (request->action == REQUEST_QUERY) {
if (!found || !request->update) {
// This is a query without update or for a new record, so we're done.
return UDS_SUCCESS;
}
}
/*
* Add a new entry to the master index referencing the open chapter.
* This needs to be done both for new records, and for records from
* cached sparse chapters.
*/
result = putMasterIndexRecord(&record, chapter);
}
if (result == UDS_OVERFLOW) {
/*
* The master index encountered a delta list overflow. The condition
* was already logged. We will go on without adding the chunk to the
* open chapter.
*/
return UDS_SUCCESS;
}
if (result != UDS_SUCCESS) {
return result;
}
UdsChunkData *metadata;
if (!found || (request->action == REQUEST_UPDATE)) {
// This is a new record or we're updating an existing record.
metadata = &request->newMetadata;
} else {
// This is a duplicate, so move the record to the open chapter (for LRU).
metadata = &request->oldMetadata;
}
return putRecordInZone(zone, request, metadata);
}
/**********************************************************************/
static int removeFromIndexZone(IndexZone *zone, Request *request)
{
MasterIndexRecord record;
int result = getMasterIndexRecord(zone->index->masterIndex,
&request->chunkName, &record);
if (result != UDS_SUCCESS) {
return result;
}
if (!record.isFound) {
// The name does not exist in master index, so there is nothing to remove.
return UDS_SUCCESS;
}
if (!record.isCollision) {
// Non-collision records are hints, so resolve the name in the chapter.
bool found;
int result = getRecordFromZone(zone, request, &found,
record.virtualChapter);
if (result != UDS_SUCCESS) {
return result;
}
if (!found) {
// The name does not exist in the chapter, so there is nothing to remove.
return UDS_SUCCESS;
}
}
request->location = computeIndexRegion(zone, record.virtualChapter);
/*
* Delete the master index entry for the named record only. Note that a
* later search might later return stale advice if there is a colliding name
* in the same chapter, but it's a very rare case (1 in 2^21).
*/
result = removeMasterIndexRecord(&record);
if (result != UDS_SUCCESS) {
return result;
}
// If the record is in the open chapter, we must remove it or mark it
// deleted to avoid trouble if the record is added again later.
if (request->location == LOC_IN_OPEN_CHAPTER) {
bool hashExists = false;
removeFromOpenChapter(zone->openChapter, &request->chunkName, &hashExists);
result = ASSERT(hashExists, "removing record not found in open chapter");
if (result != UDS_SUCCESS) {
return result;
}
}
return UDS_SUCCESS;
}
/**
* Simulate the creation of a sparse cache barrier message by the triage
* queue, and the later execution of that message in an index zone.
*
* If the index receiving the request is multi-zone or dense, this function
* does nothing. This simulation is an optimization for single-zone sparse
* indexes. It also supports unit testing of indexes without routers and
* queues.
*
* @param zone the index zone responsible for the index request
* @param request the index request about to be executed
*
* @return UDS_SUCCESS always
**/
static int simulateIndexZoneBarrierMessage(IndexZone *zone, Request *request)
{
// Do nothing unless this is a single-zone sparse index.
if ((zone->index->zoneCount > 1)
|| !isSparse(zone->index->volume->geometry)) {
return UDS_SUCCESS;
}
// Check if the index request is for a sampled name in a sparse chapter.
uint64_t sparseVirtualChapter = triageIndexRequest(zone->index, request);
if (sparseVirtualChapter == UINT64_MAX) {
// Not indexed, not a hook, or in a chapter that is still dense, which
// means there should be no change to the sparse chapter index cache.
return UDS_SUCCESS;
}
/*
* The triage queue would have generated and enqueued a barrier message
* preceding this request, which we simulate by directly invoking the
* execution hook for an equivalent message.
*/
BarrierMessageData barrier = { .virtualChapter = sparseVirtualChapter };
return executeSparseCacheBarrierMessage(zone, &barrier);
}
/**********************************************************************/
static int dispatchIndexZoneRequest(IndexZone *zone, Request *request)
{
if (!request->requeued) {
// Single-zone sparse indexes don't have a triage queue to generate cache
// barrier requests, so see if we need to synthesize a barrier.
int result = simulateIndexZoneBarrierMessage(zone, request);
if (result != UDS_SUCCESS) {
return result;
}
}
// Set the default location. It will be overwritten if we find the chunk.
request->location = LOC_UNAVAILABLE;
int result;
switch (request->action) {
case REQUEST_INDEX:
case REQUEST_UPDATE:
case REQUEST_QUERY:
result = makeUnrecoverable(searchIndexZone(zone, request));
break;
case REQUEST_DELETE:
result = makeUnrecoverable(removeFromIndexZone(zone, request));
break;
default:
result = logWarningWithStringError(UDS_INVALID_ARGUMENT,
"attempted to execute invalid action:"
" %d",
request->action);
break;
}
return result;
}
/**********************************************************************/
int dispatchIndexRequest(Index *index, Request *request)
{
return dispatchIndexZoneRequest(getRequestZone(index, request), request);
}
/**********************************************************************/
static int rebuildIndexPageMap(Index *index, uint64_t vcn)
{
Geometry *geometry = index->volume->geometry;
unsigned int chapter = mapToPhysicalChapter(geometry, vcn);
unsigned int expectedListNumber = 0;
unsigned int indexPageNumber;
for (indexPageNumber = 0;
indexPageNumber < geometry->indexPagesPerChapter;
indexPageNumber++) {
DeltaIndexPage *chapterIndexPage;
int result = getPage(index->volume, chapter, indexPageNumber,
CACHE_PROBE_INDEX_FIRST, NULL, &chapterIndexPage);
if (result != UDS_SUCCESS) {
return logErrorWithStringError(result,
"failed to read index page %u"
" in chapter %u",
indexPageNumber, chapter);
}
unsigned int lowestDeltaList = chapterIndexPage->lowestListNumber;
unsigned int highestDeltaList = chapterIndexPage->highestListNumber;
if (lowestDeltaList != expectedListNumber) {
return logErrorWithStringError(UDS_CORRUPT_DATA,
"chapter %u index page %u is corrupt",
chapter, indexPageNumber);
}
result = updateIndexPageMap(index->volume->indexPageMap, vcn, chapter,
indexPageNumber, highestDeltaList);
if (result != UDS_SUCCESS) {
return logErrorWithStringError(result,
"failed to update chapter %u index page"
" %u",
chapter, indexPageNumber);
}
expectedListNumber = highestDeltaList + 1;
}
return UDS_SUCCESS;
}
/**
* Add an entry to the master index when rebuilding.
*
* @param index The index to query.
* @param name The block name of interest.
* @param virtualChapter The virtual chapter number to write to the
* master index
* @param willBeSparseChapter True if this entry will be in the sparse portion
* of the index at the end of rebuilding
*
* @return UDS_SUCCESS or an error code
**/
static int replayRecord(Index *index,
const UdsChunkName *name,
uint64_t virtualChapter,
bool willBeSparseChapter)
{
if (willBeSparseChapter && !isMasterIndexSample(index->masterIndex, name)) {
// This entry will be in a sparse chapter after the rebuild completes,
// and it is not a sample, so just skip over it.
return UDS_SUCCESS;
}
MasterIndexRecord record;
int result = getMasterIndexRecord(index->masterIndex, name, &record);
if (result != UDS_SUCCESS) {
return result;
}
bool updateRecord;
if (record.isFound) {
if (record.isCollision) {
if (record.virtualChapter == virtualChapter) {
/* The record is already correct, so we don't need to do anything */
return UDS_SUCCESS;
}
updateRecord = true;
} else if (record.virtualChapter == virtualChapter) {
/*
* There is a master index entry pointing to the current
* chapter, but we don't know if it is for the same name as the
* one we are currently working on or not. For now, we're just
* going to assume that it isn't. This will create one extra
* collision record if there was a deleted record in the current
* chapter.
*/
updateRecord = false;
} else {
/*
* If we're rebuilding, we don't normally want to go to disk to see if
* the record exists, since we will likely have just read the record from
* disk (i.e. we know it's there). The exception to this is when we
* already find an entry in the master index that has a different chapter.
* In this case, we need to search that chapter to determine if the
* master index entry was for the same record or a different one.
*/
result = searchVolumePageCache(index->volume, NULL, name,
record.virtualChapter, NULL,
&updateRecord);
if (result != UDS_SUCCESS) {
return result;
}
}
} else {
updateRecord = false;
}
if (updateRecord) {
/*
* Update the master index to reference the new chapter for the block.
* If the record had been deleted or dropped from the chapter index, it
* will be back.
*/
result = setMasterIndexRecordChapter(&record, virtualChapter);
} else {
/*
* Add a new entry to the master index referencing the open
* chapter. This should be done regardless of whether we are a brand
* new record or a sparse record, i.e. one that doesn't exist in the
* index but does on disk, since for a sparse record, we would want to
* un-sparsify if it did exist.
*/
result = putMasterIndexRecord(&record, virtualChapter);
}
if ((result == UDS_DUPLICATE_NAME) || (result == UDS_OVERFLOW)) {
/* Ignore duplicate record and delta list overflow errors */
return UDS_SUCCESS;
}
return result;
}
/**********************************************************************/
void beginSave(Index *index, bool checkpoint, uint64_t openChapterNumber)
{
index->prevCheckpoint = index->lastCheckpoint;
index->lastCheckpoint = ((openChapterNumber == 0)
? NO_LAST_CHECKPOINT
: openChapterNumber - 1);
const char *what = (checkpoint ? "checkpoint" : "save");
logInfo("beginning %s (vcn %llu)", what, index->lastCheckpoint);
}
/**
* Suspend the index if necessary and wait for a signal to resume.
*
* @param index The index to replay
*
* @return true
if the replay should terminate
**/
static bool checkForSuspend(Index *index)
{
if (index->loadContext == NULL) {
return false;
}
lockMutex(&index->loadContext->mutex);
if (index->loadContext->status != INDEX_SUSPENDING) {
unlockMutex(&index->loadContext->mutex);
return false;
}
// Notify that we are suspended and wait for the resume.
index->loadContext->status = INDEX_SUSPENDED;
broadcastCond(&index->loadContext->cond);
while ((index->loadContext->status != INDEX_OPENING)
&& (index->loadContext->status != INDEX_FREEING)) {
waitCond(&index->loadContext->cond, &index->loadContext->mutex);
}
bool retVal = (index->loadContext->status == INDEX_FREEING);
unlockMutex(&index->loadContext->mutex);
return retVal;
}
/**********************************************************************/
int replayVolume(Index *index, uint64_t fromVCN)
{
int result;
uint64_t uptoVCN = index->newestVirtualChapter;
logInfo("Replaying volume from chapter %llu through chapter %"
PRIu64,
fromVCN, uptoVCN);
setMasterIndexOpenChapter(index->masterIndex, uptoVCN);
setMasterIndexOpenChapter(index->masterIndex, fromVCN);
/*
* At least two cases to deal with here!
* - index loaded but replaying from lastCheckpoint; maybe full, maybe not
* - index failed to load, full rebuild
* Starts empty, then dense-only, then dense-plus-sparse.
* Need to sparsify while processing individual chapters.
*/
IndexLookupMode oldLookupMode = index->volume->lookupMode;
index->volume->lookupMode = LOOKUP_FOR_REBUILD;
/*
* Go through each record page of each chapter and add the records back to
* the master index. This should not cause anything to be written to either
* the open chapter or on disk volume. Also skip the on disk chapter
* corresponding to upto, as this would have already been
* purged from the master index when the chapter was opened.
*
* Also, go through each index page for each chapter and rebuild the
* index page map.
*/
const Geometry *geometry = index->volume->geometry;
uint64_t oldIPMupdate = getLastUpdate(index->volume->indexPageMap);
uint64_t vcn;
for (vcn = fromVCN; vcn < uptoVCN; ++vcn) {
if (checkForSuspend(index)) {
logInfo("Replay interrupted by index shutdown at chapter %llu", vcn);
return UDS_SHUTTINGDOWN;
}
bool willBeSparseChapter = isChapterSparse(geometry, fromVCN, uptoVCN,
vcn);
unsigned int chapter = mapToPhysicalChapter(geometry, vcn);
prefetchVolumePages(&index->volume->volumeStore,
mapToPhysicalPage(geometry, chapter, 0),
geometry->pagesPerChapter);
setMasterIndexOpenChapter(index->masterIndex, vcn);
result = rebuildIndexPageMap(index, vcn);
if (result != UDS_SUCCESS) {
index->volume->lookupMode = oldLookupMode;
return logErrorWithStringError(result,
"could not rebuild index page map for"
" chapter %u",
chapter);
}
unsigned int j;
for (j = 0; j < geometry->recordPagesPerChapter; j++) {
unsigned int recordPageNumber = geometry->indexPagesPerChapter + j;
byte *recordPage;
result = getPage(index->volume, chapter, recordPageNumber,
CACHE_PROBE_RECORD_FIRST, &recordPage, NULL);
if (result != UDS_SUCCESS) {
index->volume->lookupMode = oldLookupMode;
return logUnrecoverable(result, "could not get page %d",
recordPageNumber);
}
unsigned int k;
for (k = 0; k < geometry->recordsPerPage; k++) {
const byte *nameBytes = recordPage + (k * BYTES_PER_RECORD);
UdsChunkName name;
memcpy(&name.name, nameBytes, UDS_CHUNK_NAME_SIZE);
result = replayRecord(index, &name, vcn, willBeSparseChapter);
if (result != UDS_SUCCESS) {
char hexName[(2 * UDS_CHUNK_NAME_SIZE) + 1];
if (chunkNameToHex(&name, hexName, sizeof(hexName)) != UDS_SUCCESS) {
strncpy(hexName, "", sizeof(hexName));
}
index->volume->lookupMode = oldLookupMode;
return logUnrecoverable(result,
"could not find block %s during rebuild",
hexName);
}
}
}
}
index->volume->lookupMode = oldLookupMode;
// We also need to reap the chapter being replaced by the open chapter
setMasterIndexOpenChapter(index->masterIndex, uptoVCN);
uint64_t newIPMupdate = getLastUpdate(index->volume->indexPageMap);
if (newIPMupdate != oldIPMupdate) {
logInfo("replay changed index page map update from %llu to %llu",
oldIPMupdate, newIPMupdate);
}
return UDS_SUCCESS;
}
/**********************************************************************/
void getIndexStats(Index *index, UdsIndexStats *counters)
{
uint64_t cwAllocated = getChapterWriterMemoryAllocated(index->chapterWriter);
// We're accessing the master index while not on a zone thread, but that's
// safe to do when acquiring statistics.
MasterIndexStats denseStats, sparseStats;
getMasterIndexStats(index->masterIndex, &denseStats, &sparseStats);
counters->entriesIndexed = (denseStats.recordCount
+ sparseStats.recordCount);
counters->memoryUsed = ((uint64_t) denseStats.memoryAllocated
+ (uint64_t) sparseStats.memoryAllocated
+ (uint64_t) getCacheSize(index->volume)
+ cwAllocated);
counters->collisions = (denseStats.collisionCount
+ sparseStats.collisionCount);
counters->entriesDiscarded = (denseStats.discardCount
+ sparseStats.discardCount);
counters->checkpoints = getCheckpointCount(index->checkpoint);
}
/**********************************************************************/
void advanceActiveChapters(Index *index)
{
index->newestVirtualChapter++;
if (areSamePhysicalChapter(index->volume->geometry,
index->newestVirtualChapter,
index->oldestVirtualChapter)) {
index->oldestVirtualChapter++;
}
}
/**********************************************************************/
uint64_t triageIndexRequest(Index *index, Request *request)
{
MasterIndexTriage triage;
lookupMasterIndexName(index->masterIndex, &request->chunkName, &triage);
if (!triage.inSampledChapter) {
// Not indexed or not a hook.
return UINT64_MAX;
}
IndexZone *zone = getRequestZone(index, request);
if (!isZoneChapterSparse(zone, triage.virtualChapter)) {
return UINT64_MAX;
}
// XXX Optimize for a common case by remembering the chapter from the most
// recent barrier message and skipping this chapter if is it the same.
// Return the sparse chapter number to trigger the barrier messages.
return triage.virtualChapter;
}