Skip to content

Performance: Push notification cron job leaks dead subscriptions causing API exhaustion #1729

Description

@nyxsky404

Exact File Paths

backend/controllers/cronController.js (Lines 112 - 130)

The Core Problem

The dispatchPushNotifications cron job maps over all database subscriptions to send push notifications via Promise.allSettled. However, when a browser revokes permission or a subscription expires, web-push rejects the promise with a 410 Gone or 404 status code. The current implementation catches these failures but never deletes the dead endpoints from the database.

The Reproducible Scenario / Impact

Over time, the push_subscriptions table will fill up with dead endpoints as users switch devices or revoke permissions. Because the cron job runs continuously, it will waste network bandwidth, CPU time, and database resources attempting to execute VAPID handshakes with endpoints that no longer exist, eventually causing the cron job to timeout or hit database connection limits.

Step-by-Step Suggested Solution

  1. After await Promise.allSettled(...), iterate through pushResults.
  2. Filter for rejected promises where result.reason.statusCode === 410 || result.reason.statusCode === 404.
  3. Map these to their original subscription.endpoint.
  4. Run a bulk Supabase deletion: await supabase.from("push_subscriptions").delete().in("endpoint", deadEndpoints);.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions