Skip to content

Commit 8fd3571

Browse files
committed
feat(ci): xdist backend test workflow
Enable pytest-xdist in the backend CI workflow with per-worker Snuba isolation. Key changes: - Static shard fast-path (skip pytest --collect-only on non-PR runs) - Per-worker Snuba bootstrap step (conditional on XDIST_PER_WORKER_SNUBA) - Inline pytest invocation with -n 3 --dist=loadfile - Snuba container log dump on failure for debugging
1 parent 8debc89 commit 8fd3571

File tree

1 file changed

+79
-9
lines changed

1 file changed

+79
-9
lines changed

.github/workflows/backend.yml

Lines changed: 79 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@ name: backend
33
on:
44
push:
55
branches:
6-
- master
7-
pull_request:
8-
types: [opened, synchronize, reopened, labeled]
6+
- mchen/tiered-xdist-v2
7+
- mchen/tiered-xdist-v2-clean
98
workflow_dispatch:
109

1110
# Cancel in progress workflows on pull_requests.
@@ -204,15 +203,24 @@ jobs:
204203
needs: [files-changed, prepare-selective-tests]
205204
name: calculate test shards
206205
runs-on: ubuntu-24.04
207-
timeout-minutes: 5
206+
timeout-minutes: ${{ needs.prepare-selective-tests.outputs.has-selected-tests == 'true' && 5 || 1 }}
208207
outputs:
209-
shard-count: ${{ steps.calculate-shards.outputs.shard-count }}
210-
shard-indices: ${{ steps.calculate-shards.outputs.shard-indices }}
208+
shard-count: ${{ steps.static-shards.outputs.shard-count || steps.calculate-shards.outputs.shard-count }}
209+
shard-indices: ${{ steps.static-shards.outputs.shard-indices || steps.calculate-shards.outputs.shard-indices }}
211210

212211
steps:
212+
- name: Use default shards (no selective testing)
213+
id: static-shards
214+
if: needs.prepare-selective-tests.outputs.has-selected-tests != 'true'
215+
run: |
216+
echo "shard-count=22" >> "$GITHUB_OUTPUT"
217+
echo "shard-indices=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21]" >> "$GITHUB_OUTPUT"
218+
213219
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
220+
if: needs.prepare-selective-tests.outputs.has-selected-tests == 'true'
214221

215222
- name: Setup sentry env
223+
if: needs.prepare-selective-tests.outputs.has-selected-tests == 'true'
216224
uses: ./.github/actions/setup-sentry
217225
id: setup
218226
with:
@@ -228,8 +236,9 @@ jobs:
228236

229237
- name: Calculate test shards
230238
id: calculate-shards
239+
if: needs.prepare-selective-tests.outputs.has-selected-tests == 'true'
231240
env:
232-
SELECTED_TESTS_FILE: ${{ needs.prepare-selective-tests.outputs.has-selected-tests == 'true' && '.artifacts/selected-tests.txt' || '' }}
241+
SELECTED_TESTS_FILE: '.artifacts/selected-tests.txt'
233242
SELECTED_TEST_COUNT: ${{ needs.prepare-selective-tests.outputs.test-count || '' }}
234243
run: |
235244
python3 .github/workflows/scripts/calculate-backend-test-shards.py
@@ -258,9 +267,11 @@ jobs:
258267
instance: ${{ fromJSON(needs.calculate-shards.outputs.shard-indices) }}
259268

260269
env:
261-
# Dynamic total from calculate-shards
262270
MATRIX_INSTANCE_TOTAL: ${{ needs.calculate-shards.outputs.shard-count }}
263271
TEST_GROUP_STRATEGY: roundrobin
272+
PYTHONHASHSEED: '0'
273+
XDIST_PER_WORKER_SNUBA: '1'
274+
XDIST_WORKERS: '3'
264275

