flow-casfs: at most 256 MB of files held in memory, oldest dropped first (refs #21) 602ac9c8ed1d
A full read of the Linux tip held 2 GB; now the process stays near 730 MB (the state and 256 MB of files), and the mount still matches the working copy under diff -r.
- Author
- Andy McCall @andymccall <[email protected]>
- When
- · 2026-10-01 03:19 UTC
- Signature
- ✔ signed by Andy McCall
- Hash
602ac9c8ed1ddee7f98e75b3fc3972df0c85625e8ac3d6dba5405f4e49657a63- Depends on
- f849c7889471
- Depended on by
- nothing yet
- In bookmarks
- main newer-pipeline push-uses-login
- In tags
- v0.2.0 v0.3.0 v0.3.1 v0.4.0 v0.5.0 v0.5.1 v0.5.2 v0.5.3
- Edits
- 8 atoms, 0 tree operations, 1 file
Browse the tree at this change
modified crates/flow-casfs/src/main.rs +27 −3
| … | 25 unchanged lines |
||
| 26 | 26 | /// How long the kernel may keep attributes and names: the mount never |
|
| 27 | 27 | /// changes. |
|
| 28 | 28 | const TTL: Duration = Duration::from_secs(3600); |
|
| 29 | + | /// Bytes of files kept in memory; the kernel's page cache keeps the hot |
|
| 30 | + | /// ones too, and a file dropped here is cheap to read again. |
|
| 31 | + | const MEMORY_BYTES: usize = 256 << 20; |
|
| 29 | 32 | ||
| 30 | 33 | struct Inode { |
|
| 31 | 34 | parent: u64, |
|
| … | 9 unchanged lines |
||
| 41 | 44 | repo: Repo, |
|
| 42 | 45 | state: State, |
|
| 43 | 46 | inodes: Vec<Inode>, |
|
| 44 | - | /// Bytes already read, by inode. |
|
| 47 | + | /// Bytes already read, by inode, with the order they came in and their |
|
| 48 | + | /// total: the oldest go first beyond `MEMORY_BYTES`. |
|
| 45 | 49 | bytes: HashMap<u64, Arc<Vec<u8>>>, |
|
| 50 | + | order: std::collections::VecDeque<u64>, |
|
| 51 | + | held: usize, |
|
| 46 | 52 | cache_dir: PathBuf, |
|
| 47 | 53 | uid: u32, |
|
| 48 | 54 | gid: u32, |
|
| … | 27 unchanged lines |
||
| 76 | 82 | state, |
|
| 77 | 83 | inodes, |
|
| 78 | 84 | bytes: HashMap::new(), |
|
| 85 | + | order: std::collections::VecDeque::new(), |
|
| 86 | + | held: 0, |
|
| 79 | 87 | cache_dir, |
|
| 80 | 88 | uid: unsafe { libc::getuid() }, |
|
| 81 | 89 | gid: unsafe { libc::getgid() }, |
|
| … | 30 unchanged lines |
||
| 112 | 120 | .collect(); |
|
| 113 | 121 | let texts: Vec<oxy_core::objects::TextRef> = siblings.iter().map(|(_, t)| *t).collect(); |
|
| 114 | 122 | for ((c, _), b) in siblings.iter().zip(self.repo.single_texts(&texts)?) { |
|
| 115 | - | self.bytes.insert(*c, Arc::new(b)); |
|
| 123 | + | self.keep(*c, Arc::new(b)); |
|
| 116 | 124 | } |
|
| 117 | 125 | if let Some(b) = self.bytes.get(&ino) { |
|
| 118 | 126 | return Ok(b.clone()); |
|
| … | 8 unchanged lines |
||
| 127 | 135 | } |
|
| 128 | 136 | }; |
|
| 129 | 137 | let bytes = Arc::new(bytes); |
|
| 130 | - | self.bytes.insert(ino, bytes.clone()); |
|
| 138 | + | self.keep(ino, bytes.clone()); |
|
| 131 | 139 | Ok(bytes) |
|
| 132 | 140 | } |
|
| 133 | 141 | ||
| 142 | + | /// Keep a file's bytes, dropping the oldest beyond `MEMORY_BYTES`. |
|
| 143 | + | fn keep(&mut self, ino: u64, bytes: Arc<Vec<u8>>) { |
|
| 144 | + | self.held += bytes.len(); |
|
| 145 | + | if let Some(old) = self.bytes.insert(ino, bytes) { |
|
| 146 | + | self.held -= old.len(); |
|
| 147 | + | } else { |
|
| 148 | + | self.order.push_back(ino); |
|
| 149 | + | } |
|
| 150 | + | while self.held > MEMORY_BYTES && self.order.len() > 1 { |
|
| 151 | + | let Some(oldest) = self.order.pop_front() else { break }; |
|
| 152 | + | if let Some(b) = self.bytes.remove(&oldest) { |
|
| 153 | + | self.held -= b.len(); |
|
| 154 | + | } |
|
| 155 | + | } |
|
| 156 | + | } |
|
| 157 | + | ||
| 134 | 158 | fn rendering(&self, node: &NodeId) -> Result<Vec<u8>> { |
|
| 135 | 159 | // A file added once and never edited is its own text: no graph, and |
|
| 136 | 160 | // nothing to cache. |
|
| … | 211 unchanged lines |