mirror of
https://github.com/Luchexx/wacrm.git
synced 2026-11-02 09:19:51 +00:00
The engine did a client-side read-modify-write on automations.execution_count, so two concurrent dispatches of the same automation (e.g. fired for two different contacts within the same second) could both read N and both write N+1, permanently losing one count. - Migration 007 adds increment_automation_execution_count(p_automation_id) as a SECURITY DEFINER sql function. Only service_role can EXECUTE it — an authenticated user can't juice someone else's counter by calling the RPC directly. - Engine now calls this RPC instead of reading automation.execution_count and adding 1. Atomic in one statement; concurrent dispatches both land correctly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
36 lines
1.4 KiB
PL/PgSQL
36 lines
1.4 KiB
PL/PgSQL
-- ============================================================
|
|
-- 007_automations_increment_counter.sql
|
|
--
|
|
-- Atomic increment of automations.execution_count + refresh of
|
|
-- last_executed_at. Called via PostgREST RPC from the engine.
|
|
--
|
|
-- Before this, the engine did a read-modify-write:
|
|
-- UPDATE automations SET execution_count = <cached + 1> WHERE id = ...
|
|
-- so two concurrent dispatches (e.g. the same automation firing for
|
|
-- two different contacts in the same second) could both read N and
|
|
-- both write N+1, permanently losing one count.
|
|
--
|
|
-- Idempotent — safe to re-run.
|
|
-- ============================================================
|
|
|
|
CREATE OR REPLACE FUNCTION increment_automation_execution_count(p_automation_id UUID)
|
|
RETURNS VOID
|
|
LANGUAGE sql
|
|
SECURITY DEFINER
|
|
SET search_path = public
|
|
AS $$
|
|
UPDATE automations
|
|
SET
|
|
execution_count = execution_count + 1,
|
|
last_executed_at = NOW()
|
|
WHERE id = p_automation_id;
|
|
$$;
|
|
|
|
-- Only the service role needs to call this (engine uses the
|
|
-- service-role client). Explicitly lock anon / authenticated out so
|
|
-- an authenticated user can't juice someone else's counter via RPC.
|
|
REVOKE ALL ON FUNCTION increment_automation_execution_count(UUID) FROM PUBLIC;
|
|
REVOKE ALL ON FUNCTION increment_automation_execution_count(UUID) FROM anon;
|
|
REVOKE ALL ON FUNCTION increment_automation_execution_count(UUID) FROM authenticated;
|
|
GRANT EXECUTE ON FUNCTION increment_automation_execution_count(UUID) TO service_role;
|