OXYGENHUB

Fetch: compute the attestation signers once per operation head e10ba80000e1

Every fetch call gathered every bookmark attestation's signers (#266).
A client sends one fetch per bookmark and, on a bucket store, each
signature list is a LIST request: metalgear2 (145 bookmarks) fetches took
14 s. The set is now kept per repository and operation head; every
bookmark move and tag is an operation, and the attestation pointer is
written before the operation that publishes it.

Refs andymccall/oxygenhub#271
Author
Andy McCall @andymccall <[email protected]>
via
agent agent:claude@andymccall <[email protected]> · keyless, under your grant for changes,refs,seal · key 1832fc06b430
When
· 2026-10-19 18:03 UTC
Signature
◈ sealed by agent:claude@andymccall
Hash
e10ba80000e10f8a410cc4e22471a10263bc6c2ad81c8c4b8dbf454bbb8c3e97
Depends on
982864068a59 d36ba2f79f95
Depended on by
nothing yet
In bookmarks
identical-sides keyless-tags main push-notes subrepo-pin-state
In tags
v0.10.0 v0.11.0 v0.5.0 v0.5.1 v0.6.0 v0.7.0 v0.8.0 v0.9.0
Edits
3 atoms, 0 tree operations, 1 file

Browse the tree at this change

modified crates/oxy-core/src/repo/sync.rs +30 −1
…
321 unchanged lines
322 322
    /// client can check an attestation whose signer signed none of the
323 323
    /// changes it receives, a delegate or a short-lived key that only
324 324
    /// moved a bookmark (oxygenhub#266). One pointer and one signature
325 -
    /// list per reference; nothing per change.
325 +
    /// list per reference, computed once per operation head and kept
326 +
    /// (every bookmark move and tag is an operation): a client sends one
327 +
    /// fetch per bookmark, and on a bucket store each signature list is a
328 +
    /// LIST request, so recomputing per fetch cost metalgear2 (145
329 +
    /// bookmarks) 14 s a fetch.
326 330
    pub fn served_attest_signers(&self) -> Result<BTreeSet<KeyId>> {
331 +
        type Signers = std::collections::HashMap<(PathBuf, ObjectRef), BTreeSet<KeyId>>;
332 +
        static SERVED: std::sync::Mutex<Option<Signers>> = std::sync::Mutex::new(None);
333 +
        let key = (self.oxy.clone(), self.head_op()?.0);
334 +
        if let Some(keys) = SERVED
335 +
            .lock()
336 +
            .unwrap_or_else(|p| p.into_inner())
337 +
            .as_ref()
338 +
            .and_then(|m| m.get(&key))
339 +
        {
340 +
            return Ok(keys.clone());
341 +
        }
342 +
        let keys = self.attest_signers_now()?;
343 +
        let mut served = SERVED.lock().unwrap_or_else(|p| p.into_inner());
344 +
        let map = served.get_or_insert_with(Default::default);
345 +
        // One entry per repository: an older operation head is never asked
346 +
        // for again once a newer one is current.
347 +
        map.retain(|(oxy, _), _| *oxy != key.0);
348 +
        if map.len() >= 1024 {
349 +
            map.clear();
350 +
        }
351 +
        map.insert(key, keys.clone());
352 +
        Ok(keys)
353 +
    }
354 +
355 +
    fn attest_signers_now(&self) -> Result<BTreeSet<KeyId>> {
327 356
        let view = self.view()?;
328 357
        let pointers = view
329 358
            .bookmarks
…
2214 unchanged lines