[JDBC-145] Deadlock when using JDBC from two separate threads with two separate drivers Created: 06/Oct/16 Updated: 18/Oct/16
|Reporter:||Rogan Morrow||Assignee:||Sean Corfield|
Ubuntu 16.04, Leiningen
|Attachments:||core.clj project.clj staticinitializer.zip|
In `clojure.java.jdbc`, it is possible for the function `get-connection` to call `clojure.lang.RT/loadClassForName` on the driver's classname. JDBC drivers have a static initializer to register themselves with `java.sql.DriverManager` upon load. However, `DriverManager`, as part of its static initializer, loads every implementation of `Driver` on the classpath. This causes an insidious bug when trying to use JDBC from two separate threads where each thread is using a different driver.
For example, if thread A tries to call `get-connection` with a db-spec using driver X, and thread B tries to call `get-connection` with a db-spec using driver Y, then thread A will load driver X which in turn loads `DriverManager`, which again in turn tries to load all drivers on the classpath, including driver Y. If driver Y is simultaneously being loaded by thread B, then driver Y will be trying to load `DriverManager` which is trying to load driver Y, thus creating deadlock. This behaviour can be reliably reproduced using the Clojure files attached.
I am not sure if this is even the responsibility of JDBC to fix. If you want to patch it on the JDBC side, it is easily possible to do so by making sure `DriverManager` is loaded before trying to load the driver.
|Comment by Sean Corfield [ 17/Oct/16 11:56 PM ]|
Definitely an interesting edge case and, like you, I am not sure if this is the responsibility of java.jdbc to fix. I would note that java.jdbc imports DriverManager so I would expect its static initializer to be run as part of loading the clojure.java.jdbc namespace and thus DriverManager should be loaded before get-connection is called...?
|Comment by Rogan Morrow [ 18/Oct/16 12:31 AM ]|
It would appear that import does not actually load the class and that classes are only loaded by Clojure at the point that they are first referenced, at least that is my observation based on the output of lein run on the attached project in staticinitializer.zip.
|Comment by Sean Corfield [ 18/Oct/16 12:38 AM ]|
Cool, will take a look at that. Not what I expected the behavior to be, if you're right.