mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-20 00:11:50 +00:00
fix(memory): enforce unique key per scope by using NULLS NOT DISTINCT
The unique constraint `(key, user_id, team_id)` on LiteLLM_MemoryTable silently allowed duplicates when user_id or team_id was NULL, because Postgres treats every NULL as distinct by default (ANSI semantics). A caller with no team_id could POST the same key three times and get three rows. Migration: 1. Dedupe existing rows, keeping the most recent per (key, user_id, team_id), using `IS NOT DISTINCT FROM` so NULL == NULL. 2. Drop the old unique index. 3. Recreate it with `NULLS NOT DISTINCT` (Postgres 15+). No code change: POST already returns 409 on unique-violation error messages — it just wasn't firing before because the constraint didn't catch the NULL-team case. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
cc1f0d4cf3
commit
bad0d0cd45
1 changed files with 34 additions and 0 deletions
|
|
@ -0,0 +1,34 @@
|
|||
-- Fix duplicate-key issue on LiteLLM_MemoryTable.
|
||||
--
|
||||
-- Problem: the unique constraint `(key, user_id, team_id)` does not block
|
||||
-- duplicates when `user_id` or `team_id` is NULL, because by default
|
||||
-- Postgres treats each NULL as distinct (ANSI SQL semantics). Callers with
|
||||
-- no team_id could POST the same key repeatedly and get multiple rows.
|
||||
--
|
||||
-- Fix (Postgres 15+): recreate the unique index with NULLS NOT DISTINCT so
|
||||
-- NULL values are treated as equal for uniqueness checks.
|
||||
--
|
||||
-- Note: Prisma has no schema syntax for NULLS NOT DISTINCT (as of writing),
|
||||
-- so this lives as a raw SQL migration. Prisma introspection will still see
|
||||
-- the index as uniquely covering `(key, user_id, team_id)`, matching the
|
||||
-- `@@unique` in schema.prisma.
|
||||
|
||||
-- 1. Deduplicate existing rows: keep only the most-recently-updated row per
|
||||
-- (key, user_id, team_id), using IS NOT DISTINCT FROM so NULL == NULL.
|
||||
DELETE FROM "LiteLLM_MemoryTable" a
|
||||
USING "LiteLLM_MemoryTable" b
|
||||
WHERE a.memory_id <> b.memory_id
|
||||
AND a.key = b.key
|
||||
AND a.user_id IS NOT DISTINCT FROM b.user_id
|
||||
AND a.team_id IS NOT DISTINCT FROM b.team_id
|
||||
AND (
|
||||
a.updated_at < b.updated_at
|
||||
OR (a.updated_at = b.updated_at AND a.memory_id < b.memory_id)
|
||||
);
|
||||
|
||||
-- 2. Drop the old (NULL-distinct) unique index.
|
||||
DROP INDEX IF EXISTS "LiteLLM_MemoryTable_key_user_id_team_id_key";
|
||||
|
||||
-- 3. Recreate with NULLS NOT DISTINCT.
|
||||
CREATE UNIQUE INDEX "LiteLLM_MemoryTable_key_user_id_team_id_key"
|
||||
ON "LiteLLM_MemoryTable"("key", "user_id", "team_id") NULLS NOT DISTINCT;
|
||||
Loading…
Add table
Reference in a new issue