mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
chore: relocate backend into apps/api ahead of monorepo merge
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
/***********************************************************************************************************************************************************************
|
||||
* File Name: withTransactionRetry.util.js
|
||||
* Type of Program: Utility
|
||||
* Description: Runs a Sequelize transaction, automatically retrying on serialization_failure
|
||||
* (SQLSTATE 40001) — the transient "restart transaction" error CockroachDB (and
|
||||
* Postgres SERIALIZABLE) throw when concurrent transactions can't be safely
|
||||
* ordered. Without a retry, these surface as unhandled 500s on otherwise-correct
|
||||
* code the moment two requests touch the same row close together.
|
||||
* Author: Kenneth Obsequio (@lash0000)
|
||||
* Date Created: Jul. 17, 2026
|
||||
***********************************************************************************************************************************************************************
|
||||
* HOW TO USE:
|
||||
* const { withTransactionRetry } = require('../utils/withTransactionRetry.util');
|
||||
* const result = await withTransactionRetry(sequelize, async (t) => {
|
||||
* await Model.upsert({...}, { transaction: t });
|
||||
* return something;
|
||||
* });
|
||||
***********************************************************************************************************************************************************************/
|
||||
'use strict';
|
||||
|
||||
const SERIALIZATION_FAILURE = '40001';
|
||||
const MAX_RETRIES = 5;
|
||||
const BASE_DELAY_MS = 50;
|
||||
|
||||
function isSerializationFailure(err) {
|
||||
return err?.parent?.code === SERIALIZATION_FAILURE || err?.original?.code === SERIALIZATION_FAILURE;
|
||||
}
|
||||
|
||||
async function withTransactionRetry(sequelize, fn, { maxRetries = MAX_RETRIES } = {}) {
|
||||
for (let attempt = 0; ; attempt++) {
|
||||
const t = await sequelize.transaction();
|
||||
try {
|
||||
const result = await fn(t);
|
||||
await t.commit();
|
||||
return result;
|
||||
} catch (err) {
|
||||
await t.rollback();
|
||||
if (isSerializationFailure(err) && attempt < maxRetries) {
|
||||
const delay = BASE_DELAY_MS * 2 ** attempt + Math.random() * BASE_DELAY_MS;
|
||||
await new Promise((resolve) => setTimeout(resolve, delay));
|
||||
continue;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { withTransactionRetry, isSerializationFailure };
|
||||
Reference in New Issue
Block a user