From c0c664b3273ae1abd901c48188c8e44ff86d7061 Mon Sep 17 00:00:00 2001 From: Diogo Correia Date: Fri, 24 Oct 2025 18:59:37 +0100 Subject: [PATCH 1/2] nixos/immich: reindex VectorChord indexes on update VectorChord requires its indexes to be reindexed when the extension is updated. [1] This commit adds functionality to save the current version of the extension before performing an update, and then compare it with the updated version to decide whether it should reindex Immich's indexes. This complexity is needed to avoid reindexing every time PostgreSQL is started, as it is an expensive operation that would slow down startup. [1]: https://docs.immich.app/administration/postgres-standalone/#updating-vectorchord --- nixos/modules/services/web-apps/immich.nix | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/nixos/modules/services/web-apps/immich.nix b/nixos/modules/services/web-apps/immich.nix index 21a92a5a627b..23017629a329 100644 --- a/nixos/modules/services/web-apps/immich.nix +++ b/nixos/modules/services/web-apps/immich.nix @@ -323,7 +323,11 @@ in "vchord" ]; sqlFile = pkgs.writeText "immich-pgvectors-setup.sql" ( + # save previous version of vectorchord to trigger reindex on update + lib.optionalString cfg.database.enableVectorChord '' + SELECT COALESCE(installed_version, ''') AS vchord_version_before FROM pg_available_extensions WHERE name = 'vchord' \gset '' + + '' ${lib.concatMapStringsSep "\n" (ext: "CREATE EXTENSION IF NOT EXISTS \"${ext}\";") extensions} ${lib.concatMapStringsSep "\n" (ext: "ALTER EXTENSION \"${ext}\" UPDATE;") extensions} ALTER SCHEMA public OWNER TO ${cfg.database.user}; @@ -332,6 +336,17 @@ in ALTER SCHEMA vectors OWNER TO ${cfg.database.user}; GRANT SELECT ON TABLE pg_vector_index_stat TO ${cfg.database.user}; '' + # trigger reindex if vectorchord updates + # https://docs.immich.app/administration/postgres-standalone/#updating-vectorchord + + lib.optionalString cfg.database.enableVectorChord '' + SELECT COALESCE(installed_version, ''') AS vchord_version_after FROM pg_available_extensions WHERE name = 'vchord' \gset + + SELECT (:'vchord_version_before' != ''' AND :'vchord_version_before' != :'vchord_version_after') AS has_vchord_updated \gset + \if :has_vchord_updated + REINDEX INDEX face_index; + REINDEX INDEX clip_index; + \endif + '' ); in [ From 007307973c7183cc2d529b83b1a1e81e14b85ebe Mon Sep 17 00:00:00 2001 From: Diogo Correia Date: Sat, 25 Oct 2025 01:45:40 +0100 Subject: [PATCH 2/2] nixos/tests/immich-vectorchord-reindex: init --- nixos/tests/all-tests.nix | 1 + .../web-apps/immich-vectorchord-reindex.nix | 74 +++++++++++++++++++ pkgs/by-name/im/immich/package.nix | 2 +- 3 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 nixos/tests/web-apps/immich-vectorchord-reindex.nix diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 3af4002a6aa2..198cbba66966 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -733,6 +733,7 @@ in immich = runTest ./web-apps/immich.nix; immich-public-proxy = runTest ./web-apps/immich-public-proxy.nix; immich-vectorchord-migration = runTest ./web-apps/immich-vectorchord-migration.nix; + immich-vectorchord-reindex = runTest ./web-apps/immich-vectorchord-reindex.nix; incron = runTest ./incron.nix; incus = pkgs.recurseIntoAttrs ( handleTest ./incus { diff --git a/nixos/tests/web-apps/immich-vectorchord-reindex.nix b/nixos/tests/web-apps/immich-vectorchord-reindex.nix new file mode 100644 index 000000000000..65cd403feca7 --- /dev/null +++ b/nixos/tests/web-apps/immich-vectorchord-reindex.nix @@ -0,0 +1,74 @@ +{ ... }: +{ + name = "immich-vectorchord-reindex"; + + nodes.machine = + { lib, pkgs, ... }: + { + # These tests need a little more juice + virtualisation = { + cores = 2; + memorySize = 2048; + diskSize = 4096; + }; + + services.immich = { + enable = true; + environment.IMMICH_LOG_LEVEL = "verbose"; + }; + + services.postgresql.extensions = lib.mkForce (ps: [ + ps.pgvector + # pin vectorchord to an older version simulate version bump + (ps.vectorchord.overrideAttrs (prevAttrs': rec { + version = "0.5.2"; + src = pkgs.fetchFromGitHub { + owner = "tensorchord"; + repo = "vectorchord"; + tag = version; + hash = "sha256-KGwiY5t1ivFiYex3D20y3sdiu3CT9LCDd2fPnRE56jM="; + }; + + cargoDeps = pkgs.rustPlatform.fetchCargoVendor { + inherit src; + hash = "sha256-Vn3c/xuUpQzERJ74I0qbvufTZtW3goefPa5B/nOUO48="; + }; + })) + ]); + + specialisation."immich-vectorchord-upgraded".configuration = { + # needs to be lower than mkForce, otherwise it does not get rid of the previous version + services.postgresql.extensions = lib.mkOverride 40 (ps: [ + ps.pgvector + ps.vectorchord + ]); + }; + + }; + + testScript = + { nodes, ... }: + let + specBase = "${nodes.machine.system.build.toplevel}/specialisation"; + vectorchordUpgraded = "${specBase}/immich-vectorchord-upgraded"; + in + '' + def immich_works(): + machine.wait_for_unit("immich-server.service") + + machine.wait_for_open_port(2283) # Server + machine.wait_for_open_port(3003) # Machine learning + machine.succeed("curl --fail http://localhost:2283/") + + immich_works() + + machine.succeed("${vectorchordUpgraded}/bin/switch-to-configuration test") + + # just tests that reindexing is triggered + machine.wait_until_succeeds( + "journalctl -o cat -u postgresql-setup.service | grep 'REINDEX'" + ) + + immich_works() + ''; +} diff --git a/pkgs/by-name/im/immich/package.nix b/pkgs/by-name/im/immich/package.nix index 9ebb052dbb2f..a354d5dbbe5d 100644 --- a/pkgs/by-name/im/immich/package.nix +++ b/pkgs/by-name/im/immich/package.nix @@ -251,7 +251,7 @@ stdenv.mkDerivation { passthru = { tests = { - inherit (nixosTests) immich immich-vectorchord-migration; + inherit (nixosTests) immich immich-vectorchord-migration immich-vectorchord-reindex; }; machine-learning = immich-machine-learning;