Skip to content

Commit

Permalink
optimized the reset code and add a custom EphemeryException
Browse files Browse the repository at this point in the history
Signed-off-by: gconnect <agatevureglory@gmail.com>
  • Loading branch information
gconnect committed Sep 30, 2024
1 parent d496675 commit 1bbc3c4
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 128 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
import static tech.pegasys.teku.infrastructure.async.AsyncRunnerFactory.DEFAULT_MAX_QUEUE_SIZE;
import static tech.pegasys.teku.spec.config.Constants.STORAGE_QUERY_CHANNEL_PARALLELISM;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
Expand All @@ -42,6 +45,7 @@
import tech.pegasys.teku.storage.server.RetryingStorageUpdateChannel;
import tech.pegasys.teku.storage.server.StorageConfiguration;
import tech.pegasys.teku.storage.server.VersionedDatabaseFactory;
import tech.pegasys.teku.storage.server.network.EphemeryException;
import tech.pegasys.teku.storage.server.pruner.BlobSidecarPruner;
import tech.pegasys.teku.storage.server.pruner.BlockPruner;
import tech.pegasys.teku.storage.server.pruner.StatePruner;
Expand Down Expand Up @@ -80,14 +84,24 @@ protected SafeFuture<?> doStart() {
1,
DEFAULT_MAX_QUEUE_SIZE,
Thread.NORM_PRIORITY - 1);
final VersionedDatabaseFactory dbFactory =
VersionedDatabaseFactory dbFactory =
new VersionedDatabaseFactory(
serviceConfig.getMetricsSystem(),
serviceConfig.getDataDirLayout().getBeaconDataDirectory(),
config);
database = dbFactory.createDatabase();

database.migrate();
try {
database = dbFactory.createDatabase();
database.migrate();
} catch (EphemeryException e) {
try {
resetDatabaseDirectories(serviceConfig);
database = dbFactory.createDatabase();
database.migrate();
} catch (Exception ex) {
throw new RuntimeException("Failed to reset and recreate the database.", ex);
}
}

