From 14dad64c1442d49460d74c4919fabd9b954b51d6 Mon Sep 17 00:00:00 2001 From: Raymond Jacobson Date: Mon, 8 Jun 2026 22:12:03 -0700 Subject: [PATCH] perf(api): cache total wallets metric --- api/server.go | 10 ++++++++++ api/v1_metrics_total_wallets.go | 10 ++++++++++ api/v1_metrics_total_wallets_test.go | 16 ++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/api/server.go b/api/server.go index 03f794e3..1727ecfd 100644 --- a/api/server.go +++ b/api/server.go @@ -158,6 +158,14 @@ func NewApiServer(config config.Config) *ApiServer { panic(err) } + totalWalletsCache, err := otter.MustBuilder[string, int64](16). + WithTTL(totalWalletsCacheTTL). + CollectStats(). + Build() + if err != nil { + panic(err) + } + privateKey, err := crypto.HexToECDSA(config.DelegatePrivateKey) if err != nil { panic(err) @@ -268,6 +276,7 @@ func NewApiServer(config config.Config) *ApiServer { oauthTokenCache: &oauthTokenCache, qualifiedPlaylistsCache: &qualifiedPlaylistsCache, relatedUsersCache: &relatedUsersCache, + totalWalletsCache: &totalWalletsCache, requestValidator: requestValidator, rewardAttester: rewardAttester, transactionSender: transactionSender, @@ -822,6 +831,7 @@ type ApiServer struct { oauthTokenCache *otter.Cache[string, oauthTokenCacheEntry] qualifiedPlaylistsCache *otter.Cache[string, []int32] relatedUsersCache *otter.Cache[string, []int32] + totalWalletsCache *otter.Cache[string, int64] requestValidator *RequestValidator rewardManagerClient *reward_manager.RewardManagerClient claimableTokensClient *claimable_tokens.ClaimableTokensClient diff --git a/api/v1_metrics_total_wallets.go b/api/v1_metrics_total_wallets.go index bb5c2063..57a7507a 100644 --- a/api/v1_metrics_total_wallets.go +++ b/api/v1_metrics_total_wallets.go @@ -1,10 +1,19 @@ package api import ( + "time" + "github.com/gofiber/fiber/v2" ) +const totalWalletsCacheTTL = 5 * time.Minute +const totalWalletsCacheKey = "total" + func (app *ApiServer) v1MetricsTotalWallets(c *fiber.Ctx) error { + if total, ok := app.totalWalletsCache.Get(totalWalletsCacheKey); ok { + return c.JSON(fiber.Map{"data": map[string]int64{"total": total}}) + } + var total int64 if err := app.pool.QueryRow(c.Context(), ` SELECT SUM(count)::bigint AS total @@ -17,5 +26,6 @@ func (app *ApiServer) v1MetricsTotalWallets(c *fiber.Ctx) error { return err } + app.totalWalletsCache.Set(totalWalletsCacheKey, total) return c.JSON(fiber.Map{"data": map[string]int64{"total": total}}) } diff --git a/api/v1_metrics_total_wallets_test.go b/api/v1_metrics_total_wallets_test.go index 6fc4d083..c6656702 100644 --- a/api/v1_metrics_total_wallets_test.go +++ b/api/v1_metrics_total_wallets_test.go @@ -4,6 +4,7 @@ import ( "testing" "api.audius.co/database" + "github.com/stretchr/testify/require" ) func TestMetricsTotalWallets_Empty(t *testing.T) { @@ -46,3 +47,18 @@ func TestMetricsTotalWallets_WithFixtures(t *testing.T) { "data.total": 7, }) } + +func TestMetricsTotalWallets_Cache(t *testing.T) { + app := emptyTestApp(t) + + _, ok := app.totalWalletsCache.Get(totalWalletsCacheKey) + require.False(t, ok) + + app.totalWalletsCache.Set(totalWalletsCacheKey, 42) + + status, body := testGet(t, app, "/v1/metrics/total_wallets") + require.Equal(t, 200, status) + jsonAssert(t, body, map[string]any{ + "data.total": 42, + }) +}