Dependency conflict with Ring & Jetty

I’ve been switching my little Datomic project to use Datomic Cloud. The project is a simple API for documents and it uses Ring.

One of the most difficult parts of switching to Datomic Cloud have been some dependency conflicts that the cloud has with Ring. I created a Stackoverflow issue about it here.


I thought I’d solved the ticket when I posted this solution to the ticket:

I found a solution for this based on the suggestion by @Aleph Aleph in the comments to the question above. What I did was add exclusions to the package that had dependencies conflicting with datomic/cloud-client.

I looked at this conflicts by looking more closely with lein deps :tree | grep jetty. It showed the following in particular:

[ring "1.6.3" :exclusions [org.eclipse.jetty/jetty-client org.eclipse.jetty/jetty-http org.eclipse.jetty/jetty-util]] -> [ring/ring-jetty-adapter "1.6.3"] -> [org.eclipse.jetty/jetty-server "9.2.21.v20170120"] -> [org.eclipse.jetty/jetty-
io "9.2.21.v20170120"]
 overrides
[com.datomic/client-cloud "0.8.50"] -> [com.datomic/client "0.8.40"] -> [com.datomic/client-impl-shared "0.8.34"] -> [com.cognitect/http-client "0.1.83"] -> [org.eclipse.jetty/jetty-client "9.3.7.v20160115"] -> [org.eclipse.jetty/jetty-io
 "9.3.7.v20160115"]

and

[com.datomic/client-cloud "0.8.50"] -> [com.datomic/client-impl-shared "0.8.34"] -> [com.cognitect/http-client "0.1.83"] -> [org.eclipse.jetty/jetty-client "9.3.7.v20160115"] -> [org.eclipse.jetty/jetty-io "9.3.7.v20160115"]
 and
[com.datomic/client-cloud "0.8.50"] -> [com.datomic/client "0.8.40"] -> [com.cognitect/http-client "0.1.83"] -> [org.eclipse.jetty/jetty-client "9.3.7.v20160115"] -> [org.eclipse.jetty/jetty-io "9.3.7.v20160115"]

As is evident there, the dependency coming from ring called ring/ring-jetty-adapter contained some package versions overriding those in datomic cloud. So I added the following, fixing the problem:

[ring "1.6.3"
 :exclusions [ring/ring-jetty-adapter]]

Trouble is that this solution ultimately did not work because it caused ring to fail when I tried to start the server. So I’m now trying to solve the dependency conflict again. This is proving difficult, though, because it seems that I either end up causing Ring to fail or Datomic Cloud.

If anyone has encountered this problem and knows how to resolve it, I’d be grateful for any guidance. The problem is quite annoying and I have to admit that I find the :exclusions flag hard to understand. What is being excluded from where?

This conversation about dependency conflicts is a bit helpful. It starts to make sense of one question I’d had which is why can’t one project have a dependency on one version of a package while another package depends on another. Quoting from the conversation, here’s an answer:

Blame the JVM designers. Classes are a global namespace, so two libraries (or two versions of the same library) that define the same classes will conflict.

Your comments are astute. These are known problems on the JVM. Managing the classpath (which is essentially what Leiningen is doing) has always been tough in Java, and sometimes even outright broken. It is something that takes effort and human input. Sometimes you get incompatible versions and you have to hold a package behind because the new version depends on a new version of something else. It can be a pain, but lein tree :deps gives you good transparency into what’s going on.

Its recommendations seem to default to excluding the more recent version. There are also two other things that I find helpful: a plugin called lein ancient30 and this setting in the project file64, which lets you abort or warn when there are version conflicts.

I discovered also recently that the “Troubleshooting” section of the Datomic Cloud docs suggest a fix relevant at least for an older version of Datomic Cloud here.

However, trying this fix out did not provide a solution either, see project.clj and error stack here.

I’ve gone through the same trouble and ended up adding this dependency at the top level and no exclusions to datomic.cloud or jetty.
[org.eclipse.jetty/jetty-server “9.3.7.v20160115”]

I copied this solution from the following site, so I cannot explain whether or not this is the best solution.

1 Like

I added this as a top level dependency as you suggested and it worked! I must thank you immensely. Now I just wish I knew the reason this worked.

Glad it’s worked out for you. I’ve also wasted quite some time on this thing and it saddens me to see other people trapped in the same black hole. I have no clue as to why this works and the other solutions don’t.

I’m a believer of the power Datomic Cloud brings to us but have to say the online documentation is subpar (typos, inconsistencies, missing links, etc). The official source of truth is not really trustworthy… Also, it seems to me that the development team are more responsive to questions at Slack than here.

1 Like

Just ran into the same problem here, however pinning the jetty-server version didn’t
fully resolve it for me. Instead of going down the dependency/classloading rabbit hole, I just decided to use Immutant instead, mostly since that’s what they picked for Luminus, so I could use their setup as a guide.

I think this issue will be common since a sensible path of least resistance if you’re developing a web app is to use Ring with the included Jetty adapter, which is a different version than the one com.datomic/client-cloud is using (you can see via clojure -A:dev -Stree if you’re setting up deps.edn similar to the guides).

Pinning the jetty-server version as explained above worked because it causes the dependency resolution algorithm (Maven’s I guess?) to prevent ring-jetty-adapter from bringing in it’s conflicting version. The version you explicitly asked for takes precedence and excludes the implicit/transitive one. I’m not sure if it being the newer version makes any difference. If you run clojure -A:dev -Stree again, you’ll notice that ring/ring-jetty-adapter no longer lists any sub-dependencies of its own once you pick the jetty-server you want.

Gaining a full understanding of JVM classloading details is hard, but by default (eg when you’re not using OSGi or some other framework with a non-standard classloading process) loading the same class more than once in the same JVM is risky and usually leads to these kinds of errors.