com.datastax.oss.driver.api.core.config.OptionsMap Maven / Gradle / Ivy
/*
* 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.
*/
package com.datastax.oss.driver.api.core.config;
import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Consumer;
import net.jcip.annotations.Immutable;
import net.jcip.annotations.ThreadSafe;
/**
* An in-memory repository of config options, for use with {@link
* DriverConfigLoader#fromMap(OptionsMap)}.
*
* This class is intended for clients who wish to assemble the driver configuration in memory,
* instead of loading it from configuration files. Note that {@link #driverDefaults()} can be used
* to pre-initialize the map with the driver's built-in defaults.
*
*
It functions like a two-dimensional map indexed by execution profile and option. All methods
* have a profile-less variant that applies to the default profile, for example {@link #get(String,
* TypedDriverOption)} and {@link #get(TypedDriverOption)}. Options are represented by {@link
* TypedDriverOption}, which allows this class to enforce additional type-safety guarantees (an
* option can only be set to a value of its intended type).
*
*
This class is mutable and thread-safe. Live changes are reflected in real time to the driver
* session(s) that use this configuration.
*
* @since 4.6.0
*/
@ThreadSafe
public class OptionsMap implements Serializable {
private static final long serialVersionUID = 1;
/**
* Creates a new instance that contains the driver's default configuration.
*
*
This will produce a configuration that is equivalent to the {@code reference.conf} file
* bundled with the driver (however, this method does not load any file, and doesn't require
* Typesafe config in the classpath).
*/
@NonNull
public static OptionsMap driverDefaults() {
OptionsMap source = new OptionsMap();
fillWithDriverDefaults(source);
return source;
}
private final ConcurrentHashMap> map;
private final List> changeListeners = new CopyOnWriteArrayList<>();
public OptionsMap() {
this(new ConcurrentHashMap<>());
}
private OptionsMap(ConcurrentHashMap> map) {
this.map = map;
}
/**
* Associates the specified value for the specified option, in the specified execution profile.
*
* @return the previous value associated with {@code option}, or {@code null} if the option was
* not defined.
*/
@Nullable
public ValueT put(
@NonNull String profile, @NonNull TypedDriverOption option, @NonNull ValueT value) {
Objects.requireNonNull(option, "option");
Objects.requireNonNull(value, "value");
Object previous = getProfileMap(profile).put(option.getRawOption(), value);
if (!value.equals(previous)) {
for (Consumer listener : changeListeners) {
listener.accept(this);
}
}
return cast(previous);
}
/**
* Associates the specified value for the specified option, in the default execution profile.
*
* @return the previous value associated with {@code option}, or {@code null} if the option was
* not defined.
*/
@Nullable
public ValueT put(@NonNull TypedDriverOption option, @NonNull ValueT value) {
return put(DriverExecutionProfile.DEFAULT_NAME, option, value);
}
/**
* Returns the value to which the specified option is mapped in the specified profile, or {@code
* null} if the option is not defined.
*/
@Nullable
public ValueT get(@NonNull String profile, @NonNull TypedDriverOption option) {
Objects.requireNonNull(option, "option");
Object result = getProfileMap(profile).get(option.getRawOption());
return cast(result);
}
/**
* Returns the value to which the specified option is mapped in the default profile, or {@code
* null} if the option is not defined.
*/
@Nullable
public ValueT get(@NonNull TypedDriverOption option) {
return get(DriverExecutionProfile.DEFAULT_NAME, option);
}
/**
* Removes the specified option from the specified profile.
*
* @return the previous value associated with {@code option}, or {@code null} if the option was
* not defined.
*/
@Nullable
public ValueT remove(
@NonNull String profile, @NonNull TypedDriverOption option) {
Objects.requireNonNull(option, "option");
Object previous = getProfileMap(profile).remove(option.getRawOption());
if (previous != null) {
for (Consumer listener : changeListeners) {
listener.accept(this);
}
}
return cast(previous);
}
/**
* Removes the specified option from the default profile.
*
* @return the previous value associated with {@code option}, or {@code null} if the option was
* not defined.
*/
@Nullable
public ValueT remove(@NonNull TypedDriverOption option) {
return remove(DriverExecutionProfile.DEFAULT_NAME, option);
}
/**
* Registers a listener that will get notified when this object changes.
*
* This is mostly for internal use by the driver. Note that listeners are transient, and not
* taken into account by {@link #equals(Object)} and {@link #hashCode()}.
*/
public void addChangeListener(@NonNull Consumer listener) {
changeListeners.add(Objects.requireNonNull(listener));
}
/**
* Unregisters a listener that was previously registered with {@link
* #addChangeListener(Consumer)}.
*
* @return {@code true} if the listener was indeed registered for this object.
*/
public boolean removeChangeListener(@NonNull Consumer listener) {
return changeListeners.remove(Objects.requireNonNull(listener));
}
@Override
public boolean equals(Object other) {
if (other == this) {
return true;
} else if (other instanceof OptionsMap) {
OptionsMap that = (OptionsMap) other;
return this.map.equals(that.map);
} else {
return false;
}
}
@Override
public int hashCode() {
return map.hashCode();
}
/**
* Returns a live view of this object, using the driver's untyped {@link DriverOption}.
*
* This is intended for internal usage by the driver. Modifying the resulting map is strongly
* discouraged, as it could break the type-safety guarantees provided by the public methods.
*/
@NonNull
protected Map> asRawMap() {
return map;
}
@NonNull
private Map getProfileMap(@NonNull String profile) {
Objects.requireNonNull(profile, "profile");
return map.computeIfAbsent(profile, p -> new ConcurrentHashMap<>());
}
// Isolate the suppressed warning for retrieval. The cast should always succeed unless the user
// messes with asMap() directly.
@SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"})
@Nullable
private ValueT cast(@Nullable Object value) {
return (ValueT) value;
}
/**
* This object gets replaced by an internal proxy for serialization.
*
* @serialData the serialized form of the {@code Map>} used to
* store options internally (listeners are transient).
*/
private Object writeReplace() {
return new SerializationProxy(this.map);
}
// Should never be called since we serialize a proxy
@SuppressWarnings("UnusedVariable")
private void readObject(ObjectInputStream stream) throws InvalidObjectException {
throw new InvalidObjectException("Proxy required");
}
protected static void fillWithDriverDefaults(OptionsMap map) {
Duration initQueryTimeout = Duration.ofSeconds(5);
Duration requestTimeout = Duration.ofSeconds(2);
int requestPageSize = 5000;
int continuousMaxPages = 0;
int continuousMaxPagesPerSecond = 0;
int continuousMaxEnqueuedPages = 4;
// Sorted by order of appearance in reference.conf:
// Skip CONFIG_RELOAD_INTERVAL because the map-based config doesn't need periodic reloading
map.put(TypedDriverOption.REQUEST_TIMEOUT, requestTimeout);
map.put(TypedDriverOption.REQUEST_CONSISTENCY, "LOCAL_ONE");
map.put(TypedDriverOption.REQUEST_PAGE_SIZE, requestPageSize);
map.put(TypedDriverOption.REQUEST_SERIAL_CONSISTENCY, "SERIAL");
map.put(TypedDriverOption.REQUEST_DEFAULT_IDEMPOTENCE, false);
map.put(TypedDriverOption.GRAPH_TRAVERSAL_SOURCE, "g");
map.put(TypedDriverOption.LOAD_BALANCING_POLICY_CLASS, "DefaultLoadBalancingPolicy");
map.put(TypedDriverOption.LOAD_BALANCING_POLICY_SLOW_AVOIDANCE, true);
map.put(TypedDriverOption.SESSION_LEAK_THRESHOLD, 4);
map.put(TypedDriverOption.CONNECTION_CONNECT_TIMEOUT, Duration.ofSeconds(5));
map.put(TypedDriverOption.CONNECTION_INIT_QUERY_TIMEOUT, initQueryTimeout);
map.put(TypedDriverOption.CONNECTION_SET_KEYSPACE_TIMEOUT, initQueryTimeout);
map.put(TypedDriverOption.CONNECTION_POOL_LOCAL_SIZE, 1);
map.put(TypedDriverOption.CONNECTION_POOL_REMOTE_SIZE, 1);
map.put(TypedDriverOption.CONNECTION_MAX_REQUESTS, 1024);
map.put(TypedDriverOption.CONNECTION_MAX_ORPHAN_REQUESTS, 256);
map.put(TypedDriverOption.CONNECTION_WARN_INIT_ERROR, true);
map.put(TypedDriverOption.RECONNECT_ON_INIT, false);
map.put(TypedDriverOption.RECONNECTION_POLICY_CLASS, "ExponentialReconnectionPolicy");
map.put(TypedDriverOption.RECONNECTION_BASE_DELAY, Duration.ofSeconds(1));
map.put(TypedDriverOption.RECONNECTION_MAX_DELAY, Duration.ofSeconds(60));
map.put(TypedDriverOption.RETRY_POLICY_CLASS, "DefaultRetryPolicy");
map.put(TypedDriverOption.SPECULATIVE_EXECUTION_POLICY_CLASS, "NoSpeculativeExecutionPolicy");
map.put(TypedDriverOption.TIMESTAMP_GENERATOR_CLASS, "AtomicTimestampGenerator");
map.put(TypedDriverOption.TIMESTAMP_GENERATOR_DRIFT_WARNING_THRESHOLD, Duration.ofSeconds(1));
map.put(TypedDriverOption.TIMESTAMP_GENERATOR_DRIFT_WARNING_INTERVAL, Duration.ofSeconds(10));
map.put(TypedDriverOption.TIMESTAMP_GENERATOR_FORCE_JAVA_CLOCK, false);
map.put(TypedDriverOption.REQUEST_THROTTLER_CLASS, "PassThroughRequestThrottler");
map.put(TypedDriverOption.ADDRESS_TRANSLATOR_CLASS, "PassThroughAddressTranslator");
map.put(TypedDriverOption.RESOLVE_CONTACT_POINTS, true);
map.put(TypedDriverOption.PROTOCOL_MAX_FRAME_LENGTH, 256L * 1024 * 1024);
map.put(TypedDriverOption.REQUEST_WARN_IF_SET_KEYSPACE, true);
map.put(TypedDriverOption.REQUEST_TRACE_ATTEMPTS, 5);
map.put(TypedDriverOption.REQUEST_TRACE_INTERVAL, Duration.ofMillis(3));
map.put(TypedDriverOption.REQUEST_TRACE_CONSISTENCY, "ONE");
map.put(TypedDriverOption.REQUEST_LOG_WARNINGS, true);
map.put(TypedDriverOption.GRAPH_PAGING_ENABLED, "AUTO");
map.put(TypedDriverOption.GRAPH_CONTINUOUS_PAGING_PAGE_SIZE, requestPageSize);
map.put(TypedDriverOption.GRAPH_CONTINUOUS_PAGING_MAX_PAGES, continuousMaxPages);
map.put(
TypedDriverOption.GRAPH_CONTINUOUS_PAGING_MAX_PAGES_PER_SECOND,
continuousMaxPagesPerSecond);
map.put(
TypedDriverOption.GRAPH_CONTINUOUS_PAGING_MAX_ENQUEUED_PAGES, continuousMaxEnqueuedPages);
map.put(TypedDriverOption.CONTINUOUS_PAGING_PAGE_SIZE, requestPageSize);
map.put(TypedDriverOption.CONTINUOUS_PAGING_PAGE_SIZE_BYTES, false);
map.put(TypedDriverOption.CONTINUOUS_PAGING_MAX_PAGES, continuousMaxPages);
map.put(TypedDriverOption.CONTINUOUS_PAGING_MAX_PAGES_PER_SECOND, continuousMaxPagesPerSecond);
map.put(TypedDriverOption.CONTINUOUS_PAGING_MAX_ENQUEUED_PAGES, continuousMaxEnqueuedPages);
map.put(TypedDriverOption.CONTINUOUS_PAGING_TIMEOUT_FIRST_PAGE, Duration.ofSeconds(2));
map.put(TypedDriverOption.CONTINUOUS_PAGING_TIMEOUT_OTHER_PAGES, Duration.ofSeconds(1));
map.put(TypedDriverOption.MONITOR_REPORTING_ENABLED, true);
map.put(TypedDriverOption.METRICS_SESSION_ENABLED, Collections.emptyList());
map.put(TypedDriverOption.METRICS_SESSION_CQL_REQUESTS_HIGHEST, Duration.ofSeconds(3));
map.put(TypedDriverOption.METRICS_SESSION_CQL_REQUESTS_LOWEST, Duration.ofMillis(1));
map.put(TypedDriverOption.METRICS_SESSION_CQL_REQUESTS_DIGITS, 3);
map.put(TypedDriverOption.METRICS_SESSION_CQL_REQUESTS_INTERVAL, Duration.ofMinutes(5));
map.put(TypedDriverOption.METRICS_SESSION_THROTTLING_HIGHEST, Duration.ofSeconds(3));
map.put(TypedDriverOption.METRICS_SESSION_THROTTLING_LOWEST, Duration.ofMillis(1));
map.put(TypedDriverOption.METRICS_SESSION_THROTTLING_DIGITS, 3);
map.put(TypedDriverOption.METRICS_SESSION_THROTTLING_INTERVAL, Duration.ofMinutes(5));
map.put(
TypedDriverOption.CONTINUOUS_PAGING_METRICS_SESSION_CQL_REQUESTS_HIGHEST,
Duration.ofMinutes(2));
map.put(
TypedDriverOption.CONTINUOUS_PAGING_METRICS_SESSION_CQL_REQUESTS_LOWEST,
Duration.ofMillis(10));
map.put(TypedDriverOption.CONTINUOUS_PAGING_METRICS_SESSION_CQL_REQUESTS_DIGITS, 3);
map.put(
TypedDriverOption.CONTINUOUS_PAGING_METRICS_SESSION_CQL_REQUESTS_INTERVAL,
Duration.ofMinutes(5));
map.put(TypedDriverOption.METRICS_FACTORY_CLASS, "DefaultMetricsFactory");
map.put(TypedDriverOption.METRICS_ID_GENERATOR_CLASS, "DefaultMetricIdGenerator");
map.put(TypedDriverOption.METRICS_SESSION_GRAPH_REQUESTS_HIGHEST, Duration.ofSeconds(12));
map.put(TypedDriverOption.METRICS_SESSION_GRAPH_REQUESTS_LOWEST, Duration.ofMillis(1));
map.put(TypedDriverOption.METRICS_SESSION_GRAPH_REQUESTS_DIGITS, 3);
map.put(TypedDriverOption.METRICS_SESSION_GRAPH_REQUESTS_INTERVAL, Duration.ofMinutes(5));
map.put(TypedDriverOption.METRICS_NODE_ENABLED, Collections.emptyList());
map.put(TypedDriverOption.METRICS_NODE_CQL_MESSAGES_HIGHEST, Duration.ofSeconds(3));
map.put(TypedDriverOption.METRICS_NODE_CQL_MESSAGES_LOWEST, Duration.ofMillis(1));
map.put(TypedDriverOption.METRICS_NODE_CQL_MESSAGES_DIGITS, 3);
map.put(TypedDriverOption.METRICS_NODE_CQL_MESSAGES_INTERVAL, Duration.ofMinutes(5));
map.put(TypedDriverOption.METRICS_NODE_GRAPH_MESSAGES_HIGHEST, Duration.ofSeconds(3));
map.put(TypedDriverOption.METRICS_NODE_GRAPH_MESSAGES_LOWEST, Duration.ofMillis(1));
map.put(TypedDriverOption.METRICS_NODE_GRAPH_MESSAGES_DIGITS, 3);
map.put(TypedDriverOption.METRICS_NODE_GRAPH_MESSAGES_INTERVAL, Duration.ofMinutes(5));
map.put(TypedDriverOption.METRICS_NODE_EXPIRE_AFTER, Duration.ofHours(1));
map.put(TypedDriverOption.SOCKET_TCP_NODELAY, true);
map.put(TypedDriverOption.HEARTBEAT_INTERVAL, Duration.ofSeconds(30));
map.put(TypedDriverOption.HEARTBEAT_TIMEOUT, initQueryTimeout);
map.put(TypedDriverOption.METADATA_TOPOLOGY_WINDOW, Duration.ofSeconds(1));
map.put(TypedDriverOption.METADATA_TOPOLOGY_MAX_EVENTS, 20);
map.put(TypedDriverOption.METADATA_SCHEMA_ENABLED, true);
map.put(
TypedDriverOption.METADATA_SCHEMA_REFRESHED_KEYSPACES,
ImmutableList.of("!system", "!/^system_.*/", "!/^dse_.*/", "!solr_admin", "!OpsCenter"));
map.put(TypedDriverOption.METADATA_SCHEMA_REQUEST_TIMEOUT, requestTimeout);
map.put(TypedDriverOption.METADATA_SCHEMA_REQUEST_PAGE_SIZE, requestPageSize);
map.put(TypedDriverOption.METADATA_SCHEMA_WINDOW, Duration.ofSeconds(1));
map.put(TypedDriverOption.METADATA_SCHEMA_MAX_EVENTS, 20);
map.put(TypedDriverOption.METADATA_TOKEN_MAP_ENABLED, true);
map.put(TypedDriverOption.CONTROL_CONNECTION_TIMEOUT, initQueryTimeout);
map.put(TypedDriverOption.CONTROL_CONNECTION_AGREEMENT_INTERVAL, Duration.ofMillis(200));
map.put(TypedDriverOption.CONTROL_CONNECTION_AGREEMENT_TIMEOUT, Duration.ofSeconds(10));
map.put(TypedDriverOption.CONTROL_CONNECTION_AGREEMENT_WARN, true);
map.put(TypedDriverOption.PREPARE_ON_ALL_NODES, true);
map.put(TypedDriverOption.REPREPARE_ENABLED, true);
map.put(TypedDriverOption.REPREPARE_CHECK_SYSTEM_TABLE, false);
map.put(TypedDriverOption.REPREPARE_MAX_STATEMENTS, 0);
map.put(TypedDriverOption.REPREPARE_MAX_PARALLELISM, 100);
map.put(TypedDriverOption.REPREPARE_TIMEOUT, initQueryTimeout);
map.put(TypedDriverOption.NETTY_DAEMON, false);
map.put(TypedDriverOption.NETTY_IO_SIZE, 0);
map.put(TypedDriverOption.NETTY_IO_SHUTDOWN_QUIET_PERIOD, 2);
map.put(TypedDriverOption.NETTY_IO_SHUTDOWN_TIMEOUT, 15);
map.put(TypedDriverOption.NETTY_IO_SHUTDOWN_UNIT, "SECONDS");
map.put(TypedDriverOption.NETTY_ADMIN_SIZE, 2);
map.put(TypedDriverOption.NETTY_ADMIN_SHUTDOWN_QUIET_PERIOD, 2);
map.put(TypedDriverOption.NETTY_ADMIN_SHUTDOWN_TIMEOUT, 15);
map.put(TypedDriverOption.NETTY_ADMIN_SHUTDOWN_UNIT, "SECONDS");
map.put(TypedDriverOption.NETTY_TIMER_TICK_DURATION, Duration.ofMillis(100));
map.put(TypedDriverOption.NETTY_TIMER_TICKS_PER_WHEEL, 2048);
map.put(TypedDriverOption.COALESCER_INTERVAL, Duration.of(10, ChronoUnit.MICROS));
map.put(TypedDriverOption.LOAD_BALANCING_DC_FAILOVER_MAX_NODES_PER_REMOTE_DC, 0);
map.put(TypedDriverOption.LOAD_BALANCING_DC_FAILOVER_ALLOW_FOR_LOCAL_CONSISTENCY_LEVELS, false);
map.put(TypedDriverOption.METRICS_GENERATE_AGGREGABLE_HISTOGRAMS, true);
map.put(
TypedDriverOption.LOAD_BALANCING_DC_FAILOVER_PREFERRED_REMOTE_DCS, ImmutableList.of(""));
}
@Immutable
private static class SerializationProxy implements Serializable {
private static final long serialVersionUID = 1L;
private final ConcurrentHashMap> map;
private SerializationProxy(ConcurrentHashMap> map) {
this.map = map;
}
private Object readResolve() {
return new OptionsMap(map);
}
}
}