/*******************************************************************************
* Copyright © 2012-2015 eBay Software Foundation
* This program is dual licensed under the MIT and Apache 2.0 licenses.
* Please see LICENSE for more information.
*******************************************************************************/
package com.ebay.jetstream.event;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author shmurthy@ebay.com (shmurthy@ebay.com)
*
*/
@edu.umd.cs.findbugs.annotations.SuppressWarnings(value="NN_NAKED_NOTIFY")
public class EventBatcher implements Runnable {
private static final Logger LOGGER = LoggerFactory.getLogger("com.ebay.jetstream.event");
private static final int BATCH_SIZE = 5;
private static final int MAX_RETRIES = 3;
private final ConcurrentLinkedQueue<JetstreamEvent> m_queue = new ConcurrentLinkedQueue<JetstreamEvent>();
private int m_batchSize = BATCH_SIZE;
private int m_flushTime = 20; // 20 secs
private int m_maxQueueSz = 5000;
public EventBatcher() {}
public int getMaxQueueSz() {
return m_maxQueueSz;
}
public void setMaxQueueSz(int m_maxQueueSz) {
this.m_maxQueueSz = m_maxQueueSz;
}
private final AtomicBoolean m_shutdown = new AtomicBoolean(false);
private BatchEventSink m_batchEventSink;
private Thread m_queueProcessor;
public void start() {
m_queueProcessor = new Thread(this, "EventBatcherThread");
m_queueProcessor.start();
}
public BatchEventSink getBatchEventSink() {
return m_batchEventSink;
}
public void setBatchEventSink(BatchEventSink m_batchEventSink) {
this.m_batchEventSink = m_batchEventSink;
}
public EventBatcher(BatchEventSink sink) {
m_batchEventSink = sink;
}
public void flushQueue() {
List<JetstreamEvent> eventList = new ArrayList<JetstreamEvent>();
while(!m_queue.isEmpty()) {
eventList.clear();
for (int i = 0; i < Math.min(m_batchSize, m_queue.size()); i++) {
eventList.add(m_queue.poll());
}
writeEvents(eventList);
}
}
/**
* @return the batchSize
*/
public int getBatchSize() {
return m_batchSize;
}
/**
* @return the flushTime
*/
public int getFlushTime() {
return m_flushTime;
}
private void processQueue() {
List<JetstreamEvent> eventList = new ArrayList<JetstreamEvent>();
while(!m_shutdown.get()) {
try {
eventList.clear();
if (m_queue.size() != 0) {
for (int i = 0; i < m_batchSize; i++) {
eventList.add(m_queue.poll());
}
writeEvents(eventList);
}
synchronized (this) {
try {
this.wait(m_flushTime * 1000);
}
catch (InterruptedException e) {
LOGGER.debug( "EventBatcher:processQueue wait interrupted");
}
if (m_shutdown.get())
break;
}
} catch (Throwable t) {
LOGGER.error( "EventBatcher:processQueue Exception " + t.getLocalizedMessage());
}
}
}
private void writeEvents(List<JetstreamEvent> eventList) {
int retryCount = MAX_RETRIES;
while (--retryCount > 0) {
m_batchEventSink.sendEvents(eventList, null);
break;
}
}
@Override
public void run() {
try {
processQueue();
flushQueue(); // we will flush whatever is remaining in queue
} catch (Throwable t) {
LOGGER.error( "EventBatcher:run - shutting down");
}
}
/**
* @param batchSize
* the batchSize to set
*/
public void setBatchSize(int batchSize) {
m_batchSize = batchSize;
}
/**
* @param flushTime
* the flushTime to set
*/
public void setFlushTime(int flushTime) {
m_flushTime = flushTime;
}
/**
* @param shutdown
* the shutdown to set
*/
public void shutdown() {
m_shutdown.set(true);
synchronized (this) {
this.notifyAll();
}
}
public void submit(JetstreamEvent event) throws Exception {
if (m_shutdown.get())
throw new Exception("shutting down");
if (m_queue.size() >= m_maxQueueSz)
throw new Exception("Queue full");
m_queue.add(event);
if (m_queue.size() >= m_batchSize)
synchronized (this) {
this.notifyAll();
}
}
}