Skip to content

Instantly share code, notes, and snippets.

@ephys
Created May 29, 2020 12:19
Show Gist options
  • Save ephys/3c51f5558ae9f3ac3d4994080c93b7d8 to your computer and use it in GitHub Desktop.
Save ephys/3c51f5558ae9f3ac3d4994080c93b7d8 to your computer and use it in GitHub Desktop.
sequelize's transaction and async_hooks
// @flow
import { AsyncLocalStorage } from 'async_hooks';
import type { Sequelize, Transaction } from 'sequelize';
const asyncStore = new AsyncLocalStorage();
/**
* This works like {@link Sequelize#transaction}, but if this is called inside an active transaction,
* the active transaction will be returned.
*
* Note: You should use this as a replacement for {@link Sequelize#transaction},
* otherwise {@link getCurrentTransaction} will break.
*
* Note: SAVEPOINT functionality has not been implemented, if a sub-transaction fail,
* you should let the whole transaction fail or you'll end-up with inconsistent state.
* If SAVEPOINT is needed, ping @ephys
*
* @param {!Sequelize} sequelize The sequelize instance on which the transaction will run
* @param {!Function} callback The callback to call with the transaction.
*
* @returns {any} The returned value of {callback}
*/
export function withTransaction<T>(sequelize: Sequelize, callback: (t: Transaction) => T) {
const transaction = getCurrentTransaction();
if (transaction) {
return callback(transaction);
}
return sequelize.transaction(async newTransaction => {
return asyncStore.run(newTransaction, () => {
return callback(newTransaction);
});
});
}
/**
* Returns the transaction of the current {@link withTransaction} block, if any.
*
* This method does not create a new transaction if none is active.
*
* @returns {Transaction | null} The transaction
*/
export function getCurrentTransaction(): Transaction | null {
return asyncStore.getStore() ?? null;
}
// AFTER
function sendMessage(from, to, message) {
return withTransaction(sequelize, () => {
const conversation = await createConversation(from, to);
return await createMessage(conversation, from, message);
});
}
function createConversation(from, to) {
Conversation.create(stuff, { transaction: getCurrentTransaction() });
}
function createMessage(conversation, from, message) {
Message.create(stuff, { transaction: getCurrentTransaction() });
}
// BEFORE
function sendMessage(from, to, message) {
return sequelize.transaction(transaction => {
const conversation = await createConversation(from, to, { transaction });
return await createMessage(conversation, from, message, { transaction });
});
}
function createConversation(from, to, { transaction }) {
Conversation.create(stuff, { transaction });
}
function createMessage(conversation, from, message, { transaction }) {
Message.create(stuff, { transaction });
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment