/** Basic test of compatibility check between identical triggers. */ @Test public void testCompatibilityIdentical() throws Exception { Trigger t1 = AfterProcessingTime.pastFirstElementInPane().plusDelayOf(Duration.standardMinutes(1L)); Trigger t2 = AfterProcessingTime.pastFirstElementInPane().plusDelayOf(Duration.standardMinutes(1L)); assertTrue(t1.isCompatible(t2)); }
/** * Aligns the time to be the smallest multiple of {@code period} greater than the epoch boundary * (aka {@code new Instant(0)}). */ public AfterProcessingTime alignedTo(final Duration period) { return alignedTo(period, new Instant(0)); }
@Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof AfterProcessingTime)) { return false; } AfterProcessingTime that = (AfterProcessingTime) obj; return getTimestampTransforms().equals(that.getTimestampTransforms()); }
toProtoAndBackSpec(Never.ever()), toProtoAndBackSpec(DefaultTrigger.of()), toProtoAndBackSpec(AfterProcessingTime.pastFirstElementInPane()), toProtoAndBackSpec( AfterProcessingTime.pastFirstElementInPane().plusDelayOf(Duration.millis(23))), toProtoAndBackSpec( AfterProcessingTime.pastFirstElementInPane() .alignedTo(Duration.millis(5), new Instant(27))), toProtoAndBackSpec( AfterProcessingTime.pastFirstElementInPane() .plusDelayOf(Duration.standardSeconds(3)) .alignedTo(Duration.millis(5), new Instant(27)) .plusDelayOf(Duration.millis(13))), AfterWatermark.pastEndOfWindow() .withEarlyFirings( AfterProcessingTime.pastFirstElementInPane().plusDelayOf(Duration.millis(42))) .withLateFirings(AfterPane.elementCountAtLeast(3))), toProtoAndBackSpec(Repeatedly.forever(AfterWatermark.pastEndOfWindow())),
@Test public void testToString() { Trigger trigger = AfterProcessingTime.pastFirstElementInPane(); assertEquals("AfterProcessingTime.pastFirstElementInPane()", trigger.toString()); }
@Override public PCollection<String> expand(PCollection<KV<String, Integer>> input) { return input .apply(Window.<KV<String, Integer>>into(FixedWindows.of(TWO_MINUTES)) .triggering(Repeatedly.forever(AfterProcessingTime .pastFirstElementInPane() .alignedTo(TWO_MINUTES, Utils.parseTime("12:05:00")))) .withAllowedLateness(Duration.standardDays(1000)) .accumulatingFiredPanes()) .apply(Sum.integersPerKey()) .apply(ParDo.of(new FormatAsStrings())); }
@Test public void testFireDeadline() throws Exception { assertEquals( BoundedWindow.TIMESTAMP_MAX_VALUE, AfterProcessingTime.pastFirstElementInPane() .getWatermarkThatGuaranteesFiring(new IntervalWindow(new Instant(0), new Instant(10)))); }
/** * Creates a trigger that fires when the current processing time passes the processing time at * which this trigger saw the first element in a pane. */ public static AfterProcessingTime pastFirstElementInPane() { return new AfterProcessingTime(Collections.emptyList()); }
AfterProcessingTime trigger = AfterProcessingTime.pastFirstElementInPane(); for (RunnerApi.TimestampTransform transform : triggerProto.getAfterProcessingTime().getTimestampTransformsList()) { case ALIGN_TO: trigger = trigger.alignedTo( Duration.millis(transform.getAlignTo().getPeriod()), new Instant(transform.getAlignTo().getOffset())); break; case DELAY: trigger = trigger.plusDelayOf(Duration.millis(transform.getDelay().getDelayMillis())); break; case TIMESTAMPTRANSFORM_NOT_SET:
@Test public void testContinuation() throws Exception { OnceTrigger trigger1 = AfterProcessingTime.pastFirstElementInPane(); OnceTrigger trigger2 = AfterWatermark.pastEndOfWindow(); Trigger afterAll = AfterAll.of(trigger1, trigger2); assertEquals( AfterAll.of(trigger1.getContinuationTrigger(), trigger2.getContinuationTrigger()), afterAll.getContinuationTrigger()); }
/** * Aligns timestamps to the smallest multiple of {@code period} since the {@code offset} greater * than the timestamp. */ public AfterProcessingTime alignedTo(final Duration period, final Instant offset) { return new AfterProcessingTime( ImmutableList.<TimestampTransform>builder() .addAll(timestampTransforms) .add(TimestampTransform.alignTo(period, offset)) .build()); }
@Override public PCollection<KV<String, Integer>> expand(PCollection<GameActionInfo> infos) { return infos .apply( "LeaderboardTeamFixedWindows", Window.<GameActionInfo>into(FixedWindows.of(teamWindowDuration)) // We will get early (speculative) results as well as cumulative // processing of late data. .triggering( AfterWatermark.pastEndOfWindow() .withEarlyFirings( AfterProcessingTime.pastFirstElementInPane() .plusDelayOf(FIVE_MINUTES)) .withLateFirings( AfterProcessingTime.pastFirstElementInPane() .plusDelayOf(TEN_MINUTES))) .withAllowedLateness(allowedLateness) .accumulatingFiredPanes()) // Extract and sum teamname/score pairs from the event data. .apply("ExtractTeamScore", new ExtractAndSumScore("team")); } }
@Test public void testContinuation() throws Exception { OnceTrigger trigger1 = AfterProcessingTime.pastFirstElementInPane(); OnceTrigger trigger2 = AfterWatermark.pastEndOfWindow(); Trigger afterFirst = AfterFirst.of(trigger1, trigger2); assertEquals( AfterFirst.of(trigger1.getContinuationTrigger(), trigger2.getContinuationTrigger()), afterFirst.getContinuationTrigger()); }
@Override public int hashCode() { return Objects.hash(getTimestampTransforms()); } }
/** * Adds some delay to the original target time. * * @param delay the delay to add * @return An updated time trigger that will wait the additional time before firing. */ public AfterProcessingTime plusDelayOf(final Duration delay) { return new AfterProcessingTime( ImmutableList.<TimestampTransform>builder() .addAll(timestampTransforms) .add(TimestampTransform.delay(delay)) .build()); }
@Test public void testWithDelayToString() { Trigger trigger = AfterProcessingTime.pastFirstElementInPane().plusDelayOf(Duration.standardMinutes(5)); assertEquals( "AfterProcessingTime.pastFirstElementInPane().plusDelayOf(5 minutes)", trigger.toString()); }
@Test public void testContinuation() throws Exception { OnceTrigger trigger1 = AfterProcessingTime.pastFirstElementInPane(); OnceTrigger trigger2 = AfterWatermark.pastEndOfWindow(); Trigger afterEach = AfterEach.inOrder(trigger1, trigger2); assertEquals( Repeatedly.forever( AfterFirst.of(trigger1.getContinuationTrigger(), trigger2.getContinuationTrigger())), afterEach.getContinuationTrigger()); }
@Override public String toString() { StringBuilder builder = new StringBuilder("AfterProcessingTime.pastFirstElementInPane()"); for (TimestampTransform transform : getTimestampTransforms()) { if (transform instanceof TimestampTransform.Delay) { TimestampTransform.Delay delay = (TimestampTransform.Delay) transform; builder .append(".plusDelayOf(") .append(DURATION_FORMATTER.print(delay.getDelay().toPeriod())) .append(")"); } else if (transform instanceof TimestampTransform.AlignTo) { TimestampTransform.AlignTo alignTo = (TimestampTransform.AlignTo) transform; builder .append(".alignedTo(") .append(DURATION_FORMATTER.print(alignTo.getPeriod().toPeriod())) .append(", ") .append(alignTo.getOffset()) .append(")"); } } return builder.toString(); }
@Override public PCollection<KV<String, Integer>> expand(PCollection<GameActionInfo> input) { return input .apply( "LeaderboardUserGlobalWindow", Window.<GameActionInfo>into(new GlobalWindows()) // Get periodic results every ten minutes. .triggering( Repeatedly.forever( AfterProcessingTime.pastFirstElementInPane().plusDelayOf(TEN_MINUTES))) .accumulatingFiredPanes() .withAllowedLateness(allowedLateness)) // Extract and sum username/score pairs from the event data. .apply("ExtractUserScore", new ExtractAndSumScore("user")); } }
@Test public void testContinuation() throws Exception { Trigger trigger = AfterProcessingTime.pastFirstElementInPane(); Trigger repeatedly = Repeatedly.forever(trigger); assertEquals( Repeatedly.forever(trigger.getContinuationTrigger()), repeatedly.getContinuationTrigger()); assertEquals( Repeatedly.forever(trigger.getContinuationTrigger().getContinuationTrigger()), repeatedly.getContinuationTrigger().getContinuationTrigger()); }