resid pt 3: friendship checks & auth session reads

This commit is contained in:
2026-03-30 19:34:03 +08:00
parent d12d3e1ec7
commit ad09de2b11
11 changed files with 295 additions and 48 deletions

View File

@@ -4,6 +4,8 @@ export const CACHE_NAMESPACE = {
FRIENDS_LIST: 'friends-list',
DOLLS_LIST: 'dolls-list',
USERS_SEARCH: 'users-search',
FRIENDSHIP_CHECK: 'friendship-check',
AUTH_SESSION: 'auth-session',
} as const;
function normalizeKeyPart(value: string | undefined): string {
@@ -18,6 +20,8 @@ export const CACHE_TTL_SECONDS = {
FRIENDS_LIST: 30,
DOLLS_LIST: 30,
USERS_SEARCH: 20,
FRIENDSHIP_CHECK: 120,
AUTH_SESSION: 30,
} as const;
export function friendsListCacheKey(userId: string): string {
@@ -56,6 +60,25 @@ export function usersSearchCacheKey(
export const USERS_SEARCH_GLOBAL_TAG = 'global';
export function friendshipCheckCacheKey(
userId: string,
friendId: string,
): string {
return `${normalizeKeyPart(userId)}:${normalizeKeyPart(friendId)}`;
}
export function friendshipCheckUserTag(userId: string): string {
return `user:${normalizeKeyPart(userId)}`;
}
export function authSessionCacheKey(sessionId: string): string {
return normalizeKeyPart(sessionId);
}
export function authSessionUserTag(userId: string): string {
return `user:${normalizeKeyPart(userId)}`;
}
export function usersSearchUserTag(userId: string): string {
return `user:${normalizeKeyPart(userId)}`;
}

View File

@@ -1,11 +1,24 @@
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { CacheService } from './cache.service';
import { parsePositiveInteger } from '../config/env.utils';
const CACHE_TAG_SET_TTL_SECONDS = 86_400;
const DEFAULT_CACHE_TAG_MAX_ENTRIES = 5_000;
@Injectable()
export class CacheTagsService {
constructor(private readonly cacheService: CacheService) {}
private readonly cacheTagMaxEntries: number;
constructor(
private readonly cacheService: CacheService,
private readonly configService: ConfigService,
) {
this.cacheTagMaxEntries = parsePositiveInteger(
this.configService.get<string>('CACHE_TAG_MAX_ENTRIES'),
DEFAULT_CACHE_TAG_MAX_ENTRIES,
);
}
async rememberKeyForTag(
namespace: string,
@@ -28,6 +41,11 @@ export class CacheTagsService {
redisClient.sadd(tagSetKey, keyWithNamespace),
redisClient.expire(tagSetKey, CACHE_TAG_SET_TTL_SECONDS),
]);
const size = await redisClient.scard(tagSetKey);
if (size > this.cacheTagMaxEntries) {
await this.trimTagSet(tagSetKey, size - this.cacheTagMaxEntries);
}
} catch (error) {
this.cacheService.recordError('tag remember', tagSetKey, error);
}
@@ -63,4 +81,26 @@ export class CacheTagsService {
`${namespace}:${tag}`,
);
}
private async trimTagSet(
tagSetKey: string,
countToDrop: number,
): Promise<void> {
const redisClient = this.cacheService.getRedisClient();
if (!redisClient || countToDrop <= 0) {
return;
}
try {
const sample = await redisClient.srandmember(tagSetKey, countToDrop);
const members = Array.isArray(sample) ? sample : [sample].filter(Boolean);
if (members.length === 0) {
return;
}
await redisClient.srem(tagSetKey, ...members);
} catch (error) {
this.cacheService.recordError('tag trim', tagSetKey, error);
}
}
}

View File

@@ -1,3 +1,4 @@
export { CacheModule } from './cache.module';
export { CacheService } from './cache.service';
export { CacheTagsService } from './cache-tags.service';
export { RedisThrottlerStorage } from './redis-throttler.storage';