Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preserve logic via dependency migration test #9723

Merged
merged 2 commits into from
Nov 7, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
import com.hedera.node.app.fees.congestion.EntityUtilizationMultiplier;
import com.hedera.node.app.fees.congestion.ThrottleMultiplier;
import com.hedera.node.app.ids.EntityIdService;
import com.hedera.node.app.ids.WritableEntityIdStore;
import com.hedera.node.app.info.CurrentPlatformStatusImpl;
import com.hedera.node.app.info.NetworkInfoImpl;
import com.hedera.node.app.info.SelfNodeInfoImpl;
Expand All @@ -57,7 +56,6 @@
import com.hedera.node.app.spi.workflows.record.GenesisRecordsBuilder;
import com.hedera.node.app.state.HederaState;
import com.hedera.node.app.state.merkle.MerkleHederaState;
import com.hedera.node.app.state.merkle.MerkleSchemaRegistry;
import com.hedera.node.app.state.recordcache.RecordCacheService;
import com.hedera.node.app.throttle.CongestionThrottleService;
import com.hedera.node.app.throttle.SynchronizedThrottleAccumulator;
Expand Down Expand Up @@ -98,7 +96,6 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import java.util.function.IntSupplier;
import org.apache.logging.log4j.LogManager;
Expand Down Expand Up @@ -422,53 +419,8 @@ private void onMigrate(
final var selfNodeInfo = SelfNodeInfoImpl.of(nodeAddress, version);
final var networkInfo = new NetworkInfoImpl(selfNodeInfo, platform, bootstrapConfigProvider);

logger.info("Migrating Entity ID Service as pre-requisite for other services");
final var entityIdRegistration = servicesRegistry.registrations().stream()
.filter(service -> EntityIdService.NAME.equals(service.service().getServiceName()))
.findFirst()
.orElseThrow();
final var entityIdRegistry = (MerkleSchemaRegistry) entityIdRegistration.registry();
entityIdRegistry.migrate(
state,
previousVersion,
currentVersion,
configProvider.getConfiguration(),
networkInfo,
backendThrottle,
// We call with null here because we're migrating the entity ID service itself
null);
// Now that the Entity ID Service is migrated, migrate the remaining services
servicesRegistry.registrations().stream()
.filter(r -> !Objects.equals(entityIdRegistration, r))
.forEach(registration -> {
// FUTURE We should have metrics here to keep track of how long it takes to migrate each service
final var service = registration.service();
final var serviceName = service.getServiceName();
logger.info("Migrating Service {}", serviceName);
final var registry = (MerkleSchemaRegistry) registration.registry();

// The token service has a dependency on the entity ID service during genesis migrations, so we
// CAREFULLY create a different WritableStates specific to the entity ID service. The different
// WritableStates instances won't be able to see the changes made by each other, but there shouldn't
// be any conflicting changes. We'll inject this into the MigrationContext below to enable
// generation of entity IDs.
final var entityIdWritableStates = state.createWritableStates(EntityIdService.NAME);
final var entityIdStore = new WritableEntityIdStore(entityIdWritableStates);

registry.migrate(
state,
previousVersion,
currentVersion,
configProvider.getConfiguration(),
networkInfo,
backendThrottle,
requireNonNull(entityIdStore));
// Now commit any changes that were made to the entity ID state (since other service entities could
// depend on newly-generated entity IDs)
if (entityIdWritableStates instanceof MerkleHederaState.MerkleWritableStates mws) {
mws.commit();
}
});
final var migrator = new OrderedServiceMigrator(servicesRegistry, backendThrottle);
migrator.doMigrations(state, currentVersion, previousVersion, configProvider.getConfiguration(), networkInfo);
logger.info("Migration complete");
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* Copyright (C) 2023 Hedera Hashgraph, LLC
*
* 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 com.hedera.node.app;

import static java.util.Objects.requireNonNull;

import com.hedera.hapi.node.base.SemanticVersion;
import com.hedera.node.app.ids.EntityIdService;
import com.hedera.node.app.ids.WritableEntityIdStore;
import com.hedera.node.app.services.ServicesRegistry;
import com.hedera.node.app.services.ServicesRegistryImpl;
import com.hedera.node.app.spi.info.NetworkInfo;
import com.hedera.node.app.state.merkle.MerkleHederaState;
import com.hedera.node.app.state.merkle.MerkleSchemaRegistry;
import com.hedera.node.app.throttle.ThrottleAccumulator;
import com.hedera.node.config.VersionedConfiguration;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.util.Objects;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/**
* The entire purpose of this class is to ensure that inter-service dependencies are respected between
* migrations. The only required dependency right now is the {@link EntityIdService}, which is needed
* for genesis blocklist accounts.
*/
public class OrderedServiceMigrator {
david-bakin-sl marked this conversation as resolved.
Show resolved Hide resolved
private static final Logger logger = LogManager.getLogger(OrderedServiceMigrator.class);
private final ServicesRegistry servicesRegistry;
private final ThrottleAccumulator backendThrottle;

public OrderedServiceMigrator(
@NonNull final ServicesRegistryImpl servicesRegistry, @NonNull final ThrottleAccumulator backendThrottle) {
david-bakin-sl marked this conversation as resolved.
Show resolved Hide resolved
this.servicesRegistry = requireNonNull(servicesRegistry);
this.backendThrottle = requireNonNull(backendThrottle);
}

/**
* Migrates the services registered with the {@link ServicesRegistry}
*/
public void doMigrations(
@NonNull final MerkleHederaState state,
@NonNull final SemanticVersion currentVersion,
@Nullable final SemanticVersion previousVersion,
@NonNull final VersionedConfiguration versionedConfiguration,
@NonNull final NetworkInfo networkInfo) {
requireNonNull(state);
requireNonNull(currentVersion);
requireNonNull(versionedConfiguration);
requireNonNull(networkInfo);

logger.info("Migrating Entity ID Service as pre-requisite for other services");
final var entityIdRegistration = servicesRegistry.registrations().stream()
.filter(service -> EntityIdService.NAME.equals(service.service().getServiceName()))
.findFirst()
.orElseThrow();
david-bakin-sl marked this conversation as resolved.
Show resolved Hide resolved
final var entityIdRegistry = (MerkleSchemaRegistry) entityIdRegistration.registry();
entityIdRegistry.migrate(
state,
previousVersion,
currentVersion,
versionedConfiguration,
networkInfo,
backendThrottle,
// We call with null here because we're migrating the entity ID service itself
null);

// Now that the Entity ID Service is migrated, migrate the remaining services
servicesRegistry.registrations().stream()
.filter(r -> !Objects.equals(entityIdRegistration, r))
.forEach(registration -> {
// FUTURE We should have metrics here to keep track of how long it takes to
// migrate each service
final var service = registration.service();
final var serviceName = service.getServiceName();
logger.info("Migrating Service {}", serviceName);
final var registry = (MerkleSchemaRegistry) registration.registry();

// The token service has a dependency on the entity ID service during genesis migrations, so we
// CAREFULLY create a different WritableStates specific to the entity ID service. The different
// WritableStates instances won't be able to see the changes made by each other, but there shouldn't
// be any conflicting changes. We'll inject this into the MigrationContext below to enable
david-bakin-sl marked this conversation as resolved.
Show resolved Hide resolved
// generation of entity IDs.
final var entityIdWritableStates = state.createWritableStates(EntityIdService.NAME);
final var entityIdStore = new WritableEntityIdStore(entityIdWritableStates);

registry.migrate(
state,
previousVersion,
currentVersion,
versionedConfiguration,
networkInfo,
backendThrottle,
requireNonNull(entityIdStore));
mhess-swl marked this conversation as resolved.
Show resolved Hide resolved
// Now commit any changes that were made to the entity ID state (since other service entities could
// depend on newly-generated entity IDs)
if (entityIdWritableStates instanceof MerkleHederaState.MerkleWritableStates mws) {
mws.commit();
}
});
}
}
Loading
Loading