/* * Copyright 2008-2013 LinkedIn, 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 voldemort.client; import static voldemort.cluster.failuredetector.FailureDetectorUtils.create; import java.net.URI; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; import voldemort.VoldemortException; import voldemort.client.protocol.RequestFormatType; import voldemort.cluster.Cluster; import voldemort.cluster.Node; import voldemort.cluster.failuredetector.ClientStoreConnectionVerifier; import voldemort.cluster.failuredetector.FailureDetector; import voldemort.cluster.failuredetector.FailureDetectorConfig; import voldemort.cluster.failuredetector.FailureDetectorListener; import voldemort.server.RequestRoutingType; import voldemort.store.Store; import voldemort.store.metadata.MetadataStore; import voldemort.store.socket.SocketDestination; import voldemort.store.socket.SocketStoreFactory; import voldemort.store.socket.clientrequest.ClientRequestExecutorPool; import voldemort.store.system.SystemStoreConstants; import voldemort.utils.ByteArray; import voldemort.versioning.InconsistencyResolver; import voldemort.versioning.Versioned; /** * A StoreClientFactory abstracts away the connection pooling, threading, and * bootstrapping mechanism. It can be used to create any number of * {@link voldemort.client.StoreClient StoreClient} instances for different * stores. * * */ public class SocketStoreClientFactory extends AbstractStoreClientFactory { public static final String URL_SCHEME = "tcp"; private final SocketStoreFactory storeFactory; private FailureDetectorListener failureDetectorListener; private final RequestRoutingType requestRoutingType; public SocketStoreClientFactory(ClientConfig config) { super(config); this.requestRoutingType = RequestRoutingType.getRequestRoutingType(RoutingTier.SERVER.equals(config.getRoutingTier()), false); this.storeFactory = new ClientRequestExecutorPool(config.getSelectors(), config.getMaxConnectionsPerNode(), config.getConnectionTimeout(TimeUnit.MILLISECONDS), config.getSocketTimeout(TimeUnit.MILLISECONDS), config.getIdleConnectionTimeout(TimeUnit.MILLISECONDS), config.getSocketBufferSize(), config.getSocketKeepAlive(), config.isJmxEnabled(), identifierString); } @Override public <K, V> StoreClient<K, V> getStoreClient(final String storeName, final InconsistencyResolver<Versioned<V>> resolver) { if(getConfig().isLazyEnabled()) return new LazyStoreClient<K, V>(new Callable<StoreClient<K, V>>() { public StoreClient<K, V> call() throws Exception { return getParentStoreClient(storeName, resolver); } }); return getParentStoreClient(storeName, resolver); } private <K, V> StoreClient<K, V> getParentStoreClient(String storeName, InconsistencyResolver<Versioned<V>> resolver) { return super.getStoreClient(storeName, resolver); } @Override protected List<Versioned<String>> getRemoteMetadata(String key, URI url) { try { return super.getRemoteMetadata(key, url); } catch(VoldemortException e) { // Fix SNA-4227: When an error occurs during bootstrap, close the // socket SocketDestination destination = new SocketDestination(url.getHost(), url.getPort(), getRequestFormatType()); storeFactory.close(destination); throw new VoldemortException(e); } } @Override protected Store<ByteArray, byte[], byte[]> getStore(String storeName, String host, int port, RequestFormatType type) { return storeFactory.create(storeName, host, port, type, requestRoutingType); } @Override protected FailureDetector initFailureDetector(final ClientConfig config, Cluster cluster) { failureDetectorListener = new FailureDetectorListener() { public void nodeAvailable(Node node) { } public void nodeUnavailable(Node node) { if(logger.isInfoEnabled()) logger.info(node + " has been marked as unavailable, destroying socket pool"); // Kill the socket pool for this node... SocketDestination destination = new SocketDestination(node.getHost(), node.getSocketPort(), config.getRequestFormatType()); storeFactory.close(destination); } }; ClientStoreConnectionVerifier verifier = new ClientStoreConnectionVerifier() { @Override protected Store<ByteArray, byte[], byte[]> getStoreInternal(Node node) { logger.debug("Returning a new store verifier for node: " + node); return SocketStoreClientFactory.this.getStore(MetadataStore.METADATA_STORE_NAME, node.getHost(), node.getSocketPort(), config.getRequestFormatType()); } }; FailureDetectorConfig failureDetectorConfig = new FailureDetectorConfig(config).setCluster(cluster) .setConnectionVerifier(verifier); return create(failureDetectorConfig, false, failureDetectorListener); } @Override protected int getPort(Node node) { return node.getSocketPort(); } @Override protected void validateUrl(URI url) { if(!URL_SCHEME.equals(url.getScheme())) throw new IllegalArgumentException("Illegal scheme in bootstrap URL for SocketStoreClientFactory:" + " expected '" + URL_SCHEME + "' but found '" + url.getScheme() + "'."); } @Override public void close() { this.storeFactory.close(); if(failureDetector != null) this.failureDetector.removeFailureDetectorListener(failureDetectorListener); super.close(); } public <K, V, T> Store<K, V, T> getSystemStore(String storeName, String clusterXml, FailureDetector fd) { return getRawStore(storeName, null, SystemStoreConstants.SYSTEM_STORE_SCHEMA, clusterXml, fd); } }