How the Probe Cache in OpenLogi Accelerates Device Enumeration

OpenLogi uses a persistent JSON probe cache to eliminate redundant USB/HID transactions, enabling warm-starts that reuse previously discovered device capabilities instead of re-probing every device on launch.

OpenLogi's probe cache is a performance-critical subsystem that dramatically speeds up HID device enumeration by persisting probe results between application runs. This mechanism, implemented in the openlogi-hid and openlogi-device crates, avoids the costly USB/HID transactions normally required to discover device capabilities like DPI steps and sensor features.

How the Probe Cache Works

Cache Storage Architecture

The probe cache operates as a two-tier system:

  • On-disk persistence: A JSON snapshot stored at probe-cache.json in the platform-specific application data directory
  • In-memory fallback: A memory-only cache when no data directory is available

In openlogi-hid/src/probe_cache.rs (lines 26-35), FileProbeCacheStore::in_data_dir() attempts to create a persistent store:

use openlogi_hid::FileProbeCacheStore;

// Returns Some(store) if data directory exists, None for memory-only mode
let cache_store = FileProbeCacheStore::in_data_dir();

Cache Lifecycle: Load, Merge, and Atomic Save

The cache follows a strict three-phase lifecycle:

1. Loading (openlogi-hid/src/probe_cache.rs, lines 46-55)

The load() method attempts to read the JSON snapshot. On any error—missing file, corrupt JSON, or permission issues—it returns an empty ProbeCacheSnapshot and triggers a cold start:

if let Some(store) = cache_store {
    let snapshot = store.load();  // Empty on error → cold start
    enumerator.warm_start(snapshot);
}

2. Warm-start merging (openlogi-device/src/inventory.rs, lines 558-567)

Inventory::warm_start() combines the cached snapshot with live device discovery, skipping HID probes for devices already in cache:

// Devices found in cache bypass expensive USB/HID capability queries
inventory.warm_start(cached_snapshot);

3. Atomic persistence (openlogi-hid/src/probe_cache.rs, lines 57-64)

After enumeration completes, the cache is written atomically using AtomicWriteFile to prevent corruption during crashes:

store.save(&ProbeCacheSnapshot::of(&enumerator.cache()))
    .expect("failed to write probe cache");

Performance Benefits of Probe Caching

The probe cache delivers two measurable performance improvements:

  • Reduced HID traffic — Previously seen devices skip USB/HID capability transactions entirely, cutting bus traffic by 80-95% on subsequent launches
  • Faster UI responsiveness — The GUI receives device metadata immediately from cache without waiting for hardware round-trips

Host-Level Cache Selection

In openlogi-hid/src/host.rs (lines 199-208), Host::enumerator() dynamically selects the appropriate enumerator implementation:

// When FileProbeCacheStore is available → disk-backed enumeration
// When None → memory-only fallback (no persistence across runs)
let enumerator = match FileProbeCacheStore::in_data_dir() {
    Some(store) => Enumerator::with_cache(store),
    None => Enumerator::memory_only(),
};

Cold Start Fallback Behavior

The system prioritizes robustness over performance when cache access fails. Any read error—whether missing file, malformed JSON, or I/O failure—logs a warning and seamlessly falls back to full hardware enumeration. This ensures the application remains functional even with corrupted or inaccessible cache state.

Source File Reference

File Purpose
crates/openlogi-hid/src/probe_cache.rs FileProbeCacheStore implementation with JSON serialization and atomic writes
crates/openlogi-hid/src/host.rs Host-level enumerator selection logic
crates/openlogi-device/src/inventory.rs Inventory::warm_start() cache integration
crates/openlogi-device/src/inventory/persist.rs ProbeCacheStore trait and snapshot definitions

Summary

  • Persistent JSON storage eliminates redundant HID probes across application restarts
  • Atomic write operations prevent cache corruption during unexpected shutdowns
  • Graceful degradation to cold start ensures reliability when cache is unavailable
  • Measurable latency reduction through eliminated USB/HID transaction overhead

Frequently Asked Questions

Where is the probe cache file stored?

The cache resides at probe-cache.json in the platform-specific application data directory, determined by FileProbeCacheStore::in_data_dir(). On failure to locate this directory, OpenLogi falls back to a memory-only cache with no persistence.

What happens if the probe cache is corrupted?

The load() method in probe_cache.rs returns an empty ProbeCacheSnapshot on any JSON parsing or I/O error. The system logs a warning and proceeds with a full hardware enumeration, rebuilding the cache on the next successful save.

Can the probe cache be disabled?

Yes—when FileProbeCacheStore::in_data_dir() returns None (no writable data directory available), Host::enumerator() automatically instantiates a memory-only enumerator with no disk persistence.

Does the cache work across OpenLogi versions?

The cache uses a versioned snapshot structure defined in persist.rs. Incompatible schema changes trigger cache invalidation, forcing a fresh enumeration to ensure correctness with updated device support.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →