How OpenLogi's Device Probing Cache Manages On-Disk Enumeration State
OpenLogi stores HID device enumeration results in a file-backed JSON cache to eliminate redundant hardware probing and accelerate startup times.
The device probing cache in OpenLogi is a lightweight persistence layer that solves a critical performance problem: HID device enumeration is slow and should not repeat every session. This article examines how the cache manages on-disk enumeration state through its trait-based abstraction, file-backed implementation, and seamless integration with the device inventory system.
Core Cache Abstraction: The ProbeCacheStore Trait
OpenLogi defines its cache contract in crates/openlogi-device/src/inventory/persist.rs. The ProbeCacheStore trait provides a minimal, two-method interface for persistence:
load()— Returns aProbeCacheSnapshotrepresenting the immutable enumeration statesave(snapshot)— Writes the snapshot to persistent storage
This abstraction allows the device inventory to remain agnostic about storage backends while ensuring the on-disk enumeration state can be recovered across sessions.
File-Backed Implementation in FileProbeCacheStore
The concrete implementation lives in crates/openlogi-hid/src/probe_cache.rs. The FileProbeCacheStore struct handles platform-specific data directory resolution and atomic JSON serialization.
Platform-Specific Cache Location
FileProbeCacheStore::in_data_dir() resolves to:
| Platform | Cache Directory |
|---|---|
| Linux | $XDG_DATA_HOME/OpenLogi or ~/.local/share/OpenLogi |
| Windows | %APPDATA%\OpenLogi |
| macOS | ~/Library/Application Support/OpenLogi |
Atomic Read and Write Operations
The cache file (probe-cache.json) is managed with defensive I/O patterns:
// Load: Deserialize or fall back to empty snapshot
let snapshot = store.load(); // ProbeCacheSnapshot
// Save: Atomic write with directory creation
store.save(&ProbeCacheSnapshot::of(&inventory.cache))?;
The load() method tolerates missing or corrupted files by returning an empty snapshot. The save() method uses std::fs::write for atomic writes and automatically creates parent directories.
Integration with Device Inventory
The device inventory in crates/openlogi-device/src/inventory.rs orchestrates cache usage through its with_probe_cache constructor method.
Startup: Cache Hydration
When constructed with a cache store, the inventory immediately calls store.load() to populate its in-memory state, bypassing fresh HID enumeration.
Runtime: Cache Persistence
Following any mutation—device pairing, profile updates, or configuration changes—the inventory persists the new state:
inventory.store.save(&ProbeCacheSnapshot::of(&inventory.cache))?;
This ensures the on-disk enumeration state remains synchronized with runtime changes without blocking the critical path.
Schema Versioning and Immutability
The ProbeCacheSnapshot includes a SCHEMA_VERSION constant for forward compatibility. When the on-disk format changes:
- Older caches are detected by version mismatch
- They are silently ignored (treated as empty)
- A new cache is built from fresh enumeration
The snapshot itself is immutable—mutations produce new snapshots rather than modifying existing ones, preventing accidental state corruption.
Error Handling with ProbeCacheError
All I/O failures are wrapped in ProbeCacheError with contextual path information via self.path.display(). This design choice ensures that:
- Cache read failures degrade gracefully to empty enumeration
- Cache write failures log diagnostic context without crashing the inventory
- The rest of the system continues operating regardless of disk state
Complete Working Example
use std::sync::Arc;
use openlogi_hid::FileProbeCacheStore;
use openlogi_device::inventory::{Inventory, ProbeCacheSnapshot};
// Initialize cache in OS-specific data directory
let cache = FileProbeCacheStore::in_data_dir();
// Inject cache into inventory (implicitly calls load())
let mut inventory = Inventory::default()
.with_probe_cache(Arc::new(cache));
// ... perform device operations ...
// Persist updated state after mutations
inventory
.store
.save(&ProbeCacheSnapshot::of(&inventory.cache))
.expect("Failed to write probe cache");
Summary
- Trait abstraction:
ProbeCacheStoreincrates/openlogi-device/src/inventory/persist.rsdecouples storage from business logic - File implementation:
FileProbeCacheStoreincrates/openlogi-hid/src/probe_cache.rsprovides platform-aware JSON persistence - Atomic operations:
load()with fallback,save()with directory creation ensure reliability - Version safety:
SCHEMA_VERSIONenables format evolution without breaking existing installations - Graceful degradation:
ProbeCacheErrorwrapping allows the system to function despite disk issues
Frequently Asked Questions
Where does OpenLogi store the device probing cache on disk?
The cache resides in a JSON file named probe-cache.json within a platform-specific data directory. On Linux, this is $XDG_DATA_HOME/OpenLogi (typically ~/.local/share/OpenLogi); on Windows, %APPDATA%\OpenLogi; on macOS, ~/Library/Application Support/OpenLogi. The FileProbeCacheStore::in_data_dir() method handles this resolution automatically.
What happens if the probe cache file is corrupted or deleted?
FileProbeCacheStore::load() detects missing or unreadable files and returns an empty ProbeCacheSnapshot. The inventory then performs fresh HID enumeration and populates a new cache. This graceful fallback ensures OpenLogi remains functional even with disk failures.
How does OpenLogi handle changes to the cache file format?
The ProbeCacheSnapshot struct includes a SCHEMA_VERSION constant. When loading, the implementation checks this version against the expected value. Version mismatches trigger the same fallback as missing files—empty snapshot, fresh enumeration, and new cache creation—allowing seamless format upgrades.
Can multiple OpenLogi instances share the same probe cache?
The current implementation does not include file locking or coordination mechanisms. Concurrent access from multiple processes could result in race conditions during save() operations. For multi-instance deployments, each instance should use isolated cache directories or external coordination should be implemented.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →