mirror of
https://github.com/immich-app/immich.git
synced 2025-01-21 00:52:43 -05:00
feat(server): use pg_dumpall version that matches the database version (#14083)
This commit is contained in:
parent
b9a0c3c79f
commit
dfa8a8a6e1
2 changed files with 52 additions and 6 deletions
|
@ -149,6 +149,7 @@ describe(BackupService.name, () => {
|
||||||
storageMock.unlink.mockResolvedValue();
|
storageMock.unlink.mockResolvedValue();
|
||||||
systemMock.get.mockResolvedValue(systemConfigStub.backupEnabled);
|
systemMock.get.mockResolvedValue(systemConfigStub.backupEnabled);
|
||||||
storageMock.createWriteStream.mockReturnValue(new PassThrough());
|
storageMock.createWriteStream.mockReturnValue(new PassThrough());
|
||||||
|
databaseMock.getPostgresVersion.mockResolvedValue('14.3.2');
|
||||||
});
|
});
|
||||||
it('should run a database backup successfully', async () => {
|
it('should run a database backup successfully', async () => {
|
||||||
const result = await sut.handleBackupDatabase();
|
const result = await sut.handleBackupDatabase();
|
||||||
|
@ -196,5 +197,33 @@ describe(BackupService.name, () => {
|
||||||
expect(storageMock.unlink).toHaveBeenCalled();
|
expect(storageMock.unlink).toHaveBeenCalled();
|
||||||
expect(result).toBe(JobStatus.FAILED);
|
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);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { default as path } from 'node:path';
|
import { default as path } from 'node:path';
|
||||||
|
import semver from 'semver';
|
||||||
import { StorageCore } from 'src/cores/storage.core';
|
import { StorageCore } from 'src/cores/storage.core';
|
||||||
import { OnEvent, OnJob } from 'src/decorators';
|
import { OnEvent, OnJob } from 'src/decorators';
|
||||||
import { ImmichWorker, StorageFolder } from 'src/enum';
|
import { ImmichWorker, StorageFolder } from 'src/enum';
|
||||||
|
@ -101,14 +102,30 @@ export class BackupService extends BaseService {
|
||||||
`immich-db-backup-${Date.now()}.sql.gz.tmp`,
|
`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 {
|
try {
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
const pgdump = this.processRepository.spawn(`pg_dumpall`, databaseParams, {
|
const pgdump = this.processRepository.spawn(
|
||||||
env: {
|
`/usr/lib/postgresql/${databaseMajorVersion}/bin/pg_dumpall`,
|
||||||
PATH: process.env.PATH,
|
databaseParams,
|
||||||
PGPASSWORD: isUrlConnection ? undefined : config.password,
|
{
|
||||||
|
env: {
|
||||||
|
PATH: process.env.PATH,
|
||||||
|
PGPASSWORD: isUrlConnection ? undefined : config.password,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
);
|
||||||
|
|
||||||
// NOTE: `--rsyncable` is only supported in GNU gzip
|
// NOTE: `--rsyncable` is only supported in GNU gzip
|
||||||
const gzip = this.processRepository.spawn(`gzip`, ['--rsyncable']);
|
const gzip = this.processRepository.spawn(`gzip`, ['--rsyncable']);
|
||||||
|
@ -169,7 +186,7 @@ export class BackupService extends BaseService {
|
||||||
return JobStatus.FAILED;
|
return JobStatus.FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.debug(`Database Backup Success`);
|
this.logger.log(`Database Backup Success`);
|
||||||
await this.cleanupDatabaseBackups();
|
await this.cleanupDatabaseBackups();
|
||||||
return JobStatus.SUCCESS;
|
return JobStatus.SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue