datahike-browser-tests 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (324) hide show
  1. package/.circleci/config.yml +405 -0
  2. package/.circleci/scripts/gen_ci.clj +194 -0
  3. package/.cirrus.yml +60 -0
  4. package/.clj-kondo/babashka/sci/config.edn +1 -0
  5. package/.clj-kondo/babashka/sci/sci/core.clj +9 -0
  6. package/.clj-kondo/config.edn +95 -0
  7. package/.dir-locals.el +2 -0
  8. package/.github/FUNDING.yml +3 -0
  9. package/.github/ISSUE_TEMPLATE/1-bug-report.yml +68 -0
  10. package/.github/ISSUE_TEMPLATE/2-feature-request.yml +28 -0
  11. package/.github/ISSUE_TEMPLATE/config.yml +6 -0
  12. package/.github/pull_request_template.md +24 -0
  13. package/.github/workflows/native-image.yml +84 -0
  14. package/LICENSE +203 -0
  15. package/README.md +273 -0
  16. package/bb/deps.edn +9 -0
  17. package/bb/resources/github-fingerprints +3 -0
  18. package/bb/resources/native-image-tests/run-bb-pod-tests.clj +162 -0
  19. package/bb/resources/native-image-tests/run-libdatahike-tests +12 -0
  20. package/bb/resources/native-image-tests/run-native-image-tests +74 -0
  21. package/bb/resources/native-image-tests/run-python-tests +22 -0
  22. package/bb/resources/native-image-tests/testconfig.attr-refs.edn +6 -0
  23. package/bb/resources/native-image-tests/testconfig.edn +5 -0
  24. package/bb/resources/template/.settings/org.eclipse.jdt.apt.core.prefs +2 -0
  25. package/bb/resources/template/.settings/org.eclipse.jdt.core.prefs +9 -0
  26. package/bb/resources/template/.settings/org.eclipse.m2e.core.prefs +4 -0
  27. package/bb/resources/template/pom.xml +22 -0
  28. package/bb/src/tools/build.clj +132 -0
  29. package/bb/src/tools/clj_kondo.clj +32 -0
  30. package/bb/src/tools/deploy.clj +26 -0
  31. package/bb/src/tools/examples.clj +19 -0
  32. package/bb/src/tools/npm.clj +100 -0
  33. package/bb/src/tools/python.clj +14 -0
  34. package/bb/src/tools/release.clj +94 -0
  35. package/bb/src/tools/test.clj +148 -0
  36. package/bb/src/tools/version.clj +47 -0
  37. package/bb.edn +269 -0
  38. package/benchmark/src/benchmark/cli.clj +195 -0
  39. package/benchmark/src/benchmark/compare.clj +157 -0
  40. package/benchmark/src/benchmark/config.clj +316 -0
  41. package/benchmark/src/benchmark/measure.clj +187 -0
  42. package/benchmark/src/benchmark/store.clj +190 -0
  43. package/benchmark/test/benchmark/measure_test.clj +156 -0
  44. package/build.clj +30 -0
  45. package/config.edn +49 -0
  46. package/deps.edn +138 -0
  47. package/dev/sandbox.clj +82 -0
  48. package/dev/sandbox.cljs +127 -0
  49. package/dev/sandbox_benchmarks.clj +27 -0
  50. package/dev/sandbox_client.clj +87 -0
  51. package/dev/sandbox_transact_bench.clj +109 -0
  52. package/dev/user.clj +79 -0
  53. package/doc/README.md +96 -0
  54. package/doc/adl/README.md +6 -0
  55. package/doc/adl/adr-000-adr.org +28 -0
  56. package/doc/adl/adr-001-attribute-references.org +15 -0
  57. package/doc/adl/adr-002-build-tooling.org +54 -0
  58. package/doc/adl/adr-003-db-meta-data.md +52 -0
  59. package/doc/adl/adr-004-github-flow.md +40 -0
  60. package/doc/adl/adr-XYZ-template.md +30 -0
  61. package/doc/adl/index.org +3 -0
  62. package/doc/assets/datahike-logo.svg +3 -0
  63. package/doc/assets/datahiking-invoice.org +85 -0
  64. package/doc/assets/hhtree2.png +0 -0
  65. package/doc/assets/network_topology.svg +624 -0
  66. package/doc/assets/perf.png +0 -0
  67. package/doc/assets/schema_mindmap.mm +132 -0
  68. package/doc/assets/schema_mindmap.svg +970 -0
  69. package/doc/assets/temporal_index.mm +74 -0
  70. package/doc/backend-development.md +78 -0
  71. package/doc/bb-pod.md +89 -0
  72. package/doc/benchmarking.md +360 -0
  73. package/doc/bindings/edn-conversion.md +383 -0
  74. package/doc/cli.md +162 -0
  75. package/doc/cljdoc.edn +27 -0
  76. package/doc/cljs-support.md +133 -0
  77. package/doc/config.md +406 -0
  78. package/doc/contributing.md +114 -0
  79. package/doc/datalog-vs-sql.md +210 -0
  80. package/doc/datomic_differences.md +109 -0
  81. package/doc/development/pull-api-ns.md +186 -0
  82. package/doc/development/pull-frame-state-diagram.jpg +0 -0
  83. package/doc/distributed.md +566 -0
  84. package/doc/entity_spec.md +92 -0
  85. package/doc/gc.md +273 -0
  86. package/doc/java-api.md +808 -0
  87. package/doc/javascript-api.md +421 -0
  88. package/doc/libdatahike.md +86 -0
  89. package/doc/logging_and_error_handling.md +43 -0
  90. package/doc/norms.md +66 -0
  91. package/doc/schema-migration.md +85 -0
  92. package/doc/schema.md +287 -0
  93. package/doc/storage-backends.md +363 -0
  94. package/doc/store-id-refactoring.md +596 -0
  95. package/doc/time_variance.md +325 -0
  96. package/doc/unstructured.md +167 -0
  97. package/doc/versioning.md +261 -0
  98. package/examples/basic/README.md +19 -0
  99. package/examples/basic/deps.edn +6 -0
  100. package/examples/basic/docker-compose.yml +13 -0
  101. package/examples/basic/src/examples/core.clj +60 -0
  102. package/examples/basic/src/examples/schema.clj +155 -0
  103. package/examples/basic/src/examples/store.clj +60 -0
  104. package/examples/basic/src/examples/time_travel.clj +185 -0
  105. package/examples/java/.settings/org.eclipse.core.resources.prefs +3 -0
  106. package/examples/java/.settings/org.eclipse.jdt.apt.core.prefs +2 -0
  107. package/examples/java/.settings/org.eclipse.jdt.core.prefs +9 -0
  108. package/examples/java/.settings/org.eclipse.m2e.core.prefs +4 -0
  109. package/examples/java/README.md +162 -0
  110. package/examples/java/pom.xml +62 -0
  111. package/examples/java/src/main/java/examples/QuickStart.java +115 -0
  112. package/examples/java/src/main/java/examples/SchemaExample.java +148 -0
  113. package/examples/java/src/main/java/examples/TimeTravelExample.java +121 -0
  114. package/flake.lock +27 -0
  115. package/flake.nix +27 -0
  116. package/http-server/datahike/http/middleware.clj +75 -0
  117. package/http-server/datahike/http/server.clj +269 -0
  118. package/java/src/datahike/java/Database.java +274 -0
  119. package/java/src/datahike/java/Datahike.java +281 -0
  120. package/java/src/datahike/java/DatahikeGeneratedTest.java +349 -0
  121. package/java/src/datahike/java/DatahikeTest.java +370 -0
  122. package/java/src/datahike/java/EDN.java +170 -0
  123. package/java/src/datahike/java/IEntity.java +11 -0
  124. package/java/src/datahike/java/Keywords.java +161 -0
  125. package/java/src/datahike/java/SchemaFlexibility.java +52 -0
  126. package/java/src/datahike/java/Util.java +219 -0
  127. package/karma.conf.js +19 -0
  128. package/libdatahike/compile-cpp +7 -0
  129. package/libdatahike/src/datahike/impl/LibDatahikeBase.java +203 -0
  130. package/libdatahike/src/datahike/impl/libdatahike.clj +59 -0
  131. package/libdatahike/src/test_cpp.cpp +61 -0
  132. package/npm-package/PUBLISHING.md +140 -0
  133. package/npm-package/README.md +226 -0
  134. package/npm-package/package.template.json +34 -0
  135. package/npm-package/test-isomorphic.ts +281 -0
  136. package/npm-package/test.js +557 -0
  137. package/npm-package/typescript-test.ts +70 -0
  138. package/package.json +16 -0
  139. package/pydatahike/README.md +569 -0
  140. package/pydatahike/pyproject.toml +91 -0
  141. package/pydatahike/setup.py +42 -0
  142. package/pydatahike/src/datahike/__init__.py +134 -0
  143. package/pydatahike/src/datahike/_native.py +250 -0
  144. package/pydatahike/src/datahike/_version.py +2 -0
  145. package/pydatahike/src/datahike/database.py +722 -0
  146. package/pydatahike/src/datahike/edn.py +311 -0
  147. package/pydatahike/src/datahike/py.typed +0 -0
  148. package/pydatahike/tests/conftest.py +17 -0
  149. package/pydatahike/tests/test_basic.py +170 -0
  150. package/pydatahike/tests/test_database.py +51 -0
  151. package/pydatahike/tests/test_edn_conversion.py +299 -0
  152. package/pydatahike/tests/test_query.py +99 -0
  153. package/pydatahike/tests/test_schema.py +55 -0
  154. package/resources/clj-kondo.exports/io.replikativ/datahike/config.edn +5 -0
  155. package/resources/example_server.edn +4 -0
  156. package/shadow-cljs.edn +56 -0
  157. package/src/data_readers.clj +7 -0
  158. package/src/datahike/api/impl.cljc +176 -0
  159. package/src/datahike/api/specification.cljc +633 -0
  160. package/src/datahike/api/types.cljc +261 -0
  161. package/src/datahike/api.cljc +41 -0
  162. package/src/datahike/array.cljc +99 -0
  163. package/src/datahike/cli.clj +166 -0
  164. package/src/datahike/cljs.cljs +6 -0
  165. package/src/datahike/codegen/cli.clj +406 -0
  166. package/src/datahike/codegen/clj_kondo.clj +291 -0
  167. package/src/datahike/codegen/java.clj +403 -0
  168. package/src/datahike/codegen/naming.cljc +33 -0
  169. package/src/datahike/codegen/native.clj +559 -0
  170. package/src/datahike/codegen/pod.clj +488 -0
  171. package/src/datahike/codegen/python.clj +838 -0
  172. package/src/datahike/codegen/report.clj +55 -0
  173. package/src/datahike/codegen/typescript.clj +262 -0
  174. package/src/datahike/codegen/validation.clj +145 -0
  175. package/src/datahike/config.cljc +294 -0
  176. package/src/datahike/connections.cljc +16 -0
  177. package/src/datahike/connector.cljc +265 -0
  178. package/src/datahike/constants.cljc +142 -0
  179. package/src/datahike/core.cljc +297 -0
  180. package/src/datahike/datom.cljc +459 -0
  181. package/src/datahike/db/interface.cljc +119 -0
  182. package/src/datahike/db/search.cljc +305 -0
  183. package/src/datahike/db/transaction.cljc +937 -0
  184. package/src/datahike/db/utils.cljc +338 -0
  185. package/src/datahike/db.cljc +956 -0
  186. package/src/datahike/experimental/unstructured.cljc +126 -0
  187. package/src/datahike/experimental/versioning.cljc +172 -0
  188. package/src/datahike/externs.js +31 -0
  189. package/src/datahike/gc.cljc +69 -0
  190. package/src/datahike/http/client.clj +188 -0
  191. package/src/datahike/http/writer.clj +79 -0
  192. package/src/datahike/impl/entity.cljc +218 -0
  193. package/src/datahike/index/interface.cljc +93 -0
  194. package/src/datahike/index/persistent_set.cljc +469 -0
  195. package/src/datahike/index/utils.cljc +44 -0
  196. package/src/datahike/index.cljc +32 -0
  197. package/src/datahike/js/api.cljs +172 -0
  198. package/src/datahike/js/api_macros.clj +22 -0
  199. package/src/datahike/js.cljs +163 -0
  200. package/src/datahike/json.cljc +209 -0
  201. package/src/datahike/lru.cljc +146 -0
  202. package/src/datahike/migrate.clj +39 -0
  203. package/src/datahike/norm/norm.clj +245 -0
  204. package/src/datahike/online_gc.cljc +252 -0
  205. package/src/datahike/pod.clj +155 -0
  206. package/src/datahike/pull_api.cljc +325 -0
  207. package/src/datahike/query.cljc +1945 -0
  208. package/src/datahike/query_stats.cljc +88 -0
  209. package/src/datahike/readers.cljc +62 -0
  210. package/src/datahike/remote.cljc +218 -0
  211. package/src/datahike/schema.cljc +228 -0
  212. package/src/datahike/schema_cache.cljc +42 -0
  213. package/src/datahike/spec.cljc +101 -0
  214. package/src/datahike/store.cljc +80 -0
  215. package/src/datahike/tools.cljc +308 -0
  216. package/src/datahike/transit.cljc +80 -0
  217. package/src/datahike/writer.cljc +239 -0
  218. package/src/datahike/writing.cljc +362 -0
  219. package/src/deps.cljs +1 -0
  220. package/src-hitchhiker-tree/datahike/index/hitchhiker_tree/insert.cljc +76 -0
  221. package/src-hitchhiker-tree/datahike/index/hitchhiker_tree/upsert.cljc +128 -0
  222. package/src-hitchhiker-tree/datahike/index/hitchhiker_tree.cljc +213 -0
  223. package/test/datahike/backward_compatibility_test/src/backward_test.clj +37 -0
  224. package/test/datahike/integration_test/config_record_file_test.clj +14 -0
  225. package/test/datahike/integration_test/config_record_test.clj +14 -0
  226. package/test/datahike/integration_test/depr_config_uri_test.clj +15 -0
  227. package/test/datahike/integration_test/return_map_test.clj +62 -0
  228. package/test/datahike/integration_test.cljc +67 -0
  229. package/test/datahike/norm/norm_test.clj +124 -0
  230. package/test/datahike/norm/resources/naming-and-sorting-test/001-a1-example.edn +5 -0
  231. package/test/datahike/norm/resources/naming-and-sorting-test/002-a2-example.edn +5 -0
  232. package/test/datahike/norm/resources/naming-and-sorting-test/003-tx-fn-test.edn +1 -0
  233. package/test/datahike/norm/resources/naming-and-sorting-test/004-tx-data-and-tx-fn-test.edn +5 -0
  234. package/test/datahike/norm/resources/naming-and-sorting-test/01-transact-basic-characters.edn +2 -0
  235. package/test/datahike/norm/resources/naming-and-sorting-test/02 add occupation.edn +5 -0
  236. package/test/datahike/norm/resources/naming-and-sorting-test/checksums.edn +12 -0
  237. package/test/datahike/norm/resources/simple-test/001-a1-example.edn +5 -0
  238. package/test/datahike/norm/resources/simple-test/002-a2-example.edn +5 -0
  239. package/test/datahike/norm/resources/simple-test/checksums.edn +4 -0
  240. package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/001-a1-example.edn +5 -0
  241. package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/002-a2-example.edn +5 -0
  242. package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/003-tx-fn-test.edn +1 -0
  243. package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/checksums.edn +6 -0
  244. package/test/datahike/norm/resources/tx-data-and-tx-fn-test/second/004-tx-data-and-tx-fn-test.edn +5 -0
  245. package/test/datahike/norm/resources/tx-data-and-tx-fn-test/second/checksums.edn +2 -0
  246. package/test/datahike/norm/resources/tx-fn-test/first/001-a1-example.edn +5 -0
  247. package/test/datahike/norm/resources/tx-fn-test/first/002-a2-example.edn +5 -0
  248. package/test/datahike/norm/resources/tx-fn-test/first/checksums.edn +4 -0
  249. package/test/datahike/norm/resources/tx-fn-test/second/003-tx-fn-test.edn +1 -0
  250. package/test/datahike/norm/resources/tx-fn-test/second/checksums.edn +2 -0
  251. package/test/datahike/test/api_test.cljc +895 -0
  252. package/test/datahike/test/array_test.cljc +40 -0
  253. package/test/datahike/test/attribute_refs/datoms_test.cljc +140 -0
  254. package/test/datahike/test/attribute_refs/db_test.cljc +42 -0
  255. package/test/datahike/test/attribute_refs/differences_test.cljc +515 -0
  256. package/test/datahike/test/attribute_refs/entity_test.cljc +89 -0
  257. package/test/datahike/test/attribute_refs/pull_api_test.cljc +320 -0
  258. package/test/datahike/test/attribute_refs/query_find_specs_test.cljc +59 -0
  259. package/test/datahike/test/attribute_refs/query_fns_test.cljc +130 -0
  260. package/test/datahike/test/attribute_refs/query_interop_test.cljc +47 -0
  261. package/test/datahike/test/attribute_refs/query_not_test.cljc +193 -0
  262. package/test/datahike/test/attribute_refs/query_or_test.cljc +137 -0
  263. package/test/datahike/test/attribute_refs/query_pull_test.cljc +156 -0
  264. package/test/datahike/test/attribute_refs/query_rules_test.cljc +176 -0
  265. package/test/datahike/test/attribute_refs/query_test.cljc +241 -0
  266. package/test/datahike/test/attribute_refs/temporal_search.cljc +22 -0
  267. package/test/datahike/test/attribute_refs/transact_test.cljc +220 -0
  268. package/test/datahike/test/attribute_refs/utils.cljc +128 -0
  269. package/test/datahike/test/cache_test.cljc +38 -0
  270. package/test/datahike/test/components_test.cljc +92 -0
  271. package/test/datahike/test/config_test.cljc +158 -0
  272. package/test/datahike/test/core_test.cljc +105 -0
  273. package/test/datahike/test/datom_test.cljc +44 -0
  274. package/test/datahike/test/db_test.cljc +54 -0
  275. package/test/datahike/test/entity_spec_test.cljc +159 -0
  276. package/test/datahike/test/entity_test.cljc +103 -0
  277. package/test/datahike/test/explode_test.cljc +143 -0
  278. package/test/datahike/test/filter_test.cljc +75 -0
  279. package/test/datahike/test/gc_test.cljc +159 -0
  280. package/test/datahike/test/http/server_test.clj +192 -0
  281. package/test/datahike/test/http/writer_test.clj +86 -0
  282. package/test/datahike/test/ident_test.cljc +32 -0
  283. package/test/datahike/test/index_test.cljc +345 -0
  284. package/test/datahike/test/insert.cljc +125 -0
  285. package/test/datahike/test/java_bindings_test.clj +6 -0
  286. package/test/datahike/test/listen_test.cljc +41 -0
  287. package/test/datahike/test/lookup_refs_test.cljc +266 -0
  288. package/test/datahike/test/lru_test.cljc +27 -0
  289. package/test/datahike/test/migrate_test.clj +297 -0
  290. package/test/datahike/test/model/core.cljc +376 -0
  291. package/test/datahike/test/model/invariant.cljc +142 -0
  292. package/test/datahike/test/model/rng.cljc +82 -0
  293. package/test/datahike/test/model_test.clj +217 -0
  294. package/test/datahike/test/nodejs_test.cljs +262 -0
  295. package/test/datahike/test/online_gc_test.cljc +475 -0
  296. package/test/datahike/test/pod_test.clj +369 -0
  297. package/test/datahike/test/pull_api_test.cljc +474 -0
  298. package/test/datahike/test/purge_test.cljc +144 -0
  299. package/test/datahike/test/query_aggregates_test.cljc +101 -0
  300. package/test/datahike/test/query_find_specs_test.cljc +52 -0
  301. package/test/datahike/test/query_fns_test.cljc +523 -0
  302. package/test/datahike/test/query_interop_test.cljc +47 -0
  303. package/test/datahike/test/query_not_test.cljc +189 -0
  304. package/test/datahike/test/query_or_test.cljc +158 -0
  305. package/test/datahike/test/query_pull_test.cljc +147 -0
  306. package/test/datahike/test/query_rules_test.cljc +248 -0
  307. package/test/datahike/test/query_stats_test.cljc +218 -0
  308. package/test/datahike/test/query_test.cljc +984 -0
  309. package/test/datahike/test/schema_test.cljc +424 -0
  310. package/test/datahike/test/specification_test.cljc +30 -0
  311. package/test/datahike/test/store_test.cljc +78 -0
  312. package/test/datahike/test/stress_test.cljc +57 -0
  313. package/test/datahike/test/time_variance_test.cljc +518 -0
  314. package/test/datahike/test/tools_test.clj +134 -0
  315. package/test/datahike/test/transact_test.cljc +518 -0
  316. package/test/datahike/test/tuples_test.cljc +564 -0
  317. package/test/datahike/test/unstructured_test.cljc +291 -0
  318. package/test/datahike/test/upsert_impl_test.cljc +205 -0
  319. package/test/datahike/test/upsert_test.cljc +363 -0
  320. package/test/datahike/test/utils.cljc +110 -0
  321. package/test/datahike/test/validation_test.cljc +48 -0
  322. package/test/datahike/test/versioning_test.cljc +56 -0
  323. package/test/datahike/test.cljc +66 -0
  324. package/tests.edn +24 -0
