/*
* The contents of this file are subject to the terms of the Common Development and
* Distribution License (the License). You may not use this file except in compliance with the
* License.
*
* You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
* specific language governing permission and limitations under the License.
*
* When distributing Covered Software, include this CDDL Header Notice in each file and include
* the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions Copyrighted [year] [name of copyright owner]".
*
* Copyright © 2012 ForgeRock AS. All rights reserved.
*/
package org.forgerock.openidm.smartevent.core;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import com.lmax.disruptor.dsl.ProducerType;
import org.forgerock.openidm.smartevent.EventEntry;
import org.forgerock.openidm.smartevent.Name;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.SleepingWaitStrategy;
import com.lmax.disruptor.dsl.Disruptor;
/**
* Publisher that uses the disruptor pattern and RingBuffer to process events in
* a highly concurrent manner.
*
* Start event issues EventEntry without limitation on the outstanding start/end
* events, making it suitable for long(er) time span events/measurements
*
*/
public class DisruptorReferringPublisher implements PluggablePublisher {
static PluggablePublisher INSTANCE = new DisruptorReferringPublisher();
/**
* Ring size has considerable implications:
*
* - Pre-allocation of memory/event type objects - Deliberate backpressure
* when ring is full, i.e. consumers can't keep up with producers - When
* used for monitoring may keep objects alive for recent history purposes
* Ring size needs to be a power of 2
*/
static int RING_SIZE = (1024 * 2);
// TODO: consider optimizations for number of ring buffers and sizing
// To process events with different requirements such as statistics vs.
// application events
static Executor executor = Executors.newCachedThreadPool();
static Disruptor<DisruptorReferringEventEntry> disruptor =
new Disruptor<DisruptorReferringEventEntry>(DisruptorReferringEventEntry.EVENT_FACTORY,
RING_SIZE, executor, ProducerType.MULTI, new SleepingWaitStrategy());
static RingBuffer<DisruptorReferringEventEntry> ringBuffer;
static {
disruptor.handleEventsWith(new StatisticsHandler(disruptor));
ringBuffer = disruptor.start();
}
static long sequence = -1;
/**
* Factory method
*/
public static PluggablePublisher getInstance() {
return INSTANCE;
}
/**
* @inheritDoc
*/
public final EventEntry start(Name eventName, Object payload, Object context) {
// For start event, do not hold a place in the ringbuffer (yet)
// to avoid limiting long running measurements
EventEntryImpl eventEntry = new EventEntryImpl();
eventEntry.eventName = eventName;
eventEntry.publisher = this;
eventEntry.payload = payload;
eventEntry.context = context;
eventEntry.start();
// TODO: consider adding option to monitor outstanding requests
return eventEntry;
}
/**
* @inheritDoc
*/
public final void setResult(Object result, EventEntry delegate) {
}
/**
* @inheritDoc
*/
public final void end(Name eventName, EventEntry delegate) {
sequence = ringBuffer.next();
final DisruptorReferringEventEntry eventEntry = ringBuffer.claimAndGetPreallocated(sequence);
eventEntry.publisher = this;
EventEntryImpl delegateImpl = (EventEntryImpl) delegate;
eventEntry.delegate = delegateImpl;
// Copy data into ring buffer for optimization
eventEntry.startTime = delegateImpl.startTime;
eventEntry.end();
ringBuffer.publish(sequence);
}
}