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#
- Cache Hit: Returns DID immediately from Redis
- Cache Miss: Resolves through inner resolver, caches result
- Resolution Error: Caches empty string to prevent repeated failures
- Redis Error: Falls back to inner resolver without caching
Key Format#
Redis keys follow this pattern:
- Success:
handle:alice.bsky.social→did: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/missesINFO: Redis pool creation and resolver selectionWARN: 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:
- Deploy Redis instance
- Set
REDIS_URLenvironment variable - Restart QuickDID service
- Service will automatically use Redis for new resolutions
No data migration needed - cache will populate on first resolution.