2023-12-21 17:06:26 +01:00
|
|
|
import { ImmichLogger } from '@app/infra/logger';
|
|
|
|
import { Inject, Injectable } from '@nestjs/common';
|
|
|
|
import { QueryFailedError } from 'typeorm';
|
|
|
|
import { Version } from '../domain.constant';
|
|
|
|
import { DatabaseExtension, IDatabaseRepository } from '../repositories';
|
|
|
|
|
|
|
|
@Injectable()
|
|
|
|
export class DatabaseService {
|
|
|
|
private logger = new ImmichLogger(DatabaseService.name);
|
2024-01-07 01:24:09 +01:00
|
|
|
minPostgresVersion = 14;
|
2023-12-21 17:06:26 +01:00
|
|
|
minVectorsVersion = new Version(0, 1, 1);
|
|
|
|
maxVectorsVersion = new Version(0, 1, 11);
|
|
|
|
|
|
|
|
constructor(@Inject(IDatabaseRepository) private databaseRepository: IDatabaseRepository) {}
|
|
|
|
|
|
|
|
async init() {
|
2024-01-07 01:24:09 +01:00
|
|
|
await this.assertPostgresql();
|
2023-12-21 17:06:26 +01:00
|
|
|
await this.createVectors();
|
|
|
|
await this.assertVectors();
|
|
|
|
await this.databaseRepository.runMigrations();
|
|
|
|
}
|
|
|
|
|
|
|
|
private async assertVectors() {
|
|
|
|
const version = await this.databaseRepository.getExtensionVersion(DatabaseExtension.VECTORS);
|
|
|
|
if (version == null) {
|
|
|
|
throw new Error('Unexpected: The pgvecto.rs extension is not installed.');
|
|
|
|
}
|
|
|
|
|
|
|
|
const image = await this.getVectorsImage();
|
|
|
|
const suggestion = image ? `, such as with the docker image '${image}'` : '';
|
|
|
|
|
|
|
|
if (version.isEqual(new Version(0, 0, 0))) {
|
|
|
|
throw new Error(
|
|
|
|
`The pgvecto.rs extension version is ${version}, which means it is a nightly release.` +
|
|
|
|
`Please run 'DROP EXTENSION IF EXISTS vectors' and switch to a release version${suggestion}.`,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (version.isNewerThan(this.maxVectorsVersion)) {
|
|
|
|
throw new Error(`
|
|
|
|
The pgvecto.rs extension version is ${version} instead of ${this.maxVectorsVersion}.
|
|
|
|
Please run 'DROP EXTENSION IF EXISTS vectors' and switch to ${this.maxVectorsVersion}${suggestion}.`);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (version.isOlderThan(this.minVectorsVersion)) {
|
|
|
|
throw new Error(`
|
|
|
|
The pgvecto.rs extension version is ${version}, which is older than the minimum supported version ${this.minVectorsVersion}.
|
|
|
|
Please upgrade to this version or later${suggestion}.`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private async createVectors() {
|
|
|
|
await this.databaseRepository.createExtension(DatabaseExtension.VECTORS).catch(async (err: QueryFailedError) => {
|
|
|
|
const image = await this.getVectorsImage();
|
|
|
|
this.logger.fatal(`
|
|
|
|
Failed to create pgvecto.rs extension.
|
|
|
|
If you have not updated your Postgres instance to a docker image that supports pgvecto.rs (such as '${image}'), please do so.
|
|
|
|
See the v1.91.0 release notes for more info: https://github.com/immich-app/immich/releases/tag/v1.91.0'
|
|
|
|
`);
|
|
|
|
throw err;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
private async getVectorsImage() {
|
|
|
|
const { major } = await this.databaseRepository.getPostgresVersion();
|
|
|
|
if (![14, 15, 16].includes(major)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return `tensorchord/pgvecto-rs:pg${major}-v${this.maxVectorsVersion}`;
|
|
|
|
}
|
2024-01-07 01:24:09 +01:00
|
|
|
|
|
|
|
private async assertPostgresql() {
|
|
|
|
const { major } = await this.databaseRepository.getPostgresVersion();
|
|
|
|
if (major < this.minPostgresVersion) {
|
|
|
|
throw new Error(`
|
|
|
|
The PostgreSQL version is ${major}, which is older than the minimum supported version ${this.minPostgresVersion}.
|
|
|
|
Please upgrade to this version or later.`);
|
|
|
|
}
|
|
|
|
}
|
2023-12-21 17:06:26 +01:00
|
|
|
}
|