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 |