1+ import crypto from 'node:crypto'
12import { createLogger } from '@sim/logger'
23import { getRedisClient } from '@/lib/core/config/redis'
4+ import type { PaginatedCacheStorageAdapter } from '@/lib/paginated-cache/adapter'
35import { RedisPaginatedCache } from '@/lib/paginated-cache/redis-cache'
4- import { isPaginatedCacheReference } from '@/lib/paginated-cache/types'
56import type { PaginatedCacheReference , ToolPaginationConfig } from '@/lib/paginated-cache/types'
7+ import { isPaginatedCacheReference } from '@/lib/paginated-cache/types'
68import type { ToolResponse } from '@/tools/types'
79
810const logger = createLogger ( 'Paginator' )
911
10- const DEFAULT_MAX_PAGES = 100
12+ const DEFAULT_MAX_PAGES = 10_000
1113
1214interface AutoPaginateOptions {
1315 initialResult : ToolResponse
@@ -23,8 +25,14 @@ interface AutoPaginateOptions {
2325}
2426
2527export async function autoPaginate ( options : AutoPaginateOptions ) : Promise < ToolResponse > {
26- const { initialResult, params, paginationConfig : config , executeTool, toolId, executionId } =
27- options
28+ const {
29+ initialResult,
30+ params,
31+ paginationConfig : config ,
32+ executeTool,
33+ toolId,
34+ executionId,
35+ } = options
2836 const maxPages = config . maxPages ?? DEFAULT_MAX_PAGES
2937
3038 const redis = getRedisClient ( )
@@ -33,7 +41,7 @@ export async function autoPaginate(options: AutoPaginateOptions): Promise<ToolRe
3341 }
3442
3543 const cache = new RedisPaginatedCache ( redis )
36- const cacheId = `${ executionId } :${ toolId } :${ config . pageField } :${ Date . now ( ) } `
44+ const cacheId = `${ executionId } :${ toolId } :${ config . pageField } :${ crypto . randomUUID ( ) } `
3745
3846 let totalItems = 0
3947 let pageIndex = 0
@@ -97,7 +105,14 @@ export async function hydrateCacheReferences(
97105 if ( ! containsCacheReference ( inputs ) ) {
98106 return inputs
99107 }
100- return ( await deepHydrate ( inputs ) ) as Record < string , unknown >
108+
109+ const redis = getRedisClient ( )
110+ if ( ! redis ) {
111+ throw new Error ( 'Redis is required to hydrate paginated cache references but is not available' )
112+ }
113+
114+ const adapter = new RedisPaginatedCache ( redis )
115+ return ( await deepHydrate ( inputs , adapter ) ) as Record < string , unknown >
101116}
102117
103118function containsCacheReference ( value : unknown ) : boolean {
@@ -109,37 +124,35 @@ function containsCacheReference(value: unknown): boolean {
109124 return false
110125}
111126
112- async function deepHydrate ( value : unknown ) : Promise < unknown > {
127+ async function deepHydrate (
128+ value : unknown ,
129+ adapter : PaginatedCacheStorageAdapter
130+ ) : Promise < unknown > {
113131 if ( isPaginatedCacheReference ( value ) ) {
114- return hydrateReference ( value )
132+ return hydrateReference ( value , adapter )
115133 }
116134
117135 if ( Array . isArray ( value ) ) {
118- return Promise . all ( value . map ( deepHydrate ) )
136+ return Promise . all ( value . map ( ( v ) => deepHydrate ( v , adapter ) ) )
119137 }
120138
121139 if ( typeof value === 'object' && value !== null ) {
122140 const entries = Object . entries ( value as Record < string , unknown > )
123141 const hydrated : Record < string , unknown > = { }
124142 for ( const [ key , val ] of entries ) {
125- hydrated [ key ] = await deepHydrate ( val )
143+ hydrated [ key ] = await deepHydrate ( val , adapter )
126144 }
127145 return hydrated
128146 }
129147
130148 return value
131149}
132150
133- async function hydrateReference ( ref : PaginatedCacheReference ) : Promise < unknown [ ] > {
134- const redis = getRedisClient ( )
135- if ( ! redis ) {
136- throw new Error (
137- `Redis is required to hydrate paginated cache reference (cacheId: ${ ref . cacheId } ) but is not available`
138- )
139- }
140-
141- const cache = new RedisPaginatedCache ( redis )
142- const pages = await cache . getAllPages ( ref . cacheId , ref . totalPages )
151+ async function hydrateReference (
152+ ref : PaginatedCacheReference ,
153+ adapter : PaginatedCacheStorageAdapter
154+ ) : Promise < unknown [ ] > {
155+ const pages = await adapter . getAllPages ( ref . cacheId , ref . totalPages )
143156
144157 const items : unknown [ ] = [ ]
145158 for ( const page of pages ) {
@@ -165,21 +178,23 @@ export async function cleanupPaginatedCache(executionId: string): Promise<void>
165178 return
166179 }
167180
168- const pattern = `pagcache:* ${ executionId } :*`
181+ const patterns = [ `pagcache:page: ${ executionId } :*` , `pagcache:meta: ${ executionId } :*`]
169182
170183 try {
171- let cursor = '0'
172184 let deletedCount = 0
173185
174- do {
175- const [ nextCursor , keys ] = await redis . scan ( cursor , 'MATCH' , pattern , 'COUNT' , 100 )
176- cursor = nextCursor
177-
178- if ( keys . length > 0 ) {
179- await redis . del ( ...keys )
180- deletedCount += keys . length
181- }
182- } while ( cursor !== '0' )
186+ for ( const pattern of patterns ) {
187+ let cursor = '0'
188+ do {
189+ const [ nextCursor , keys ] = await redis . scan ( cursor , 'MATCH' , pattern , 'COUNT' , 100 )
190+ cursor = nextCursor
191+
192+ if ( keys . length > 0 ) {
193+ await redis . del ( ...keys )
194+ deletedCount += keys . length
195+ }
196+ } while ( cursor !== '0' )
197+ }
183198
184199 if ( deletedCount > 0 ) {
185200 logger . info ( `Cleaned up ${ deletedCount } paginated cache entries for execution ${ executionId } ` )
0 commit comments