-
Notifications
You must be signed in to change notification settings - Fork 141
Expand file tree
/
Copy pathtest_output_buffer_checkpoint.py
More file actions
114 lines (98 loc) · 3.65 KB
/
Copy pathtest_output_buffer_checkpoint.py
File metadata and controls
114 lines (98 loc) · 3.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
"""
Regression test for issue #6100: a pipeline with enable_output_buffer=true and
max_output_buffer_time_millis=60000 stalls processing for ~60 seconds on every
checkpoint when checkpoint_interval_secs=5.
The root cause: while a checkpoint is in progress, the circuit skips non-barrier
inputs (datagen, ad-hoc queries, etc.), so no records are processed until the
checkpoint completes. The checkpoint waits for output connectors to finish
transmitting records up to the checkpoint threshold, but with output buffering
enabled the connector holds records in memory until the buffer timeout expires.
The fix lets the pipeline continue processing inputs while the checkpoint runs
in the background.
"""
import time
import uuid
from feldera import PipelineBuilder
from feldera.enums import FaultToleranceModel
from feldera.runtime_config import RuntimeConfig
from feldera.testutils import (
FELDERA_TEST_NUM_HOSTS,
FELDERA_TEST_NUM_WORKERS,
enterprise_only,
single_host_only,
)
from tests import TEST_CLIENT
from .helper import gen_pipeline_name
@enterprise_only
@single_host_only
@gen_pipeline_name
def test_output_buffer_does_not_stall_checkpoint(pipeline_name):
"""Throughput must not drop to zero while automated checkpointing is active."""
output_path = f"/tmp/feldera_ob_{uuid.uuid4().hex}.jsonl"
sql = f"""
CREATE TABLE t (id BIGINT NOT NULL PRIMARY KEY)
WITH (
'connectors' = '[{{
"transport": {{
"name": "datagen",
"config": {{"plan": [{{"rate": 10000}}]}}
}}
}}]'
);
CREATE MATERIALIZED VIEW v
WITH (
'connectors' = '[{{
"transport": {{
"name": "file_output",
"config": {{"path": "{output_path}"}}
}},
"format": {{"name": "json"}},
"enable_output_buffer": true,
"max_output_buffer_time_millis": 60000
}}]'
) AS SELECT * FROM t;
""".strip()
pipeline = PipelineBuilder(
TEST_CLIENT,
pipeline_name,
sql,
runtime_config=RuntimeConfig(
workers=FELDERA_TEST_NUM_WORKERS,
hosts=FELDERA_TEST_NUM_HOSTS,
fault_tolerance_model=FaultToleranceModel.AtLeastOnce,
checkpoint_interval_secs=5,
),
).create_or_replace()
pipeline.start()
interval_s = 5.0
num_intervals = 3
total_s = interval_s * num_intervals
try:
# Wait for the pipeline to start processing.
deadline = time.monotonic() + total_s
while time.monotonic() < deadline:
if (pipeline.stats().global_metrics.total_processed_records or 0) > 0:
break
time.sleep(0.5)
# Sample total_processed_records once per checkpoint interval.
# Checkpoints fire at ~5 s intervals, so at least 2 checkpoints occur.
# Without the fix, each checkpoint stalls the circuit for ~60 s, and
# some windows show zero new records.
samples = [pipeline.stats().global_metrics.total_processed_records or 0]
for _ in range(num_intervals):
time.sleep(interval_s)
samples.append(pipeline.stats().global_metrics.total_processed_records or 0)
import sys
print(
f"\nprocessed_records samples (one per {interval_s}s): {samples}",
file=sys.stderr,
)
for i in range(1, len(samples)):
delta = samples[i] - samples[i - 1]
assert delta > 0, (
f"Throughput dropped to zero in window {i - 1}→{i} "
f"(processed_records: {samples[i - 1]} → {samples[i]}). "
f"The output buffer likely stalled the checkpoint (issue #6100)."
)
finally:
pipeline.stop(force=True)