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

com.basho.riak.client.operations.MultiFetchObject Maven / Gradle / Ivy

There is a newer version: 2.1.1
Show newest version
/*
 * Copyright 2013 Basho Technologies 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.basho.riak.client.operations;

import com.basho.riak.client.cap.ConflictResolver;
import com.basho.riak.client.cap.Quora;
import com.basho.riak.client.cap.Quorum;
import com.basho.riak.client.cap.Retrier;
import com.basho.riak.client.cap.VClock;
import com.basho.riak.client.convert.Converter;
import com.basho.riak.client.query.MultiFetchFuture;
import com.basho.riak.client.raw.FetchMeta;
import com.basho.riak.client.raw.RawClient;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * An operation to fetch multiple values from Riak
 * 
 * 

* Use the {@link com.basho.riak.client.bucket.Bucket#multiFetch(java.lang.String[])}, * {@link com.basho.riak.client.bucket.Bucket#multiFetch(java.util.List, java.lang.Class)}, * or {@link com.basho.riak.client.bucket.Bucket#multiFetch(java.util.List, java.lang.Class)} * methods to create a mutli-fetch operation. *

*

* Riak itself does not support pipelining of requests. The MutliFetchObject addresses * this issue by using a threadpool to parallelize a set of fetch operations for * a given set of keys. *

*

* The result of calling {@link #execute() } is a {@code List} of {@link MultiFetchFuture} * objects each one representing a fetch operation. The simplest use would be a loop where * you iterate through and wait for them to complete: * *

 * {@code
 * List> futures = bucket.multiFetch(keys).execute();
 * List myResults = new ArrayList();
 * for (MultiFetchFuture f : futures)
 * {
 *     try 
 *     {
 *          MyPojo mp = f.get();
 *          myResults.add(mp);
 *     }
 *     catch (ExecutionException e)
 *     {
 *         // log error, etc.
 *     }
 * }
 * }
 * 
*

*

* Thread Pool:
* The internal {@link ThreadPoolExecutor} is static; all multi-fetch operations * performed by a single instance of the client use the same pool. This is to prevent resource * starvation in the case of multiple simultaneous multi-fetch operations. Idle threads * (including core threads) are timed out after 5 seconds.

* The defaults for {@code corePoolSize} is determined by the Java * Runtime using:

* {@code Runtime.getRuntime().availableProcessors() * 2;} *

*

* Advanced users can tune this via the {@link #setCorePoolSize(int)} * method; this is passed directly to its counterpart in the * {@link ThreadPoolExecutor}. The queue feeding the threadpool * is unbounded therefore {@link ThreadPoolExecutor#setMaximumPoolSize(int) } * has no effect and is simply set to match. *

*

* Be aware that because requests are being parallelized performance is also * dependent on the client's underlying connection pool. If there are no connections * available performance will suffer initially as connections will need to be established * or worse they could time out. *

* * @author Brian Roach * @see com.basho.riak.client.bucket.Bucket * @see com.basho.riak.client.RiakFactory */ public class MultiFetchObject implements RiakOperation>> { /** * The initial value for both corePoolSize and maximumPoolSize. This is determined via: * {@code Runtime.getRuntime().availableProcessors() * 2;} * @see ThreadPoolExecutor */ public static final int DEFAULT_POOL_MAX_SIZE = Runtime.getRuntime().availableProcessors() * 2; private static final LinkedBlockingQueue workQueue = new LinkedBlockingQueue(); private static final ThreadPoolExecutor threadPool = new ThreadPoolExecutor(DEFAULT_POOL_MAX_SIZE, DEFAULT_POOL_MAX_SIZE, 5, TimeUnit.SECONDS, workQueue); static { threadPool.allowCoreThreadTimeOut(true); } private final String bucket; private final RawClient client; private final List keys; private FetchMeta.Builder builder = new FetchMeta.Builder(); private ConflictResolver resolver; private Converter converter; private Retrier retrier; /** * Create a new MultiFetchOperation that delegates to the given * client to fetch the data from bucket for * keys using retrier *

* Use {@link com.basho.riak.client.bucket.Bucket} to create a Fetch operation. * * @param client * the {@link RawClient} to use for the operation * @param bucket * the name of the bucket to get the data item from * @param keys * the keys to get the data items from * @param retrier * the {@link Retrier} to use when executing the operation. */ public MultiFetchObject(final RawClient client, final String bucket, final List keys, final Retrier retrier) { this.bucket = bucket; this.client = client; this.keys = keys; this.retrier = retrier; } /** * Attempts to fetch the data for all the keys, convert it with * {@link Converter} and resolve any siblings with {@link ConflictResolver} * * @return a List of {@link MultiFetchFuture} objects. */ public List> execute() { List> futureList = new ArrayList>(keys.size()); FetchMeta fetchMeta = builder.build(); for (String key : keys) { FetchObject fetchObject = new FetchObject(client, bucket, key, retrier, fetchMeta) .withConverter(converter) .withResolver(resolver); MultiFetchCallable callable = new MultiFetchCallable(fetchObject); MultiFetchFuture task = new MultiFetchFuture(key, callable); futureList.add(task); threadPool.execute(task); } return futureList; } /** * Sets the core number of threads in the internal {@link ThreadPoolExecutor}. * * @param size - the new core size * @see ThreadPoolExecutor#setCorePoolSize(int) */ public static void setCorePoolSize(int size) { threadPool.setCorePoolSize(size); threadPool.setMaximumPoolSize(size); } /** * Returns the core number of threads from the internal {@link ThreadPoolExecutor} * @return the core number of threads * @see ThreadPoolExecutor#getCorePoolSize() */ public static int getCorePoolSize() { return threadPool.getCorePoolSize(); } /** * Sets the {@link ConflictResolver} to use for this multi-fetch operation. * @param resolver * @return this */ public MultiFetchObject withResolver(ConflictResolver resolver) { this.resolver = resolver; return this; } /** * The read quorum for this fetch operation * @param r an Integer for the read quorum * @return this */ public MultiFetchObject r(int r) { builder.r(r); return this; } /** * The read quorum for this fetch operation * @param r an Quora for the read quorum * @return this */ public MultiFetchObject r(Quora r) { builder.r(r); return this; } /** * The read quorum for this fetch operation * @param r an Quorum for the read quorum * @return this */ public MultiFetchObject r(Quorum r) { builder.r(r); return this; } /** * @param pr * @return this * @see com.basho.riak.client.raw.FetchMeta.Builder#pr(int) */ public MultiFetchObject pr(int pr) { builder.pr(pr); return this; } /** * @param pr * @return this * @see com.basho.riak.client.raw.FetchMeta.Builder#pr(Quora) */ public MultiFetchObject pr(Quora pr) { builder.pr(pr); return this; } /** * @param pr * @return this * @see com.basho.riak.client.raw.FetchMeta.Builder#pr(Quora) */ public MultiFetchObject pr(Quorum pr) { builder.pr(pr); return this; } /** * @param notFoundOK * @return this * @see com.basho.riak.client.raw.FetchMeta.Builder#notFoundOK(boolean) */ public MultiFetchObject notFoundOK(boolean notFoundOK) { builder.notFoundOK(notFoundOK); return this; } /** * @param basicQuorum * @return this * @see com.basho.riak.client.raw.FetchMeta.Builder#basicQuorum(boolean) */ public MultiFetchObject basicQuorum(boolean basicQuorum) { builder.basicQuorum(basicQuorum); return this; } /** * Set an operation timeout in milliseconds to be sent to Riak * * As of 1.4 Riak allows a timeout to be sent for get, put, and delete operations. * The client will receive a timeout error if the operation is not completed * within the specified time * * @param timeout the timeout in milliseconds * @return this */ public MultiFetchObject timeout(int timeout) { builder.timeout(timeout); return this; } /** * @param returnDeletedVClock * @return this * @see com.basho.riak.client.raw.FetchMeta.Builder#returnDeletedVClock(boolean) */ public MultiFetchObject returnDeletedVClock(boolean returnDeletedVClock) { builder.returnDeletedVClock(returnDeletedVClock); return this; } /** * *NOTE* HTTP Only. * * @param modifiedSince * a last modified date. * * @return this */ public MultiFetchObject modifiedSince(Date modifiedSince) { builder.modifiedSince(modifiedSince); return this; } /** * Causes the client to retrieve only the metadata and not the value * of this object. * * Note if you are using HTTP: If siblings are present the client * does a second get and retrieves all the values. This is due to how * the HTTP API handles siblings. * * Note: The {@link Converter} being used must be able to handle an empty * value. * * @return this * @see com.basho.riak.client.raw.FetchMeta.Builder#headOnly(boolean headOnly) */ public MultiFetchObject headOnly() { builder.headOnly(true); return this; } /** * A {@link Converter} to use to convert the data fetched to some other type * @param converter * @return this */ public MultiFetchObject withConverter(Converter converter) { this.converter = converter; return this; } /** * A {@link Retrier} to use * @param retrier * @return this */ public MultiFetchObject withRetrier(final Retrier retrier) { this.retrier = retrier; return this; } private class MultiFetchCallable implements Callable { private FetchObject fetchObject; public MultiFetchCallable(FetchObject fetchObject) { this.fetchObject = fetchObject; } public T call() throws Exception { return fetchObject.execute(); } } }