Skip to content

feature(request-context): implement request-scoped memoization for optimized DB reads#5990

Open
victorvhs017 wants to merge 5 commits intomainfrom
feature/request-memoization
Open

feature(request-context): implement request-scoped memoization for optimized DB reads#5990
victorvhs017 wants to merge 5 commits intomainfrom
feature/request-memoization

Conversation

@victorvhs017
Copy link
Copy Markdown
Contributor

@victorvhs017 victorvhs017 commented Apr 9, 2026

Context

Adds request-scoped memoization so repeated read-only work in a single HTTP request (permissions, project/user/identity lookups) hits the database once instead of many times—especially on batch secret paths.

Before: Each call path could repeat getProjectPermission / getOrgPermission, projectDAL.findById, and actor lookups independently within the same request.

After:

  • Introduces RequestMemoizer (getOrSet with in-flight deduplication) stored on @fastify/request-context as memoizer (wired in app.ts per existing pattern in the branch).
  • Adds requestMemoize(key, fetcher) which uses the memoizer when present and falls back to a direct fetch when there is no request context (e.g. workers).
  • Centralizes memo key strings in src/lib/request-context/memo-keys.ts and request-context string keys in request-context-keys.ts (kept in sync with RequestContextData in fastify.d.ts).
  • Permission service: getOrgPermission wrapped with requestMemoize; getProjectPermission uses memoizer.getOrSet for full-function memoization (key includes actorOrgId) and defers requestContext.set for projectDetails / identityPermissionMetadata until after the memoized load completes. DAL reads use requestMemoize for projectFindById, userFindById, and identityFindById.
  • Replaces raw requestContext.get("…") / set string literals with requestContextKeys in touched files (logger, audit log, SAML/OIDC telemetry, assume-privilege router, permission service).
  • Documents the pattern in backend/CLAUDE.md (Request-Scoped Memoization section).
  • Unit tests in request-memoizer.test.ts for caching, in-flight coalescing, error behavior, and cross-request isolation.

Steps to verify the change

  1. Knex query logging (temporary): Enable SQL logging (e.g. Knex debug against the dev DB) while exercising a hot path that previously repeated lookups (e.g. batch secret read/list for one project).

backend/src/db/instance.ts

image
  1. API call: Issue the same request shape you use in production or staging (same projectId, same auth) and compare number of SELECT statements (or log lines) before vs after—project load, permission resolution, and actor lookups should collapse to a single round-trip per distinct key within one request.

For example:

# Create a machine identity + access token if you don't have one
# Then fetch a single secret:
curl -H "Authorization: Bearer <identity-access-token>" \
  http://localhost:8080/api/v3/secrets/raw/<secretName>?workspaceId=<projectId>&environment=dev
# List all secrets (triggers getProjectPermission once, reused for all):
curl -H "Authorization: Bearer <identity-access-token>" \
  "http://localhost:8080/api/v3/secrets/raw?workspaceId=<projectId>&environment=dev"

Type

  • Fix
  • Feature
  • Improvement
  • Breaking
  • Docs
  • Chore

Checklist

  • Title follows the conventional commit format: type(scope): short description (scope is optional, e.g., fix: prevent crash on sync or fix(api): handle null response).
    Suggested title: perf(backend): request-scoped memoization for permissions and project lookups
  • Tested locally
  • Updated docs (if needed) — backend/CLAUDE.md updated for memoization
  • Updated CLAUDE.md files (if needed)
  • Read the contributing guide

…ized DB reads

- Introduced `RequestMemoizer` to cache results of database calls within a single HTTP request, reducing redundant reads.
- Updated various services to utilize memoization for permission checks and data retrieval, enhancing performance.
- Added new utility functions for generating memoization keys to ensure consistency across the application.
- Refactored request context handling to incorporate memoization seamlessly, improving overall request processing efficiency.
@maidul98
Copy link
Copy Markdown
Collaborator

maidul98 commented Apr 9, 2026

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c0c0b7521b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Victor Hugo dos Santos added 2 commits April 9, 2026 20:23
…nd improve memoization

- Updated permission service to include actorOrgId in memoization keys for more accurate permission checks.
- Refactored permission retrieval logic to utilize memoization, reducing redundant database calls.
- Improved error handling for project ownership and action type validation to ensure proper access control.
- Enhanced request context handling to support new memoization strategies, optimizing performance for permission-related operations.
…ect permissions

- Introduced a new variable to determine the actor type for project permissions, enhancing clarity and maintainability.
- Updated error messages to reflect the new actor type variable, ensuring consistent feedback for permission-related issues.
@victorvhs017 victorvhs017 requested a review from akhilmhdh April 10, 2026 13:01
@linear
Copy link
Copy Markdown

linear bot commented Apr 10, 2026

Copy link
Copy Markdown
Member

@akhilmhdh akhilmhdh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looking good! Some questions

Pending application testing

conditionsMatcher
}

const permissionData = await permissionDAL.getPermission({
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is permission a big hitter? Generally i feel like this only called once per endpoint

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My reasoning is - permission is a big object, so storing it in context may not be desirable

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did some search and we have this pattern mostly on secret v3 endpoints and secrets overview, which would be enough reason to use it. But, I also find it good to have as a safeguard, because it's used a lot and we may miss a duplicated call here and there.

We can track the memory usage after the change, if the footprint rises too much, we remove it from here and fix the places we know directly

@akhilmhdh
Copy link
Copy Markdown
Member

Application testing looking good. Just need to check on the above comments

Victor Hugo dos Santos added 2 commits April 10, 2026 18:37
…textKey enum

- Updated all instances of requestContextKeys to use the new RequestContextKey enum for improved type safety and consistency across the codebase.
- Enhanced readability and maintainability by standardizing the way request context keys are accessed throughout the application.
- Changed the error message in the permission service to use a more descriptive key for the ForbiddenRequestError, enhancing clarity in error reporting when project ownership is violated.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants