/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package com.github.geophile.erdo.forest;
import com.github.geophile.erdo.apiimpl.DatabaseImpl;
import com.github.geophile.erdo.apiimpl.DatabaseOnDisk;
import com.github.geophile.erdo.map.Factory;
import com.github.geophile.erdo.map.SealedMap;
import com.github.geophile.erdo.map.diskmap.DBStructure;
import com.github.geophile.erdo.map.diskmap.DiskMap;
import com.github.geophile.erdo.map.diskmap.Manifest;
import com.github.geophile.erdo.segmentfilemanager.AbstractSegmentFileManager;
import com.github.geophile.erdo.transaction.TransactionOwners;
import com.github.geophile.erdo.util.FileUtil;
import com.github.geophile.erdo.util.LongArray;
import java.io.IOException;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import static java.lang.Math.max;
public class ForestRecoveryOnDisk implements ForestRecovery
{
// ForestRecovery interface
public Forest recoverForest(DatabaseImpl databaseImpl) throws IOException, InterruptedException
{
LOG.log(Level.WARNING, "Starting recovery of {0}", databaseImpl);
DatabaseOnDisk database = (DatabaseOnDisk) databaseImpl;
Factory factory = database.factory();
AbstractSegmentFileManager segmentFileManager = factory.segmentFileManager();
Forest forest;
java.util.Map<Long, SealedMap> maps = new HashMap<>(); // map id -> map
TransactionOwners transactionOwners = new TransactionOwners();
LongArray obsoleteTreeIds = new LongArray();
// Find trees on disk and gather ids of obsolete trees. Also compute max map id to restore
// map id generator.
DBStructure dbStructure = database.dbStructure();
long maxMapId = -1L;
for (int treeId : dbStructure.treeIds()) {
DiskMap map = DiskMap.recover(database, treeId);
if (map == null) {
if (deleteUnrecoverableTrees()) {
LOG.log(Level.WARNING, "{0} is not recoverable - deleting directory", treeId);
FileUtil.deleteFile(dbStructure.manifestFile(treeId));
}
} else {
maps.put(map.mapId(), map);
if (map.mapId() > maxMapId) {
maxMapId = map.mapId();
}
for (long obsoleteTreeId : map.obsoleteTreeIds()) {
obsoleteTreeIds.append(obsoleteTreeId);
}
}
}
// Delete obsolete trees
for (Long obsoleteTreeId : obsoleteTreeIds) {
SealedMap obsoleteTree = maps.remove(obsoleteTreeId);
if (obsoleteTree != null) {
obsoleteTree.destroyPersistentState();
}
}
// Compute transactionOwners and compute max transaction timestamp
long maxTransactionTimestamp = -1L;
long maxSegmentId = -1L;
for (SealedMap map : maps.values()) {
transactionOwners.add(map);
maxTransactionTimestamp = max(maxTransactionTimestamp, map.timestamps().maxTimestamp());
maxSegmentId = max(maxSegmentId, maxSegmentId(map));
}
transactionOwners.checkCoverage(maxTransactionTimestamp);
factory.restoreTransactionTimestampGenerator(maxTransactionTimestamp);
factory.restoreMapIdGenerator(maxMapId);
segmentFileManager.restoreSegmentIdGenerator(maxSegmentId);
forest = Forest.recover(database, transactionOwners, maps.values());
factory.transactionManager(forest);
LOG.log(Level.WARNING, "Finished recovery of {0}", databaseImpl);
return forest;
}
public boolean deleteUnrecoverableTrees()
{
return true;
}
// For use by this class
private long maxSegmentId(SealedMap map)
{
DiskMap diskMap = (DiskMap) map;
Manifest manifest = diskMap.manifest();
long maxSegmentId = -1L;
for (int level = 0; level < manifest.levels(); level++) {
LongArray segmentIds = manifest.segmentIds(level);
for (int i = 0; i < segmentIds.size(); i++) {
maxSegmentId = max(maxSegmentId, segmentIds.at(i));
}
}
return maxSegmentId;
}
// Class state
private static final Logger LOG = Logger.getLogger(ForestRecoveryOnDisk.class.getName());
}