Files
psp_api/src/redis/redis.service.ts
T
ahasani 62b659246f feat: implement Redis caching for business activities and consumers
- Added Redis caching to BusinessActivitiesService for findAll and findOne methods.
- Integrated Redis caching in BusinessActivityComplexesService for findAll and findOne methods.
- Enhanced ConsumersService with Redis caching for findAll and findOne methods.
- Introduced cache invalidation for partner consumers and business activities.
- Created RedisKeyMaker utility for generating cache keys for consumers, partners, and POS.
- Implemented cache invalidation services for partners and POS.
- Added Redis service methods for JSON handling and key deletion by patterns.
- Updated goods service to include caching and invalidation for goods list.
- Introduced DTO for updating goods.
2026-05-19 15:40:45 +03:30

134 lines
3.2 KiB
TypeScript

import { Injectable, Logger, OnModuleDestroy } from '@nestjs/common'
import Redis from 'ioredis'
@Injectable()
export class RedisService implements OnModuleDestroy {
private readonly logger = new Logger(RedisService.name)
private readonly client: Redis
constructor() {
const host = process.env.REDIS_HOST || 'redis'
const port = Number(process.env.REDIS_PORT || 6379)
const password = process.env.REDIS_PASSWORD || undefined
const db = Number(process.env.REDIS_DB || 0)
this.client = new Redis({
host,
port,
password,
db,
lazyConnect: true,
maxRetriesPerRequest: 3,
})
this.client.on('error', (error) => {
this.logger.error(`Redis error: ${error.message}`)
})
}
async getClient(): Promise<Redis> {
if (this.client.status !== 'ready') {
await this.client.connect()
}
return this.client
}
async get(key: string): Promise<string | null> {
const client = await this.getClient()
return client.get(key)
}
async getJson<T>(key: string): Promise<T | null> {
const value = await this.get(key)
if (!value) {
return null
}
try {
return JSON.parse(value) as T
} catch {
this.logger.warn(`Invalid JSON for key "${key}"`)
return null
}
}
async set(
key: string,
value: string,
ttlSeconds?: number,
): Promise<'OK' | null> {
const client = await this.getClient()
if (ttlSeconds && ttlSeconds > 0) {
return client.set(key, value, 'EX', ttlSeconds)
}
return client.set(key, value)
}
async setJson(
key: string,
value: unknown,
ttlSeconds?: number,
): Promise<'OK' | null> {
return this.set(key, JSON.stringify(value), ttlSeconds)
}
async delete(key: string): Promise<number> {
const client = await this.getClient()
return client.del(key)
}
async exists(key: string): Promise<boolean> {
const client = await this.getClient()
const result = await client.exists(key)
return result === 1
}
async expire(key: string, ttlSeconds: number): Promise<boolean> {
const client = await this.getClient()
const result = await client.expire(key, ttlSeconds)
return result === 1
}
async deleteByPattern(pattern: string): Promise<number> {
const client = await this.getClient()
let deletedCount = 0
let cursor = '0'
do {
const [nextCursor, keys] = await client.scan(
cursor,
'MATCH',
pattern,
'COUNT',
100,
)
cursor = nextCursor
if (keys.length > 0) {
const pipeline = client.pipeline()
keys.forEach(k => pipeline.del(k))
const results = await pipeline.exec()
if (results) {
deletedCount += results.reduce((sum, [, value]) => {
return sum + (typeof value === 'number' ? value : 0)
}, 0)
}
}
} while (cursor !== '0')
return deletedCount
}
async deleteByPatterns(patterns: string[]): Promise<number> {
const deletedCounts = await Promise.all(
patterns.map(pattern => this.deleteByPattern(pattern)),
)
return deletedCounts.reduce((sum, count) => sum + count, 0)
}
async onModuleDestroy() {
await this.client.quit()
}
}