jam:mongo-transactions
enables an easy way to work with Mongo Transactions in Meteor apps. Here are a few of the benefits:
rawCollection()
, though if you need a particular rawCollection()
method, you can still use it.session
around. This package takes care of that for you.jam:easy-schema
and aldeed:collection2
.autoRetry: false
.Important: This package expects that you'll use the promise-based *Async
Meteor collection methods introduced in v2.8.1
though it will technically work with the older syntax without the *Async
suffix as long as you don't use callbacks. It does not cover using callbacks with Meteor collection methods.
meteor add jam:mongo-transactions
Note: there's no need to pass session
into the *Async
collection methods. This package handles that for you.
import { Mongo } from 'meteor/mongo';
async function purchase(purchaseData) {
try {
const { invoiceId } = await Mongo.withTransaction(async () => {
const invoiceId = await Invoices.insertAsync(purchaseData);
const changeQuantity = await Items.updateAsync(purchaseData.itemId, { $set: {...} });
return { invoiceId, changeQuantity } // you can return whatever you'd like
});
return invoiceId;
} catch (error) {
// something went wrong with the transaction and it could not be automatically retried
// handle the error as you see fit
}
}
If you want customize how the Transaction runs, pass in the Transaction options as the second argument, for example:
await Mongo.withTransaction(async () => {
...
}, { writeConcern: { w: 1 } });
Refer to the Mongo Node API docs for more information about Transaction options.
Most of the time, you'll want the default behavior where the Transaction is automatically retried for a TransientTransactionError
or UnknownTransactionCommitResult
commit error. But if you don't want that behavior, simply pass in { autoRetry: false }
like this:
await Mongo.withTransaction(async () => {
...
}, { autoRetry: false });
Setting { autoRetry: false }
, means the Transactions Core API will be used rather than the Callback API and you'll be responsible for handling all errors. You can read more about the differences in the Mongo Docs.
To determine if the code is currently running in a Transaction, use:
Mongo.inTransaction(); // returns true or false
You can write Mongo.withTransaction
and Mongo.inTransaction
isomorphically for Optimistic UI however note that the Transaction will only truly be performed on the server.
As with any isomorphic code, you should be aware that it may fail because the operation can't succeed on the client but will succeed on the server. For example, let's say you're using .find
within the Transaction and Minimongo on the client doesn't have that particular data, the client will fail but the server should still succeed. You can wrap specific server-only code with if (Meteor.isServer)
, but in these cases, you'll likely want to avoid isomorphic code and make sure the entire Transaction code only runs on the server.
Important: In my experience, you must use a paid tier for Transactions to work as expected with Meteor. The free tier would not tail the oplog for Transactions. So if you're trying this out in production, be sure to use a paid tier.