OXYGENHUB

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