fix: cache key uses filenames only — CWD-independent
Cache key hashes filename + size + mtime instead of full paths. Resolves mbp10/trades relative paths by walking up from data_dir. Ensures precompute binary and cargo test produce the same key regardless of working directory. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -28,29 +28,21 @@ pub fn calculate_dbn_cache_key_full(
|
||||
trades_dir: Option<&Path>,
|
||||
) -> Result<String> {
|
||||
let mut hasher = Sha256::new();
|
||||
// Canonicalize paths so relative/absolute produce the same key
|
||||
let data_dir = &data_dir.canonicalize().unwrap_or_else(|_| data_dir.to_path_buf());
|
||||
hasher.update(data_dir.to_string_lossy().as_bytes());
|
||||
|
||||
// Hash only file contents (size + mtime), not paths.
|
||||
// This makes the key independent of CWD, relative/absolute paths,
|
||||
// and directory naming — only actual data changes invalidate it.
|
||||
let mut files: Vec<_> = collect_dbn_files_for_hash(data_dir);
|
||||
// Also include MBP-10 and trades files in the hash
|
||||
if let Some(dir) = mbp10_dir {
|
||||
let dir = &dir.canonicalize().unwrap_or_else(|_| dir.to_path_buf());
|
||||
hasher.update(b"mbp10:");
|
||||
hasher.update(dir.to_string_lossy().as_bytes());
|
||||
files.extend(collect_dbn_files_for_hash(dir));
|
||||
} else {
|
||||
hasher.update(b"mbp10:none");
|
||||
}
|
||||
if let Some(dir) = trades_dir {
|
||||
let dir = &dir.canonicalize().unwrap_or_else(|_| dir.to_path_buf());
|
||||
hasher.update(b"trades:");
|
||||
hasher.update(dir.to_string_lossy().as_bytes());
|
||||
files.extend(collect_dbn_files_for_hash(dir));
|
||||
} else {
|
||||
hasher.update(b"trades:none");
|
||||
}
|
||||
files.sort(); // deterministic ordering
|
||||
// Sort by filename only (strip parent dirs) for deterministic ordering
|
||||
files.sort_by(|a, b| {
|
||||
a.file_name().cmp(&b.file_name())
|
||||
});
|
||||
|
||||
if files.is_empty() {
|
||||
return Err(anyhow::anyhow!(
|
||||
@@ -60,7 +52,10 @@ pub fn calculate_dbn_cache_key_full(
|
||||
}
|
||||
|
||||
for path in &files {
|
||||
hasher.update(path.to_string_lossy().as_bytes());
|
||||
// Hash filename (not full path) + size + mtime
|
||||
if let Some(name) = path.file_name() {
|
||||
hasher.update(name.to_string_lossy().as_bytes());
|
||||
}
|
||||
let meta = path
|
||||
.metadata()
|
||||
.with_context(|| format!("Failed to stat {:?}", path))?;
|
||||
|
||||
@@ -96,11 +96,27 @@ impl DQNTrainer {
|
||||
if let Some(ref cache_dir) = fxcache_dir {
|
||||
// Try exact key match. On miss, delete stale .fxcache files.
|
||||
let data_dir_path = Path::new(dbn_data_dir);
|
||||
let mbp10_dir = if self.hyperparams.mbp10_data_dir.is_empty() { None } else { Some(Path::new(self.hyperparams.mbp10_data_dir.as_str())) };
|
||||
let trades_dir = if self.hyperparams.trades_data_dir.is_empty() { None } else { Some(Path::new(self.hyperparams.trades_data_dir.as_str())) };
|
||||
// Resolve mbp10/trades paths: if relative and not found from CWD,
|
||||
// try relative to data_dir ancestors (handles cargo test CWD = crates/ml/)
|
||||
let resolve_sibling = |raw: &str| -> Option<std::path::PathBuf> {
|
||||
if raw.is_empty() { return None; }
|
||||
let p = Path::new(raw);
|
||||
if p.exists() { return Some(p.to_path_buf()); }
|
||||
// Walk up from data_dir looking for the relative path
|
||||
let mut dir = data_dir_path;
|
||||
while let Some(parent) = dir.parent() {
|
||||
let candidate = parent.join(raw);
|
||||
if candidate.exists() { return Some(candidate); }
|
||||
if parent == dir { break; }
|
||||
dir = parent;
|
||||
}
|
||||
None
|
||||
};
|
||||
let mbp10_dir = resolve_sibling(&self.hyperparams.mbp10_data_dir);
|
||||
let trades_dir = resolve_sibling(&self.hyperparams.trades_data_dir);
|
||||
|
||||
let fxcache_path = crate::feature_cache::calculate_dbn_cache_key_full(
|
||||
data_dir_path, mbp10_dir, trades_dir,
|
||||
data_dir_path, mbp10_dir.as_deref(), trades_dir.as_deref(),
|
||||
).ok()
|
||||
.and_then(|hex| hex::decode(&hex).ok())
|
||||
.and_then(|b| <[u8; 32]>::try_from(b).ok())
|
||||
|
||||
Reference in New Issue
Block a user