Klivka The Klivka wordmark beside a balanced orange line symbol. Klivka
Back to updates

A Reminder Scheduler That Can Safely Run Twice

By Klivka Team. Published August 24, 2026

At noon on August 8, Klivka sees that your weekly reminder for Anna is due. After that, it records that an email needs to be sent. One hour later, Klivka checks again.

That second check is intentional. Someone might create a reminder at 12:30, and Klivka should find it without waiting until tomorrow. Anna’s reminder is still due at one o’clock, though, so it appears in both scans. Unless Klivka remembers what happened at noon, the later scan could schedule the same email again.

Klivka remembers this by storing a delivery row in the database before it sends anything. While the weekly reminder is the rule that says when to contact Anna, the delivery row belongs only to “August 8”, and records what happens to that email. Next week’s reminder will use the same rule and create a new delivery.

This gives the two records different jobs: the rule keeps the schedule, and the delivery keeps the result. It also lets email and in-app notifications proceed independently, since each channel gets its own delivery row.

Letting the database spot the duplicate

The database needs a precise way to recognize the August 8 email when it sees it again. To do that, Klivka uses a unique index that treats a delivery as the combination of the particular reminder rule, the date it is due, and the channel that should carry it. For Anna, those are her “weekly reminder”, “August 8”, and “email”.

A noon scan and a one o'clock scan both try to insert the same reminder delivery. A unique index over the rule, due date, and channel allows one pending row.

Both scans attempt the same insert. The unique index accepts the first and skips the second.

At noon, the insert succeeds. At one o’clock, the scan supplies the same reminder, date, and channel, and the database skips the duplicate. If both scans reached the database at nearly the same time, the result would stay the same because the database enforces the unique combination itself.

Klivka also saves the last date that it scanned successfully. When a scan fails partway through, the saved date does not move, and the next scan revisits the missed dates. Some delivery rows may already exist from the failed run, but the same unique index makes that catch-up safe.

Storing the work before sending it

The scan could call the email provider directly, but then finding reminders and sending them would share one failure path. The provider might accept Anna’s email just before Klivka loses the response. A later scan would see a due rule without knowing whether the earlier email had left. Recording success before calling the provider creates the opposite risk, since a crash could mark an email as sent when it never was.

Klivka avoids tying those steps together. The scan commits a pending delivery row, and a separate worker claims that row, sends the email, and records the outcome. If the provider reports a failure, the worker can return to the same row with its existing attempt count and status. In other words, Klivka stores the work before handing it to the email provider.

Before sending, the worker checks the rule once more. You may have contacted Anna, snoozed the reminder, or disabled email since the scan ran. In any of those cases, Klivka cancels the pending delivery and leaves completed history alone.

The extra table is a real cost. Every occurrence on every channel needs a row, and pending rows must stay aligned with rules that can change. A product with one-time reminders and one channel could reasonably put a sent_at column on the reminder itself.

Klivka supports recurring rules, two delivery channels, retries, and delivery history, so a row for each occurrence gives that state a clear home. The email provider still sits outside the database transaction, which means Klivka cannot promise exactly one email. Its narrower guarantee is still useful: at most one stored delivery for each rule, due date, and channel.