lora-python 0.8.1__tar.gz → 0.8.3__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.
Files changed (253) hide show
  1. {lora_python-0.8.1 → lora_python-0.8.3}/Cargo.lock +16 -16
  2. {lora_python-0.8.1 → lora_python-0.8.3}/Cargo.toml +11 -11
  3. {lora_python-0.8.1 → lora_python-0.8.3}/PKG-INFO +1 -1
  4. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/Cargo.toml +4 -0
  5. lora_python-0.8.3/crates/lora-database/benches/concurrency_guard_benchmarks.rs +303 -0
  6. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/src/database/builder.rs +2 -3
  7. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/src/database/execute.rs +11 -19
  8. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/src/database/mod.rs +16 -14
  9. lora_python-0.8.3/crates/lora-database/src/database/occ.rs +52 -0
  10. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/src/database/write_guard.rs +113 -17
  11. lora_python-0.8.3/crates/lora-database/src/durable_io.rs +30 -0
  12. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/src/lib.rs +1 -0
  13. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/src/snapshot/mod.rs +7 -18
  14. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/src/snapshot/store.rs +4 -14
  15. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/src/stream.rs +3 -5
  16. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/src/transaction.rs +0 -5
  17. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/src/wal/admin.rs +4 -4
  18. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/src/wal/archive/format.rs +3 -2
  19. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/src/wal/archive/platform.rs +3 -11
  20. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/src/wal/write_scope.rs +1 -1
  21. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-wal/src/config.rs +16 -17
  22. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-wal/src/dir.rs +5 -13
  23. lora_python-0.8.3/crates/lora-wal/src/io.rs +31 -0
  24. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-wal/src/lib.rs +1 -0
  25. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-wal/src/recorder/errors.rs +4 -5
  26. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-wal/src/recorder/recorder.rs +92 -105
  27. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-wal/src/recorder/tests.rs +1 -1
  28. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-wal/src/segment.rs +10 -10
  29. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-wal/src/testing.rs +10 -2
  30. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-wal/src/wal/group_flusher.rs +5 -0
  31. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-wal/src/wal/mod.rs +7 -4
  32. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-wal/src/wal/tests.rs +2 -22
  33. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-wal/src/wal/wal.rs +132 -48
  34. {lora_python-0.8.1 → lora_python-0.8.3}/pyproject.toml +1 -1
  35. lora_python-0.8.1/crates/lora-database/src/database/occ.rs +0 -137
  36. {lora_python-0.8.1 → lora_python-0.8.3}/LICENSE +0 -0
  37. {lora_python-0.8.1 → lora_python-0.8.3}/README.md +0 -0
  38. {lora_python-0.8.1 → lora_python-0.8.3}/crates/bindings/lora-python/.gitignore +0 -0
  39. {lora_python-0.8.1 → lora_python-0.8.3}/crates/bindings/lora-python/Cargo.toml +0 -0
  40. {lora_python-0.8.1 → lora_python-0.8.3}/crates/bindings/lora-python/LICENSE +0 -0
  41. {lora_python-0.8.1 → lora_python-0.8.3}/crates/bindings/lora-python/README.md +0 -0
  42. {lora_python-0.8.1 → lora_python-0.8.3}/crates/bindings/lora-python/build.rs +0 -0
  43. {lora_python-0.8.1 → lora_python-0.8.3}/crates/bindings/lora-python/examples/async_demo.py +0 -0
  44. {lora_python-0.8.1 → lora_python-0.8.3}/crates/bindings/lora-python/examples/basic.py +0 -0
  45. {lora_python-0.8.1 → lora_python-0.8.3}/crates/bindings/lora-python/src/errors.rs +0 -0
  46. {lora_python-0.8.1 → lora_python-0.8.3}/crates/bindings/lora-python/src/from_python.rs +0 -0
  47. {lora_python-0.8.1 → lora_python-0.8.3}/crates/bindings/lora-python/src/lib.rs +0 -0
  48. {lora_python-0.8.1 → lora_python-0.8.3}/crates/bindings/lora-python/src/to_python.rs +0 -0
  49. {lora_python-0.8.1 → lora_python-0.8.3}/crates/bindings/lora-python/tests/test_async.py +0 -0
  50. {lora_python-0.8.1 → lora_python-0.8.3}/crates/bindings/lora-python/tests/test_explain_profile.py +0 -0
  51. {lora_python-0.8.1 → lora_python-0.8.3}/crates/bindings/lora-python/tests/test_sync.py +0 -0
  52. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-analyzer/Cargo.toml +0 -0
  53. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-analyzer/src/analyzer/clauses.rs +0 -0
  54. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-analyzer/src/analyzer/expressions.rs +0 -0
  55. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-analyzer/src/analyzer/mod.rs +0 -0
  56. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-analyzer/src/analyzer/patterns.rs +0 -0
  57. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-analyzer/src/analyzer/state.rs +0 -0
  58. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-analyzer/src/analyzer/tests.rs +0 -0
  59. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-analyzer/src/errors.rs +0 -0
  60. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-analyzer/src/lib.rs +0 -0
  61. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-analyzer/src/resolved.rs +0 -0
  62. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-analyzer/src/scope.rs +0 -0
  63. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-analyzer/src/symbols.rs +0 -0
  64. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-analyzer/tests/error_messages.rs +0 -0
  65. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-ast/Cargo.toml +0 -0
  66. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-ast/src/ast.rs +0 -0
  67. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-ast/src/lib.rs +0 -0
  68. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-compiler/Cargo.toml +0 -0
  69. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-compiler/src/lib.rs +0 -0
  70. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-compiler/src/logical.rs +0 -0
  71. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-compiler/src/optimizer.rs +0 -0
  72. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-compiler/src/pattern.rs +0 -0
  73. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-compiler/src/physical.rs +0 -0
  74. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-compiler/src/plan_tree.rs +0 -0
  75. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-compiler/src/planner.rs +0 -0
  76. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/benches/advanced_benchmarks.rs +0 -0
  77. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/benches/concurrent_benchmarks.rs +0 -0
  78. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/benches/engine_benchmarks.rs +0 -0
  79. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/benches/fixtures.rs +0 -0
  80. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/benches/perf_smoke_baseline.json +0 -0
  81. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/benches/perf_smoke_benchmarks.rs +0 -0
  82. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/benches/scale_benchmarks.rs +0 -0
  83. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/benches/temporal_spatial_benchmarks.rs +0 -0
  84. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/benches/wal_benchmarks.rs +0 -0
  85. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/src/database/compile.rs +0 -0
  86. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/src/database/explain.rs +0 -0
  87. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/src/database/graph_api.rs +0 -0
  88. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/src/database/profile.rs +0 -0
  89. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/src/database/pull_mode.rs +0 -0
  90. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/src/database/replay.rs +0 -0
  91. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/src/database/stream.rs +0 -0
  92. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/src/error.rs +0 -0
  93. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/src/explain.rs +0 -0
  94. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/src/live_store.rs +0 -0
  95. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/src/named.rs +0 -0
  96. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/src/plan_cache.rs +0 -0
  97. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/src/snapshot/json.rs +0 -0
  98. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/src/wal/archive/lock.rs +0 -0
  99. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/src/wal/archive/worker.rs +0 -0
  100. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/src/wal/archive/workspace.rs +0 -0
  101. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/src/wal/archive.rs +0 -0
  102. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/src/wal/mod.rs +0 -0
  103. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/tests/advanced_queries.rs +0 -0
  104. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/tests/aggregation.rs +0 -0
  105. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/tests/backend_stub.rs +0 -0
  106. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/tests/binary.rs +0 -0
  107. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/tests/create.rs +0 -0
  108. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/tests/error_messages.rs +0 -0
  109. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/tests/errors.rs +0 -0
  110. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/tests/explain_profile.rs +0 -0
  111. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/tests/expressions.rs +0 -0
  112. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/tests/functions_extended.rs +0 -0
  113. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/tests/invariants.rs +0 -0
  114. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/tests/managed_snapshots.rs +0 -0
  115. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/tests/match.rs +0 -0
  116. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/tests/merge.rs +0 -0
  117. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/tests/ordering.rs +0 -0
  118. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/tests/parameters.rs +0 -0
  119. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/tests/parser.rs +0 -0
  120. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/tests/paths.rs +0 -0
  121. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/tests/projection.rs +0 -0
  122. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/tests/scale.rs +0 -0
  123. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/tests/seeds.rs +0 -0
  124. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/tests/snapshot.rs +0 -0
  125. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/tests/spatial.rs +0 -0
  126. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/tests/temporal.rs +0 -0
  127. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/tests/test_helpers.rs +0 -0
  128. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/tests/transactions.rs +0 -0
  129. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/tests/types_advanced.rs +0 -0
  130. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/tests/union.rs +0 -0
  131. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/tests/update.rs +0 -0
  132. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/tests/vectors.rs +0 -0
  133. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/tests/wal.rs +0 -0
  134. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/tests/where_clause.rs +0 -0
  135. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-database/tests/with.rs +0 -0
  136. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/Cargo.toml +0 -0
  137. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/src/errors.rs +0 -0
  138. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/src/eval/binops.rs +0 -0
  139. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/src/eval/errors.rs +0 -0
  140. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/src/eval/expr.rs +0 -0
  141. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/src/eval/functions.rs +0 -0
  142. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/src/eval/mod.rs +0 -0
  143. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/src/eval/point.rs +0 -0
  144. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/src/eval/vector.rs +0 -0
  145. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/src/executor/helpers.rs +0 -0
  146. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/src/executor/immutable.rs +0 -0
  147. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/src/executor/mod.rs +0 -0
  148. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/src/executor/mutable.rs +0 -0
  149. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/src/lib.rs +0 -0
  150. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/src/profile.rs +0 -0
  151. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/src/pull/aggregate.rs +0 -0
  152. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/src/pull/columns.rs +0 -0
  153. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/src/pull/context.rs +0 -0
  154. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/src/pull/expand.rs +0 -0
  155. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/src/pull/filter.rs +0 -0
  156. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/src/pull/hydration.rs +0 -0
  157. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/src/pull/mod.rs +0 -0
  158. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/src/pull/mutable.rs +0 -0
  159. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/src/pull/optional.rs +0 -0
  160. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/src/pull/path.rs +0 -0
  161. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/src/pull/projection.rs +0 -0
  162. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/src/pull/scan.rs +0 -0
  163. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/src/pull/shape.rs +0 -0
  164. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/src/pull/sort.rs +0 -0
  165. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/src/pull/source.rs +0 -0
  166. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/src/pull/tests.rs +0 -0
  167. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/src/pull/traits.rs +0 -0
  168. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/src/pull/union.rs +0 -0
  169. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/src/value.rs +0 -0
  170. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-executor/tests/error_messages.rs +0 -0
  171. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-parser/Cargo.toml +0 -0
  172. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-parser/src/cypher.pest +0 -0
  173. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-parser/src/errors.rs +0 -0
  174. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-parser/src/lib.rs +0 -0
  175. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-parser/src/parser/clauses.rs +0 -0
  176. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-parser/src/parser/expressions.rs +0 -0
  177. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-parser/src/parser/literals.rs +0 -0
  178. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-parser/src/parser/mod.rs +0 -0
  179. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-parser/src/parser/patterns.rs +0 -0
  180. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-parser/src/parser/query.rs +0 -0
  181. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-parser/src/parser/tests.rs +0 -0
  182. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-parser/src/parser/util.rs +0 -0
  183. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-parser/tests/error_messages.rs +0 -0
  184. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-snapshot/Cargo.toml +0 -0
  185. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-snapshot/src/body.rs +0 -0
  186. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-snapshot/src/codec.rs +0 -0
  187. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-snapshot/src/columnar.rs +0 -0
  188. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-snapshot/src/envelope.rs +0 -0
  189. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-snapshot/src/errors.rs +0 -0
  190. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-snapshot/src/format.rs +0 -0
  191. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-snapshot/src/lib.rs +0 -0
  192. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-snapshot/src/options.rs +0 -0
  193. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-snapshot/src/tests.rs +0 -0
  194. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-snapshot/src/transform.rs +0 -0
  195. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-snapshot/src/view.rs +0 -0
  196. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-snapshot/tests/error_messages.rs +0 -0
  197. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/Cargo.toml +0 -0
  198. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/lib.rs +0 -0
  199. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/lock_table.rs +0 -0
  200. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/memory/graph.rs +0 -0
  201. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/memory/impls.rs +0 -0
  202. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/memory/mod.rs +0 -0
  203. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/memory/property_index.rs +0 -0
  204. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/memory/snapshot.rs +0 -0
  205. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/memory/tests.rs +0 -0
  206. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/mutation.rs +0 -0
  207. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/snapshot.rs +0 -0
  208. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/traits.rs +0 -0
  209. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/types/binary/mod.rs +0 -0
  210. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/types/binary/tests.rs +0 -0
  211. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/types/binary/traits.rs +0 -0
  212. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/types/binary/types.rs +0 -0
  213. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/types/graph.rs +0 -0
  214. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/types/mod.rs +0 -0
  215. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/types/property_value.rs +0 -0
  216. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/types/spatial/distance.rs +0 -0
  217. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/types/spatial/mod.rs +0 -0
  218. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/types/spatial/point.rs +0 -0
  219. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/types/spatial/srid.rs +0 -0
  220. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/types/spatial/tests.rs +0 -0
  221. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/types/temporal/calendar.rs +0 -0
  222. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/types/temporal/date.rs +0 -0
  223. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/types/temporal/datetime.rs +0 -0
  224. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/types/temporal/duration.rs +0 -0
  225. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/types/temporal/format.rs +0 -0
  226. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/types/temporal/mod.rs +0 -0
  227. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/types/temporal/parsing.rs +0 -0
  228. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/types/temporal/time.rs +0 -0
  229. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/types/vector/build.rs +0 -0
  230. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/types/vector/mod.rs +0 -0
  231. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/types/vector/similarity.rs +0 -0
  232. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/types/vector/tests.rs +0 -0
  233. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/src/types/vector/types.rs +0 -0
  234. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-store/tests/error_messages.rs +0 -0
  235. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-wal/Cargo.toml +0 -0
  236. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-wal/src/codec/decode.rs +0 -0
  237. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-wal/src/codec/encode.rs +0 -0
  238. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-wal/src/codec/format.rs +0 -0
  239. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-wal/src/codec/mod.rs +0 -0
  240. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-wal/src/codec/tests.rs +0 -0
  241. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-wal/src/errors.rs +0 -0
  242. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-wal/src/lock.rs +0 -0
  243. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-wal/src/lsn.rs +0 -0
  244. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-wal/src/record.rs +0 -0
  245. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-wal/src/recorder/mirror.rs +0 -0
  246. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-wal/src/recorder/mod.rs +0 -0
  247. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-wal/src/replay.rs +0 -0
  248. {lora_python-0.8.1 → lora_python-0.8.3}/crates/lora-wal/tests/error_messages.rs +0 -0
  249. {lora_python-0.8.1 → lora_python-0.8.3}/python/lora_python/__init__.py +0 -0
  250. {lora_python-0.8.1 → lora_python-0.8.3}/python/lora_python/_async.py +0 -0
  251. {lora_python-0.8.1 → lora_python-0.8.3}/python/lora_python/_native.pyi +0 -0
  252. {lora_python-0.8.1 → lora_python-0.8.3}/python/lora_python/py.typed +0 -0
  253. {lora_python-0.8.1 → lora_python-0.8.3}/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.1"
