1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
//! How a test opens a disk store, and how a fixture's history is built on one.
//!
//! Every test store is created and opened with one configuration
//! (`test_database_config`, `test_runtime_options`), the door's shape. A
//! fixture's history is driven through the production handler once per test
//! binary and copied per test; that one build runs under haematite's EP-1
//! fences (`Database::open_ephemeral`: a plain `fsync(2)` per commit, never
//! the device flush a durable commit issues, `F_FULLFSYNC` on Apple). The
//! store is created durable and its `config.json` is not rewritten, so the
//! copy each test opens through `open_disk_store_for_tests` is the production
//! store holding every commit the build made and flushed. On this Mac a
//! device flush is 30 to 80 ms and a plain fsync about a millisecond; the
//! 1,000-conversation history of DUR-027 R7 is 26,000 commits, which was 20
//! to 30 minutes of every battery under full fences (18 Oct 2026). A plain
//! fsync survives a process exit, not a power loss: a fixture torn by one is
//! refused at open and is rebuilt (its key removed), never reused; the copy
//! is taken only after the build's handle is dropped (Apollo, 18 Oct).
use std::error::Error;
use std::path::Path;
use std::sync::Arc;
use haematite::{Database, DatabaseConfig, EventStore, RuntimeOptions};
use liminal::durability::{DurableStore, HaematiteStore};
/// The configuration every test's disk store is created with.
pub(super) fn test_database_config(data_dir: &Path) -> DatabaseConfig {
DatabaseConfig {
data_dir: data_dir.to_path_buf(),
shard_count: 2,
distributed: None,
executor_threads: None,
node_cache_budget: Some(haematite::NodeCacheBudget::Unlimited),
retention: haematite::Retention::reclaim(0),
}
}
/// The runtime options every test's disk store is opened with.
pub(super) fn test_runtime_options() -> RuntimeOptions {
RuntimeOptions {
long_wait_interval: std::time::Duration::from_secs(5),
reclaim_start: haematite::ReclaimStart::AtOpen,
}
}
/// A durable store at `data_dir`, created there if absent, opened under
/// plain-fsync fences for the one build of a fixture's history. The caller
/// flushes it when the history is written; a test opens its copy with
/// `open_disk_store_for_tests`, as the door opens a store.
///
/// # Errors
/// The store cannot be created or opened.
pub(super) fn open_store_for_fixture_build(
data_dir: &Path,
) -> Result<Arc<dyn DurableStore>, Box<dyn Error>> {
if !data_dir.join("config.json").exists() {
drop(Database::create(
test_database_config(data_dir),
test_runtime_options(),
)?);
}
let database = Database::open_ephemeral(data_dir, test_runtime_options())?;
liminal::durability::record_store_opened();
Ok(Arc::new(HaematiteStore::new(Arc::new(EventStore::new(
database,
)))))
}