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.
- package/.circleci/config.yml +405 -0
- package/.circleci/scripts/gen_ci.clj +194 -0
- package/.cirrus.yml +60 -0
- package/.clj-kondo/babashka/sci/config.edn +1 -0
- package/.clj-kondo/babashka/sci/sci/core.clj +9 -0
- package/.clj-kondo/config.edn +95 -0
- package/.dir-locals.el +2 -0
- package/.github/FUNDING.yml +3 -0
- package/.github/ISSUE_TEMPLATE/1-bug-report.yml +68 -0
- package/.github/ISSUE_TEMPLATE/2-feature-request.yml +28 -0
- package/.github/ISSUE_TEMPLATE/config.yml +6 -0
- package/.github/pull_request_template.md +24 -0
- package/.github/workflows/native-image.yml +84 -0
- package/LICENSE +203 -0
- package/README.md +273 -0
- package/bb/deps.edn +9 -0
- package/bb/resources/github-fingerprints +3 -0
- package/bb/resources/native-image-tests/run-bb-pod-tests.clj +162 -0
- package/bb/resources/native-image-tests/run-libdatahike-tests +12 -0
- package/bb/resources/native-image-tests/run-native-image-tests +74 -0
- package/bb/resources/native-image-tests/run-python-tests +22 -0
- package/bb/resources/native-image-tests/testconfig.attr-refs.edn +6 -0
- package/bb/resources/native-image-tests/testconfig.edn +5 -0
- package/bb/resources/template/.settings/org.eclipse.jdt.apt.core.prefs +2 -0
- package/bb/resources/template/.settings/org.eclipse.jdt.core.prefs +9 -0
- package/bb/resources/template/.settings/org.eclipse.m2e.core.prefs +4 -0
- package/bb/resources/template/pom.xml +22 -0
- package/bb/src/tools/build.clj +132 -0
- package/bb/src/tools/clj_kondo.clj +32 -0
- package/bb/src/tools/deploy.clj +26 -0
- package/bb/src/tools/examples.clj +19 -0
- package/bb/src/tools/npm.clj +100 -0
- package/bb/src/tools/python.clj +14 -0
- package/bb/src/tools/release.clj +94 -0
- package/bb/src/tools/test.clj +148 -0
- package/bb/src/tools/version.clj +47 -0
- package/bb.edn +269 -0
- package/benchmark/src/benchmark/cli.clj +195 -0
- package/benchmark/src/benchmark/compare.clj +157 -0
- package/benchmark/src/benchmark/config.clj +316 -0
- package/benchmark/src/benchmark/measure.clj +187 -0
- package/benchmark/src/benchmark/store.clj +190 -0
- package/benchmark/test/benchmark/measure_test.clj +156 -0
- package/build.clj +30 -0
- package/config.edn +49 -0
- package/deps.edn +138 -0
- package/dev/sandbox.clj +82 -0
- package/dev/sandbox.cljs +127 -0
- package/dev/sandbox_benchmarks.clj +27 -0
- package/dev/sandbox_client.clj +87 -0
- package/dev/sandbox_transact_bench.clj +109 -0
- package/dev/user.clj +79 -0
- package/doc/README.md +96 -0
- package/doc/adl/README.md +6 -0
- package/doc/adl/adr-000-adr.org +28 -0
- package/doc/adl/adr-001-attribute-references.org +15 -0
- package/doc/adl/adr-002-build-tooling.org +54 -0
- package/doc/adl/adr-003-db-meta-data.md +52 -0
- package/doc/adl/adr-004-github-flow.md +40 -0
- package/doc/adl/adr-XYZ-template.md +30 -0
- package/doc/adl/index.org +3 -0
- package/doc/assets/datahike-logo.svg +3 -0
- package/doc/assets/datahiking-invoice.org +85 -0
- package/doc/assets/hhtree2.png +0 -0
- package/doc/assets/network_topology.svg +624 -0
- package/doc/assets/perf.png +0 -0
- package/doc/assets/schema_mindmap.mm +132 -0
- package/doc/assets/schema_mindmap.svg +970 -0
- package/doc/assets/temporal_index.mm +74 -0
- package/doc/backend-development.md +78 -0
- package/doc/bb-pod.md +89 -0
- package/doc/benchmarking.md +360 -0
- package/doc/bindings/edn-conversion.md +383 -0
- package/doc/cli.md +162 -0
- package/doc/cljdoc.edn +27 -0
- package/doc/cljs-support.md +133 -0
- package/doc/config.md +406 -0
- package/doc/contributing.md +114 -0
- package/doc/datalog-vs-sql.md +210 -0
- package/doc/datomic_differences.md +109 -0
- package/doc/development/pull-api-ns.md +186 -0
- package/doc/development/pull-frame-state-diagram.jpg +0 -0
- package/doc/distributed.md +566 -0
- package/doc/entity_spec.md +92 -0
- package/doc/gc.md +273 -0
- package/doc/java-api.md +808 -0
- package/doc/javascript-api.md +421 -0
- package/doc/libdatahike.md +86 -0
- package/doc/logging_and_error_handling.md +43 -0
- package/doc/norms.md +66 -0
- package/doc/schema-migration.md +85 -0
- package/doc/schema.md +287 -0
- package/doc/storage-backends.md +363 -0
- package/doc/store-id-refactoring.md +596 -0
- package/doc/time_variance.md +325 -0
- package/doc/unstructured.md +167 -0
- package/doc/versioning.md +261 -0
- package/examples/basic/README.md +19 -0
- package/examples/basic/deps.edn +6 -0
- package/examples/basic/docker-compose.yml +13 -0
- package/examples/basic/src/examples/core.clj +60 -0
- package/examples/basic/src/examples/schema.clj +155 -0
- package/examples/basic/src/examples/store.clj +60 -0
- package/examples/basic/src/examples/time_travel.clj +185 -0
- package/examples/java/.settings/org.eclipse.core.resources.prefs +3 -0
- package/examples/java/.settings/org.eclipse.jdt.apt.core.prefs +2 -0
- package/examples/java/.settings/org.eclipse.jdt.core.prefs +9 -0
- package/examples/java/.settings/org.eclipse.m2e.core.prefs +4 -0
- package/examples/java/README.md +162 -0
- package/examples/java/pom.xml +62 -0
- package/examples/java/src/main/java/examples/QuickStart.java +115 -0
- package/examples/java/src/main/java/examples/SchemaExample.java +148 -0
- package/examples/java/src/main/java/examples/TimeTravelExample.java +121 -0
- package/flake.lock +27 -0
- package/flake.nix +27 -0
- package/http-server/datahike/http/middleware.clj +75 -0
- package/http-server/datahike/http/server.clj +269 -0
- package/java/src/datahike/java/Database.java +274 -0
- package/java/src/datahike/java/Datahike.java +281 -0
- package/java/src/datahike/java/DatahikeGeneratedTest.java +349 -0
- package/java/src/datahike/java/DatahikeTest.java +370 -0
- package/java/src/datahike/java/EDN.java +170 -0
- package/java/src/datahike/java/IEntity.java +11 -0
- package/java/src/datahike/java/Keywords.java +161 -0
- package/java/src/datahike/java/SchemaFlexibility.java +52 -0
- package/java/src/datahike/java/Util.java +219 -0
- package/karma.conf.js +19 -0
- package/libdatahike/compile-cpp +7 -0
- package/libdatahike/src/datahike/impl/LibDatahikeBase.java +203 -0
- package/libdatahike/src/datahike/impl/libdatahike.clj +59 -0
- package/libdatahike/src/test_cpp.cpp +61 -0
- package/npm-package/PUBLISHING.md +140 -0
- package/npm-package/README.md +226 -0
- package/npm-package/package.template.json +34 -0
- package/npm-package/test-isomorphic.ts +281 -0
- package/npm-package/test.js +557 -0
- package/npm-package/typescript-test.ts +70 -0
- package/package.json +16 -0
- package/pydatahike/README.md +569 -0
- package/pydatahike/pyproject.toml +91 -0
- package/pydatahike/setup.py +42 -0
- package/pydatahike/src/datahike/__init__.py +134 -0
- package/pydatahike/src/datahike/_native.py +250 -0
- package/pydatahike/src/datahike/_version.py +2 -0
- package/pydatahike/src/datahike/database.py +722 -0
- package/pydatahike/src/datahike/edn.py +311 -0
- package/pydatahike/src/datahike/py.typed +0 -0
- package/pydatahike/tests/conftest.py +17 -0
- package/pydatahike/tests/test_basic.py +170 -0
- package/pydatahike/tests/test_database.py +51 -0
- package/pydatahike/tests/test_edn_conversion.py +299 -0
- package/pydatahike/tests/test_query.py +99 -0
- package/pydatahike/tests/test_schema.py +55 -0
- package/resources/clj-kondo.exports/io.replikativ/datahike/config.edn +5 -0
- package/resources/example_server.edn +4 -0
- package/shadow-cljs.edn +56 -0
- package/src/data_readers.clj +7 -0
- package/src/datahike/api/impl.cljc +176 -0
- package/src/datahike/api/specification.cljc +633 -0
- package/src/datahike/api/types.cljc +261 -0
- package/src/datahike/api.cljc +41 -0
- package/src/datahike/array.cljc +99 -0
- package/src/datahike/cli.clj +166 -0
- package/src/datahike/cljs.cljs +6 -0
- package/src/datahike/codegen/cli.clj +406 -0
- package/src/datahike/codegen/clj_kondo.clj +291 -0
- package/src/datahike/codegen/java.clj +403 -0
- package/src/datahike/codegen/naming.cljc +33 -0
- package/src/datahike/codegen/native.clj +559 -0
- package/src/datahike/codegen/pod.clj +488 -0
- package/src/datahike/codegen/python.clj +838 -0
- package/src/datahike/codegen/report.clj +55 -0
- package/src/datahike/codegen/typescript.clj +262 -0
- package/src/datahike/codegen/validation.clj +145 -0
- package/src/datahike/config.cljc +294 -0
- package/src/datahike/connections.cljc +16 -0
- package/src/datahike/connector.cljc +265 -0
- package/src/datahike/constants.cljc +142 -0
- package/src/datahike/core.cljc +297 -0
- package/src/datahike/datom.cljc +459 -0
- package/src/datahike/db/interface.cljc +119 -0
- package/src/datahike/db/search.cljc +305 -0
- package/src/datahike/db/transaction.cljc +937 -0
- package/src/datahike/db/utils.cljc +338 -0
- package/src/datahike/db.cljc +956 -0
- package/src/datahike/experimental/unstructured.cljc +126 -0
- package/src/datahike/experimental/versioning.cljc +172 -0
- package/src/datahike/externs.js +31 -0
- package/src/datahike/gc.cljc +69 -0
- package/src/datahike/http/client.clj +188 -0
- package/src/datahike/http/writer.clj +79 -0
- package/src/datahike/impl/entity.cljc +218 -0
- package/src/datahike/index/interface.cljc +93 -0
- package/src/datahike/index/persistent_set.cljc +469 -0
- package/src/datahike/index/utils.cljc +44 -0
- package/src/datahike/index.cljc +32 -0
- package/src/datahike/js/api.cljs +172 -0
- package/src/datahike/js/api_macros.clj +22 -0
- package/src/datahike/js.cljs +163 -0
- package/src/datahike/json.cljc +209 -0
- package/src/datahike/lru.cljc +146 -0
- package/src/datahike/migrate.clj +39 -0
- package/src/datahike/norm/norm.clj +245 -0
- package/src/datahike/online_gc.cljc +252 -0
- package/src/datahike/pod.clj +155 -0
- package/src/datahike/pull_api.cljc +325 -0
- package/src/datahike/query.cljc +1945 -0
- package/src/datahike/query_stats.cljc +88 -0
- package/src/datahike/readers.cljc +62 -0
- package/src/datahike/remote.cljc +218 -0
- package/src/datahike/schema.cljc +228 -0
- package/src/datahike/schema_cache.cljc +42 -0
- package/src/datahike/spec.cljc +101 -0
- package/src/datahike/store.cljc +80 -0
- package/src/datahike/tools.cljc +308 -0
- package/src/datahike/transit.cljc +80 -0
- package/src/datahike/writer.cljc +239 -0
- package/src/datahike/writing.cljc +362 -0
- package/src/deps.cljs +1 -0
- package/src-hitchhiker-tree/datahike/index/hitchhiker_tree/insert.cljc +76 -0
- package/src-hitchhiker-tree/datahike/index/hitchhiker_tree/upsert.cljc +128 -0
- package/src-hitchhiker-tree/datahike/index/hitchhiker_tree.cljc +213 -0
- package/test/datahike/backward_compatibility_test/src/backward_test.clj +37 -0
- package/test/datahike/integration_test/config_record_file_test.clj +14 -0
- package/test/datahike/integration_test/config_record_test.clj +14 -0
- package/test/datahike/integration_test/depr_config_uri_test.clj +15 -0
- package/test/datahike/integration_test/return_map_test.clj +62 -0
- package/test/datahike/integration_test.cljc +67 -0
- package/test/datahike/norm/norm_test.clj +124 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/001-a1-example.edn +5 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/002-a2-example.edn +5 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/003-tx-fn-test.edn +1 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/004-tx-data-and-tx-fn-test.edn +5 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/01-transact-basic-characters.edn +2 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/02 add occupation.edn +5 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/checksums.edn +12 -0
- package/test/datahike/norm/resources/simple-test/001-a1-example.edn +5 -0
- package/test/datahike/norm/resources/simple-test/002-a2-example.edn +5 -0
- package/test/datahike/norm/resources/simple-test/checksums.edn +4 -0
- package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/001-a1-example.edn +5 -0
- package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/002-a2-example.edn +5 -0
- package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/003-tx-fn-test.edn +1 -0
- package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/checksums.edn +6 -0
- package/test/datahike/norm/resources/tx-data-and-tx-fn-test/second/004-tx-data-and-tx-fn-test.edn +5 -0
- package/test/datahike/norm/resources/tx-data-and-tx-fn-test/second/checksums.edn +2 -0
- package/test/datahike/norm/resources/tx-fn-test/first/001-a1-example.edn +5 -0
- package/test/datahike/norm/resources/tx-fn-test/first/002-a2-example.edn +5 -0
- package/test/datahike/norm/resources/tx-fn-test/first/checksums.edn +4 -0
- package/test/datahike/norm/resources/tx-fn-test/second/003-tx-fn-test.edn +1 -0
- package/test/datahike/norm/resources/tx-fn-test/second/checksums.edn +2 -0
- package/test/datahike/test/api_test.cljc +895 -0
- package/test/datahike/test/array_test.cljc +40 -0
- package/test/datahike/test/attribute_refs/datoms_test.cljc +140 -0
- package/test/datahike/test/attribute_refs/db_test.cljc +42 -0
- package/test/datahike/test/attribute_refs/differences_test.cljc +515 -0
- package/test/datahike/test/attribute_refs/entity_test.cljc +89 -0
- package/test/datahike/test/attribute_refs/pull_api_test.cljc +320 -0
- package/test/datahike/test/attribute_refs/query_find_specs_test.cljc +59 -0
- package/test/datahike/test/attribute_refs/query_fns_test.cljc +130 -0
- package/test/datahike/test/attribute_refs/query_interop_test.cljc +47 -0
- package/test/datahike/test/attribute_refs/query_not_test.cljc +193 -0
- package/test/datahike/test/attribute_refs/query_or_test.cljc +137 -0
- package/test/datahike/test/attribute_refs/query_pull_test.cljc +156 -0
- package/test/datahike/test/attribute_refs/query_rules_test.cljc +176 -0
- package/test/datahike/test/attribute_refs/query_test.cljc +241 -0
- package/test/datahike/test/attribute_refs/temporal_search.cljc +22 -0
- package/test/datahike/test/attribute_refs/transact_test.cljc +220 -0
- package/test/datahike/test/attribute_refs/utils.cljc +128 -0
- package/test/datahike/test/cache_test.cljc +38 -0
- package/test/datahike/test/components_test.cljc +92 -0
- package/test/datahike/test/config_test.cljc +158 -0
- package/test/datahike/test/core_test.cljc +105 -0
- package/test/datahike/test/datom_test.cljc +44 -0
- package/test/datahike/test/db_test.cljc +54 -0
- package/test/datahike/test/entity_spec_test.cljc +159 -0
- package/test/datahike/test/entity_test.cljc +103 -0
- package/test/datahike/test/explode_test.cljc +143 -0
- package/test/datahike/test/filter_test.cljc +75 -0
- package/test/datahike/test/gc_test.cljc +159 -0
- package/test/datahike/test/http/server_test.clj +192 -0
- package/test/datahike/test/http/writer_test.clj +86 -0
- package/test/datahike/test/ident_test.cljc +32 -0
- package/test/datahike/test/index_test.cljc +345 -0
- package/test/datahike/test/insert.cljc +125 -0
- package/test/datahike/test/java_bindings_test.clj +6 -0
- package/test/datahike/test/listen_test.cljc +41 -0
- package/test/datahike/test/lookup_refs_test.cljc +266 -0
- package/test/datahike/test/lru_test.cljc +27 -0
- package/test/datahike/test/migrate_test.clj +297 -0
- package/test/datahike/test/model/core.cljc +376 -0
- package/test/datahike/test/model/invariant.cljc +142 -0
- package/test/datahike/test/model/rng.cljc +82 -0
- package/test/datahike/test/model_test.clj +217 -0
- package/test/datahike/test/nodejs_test.cljs +262 -0
- package/test/datahike/test/online_gc_test.cljc +475 -0
- package/test/datahike/test/pod_test.clj +369 -0
- package/test/datahike/test/pull_api_test.cljc +474 -0
- package/test/datahike/test/purge_test.cljc +144 -0
- package/test/datahike/test/query_aggregates_test.cljc +101 -0
- package/test/datahike/test/query_find_specs_test.cljc +52 -0
- package/test/datahike/test/query_fns_test.cljc +523 -0
- package/test/datahike/test/query_interop_test.cljc +47 -0
- package/test/datahike/test/query_not_test.cljc +189 -0
- package/test/datahike/test/query_or_test.cljc +158 -0
- package/test/datahike/test/query_pull_test.cljc +147 -0
- package/test/datahike/test/query_rules_test.cljc +248 -0
- package/test/datahike/test/query_stats_test.cljc +218 -0
- package/test/datahike/test/query_test.cljc +984 -0
- package/test/datahike/test/schema_test.cljc +424 -0
- package/test/datahike/test/specification_test.cljc +30 -0
- package/test/datahike/test/store_test.cljc +78 -0
- package/test/datahike/test/stress_test.cljc +57 -0
- package/test/datahike/test/time_variance_test.cljc +518 -0
- package/test/datahike/test/tools_test.clj +134 -0
- package/test/datahike/test/transact_test.cljc +518 -0
- package/test/datahike/test/tuples_test.cljc +564 -0
- package/test/datahike/test/unstructured_test.cljc +291 -0
- package/test/datahike/test/upsert_impl_test.cljc +205 -0
- package/test/datahike/test/upsert_test.cljc +363 -0
- package/test/datahike/test/utils.cljc +110 -0
- package/test/datahike/test/validation_test.cljc +48 -0
- package/test/datahike/test/versioning_test.cljc +56 -0
- package/test/datahike/test.cljc +66 -0
- package/tests.edn +24 -0
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
(ns datahike.test.online-gc-test
|
|
2
|
+
"Comprehensive tests for online garbage collection.
|
|
3
|
+
|
|
4
|
+
Tests verify exact freed address counts to detect subtle memory issues.
|
|
5
|
+
Pattern: 3 + 2n addresses for n data transactions
|
|
6
|
+
- Schema tx: 3 (EAVT, AEVT, AVET roots)
|
|
7
|
+
- Data tx: 2 (EAVT, AEVT only - AVET empty for non-indexed attrs)"
|
|
8
|
+
(:require
|
|
9
|
+
#?(:cljs [cljs.test :as t :refer-macros [is deftest testing]]
|
|
10
|
+
:clj [clojure.test :as t :refer [is deftest testing]])
|
|
11
|
+
[datahike.api :as d]
|
|
12
|
+
[datahike.online-gc :as online-gc]
|
|
13
|
+
[konserve.core :as k]
|
|
14
|
+
#?(:clj [clojure.core.async :as async]
|
|
15
|
+
:cljs [cljs.core.async :as async]))
|
|
16
|
+
#?(:clj (:import [java.util Date])))
|
|
17
|
+
|
|
18
|
+
#?(:cljs (def Throwable js/Error))
|
|
19
|
+
|
|
20
|
+
(defn- count-store [db]
|
|
21
|
+
(count (k/keys (:store db) {:sync? true})))
|
|
22
|
+
|
|
23
|
+
(defn- get-freed-addresses [db]
|
|
24
|
+
"Get current freed addresses vector"
|
|
25
|
+
@(-> db :store :storage :freed-addresses))
|
|
26
|
+
|
|
27
|
+
(defn- get-freed-count [db]
|
|
28
|
+
(count (get-freed-addresses db)))
|
|
29
|
+
|
|
30
|
+
(def base-cfg {:store {:backend :file
|
|
31
|
+
:path "/tmp/online-gc-test"
|
|
32
|
+
:id #uuid "a0000000-0000-0000-0000-000000000001"}
|
|
33
|
+
:keep-history? false
|
|
34
|
+
:schema-flexibility :write
|
|
35
|
+
:index :datahike.index/persistent-set})
|
|
36
|
+
|
|
37
|
+
(def schema [{:db/ident :name
|
|
38
|
+
:db/cardinality :db.cardinality/one
|
|
39
|
+
:db/valueType :db.type/string}
|
|
40
|
+
{:db/ident :age
|
|
41
|
+
:db/cardinality :db.cardinality/one
|
|
42
|
+
:db/valueType :db.type/long}])
|
|
43
|
+
|
|
44
|
+
;;; ============================================================================
|
|
45
|
+
;;; Exact Freed Count Tests (Most Important)
|
|
46
|
+
;;; ============================================================================
|
|
47
|
+
|
|
48
|
+
(deftest precise-freed-count-tracking-test
|
|
49
|
+
(testing "Track exact number of freed addresses per transaction"
|
|
50
|
+
(let [cfg (-> base-cfg
|
|
51
|
+
(assoc-in [:store :path] "/tmp/online-gc-precise-freed-test")
|
|
52
|
+
(assoc :online-gc {:enabled? false})) ;; Disabled to accumulate
|
|
53
|
+
conn (do
|
|
54
|
+
(d/delete-database cfg)
|
|
55
|
+
(d/create-database cfg)
|
|
56
|
+
(d/connect cfg))]
|
|
57
|
+
|
|
58
|
+
;; Initially should be zero
|
|
59
|
+
(is (= 0 (get-freed-count @conn))
|
|
60
|
+
"Initially no freed addresses")
|
|
61
|
+
|
|
62
|
+
;; Transact schema
|
|
63
|
+
(d/transact conn schema)
|
|
64
|
+
(is (= 3 (get-freed-count @conn))
|
|
65
|
+
"Schema transaction frees 3 addresses (EAVT, AEVT, AVET roots)")
|
|
66
|
+
|
|
67
|
+
;; First data transaction
|
|
68
|
+
(d/transact conn [{:name "Alice" :age 30}])
|
|
69
|
+
(is (= 5 (get-freed-count @conn))
|
|
70
|
+
"Alice tx frees 2 more addresses (EAVT, AEVT roots). Total: 3+2=5")
|
|
71
|
+
|
|
72
|
+
;; Second transaction
|
|
73
|
+
(d/transact conn [{:name "Bob" :age 25}])
|
|
74
|
+
(is (= 7 (get-freed-count @conn))
|
|
75
|
+
"Bob tx frees 2 more addresses (EAVT, AEVT roots). Total: 5+2=7")
|
|
76
|
+
|
|
77
|
+
;; Third transaction
|
|
78
|
+
(d/transact conn [{:name "Charlie" :age 35}])
|
|
79
|
+
(is (= 9 (get-freed-count @conn))
|
|
80
|
+
"Charlie tx frees 2 more addresses (EAVT, AEVT roots). Total: 7+2=9")
|
|
81
|
+
|
|
82
|
+
;; Note: Only 2 per data tx, not 3, because AVET only stores indexed attributes.
|
|
83
|
+
;; Since :name and :age are not indexed, AVET stays empty and doesn't change.
|
|
84
|
+
|
|
85
|
+
;; Verify data is still queryable
|
|
86
|
+
(let [result (d/q '[:find ?name ?age
|
|
87
|
+
:where [?e :name ?name]
|
|
88
|
+
[?e :age ?age]]
|
|
89
|
+
@conn)]
|
|
90
|
+
(is (= 3 (count result))
|
|
91
|
+
"All entities should be queryable despite freed addresses"))
|
|
92
|
+
|
|
93
|
+
(d/release conn))))
|
|
94
|
+
|
|
95
|
+
(deftest precise-gc-deletion-test
|
|
96
|
+
(testing "Verify freed addresses are actually deleted from storage"
|
|
97
|
+
(let [cfg (-> base-cfg
|
|
98
|
+
(assoc-in [:store :path] "/tmp/online-gc-precise-deletion-test")
|
|
99
|
+
(assoc :online-gc {:enabled? true
|
|
100
|
+
:grace-period-ms 0
|
|
101
|
+
:max-batch 1000}))
|
|
102
|
+
conn (do
|
|
103
|
+
(d/delete-database cfg)
|
|
104
|
+
(d/create-database cfg)
|
|
105
|
+
(d/connect cfg))
|
|
106
|
+
initial-store-count (count-store @conn)]
|
|
107
|
+
|
|
108
|
+
;; Transact schema and data
|
|
109
|
+
(d/transact conn schema)
|
|
110
|
+
(d/transact conn [{:name "Alice" :age 30}])
|
|
111
|
+
(d/transact conn [{:name "Bob" :age 25}])
|
|
112
|
+
(d/transact conn [{:name "Charlie" :age 35}])
|
|
113
|
+
|
|
114
|
+
;; Freed count should be 0 (all deleted by online GC)
|
|
115
|
+
(is (= 0 (get-freed-count @conn))
|
|
116
|
+
"Online GC should have deleted all freed addresses")
|
|
117
|
+
|
|
118
|
+
;; Store should not have garbage
|
|
119
|
+
(let [final-store-count (count-store @conn)
|
|
120
|
+
growth (- final-store-count initial-store-count)]
|
|
121
|
+
;; With online GC, growth should be controlled
|
|
122
|
+
;; We have 3 entities + schema + branch metadata + indices
|
|
123
|
+
;; Should be much less than without GC
|
|
124
|
+
(is (< growth 50)
|
|
125
|
+
(str "Store growth should be controlled with online GC. Growth: " growth)))
|
|
126
|
+
|
|
127
|
+
;; Verify data integrity after releasing and reconnecting
|
|
128
|
+
(d/release conn)
|
|
129
|
+
(let [conn2 (d/connect cfg)
|
|
130
|
+
result (d/q '[:find ?name ?age
|
|
131
|
+
:where [?e :name ?name]
|
|
132
|
+
[?e :age ?age]]
|
|
133
|
+
@conn2)]
|
|
134
|
+
(is (= 3 (count result))
|
|
135
|
+
"All 3 entities should be queryable after GC and reconnect")
|
|
136
|
+
(is (= #{["Alice" 30] ["Bob" 25] ["Charlie" 35]} (set result))
|
|
137
|
+
"All data should be intact")
|
|
138
|
+
(d/release conn2)))))
|
|
139
|
+
|
|
140
|
+
(deftest manual-gc-freed-count-test
|
|
141
|
+
(testing "Manual GC invocation returns exact deletion count"
|
|
142
|
+
(let [cfg (-> base-cfg
|
|
143
|
+
(assoc-in [:store :path] "/tmp/online-gc-manual-count-test")
|
|
144
|
+
(assoc :online-gc {:enabled? false})) ;; Disabled for manual control
|
|
145
|
+
conn (do
|
|
146
|
+
(d/delete-database cfg)
|
|
147
|
+
(d/create-database cfg)
|
|
148
|
+
(d/connect cfg))]
|
|
149
|
+
|
|
150
|
+
(d/transact conn schema)
|
|
151
|
+
(d/transact conn [{:name "Alice" :age 30}])
|
|
152
|
+
(d/transact conn [{:name "Bob" :age 25}])
|
|
153
|
+
|
|
154
|
+
;; Check how many freed addresses accumulated
|
|
155
|
+
;; Schema (3) + Alice (2) + Bob (2) = 7
|
|
156
|
+
(let [freed-before-gc (get-freed-count @conn)]
|
|
157
|
+
(is (= 7 freed-before-gc)
|
|
158
|
+
"Should have 7 freed addresses (schema:3 + Alice:2 + Bob:2)")
|
|
159
|
+
|
|
160
|
+
;; Manually run GC
|
|
161
|
+
(let [deleted-count (online-gc/online-gc! (:store @conn)
|
|
162
|
+
{:enabled? true
|
|
163
|
+
:grace-period-ms 0
|
|
164
|
+
:max-batch 1000
|
|
165
|
+
:sync? true})]
|
|
166
|
+
(is (= 7 deleted-count)
|
|
167
|
+
"GC should delete exactly 7 addresses"))
|
|
168
|
+
|
|
169
|
+
;; After GC, freed count should be 0
|
|
170
|
+
(is (= 0 (get-freed-count @conn))
|
|
171
|
+
"After manual GC, freed addresses should be cleared"))
|
|
172
|
+
|
|
173
|
+
;; Data should still be queryable
|
|
174
|
+
(d/release conn)
|
|
175
|
+
(let [conn2 (d/connect cfg)
|
|
176
|
+
result (d/q '[:find (count ?e) .
|
|
177
|
+
:where [?e :name _]]
|
|
178
|
+
@conn2)]
|
|
179
|
+
(is (= 2 result)
|
|
180
|
+
"Data should be intact after manual GC")
|
|
181
|
+
(d/release conn2)))))
|
|
182
|
+
|
|
183
|
+
(deftest recycling-all-at-once-test
|
|
184
|
+
(testing "Address recycling processes all eligible addresses at once"
|
|
185
|
+
(let [cfg (-> base-cfg
|
|
186
|
+
(assoc-in [:store :path] "/tmp/online-gc-recycle-all-test")
|
|
187
|
+
(assoc :online-gc {:enabled? false})) ;; Start disabled
|
|
188
|
+
conn (do
|
|
189
|
+
(d/delete-database cfg)
|
|
190
|
+
(d/create-database cfg)
|
|
191
|
+
(d/connect cfg))]
|
|
192
|
+
|
|
193
|
+
(d/transact conn schema)
|
|
194
|
+
;; Generate many freed addresses
|
|
195
|
+
(dotimes [i 20]
|
|
196
|
+
(d/transact conn [{:name (str "Person-" i) :age (+ 20 i)}]))
|
|
197
|
+
|
|
198
|
+
;; Check accumulated freed count
|
|
199
|
+
;; Schema (3) + 20 data transactions × 2 = 43 total
|
|
200
|
+
(let [total-freed (get-freed-count @conn)]
|
|
201
|
+
(is (= 43 total-freed)
|
|
202
|
+
"Should have 43 freed addresses (schema:3 + 20×2=40)")
|
|
203
|
+
|
|
204
|
+
;; Run GC - with recycling, all eligible addresses are processed at once
|
|
205
|
+
(let [recycled (online-gc/online-gc! (:store @conn)
|
|
206
|
+
{:enabled? true
|
|
207
|
+
:grace-period-ms 0
|
|
208
|
+
:max-batch 5 ;; This only affects delete mode, not recycling
|
|
209
|
+
:sync? true})]
|
|
210
|
+
(is (= 43 recycled)
|
|
211
|
+
"Should recycle all 43 addresses at once (recycling is not limited by max-batch)"))
|
|
212
|
+
|
|
213
|
+
;; All addresses should be recycled (moved to freelist)
|
|
214
|
+
(let [remaining (get-freed-count @conn)]
|
|
215
|
+
(is (= 0 remaining)
|
|
216
|
+
"Should have 0 remaining freed addresses (all recycled)")))
|
|
217
|
+
|
|
218
|
+
(d/release conn))))
|
|
219
|
+
|
|
220
|
+
;;; ============================================================================
|
|
221
|
+
;;; Integration Tests
|
|
222
|
+
;;; ============================================================================
|
|
223
|
+
|
|
224
|
+
(deftest online-gc-disabled-test
|
|
225
|
+
(testing "Online GC disabled by default - addresses accumulate"
|
|
226
|
+
(let [cfg (assoc-in base-cfg [:store :path] "/tmp/online-gc-disabled-test")
|
|
227
|
+
conn (do
|
|
228
|
+
(d/delete-database cfg)
|
|
229
|
+
(d/create-database cfg)
|
|
230
|
+
(d/connect cfg))]
|
|
231
|
+
(d/transact conn schema)
|
|
232
|
+
(d/transact conn [{:name "Alice" :age 30}])
|
|
233
|
+
(let [freed-before (get-freed-count @conn)]
|
|
234
|
+
(d/transact conn [{:name "Bob" :age 25}])
|
|
235
|
+
(d/transact conn [{:name "Charlie" :age 35}])
|
|
236
|
+
(let [freed-after (get-freed-count @conn)]
|
|
237
|
+
;; Freed addresses should increase (not be deleted)
|
|
238
|
+
(is (>= freed-after freed-before))
|
|
239
|
+
(is (pos? freed-after))))
|
|
240
|
+
(d/release conn))))
|
|
241
|
+
|
|
242
|
+
(deftest online-gc-with-reconnect-test
|
|
243
|
+
(testing "Data integrity verified via reconnect after GC"
|
|
244
|
+
(let [cfg (-> base-cfg
|
|
245
|
+
(assoc-in [:store :path] "/tmp/online-gc-reconnect-test")
|
|
246
|
+
(assoc :online-gc {:enabled? true
|
|
247
|
+
:grace-period-ms 0
|
|
248
|
+
:max-batch 1000}))
|
|
249
|
+
conn (do
|
|
250
|
+
(d/delete-database cfg)
|
|
251
|
+
(d/create-database cfg)
|
|
252
|
+
(d/connect cfg))]
|
|
253
|
+
|
|
254
|
+
;; Insert data
|
|
255
|
+
(d/transact conn schema)
|
|
256
|
+
(d/transact conn [{:name "Alice" :age 30}])
|
|
257
|
+
(d/transact conn [{:name "Bob" :age 25}])
|
|
258
|
+
(d/transact conn [{:name "Charlie" :age 35}])
|
|
259
|
+
|
|
260
|
+
;; All freed addresses should be cleaned up
|
|
261
|
+
(is (= 0 (get-freed-count @conn))
|
|
262
|
+
"Online GC should have cleaned up all freed addresses")
|
|
263
|
+
|
|
264
|
+
;; Release and reconnect
|
|
265
|
+
(d/release conn)
|
|
266
|
+
(let [conn2 (d/connect cfg)
|
|
267
|
+
result (d/q '[:find ?name ?age
|
|
268
|
+
:where [?e :name ?name]
|
|
269
|
+
[?e :age ?age]]
|
|
270
|
+
@conn2)]
|
|
271
|
+
(is (= 3 (count result))
|
|
272
|
+
"All 3 entities queryable after reconnect")
|
|
273
|
+
(is (= #{["Alice" 30] ["Bob" 25] ["Charlie" 35]} (set result))
|
|
274
|
+
"All data intact after GC and reconnect")
|
|
275
|
+
(d/release conn2)))))
|
|
276
|
+
|
|
277
|
+
(deftest grace-period-accumulation-test
|
|
278
|
+
(testing "Grace period causes freed addresses to accumulate"
|
|
279
|
+
(let [cfg (-> base-cfg
|
|
280
|
+
(assoc-in [:store :path] "/tmp/online-gc-grace-accumulation-test")
|
|
281
|
+
(assoc :online-gc {:enabled? true
|
|
282
|
+
:grace-period-ms 300000 ;; 5 minutes
|
|
283
|
+
:max-batch 1000}))
|
|
284
|
+
conn (do
|
|
285
|
+
(d/delete-database cfg)
|
|
286
|
+
(d/create-database cfg)
|
|
287
|
+
(d/connect cfg))]
|
|
288
|
+
|
|
289
|
+
(d/transact conn schema)
|
|
290
|
+
(d/transact conn [{:name "Alice" :age 30}])
|
|
291
|
+
(d/transact conn [{:name "Bob" :age 25}])
|
|
292
|
+
|
|
293
|
+
;; With long grace period, freed addresses should accumulate
|
|
294
|
+
(let [freed-count (get-freed-count @conn)]
|
|
295
|
+
(is (pos? freed-count)
|
|
296
|
+
(str "Freed addresses should accumulate during grace period. Count: " freed-count)))
|
|
297
|
+
|
|
298
|
+
;; Data should be queryable
|
|
299
|
+
(let [result (d/q '[:find (count ?e) .
|
|
300
|
+
:where [?e :name _]]
|
|
301
|
+
@conn)]
|
|
302
|
+
(is (= 2 result)
|
|
303
|
+
"Data should be queryable with accumulated freed addresses"))
|
|
304
|
+
|
|
305
|
+
(d/release conn))))
|
|
306
|
+
|
|
307
|
+
(deftest online-gc-large-dataset-test
|
|
308
|
+
(testing "Online GC integration with larger dataset"
|
|
309
|
+
(let [cfg (-> base-cfg
|
|
310
|
+
(assoc-in [:store :path] "/tmp/online-gc-integration-test")
|
|
311
|
+
(assoc :online-gc {:enabled? true
|
|
312
|
+
:grace-period-ms 0
|
|
313
|
+
:max-batch 10000}))
|
|
314
|
+
conn (do
|
|
315
|
+
(d/delete-database cfg)
|
|
316
|
+
(d/create-database cfg)
|
|
317
|
+
(d/connect cfg))
|
|
318
|
+
initial-count (count-store @conn)]
|
|
319
|
+
(d/transact conn schema)
|
|
320
|
+
|
|
321
|
+
;; Bulk insert
|
|
322
|
+
(doseq [batch (partition-all 100 (range 1000))]
|
|
323
|
+
(d/transact conn (mapv (fn [i] {:name (str "Person-" i) :age (+ 20 i)}) batch)))
|
|
324
|
+
|
|
325
|
+
;; With online GC, growth should be controlled
|
|
326
|
+
(let [final-count (count-store @conn)
|
|
327
|
+
growth-ratio (/ (double final-count) (double initial-count))]
|
|
328
|
+
;; Growth should be reasonable - not 100x or 1000x
|
|
329
|
+
(is (< growth-ratio 50)
|
|
330
|
+
(str "Store growth should be controlled with online GC. Ratio: " growth-ratio)))
|
|
331
|
+
|
|
332
|
+
;; Verify data integrity
|
|
333
|
+
(is (= 1000 (d/q '[:find (count ?e) .
|
|
334
|
+
:where [?e :name _]]
|
|
335
|
+
@conn))
|
|
336
|
+
"All 1000 entities should be accessible")
|
|
337
|
+
|
|
338
|
+
(d/release conn))))
|
|
339
|
+
|
|
340
|
+
;;; ============================================================================
|
|
341
|
+
;;; Timestamp Filtering Tests
|
|
342
|
+
;;; ============================================================================
|
|
343
|
+
|
|
344
|
+
(deftest get-and-clear-eligible-freed-test
|
|
345
|
+
(testing "get-and-clear-eligible-freed! correctly filters by timestamp"
|
|
346
|
+
(let [cfg (-> base-cfg
|
|
347
|
+
(assoc-in [:store :path] "/tmp/online-gc-filter-test")
|
|
348
|
+
(assoc :online-gc {:enabled? false}))
|
|
349
|
+
conn (do
|
|
350
|
+
(d/delete-database cfg)
|
|
351
|
+
(d/create-database cfg)
|
|
352
|
+
(d/connect cfg))]
|
|
353
|
+
(d/transact conn schema)
|
|
354
|
+
(d/transact conn [{:name "Alice" :age 30}])
|
|
355
|
+
|
|
356
|
+
;; Manually check filtering with very long grace period
|
|
357
|
+
(let [[to-delete remaining] (online-gc/get-and-clear-eligible-freed!
|
|
358
|
+
(:store @conn)
|
|
359
|
+
300000)] ;; 5 minutes
|
|
360
|
+
;; All should be in remaining (within grace period)
|
|
361
|
+
(is (empty? to-delete) "Nothing should be eligible with long grace period")
|
|
362
|
+
(is (coll? remaining) "Remaining should be a collection"))
|
|
363
|
+
|
|
364
|
+
;; Check with zero grace period
|
|
365
|
+
(d/transact conn [{:name "Bob" :age 25}]) ;; Generate more freed addresses
|
|
366
|
+
(let [[to-delete _remaining] (online-gc/get-and-clear-eligible-freed!
|
|
367
|
+
(:store @conn)
|
|
368
|
+
0)] ;; Zero grace period
|
|
369
|
+
;; Should have eligible addresses
|
|
370
|
+
(is (>= (count to-delete) 0) "Should find eligible addresses with zero grace period"))
|
|
371
|
+
|
|
372
|
+
(d/release conn))))
|
|
373
|
+
|
|
374
|
+
;;; ============================================================================
|
|
375
|
+
;;; Background GC Tests
|
|
376
|
+
;;; ============================================================================
|
|
377
|
+
|
|
378
|
+
(deftest background-gc-test
|
|
379
|
+
(testing "Background GC runs periodically"
|
|
380
|
+
(let [cfg (-> base-cfg
|
|
381
|
+
(assoc-in [:store :path] "/tmp/online-gc-background-test")
|
|
382
|
+
(assoc :online-gc {:enabled? false})) ;; Disable automatic GC in commit
|
|
383
|
+
conn (do
|
|
384
|
+
(d/delete-database cfg)
|
|
385
|
+
(d/create-database cfg)
|
|
386
|
+
(d/connect cfg))]
|
|
387
|
+
(d/transact conn schema)
|
|
388
|
+
|
|
389
|
+
;; Start background GC with short interval
|
|
390
|
+
(let [stop-ch (online-gc/start-background-gc!
|
|
391
|
+
(:store @conn)
|
|
392
|
+
{:grace-period-ms 0
|
|
393
|
+
:interval-ms 100 ;; Run every 100ms
|
|
394
|
+
:max-batch 1000})]
|
|
395
|
+
|
|
396
|
+
;; Generate some freed addresses
|
|
397
|
+
(d/transact conn [{:name "Alice" :age 30}])
|
|
398
|
+
(d/transact conn [{:name "Bob" :age 25}])
|
|
399
|
+
(d/transact conn [{:name "Charlie" :age 35}])
|
|
400
|
+
|
|
401
|
+
;; Wait for background GC to run a few times
|
|
402
|
+
#?(:clj (Thread/sleep 500)
|
|
403
|
+
:cljs (async/<! (async/timeout 500)))
|
|
404
|
+
|
|
405
|
+
;; Stop background GC
|
|
406
|
+
(async/close! stop-ch)
|
|
407
|
+
|
|
408
|
+
;; Wait a bit for cleanup
|
|
409
|
+
#?(:clj (Thread/sleep 100)
|
|
410
|
+
:cljs (async/<! (async/timeout 100)))
|
|
411
|
+
|
|
412
|
+
;; Freed addresses should be cleaned up by background GC
|
|
413
|
+
(let [freed-count (get-freed-count @conn)]
|
|
414
|
+
(is (< freed-count 10)
|
|
415
|
+
(str "Background GC should clean up freed addresses. Remaining: " freed-count))))
|
|
416
|
+
|
|
417
|
+
(d/release conn))))
|
|
418
|
+
|
|
419
|
+
;;; ============================================================================
|
|
420
|
+
;;; Safety Tests
|
|
421
|
+
;;; ============================================================================
|
|
422
|
+
|
|
423
|
+
(deftest multi-branch-safety-test
|
|
424
|
+
(testing "Multi-branch databases skip online GC entirely for safety"
|
|
425
|
+
(let [cfg (-> base-cfg
|
|
426
|
+
(assoc-in [:store :path] "/tmp/online-gc-multi-branch-test")
|
|
427
|
+
(assoc :online-gc {:enabled? false})
|
|
428
|
+
(assoc :crypto-hash? false))
|
|
429
|
+
conn (do
|
|
430
|
+
(d/delete-database cfg)
|
|
431
|
+
(d/create-database cfg)
|
|
432
|
+
(d/connect cfg))]
|
|
433
|
+
|
|
434
|
+
(d/transact conn schema)
|
|
435
|
+
(d/transact conn [{:name "Alice" :age 30}])
|
|
436
|
+
|
|
437
|
+
;; Check initial branches - should be single branch #{:db}
|
|
438
|
+
(let [branches (k/get (:store @conn) :branches nil {:sync? true})]
|
|
439
|
+
(is (= #{:db} branches)
|
|
440
|
+
"Should start with single branch"))
|
|
441
|
+
|
|
442
|
+
;; Manually add a second branch to simulate multi-branch scenario
|
|
443
|
+
(k/assoc (:store @conn) :branches #{:db :branch-a} {:sync? true})
|
|
444
|
+
|
|
445
|
+
;; Verify multi-branch state
|
|
446
|
+
(let [branches (k/get (:store @conn) :branches nil {:sync? true})]
|
|
447
|
+
(is (= #{:db :branch-a} branches)
|
|
448
|
+
"Should have two branches"))
|
|
449
|
+
|
|
450
|
+
;; Add transaction to generate freed addresses
|
|
451
|
+
(d/transact conn [{:name "Bob" :age 25}])
|
|
452
|
+
|
|
453
|
+
(let [freed-before (get-freed-count @conn)]
|
|
454
|
+
(is (pos? freed-before)
|
|
455
|
+
"Should have freed addresses"))
|
|
456
|
+
|
|
457
|
+
;; Run online GC - should detect multi-branch and SKIP entirely
|
|
458
|
+
(let [result (online-gc/online-gc! (:store @conn)
|
|
459
|
+
{:enabled? true
|
|
460
|
+
:grace-period-ms 0
|
|
461
|
+
:sync? true})]
|
|
462
|
+
(is (= 0 result)
|
|
463
|
+
"Multi-branch GC should be skipped (return 0)"))
|
|
464
|
+
|
|
465
|
+
;; Freed addresses should remain (not deleted, left for offline GC)
|
|
466
|
+
(let [freed-after (get-freed-count @conn)]
|
|
467
|
+
(is (pos? freed-after)
|
|
468
|
+
"Multi-branch GC should leave freed addresses for offline GC"))
|
|
469
|
+
|
|
470
|
+
;; Verify database is still functional
|
|
471
|
+
(let [result (d/q '[:find ?e ?n :where [?e :name ?n]] @conn)]
|
|
472
|
+
(is (= 2 (count result))
|
|
473
|
+
"Both Alice and Bob should still exist"))
|
|
474
|
+
|
|
475
|
+
(d/release conn))))
|