Files
accounted/tests/pg/sie-rpc-statement-timeout.pg.test.ts
T
Jakob Wennberg aa6d42a167 fix(import): raise statement_timeout on import_sie_journal_entries to 290s (#1101)
A production Fortnox migration failed with "SIE-verifikationer kunde
inte importeras atomiskt: canceling statement due to statement
timeout": the atomic import RPC (20260712150000) inherits the 8s
authenticator statement_timeout, so any real-world multi-year SIE file
exceeds it and the whole import is cancelled and rolled back. Same
failure class as 20260629160100 (replace_sie_import / undo_sie_import);
same fix, a function-scoped statement_timeout of 290s sitting under the
calling routes' maxDuration = 300 ceiling.

Migration 20260721144311 is already applied to prod (proconfig verified
carrying statement_timeout=290s); the committed file is byte-identical
under the same version. The new pg-real ratchet pins the config on all
three SIE RPCs because CREATE OR REPLACE FUNCTION silently drops
ALTER FUNCTION settings.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 16:54:14 +02:00

43 lines
1.6 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { getPool } from './setup'
/**
* The SIE import/replace/undo RPCs each run an entire file's work in one
* statement, so each carries a function-scoped statement_timeout (migrations
* 20260629160100 and 20260721144311); without it they inherit the 8s
* authenticator timeout and any real-world multi-year migration is cancelled
* with "canceling statement due to statement timeout".
*
* CREATE OR REPLACE FUNCTION silently drops settings attached via
* ALTER FUNCTION ... SET, so a future redefinition of any of these functions
* would revive the bug unless the SET is carried along. This ratchet runs
* after full migration replay in CI and pins the config.
*/
const TIMEOUT_PROTECTED_FUNCTIONS = [
'import_sie_journal_entries',
'replace_sie_import',
'undo_sie_import',
]
describe('SIE RPC statement_timeout config', () => {
it.each(TIMEOUT_PROTECTED_FUNCTIONS)(
'%s carries a function-scoped statement_timeout of 290s',
async (functionName) => {
const { rows } = await getPool().query<{ proconfig: string[] | null }>(
`SELECT p.proconfig
FROM pg_proc p
JOIN pg_namespace n ON n.oid = p.pronamespace
WHERE n.nspname = 'public' AND p.proname = $1`,
[functionName],
)
expect(rows, `${functionName} should exist exactly once in public`).toHaveLength(1)
expect(
rows[0].proconfig ?? [],
`${functionName} lost its statement_timeout: re-add "SET statement_timeout = '290s'" ` +
'to the CREATE OR REPLACE FUNCTION statement that redefined it',
).toContain('statement_timeout=290s')
},
)
})