@@ -2,6 +2,7 @@ import { Cluster, ClusterOptions } from "ioredis";
22import { logger } from "./logger" ;
33import { db , folders } from "../db" ;
44import { eq } from "drizzle-orm" ;
5+ import * as Sentry from "@sentry/node" ;
56
67let client : Cluster | null = null ;
78
@@ -73,71 +74,121 @@ export async function getCache<T>(key: string): Promise<T | null> {
7374 const cache = getCacheClient ( ) ;
7475 if ( ! cache ) return null ;
7576
76- const startTime = Date . now ( ) ;
77- try {
78- const data = await cache . get ( key ) ;
79- const duration = Date . now ( ) - startTime ;
80- const hit = data !== null ;
77+ return await Sentry . startSpan (
78+ {
79+ op : "cache.get" ,
80+ name : "cache.get" ,
81+ attributes : {
82+ "cache.key" : key ,
83+ } ,
84+ } ,
85+ async ( span ) => {
86+ const startTime = Date . now ( ) ;
87+ try {
88+ const data = await cache . get ( key ) ;
89+ const duration = Date . now ( ) - startTime ;
90+ const hit = data !== null ;
91+
92+ // Set Sentry span attributes
93+ span . setAttribute ( "cache.hit" , hit ) ;
94+ if ( data ) {
95+ span . setAttribute ( "cache.item_size" , data . length ) ;
96+ }
8197
82- // Log cache operation with metrics
83- logger . cacheOperation ( "get" , key , hit , duration ) ;
98+ // Log cache operation with metrics
99+ logger . cacheOperation ( "get" , key , hit , duration ) ;
84100
85- return data ? JSON . parse ( data ) : null ;
86- } catch ( error ) {
87- logger . cacheError ( "get" , key , error instanceof Error ? error : new Error ( String ( error ) ) ) ;
88- return null ;
89- }
101+ return data ? JSON . parse ( data ) : null ;
102+ } catch ( error ) {
103+ span . setStatus ( { code : 2 , message : "error" } ) ; // SPAN_STATUS_ERROR
104+ logger . cacheError ( "get" , key , error instanceof Error ? error : new Error ( String ( error ) ) ) ;
105+ return null ;
106+ }
107+ }
108+ ) ;
90109}
91110
92111export async function setCache ( key : string , value : unknown , ttlSeconds ?: number ) : Promise < void > {
93112 const cache = getCacheClient ( ) ;
94113 if ( ! cache ) return ;
95114
96- const startTime = Date . now ( ) ;
97- try {
98- const serialized = JSON . stringify ( value ) ;
99- if ( ttlSeconds ) {
100- await cache . setex ( key , ttlSeconds , serialized ) ;
101- } else {
102- await cache . set ( key , serialized ) ;
103- }
104- const duration = Date . now ( ) - startTime ;
115+ await Sentry . startSpan (
116+ {
117+ op : "cache.put" ,
118+ name : "cache.put" ,
119+ attributes : {
120+ "cache.key" : key ,
121+ } ,
122+ } ,
123+ async ( span ) => {
124+ const startTime = Date . now ( ) ;
125+ try {
126+ const serialized = JSON . stringify ( value ) ;
127+
128+ // Set Sentry span attributes
129+ span . setAttribute ( "cache.item_size" , serialized . length ) ;
130+ if ( ttlSeconds ) {
131+ span . setAttribute ( "cache.ttl" , ttlSeconds ) ;
132+ }
105133
106- // Log cache operation with metrics
107- logger . cacheOperation ( "set" , key , undefined , duration , ttlSeconds ) ;
108- } catch ( error ) {
109- logger . cacheError ( "set" , key , error instanceof Error ? error : new Error ( String ( error ) ) ) ;
110- }
134+ if ( ttlSeconds ) {
135+ await cache . setex ( key , ttlSeconds , serialized ) ;
136+ } else {
137+ await cache . set ( key , serialized ) ;
138+ }
139+ const duration = Date . now ( ) - startTime ;
140+
141+ // Log cache operation with metrics
142+ logger . cacheOperation ( "set" , key , undefined , duration , ttlSeconds ) ;
143+ } catch ( error ) {
144+ span . setStatus ( { code : 2 , message : "error" } ) ; // SPAN_STATUS_ERROR
145+ logger . cacheError ( "set" , key , error instanceof Error ? error : new Error ( String ( error ) ) ) ;
146+ }
147+ }
148+ ) ;
111149}
112150
113151export async function deleteCache ( ...keys : string [ ] ) : Promise < void > {
114152 const cache = getCacheClient ( ) ;
115153 if ( ! cache || keys . length === 0 ) return ;
116154
117- const startTime = Date . now ( ) ;
118- try {
119- // In cluster mode, keys may hash to different slots
120- // Use pipeline to delete individually (more efficient than separate awaits)
121- if ( keys . length === 1 ) {
122- await cache . del ( keys [ 0 ] ) ;
123- } else {
124- const pipeline = cache . pipeline ( ) ;
125- for ( const key of keys ) {
126- pipeline . del ( key ) ;
155+ await Sentry . startSpan (
156+ {
157+ op : "cache.remove" ,
158+ name : "cache.remove" ,
159+ attributes : {
160+ "cache.key" : keys [ 0 ] , // Use first key as representative
161+ "cache.key_count" : keys . length ,
162+ } ,
163+ } ,
164+ async ( span ) => {
165+ const startTime = Date . now ( ) ;
166+ try {
167+ // In cluster mode, keys may hash to different slots
168+ // Use pipeline to delete individually (more efficient than separate awaits)
169+ if ( keys . length === 1 ) {
170+ await cache . del ( keys [ 0 ] ) ;
171+ } else {
172+ const pipeline = cache . pipeline ( ) ;
173+ for ( const key of keys ) {
174+ pipeline . del ( key ) ;
175+ }
176+ await pipeline . exec ( ) ;
177+ }
178+ const duration = Date . now ( ) - startTime ;
179+
180+ // Log cache operation with metrics (use first key as representative)
181+ logger . cacheOperation ( "delete" , keys [ 0 ] , undefined , duration , undefined , keys . length ) ;
182+ } catch ( error ) {
183+ span . setStatus ( { code : 2 , message : "error" } ) ; // SPAN_STATUS_ERROR
184+ logger . cacheError (
185+ "delete" ,
186+ keys . join ( ", " ) ,
187+ error instanceof Error ? error : new Error ( String ( error ) )
188+ ) ;
127189 }
128- await pipeline . exec ( ) ;
129190 }
130- const duration = Date . now ( ) - startTime ;
131-
132- // Log cache operation with metrics (use first key as representative)
133- logger . cacheOperation ( "delete" , keys [ 0 ] , undefined , duration , undefined , keys . length ) ;
134- } catch ( error ) {
135- logger . cacheError (
136- "delete" ,
137- keys . join ( ", " ) ,
138- error instanceof Error ? error : new Error ( String ( error ) )
139- ) ;
140- }
191+ ) ;
141192}
142193
143194export async function deleteCachePattern ( pattern : string ) : Promise < void > {
0 commit comments