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

com.kolibrifx.plovercrest.server.internal.folds.EngineFoldTriggerer Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2010-2017, KolibriFX AS. Licensed under the Apache License, version 2.0.
 */

package com.kolibrifx.plovercrest.server.internal.folds;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.log4j.Logger;
import com.kolibrifx.common.Disposable;
import com.kolibrifx.common.dispatcher.Dispatcher;
import com.kolibrifx.common.dispatcher.ThreadedDispatcher;
import com.kolibrifx.plovercrest.server.streams.Stream;
import com.kolibrifx.plovercrest.server.streams.StreamCreateListener;
import com.kolibrifx.plovercrest.server.streams.StreamDataTriggerer;
import com.kolibrifx.plovercrest.server.streams.StreamEngine;
import com.kolibrifx.plovercrest.server.streams.folds.CombinatorStrategy;

/**
 * Triggers fold callbacks for a set of input tables.
 * 

* An event is triggered for each element in each table from the given timstamp, with timestamps in * order across input tables. No events are triggered until there is data available in all input * tables. */ public class EngineFoldTriggerer implements Disposable { private static final Logger log = Logger.getLogger(EngineFoldTriggerer.class); private final Collection disposables = new ArrayList<>(); private final Dispatcher dispatcher = new ThreadedDispatcher("EngineFoldTriggerer"); private final Runnable consumeTask; private final AtomicBoolean closed = new AtomicBoolean(); private final AtomicInteger remainingReaders; private final long fromTimestamp; private final List readers; public EngineFoldTriggerer(final StreamEngine engine, final Collection tableNames, final long fromTimestamp, final CombinatorStrategy combinatorStrategy, final FoldCallback callback) { this.fromTimestamp = fromTimestamp; remainingReaders = new AtomicInteger(tableNames.size()); readers = new ArrayList<>(); for (int i = 0; i < tableNames.size(); i++) { readers.add(null); } final StreamDataTriggerer dataTriggerer = new StreamDataTriggerer() { @Override public void wakeUpNow() { runConsumeTask(); } @Override public void wakeUpAtTime(final long clockMillis) { if (closed.get()) { return; } final long delay = clockMillis - dispatcher.currentTimeMillis(); dispatcher.runLater(consumeTask, Math.max(0, delay)); } }; consumeTask = new Runnable() { @Override public void run() { try { int count = 0; while (!closed.get()) { if (!combinatorStrategy.tryConsumeNext(readers, callback)) { // nothing more to produce for this fold (for now) for (final PeekableInputStream reader : readers) { reader.registerDataTriggererIfReachedEnd(dataTriggerer); } break; } count++; } if (log.isTraceEnabled()) { log.trace("Consumed " + count + " elements from " + readers.size() + " streams"); } } catch (final RuntimeException e) { e.printStackTrace(); } } }; int i = 0; for (final String tableName : tableNames) { setUpCreateListener(engine, tableName, i++); } } private void runConsumeTask() { if (closed.get()) { return; } dispatcher.runLater(consumeTask); } private void setUpCreateListener(final StreamEngine engine, final String tableName, final int readerIndex) { final AtomicBoolean done = new AtomicBoolean(); final Disposable disposable = engine.addCreateListener(tableName, new StreamCreateListener() { @Override public void onCreated(final String name) { final Stream stream = engine.openRaw(name); if (stream == null) { log.error("Got create event, but failed to open stream: " + name); return; } if (!done.compareAndSet(false, true)) { log.warn("Got more than one create event for table " + tableName + " (index " + readerIndex + ")"); return; } readers.set(readerIndex, new ByteArrayPeekableInputStream(stream, fromTimestamp)); final int remaining = remainingReaders.decrementAndGet(); assert remaining >= 0; if (remaining == 0) { if (log.isInfoEnabled()) { log.info("All input tables exist, starting consume logic"); } runConsumeTask(); } } }); if (done.get()) { // this can happen if the stream already exists disposable.close(); } else { disposables.add(disposable); } } @Override public void close() { if (!closed.compareAndSet(false, true)) { return; } for (final Disposable d : disposables) { d.close(); } disposables.clear(); dispatcher.close(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy