Skip to content

feat(notifications): time-based notification jobs#882

Merged
raymondjacobson merged 2 commits into
mainfrom
api/time-based-notifications
May 29, 2026
Merged

feat(notifications): time-based notification jobs#882
raymondjacobson merged 2 commits into
mainfrom
api/time-based-notifications

Conversation

@raymondjacobson
Copy link
Copy Markdown
Member

Summary

Adds the three timer-driven notification creators the legacy Python discovery-provider celery beat produced, as scheduled parity jobs in the core-indexer. This closes the remaining functional gap between the vendored Go ETL indexer and the Python indexer for notifications: event-driven notifications are already handled by DB triggers (#851/#870), and these cover the ones that fire on elapsed time rather than an indexed entity.

  • EngagementNotificationsJob (claimable_reward): promotes 7-day-cooldown challenges to claimable_reward once their cooldown elapses and they're still undisbursed. Complements the handle_on_user_challenge trigger, which only emits an immediate claimable_reward for cooldown_days = 0 and reward_in_cooldown for cooldown_days > 0. Hence the cooldown_days = 7 filter, matching Python's hardcoded check. Scheduled every 10m.
  • ListenStreakReminderJob (listen_streak_reminder): reminds users in the 42-43h window after their last listen (6h of slack before the 48h streak breaks). Tight 1-2m window under env=stage for end-to-end testing, matching Python. Scheduled every 10s.
  • RemixContestNotificationsJob: the four fan/artist remix_contest ended / ending_soon notifications, with the same audience rules (remixers, host followers, parent-track favoriters, event subscribers; host excluded from fan types). Scheduled every 30s.

Each job is a set-based INSERT ... SELECT ... ON CONFLICT (group_id, specifier) DO NOTHING for idempotency via uq_notification, and is wired into startParityJobs. Mirrors the corresponding apps/ Python tasks.

Test plan

  • go test ./jobs/... — new tests cover the engagement full pipeline + exclusions (cooldown/disbursement/not-elapsed/incomplete), listen-streak window boundaries + idempotency, and remix-contest ended/ending-soon audiences with host exclusion.
  • Tests account for the handle_on_user_challenge and handle_event triggers that also fire on seed (verified no overlap with the asserted notification types).
  • go vet ./jobs/... ./indexer/... clean; full jobs package passes.
  • Observe in stage that the jobs emit the expected notifications on the real schedule.

🤖 Generated with Claude Code

raymondjacobson and others added 2 commits May 29, 2026 14:53
…-streak, remix-contest)

Adds the three timer-driven notification creators the legacy Python
discovery-provider beat produced, as scheduled parity jobs in the
core-indexer:

- EngagementNotificationsJob: promotes 7-day-cooldown challenges to
  claimable_reward once their cooldown elapses (complements the
  handle_on_user_challenge trigger, which only handles cooldown_days=0
  and reward_in_cooldown).
- ListenStreakReminderJob: reminds users 42-43h after last listen.
- RemixContestNotificationsJob: fan/artist contest ended + ending-soon.

Each is a set-based INSERT...SELECT...ON CONFLICT DO NOTHING keyed on
uq_notification (group_id, specifier) for idempotency, and is wired into
startParityJobs. Mirrors the corresponding apps/ Python tasks.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…FK flake

database.Seed processes non-entity tables in randomized map order, so a
single combined fixture could insert user_challenges before its FK parent
challenges, causing a flaky 23503 in CI. Split into two Seed calls to force
the ordering.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@raymondjacobson raymondjacobson merged commit 6764f89 into main May 29, 2026
5 checks passed
@raymondjacobson raymondjacobson deleted the api/time-based-notifications branch May 29, 2026 22:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant