1use std::path::{Path, PathBuf};
2use std::sync::Mutex;
3
4use crate::error::EdgestoreError;
5use crate::storage_backend::{PlacementHint, StorageBackend};
6
7pub struct FdpStorageBackend {
13 inner: Box<dyn StorageBackend>,
14}
15
16impl FdpStorageBackend {
17 pub fn new(inner: Box<dyn StorageBackend>) -> Self {
19 FdpStorageBackend { inner }
20 }
21}
22
23impl StorageBackend for FdpStorageBackend {
24 fn read(&self, path: &Path, offset: u64, data: &mut [u8]) -> Result<usize, EdgestoreError> {
25 self.inner.read(path, offset, data)
26 }
27
28 fn write(&self, path: &Path, offset: u64, data: &[u8]) -> Result<(), EdgestoreError> {
29 self.inner.write(path, offset, data)
30 }
31
32 fn write_with_hint(
33 &self,
34 path: &Path,
35 offset: u64,
36 data: &[u8],
37 hint: PlacementHint,
38 ) -> Result<(), EdgestoreError> {
39 #[cfg(target_os = "linux")]
40 {
41 if let Ok(file) = std::fs::OpenOptions::new().write(true).open(path) {
46 let _fd = std::os::fd::AsRawFd::as_raw_fd(&file);
47 log::info!("FDP hint: cohort_bucket={} for {:?}", hint.cohort_bucket, path);
50 }
51 }
52 #[cfg(not(target_os = "linux"))]
53 {
54 let _ = hint; }
56 self.inner.write_with_hint(path, offset, data, hint)
57 }
58
59 fn flush(&self, path: &Path) -> Result<(), EdgestoreError> {
60 self.inner.flush(path)
61 }
62}
63
64pub struct MockFdpBackend {
66 inner: Box<dyn StorageBackend>,
67 pub recorded_hints: Mutex<Vec<(PathBuf, u64, PlacementHint)>>,
69}
70
71impl MockFdpBackend {
72 pub fn new(inner: Box<dyn StorageBackend>) -> Self {
74 MockFdpBackend {
75 inner,
76 recorded_hints: Mutex::new(Vec::new()),
77 }
78 }
79}
80
81impl StorageBackend for MockFdpBackend {
82 fn read(&self, path: &Path, offset: u64, data: &mut [u8]) -> Result<usize, EdgestoreError> {
83 self.inner.read(path, offset, data)
84 }
85
86 fn write(&self, path: &Path, offset: u64, data: &[u8]) -> Result<(), EdgestoreError> {
87 self.inner.write(path, offset, data)
88 }
89
90 fn write_with_hint(
91 &self,
92 path: &Path,
93 offset: u64,
94 data: &[u8],
95 hint: PlacementHint,
96 ) -> Result<(), EdgestoreError> {
97 self.recorded_hints.lock().unwrap().push((path.to_path_buf(), offset, hint));
98 self.inner.write_with_hint(path, offset, data, hint)
99 }
100
101 fn flush(&self, path: &Path) -> Result<(), EdgestoreError> {
102 self.inner.flush(path)
103 }
104}
105
106#[cfg(test)]
109mod tests {
110 use super::*;
111 use crate::storage_backend::MemoryStorageBackend;
112
113 #[test]
114 fn test_mock_fdp_records_hint() {
115 let inner = Box::new(MemoryStorageBackend::new());
116 let backend = MockFdpBackend::new(inner);
117 let path = Path::new("/tmp/test.bin");
118
119 let hint = PlacementHint { cohort_bucket: 42 };
120 backend.write_with_hint(path, 0, b"hello", hint).unwrap();
121
122 let hints = backend.recorded_hints.lock().unwrap();
123 assert_eq!(hints.len(), 1);
124 assert_eq!(hints[0].0, path);
125 assert_eq!(hints[0].1, 0);
126 assert_eq!(hints[0].2.cohort_bucket, 42);
127 }
128
129 #[test]
130 fn test_fdp_backend_no_panic() {
131 let inner = Box::new(MemoryStorageBackend::new());
132 let backend = FdpStorageBackend::new(inner);
133 let path = Path::new("/tmp/test.bin");
134
135 let hint = PlacementHint { cohort_bucket: 7 };
136 backend.write_with_hint(path, 0, b"data", hint).unwrap();
137 backend.flush(path).unwrap();
138
139 let mut buf = [0u8; 4];
140 let n = backend.read(path, 0, &mut buf).unwrap();
141 assert_eq!(n, 4);
142 assert_eq!(&buf, b"data");
143 }
144}