800
+ version = "0.8.3"
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.1"
810
+ version = "0.8.3"
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.1"
817
+ version = "0.8.3"
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.1"
825
+ version = "0.8.3"
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.1"
833
+ version = "0.8.3"
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.1"
855
+ version = "0.8.3"
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.1"
870
+ version = "0.8.3"
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.1"
882
+ version = "0.8.3"
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.1"
897
+ version = "0.8.3"
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.1"
908
+ version = "0.8.3"
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.1"
920
+ version = "0.8.3"
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.1"
934
+ version = "0.8.3"
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.1"
949
+ version = "0.8.3"
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.1"
961
+ version = "0.8.3"
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.1"
971
+ version = "0.8.3"
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.1"
987
+ version = "0.8.3"
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.1"
7
+ version = "0.8.3"
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.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" }
18
+ lora-ast = { path = "crates/lora-ast", version = "=0.8.3" }
19
+ lora-parser = { path = "crates/lora-parser", version = "=0.8.3" }
20
+ lora-analyzer = { path = "crates/lora-analyzer", version = "=0.8.3" }
21
+ lora-compiler = { path = "crates/lora-compiler", version = "=0.8.3" }
22
+ lora-store = { path = "crates/lora-store", version = "=0.8.3" }
23
+ lora-snapshot = { path = "crates/lora-snapshot", version = "=0.8.3", default-features = false }
24
+ lora-wal = { path = "crates/lora-wal", version = "=0.8.3" }
25
+ lora-executor = { path = "crates/lora-executor", version = "=0.8.3" }
26
+ lora-database = { path = "crates/lora-database", version = "=0.8.3" }
27
+ lora-binding-buffer = { path = "crates/bindings/lora-binding-buffer", version = "=0.8.3" }
28
28
 
