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.

feature: set impl in redis and memory with inner pass-through

Changed files
+101
src
+26
src/handle_resolver/memory.rs
··· 175 175 176 176 result 177 177 } 178 + 179 + async fn set(&self, handle: &str, did: &str) -> Result<(), HandleResolverError> { 180 + // Normalize the handle to lowercase 181 + let handle = handle.to_lowercase(); 182 + 183 + // Update the in-memory cache 184 + { 185 + let mut cache = self.cache.write().await; 186 + let timestamp = Self::current_timestamp(); 187 + cache.insert( 188 + handle.clone(), 189 + ResolveHandleResult::Found(timestamp, did.to_string()), 190 + ); 191 + self.metrics.incr("resolver.memory.set").await; 192 + tracing::debug!("Set handle {} -> DID {} in memory cache", handle, did); 193 + 194 + // Track cache size 195 + let cache_size = cache.len() as u64; 196 + self.metrics 197 + .gauge("resolver.memory.cache_entries", cache_size) 198 + .await; 199 + } 200 + 201 + // Chain to inner resolver 202 + self.inner.set(&handle, did).await 203 + } 178 204 } 179 205 180 206 /// Create a new in-memory caching handle resolver.
+5
src/handle_resolver/proactive_refresh.rs
··· 141 141 142 142 Ok((did, resolve_time)) 143 143 } 144 + 145 + async fn set(&self, handle: &str, did: &str) -> Result<(), HandleResolverError> { 146 + // Simply chain to inner resolver - no proactive refresh needed for manual sets 147 + self.inner.set(handle, did).await 148 + } 144 149 } 145 150 146 151 /// Create a ProactiveRefreshResolver with custom threshold using trait objects
+6
src/handle_resolver/rate_limited.rs
··· 187 187 // With permit acquired, forward to inner resolver 188 188 self.inner.resolve(s).await 189 189 } 190 + 191 + async fn set(&self, handle: &str, did: &str) -> Result<(), HandleResolverError> { 192 + // Set operations don't need rate limiting since they're typically administrative 193 + // and don't involve network calls to external services 194 + self.inner.set(handle, did).await 195 + } 190 196 } 191 197 192 198 /// Create a rate-limited handle resolver.
+64
src/handle_resolver/sqlite.rs
··· 255 255 256 256 result 257 257 } 258 + 259 + async fn set(&self, handle: &str, did: &str) -> Result<(), HandleResolverError> { 260 + // Normalize the handle to lowercase 261 + let handle = handle.to_lowercase(); 262 + 263 + // Update the SQLite cache 264 + if let Ok(mut conn) = self.pool.acquire().await { 265 + // Create a resolution result for the successful mapping 266 + let resolution_result = match HandleResolutionResult::success(did) { 267 + Ok(res) => res, 268 + Err(e) => { 269 + tracing::warn!("Failed to create resolution result for set operation: {}", e); 270 + self.metrics.incr("resolver.sqlite.set_result_create_error").await; 271 + // Still chain to inner resolver even if we can't cache 272 + return self.inner.set(&handle, did).await; 273 + } 274 + }; 275 + 276 + // Serialize to bytes 277 + match resolution_result.to_bytes() { 278 + Ok(bytes) => { 279 + // Insert or update the cache entry 280 + let timestamp = std::time::SystemTime::now() 281 + .duration_since(std::time::UNIX_EPOCH) 282 + .unwrap_or_default() 283 + .as_secs() as i64; 284 + 285 + let expires_at = timestamp + self.ttl_seconds as i64; 286 + 287 + match sqlx::query( 288 + "INSERT OR REPLACE INTO handle_resolution_cache (handle, resolved_value, created_at, expires_at) VALUES (?, ?, ?, ?)" 289 + ) 290 + .bind(&handle) 291 + .bind(&bytes) 292 + .bind(timestamp) 293 + .bind(expires_at) 294 + .execute(&mut *conn) 295 + .await 296 + { 297 + Ok(_) => { 298 + tracing::debug!("Set handle {} -> DID {} in SQLite cache", handle, did); 299 + self.metrics.incr("resolver.sqlite.set_success").await; 300 + } 301 + Err(e) => { 302 + tracing::warn!("Failed to set handle->DID mapping in SQLite: {}", e); 303 + self.metrics.incr("resolver.sqlite.set_cache_error").await; 304 + // Still chain to inner resolver even if cache update fails 305 + } 306 + } 307 + } 308 + Err(e) => { 309 + tracing::warn!("Failed to serialize resolution result for set operation: {}", e); 310 + self.metrics.incr("resolver.sqlite.set_serialize_error").await; 311 + // Still chain to inner resolver even if serialization fails 312 + } 313 + } 314 + } else { 315 + tracing::warn!("Failed to get SQLite connection for set operation"); 316 + self.metrics.incr("resolver.sqlite.set_connection_error").await; 317 + } 318 + 319 + // Chain to inner resolver 320 + self.inner.set(&handle, did).await 321 + } 258 322 } 259 323 260 324 /// Create a new SQLite-backed handle resolver with default 90-day TTL.