blob: fc8c5c483cf7ac4acd7ab430a8b5f33d2dbb3ffb [file] [log] [blame]
package org.apache.helix.controller.changedetector;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.helix.HelixConstants;
import org.apache.helix.ZNRecord;
import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
import org.apache.helix.model.ClusterConfig;
import org.apache.helix.model.IdealState;
import org.apache.helix.model.InstanceConfig;
import org.apache.helix.model.LiveInstance;
import org.apache.helix.model.ResourceConfig;
/**
* ResourceChangeSnapshot is a POJO that contains the following Helix metadata:
* 1. InstanceConfig
* 2. IdealState
* 3. ResourceConfig
* 4. LiveInstance
* 5. Changed property types
* It serves as a snapshot of the main controller cache to enable the difference (change)
* calculation between two rounds of the pipeline run.
*/
class ResourceChangeSnapshot {
private Set<HelixConstants.ChangeType> _changedTypes;
private Map<String, InstanceConfig> _instanceConfigMap;
private Map<String, IdealState> _idealStateMap;
private Map<String, ResourceConfig> _resourceConfigMap;
private Map<String, LiveInstance> _liveInstances;
private ClusterConfig _clusterConfig;
/**
* Default constructor that constructs an empty snapshot.
*/
ResourceChangeSnapshot() {
_changedTypes = new HashSet<>();
_instanceConfigMap = new HashMap<>();
_idealStateMap = new HashMap<>();
_resourceConfigMap = new HashMap<>();
_liveInstances = new HashMap<>();
_clusterConfig = null;
}
/**
* Constructor using controller cache (ResourceControllerDataProvider).
*
* @param dataProvider
* @param ignoreControllerGeneratedFields if true, the snapshot won't record any changes that is
* being modified by the controller.
*/
ResourceChangeSnapshot(ResourceControllerDataProvider dataProvider,
boolean ignoreControllerGeneratedFields) {
_changedTypes = new HashSet<>(dataProvider.getRefreshedChangeTypes());
_instanceConfigMap = new HashMap<>(dataProvider.getInstanceConfigMap());
_idealStateMap = new HashMap<>(dataProvider.getIdealStates());
if (ignoreControllerGeneratedFields && (
dataProvider.getClusterConfig().isPersistBestPossibleAssignment() || dataProvider
.getClusterConfig().isPersistIntermediateAssignment())) {
for (String resourceName : _idealStateMap.keySet()) {
_idealStateMap.put(resourceName, trimIdealState(_idealStateMap.get(resourceName)));
}
}
_resourceConfigMap = new HashMap<>(dataProvider.getResourceConfigMap());
_liveInstances = new HashMap<>(dataProvider.getLiveInstances());
_clusterConfig = dataProvider.getClusterConfig();
}
/**
* Copy constructor for ResourceChangeCache.
* @param snapshot
*/
ResourceChangeSnapshot(ResourceChangeSnapshot snapshot) {
_changedTypes = new HashSet<>(snapshot._changedTypes);
_instanceConfigMap = new HashMap<>(snapshot._instanceConfigMap);
_idealStateMap = new HashMap<>(snapshot._idealStateMap);
_resourceConfigMap = new HashMap<>(snapshot._resourceConfigMap);
_liveInstances = new HashMap<>(snapshot._liveInstances);
_clusterConfig = snapshot._clusterConfig;
}
Set<HelixConstants.ChangeType> getChangedTypes() {
return _changedTypes;
}
Map<String, InstanceConfig> getInstanceConfigMap() {
return _instanceConfigMap;
}
Map<String, IdealState> getIdealStateMap() {
return _idealStateMap;
}
Map<String, ResourceConfig> getResourceConfigMap() {
return _resourceConfigMap;
}
Map<String, LiveInstance> getLiveInstances() {
return _liveInstances;
}
ClusterConfig getClusterConfig() {
return _clusterConfig;
}
// Trim the IdealState to exclude any controller modified information.
private IdealState trimIdealState(IdealState originalIdealState) {
// Clone the IdealState to avoid modifying the objects in the Cluster Data Cache, which might
// be used by the other stages in the pipeline.
IdealState trimmedIdealState = new IdealState(originalIdealState.getRecord());
ZNRecord trimmedIdealStateRecord = trimmedIdealState.getRecord();
switch (originalIdealState.getRebalanceMode()) {
// WARNING: the IdealState copy constructor is not really deep copy. So we should not modify
// the values directly or the cached values will be changed.
case FULL_AUTO:
// For FULL_AUTO resources, both map fields and list fields are not considered as data input
// for the controller. The controller will write to these two types of fields for persisting
// the assignment mapping.
trimmedIdealStateRecord.setListFields(trimmedIdealStateRecord.getListFields().keySet().stream().collect(
Collectors.toMap(partition -> partition, partition -> Collections.emptyList())));
// Continue to clean up map fields in the SEMI_AUTO case.
case SEMI_AUTO:
// For SEMI_AUTO resources, map fields are not considered as data input for the controller.
// The controller will write to the map fields for persisting the assignment mapping.
trimmedIdealStateRecord.setMapFields(trimmedIdealStateRecord.getMapFields().keySet().stream().collect(
Collectors.toMap(partition -> partition, partition -> Collections.emptyMap())));
break;
default:
break;
}
return trimmedIdealState;
}
}