Skip to content
This repository has been archived by the owner on Jun 5, 2024. It is now read-only.

Add global expanded APK cache #133

Merged
merged 1 commit into from
Oct 10, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions pkg/apk/implementation.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"path/filepath"
"runtime"
"strings"
"sync"
"time"

"github.com/chainguard-dev/go-apk/pkg/expandapk"
Expand All @@ -48,6 +49,11 @@ import (
"github.com/hashicorp/go-retryablehttp"
)

// This is terrible but simpler than plumbing around a cache for now.
// We just hold the expanded APK in memory rather than re-parsing it every time,
// which is expensive. This also dedupes simultaneous fetches.
var globalApkCache = &apkCache{}

type APK struct {
arch string
version string
Expand Down Expand Up @@ -750,7 +756,53 @@ func (a *APK) cachedPackage(ctx context.Context, pkg *repository.RepositoryPacka
return &exp, nil
}

type apkResult struct {
exp *expandapk.APKExpanded
err error
}

type apkCache struct {
// url -> *sync.Once
onces sync.Map

// url -> apkResult
resps sync.Map
}

func (c *apkCache) get(ctx context.Context, a *APK, pkg *repository.RepositoryPackage) (*expandapk.APKExpanded, error) {
u := pkg.Url()
// Do all the expensive things inside the once.
once, _ := c.onces.LoadOrStore(u, &sync.Once{})
once.(*sync.Once).Do(func() {
exp, err := expandPackage(ctx, a, pkg)
c.resps.Store(u, apkResult{
exp: exp,
err: err,
})
})

v, ok := c.resps.Load(u)
if !ok {
panic(fmt.Errorf("did not see apk %q after writing it", u))
}

result := v.(apkResult)
return result.exp, result.err
}

func (a *APK) expandPackage(ctx context.Context, pkg *repository.RepositoryPackage) (*expandapk.APKExpanded, error) {
if a.cache == nil {
// If we don't have a cache configured, don't use the global cache.
// Calling APKExpanded.Close() will clean up a tempdir.
// This is fine when we have a cache because we move all the backing files into the cache.
// This is not fine when we don't have a cache because the tempdir contains all our state.
return expandPackage(ctx, a, pkg)
}

return globalApkCache.get(ctx, a, pkg)
}

func expandPackage(ctx context.Context, a *APK, pkg *repository.RepositoryPackage) (*expandapk.APKExpanded, error) {
ctx, span := otel.Tracer("go-apk").Start(ctx, "expandPackage", trace.WithAttributes(attribute.String("package", pkg.Name)))
defer span.End()

Expand Down