npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@b.s/txn

v1.0.4

Published

Simple distributed transactions

Downloads

117

Readme

Tests

About

Simple distributed transactions using a container.

Has optional support for AsyncDisposable.

Features

  • CJS + ESM ✅
  • Lightweight ✅
  • Simple and easy to use ✅
  • Retry mechanism ✅

Background

The scenario: when a customer checks out their cart, we need to create an order via the order microservice and a payment via the payment microservice. A checkout should only be successful if both are successful, and if either one is unsuccessful, then the checkout should fail: the order should not be created and the customer should not be charged.

A request can fail for many reasons: either the request provides invalid input (in which case we would want to rollback all changes onError) or the network fails (in which case we would want to makeRetry the request, the solution to the extreme case of retries failing being throwing our hands in the air and handling things manually).

The first case is simple enough to solve, if creating an order fails then we cannot create a payment because creating a payment requires an order to be linked to it, so we don't have the required parameters to perform the checkout. Neither order nor payment gets created.

The second case is a little trickier, what if we create an order but making a payment fails and the order has to be deleted? Do we handle this on the client, introduce a new endpoint to handle the coordination, or put the checkout event and order creation event in a queue to be consumed and fulfilled by consumers?

Saga's are an architectural pattern to ensure data consistency between microservices by either choreography (placing events in a queue) or orchestration (having a single handler to ensure both operations are successful and handling the data inconsistencies which may occur if either one fails - React web app or new endpoint).

This package targets orchestration and allows for Saga's to be implemented easily on the client (say a React web app or a new endpoint utilizing a BFF) without tying the implementation to the database. It allows for easy composition with libraries such as React Query (transaction.resolve can be used as the queryFn) and provides both CJS and ESM exports to cater for use on the backend or frontend.

There are a few ways to go about achieving distributed transactions using this package, such as:

  1. two phase commits such as with Postgres: we PREPARE TRANSACTION using the queryFn and COMMIT PREPARED onSuccess or ROLLBACK PREPARED onError.

  2. optimistically create rows and have a flag field in the database and flip it onSuccess, undoing any operations on 3rd party services onError

  3. optimistically create rows and delete records (and undo operations on 3rd party services) onError

Usage

Transactions are committed at the end of the scope and rolled back if any query fails.

Simply createTransaction with your queries and execute them within resolve via exec:

const transaction = createTransaction({
	order: {
		queryFn: orderService.createOrder,
		onSuccess: onSuccessCreateOrder,
		onError: onErrorCreateOrder,
	},
	payment: {
		queryFn: paymentService.createPayment,
		onSuccess: onSuccessCreatePayment,
		onError: onErrorCreatePayment,
	},
});

const { order, payment } = await transaction.resolve(async () => {
	const newOrder = await transaction.exec("order", {
		user: "1234",
		items: [],
	});

	const paymentReceipt = await transaction.exec("payment", { order });

	return { order: newOrder, payment: paymentReceipt };
});

// With support for `AsyncDisposable`

const checkout = async () => {
	await using transaction = createTransaction({
		order: {
			queryFn: orderService.createOrder,
			onSuccess: onSuccessCreateOrder,
			onError: onErrorCreateOrder,
		},
		payment: {
			queryFn: paymentService.createPayment,
			onSuccess: onSuccessCreatePayment,
			onError: onErrorCreatePayment,
		},
	});

	const newOrder = await transaction.exec("order", {
		user: "1234",
		items: [],
	});

	const paymentReceipt = await transaction.exec("payment", { order });

	return { order: newOrder, payment: paymentReceipt };
};

const { order, payment } = await checkout();

// And ability to retry a function with `makeRetry`
// Exponential backoff
const createPayment = makeRetry({
	retryFn: paymentService.createPayment,
});

More information in src/txn/txn.spec.ts and src/e2e/txn.test.ts.

Contributions

  • Contributions are welcome, just make a pull request

"Once a day, I try but I can't find a single word"