From 43a87ff7473b1b68cad3903c93534ff23665ed71 Mon Sep 17 00:00:00 2001 From: Oliver Kieselbach Date: Thu, 25 Jun 2026 20:53:38 +0200 Subject: [PATCH] perf(backend): read-bound delegated config/all to point-reads [Phase 2b follow-up] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses a Codex review finding: the Phase 2b delegated config/all path loaded EVERY tenant config via GetAllConfigurationsAsync() (an O(all tenants) table scan + full-config materialization) and then filtered in memory. Because Phase 2b opened this endpoint to delegated MSP users, a caller scoped to a few tenants could trigger an all-tenants scan per request — response-bounded, not read-bounded. Now point-read ONLY the caller's managed tenants via the non-creating TryGetConfigurationAsync (cached; never writes a phantom default row), dropping ids with no config row (exists==false). A delegated MSP scoped to k tenants does k point reads instead of scanning n. The response is still filtered+redacted as defense in depth; the set fed to it is now itself bounded. Full Functions suite green (2464). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../Config/GetAllTenantConfigurationsFunction.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Backend/AutopilotMonitor.Functions/Functions/Config/GetAllTenantConfigurationsFunction.cs b/src/Backend/AutopilotMonitor.Functions/Functions/Config/GetAllTenantConfigurationsFunction.cs index 4a862789..4558a043 100644 --- a/src/Backend/AutopilotMonitor.Functions/Functions/Config/GetAllTenantConfigurationsFunction.cs +++ b/src/Backend/AutopilotMonitor.Functions/Functions/Config/GetAllTenantConfigurationsFunction.cs @@ -52,10 +52,13 @@ namespace AutopilotMonitor.Functions.Functions.Config var requestCtx = req.GetRequestContext(); if (requestCtx.AllowedTenantIds != null) { - var allowed = new System.Collections.Generic.HashSet( - requestCtx.AllowedTenantIds, StringComparer.OrdinalIgnoreCase); - var all = await _configService.GetAllConfigurationsAsync(); - var subset = all.Where(c => allowed.Contains(c.TenantId)).ToList(); + // READ-bounded, not just response-bounded: point-read ONLY the caller's managed tenants + // instead of scanning every tenant config and filtering in memory. A delegated MSP scoped + // to k tenants triggers k point reads (cached, non-creating — TryGetConfigurationAsync + // never writes a default row). exists==false drops any id with no config row. + var reads = await Task.WhenAll(requestCtx.AllowedTenantIds.Select(async tid => + await _configService.TryGetConfigurationAsync(tid))); + var subset = reads.Where(r => r.exists).Select(r => r.config).ToList(); _logger.LogInformation("GetAllTenantConfigurations (delegated subset, {Count} tenants) by {User}", subset.Count, userIdentifier);