refactor: database repository (#16593)

* refactor: database repository

* fix error reindex check

* chore: remove WIP code

---------

Co-authored-by: mertalev <101130780+mertalev@users.noreply.github.com>
This commit is contained in:
Jason Rasmussen
2025-03-06 13:33:24 -05:00
committed by GitHub
parent fe931faf17
commit 2cdbb0a37c
17 changed files with 197 additions and 315 deletions

View File

@@ -384,50 +384,4 @@ describe(DatabaseService.name, () => {
expect(mocks.database.runMigrations).not.toHaveBeenCalled();
});
});
describe('handleConnectionError', () => {
beforeAll(() => {
vi.useFakeTimers();
});
afterAll(() => {
vi.useRealTimers();
});
it('should not override interval', () => {
sut.handleConnectionError(new Error('Error'));
expect(mocks.logger.error).toHaveBeenCalled();
sut.handleConnectionError(new Error('foo'));
expect(mocks.logger.error).toHaveBeenCalledTimes(1);
});
it('should reconnect when interval elapses', async () => {
mocks.database.reconnect.mockResolvedValue(true);
sut.handleConnectionError(new Error('error'));
await vi.advanceTimersByTimeAsync(5000);
expect(mocks.database.reconnect).toHaveBeenCalledTimes(1);
expect(mocks.logger.log).toHaveBeenCalledWith('Database reconnected');
await vi.advanceTimersByTimeAsync(5000);
expect(mocks.database.reconnect).toHaveBeenCalledTimes(1);
});
it('should try again when reconnection fails', async () => {
mocks.database.reconnect.mockResolvedValueOnce(false);
sut.handleConnectionError(new Error('error'));
await vi.advanceTimersByTimeAsync(5000);
expect(mocks.database.reconnect).toHaveBeenCalledTimes(1);
expect(mocks.logger.warn).toHaveBeenCalledWith(expect.stringContaining('Database connection failed'));
mocks.database.reconnect.mockResolvedValueOnce(true);
await vi.advanceTimersByTimeAsync(5000);
expect(mocks.database.reconnect).toHaveBeenCalledTimes(2);
expect(mocks.logger.log).toHaveBeenCalledWith('Database reconnected');
});
});
});

View File

@@ -1,5 +1,4 @@
import { Injectable } from '@nestjs/common';
import { Duration } from 'luxon';
import semver from 'semver';
import { EXTENSION_NAMES } from 'src/constants';
import { OnEvent } from 'src/decorators';
@@ -54,12 +53,8 @@ const messages = {
If ${name} ${installedVersion} is compatible with Immich, please ensure the Postgres instance has this available.`,
};
const RETRY_DURATION = Duration.fromObject({ seconds: 5 });
@Injectable()
export class DatabaseService extends BaseService {
private reconnection?: NodeJS.Timeout;
@OnEvent({ name: 'app.bootstrap', priority: BootstrapEventPriority.DatabaseService })
async onBootstrap() {
const version = await this.databaseRepository.getPostgresVersion();
@@ -108,30 +103,9 @@ export class DatabaseService extends BaseService {
if (!database.skipMigrations) {
await this.databaseRepository.runMigrations();
}
this.databaseRepository.init();
});
}
handleConnectionError(error: Error) {
if (this.reconnection) {
return;
}
this.logger.error(`Database disconnected: ${error}`);
this.reconnection = setInterval(() => void this.reconnect(), RETRY_DURATION.toMillis());
}
private async reconnect() {
const isConnected = await this.databaseRepository.reconnect();
if (isConnected) {
this.logger.log('Database reconnected');
clearInterval(this.reconnection);
delete this.reconnection;
} else {
this.logger.warn(`Database connection failed, retrying in ${RETRY_DURATION.toHuman()}`);
}
}
private async createExtension(extension: DatabaseExtension) {
try {
await this.databaseRepository.createExtension(extension);