lora-python 0.2.0__tar.gz → 0.4.0__tar.gz
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.
- {lora_python-0.2.0 → lora_python-0.4.0}/Cargo.lock +48 -13
- {lora_python-0.2.0 → lora_python-0.4.0}/Cargo.toml +12 -9
- {lora_python-0.2.0 → lora_python-0.4.0}/PKG-INFO +36 -2
- {lora_python-0.2.0 → lora_python-0.4.0}/README.md +35 -1
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-analyzer/src/analyzer.rs +3 -3
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-database/Cargo.toml +5 -0
- lora_python-0.4.0/crates/lora-database/benches/wal_benchmarks.rs +205 -0
- lora_python-0.4.0/crates/lora-database/src/database.rs +770 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-database/src/lib.rs +10 -1
- lora_python-0.4.0/crates/lora-database/tests/backend_stub.rs +388 -0
- lora_python-0.4.0/crates/lora-database/tests/snapshot.rs +195 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-database/tests/vectors.rs +1 -1
- lora_python-0.4.0/crates/lora-database/tests/wal.rs +465 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-executor/src/eval.rs +85 -84
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-executor/src/executor.rs +139 -102
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-python/README.md +35 -1
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-python/examples/basic.py +2 -1
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-python/src/lib.rs +102 -28
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-python/tests/test_async.py +26 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-python/tests/test_sync.py +58 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-store/Cargo.toml +4 -0
- lora_python-0.4.0/crates/lora-store/src/graph.rs +837 -0
- lora_python-0.4.0/crates/lora-store/src/lib.rs +18 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-store/src/memory.rs +706 -161
- lora_python-0.4.0/crates/lora-store/src/mutation.rs +133 -0
- lora_python-0.4.0/crates/lora-store/src/snapshot.rs +458 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-store/src/spatial.rs +1 -1
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-store/src/temporal.rs +6 -6
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-store/src/vector.rs +5 -3
- lora_python-0.4.0/crates/lora-wal/Cargo.toml +19 -0
- lora_python-0.4.0/crates/lora-wal/src/config.rs +64 -0
- lora_python-0.4.0/crates/lora-wal/src/dir.rs +194 -0
- lora_python-0.4.0/crates/lora-wal/src/error.rs +51 -0
- lora_python-0.4.0/crates/lora-wal/src/lib.rs +50 -0
- lora_python-0.4.0/crates/lora-wal/src/lock.rs +104 -0
- lora_python-0.4.0/crates/lora-wal/src/lsn.rs +87 -0
- lora_python-0.4.0/crates/lora-wal/src/record.rs +443 -0
- lora_python-0.4.0/crates/lora-wal/src/recorder_adapter.rs +479 -0
- lora_python-0.4.0/crates/lora-wal/src/replay.rs +260 -0
- lora_python-0.4.0/crates/lora-wal/src/segment.rs +543 -0
- lora_python-0.4.0/crates/lora-wal/src/testing.rs +39 -0
- lora_python-0.4.0/crates/lora-wal/src/wal.rs +1002 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/pyproject.toml +1 -1
- {lora_python-0.2.0 → lora_python-0.4.0}/python/lora_python/__init__.py +7 -1
- {lora_python-0.2.0 → lora_python-0.4.0}/python/lora_python/_async.py +35 -6
- {lora_python-0.2.0 → lora_python-0.4.0}/python/lora_python/_native.pyi +6 -3
- {lora_python-0.2.0 → lora_python-0.4.0}/python/lora_python/types.py +18 -0
- lora_python-0.2.0/crates/lora-database/src/database.rs +0 -105
- lora_python-0.2.0/crates/lora-store/src/graph.rs +0 -649
- lora_python-0.2.0/crates/lora-store/src/lib.rs +0 -11
- {lora_python-0.2.0 → lora_python-0.4.0}/LICENSE +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-analyzer/Cargo.toml +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-analyzer/src/errors.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-analyzer/src/lib.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-analyzer/src/resolved.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-analyzer/src/scope.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-analyzer/src/symbols.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-ast/Cargo.toml +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-ast/src/ast.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-ast/src/lib.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-compiler/Cargo.toml +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-compiler/src/lib.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-compiler/src/logical.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-compiler/src/optimizer.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-compiler/src/pattern.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-compiler/src/physical.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-compiler/src/planner.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-database/benches/advanced_benchmarks.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-database/benches/engine_benchmarks.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-database/benches/fixtures.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-database/benches/perf_smoke_baseline.json +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-database/benches/perf_smoke_benchmarks.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-database/benches/scale_benchmarks.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-database/benches/temporal_spatial_benchmarks.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-database/tests/advanced_queries.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-database/tests/aggregation.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-database/tests/create.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-database/tests/errors.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-database/tests/expressions.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-database/tests/functions_extended.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-database/tests/invariants.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-database/tests/match.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-database/tests/merge.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-database/tests/ordering.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-database/tests/parameters.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-database/tests/parser.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-database/tests/paths.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-database/tests/projection.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-database/tests/seeds.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-database/tests/spatial.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-database/tests/temporal.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-database/tests/test_helpers.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-database/tests/types_advanced.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-database/tests/union.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-database/tests/update.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-database/tests/where_clause.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-database/tests/with.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-executor/Cargo.toml +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-executor/src/errors.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-executor/src/lib.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-executor/src/value.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-parser/Cargo.toml +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-parser/src/cypher.pest +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-parser/src/error.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-parser/src/lib.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-parser/src/parser.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-python/.gitignore +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-python/Cargo.toml +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-python/LICENSE +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-python/build.rs +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/crates/lora-python/examples/async_demo.py +0 -0
- {lora_python-0.2.0 → lora_python-0.4.0}/python/lora_python/py.typed +0 -0
|
@@ -116,6 +116,15 @@ dependencies = [
|
|
|
116
116
|
"tracing",
|
|
117
117
|
]
|
|
118
118
|
|
|
119
|
+
[[package]]
|
|
120
|
+
name = "bincode"
|
|
121
|
+
version = "1.3.3"
|
|
122
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
123
|
+
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
|
|
124
|
+
dependencies = [
|
|
125
|
+
"serde",
|
|
126
|
+
]
|
|
127
|
+
|
|
119
128
|
[[package]]
|
|
120
129
|
name = "bindgen"
|
|
121
130
|
version = "0.72.1"
|
|
@@ -283,6 +292,15 @@ dependencies = [
|
|
|
283
292
|
"libc",
|
|
284
293
|
]
|
|
285
294
|
|
|
295
|
+
[[package]]
|
|
296
|
+
name = "crc32fast"
|
|
297
|
+
version = "1.5.0"
|
|
298
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
299
|
+
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
|
|
300
|
+
dependencies = [
|
|
301
|
+
"cfg-if",
|
|
302
|
+
]
|
|
303
|
+
|
|
286
304
|
[[package]]
|
|
287
305
|
name = "criterion"
|
|
288
306
|
version = "0.8.2"
|
|
@@ -611,7 +629,7 @@ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
|
|
611
629
|
|
|
612
630
|
[[package]]
|
|
613
631
|
name = "lora-analyzer"
|
|
614
|
-
version = "0.
|
|
632
|
+
version = "0.4.0"
|
|
615
633
|
dependencies = [
|
|
616
634
|
"lora-ast",
|
|
617
635
|
"lora-parser",
|
|
@@ -621,14 +639,14 @@ dependencies = [
|
|
|
621
639
|
|
|
622
640
|
[[package]]
|
|
623
641
|
name = "lora-ast"
|
|
624
|
-
version = "0.
|
|
642
|
+
version = "0.4.0"
|
|
625
643
|
dependencies = [
|
|
626
644
|
"smallvec 2.0.0-alpha.12",
|
|
627
645
|
]
|
|
628
646
|
|
|
629
647
|
[[package]]
|
|
630
648
|
name = "lora-compiler"
|
|
631
|
-
version = "0.
|
|
649
|
+
version = "0.4.0"
|
|
632
650
|
dependencies = [
|
|
633
651
|
"lora-analyzer",
|
|
634
652
|
"lora-ast",
|
|
@@ -636,7 +654,7 @@ dependencies = [
|
|
|
636
654
|
|
|
637
655
|
[[package]]
|
|
638
656
|
name = "lora-database"
|
|
639
|
-
version = "0.
|
|
657
|
+
version = "0.4.0"
|
|
640
658
|
dependencies = [
|
|
641
659
|
"anyhow",
|
|
642
660
|
"criterion",
|
|
@@ -646,13 +664,14 @@ dependencies = [
|
|
|
646
664
|
"lora-executor",
|
|
647
665
|
"lora-parser",
|
|
648
666
|
"lora-store",
|
|
667
|
+
"lora-wal",
|
|
649
668
|
"serde_json",
|
|
650
669
|
"tower",
|
|
651
670
|
]
|
|
652
671
|
|
|
653
672
|
[[package]]
|
|
654
673
|
name = "lora-executor"
|
|
655
|
-
version = "0.
|
|
674
|
+
version = "0.4.0"
|
|
656
675
|
dependencies = [
|
|
657
676
|
"lora-analyzer",
|
|
658
677
|
"lora-ast",
|
|
@@ -666,7 +685,7 @@ dependencies = [
|
|
|
666
685
|
|
|
667
686
|
[[package]]
|
|
668
687
|
name = "lora-ffi"
|
|
669
|
-
version = "0.
|
|
688
|
+
version = "0.4.0"
|
|
670
689
|
dependencies = [
|
|
671
690
|
"lora-database",
|
|
672
691
|
"lora-store",
|
|
@@ -676,7 +695,7 @@ dependencies = [
|
|
|
676
695
|
|
|
677
696
|
[[package]]
|
|
678
697
|
name = "lora-node"
|
|
679
|
-
version = "0.
|
|
698
|
+
version = "0.4.0"
|
|
680
699
|
dependencies = [
|
|
681
700
|
"anyhow",
|
|
682
701
|
"lora-database",
|
|
@@ -690,7 +709,7 @@ dependencies = [
|
|
|
690
709
|
|
|
691
710
|
[[package]]
|
|
692
711
|
name = "lora-parser"
|
|
693
|
-
version = "0.
|
|
712
|
+
version = "0.4.0"
|
|
694
713
|
dependencies = [
|
|
695
714
|
"lora-ast",
|
|
696
715
|
"pest",
|
|
@@ -701,7 +720,7 @@ dependencies = [
|
|
|
701
720
|
|
|
702
721
|
[[package]]
|
|
703
722
|
name = "lora-python"
|
|
704
|
-
version = "0.
|
|
723
|
+
version = "0.4.0"
|
|
705
724
|
dependencies = [
|
|
706
725
|
"anyhow",
|
|
707
726
|
"lora-database",
|
|
@@ -713,10 +732,11 @@ dependencies = [
|
|
|
713
732
|
|
|
714
733
|
[[package]]
|
|
715
734
|
name = "lora-server"
|
|
716
|
-
version = "0.
|
|
735
|
+
version = "0.4.0"
|
|
717
736
|
dependencies = [
|
|
718
737
|
"anyhow",
|
|
719
738
|
"axum",
|
|
739
|
+
"http-body-util",
|
|
720
740
|
"lora-database",
|
|
721
741
|
"serde",
|
|
722
742
|
"serde_json",
|
|
@@ -726,15 +746,30 @@ dependencies = [
|
|
|
726
746
|
|
|
727
747
|
[[package]]
|
|
728
748
|
name = "lora-store"
|
|
729
|
-
version = "0.
|
|
749
|
+
version = "0.4.0"
|
|
730
750
|
dependencies = [
|
|
751
|
+
"bincode",
|
|
752
|
+
"crc32fast",
|
|
731
753
|
"js-sys",
|
|
732
754
|
"lora-ast",
|
|
755
|
+
"serde",
|
|
756
|
+
"thiserror",
|
|
757
|
+
]
|
|
758
|
+
|
|
759
|
+
[[package]]
|
|
760
|
+
name = "lora-wal"
|
|
761
|
+
version = "0.4.0"
|
|
762
|
+
dependencies = [
|
|
763
|
+
"bincode",
|
|
764
|
+
"crc32fast",
|
|
765
|
+
"lora-store",
|
|
766
|
+
"serde",
|
|
767
|
+
"thiserror",
|
|
733
768
|
]
|
|
734
769
|
|
|
735
770
|
[[package]]
|
|
736
771
|
name = "lora-wasm"
|
|
737
|
-
version = "0.
|
|
772
|
+
version = "0.4.0"
|
|
738
773
|
dependencies = [
|
|
739
774
|
"anyhow",
|
|
740
775
|
"console_error_panic_hook",
|
|
@@ -749,7 +784,7 @@ dependencies = [
|
|
|
749
784
|
|
|
750
785
|
[[package]]
|
|
751
786
|
name = "lora_ruby"
|
|
752
|
-
version = "0.
|
|
787
|
+
version = "0.4.0"
|
|
753
788
|
dependencies = [
|
|
754
789
|
"anyhow",
|
|
755
790
|
"lora-database",
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
[workspace]
|
|
2
|
-
members = ["crates/lora-ast", "crates/lora-parser", "crates/lora-analyzer", "crates/lora-compiler", "crates/lora-store", "crates/lora-executor", "crates/lora-database", "crates/lora-python"]
|
|
2
|
+
members = ["crates/lora-ast", "crates/lora-parser", "crates/lora-analyzer", "crates/lora-compiler", "crates/lora-store", "crates/lora-wal", "crates/lora-executor", "crates/lora-database", "crates/lora-python"]
|
|
3
3
|
resolver = "2"
|
|
4
4
|
|
|
5
5
|
[workspace.package]
|
|
6
6
|
edition = "2021"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.4.0"
|
|
8
8
|
license = "BUSL-1.1"
|
|
9
9
|
authors = ["LoraDB, Inc."]
|
|
10
10
|
repository = "https://github.com/lora-db/lora"
|
|
@@ -15,13 +15,14 @@ rust-version = "1.87"
|
|
|
15
15
|
# Internal crates — versions are kept in lockstep with [workspace.package].version
|
|
16
16
|
# by `scripts/sync-versions.mjs`. Both `path` and `version` are set so that
|
|
17
17
|
# `cargo publish` works (crates.io cannot resolve path-only deps).
|
|
18
|
-
lora-ast = { path = "crates/lora-ast", version = "=0.
|
|
19
|
-
lora-parser = { path = "crates/lora-parser", version = "=0.
|
|
20
|
-
lora-analyzer = { path = "crates/lora-analyzer", version = "=0.
|
|
21
|
-
lora-compiler = { path = "crates/lora-compiler", version = "=0.
|
|
22
|
-
lora-store = { path = "crates/lora-store", version = "=0.
|
|
23
|
-
lora-
|
|
24
|
-
lora-
|
|
18
|
+
lora-ast = { path = "crates/lora-ast", version = "=0.4.0" }
|
|
19
|
+
lora-parser = { path = "crates/lora-parser", version = "=0.4.0" }
|
|
20
|
+
lora-analyzer = { path = "crates/lora-analyzer", version = "=0.4.0" }
|
|
21
|
+
lora-compiler = { path = "crates/lora-compiler", version = "=0.4.0" }
|
|
22
|
+
lora-store = { path = "crates/lora-store", version = "=0.4.0" }
|
|
23
|
+
lora-wal = { path = "crates/lora-wal", version = "=0.4.0" }
|
|
24
|
+
lora-executor = { path = "crates/lora-executor", version = "=0.4.0" }
|
|
25
|
+
lora-database = { path = "crates/lora-database", version = "=0.4.0" }
|
|
25
26
|
|
|
26
27
|
# External crates.
|
|
27
28
|
anyhow = "1"
|
|
@@ -37,3 +38,5 @@ serde_json = "1.0.149"
|
|
|
37
38
|
regex = "1"
|
|
38
39
|
tower = "0.5.3"
|
|
39
40
|
criterion = { version = "0.8.2", features = ["html_reports"] }
|
|
41
|
+
bincode = "1.3"
|
|
42
|
+
crc32fast = "1.4"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lora-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Classifier: Programming Language :: Rust
|
|
5
5
|
Classifier: Programming Language :: Python :: 3
|
|
6
6
|
Classifier: Programming Language :: Python :: 3.8
|
|
@@ -30,7 +30,7 @@ Project-URL: Repository, https://github.com/lora-db/lora
|
|
|
30
30
|
|
|
31
31
|
# lora-python
|
|
32
32
|
|
|
33
|
-
Python bindings for the [Lora](../../README.md)
|
|
33
|
+
Python bindings for the [Lora](../../README.md) graph
|
|
34
34
|
engine. Ships both a synchronous PyO3 `Database` class and an
|
|
35
35
|
asyncio-compatible `AsyncDatabase` wrapper that never blocks the event loop.
|
|
36
36
|
|
|
@@ -64,6 +64,18 @@ for row in res["rows"]:
|
|
|
64
64
|
print(n["properties"]["name"])
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
+
Initialization rule:
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from lora_python import Database
|
|
71
|
+
|
|
72
|
+
scratch = Database.create() # in-memory
|
|
73
|
+
persistent = Database.create("./app") # persistent: directory string
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
If you want persistence, pass a directory string to `Database.create(...)`
|
|
77
|
+
or `Database(...)`.
|
|
78
|
+
|
|
67
79
|
## Async usage (non-blocking)
|
|
68
80
|
|
|
69
81
|
```python
|
|
@@ -79,6 +91,13 @@ async def main():
|
|
|
79
91
|
asyncio.run(main())
|
|
80
92
|
```
|
|
81
93
|
|
|
94
|
+
Async initialization follows the same rule:
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
db = await AsyncDatabase.create() # in-memory
|
|
98
|
+
db = await AsyncDatabase.create("./app") # persistent: directory string
|
|
99
|
+
```
|
|
100
|
+
|
|
82
101
|
`AsyncDatabase.execute` dispatches the query onto the default asyncio
|
|
83
102
|
thread pool via `asyncio.to_thread`. The PyO3 `Database.execute` releases
|
|
84
103
|
the Python GIL for the duration of engine work, so other coroutines on the
|
|
@@ -125,6 +144,21 @@ Constructors and guards are exported from `lora_python.types`:
|
|
|
125
144
|
|
|
126
145
|
All three are available as `lora_python.LoraError`, etc.
|
|
127
146
|
|
|
147
|
+
## Persistence
|
|
148
|
+
|
|
149
|
+
`Database.create("./app")`, `Database("./app")`, and
|
|
150
|
+
`await AsyncDatabase.create("./app")` open or create a WAL-backed
|
|
151
|
+
persistent database rooted at that directory. Reopening the same path
|
|
152
|
+
replays committed writes before returning the handle.
|
|
153
|
+
|
|
154
|
+
Call `db.close()` / `await db.close()` before reopening the same WAL
|
|
155
|
+
directory inside one process.
|
|
156
|
+
|
|
157
|
+
This first Python persistence slice intentionally stays small: the
|
|
158
|
+
binding exposes WAL-backed initialization plus the existing
|
|
159
|
+
`save_snapshot` / `load_snapshot` APIs, but not checkpoint, truncate,
|
|
160
|
+
status, or sync-mode controls.
|
|
161
|
+
|
|
128
162
|
## Architecture
|
|
129
163
|
|
|
130
164
|
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# lora-python
|
|
2
2
|
|
|
3
|
-
Python bindings for the [Lora](../../README.md)
|
|
3
|
+
Python bindings for the [Lora](../../README.md) graph
|
|
4
4
|
engine. Ships both a synchronous PyO3 `Database` class and an
|
|
5
5
|
asyncio-compatible `AsyncDatabase` wrapper that never blocks the event loop.
|
|
6
6
|
|
|
@@ -34,6 +34,18 @@ for row in res["rows"]:
|
|
|
34
34
|
print(n["properties"]["name"])
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
+
Initialization rule:
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from lora_python import Database
|
|
41
|
+
|
|
42
|
+
scratch = Database.create() # in-memory
|
|
43
|
+
persistent = Database.create("./app") # persistent: directory string
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
If you want persistence, pass a directory string to `Database.create(...)`
|
|
47
|
+
or `Database(...)`.
|
|
48
|
+
|
|
37
49
|
## Async usage (non-blocking)
|
|
38
50
|
|
|
39
51
|
```python
|
|
@@ -49,6 +61,13 @@ async def main():
|
|
|
49
61
|
asyncio.run(main())
|
|
50
62
|
```
|
|
51
63
|
|
|
64
|
+
Async initialization follows the same rule:
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
db = await AsyncDatabase.create() # in-memory
|
|
68
|
+
db = await AsyncDatabase.create("./app") # persistent: directory string
|
|
69
|
+
```
|
|
70
|
+
|
|
52
71
|
`AsyncDatabase.execute` dispatches the query onto the default asyncio
|
|
53
72
|
thread pool via `asyncio.to_thread`. The PyO3 `Database.execute` releases
|
|
54
73
|
the Python GIL for the duration of engine work, so other coroutines on the
|
|
@@ -95,6 +114,21 @@ Constructors and guards are exported from `lora_python.types`:
|
|
|
95
114
|
|
|
96
115
|
All three are available as `lora_python.LoraError`, etc.
|
|
97
116
|
|
|
117
|
+
## Persistence
|
|
118
|
+
|
|
119
|
+
`Database.create("./app")`, `Database("./app")`, and
|
|
120
|
+
`await AsyncDatabase.create("./app")` open or create a WAL-backed
|
|
121
|
+
persistent database rooted at that directory. Reopening the same path
|
|
122
|
+
replays committed writes before returning the handle.
|
|
123
|
+
|
|
124
|
+
Call `db.close()` / `await db.close()` before reopening the same WAL
|
|
125
|
+
directory inside one process.
|
|
126
|
+
|
|
127
|
+
This first Python persistence slice intentionally stays small: the
|
|
128
|
+
binding exposes WAL-backed initialization plus the existing
|
|
129
|
+
`save_snapshot` / `load_snapshot` APIs, but not checkpoint, truncate,
|
|
130
|
+
status, or sync-mode controls.
|
|
131
|
+
|
|
98
132
|
## Architecture
|
|
99
133
|
|
|
100
134
|
```
|
|
@@ -5,10 +5,10 @@ use lora_ast::{
|
|
|
5
5
|
ReadingClause, RelationshipPattern, Remove, RemoveItem, Return, Set, SetItem, SinglePartQuery,
|
|
6
6
|
SingleQuery, Statement, Unwind, UpdatingClause, With,
|
|
7
7
|
};
|
|
8
|
-
use lora_store::
|
|
8
|
+
use lora_store::GraphCatalog;
|
|
9
9
|
use std::collections::{BTreeMap, BTreeSet};
|
|
10
10
|
|
|
11
|
-
pub struct Analyzer<'a, S:
|
|
11
|
+
pub struct Analyzer<'a, S: GraphCatalog + ?Sized> {
|
|
12
12
|
storage: &'a S,
|
|
13
13
|
scopes: ScopeStack,
|
|
14
14
|
symbols: SymbolTable,
|
|
@@ -22,7 +22,7 @@ enum PatternContext {
|
|
|
22
22
|
Write,
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
impl<'a, S:
|
|
25
|
+
impl<'a, S: GraphCatalog + ?Sized> Analyzer<'a, S> {
|
|
26
26
|
pub fn new(storage: &'a S) -> Self {
|
|
27
27
|
Self {
|
|
28
28
|
storage,
|
|
@@ -23,6 +23,7 @@ lora-analyzer.workspace = true
|
|
|
23
23
|
lora-compiler.workspace = true
|
|
24
24
|
lora-executor.workspace = true
|
|
25
25
|
lora-store.workspace = true
|
|
26
|
+
lora-wal.workspace = true
|
|
26
27
|
|
|
27
28
|
serde_json.workspace = true
|
|
28
29
|
tower.workspace = true
|
|
@@ -49,3 +50,7 @@ harness = false
|
|
|
49
50
|
[[bench]]
|
|
50
51
|
name = "perf_smoke_benchmarks"
|
|
51
52
|
harness = false
|
|
53
|
+
|
|
54
|
+
[[bench]]
|
|
55
|
+
name = "wal_benchmarks"
|
|
56
|
+
harness = false
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
//! WAL-aware microbenchmarks.
|
|
2
|
+
//!
|
|
3
|
+
//! These exercise the four durability profiles end-to-end through
|
|
4
|
+
//! `Database::execute_with_params`:
|
|
5
|
+
//!
|
|
6
|
+
//! - **`no_wal`** — `Database::in_memory()` (the existing fast path;
|
|
7
|
+
//! serves as the baseline the others are compared against).
|
|
8
|
+
//! - **`per_commit`** — `WalConfig::Enabled` with `SyncMode::PerCommit`
|
|
9
|
+
//! (fsync before every commit returns).
|
|
10
|
+
//! - **`group`** — `WalConfig::Enabled` with `SyncMode::Group`
|
|
11
|
+
//! (write-only on commit, bg flusher fsyncs).
|
|
12
|
+
//! - **`none`** — `WalConfig::Enabled` with `SyncMode::None`
|
|
13
|
+
//! (no fsync at all, OS-buffered).
|
|
14
|
+
//!
|
|
15
|
+
//! The shape that matters is *commit latency* — every iteration runs a
|
|
16
|
+
//! single tiny `CREATE` statement so the engine work is negligible and
|
|
17
|
+
//! the WAL path dominates. On NVMe the gap between `no_wal` and
|
|
18
|
+
//! `per_commit` is roughly the cost of one `fsync` (50–200 µs); the
|
|
19
|
+
//! gap between `per_commit` and `group` / `none` measures how much
|
|
20
|
+
//! you save by deferring durability.
|
|
21
|
+
//!
|
|
22
|
+
//! There is also a small **`recovery`** bench that times opening a WAL
|
|
23
|
+
//! with N committed transactions and re-applying them. This is the
|
|
24
|
+
//! number that bounds startup time on a fresh process.
|
|
25
|
+
//!
|
|
26
|
+
//! Run with:
|
|
27
|
+
//! `cargo bench -p lora-database --bench wal_benchmarks`
|
|
28
|
+
|
|
29
|
+
use std::path::PathBuf;
|
|
30
|
+
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
|
31
|
+
|
|
32
|
+
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
|
33
|
+
use lora_database::{Database, ExecuteOptions, ResultFormat, SyncMode, WalConfig};
|
|
34
|
+
use std::hint::black_box;
|
|
35
|
+
|
|
36
|
+
fn opts() -> Option<ExecuteOptions> {
|
|
37
|
+
Some(ExecuteOptions {
|
|
38
|
+
format: ResultFormat::Rows,
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/// Per-iteration scratch directory. Criterion's `iter_batched_setup`
|
|
43
|
+
/// runs setup once per batch, so this is called once per Criterion
|
|
44
|
+
/// "sample" — fine even at sample_size=20.
|
|
45
|
+
struct ScratchDir {
|
|
46
|
+
path: PathBuf,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
impl ScratchDir {
|
|
50
|
+
fn new(tag: &str) -> Self {
|
|
51
|
+
let mut path = std::env::temp_dir();
|
|
52
|
+
path.push(format!(
|
|
53
|
+
"lora-wal-bench-{}-{}-{}",
|
|
54
|
+
tag,
|
|
55
|
+
std::process::id(),
|
|
56
|
+
SystemTime::now()
|
|
57
|
+
.duration_since(UNIX_EPOCH)
|
|
58
|
+
.unwrap()
|
|
59
|
+
.as_nanos()
|
|
60
|
+
));
|
|
61
|
+
std::fs::create_dir_all(&path).unwrap();
|
|
62
|
+
Self { path }
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
impl Drop for ScratchDir {
|
|
67
|
+
fn drop(&mut self) {
|
|
68
|
+
let _ = std::fs::remove_dir_all(&self.path);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
fn enabled(dir: &std::path::Path, sync_mode: SyncMode) -> WalConfig {
|
|
73
|
+
WalConfig::Enabled {
|
|
74
|
+
dir: dir.to_path_buf(),
|
|
75
|
+
sync_mode,
|
|
76
|
+
segment_target_bytes: 8 * 1024 * 1024,
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
fn smoke_config() -> Criterion {
|
|
81
|
+
// Per-bench budget of ~3 s. fsync cost dominates per_commit so we
|
|
82
|
+
// cannot run this as cheaply as the in-memory smoke suite, but we
|
|
83
|
+
// also don't want a 30-second bench on every CI run.
|
|
84
|
+
Criterion::default()
|
|
85
|
+
.warm_up_time(Duration::from_millis(500))
|
|
86
|
+
.measurement_time(Duration::from_millis(2_500))
|
|
87
|
+
.sample_size(20)
|
|
88
|
+
.noise_threshold(0.10)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
fn bench_commit_latency(c: &mut Criterion) {
|
|
92
|
+
let mut group = c.benchmark_group("wal/commit_latency");
|
|
93
|
+
|
|
94
|
+
// ---- baseline: no WAL ---------------------------------------------------
|
|
95
|
+
{
|
|
96
|
+
group.bench_function("no_wal", |b| {
|
|
97
|
+
b.iter_batched(
|
|
98
|
+
Database::in_memory,
|
|
99
|
+
|db| {
|
|
100
|
+
black_box(db.execute("CREATE (:N {v: 1})", opts()).unwrap());
|
|
101
|
+
},
|
|
102
|
+
BatchSize::SmallInput,
|
|
103
|
+
);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ---- PerCommit (fsync per commit) --------------------------------------
|
|
108
|
+
{
|
|
109
|
+
group.bench_function("per_commit", |b| {
|
|
110
|
+
b.iter_batched(
|
|
111
|
+
|| {
|
|
112
|
+
let dir = ScratchDir::new("per-commit");
|
|
113
|
+
let db =
|
|
114
|
+
Database::open_with_wal(enabled(&dir.path, SyncMode::PerCommit)).unwrap();
|
|
115
|
+
(dir, db)
|
|
116
|
+
},
|
|
117
|
+
|(_dir, db)| {
|
|
118
|
+
black_box(db.execute("CREATE (:N {v: 1})", opts()).unwrap());
|
|
119
|
+
},
|
|
120
|
+
BatchSize::SmallInput,
|
|
121
|
+
);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ---- Group (write-only on commit, bg flusher fsyncs) -------------------
|
|
126
|
+
{
|
|
127
|
+
group.bench_function("group", |b| {
|
|
128
|
+
b.iter_batched(
|
|
129
|
+
|| {
|
|
130
|
+
let dir = ScratchDir::new("group");
|
|
131
|
+
let db = Database::open_with_wal(enabled(
|
|
132
|
+
&dir.path,
|
|
133
|
+
SyncMode::Group { interval_ms: 50 },
|
|
134
|
+
))
|
|
135
|
+
.unwrap();
|
|
136
|
+
(dir, db)
|
|
137
|
+
},
|
|
138
|
+
|(_dir, db)| {
|
|
139
|
+
black_box(db.execute("CREATE (:N {v: 1})", opts()).unwrap());
|
|
140
|
+
},
|
|
141
|
+
BatchSize::SmallInput,
|
|
142
|
+
);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ---- None (no fsync at all) --------------------------------------------
|
|
147
|
+
{
|
|
148
|
+
group.bench_function("none", |b| {
|
|
149
|
+
b.iter_batched(
|
|
150
|
+
|| {
|
|
151
|
+
let dir = ScratchDir::new("none");
|
|
152
|
+
let db = Database::open_with_wal(enabled(&dir.path, SyncMode::None)).unwrap();
|
|
153
|
+
(dir, db)
|
|
154
|
+
},
|
|
155
|
+
|(_dir, db)| {
|
|
156
|
+
black_box(db.execute("CREATE (:N {v: 1})", opts()).unwrap());
|
|
157
|
+
},
|
|
158
|
+
BatchSize::SmallInput,
|
|
159
|
+
);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
group.finish();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
fn bench_recovery(c: &mut Criterion) {
|
|
167
|
+
let mut group = c.benchmark_group("wal/recovery");
|
|
168
|
+
|
|
169
|
+
// Time how long Database::open_with_wal takes on a directory that
|
|
170
|
+
// has N committed transactions waiting to be replayed. This bounds
|
|
171
|
+
// startup time on a crash-recovery boot.
|
|
172
|
+
for n in [100usize, 1_000].iter().copied() {
|
|
173
|
+
// Build the WAL once outside the iter loop.
|
|
174
|
+
let dir = ScratchDir::new(&format!("recovery-{}", n));
|
|
175
|
+
{
|
|
176
|
+
let db = Database::open_with_wal(enabled(&dir.path, SyncMode::PerCommit)).unwrap();
|
|
177
|
+
for _ in 0..n {
|
|
178
|
+
db.execute("CREATE (:N {v: 1})", opts()).unwrap();
|
|
179
|
+
}
|
|
180
|
+
// Drop to release file handles; bg flusher (none here) joins.
|
|
181
|
+
drop(db);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
group.bench_function(format!("replay_{}", n), |b| {
|
|
185
|
+
b.iter(|| {
|
|
186
|
+
let db = Database::open_with_wal(enabled(&dir.path, SyncMode::None)).unwrap();
|
|
187
|
+
black_box(db.node_count());
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// Keep `dir` alive across the iter so the WAL files persist
|
|
192
|
+
// for every iteration. ScratchDir's Drop will clean up at
|
|
193
|
+
// bench end.
|
|
194
|
+
std::mem::forget(dir);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
group.finish();
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
criterion_group! {
|
|
201
|
+
name = benches;
|
|
202
|
+
config = smoke_config();
|
|
203
|
+
targets = bench_commit_latency, bench_recovery,
|
|
204
|
+
}
|
|
205
|
+
criterion_main!(benches);
|