Skip to content

Commit f5fefa0

Browse files
Rework global APK cache as a flight cache (#1997)
This simplifies the implementation of the global APK cache to be flight cache based, which matches the same behavior but with less bespoke code. The only (desired) change in behavior is that errors are no longer cached.
1 parent 8b647a5 commit f5fefa0

File tree

1 file changed

+21
-39
lines changed

1 file changed

+21
-39
lines changed

pkg/apk/apk/implementation.go

Lines changed: 21 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ import (
3838
"runtime"
3939
"slices"
4040
"strings"
41-
"sync"
4241
"time"
4342

4443
"github.com/hashicorp/go-retryablehttp"
@@ -63,7 +62,7 @@ import (
6362
// This is terrible but simpler than plumbing around a cache for now.
6463
// We just hold the expanded APK in memory rather than re-parsing it every time,
6564
// which is expensive. This also dedupes simultaneous fetches.
66-
var globalApkCache = &apkCache{}
65+
var globalApkCache = newFlightCache[string, *expandapk.APKExpanded]()
6766

6867
type APK struct {
6968
arch string
@@ -1260,42 +1259,6 @@ func (a *APK) cachedPackage(ctx context.Context, pkg InstallablePackage, cacheDi
12601259
return &exp, nil
12611260
}
12621261

1263-
type apkResult struct {
1264-
exp *expandapk.APKExpanded
1265-
err error
1266-
}
1267-
1268-
type apkCache struct {
1269-
// url -> func() apkResult (from sync.OnceValue(...))
1270-
onces sync.Map
1271-
}
1272-
1273-
func (c *apkCache) get(ctx context.Context, a *APK, pkg InstallablePackage) (*expandapk.APKExpanded, error) {
1274-
u := pkg.URL()
1275-
// Do all the expensive things inside sync.OnceValue()
1276-
fn := func() apkResult {
1277-
exp, err := expandPackage(ctx, a, pkg)
1278-
return apkResult{
1279-
exp: exp,
1280-
err: err,
1281-
}
1282-
}
1283-
once, cached := c.onces.LoadOrStore(u, sync.OnceValue(fn))
1284-
result := once.(func() apkResult)()
1285-
if cached && result.exp != nil {
1286-
// If we find a value in the cache, we should check to make sure the tar file it references still exists.
1287-
// If it references a non-existent file, we should act as though this was a cache miss and expand the
1288-
// APK again.
1289-
if !result.exp.IsValid() {
1290-
newValue := sync.OnceValue(fn)
1291-
c.onces.Store(u, newValue)
1292-
result = newValue()
1293-
}
1294-
}
1295-
1296-
return result.exp, result.err
1297-
}
1298-
12991262
func (a *APK) expandPackage(ctx context.Context, pkg InstallablePackage) (*expandapk.APKExpanded, error) {
13001263
if a.cache == nil {
13011264
// If we don't have a cache configured, don't use the global cache.
@@ -1305,7 +1268,26 @@ func (a *APK) expandPackage(ctx context.Context, pkg InstallablePackage) (*expan
13051268
return expandPackage(ctx, a, pkg)
13061269
}
13071270

1308-
return globalApkCache.get(ctx, a, pkg)
1271+
cached := true
1272+
val, err := globalApkCache.Do(pkg.URL(), func() (*expandapk.APKExpanded, error) {
1273+
cached = false
1274+
return expandPackage(ctx, a, pkg)
1275+
})
1276+
if !cached {
1277+
// We've just executed the callback - either successfully cached or
1278+
// failed (errors aren't cached). Either way, no validation needed.
1279+
return val, err
1280+
}
1281+
if val != nil {
1282+
// If we find a value in the cache, we should check to make sure the tar file it references still exists.
1283+
// If it references a non-existent file, we should act as though this was a cache miss and expand the
1284+
// APK again.
1285+
if !val.IsValid() {
1286+
globalApkCache.Forget(pkg.URL())
1287+
return a.expandPackage(ctx, pkg)
1288+
}
1289+
}
1290+
return val, err
13091291
}
13101292

13111293
func expandPackage(ctx context.Context, a *APK, pkg InstallablePackage) (*expandapk.APKExpanded, error) {

0 commit comments

Comments
 (0)