/
githubmirror
/
strapi
Обзор
Документация
Войти
/
githubmirror
/
strapi
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
develop
packages/core/database/src/transaction-context.ts
117 строк
3 KB
Ben Irvin
fix(database): avoid double finalising completed transactions (#26122)
05 июн 2026, 14:31
Не верифицирован
05 июн 2026, 14:31
d1cd432
Код
Авторство
О чём код?
import { AsyncLocalStorage } from 'node:async_hooks'; import { Knex } from 'knex'; /** * After the user calls the Knex transactor’s `commit` or `rollback` (e.g. via * the callback object returned from `Database#transaction`, or after the * container’s promise has settled in Knex), a second finalisation is invalid * and can throw (e.g. "Transaction query already complete"). Knex exposes * `isCompleted()` for this; optional for mocks that omit it. */ const isTransactorComplete = (trx: Knex.Transaction) => { const t = trx as Knex.Transaction & { isCompleted?: () => boolean }; return typeof t.isCompleted === 'function' && t.isCompleted(); }; export type Callback = (...args: any[]) => Promise<any> | any; export interface TransactionObject { commit: () => Promise<void>; rollback: () => Promise<void>; get: () => Knex.Transaction; } export interface Store { trx: Knex.Transaction | null; commitCallbacks: Callback[]; rollbackCallbacks: Callback[]; } const storage = new AsyncLocalStorage<Store>(); const transactionCtx = { async run<TCallback extends Callback>(trx: Knex.Transaction, cb: TCallback) { const store = storage.getStore(); return storage.run<ReturnType<TCallback>, void[]>( { trx, // Fill with existing callbacks if nesting transactions commitCallbacks: store?.commitCallbacks || [], rollbackCallbacks: store?.rollbackCallbacks || [], }, cb ); }, get() { const store = storage.getStore(); return store?.trx; }, async commit(trx: Knex.Transaction) { const store = storage.getStore(); if (isTransactorComplete(trx)) { if (store?.trx) { store.trx = null; } return; } // Clear transaction from store if (store?.trx) { store.trx = null; } // Commit transaction await trx.commit(); if (!store?.commitCallbacks.length) { return; } // Run callbacks store.commitCallbacks.forEach((cb) => cb()); store.commitCallbacks = []; }, async rollback(trx: Knex.Transaction) { const store = storage.getStore(); if (isTransactorComplete(trx)) { if (store?.trx) { store.trx = null; } return; } // Clear transaction from store if (store?.trx) { store.trx = null; } // Rollback transaction await trx.rollback(); if (!store?.rollbackCallbacks.length) { return; } // Run callbacks store.rollbackCallbacks.forEach((cb) => cb()); store.rollbackCallbacks = []; }, onCommit(cb: Callback) { const store = storage.getStore(); if (store?.commitCallbacks) { store.commitCallbacks.push(cb); } }, onRollback(cb: Callback) { const store = storage.getStore(); if (store?.rollbackCallbacks) { store.rollbackCallbacks.push(cb); } }, }; export { transactionCtx };