29
29
  # External crates.
30
30
  anyhow = "1"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lora-python
3
- Version: 0.8.1
3
+ Version: 0.8.3
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: 3
6
6
  Classifier: Programming Language :: Python :: 3.8
@@ -63,3 +63,7 @@ harness = false
63
63
  [[bench]]
64
64
  name = "concurrent_benchmarks"
65
65
  harness = false
66
+
67
+ [[bench]]
68
+ name = "concurrency_guard_benchmarks"
69
+ harness = false
@@ -0,0 +1,303 @@
1
+ //! Tight local performance guard for concurrency work.
2
+ //!
3
+ //! `perf_smoke_benchmarks` is the broad CI canary and intentionally allows
4
+ //! large runner noise. This suite is narrower and meant for phase-by-phase
5
+ //! local checks while changing the concurrency/write/WAL plumbing:
6
+ //!
7
+ //! ```text
8
+ //! cargo bench -p lora-database --bench concurrency_guard_benchmarks \
9
+ //! -- --output-format bencher > /tmp/lora-before.bencher
10
+ //! # make one implementation step
11
+ //! cargo bench -p lora-database --bench concurrency_guard_benchmarks \
12
+ //! -- --output-format bencher > /tmp/lora-after.bencher
13
+ //! node scripts/check-bench-delta.mjs \
14
+ //! --baseline /tmp/lora-before.bencher \
15
+ //! --current /tmp/lora-after.bencher \
16
+ //! --threshold 1.15
17
+ //! ```
18
+ //!
19
+ //! The cases here deliberately cover the hot surfaces most likely to regress
20
+ //! while introducing concurrent writes: snapshot reads, live streams,
21
+ //! auto-commit writes, explicit transactions, mixed read/write threads, and
22
+ //! WAL-backed write paths.
23
+
24
+ mod fixtures;
25
+
26
+ use std::collections::BTreeMap;
27
+ use std::hint::black_box;
28
+ use std::path::PathBuf;
29
+ use std::sync::{Arc, Barrier};
30
+ use std::time::{Duration, SystemTime, UNIX_EPOCH};
31
+
32
+ use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
33
+ use fixtures::{build_node_graph, BenchDb, Scale};
34
+ use lora_database::{
35
+ Database, ExecuteOptions, InMemoryGraph, LoraValue, ResultFormat, SyncMode, TransactionMode,
36
+ WalConfig,
37
+ };
38
+
39
+ fn opts() -> Option<ExecuteOptions> {
40
+ Some(ExecuteOptions {
41
+ format: ResultFormat::Rows,
42
+ })
43
+ }
44
+
45
+ fn guard_config() -> Criterion {
46
+ Criterion::default()
47
+ .warm_up_time(Duration::from_millis(400))
48
+ .measurement_time(Duration::from_millis(1_800))
49
+ .sample_size(30)
50
+ .noise_threshold(0.08)
51
+ }
52
+
53
+ struct ScratchDir {
54
+ path: PathBuf,
55
+ }
56
+
57
+ impl ScratchDir {
58
+ fn new(tag: &str) -> Self {
59
+ let mut path = std::env::temp_dir();
60
+ path.push(format!(
61
+ "lora-concurrency-guard-{}-{}-{}",
62
+ tag,
63
+ std::process::id(),
64
+ SystemTime::now()
65
+ .duration_since(UNIX_EPOCH)
66
+ .unwrap()
67
+ .as_nanos()
68
+ ));
69
+ std::fs::create_dir_all(&path).unwrap();
70
+ Self { path }
71
+ }
72
+ }
73
+
74
+ impl Drop for ScratchDir {
75
+ fn drop(&mut self) {
76
+ let _ = std::fs::remove_dir_all(&self.path);
77
+ }
78
+ }
79
+
80
+ fn wal_config(dir: &std::path::Path, sync_mode: SyncMode) -> WalConfig {
81
+ WalConfig::Enabled {
82
+ dir: dir.to_path_buf(),
83
+ sync_mode,
84
+ segment_target_bytes: 8 * 1024 * 1024,
85
+ }
86
+ }
87
+
88
+ fn int_param(name: &str, value: i64) -> BTreeMap<String, LoraValue> {
89
+ let mut params = BTreeMap::new();
90
+ params.insert(name.to_string(), LoraValue::Int(value));
91
+ params
92
+ }
93
+
94
+ fn two_int_params(a: (&str, i64), b: (&str, i64)) -> BTreeMap<String, LoraValue> {
95
+ let mut params = BTreeMap::new();
96
+ params.insert(a.0.to_string(), LoraValue::Int(a.1));
97
+ params.insert(b.0.to_string(), LoraValue::Int(b.1));
98
+ params
99
+ }
100
+
101
+ fn bench_concurrency_guard(c: &mut Criterion) {
102
+ let mut group = c.benchmark_group("concurrency_guard");
103
+
104
+ // Snapshot read path: catches regressions in `LiveStore::load_full`,
105
+ // plan execution, and read-only WAL prechecks.
106
+ {
107
+ let db = build_node_graph(Scale::SMALL);
108
+ group.bench_function("read_scan_1k", |b| {
109
+ b.iter(|| {
110
+ black_box(
111
+ db.service
112
+ .execute("MATCH (n:Node) RETURN n.id", opts())
113
+ .unwrap(),
114
+ );
115
+ });
116
+ });
117
+ }
118
+
119
+ // Live read stream: pins an Arc snapshot and drops after one row.
120
+ {
121
+ let db = build_node_graph(Scale::SMALL);
122
+ group.bench_function("stream_pull_one_1k", |b| {
123
+ b.iter(|| {
124
+ let mut stream = db.service.stream("MATCH (n:Node) RETURN n.id").unwrap();
125
+ black_box(stream.next_row().unwrap());
126
+ });
127
+ });
128
+ }
129
+
130
+ // Auto-commit create on one long-lived DB. This keeps the benchmark on the
131
+ // write hot path instead of measuring fixture setup.
132
+ {
133
+ let db = BenchDb::new();
134
+ let mut next = 0i64;
135
+ group.bench_function("write_create_one_steady", |b| {
136
+ b.iter(|| {
137
+ next += 1;
138
+ black_box(
139
+ db.service
140
+ .execute_with_params(
141
+ "CREATE (:Guard {id: $id})",
142
+ opts(),
143
+ int_param("id", next),
144
+ )
145
+ .unwrap(),
146
+ );
147
+ });
148
+ });
149
+ }
150
+
151
+ // Existing-record write on a 1k graph. Future per-record commit
152
+ // validation should keep this close to the current single-writer cost.
153
+ {
154
+ let db = build_node_graph(Scale::SMALL);
155
+ let mut next = 0i64;
156
+ group.bench_function("write_set_existing_1k", |b| {
157
+ b.iter(|| {
158
+ next += 1;
159
+ black_box(
160
+ db.service
161
+ .execute_with_params(
162
+ "MATCH (n:Node {id: $id}) SET n.value = $value",
163
+ opts(),
164
+ two_int_params(("id", 500), ("value", next)),
165
+ )
166
+ .unwrap(),
167
+ );
168
+ });
169
+ });
170
+ }
171
+
172
+ // Fixed explicit transaction cost. This should stay tiny while
173
+ // introducing concurrent write bookkeeping.
174
+ {
175
+ let db = build_node_graph(Scale::SMALL);
176
+ group.bench_function("tx_roundtrip_empty", |b| {
177
+ b.iter(|| {
178
+ let tx = db
179
+ .service
180
+ .begin_transaction(TransactionMode::ReadWrite)
181
+ .unwrap();
182
+ tx.commit().unwrap();
183
+ });
184
+ });
185
+ }
186
+
187
+ // Explicit transaction write path, including buffering recorder and
188
+ // publish. Fresh DB per iteration so the committed write cannot
189
+ // accumulate into the next sample.
190
+ group.bench_function("tx_write_create_one", |b| {
191
+ b.iter_batched(
192
+ BenchDb::new,
193
+ |db| {
194
+ let mut tx = db
195
+ .service
196
+ .begin_transaction(TransactionMode::ReadWrite)
197
+ .unwrap();
198
+ black_box(tx.execute("CREATE (:TxGuard {id: 1})", opts()).unwrap());
199
+ tx.commit().unwrap();
200
+ },
201
+ BatchSize::SmallInput,
202
+ );
203
+ });
204
+
205
+ // Mixed reader/writer pressure. Thread spawn overhead is intentional here:
206
+ // this is a coarse local guard for accidental global locks on read paths.
207
+ {
208
+ let db = Arc::new(build_node_graph(Scale::SMALL).service);
209
+ let mut next = 0i64;
210
+ group.bench_function("mixed_4_readers_1_writer", |b| {
211
+ b.iter(|| {
212
+ next += 1;
213
+ let barrier = Arc::new(Barrier::new(5));
214
+ let handles: Vec<_> = (0..4)
215
+ .map(|_| {
216
+ let db = db.clone();
217
+ let barrier = barrier.clone();
218
+ std::thread::spawn(move || {
219
+ barrier.wait();
220
+ black_box(db.execute("MATCH (n:Node) RETURN n.id", opts()).unwrap());
221
+ })
222
+ })
223
+ .collect();
224
+
225
+ let writer = {
226
+ let db = db.clone();
227
+ let barrier = barrier.clone();
228
+ std::thread::spawn(move || {
229
+ barrier.wait();
230
+ black_box(
231
+ db.execute_with_params(
232
+ "CREATE (:MixedGuard {id: $id})",
233
+ opts(),
234
+ int_param("id", next),
235
+ )
236
+ .unwrap(),
237
+ );
238
+ })
239
+ };
240
+
241
+ for handle in handles {
242
+ handle.join().unwrap();
243
+ }
244
+ writer.join().unwrap();
245
+ });
246
+ });
247
+ }
248
+
249
+ // WAL write paths without the per-iteration directory setup measured.
250
+ // `None` isolates WAL encoding/flush-buffer overhead; cooperative Group
251
+ // isolates the path that future concurrent fsync coordination will touch.
252
+ {
253
+ let dir = ScratchDir::new("wal-none");
254
+ let db = Database::<InMemoryGraph>::open_with_wal(wal_config(&dir.path, SyncMode::None))
255
+ .unwrap();
256
+ let mut next = 0i64;
257
+ group.bench_function("wal_none_create_delete_one", |b| {
258
+ b.iter(|| {
259
+ next += 1;
260
+ black_box(
261
+ db.execute_with_params(
262
+ "CREATE (n:WalGuard {id: $id}) DELETE n",
263
+ opts(),
264
+ int_param("id", next),
265
+ )
266
+ .unwrap(),
267
+ );
268
+ });
269
+ });
270
+ }
271
+
272
+ {
273
+ let dir = ScratchDir::new("wal-group");
274
+ let db = Database::<InMemoryGraph>::open_with_wal(wal_config(
275
+ &dir.path,
276
+ SyncMode::Group { interval_ms: 50 },
277
+ ))
278
+ .unwrap();
279
+ let mut next = 0i64;
280
+ group.bench_function("wal_group_create_delete_one", |b| {
281
+ b.iter(|| {
282
+ next += 1;
283
+ black_box(
284
+ db.execute_with_params(
285
+ "CREATE (n:WalGuard {id: $id}) DELETE n",
286
+ opts(),
287
+ int_param("id", next),
288
+ )
289
+ .unwrap(),
290
+ );
291
+ });
292
+ });
293
+ }
294
+
295
+ group.finish();
296
+ }
297
+
298
+ criterion_group! {
299
+ name = benches;
300
+ config = guard_config();
301
+ targets = bench_concurrency_guard,
302
+ }
303
+ criterion_main!(benches);
@@ -132,9 +132,8 @@ impl Database<InMemoryGraph> {
132
132
  let mirror: Arc<dyn WalMirror> = archive;
133
133
  let recorder = Arc::new(WalRecorder::new_with_mirror(wal, Some(mirror)));
134
134
  // Mark the archive dirty so a fresh named database is materialized as
135
- // a portable ZIP. The archive writer coalesces this with any immediate
136
- // follow-up writes and flushes it in the background, with a final flush
137
- // on database drop.
135
+ // a portable ZIP. Follow-up writes refresh the archive on explicit
136
+ // `sync()` / checkpoint and on clean database drop.
138
137
  recorder.flush()?;
139
138
  Ok(Self::from_graph_with_wal(graph, recorder, None))
140
139
  }
