Using the Redis NoSql database with .NET Part 7: transactions

Introduction

In the previous post we looked at the hash data type in Redis. Hashes are key-value collections like dictionaries in .NET or maps in Java. For a given Redis key hashes can store multiple key-value pairs. Hashes offer a certain degree of object-oriented design where an object, such as a product or customer can be described by key-values like “id”: 23, “name”: “Unknown LTD” and so on. We can use the string data type to store the properties of an object as a JSON string as we saw before in this series. However, if you need to access the individual properties of an object then a hash can be a more suitable option.

We’ve now discussed all available data types in Redis. We’ll now turn our attention to transactions.

Transactions in Redis

If you’ve worked with transactions in a relational database like SQL Server then you’ll know at least a bit about them, what they are and how they work. A transaction is a group of operations that are executed as a block. They either succeed or fail together. We say that transactions are atomic, i.e. the operations in a transaction form a single unit that will run as a single operation without interruption. Ideally an external process cannot modify the resources that are part of a transaction while the transaction is executing. Also, if a single member of the transaction fails then the changes up to that point are rolled back and the operations after the failed one are not executed at all.

Database engines implement these characteristics to a varying degree. SQL Server has full support for transactions with rollbacks. MongoDb has no transactions implementation at all at this point of time. Redis lies somewhere in between. It supports transactions and we can watch specific resources while a transaction is executed for concurrency control. However, there’s no rollback if a single operation fails. If an operation fails during execution then the rest of the transaction members will still be executed. Note that the operations must first be queued up the same way as we write Redis commands. The queued up commands are at checked for syntactic correctness. We’ll see in a bit what this means.

Transaction commands

There are 5 transaction related commands in Redis at this time:

  • MULTI: marks the start of a transaction, similar to BEGIN TRANSACTION in SQL Server
  • DISCARD: marks the end of a transaction but does not execute the commands, similar to ROLLBACK TRANSACTION in SQL Server
  • EXEC: marks the end of a transaction and executes the commands, like COMMIT TRANSACTION in SQL Server
  • WATCH: monitors a specific resource during a transaction. If the monitored resource changed before the transaction was executed then the transaction won’t run. This is a simplified way of implementing a concurrency check in Redis. We’ll see in a bit how this works in practice.
  • UNWATCH: stop watching the resources declared by WATCH earlier. We probably won’t use this command too often with transactions since DISCARD and EXEC also unwatch all resources automatically so there’s no need to explicitly call UNWATCH

Demo

We’ll simulate the trading of shares between two parties. Run redis-cli in a command window and insert two start values:

SET person:1:shares 150
SET person:2:shares 300

Trading between people is always a good candidate for transactions. If one person sells some amount of goods to another then the one selling should see their level of ownership decrease by the same amount as the level increases for the buying party. We shouldn’t end up in a situation where we see that the seller sold the items but the purchase is not visible on the buyer’s side.

We start a transaction by the…

MULTI

…command which has no parameters and simply outputs OK in the console. We can now start writing Redis statements one after the other:

INCRBY person:1:shares -50

…i.e. person 1 sells 50 shares. The command is not executed at this point so person 1 still has 150 shares. You’ll see that the command returned QUEUED. Let’s add a corresponding statement for person 2:

INCRBY person:2:shares 50

This will be queued as well. We can now execute the transaction:

EXEC

…which will respond with the new share levels:

1) (integer) 100
2) (integer) 350

If we perform the same steps but run DISCARD instead of EXEC then nothing happens to the share ownership and the transaction is not executed.

Let’s see what happens if we enter a syntactically wrong operation to the transaction:

MULTI
INCRBY person:1:shares
INCRBY person:2:shares 50
EXEC

Note that we didn’t provide the mandatory argument to INCRBY for person 1 and we got the following feedback:

(error) ERR wrong number of arguments for ‘incrby’ command

INCRBY for person 2 was queued. However, the transaction fails at the EXEC command with the following message:

(error) EXECABORT Transaction discarded because of previous errors.

None of the statements in the transaction will be executed. Even if there’s at least one step that was correctly queued before the syntactically wrong command the transaction will be discarded.

So person 1 has 100 shares and person 2 has 350 shares at this point. Let’s see how the WATCH command works. Open another command prompt and start the redis-cli in it as well. Let’s say that we only want to execute the transaction if the share levels were not modified just before. Execute the following commands in the first CLI window:

WATCH person:1:shares
WATCH person:2:shares
MULTI
INCRBY person:1:shares 50
INCRBY person:2:shares -50¨

Don’t run EXEC just yet. Modify the shares of person 1 in the second CLI window:

SET person:1:shares 400

…now now run EXEC in the first window. It will respond with (nil) i.e. no statement was executed. The WATCH statement declared that we wanted to run the transaction on an unchanged state of the resources being watched. We didn’t want to run the EXEC command if the watched resources were modified just before the transaction execution.

That’s all there is about transaction basics in Redis. We’ll turn our attention to messaging in the next post.

You can view all posts related to data storage on this blog here.

Advertisement

About Andras Nemes
I'm a .NET/Java developer living and working in Stockholm, Sweden.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

Bite-size insight on Cyber Security for the not too technical.

%d bloggers like this: