Skip to content

add pgcron + scheduling primitives to Durable#75

Open
virajmehta wants to merge 7 commits intomainfrom
viraj/cron-in-durable
Open

add pgcron + scheduling primitives to Durable#75
virajmehta wants to merge 7 commits intomainfrom
viraj/cron-in-durable

Conversation

@virajmehta
Copy link
Member

@virajmehta virajmehta commented Feb 28, 2026

Note

Medium Risk
Adds new database schema/migration and runtime integration with pg_cron, plus queue drop behavior changes to unschedule jobs; issues here could leave orphaned jobs or break scheduling in production databases.

Overview
Adds cron scheduling to durable via pg_cron, including a new client API (setup_pgcron, create_schedule, list_schedules, delete_schedule) with validation, upsert semantics, and automatic injection of durable::scheduled_by/durable::cron headers.

Introduces a persistent schedule registry table durable.cron_schedules (with metadata/task-name indexes) and updates durable.drop_queue to remove registered schedules and best-effort unschedule matching pg_cron jobs. Also adds new DurableError variants for cron/schedule failures, re-exports the new types in lib.rs, and expands README + integration tests covering scheduling, filtering, and error cases.

Written by Cursor Bugbot for commit 5f45860. This will update automatically on new commits. Configure here.

@virajmehta virajmehta self-assigned this Feb 28, 2026
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 422b50a751

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

**Setup** - Enable pg_cron once at startup:

```rust
use durable::setup_pgcron;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we want to leave pgcron optional? we could make it mandatory later

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Autofix Details

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: README uses "PostgreSQL" instead of "Postgres"
    • Updated the README line to use "Postgres" in the pg_cron integration description to match project convention.
  • ✅ Fixed: Dollar-quoting breaks when content ends with tag prefix
    • Adjusted pg_literal to fall back when input ends with $durable and added a unit test covering this delimiter-boundary edge case.

Create PR

Or push these changes by commenting:

@cursor push 07c4b46d0a
Preview (07c4b46d0a)
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -326,7 +326,7 @@
 
 **Key behaviors:**
 
-- **pg_cron integration** - Schedules are backed by PostgreSQL's pg_cron extension. At each tick, pg_cron inserts a task into the queue via `durable.spawn_task()`, and workers pick it up normally.
+- **pg_cron integration** - Schedules are backed by Postgres's pg_cron extension. At each tick, pg_cron inserts a task into the queue via `durable.spawn_task()`, and workers pick it up normally.
 - **Upsert semantics** - Calling `create_schedule` with an existing name updates the schedule in place.
 - **Origin tracking** - Scheduled tasks automatically receive `durable::scheduled_by` and `durable::cron` headers, so tasks can identify how they were spawned.
 - **Metadata filtering** - Attach arbitrary JSON metadata to schedules and filter with JSONB containment queries.

diff --git a/src/cron.rs b/src/cron.rs
--- a/src/cron.rs
+++ b/src/cron.rs
@@ -504,9 +504,10 @@
 // --- SQL escaping ---
 
 /// Dollar-quote a string using `$durable$` as the delimiter.
-/// Falls back to escaped single quotes if the content contains `$durable$`.
+/// Falls back to escaped single quotes if the content contains `$durable$`
+/// or ends with `$durable` (which would collide with the closing delimiter).
 fn pg_literal(s: &str) -> String {
-    if !s.contains("$durable$") {
+    if !s.contains("$durable$") && !s.ends_with("$durable") {
         format!("$durable${s}$durable$")
     } else {
         // Fallback: single-quote escaping (double up any single quotes)
@@ -725,6 +726,12 @@
         assert_eq!(pg_literal(content), "'has $durable$ and ''quotes'''");
     }
 
+    #[test]
+    fn test_pg_literal_fallback_when_content_ends_with_delimiter_prefix() {
+        let content = "test$durable";
+        assert_eq!(pg_literal(content), "'test$durable'");
+    }
+
     // --- Schedule name validation tests ---
 
     #[test]
This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.


**Key behaviors:**

- **pg_cron integration** - Schedules are backed by PostgreSQL's pg_cron extension. At each tick, pg_cron inserts a task into the queue via `durable.spawn_task()`, and workers pick it up normally.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

README uses "PostgreSQL" instead of "Postgres"

Low Severity

The newly added line uses "PostgreSQL's pg_cron extension" instead of the preferred "Postgres's pg_cron extension", violating the project convention to prefer "Postgres" over "PostgreSQL" in documentation.

Fix in Cursor Fix in Web

Triggered by team rule: Prefer "Postgres" instead of PostgreSQL"

@virajmehta virajmehta assigned Aaron1011 and unassigned virajmehta Mar 1, 2026
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

if s.contains("$durable") {
return Err(DurableError::InvalidConfiguration {
reason: format!(
"string contains reserved delimiter sequence '$durable': {s}"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error message uses ticks instead of backticks

Low Severity

The error message in pg_literal wraps the technical term $durable with single quotes ('$durable') instead of backticks, which violates the project convention for wrapping technical terms.

Fix in Cursor Fix in Web

Triggered by team rule: Prefer backticks (`) instead of ticks (') to wrap technical terms

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.

2 participants