Skip to content

Commit

Permalink
Add one more option, now: incompatibleDatabaseBehavior could be one…
Browse files Browse the repository at this point in the history
… of `EXIT`, `IGNORE`, `RESET`
  • Loading branch information
cupuyc committed Sep 13, 2016
1 parent f7bd0bd commit 47efe11
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 57 deletions.
53 changes: 29 additions & 24 deletions ethereumj-core/src/main/java/org/ethereum/config/Initializer.java
Expand Up @@ -21,7 +21,7 @@ class Initializer implements BeanPostProcessor {
private static final Logger logger = LoggerFactory.getLogger("general");

// Util to ensure database directory is compatible with code
private CheckDatabaseVersionWithReset checkDatabaseVersion = new CheckDatabaseVersionWithReset();
private IncompatibleDatabaseHandler databaseVersionHandler = new IncompatibleDatabaseHandler();

/**
* Method to be called right after the config is instantiated.
Expand All @@ -36,12 +36,7 @@ private void initConfig(SystemProperties config) {
logger.info("Database reset done");
}

checkDatabaseVersion.validateDatabaseVersion(config);

if (config.getConfig().getBoolean("database.autoResetOldVersion")) {
FileUtil.recursiveDelete(config.databaseDir());
logger.info("Auto database reset due to not compatible database version");
}
databaseVersionHandler.process(config);

if (logger.isInfoEnabled()) {
StringBuilder versions = new StringBuilder();
Expand Down Expand Up @@ -75,34 +70,44 @@ public Object postProcessAfterInitialization(Object bean, String beanName) throw
* Database version is stored in ${database}/version.properties
* Logic will assume that database has version 1 if file with version is absent.
*/
public static class CheckDatabaseVersionWithReset {
public static class IncompatibleDatabaseHandler {

public enum Behavior {
EXIT, RESET, IGNORE
}

public void validateDatabaseVersion(SystemProperties config) {
public void process(SystemProperties config) {
final File versionFile = new File(config.databaseDir() + "/version.properties");
final Behavior behavior = Behavior.valueOf(
config.getProperty("database.incompatibleDatabaseBehavior", Behavior.EXIT.toString()).toUpperCase());


// Detect database version
final Integer expectedVersion = config.databaseVersion();
if (isDatabaseDirectoryExists(config)) {
final Integer actualVersionRaw = getDatabaseVersion(versionFile);
final boolean isFirstVersionWithoutFile = actualVersionRaw.equals(-1) && expectedVersion.equals(1);
final Integer actualVersion = isFirstVersionWithoutFile ? 1 : actualVersionRaw;
final boolean isVersionFileNotFound = actualVersionRaw.equals(-1);
final Integer actualVersion = isVersionFileNotFound ? 1 : actualVersionRaw;

if (actualVersion.equals(expectedVersion) || isFirstVersionWithoutFile) {
if (isFirstVersionWithoutFile) {
putDatabaseVersion(versionFile, config.databaseVersion());
}
logger.info(String.format("Database directory location: '%s', version: %d", config.databaseDir(), actualVersion));
if (actualVersionRaw.equals(-1)) {
putDatabaseVersion(versionFile, actualVersion);
}

if (actualVersion.equals(expectedVersion) || (isVersionFileNotFound && expectedVersion.equals(1))) {
logger.info("Database directory location: '{}', version: {}", config.databaseDir(), actualVersion);
} else {
if (config.getProperty("database.autoResetOldVersion", false)) {
logger.warn("Detected incompatible database version. Detected:{}, required:{}", actualVersion, expectedVersion);
if (behavior == Behavior.EXIT) {
System.err.println("Please remove database directory manually or set `database.incompatibleDatabaseBehavior` to `RESET`");
System.err.println("Database directory location is " + config.databaseDir());
throw new Error("Incompatible database version " + actualVersion + ". Please remove database " +
"directory manually or set `database.incompatibleDatabaseBehavior` to `RESET`");
} else if (behavior == Behavior.RESET) {
FileUtil.recursiveDelete(config.databaseDir());
logger.info("Detected incompatible database directory: %d", actualVersion);
logger.warn("Auto reset database directory according to flag");
} else {
logger.error("Detected incompatible database version. Detected:%d, required:%d", actualVersion, expectedVersion);
logger.error("Please remove database directory manually or set `database.autoResetOldVersion` to `true`");
logger.error("Database directory location is " + config.databaseDir());
throw new RuntimeException("Incompatible database version " + actualVersion + ". Please remove database " +
"directory manually or set `database.autoResetOldVersion` to `true`");
// IGNORE
logger.info("Continue working according to flag");
}
}
} else {
Expand Down Expand Up @@ -142,7 +147,7 @@ public void putDatabaseVersion(File file, Integer version) {
prop.setProperty("databaseVersion", version.toString());
prop.store(writer, "Generated database version");
} catch (Exception e) {
throw new RuntimeException("Problem writing current database version ", e);
throw new Error("Problem writing current database version ", e);
}
}
}
Expand Down
10 changes: 5 additions & 5 deletions ethereumj-core/src/main/resources/ethereumj.conf
Expand Up @@ -172,12 +172,12 @@ database {
# downloaded from peers again [true/false]
reset = false

# no effect in 1.4.0, only creates version file
# reset database when detected database version is bellow
# than currently opperating version [true/false].
# current database version is stored in `version.properties`
# handling incompatible database version:
# * EXIT - (default) show error in std out and exit by throwing Error
# * RESET - clear database directory and continue working
# * IGNORE - continue working regardless possible issues
# @since 1.4.0
autoResetOldVersion = false
incompatibleDatabaseBehavior = EXIT

# controls state database pruning
# pruned state consumes much less disk space (e.g. 50G full and 1G pruned)
Expand Down
Expand Up @@ -12,21 +12,20 @@

import java.io.File;
import java.io.IOException;
import static org.ethereum.config.Initializer.IncompatibleDatabaseHandler.Behavior;
import static org.ethereum.config.Initializer.IncompatibleDatabaseHandler.Behavior.*;

/**
* Created by Stan Reshetnyk on 11.09.16.
*/
public class InitializerTest {

final Initializer.CheckDatabaseVersionWithReset resetHelper = new Initializer.CheckDatabaseVersionWithReset();
final Initializer.IncompatibleDatabaseHandler resetHelper = new Initializer.IncompatibleDatabaseHandler();

File tempFile;
String databaseDir;
File versionFile;

final static boolean RESET = true;
final static boolean NOT_RESET = false;

@Before
public void before() {
tempFile = Files.createTempDir();
Expand All @@ -42,64 +41,101 @@ public void after() {
// RESET

@Test
public void reset_shouldCreateVersionFile() {
SystemProperties props = withConfig(1, true);
public void helper_shouldCreateVersionFile() {
SystemProperties props = withConfig(1, null);

// state without database
assertEquals(new Integer(-1), resetHelper.getDatabaseVersion(versionFile));
assertTrue(!resetHelper.isDatabaseDirectoryExists(props));

// create database version file
resetHelper.validateDatabaseVersion(props);
resetHelper.process(props);

// state with just created database
assertEquals(new Integer(1), resetHelper.getDatabaseVersion(versionFile));
assertTrue(resetHelper.isDatabaseDirectoryExists(props));

// running validate for a second time should change nothing
resetHelper.validateDatabaseVersion(props);
// running process for a second time should change nothing
resetHelper.process(props);
assertEquals(new Integer(1), resetHelper.getDatabaseVersion(versionFile));
assertTrue(resetHelper.isDatabaseDirectoryExists(props));
}

@Test(expected = RuntimeException.class)
public void reset_shouldStop_whenNoVersionFileAndNotFirstVersion() throws IOException {
SystemProperties props = withConfig(2, NOT_RESET);
resetHelper.validateDatabaseVersion(props);
@Test
public void helper_shouldCreateVersionFile_whenOldVersion() {
// create database without version
SystemProperties props1 = withConfig(1, null);
resetHelper.process(props1);
versionFile.renameTo(new File(versionFile.getAbsoluteFile() + ".renamed"));

SystemProperties props2 = withConfig(2, IGNORE);
resetHelper.process(props2);

assertEquals(new Integer(1), resetHelper.getDatabaseVersion(versionFile));
assertTrue(resetHelper.isDatabaseDirectoryExists(props2));
}

@Test(expected = Error.class)
public void helper_shouldStop_whenNoVersionFileAndNotFirstVersion() throws IOException {
SystemProperties props = withConfig(2, EXIT);
resetHelper.process(props);

// database is assumed to exist if dir is not empty
versionFile.renameTo(new File(versionFile.getAbsoluteFile() + ".renamed"));

resetHelper.validateDatabaseVersion(props);
resetHelper.process(props);
}


@Test
public void reset_shouldReset_whenDifferentVersionAndFlag() {
SystemProperties props1 = withConfig(1, true);
resetHelper.validateDatabaseVersion(props1);
public void helper_shouldReset_whenDifferentVersionAndFlag() {
SystemProperties props1 = withConfig(1, null);
resetHelper.process(props1);

SystemProperties props2 = withConfig(2, RESET);
resetHelper.validateDatabaseVersion(props2);
resetHelper.process(props2);
assertTrue(!resetHelper.isDatabaseDirectoryExists(props2));
}

@Test(expected = RuntimeException.class)
public void reset_shouldNotReset_whenDifferentVersionAndNoFlag() {
final SystemProperties props1 = withConfig(1, true);
resetHelper.validateDatabaseVersion(props1);
@Test(expected = Error.class)
public void helper_shouldExit_whenDifferentVersionAndFlag() {
final SystemProperties props1 = withConfig(1, null);
resetHelper.process(props1);

final SystemProperties props2 = withConfig(2, NOT_RESET);
resetHelper.validateDatabaseVersion(props2);
final SystemProperties props2 = withConfig(2, EXIT);
resetHelper.process(props2);
}

@Test(expected = Error.class)
public void helper_shouldExit_byDefault() {
final SystemProperties props1 = withConfig(1, null);
resetHelper.process(props1);

final SystemProperties props2 = withConfig(2, null);
resetHelper.process(props2);
}

@Test
public void helper_shouldIgnore_whenDifferentVersionAndFlag() {
final SystemProperties props1 = withConfig(1, EXIT);
resetHelper.process(props1);

final SystemProperties props2 = withConfig(2, IGNORE);
resetHelper.process(props2);
assertTrue(resetHelper.isDatabaseDirectoryExists(props2));
assertEquals(new Integer(1), resetHelper.getDatabaseVersion(versionFile));
}


// HELPERS

private SystemProperties withConfig(int databaseVersion, boolean autoResetOldVersion) {
Config config = ConfigFactory.empty()
.withValue("database.autoResetOldVersion", ConfigValueFactory.fromAnyRef(autoResetOldVersion));
private SystemProperties withConfig(int databaseVersion, Behavior behavior) {
Config config = ConfigFactory.empty();

if (behavior != null) {
config = config.withValue("database.incompatibleDatabaseBehavior",
ConfigValueFactory.fromAnyRef(behavior.toString().toLowerCase()));
}


SPO systemProperties = new SPO(config);
systemProperties.setDataBaseDir(databaseDir);
Expand Down

0 comments on commit 47efe11

Please sign in to comment.