On Oct 13, 9:51 pm, jim <[EMAIL PROTECTED]> wrote: > Rich, > > I've been working with refs tonight and I appreciate the validate-fn > addition. Makes my life a lot easier. > > From reading the docs, 'ensure' keeps a ref from changing inside a > dosync. But if accesses to refs are contained in function calls that > are called during the transaction, the programmer might overlook them > when writing the dosync. > > A hack I came up with to deal with this is to bind a new value to > 'deref' that saves the ref to a list before calling the normal deref > function. Then as a final step in the transaction, to go through the > list of deref'd refs and call 'ensure' on each one. > > Do you have any thoughts on how to handle the situation better? >
Yes - don't do it! First, let me explain ensure a bit more. When running in a transaction, you will see the ref world as-of a point in time, entirely consistent. Refs never change inside a dosync unless you change them. This is known as snapshot isolation, and, under MVCC, is an efficient and robust way to do things. Reads don't need to be tracked, and consistency is good. In most cases, you only care that the refs you are writing to haven't changed before you commit - this is done automatically by the transaction. ensure is there to, well, ensure that a ref >that you've only read< is no different when your transaction commits. When would you want to do this? - when your business rule has an invariant on read data. Failing to lock down the invariant read items in this case risks what is called a write skew. A classic example is a financial program that lets you have two accounts, at most one of which can run a negative balance. Any debit to an account will change only that account, but read and rely upon the value in the other account not changing. In some MVCC systems you'd have to issue a dummy write to the other account in order to get it to participate in the transaction. ensure is just a cleaner, clearer way to do this. Why wouldn't we want to ensure all reads? For two reasons - one, it's terribly inefficient, and two, that's not the way the concurrent world works. In the real world we make an observation, make a decision, and act, and the world doesn't stop in the meantime. Physics keeps actions from conflicting, and, similarly, MVCC transactions prevent write conflicts. You're seeing more and more assaults on transactional DBMS systems not being able to scale, people accepting eventual or partial consistency etc. I think some of these problems stem from a stop-the-world approach, one that we have to question in our in-memory designs as well. Many STMs do read tracking and ensure that nothing you've read has changed during the transaction. They also may keep track of each ref only from the point in time you first read it, offering no whole-world consistency at all. I think MVCC is a better approach. As an example, consider a program like one that needs to give all salespeople that have made their quota a bonus. The program will need to read the quotas and sales, and will change their paycheck. By doing so in a Clojure transaction, it is making decisions based upon consistent data. There's no reason to stop people from selling in the meantime, concurrently, i.e. you don't care if, at the end of issuing bonuses, more sales have occurred. It will be possible for me to add an optional ensure-all mode to the transactions if there is sufficient demand, but I encourage you to first understand and rely upon the substantial consistency guarantees and efficiencies offered by Clojure's MVCC snapshot isolation, using ensure only when you have a true read invariant/write skew situation. Rich --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~----------~----~----~----~------~----~------~--~---