@@ -38,6 +38,7 @@ type valkeyStore struct {
3838 sessionPrefix string
3939 expiryIndexKey string
4040 lastActivityIndexKey string
41+ kindIndexKeyPrefix string
4142}
4243
4344// initValkeyStore init valkey store client
@@ -56,6 +57,7 @@ func initValkeyStore() (*valkeyStore, error) {
5657 sessionPrefix : "session:" ,
5758 expiryIndexKey : "session:expiry" ,
5859 lastActivityIndexKey : "session:last_activity" ,
60+ kindIndexKeyPrefix : "session:kind:" ,
5961 }, nil
6062}
6163
@@ -178,51 +180,30 @@ func (vs *valkeyStore) GetSandboxBySessionID(ctx context.Context, sessionID stri
178180// ListSandboxesByKind returns all active sandboxes matching the given kind.
179181// Uses SCAN to prevent blocking the Valkey instance on large datasets.
180182func (vs * valkeyStore ) ListSandboxesByKind (ctx context.Context , kind string ) ([]* types.SandboxInfo , error ) {
181- allSandboxes := make ([]* types.SandboxInfo , 0 ) // Initialize as empty array, not nil
182- cursor := uint64 (0 )
183- matchPattern := vs .sessionPrefix + "*"
184- seenKeys := make (map [string ]bool )
183+ kindKey := vs .kindIndexKeyPrefix + kind
184+ sessionIDs , err := vs .cli .Do (ctx , vs .cli .B ().Smembers ().Key (kindKey ).Build ()).AsStrSlice ()
185+ if err != nil && ! valkey .IsValkeyNil (err ) {
186+ return nil , fmt .Errorf ("ListSandboxesByKind smembers failed: %w" , err )
187+ }
185188
186- for {
187- scanRes , err := vs .cli .Do (ctx , vs .cli .B ().Scan ().Cursor (cursor ).Match (matchPattern ).Count (100 ).Build ()).AsScanEntry ()
188- if err != nil {
189- return nil , fmt .Errorf ("ListSandboxesByKind scan failed: %w" , err )
190- }
189+ if len (sessionIDs ) == 0 {
190+ return make ([]* types.SandboxInfo , 0 ), nil
191+ }
191192
192- if len (scanRes .Elements ) > 0 {
193- sessionIDs := make ([]string , 0 , len (scanRes .Elements ))
194- for _ , key := range scanRes .Elements {
195- if key == vs .expiryIndexKey || key == vs .lastActivityIndexKey {
196- continue
197- }
198- if seenKeys [key ] {
199- continue
200- }
201- seenKeys [key ] = true
202- sessionIDs = append (sessionIDs , strings .TrimPrefix (key , vs .sessionPrefix ))
203- }
204-
205- if len (sessionIDs ) > 0 {
206- // Batch load the fetched keys
207- sandboxes , err := vs .loadSandboxesBySessionIDs (ctx , sessionIDs )
208- if err != nil {
209- return nil , err
210- }
211-
212- // Filter by requested kind
213- for _ , sb := range sandboxes {
214- if sb != nil && sb .Kind == kind {
215- allSandboxes = append (allSandboxes , sb )
216- }
217- }
218- }
219- }
193+ // Batch load the fetched keys
194+ sandboxes , err := vs .loadSandboxesBySessionIDs (ctx , sessionIDs )
195+ if err != nil {
196+ return nil , err
197+ }
220198
221- cursor = scanRes .Cursor
222- if cursor == 0 {
223- break // Scan complete
199+ allSandboxes := make ([]* types.SandboxInfo , 0 , len (sandboxes ))
200+ // Filter by requested kind
201+ for _ , sb := range sandboxes {
202+ if sb != nil && sb .Kind == kind {
203+ allSandboxes = append (allSandboxes , sb )
224204 }
225205 }
206+
226207 return allSandboxes , nil
227208}
228209
@@ -248,6 +229,7 @@ func (vs *valkeyStore) StoreSandbox(ctx context.Context, sandboxStore *types.San
248229 ScoreMember (float64 (sandboxStore .ExpiresAt .Unix ()), sandboxStore .SessionID ).Build ())
249230 commands = append (commands , vs .cli .B ().Zadd ().Key (vs .lastActivityIndexKey ).ScoreMember ().
250231 ScoreMember (float64 (time .Now ().Unix ()), sandboxStore .SessionID ).Build ())
232+ commands = append (commands , vs .cli .B ().Sadd ().Key (vs .kindIndexKeyPrefix + sandboxStore .Kind ).Member (sandboxStore .SessionID ).Build ())
251233
252234 for i , resp := range vs .cli .DoMulti (ctx , commands ... ) {
253235 if err = resp .Error (); err != nil {
@@ -286,10 +268,22 @@ func (vs *valkeyStore) UpdateSandbox(ctx context.Context, sandboxStore *types.Sa
286268func (vs * valkeyStore ) DeleteSandboxBySessionID (ctx context.Context , sessionID string ) error {
287269 sessionKey := vs .sessionKey (sessionID )
288270
271+ // Fetch sandbox to get its kind for secondary index cleanup
272+ var kind string
273+ if b , err := vs .cli .Do (ctx , vs .cli .B ().Get ().Key (sessionKey ).Build ()).AsBytes (); err == nil {
274+ var sb types.SandboxInfo
275+ if json .Unmarshal (b , & sb ) == nil {
276+ kind = sb .Kind
277+ }
278+ }
279+
289280 commands := make (valkey.Commands , 0 , 4 )
290281 commands = append (commands , vs .cli .B ().Del ().Key (sessionKey ).Build ())
291282 commands = append (commands , vs .cli .B ().Zrem ().Key (vs .expiryIndexKey ).Member (sessionID ).Build ())
292283 commands = append (commands , vs .cli .B ().Zrem ().Key (vs .lastActivityIndexKey ).Member (sessionID ).Build ())
284+ if kind != "" {
285+ commands = append (commands , vs .cli .B ().Srem ().Key (vs .kindIndexKeyPrefix + kind ).Member (sessionID ).Build ())
286+ }
293287
294288 for i , resp := range vs .cli .DoMulti (ctx , commands ... ) {
295289 if err := resp .Error (); err != nil {
0 commit comments