Skip to content

feat: vectorchord - #18042

Merged
alextran1502 merged 23 commits into
mainfrom
feat/vectorchord
May 20, 2025
Merged

alextran1502 merged 23 commits into
mainfrom
feat/vectorchord

Conversation

@mertalev

@mertalev mertalev commented May 2, 2025 •

Copy link
Copy Markdown
Member

Description

This PR introduces support for VectorChord, the successor to pgvecto.rs. It's capable of higher QPS at the same recall, much faster indexing, lower RAM usage as well as better handling of inserts, updates and deletes. In addition, it is more integrated into Postgres using native indexing workers and memory management, and can coexist better with pgvector (which it comes bundled with).

The PR does not remove support for pgvecto.rs, meaning that the existing 0.2.0 image will continue to work, but will be deprecated in a future release. The correct extension to use (vchord, pgvecto.rs or pgvector) is autodetected at startup, and can be overridden with the DB_VECTOR_EXTENSION environmental variable. Switching between supported extensions is possible in this way as long as both extensions exist in the DB image. The intention is to make the migration as smooth as possible while giving both admins and build maintainers time to transition.

However, upgrading directly from releases using TypeSense or pgvecto.rs 0.1.11 (i.e. releases before 1.95.0) is no longer supported as of this PR. However, upgrading directly from releases below 1.107.0 (released 10 months ago) is no longer supported as of this PR, as it assumes the face_search relation exists. Users on these old versions should upgrade to the release before this commit before upgrading to the latest.

A quirk of VectorChord inherent to all IVF indices is that the optimal index parameters depend on the number of vectors. Since this is not something we know beforehand, there are checks in place to ensure roughly optimal settings at startup. This only takes place if the current settings are far from ideal so as to avoid frequent reindexing. The settings applied in this PR were tested for facial recognition with 15k vectors, 130k and 166k vectors to achieve very similar results to brute-force clustering. Smart search recall was tested at 1 million and 3.3 million vectors, but could benefit from similar testing with duplicate detection.

To do:

  • It may be beneficial to do the parameter checks outside of startup as well in the case of a large import without restart
  • The number of probes for a query needs to be set as well. This number depends on the number of lists, so this should be computed whenever it checks for lists or reindexes with a different number of lists
  • The provided migration images only target amd64 and are for VectorChord 0.2.2 while 0.3.0 is out already, so we may need to build our own images even without targeting pg17
  • We need instructions on how to do this migration in a non-superuser context
  • Test upgrading immich from certain versions with pgvecto.rs 0.2.0, 0.2.1 and 0.3.0 (tested 1.107 with 0.2.0 and 0.2.1, 1.120 with 0.3.0, and 1.132.3 with 0.2.0 and 0.3.0).

An informal facial recognition comparison with 166495 faces (min detection=0.2, min faces=8, max distance=0.4):

People Clustered Faces Faces/s Index time RAM (startup) RAM (active) Search CPU (%)
vectors 1328 61931 321 28m 472 MiB 1090 MiB 65-70
vchord 1264 66939 481 29s 136 MiB 645 MiB 55-60

The selected VectorChord parameters are extremely biased in favor of quality and it still substantially outperforms pgvecto.rs in every metric.

@bo0tzz

bo0tzz commented May 3, 2025

Copy link
Copy Markdown
Member

Is there a migration path without having both extensions available at the same time?

@mertalev

mertalev commented May 3, 2025

Copy link
Copy Markdown
Member Author

It should be possible to remove the references to the old extension manually with some queries before starting immich with the new extension.

@mertalev
mertalev force-pushed the feat/vectorchord branch 3 times, most recently from b024fbb to 878a80e Compare May 9, 2025 22:58
@@ -1,13 +1,12 @@
import { ConfigRepository } from 'src/repositories/config.repository';
import { getVectorExtension } from 'src/repositories/database.repository';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you changing this? It's not great to keep needing to modify past migrations.

@mertalev mertalev May 13, 2025 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, we're adding another extension so it's past the point of whether to modify the migration. The change is superficial, though, and won't change the actual behavior of the migration for existing extensions.

There is an option to instead remove TypeORM migrations as of 1.107 since we're setting that as a minimum, and it happens to include all the TypeORM vector migrations.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lol that's an option I guess. It would be nice to remove all of them at the same time. I guess it helps lessen the chance of bugs if that code path won't even be executed.

config: DatabaseConnectionParams;
skipMigrations: boolean;
vectorExtension: VectorExtension;
vectorExtension?: VectorExtension;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this no longer required?

import { vectorIndexQuery } from 'src/utils/database';
import { isValidInteger } from 'src/validation';
import { DataSource } from 'typeorm';
import { DataSource, QueryRunner } from 'typeorm';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please stop adding in more typeorm imports...

@github-actions github-actions Bot added the documentation Improvements or additions to documentation label May 12, 2025
@mertalev
mertalev force-pushed the feat/vectorchord branch from ddbf125 to 6126ac7 Compare May 13, 2025 00:57
@mertalev
mertalev marked this pull request as ready for review May 13, 2025 14:34
Comment thread docs/docs/administration/postgres-standalone.md Outdated
Comment thread server/src/constants.ts Outdated
Comment thread server/src/repositories/database.repository.ts Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This repository is quite complex by now. Is that really necessary?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can't get much simpler without loss of functionality or robustness. The only optional parts are the auto-detection and correcting missing indices and embedding columns. The former is convenient, both for admins and for development, and the latter came up a lot during the 0.2.0 migration and resulted in people running their own SQL commands and making things worse. The more robust this code is, the less support burden we will have.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I definitely wouldn't advocate for losing robustness. Wondering if we need to support this broad variety of extensions though.

Comment thread server/src/repositories/search.repository.ts
@alextran1502
alextran1502 merged commit 0d773af into main May 20, 2025
@alextran1502
alextran1502 deleted the feat/vectorchord branch May 20, 2025 13:36
@mmomjian

mmomjian commented May 20, 2025 •

Copy link
Copy Markdown
Collaborator

@mertalev Do extensions need to be dropped before "6. Remove the vectors.so entry from the shared_preload_libraries setting" and then removing the package from the system? I'm not familiar with extension uninstallation

@mertalev

Copy link
Copy Markdown
Member Author

I didn't need to for the migration, but it's probably still a good idea to drop the old extension. I imagine it could cause issues with restoring from backups if it still exists. Maybe Immich should try to drop the extension too.

@mmomjian

Copy link
Copy Markdown
Collaborator

I think we probably should consider doing that, if not with this release in the near future if we want to wait for safety.

savely-krasovsky pushed a commit to savely-krasovsky/immich that referenced this pull request Jun 8, 2025
* wip

auto-detect available extensions

auto-recovery, fix reindexing check

use original image for ml

* set probes

* update image for sql checker

update images for gha

* cascade

* fix new instance

* accurate dummy vector

* simplify dummy

* preexisiting pg docs

* handle different db name

* maybe fix sql generation

* revert refreshfaces sql change

* redundant switch

* outdated message

* update docker compose files

* Update docs/docs/administration/postgres-standalone.md

Co-authored-by: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com>

* tighten range

* avoid always printing "vector reindexing complete"

* remove nesting

* use new images

* add vchord to unit tests

* debug e2e image

* mention 1.107.2 in startup error

* support new vchord versions

---------

Co-authored-by: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com>
await sql`ALTER DATABASE ${dbName} SET vchordrq.prewarm_dim = '512,640,768,1024,1152,1536'`.execute(this.db);
await sql`SET vchordrq.prewarm_dim = '512,640,768,1024,1152,1536'`.execute(this.db);
await sql`ALTER DATABASE ${dbName} SET vchordrq.probes = 1`.execute(this.db);
await sql`SET vchordrq.probes = 1`.execute(this.db);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't the version without ALTER DATABASE redundant? It also requires superuser privileges.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It isn't because the parameters in alter database only take effect for new connections. You need to run the normal set version to make it apply to the current connection as well.

It shouldn't require superuser permission though - even the alter database command only requires db ownership, and the session-level version shouldn't require even that.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. I was erroneously assuming that SET means ALTER SYSTEM SET.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changelog:breaking-change documentation Improvements or additions to documentation 🗄️server

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants