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