/*
* Copyright (c) 2016 Couchbase, Inc.
*
* 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.couchbase.client.core.env;
import com.couchbase.client.core.ClusterFacade;
import com.couchbase.client.core.annotations.InterfaceAudience;
import com.couchbase.client.core.annotations.InterfaceStability;
import com.couchbase.client.core.env.resources.IoPoolShutdownHook;
import com.couchbase.client.core.env.resources.NettyShutdownHook;
import com.couchbase.client.core.env.resources.NoOpShutdownHook;
import com.couchbase.client.core.env.resources.ShutdownHook;
import com.couchbase.client.core.event.CouchbaseEvent;
import com.couchbase.client.core.event.DefaultEventBus;
import com.couchbase.client.core.event.EventBus;
import com.couchbase.client.core.event.EventType;
import com.couchbase.client.core.event.consumers.LoggingConsumer;
import com.couchbase.client.core.event.system.TooManyEnvironmentsEvent;
import com.couchbase.client.core.logging.CouchbaseLogLevel;
import com.couchbase.client.core.logging.CouchbaseLogger;
import com.couchbase.client.core.logging.CouchbaseLoggerFactory;
import com.couchbase.client.core.message.observe.Observe;
import com.couchbase.client.core.metrics.DefaultLatencyMetricsCollectorConfig;
import com.couchbase.client.core.metrics.DefaultMetricsCollectorConfig;
import com.couchbase.client.core.metrics.LatencyMetricsCollectorConfig;
import com.couchbase.client.core.metrics.MetricsCollector;
import com.couchbase.client.core.metrics.MetricsCollectorConfig;
import com.couchbase.client.core.metrics.NetworkLatencyMetricsCollector;
import com.couchbase.client.core.metrics.RuntimeMetricsCollector;
import com.couchbase.client.core.node.DefaultMemcachedHashingStrategy;
import com.couchbase.client.core.node.MemcachedHashingStrategy;
import com.couchbase.client.core.retry.BestEffortRetryStrategy;
import com.couchbase.client.core.retry.RetryStrategy;
import com.couchbase.client.core.time.Delay;
import com.lmax.disruptor.BlockingWaitStrategy;
import com.lmax.disruptor.WaitStrategy;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.concurrent.DefaultThreadFactory;
import rx.Observable;
import rx.Scheduler;
import rx.Subscription;
import rx.functions.Action0;
import rx.functions.Action1;
import rx.functions.Func1;
import rx.functions.Func2;
import com.couchbase.client.core.utils.Blocking;
import java.security.KeyStore;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
public class DefaultCoreEnvironment implements CoreEnvironment {
/**
* The logger used.
*/
private static final CouchbaseLogger LOGGER = CouchbaseLoggerFactory.getInstance(CoreEnvironment.class);
public static final boolean DCP_ENABLED = false;
public static final boolean SSL_ENABLED = false;
public static final String SSL_KEYSTORE_FILE = null;
public static final String SSL_KEYSTORE_PASSWORD = null;
public static final KeyStore SSL_KEYSTORE = null;
public static final boolean BOOTSTRAP_HTTP_ENABLED = true;
public static final boolean BOOTSTRAP_CARRIER_ENABLED = true;
public static final int BOOTSTRAP_HTTP_DIRECT_PORT = 8091;
public static final int BOOTSTRAP_HTTP_SSL_PORT = 18091;
public static final int BOOTSTRAP_CARRIER_DIRECT_PORT = 11210;
public static final int BOOTSTRAP_CARRIER_SSL_PORT = 11207;
public static final int REQUEST_BUFFER_SIZE = 16384;
public static final int RESPONSE_BUFFER_SIZE = 16384;
public static final int DCP_CONNECTION_BUFFER_SIZE = 20971520; // 20MiB
public static final double DCP_CONNECTION_BUFFER_ACK_THRESHOLD = 0.2; // for 20Mib it is 4MiB
public static final String DCP_CONNECTION_NAME = "dcp/core-io";
public static final int IO_POOL_SIZE = Runtime.getRuntime().availableProcessors();
public static final int COMPUTATION_POOL_SIZE = Runtime.getRuntime().availableProcessors();
public static final int KEYVALUE_ENDPOINTS = 1;
public static final int VIEW_ENDPOINTS = 12;
public static final int QUERY_ENDPOINTS = 12;
public static final int SEARCH_ENDPOINTS = 12;
public static final Delay OBSERVE_INTERVAL_DELAY = Delay.exponential(TimeUnit.MICROSECONDS, 100000, 10);
public static final Delay RECONNECT_DELAY = Delay.exponential(TimeUnit.MILLISECONDS, 4096, 32);
public static final Delay RETRY_DELAY = Delay.exponential(TimeUnit.MICROSECONDS, 100000, 100);
public static final RetryStrategy RETRY_STRATEGY = BestEffortRetryStrategy.INSTANCE;
public static final long MAX_REQUEST_LIFETIME = TimeUnit.SECONDS.toMillis(75);
public static final long KEEPALIVEINTERVAL = TimeUnit.SECONDS.toMillis(30);
public static final long AUTORELEASE_AFTER = TimeUnit.SECONDS.toMillis(2);
public static final boolean BUFFER_POOLING_ENABLED = true;
public static final boolean TCP_NODELAY_ENALED = true;
public static final boolean MUTATION_TOKENS_ENABLED = false;
public static final int SOCKET_CONNECT_TIMEOUT = 1000;
public static final boolean CALLBACKS_ON_IO_POOL = false;
public static final long DISCONNECT_TIMEOUT = TimeUnit.SECONDS.toMillis(25);
public static final MemcachedHashingStrategy MEMCACHED_HASHING_STRATEGY =
DefaultMemcachedHashingStrategy.INSTANCE;
public static final long CONFIG_POLL_INTERVAL = TimeUnit.SECONDS.toMillis(10);
public static String CORE_VERSION;
public static String CORE_GIT_VERSION;
public static String PACKAGE_NAME_AND_VERSION = "couchbase-jvm-core";
public static String USER_AGENT = PACKAGE_NAME_AND_VERSION;
private static final String NAMESPACE = "com.couchbase.";
/**
* The minimum size of the io and computation pools in order to prevent deadlock and resource
* starvation.
*
* Normally this should be higher by default, but if the number of cores are very small or the configuration
* is wrong it can even go down to 1.
*/
static final int MIN_POOL_SIZE = 3;
private static final String VERSION_PROPERTIES = "com.couchbase.client.core.properties";
/**
* Sets up the package version and user agent.
*
* Note that because the class loader loads classes on demand, one class from the package
* is loaded upfront.
*/
static {
try {
Class<ClusterFacade> facadeClass = ClusterFacade.class;
if (facadeClass == null) {
throw new IllegalStateException("Could not locate ClusterFacade");
}
String version = null;
String gitVersion = null;
try {
Properties versionProp = new Properties();
versionProp.load(DefaultCoreEnvironment.class.getClassLoader().getResourceAsStream(VERSION_PROPERTIES));
version = versionProp.getProperty("specificationVersion");
gitVersion = versionProp.getProperty("implementationVersion");
} catch (Exception e) {
LOGGER.info("Could not retrieve core version properties, defaulting.", e);
}
CORE_VERSION = version == null ? "unknown" : version;
CORE_GIT_VERSION = gitVersion == null ? "unknown" : gitVersion;
PACKAGE_NAME_AND_VERSION = String.format("couchbase-jvm-core/%s (git: %s)",
CORE_VERSION, CORE_GIT_VERSION);
USER_AGENT = String.format("%s (%s/%s %s; %s %s)",
PACKAGE_NAME_AND_VERSION,
System.getProperty("os.name"),
System.getProperty("os.version"),
System.getProperty("os.arch"),
System.getProperty("java.vm.name"),
System.getProperty("java.runtime.version")
);
} catch (Exception ex) {
LOGGER.info("Could not set up user agent and packages, defaulting.", ex);
}
try {
if (System.getProperty("com.couchbase.client.deps.io.netty.packagePrefix") == null) {
System.setProperty("com.couchbase.client.deps.io.netty.packagePrefix", "com.couchbase.client.deps.");
}
} catch (Exception ex) {
LOGGER.warn("Could not configure bundled netty's package prefix", ex);
}
}
private final boolean dcpEnabled;
private final boolean sslEnabled;
private final String sslKeystoreFile;
private final String sslKeystorePassword;
private final KeyStore sslKeystore;
private final boolean bootstrapHttpEnabled;
private final boolean bootstrapCarrierEnabled;
private final int bootstrapHttpDirectPort;
private final int bootstrapHttpSslPort;
private final int bootstrapCarrierDirectPort;
private final int bootstrapCarrierSslPort;
private final int ioPoolSize;
private final int computationPoolSize;
private final int responseBufferSize;
private final int requestBufferSize;
private final int dcpConnectionBufferSize;
private final double dcpConnectionBufferAckThreshold;
private final String dcpConnectionName;
private final int kvServiceEndpoints;
private final int viewServiceEndpoints;
private final int queryServiceEndpoints;
private final int searchServiceEndpoints;
private final Delay observeIntervalDelay;
private final Delay reconnectDelay;
private final Delay retryDelay;
private final String userAgent;
private final String packageNameAndVersion;
private final RetryStrategy retryStrategy;
private final long maxRequestLifetime;
private final long keepAliveInterval;
private final long autoreleaseAfter;
private final boolean bufferPoolingEnabled;
private final boolean tcpNodelayEnabled;
private final boolean mutationTokensEnabled;
private final int socketConnectTimeout;
private final boolean callbacksOnIoPool;
private final long disconnectTimeout;
private final WaitStrategyFactory requestBufferWaitStrategy;
private final MemcachedHashingStrategy memcachedHashingStrategy;
private final long configPollInterval;
private static final int MAX_ALLOWED_INSTANCES = 1;
private static volatile int instanceCounter = 0;
private final EventLoopGroup ioPool;
private final EventLoopGroup kvIoPool;
private final EventLoopGroup queryIoPool;
private final EventLoopGroup viewIoPool;
private final EventLoopGroup searchIoPool;
private final Scheduler coreScheduler;
private final EventBus eventBus;
private final ShutdownHook ioPoolShutdownHook;
private final ShutdownHook kvIoPoolShutdownHook;
private final ShutdownHook queryIoPoolShutdownHook;
private final ShutdownHook viewIoPoolShutdownHook;
private final ShutdownHook searchIoPoolShutdownHook;
private final KeyValueServiceConfig keyValueServiceConfig;
private final QueryServiceConfig queryServiceConfig;
private final ViewServiceConfig viewServiceConfig;
private final SearchServiceConfig searchServiceConfig;
private final ShutdownHook nettyShutdownHook;
private final ShutdownHook coreSchedulerShutdownHook;
private final MetricsCollector runtimeMetricsCollector;
private final NetworkLatencyMetricsCollector networkLatencyMetricsCollector;
private final Subscription metricsCollectorSubscription;
protected DefaultCoreEnvironment(final Builder builder) {
boolean emitEnvWarnMessage = false;
if (++instanceCounter > MAX_ALLOWED_INSTANCES) {
LOGGER.warn("More than " + MAX_ALLOWED_INSTANCES + " Couchbase Environments found (" + instanceCounter
+ "), this can have severe impact on performance and stability. Reuse environments!");
emitEnvWarnMessage = true;
}
dcpEnabled = booleanPropertyOr("dcpEnabled", builder.dcpEnabled);
sslEnabled = booleanPropertyOr("sslEnabled", builder.sslEnabled);
sslKeystoreFile = stringPropertyOr("sslKeystoreFile", builder.sslKeystoreFile);
sslKeystorePassword = stringPropertyOr("sslKeystorePassword", builder.sslKeystorePassword);
bootstrapHttpEnabled = booleanPropertyOr("bootstrapHttpEnabled", builder.bootstrapHttpEnabled);
bootstrapHttpDirectPort = intPropertyOr("bootstrapHttpDirectPort", builder.bootstrapHttpDirectPort);
bootstrapHttpSslPort = intPropertyOr("bootstrapHttpSslPort", builder.bootstrapHttpSslPort);
bootstrapCarrierEnabled = booleanPropertyOr("bootstrapCarrierEnabled", builder.bootstrapCarrierEnabled);
bootstrapCarrierDirectPort = intPropertyOr("bootstrapCarrierDirectPort", builder.bootstrapCarrierDirectPort);
bootstrapCarrierSslPort = intPropertyOr("bootstrapCarrierSslPort", builder.bootstrapCarrierSslPort);
int ioPoolSize = intPropertyOr("ioPoolSize", builder.ioPoolSize);
int computationPoolSize = intPropertyOr("computationPoolSize", builder.computationPoolSize);
responseBufferSize = intPropertyOr("responseBufferSize", builder.responseBufferSize);
requestBufferSize = intPropertyOr("requestBufferSize", builder.requestBufferSize);
dcpConnectionBufferSize = intPropertyOr("dcpConnectionBufferSize", builder.dcpConnectionBufferSize);
dcpConnectionBufferAckThreshold = doublePropertyOr("dcpConnectionBufferAckThreshold", builder.dcpConnectionBufferAckThreshold);
dcpConnectionName = stringPropertyOr("dcpConnectionName", builder.dcpConnectionName);
kvServiceEndpoints = intPropertyOr("kvEndpoints", builder.kvEndpoints);
viewServiceEndpoints = intPropertyOr("viewEndpoints", builder.viewEndpoints);
queryServiceEndpoints = intPropertyOr("queryEndpoints", builder.queryEndpoints);
searchServiceEndpoints = intPropertyOr("searchEndpoints", builder.searchEndpoints);
packageNameAndVersion = stringPropertyOr("packageNameAndVersion", builder.packageNameAndVersion);
userAgent = stringPropertyOr("userAgent", builder.userAgent);
observeIntervalDelay = builder.observeIntervalDelay;
reconnectDelay = builder.reconnectDelay;
retryDelay = builder.retryDelay;
retryStrategy = builder.retryStrategy;
maxRequestLifetime = longPropertyOr("maxRequestLifetime", builder.maxRequestLifetime);
keepAliveInterval = longPropertyOr("keepAliveInterval", builder.keepAliveInterval);
autoreleaseAfter = longPropertyOr("autoreleaseAfter", builder.autoreleaseAfter);
bufferPoolingEnabled = booleanPropertyOr("bufferPoolingEnabled", builder.bufferPoolingEnabled);
tcpNodelayEnabled = booleanPropertyOr("tcpNodelayEnabled", builder.tcpNodelayEnabled);
mutationTokensEnabled = booleanPropertyOr("mutationTokensEnabled", builder.mutationTokensEnabled);
socketConnectTimeout = intPropertyOr("socketConnectTimeout", builder.socketConnectTimeout);
callbacksOnIoPool = booleanPropertyOr("callbacksOnIoPool", builder.callbacksOnIoPool);
disconnectTimeout = longPropertyOr("disconnectTimeout", builder.disconnectTimeout);
sslKeystore = builder.sslKeystore;
memcachedHashingStrategy = builder.memcachedHashingStrategy;
configPollInterval = longPropertyOr("configPollInterval", builder.configPollInterval);
if (ioPoolSize < MIN_POOL_SIZE) {
LOGGER.info("ioPoolSize is less than {} ({}), setting to: {}", MIN_POOL_SIZE, ioPoolSize, MIN_POOL_SIZE);
this.ioPoolSize = MIN_POOL_SIZE;
} else {
this.ioPoolSize = ioPoolSize;
}
if (computationPoolSize < MIN_POOL_SIZE) {
LOGGER.info("computationPoolSize is less than {} ({}), setting to: {}", MIN_POOL_SIZE, computationPoolSize,
MIN_POOL_SIZE);
this.computationPoolSize = MIN_POOL_SIZE;
} else {
this.computationPoolSize = computationPoolSize;
}
if (builder.ioPool == null) {
this.ioPool = new NioEventLoopGroup(ioPoolSize(), new DefaultThreadFactory("cb-io", true));
this.ioPoolShutdownHook = new IoPoolShutdownHook(this.ioPool);
} else {
this.ioPool = builder.ioPool;
this.ioPoolShutdownHook = builder.ioPoolShutdownHook == null
? new NoOpShutdownHook()
: builder.ioPoolShutdownHook;
}
if (builder.kvIoPool != null) {
this.kvIoPool = builder.kvIoPool;
this.kvIoPoolShutdownHook = builder.kvIoPoolShutdownHook == null
? new NoOpShutdownHook()
: builder.kvIoPoolShutdownHook;
} else {
this.kvIoPool = null;
this.kvIoPoolShutdownHook = new NoOpShutdownHook();
}
if (builder.queryIoPool != null) {
this.queryIoPool = builder.queryIoPool;
this.queryIoPoolShutdownHook = builder.queryIoPoolShutdownHook == null
? new NoOpShutdownHook()
: builder.queryIoPoolShutdownHook;
} else {
this.queryIoPool = null;
this.queryIoPoolShutdownHook = new NoOpShutdownHook();
}
if (builder.viewIoPool != null) {
this.viewIoPool = builder.viewIoPool;
this.viewIoPoolShutdownHook = builder.viewIoPoolShutdownHook == null
? new NoOpShutdownHook()
: builder.viewIoPoolShutdownHook;
} else {
this.viewIoPool = null;
this.viewIoPoolShutdownHook = new NoOpShutdownHook();
}
if (builder.searchIoPool != null) {
this.searchIoPool = builder.searchIoPool;
this.searchIoPoolShutdownHook = builder.searchIoPoolShutdownHook == null
? new NoOpShutdownHook()
: builder.searchIoPoolShutdownHook;
} else {
this.searchIoPool = null;
this.searchIoPoolShutdownHook = new NoOpShutdownHook();
}
if (!(this.ioPoolShutdownHook instanceof NoOpShutdownHook)) {
this.nettyShutdownHook = new NettyShutdownHook();
} else {
this.nettyShutdownHook = this.ioPoolShutdownHook;
}
if (builder.scheduler == null) {
CoreScheduler managed = new CoreScheduler(computationPoolSize());
this.coreScheduler = managed;
this.coreSchedulerShutdownHook = managed;
} else {
this.coreScheduler = builder.scheduler;
this.coreSchedulerShutdownHook = builder.schedulerShutdownHook == null
? new NoOpShutdownHook()
: builder.schedulerShutdownHook;
}
this.eventBus = builder.eventBus == null ? new DefaultEventBus(coreScheduler) : builder.eventBus;
this.runtimeMetricsCollector = new RuntimeMetricsCollector(
eventBus,
coreScheduler,
builder.runtimeMetricsCollectorConfig == null
? DefaultMetricsCollectorConfig.create()
: builder.runtimeMetricsCollectorConfig
);
this.networkLatencyMetricsCollector = new NetworkLatencyMetricsCollector(
eventBus,
coreScheduler,
builder.networkLatencyMetricsCollectorConfig == null
? DefaultLatencyMetricsCollectorConfig.create()
: builder.networkLatencyMetricsCollectorConfig
);
if (builder.defaultMetricsLoggingConsumer != null) {
metricsCollectorSubscription = eventBus
.get()
.filter(new Func1<CouchbaseEvent, Boolean>() {
@Override
public Boolean call(CouchbaseEvent evt) {
return evt.type().equals(EventType.METRIC);
}
})
.subscribe(builder.defaultMetricsLoggingConsumer);
} else {
metricsCollectorSubscription = null;
}
if (builder.requestBufferWaitStrategy == null) {
requestBufferWaitStrategy = new WaitStrategyFactory() {
@Override
public WaitStrategy newWaitStrategy() {
return new BlockingWaitStrategy();
}
};
} else {
requestBufferWaitStrategy = builder.requestBufferWaitStrategy;
}
if (builder.keyValueServiceConfig != null) {
this.keyValueServiceConfig = builder.keyValueServiceConfig;
} else {
this.keyValueServiceConfig = KeyValueServiceConfig.create(kvEndpoints());
}
if (builder.viewServiceConfig != null) {
this.viewServiceConfig = builder.viewServiceConfig;
} else {
int minEndpoints = viewEndpoints() == VIEW_ENDPOINTS ? 0 : viewEndpoints();
this.viewServiceConfig = ViewServiceConfig.create(minEndpoints, viewEndpoints());
}
if (builder.queryServiceConfig != null) {
this.queryServiceConfig = builder.queryServiceConfig;
} else {
int minEndpoints = queryEndpoints() == QUERY_ENDPOINTS ? 0 : queryEndpoints();
this.queryServiceConfig = QueryServiceConfig.create(minEndpoints, queryEndpoints());
}
if (builder.searchServiceConfig != null) {
this.searchServiceConfig = builder.searchServiceConfig;
} else {
int minEndpoints = searchEndpoints() == SEARCH_ENDPOINTS ? 0 : searchEndpoints();
this.searchServiceConfig = SearchServiceConfig.create(minEndpoints, searchEndpoints());
}
if (emitEnvWarnMessage) {
eventBus.publish(new TooManyEnvironmentsEvent(instanceCounter));
}
}
public static DefaultCoreEnvironment create() {
return new DefaultCoreEnvironment(builder());
}
public static Builder builder() {
return new Builder();
}
protected boolean booleanPropertyOr(String path, boolean def) {
String found = System.getProperty(NAMESPACE + path);
if (found == null) {
return def;
}
return Boolean.parseBoolean(found);
}
protected String stringPropertyOr(String path, String def) {
String found = System.getProperty(NAMESPACE + path);
return found == null ? def : found;
}
protected int intPropertyOr(String path, int def) {
String found = System.getProperty(NAMESPACE + path);
if (found == null) {
return def;
}
return Integer.parseInt(found);
}
protected static long longPropertyOr(String path, long def) {
String found = System.getProperty(NAMESPACE + path);
if (found == null) {
return def;
}
return Integer.parseInt(found);
}
protected static double doublePropertyOr(String path, double def) {
String found = System.getProperty(NAMESPACE + path);
if (found == null) {
return def;
}
return Double.parseDouble(found);
}
@Override
public EventLoopGroup ioPool() {
return ioPool;
}
@Override
public boolean shutdown() {
return shutdown(disconnectTimeout(), TimeUnit.MILLISECONDS);
}
@Override
public boolean shutdown(long timeout, TimeUnit timeUnit) {
return Blocking.blockForSingle(shutdownAsync(), timeout, timeUnit);
}
@Override
@SuppressWarnings("unchecked")
public Observable<Boolean> shutdownAsync() {
if (metricsCollectorSubscription != null && !metricsCollectorSubscription.isUnsubscribed()) {
metricsCollectorSubscription.unsubscribe();
}
Observable<Boolean> result = Observable.merge(
wrapShutdown(ioPoolShutdownHook.shutdown(), "IoPool"),
wrapBestEffortShutdown(nettyShutdownHook.shutdown(), "Netty"),
wrapShutdown(kvIoPoolShutdownHook.shutdown(), "kvIoPool"),
wrapShutdown(viewIoPoolShutdownHook.shutdown(), "viewIoPool"),
wrapShutdown(queryIoPoolShutdownHook.shutdown(), "queryIoPool"),
wrapShutdown(searchIoPoolShutdownHook.shutdown(), "searchIoPool"),
wrapShutdown(coreSchedulerShutdownHook.shutdown(), "Core Scheduler"),
wrapShutdown(Observable.just(runtimeMetricsCollector.shutdown()), "Runtime Metrics Collector"),
wrapShutdown(Observable.just(networkLatencyMetricsCollector.shutdown()), "Latency Metrics Collector"))
.reduce(true,
new Func2<Boolean, ShutdownStatus, Boolean>() {
@Override
public Boolean call(Boolean previousStatus, ShutdownStatus currentStatus) {
return previousStatus && currentStatus.success;
}
})
.doOnTerminate(new Action0() {
@Override
public void call() {
instanceCounter--;
}
});
return result;
}
/**
* This method wraps an Observable of Boolean (for shutdown hook) into an Observable of ShutdownStatus.
* It will log each status with a short message indicating which target has been shut down, and the result of
* the call.
* Additionally it will ignore signals that shutdown status is false (as long as no exception is detected), logging that the target is "best effort" only.
*/
private Observable<ShutdownStatus> wrapBestEffortShutdown(Observable<Boolean> source, final String target) {
return wrapShutdown(source, target)
.map(new Func1<ShutdownStatus, ShutdownStatus>() {
@Override
public ShutdownStatus call(ShutdownStatus original) {
if (original.cause == null && !original.success) {
LOGGER.info(target + " shutdown is best effort, ignoring failure");
return new ShutdownStatus(target, true, null);
} else {
return original;
}
}
});
}
/**
* This method wraps an Observable of Boolean (for shutdown hook) into an Observable of ShutdownStatus.
* It will log each status with a short message indicating which target has been shut down, and the result of
* the call.
*/
private Observable<ShutdownStatus> wrapShutdown(Observable<Boolean> source, final String target) {
return source.
reduce(true, new Func2<Boolean, Boolean, Boolean>() {
@Override
public Boolean call(Boolean previousStatus, Boolean currentStatus) {
return previousStatus && currentStatus;
}
})
.map(new Func1<Boolean, ShutdownStatus>() {
@Override
public ShutdownStatus call(Boolean status) {
return new ShutdownStatus(target, status, null);
}
})
.onErrorReturn(new Func1<Throwable, ShutdownStatus>() {
@Override
public ShutdownStatus call(Throwable throwable) {
return new ShutdownStatus(target, false, throwable);
}
})
.doOnNext(new Action1<ShutdownStatus>() {
@Override
public void call(ShutdownStatus shutdownStatus) {
LOGGER.info(shutdownStatus.toString());
}
});
}
@Override
public Scheduler scheduler() {
return coreScheduler;
}
@Override
public boolean sslEnabled() {
return sslEnabled;
}
@Override
public boolean dcpEnabled() {
return dcpEnabled;
}
@Override
public String sslKeystoreFile() {
return sslKeystoreFile;
}
@Override
public String sslKeystorePassword() {
return sslKeystorePassword;
}
@Override
public KeyStore sslKeystore() {
return sslKeystore;
}
@Override
public boolean bootstrapHttpEnabled() {
return bootstrapHttpEnabled;
}
@Override
public boolean bootstrapCarrierEnabled() {
return bootstrapCarrierEnabled;
}
@Override
public int bootstrapHttpDirectPort() {
return bootstrapHttpDirectPort;
}
@Override
public int bootstrapHttpSslPort() {
return bootstrapHttpSslPort;
}
@Override
public int bootstrapCarrierDirectPort() {
return bootstrapCarrierDirectPort;
}
@Override
public int bootstrapCarrierSslPort() {
return bootstrapCarrierSslPort;
}
@Override
public int ioPoolSize() {
return ioPoolSize;
}
@Override
public int computationPoolSize() {
return computationPoolSize;
}
@Override
public int requestBufferSize() {
return requestBufferSize;
}
@Override
public int responseBufferSize() {
return responseBufferSize;
}
@Override
public int dcpConnectionBufferSize() {
return dcpConnectionBufferSize;
}
@Override
public double dcpConnectionBufferAckThreshold() {
return dcpConnectionBufferAckThreshold;
}
@Override
@InterfaceStability.Experimental
@InterfaceAudience.Public
public String dcpConnectionName() {
return dcpConnectionName;
}
@Override
public int kvEndpoints() {
return kvServiceEndpoints;
}
@Override
public int viewEndpoints() {
return viewServiceEndpoints;
}
@Override
public int queryEndpoints() {
return queryServiceEndpoints;
}
@Override
public int searchEndpoints() {
return searchServiceEndpoints;
}
@Override
public String coreVersion() {
return CORE_VERSION;
}
@Override
public String coreBuild() {
return CORE_GIT_VERSION;
}
@Override
public String userAgent() {
return userAgent;
}
@Override
public String packageNameAndVersion() {
return packageNameAndVersion;
}
@Override
public Delay observeIntervalDelay() {
return observeIntervalDelay;
}
@Override
public Delay reconnectDelay() {
return reconnectDelay;
}
@Override
public Delay retryDelay() {
return retryDelay;
}
@Override
public RetryStrategy retryStrategy() {
return retryStrategy;
}
@Override
public long maxRequestLifetime() {
return maxRequestLifetime;
}
@Override
public long keepAliveInterval() {
return this.keepAliveInterval;
}
@Override
public EventBus eventBus() {
return eventBus;
}
@Override
public long autoreleaseAfter() {
return autoreleaseAfter;
}
@Override
public boolean bufferPoolingEnabled() {
return bufferPoolingEnabled;
}
@Override
public boolean tcpNodelayEnabled() {
return tcpNodelayEnabled;
}
@Override
public boolean mutationTokensEnabled() {
return mutationTokensEnabled;
}
@Override
public MetricsCollector runtimeMetricsCollector() {
return runtimeMetricsCollector;
}
@Override
public NetworkLatencyMetricsCollector networkLatencyMetricsCollector() {
return networkLatencyMetricsCollector;
}
@Override
public int socketConnectTimeout() {
return socketConnectTimeout;
}
@Override
public boolean callbacksOnIoPool() {
return callbacksOnIoPool;
}
@Override
public long disconnectTimeout() {
return disconnectTimeout;
}
@Override
public WaitStrategyFactory requestBufferWaitStrategy() {
return requestBufferWaitStrategy;
}
@Override
public MemcachedHashingStrategy memcachedHashingStrategy() {
return memcachedHashingStrategy;
}
@Override
public EventLoopGroup kvIoPool() {
return kvIoPool;
}
@Override
public EventLoopGroup viewIoPool() {
return viewIoPool;
}
@Override
public EventLoopGroup queryIoPool() {
return queryIoPool;
}
@Override
public EventLoopGroup searchIoPool() {
return searchIoPool;
}
public static int instanceCounter() {
return instanceCounter;
}
@Override
public KeyValueServiceConfig kvServiceConfig() {
return keyValueServiceConfig;
}
@Override
public QueryServiceConfig queryServiceConfig() {
return queryServiceConfig;
}
@Override
public ViewServiceConfig viewServiceConfig() {
return viewServiceConfig;
}
@Override
public SearchServiceConfig searchServiceConfig() {
return searchServiceConfig;
}
@InterfaceStability.Experimental
@InterfaceAudience.Public
@Override
public long configPollInterval() {
return configPollInterval;
}
public static class Builder {
private boolean dcpEnabled = DCP_ENABLED;
private boolean sslEnabled = SSL_ENABLED;
private String sslKeystoreFile = SSL_KEYSTORE_FILE;
private String sslKeystorePassword = SSL_KEYSTORE_PASSWORD;
private KeyStore sslKeystore = SSL_KEYSTORE;
private String userAgent = USER_AGENT;
private String packageNameAndVersion = PACKAGE_NAME_AND_VERSION;
private boolean bootstrapHttpEnabled = BOOTSTRAP_HTTP_ENABLED;
private boolean bootstrapCarrierEnabled = BOOTSTRAP_CARRIER_ENABLED;
private int bootstrapHttpDirectPort = BOOTSTRAP_HTTP_DIRECT_PORT;
private int bootstrapHttpSslPort = BOOTSTRAP_HTTP_SSL_PORT;
private int bootstrapCarrierDirectPort = BOOTSTRAP_CARRIER_DIRECT_PORT;
private int bootstrapCarrierSslPort = BOOTSTRAP_CARRIER_SSL_PORT;
private int ioPoolSize = IO_POOL_SIZE;
private int computationPoolSize = COMPUTATION_POOL_SIZE;
private int responseBufferSize = RESPONSE_BUFFER_SIZE;
private int requestBufferSize = REQUEST_BUFFER_SIZE;
private int dcpConnectionBufferSize = DCP_CONNECTION_BUFFER_SIZE;
private double dcpConnectionBufferAckThreshold = DCP_CONNECTION_BUFFER_ACK_THRESHOLD;
private String dcpConnectionName = DCP_CONNECTION_NAME;
private int kvEndpoints = KEYVALUE_ENDPOINTS;
private int viewEndpoints = VIEW_ENDPOINTS;
private int queryEndpoints = QUERY_ENDPOINTS;
private int searchEndpoints = SEARCH_ENDPOINTS;
private Delay observeIntervalDelay = OBSERVE_INTERVAL_DELAY;
private Delay reconnectDelay = RECONNECT_DELAY;
private Delay retryDelay = RETRY_DELAY;
private RetryStrategy retryStrategy = RETRY_STRATEGY;
private EventLoopGroup ioPool;
private EventLoopGroup kvIoPool;
private EventLoopGroup viewIoPool;
private EventLoopGroup queryIoPool;
private EventLoopGroup searchIoPool;
private ShutdownHook ioPoolShutdownHook;
private ShutdownHook kvIoPoolShutdownHook;
private ShutdownHook viewIoPoolShutdownHook;
private ShutdownHook queryIoPoolShutdownHook;
private ShutdownHook searchIoPoolShutdownHook;
private Scheduler scheduler;
private ShutdownHook schedulerShutdownHook;
private EventBus eventBus;
private long maxRequestLifetime = MAX_REQUEST_LIFETIME;
private long keepAliveInterval = KEEPALIVEINTERVAL;
private long autoreleaseAfter = AUTORELEASE_AFTER;
private boolean bufferPoolingEnabled = BUFFER_POOLING_ENABLED;
private boolean tcpNodelayEnabled = TCP_NODELAY_ENALED;
private boolean mutationTokensEnabled = MUTATION_TOKENS_ENABLED;
private int socketConnectTimeout = SOCKET_CONNECT_TIMEOUT;
private boolean callbacksOnIoPool = CALLBACKS_ON_IO_POOL;
private long disconnectTimeout = DISCONNECT_TIMEOUT;
private WaitStrategyFactory requestBufferWaitStrategy;
private MemcachedHashingStrategy memcachedHashingStrategy = MEMCACHED_HASHING_STRATEGY;
private long configPollInterval = CONFIG_POLL_INTERVAL;
private MetricsCollectorConfig runtimeMetricsCollectorConfig;
private LatencyMetricsCollectorConfig networkLatencyMetricsCollectorConfig;
private LoggingConsumer defaultMetricsLoggingConsumer = LoggingConsumer.create();
private KeyValueServiceConfig keyValueServiceConfig;
private QueryServiceConfig queryServiceConfig;
private ViewServiceConfig viewServiceConfig;
private SearchServiceConfig searchServiceConfig;
protected Builder() {
}
/**
* Set if DCP should be enabled (only makes sense with server versions >= 3.0.0, default {@value #DCP_ENABLED}).
*/
@Deprecated
public Builder dcpEnabled(final boolean dcpEnabled) {
this.dcpEnabled = dcpEnabled;
return this;
}
/**
* Set if SSL should be enabled (default value {@value #SSL_ENABLED}).
* If true, also set {@link #sslKeystoreFile(String)} and {@link #sslKeystorePassword(String)}.
*/
public Builder sslEnabled(final boolean sslEnabled) {
this.sslEnabled = sslEnabled;
return this;
}
/**
* Defines the location of the SSL Keystore file (default value null, none).
*
* You can either specify a file or the keystore directly via {@link #sslKeystore(KeyStore)}. If the explicit
* keystore is used it takes precedence over the file approach.
*/
public Builder sslKeystoreFile(final String sslKeystoreFile) {
this.sslKeystoreFile = sslKeystoreFile;
return this;
}
/**
* Sets the SSL Keystore password to be used with the Keystore file (default value null, none).
*
* @see #sslKeystoreFile(String)
*/
public Builder sslKeystorePassword(final String sslKeystorePassword) {
this.sslKeystorePassword = sslKeystorePassword;
return this;
}
/**
* Sets the SSL Keystore directly and not indirectly via filepath.
*
* You can either specify a file or the keystore directly via {@link #sslKeystore(KeyStore)}. If the explicit
* keystore is used it takes precedence over the file approach.
*
* @param sslKeystore the keystore to use.
*/
public Builder sslKeystore(final KeyStore sslKeystore) {
this.sslKeystore = sslKeystore;
return this;
}
/**
* Toggles bootstrap via Http (default value {@value #BOOTSTRAP_HTTP_ENABLED}).
*/
public Builder bootstrapHttpEnabled(final boolean bootstrapHttpEnabled) {
this.bootstrapHttpEnabled = bootstrapHttpEnabled;
return this;
}
/**
* Toggles bootstrap via carrier publication (default value {@value #BOOTSTRAP_CARRIER_ENABLED}).
*/
public Builder bootstrapCarrierEnabled(final boolean bootstrapCarrierEnabled) {
this.bootstrapCarrierEnabled = bootstrapCarrierEnabled;
return this;
}
/**
* If Http bootstrap is enabled and not SSL, sets the port to use
* (default value {@value #BOOTSTRAP_HTTP_DIRECT_PORT}).
*/
public Builder bootstrapHttpDirectPort(final int bootstrapHttpDirectPort) {
this.bootstrapHttpDirectPort = bootstrapHttpDirectPort;
return this;
}
/**
* If Http bootstrap and SSL are enabled, sets the port to use
* (default value {@value #BOOTSTRAP_HTTP_SSL_PORT}).
*/
public Builder bootstrapHttpSslPort(final int bootstrapHttpSslPort) {
this.bootstrapHttpSslPort = bootstrapHttpSslPort;
return this;
}
/**
* If carrier publication bootstrap is enabled and not SSL, sets the port to use
* (default value {@value #BOOTSTRAP_CARRIER_DIRECT_PORT}).
*/
public Builder bootstrapCarrierDirectPort(final int bootstrapCarrierDirectPort) {
this.bootstrapCarrierDirectPort = bootstrapCarrierDirectPort;
return this;
}
/**
* If carrier publication bootstrap and SSL are enabled, sets the port to use
* (default value {@value #BOOTSTRAP_CARRIER_SSL_PORT}).
*/
public Builder bootstrapCarrierSslPort(final int bootstrapCarrierSslPort) {
this.bootstrapCarrierSslPort = bootstrapCarrierSslPort;
return this;
}
/**
* Sets the pool size (number of threads to use) for I/O
* operations (default value is the number of CPUs).
*
* If there is more nodes in the cluster than the defined
* ioPoolSize, multiplexing will automatically happen.
*/
public Builder ioPoolSize(final int ioPoolSize) {
this.ioPoolSize = ioPoolSize;
return this;
}
/**
* Sets the pool size (number of threads to use) for all non blocking operations in the core and clients
* (default value is the number of CPUs).
*
* Don't size it too small since it would significantly impact performance.
*/
public Builder computationPoolSize(final int computationPoolSize) {
this.computationPoolSize = computationPoolSize;
return this;
}
/**
* Sets the size of the RingBuffer structure that queues requests (default value {@value #REQUEST_BUFFER_SIZE}).
* This is an advanced parameter that usually shouldn't need to be changed.
*/
public Builder requestBufferSize(final int requestBufferSize) {
this.requestBufferSize = requestBufferSize;
return this;
}
/**
* Sets the size of the RingBuffer structure that queues responses
* (default value {@value #RESPONSE_BUFFER_SIZE}).
* This is an advanced parameter that usually shouldn't need to be changed
*/
public Builder responseBufferSize(final int responseBufferSize) {
this.responseBufferSize = responseBufferSize;
return this;
}
/**
* Sets the size of the buffer to control speed of DCP producer. The server will stop emitting data if
* the current value of the buffer reach this limit. Set it to zero to disable DCP flow control.
* (default value {@value #DCP_CONNECTION_BUFFER_SIZE}).
*/
@Deprecated
public Builder dcpConnectionBufferSize(final int dcpConnectionBufferSize) {
this.dcpConnectionBufferSize = dcpConnectionBufferSize;
return this;
}
/**
* When a DCP connection read bytes reaches this percentage of the {@link #dcpConnectionBufferSize},
* a DCP Buffer Acknowledge message is sent to the server to signal producer how much data has been processed.
* (default value {@value #DCP_CONNECTION_BUFFER_ACK_THRESHOLD}).
*/
@Deprecated
public Builder dcpConnectionBufferAckThreshold(final double dcpConnectionBufferAckThreshold) {
this.dcpConnectionBufferAckThreshold = dcpConnectionBufferAckThreshold;
return this;
}
/**
* Sets default name for DCP connection. It is used to identify streams on the server.
* (default value {@value #DCP_CONNECTION_NAME}).
*/
@InterfaceStability.Experimental
@InterfaceAudience.Public
@Deprecated
public Builder dcpConnectionName(final String dcpConnectionName) {
this.dcpConnectionName = dcpConnectionName;
return this;
}
/**
* Sets the number of Key/Value endpoints to open per nodes in the cluster
* (default value {@value #KEYVALUE_ENDPOINTS}).
*
* Only tune to more if IO has been identified as the most probable bottleneck,
* since it can reduce batching on the tcp/network level.
*
* @deprecated Please use {@link Builder#keyValueServiceConfig(KeyValueServiceConfig)} going forward.
*/
public Builder kvEndpoints(final int kvEndpoints) {
this.kvEndpoints = kvEndpoints;
return this;
}
/**
* Sets the number of View endpoints to open per node in the cluster (default value {@value #VIEW_ENDPOINTS}).
*
* Setting this to a higher number is advised in heavy view workloads.
*
* @deprecated Please use {@link Builder#viewServiceConfig(ViewServiceConfig)} going forward.
*/
public Builder viewEndpoints(final int viewEndpoints) {
this.viewEndpoints = viewEndpoints;
return this;
}
/**
* Sets the number of Query (N1QL) endpoints to open per node in the cluster
* (default value {@value #QUERY_ENDPOINTS}).
*
* Setting this to a higher number is advised in heavy query workloads.
*
* @deprecated Please use {@link Builder#queryServiceConfig(QueryServiceConfig)} going forward.
*/
public Builder queryEndpoints(final int queryEndpoints) {
this.queryEndpoints = queryEndpoints;
return this;
}
/**
* Sets the number of Search (CBFT) endpoints to open per node in the cluster
* (default value {@value #SEARCH_ENDPOINTS}).
*
* Setting this to a higher number is advised in heavy query workloads.
*
* @deprecated Please use {@link Builder#searchServiceConfig(SearchServiceConfig)} going forward.
*/
public Builder searchEndpoints(final int searchEndpoints) {
this.searchEndpoints = searchEndpoints;
return this;
}
/**
* Sets the USER-AGENT String to be sent in HTTP requests headers (should usually not be tweaked,
* default value is computed from the SDK {@link #packageNameAndVersion()}).
*/
public Builder userAgent(final String userAgent) {
this.userAgent = userAgent;
return this;
}
/**
* Sets the String to be used as identifier for the library namespace and version.
* (should usually not be tweaked, default value is computed at build time from VCS tags/commits).
*
* This is used in {@link #userAgent()} notably.
*/
public Builder packageNameAndVersion(final String packageNameAndVersion) {
this.packageNameAndVersion = packageNameAndVersion;
return this;
}
/**
* Sets the {@link Delay} for {@link Observe} poll operations (default value
* is a delay growing exponentially between 10us and 100ms).
*/
public Builder observeIntervalDelay(final Delay observeIntervalDelay) {
this.observeIntervalDelay = observeIntervalDelay;
return this;
}
/**
* Sets the {@link Delay} for node reconnects (default value is a delay growing exponentially
* between 32ms and 4096ms).
*/
public Builder reconnectDelay(final Delay reconnectDelay) {
this.reconnectDelay = reconnectDelay;
return this;
}
/**
* Sets the {@link Delay} for retries of requests (default value is a delay growing exponentially
* between 100us and 100ms).
*/
public Builder retryDelay(final Delay retryDelay) {
this.retryDelay = retryDelay;
return this;
}
/**
* Sets the I/O Pool implementation for the underlying IO framework.
* This is an advanced configuration that should only be used if you know what you are doing.
*
* @deprecated use {@link #ioPool(EventLoopGroup, ShutdownHook)} to also provide a shutdown hook.
*/
@Deprecated
public Builder ioPool(final EventLoopGroup group) {
return ioPool(group, new NoOpShutdownHook());
}
/**
* Sets the I/O Pool implementation for the underlying IO framework, along with the action
* to execute when this environment is shut down.
* This is an advanced configuration that should only be used if you know what you are doing.
*/
public Builder ioPool(final EventLoopGroup group, final ShutdownHook shutdownHook) {
this.ioPool = group;
this.ioPoolShutdownHook = shutdownHook;
return this;
}
/**
* Sets the KV I/O Pool implementation for the underlying IO framework, along with the action
* to execute when this environment is shut down.
* This is an advanced configuration that should only be used if you know what you are doing.
*/
public Builder kvIoPool(final EventLoopGroup group, final ShutdownHook shutdownHook) {
this.kvIoPool = group;
this.kvIoPoolShutdownHook = shutdownHook;
return this;
}
/**
* Sets the View I/O Pool implementation for the underlying IO framework, along with the action
* to execute when this environment is shut down.
* This is an advanced configuration that should only be used if you know what you are doing.
*/
public Builder viewIoPool(final EventLoopGroup group, final ShutdownHook shutdownHook) {
this.viewIoPool = group;
this.viewIoPoolShutdownHook = shutdownHook;
return this;
}
/**
* Sets the Query I/O Pool implementation for the underlying IO framework, along with the action
* to execute when this environment is shut down.
* This is an advanced configuration that should only be used if you know what you are doing.
*/
public Builder queryIoPool(final EventLoopGroup group, final ShutdownHook shutdownHook) {
this.queryIoPool = group;
this.queryIoPoolShutdownHook = shutdownHook;
return this;
}
/**
* Sets the Search I/O Pool implementation for the underlying IO framework, along with the action
* to execute when this environment is shut down.
* This is an advanced configuration that should only be used if you know what you are doing.
*/
public Builder searchIoPool(final EventLoopGroup group, final ShutdownHook shutdownHook) {
this.searchIoPool = group;
this.searchIoPoolShutdownHook = shutdownHook;
return this;
}
/**
* Sets the Scheduler implementation for the underlying computation framework.
* This is an advanced configuration that should only be used if you know what you are doing.
*
* @deprecated use {@link #ioPool(EventLoopGroup, ShutdownHook)} to also provide a shutdown hook.
*/
@Deprecated
public Builder scheduler(final Scheduler scheduler) {
return scheduler(scheduler, new NoOpShutdownHook());
}
/**
* Sets the Scheduler implementation for the underlying computation framework, along with the action
* to execute when this environment is shut down.
* This is an advanced configuration that should only be used if you know what you are doing.
*/
public Builder scheduler(final Scheduler scheduler, final ShutdownHook shutdownHook) {
this.scheduler = scheduler;
this.schedulerShutdownHook = shutdownHook;
return this;
}
/**
* Sets the {@link RetryStrategy} to be used during request retries
* (default value is a {@link BestEffortRetryStrategy}).
*/
public Builder retryStrategy(final RetryStrategy retryStrategy) {
this.retryStrategy = retryStrategy;
return this;
}
/**
* Sets the maximum time in milliseconds a request is allowed to live.
*
* If the best effort retry strategy is used, the request will still be cancelled after this
* period to make sure that requests are not sticking around forever. Make sure it is longer than any
* timeout you potentially have configured.
*
* Default is 75s.
*/
public Builder maxRequestLifetime(final long maxRequestLifetime) {
this.maxRequestLifetime = maxRequestLifetime;
return this;
}
/**
* Sets the time of inactivity, in milliseconds, after which some services
* will issue a form of keep-alive request to their corresponding server/nodes
* (default is 30s, values <= 0 deactivate the idle check).
*/
public Builder keepAliveInterval(long keepAliveIntervalMilliseconds) {
this.keepAliveInterval = keepAliveIntervalMilliseconds;
return this;
}
/**
* Sets the time after which any non-consumed buffers will be automatically released.
* Setting this to a higher value than a few seconds is not recommended since this
* may lead to increased garbage collection.
*/
public Builder autoreleaseAfter(long autoreleaseAfter) {
this.autoreleaseAfter = autoreleaseAfter;
return this;
}
/**
* Sets the event bus to an alternative implementation.
*
* This setting should only be tweaked in advanced cases.
*/
public Builder eventBus(final EventBus eventBus) {
this.eventBus = eventBus;
return this;
}
/**
* Forcefully disable buffer pooling by setting the value to false.
*
* This should not be used in general because buffer pooling is in place to reduce GC
* pressure during workloads. It is implemented to be used as a "last resort" if the
* client is suspect to a buffer leak which can terminate the application. Until a
* solution is found to the leak buffer pooling can be disabled at the cost of higher
* GC.
*/
public Builder bufferPoolingEnabled(boolean bufferPoolingEnabled) {
this.bufferPoolingEnabled = bufferPoolingEnabled;
return this;
}
/**
* If TCP_NODELAY is manually disabled, Nagle'ing will take effect on both the client
* and (if supported) the server side.
*/
public Builder tcpNodelayEnabled(boolean tcpNodelayEnabled) {
this.tcpNodelayEnabled = tcpNodelayEnabled;
return this;
}
/**
* If mutation tokens are enabled, they can be used for advanced durability requirements,
* as well as optimized RYOW consistency.
*
* Note that just enabling it here won't help if the server does not support it as well. Use at
* least Couchbase Server 4.0. Also, consider the additional overhead of 16 bytes per mutation response
* (8 byte for the vbucket uuid and 8 byte for the sequence number).
*/
public Builder mutationTokensEnabled(boolean mutationTokensEnabled) {
this.mutationTokensEnabled = mutationTokensEnabled;
return this;
}
/**
* Sets a custom configuration for the {@link RuntimeMetricsCollector}.
*
* @param metricsCollectorConfig the custom configuration
*/
public Builder runtimeMetricsCollectorConfig(MetricsCollectorConfig metricsCollectorConfig) {
this.runtimeMetricsCollectorConfig = metricsCollectorConfig;
return this;
}
/**
* Sets a custom configuration for the {@link NetworkLatencyMetricsCollector}.
*
* @param metricsCollectorConfig the custom configuration for the collector.
*/
public Builder networkLatencyMetricsCollectorConfig(LatencyMetricsCollectorConfig metricsCollectorConfig) {
this.networkLatencyMetricsCollectorConfig = metricsCollectorConfig;
return this;
}
public Builder defaultMetricsLoggingConsumer(boolean enabled, CouchbaseLogLevel level, LoggingConsumer.OutputFormat format) {
if (enabled) {
defaultMetricsLoggingConsumer = LoggingConsumer.create(level, format);
} else {
defaultMetricsLoggingConsumer = null;
}
return this;
}
public Builder defaultMetricsLoggingConsumer(boolean enabled, CouchbaseLogLevel level) {
return defaultMetricsLoggingConsumer(enabled, level, LoggingConsumer.DEFAULT_FORMAT);
}
/**
* Sets a custom socket connect timeout.
*
* @param socketConnectTimeout the socket connect timeout in milliseconds.
*/
public Builder socketConnectTimeout(int socketConnectTimeout) {
this.socketConnectTimeout = socketConnectTimeout;
return this;
}
/**
* Set to true if the {@link Observable} callbacks should be completed on the IO event loops.
*
* Note: this is an advanced option and must be used with care. It can be used to improve performance since it
* removes additional scheduling overhead on the response path, but any blocking calls in the callbacks will
* lead to more work on the event loops itself and eventually stall them.
*
* USE WITH CARE!
*/
public Builder callbacksOnIoPool(boolean callbacksOnIoPool) {
this.callbacksOnIoPool = callbacksOnIoPool;
return this;
}
/**
* Sets a custom disconnect timeout.
*
* @param disconnectTimeout the disconnect timeout in milliseconds.
*/
public Builder disconnectTimeout(long disconnectTimeout) {
this.disconnectTimeout = disconnectTimeout;
return this;
}
/**
* Sets a custom waiting strategy for requests. Default is {@link BlockingWaitStrategy}.
*
* @param waitStrategy waiting strategy
*/
@InterfaceStability.Experimental
@InterfaceAudience.Public
public Builder requestBufferWaitStrategy(WaitStrategyFactory waitStrategy) {
this.requestBufferWaitStrategy = waitStrategy;
return this;
}
/**
* Sets a custom memcached node hashing strategy, mainly used for compatibility with other clients.
*
* @param memcachedHashingStrategy the strategy to use.
*/
public Builder memcachedHashingStrategy(MemcachedHashingStrategy memcachedHashingStrategy) {
this.memcachedHashingStrategy = memcachedHashingStrategy;
return this;
}
/**
* Allows to set a custom configuration for the KV service.
*
* @param keyValueServiceConfig the config to apply.
*/
public Builder keyValueServiceConfig(KeyValueServiceConfig keyValueServiceConfig) {
this.keyValueServiceConfig = keyValueServiceConfig;
return this;
}
/**
* Allows to set a custom configuration for the View service.
*
* @param viewServiceConfig the config to apply.
*/
public Builder viewServiceConfig(ViewServiceConfig viewServiceConfig) {
this.viewServiceConfig = viewServiceConfig;
return this;
}
/**
* Allows to set a custom configuration for the Query service.
*
* @param queryServiceConfig the config to apply.
*/
public Builder queryServiceConfig(QueryServiceConfig queryServiceConfig) {
this.queryServiceConfig = queryServiceConfig;
return this;
}
/**
* Allows to set a custom configuration for the Search service.
*
* @param searchServiceConfig the config to apply.
*/
public Builder searchServiceConfig(SearchServiceConfig searchServiceConfig) {
this.searchServiceConfig = searchServiceConfig;
return this;
}
/**
* Allows to set the configuration poll interval which polls the server cluster
* configuration proactively.
*
* Note that the interval cannot be set lower than 2500 millisconds (other than 0
* to disable it).
* @param configPollInterval the interval in milliseconds, 0 deactivates the polling.
*/
@InterfaceStability.Experimental
@InterfaceAudience.Public
public Builder configPollInterval(long configPollInterval) {
if (configPollInterval < 2500 && configPollInterval != 0) {
throw new IllegalArgumentException("The poll interval cannot be lower than " +
"2500 milliseconds");
}
this.configPollInterval = configPollInterval;
return this;
}
public DefaultCoreEnvironment build() {
return new DefaultCoreEnvironment(this);
}
}
/**
* Dumps the environment parameters known to this implementation into a {@link StringBuilder},
* which is returned for method chaining.
*
* @param sb the StringBuilder in which to dump parameters.
* @return the same StringBuilder for method chaining.
*/
protected StringBuilder dumpParameters(StringBuilder sb) {
sb.append("sslEnabled=").append(sslEnabled);
sb.append(", sslKeystoreFile='").append(sslKeystoreFile).append('\'');
sb.append(", sslKeystorePassword=").append(sslKeystorePassword != null && !sslKeystorePassword.isEmpty());
sb.append(", sslKeystore=").append(sslKeystore);
sb.append(", bootstrapHttpEnabled=").append(bootstrapHttpEnabled);
sb.append(", bootstrapCarrierEnabled=").append(bootstrapCarrierEnabled);
sb.append(", bootstrapHttpDirectPort=").append(bootstrapHttpDirectPort);
sb.append(", bootstrapHttpSslPort=").append(bootstrapHttpSslPort);
sb.append(", bootstrapCarrierDirectPort=").append(bootstrapCarrierDirectPort);
sb.append(", bootstrapCarrierSslPort=").append(bootstrapCarrierSslPort);
sb.append(", ioPoolSize=").append(ioPoolSize);
sb.append(", computationPoolSize=").append(computationPoolSize);
sb.append(", responseBufferSize=").append(responseBufferSize);
sb.append(", requestBufferSize=").append(requestBufferSize);
sb.append(", kvServiceEndpoints=").append(kvServiceEndpoints);
sb.append(", viewServiceEndpoints=").append(viewServiceEndpoints);
sb.append(", queryServiceEndpoints=").append(queryServiceEndpoints);
sb.append(", searchServiceEndpoints=").append(searchServiceEndpoints);
sb.append(", configPollInterval=").append(configPollInterval);
sb.append(", ioPool=").append(ioPool.getClass().getSimpleName());
if (ioPoolShutdownHook == null || ioPoolShutdownHook instanceof NoOpShutdownHook) {
sb.append("!unmanaged");
}
if (kvIoPool != null) {
sb.append(", kvIoPool=").append(kvIoPool.getClass().getSimpleName());
if (kvIoPoolShutdownHook == null || kvIoPoolShutdownHook instanceof NoOpShutdownHook) {
sb.append("!unmanaged");
}
} else {
sb.append(", kvIoPool=").append("null");
}
if (viewIoPool != null) {
sb.append(", viewIoPool=").append(viewIoPool.getClass().getSimpleName());
if (viewIoPoolShutdownHook == null || viewIoPoolShutdownHook instanceof NoOpShutdownHook) {
sb.append("!unmanaged");
}
} else {
sb.append(", viewIoPool=").append("null");
}
if (searchIoPool != null) {
sb.append(", searchIoPool=").append(searchIoPool.getClass().getSimpleName());
if (searchIoPoolShutdownHook == null || searchIoPoolShutdownHook instanceof NoOpShutdownHook) {
sb.append("!unmanaged");
}
} else {
sb.append(", searchIoPool=").append("null");
}
if (queryIoPool != null) {
sb.append(", queryIoPool=").append(queryIoPool.getClass().getSimpleName());
if (queryIoPoolShutdownHook == null || queryIoPoolShutdownHook instanceof NoOpShutdownHook) {
sb.append("!unmanaged");
}
} else {
sb.append(", queryIoPool=").append("null");
}
sb.append(", coreScheduler=").append(coreScheduler.getClass().getSimpleName());
if (coreSchedulerShutdownHook == null || coreSchedulerShutdownHook instanceof NoOpShutdownHook) {
sb.append("!unmanaged");
}
sb.append(", memcachedHashingStrategy=").append(memcachedHashingStrategy.getClass().getSimpleName());
sb.append(", eventBus=").append(eventBus.getClass().getSimpleName());
sb.append(", packageNameAndVersion=").append(packageNameAndVersion);
sb.append(", dcpEnabled=").append(dcpEnabled);
sb.append(", retryStrategy=").append(retryStrategy);
sb.append(", maxRequestLifetime=").append(maxRequestLifetime);
sb.append(", retryDelay=").append(retryDelay);
sb.append(", reconnectDelay=").append(reconnectDelay);
sb.append(", observeIntervalDelay=").append(observeIntervalDelay);
sb.append(", keepAliveInterval=").append(keepAliveInterval);
sb.append(", autoreleaseAfter=").append(autoreleaseAfter);
sb.append(", bufferPoolingEnabled=").append(bufferPoolingEnabled);
sb.append(", tcpNodelayEnabled=").append(tcpNodelayEnabled);
sb.append(", mutationTokensEnabled=").append(mutationTokensEnabled);
sb.append(", socketConnectTimeout=").append(socketConnectTimeout);
sb.append(", dcpConnectionBufferSize=").append(dcpConnectionBufferSize);
sb.append(", dcpConnectionBufferAckThreshold=").append(dcpConnectionBufferAckThreshold);
sb.append(", dcpConnectionName=").append(dcpConnectionName);
sb.append(", callbacksOnIoPool=").append(callbacksOnIoPool);
sb.append(", disconnectTimeout=").append(disconnectTimeout);
sb.append(", requestBufferWaitStrategy=").append(requestBufferWaitStrategy);
return sb;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("CoreEnvironment: {");
dumpParameters(sb).append('}');
return sb.toString();
}
/**
* An internal class used to keep track of components being shut down and
* the result of their shutdown call / cause for failures.
*/
private static final class ShutdownStatus {
public final String target;
public final boolean success;
public final Throwable cause;
public ShutdownStatus(String target, boolean success, Throwable cause) {
this.target = target;
this.success = success;
this.cause = cause;
}
@Override
public String toString() {
return "Shutdown " + target + ": " + (success ? "success " : "failure ") + (cause == null ? "" : " due to "
+ cause.toString());
}
}
}