fix(plex): make watch import concurrency-safe (upsert), guard toggle

The Plex watch import inserted plex_states via per-row ORM add against a pre-read 'existing' set,
which is not safe when the enable call fires more than once while the multi-second import runs (the
toggle wasn't disabled during it): overlapping imports raced to INSERT the same (user_id, item_id)
rows and all but the first hit a UniqueViolation -> 500 (seen on prod). Rewrite the write path as a
chunked PostgreSQL UPSERT (on_conflict_do_update, rows de-duped by item_id) — idempotent and
concurrency-safe, Plex still wins on the intersection and Siftlode-only states are untouched.

Also disable the Settings toggle (and ignore its onChange) while a sync mutation is pending, so it
can't be fired repeatedly; adds a reusable 'disabled' prop to the Switch primitive.
This commit is contained in:
npeter83 2026-07-09 15:18:19 +02:00
parent b09963be2b
commit 5592f60e85
3 changed files with 43 additions and 18 deletions

View file

@ -469,7 +469,11 @@ function PlexWatchSync() {
<Section title={t("settings.plexSync.title")}>
<p className="text-xs text-muted leading-relaxed mb-2">{t("settings.plexSync.intro")}</p>
<SettingRow label={t("settings.plexSync.toggle")} hint={t("settings.plexSync.toggleHint")}>
<Switch checked={enabled} onChange={(v) => toggle.mutate(v)} />
<Switch
checked={enabled}
disabled={toggle.isPending || reimport.isPending}
onChange={(v) => !toggle.isPending && toggle.mutate(v)}
/>
</SettingRow>
{enabled && (
<div className="mt-2 flex items-center justify-between gap-3">