Ask
96
@merino_max ·

durable object alarms or a real queue for 5k scheduled reminders a day Queue Choice

Reminder feature. User picks a time, we send a push. Roughly 5,000 a day spread over 24 hours, and they can be edited or cancelled right up to the moment they fire.

Option A: a Durable Object per user holding an alarm for the next due reminder.
Option B: a table and a worker polling once a minute.
Option C: a queue with delayed delivery.

Editability is what makes me unsure. A queued message is not easy to un-queue.

8 answers Share
Report

Answering anonymously — a moderator will review it first.

  • @keg_line_ken · 6mo ago · 2 replies

    A per-user Durable Object with a single alarm for its next due reminder, for exactly the reason you identified: the state that changes and the scheduling live in the same place, so an edit is just recomputing the next alarm time and calling setAlarm again.

    Per user, not per reminder. Thousands of objects holding a handful of reminders each is the shape this is designed for; one object per reminder is a lot of objects doing nothing for hours.

    You also get retry with backoff on alarm handler failure without writing it, which is the part people forget they would otherwise owe themselves.

    148
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report
    • @wick_and_wax · 6mo ago

      And keep the reminders in the object's own storage so the alarm handler does not need a round trip to a database to know what to send. Co-locating the next-due timestamp with the data it refers to is the entire benefit of the pattern; if you store the reminders elsewhere you have kept the complexity and thrown away the win.

      42
      Share
      Reply

      Answering anonymously — a moderator will review it first.

      Report
  • @thermal_theo · 6mo ago · 3 replies

    Option B is not the embarrassing choice you think it is.

    select * from reminders
    where due_at <= now() and sent_at is null
    order by due_at
    for update skip locked
    limit 100
    

    once a minute. 5,000 a day is 3.5 a minute. One table, one query, one cron. Edits are an UPDATE. Cancels are a DELETE. At 3am when something is wrong you can see the entire state of the system with a SELECT, which is worth more than people admit until the night they need it.

    107
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report
    • @hollis_pike · 6mo ago

      Minute granularity is the trade-off. Pick 09:07 and you might get it at 09:07:58. Nobody notices for reminders. People absolutely notice for anything whose copy says "at exactly".

      36
      Share
      Reply

      Answering anonymously — a moderator will review it first.

      Report
    • @shopvac_ghost · 6mo ago

      It also will not carry 500k a day without work. At 5k it will comfortably outlive the product, which is the relevant timescale.

      15
      Share
      Reply

      Answering anonymously — a moderator will review it first.

      Report
  • @stropping_sal · 6mo ago

    Do not use delayed queue messages for anything editable. You end up keeping a cancellation table so the consumer can check "is this still wanted" on delivery - at which point you have built option B anyway, but with two sources of truth and a race between them.

    Delayed messages are for things that are true when you enqueue them and stay true.

    64
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report
  • @sawdust_pete · 6mo ago

    Whichever you pick, make the send idempotent on (reminder_id, scheduled_for). Every one of these designs fires twice eventually, and "user got two pushes" is a bug report you can avoid with a unique index.

    25
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report
  • @secondhandamps · 6mo ago

    The boring answer nobody wants: you are already running Postgres. Do B until it hurts. It will not hurt at 5k.

    9
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report