Skip to content

Commit

Permalink
feat(server): use pg_dumpall version that matches the database version (
Browse files Browse the repository at this point in the history
  • Loading branch information
zackpollard authored Nov 12, 2024
1 parent b9a0c3c commit dfa8a8a
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 6 deletions.
29 changes: 29 additions & 0 deletions server/src/services/backup.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ describe(BackupService.name, () => {
storageMock.unlink.mockResolvedValue();
systemMock.get.mockResolvedValue(systemConfigStub.backupEnabled);
storageMock.createWriteStream.mockReturnValue(new PassThrough());
databaseMock.getPostgresVersion.mockResolvedValue('14.3.2');
});
it('should run a database backup successfully', async () => {
const result = await sut.handleBackupDatabase();
Expand Down Expand Up @@ -196,5 +197,33 @@ describe(BackupService.name, () => {
expect(storageMock.unlink).toHaveBeenCalled();
expect(result).toBe(JobStatus.FAILED);
});
it.each`
postgresVersion | expectedVersion
${'14.6.4'} | ${14}
${'15.3.3'} | ${15}
${'16.4.2'} | ${16}
${'17.15.1'} | ${17}
`(
`should use pg_dumpall $expectedVersion with postgres version $postgresVersion`,
async ({ postgresVersion, expectedVersion }) => {
databaseMock.getPostgresVersion.mockResolvedValue(postgresVersion);
await sut.handleBackupDatabase();
expect(processMock.spawn).toHaveBeenCalledWith(
`/usr/lib/postgresql/${expectedVersion}/bin/pg_dumpall`,
expect.any(Array),
expect.any(Object),
);
},
);
it.each`
postgresVersion
${'13.99.99'}
${'18.0.0'}
`(`should fail if postgres version $postgresVersion is not supported`, async ({ postgresVersion }) => {
databaseMock.getPostgresVersion.mockResolvedValue(postgresVersion);
const result = await sut.handleBackupDatabase();
expect(processMock.spawn).not.toHaveBeenCalled();
expect(result).toBe(JobStatus.FAILED);
});
});
});
29 changes: 23 additions & 6 deletions server/src/services/backup.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Injectable } from '@nestjs/common';
import { default as path } from 'node:path';
import semver from 'semver';
import { StorageCore } from 'src/cores/storage.core';
import { OnEvent, OnJob } from 'src/decorators';
import { ImmichWorker, StorageFolder } from 'src/enum';
Expand Down Expand Up @@ -101,14 +102,30 @@ export class BackupService extends BaseService {
`immich-db-backup-${Date.now()}.sql.gz.tmp`,
);

const databaseVersion = await this.databaseRepository.getPostgresVersion();
const databaseSemver = semver.coerce(databaseVersion);
const databaseMajorVersion = databaseSemver?.major;
const databaseSupported = semver.satisfies(databaseVersion, '>=14.0.0 <18.0.0');

if (!databaseMajorVersion || !databaseSupported) {
this.logger.error(`Database Backup Failure: Unsupported PostgreSQL version: ${databaseVersion}`);
return JobStatus.FAILED;
}

this.logger.log(`Database Backup Starting. Database Version: ${databaseMajorVersion}`);

try {
await new Promise<void>((resolve, reject) => {
const pgdump = this.processRepository.spawn(`pg_dumpall`, databaseParams, {
env: {
PATH: process.env.PATH,
PGPASSWORD: isUrlConnection ? undefined : config.password,
const pgdump = this.processRepository.spawn(
`/usr/lib/postgresql/${databaseMajorVersion}/bin/pg_dumpall`,
databaseParams,
{
env: {
PATH: process.env.PATH,
PGPASSWORD: isUrlConnection ? undefined : config.password,
},
},
});
);

// NOTE: `--rsyncable` is only supported in GNU gzip
const gzip = this.processRepository.spawn(`gzip`, ['--rsyncable']);
Expand Down Expand Up @@ -169,7 +186,7 @@ export class BackupService extends BaseService {
return JobStatus.FAILED;
}

this.logger.debug(`Database Backup Success`);
this.logger.log(`Database Backup Success`);
await this.cleanupDatabaseBackups();
return JobStatus.SUCCESS;
}
Expand Down

0 comments on commit dfa8a8a

Please sign in to comment.