All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.couchbase.client.CouchbaseConnectionFactory Maven / Gradle / Ivy

There is a newer version: 1.4.13
Show newest version
/**
 * Copyright (C) 2009-2013 Couchbase, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALING
 * IN THE SOFTWARE.
 */

package com.couchbase.client;

import com.couchbase.client.vbucket.ConfigurationException;
import com.couchbase.client.vbucket.provider.BucketConfigurationProvider;
import com.couchbase.client.vbucket.provider.ConfigurationProvider;
import com.couchbase.client.vbucket.ConfigurationProviderHTTP;
import com.couchbase.client.vbucket.CouchbaseNodeOrder;
import com.couchbase.client.vbucket.Reconfigurable;
import com.couchbase.client.vbucket.VBucketNodeLocator;
import com.couchbase.client.vbucket.config.Bucket;
import com.couchbase.client.vbucket.config.Config;
import com.couchbase.client.vbucket.config.ConfigType;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;

import net.spy.memcached.BinaryConnectionFactory;
import net.spy.memcached.DefaultHashAlgorithm;
import net.spy.memcached.FailureMode;
import net.spy.memcached.HashAlgorithm;
import net.spy.memcached.KetamaNodeLocator;
import net.spy.memcached.MemcachedConnection;
import net.spy.memcached.MemcachedNode;
import net.spy.memcached.NodeLocator;
import net.spy.memcached.auth.AuthDescriptor;
import net.spy.memcached.auth.PlainCallbackHandler;

/**
 * Couchbase implementation of ConnectionFactory.
 *
 * 

* This implementation creates connections where the operation queue is an * ArrayBlockingQueue and the read and write queues are unbounded * LinkedBlockingQueues. The Retry FailureMode and * KetamaHash VBucket hashing mechanism are always used. If other * configurations are needed, look at the ConnectionFactoryBuilder. * *

*/ public class CouchbaseConnectionFactory extends BinaryConnectionFactory { /** * Default failure mode. */ public static final FailureMode DEFAULT_FAILURE_MODE = FailureMode.Redistribute; /** * Default hash algorithm. */ public static final HashAlgorithm DEFAULT_HASH = DefaultHashAlgorithm.NATIVE_HASH; /** * Maximum length of the operation queue returned by this connection factory. */ public static final int DEFAULT_OP_QUEUE_LEN = 16384; /** * Specify a default minimum reconnect interval of 1.1s. * * This means that if a reconnect is needed, it won't try to reconnect * more frequently than 1.1s between tries. The initial HTTP connections * under us take up to 500ms per request. */ public static final long DEFAULT_MIN_RECONNECT_INTERVAL = 1100; /** * Default View request timeout in ms. */ public static final int DEFAULT_VIEW_TIMEOUT = 75000; /** * Default size of view io worker threads. */ public static final int DEFAULT_VIEW_WORKER_SIZE = 1; /** * Default amount of max connections per node. */ public static final int DEFAULT_VIEW_CONNS_PER_NODE = 10; /** * Default Timeout when persistence/replication constraints are used (in ms). */ public static final long DEFAULT_OBS_TIMEOUT = 5000; /** * Default Observe poll interval in ms. */ public static final long DEFAULT_OBS_POLL_INTERVAL = 10; /** * Default maximum amount of poll cycles before failure. * * See {@link #DEFAULT_OBS_TIMEOUT} for correct use. The number of polls is * now calculated automatically based on the {@link #DEFAULT_OBS_TIMEOUT} and * {@link #DEFAULT_OBS_POLL_INTERVAL}. */ @Deprecated public static final int DEFAULT_OBS_POLL_MAX = 500; /** * Default auth wait time. */ public static final long DEFAULT_AUTH_WAIT_TIME = 2500; /** * Default Node ordering to use for streaming connection. */ public static final CouchbaseNodeOrder DEFAULT_STREAMING_NODE_ORDER = CouchbaseNodeOrder.RANDOM; protected volatile ConfigurationProvider configurationProvider; private volatile String bucket; private volatile String pass; private volatile List storedBaseList; private static final Logger LOGGER = Logger.getLogger(CouchbaseConnectionFactory.class.getName()); private volatile boolean needsReconnect; private final AtomicBoolean doingResubscribe = new AtomicBoolean(false); private volatile long thresholdLastCheck = System.nanoTime(); private final AtomicInteger configThresholdCount = new AtomicInteger(0); private final int maxConfigCheck = 10; //maximum allowed checks before we // reconnect in a 10 sec interval private volatile long configProviderLastUpdateTimestamp; private long minReconnectInterval = DEFAULT_MIN_RECONNECT_INTERVAL; private final ExecutorService resubExec = Executors.newSingleThreadExecutor(); private final CouchbaseNodeOrder nodeOrder = DEFAULT_STREAMING_NODE_ORDER; private ClusterManager clusterManager; /** * Create a new {@link CouchbaseConnectionFactory} and load the required * connection information from system properties. * *

The following properties need to be set in order to bootstrap: * - cbclient.nodes: ;-separated list of URIs * - cbclient.bucket: name of the bucket * - cbclient.password: password of the bucket *

*/ public CouchbaseConnectionFactory() { String nodes = CouchbaseProperties.getProperty("nodes"); String bucket = CouchbaseProperties.getProperty("bucket"); String password = CouchbaseProperties.getProperty("password"); if (nodes == null) { throw new IllegalArgumentException("System property cbclient.nodes " + "not set or empty"); } if (bucket == null) { throw new IllegalArgumentException("System property cbclient.bucket " + "not set or empty"); } if (password == null) { throw new IllegalArgumentException("System property cbclient.password " + "not set or empty"); } List baseList = new ArrayList(); String[] nodeList = nodes.split(";"); for (String node : nodeList) { try { baseList.add(new URI(node)); } catch (Exception e) { throw new IllegalArgumentException("Could not parse node list into " + " URI format."); } } initialize(baseList, bucket, password); } public CouchbaseConnectionFactory(final List baseList, final String bucketName, String password) throws IOException { initialize(baseList, bucketName, password); } private void initialize(List baseList, String bucket, String password) { storedBaseList = new ArrayList(); for (URI bu : baseList) { if (!bu.isAbsolute()) { throw new IllegalArgumentException("The base URI must be absolute"); } storedBaseList.add(bu); } if (bucket == null || bucket.isEmpty()) { throw new IllegalArgumentException("The bucket name must not be null " + "or empty."); } if (password == null) { throw new IllegalArgumentException("The bucket password must not be " + " null."); } this.bucket = bucket; pass = password; configurationProvider = new BucketConfigurationProvider(baseList, bucket, password, this); } @Override public MemcachedConnection createConnection(List addrs) throws IOException { Config config = getVBucketConfig(); if (config.getConfigType() == ConfigType.MEMCACHE) { return new CouchbaseMemcachedConnection(getReadBufSize(), this, addrs, getInitialObservers(), getFailureMode(), getOperationFactory()); } else if (config.getConfigType() == ConfigType.COUCHBASE) { return new CouchbaseConnection(getReadBufSize(), this, addrs, getInitialObservers(), getFailureMode(), getOperationFactory()); } throw new IOException("No ConnectionFactory for bucket type " + config.getConfigType()); } public ViewConnection createViewConnection( List addrs) throws IOException { return new ViewConnection(this, addrs, bucket, pass); } @Override public NodeLocator createLocator(List nodes) { Config config = getVBucketConfig(); if (config == null) { throw new IllegalStateException("Couldn't get config"); } if (config.getConfigType() == ConfigType.MEMCACHE) { return new KetamaNodeLocator(nodes, DefaultHashAlgorithm.KETAMA_HASH); } else if (config.getConfigType() == ConfigType.COUCHBASE) { return new VBucketNodeLocator(nodes, getVBucketConfig()); } else { throw new IllegalStateException("Unhandled locator type: " + config.getConfigType()); } } /* * (non-Javadoc) * * @see net.spy.memcached.ConnectionFactory#shouldOptimize() */ @Override public boolean shouldOptimize() { return false; } @Override public AuthDescriptor getAuthDescriptor() { if (!configurationProvider.getAnonymousAuthBucket().equals(bucket) && bucket != null) { return new AuthDescriptor(new String[] {}, new PlainCallbackHandler(bucket, pass)); } else { return null; } } public String getBucketName() { return bucket; } public int getViewTimeout() { return DEFAULT_VIEW_TIMEOUT; } public int getViewWorkerSize() { return DEFAULT_VIEW_WORKER_SIZE; } public int getViewConnsPerNode() { return DEFAULT_VIEW_CONNS_PER_NODE; } public CouchbaseNodeOrder getStreamingNodeOrder() { return nodeOrder; } public Config getVBucketConfig() { return configurationProvider.getConfig().getConfig(); } public synchronized ConfigurationProvider getConfigurationProvider() { return configurationProvider; } protected void requestConfigReconnect(String bucketName, Reconfigurable rec) { configurationProvider.signalOutdated(); needsReconnect = true; } synchronized void setConfigurationProvider( ConfigurationProvider configProvider) { this.configProviderLastUpdateTimestamp = System.currentTimeMillis(); this.configurationProvider = configProvider; } void setMinReconnectInterval(long reconnIntervalMsecs) { this.minReconnectInterval = reconnIntervalMsecs; } /** * Check if a configuration update is needed. * * There are two main reasons that can trigger a configuration update. Either * there is a configuration update happening in the cluster, or operations * added to the queue can not find their corresponding node. For the latter, * see the {@link #pastReconnThreshold()} method for further details. * * If a configuration update is needed, a resubscription for configuration * updates is triggered. Note that since reconnection takes some time, * the method will also wait a time period given by * {@link #getMinReconnectInterval()} before the resubscription is triggered. */ void checkConfigUpdate() { if (needsReconnect || pastReconnThreshold()) { long now = System.currentTimeMillis(); long intervalWaited = now - this.configProviderLastUpdateTimestamp; if (intervalWaited < this.getMinReconnectInterval()) { LOGGER.log(Level.FINE, "Ignoring config update check. Only {0}ms out" + " of a threshold of {1}ms since last update.", new Object[]{intervalWaited, this.getMinReconnectInterval()}); return; } if (doingResubscribe.compareAndSet(false, true)) { getConfigurationProvider().signalOutdated(); } else { LOGGER.log(Level.CONFIG, "Duplicate resubscribe for config updates" + " suppressed."); } } else { LOGGER.log(Level.FINE, "No reconnect required, though check requested." + " Current config check is {0} out of a threshold of {1}.", new Object[]{configThresholdCount, maxConfigCheck}); } } /** * Checks if there have been more requests than allowed through * maxConfigCheck in a 10 second period. * * If this is the case, then true is returned. If the timeframe between * two distinct requests is more than 10 seconds, a fresh timeframe starts. * This means that 10 calls every second would trigger an update while * 1 operation, then a 11 second sleep and one more operation would not. * * @return true if there were more config check requests than maxConfigCheck * in the 10 second period. */ protected boolean pastReconnThreshold() { long currentTime = System.nanoTime(); if (currentTime - thresholdLastCheck >= TimeUnit.SECONDS.toNanos(10)) { configThresholdCount.set(0); } thresholdLastCheck = currentTime; if (configThresholdCount.incrementAndGet() >= maxConfigCheck) { return true; } return false; } /** * The minimum reconnect interval in milliseconds. * * @return the minReconnectInterval */ public long getMinReconnectInterval() { return minReconnectInterval; } /** * The observe poll interval in milliseconds. * * @return the observe poll interval. */ public long getObsPollInterval() { return DEFAULT_OBS_POLL_INTERVAL; } /** * The observe timeout in milliseconds. * * @return the observe timeout. */ public long getObsTimeout() { return DEFAULT_OBS_TIMEOUT; } @Override public long getAuthWaitTime() { return DEFAULT_AUTH_WAIT_TIME; } /** * The number of observe polls to execute before giving up. * * It is calculated out of the observe timeout and the observe interval, * rounded to the next largest integer value. * * @return the number of polls. */ public int getObsPollMax() { return new Double( Math.ceil((double) getObsTimeout() / getObsPollInterval()) ).intValue(); } /** * Returns the amount of how many config checks in a given time period * (currently 10 seconds) are allowed before a reconfiguration is triggered. * * @return the number of config checks allowed. */ int getMaxConfigCheck() { return maxConfigCheck; } /** * Returns a ClusterManager and initializes one if it does not exist. * @return Returns an instance of a ClusterManager. */ public ClusterManager getClusterManager() { if(clusterManager == null) { clusterManager = new ClusterManager(storedBaseList, bucket, pass); } return clusterManager; } /** * Returns the current base list. * * @return the base list. */ List getStoredBaseList() { return storedBaseList; } @Override public String toString() { final StringBuilder sb = new StringBuilder("CouchbaseConnectionFactory{"); sb.append("bucket='").append(getBucketName()).append('\''); sb.append(", nodes=").append(getStoredBaseList()); sb.append(", order=").append(getStreamingNodeOrder()); sb.append(", opTimeout=").append(getOperationTimeout()); sb.append(", opQueue=").append(getOpQueueLen()); sb.append(", opQueueBlockTime=").append(getOpQueueMaxBlockTime()); sb.append(", obsPollInt=").append(getObsPollInterval()); sb.append(", obsPollMax=").append(getObsPollMax()); sb.append(", obsTimeout=").append(getObsTimeout()); sb.append(", viewConns=").append(getViewConnsPerNode()); sb.append(", viewTimeout=").append(getViewTimeout()); sb.append(", viewWorkers=").append(getViewWorkerSize()); sb.append(", configCheck=").append(getMaxConfigCheck()); sb.append(", reconnectInt=").append(getMinReconnectInterval()); sb.append(", failureMode=").append(getFailureMode()); sb.append(", hashAlgo=").append(getHashAlg()); sb.append(", authWaitTime=").append(getAuthWaitTime()); sb.append('}'); return sb.toString(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy