ControlWriter::open refuses a store holding a record it does not read
The CultLib pin makes a pull carry unregistered records instead of refusing; the writer guards like the heartbeat store does. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
477482b1a0
commit
0c2d1e2cab
2 changed files with 21 additions and 1 deletions
|
|
@ -36,7 +36,7 @@
|
||||||
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result, bail};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use cultcache_rs::{CultCache, CultCacheEnvelope, DatabaseEntry, SingleFileMessagePackBackingStore};
|
use cultcache_rs::{CultCache, CultCacheEnvelope, DatabaseEntry, SingleFileMessagePackBackingStore};
|
||||||
use eureka_pipeline::Slug;
|
use eureka_pipeline::Slug;
|
||||||
|
|
@ -414,6 +414,10 @@ impl ControlWriter {
|
||||||
let mut cache = registered_cache()?;
|
let mut cache = registered_cache()?;
|
||||||
cache.add_generic_backing_store(SingleFileMessagePackBackingStore::new(&path))?;
|
cache.add_generic_backing_store(SingleFileMessagePackBackingStore::new(&path))?;
|
||||||
cache.pull_all_backing_stores()?;
|
cache.pull_all_backing_stores()?;
|
||||||
|
// A pull carries a record no registered type claims instead of refusing it; the writer still must not write beside one.
|
||||||
|
if !cache.foreign_records().is_empty() {
|
||||||
|
bail!("{} holds a record this control store does not read: archive it, nothing migrates it", path.display());
|
||||||
|
}
|
||||||
Ok(Self { cache, path })
|
Ok(Self { cache, path })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -255,3 +255,19 @@ fn release(root: &Path, name: &str) {
|
||||||
ControlWriter::open(root.join(name).join("control.cc")).unwrap().set_brake(true, at(0), "op").unwrap();
|
ControlWriter::open(root.join(name).join("control.cc")).unwrap().set_brake(true, at(0), "op").unwrap();
|
||||||
release_swarm(root);
|
release_swarm(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn a_writer_refuses_to_open_over_a_store_holding_a_record_it_does_not_read() {
|
||||||
|
let dir = tempfile::tempdir().unwrap();
|
||||||
|
let path = store_path(dir.path());
|
||||||
|
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
|
||||||
|
let mut cache = CultCache::new();
|
||||||
|
cache.register_entry_type::<OldDial>().unwrap();
|
||||||
|
cache.add_generic_backing_store(SingleFileMessagePackBackingStore::new(&path)).unwrap();
|
||||||
|
cache.pull_all_backing_stores().unwrap();
|
||||||
|
cache.put("dial", &OldDial { heat: "1".into() }).unwrap();
|
||||||
|
let before = std::fs::read(&path).unwrap();
|
||||||
|
let refusal = ControlWriter::open(&path).err().expect("a foreign record refuses the open").to_string();
|
||||||
|
assert!(refusal.contains("does not read"), "{refusal}");
|
||||||
|
assert_eq!(std::fs::read(&path).unwrap(), before, "the refused open leaves the store bytes alone");
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue