From d9f39773e7ca1ecf98c483a7bd34d20d39446097 Mon Sep 17 00:00:00 2001 From: Ralf Haferkamp Date: Thu, 2 Apr 2026 17:15:19 +0200 Subject: [PATCH] proxy: add memory cache for tenant id mapping This is to reduce the number of "proxy->gateway->users->ldap" roundtrips for the tenant id mapping. The cache currently has a non-configurable ttl of 10 min. Related: #2310 --- .../proxy/pkg/middleware/account_resolver.go | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/services/proxy/pkg/middleware/account_resolver.go b/services/proxy/pkg/middleware/account_resolver.go index 33702ad636..ef90c91409 100644 --- a/services/proxy/pkg/middleware/account_resolver.go +++ b/services/proxy/pkg/middleware/account_resolver.go @@ -40,6 +40,12 @@ func AccountResolver(optionSetters ...Option) func(next http.Handler) http.Handl ) go lastGroupSyncCache.Start() + tenantIDCache := ttlcache.New( + ttlcache.WithTTL[string, string](10*time.Minute), + ttlcache.WithDisableTouchOnHit[string, string](), + ) + go tenantIDCache.Start() + return func(next http.Handler) http.Handler { return &accountResolver{ next: next, @@ -56,6 +62,7 @@ func AccountResolver(optionSetters ...Option) func(next http.Handler) http.Handl autoProvisionAccounts: options.AutoprovisionAccounts, multiTenantEnabled: options.MultiTenantEnabled, lastGroupSyncCache: lastGroupSyncCache, + tenantIDCache: tenantIDCache, eventsPublisher: options.EventsPublisher, } } @@ -79,7 +86,9 @@ type accountResolver struct { // memberships was done for a specific user. This is used to trigger a sync // with every single request. lastGroupSyncCache *ttlcache.Cache[string, struct{}] - eventsPublisher events.Publisher + // tenantIDCache maps external tenant IDs (from OIDC claims) to internal tenant IDs. + tenantIDCache *ttlcache.Cache[string, string] + eventsPublisher events.Publisher } func readStringClaim(path string, claims map[string]interface{}) (string, error) { @@ -292,10 +301,15 @@ func (m accountResolver) verifyTenantClaim(ctx context.Context, userTenantID str return nil } -// resolveInternalTenantID calls the gateway's TenantAPI to map an external tenant ID (as it -// appears in OIDC claims) to the internal tenant ID stored on the user object. +// resolveInternalTenantID maps an external tenant ID (as it appears in OIDC claims) to the +// internal tenant ID stored on the user object by calling the gateway's TenantAPI. +// Results are cached for 10 minutes to avoid repeated lookups on every request. // The call is authenticated using the configured service account. func (m accountResolver) resolveInternalTenantID(ctx context.Context, externalTenantID string) (string, error) { + if item := m.tenantIDCache.Get(externalTenantID); item != nil { + return item.Value(), nil + } + gwc, err := m.gatewaySelector.Next() if err != nil { return "", fmt.Errorf("could not get gateway client: %w", err) @@ -314,5 +328,8 @@ func (m accountResolver) resolveInternalTenantID(ctx context.Context, externalTe if resp.GetStatus().GetCode() != rpcpb.Code_CODE_OK { return "", fmt.Errorf("TenantAPI returned status %s: %s", resp.GetStatus().GetCode(), resp.GetStatus().GetMessage()) } - return resp.GetTenant().GetId(), nil + + internalID := resp.GetTenant().GetId() + m.tenantIDCache.Set(externalTenantID, internalID, ttlcache.DefaultTTL) + return internalID, nil }