Creating a partition deadlocked with file inserts 11 times in an hour, and each failure ended the GC sweep before its deletes. The parent is now locked first (lock_timeout 10 s), four partitions are kept ahead, and an error is logged while the sweep goes on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
52 lines
2.7 KiB
TypeScript
52 lines
2.7 KiB
TypeScript
import { test } from '@japa/runner'
|
|
import db from '@adonisjs/lucid/services/db'
|
|
import testUtils from '@adonisjs/core/services/test_utils'
|
|
import FileStore from '#services/FileStore'
|
|
import FilePartitions, { FILE_PARTITION_SIZE } from '#services/FilePartitions'
|
|
|
|
const file = (fileKey: string, name: string) => {
|
|
const now = new Date()
|
|
return { file_key: fileKey, filename: name, poster: 'p', date: now.getTime(), parts: 1, size: 1, groups: '[]', segments: [], created_at: now, updated_at: now }
|
|
}
|
|
|
|
test.group('files partitions', (group) => {
|
|
group.each.setup(() => testUtils.db().withGlobalTransaction())
|
|
// Sequences ignore rollbacks: put files_id_seq back after each test.
|
|
group.each.setup(async () => {
|
|
const { rows } = await db.rawQuery(`SELECT last_value, is_called FROM files_id_seq`)
|
|
return async () => {
|
|
await db.rawQuery(`SELECT setval('files_id_seq', ?, ?)`, [rows[0].last_value, rows[0].is_called])
|
|
}
|
|
})
|
|
|
|
test('a file_key already in an older partition is not inserted again', async ({ assert }) => {
|
|
await db.rawQuery(`SELECT setval('files_id_seq', 500000, false)`)
|
|
const [first] = await FileStore.insert([file('k'.repeat(40), 'old.bin')])
|
|
// The next ids fall in the second partition, whose own unique index doesn't know the key.
|
|
await db.rawQuery(`SELECT setval('files_id_seq', 1000000, false)`)
|
|
const again = await FileStore.insert([file('k'.repeat(40), 'again.bin'), file('n'.repeat(40), 'new.bin'), file('n'.repeat(40), 'twice.bin')])
|
|
assert.deepEqual(again.map((f) => f.filename), ['new.bin'])
|
|
assert.isAtLeast(again[0].id, 1000000)
|
|
assert.equal(await db.from('files').where('file_key', 'k'.repeat(40)).count('* as n').then((r) => Number(r[0].n)), 1)
|
|
assert.isBelow(first.id, 1000000)
|
|
})
|
|
|
|
test('partitions are kept ready ahead of the newest id', async ({ assert }) => {
|
|
const upper = async () => {
|
|
const { rows } = await db.rawQuery(
|
|
`SELECT max((regexp_match(pg_get_expr(c.relpartbound, c.oid), 'TO \\(''{0,1}(\\d+)''{0,1}\\)'))[1]::bigint) AS upper
|
|
FROM pg_inherits i JOIN pg_class c ON c.oid = i.inhrelid WHERE i.inhparent = 'files'::regclass`
|
|
)
|
|
return Number(rows[0].upper)
|
|
}
|
|
await FilePartitions.ensure()
|
|
const before = await upper()
|
|
assert.deepEqual(await FilePartitions.ensure(), [])
|
|
// Ids handed out up to just inside the four partitions kept ahead: one more is needed.
|
|
await db.rawQuery(`SELECT setval('files_id_seq', ?, false)`, [before - 4 * FILE_PARTITION_SIZE + 10])
|
|
await FileStore.insert([file('e'.repeat(40), 'late.bin')])
|
|
assert.deepEqual(await FilePartitions.ensure(), [`files_p${before}`])
|
|
assert.equal(await upper(), before + FILE_PARTITION_SIZE)
|
|
})
|
|
})
|