From dbc3deeb32d3390d2766499fd5e3dd490444710c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Korecki?= Date: Wed, 29 Nov 2023 09:57:12 -0800 Subject: [PATCH] Add is-closed? and is-running? functions Plus a bunch of improvements: - Closes #97 - A proper connection test with H2 - Updated documentation to use `next.jdbc` --- README.md | 22 ++--- project.clj | 18 ++-- src/hikari_cp/core.clj | 12 ++- test/hikari_cp/connection_test.clj | 32 +++++++ test/hikari_cp/core_test.clj | 136 +++++++++++++++-------------- 5 files changed, 135 insertions(+), 85 deletions(-) create mode 100644 test/hikari_cp/connection_test.clj diff --git a/README.md b/README.md index ae6545e..58cf40b 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ Custom translations of properties can be added by extending the ```clojure (ns hikari-cp.example (:require [hikari-cp.core :refer :all] - [clojure.java.jdbc :as jdbc])) + [next.jdbc :as jdbc])) (def datasource-options {:auto-commit true :read-only false @@ -124,7 +124,7 @@ Custom translations of properties can be added by extending the (delay (make-datasource datasource-options))) (defn -main [& args] - (jdbc/with-db-connection [conn {:datasource @datasource}] + (with-open [conn (jdbc/get-connection @datasource {})] (let [rows (jdbc/query conn "SELECT 0")] (println rows))) (close-datasource @datasource)) @@ -134,7 +134,7 @@ Custom translations of properties can be added by extending the ```clojure (ns hikari-cp.example (:require [hikari-cp.core :refer :all] - [clojure.java.jdbc :as jdbc])) + [next.jdbc :as jdbc])) (def datasource-options {:jdbc-url "jdbc:neo4j:bolt://host:port/?username=neo4j&password=xxxx&debug=true"}) @@ -142,7 +142,7 @@ Custom translations of properties can be added by extending the (delay (make-datasource datasource-options))) (defn -main [& args] - (jdbc/with-db-connection [conn {:datasource @datasource}] + (with-open [conn (jdbc/get-connection @datasource {})] (let [rows (jdbc/query conn ["MATCH (n:Foo) WHERE n.name = ? RETURN n" "john"])] (println rows))) (close-datasource @datasource)) @@ -159,7 +159,7 @@ Custom translations of properties can be added by extending the ```clojure (ns hikari-cp.example (:require [hikari-cp.core :refer :all] - [clojure.java.jdbc :as jdbc])) + [next.jdbc :as jdbc])) (def datasource-options {:username "username" :password "password" @@ -173,7 +173,7 @@ Custom translations of properties can be added by extending the (delay (make-datasource datasource-options))) (defn -main [& args] - (jdbc/with-db-connection [conn {:datasource @datasource}] + (with-open [conn (jdbc/get-connection @datasource {})] (let [rows (jdbc/query conn "SELECT 0 FROM dual")] (println rows))) (close-datasource @datasource)) @@ -184,7 +184,7 @@ Custom translations of properties can be added by extending the ```clojure (ns hikari-cp.example (:require [hikari-cp.core :refer :all] - [clojure.java.jdbc :as jdbc])) + [next.jdbc :as jdbc])) (def datasource-options {:username "username" :password "password" @@ -202,7 +202,7 @@ Custom translations of properties can be added by extending the (delay (make-datasource datasource-options))) (defn -main [& args] - (jdbc/with-db-connection [conn {:datasource @datasource}] + (with-open [conn (jdbc/get-connection @datasource {})] (let [rows (jdbc/query conn "SELECT 0")] (println rows))) (close-datasource @datasource)) @@ -245,12 +245,12 @@ Custom translations of properties can be added by extending the :max-lifetime 300000 :pool-name "clickhouse-conn-pool"}) -(defonce datasource +(defonce datasource (delay (make-datasource datasource-options))) (defn -main [& args] - (with-open [conn (jdbc/get-connection {:datasource @datasource})] + (with-open [conn (jdbc/get-connection @datasource {})] (let [rows (jdbc/execute! conn ["SELECT 0"])] (println rows))) (close-datasource @datasource)) @@ -277,7 +277,7 @@ Custom translations of properties can be added by extending the (defn -main [& args] - (with-open [conn (jdbc/get-connection {:datasource @datasource})] + (with-open [conn (jdbc/get-connection @datasource {})] (let [rows (jdbc/execute! conn ["SELECT 0"])] (println rows))) (close-datasource @datasource)) diff --git a/project.clj b/project.clj index c285da7..4f46317 100644 --- a/project.clj +++ b/project.clj @@ -7,17 +7,19 @@ :url "https://github.com/tomekw/hikari-cp"} :dependencies [[org.clojure/clojure "1.11.1" :scope "provided"] [org.tobereplaced/lettercase "1.0.0"] - [com.zaxxer/HikariCP "5.0.1"]] + [com.zaxxer/HikariCP "5.1.0"]] + :global-vars {*warn-on-reflection* true} :deploy-repositories [["clojars" {:sign-releases false :url "https://clojars.org/repo"}]] :profiles {:dev {:dependencies [[expectations "2.1.10"] - [org.slf4j/slf4j-nop "1.7.36"] - [org.clojure/java.jdbc "0.7.12"] - [mysql/mysql-connector-java "8.0.30"] - [org.neo4j.driver/neo4j-java-driver "5.0.0"] - [org.postgresql/postgresql "42.5.0"] - [io.dropwizard.metrics/metrics-core "4.2.12"] - [io.dropwizard.metrics/metrics-healthchecks "4.2.12"] + [org.slf4j/slf4j-nop "2.0.9"] + [com.github.seancorfield/next.jdbc "1.3.894"] + [mysql/mysql-connector-java "8.0.33"] + [org.neo4j.driver/neo4j-java-driver "5.15.0"] + [org.postgresql/postgresql "42.7.0"] + [io.dropwizard.metrics/metrics-core "4.2.22"] + [io.dropwizard.metrics/metrics-healthchecks "4.2.22"] [io.prometheus/simpleclient "0.16.0"] + [com.h2database/h2 "2.2.224"] ; The Oracle driver is only accessible from maven.oracle.com ; which requires a userId and password diff --git a/src/hikari_cp/core.clj b/src/hikari_cp/core.clj index 1656bf1..cd0584d 100644 --- a/src/hikari_cp/core.clj +++ b/src/hikari_cp/core.clj @@ -194,7 +194,7 @@ (defn datasource-config "Create datasource config from `datasource-options`" - [datasource-options] + ^HikariConfig [datasource-options] (let [config (HikariConfig.) options (validate-options datasource-options) not-core-options (apply dissoc options core-options) @@ -273,3 +273,13 @@ "Close given `datasource`" [^HikariDataSource datasource] (.close datasource)) + +(defn is-running? + "Check if given `datasource` is running" + [^HikariDataSource datasource] + (.isRunning datasource)) + +(defn is-closed? + "Check if given `datasource` is closed" + [^HikariDataSource datasource] + (.isClosed datasource)) diff --git a/test/hikari_cp/connection_test.clj b/test/hikari_cp/connection_test.clj new file mode 100644 index 0000000..909db96 --- /dev/null +++ b/test/hikari_cp/connection_test.clj @@ -0,0 +1,32 @@ +(ns hikari-cp.connection-test + (:require + [expectations :refer [expect]] + [hikari-cp.core :as hikari-cp] + [next.jdbc :as jdbc])) + +(let [pool (hikari-cp/make-datasource {:adapter "h2" + :url "jdbc:h2:mem:test" + ;; :register-mbeans true + ;; :connection-timeout 1000 + ;; :connection-test-query "select 0" + })] + + (with-open [connection (jdbc/get-connection pool {})] + (let [result (jdbc/execute-one! connection ["select 1 as count"])] + (expect {:COUNT 1} result))) + + ;; NOTE: + ;; can't do this: (expect true (hikari-cp/is-running? pool)) + ;; most likely due to how expectations works + (let [running? (hikari-cp/is-running? pool) + closed? (hikari-cp/is-closed? pool)] + (expect true running?) + (expect false closed?)) + + (hikari-cp/close-datasource pool) + + (let [running? (hikari-cp/is-running? pool) + closed? (hikari-cp/is-closed? pool)] + + (expect false running?) + (expect true closed?))) diff --git a/test/hikari_cp/core_test.clj b/test/hikari_cp/core_test.clj index 73a1aad..cac0317 100644 --- a/test/hikari_cp/core_test.clj +++ b/test/hikari_cp/core_test.clj @@ -1,10 +1,13 @@ (ns hikari-cp.core-test - (:require [hikari-cp.core :refer :all]) - (:use expectations) - (:import (com.zaxxer.hikari.pool HikariPool$PoolInitializationException) - (com.codahale.metrics MetricRegistry) - (com.codahale.metrics.health HealthCheckRegistry) - (com.zaxxer.hikari.metrics.prometheus PrometheusMetricsTrackerFactory))) + (:require + [hikari-cp.core :as hikari-cp] + [expectations :refer [expect]]) + (:import + (com.codahale.metrics MetricRegistry) + (com.codahale.metrics.health HealthCheckRegistry) + com.zaxxer.hikari.HikariConfig + (com.zaxxer.hikari.metrics.prometheus PrometheusMetricsTrackerFactory) + (com.zaxxer.hikari.pool HikariPool$PoolInitializationException))) (def valid-options {:auto-commit false @@ -44,36 +47,39 @@ (def metrics-tracker-factory-options {:metrics-tracker-factory (PrometheusMetricsTrackerFactory.)}) -(def datasource-config-with-required-settings - (datasource-config (apply dissoc valid-options (keys default-datasource-options)))) +(def ^HikariConfig datasource-config-with-required-settings + (hikari-cp/datasource-config (apply dissoc valid-options (keys hikari-cp/default-datasource-options)))) -(def datasource-config-with-overrides - (datasource-config valid-options)) +(def ^HikariConfig datasource-config-with-overrides + (hikari-cp/datasource-config valid-options)) -(def datasource-config-with-overrides-alternate - (datasource-config (-> (dissoc valid-options :adapter) +(def ^HikariConfig datasource-config-with-overrides-alternate + (hikari-cp/datasource-config (-> (dissoc valid-options :adapter) (merge alternate-valid-options)))) -(def datasource-config-with-overrides-alternate2 - (datasource-config (-> (dissoc valid-options :adapter) +(def ^HikariConfig datasource-config-with-overrides-alternate2 + (hikari-cp/datasource-config (-> (dissoc valid-options :adapter) (merge alternate-valid-options2)))) -(def mysql8-datasource-config - (datasource-config (merge valid-options {:adapter "mysql8"}))) +(def ^HikariConfig mysql8-datasource-config + (hikari-cp/datasource-config (merge valid-options {:adapter "mysql8"}))) -(def mysql-datasource-config - (datasource-config (merge valid-options +(def ^HikariConfig mysql-datasource-config + (hikari-cp/datasource-config (merge valid-options {:adapter "mysql" :use-legacy-datetime-code false}))) -(def metric-registry-config (datasource-config (merge valid-options metric-registry-options))) +(def ^HikariConfig metric-registry-config + (hikari-cp/datasource-config (merge valid-options metric-registry-options))) -(def health-check-registry-config (datasource-config (merge valid-options health-check-registry-options))) +(def ^HikariConfig health-check-registry-config + (hikari-cp/datasource-config (merge valid-options health-check-registry-options))) -(def metrics-tracker-factory-config (datasource-config (merge valid-options metrics-tracker-factory-options))) +(def ^HikariConfig metrics-tracker-factory-config + (hikari-cp/datasource-config (merge valid-options metrics-tracker-factory-options))) (expect false - (get (.getDataSourceProperties mysql-datasource-config) "useLegacyDatetimeCode")) + (get (.getDataSourceProperties ^HikariConfig mysql-datasource-config) "useLegacyDatetimeCode")) (expect "com.mysql.jdbc.jdbc2.optional.MysqlDataSource" (.getDataSourceClassName mysql-datasource-config)) (expect "com.mysql.cj.jdbc.MysqlDataSource" @@ -156,106 +162,106 @@ (.getDataSourceClassName datasource-config-with-overrides-alternate2)) (expect IllegalArgumentException - (datasource-config (dissoc valid-options :adapter))) + (hikari-cp/datasource-config (dissoc valid-options :adapter))) (expect #"contains\? % :adapter" (try - (datasource-config (validate-options (dissoc valid-options :adapter))) + (hikari-cp/datasource-config (hikari-cp/validate-options (dissoc valid-options :adapter))) (catch IllegalArgumentException e (str (.getMessage e))))) (expect "jdbc:postgres:test" - (.getJdbcUrl (datasource-config {:jdbc-url "jdbc:postgres:test"}))) + (.getJdbcUrl (hikari-cp/datasource-config {:jdbc-url "jdbc:postgres:test"}))) (expect map? - (validate-options valid-options)) + (hikari-cp/validate-options valid-options)) (expect IllegalArgumentException - (validate-options (merge valid-options {:auto-commit 1}))) + (hikari-cp/validate-options (merge valid-options {:auto-commit 1}))) (expect IllegalArgumentException - (validate-options (merge valid-options {:read-only 1}))) + (hikari-cp/validate-options (merge valid-options {:read-only 1}))) (expect IllegalArgumentException - (validate-options (merge valid-options {:connection-timeout "foo"}))) + (hikari-cp/validate-options (merge valid-options {:connection-timeout "foo"}))) (expect IllegalArgumentException - (validate-options (merge valid-options {:connection-timeout 999}))) + (hikari-cp/validate-options (merge valid-options {:connection-timeout 999}))) (expect IllegalArgumentException - (validate-options (merge valid-options {:validation-timeout 999}))) + (hikari-cp/validate-options (merge valid-options {:validation-timeout 999}))) (expect IllegalArgumentException - (validate-options (merge valid-options {:idle-timeout -1}))) + (hikari-cp/validate-options (merge valid-options {:idle-timeout -1}))) (expect IllegalArgumentException - (validate-options (merge valid-options {:max-lifetime -1}))) + (hikari-cp/validate-options (merge valid-options {:max-lifetime -1}))) (expect IllegalArgumentException - (validate-options (merge valid-options {:minimum-idle -1}))) + (hikari-cp/validate-options (merge valid-options {:minimum-idle -1}))) (expect IllegalArgumentException - (validate-options (merge valid-options {:maximum-pool-size -1}))) + (hikari-cp/validate-options (merge valid-options {:maximum-pool-size -1}))) (expect IllegalArgumentException - (validate-options (merge valid-options {:maximum-pool-size 0}))) + (hikari-cp/validate-options (merge valid-options {:maximum-pool-size 0}))) (expect IllegalArgumentException - (validate-options (merge valid-options {:adapter :foo}))) + (hikari-cp/validate-options (merge valid-options {:adapter :foo}))) (expect IllegalArgumentException - (validate-options (merge valid-options {:datasource-classname "adsf"}))) + (hikari-cp/validate-options (merge valid-options {:datasource-classname "adsf"}))) (expect IllegalArgumentException - (validate-options (merge (dissoc valid-options :adapter) {:jdbc-url nil}))) + (hikari-cp/validate-options (merge (dissoc valid-options :adapter) {:jdbc-url nil}))) (expect IllegalArgumentException - (validate-options (merge (dissoc valid-options :adapter) {:jdbc-url "jdbc:h2:~/test" + (hikari-cp/validate-options (merge (dissoc valid-options :adapter) {:jdbc-url "jdbc:h2:~/test" :driver-class-name nil}))) (expect IllegalArgumentException - (validate-options (merge valid-options {:transaction-isolation 1}))) + (hikari-cp/validate-options (merge valid-options {:transaction-isolation 1}))) (expect map? - (validate-options (merge valid-options {:username nil}))) + (hikari-cp/validate-options (merge valid-options {:username nil}))) (expect map? - (validate-options (dissoc valid-options :username))) + (hikari-cp/validate-options (dissoc valid-options :username))) (expect map? - (validate-options (dissoc valid-options :password))) + (hikari-cp/validate-options (dissoc valid-options :password))) (expect map? - (validate-options (merge valid-options {:password nil}))) + (hikari-cp/validate-options (merge valid-options {:password nil}))) (expect map? - (validate-options (merge valid-options {:database-name nil}))) + (hikari-cp/validate-options (merge valid-options {:database-name nil}))) (expect map? - (validate-options (dissoc valid-options :database-name))) + (hikari-cp/validate-options (dissoc valid-options :database-name))) (expect map? - (validate-options (dissoc valid-options :server-name))) + (hikari-cp/validate-options (dissoc valid-options :server-name))) (expect map? - (validate-options (merge valid-options {:server-name nil}))) + (hikari-cp/validate-options (merge valid-options {:server-name nil}))) (expect map? - (validate-options (merge valid-options {:port-number -1}))) + (hikari-cp/validate-options (merge valid-options {:port-number -1}))) (expect map? - (validate-options (dissoc valid-options :port-number))) + (hikari-cp/validate-options (dissoc valid-options :port-number))) (expect map? - (validate-options (merge (dissoc valid-options :adapter) {:jdbc-url "jdbc:h2:~/test"}))) + (hikari-cp/validate-options (merge (dissoc valid-options :adapter) {:jdbc-url "jdbc:h2:~/test"}))) (expect map? - (validate-options (merge (dissoc valid-options :adapter) {:jdbc-url "jdbc:h2:~/test" + (hikari-cp/validate-options (merge (dissoc valid-options :adapter) {:jdbc-url "jdbc:h2:~/test" :driver-class-name "org.h2.Driver"}))) ;; -- check leak detections option ;; default should stay 0 (expect 0 (-> valid-options - (datasource-config) + (hikari-cp/datasource-config) (.getLeakDetectionThreshold))) ;; it should apply a correct value -(let [config (datasource-config (assoc valid-options :leak-detection-threshold 3000))] +(let [config (hikari-cp/datasource-config (assoc valid-options :leak-detection-threshold 3000))] (expect 3000 (.getLeakDetectionThreshold config))) ;; it should complain, that value is too small (expect IllegalArgumentException - (validate-options (assoc valid-options :leak-detection-threshold 1))) + (hikari-cp/validate-options (assoc valid-options :leak-detection-threshold 1))) (expect IllegalArgumentException - (validate-options (assoc valid-options :leak-detection-threshold 1999))) + (hikari-cp/validate-options (assoc valid-options :leak-detection-threshold 1999))) ;; Ensure that core options aren't being set as datasource properties (expect #{"portNumber" "databaseName" "serverName"} (set (keys (.getDataSourceProperties metric-registry-config)))) (expect HikariPool$PoolInitializationException - (make-datasource valid-options)) + (hikari-cp/make-datasource valid-options)) -(expect "tinyInt1isBit" (translate-property :tinyInt1isBit)) -(expect "tinyInt1isBit" (translate-property :tiny-int1is-bit)) -(expect "useSSL" (translate-property :useSSL)) -(expect "useSSL" (translate-property :use-ssl)) -(expect "useFoo" (translate-property :useFOO)) +(expect "tinyInt1isBit" (hikari-cp/translate-property :tinyInt1isBit)) +(expect "tinyInt1isBit" (hikari-cp/translate-property :tiny-int1is-bit)) +(expect "useSSL" (hikari-cp/translate-property :useSSL)) +(expect "useSSL" (hikari-cp/translate-property :use-ssl)) +(expect "useFoo" (hikari-cp/translate-property :useFOO)) ;; translate-property is extensible -(defmethod translate-property ::extend-translate-test [_] 42) -(expect 42 (translate-property ::extend-translate-test)) +(defmethod hikari-cp/translate-property ::extend-translate-test [_] 42) +(expect 42 (hikari-cp/translate-property ::extend-translate-test))