
cz.seznam.euphoria.inmem.WatermarkTriggerScheduler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of euphoria-inmem Show documentation
Show all versions of euphoria-inmem Show documentation
An all-in-memory executing euphoria executor suitable
for executing flows in unit tests.
The newest version!
/**
* Copyright 2016-2017 Seznam.cz, a.s.
*
* 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 cz.seznam.euphoria.inmem;
import cz.seznam.euphoria.core.client.dataset.windowing.Window;
import cz.seznam.euphoria.core.client.util.Pair;
import cz.seznam.euphoria.shaded.guava.com.google.common.collect.Iterables;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.stream.Collectors;
/**
* Trigger scheduler based on watermarks. Uses event-time instead of real
* wall-clock time.
*/
public class WatermarkTriggerScheduler
implements TriggerScheduler {
// how delayed is the watermark time after the event time (how long are
// we going to accept latecomers)
private final long watermarkDuration;
// read-only accessed from multiple threads
private volatile long currentWatermark;
// following fields are accessed from owner thread only
private final SortedMap, Triggerable>>>
scheduledEvents = new TreeMap<>();
private final Map, Set>
eventStampsForWindow = new HashMap<>();
/**
* Create the triggering with specified duration in ms.
* @param duration duration of the watermark in ms.
*/
public WatermarkTriggerScheduler(long duration) {
this.watermarkDuration = duration;
this.currentWatermark = 0;
}
@Override
@SuppressWarnings("unchecked")
public void updateStamp(long stamp) {
if (currentWatermark < stamp) {
final long triggeringStamp = stamp - watermarkDuration;
// fire all triggers that passed watermark duration
SortedMap, Triggerable>>> headMap;
headMap = scheduledEvents.headMap(triggeringStamp);
headMap.entrySet().stream()
.flatMap(e -> e.getValue().stream().map(p -> Pair.of(e.getKey(), p)))
// need to collect to list to prevent ConcurrentModificationException
.collect(Collectors.toList())
.forEach(p -> {
// move the clock to the triggering time
currentWatermark = p.getFirst();
KeyedWindow w = p.getSecond().getFirst();
Triggerable purged = purge(w, currentWatermark);
purged.fire(currentWatermark, w);
});
// and set the final watermark
currentWatermark = stamp;
}
}
@Override
public long getCurrentTimestamp() {
return currentWatermark;
}
@Override
public boolean scheduleAt(
long stamp, KeyedWindow window, Triggerable trigger) {
if (stamp <= currentWatermark - watermarkDuration) return false;
purge(window, stamp);
add(stamp, trigger, window);
return true;
}
@Override
public void cancelAll() {
scheduledEvents.clear();
eventStampsForWindow.clear();
}
@Override
public void cancel(long timestamp, KeyedWindow window) {
purge(window, timestamp);
}
@Override
public void close() {
cancelAll();
}
/** Purge the scheduled event from all indices and return it (if exists). */
private Triggerable purge(KeyedWindow w, long stamp) {
Set scheduled = eventStampsForWindow.get(w);
if (scheduled == null || !scheduled.remove(stamp)) {
return null;
}
List, Triggerable>> eventsForWindow;
eventsForWindow = scheduledEvents.get(stamp);
List, Triggerable>> filtered
= eventsForWindow.stream()
.filter(p -> w.equals(p.getFirst()))
.collect(Collectors.toList());
Pair, Triggerable> event =
Iterables.getOnlyElement(filtered);
eventsForWindow.remove(event);
if (eventsForWindow.isEmpty()) {
scheduledEvents.remove(stamp);
}
if (scheduled.isEmpty()) {
eventStampsForWindow.remove(w);
}
return event.getSecond();
}
private void add(long stamp, Triggerable trigger, KeyedWindow w) {
List, Triggerable>> scheduledForTime =
scheduledEvents.get(stamp);
if (scheduledForTime == null) {
scheduledEvents.put(stamp, scheduledForTime = new ArrayList<>());
}
scheduledForTime.add(Pair.of(w, trigger));
Set stamps = eventStampsForWindow.get(w);
if (stamps == null) {
eventStampsForWindow.put(w, stamps = new HashSet<>());
}
stamps.add(stamp);
}
public long getWatermarkDuration() {
return watermarkDuration;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy