-
-
Notifications
You must be signed in to change notification settings - Fork 91
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow transactions on distinct connections in the same thread #282
Comments
I got sidetracked a bit by other things. This is still on my mind. As soon as I have the time, I'll come back to this. |
FYI: This is a variant (not merged yet) that we'll likely use in production. I'll report back after we've run it for a while and if we don't see any problems there.
Note that the code above was simplified by assuming the |
@mbezjak Seems strange to track |
@seancorfield You are right, of course! With the code above, the following tests would fail: (try
(jdbc/with-transaction [tx1 data-source]
(insert-into-foo tx1)
(jdbc/with-transaction [tx2 tx1]
;; Running this SQL shouldn't commit anything! It's as if `with-transaction` block is not
;; there at all.
(is (= 1 (count-foo tx2))))
(throw (Exception. "rollback outer!")))
(catch Exception _))
(is (= 0 (count-foo data-source))) and (jdbc/with-transaction [tx1 data-source]
(is (= 0 (count-foo tx1)))
(try
(jdbc/with-transaction [tx2 data-source]
(insert-into-foo tx2)
(is (= 1 (count-foo tx2)))
;; tx2 is a transaction on its own that can be rollbacked. tx1 shouldn't see anything that
;; happened in tx2.
(throw (Exception. "rollback inner!")))
(catch Exception _)))
(is (= 0 (count-foo data-source))) (I know about With that in mind, the updated Transactables: (ns something
(:require
[next.jdbc.protocols :as p]
[next.jdbc.transaction :as tx])
(:import
(java.sql Connection)))
(defonce ^:private ^:dynamic *active-tx* #{})
(defn transact [con body-fn opts]
(@#'tx/transact* con body-fn opts))
(extend-protocol p/Transactable
java.sql.Connection
(-transact [this body-fn opts]
(let [raw (if (.isWrapperFor this Connection)
(.unwrap this Connection)
this)]
(if (contains? *active-tx* raw)
(body-fn this)
(binding [*active-tx* (conj *active-tx* raw)]
(transact this body-fn opts)))))
javax.sql.DataSource
(-transact [this body-fn opts]
(with-open [con (p/get-connection this opts)]
(p/-transact con body-fn opts)))) ☝️ is really simple without As before, we haven't merged any of this yet. It's all waiting for me to play around with transactions some more. I'll report back after we've run it for a while and if we don't see any problems. |
Merged 3 weeks ago. No issues so far. 😄 |
Is your feature request related to a problem? Please describe.
Should open two separate transactions because those are two separate data sources, with two separate SQL connections. They just happen to execute in the same thread.
tx2
should be rolled back due an exception.Right now, the above snippet is equivalent to
More context: https://clojurians.slack.com/archives/C1Q164V29/p1720188613556859?thread_ts=1720104756.702669&cid=C1Q164V29
Describe the solution you'd like
next.jdbc.transaction/*active-tx*
should probably be a hash-map java.sql.Connection -> boolean, or simply a hash-set ofjava.sql.Connection
objects.Implementing
java.sql.Connection
is usually done with proxies, so the key to the map above (element in a set) should probably be the result ofunwrap
call. Doing the same tojavax.sql.DataSource
might be needed.For backwards compability,
next.jdbc/active-tx?
should returntrue
if the hash-map/hash-set is not empty. 1-arity variant accepting atransactable
could be introduced.Describe alternatives you've considered
I haven't yet started thinking about how to solve the problem above.
The text was updated successfully, but these errors were encountered: