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

com.usergrid.count.AbstractBatcher Maven / Gradle / Ivy

There is a newer version: 0.0.27.1
Show newest version
/*******************************************************************************
 * Copyright 2012 Apigee Corporation
 * 
 * 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.usergrid.count;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

import com.usergrid.count.common.Count;
import com.yammer.metrics.Metrics;
import com.yammer.metrics.core.Counter;
import com.yammer.metrics.core.Timer;
import com.yammer.metrics.core.TimerContext;

/**
 * Base batcher implementation, handles concurrency and locking throughput
 * throttling.
 *
 * @author zznate
 */
public abstract class AbstractBatcher implements Batcher {
    protected BatchSubmitter batchSubmitter;

    private Batch batch;

    private final AtomicLong opCount = new AtomicLong();
    private final Timer addTimer =
            Metrics.newTimer(AbstractBatcher.class, "add_invocation", TimeUnit.MICROSECONDS, TimeUnit.SECONDS);
    private final Counter invocationCounter =
            Metrics.newCounter(AbstractBatcher.class, "batch_add_invocations");
    private final Counter existingCounterHit =
            Metrics.newCounter(AbstractBatcher.class,"counter_existed");

    
    
    public AbstractBatcher(int queueSize) {
      batch = new Batch();
    }

    public void setBatchSubmitter(BatchSubmitter batchSubmitter) {
        this.batchSubmitter = batchSubmitter;
    }

    /**
     * Individual {@link Count} for the same counter get rolled up, so
     * we track the individual number of operations.
     * @return the number of operation against this SimpleBatcher
     */
    public long getOpCount() {
        return opCount.get();
    }

    protected abstract boolean shouldSubmit(Batch batch);
    protected abstract void submit(Batch batch);

  /**
     * Add a count object to this batcher
     * @param count
     * @throws CounterProcessingUnavailableException
     */
    public void add(Count count) throws CounterProcessingUnavailableException {
      invocationCounter.inc();
      final TimerContext context = addTimer.time();

      // This should be fairly safe while still avoid locking. Assumptions:
      // 1) A batch may grow slightly beyond the "shouldSubmit" bounds.
      // 2) The chance of losing a count by adding it to a copied batch after
      //    it is stored is approximately zero.
     
      batch.add(count);
      
      if (shouldSubmit(batch)) {
        Batch copy = batch;
        batch = new Batch();
        submit(copy);
      }

      context.stop();
    }
    

    class Batch {
        private final Map counts;
        private final AtomicInteger localCallCounter = new AtomicInteger();

        Batch() {
            counts = new HashMap();
        }

        /* copy constructor */
        Batch(Batch batch) {
            batch.localCallCounter.set(batch.localCallCounter.get());
            counts = new HashMap(batch.counts);
        }

        void clear() {
          counts.clear();
          localCallCounter.set(0);
        }

        void add(Count count) {
            opCount.incrementAndGet();
            localCallCounter.incrementAndGet();
            Count found = counts.get(count.getCounterName());
            if ( found != null ) {
                existingCounterHit.inc();
                counts.put(found.getCounterName(), found.apply(count));
            } else {
                counts.put(count.getCounterName(),count);
            }
        }

        /**
         * The number of distinct counters which have been seen
         * @return
         */
        public int getPayloadSize() {
            return counts.size();
        }

        public Collection getCounts() {
            return counts.values();
        }

        /**
         * The number of times the {@link #add(com.usergrid.count.common.Count)} method has been
         * invoked on this batch instance
         * @return
         */
        public int getLocalCallCount() {
            return localCallCounter.get();
        }

    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy