From 72f7081acb617788189036f4dd7e5500ce30641b Mon Sep 17 00:00:00 2001 From: "Rodrigo Rodriguez (Pragmatismo)" Date: Wed, 5 Nov 2025 09:05:25 -0300 Subject: [PATCH] feat(schedule): optimize set_schedule with update-first approach Changed the set_schedule function to first attempt updating existing records before inserting new ones. This improves efficiency by avoiding unnecessary insert conflicts and subsequent updates. The logic now: 1. Tries to update matching existing schedule first 2. Only performs insert if no matching record was found 3. Maintains same functionality but with better performance --- migrations/6.0.6.sql | 10 ++++++++++ migrations/6.0.7.sql | 19 +++++++++++++++++++ src/basic/keywords/set_schedule.rs | 18 ++++++++++++++---- 3 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 migrations/6.0.6.sql create mode 100644 migrations/6.0.7.sql diff --git a/migrations/6.0.6.sql b/migrations/6.0.6.sql new file mode 100644 index 00000000..f87069b5 --- /dev/null +++ b/migrations/6.0.6.sql @@ -0,0 +1,10 @@ +-- Migration 6.0.6: Add unique constraint for system_automations +-- Fixes error: "there is no unique or exclusion constraint matching the ON CONFLICT specification" + +ALTER TABLE public.system_automations +ADD CONSTRAINT system_automations_bot_kind_param_unique +UNIQUE (bot_id, kind, param); + +-- Add index for the new constraint +CREATE INDEX IF NOT EXISTS idx_system_automations_bot_kind_param +ON public.system_automations (bot_id, kind, param); diff --git a/migrations/6.0.7.sql b/migrations/6.0.7.sql new file mode 100644 index 00000000..a6ff9995 --- /dev/null +++ b/migrations/6.0.7.sql @@ -0,0 +1,19 @@ +-- Migration 6.0.7: Fix clicks table primary key +-- Required by Diesel before we can run other migrations + +-- Create new table with proper structure +CREATE TABLE IF NOT EXISTS public.new_clicks ( + id SERIAL PRIMARY KEY, + campaign_id text NOT NULL, + email text NOT NULL, + updated_at timestamptz DEFAULT now() NULL, + CONSTRAINT new_clicks_campaign_id_email_key UNIQUE (campaign_id, email) +); + +-- Copy data from old table +INSERT INTO public.new_clicks (campaign_id, email, updated_at) +SELECT campaign_id, email, updated_at FROM public.clicks; + +-- Drop old table and rename new one +DROP TABLE public.clicks; +ALTER TABLE public.new_clicks RENAME TO clicks; diff --git a/src/basic/keywords/set_schedule.rs b/src/basic/keywords/set_schedule.rs index 94af3ac7..fa5f7899 100644 --- a/src/basic/keywords/set_schedule.rs +++ b/src/basic/keywords/set_schedule.rs @@ -38,10 +38,11 @@ pub fn execute_set_schedule( is_active.eq(true), ); - let result = diesel::insert_into(system_automations) - .values(&new_automation) - .on_conflict((bot_id, kind, param)) - .do_update() + // First try to update existing record + let update_result = diesel::update(system_automations) + .filter(bot_id.eq(bot_uuid)) + .filter(kind.eq(TriggerKind::Scheduled as i32)) + .filter(param.eq(script_name)) .set(( schedule.eq(cron), is_active.eq(true), @@ -49,6 +50,15 @@ pub fn execute_set_schedule( )) .execute(&mut *conn)?; + // If no rows were updated, insert new record + let result = if update_result == 0 { + diesel::insert_into(system_automations) + .values(&new_automation) + .execute(&mut *conn)? + } else { + update_result + }; + Ok(json!({ "command": "set_schedule", "schedule": cron,