@@ -68,21 +68,17 @@ where
68
68
  /// 2. The executor runs; every primitive mutation fires
69
69
  /// `MutationRecorder::record`, which buffers events in memory.
70
70
  /// 3. On Ok, `recorder.commit()` writes `TxBegin`, one batched
71
- /// mutation record, and `TxCommit` only when mutations occurred;
72
- /// the surrounding `recorder.flush()` runs only in that case so
73
- /// a read-only query never pays an `fsync`.
71
+ /// mutation record, and `TxCommit` only when mutations occurred,
72
+ /// then applies the WAL's configured single-thread flush policy.
74
73
  /// 4. On Err, `recorder.abort()` clears the pending batch. The
75
74
  /// engine has no rollback, so the in-memory state may already
76
75
  /// be partially mutated; the live handle is quarantined while
77
76
  /// durable recovery stays atomic because no committed batch was
78
77
  /// written.
79
- /// 5. The recorder's poisoned flag is polled once (it also
80
- /// surfaces background-flusher fsync failures from
81
- /// `SyncMode::Group`). If set, the query fails loudly with the
82
- /// durability error so the caller can act on it; the WAL
83
- /// refuses further appends until the operator restarts the
84
- /// database, which recovers from the last consistent
85
- /// snapshot + WAL.
78
+ /// 5. The recorder's poisoned flag is polled once. If set, the query
79
+ /// fails loudly with the durability error so the caller can act on it;
80
+ /// the WAL refuses further appends until the operator restarts the
81
+ /// database, which recovers from the last consistent snapshot + WAL.
86
82
  pub fn execute_with_params(
87
83
  &self,
88
84
  query: &str,
@@ -157,15 +153,11 @@ where
157
153
  .map_err(anyhow::Error::from);
158
154
  }
