lora-python 0.8.0__tar.gz → 0.8.1__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.8.0 → lora_python-0.8.1}/Cargo.lock +16 -16
- {lora_python-0.8.0 → lora_python-0.8.1}/Cargo.toml +11 -11
- {lora_python-0.8.0 → lora_python-0.8.1}/PKG-INFO +1 -1
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/src/database/builder.rs +4 -4
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/src/database/mod.rs +25 -24
- lora_python-0.8.1/crates/lora-database/src/database/occ.rs +137 -0
- lora_python-0.8.1/crates/lora-database/src/database/replay.rs +144 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/src/lib.rs +1 -0
- lora_python-0.8.1/crates/lora-database/src/live_store.rs +92 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/src/stream.rs +3 -4
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/src/transaction.rs +3 -4
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/tests/transactions.rs +13 -13
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/tests/wal.rs +29 -18
- {lora_python-0.8.0 → lora_python-0.8.1}/pyproject.toml +1 -1
- lora_python-0.8.0/crates/lora-database/src/database/occ.rs +0 -184
- lora_python-0.8.0/crates/lora-database/src/database/replay.rs +0 -303
- {lora_python-0.8.0 → lora_python-0.8.1}/LICENSE +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/README.md +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/bindings/lora-python/.gitignore +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/bindings/lora-python/Cargo.toml +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/bindings/lora-python/LICENSE +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/bindings/lora-python/README.md +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/bindings/lora-python/build.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/bindings/lora-python/examples/async_demo.py +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/bindings/lora-python/examples/basic.py +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/bindings/lora-python/src/errors.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/bindings/lora-python/src/from_python.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/bindings/lora-python/src/lib.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/bindings/lora-python/src/to_python.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/bindings/lora-python/tests/test_async.py +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/bindings/lora-python/tests/test_explain_profile.py +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/bindings/lora-python/tests/test_sync.py +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-analyzer/Cargo.toml +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-analyzer/src/analyzer/clauses.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-analyzer/src/analyzer/expressions.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-analyzer/src/analyzer/mod.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-analyzer/src/analyzer/patterns.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-analyzer/src/analyzer/state.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-analyzer/src/analyzer/tests.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-analyzer/src/errors.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-analyzer/src/lib.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-analyzer/src/resolved.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-analyzer/src/scope.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-analyzer/src/symbols.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-analyzer/tests/error_messages.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-ast/Cargo.toml +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-ast/src/ast.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-ast/src/lib.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-compiler/Cargo.toml +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-compiler/src/lib.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-compiler/src/logical.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-compiler/src/optimizer.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-compiler/src/pattern.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-compiler/src/physical.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-compiler/src/plan_tree.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-compiler/src/planner.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/Cargo.toml +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/benches/advanced_benchmarks.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/benches/concurrent_benchmarks.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/benches/engine_benchmarks.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/benches/fixtures.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/benches/perf_smoke_baseline.json +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/benches/perf_smoke_benchmarks.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/benches/scale_benchmarks.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/benches/temporal_spatial_benchmarks.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/benches/wal_benchmarks.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/src/database/compile.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/src/database/execute.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/src/database/explain.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/src/database/graph_api.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/src/database/profile.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/src/database/pull_mode.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/src/database/stream.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/src/database/write_guard.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/src/error.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/src/explain.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/src/named.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/src/plan_cache.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/src/snapshot/json.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/src/snapshot/mod.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/src/snapshot/store.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/src/wal/admin.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/src/wal/archive/format.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/src/wal/archive/lock.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/src/wal/archive/platform.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/src/wal/archive/worker.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/src/wal/archive/workspace.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/src/wal/archive.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/src/wal/mod.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/src/wal/write_scope.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/tests/advanced_queries.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/tests/aggregation.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/tests/backend_stub.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/tests/binary.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/tests/create.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/tests/error_messages.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/tests/errors.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/tests/explain_profile.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/tests/expressions.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/tests/functions_extended.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/tests/invariants.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/tests/managed_snapshots.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/tests/match.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/tests/merge.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/tests/ordering.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/tests/parameters.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/tests/parser.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/tests/paths.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/tests/projection.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/tests/scale.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/tests/seeds.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/tests/snapshot.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/tests/spatial.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/tests/temporal.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/tests/test_helpers.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/tests/types_advanced.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/tests/union.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/tests/update.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/tests/vectors.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/tests/where_clause.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-database/tests/with.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/Cargo.toml +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/src/errors.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/src/eval/binops.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/src/eval/errors.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/src/eval/expr.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/src/eval/functions.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/src/eval/mod.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/src/eval/point.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/src/eval/vector.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/src/executor/helpers.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/src/executor/immutable.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/src/executor/mod.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/src/executor/mutable.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/src/lib.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/src/profile.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/src/pull/aggregate.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/src/pull/columns.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/src/pull/context.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/src/pull/expand.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/src/pull/filter.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/src/pull/hydration.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/src/pull/mod.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/src/pull/mutable.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/src/pull/optional.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/src/pull/path.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/src/pull/projection.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/src/pull/scan.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/src/pull/shape.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/src/pull/sort.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/src/pull/source.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/src/pull/tests.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/src/pull/traits.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/src/pull/union.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/src/value.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-executor/tests/error_messages.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-parser/Cargo.toml +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-parser/src/cypher.pest +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-parser/src/errors.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-parser/src/lib.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-parser/src/parser/clauses.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-parser/src/parser/expressions.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-parser/src/parser/literals.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-parser/src/parser/mod.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-parser/src/parser/patterns.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-parser/src/parser/query.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-parser/src/parser/tests.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-parser/src/parser/util.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-parser/tests/error_messages.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-snapshot/Cargo.toml +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-snapshot/src/body.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-snapshot/src/codec.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-snapshot/src/columnar.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-snapshot/src/envelope.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-snapshot/src/errors.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-snapshot/src/format.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-snapshot/src/lib.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-snapshot/src/options.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-snapshot/src/tests.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-snapshot/src/transform.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-snapshot/src/view.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-snapshot/tests/error_messages.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/Cargo.toml +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/lib.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/lock_table.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/memory/graph.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/memory/impls.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/memory/mod.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/memory/property_index.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/memory/snapshot.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/memory/tests.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/mutation.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/snapshot.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/traits.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/types/binary/mod.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/types/binary/tests.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/types/binary/traits.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/types/binary/types.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/types/graph.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/types/mod.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/types/property_value.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/types/spatial/distance.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/types/spatial/mod.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/types/spatial/point.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/types/spatial/srid.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/types/spatial/tests.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/types/temporal/calendar.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/types/temporal/date.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/types/temporal/datetime.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/types/temporal/duration.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/types/temporal/format.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/types/temporal/mod.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/types/temporal/parsing.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/types/temporal/time.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/types/vector/build.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/types/vector/mod.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/types/vector/similarity.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/types/vector/tests.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/src/types/vector/types.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-store/tests/error_messages.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-wal/Cargo.toml +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-wal/src/codec/decode.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-wal/src/codec/encode.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-wal/src/codec/format.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-wal/src/codec/mod.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-wal/src/codec/tests.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-wal/src/config.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-wal/src/dir.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-wal/src/errors.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-wal/src/lib.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-wal/src/lock.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-wal/src/lsn.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-wal/src/record.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-wal/src/recorder/errors.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-wal/src/recorder/mirror.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-wal/src/recorder/mod.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-wal/src/recorder/recorder.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-wal/src/recorder/tests.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-wal/src/replay.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-wal/src/segment.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-wal/src/testing.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-wal/src/wal/group_flusher.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-wal/src/wal/mod.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-wal/src/wal/tests.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-wal/src/wal/wal.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/crates/lora-wal/tests/error_messages.rs +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/python/lora_python/__init__.py +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/python/lora_python/_async.py +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/python/lora_python/_native.pyi +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/python/lora_python/py.typed +0 -0
- {lora_python-0.8.0 → lora_python-0.8.1}/python/lora_python/types.py +0 -0
|
@@ -797,7 +797,7 @@ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
|
|
797
797
|
|
|
798
798
|
[[package]]
|
|
799
799
|
name = "lora-analyzer"
|
|
800
|
-
version = "0.8.
|
|
800
|
+
version = "0.8.1"
|
|
801
801
|
dependencies = [
|
|
802
802
|
"lora-ast",
|
|
803
803
|
"lora-parser",
|
|
@@ -807,14 +807,14 @@ dependencies = [
|
|
|
807
807
|
|
|
808
808
|
[[package]]
|
|
809
809
|
name = "lora-ast"
|
|
810
|
-
version = "0.8.
|
|
810
|
+
version = "0.8.1"
|
|
811
811
|
dependencies = [
|
|
812
812
|
"smallvec 2.0.0-alpha.12",
|
|
813
813
|
]
|
|
814
814
|
|
|
815
815
|
[[package]]
|
|
816
816
|
name = "lora-binding-buffer"
|
|
817
|
-
version = "0.8.
|
|
817
|
+
version = "0.8.1"
|
|
818
818
|
dependencies = [
|
|
819
819
|
"lora-database",
|
|
820
820
|
"lora-store",
|
|
@@ -822,7 +822,7 @@ dependencies = [
|
|
|
822
822
|
|
|
823
823
|
[[package]]
|
|
824
824
|
name = "lora-compiler"
|
|
825
|
-
version = "0.8.
|
|
825
|
+
version = "0.8.1"
|
|
826
826
|
dependencies = [
|
|
827
827
|
"lora-analyzer",
|
|
828
828
|
"lora-ast",
|
|
@@ -830,7 +830,7 @@ dependencies = [
|
|
|
830
830
|
|
|
831
831
|
[[package]]
|
|
832
832
|
name = "lora-database"
|
|
833
|
-
version = "0.8.
|
|
833
|
+
version = "0.8.1"
|
|
834
834
|
dependencies = [
|
|
835
835
|
"anyhow",
|
|
836
836
|
"arc-swap",
|
|
@@ -852,7 +852,7 @@ dependencies = [
|
|
|
852
852
|
|
|
853
853
|
[[package]]
|
|
854
854
|
name = "lora-executor"
|
|
855
|
-
version = "0.8.
|
|
855
|
+
version = "0.8.1"
|
|
856
856
|
dependencies = [
|
|
857
857
|
"lora-analyzer",
|
|
858
858
|
"lora-ast",
|
|
@@ -867,7 +867,7 @@ dependencies = [
|
|
|
867
867
|
|
|
868
868
|
[[package]]
|
|
869
869
|
name = "lora-ffi"
|
|
870
|
-
version = "0.8.
|
|
870
|
+
version = "0.8.1"
|
|
871
871
|
dependencies = [
|
|
872
872
|
"anyhow",
|
|
873
873
|
"lora-binding-buffer",
|
|
@@ -879,7 +879,7 @@ dependencies = [
|
|
|
879
879
|
|
|
880
880
|
[[package]]
|
|
881
881
|
name = "lora-node"
|
|
882
|
-
version = "0.8.
|
|
882
|
+
version = "0.8.1"
|
|
883
883
|
dependencies = [
|
|
884
884
|
"anyhow",
|
|
885
885
|
"lora-binding-buffer",
|
|
@@ -894,7 +894,7 @@ dependencies = [
|
|
|
894
894
|
|
|
895
895
|
[[package]]
|
|
896
896
|
name = "lora-parser"
|
|
897
|
-
version = "0.8.
|
|
897
|
+
version = "0.8.1"
|
|
898
898
|
dependencies = [
|
|
899
899
|
"lora-ast",
|
|
900
900
|
"pest",
|
|
@@ -905,7 +905,7 @@ dependencies = [
|
|
|
905
905
|
|
|
906
906
|
[[package]]
|
|
907
907
|
name = "lora-python"
|
|
908
|
-
version = "0.8.
|
|
908
|
+
version = "0.8.1"
|
|
909
909
|
dependencies = [
|
|
910
910
|
"anyhow",
|
|
911
911
|
"lora-database",
|
|
@@ -917,7 +917,7 @@ dependencies = [
|
|
|
917
917
|
|
|
918
918
|
[[package]]
|
|
919
919
|
name = "lora-server"
|
|
920
|
-
version = "0.8.
|
|
920
|
+
version = "0.8.1"
|
|
921
921
|
dependencies = [
|
|
922
922
|
"anyhow",
|
|
923
923
|
"axum",
|
|
@@ -931,7 +931,7 @@ dependencies = [
|
|
|
931
931
|
|
|
932
932
|
[[package]]
|
|
933
933
|
name = "lora-snapshot"
|
|
934
|
-
version = "0.8.
|
|
934
|
+
version = "0.8.1"
|
|
935
935
|
dependencies = [
|
|
936
936
|
"argon2",
|
|
937
937
|
"bincode",
|
|
@@ -946,7 +946,7 @@ dependencies = [
|
|
|
946
946
|
|
|
947
947
|
[[package]]
|
|
948
948
|
name = "lora-store"
|
|
949
|
-
version = "0.8.
|
|
949
|
+
version = "0.8.1"
|
|
950
950
|
dependencies = [
|
|
951
951
|
"bincode",
|
|
952
952
|
"crc32fast",
|
|
@@ -958,7 +958,7 @@ dependencies = [
|
|
|
958
958
|
|
|
959
959
|
[[package]]
|
|
960
960
|
name = "lora-wal"
|
|
961
|
-
version = "0.8.
|
|
961
|
+
version = "0.8.1"
|
|
962
962
|
dependencies = [
|
|
963
963
|
"crc32fast",
|
|
964
964
|
"lora-store",
|
|
@@ -968,7 +968,7 @@ dependencies = [
|
|
|
968
968
|
|
|
969
969
|
[[package]]
|
|
970
970
|
name = "lora-wasm"
|
|
971
|
-
version = "0.8.
|
|
971
|
+
version = "0.8.1"
|
|
972
972
|
dependencies = [
|
|
973
973
|
"anyhow",
|
|
974
974
|
"console_error_panic_hook",
|
|
@@ -984,7 +984,7 @@ dependencies = [
|
|
|
984
984
|
|
|
985
985
|
[[package]]
|
|
986
986
|
name = "lora_ruby"
|
|
987
|
-
version = "0.8.
|
|
987
|
+
version = "0.8.1"
|
|
988
988
|
dependencies = [
|
|
989
989
|
"anyhow",
|
|
990
990
|
"lora-database",
|
|
@@ -4,7 +4,7 @@ resolver = "2"
|
|
|
4
4
|
|
|
5
5
|
[workspace.package]
|
|
6
6
|
edition = "2021"
|
|
7
|
-
version = "0.8.
|
|
7
|
+
version = "0.8.1"
|
|
8
8
|
license = "BUSL-1.1"
|
|
9
9
|
authors = ["LoraDB, Inc."]
|
|
10
10
|
repository = "https://github.com/lora-db/lora"
|
|
@@ -15,16 +15,16 @@ 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.8.
|
|
19
|
-
lora-parser = { path = "crates/lora-parser", version = "=0.8.
|
|
20
|
-
lora-analyzer = { path = "crates/lora-analyzer", version = "=0.8.
|
|
21
|
-
lora-compiler = { path = "crates/lora-compiler", version = "=0.8.
|
|
22
|
-
lora-store = { path = "crates/lora-store", version = "=0.8.
|
|
23
|
-
lora-snapshot = { path = "crates/lora-snapshot", version = "=0.8.
|
|
24
|
-
lora-wal = { path = "crates/lora-wal", version = "=0.8.
|
|
25
|
-
lora-executor = { path = "crates/lora-executor", version = "=0.8.
|
|
26
|
-
lora-database = { path = "crates/lora-database", version = "=0.8.
|
|
27
|
-
lora-binding-buffer = { path = "crates/bindings/lora-binding-buffer", version = "=0.8.
|
|
18
|
+
lora-ast = { path = "crates/lora-ast", version = "=0.8.1" }
|
|
19
|
+
lora-parser = { path = "crates/lora-parser", version = "=0.8.1" }
|
|
20
|
+
lora-analyzer = { path = "crates/lora-analyzer", version = "=0.8.1" }
|
|
21
|
+
lora-compiler = { path = "crates/lora-compiler", version = "=0.8.1" }
|
|
22
|
+
lora-store = { path = "crates/lora-store", version = "=0.8.1" }
|
|
23
|
+
lora-snapshot = { path = "crates/lora-snapshot", version = "=0.8.1", default-features = false }
|
|
24
|
+
lora-wal = { path = "crates/lora-wal", version = "=0.8.1" }
|
|
25
|
+
lora-executor = { path = "crates/lora-executor", version = "=0.8.1" }
|
|
26
|
+
lora-database = { path = "crates/lora-database", version = "=0.8.1" }
|
|
27
|
+
lora-binding-buffer = { path = "crates/bindings/lora-binding-buffer", version = "=0.8.1" }
|
|
28
28
|
|
|
29
29
|
# External crates.
|
|
30
30
|
anyhow = "1"
|
|
@@ -21,12 +21,12 @@ use std::io::BufReader;
|
|
|
21
21
|
use std::path::Path;
|
|
22
22
|
use std::sync::{Arc, Mutex};
|
|
23
23
|
|
|
24
|
-
use arc_swap::ArcSwap;
|
|
25
24
|
use lora_store::{GraphStorage, GraphStorageMut, InMemoryGraph, MutationRecorder};
|
|
26
25
|
use lora_wal::{replay_dir, Lsn, Wal, WalConfig, WalMirror, WalRecorder};
|
|
27
26
|
|
|
28
27
|
use crate::database::Database;
|
|
29
28
|
use crate::error::{LoraError, LoraErrorCode};
|
|
29
|
+
use crate::live_store::LiveStore;
|
|
30
30
|
use crate::named::{DatabaseName, DatabaseOpenOptions};
|
|
31
31
|
use crate::plan_cache::PlanCache;
|
|
32
32
|
use crate::snapshot::{ManagedSnapshotStore, SnapshotConfig};
|
|
@@ -221,7 +221,7 @@ impl Database<InMemoryGraph> {
|
|
|
221
221
|
) -> Self {
|
|
222
222
|
graph.set_mutation_recorder(Some(recorder.clone() as Arc<dyn MutationRecorder>));
|
|
223
223
|
Self {
|
|
224
|
-
store: Arc::new(
|
|
224
|
+
store: Arc::new(LiveStore::new(Arc::new(graph))),
|
|
225
225
|
writer: Arc::new(Mutex::new(())),
|
|
226
226
|
lock_table: Arc::new(lora_store::LockTable::new()),
|
|
227
227
|
wal: Some(recorder),
|
|
@@ -236,7 +236,7 @@ where
|
|
|
236
236
|
S: GraphStorage + GraphStorageMut + Any + Clone + Send + Sync + 'static,
|
|
237
237
|
{
|
|
238
238
|
/// Build a database from a pre-wrapped, shared store.
|
|
239
|
-
pub fn new(store: Arc<
|
|
239
|
+
pub(crate) fn new(store: Arc<LiveStore<S>>) -> Self {
|
|
240
240
|
Self {
|
|
241
241
|
store,
|
|
242
242
|
writer: Arc::new(Mutex::new(())),
|
|
@@ -249,6 +249,6 @@ where
|
|
|
249
249
|
|
|
250
250
|
/// Build a database by taking ownership of a bare graph store.
|
|
251
251
|
pub fn from_graph(graph: S) -> Self {
|
|
252
|
-
Self::new(Arc::new(
|
|
252
|
+
Self::new(Arc::new(LiveStore::new(Arc::new(graph))))
|
|
253
253
|
}
|
|
254
254
|
}
|
|
@@ -2,8 +2,6 @@ use std::any::Any;
|
|
|
2
2
|
use std::collections::BTreeMap;
|
|
3
3
|
use std::sync::{Arc, Mutex};
|
|
4
4
|
|
|
5
|
-
use arc_swap::ArcSwap;
|
|
6
|
-
|
|
7
5
|
use anyhow::{anyhow, Result};
|
|
8
6
|
use lora_ast::{Direction, Document};
|
|
9
7
|
use lora_executor::{lora_value_to_property, ExecuteOptions, LoraValue, QueryResult};
|
|
@@ -25,6 +23,7 @@ mod write_guard;
|
|
|
25
23
|
|
|
26
24
|
use crate::error::LoraError;
|
|
27
25
|
use crate::explain::{QueryPlan, QueryProfile};
|
|
26
|
+
use crate::live_store::LiveStore;
|
|
28
27
|
use crate::plan_cache::PlanCache;
|
|
29
28
|
use crate::snapshot::ManagedSnapshotStore;
|
|
30
29
|
use crate::wal::write_scope::WalAbortPolicy;
|
|
@@ -72,25 +71,25 @@ pub trait QueryRunner: Send + Sync + 'static {
|
|
|
72
71
|
/// the WAL handle is `None` and the engine pays only the existing
|
|
73
72
|
/// `MutationRecorder::record` null-pointer check per mutation.
|
|
74
73
|
pub struct Database<S> {
|
|
75
|
-
/// The current authoritative store
|
|
76
|
-
///
|
|
77
|
-
///
|
|
78
|
-
///
|
|
79
|
-
///
|
|
80
|
-
///
|
|
81
|
-
///
|
|
82
|
-
///
|
|
83
|
-
|
|
84
|
-
///
|
|
85
|
-
|
|
86
|
-
///
|
|
74
|
+
/// The current authoritative store. Reads call `store.load_full()`
|
|
75
|
+
/// to obtain an independent `Arc<S>` snapshot; writers take the
|
|
76
|
+
/// `writer` Mutex and then a brief write-lock on the inner
|
|
77
|
+
/// `RwLock<Arc<S>>`, mutating in-place via `Arc::make_mut`. When
|
|
78
|
+
/// no in-flight reader holds a snapshot Arc, `make_mut` returns
|
|
79
|
+
/// `&mut S` without cloning the graph — that's the single-writer
|
|
80
|
+
/// fast path that "CREATE one node" / "SET property" depend on
|
|
81
|
+
/// for graph-size-independent throughput. When concurrent readers
|
|
82
|
+
/// are alive, `make_mut` clones once and the readers keep
|
|
83
|
+
/// observing the pre-mutation state via their old Arc.
|
|
84
|
+
pub(crate) store: Arc<LiveStore<S>>,
|
|
85
|
+
/// Serializes commit ordering. Held across `mutate-WAL-publish` so
|
|
86
|
+
/// WAL records are appended in the same order live state advances.
|
|
87
|
+
/// Readers never touch this Mutex; only writers contend.
|
|
87
88
|
pub(crate) writer: Arc<Mutex<()>>,
|
|
88
|
-
/// Per-record write locks
|
|
89
|
-
///
|
|
90
|
-
///
|
|
91
|
-
///
|
|
92
|
-
/// concurrent commits, where two writers with overlapping write
|
|
93
|
-
/// sets need a real serialization point.
|
|
89
|
+
/// Per-record write locks. Plumbed for a future phase that allows
|
|
90
|
+
/// concurrent commits across disjoint write sets; today the writer
|
|
91
|
+
/// Mutex provides single-writer-at-a-time semantics so this table
|
|
92
|
+
/// is idle.
|
|
94
93
|
#[allow(dead_code)]
|
|
95
94
|
pub(crate) lock_table: Arc<lora_store::LockTable>,
|
|
96
95
|
pub(crate) wal: Option<Arc<WalRecorder>>,
|
|
@@ -159,10 +158,12 @@ where
|
|
|
159
158
|
self.wal.as_ref()
|
|
160
159
|
}
|
|
161
160
|
|
|
162
|
-
///
|
|
163
|
-
///
|
|
164
|
-
|
|
165
|
-
|
|
161
|
+
/// Snapshot the current authoritative graph. Equivalent to the
|
|
162
|
+
/// historical `database.store().load_full()` pattern, exposed here
|
|
163
|
+
/// so external callers don't need to name the internal storage
|
|
164
|
+
/// wrapper.
|
|
165
|
+
pub fn snapshot(&self) -> Arc<S> {
|
|
166
|
+
self.store.load_full()
|
|
166
167
|
}
|
|
167
168
|
|
|
168
169
|
/// Parse a query string into an AST without executing it.
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
//! Single-writer auto-commit write path for `InMemoryGraph`.
|
|
2
|
+
//!
|
|
3
|
+
//! Earlier revisions cloned the entire snapshot into a "staged" graph
|
|
4
|
+
//! per mutating query (see `git log` for the OCC + `ArcSwap` design).
|
|
5
|
+
//! That made every CREATE / SET / DELETE pay an O(N+E) graph clone,
|
|
6
|
+
//! which dominated wall-clock for write-heavy benches against large
|
|
7
|
+
//! graphs.
|
|
8
|
+
//!
|
|
9
|
+
//! The current implementation acquires the writer mutex, takes the
|
|
10
|
+
//! `RwLock` write guard inside [`LiveStore`], and uses
|
|
11
|
+
//! `Arc::make_mut` against the live `Arc<S>`. When no in-flight reader
|
|
12
|
+
//! holds a snapshot Arc clone, `make_mut` returns `&mut S` *without*
|
|
13
|
+
//! cloning the graph — restoring v0.6's mutate-in-place cost.
|
|
14
|
+
//! Concurrent readers, when present, force a single CoW clone and keep
|
|
15
|
+
//! observing their pre-mutation snapshot via the Arc they already
|
|
16
|
+
//! hold.
|
|
17
|
+
//!
|
|
18
|
+
//! Trade-off vs. the prior OCC clone-then-publish: a query that
|
|
19
|
+
//! fails mid-execution leaves the live graph partially mutated. The
|
|
20
|
+
//! WAL records are not appended (the buffer is drained without
|
|
21
|
+
//! commit), so durable state stays consistent and recovery from
|
|
22
|
+
//! snapshot+WAL is unaffected. The same caveat applies to the
|
|
23
|
+
//! pessimistic `with_logged_write_guard` path with
|
|
24
|
+
//! `WalAbortPolicy::PoisonIfMutated`.
|
|
25
|
+
|
|
26
|
+
use std::any::Any;
|
|
27
|
+
use std::collections::BTreeMap;
|
|
28
|
+
use std::sync::{Arc, Mutex};
|
|
29
|
+
use std::time::Instant;
|
|
30
|
+
|
|
31
|
+
use anyhow::Result;
|
|
32
|
+
use lora_compiler::CompiledQuery;
|
|
33
|
+
use lora_executor::{LoraValue, MutableExecutionContext, MutableExecutor, Row};
|
|
34
|
+
use lora_store::{GraphStorage, GraphStorageMut, MutationEvent, MutationRecorder};
|
|
35
|
+
|
|
36
|
+
use crate::database::Database;
|
|
37
|
+
use crate::transaction::BufferingRecorder;
|
|
38
|
+
use crate::wal::write_scope::ensure_wal_query_can_start;
|
|
39
|
+
|
|
40
|
+
use super::replay::install_recorder_if_inmemory;
|
|
41
|
+
|
|
42
|
+
impl<S> Database<S>
|
|
43
|
+
where
|
|
44
|
+
S: GraphStorage + GraphStorageMut + Any + Clone + Send + Sync + 'static,
|
|
45
|
+
{
|
|
46
|
+
/// Single-writer auto-commit write path. Takes the writer mutex,
|
|
47
|
+
/// mutates the live store in place via `Arc::make_mut`, and (for
|
|
48
|
+
/// WAL-backed databases) appends a single batched commit record.
|
|
49
|
+
pub(crate) fn execute_mutating_optimistic(
|
|
50
|
+
&self,
|
|
51
|
+
params: BTreeMap<String, LoraValue>,
|
|
52
|
+
deadline: Option<Instant>,
|
|
53
|
+
compiled: &Arc<CompiledQuery>,
|
|
54
|
+
) -> Result<Vec<Row>> {
|
|
55
|
+
// Serialize commit ordering so WAL records appear in the same
|
|
56
|
+
// order writers publish to the live store.
|
|
57
|
+
let _commit_lock = self
|
|
58
|
+
.writer
|
|
59
|
+
.lock()
|
|
60
|
+
.unwrap_or_else(|poisoned| poisoned.into_inner());
|
|
61
|
+
|
|
62
|
+
// Buffer mutation events tx-locally. We replay them into the
|
|
63
|
+
// durable WAL only after the executor returns success — a
|
|
64
|
+
// failed query leaves no on-disk trace.
|
|
65
|
+
let buffer = Arc::new(Mutex::new(Vec::<MutationEvent>::new()));
|
|
66
|
+
let buffering_rec: Arc<dyn MutationRecorder> =
|
|
67
|
+
Arc::new(BufferingRecorder::new(buffer.clone()));
|
|
68
|
+
|
|
69
|
+
let mut handle = self.store.write();
|
|
70
|
+
|
|
71
|
+
// Install the buffering recorder *after* `make_mut` so the
|
|
72
|
+
// (rare) CoW clone that `make_mut` performs when concurrent
|
|
73
|
+
// readers hold a snapshot doesn't drop the recorder we just
|
|
74
|
+
// installed — `InMemoryGraph::clone` intentionally drops the
|
|
75
|
+
// recorder on the cloned copy.
|
|
76
|
+
let exec_result = {
|
|
77
|
+
let staged = handle.as_mut();
|
|
78
|
+
install_recorder_if_inmemory(staged, Some(buffering_rec));
|
|
79
|
+
let mut executor = MutableExecutor::with_deadline(
|
|
80
|
+
MutableExecutionContext {
|
|
81
|
+
storage: staged,
|
|
82
|
+
params,
|
|
83
|
+
},
|
|
84
|
+
deadline,
|
|
85
|
+
);
|
|
86
|
+
let r = executor.execute_compiled_rows(compiled);
|
|
87
|
+
install_recorder_if_inmemory(staged, None);
|
|
88
|
+
r
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
let rows = match exec_result {
|
|
92
|
+
Ok(rows) => rows,
|
|
93
|
+
Err(e) => return Err(anyhow::Error::from(e)),
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
let events: Vec<MutationEvent> = std::mem::take(&mut buffer.lock().unwrap());
|
|
97
|
+
|
|
98
|
+
if events.is_empty() {
|
|
99
|
+
// Mutating-shape query that didn't actually mutate
|
|
100
|
+
// (e.g., MATCH that found nothing to SET). Reinstall the
|
|
101
|
+
// durable recorder if there is one, then return.
|
|
102
|
+
if let Some(rec) = self.wal.as_ref() {
|
|
103
|
+
let staged = handle.as_mut();
|
|
104
|
+
install_recorder_if_inmemory(
|
|
105
|
+
staged,
|
|
106
|
+
Some(rec.clone() as Arc<dyn MutationRecorder>),
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
return Ok(rows);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Durable WAL append. WAL goes first so a crash between WAL
|
|
113
|
+
// and the in-memory state stays recoverable: the next boot
|
|
114
|
+
// replays our events on top of the snapshot.
|
|
115
|
+
let mut wrote_commit = false;
|
|
116
|
+
if let Some(rec) = self.wal.as_ref() {
|
|
117
|
+
ensure_wal_query_can_start(rec)?;
|
|
118
|
+
wrote_commit = rec.commit_events(events)?.wrote();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Reinstall the durable recorder so the post-publish live
|
|
122
|
+
// store keeps observing future mutations.
|
|
123
|
+
if let Some(rec) = self.wal.as_ref() {
|
|
124
|
+
let staged = handle.as_mut();
|
|
125
|
+
install_recorder_if_inmemory(staged, Some(rec.clone() as Arc<dyn MutationRecorder>));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if wrote_commit {
|
|
129
|
+
if let Some(rec) = self.wal.as_ref() {
|
|
130
|
+
let live = handle.snapshot();
|
|
131
|
+
self.observe_snapshot_commit_if_needed(&*live, rec)?;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
Ok(rows)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
//! Mutation-event replay helpers used by recovery and any caller that
|
|
2
|
+
//! needs to apply a stream of [`MutationEvent`]s against a graph.
|
|
3
|
+
//!
|
|
4
|
+
//! Two concerns live here:
|
|
5
|
+
//!
|
|
6
|
+
//! * [`install_recorder_if_inmemory`] — best-effort attach/detach of a
|
|
7
|
+
//! [`MutationRecorder`] when the storage happens to be `InMemoryGraph`.
|
|
8
|
+
//! * [`replay_into`] — the WAL-recovery replay entry point: applies a
|
|
9
|
+
//! committed event stream to a fresh graph using id-preserving
|
|
10
|
+
//! create paths, with descriptive errors keyed by event index.
|
|
11
|
+
|
|
12
|
+
use std::any::Any;
|
|
13
|
+
use std::sync::Arc;
|
|
14
|
+
|
|
15
|
+
use anyhow::{anyhow, Result};
|
|
16
|
+
|
|
17
|
+
use lora_store::{GraphStorage, GraphStorageMut, InMemoryGraph, MutationEvent, MutationRecorder};
|
|
18
|
+
|
|
19
|
+
/// Best-effort: install a mutation recorder on the storage when the
|
|
20
|
+
/// concrete type is `InMemoryGraph`. The WAL's recorder lives on the
|
|
21
|
+
/// store, but `GraphStorage` is an open trait — backends that don't
|
|
22
|
+
/// support recorders are simply skipped (their writes go unobserved
|
|
23
|
+
/// for WAL purposes, which is correct because they don't drive the WAL
|
|
24
|
+
/// in the first place).
|
|
25
|
+
pub(crate) fn install_recorder_if_inmemory<S: GraphStorage + Any + Sized>(
|
|
26
|
+
store: &mut S,
|
|
27
|
+
recorder: Option<Arc<dyn MutationRecorder>>,
|
|
28
|
+
) {
|
|
29
|
+
let any: &mut dyn Any = store;
|
|
30
|
+
if let Some(graph) = any.downcast_mut::<InMemoryGraph>() {
|
|
31
|
+
graph.set_mutation_recorder(recorder);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/// Apply a `MutationEvent` stream to an in-memory graph by dispatching
|
|
36
|
+
/// each variant to the matching store operation.
|
|
37
|
+
///
|
|
38
|
+
/// Creation events are replayed through id-preserving paths, not the
|
|
39
|
+
/// normal allocator-backed mutation methods. That matters after aborted
|
|
40
|
+
/// transactions: an aborted create can consume id `N` in the original
|
|
41
|
+
/// process, be dropped by replay, and leave the next committed create at
|
|
42
|
+
/// id `N + 1`. Reusing the regular allocator would shift ids downward.
|
|
43
|
+
///
|
|
44
|
+
/// Replay must be invoked **before** the `WalRecorder` is installed
|
|
45
|
+
/// on the graph. Otherwise the replay's own mutations would fire the
|
|
46
|
+
/// recorder and re-write the same events to the WAL, doubling them on
|
|
47
|
+
/// the next recovery.
|
|
48
|
+
pub(crate) fn replay_into(graph: &mut InMemoryGraph, events: Vec<MutationEvent>) -> Result<()> {
|
|
49
|
+
for (idx, event) in events.into_iter().enumerate() {
|
|
50
|
+
match event {
|
|
51
|
+
MutationEvent::CreateNode {
|
|
52
|
+
id,
|
|
53
|
+
labels,
|
|
54
|
+
properties,
|
|
55
|
+
} => {
|
|
56
|
+
graph
|
|
57
|
+
.replay_create_node(id, labels, properties)
|
|
58
|
+
.map_err(|e| anyhow!("WAL replay failed at event {idx}: {e}"))?;
|
|
59
|
+
}
|
|
60
|
+
MutationEvent::CreateRelationship {
|
|
61
|
+
id,
|
|
62
|
+
src,
|
|
63
|
+
dst,
|
|
64
|
+
rel_type,
|
|
65
|
+
properties,
|
|
66
|
+
} => {
|
|
67
|
+
graph
|
|
68
|
+
.replay_create_relationship(id, src, dst, &rel_type, properties)
|
|
69
|
+
.map_err(|e| anyhow!("WAL replay failed at event {idx}: {e}"))?;
|
|
70
|
+
}
|
|
71
|
+
MutationEvent::SetNodeProperty {
|
|
72
|
+
node_id,
|
|
73
|
+
key,
|
|
74
|
+
value,
|
|
75
|
+
} => {
|
|
76
|
+
if !graph.set_node_property(node_id, key, value) {
|
|
77
|
+
return Err(anyhow!(
|
|
78
|
+
"WAL replay failed at event {idx}: missing node {node_id} for property set"
|
|
79
|
+
));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
MutationEvent::RemoveNodeProperty { node_id, key } => {
|
|
83
|
+
if !graph.remove_node_property(node_id, &key) {
|
|
84
|
+
return Err(anyhow!(
|
|
85
|
+
"WAL replay failed at event {idx}: missing node {node_id} for property removal"
|
|
86
|
+
));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
MutationEvent::AddNodeLabel { node_id, label } => {
|
|
90
|
+
if !graph.add_node_label(node_id, &label) {
|
|
91
|
+
return Err(anyhow!(
|
|
92
|
+
"WAL replay failed at event {idx}: missing node {node_id} for label add"
|
|
93
|
+
));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
MutationEvent::RemoveNodeLabel { node_id, label } => {
|
|
97
|
+
if !graph.remove_node_label(node_id, &label) {
|
|
98
|
+
return Err(anyhow!(
|
|
99
|
+
"WAL replay failed at event {idx}: missing node {node_id} for label removal"
|
|
100
|
+
));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
MutationEvent::SetRelationshipProperty { rel_id, key, value } => {
|
|
104
|
+
if !graph.set_relationship_property(rel_id, key, value) {
|
|
105
|
+
return Err(anyhow!(
|
|
106
|
+
"WAL replay failed at event {idx}: missing relationship {rel_id} for property set"
|
|
107
|
+
));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
MutationEvent::RemoveRelationshipProperty { rel_id, key } => {
|
|
111
|
+
if !graph.remove_relationship_property(rel_id, &key) {
|
|
112
|
+
return Err(anyhow!(
|
|
113
|
+
"WAL replay failed at event {idx}: missing relationship {rel_id} for property removal"
|
|
114
|
+
));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
MutationEvent::DeleteRelationship { rel_id } => {
|
|
118
|
+
if !graph.delete_relationship(rel_id) {
|
|
119
|
+
return Err(anyhow!(
|
|
120
|
+
"WAL replay failed at event {idx}: missing relationship {rel_id} for delete"
|
|
121
|
+
));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
MutationEvent::DeleteNode { node_id } => {
|
|
125
|
+
if !graph.delete_node(node_id) {
|
|
126
|
+
return Err(anyhow!(
|
|
127
|
+
"WAL replay failed at event {idx}: missing or attached node {node_id} for delete"
|
|
128
|
+
));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
MutationEvent::DetachDeleteNode { node_id } => {
|
|
132
|
+
// After the cascading DeleteRelationship +
|
|
133
|
+
// DeleteNode events have already replayed, the node
|
|
134
|
+
// is gone and this becomes a no-op. Calling it
|
|
135
|
+
// anyway is harmless.
|
|
136
|
+
graph.detach_delete_node(node_id);
|
|
137
|
+
}
|
|
138
|
+
MutationEvent::Clear => {
|
|
139
|
+
graph.clear();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
Ok(())
|
|
144
|
+
}
|