265276
steps:
266277
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
@@ -278,6 +289,50 @@ jobs:
278289
sudo install -m 755 odiff-linux-x64 /usr/local/bin/odiff
279290
rm odiff-linux-x64
280291
292+
- name: Bootstrap per-worker Snuba instances
293+
if: env.XDIST_PER_WORKER_SNUBA == '1'
294+
run: |
295+
set -eo pipefail
296+
XDIST_N=${XDIST_WORKERS:-3}
297+
SNUBA_IMAGE=$(docker inspect snuba-snuba-1 --format '{{.Config.Image}}')
298+
SNUBA_NETWORK=$(docker inspect snuba-snuba-1 --format '{{range $k, $v := .NetworkSettings.Networks}}{{$k}}{{end}}')
299+
if [ -z "$SNUBA_IMAGE" ] || [ -z "$SNUBA_NETWORK" ]; then
300+
echo "ERROR: Could not inspect snuba-snuba-1 container. Is devservices running?"
301+
exit 1
302+
fi
303+
304+
for i in $(seq 0 $((XDIST_N - 1))); do
305+
(
306+
WORKER_DB="default_gw${i}"
307+
WORKER_PORT=$((1230 + i))
308+
curl -sf 'http://localhost:8123/' --data-binary "CREATE DATABASE IF NOT EXISTS ${WORKER_DB}"
309+
docker run --rm --network "$SNUBA_NETWORK" \
310+
-e "CLICKHOUSE_DATABASE=${WORKER_DB}" -e "CLICKHOUSE_HOST=clickhouse" \
311+
-e "CLICKHOUSE_PORT=9000" -e "CLICKHOUSE_HTTP_PORT=8123" \
312+
-e "DEFAULT_BROKERS=kafka:9093" -e "REDIS_HOST=redis" \
313+
-e "REDIS_PORT=6379" -e "REDIS_DB=1" -e "SNUBA_SETTINGS=docker" \
314+
"$SNUBA_IMAGE" bootstrap --force 2>&1 | tail -3
315+
docker run -d --name "snuba-gw${i}" --network "$SNUBA_NETWORK" \
316+
-p "${WORKER_PORT}:1218" \
317+
-e "CLICKHOUSE_DATABASE=${WORKER_DB}" -e "CLICKHOUSE_HOST=clickhouse" \
318+
-e "CLICKHOUSE_PORT=9000" -e "CLICKHOUSE_HTTP_PORT=8123" \
319+
-e "DEFAULT_BROKERS=kafka:9093" -e "REDIS_HOST=redis" \
320+
-e "REDIS_PORT=6379" -e "REDIS_DB=1" -e "SNUBA_SETTINGS=docker" \
321+
-e "DEBUG=1" "$SNUBA_IMAGE" api
322+
) &
323+
done
324+
wait
325+
326+
for i in $(seq 0 $((XDIST_N - 1))); do
327+
WORKER_PORT=$((1230 + i))
328+
for attempt in $(seq 1 30); do
329+
if curl -sf "http://localhost:${WORKER_PORT}/health" > /dev/null 2>&1; then
330+
echo "[snuba-gw${i}] ready on port ${WORKER_PORT}"; break; fi
331+
[ "$attempt" -eq 30 ] && echo "[snuba-gw${i}] FAILED" && docker logs "snuba-gw${i}" 2>&1 | tail -20 && exit 1
332+
sleep 1
333+
done
334+
done
335+
281336
- name: Download selected tests artifact
282337
if: needs.prepare-selective-tests.outputs.has-selected-tests == 'true'
283338
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
@@ -289,7 +344,19 @@ jobs:
289344
env:
290345
SELECTED_TESTS_FILE: ${{ needs.prepare-selective-tests.outputs.has-selected-tests == 'true' && '.artifacts/selected-tests.txt' || '' }}
291346
run: |
292-
make test-python-ci
347+
python3 -b -m pytest tests \
348+
--reuse-db \
349+
-n ${XDIST_WORKERS:-3} \
350+
--dist=loadfile \
351+
--ignore tests/acceptance \
352+
--ignore tests/apidocs \
353+
--ignore tests/js \
354+
--ignore tests/tools \
355+
--json-report \
356+
--json-report-file=".artifacts/pytest.json" \
357+
--json-report-omit=log \
358+
--junit-xml=.artifacts/pytest.junit.xml \
359+
-o junit_suite_name=pytest
293360
294361
- name: Report failures
295362
if: ${{ !cancelled() && github.event_name == 'pull_request' }}
@@ -306,6 +373,9 @@ jobs:
306373
- name: Inspect failure
307374
if: failure()
308375
run: |
376+
for name in $(docker ps -a --filter "name=snuba-gw" --format '{{.Names}}'); do
377+
echo "--- $name ---"; docker logs "$name" 2>&1 | tail -30
378+
done
309379
if command -v devservices; then
310380
devservices logs
311381
fi

0 commit comments

Comments
 (0)