159
155
 
160
- // Mutating path. Drop the snapshot we used for compilation and
161
- // route through the optimistic auto-commit path: build the
162
- // working copy + mutate without holding any lock, then take
163
- // the writer Mutex only briefly to do a CAS publish (replays
164
- // buffered mutation events to the durable WAL inside the
165
- // critical section). Multiple auto-commit writers can run
166
- // their prep work in parallel; they only serialize at the
167
- // commit point. On conflict (another writer published since
168
- // we took our snapshot), retry from a fresh snapshot.
156
+ // Mutating path. Drop the snapshot used for compilation and route
157
+ // through the single-writer auto-commit path. The writer mutex is held
158
+ // for mutate + WAL commit so live state and WAL order stay identical.
159
+ // The method name still says "optimistic" because this is the seam a
160
+ // future write-set/CAS implementation can reuse.
169
161
  drop(store);
170
162
  debug_assert!(shape.is_mutating());
171
163
 
@@ -26,7 +26,6 @@ use crate::explain::{QueryPlan, QueryProfile};
26
26
  use crate::live_store::LiveStore;
27
27
  use crate::plan_cache::PlanCache;
28
28
  use crate::snapshot::ManagedSnapshotStore;
29
- use crate::wal::write_scope::WalAbortPolicy;
30
29
 