final SettableLabelledGauge pruningTimingsLabelledGauge =
SettableLabelledGauge.create(
Expand Down Expand Up @@ -222,4 +236,30 @@ protected SafeFuture<?> doStop() {
public ChainStorage getChainStorage() {
return chainStorage;
}

/** This method is called only on Ephemery network when reset is due. */
void resetDatabaseDirectories(final ServiceConfig serviceConfig) throws IOException {
final Path beaconDataDir = serviceConfig.getDataDirLayout().getBeaconDataDirectory();
final Path slashProtectionDir =
serviceConfig.getDataDirLayout().getValidatorDataDirectory().resolve("slashprotection");
deleteDirectoryRecursively(beaconDataDir);
deleteDirectoryRecursively(slashProtectionDir);
}

private void deleteDirectoryRecursively(final Path path) throws IOException {
if (Files.isDirectory(path)) {
try (var stream = Files.walk(path)) {
stream
.sorted((o1, o2) -> o2.compareTo(o1))
.forEach(
p -> {
try {
Files.delete(p);
} catch (IOException e) {
throw new RuntimeException("Failed to delete file: " + p, e);
}
});
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,13 @@

package tech.pegasys.teku.storage.server;

import static com.fasterxml.jackson.dataformat.yaml.YAMLGenerator.Feature.WRITE_DOC_START_MARKER;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.google.common.annotations.VisibleForTesting;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Locale;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.tuweni.bytes.Bytes;
Expand All @@ -51,8 +46,6 @@ public class VersionedDatabaseFactory implements DatabaseFactory {
@VisibleForTesting static final String STORAGE_MODE_PATH = "data-storage-mode.txt";
@VisibleForTesting static final String METADATA_FILENAME = "metadata.yml";
@VisibleForTesting static final String NETWORK_FILENAME = "network.yml";
private static final String EPHEMERY_DEPOSIT_CONTRACT_ADDRESS =
"0x4242424242424242424242424242424242424242";
private final MetricsSystem metricsSystem;
private final File dataDirectory;
private final int maxKnownNodeCacheSize;
Expand Down Expand Up @@ -94,7 +87,6 @@ public VersionedDatabaseFactory(
public Database createDatabase() {
LOG.info("Beacon data directory set to: {}", dataDirectory.getAbsolutePath());
validateDataPaths();
resetDatabaseDirectories();
final DatabaseVersion dbVersion = getDatabaseVersion();
createDirectories(dbVersion);
saveDatabaseVersion(dbVersion);
Expand Down Expand Up @@ -391,74 +383,4 @@ private void saveStorageMode(final StateStorageMode storageMode) {
e);
}
}

@VisibleForTesting
public boolean shouldResetForEphemery() {
File networkFile = getNetworkFile();
if (!networkFile.exists()) {
return false;
}
try {
Long depositChainId = spec.getGenesisSpecConfig().getDepositChainId();
String depositContractString = eth1Address.toHexString().toLowerCase(Locale.ROOT);
String networkContent = Files.readString(networkFile.toPath());

ObjectMapper objectMapper =
new ObjectMapper(new YAMLFactory().disable(WRITE_DOC_START_MARKER));
DatabaseNetwork readDatabaseNetwork =
objectMapper.readerFor(DatabaseNetwork.class).readValue(networkFile);

return depositContractString.equals(EPHEMERY_DEPOSIT_CONTRACT_ADDRESS)
&& networkContent.contains("deposit_chain_id")
&& !readDatabaseNetwork.getDepositChainId().equals(depositChainId);

} catch (IOException e) {
throw new RuntimeException(
String.format(
"Failed to read network file at %s during reset check",
networkFile.getAbsolutePath()),
e);
}
}

@VisibleForTesting
public void resetDatabaseDirectories() {
File networkFile = getNetworkFile();
try {
if (dbDirectory.exists()) {
deleteDirectoryRecursively(dbDirectory.toPath());
}
if (v5ArchiveDirectory.exists()) {
deleteDirectoryRecursively(v5ArchiveDirectory.toPath());
}
if (networkFile.exists()) {
if (shouldResetForEphemery()) {
Files.delete(networkFile.toPath());
}
}
} catch (IOException e) {
throw DatabaseStorageException.unrecoverable(
String.format(
"Failed to reset database directories or network file at %s",
networkFile.getAbsolutePath()),
e);
}
}

private void deleteDirectoryRecursively(final Path path) throws IOException {
if (Files.isDirectory(path)) {
try (var stream = Files.walk(path)) {
stream
.sorted((o1, o2) -> o2.compareTo(o1))
.forEach(
p -> {
try {
Files.delete(p);
} catch (IOException e) {
throw new RuntimeException("Failed to delete file: " + p, e);
}
});
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ public static DatabaseNetwork init(
String.valueOf(depositChainId),
String.valueOf(databaseNetwork.depositChainId)));
}
if (databaseNetwork.depositChainId != null
&& depositContractString.equals(EPHEMERY_DEPOSIT_CONTRACT_ADDRESS)
&& !databaseNetwork.depositChainId.equals(depositChainId)) {
throw new EphemeryException();
}
return databaseNetwork;
} else {
DatabaseNetwork databaseNetwork =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright Consensys Software Inc., 2024
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/

package tech.pegasys.teku.storage.server.network;

public class EphemeryException extends RuntimeException {}
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,6 @@

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static tech.pegasys.teku.storage.server.StateStorageMode.PRUNE;

import java.io.File;
Expand Down Expand Up @@ -144,48 +139,6 @@ public void createDatabase_shouldAllowAllSupportedDatabases(final DatabaseVersio
assertThat(dbFactory.getDatabaseVersion()).isEqualTo(version);
}

@Test
void shouldOnlyResetWhenOnEphemeryAndDueForReset() throws Exception {
VersionedDatabaseFactory dbFactorySpy =
spy(
new VersionedDatabaseFactory(
new StubMetricsSystem(),
dataDir,
StorageConfiguration.builder()
.specProvider(spec)
.eth1DepositContract(eth1Address)
.build()));

doReturn(true).when(dbFactorySpy).shouldResetForEphemery();
dbFactorySpy.resetDatabaseDirectories();
verify(dbFactorySpy, times(1)).resetDatabaseDirectories();

try (final Database db = dbFactorySpy.createDatabase()) {
assertThat(db).isNotNull();
verify(dbFactorySpy, times(1)).createDatabase();
}
}

@Test
void shouldNotResetWhenOnEphemeryButNotDueForRestAndWhenNotOnEphemery() throws Exception {
VersionedDatabaseFactory dbFactorySpy =
spy(
new VersionedDatabaseFactory(
new StubMetricsSystem(),
dataDir,
StorageConfiguration.builder()
.specProvider(spec)
.eth1DepositContract(eth1Address)
.build()));

doReturn(false).when(dbFactorySpy).shouldResetForEphemery();
verifyNoInteractions(dbFactorySpy);
try (final Database db = dbFactorySpy.createDatabase()) {
assertThat(db).isNotNull();
verify(dbFactorySpy, times(1)).createDatabase();
}
}

private void createDbDirectory(final Path dataPath) {
final File dbDirectory =
Paths.get(dataPath.toAbsolutePath().toString(), VersionedDatabaseFactory.DB_PATH).toFile();
Expand Down

0 comments on commit 1bbc3c4

Please sign in to comment.