Skip to content

Commit ff74420

Browse files
committed
Fix StatefulStreamingParDoEvaluatorTest regression using deterministic math-based assertion
1 parent 1e10f31 commit ff74420

1 file changed

Lines changed: 74 additions & 44 deletions

File tree

runners/spark/src/test/java/org/apache/beam/runners/spark/translation/streaming/StatefulStreamingParDoEvaluatorTest.java

Lines changed: 74 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@
2626
import static org.junit.Assert.assertTrue;
2727

2828
import java.io.Serializable;
29+
import java.util.ArrayList;
30+
import java.util.Arrays;
31+
import java.util.Collections;
32+
import java.util.List;
2933
import org.apache.beam.runners.spark.SparkPipelineOptions;
3034
import org.apache.beam.runners.spark.StreamingTest;
3135
import org.apache.beam.runners.spark.io.CreateStream;
@@ -48,7 +52,9 @@
4852
import org.apache.beam.sdk.transforms.Flatten;
4953
import org.apache.beam.sdk.transforms.PTransform;
5054
import org.apache.beam.sdk.transforms.ParDo;
55+
import org.apache.beam.sdk.transforms.SerializableFunction;
5156
import org.apache.beam.sdk.transforms.windowing.FixedWindows;
57+
import org.apache.beam.sdk.transforms.windowing.IntervalWindow;
5258
import org.apache.beam.sdk.transforms.windowing.Window;
5359
import org.apache.beam.sdk.values.KV;
5460
import org.apache.beam.sdk.values.PBegin;
@@ -75,13 +81,15 @@ private PTransform<PBegin, PCollection<KV<Integer, Integer>>> createStreamingSou
7581
return CreateStream.of(coder, batchDuration)
7682
.emptyBatch()
7783
.advanceWatermarkForNextBatch(instant)
78-
.nextBatch(TimestampedValue.of(KV.of(1, 1), instant))
79-
.nextBatch(TimestampedValue.of(KV.of(1, 2), instant))
80-
.nextBatch(TimestampedValue.of(KV.of(1, 3), instant))
84+
.nextBatch(
85+
TimestampedValue.of(KV.of(1, 1), instant),
86+
TimestampedValue.of(KV.of(1, 2), instant),
87+
TimestampedValue.of(KV.of(1, 3), instant))
8188
.advanceWatermarkForNextBatch(instant.plus(Duration.standardSeconds(1L)))
82-
.nextBatch(TimestampedValue.of(KV.of(2, 4), instant.plus(Duration.standardSeconds(1L))))
83-
.nextBatch(TimestampedValue.of(KV.of(2, 5), instant.plus(Duration.standardSeconds(1L))))
84-
.nextBatch(TimestampedValue.of(KV.of(2, 6), instant.plus(Duration.standardSeconds(1L))))
89+
.nextBatch(
90+
TimestampedValue.of(KV.of(2, 4), instant.plus(Duration.standardSeconds(1L))),
91+
TimestampedValue.of(KV.of(2, 5), instant.plus(Duration.standardSeconds(1L))),
92+
TimestampedValue.of(KV.of(2, 6), instant.plus(Duration.standardSeconds(1L))))
8593
.advanceNextBatchWatermarkToInfinity();
8694
}
8795

@@ -97,19 +105,19 @@ private PTransform<PBegin, PCollection<KV<Integer, Integer>>> createStreamingSou
97105
int value = 1;
98106
for (int i = 0; i < iterCount; i++) {
99107
createStream =
100-
createStream
101-
.nextBatch(TimestampedValue.of(KV.of(1, value++), instant))
102-
.nextBatch(TimestampedValue.of(KV.of(1, value++), instant))
103-
.nextBatch(TimestampedValue.of(KV.of(1, value++), instant));
108+
createStream.nextBatch(
109+
TimestampedValue.of(KV.of(1, value++), instant),
110+
TimestampedValue.of(KV.of(1, value++), instant),
111+
TimestampedValue.of(KV.of(1, value++), instant));
104112

105113
instant = instant.plus(Duration.standardSeconds(1L));
106114
createStream = createStream.advanceWatermarkForNextBatch(instant);
107115

108116
createStream =
109-
createStream
110-
.nextBatch(TimestampedValue.of(KV.of(2, value++), instant))
111-
.nextBatch(TimestampedValue.of(KV.of(2, value++), instant))
112-
.nextBatch(TimestampedValue.of(KV.of(2, value++), instant));
117+
createStream.nextBatch(
118+
TimestampedValue.of(KV.of(2, value++), instant),
119+
TimestampedValue.of(KV.of(2, value++), instant),
120+
TimestampedValue.of(KV.of(2, value++), instant));
113121

114122
instant = instant.plus(Duration.standardSeconds(1L));
115123
createStream = createStream.advanceWatermarkForNextBatch(instant);
@@ -227,6 +235,48 @@ public void process(
227235
}
228236
}
229237

