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

com.netflix.dyno.recipes.counter.DynoJedisPipelineCounter Maven / Gradle / Ivy

There is a newer version: 1.9.1
Show newest version
/*******************************************************************************
 * Copyright 2011 Netflix
 *
 * 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.netflix.dyno.recipes.counter;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.annotation.concurrent.ThreadSafe;

import org.slf4j.LoggerFactory;

import com.netflix.dyno.jedis.DynoJedisClient;
import com.netflix.dyno.jedis.DynoJedisPipeline;
import com.netflix.dyno.recipes.util.Tuple;

/**
 * Pipeline implementation of {@link DynoCounter}. This implementation has slightly different semantics than
 * {@link DynoJedisPipeline} in that both {@link #incr()} and {@link #sync()} are asynchronous.
 * 

* Note that this implementation is thread-safe whereas {@link DynoJedisPipeline} is not. *

* * @see Redis Pipelining * * @author jcacciatore */ @ThreadSafe public class DynoJedisPipelineCounter extends DynoJedisCounter { private enum Command { INCR, SYNC, STOP } private static final org.slf4j.Logger logger = LoggerFactory.getLogger(DynoJedisPipelineCounter.class); private final LinkedBlockingQueue queue = new LinkedBlockingQueue(); private final ExecutorService counterThreadPool = Executors.newSingleThreadExecutor(new ThreadFactory() { @Override public Thread newThread(Runnable r) { return new Thread(r, "DynoJedisPipelineCounter-Poller"); } }); private final AtomicBoolean initialized = new AtomicBoolean(false); private final CountDownLatch latch = new CountDownLatch(1); private final Consumer consumer; public DynoJedisPipelineCounter(String key, DynoJedisClient client) { super(key, client); this.consumer = new Consumer(queue, generatedKeys); } @Override public void initialize() { if (initialized.compareAndSet(false, true)) { super.initialize(); counterThreadPool.submit(consumer); } } @Override public void incr() { if (!initialized.get()) { throw new IllegalStateException("Counter has not been initialized"); } queue.offer(Command.INCR); } public void sync() { if (!initialized.get()) { throw new IllegalStateException("Counter has not been initialized"); } logger.debug("sending SYNC offer"); queue.offer(Command.SYNC); } @Override public void close() { if (!initialized.get()) { throw new IllegalStateException("Counter has not been initialized"); } queue.offer(Command.STOP); try { latch.await(2000L, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { // ignore } } class Consumer implements Runnable { /** * Used as a synchronizer between producer threads and this Consumer. */ private final LinkedBlockingQueue queue; /** * Keys that were generated by an instance of a {@link DynoCounter}. */ private final List keys; /** * Used for debugging */ private Long syncCount = 0L; /** * Used to ensure there are operations to sync in the pipeline. This is not * an optimization; the pipeline can block if multiple SYNCs are processed */ private int pipelineOps = 0; /** * Contains a mapping of sharded-key to pipeline. */ private List> keysAndPipelines; public Consumer(final LinkedBlockingQueue queue, final List keys) { this.queue = queue; this.keys = keys; keysAndPipelines = new ArrayList>(keys.size()); for (String key: keys) { keysAndPipelines.add(new Tuple(key, client.pipelined())); } } @Override public void run() { Command cmd = null; do { try { cmd = queue.take(); switch (cmd) { case INCR: { Tuple tuple = keysAndPipelines.get(randomIntFrom0toN()); tuple._2().incr(tuple._1()); pipelineOps++; break; } case SYNC: { syncCount++; logger.debug(Thread.currentThread().getName() + " - SYNC " + syncCount + " received"); if (pipelineOps > 0) { for (Tuple tuple : keysAndPipelines) { tuple._2().sync(); } keysAndPipelines = new ArrayList>(keys.size()); for (String key : keys) { keysAndPipelines.add(new Tuple(key, client.pipelined())); } pipelineOps = 0; } logger.debug(Thread.currentThread().getName() + " - SYNC " + syncCount + " done"); break; } case STOP: { counterThreadPool.shutdownNow(); latch.countDown(); break; } } } catch (InterruptedException e) { // ignore } } while (cmd != Command.STOP); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy