Skip to content

Commit 0710f3d

Browse files
authored
Merge pull request #7 from rahulbsw/feat/observability-and-ci-fixes
feat: Add Prometheus observability, envelope transforms, and fix CI (v0.4.0)
2 parents b1f647c + 668fe1d commit 0710f3d

61 files changed

Lines changed: 17154 additions & 1844 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ jobs:
2424
libsasl2-dev \
2525
libssl-dev \
2626
libzstd-dev \
27+
libcurl4-openssl-dev \
2728
cmake \
2829
pkg-config \
2930
clang \
@@ -54,7 +55,7 @@ jobs:
5455
strategy:
5556
fail-fast: false
5657
matrix:
57-
os: [ubuntu-latest, windows-latest, macos-latest]
58+
os: [ubuntu-latest, macos-latest]
5859
steps:
5960
- uses: actions/checkout@v4
6061

@@ -66,6 +67,7 @@ jobs:
6667
libsasl2-dev \
6768
libssl-dev \
6869
libzstd-dev \
70+
libcurl4-openssl-dev \
6971
cmake \
7072
pkg-config
7173
@@ -111,7 +113,9 @@ jobs:
111113
run: cargo install cargo-audit --locked
112114

113115
- name: Run security audit
114-
run: cargo audit
116+
# RUSTSEC-2024-0437: protobuf crash via prometheus 0.13 — no prometheus fix available yet,
117+
# only reachable from internal metrics scraper, not public-facing.
118+
run: cargo audit --ignore RUSTSEC-2024-0437
115119

116120
rust-benchmarks:
117121
name: Rust - Benchmarks
@@ -126,6 +130,7 @@ jobs:
126130
libsasl2-dev \
127131
libssl-dev \
128132
libzstd-dev \
133+
libcurl4-openssl-dev \
129134
cmake \
130135
pkg-config \
131136
clang \
Lines changed: 341 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,341 @@
1+
name: Performance Tests
2+
3+
# Manual trigger only - don't run on every commit
4+
on:
5+
workflow_dispatch:
6+
inputs:
7+
messages:
8+
description: 'Number of messages to test'
9+
required: false
10+
default: '100000'
11+
partitions:
12+
description: 'Number of partitions'
13+
required: false
14+
default: '8'
15+
threads:
16+
description: 'Number of threads'
17+
required: false
18+
default: '8'
19+
20+
jobs:
21+
# ==================== MICROBENCHMARKS ====================
22+
criterion-benchmarks:
23+
name: Criterion Benchmarks
24+
runs-on: ubuntu-latest
25+
steps:
26+
- uses: actions/checkout@v4
27+
28+
- name: Install system dependencies
29+
run: |
30+
sudo apt-get update
31+
sudo apt-get install -y \
32+
libsasl2-dev \
33+
libssl-dev \
34+
libzstd-dev \
35+
cmake \
36+
pkg-config \
37+
clang \
38+
libclang-dev
39+
40+
- name: Install Rust
41+
uses: dtolnay/rust-toolchain@stable
42+
43+
- name: Cache cargo
44+
uses: Swatinem/rust-cache@v2
45+
46+
- name: Run Criterion benchmarks
47+
run: cargo bench --bench filter_benchmarks --bench transform_benchmarks
48+
49+
- name: Upload benchmark results
50+
uses: actions/upload-artifact@v4
51+
with:
52+
name: criterion-results
53+
path: target/criterion/
54+
55+
# ==================== THROUGHPUT TEST ====================
56+
throughput-test:
57+
name: End-to-End Throughput Test
58+
runs-on: ubuntu-latest
59+
timeout-minutes: 30
60+
steps:
61+
- uses: actions/checkout@v4
62+
63+
- name: Install system dependencies
64+
run: |
65+
sudo apt-get update
66+
sudo apt-get install -y \
67+
libsasl2-dev \
68+
libssl-dev \
69+
libzstd-dev \
70+
cmake \
71+
pkg-config \
72+
clang \
73+
libclang-dev \
74+
kafkacat \
75+
jq
76+
77+
- name: Install Rust
78+
uses: dtolnay/rust-toolchain@stable
79+
80+
- name: Cache cargo
81+
uses: Swatinem/rust-cache@v2
82+
83+
- name: Build Streamforge
84+
run: cargo build --release
85+
86+
- name: Start Kafka
87+
run: |
88+
docker-compose -f docker-compose.benchmark.yml up -d
89+
sleep 30
90+
91+
- name: Wait for Kafka
92+
run: |
93+
for i in {1..30}; do
94+
if nc -z localhost 9092; then
95+
echo "Kafka is ready"
96+
exit 0
97+
fi
98+
echo "Waiting for Kafka... ($i/30)"
99+
sleep 2
100+
done
101+
echo "Kafka failed to start"
102+
exit 1
103+
104+
- name: Create topics
105+
run: |
106+
docker exec benchmark-kafka kafka-topics \
107+
--create --topic test-${{ inputs.partitions }}p-input \
108+
--partitions ${{ inputs.partitions }} \
109+
--replication-factor 1 \
110+
--bootstrap-server localhost:9092 || true
111+
112+
docker exec benchmark-kafka kafka-topics \
113+
--create --topic test-${{ inputs.partitions }}p-output \
114+
--partitions ${{ inputs.partitions }} \
115+
--replication-factor 1 \
116+
--bootstrap-server localhost:9092 || true
117+
118+
- name: Generate test data
119+
run: |
120+
cd benchmarks
121+
./generate_json_test_data.sh ${{ inputs.messages }} test_data.jsonl
122+
123+
- name: Create test config
124+
run: |
125+
cat > config.json << EOF
126+
{
127+
"appid": "ci-perf-test",
128+
"bootstrap": "localhost:9092",
129+
"target_broker": "localhost:9092",
130+
"input": "test-${{ inputs.partitions }}p-input",
131+
"output": "test-${{ inputs.partitions }}p-output",
132+
"threads": ${{ inputs.threads }},
133+
"offset": "earliest",
134+
"observability": {
135+
"metrics_enabled": true,
136+
"metrics_port": 9090,
137+
"lag_monitoring_enabled": true,
138+
"lag_monitoring_interval_secs": 10
139+
},
140+
"routing": {
141+
"routing_type": "filter",
142+
"destinations": [
143+
{
144+
"output": "test-${{ inputs.partitions }}p-output",
145+
"key_transform": "/userId",
146+
"headers": {
147+
"x-processed": "true"
148+
}
149+
}
150+
]
151+
}
152+
}
153+
EOF
154+
155+
- name: Start Streamforge
156+
run: |
157+
./target/release/streamforge > streamforge.log 2>&1 &
158+
echo $! > streamforge.pid
159+
sleep 5
160+
161+
- name: Send test messages
162+
run: |
163+
date +%s > start_time.txt
164+
cat benchmarks/test_data.jsonl | docker exec -i benchmark-kafka \
165+
kafka-console-producer \
166+
--bootstrap-server localhost:9092 \
167+
--topic test-${{ inputs.partitions }}p-input \
168+
--batch-size 2000
169+
date +%s > end_time.txt
170+
171+
- name: Wait for processing
172+
run: |
173+
for i in {1..60}; do
174+
CONSUMED=$(curl -s http://localhost:9090/metrics | grep "^streamforge_messages_consumed_total " | awk '{print $2}')
175+
PRODUCED=$(curl -s http://localhost:9090/metrics | grep "^streamforge_messages_produced_total" | awk '{print $2}')
176+
echo "[$i] Consumed: $CONSUMED | Produced: $PRODUCED"
177+
178+
if [[ "$CONSUMED" -ge "${{ inputs.messages }}" ]]; then
179+
echo "✓ All messages processed!"
180+
break
181+
fi
182+
sleep 5
183+
done
184+
185+
- name: Collect metrics
186+
run: |
187+
curl -s http://localhost:9090/metrics > final_metrics.txt
188+
189+
START=$(cat start_time.txt)
190+
END=$(cat end_time.txt)
191+
DURATION=$((END - START))
192+
193+
CONSUMED=$(grep "^streamforge_messages_consumed_total " final_metrics.txt | awk '{print $2}')
194+
PRODUCED=$(grep "^streamforge_messages_produced_total" final_metrics.txt | awk -F'}' '{print $2}' | awk '{print $1}')
195+
ERRORS=$(grep "^streamforge_processing_errors_total " final_metrics.txt | awk '{print $2}' | head -1)
196+
197+
THROUGHPUT=$((CONSUMED / DURATION))
198+
199+
echo "=== PERFORMANCE TEST RESULTS ===" | tee -a $GITHUB_STEP_SUMMARY
200+
echo "" | tee -a $GITHUB_STEP_SUMMARY
201+
echo "**Configuration:**" | tee -a $GITHUB_STEP_SUMMARY
202+
echo "- Messages: ${{ inputs.messages }}" | tee -a $GITHUB_STEP_SUMMARY
203+
echo "- Partitions: ${{ inputs.partitions }}" | tee -a $GITHUB_STEP_SUMMARY
204+
echo "- Threads: ${{ inputs.threads }}" | tee -a $GITHUB_STEP_SUMMARY
205+
echo "" | tee -a $GITHUB_STEP_SUMMARY
206+
echo "**Results:**" | tee -a $GITHUB_STEP_SUMMARY
207+
echo "- Consumed: $CONSUMED" | tee -a $GITHUB_STEP_SUMMARY
208+
echo "- Produced: $PRODUCED" | tee -a $GITHUB_STEP_SUMMARY
209+
echo "- Errors: ${ERRORS:-0}" | tee -a $GITHUB_STEP_SUMMARY
210+
echo "- Duration: ${DURATION}s" | tee -a $GITHUB_STEP_SUMMARY
211+
echo "- **Throughput: ${THROUGHPUT} msg/s**" | tee -a $GITHUB_STEP_SUMMARY
212+
echo "" | tee -a $GITHUB_STEP_SUMMARY
213+
214+
- name: Stop Streamforge
215+
if: always()
216+
run: |
217+
if [ -f streamforge.pid ]; then
218+
kill $(cat streamforge.pid) || true
219+
fi
220+
221+
- name: Upload logs
222+
if: always()
223+
uses: actions/upload-artifact@v4
224+
with:
225+
name: performance-test-logs
226+
path: |
227+
streamforge.log
228+
final_metrics.txt
229+
benchmarks/test_data.jsonl
230+
231+
- name: Stop Kafka
232+
if: always()
233+
run: |
234+
docker-compose -f docker-compose.benchmark.yml down -v
235+
236+
# ==================== LATENCY TEST ====================
237+
latency-test:
238+
name: Latency Profile Test
239+
runs-on: ubuntu-latest
240+
timeout-minutes: 20
241+
steps:
242+
- uses: actions/checkout@v4
243+
244+
- name: Install dependencies
245+
run: |
246+
sudo apt-get update
247+
sudo apt-get install -y \
248+
libsasl2-dev libssl-dev libzstd-dev cmake pkg-config clang libclang-dev
249+
250+
- name: Build
251+
run: cargo build --release
252+
253+
- name: Start Kafka
254+
run: |
255+
docker-compose -f docker-compose.benchmark.yml up -d
256+
sleep 30
257+
258+
- name: Run latency test
259+
run: |
260+
# Create small config for latency test
261+
cat > config.json << EOF
262+
{
263+
"appid": "latency-test",
264+
"bootstrap": "localhost:9092",
265+
"input": "test-latency-input",
266+
"output": "test-latency-output",
267+
"threads": 4,
268+
"observability": {
269+
"metrics_enabled": true,
270+
"metrics_port": 9090
271+
}
272+
}
273+
EOF
274+
275+
# Create topics
276+
docker exec benchmark-kafka kafka-topics \
277+
--create --topic test-latency-input --partitions 4 --replication-factor 1 \
278+
--bootstrap-server localhost:9092 || true
279+
docker exec benchmark-kafka kafka-topics \
280+
--create --topic test-latency-output --partitions 4 --replication-factor 1 \
281+
--bootstrap-server localhost:9092 || true
282+
283+
# Start Streamforge
284+
./target/release/streamforge > streamforge.log 2>&1 &
285+
STREAMFORGE_PID=$!
286+
sleep 5
287+
288+
# Generate and send 10K messages
289+
cd benchmarks
290+
./generate_json_test_data.sh 10000 latency_test.jsonl
291+
cat latency_test.jsonl | docker exec -i benchmark-kafka \
292+
kafka-console-producer --bootstrap-server localhost:9092 --topic test-latency-input
293+
294+
# Wait for processing
295+
sleep 10
296+
297+
# Extract latency metrics
298+
curl -s http://localhost:9090/metrics > latency_metrics.txt
299+
300+
echo "=== LATENCY RESULTS ===" | tee -a $GITHUB_STEP_SUMMARY
301+
echo "" | tee -a $GITHUB_STEP_SUMMARY
302+
grep "streamforge_processing_duration_seconds" latency_metrics.txt | grep -v "^#" | tee -a $GITHUB_STEP_SUMMARY
303+
304+
# Cleanup
305+
kill $STREAMFORGE_PID || true
306+
307+
- name: Stop Kafka
308+
if: always()
309+
run: docker-compose -f docker-compose.benchmark.yml down -v
310+
311+
# ==================== COMPARISON REPORT ====================
312+
performance-report:
313+
name: Generate Performance Report
314+
needs: [criterion-benchmarks, throughput-test, latency-test]
315+
runs-on: ubuntu-latest
316+
if: always()
317+
steps:
318+
- name: Download all artifacts
319+
uses: actions/download-artifact@v4
320+
321+
- name: Generate summary report
322+
run: |
323+
echo "# 🚀 Performance Test Summary" >> $GITHUB_STEP_SUMMARY
324+
echo "" >> $GITHUB_STEP_SUMMARY
325+
echo "Test run completed for:" >> $GITHUB_STEP_SUMMARY
326+
echo "- SHA: ${{ github.sha }}" >> $GITHUB_STEP_SUMMARY
327+
echo "- Branch: ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY
328+
echo "- Triggered by: ${{ github.actor }}" >> $GITHUB_STEP_SUMMARY
329+
echo "" >> $GITHUB_STEP_SUMMARY
330+
echo "## 📊 Test Configuration" >> $GITHUB_STEP_SUMMARY
331+
echo "- Messages: ${{ inputs.messages }}" >> $GITHUB_STEP_SUMMARY
332+
echo "- Partitions: ${{ inputs.partitions }}" >> $GITHUB_STEP_SUMMARY
333+
echo "- Threads: ${{ inputs.threads }}" >> $GITHUB_STEP_SUMMARY
334+
echo "" >> $GITHUB_STEP_SUMMARY
335+
echo "## 📥 Artifacts" >> $GITHUB_STEP_SUMMARY
336+
echo "- Criterion benchmark results" >> $GITHUB_STEP_SUMMARY
337+
echo "- Performance test logs" >> $GITHUB_STEP_SUMMARY
338+
echo "- Prometheus metrics" >> $GITHUB_STEP_SUMMARY
339+
echo "" >> $GITHUB_STEP_SUMMARY
340+
echo "---" >> $GITHUB_STEP_SUMMARY
341+
echo "*Note: CI performance results are indicative only. Production performance will vary based on hardware and network.*" >> $GITHUB_STEP_SUMMARY

0 commit comments

Comments
 (0)