Using ensure in dosync - IllegalStateException: No transaction running

246 views
Skip to first unread message

Eric Lavigne

unread,
Feb 10, 2009, 10:01:36 PM2/10/09
to clo...@googlegroups.com
The way I understand it, "transaction running" means that the code is executing inside a dosync block. So this should work:


(def account1 (ref 1000))
(def account2 (ref 2000))

(dosync (map ensure [account1 account2]))


However, I get the following error:

java.lang.IllegalStateException: No transaction running


I use ensure rather than deref because I want to know that the result is representative of all accounts at the same point in time. I intended to use this as a trivial example of STM for a Clojure presentation, and was surprised when it didn't work.

Why can't I use ensure inside of a dosync block? Is there another way to perform a transaction?

Stephen C. Gilardi

unread,
Feb 10, 2009, 10:20:05 PM2/10/09
to clo...@googlegroups.com

On Feb 10, 2009, at 10:01 PM, Eric Lavigne wrote:

> Why can't I use ensure inside of a dosync block? Is there another
> way to perform a transaction?

You can, but "map" is lazy. It's not being evaluated within the
dosync. One way to fix the code is to force map to evaluate within the
dosync using "dorun":

(dosync (dorun (map ensure [account1 account2])))

See (doc dorun) for some more info.

For a simple demo (for others), you might want to stick with:

(ensure account1)
(ensure account2)

--Steve

Jeffrey Straszheim

unread,
Feb 10, 2009, 10:24:28 PM2/10/09
to clo...@googlegroups.com
Say hello to laziness.

Your code is returning a lazy seq (from the map), which is evaluated by the reader outside of the dosync.


Now what I don't understand is why, when I run:

  (dosync (dorun (map ensure [account1])))

I get:

  java.lang.RuntimeException: java.lang.IllegalArgumentException: No matching method found: get for class clojure.lang.Ref (NO_SOURCE_FILE:0)

*sigh*

Jeffrey Straszheim

unread,
Feb 10, 2009, 10:26:33 PM2/10/09
to clo...@googlegroups.com
Since we're on the subject, what exactly does ensure *do* that simply reading the ref does not?  And then, how does it differ from writing.

Rich Hickey

unread,
Feb 10, 2009, 10:32:50 PM2/10/09
to Clojure


On Feb 10, 10:24 pm, Jeffrey Straszheim <straszheimjeff...@gmail.com>
wrote:
> Say hello to laziness.
>
> Your code is returning a lazy seq (from the map), which is evaluated by the
> reader outside of the dosync.
>
> Now what I don't understand is why, when I run:
>
> (dosync (dorun (map ensure [account1])))
>
> I get:
>
> java.lang.RuntimeException: java.lang.IllegalArgumentException: No
> matching method found: get for class clojure.lang.Ref (NO_SOURCE_FILE:0)
>
> *sigh*
>

Fixed in SVN 1271 - thanks for the report.

Rich

Jeffrey Straszheim

unread,
Feb 10, 2009, 10:33:57 PM2/10/09
to clo...@googlegroups.com
My pleasure.

BTW, thanks for Clojure!

Eric Lavigne

unread,
Feb 10, 2009, 11:19:00 PM2/10/09
to clo...@googlegroups.com

Why can't I use ensure inside of a dosync block? Is there another way to perform a transaction?

You can, but "map" is lazy. It's not being evaluated within the dosync. One way to fix the code is to force map to evaluate within the dosync using "dorun":

       (dosync (dorun (map ensure [account1 account2])))

See (doc dorun) for some more info.

For a simple demo (for others), you might want to stick with:

       (ensure account1)
       (ensure account2)

Thanks for the explanation. This is my modified demo - no more laziness:

(dosync [(ensure account1) (ensure account2)])

Rich Hickey

unread,
Feb 11, 2009, 7:52:58 AM2/11/09
to Clojure


On Feb 10, 10:26 pm, Jeffrey Straszheim <straszheimjeff...@gmail.com>
wrote:
> Since we're on the subject, what exactly does ensure *do* that simply
> reading the ref does not? And then, how does it differ from writing.
>

If you merely read a ref in a transaction that won't prevent another
transaction from changing it. (You won't see that change in your
transaction, of course). If you ensure it, and it is changed by
another transaction, your transaction will retry.

ensure has the rollback-if-modified effects of writing, but differs
from writing in that it doesn't write anything. That matters:

a) for clarity - a fake write to ensure integrity confuses maintainers
b) for concurrency - a fake write looks like a write to other
transactions, and might cause them to roll back.
c) for lack of effects - a fake write might trigger validation or
watch notifications.
d) for some unknown future reason

i.e., I think it is preferable to have an explicit ensure operation
for when that is all that is required, so Clojure's STM has one.

Rich

Jeffrey Straszheim

unread,
Feb 11, 2009, 10:55:19 AM2/11/09
to clo...@googlegroups.com
So, ensure can make you rollback if the value is not current when you commit.  Can it make other transactions rollback if you've ensured a ref, and commit before them?

Rich Hickey

unread,
Feb 11, 2009, 3:50:00 PM2/11/09
to Clojure


On Feb 11, 10:55 am, Jeffrey Straszheim <straszheimjeff...@gmail.com>
wrote:
> So, ensure can make you rollback if the value is not current when you
> commit. Can it make other transactions rollback if you've ensured a ref,
> and commit before them?
>

No. That's what I meant to imply by (b). ensure has no effect on other
transactions.

Rich
Reply all
Reply to author
Forward
0 new messages