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

node-bemis

v0.9.0

Published

A simple transaction manager for Node.js

Downloads

6

Readme

Bemis: An asynchronous transaction manager for Node.js apps

Bemis makes it easy to perform reversable multi-step operations in a Node.js app by allowing you to progressively build a rollback operation.

Installation

npm install node-bemis

Example usage

You use Bemis as part of a multi-step operation to create an undo process. For example, suppose you want to charge a customer and then update their order with the payment information. Normally, you'd do something like this:

customer.charge(order.total, function(err, transaction) {
  if (err) return; // Bail
  
  order.update(transaction.details);
});

If the call to order.update() fails, your payment will be half-complete: the customer will have been charged, but the payment will not be reflected in the order (normally leading to a very upset customer and lots of head scratching in your customer support department).

With Bemis, you can build a transaction reversal operation with a few lines of code:


var transaction = new Bemis.Serial();

customer.charge(order.total, function(err, transaction) {
  if (err) return; // Bail
  
  transaction.addTask(function(cb) {
    customer.refund(order.total, cb);
  });
  
  order.update(transaction.details, function(err) {
    if (err) return transaction.rollback();
    
    // Continue as normal
  });
});

Important: Note that it's up to you to ensure the atomicity of each task. Bemis can't help you with that!

Serial and parallel rollbacks

Bemis works asynchronously, performing each task without blocking. However, it supports two kinds of transactions:

  • Bemis.Serial performs its tasks in reverse order, one after the other. So, if you add Task A and Task B to one, a rollback will cause Task B to be executed, and then Task A.
  • Bemis.Parallel as its name implies, performs its tasks in parallel, without guaranteeing a specific order.

Nested transaction blocks

You can nest transaction blocks by simply adding them to each other as tasks. They will be automatically executed using the appropriate causality rules.

Nesting is useful when one of your tasks includes an indeterminate number of parallel operations that must all complete successfully; since it's impossible to predict at design time which step will fail, you can simply accumulate your rollback tasks progressively and nest them in a larger overall transaction. For example, using @caolan's async module:

var transaction = new Bemis.Serial();

async.series(
  [
    function(cb) {
      doSometing(function(err) {
        if (err) return cb(err);
        
        transaction.addTask(function(cb) {
          // Undo here
        });
      });
    },
      
    function(cb) {
      var parallelTransaction = new Bemis.Paralle();
      
      transaction.addTask(parallelTransaction);
      
      async.each(
        arrayWithManyItems,
        
        function(cb) {
          // There is no way of telling whether _any_ of these will fail, so we
          // simply keep populating the parallel transaction at east step.
        },
          
        cb
      );
    }  
  ],
  
  function(err) {
    if (err) return transaction.rollback(function() {
      // Do something here.
    });
  }
);

API

Bemis.Serial()

Instantiates a new serial transaction.

Bemis.Parallel()

Instantiates a new parallel transaction. Both Bemis.Serial and Bemis.Parallel have the exact same interface.

transaction.addTask({fn(callback)|transaction})

Adds a new task to a transaction. A task is either a callback or another transaction, which will be nested within the current transaction.

Note that the task will be performed asynchronously, and must call callback when it's complete.

The callback parameter accepts an err parameter, which can be null or undefined. Note that, if you pass err, the entire rollback operation will immediately stop.

transaction.addParallelTasks(tasks)

Adds a an array of tasks to be executed in series as part of a single task in the rollback process. Note that these tasks are executed exactly in the order in which you specify them; they are not reversed.

Each tasks is executed asynchronously and receives a callback that must be called on completion. The callback behaves exactly as in addTask().

transaction.addSerialTasks(tasks)

Adds a an array of tasks to be executed in parallel as part of a single task in the rollback process. Each tasks is executed asynchronously and receives a callback that must be called on completion. The callback behaves exactly as in addTask().

transaction.rollback([callback])

Rolls back a transaction, as described above. The optional callback closure, if present, is called on completion with an err parameter that indicates whether the rollback procedure was interrupted by an error.