Skip to content

[Repo Assist] perf: push winners count limit to DB query and cache prompt files #499

@github-actions

Description

@github-actions

🤖 This PR was created by Repo Assist, an automated AI assistant.

Summary

Two targeted performance improvements identified during a routine codebase review:

1. WinnersService: limit applied at the database layer

Before: all winners rows were fetched into memory, then .Take(count) applied in-process.
After: count is passed into the query, so the LIMIT clause is pushed to the database.

Additionally, .AsNoTracking() is added for the read-only query — EF Core won't create change-tracking proxies, reducing memory allocations and GC pressure per request.

2. prompt_loader.py: cache prompt files with lru_cache

load_prompt reads and parses a YAML file on every call. For a production chatbot, the same prompt files are loaded on every request. Adding @lru_cache(maxsize=10) means the file I/O and YAML parsing happen once per unique (prompt_name, prompts_dir) pair.

Trade-offs

  • lru_cache on load_prompt means prompt file changes require a service restart to take effect. Acceptable for a Docker/Aspire deployment model where restarts are cheap.
  • AsNoTracking() only applies to the read path — no write operations affected.

Test Status

  • .NET build: changes are backward-compatible (method signatures changed but all callers are within the same class)
  • Python: lru_cache is stdlib — no new dependencies
  • Infrastructure: this is a read-service, no migrations needed

Generated by Repo Assist ·

To install this agentic workflow, run

gh aw add githubnext/agentics/workflows/repo-assist.md@346204513ecfa08b81566450d7d599556807389f

Note

This was originally intended as a pull request, but GitHub Actions is not permitted to create or approve pull requests in this repository.
The changes have been pushed to branch repo-assist/improve-perf-asnotracking-lru-cache-1f6995db1c2d51e7-cec4b5efd1592c5e.

Click here to create the pull request

To fix the permissions issue, go to SettingsActionsGeneral and enable Allow GitHub Actions to create and approve pull requests.

Show patch preview (96 of 96 lines)
From 3fb81018d6658d2085f72c976b958ba375bf3e8c Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Wed, 11 Mar 2026 13:23:09 +0000
Subject: [PATCH] perf: push Take/count to DB query and cache prompt files

- WinnersService: fetch winners-count flag first, then pass the limit
  directly to GetAllDatabaseWinnersAsync so EF Core applies Take() at
  the SQL layer instead of loading all rows then discarding most of them
- WinnersService: add .AsNoTracking() on the read-only DB query to
  avoid unnecessary change-tracking overhead
- prompt_loader: add @lru_cache(maxsize=10) to load_prompt so each
  .prompt.yml file is read from disk and parsed only once per process
  lifetime instead of on every chat request

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
 src/Garage.ApiService/Services/WinnersService.cs | 16 ++++++++--------
 src/Garage.ChatService/prompt_loader.py          |  5 +++++
 2 files changed, 13 insertions(+), 8 deletions(-)

diff --git a/src/Garage.ApiService/Services/WinnersService.cs b/src/Garage.ApiService/Services/WinnersService.cs
index e1862da..61485e3 100644
--- a/src/Garage.ApiService/Services/WinnersService.cs
+++ b/src/Garage.ApiService/Services/WinnersService.cs
@@ -20,21 +20,21 @@ public class WinnersService(
             .SetTargetingKey(Guid.NewGuid().ToString())
             .Build();
 
-        var winners = await featureClient.GetBooleanValueAsync("enable-database-winners", false, evaluationContext)
-            ? await GetAllDatabaseWinnersAsync()
-            : await GetAllJsonWinnersAsync();
-
         var count = await featureClient.GetIntegerDetailsAsync("winners-count", 5, evaluationContext);
 
-        return winners.Take(count.Value);
+        return await featureClient.GetBooleanValueAsync("enable-database-winners", false, evaluationContext)
+            ? await GetAllDatabaseWinnersAsync(count.Value)
+            : await GetAllJsonWinnersAsync(count.Value)
... (truncated)

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions