Jump to content

Transactions in PW?


clsource
 Share

Recommended Posts

Hello, 

I wonder if you can have some sort of transactions using PW API.

in order to rollback any changes made to a previous state if something went wrong.

For example if you have a shopping cart.

a product that only have 1 item left.

two people that want that product at the same time.

One gets the product, the other gets an error.


You can have something similar using many 'if '.

But something similar to this, I think could be cleaner.

// Closure to add to cart

$addToCart = function() {
      // Logic
};
 

$transaction = new Transaction();

$transaction->begin();

$transaction->commands->add($addToCart);

try {

  // Execute all commands

  $transaction->execute();

} catch(Exception $e) {

  // Cleans all data saved inside transactions

  // Recovers to a previous state before transaction execution

   $transaction->rollback();

}

Thanks :)

Link to comment
Share on other sites

I don't know much about this subject but i think this will be hard, because PW uses the the myisam storage engine, and i don't think this supports transactions. For transactions you would need innodb i think.

Link to comment
Share on other sites

@clsource

Transactions cover quite a wide area but using DB transactions isn't the only way to address the race condition for that last item or items that you have in your use-case. Here's a solution using a module I'm developing and hope to release commercially.

How does this look?

/**
 * Somewhere in your application you load the module
 */
$counters = $modules->get('ThreadsafeCounters');

/**
 * In add-to-cart logic you request a given qty of product.
 * You may get zero (if all taken), the number you requested (if many still left)
 * or some number in between (if there are too few left to fulfil entire request.)
 */
$reserved = $counters->reserve('product_qty_remaining', $number_requested);
if (0 == $reserved) {
    /**
     * All taken - sorry!
     */
} else if ($reserved == $number_requested) {
    /**
     * Entire number can be fulfilled.
     */
} else {
    /**
     * Only a partial fill is possible.
     * If you don't want to handle this case in your application you either return
     * the $reserved qty straight away or call reserve() with the $all_or_nothing
     * flag set to true: $reserved = $counters->reserve(<counter_name>, $qty, true);
     */
}

/**
 * If a sale falls through later (perhaps payment rejected) simply return the
 * number the customer ordered...
 */
$counters->inc('product_qty_remaining', $qty_of_product_in_order);

This uses thread-safe, persistent, counters to prevent races and simplify application logic.

  • Like 8
Link to comment
Share on other sites

@clsource

Transactions cover quite a wide area but using DB transactions isn't the only way to address the race condition for that last item or items that you have in your use-case. Here's a solution using a module I'm developing and hope to release commercially.

How does this look?

/**
 * Somewhere in your application you load the module
 */
$counters = $modules->get('ThreadsafeCounters');

/**
 * In add-to-cart logic you request a given qty of product.
 * You may get zero (if all taken), the number you requested (if many still left)
 * or some number in between (if there are too few left to fulfil entire request.)
 */
$reserved = $counters->reserve('product_qty_remaining', $number_requested);
if (0 == $reserved) {
    /**
     * All taken - sorry!
     */
} else if ($reserved == $number_requested) {
    /**
     * Entire number can be fulfilled.
     */
} else {
    /**
     * Only a partial fill is possible.
     * If you don't want to handle this case in your application you either return
     * the $reserved qty straight away or call reserve() with the $all_or_nothing
     * flag set to true: $reserved = $counters->reserve(<counter_name>, $qty, true);
     */
}

/**
 * If a sale falls through later (perhaps payment rejected) simply return the
 * number the customer ordered...
 */
$counters->inc('product_qty_remaining', $qty_of_product_in_order)

This uses thread-safe, persistent, counters to prevent races and simplify application logic.

Yes that wil be very useful.

How you handle multi threading?

Lazy Cron Module creates a dummy lock file I think.

that if exists does not execute the cron job.

:o

Link to comment
Share on other sites

@clsource

It currently provides two implementations of thread-safe counters. The first uses PHP file locks in blocking mode and stores the counters as files on local disk (under your assets folder). This allows the counters to be migrated alongside your application code if needed. The other, faster, implementation uses Redis as the counter storage engine leveraging its atomic increment and decrement functions and atomic LUA scripting. The neat thing about the Redis implementation is that the counters can be shared between multiple instances of your application (or even different applications) running on different servers. This really allows things to be distributed. It does, however, require some extra configuration work to get the Redis server set up in the first place (but that's actually pretty simple under debian and ubuntu at least.)

Both implementations have mechanisms built-in to allow recovery from the deletion of (or simply forgetting to migrate) the underlying counters.

I hope to have this module available for sale over the coming weekend on my first native PW shop site and I'll post more about it then.

  • Like 5
Link to comment
Share on other sites

  • 2 weeks later...

@clsource, other interested parties

I've been busy and haven't got this totally finished yet -- I still need to work on a decent method of getting these counters into a page in PW which probably means a fieldtype/inputfield.

The actual counter code is, however, all done for both files and redis. Here's part of the documentation which covers more of what I posted above (essentially an escrow problem.) Any feedback would be appreciated.

  • Like 1
Link to comment
Share on other sites

  • 2 years later...

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...