use of org.apache.helix.store.HelixPropertyStore in project ambry by linkedin.
the class HelixBootstrapUpgradeUtil method addUpdateResources.
/**
* Add and/or update resources in Helix based on the information in the static cluster map. This may involve adding
* or removing partitions from under a resource, and adding or dropping resources altogether. This may also involve
* changing the instance set for a partition under a resource, based on the static cluster map.
* @param dcName the name of the datacenter being processed.
* @param partitionsToInstancesInDc a map to be filled with the mapping of partitions to their instance sets in the
* given datacenter.
*/
private void addUpdateResources(String dcName, Map<String, Set<String>> partitionsToInstancesInDc) {
HelixAdmin dcAdmin = adminForDc.get(dcName);
List<String> resourcesInCluster = dcAdmin.getResourcesInCluster(clusterName);
List<String> instancesWithDisabledPartition = new ArrayList<>();
HelixPropertyStore<ZNRecord> helixPropertyStore = helixAdminOperation == HelixAdminOperation.DisablePartition ? createHelixPropertyStore(dcName) : null;
// maxResource may vary from one dc to another (special partition class allows partitions to exist in one dc only)
int maxResource = -1;
for (String resourceName : resourcesInCluster) {
boolean resourceModified = false;
if (!resourceName.matches("\\d+")) {
// cluster map. These will be ignored.
continue;
}
maxResource = Math.max(maxResource, Integer.parseInt(resourceName));
IdealState resourceIs = dcAdmin.getResourceIdealState(clusterName, resourceName);
for (String partitionName : new HashSet<>(resourceIs.getPartitionSet())) {
Set<String> instanceSetInHelix = resourceIs.getInstanceSet(partitionName);
Set<String> instanceSetInStatic = partitionsToInstancesInDc.remove(partitionName);
if (instanceSetInStatic == null || instanceSetInStatic.isEmpty()) {
if (forceRemove) {
info("[{}] *** Partition {} no longer present in the static clustermap, {} *** ", dcName.toUpperCase(), partitionName, dryRun ? "no action as dry run" : "removing from Resource");
// Helix team is planning to provide an API for this.
if (!dryRun) {
resourceIs.getRecord().getListFields().remove(partitionName);
}
resourceModified = true;
} else {
info("[{}] *** forceRemove option not provided, resources will not be removed (use --forceRemove to forcefully remove)", dcName.toUpperCase());
expectMoreInHelixDuringValidate = true;
partitionsNotForceRemovedByDc.computeIfAbsent(dcName, k -> ConcurrentHashMap.newKeySet()).add(partitionName);
}
} else if (!instanceSetInStatic.equals(instanceSetInHelix)) {
// we change the IdealState only when the operation is meant to bootstrap cluster or indeed update IdealState
if (EnumSet.of(HelixAdminOperation.UpdateIdealState, HelixAdminOperation.BootstrapCluster).contains(helixAdminOperation)) {
// @formatter:off
info("[{}] Different instance sets for partition {} under resource {}. {}. " + "Previous instance set: [{}], new instance set: [{}]", dcName.toUpperCase(), partitionName, resourceName, dryRun ? "No action as dry run" : "Updating Helix using static", String.join(",", instanceSetInHelix), String.join(",", instanceSetInStatic));
// @formatter:on
if (!dryRun) {
ArrayList<String> newInstances = new ArrayList<>(instanceSetInStatic);
Collections.shuffle(newInstances);
resourceIs.setPreferenceList(partitionName, newInstances);
// Existing resources may not have ANY_LIVEINSTANCE set as the numReplicas (which allows for different
// replication for different partitions under the same resource). So set it here (We use the name() method and
// not the toString() method for the enum as that is what Helix uses).
resourceIs.setReplicas(ResourceConfig.ResourceConfigConstants.ANY_LIVEINSTANCE.name());
}
resourceModified = true;
} else if (helixAdminOperation == HelixAdminOperation.DisablePartition) {
// if this is DisablePartition operation, we don't modify IdealState and only make InstanceConfig to disable
// certain partition on specific node.
// 1. extract difference between Helix and Static instance sets. Determine which replica is removed
instanceSetInHelix.removeAll(instanceSetInStatic);
// 2. disable removed replica on certain node.
for (String instanceInHelixOnly : instanceSetInHelix) {
info("Partition {} under resource {} on node {} is no longer in static clustermap. {}.", partitionName, resourceName, instanceInHelixOnly, dryRun ? "No action as dry run" : "Disabling it");
if (!dryRun) {
InstanceConfig instanceConfig = dcAdmin.getInstanceConfig(clusterName, instanceInHelixOnly);
String instanceName = instanceConfig.getInstanceName();
// on updating InstanceConfig until this tool has completed. TODO, remove this logic once migration to PropertyStore is done.
if (!instancesWithDisabledPartition.contains(instanceName)) {
ZNRecord znRecord = new ZNRecord(instanceName);
String path = PARTITION_DISABLED_ZNODE_PATH + instanceName;
if (!helixPropertyStore.create(path, znRecord, AccessOption.PERSISTENT)) {
logger.error("Failed to create a ZNode for {} in datacenter {} before disabling partition.", instanceName, dcName);
continue;
}
}
instanceConfig.setInstanceEnabledForPartition(resourceName, partitionName, false);
dcAdmin.setInstanceConfig(clusterName, instanceInHelixOnly, instanceConfig);
instancesWithDisabledPartition.add(instanceName);
}
partitionsDisabled.getAndIncrement();
}
// Disabling partition won't remove certain replica from IdealState. So replicas of this partition in Helix
// will be more than that in static clustermap.
expectMoreInHelixDuringValidate = true;
}
}
}
// update state model def if necessary
if (!resourceIs.getStateModelDefRef().equals(stateModelDef)) {
info("[{}] Resource {} has different state model {}. Updating it with {}", dcName.toUpperCase(), resourceName, resourceIs.getStateModelDefRef(), stateModelDef);
resourceIs.setStateModelDefRef(stateModelDef);
resourceModified = true;
}
resourceIs.setNumPartitions(resourceIs.getPartitionSet().size());
if (resourceModified) {
if (resourceIs.getPartitionSet().isEmpty()) {
info("[{}] Resource {} has no partition, {}", dcName.toUpperCase(), resourceName, dryRun ? "no action as dry run" : "dropping");
if (!dryRun) {
dcAdmin.dropResource(clusterName, resourceName);
}
resourcesDropped.getAndIncrement();
} else {
if (!dryRun) {
dcAdmin.setResourceIdealState(clusterName, resourceName, resourceIs);
System.out.println("------------------add resource!");
System.out.println(resourceName);
System.out.println(resourceName);
}
resourcesUpdated.getAndIncrement();
}
}
}
// note that disabling partition also updates InstanceConfig of certain nodes which host the partitions.
instancesUpdated.getAndAdd(instancesWithDisabledPartition.size());
// replica decommission thread on each datanode.
if (helixPropertyStore != null) {
maybeAwaitForLatch();
for (String instanceName : instancesWithDisabledPartition) {
String path = PARTITION_DISABLED_ZNODE_PATH + instanceName;
if (!helixPropertyStore.remove(path, AccessOption.PERSISTENT)) {
logger.error("Failed to remove a ZNode for {} in datacenter {} after disabling partition completed.", instanceName, dcName);
}
}
helixPropertyStore.stop();
}
// Add what is not already in Helix under new resources.
int fromIndex = 0;
List<Map.Entry<String, Set<String>>> newPartitions = new ArrayList<>(partitionsToInstancesInDc.entrySet());
while (fromIndex < newPartitions.size()) {
String resourceName = Integer.toString(++maxResource);
int toIndex = Math.min(fromIndex + maxPartitionsInOneResource, newPartitions.size());
List<Map.Entry<String, Set<String>>> partitionsUnderNextResource = newPartitions.subList(fromIndex, toIndex);
fromIndex = toIndex;
IdealState idealState = new IdealState(resourceName);
idealState.setStateModelDefRef(stateModelDef);
info("[{}] Adding partitions for next resource {} in {}. {}.", dcName.toUpperCase(), resourceName, dcName, dryRun ? "Actual IdealState is not changed as dry run" : "IdealState is being updated");
for (Map.Entry<String, Set<String>> entry : partitionsUnderNextResource) {
String partitionName = entry.getKey();
ArrayList<String> instances = new ArrayList<>(entry.getValue());
Collections.shuffle(instances);
idealState.setPreferenceList(partitionName, instances);
}
idealState.setNumPartitions(partitionsUnderNextResource.size());
idealState.setReplicas(ResourceConfig.ResourceConfigConstants.ANY_LIVEINSTANCE.name());
if (!idealState.isValid()) {
throw new IllegalStateException("IdealState could not be validated for new resource " + resourceName);
}
if (!dryRun) {
dcAdmin.addResource(clusterName, resourceName, idealState);
info("[{}] Added {} new partitions under resource {} in datacenter {}", dcName.toUpperCase(), partitionsUnderNextResource.size(), resourceName, dcName);
} else {
info("[{}] Under DryRun mode, {} new partitions are added to resource {} in datacenter {}", dcName.toUpperCase(), partitionsUnderNextResource.size(), resourceName, dcName);
}
resourcesAdded.getAndIncrement();
}
}
Aggregations