@@ -0,0 +1,294 @@
1
+ (ns ^:no-doc datahike.config
2
+ (:require [clojure.edn :as edn]
3
+ [clojure.walk :refer [postwalk]]
4
+ [clojure.string :as str]
5
+ [clojure.spec.alpha :as s]
6
+ [environ.core :refer [env]]
7
+ [datahike.tools :as dt]
8
+ [datahike.store :as ds]
9
+ [datahike.index :as di]
10
+ [taoensso.timbre :as log])
11
+ (:import #?(:clj [java.net URI]
12
+ :cljs [goog.Uri])))
13
+
14
+ ;; global
15
+ (def ^:dynamic *schema-meta-cache-size* (env :schema-meta-cache-size 1024))
16
+ (def ^:dynamic *schema-write-cache-max-db-count* (env :schema-write-cache-size 1024))
17
+
18
+ ;; per database
19
+ (def ^:dynamic *default-index* :datahike.index/persistent-set)
20
+ (def ^:dynamic *default-schema-flexibility* :write)
21
+ (def ^:dynamic *default-keep-history?* true)
22
+ (def ^:dynamic *default-attribute-refs?* false)
23
+ (def ^:dynamic *default-search-cache-size* 10000)
24
+ (def ^:dynamic *default-store-cache-size* 1000)
25
+ (def ^:dynamic *default-crypto-hash?* false)
26
+ (def ^:dynamic *default-store* :memory) ;; store-less = in-memory?
27
+ (def ^:dynamic *default-db-name* nil) ;; when nil creates random name
28
+ (def ^:dynamic *default-db-branch* :db) ;; when nil creates random name
29
+
30
+ (s/def ::index #{:datahike.index/hitchhiker-tree :datahike.index/persistent-set})
31
+ (s/def ::keep-history? boolean?)
32
+ (s/def ::schema-flexibility #{:read :write})
33
+ (s/def ::attribute-refs? boolean?)
34
+ (s/def ::search-cache-size nat-int?)
35
+ (s/def ::store-cache-size pos-int?)
36
+ (s/def ::crypto-hash? boolean?)
37
+ (s/def ::writer map?)
38
+ (s/def ::branch keyword?)
39
+ (s/def ::entity (s/or :map associative? :vec vector?))
40
+ (s/def ::initial-tx (s/nilable (s/or :data (s/coll-of ::entity) :path string?
41
+ :was-added? boolean?)))
42
+ (s/def ::name string?)
43
+
44
+ (s/def ::index-config map?)
45
+
46
+ (s/def ::store map?)
47
+
48
+ (s/def :datahike/config (s/keys :opt-un [:datahike/store
49
+ ::index
50
+ ::index-config
51
+ ::keep-history?
52
+ ::schema-flexibility
53
+ ::attribute-refs?
54
+ ::search-cache-size
55
+ ::store-cache-size
56
+ ::crypto-hash?
57
+ ::initial-tx
58
+ ::name
59
+ ::branch
60
+ ::writer]))
61
+
62
+ (s/def :deprecated/schema-on-read boolean?)
63
+ (s/def :deprecated/temporal-index boolean?)
64
+ (s/def :deprecated/config (s/keys :req-un [:datahike/store]
65
+ :opt-un [:deprecated/temporal-index :deprecated/schema-on-read]))
66
+
67
+ (def self-writer {:backend :self})
68
+
69
+ (defn from-deprecated
70
+ [{:keys [backend username password path host port id] :as _backend-cfg}
71
+ & {:keys [schema-on-read temporal-index index initial-tx]
72
+ :as _index-cfg
73
+ :or {schema-on-read false
74
+ index *default-index*
75
+ temporal-index true}}]
76
+ {:store (merge {:backend backend}
77
+ (case backend
78
+ :memory {:id (or id
79
+ (when (or host path)
80
+ #?(:clj (java.util.UUID/nameUUIDFromBytes (.getBytes (str (or host path)) "UTF-8"))
81
+ :cljs (uuid (str (or host path))))))}
82
+ :pg {:username username
83
+ :password password
84
+ :path path
85
+ :host host
86
+ :port port
87
+ :id (or id #?(:clj (java.util.UUID/randomUUID) :cljs (random-uuid)))}
88
+ :level {:path path
89
+ :id (or id
90
+ (when path
91
+ #?(:clj (java.util.UUID/nameUUIDFromBytes (.getBytes path "UTF-8"))
92
+ :cljs (uuid path))))}
93
+ :file {:path path
94
+ :id (or id
95
+ (when path
96
+ #?(:clj (java.util.UUID/nameUUIDFromBytes (.getBytes path "UTF-8"))
97
+ :cljs (uuid path))))}))
98
+ :index index
99
+ :index-config (di/default-index-config index)
100
+ :keep-history? temporal-index
101
+ :attribute-refs? *default-attribute-refs?*
102
+ :initial-tx initial-tx
103
+ :schema-flexibility (if (true? schema-on-read) :read :write)
104
+ :branch *default-db-branch*
105
+ :writer self-writer
106
+ :crypto-hash? *default-crypto-hash?*
107
+ :search-cache-size *default-search-cache-size*
108
+ :store-cache-size *default-store-cache-size*})
109
+
110
+ (defn int-from-env
111
+ [key default]
112
+ (try
113
+ (#?(:clj Integer/parseInt :cljs js/parseInt) (get env key (str default)))
114
+ (catch #?(:clj Exception :cljs js/Error) _ default)))
115
+
116
+ (defn bool-from-env
117
+ [key default]
118
+ (try
119
+ #?(:clj (Boolean/parseBoolean (get env key default))
120
+ :cljs (= "true" (get env key default)))
121
+ (catch #?(:clj Exception :cljs js/Error) _ default)))
122
+
123
+ (defn map-from-env [key default]
124
+ (try
125
+ (edn/read-string (get env key (str default)))
126
+ (catch #?(:clj Exception :cljs js/Error) _ default)))
127
+
128
+ (defn validate-config-attribute [attribute value config]
129
+ (when-not (s/valid? attribute value)
130
+ (throw (ex-info (str "Bad value " value " at " (name attribute)
131
+ ", value does not match configuration definition. Must be conform to: "
132
+ (s/describe attribute)) config))))
133
+
134
+ (defn validate-config [{:keys [attribute-refs? schema-flexibility] :as config}]
135
+ (when-not (s/valid? :datahike/config config)
136
+ (throw (ex-info "Invalid Datahike configuration." (s/explain-data :datahike/config config))))
137
+ (when (and attribute-refs? (= :read schema-flexibility))
138
+ (throw (ex-info "Attribute references cannot be used with schema-flexibility ':read'." config))))
139
+
140
+ (defn storeless-config []
141
+ {:store nil
142
+ :keep-history? false
143
+ :schema-flexibility :read
144
+ :attribute-refs? false
145
+ :index *default-index*
146
+ :search-cache-size *default-search-cache-size*
147
+ :store-cache-size *default-store-cache-size*
148
+ :crypto-hash? *default-crypto-hash?*
149
+ :branch *default-db-branch*
150
+ :writer self-writer
151
+ :index-config (di/default-index-config *default-index*)})
152
+
153
+ (defn remove-nils
154
+ "Thanks to https://stackoverflow.com/a/34221816"
155
+ [m]
156
+ (let [f (fn [x]
157
+ (if (map? x)
158
+ (let [kvs (filter (comp not nil? second) x)]
159
+ (if (empty? kvs) nil (into {} kvs)))
160
+ x))]
161
+ (postwalk f m)))
162
+
163
+ (defn- validate-store-backend
164
+ "Validate that the store backend is compatible with Datahike's sync query engine.
165
+ Throws an informative error for async-only backends like IndexedDB."
166
+ [store-config]
167
+ (when (= :indexeddb (:backend store-config))
168
+ (throw (ex-info
169
+ (str "The :indexeddb backend cannot be used directly.\n\n"
170
+ "Datahike's query engine requires synchronous reads, but "
171
+ "IndexedDB is async-only.\n\n"
172
+ "Use a TieredStore with memory frontend instead:\n\n"
173
+ "{:store {:backend :tiered\n"
174
+ " :id <your-uuid>\n"
175
+ " :frontend-config {:backend :memory :id <your-uuid>}\n"
176
+ " :backend-config {:backend :indexeddb :id <your-uuid>}}}\n\n"
177
+ "See: https://github.com/replikativ/datahike/blob/main/doc/cljs-support.md")
178
+ {:type :async-only-backend
179
+ :backend :indexeddb
180
+ :config store-config}))))
181
+
182
+ (defn load-config
183
+ "Load and validate configuration with defaults from the store."
184
+ ([]
185
+ (load-config nil nil))
186
+ ([config-as-arg]
187
+ (load-config config-as-arg nil))
188
+ ([config-as-arg opts]
189
+ (let [config-as-arg (if (s/valid? :datahike/config-depr config-as-arg)
190
+ (apply from-deprecated config-as-arg (first opts))
191
+ config-as-arg)
192
+ ;; Store config now provided by user - konserve validates :id requirement
193
+ raw-store-config (merge
194
+ {:backend (keyword (:datahike-store-backend env *default-store*))}
195
+ (:store config-as-arg))
196
+ ;; Normalize :mem to :memory for backward compatibility
197
+ store-config (if (= (:backend raw-store-config) :mem)
198
+ (do
199
+ (log/warn "DEPRECATION: :mem backend is deprecated, use :memory instead. Support for :mem will be removed in a future version.")
200
+ (assoc raw-store-config :backend :memory))
201
+ raw-store-config)
202
+ _ (validate-store-backend store-config)
203
+ index (if (:datahike-index env)
204
+ (keyword "datahike.index" (:datahike-index env))
205
+ *default-index*)
206
+ config {:store store-config
207
+ :initial-tx (:datahike-initial-tx env)
208
+ :keep-history? (bool-from-env :datahike-keep-history *default-keep-history?*)
209
+ :attribute-refs? (bool-from-env :datahike-attribute-refs *default-attribute-refs?*)
210
+ :schema-flexibility (keyword (:datahike-schema-flexibility env *default-schema-flexibility*))
211
+ :index index
212
+ :branch *default-db-branch*
213
+ :crypto-hash? *default-crypto-hash?*
214
+ :writer self-writer
215
+ :search-cache-size (int-from-env :datahike-search-cache-size *default-search-cache-size*)
216
+ :store-cache-size (int-from-env :datahike-store-cache-size *default-store-cache-size*)
217
+ :index-config (if-let [index-config (map-from-env :datahike-index-config nil)]
218
+ index-config
219
+ (di/default-index-config index))}
220
+ merged-config ((comp remove-nils dt/deep-merge) config config-as-arg)
221
+ {:keys [schema-flexibility initial-tx store attribute-refs?]} merged-config]
222
+ ;; konserve now handles store config validation at runtime
223
+ (when-not (s/valid? :datahike/config merged-config)
224
+ (throw (ex-info "Invalid Datahike configuration." (s/explain-data :datahike/config merged-config))))
225
+ (when (and attribute-refs? (= :read schema-flexibility))
226
+ (throw (ex-info "Attribute references cannot be used with schema-flexibility ':read'." config)))
227
+ (if (string? initial-tx)
228
+ #?(:clj (update merged-config :initial-tx (fn [path] (-> path slurp read-string)))
229
+ :cljs (throw (ex-info ":initial-tx from path is not supported in cljs at this time" merged-config)))
230
+ merged-config))))
231
+
232
+ ;; deprecation begin
233
+ (s/def ::backend-depr keyword?)
234
+ (s/def ::username-depr string?)
235
+ (s/def ::password-depr string?)
236
+ (s/def ::path-depr string?)
237
+ (s/def ::host-depr string?)
238
+ (s/def ::port-depr int?)
239
+ (s/def ::uri-depr string?)
240
+
241
+ (s/def :datahike/config-depr (s/keys :req-un [::backend]
242
+ :opt-un [::username ::password ::path ::host ::port]))
243
+
244
+ (defn uri->config [uri]
245
+ (log/warn "DEPRECATION WARNING: URI string format for Datahike configuration is deprecated and may be removed in a future version. Please migrate to the map-based configuration format. See documentation for details.")
246
+ (let [base-uri (#?(:clj URI. :cljs goog.Uri.) uri)
247
+ _ (when-not (= (.getScheme base-uri) "datahike")
248
+ (throw (ex-info "URI scheme is not datahike conform." {:uri uri})))
249
+ sub-uri #?(:clj (URI. (.getSchemeSpecificPart base-uri))
250
+ :cljs (goog.Uri. (.getScheme base-uri)))
251
+ backend-raw (keyword (.getScheme sub-uri))
252
+ ;; Normalize :mem to :memory for backward compatibility
253
+ backend (if (= backend-raw :mem) :memory backend-raw)
254
+ [username password] (when-let [user-info (.getUserInfo sub-uri)]
255
+ (str/split user-info #":"))
256
+ credentials (when-not (and (nil? username) (nil? password))
257
+ {:username username
258
+ :password password})
259
+ port (.getPort sub-uri)
260
+ path (.getPath sub-uri)
261
+ host (.getHost sub-uri)
262
+ ;; Parse query parameters to extract id if present
263
+ query-string #?(:clj (.getQuery sub-uri)
264
+ :cljs (when-let [qd (.getQueryData sub-uri)]
265
+ (when (.containsKey qd "id")
266
+ (str "id=" (.get qd "id")))))
267
+ parsed-id (when query-string
268
+ (when-let [[_ id-str] (re-find #"id=([0-9a-fA-F-]+)" query-string)]
269
+ #?(:clj (try (java.util.UUID/fromString id-str)
270
+ (catch Exception e
271
+ (throw (ex-info "Invalid UUID format in ?id= query parameter"
272
+ {:uri uri :id-str id-str :error (.getMessage e)}))))
273
+ :cljs (try (uuid id-str)
274
+ (catch js/Error e
275
+ (throw (ex-info "Invalid UUID format in ?id= query parameter"
276
+ {:uri uri :id-str id-str :error (.-message e)})))))))
277
+ config (merge
278
+ {:backend backend
279
+ :uri uri}
280
+ credentials
281
+ (when host
282
+ {:host host})
283
+ (when-not (empty? path)
284
+ {:path path})
285
+ (when (<= 0 port)
286
+ {:port port})
287
+ (when parsed-id
288
+ {:id parsed-id}))]
289
+ config))
290
+
291
+ (defn validate-config-depr [config]
292
+ (when-not (s/valid? :datahike/config-depr config)
293
+ (throw (ex-info "Invalid datahike configuration." config))))
294
+ ;; deprecation end
@@ -0,0 +1,16 @@
1
+ (ns ^:no-doc datahike.connections)
2
+
3
+ (def ^:dynamic *connections* (atom {}))
4
+
5
+ (defn get-connection [conn-id]
6
+ (when-let [conn (get-in @*connections* [conn-id :conn])]
7
+ (swap! *connections* update-in [conn-id :count] inc)
8
+ conn))
9
+
10
+ (defn add-connection! [conn-id conn]
11
+ (swap! *connections* assoc conn-id {:conn conn :count 1}))
12
+
13
+ (defn delete-connection! [conn-id]
14
+ (when-let [conn (get-connection conn-id)]
15
+ (reset! conn :released)
16
+ (swap! *connections* dissoc conn-id)))
@@ -0,0 +1,265 @@
1
+ (ns ^:no-doc datahike.connector
2
+ (:require [datahike.connections :refer [get-connection add-connection! delete-connection!
3
+ *connections*]]
4
+ [datahike.readers]
5
+ [datahike.store :as ds]
6
+ [datahike.writing :as dsi]
7
+ [datahike.config :as dc]
8
+ [datahike.tools :as dt #?(:clj :refer :cljs :refer-macros) [meta-data]]
9
+ [datahike.writer :as w]
10
+ [konserve.core :as k]
11
+ [konserve.store :as ks]
12
+ [taoensso.timbre :as log]
13
+ [clojure.spec.alpha :as s]
14
+ [clojure.data :refer [diff]]
15
+ [konserve.utils :refer [#?(:clj async+sync) *default-sync-translation*]
16
+ #?@(:cljs [:refer-macros [async+sync]])]
17
+ [superv.async :refer [go-try- <?-]]
18
+ [clojure.core.async :refer [go] :as async])
19
+ #?(:clj (:import [clojure.lang IDeref IAtom IMeta ILookup IRef])))
20
+
21
+ ;; connection
22
+
23
+ (declare deref-conn)
24
+
25
+ (deftype Connection [wrapped-atom]
26
+ IDeref
27
+ (#?(:clj deref :cljs -deref) [conn] (deref-conn conn))
28
+ ;; These interfaces should not be used from the outside, they are here to keep
29
+ ;; the internal interfaces lean and working.
30
+ ILookup
31
+ (#?(:clj valAt :cljs -lookup) [c k] (if (= k :wrapped-atom) wrapped-atom nil))
32
+ IMeta
33
+ (#?(:clj meta :cljs -meta) [_] (meta wrapped-atom))
34
+ #?@(:cljs
35
+ [IAtom
36
+ ISwap
37
+ (-swap! [_ f] (swap! wrapped-atom f))
38
+ (-swap! [_ f arg] (swap! wrapped-atom f arg))
39
+ (-swap! [_ f arg1 arg2] (swap! wrapped-atom f arg1 arg2))
40
+ (-swap! [_ f arg1 arg2 args] (apply swap! wrapped-atom f arg1 arg2 args))
41
+ IReset
42
+ (-reset! [_ newval] (reset! wrapped-atom newval))
43
+ IWatchable ;; TODO This is unofficially supported, it triggers watches on each update, not on commits. For proper listeners use the API.
44
+ (-add-watch [_ key f] (add-watch wrapped-atom key f))
45
+ (-remove-watch [_ key] (remove-watch wrapped-atom key))
46
+ (-notify-watches [_ old new] (-notify-watches wrapped-atom old new))])
47
+ #?@(:clj
48
+ [IAtom
49
+ (swap [_ f] (swap! wrapped-atom f))
50
+ (swap [_ f arg] (swap! wrapped-atom f arg))
51
+ (swap [_ f arg1 arg2] (swap! wrapped-atom f arg1 arg2))
52
+ (swap [_ f arg1 arg2 args] (apply swap! wrapped-atom f arg1 arg2 args))
53
+ (compareAndSet [_ oldv newv] (compare-and-set! wrapped-atom oldv newv))
54
+ (reset [_ newval] (reset! wrapped-atom newval))
55
+ IRef ;; TODO This is unofficially supported, it triggers watches on each update, not on commits. For proper listeners use the API.
56
+ (addWatch [_ key f] (add-watch wrapped-atom key f))
57
+ (removeWatch [_ key] (remove-watch wrapped-atom key))]))
58
+
59
+ (defn connection? [x]
60
+ (instance? Connection x))
61
+
62
+ #?(:clj
63
+ (defmethod print-method Connection
64
+ [^Connection conn ^java.io.Writer w]
65
+ (let [config (:config @(:wrapped-atom conn))]
66
+ (.write w "#datahike/Connection")
67
+ (.write w (pr-str [(ds/store-identity (:store config)) (:branch config)])))))
68
+
69
+ (defn deref-conn [^Connection conn]
70
+ (let [wrapped-atom (.-wrapped-atom conn)]
71
+ (when (= @wrapped-atom :released)
72
+ (throw (ex-info "Connection has been released."
73
+ {:type :connection-has-been-released})))
74
+ (if (not (w/streaming? (get @wrapped-atom :writer)))
75
+ (let [store (:store @wrapped-atom)
76
+ stored (k/get store (:branch (:config @wrapped-atom)) nil {:sync? true})]
77
+ (log/trace "Fetched db for deref: " (:config stored))
78
+ (dsi/stored->db stored store))
79
+ @wrapped-atom)))
80
+
81
+ (defn conn-from-db
82
+ "Creates a mutable reference to a given immutable database. See [[create-conn]]."
83
+ [db]
84
+ (Connection. (atom db :meta {:listeners (atom {})})))
85
+
86
+ (s/def ::connection #(and (instance? Connection %)
87
+ (not= @(:wrapped-atom %) :released)))
88
+
89
+ (defn version-check [{:keys [meta config] :as db}]
90
+ (let [{dh-stored :datahike/version
91
+ hh-stored :hitchhiker.tree/version
92
+ pss-stored :persistent.set/version
93
+ ksv-stored :konserve/version} meta
94
+ {dh-now :datahike/version
95
+ hh-now :hitchhiker.tree/version
96
+ pss-now :persistent.set/version
97
+ ksv-now :konserve/version} (meta-data)]
98
+ (when-not (or (= dh-now "DEVELOPMENT")
99
+ (= dh-stored "DEVELOPMENT")
100
+ (>= (compare dh-now dh-stored) 0))
101
+ (dt/raise "Database was written with newer Datahike version."
102
+ {:type :db-was-written-with-newer-datahike-version
103
+ :stored dh-stored
104
+ :now dh-now
105
+ :config config}))
106
+ (when (and hh-stored hh-now
107
+ (not (>= (compare hh-now hh-stored) 0)))
108
+ (dt/raise "Database was written with newer hitchhiker-tree version."
109
+ {:type :db-was-written-with-newer-hht-version
110
+ :stored hh-stored
111
+ :now hh-now
112
+ :config config}))
113
+ (when-not (>= (compare pss-now pss-stored) 0)
114
+ (dt/raise "Database was written with newer persistent-sorted-set version."
115
+ {:type :db-was-written-with-newer-pss-version
116
+ :stored pss-stored
117
+ :now pss-now
118
+ :config config}))
119
+ (when-not (>= (compare ksv-now ksv-stored) 0)
120
+ (dt/raise "Database was written with newer konserve version."
121
+ {:type :db-was-written-with-newer-konserve-version
122
+ :stored ksv-stored
123
+ :now ksv-now
124
+ :config config}))))
125
+
126
+ (defn ensure-stored-config-consistency [config stored-config]
127
+ (let [;; Remove runtime parameters and creation-time parameters
128
+ config (dissoc config :name :search-cache-size :store-cache-size)
129
+ stored-config (dissoc stored-config :initial-tx :name :search-cache-size :store-cache-size)
130
+ stored-config (merge {:writer dc/self-writer} stored-config)
131
+ stored-config (if (empty? (:index-config stored-config))
132
+ (dissoc stored-config :index-config)
133
+ stored-config)
134
+ ;; if we connect to remote allow writer to be different
135
+ [config stored-config] (if-not (= dc/self-writer config)
136
+ [(dissoc config :writer)
137
+ (dissoc stored-config :writer)]
138
+ [config stored-config])
139
+
140
+ ;; Validate store identities match (prevents connecting to wrong database)
141
+ ;; Store configuration details (backend, path, credentials) can differ
142
+ stored-store-id (get-in stored-config [:store :id])
143
+ connect-store-id (get-in config [:store :id])
144
+ _ (when (and stored-store-id connect-store-id
145
+ (not= stored-store-id connect-store-id))
146
+ (dt/raise "Store identity mismatch: connecting to wrong database."
147
+ {:type :store-identity-mismatch
148
+ :stored-id stored-store-id
149
+ :connect-id connect-store-id
150
+ :config config
151
+ :stored-config stored-config}))
152
+
153
+ ;; Remove entire :store from comparison (backend, path, credentials can change)
154
+ ;; Only the :id needs to match (checked above)
155
+ config (dissoc config :store)
156
+ stored-config (dissoc stored-config :store)]
157
+
158
+ (when-not (= config stored-config)
159
+ (dt/raise "Configuration does not match stored configuration. In some cases this check is too restrictive. If you are sure you are loading the right database with the right configuration then you can disable this check by setting :allow-unsafe-config to true in your config."
160
+ {:type :config-does-not-match-stored-db
161
+ :config config
162
+ :stored-config stored-config
163
+ :diff (diff config stored-config)}))))
164
+
165
+ (defn- normalize-config [cfg]
166
+ (-> cfg
167
+ (dissoc :writer :store :store-cache-size :search-cache-size)))
168
+
169
+ (defn -connect-impl* [config opts]
170
+ (async+sync (:sync? opts) *default-sync-translation*
171
+ (go-try-
172
+ (let [_ (log/debug "Using config " (update-in config [:store] dissoc :password))
173
+ store-config (:store config)
174
+ store-id (ds/store-identity store-config)
175
+ conn-id [store-id (:branch config)]]
176
+ (if-let [conn (get-connection conn-id)]
177
+ (let [conn-config (:config @(:wrapped-atom conn))
178
+ ;; replace store config with its identity
179
+ cfg (normalize-config config)
180
+ conn-cfg (normalize-config conn-config)]
181
+ (when-not (= cfg conn-cfg)
182
+ (dt/raise "Configuration does not match existing connections."
183
+ {:type :config-does-not-match-existing-connections
184
+ :config cfg
185
+ :existing-connections-config conn-cfg
186
+ :diff (diff cfg conn-cfg)}))
187
+ conn)
188
+ (let [raw-store (<?- (ks/connect-store store-config opts))
189
+ _ (when-not raw-store
190
+ (dt/raise "Backend does not exist." {:type :backend-does-not-exist
191
+ :config store-config}))
192
+ store (ds/add-cache-and-handlers raw-store config)
193
+ _ (<?- (ds/ready-store (assoc store-config :opts opts) store))
194
+ stored-db (<?- (k/get store (:branch config) nil opts))
195
+ _ (when-not stored-db
196
+ (ks/release-store store-config store opts)
197
+ (dt/raise "Database does not exist." {:type :db-does-not-exist
198
+ :config config}))
199
+ [config store stored-db]
200
+ (let [intended-index (:index config)
201
+ stored-index (get-in stored-db [:config :index])]
202
+ (if-not (= intended-index stored-index)
203
+ (do
204
+ (log/warn (str "Stored index does not match configuration. Please set :index explicitly to " stored-index " in config. The default index is now :datahike/persistent-set. Using stored index setting now, but this might throw an error in the future."))
205
+ (let [config (assoc config :index stored-index)
206
+ store (ds/add-cache-and-handlers raw-store config)
207
+ _ (<?- (ds/ready-store (assoc store-config :opts opts) store))
208
+ stored-db (<?- (k/get store (:branch config) nil opts))]
209
+ [config store stored-db]))
210
+ [config store stored-db]))
211
+ _ (version-check stored-db)
212
+ _ (when-not (:allow-unsafe-config config)
213
+ (ensure-stored-config-consistency config (:config stored-db)))
214
+ conn (conn-from-db (dsi/stored->db (assoc stored-db :config config) store))]
215
+ (swap! (:wrapped-atom conn) assoc :writer
216
+ (w/create-writer (:writer config) conn))
217
+ (add-connection! conn-id conn)
218
+ conn))))))
219
+
220
+ ;; Multimethod dispatch for different writer backends
221
+
222
+ (defn backend-dispatch [config & _]
223
+ (get-in config [:writer :backend] :self))
224
+
225
+ (defmulti -connect* #'backend-dispatch)
226
+
227
+ (defmethod -connect* :self [config opts]
228
+ (-connect-impl* config opts))
229
+
230
+ ;; public API
231
+
232
+ (defn connect
233
+ "Connect to a Datahike database.
234
+
235
+ Config can be a map or URI string. Opts map supports:
236
+ - :sync? (default true) - Block and return connection, or return channel for async"
237
+ ([] (connect {} {}))
238
+ ([config] (connect config {}))
239
+ ([config opts]
240
+ (let [opts (merge {:sync? true} opts)
241
+ normalized (cond
242
+ (string? config) (dc/uri->config config)
243
+ (map? config) config
244
+ :else config)
245
+ loaded (dissoc (dc/load-config normalized) :initial-tx :remote-peer :name)]
246
+ (-connect* loaded opts))))
247
+
248
+ (defn release
249
+ ([connection] (release connection false))
250
+ ([connection release-all?]
251
+ (when-not (= @(:wrapped-atom connection) :released)
252
+ (let [db @(:wrapped-atom connection)
253
+ _ (log/info "Releasing connection, config store backend:"
254
+ (get-in db [:config :store :backend]))
255
+ conn-id [(ds/store-identity (get-in db [:config :store]))
256
+ (get-in db [:config :branch])]]
257
+ (if-not (get @*connections* conn-id)
258
+ (log/info "Connection already released." conn-id)
259
+ (let [new-conns (swap! *connections* update-in [conn-id :count] dec)]
260
+ (when (or release-all? (zero? (get-in new-conns [conn-id :count])))
261
+ (delete-connection! conn-id)
262
+ (w/shutdown (:writer db))
263
+ ;; Release the underlying store to clean up resources (memory registry, etc.)
264
+ (ks/release-store (get-in db [:config :store]) (:store db))
265
+ nil)))))))