31
30
  /// Minimal abstraction any transport can depend on to run Lora queries.
32
31
  ///
@@ -192,14 +191,12 @@ where
192
191
  /// Drop every node and relationship, returning WAL/archive errors to the
193
192
  /// caller.
194
193
  ///
195
- /// When a WAL is attached, the clear is wrapped in `arm`/`commit` so the
196
- /// `MutationEvent::Clear` fired by the store reaches the log inside a
197
- /// transaction. If a failure happens after the in-memory graph has been
198
- /// cleared, the recorder is poisoned by the failing WAL path and future
199
- /// writes fail until the database is reopened from durable state.
194
+ /// When a WAL is attached, the buffered `MutationEvent::Clear` is appended
195
+ /// to the log on success. The clear runs in place against the live
196
+ /// graph via the same fast path as `with_logged_store_mut`, so a
197
+ /// large graph clear no longer pays an O(N+E) snapshot clone.
200
198
  pub fn try_clear(&self) -> Result<(), LoraError> {
201
- let guard = self.write_store();
202
- self.with_logged_write_guard(guard, WalAbortPolicy::AbortOnly, |store| {
199
+ self.with_logged_store_mut(|store| {
203
200
  store.clear();
204
201
  Ok(())
205
202
  })
@@ -236,13 +233,18 @@ where
236
233
 
237
234
  /// Run a closure with an exclusive borrow of the underlying store. Reserved
238
235
  /// for admin paths (restore, bulk load); regular mutation goes through
239
- /// `execute_with_params`. The closure mutates a staged copy that is
240
- /// published atomically when the closure returns.
236
+ /// `execute_with_params`. The closure mutates the live graph in place
237
+ /// via `Arc::make_mut`, so callers don't pay an O(N+E) snapshot clone
238
+ /// just to overwrite the graph. Concurrent readers, when present,
239
+ /// force a single CoW clone and keep observing their pre-mutation
240
+ /// snapshot via the `Arc<S>` they already hold.
241
241
  pub fn with_store_mut<R>(&self, f: impl FnOnce(&mut S) -> R) -> R {
242
- let mut guard = self.write_store();
243
- let result = f(&mut *guard);
244
- guard.publish();
245
- result
242
+ let _lock = self
243
+ .writer
244
+ .lock()
245
+ .unwrap_or_else(|poisoned| poisoned.into_inner());
246
+ let mut handle = self.store.write();
247
+ f(handle.as_mut())
246
248
  }
247
249
  }
248
250
 
@@ -0,0 +1,52 @@
1
+ //! Auto-commit write path for `InMemoryGraph`.
2
+ //!
3
+ //! This module is the dispatcher between [`Database::execute_with_params`]
4
+ //! and the canonical mutating shape in [`super::write_guard`]. It
5
+ //! builds a `MutableExecutor` against the live store and hands it to
6
+ //! [`Database::run_with_durable_recorder`], which owns the writer
7
+ //! mutex, recorder arm/commit/abort lifecycle, and the
8
+ //! managed-snapshot trigger. The single-writer design (`Arc::make_mut`
9
+ //! against the live `Arc<S>`) and the failure trade-off — a query
10
+ //! that fails mid-execution can leave the live graph partially
11
+ //! mutated, but never the durable log — are documented in
12
+ //! [`super::write_guard`].
13
+
14
+ use std::any::Any;
15
+ use std::collections::BTreeMap;
16
+ use std::sync::Arc;
17
+ use std::time::Instant;
18
+
19
+ use anyhow::Result;
20
+ use lora_compiler::CompiledQuery;
21
+ use lora_executor::{LoraValue, MutableExecutionContext, MutableExecutor, Row};
22
+ use lora_store::{GraphStorage, GraphStorageMut};
23
+
24
+ use crate::database::Database;
25
+
26
+ impl<S> Database<S>
27
+ where
28
+ S: GraphStorage + GraphStorageMut + Any + Clone + Send + Sync + 'static,
29
+ {
30
+ /// Auto-commit a mutating query. Builds a `MutableExecutor`
31
+ /// against the staged graph and routes through the canonical
32
+ /// write shape in [`Database::run_with_durable_recorder`].
33
+ pub(crate) fn execute_mutating_optimistic(
34
+ &self,
35
+ params: BTreeMap<String, LoraValue>,
36
+ deadline: Option<Instant>,
37
+ compiled: &Arc<CompiledQuery>,
38
+ ) -> Result<Vec<Row>> {
39
+ self.run_with_durable_recorder(|staged| {
40
+ let mut executor = MutableExecutor::with_deadline(
41
+ MutableExecutionContext {
42
+ storage: staged,
43
+ params,
44
+ },
45
+ deadline,
46
+ );
47
+ executor
48
+ .execute_compiled_rows(compiled)
49
+ .map_err(anyhow::Error::from)
50
+ })
51
+ }
52
+ }