duroxide-pg 0.1.25

A PostgreSQL-based provider implementation for Duroxide, a durable task orchestration framework
Documentation
# Diff for migration 0011

**Migration file:** `0011_fix_cancellation_ordering.sql`

This diff was auto-generated by comparing schema state before and after applying migration 0011.

## Schema Diff

```diff
--- /var/folders/v5/0qbxlktj4nb4jzvgfg6jqljw0000gn/T/tmp.wGxSZE83jA	2026-01-30 21:32:28
+++ /var/folders/v5/0qbxlktj4nb4jzvgfg6jqljw0000gn/T/tmp.ghFPiJRC0E	2026-01-30 21:32:28
@@ -251,20 +251,19 @@
                 RAISE EXCEPTION 'Invalid lock token';
             END IF;
 
-            -- Step 2: Extract metadata from JSONB (including parent_instance_id)
+            -- Step 2: Extract metadata from JSONB
             v_orchestration_name := p_metadata->>'orchestration_name';
             v_orchestration_version := p_metadata->>'orchestration_version';
             v_parent_instance_id := p_metadata->>'parent_instance_id';
             v_status := p_metadata->>'status';
             v_output := p_metadata->>'output';
 
-            -- Step 3: Create or update instance metadata (including parent_instance_id)
+            -- Step 3: Create or update instance metadata
             IF v_orchestration_name IS NOT NULL AND v_orchestration_version IS NOT NULL THEN
                 INSERT INTO SCHEMA.instances (instance_id, orchestration_name, orchestration_version, current_execution_id, parent_instance_id)
                 VALUES (v_instance_id, v_orchestration_name, v_orchestration_version, p_execution_id, v_parent_instance_id)
                 ON CONFLICT (instance_id) DO NOTHING;
 
-                -- Update existing instance (but only set parent_instance_id if it was NULL)
                 UPDATE SCHEMA.instances i
                 SET orchestration_name = v_orchestration_name,
                     orchestration_version = v_orchestration_version,
@@ -304,20 +303,12 @@
                 UPDATE SCHEMA.executions e
                 SET status = v_status, output = v_output, completed_at = v_completed_at
                 WHERE e.instance_id = v_instance_id AND e.execution_id = p_execution_id;
-            END IF;
-
-            -- Step 8: Delete cancelled activities from worker_queue (lock stealing)
-            -- This removes activities that were scheduled but not yet started/completed
-            IF p_cancelled_activities IS NOT NULL AND JSONB_ARRAY_LENGTH(p_cancelled_activities) > 0 THEN
-                FOR v_elem IN SELECT value FROM JSONB_ARRAY_ELEMENTS(p_cancelled_activities) LOOP
-                    DELETE FROM SCHEMA.worker_queue
-                    WHERE instance_id = v_elem->>'instance'
-                      AND execution_id = (v_elem->>'execution_id')::BIGINT
-                      AND activity_id = (v_elem->>'activity_id')::BIGINT;
-                END LOOP;
             END IF;
 
-            -- Step 9: Enqueue worker items (batch) - now with activity identification
+            -- Step 8: Enqueue worker items FIRST - now with activity identification
+            -- IMPORTANT: This must happen BEFORE deleting cancelled activities so that
+            -- activities that are both scheduled AND cancelled in the same call get inserted
+            -- first, then deleted (supporting schedule-then-drop pattern)
             IF p_worker_items IS NOT NULL AND JSONB_ARRAY_LENGTH(p_worker_items) > 0 THEN
                 FOR v_elem IN SELECT value FROM JSONB_ARRAY_ELEMENTS(p_worker_items) LOOP
                     -- Extract activity identification from ActivityExecute work item
@@ -333,6 +324,18 @@
 
                     INSERT INTO SCHEMA.worker_queue (work_item, visible_at, created_at, instance_id, execution_id, activity_id)
                     VALUES (v_elem::TEXT, v_now_ts, v_now_ts, v_item_instance_id, v_item_execution_id, v_item_activity_id);
+                END LOOP;
+            END IF;
+
+            -- Step 9: Delete cancelled activities from worker_queue (lock stealing)
+            -- IMPORTANT: This must happen AFTER inserting worker items so that activities
+            -- scheduled and cancelled in the same call are properly removed
+            IF p_cancelled_activities IS NOT NULL AND JSONB_ARRAY_LENGTH(p_cancelled_activities) > 0 THEN
+                FOR v_elem IN SELECT value FROM JSONB_ARRAY_ELEMENTS(p_cancelled_activities) LOOP
+                    DELETE FROM SCHEMA.worker_queue
+                    WHERE instance_id = v_elem->>'instance'
+                      AND execution_id = (v_elem->>'execution_id')::BIGINT
+                      AND activity_id = (v_elem->>'activity_id')::BIGINT;
                 END LOOP;
             END IF;
 
```

---
*Generated on 2026-01-31 05:32:28 UTC*