views:

1576

answers:

2

Please excuse any mistakes in terminology. In particular, I am using relational database terms.

There are a number of persistent key-value stores, including CouchDB and Cassandra, along with plenty of other projects.

A typical argument against them is that they do not generally permit atomic transactions across multiple rows or tables. I wonder if there's a general approach would would solve this issue.

Take for example the situation of a set of bank accounts. How do we move money from one bank account to another? If each bank account is a row, we want to update two rows as part of the same transaction, reducing the value in one and increasing the value in another.

One obvious approach is to have a separate table which describes transactions. Then, moving money from one bank account to another consists of simply inserting a new row into this table. We do not store the current balances of either of the two bank accounts and instead rely on summing up all the appropriate rows in the transactions table. It is easy to imagine that this would be far too much work, however; a bank may have millions of transactions a day and an individual bank account may quickly have several thousand 'transactions' associated with it.

A number (all?) of key-value stores will 'roll back' an action if the underlying data has changed since you last grabbed it. Possibly this could be used to simulate atomic transactions, then, as you could then indicate that a particular field is locked. There are some obvious issues with this approach.

Any other ideas? It is entirely possible that my approach is simply incorrect and I have not yet wrapped my brain around the new way of thinking.

+4  A: 

If, taking you example, you want to atomically update the value in a single document, you can do so in CouchDB. You will get a conflict error when you try to commit the change if an other contending client has updated the same document since you read it. You will then have to read the new value, update and re-try the commit. There is an indeterminate (possibly infinite if there is a lot of contention) number of times you may have to repeat this process, but you are guaranteed to have a document in the database with an atomically updated balance if your commit ever succeeds.

If you need to update two balances (i.e. a transfer from one account to an other), then you need to use a separate transaction document that gives the amount and the two accounts (in and out). This is a common bookkeeping practice, by the way. Since CouchDB computes views only as needed, it is actually still very efficient to compute the current amount in an account from the transactions that list that account. In CouchDB you would use a map function that emitted the account number as key and the amount of the transaction (positive for incoming, negative for outgoing). Your reduce function would simply sum the values for each key, emitting the same key and total sum. You could then use a view with group=True to get the account balances, keyed by account number.

Barry Wark
Thank you for explaining this. You say that it is "still very efficient" to do a group. Can you elaborate on this a bit? For high-traffic relational databases, it is common practice to denormalise a column. I can imagine that CouchDB and others store data significantly differently and this means the grouping of transactions could be more efficient. But would you do this with 10 transactions to group? 100? 100,000?
ChrisInEdmonton
CouchDB uses a Map/Reduce paradigm to view documents in the database. Since the map is applied only to changed documents, its (time) efficiency is essentially O(1) in the total number of documents, but O(n) in the number of changed documents. Reduced values are computed and stored in a b-tree. Obviously all nodes who have a child document that is changed will need to be recomputed. Thus it may be more time consuming to run the reduction. CouchDB has been demonstrated in production with many millions of documents so I don't think that will be an issue in this case.
Barry Wark
Thank you. By the way, I work for a social network site. We aren't planning on switching to a persistent key-value store in the medium future. We use sharded MySQL database servers and memcache, of course. Looks like our busy tables have seen hundreds of millions of rows but nothing beyond that. From your answers, it looks like CouchDB, at least, has been specifically designed to handle the sorts of issues I expect would come up for a site like ours. Not too surprising but still good to hear.I'm sure CouchDB and others would do some things better and the occasional thing worse.
ChrisInEdmonton
I think I'd ammend your summary: "CouchDB and others would do some things better and some things worse". Damien Katz, the creator of CouchDB has often said that if your data would be described by documents (or cards or pages etc.) if it were physical, then CouchDB is a good match. If not (e.g. you're representing a graph of objects) CouchDB may be a very bad fit. It's just a tool, after all.
Barry Wark
A: 

Hi, I've been interested in that question for a while and the answer is very helpful. Can someone please complete the question by explain how eventually we will "commit" all the pending transactions.

This is my understanding: It is possible to treat each transaction as a separate document and have a view to calculate the balance of each account. On a "night-job" we will take the database "offline" (not allow outside access) and process the transactions and update the actual balance of the account?

Is that make the case close?

Thank you, Ido

Ido Ran