Skip to content

Commit 7c26f40

Browse files
committed
Fix some edge cases and add a fuzzy interval test.
1 parent 2991877 commit 7c26f40

2 files changed

Lines changed: 27 additions & 18 deletions

File tree

sdks/python/apache_beam/transforms/periodicsequence.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def initial_restriction(self, element):
4747

4848
assert start <= end
4949
assert interval > 0
50-
total_outputs = math.ceil((end - start) / interval)
50+
total_outputs = round((end - start) / interval)
5151
return OffsetRange(0, total_outputs)
5252

5353
def create_tracker(self, restriction):
@@ -232,10 +232,10 @@ def _validate_and_adjust_duration(self):
232232

233233
if isinstance(self.stop_ts, Timestamp):
234234
if self.stop_ts == MAX_TIMESTAMP:
235-
# adjust stop timestamp to match the data duration
236-
end = start + data_duration
237-
if self.interval > 1e-6:
238-
end += 1e-6
235+
# When the stop timestamp is unbounded (MAX_TIMESTAMP), set it to the
236+
# data's actual end time plus an extra fire interval, because the
237+
# impulse duration's upper bound is exclusive.
238+
end = start + data_duration + self.interval
239239
self.stop_ts = Timestamp.of(end)
240240
else:
241241
end = self.stop_ts.micros / 1000000
@@ -244,7 +244,7 @@ def _validate_and_adjust_duration(self):
244244

245245
# The total time for the impulse signal which occurs in [start, end).
246246
impulse_duration = end - start
247-
if data_duration + self.interval < impulse_duration:
247+
if round(data_duration + self.interval, 6) < round(impulse_duration, 6):
248248
# We don't have enough data for the impulse.
249249
# If we can fit at least one more data point in the impulse duration,
250250
# then we will be in the repeat mode.
@@ -264,8 +264,8 @@ def _validate_and_adjust_duration(self):
264264

265265
def __init__(
266266
self,
267-
start_timestamp: Union[Timestamp, float] = Timestamp.now(),
268-
stop_timestamp: Union[Timestamp, float] = MAX_TIMESTAMP,
267+
start_timestamp: Timestamp = Timestamp.now(),
268+
stop_timestamp: Timestamp = MAX_TIMESTAMP,
269269
fire_interval: float = 360.0,
270270
apply_windowing: bool = False,
271271
data: Optional[Sequence[Any]] = None):

sdks/python/apache_beam/transforms/periodicsequence_test.py

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919

2020
# pytype: skip-file
2121

22+
import logging
2223
import inspect
24+
import random
2325
import time
2426
import unittest
2527

@@ -174,7 +176,7 @@ def test_repeat(self):
174176
ret = (
175177
p | PeriodicImpulse(
176178
start_timestamp=now,
177-
stop_timestamp=now + 2.6,
179+
stop_timestamp=now + 3.0,
178180
data=[1, 2, 3, 4],
179181
fire_interval=0.5)
180182
| beam.WindowInto(FixedWindows(0.5))
@@ -208,15 +210,22 @@ def test_not_enough_timestamped_value(self):
208210
data=data,
209211
fire_interval=0.5))
210212

211-
def test_small_interval(self):
212-
data = [(Timestamp(1), 1), (Timestamp(2), 2), (Timestamp(3), 3),
213-
(Timestamp(6), 6), (Timestamp(4), 4), (Timestamp(5), 5),
214-
(Timestamp(7), 7), (Timestamp(8), 8), (Timestamp(9), 9),
215-
(Timestamp(10), 10)]
216-
expected = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
217-
with TestPipeline() as p:
218-
ret = (p | PeriodicImpulse(data=data, fire_interval=0.0001))
219-
assert_that(ret, equal_to(expected))
213+
def test_fuzzy_interval(self):
214+
seed = int(time.time() * 1000)
215+
times = 25
216+
logging.warning("random seed=%d", seed)
217+
random.seed(seed)
218+
for _ in range(times):
219+
n = int(random.randint(1, 100))
220+
data = list(range(n))
221+
m = random.randint(1, 1000)
222+
interval = m / 1e6
223+
now = Timestamp.now()
224+
with TestPipeline() as p:
225+
ret = (
226+
p | PeriodicImpulse(
227+
start_timestamp=now, data=data, fire_interval=interval))
228+
assert_that(ret, equal_to(data))
220229

221230

222231
if __name__ == '__main__':

0 commit comments

Comments
 (0)