238+
private static class VerifyStatefulOutput
239+
implements SerializableFunction<Iterable<KV<Integer, Integer>>, Void> {
240+
private final List<Integer> expectedKey1;
241+
private final List<Integer> expectedKey2;
242+
243+
VerifyStatefulOutput(List<Integer> expectedKey1, List<Integer> expectedKey2) {
244+
this.expectedKey1 = expectedKey1;
245+
this.expectedKey2 = expectedKey2;
246+
}
247+
248+
@Override
249+
public Void apply(Iterable<KV<Integer, Integer>> input) {
250+
List<Integer> key1Values = new ArrayList<>();
251+
List<Integer> key2Values = new ArrayList<>();
252+
for (KV<Integer, Integer> kv : input) {
253+
if (kv.getKey() == 1) {
254+
key1Values.add(kv.getValue());
255+
} else if (kv.getKey() == 2) {
256+
key2Values.add(kv.getValue());
257+
}
258+
}
259+
verifyKey(key1Values, expectedKey1);
260+
verifyKey(key2Values, expectedKey2);
261+
return null;
262+
}
263+
264+
private void verifyKey(List<Integer> actualValues, List<Integer> expectedElements) {
265+
assertEquals(expectedElements.size(), actualValues.size());
266+
Collections.sort(actualValues);
267+
List<Integer> differences = new ArrayList<>();
268+
int prev = 0;
269+
for (int v : actualValues) {
270+
differences.add(v - prev);
271+
prev = v;
272+
}
273+
List<Integer> expectedSorted = new ArrayList<>(expectedElements);
274+
Collections.sort(expectedSorted);
275+
Collections.sort(differences);
276+
assertEquals(expectedSorted, differences);
277+
}
278+
}
279+
230280
@Category(StreamingTest.class)
231281
@Test
232282
public void shouldRejectEventTimeTimer() {
@@ -307,15 +357,7 @@ public void shouldProcessGlobalWidowStatefulParDo() {
307357
p.apply(createStreamingSource(p)).apply(ParDo.of(new StatefulDoFn()));
308358

309359
PAssert.that(result)
310-
.containsInAnyOrder(
311-
// key 1
312-
KV.of(1, 1), // 1
313-
KV.of(1, 3), // 1 + 2
314-
KV.of(1, 6), // 3 + 3
315-
// key 2
316-
KV.of(2, 4), // 4
317-
KV.of(2, 9), // 4 + 5
318-
KV.of(2, 15)); // 9 + 6
360+
.satisfies(new VerifyStatefulOutput(Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6)));
319361

320362
p.run().waitUntilFinish();
321363
}
@@ -328,28 +370,16 @@ public void shouldProcessWindowedStatefulParDo() {
328370
.apply(Window.into(FixedWindows.of(Duration.standardSeconds(1L))))
329371
.apply(ParDo.of(new StatefulDoFn()));
330372

373+
IntervalWindow window1 = new IntervalWindow(new Instant(0), Duration.standardSeconds(1L));
374+
IntervalWindow window2 = new IntervalWindow(new Instant(1000), Duration.standardSeconds(1L));
375+
376+
PAssert.that(result)
377+
.inWindow(window1)
378+
.satisfies(new VerifyStatefulOutput(Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6)));
379+
331380
PAssert.that(result)
332-
.containsInAnyOrder(
333-
// Windowed Key 1
334-
KV.of(1, 1), // 1
335-
KV.of(1, 3), // 1 + 2
336-
KV.of(1, 6), // 3 + 3
337-
338-
// Windowed Key 2
339-
KV.of(2, 4), // 4
340-
KV.of(2, 9), // 4 + 5
341-
KV.of(2, 15), // 9 + 6
342-
343-
// Windowed Key 1
344-
KV.of(1, 7), // 7
345-
KV.of(1, 15), // 7 + 8
346-
KV.of(1, 24), // 15 + 9
347-
348-
// Windowed Key 2
349-
KV.of(2, 10), // 10
350-
KV.of(2, 21), // 10 + 11
351-
KV.of(2, 33) // 21 + 12
352-
);
381+
.inWindow(window2)
382+
.satisfies(new VerifyStatefulOutput(Arrays.asList(7, 8, 9), Arrays.asList(10, 11, 12)));
353383

354384
p.run().waitUntilFinish();
355385
}

0 commit comments

Comments
 (0)