56 lines
2.4 KiB
TypeScript
56 lines
2.4 KiB
TypeScript
import { Pool } from 'pg';
|
|
import type { AssessorRepository } from '../../src/persistence/assessor-repository';
|
|
import { reportReadPortFrom, type ReportReadPort } from '../../src/reporting/report-read-port';
|
|
import { PostgresAssessorRepository } from '../postgres/postgres-assessor-repository';
|
|
import { repositoryConnectionString, sslConfiguration, type RepositoryEnvironment } from '../repository-factory';
|
|
|
|
/** Session option that makes PostgreSQL reject every write of this pool. */
|
|
export const REPORT_READ_ONLY_SESSION_OPTIONS = '-c default_transaction_read_only=on';
|
|
|
|
export interface ReportReadRuntime {
|
|
port: ReportReadPort;
|
|
readOnlyDatabase: boolean;
|
|
close(): Promise<void>;
|
|
}
|
|
|
|
/**
|
|
* The Report Engine's own, separate database reader. It uses its own small pool on the same
|
|
* canonical database, whose sessions are read-only, runs no migrations and starts no rebuild.
|
|
*/
|
|
export async function createPostgresReportReadRuntime(environment: RepositoryEnvironment): Promise<ReportReadRuntime> {
|
|
const connectionString = repositoryConnectionString(environment);
|
|
if (!connectionString) throw new Error('Der Report-Leser benötigt die bestehende PostgreSQL-Verbindung.');
|
|
const hostname = new URL(connectionString).hostname;
|
|
const sslMode = environment.ASSESSOR_DATABASE_SSL?.trim()
|
|
|| (['localhost', '127.0.0.1', '[::1]'].includes(hostname) ? 'disable' : 'require');
|
|
const pool = new Pool({
|
|
connectionString,
|
|
ssl: sslConfiguration(sslMode),
|
|
max: 2,
|
|
connectionTimeoutMillis: 5000,
|
|
idleTimeoutMillis: 30000,
|
|
application_name: 'assessor-report-reader',
|
|
options: REPORT_READ_ONLY_SESSION_OPTIONS
|
|
});
|
|
try {
|
|
const repository = await PostgresAssessorRepository.openReadOnly(pool);
|
|
const port = reportReadPortFrom(repository, async () => {
|
|
const result = await pool.query<{ revision: string }>('SELECT revision FROM assessor_repository_state WHERE singleton = true');
|
|
return Number(result.rows[0]?.revision ?? -1);
|
|
});
|
|
return { port, readOnlyDatabase: true, close: () => pool.end() };
|
|
} catch (error) {
|
|
await pool.end().catch(() => undefined);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/** In-memory operation has no database; the port only narrows the running repository to reads. */
|
|
export function memoryReportReadRuntime(repository: AssessorRepository): ReportReadRuntime {
|
|
return {
|
|
port: reportReadPortFrom(repository, async () => repository.revision),
|
|
readOnlyDatabase: false,
|
|
async close() {}
|
|
};
|
|
}
|