refactor(accounts): the key store upserts on (user, provider, kind)

Merged
CH
5d ago
+21-6

Step one of P04b in the "Bringing Codex or Claude Code Max plans into Luke" rollout (plan). It is code only, with no migration.

Why two steps

The live deployment (#4180) runs upsertRow with ON CONFLICT (user_id, provider). If the primary key moved to (user_id, provider, kind) in the same PR, nothing unique would remain on (user_id, provider). Key saves on the previous deployment would then fail for the whole time between the build's db:migrate and promotion. So this PR first moves the code off the two-column target. The PK swap follows once this is live.

What changes

  • upsertRow's conflict target is now (user_id, provider, kind). #4180's unique index account_provider_key_user_provider_kind_idx already backs it. The setWhere kind match stays for now. It becomes redundant after the swap.
  • While the PK is still (user_id, provider), a save of one kind where the provider holds another kind no longer matches the conflict target. It now fails on the primary key (UniqueViolation). saveProviderKey catches that and answers false (not kept), as before. So other_kind_held and the procedures' subscriptionHeld pre-check behave exactly as they do today.
  • Behavior is unchanged. The existing store and procedure tests cover both paths: the same-kind replace now runs through the three-column target, and the cross-kind refusal through the caught violation.

Next step (separate PR, after this one is deployed)

The migration moves the PK onto #4180's unique index in one ALTER, so nothing is rebuilt and the table never stands without a key:

DO $$
BEGIN
  IF EXISTS (
    SELECT 1 FROM pg_constraint
    WHERE conrelid = 'public.account_provider_key'::regclass
      AND conname = 'account_provider_key_user_id_provider_pk'
  ) THEN
    ALTER TABLE "account_provider_key"
      DROP CONSTRAINT "account_provider_key_user_id_provider_pk",
      ADD CONSTRAINT "account_provider_key_pkey" PRIMARY KEY USING INDEX "account_provider_key_user_provider_kind_idx";
  END IF;
END $$;

Notes for that PR:

  • Generate the migration with pnpm --filter @luke/database db:generate --name=account_provider_key_primary_key_kind --hints '[{"type":"create","kind":"primary_key","entity":["public","account_provider_key","account_provider_key_pkey"]}]'. Then replace the generated SQL (DROP INDEX …; DROP CONSTRAINT …; ADD PRIMARY KEY …) with the block above. The snapshot names the new PK account_provider_key_pkey, which is the name USING INDEX gives it.
  • In the same PR: drop the uniqueIndex from the schema, the otherKindHeld catch, setWhere, the subscriptionHeld pre-check and other_kind_held. Add coexistence, deletion and isolation tests, and add kind to the rewrap's where.

Checks

  • pnpm check:affected ran locally. Typecheck and tests passed. discarded-effect was OOM-killed in the sandbox (exit 137), so it was rerun on its own. CI gates the rest.
  • The developer waived WORKFLOW.md's Preview/Mac runs for this rollout until the cohort.

Made with Luke


Devin Review