From 4e7350363da195ac003653ac1b84b3b65ea74627 Mon Sep 17 00:00:00 2001 From: Lukasz Raczylo Date: Sun, 4 Jan 2026 13:50:42 +0000 Subject: [PATCH] feat: track download counts for both cache hits and cache misses Previously, download counts only incremented on cache hits (when package was served from cache). First-time downloads (cache misses) were not counted. Changes: - Add UpdateDownloadCount() call when serving newly cached packages - This ensures every download through the proxy increments the counter - Analytics tracking also added for cache misses Behavior now: - First download (cache miss): counter = 1 - Second download (cache hit): counter = 2 - Third download (cache hit): counter = 3 - etc. Updated all relevant tests to expect the additional UpdateDownloadCount call. Resolves user requirement: "I want the counters to increase whenever package is downloaded via proxy - regardless of it being new download or cached download" --- pkg/cache/cache.go | 16 ++++++++++++++++ pkg/cache/cache_test.go | 3 +++ 2 files changed, 19 insertions(+) diff --git a/pkg/cache/cache.go b/pkg/cache/cache.go index 33968af..beb7b84 100644 --- a/pkg/cache/cache.go +++ b/pkg/cache/cache.go @@ -320,6 +320,22 @@ servePkg: return nil, errors.Wrap(err, errors.ErrCodeStorageFailure, "failed to retrieve just-stored package") } + // Track download count for first-time download (cache miss) + // This ensures download count increments regardless of cache hit/miss + if err := m.metadata.UpdateDownloadCount(ctx, registry, name, version); err != nil { + log.Warn(). + Err(err). + Str("registry", registry). + Str("package", name). + Str("version", version). + Msg("Failed to update download count for newly cached package") + } + + // Track download in analytics if enabled + if m.analytics != nil { + m.trackDownload(registry, name, version, storedPkg.Size) + } + return &CacheEntry{ Package: storedPkg, Data: storedData, diff --git a/pkg/cache/cache_test.go b/pkg/cache/cache_test.go index 7d232a0..44c0073 100644 --- a/pkg/cache/cache_test.go +++ b/pkg/cache/cache_test.go @@ -343,6 +343,7 @@ func TestGet(t *testing.T) { s.On("Put", mock.Anything, "npm/lodash/4.17.21", mock.Anything, mock.Anything).Return(nil) m.On("SavePackage", mock.Anything, mock.Anything).Return(nil) s.On("Get", mock.Anything, "npm/lodash/4.17.21").Return(io.NopCloser(strings.NewReader("upstream data")), nil) + m.On("UpdateDownloadCount", mock.Anything, "npm", "lodash", "4.17.21").Return(nil) }, fetchFunc: func(ctx context.Context) (io.ReadCloser, string, error) { return io.NopCloser(strings.NewReader("upstream data")), "https://registry.npmjs.org/lodash", nil @@ -374,6 +375,7 @@ func TestGet(t *testing.T) { s.On("Put", mock.Anything, "npm/expired-pkg/1.0.0", mock.Anything, mock.Anything).Return(nil) m.On("SavePackage", mock.Anything, mock.Anything).Return(nil) s.On("Get", mock.Anything, "npm/expired-pkg/1.0.0").Return(io.NopCloser(strings.NewReader("refreshed data")), nil) + m.On("UpdateDownloadCount", mock.Anything, "npm", "expired-pkg", "1.0.0").Return(nil) }, fetchFunc: func(ctx context.Context) (io.ReadCloser, string, error) { return io.NopCloser(strings.NewReader("refreshed data")), "https://registry.npmjs.org/expired-pkg", nil @@ -435,6 +437,7 @@ func TestGet(t *testing.T) { m.On("SavePackage", mock.Anything, mock.Anything).Return(nil) // Second Get succeeds (after re-storing) s.On("Get", mock.Anything, "npm/inconsistent/1.0.0").Return(io.NopCloser(strings.NewReader("recovered data")), nil).Once() + m.On("UpdateDownloadCount", mock.Anything, "npm", "inconsistent", "1.0.0").Return(nil) }, fetchFunc: func(ctx context.Context) (io.ReadCloser, string, error) { return io.NopCloser(strings.NewReader("recovered data")), "https://registry.npmjs.org/inconsistent", nil