From ec37e237185e46953e586e4bd38341477dfe271d Mon Sep 17 00:00:00 2001 From: Deluan Date: Fri, 4 Sep 2026 22:36:13 -0400 Subject: [PATCH] perf(pglite): purge unused tags with NOT EXISTS instead of NOT IN On a 97k-track library the tag purge in the scanner GC ran for many minutes: PostgreSQL re-evaluates a NOT IN subquery of ~1M tag references per tag row. A materialized set with NOT EXISTS takes 1.4 s locally versus 55 s for NOT IN at that scale; work_mem made no difference. --- persistence/tag_repository.go | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/persistence/tag_repository.go b/persistence/tag_repository.go index 67c4aa4d9..dec9434d8 100644 --- a/persistence/tag_repository.go +++ b/persistence/tag_repository.go @@ -82,16 +82,20 @@ func (r *tagRepository) GetAll(name model.TagName, options ...model.QueryOptions } func (r *tagRepository) purgeUnused() error { + // NOT EXISTS over a materialized set: NOT IN re-scans the ~1M-row subquery per tag (40x slower). del := Delete(r.tableName).Where(` - id not in (select elem->>'id' - from album - cross join lateral jsonb_each(coalesce(album.tags, '{}'::jsonb)) as e(k, arr) - cross join lateral jsonb_array_elements(e.arr) as elem - UNION - select elem->>'id' - from media_file - cross join lateral jsonb_each(coalesce(media_file.tags, '{}'::jsonb)) as e(k, arr) - cross join lateral jsonb_array_elements(e.arr) as elem) + not exists ( + with used(id) as materialized ( + select elem->>'id' + from album + cross join lateral jsonb_each(coalesce(album.tags, '{}'::jsonb)) as e(k, arr) + cross join lateral jsonb_array_elements(e.arr) as elem + union + select elem->>'id' + from media_file + cross join lateral jsonb_each(coalesce(media_file.tags, '{}'::jsonb)) as e(k, arr) + cross join lateral jsonb_array_elements(e.arr) as elem) + select 1 from used where used.id = ` + r.tableName + `.id) `) c, err := r.executeSQL(del) if err != nil {