Well, traditional transactions map to refs in Clojure. You could use a ref, as in any transactional stuff must be performed in a dosync form.
However, using a agent is a much cleaner solution for your needs IMHO. An agent, by definition, can be executing only one operation at a given time, and other requests/actions are queued. So, an agent would guarantee that only 1 task is removed from the queue and then served. If a different thread calls dequeue, its actually sent to the agent's internal action queue. But dereferencing the agent would return you the value in the agent at that given time itself, not after executing the task. If you would actually want to wait until the de-queued task is finished, then use await to wait until all actions sent to the agent are served. On Wed, Mar 6, 2013 at 10:06 PM, bruce li <leilmy...@gmail.com> wrote: > Hello, > I'm working on a piece of code that uses congomongo to access mongodb. > What I want to implement is to use one collection of the DB as a > queue(let's say, it's called "task"). > > Using congomongo, it's easy to fetch a task that is of status :queue : > (def t (fetch-one :task :where {:status :queue})), > and also it's simple to update it: (update! :task t (assoc t :status > :running)) > > But how to make a safe and consistent "dequeue" operation in concurrent > context? A naive dequeue should look like this: > > (let [t (fetch-one:task :where {:status :queue})] > (update! :task t (assoc t :status :running)) > t) > > But taking concurrency into consideration, this implementation is > error-prone, at least from my point of view. Consider this: > > When 2 threads are fetching the task, it's very likely that the task is > fetched twice using the previous piece of code and they would be executed > twice. > > Do we have something like transaction? Something that will enforce the > "fetch-one" and "update!" statement are both executed before another > “fetch-one" operation is adopted? > > What I can think of is to use an agent. And my code looks like this: > > (def db-agent (agent nil)) > (defn dequeue [] > (letfn [(do-dequeue [da] > (let [task (mongo/fetch-one :task :where {:status :queue})] > (when task > (mongo/update! :task feed (assoc task :status :running))) > task))] > (send db-agent do-dequeue) > @db-agent))) > > However, I still doubt the correctness of this solution. Right before the > @db-agent is called, won't another thread call "dequeue", which will > involve another "send” and change the value of db-agent? > > I'm wondering if anyone can help. Thanks. > > > > -- > -- > 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 > Note that posts from new members are moderated - please be patient with > your first post. > To unsubscribe from this group, send email to > clojure+unsubscr...@googlegroups.com > For more options, visit this group at > http://groups.google.com/group/clojure?hl=en > --- > You received this message because you are subscribed to the Google Groups > "Clojure" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to clojure+unsubscr...@googlegroups.com. > For more options, visit https://groups.google.com/groups/opt_out. > > > -- Akhil Wali # http://github.com/darth10 <https://github.com/darth10> # http://darth10.github.com -- -- 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 Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.