directus

Форк
0
/
transaction.ts 
57 строк · 1.9 Кб
1
import { type Knex } from 'knex';
2
import { getDatabaseClient } from '../database/index.js';
3
import { useLogger } from '../logger.js';
4

5
/**
6
 * Execute the given handler within the current transaction or a newly created one
7
 * if the current knex state isn't a transaction yet.
8
 *
9
 * Can be used to ensure the handler is run within a transaction,
10
 * while preventing nested transactions.
11
 */
12
export const transaction = async <T = unknown>(knex: Knex, handler: (knex: Knex) => Promise<T>): Promise<T> => {
13
	if (knex.isTransaction) {
14
		return handler(knex);
15
	} else {
16
		try {
17
			return await knex.transaction((trx) => handler(trx));
18
		} catch (error: any) {
19
			const client = getDatabaseClient(knex);
20

21
			/**
22
			 * This error code indicates that the transaction failed due to another
23
			 * concurrent or recent transaction attempting to write to the same data.
24
			 * This can usually be solved by restarting the transaction on client-side
25
			 * after a short delay, so that it is executed against the latest state.
26
			 *
27
			 * @link https://www.cockroachlabs.com/docs/stable/transaction-retry-error-reference
28
			 */
29
			const COCKROACH_RETRY_ERROR_CODE = '40001';
30

31
			if (client !== 'cockroachdb' || error?.code !== COCKROACH_RETRY_ERROR_CODE) throw error;
32

33
			const MAX_ATTEMPTS = 3;
34
			const BASE_DELAY = 100;
35

36
			const logger = useLogger();
37

38
			for (let attempt = 0; attempt < MAX_ATTEMPTS; ++attempt) {
39
				const delay = 2 ** attempt * BASE_DELAY;
40

41
				await new Promise((resolve) => setTimeout(resolve, delay));
42

43
				logger.trace(`Restarting failed transaction (attempt ${attempt + 1}/${MAX_ATTEMPTS})`);
44

45
				try {
46
					return await knex.transaction((trx) => handler(trx));
47
				} catch (error: any) {
48
					if (error?.code !== COCKROACH_RETRY_ERROR_CODE) throw error;
49
				}
50
			}
51

52
			/** Initial execution + additional attempts */
53
			const attempts = 1 + MAX_ATTEMPTS;
54
			throw new Error(`Transaction failed after ${attempts} attempts`, { cause: error });
55
		}
56
	}
57
};
58

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.