This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

semble / src / modules / atproto / infrastructure / services / CachedBlueskyProfileService.ts
2.5 kB 88 lines
1import Redis from 'ioredis'; 2import { 3 IProfileService, 4 UserProfile, 5} from 'src/modules/cards/domain/services/IProfileService'; 6import { Result, ok } from 'src/shared/core/Result'; 7 8export class CachedBlueskyProfileService implements IProfileService { 9 private readonly CACHE_TTL_SECONDS = 3600 * 12; // 12 hours 10 private readonly CACHE_KEY_PREFIX = 'profile:'; 11 12 constructor( 13 private readonly profileService: IProfileService, 14 private readonly redis: Redis, 15 ) {} 16 17 async getProfile( 18 userId: string, 19 callerId?: string, 20 ): Promise<Result<UserProfile>> { 21 const cacheKey = this.getCacheKey(userId); 22 23 try { 24 // Try cache first 25 const cached = await this.redis.get(cacheKey); 26 if (cached) { 27 try { 28 const profile = JSON.parse(cached) as UserProfile; 29 return ok(profile); 30 } catch (parseError) { 31 // If JSON parsing fails, continue to fetch fresh data 32 console.warn( 33 `Failed to parse cached profile for ${userId}:`, 34 parseError, 35 ); 36 } 37 } 38 39 // Cache miss or parse error - fetch from underlying service 40 const result = await this.profileService.getProfile(userId, callerId); 41 42 if (result.isOk()) { 43 // Cache the successful result 44 try { 45 await this.redis.setex( 46 cacheKey, 47 this.CACHE_TTL_SECONDS, 48 JSON.stringify(result.value), 49 ); 50 } catch (cacheError) { 51 // Log cache error but don't fail the request 52 console.warn(`Failed to cache profile for ${userId}:`, cacheError); 53 } 54 } 55 56 return result; 57 } catch (redisError) { 58 // If Redis is down, fall back to direct service call 59 console.warn( 60 `Redis error when fetching profile for ${userId}:`, 61 redisError, 62 ); 63 return this.profileService.getProfile(userId, callerId); 64 } 65 } 66 67 private getCacheKey(userId: string): string { 68 return `${this.CACHE_KEY_PREFIX}${userId}`; 69 } 70 71 /** 72 * Invalidate cached profile for a specific user 73 */ 74 async invalidateProfile(userId: string): Promise<void> { 75 try { 76 await this.redis.del(this.getCacheKey(userId)); 77 } catch (error) { 78 console.warn(`Failed to invalidate profile cache for ${userId}:`, error); 79 } 80 } 81 82 /** 83 * Warm the cache by pre-fetching a profile 84 */ 85 async warmCache(userId: string, callerId?: string): Promise<void> { 86 await this.getProfile(userId, callerId); 87 } 88}