QuickDID is a high-performance AT Protocol identity resolution service written in Rust. It provides handle-to-DID resolution with Redis-backed caching and queue processing.

Redis-Backed Handle Resolver#

The QuickDID service now supports Redis-backed caching for handle resolution, providing persistent caching with 90-day expiration times.

Features#

  • 90-Day Cache TTL: Resolved handles are cached in Redis for 90 days, significantly reducing resolution latency
  • Error Caching: Failed resolutions are also cached (as empty strings) to prevent repeated failed lookups
  • Automatic Fallback: If Redis is unavailable, the service falls back to in-memory caching
  • Configurable Key Prefix: Supports custom Redis key prefixes for multi-tenant deployments

Configuration#

Set the REDIS_URL environment variable to enable Redis caching:

export REDIS_URL="redis://localhost:6379"
# or with authentication
export REDIS_URL="redis://username:password@localhost:6379/0"

When Redis is configured:

  • Handle resolutions are cached with 90-day expiration
  • Cache keys use the format: handle:{handle_name}
  • Failed resolutions are cached as empty strings

When Redis is not configured:

  • Falls back to in-memory caching with 10-minute TTL
  • Cache is not persisted between service restarts

Architecture#

┌─────────────────┐
│  HTTP Request   │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│ Handle Resolver │
│   Endpoint      │
└────────┬────────┘
         │
         ▼
┌─────────────────────┐     Cache Miss    ┌───────────────────┐
│ RedisHandleResolver │ ─────────────────► │ BaseHandleResolver│
│                     │                    │                   │
│  - Check Redis      │                    │ - DNS TXT lookup  │
│  - 90-day TTL       │ ◄───────────────── │ - Well-known      │
│  - Cache result     │     Resolution     │   endpoint        │
└─────────┬───────────┘                    └───────────────────┘
          │
          │ Cache Hit
          ▼
┌─────────────────┐
│  Redis Cache    │
│                 │
│ handle:alice -> │
│ did:plc:xyz...  │
└─────────────────┘

Implementation Details#

RedisHandleResolver#

The RedisHandleResolver struct wraps a base handle resolver and adds Redis caching:

pub struct RedisHandleResolver {
    inner: Arc<dyn HandleResolver>,
    pool: RedisPool,
    key_prefix: String,
}

Cache Behavior#

  1. Cache Hit: Returns DID immediately from Redis
  2. Cache Miss: Resolves through inner resolver, caches result
  3. Resolution Error: Caches empty string to prevent repeated failures
  4. Redis Error: Falls back to inner resolver without caching

Key Format#

Redis keys follow this pattern:

  • Success: handle:alice.bsky.socialdid:plc:xyz123...
  • Error: handle:invalid.handle"" (empty string)

Testing#

Tests can be run with a local Redis instance:

# Start Redis locally
docker run -d -p 6379:6379 redis:latest

# Run tests with Redis
export TEST_REDIS_URL="redis://localhost:6379"
cargo test

# Tests will skip if TEST_REDIS_URL is not set
cargo test

Performance Considerations#

  • Cache Hits: Near-instantaneous (<1ms typical)
  • Cache Misses: Same as base resolver (DNS/HTTP lookups)
  • Memory Usage: Minimal, only stores handle → DID mappings
  • Network Overhead: Single Redis round-trip per resolution

Monitoring#

The service logs cache operations at various levels:

  • DEBUG: Cache hits/misses
  • INFO: Redis pool creation and resolver selection
  • WARN: Redis connection failures and fallbacks

Example logs:

INFO Using Redis-backed handle resolver with 90-day cache TTL
DEBUG Cache miss for handle alice.bsky.social, resolving...
DEBUG Caching successful resolution for handle alice.bsky.social: did:plc:xyz123
DEBUG Cache hit for handle alice.bsky.social: did:plc:xyz123

Migration#

To migrate from in-memory to Redis caching:

  1. Deploy Redis instance
  2. Set REDIS_URL environment variable
  3. Restart QuickDID service
  4. Service will automatically use Redis for new resolutions

No data migration needed - cache will populate on first resolution.