@@ -16,6 +16,8 @@ type RapidCacheIndex interface {
1616 Admit (ctx context.Context , path string , size int64 ) error
1717 Evict (ctx context.Context , path string , size int64 ) error
1818 Candidates (ctx context.Context , before time.Time , limit int64 ) ([]string , error )
19+ IsRecent (ctx context.Context , path string ) (bool , error )
20+ EvictPrefix (ctx context.Context , prefix string ) error
1921}
2022
2123type noopRapidCacheIndex struct {}
@@ -28,6 +30,8 @@ func (noopRapidCacheIndex) Evict(context.Context, string, int64) error { return
2830func (noopRapidCacheIndex ) Candidates (context.Context , time.Time , int64 ) ([]string , error ) {
2931 return nil , nil
3032}
33+ func (noopRapidCacheIndex ) IsRecent (context.Context , string ) (bool , error ) { return false , nil }
34+ func (noopRapidCacheIndex ) EvictPrefix (context.Context , string ) error { return nil }
3135
3236type redisRapidCacheIndex struct {
3337 redis redis.UniversalClient
@@ -110,6 +114,61 @@ func (i *redisRapidCacheIndex) Candidates(ctx context.Context, before time.Time,
110114 }).Result ()
111115}
112116
117+ func (i * redisRapidCacheIndex ) IsRecent (ctx context.Context , path string ) (bool , error ) {
118+ score := i .redis .ZScore (ctx , i .keys .chunks , path )
119+ if score .Err () == redis .Nil {
120+ return false , nil
121+ }
122+ if score .Err () != nil {
123+ return false , score .Err ()
124+ }
125+
126+ return true , nil
127+ }
128+
129+ func (i * redisRapidCacheIndex ) EvictPrefix (ctx context.Context , prefix string ) error {
130+ script := redis .NewScript (`
131+ local cursor = "0"
132+ local prefix = ARGV[1]
133+ local chunks_key = KEYS[1]
134+ local build_bytes_key = KEYS[2]
135+ local build_chunks_key = KEYS[3]
136+
137+ repeat
138+ local scan_result = redis.call('ZSCAN', chunks_key, cursor, 'MATCH', prefix .. '*', 'COUNT', 100)
139+ cursor = scan_result[1]
140+ local members = scan_result[2]
141+
142+ for i = 1, #members, 2 do
143+ local path = members[i]
144+ redis.call('ZREM', chunks_key, path)
145+
146+ local path_without_prefix = string.gsub(path, '^rapid%-cache/', '')
147+ local build_id = string.match(path_without_prefix, '^([^/]+)')
148+ if build_id then
149+ local obj_result = redis.call('HGET', build_bytes_key, build_id)
150+ if obj_result then
151+ redis.call('HINCRBY', build_chunks_key, build_id, -1)
152+ local chunks_left = redis.call('HGET', build_chunks_key, build_id)
153+ if tonumber(chunks_left) <= 0 then
154+ redis.call('HDEL', build_bytes_key, build_id)
155+ redis.call('HDEL', build_chunks_key, build_id)
156+ end
157+ end
158+ end
159+ end
160+ until cursor == "0"
161+
162+ return 0
163+ ` )
164+
165+ return script .Run (ctx , i .redis , []string {
166+ i .keys .chunks ,
167+ i .keys .buildBytes ,
168+ i .keys .buildChunks ,
169+ }, "rapid-cache/" + prefix ).Err ()
170+ }
171+
113172func rapidCacheBuildID (path string ) string {
114173 path = strings .TrimPrefix (path , rapidCachePrefix )
115174 buildID , _ := SplitPath (path )
0 commit comments