Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions db/migrations/20260518000000_pending_interactions.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
-- migrate:up

-- Per-question state for the chat-interaction bridge — moved out of the
-- gateway's in-process Map so a button click that lands on pod B can claim
-- a question registered on pod A. The bridge keeps a small per-pod cache for
-- the platform `SentMessage` (used to edit the original card on click) since
-- that's a non-serializable SDK handle; everything that matters for routing
-- the click back into the worker (PostedQuestion + connection context) lives
-- here.
--
-- The claim path scopes by `(id, organization_id, connection_id,
-- expected_user_id)` — keying by `id` alone would let a click from one
-- connection or one user consume a question registered for another. The
-- columns are NOT NULL so the SQL claim is a single index hit with no
-- branching for NULL semantics.

CREATE TABLE public.pending_interactions (
id text PRIMARY KEY,
organization_id text NOT NULL REFERENCES public.organization(id) ON DELETE CASCADE,
connection_id text NOT NULL,
expected_user_id text NOT NULL,
entry_payload jsonb NOT NULL,
created_at timestamp with time zone NOT NULL DEFAULT now(),
claimed_at timestamp with time zone
);

-- Claim path is
-- UPDATE pending_interactions
-- SET claimed_at = now()
-- WHERE id = $1
-- AND organization_id = $2
-- AND connection_id = $3
-- AND expected_user_id = $4
-- AND claimed_at IS NULL
-- RETURNING entry_payload
-- — a partial index on the unclaimed predicate keeps the lookup index-only.
CREATE INDEX idx_pending_interactions_unclaimed
ON public.pending_interactions (id, organization_id, connection_id, expected_user_id)
WHERE claimed_at IS NULL;

-- Background sweeper drops rows older than 24h; index keeps that scan cheap.
CREATE INDEX idx_pending_interactions_created_at
ON public.pending_interactions (created_at);

-- migrate:down

DROP INDEX IF EXISTS public.idx_pending_interactions_created_at;
DROP INDEX IF EXISTS public.idx_pending_interactions_unclaimed;
DROP TABLE IF EXISTS public.pending_interactions;
43 changes: 42 additions & 1 deletion db/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -1419,6 +1419,20 @@ CREATE TABLE public.organization_lobu_links (
updated_at timestamp with time zone DEFAULT now() NOT NULL
);

--
-- Name: pending_interactions; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.pending_interactions (
id text NOT NULL,
organization_id text NOT NULL,
connection_id text NOT NULL,
expected_user_id text NOT NULL,
entry_payload jsonb NOT NULL,
created_at timestamp with time zone DEFAULT now() NOT NULL,
claimed_at timestamp with time zone
);

--
-- Name: personal_access_tokens; Type: TABLE; Schema: public; Owner: -
--
Expand Down Expand Up @@ -2633,6 +2647,13 @@ ALTER TABLE ONLY public.organization
ALTER TABLE ONLY public.organization
ADD CONSTRAINT organization_slug_key UNIQUE (slug);

--
-- Name: pending_interactions pending_interactions_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.pending_interactions
ADD CONSTRAINT pending_interactions_pkey PRIMARY KEY (id);

--
-- Name: personal_access_tokens personal_access_tokens_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
Expand Down Expand Up @@ -3613,6 +3634,18 @@ CREATE INDEX idx_notification_targets_user_all ON public.notification_targets US

CREATE INDEX idx_notification_targets_user_unread ON public.notification_targets USING btree (user_id, delivered_at DESC) WHERE (read_at IS NULL);

--
-- Name: idx_pending_interactions_created_at; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_pending_interactions_created_at ON public.pending_interactions USING btree (created_at);

--
-- Name: idx_pending_interactions_unclaimed; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_pending_interactions_unclaimed ON public.pending_interactions USING btree (id, organization_id, connection_id, expected_user_id) WHERE (claimed_at IS NULL);

--
-- Name: idx_personal_access_tokens_worker_id; Type: INDEX; Schema: public; Owner: -
--
Expand Down Expand Up @@ -4768,6 +4801,13 @@ ALTER TABLE ONLY public.organization_lobu_links
ALTER TABLE ONLY public.organization_lobu_links
ADD CONSTRAINT organization_lobu_links_organization_id_fkey FOREIGN KEY (organization_id) REFERENCES public.organization(id) ON DELETE CASCADE;

--
-- Name: pending_interactions pending_interactions_organization_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.pending_interactions
ADD CONSTRAINT pending_interactions_organization_id_fkey FOREIGN KEY (organization_id) REFERENCES public.organization(id) ON DELETE CASCADE;

--
-- Name: personal_access_tokens personal_access_tokens_organization_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
Expand Down Expand Up @@ -5030,4 +5070,5 @@ INSERT INTO public.schema_migrations (version) VALUES
('20260517050000'),
('20260517060000'),
('20260517150000'),
('20260517160000');
('20260517160000'),
('20260518000000');
Loading
Loading