/* * 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.service.strategies; import com.couchbase.client.core.endpoint.Endpoint; import com.couchbase.client.core.message.CouchbaseRequest; import com.couchbase.client.core.message.kv.BinaryRequest; import com.couchbase.client.core.message.kv.GetBucketConfigRequest; import com.couchbase.client.core.state.LifecycleState; import java.util.List; /** * Selects the {@link Endpoint} based on the information enclosed in the {@link CouchbaseRequest}. * * This strategy can be used to "pin" certain requests to specific endpoints based on the supplied information. The * current implementation uses this technique to tie ID-based {@link BinaryRequest}s to the same endpoint to enforce * at least some amount of ordering guarantees. * * @author Michael Nitschinger * @since 1.0 */ public class PartitionSelectionStrategy implements SelectionStrategy { public static final PartitionSelectionStrategy INSTANCE = new PartitionSelectionStrategy(); private PartitionSelectionStrategy() { // singleton. } @Override public Endpoint select(final CouchbaseRequest request, final List<Endpoint> endpoints) { if (endpoints.size() == 0) { return null; } if (request instanceof BinaryRequest) { if (request instanceof GetBucketConfigRequest) { return selectFirstConnected(endpoints); } else { return selectByPartition(endpoints, ((BinaryRequest) request).partition()); } } else { throw new IllegalStateException("The PartitionSelectionStrategy does not understand: " + request); } } /** * Helper method to select the proper target endpoint by partition. * * @param endpoints the list of currently available endpoints. * @param partition the partition of the incoming request. * @return the selected endpoint, or null if no acceptable one found. */ private static Endpoint selectByPartition(final List<Endpoint> endpoints, final short partition) { if (partition >= 0) { int numEndpoints = endpoints.size(); Endpoint endpoint = numEndpoints == 1 ? endpoints.get(0) : endpoints.get(partition % numEndpoints); if (endpoint != null && endpoint.isState(LifecycleState.CONNECTED) && endpoint.isFree()) { return endpoint; } return null; } else { return selectFirstConnected(endpoints); } } /** * Helper method to select the first connected endpoint if no particular pinning is needed. * * @param endpoints the list of endpoints. * @return the first connected or null if none found. */ private static Endpoint selectFirstConnected(final List<Endpoint> endpoints) { for (Endpoint endpoint : endpoints) { if (endpoint.isState(LifecycleState.CONNECTED) && endpoint.isFree()) { return endpoint; } } return null; } }