
io.servicetalk.client.api.internal.partition.PowerSetPartitionMap Maven / Gradle / Ivy
/*
* Copyright © 2018 Apple Inc. and the ServiceTalk project authors
*
* 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 io.servicetalk.client.api.internal.partition;
import io.servicetalk.client.api.ClientGroup;
import io.servicetalk.client.api.partition.PartitionAttributes;
import io.servicetalk.client.api.partition.PartitionAttributesBuilder;
import io.servicetalk.client.api.partition.PartitionMap;
import io.servicetalk.concurrent.api.AsyncCloseable;
import io.servicetalk.concurrent.api.Completable;
import io.servicetalk.concurrent.api.ListenableAsyncCloseable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.function.IntFunction;
import javax.annotation.Nullable;
import static io.servicetalk.concurrent.api.AsyncCloseables.toAsyncCloseable;
import static java.lang.Integer.bitCount;
import static java.lang.Integer.numberOfTrailingZeros;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
import static java.util.Objects.requireNonNull;
/**
* A {@link PartitionMap} that creates the full power set using the individual attributes in
* {@link PartitionAttributes}es to create partitions for each {@link #add(PartitionAttributes)}.
*
* @param The partition type.
* @deprecated We are unaware of anyone using "partition" feature and plan to remove it in future releases.
* If you depend on it, consider using {@link ClientGroup} as an alternative or reach out to the maintainers describing
* the use-case.
*/
@Deprecated
public final class PowerSetPartitionMap implements PartitionMap {
private static final byte CLOSED_GRACEFULLY = 1;
private static final byte HARD_CLOSE = 2;
private static final int MAX_PARTITION_ATTRIBUTE_SIZE = 15;
private final Function valueFactory;
private final IntFunction partitionAttributesBuilderFunc;
/**
* Key = Absolute Attribute
* Value = List of Wild Card Attributes. Each value must be a key in {@link #wildCardToValueMap}.
*
* This map is used to prevent a complete iteration over {@link #wildCardToValueMap} when an attributes needs to
* be removed. Since {@link #wildCardToValueMap} holds combinations it is expected to be big and we should avoid
* iterating it when possible.
*/
private final Map> absoluteToWildCardIndexMap;
/**
* Key = Wild Card Attribute.
* Value = Object associated with the wildcard attributes.
*
* This map contains all combinations of the attributes of each absolute Attribute to provide a fast lookup when
* given a Wild Card Attributes.
*
This map and the values contained within are unmodifiable and modifications are treated as copy on write.
* This approach allows the {@link #get(PartitionAttributes)} method which reads from this map and is expected to be
* called much more frequently than other methods which modify to the map can function with minimal synchronization.
*/
private volatile Map> wildCardToValueMap;
private volatile byte closed;
private final ListenableAsyncCloseable asyncCloseable = toAsyncCloseable(graceful -> {
closed = graceful ? CLOSED_GRACEFULLY : HARD_CLOSE;
return closeAllValues(wildCardToValueMap, graceful);
});
/**
* Create a new instance with the {@link DefaultPartitionAttributesBuilder}.
* @param valueFactory Generates values for new partitions.
*/
PowerSetPartitionMap(Function valueFactory) {
this(valueFactory, DefaultPartitionAttributesBuilder::new);
}
/**
* Create a new instance.
* @param valueFactory Generates values for new partitions.
* @param partitionAttributesBuilderFunc Generates new {@link PartitionAttributes} objects, this factory must be
* consistent with the factory used to build the {@link PartitionAttributes} objects for
* {@link #add(PartitionAttributes)} and {@link #remove(PartitionAttributes)} to ensure {@link #hashCode()} and
* {@link #equals(Object)} are consistent.
*/
public PowerSetPartitionMap(Function valueFactory,
IntFunction partitionAttributesBuilderFunc) {
this.valueFactory = requireNonNull(valueFactory);
this.partitionAttributesBuilderFunc = requireNonNull(partitionAttributesBuilderFunc);
absoluteToWildCardIndexMap = new HashMap<>();
wildCardToValueMap = emptyMap();
}
@Override
public T get(@Nullable PartitionAttributes wildCardAttributes) {
ValueHolder valueHolder = wildCardToValueMap.get(wildCardAttributes);
return valueHolder == null ? null : valueHolder.value;
}
@Override
public List add(final PartitionAttributes partition) {
final int partitionSize = partition.size();
if (partitionSize <= 0 || partitionSize > MAX_PARTITION_ATTRIBUTE_SIZE) {
throw new IllegalArgumentException("attribute size: " + partitionSize + " must be in the range [0," +
MAX_PARTITION_ATTRIBUTE_SIZE + ")");
}
// Make a copy of the current wildCardToValueMap because we will copy/swap
Map> newWildCardToAttributes = new HashMap<>(wildCardToValueMap);
// Calculate all combinations of attributes from the new absoluteAttributes map
// This involves first putting the elements into a fixed array, and then iterate over that array
// and select all unique subsets to make all combinations.
List