You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit c39cb0e
Browse filesBrowse the repository at this point in the historyBrowse files
Tests using `pytestmark = [requires_snuba]` were not detected by our classifier because `_requires_snuba` was missing from the static detection fixture map. The fixture is session-scoped (runs once), so runtime socket detection didn't catch it either.
410
+
411
+
**Fix:** Added `_requires_snuba` to `FIXTURE_SERVICE_MAP` in the classifier plugin. These tests now correctly go to Tier 2.
Tests parametrized with `[cluster]` (e.g., `TestRedisHashSortedSetBuffer[cluster]`) need Redis-cluster (ports 7000-7005). The `migrations` devservices mode only starts `[postgres, redis]`, not `redis-cluster`.
416
+
417
+
**Fix:** Start redis-cluster via GitHub Actions `services:` block in Tier 1 (temporary; long-term solution is a new devservices mode `backend-ci-light`).
418
+
419
+
#### Category 3: Kafka timeouts (test failures)
420
+
421
+
3-6 tests per shard fail with `KafkaError{code=_MSG_TIMED_OUT}`. These are NOT tests that explicitly need Kafka. They trigger Kafka produces as a side effect of normal application code.
422
+
423
+
### Deep Dive: Kafka as an Implicit Dependency
424
+
425
+
#### What is Kafka in this context?
426
+
427
+
Kafka is a message queue. Sentry's taskworker system uses it to dispatch asynchronous tasks. When application code calls `task.apply_async()`, it produces a message to a Kafka topic. In production, a separate worker (taskbroker) consumes and executes the task. In CI tests, **no consumer runs** - even in the current `backend-ci` mode. The messages go to Kafka and are never read.
428
+
429
+
#### Why do "simple" tests trigger Kafka?
430
+
431
+
Django model managers have `post_save`/`post_delete` hooks that call `schedule_invalidate_project_config()`, which wraps `invalidate_project_config.apply_async()` in `transaction.on_commit()`. This fires whenever these common models are saved:
432
+
433
+
- `ProjectKey`(created in almost every test via fixtures)
434
+
- `ProjectOption`
435
+
- `OrganizationOption`
436
+
- `ReleaseProject`
437
+
438
+
So any test that creates a project, sets an option, or creates a release triggers a Kafka produce on transaction commit.
439
+
440
+
#### Why does it timeout instead of failing fast?
441
+
442
+
The `SingletonProducer` (in `src/sentry/utils/arroyo_producer.py`) tracks up to 1,000 futures. When Kafka is down, all futures fail. Once 1,000 accumulate, the producer blocks on `future.result()` of the oldest future, which times out with `MSG_TIMED_OUT`. Most tests don't hit this limit (< 1,000 Kafka produces per test), but a few long-running tests do.
443
+
444
+
#### Why not set `TASKWORKER_ALWAYS_EAGER=True`?
445
+
446
+
Test settings explicitly set `TASKWORKER_ALWAYS_EAGER = False` (in `src/sentry/testutils/pytest/sentry.py`). This is deliberate because:
447
+
448
+
- Tests use `BurstTaskRunner` to control when tasks execute. `TASKWORKER_ALWAYS_EAGER=True` would bypass this, executing tasks immediately and changing execution order.
449
+
- Some tests assert tasks were queued but NOT executed.
Setting it to `True` globally would change test behavior and could introduce false passes/failures.
453
+
454
+
### Kafka Resolution: Mock Approach (Chosen for PoC)
455
+
456
+
For Tier 1 tests, Kafka produces are purely side effects - the tests don't check whether the messages were delivered, and no consumer processes them even in the current `backend-ci` setup. The mock approach patches `SingletonProducer.produce` to return an immediately-resolved future, skipping the actual Kafka produce.
457
+
458
+
This is safe for Tier 1 because:
459
+
- Tasks already don't execute (no consumer in either tier)
460
+
- The produce is fire-and-forget (`wait_for_delivery=False` by default)
461
+
- Tests that explicitly need Kafka use `requires_kafka` and are in Tier 2
462
+
463
+
**Risk:** If a Tier 1 test is added that subtly depends on Kafka produce succeeding (without using `requires_kafka`), the mock would silently swallow it. The test would pass but behavior would be wrong. Over time this could mask regressions.
464
+
465
+
### Alternative: Kafka via GitHub Actions `services:` block
466
+
467
+
A more robust production alternative: start a real Kafka container alongside Tier 1 using GitHub Actions `services:` block. The Relay repo already uses this pattern:
If the mock approach proves problematic, switching to the `services:` block approach requires only a workflow change - no code changes needed.
495
+
399
496
## Validation
400
497
401
-
The classification can be validated empirically: run Tier 1 tests with only Postgres + Redis running (no Snuba). Any test that fails reveals a misclassification. The `requires_*` fixtures provide built-in fail-fast guards for their respective services.
498
+
The classification can be validated empirically: run Tier 1 tests with only the Tier 1 services. Any test that fails reveals a misclassification. The `requires_*` fixtures provide built-in fail-fast guards for their respective services.
402
499
403
500
## Expected Impact
404
501
405
-
~71% of backend tests can run with just Postgres + Redis (setup time: ~10s) instead of the full Snuba stack (setup time: 4-5min). Combined with intelligent sharding, this could significantly reduce CI wall-clock time and runner costs.
502
+
~71% of backend tests can run with just Postgres + Redis + Redis-cluster (setup time: ~15s) instead of the full Snuba stack (setup time: 4-5min). Combined with intelligent sharding, this could significantly reduce CI wall-clock time and runner costs.
0 commit comments