This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-02-15
Channels
- # announcements (5)
- # babashka (56)
- # beginners (24)
- # biff (15)
- # calva (7)
- # clj-kondo (12)
- # cljsrn (8)
- # clojure (68)
- # clojure-denmark (1)
- # clojure-europe (55)
- # clojure-norway (4)
- # clojure-spec (9)
- # clojure-uk (2)
- # clojurescript (8)
- # cursive (11)
- # data-science (7)
- # datahike (1)
- # datomic (66)
- # emacs (12)
- # etaoin (3)
- # fulcro (10)
- # graphql (3)
- # hyperfiddle (97)
- # jobs (1)
- # kaocha (8)
- # lsp (3)
- # malli (15)
- # meander (1)
- # off-topic (3)
- # overtone (4)
- # polylith (7)
- # rdf (25)
- # re-frame (4)
- # reagent (14)
- # remote-jobs (1)
- # shadow-cljs (126)
- # sql (30)
- # vscode (3)
- # xtdb (8)
A question regarding connection pooling
and PreparedStatement caching
I am trying to optimize my SQLite queries by working with connection pooling
and with a prepared statement using jdbc/prepare
and I’m getting an Error saying this:
:cause No implementation of method: :prepare of protocol: #'next.jdbc.protocols/Preparable found for class: com.zaxxer.hikari.HikariDataSource
My use case - My app runs the exact same query, but only with a different value for the same indexed column.
I was working with the HikariCP
. I can try work with c3p0
but I’m trying to understand if it is possible to do it with HikariCP
as well and also looking to understand this better.
Looking at the next.jdbc
docs I couldn’t see that there is a limitation but it say that the prepare is meant to work https://cljdoc.org/d/seancorfield/next.jdbc/1.2.659/doc/getting-started/prepared-statements#:~:text=If%20you%20have%20a%20SQL%20operation%20that%20you%20intend%20to%20run%20multiple%20times%20on%20the%20same%20java.sql.Connection%2C%20it%20may%20be%20worth%20creating%20the%20prepared%20statement%20yourself%20and%20reusing%20it., that mean it won’t work with connection pooling?
On the other hand, in the HikaryCP docs it say that it doesn’t offer https://cljdoc.org/d/seancorfield/next.jdbc/1.2.659/doc/getting-started/prepared-statements#:~:text=If%20you%20have%20a%20SQL%20operation%20that%20you%20intend%20to%20run%20multiple%20times%20on%20the%20same%20java.sql.Connection%2C%20it%20may%20be%20worth%20creating%20the%20prepared%20statement%20yourself%20and%20reusing%20it. and that it should be done in the driver level.
Later on, it has some https://github.com/brettwooldridge/HikariCP#statement-cache:~:text=You%20can%20use%20the%20HikariConfig%20class%20like%20so1%3A that seem to be related to allowing prepared statement in initialization so I wasn’t sure if that can work and how 😐
So I got confused by how I should actually make this work together, any help on that will be much appreciated 🙂
Thanks
you are calling prepare on the datasource (the connection pool) not a connection from the pool
using prepared statements is going to be of questionable utility with a connection pool, because typically the way you use a connection pool is you take a connection from it for some short amount of time then return it, then letter do the same again, likely getting a different connection object from the pool
unless you are doing complex queries (that result in a complex query plan, lots of joins, multiple indices, etc) then it is unlikely that a prepared statement will get you much anyway
(and the documentation explains that you call prepare
on a Connection
only)
So you get a Connection
from the pool, prepare
a statement on it, and reuse that that PreparedStatement
with various parameters (passing it into execute!
etc). And then you close the PreparedStatement
and the Connection
when you're done (`with-open` is your friend there).
This page of the docs has some examples: https://cljdoc.org/d/com.github.seancorfield/next.jdbc/1.3.847/doc/getting-started/prepared-statements
@seancorfield
How would you do the following:
• I have a service with connection pooling where my service runs the exact single query with different values to one parameter only (it’s also the primary key).
• I have thousands of requests per second that do the exact same query with different values of the parameter above.
I assume that I can benefit from performance improvement by working with Prepared statement.
I understand that the below code take a connection and at the end returns it to the connection pooling
(with-open [con (jdbc/get-connection ds)] …)
In the documentation, I see that with-open
is used also for the statement and than we execute
multiple command.
How can I save the statement “attached” to that connection without running it again and again?
From the documentation:
https://cljdoc.org/d/com.github.seancorfield/next.jdbc/1.3.847/doc/getting-started/prepared-statements#:~:text=It%20is%20your%20responsibility%20to%20close%20the%20prepared%20statement%20after%20it%20has%20been%20used
I would want my preparedStatement to be “attached” to a connection all of it’s lifetime without running it for every execute-one
My code now:
(defonce ^:private db {:dbtype "sqlite" :dbname db-name})
(defonce ^:private ds (connection/->pool HikariDataSource db))
(defn get-single-result-by-key
[value]
(with-open [con (jdbc/get-connection ds)]
(let [ps (jdbc/prepare con [(str "SELECT * FROM table1 WHERE id= ? LIMIT 1")])]
(jdbc/execute-one! ps [value]
{:builder-fn rs/as-unqualified-lower-maps}))))
My service run the get-single-result-by-key
function for every request.
How would you do that?
Posting it also in channel is it can be useful for more people.
ThanksThe example in the documentation shows this:
;; assuming require next.jdbc.prepare :as p
(with-open [con (jdbc/get-connection ds)
ps (jdbc/prepare con ["..."])]
(jdbc/execute-one! (p/set-parameters ps [...])))
I don't think a single Connection
is going to be thread-safe in terms of supporting multiple concurrent threads using it at the same time (i.e., multiple separate requests).
So you have to have at least one Connection
per thread (per request).
I want to use connection pool for sure, I just wonder how can I benefit the prepared statement with all the connections.
In other words, how can I preserve and reuse the single statement that can be used by all connections for all the requests?
That's not possible. A PreparedStatement
"belongs" to a Connection
. That's just how JDBC works.
a prepared statement caches the execution plan that the sql engine constructs for a given query, if your query is a simple primary key lookup, generating the execution plan is likely cheap enough not to matter
@U0NCTKEV8 very simple query. Just asking for object with different values of my pk. I’m just trying to optimize the queries to the maximum.
You're optimizing before you've proven you have a performance problem.
if you tie your architecture into knots to serve the ability to use preparedstatements, then preparedstatements better be a significant performance boost for you
If you have very lightweight requests that mostly just SELECT
a single row by pk, you are only going to be able to speed that up by caching in front of the JDBC machinery, e.g,. Redis.
(using JDBC for in-memory DBs has always seemed a bit strange to me since JDBC itself is pretty "heavy" machinery)
Thanks for the input. As I mentioned in earlier messages, the in-memory DB in my use-case have some other uses as well so trying to optimize it as an in process data store.
have you considered using something that is not sqlite? sqlite is a native library, and there have been a few versions of jdbc drivers for it over the years, so I don't know what is available for it now, but the options have never been great for performance
sqlite libraries may need to do a lot of jni (which introduces a certain amount of over head per call) or their used to be a jdbc sqlite library that compiled sqlite to mips instructions and then just write a mips emulator in java
I mentioned H2 to @U03A86XSH52 in another thread just over a week ago. I'd certainly lean toward H2 (or Derby) rather than SQLite for JVM-based work.
But I'd still want to consider some sort of in-memory k/v store to cache simple pk-based lookup if performance is that critical and staleness is not. Even org.clojure/core.cache
(using the wrapped
ns) and perhaps a TTL or LRU cache underneath.
https://stackoverflow.com/a/62845028 mentions finding h2 to be much faster than sqlite, and if you are using the in memory store, the file corruption complained about shouldn't be an issue
As I said, SQLite is used here for other purposes. I will maybe consider other solutions like H2 etc.. but for now I’m also trying to do the best possible effort with SQLite. Thanks for all the great info
@seancorfield
How would you do the following:
• I have a service with connection pooling where my service runs the exact single query with different values to one parameter only (it’s also the primary key).
• I have thousands of requests per second that do the exact same query with different values of the parameter above.
I assume that I can benefit from performance improvement by working with Prepared statement.
I understand that the below code take a connection and at the end returns it to the connection pooling
(with-open [con (jdbc/get-connection ds)] …)
In the documentation, I see that with-open
is used also for the statement and than we execute
multiple command.
How can I save the statement “attached” to that connection without running it again and again?
From the documentation:
https://cljdoc.org/d/com.github.seancorfield/next.jdbc/1.3.847/doc/getting-started/prepared-statements#:~:text=It%20is%20your%20responsibility%20to%20close%20the%20prepared%20statement%20after%20it%20has%20been%20used
I would want my preparedStatement to be “attached” to a connection all of it’s lifetime without running it for every execute-one
My code now:
(defonce ^:private db {:dbtype "sqlite" :dbname db-name})
(defonce ^:private ds (connection/->pool HikariDataSource db))
(defn get-single-result-by-key
[value]
(with-open [con (jdbc/get-connection ds)]
(let [ps (jdbc/prepare con [(str "SELECT * FROM table1 WHERE id= ? LIMIT 1")])]
(jdbc/execute-one! ps [value]
{:builder-fn rs/as-unqualified-lower-maps}))))
My service run the get-single-result-by-key
function for every request.
How would you do that?
Posting it also in channel is it can be useful for more people.
ThanksThe example in the documentation shows this:
;; assuming require next.jdbc.prepare :as p
(with-open [con (jdbc/get-connection ds)
ps (jdbc/prepare con ["..."])]
(jdbc/execute-one! (p/set-parameters ps [...])))