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

com.ebay.jetstream.event.processor.AbstractQueuedEventProcessor Maven / Gradle / Ivy

/*******************************************************************************
 *  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.processor;

import java.util.concurrent.atomic.AtomicLong;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.jmx.export.annotation.ManagedOperation;

import com.ebay.jetstream.counter.LongEWMACounter;
import com.ebay.jetstream.event.EventException;
import com.ebay.jetstream.event.JetstreamErrorCodes;
import com.ebay.jetstream.event.JetstreamEvent;
import com.ebay.jetstream.event.JetstreamReservedKeys;
import com.ebay.jetstream.event.RetryEventCode;
import com.ebay.jetstream.event.advice.Advice;
import com.ebay.jetstream.event.support.AbstractEventProcessor;
import com.ebay.jetstream.event.support.channel.PipelineFlowControl;
import com.ebay.jetstream.messaging.MessageServiceTimer;
import com.ebay.jetstream.util.RequestQueueProcessor;
import com.ebay.jetstream.xmlser.Hidden;

public abstract class AbstractQueuedEventProcessor extends AbstractEventProcessor {
	
	private enum ActivityState {PAUSED, STOPPED, RUNNING, PREPARED, UNINITIALIZED}
    private RequestQueueProcessor m_workQueue;
    private int m_nMaxWorkQueueSize = 60000;  // default 100k
    private int m_nResumeThreshold;
	private int m_nNumWorkerThreads = 1; // default 1;	
	private QueuedEventProcessorConfiguration m_configuration;
	
	private AtomicLong m_highWater = new AtomicLong();
	private volatile ActivityState m_state = ActivityState.UNINITIALIZED;
	private final LongEWMACounter m_avgLatency = new LongEWMACounter(60, MessageServiceTimer.sInstance().getTimer());
	
	private final PipelineFlowControl m_flowHandler = new PipelineFlowControl(this);
	Logger logger = LoggerFactory.getLogger("com.ebay.jetstream.event.processor.esper");
	
	
	@Override
	public void afterPropertiesSet() throws Exception {
		m_nResumeThreshold = m_nMaxWorkQueueSize / 20; // 5%
		prepare();
	}
	
	public long getAverageLatencyFromSource() {
		return m_avgLatency.get();
	}
	
	public int getEventQueueHighWaterMark() {
		return m_highWater.intValue();
	}
	
	public int getMaxWorkQueueSize() {
		return m_nMaxWorkQueueSize;
	}
	
	public int getNumWorkerThreads() {
		return m_nNumWorkerThreads;
	}
	
	public long getQueuedEventCount() {
		return m_workQueue != null ? m_workQueue.getPendingRequests() : 0;
	}
	
	public int getResumeThreshold() {
		return m_nResumeThreshold;
	}

	public String getState() {
		return m_state.toString();
	}

	@Override
	@ManagedOperation 
	public void pause() {
		pausePublisher("pauseEvent received from EventSink");
	}
	
	@Override
	@ManagedOperation
	public void resume() {
		resumePublisher("ResumeEvent received from EventSink");
	}
	
	public void setAdviceListener(Advice adviceListener) {
		m_adviceListener = adviceListener;
	}

	public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
		m_flowHandler.setApplicationEventPublisher(applicationEventPublisher);
	}

	@Required
	public  void setConfiguration(C config) {
		m_configuration = config;
		m_nNumWorkerThreads = m_configuration.getThreadPoolSize();
		m_nMaxWorkQueueSize = m_configuration.getQueueSizeLimit();
	}


	public void shutDown() {
	
		pausePublisher("Application getting gracefulShutdown");
		if (m_workQueue != null) {
			while (m_workQueue.getPendingRequests() != 0) {
				try {
					Thread.sleep(100);
				}
				catch (InterruptedException e) {
					logger.error( e.getLocalizedMessage(), e);
				}
			}
		}
		
		stop(true);
		logger.warn( getBeanName() + " Shutdown has been completed");
	}

	@ManagedOperation
	public void stop() {
		stop(true);
	}

	protected abstract String getComponentName();

	@SuppressWarnings("unchecked")
	@Hidden
	protected  C getConfiguration() {
		return (C)m_configuration;
	}

	protected abstract EventProcessRequest getProcessEventRequest(JetstreamEvent event);

	protected abstract void init();
	
	protected synchronized void pausePublisher(String cause) {
		if (m_state != ActivityState.PAUSED) {
			m_flowHandler.pause();
			m_state = ActivityState.PAUSED;
			incrementPauseCounter();
			logger.warn( "Processor " + getBeanName() + " paused: " + cause);
		}
	}
	
	protected void postEventProcessed(JetstreamEvent event) {
		fireSendEvent(event);
		incrementEventSentCounter();
		if (m_state == ActivityState.PAUSED && m_workQueue.getPendingRequests() < m_nResumeThreshold)
			resume();
	}
	
	@Override
	protected void processApplicationEvent(ApplicationEvent event) {
		if (event instanceof ContextRefreshedEvent)
			start();
	}
	
	protected void queueEvent(EventProcessRequest eventRequest) throws EventException {
		
		long nQueueSize = m_workQueue.getPendingRequests();
		boolean bQueued = nQueueSize < getMaxWorkQueueSize();
		if (bQueued) {
			bQueued = m_workQueue.processRequest(eventRequest);
			if (bQueued && ++nQueueSize > m_highWater.get())
				m_highWater.set(nQueueSize);
		}
		
		if (!bQueued) {
			if (logger.isDebugEnabled())
				logger.debug( "Failed to offer event to queue, current size: " + m_workQueue.getPendingRequests());
			
			JetstreamEvent event = eventRequest.getEvent();
			if (event instanceof ShutdownJetstreamEvent == false) {
				// This check is mandatory. Or else we will ending up retrying a shutdhown jetstream event
				if (getAdviceListener() != null)
					sendToAdviceListener(event, RetryEventCode.QUEUE_FULL, " Queue is full");
				else {
					incrementEventDroppedCounter();
					pausePublisher(getBeanName() + " has reached upper limit " + m_workQueue.getPendingRequests()
							+ " and tried to pause publisher.");
				}
			}
		}
	}
    
	protected void queueEvent(JetstreamEvent event) throws EventException {
		queueEvent(getProcessEventRequest(event));
	}

	protected synchronized void resumePublisher(String cause) {
		if (m_state != ActivityState.RUNNING) { // Bug 6653 fix
			m_flowHandler.resume();
			m_state = ActivityState.RUNNING;
			incrementResumeCounter();
			logger.warn( "Processor " + getBeanName() + " resumed: " + cause);
		}
	}
	
	@Override
	public void sendEvent(JetstreamEvent event) throws EventException {
		switch (m_state) {
			case PAUSED:
				break;
			case STOPPED: {
				logger.error( "Processor" + getBeanName() + "has been paused or stopped. Cannot accept the event: ", getComponentName());
				throw new EventException("Processor "  + getBeanName() + " is paused or stopped. Can not take in any more events.",
					isPauseIfAnyEventSinkPauses() ? JetstreamErrorCodes.EVENT_SINK_PAUSED.toString()
							: JetstreamErrorCodes.EVENT_SINKS_PAUSED.toString());
			}
			case UNINITIALIZED:
				prepare();
			case PREPARED:
				start();
			case RUNNING: {
				incrementEventRecievedCounter();
				queueEvent(event);
				Long lngOrigin = (Long)event.get(JetstreamReservedKeys.EventTime.toString());
				if (lngOrigin != null) {
					long lNow = System.currentTimeMillis();
					long lOrigin = lngOrigin.longValue();
					if (lNow >= lOrigin)
						m_avgLatency.add(lNow - lOrigin + (lOrigin % 2));
					else
						logger.warn( "Event origin timestamp is in the future (" + 
								lOrigin + ", " + lNow + ")");
				}
			}
			default: break;
		}
	}
	
	@ManagedOperation
	protected void start() {
		start(true);
	}

	protected void stop(boolean bHardStop) {
	
		pausePublisher(bHardStop ? "Paused by stop call" : "Received Pause from sink");
		try {
			m_state = ActivityState.STOPPED;
			// More stringent test than isStarted(), in case events lost && shutdown incomplete
			m_workQueue.shutdown();
		}
		catch (Throwable e) {
			logger.error( e.getLocalizedMessage(), e );
		}
	}

	private synchronized void prepare() {
		if (m_state == ActivityState.UNINITIALIZED || m_state == ActivityState.STOPPED) {
			m_state = ActivityState.PREPARED;
	
			if (m_workQueue == null) {
				m_workQueue = new RequestQueueProcessor(getMaxWorkQueueSize(), getNumWorkerThreads(), getBeanName());
			}
				
			init();
		}
	}

	private void sendToAdviceListener(JetstreamEvent event, RetryEventCode code, String errorMsg) {
		if (getAdviceListener() != null)
			getAdviceListener().retry(event, code, errorMsg);
	}
	
	private void start(boolean isExplicitCall) {

		// we need to call this BEFORE checking whether we are in sync mode
		prepare();

		if (m_state != ActivityState.RUNNING) {
		
			if (m_state == ActivityState.PREPARED) {
				m_state = ActivityState.RUNNING;
			
				if (logger.isInfoEnabled()) {
					logger.info( "Successfully started \'" + getBeanName() + "\' " + getBeanName());
							
				}
			}
			resumePublisher("Resumed by start call");
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy