@@ -32,46 +32,76 @@ export class UpstashCache extends Cache {
3232 : undefined ;
3333 }
3434
35- override async get ( key : string , _tables : string [ ] , _isTag : boolean ) {
36- const res = await this . redis . get < any [ ] > ( key ) ?? undefined ;
37- return res ;
35+ override async get ( key : string , tables : string [ ] , isTag : boolean = false ) : Promise < any [ ] | undefined > {
36+ const compositeKey = tables . sort ( ) . join ( ',' ) ; // Generate the composite key for the query
37+
38+ if ( isTag ) {
39+ // Handle cache lookup for tags
40+ const tagCompositeKey = await this . redis . hget < string > ( 'tagsMap' , key ) ; // Retrieve composite key associated with the tag
41+ if ( tagCompositeKey ) {
42+ return await this . redis . hget ( tagCompositeKey , key ) as any [ ] ; // Retrieve the cached result for the tag
43+ }
44+ return undefined ;
45+ }
46+
47+ // Normal cache lookup for the composite key
48+ return await this . redis . hget ( compositeKey , key ) ?? undefined ; // Retrieve result for normal query
3849 }
3950
40- override async put ( key : string , response : any , tables : string [ ] , isTag : boolean , config ?: CacheConfig ) {
41- await this . redis . set ( key , response , config ? this . toInternalConfig ( config ) : this . internalConfig ) ;
42- for ( const table of tables ) {
43- await this . redis . sadd ( table , key ) ;
51+ override async put (
52+ key : string ,
53+ response : any ,
54+ tables : string [ ] ,
55+ isTag : boolean = false ,
56+ _config ?: CacheConfig ,
57+ ) : Promise < void > {
58+ if ( isTag ) {
59+ // When it's a tag, store the response in the composite key
60+ const compositeKey = tables . sort ( ) . join ( ',' ) ;
61+ await this . redis . hset ( compositeKey , { [ key ] : response } ) ; // Store the result with the tag under the composite key
62+ await this . redis . hset ( 'tagsMap' , { [ key ] : compositeKey } ) ; // Store the tag and its composite key in the map
63+ for ( const table of tables ) {
64+ await this . redis . sadd ( `prefix_${ table } ` , compositeKey ) ;
65+ }
66+ } else {
67+ // Normal cache store
68+ const compositeKey = tables . sort ( ) . join ( ',' ) ;
69+ await this . redis . hset ( compositeKey , { [ key ] : response } ) ; // Store the result with the composite key
4470 }
4571 }
4672
4773 override async onMutate ( params : MutationOption ) {
48- const tagsArray = params . tags ? Array . isArray ( params . tags ) ? params . tags : [ params . tags ] : [ ] ;
49- const tablesArray = params . tables ? Array . isArray ( params . tables ) ? params . tables : [ params . tables ] : [ ] ;
74+ const tags = Array . isArray ( params . tags ) ? params . tags : params . tags ? [ params . tags ] : [ ] ;
75+ const tables = Array . isArray ( params . tables ) ? params . tables : params . tables ? [ params . tables ] : [ ] ;
76+ const mappedTables = tables . map ( ( table ) => is ( table , Table ) ? table [ OriginalName ] : table as string ) ;
77+ const prefixedMappedTables = tables . map ( ( table ) => `prefix_${ table } ` ) ;
5078
5179 const keysToDelete = new Set < string > ( ) ;
5280
53- for ( const table of tablesArray ) {
54- const tableName = is ( table , Table ) ? table [ OriginalName ] : table as string ;
55- const keys = await this . redis . smembers ( tableName ) ;
56- for ( const key of keys ) keysToDelete . add ( key ) ; // Add to the set
57- }
58-
59- if ( keysToDelete . size > 0 || tagsArray . length > 0 ) {
60- const pipeline = this . redis . pipeline ( ) ;
61-
62- for ( const tag of tagsArray ) {
63- pipeline . del ( tag ) ;
81+ // Invalidate by table
82+ if ( tables . length > 0 ) {
83+ // @ts -expect-error
84+ const compositeKeys : string [ ] = await this . redis . sunion ( ...prefixedMappedTables ) ;
85+ for ( const composite of compositeKeys ) {
86+ const keys = await this . redis . hkeys ( composite ) ;
87+ for ( const key of keys ) keysToDelete . add ( key ) ;
88+ await this . redis . del ( composite ) ; // Remove composite entry
6489 }
90+ await this . redis . del ( ...prefixedMappedTables , ...mappedTables ) ; // Remove table mappings
91+ }
6592
66- for ( const key of keysToDelete ) {
67- pipeline . del ( key ) ;
68- for ( const table of tablesArray ) {
69- const tableName = is ( table , Table ) ? table [ OriginalName ] : table as string ;
70- pipeline . srem ( tableName , key ) ;
71- }
93+ // Invalidate by tag (WITHOUT invalidating the entire table)
94+ for ( const tag of tags ) {
95+ const compositeKey = await this . redis . hget < string > ( 'tagsMap' , tag ) ;
96+ if ( compositeKey ) {
97+ await this . redis . hdel ( compositeKey , tag ) ; // Only remove the tag-related entry
98+ await this . redis . hdel ( 'tagsMap' , tag ) ; // Remove tag reference
7299 }
100+ }
73101
74- await pipeline . exec ( ) ;
102+ // Delete affected cache entries
103+ if ( keysToDelete . size > 0 ) {
104+ await this . redis . del ( ...keysToDelete ) ;
75105 }
76106 }
77107}
0 commit comments