Skip to content

fix: improve caching function#1805

Merged
baktun14 merged 8 commits intomainfrom
fix/api-memoize
Aug 14, 2025
Merged

fix: improve caching function#1805
baktun14 merged 8 commits intomainfrom
fix/api-memoize

Conversation

@baktun14
Copy link
Contributor

@baktun14 baktun14 commented Aug 13, 2025

#1803

Summary by CodeRabbit

  • Refactor

    • Caching now follows stale-while-revalidate: returns cached data immediately, refreshes expired entries in background, and prevents duplicate refreshes under concurrency.
    • Simplified public cache API and improved cache retrieval typing; added utilities for listing and clearing cache keys.
  • Bug Fixes

    • Avoid parsing non-string JSON values from cache to prevent runtime errors.
  • Tests

    • Added comprehensive unit tests for caching/memoization; test setup includes cache isolation and a clearCache helper.
    • Functional tests: targeted cache clears, persistent external API mocks, deterministic timers, and extended timeout.

@baktun14 baktun14 requested a review from a team as a code owner August 13, 2025 15:30
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 13, 2025

Walkthrough

Refactors cacheResponse to a stale-while-revalidate model (drops keepData param), makes MemoryCacheEngine.getFromCache generic (returns T | false), adds cache control helpers, tightens JSON parsing guard in middleware, introduces unit tests for caching and Memoize, and improves test cache isolation and stability.

Changes

Cohort / File(s) Summary
Caching core
apps/api/src/caching/helpers.ts, apps/api/src/caching/memoryCacheEngine.ts
cacheResponse refactored: removed keepData param; returns cached data immediately when present; computes expiry only when data exists; starts non-blocking background refresh on expiry; deduplicates refreshes via pendingRequests migrated to Map; stores refreshed data with timestamp (no TTL); propagates errors only on cold misses. MemoryCacheEngine.getFromCache made generic and now returns `T
Caching tests
apps/api/src/caching/helpers.spec.ts
New comprehensive unit tests for cacheResponse (hit, stale-with-background-refresh, concurrent dedupe, cold miss, background failure handling, error propagation) and Memoize key generation behaviors; includes setup helper to clear mocks and purge cache between tests.
Controller adaptation
apps/api/src/provider/controllers/provider/provider.controller.ts
Updated getProviderList to call new cacheResponse signature (removed fourth arg); call condensed and return cast preserved.
Test setup & functional tests
apps/api/test/setup-functional-tests.ts, apps/api/test/functional/lease-flow.spec.ts, apps/api/test/functional/market-data.spec.ts, apps/api/test/functional/provider-graph-data.spec.ts, apps/api/test/functional/deployment-setting.spec.ts
Added cacheEngine imports and clearCache helper; added global and per-test cache clearing hooks; replaced ad-hoc cache invalidation in lease-flow with clearCache("WalletController#getBalances"); made external HTTP mocks persistent and cleaned up in market-data; applied Jest fake timers in provider-graph-data for deterministic snapshots; increased jest timeout in deployment-setting.
Middleware JSON guard
apps/api/src/middlewares/userMiddleware.ts
Adds type guard so JSON.parse runs only when cached value is a string (format === "json" && typeof result === "string"), avoiding parse errors for non-string cached values.

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant Cache
  participant Helpers as cacheResponse
  participant Service as refreshRequest

  Note over Caller,Helpers: Stale-while-revalidate (cached & expired)
  Caller->>Helpers: cacheResponse(seconds, key, refreshRequest)
  Helpers->>Cache: getFromCache(key)
  Cache-->>Helpers: { data, ts (expired) }
  Helpers-->>Caller: return cached data immediately
  alt no pending refresh for key
    Helpers->>Service: background refresh (async)
    Service-->>Helpers: fresh data
    Helpers->>Cache: storeInCache(key, { data, ts: now })
  else pending refresh exists
    Helpers-->>Helpers: skip duplicate refresh
  end
Loading
sequenceDiagram
  participant Caller
  participant Cache
  participant Helpers as cacheResponse
  participant Service as refreshRequest

  Note over Caller,Helpers: Cold miss (no cached data)
  Caller->>Helpers: cacheResponse(seconds, key, refreshRequest)
  Helpers->>Cache: getFromCache(key)
  Cache-->>Helpers: miss (false)
  Helpers->>Service: refreshRequest()
  Service-->>Helpers: data
  Helpers->>Cache: storeInCache(key, { data, ts: now })
  Helpers-->>Caller: return fresh data
Loading
sequenceDiagram
  participant CallerA as Caller A
  participant CallerB as Caller B
  participant Helpers as cacheResponse
  participant Service as refreshRequest

  Note over Helpers: Prevent duplicate refreshes
  CallerA->>Helpers: cacheResponse(stale cached)
  Helpers-->>CallerA: return cached data
  Helpers->>Service: start background refresh
  CallerB->>Helpers: cacheResponse(stale cached)
  Helpers-->>CallerB: return cached data
  Helpers-->>Helpers: detect pending refresh, do not start another
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~30 minutes

Suggested reviewers

  • stalniy
  • ygrishajev
  • devalpatel67

Poem

I tuck a carrot, stash a key,
Return the old, then fetch for thee.
One hop refresh, no double chase,
Timestamps set, cache finds its place.
Tests patrol the burrow—clean and neat 🥕🐇

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/api-memoize

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (4)
apps/api/src/caching/helpers.ts (1)

84-97: Consider deduplicating concurrent initial requests.

When no cached data exists and multiple concurrent requests arrive for the same key, each will trigger a separate refresh. This differs from the background refresh behavior which properly deduplicates.

Consider checking pendingRequests[key] before starting a new request:

   // If no cached data exists, make the request and wait for it
   logger.debug(`No cached data, making new request for key: ${key}`);
+  
+  // If there's already a pending request for this key, wait for it
+  if (key in pendingRequests) {
+    logger.debug(`Joining existing request for key: ${key}`);
+    return (await pendingRequests[key]) as T;
+  }
+  
   pendingRequests[key] = refreshRequest()
apps/api/src/provider/controllers/provider/provider.controller.ts (1)

29-31: Unnecessary type casting can be avoided.

The double type casting as unknown as Promise<ProviderListResponse> suggests a type mismatch. Since cacheResponse is generic and getProviderList already returns the correct type, the casting shouldn't be necessary.

-    return cacheResponse(60, scope === "trial" ? cacheKeys.getTrialProviderList : cacheKeys.getProviderList, () =>
-      this.providerService.getProviderList({ trial: scope === "trial" })
-    ) as unknown as Promise<ProviderListResponse>;
+    return cacheResponse<ProviderListResponse>(
+      60,
+      scope === "trial" ? cacheKeys.getTrialProviderList : cacheKeys.getProviderList,
+      () => this.providerService.getProviderList({ trial: scope === "trial" })
+    );
apps/api/src/caching/helpers.spec.ts (2)

118-130: Test doesn't verify the actual cache key generation.

While the test checks that the decorator creates a function, it doesn't verify that the cache key is correctly generated as TestClass#testMethod.

Consider enhancing the test to verify the actual cache key:

     it("should create cache key from class and method name", () => {
+      const mockCacheResponse = jest.fn();
+      jest.doMock("./helpers", () => ({
+        ...jest.requireActual("./helpers"),
+        cacheResponse: mockCacheResponse
+      }));
+      
       class TestClass {
         @Memoize()
         async testMethod() {
           return "test";
         }
       }
 
-      const descriptor = Object.getOwnPropertyDescriptor(TestClass.prototype, "testMethod");
-
-      expect(descriptor?.value).toBeDefined();
-      expect(typeof descriptor?.value).toBe("function");
+      const instance = new TestClass();
+      instance.testMethod();
+      
+      expect(mockCacheResponse).toHaveBeenCalledWith(
+        120,
+        "TestClass#testMethod",
+        expect.any(Function)
+      );
     });

145-156: Test doesn't verify that string arguments are actually included in the cache key.

The test only checks that the function exists but doesn't verify the cache key format with arguments.

Consider testing the actual cache key generation with arguments:

     it("should include string arguments in cache key", () => {
+      const mockCacheResponse = jest.fn();
+      jest.doMock("./helpers", () => ({
+        ...jest.requireActual("./helpers"),
+        cacheResponse: mockCacheResponse
+      }));
+      
       class TestClass {
         @Memoize()
         async testMethod(_arg1: string, _arg2: string) {
           return "test";
         }
       }
 
-      const descriptor = Object.getOwnPropertyDescriptor(TestClass.prototype, "testMethod");
-
-      expect(descriptor?.value).toBeDefined();
+      const instance = new TestClass();
+      instance.testMethod("foo", "bar");
+      
+      expect(mockCacheResponse).toHaveBeenCalledWith(
+        120,
+        "TestClass#testMethod#foo#bar",
+        expect.any(Function)
+      );
     });
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 844ea53 and 0cb2bbc.

📒 Files selected for processing (3)
  • apps/api/src/caching/helpers.spec.ts (1 hunks)
  • apps/api/src/caching/helpers.ts (1 hunks)
  • apps/api/src/provider/controllers/provider/provider.controller.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/general.mdc)

Never use type any or cast to type any. Always define the proper TypeScript types.

Files:

  • apps/api/src/provider/controllers/provider/provider.controller.ts
  • apps/api/src/caching/helpers.spec.ts
  • apps/api/src/caching/helpers.ts
**/*.{js,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/general.mdc)

**/*.{js,ts,tsx}: Never use deprecated methods from libraries.
Don't add unnecessary comments to the code

Files:

  • apps/api/src/provider/controllers/provider/provider.controller.ts
  • apps/api/src/caching/helpers.spec.ts
  • apps/api/src/caching/helpers.ts
**/*.spec.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/no-jest-mock.mdc)

Don't use jest.mock() to mock dependencies in test files. Instead, use jest-mock-extended to create mocks and pass mocks as dependencies to the service under test.

**/*.spec.{ts,tsx}: Use setup function instead of beforeEach in test files
setup function must be at the bottom of the root describe block in test files
setup function creates an object under test and returns it
setup function should accept a single parameter with inline type definition
Don't use shared state in setup function
Don't specify return type of setup function

Files:

  • apps/api/src/caching/helpers.spec.ts
🧬 Code Graph Analysis (2)
apps/api/src/provider/controllers/provider/provider.controller.ts (1)
apps/api/src/caching/helpers.ts (2)
  • cacheResponse (40-100)
  • cacheKeys (102-116)
apps/api/src/caching/helpers.spec.ts (1)
apps/api/src/caching/helpers.ts (3)
  • cacheEngine (8-8)
  • cacheResponse (40-100)
  • Memoize (22-38)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: validate / validate-app
  • GitHub Check: test-build
🔇 Additional comments (2)
apps/api/src/caching/helpers.ts (2)

36-36: LGTM! Clean API update.

The removal of the keepData parameter simplifies the API while maintaining backward compatibility through the decorator pattern.


40-51: Solid implementation of stale-while-revalidate pattern.

The logic correctly handles the cache existence check and expiration calculation. The pattern of returning stale data immediately while refreshing in the background provides excellent user experience.

@codecov
Copy link

codecov bot commented Aug 13, 2025

Codecov Report

❌ Patch coverage is 96.22642% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 42.61%. Comparing base (844ea53) to head (879701c).
⚠️ Report is 5 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
apps/api/src/caching/memoryCacheEngine.ts 81.81% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1805      +/-   ##
==========================================
+ Coverage   42.47%   42.61%   +0.14%     
==========================================
  Files         933      938       +5     
  Lines       26293    26443     +150     
  Branches     6979     6927      -52     
==========================================
+ Hits        11167    11270     +103     
- Misses      14760    14892     +132     
+ Partials      366      281      -85     
Flag Coverage Δ
api 81.18% <96.22%> (+<0.01%) ⬆️
deploy-web 20.77% <ø> (-0.01%) ⬇️
log-collector 84.74% <ø> (ø)
notifications 87.59% <ø> (ø)
provider-console 81.48% <ø> (ø)
provider-proxy 84.75% <ø> (ø)
Files with missing lines Coverage Δ
apps/api/src/caching/helpers.ts 100.00% <100.00%> (+2.50%) ⬆️
apps/api/src/middlewares/userMiddleware.ts 78.94% <100.00%> (-10.53%) ⬇️
...ovider/controllers/provider/provider.controller.ts 81.81% <100.00%> (-0.80%) ⬇️
apps/api/src/caching/memoryCacheEngine.ts 81.81% <81.81%> (+12.58%) ⬆️

... and 72 files with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🔭 Outside diff range comments (2)
apps/api/src/caching/helpers.ts (2)

28-36: Fix key generation edge cases and drop unnecessary await in Memoize.

  • filter(Boolean) removes empty string values, causing collisions between "" and omitted args.
  • Joining raw strings with "#" can collide with args that themselves contain "#".
  • return await is unnecessary here; returning the promise is sufficient.

Apply this diff:

-    const argsKey =
-      args.length > 0
-        ? `${cacheKey}#${args
-            .map(arg => (typeof arg === "string" ? arg : null))
-            .filter(Boolean)
-            .join("#")}`
-        : cacheKey;
-
-    return await cacheResponse(options?.ttlInSeconds || 60 * 2, argsKey, originalMethod.bind(this, ...args));
+    const argsKey =
+      args.length > 0
+        ? `${cacheKey}#${args
+            .map(arg => (typeof arg === "string" ? encodeURIComponent(arg) : null))
+            .filter((arg): arg is string => arg !== null)
+            .join("#")}`
+        : cacheKey;
+
+    return cacheResponse(options?.ttlInSeconds || 60 * 2, argsKey, originalMethod.bind(this, ...args));

16-20: Remove unused keepData from MemoizeOptions

The keepData field is only declared in apps/api/src/caching/helpers.ts and isn’t referenced anywhere else. To avoid confusion, it should be removed.

  • Location: apps/api/src/caching/helpers.ts:16-20

Recommended change:

 interface MemoizeOptions {
-  keepData?: boolean;
   ttlInSeconds?: number;
   key?: string;
 }
🧹 Nitpick comments (1)
apps/api/src/caching/helpers.ts (1)

97-99: Redundant catch that rethrows; let the rejection propagate.

The explicit catch that rethrows is unnecessary; finally still executes without it.

Apply this diff:

-    .catch(err => {
-      throw err;
-    })
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0cb2bbc and 856e8b3.

📒 Files selected for processing (2)
  • apps/api/src/caching/helpers.spec.ts (1 hunks)
  • apps/api/src/caching/helpers.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/api/src/caching/helpers.spec.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/general.mdc)

Never use type any or cast to type any. Always define the proper TypeScript types.

Files:

  • apps/api/src/caching/helpers.ts
**/*.{js,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/general.mdc)

**/*.{js,ts,tsx}: Never use deprecated methods from libraries.
Don't add unnecessary comments to the code

Files:

  • apps/api/src/caching/helpers.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: validate / validate-app
  • GitHub Check: test-build
🔇 Additional comments (2)
apps/api/src/caching/helpers.ts (2)

47-51: Confirm intended TTL boundary (strict vs. lenient).

Using timeDiff > seconds treats values at exactly the TTL boundary as still fresh. Many TTLs expire at or beyond the threshold, i.e., >=.

If the intended behavior is to expire at the boundary, switch to >=:

-    isExpired = timeDiff > seconds;
+    isExpired = timeDiff >= seconds;

Please confirm which semantic you want (strict vs. lenient) and whether tests align with it.


61-79: Background refresh error handling and “no-undefined” cache writes — LGTM.

  • Returning cached data on background refresh errors avoids poisoning the cache.
  • Guarding writes with data !== undefined prevents overwriting the cache with undefined.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🔭 Outside diff range comments (1)
apps/api/src/caching/helpers.ts (1)

25-35: Prevent cache key collisions in Memoize (drops empty strings; ignores numeric/boolean args).

The current key builder:

  • Filters out empty strings due to filter(Boolean), collapsing ('', 'foo') into ('foo').
  • Ignores non-string primitives (numbers/booleans), collapsing distinct calls that differ only in these args.
    This can return incorrect cached results.

Refine the key construction to include string/number/boolean values and preserve empty strings.

Apply this diff:

-    const argsKey =
-      args.length > 0
-        ? `${cacheKey}#${args
-            .map(arg => (typeof arg === "string" ? arg : null))
-            .filter(Boolean)
-            .join("#")}`
-        : cacheKey;
+    const argsKey =
+      args.length > 0
+        ? `${cacheKey}#${args
+            .map(arg => {
+              const t = typeof arg;
+              return t === "string" || t === "number" || t === "boolean" ? String(arg) : null;
+            })
+            .filter((v): v is string => v !== null)
+            .join("#")}`
+        : cacheKey;
🧹 Nitpick comments (4)
apps/api/src/caching/helpers.ts (4)

16-20: Remove unused keepData from MemoizeOptions.

The API dropped keepData; leaving it in the options type is misleading.

Apply this diff:

 interface MemoizeOptions {
-  keepData?: boolean;
   ttlInSeconds?: number;
   key?: string;
 }

36-36: Avoid redundant await in return.

No try/catch here; return await just adds an extra microtask.

Apply this diff:

-    return await cacheResponse(options?.ttlInSeconds || 60 * 2, argsKey, originalMethod.bind(this, ...args));
+    return cacheResponse(options?.ttlInSeconds || 60 * 2, argsKey, originalMethod.bind(this, ...args));

47-51: Simplify expiry calculation to avoid Math.abs.

Passing current time as the first arg yields a non-negative delta; no need for Math.abs.

Apply this diff:

-    const timeDiff = Math.abs(differenceInSeconds(cachedObject.date, new Date()));
+    const timeDiff = differenceInSeconds(new Date(), cachedObject.date);

9-9: Prefer Map for pendingRequests for robustness and clarity.

Using a plain object risks prototype key collisions (e.g., "proto"), requires in checks, and string-only keys. Map better expresses intent.

Apply this diff:

-const pendingRequests: { [key: string]: Promise<unknown> } = {};
+const pendingRequests = new Map<string, Promise<unknown>>();

And adjust usages:

-    if (isExpired && !(key in pendingRequests)) {
+    if (isExpired && !pendingRequests.has(key)) {
       logger.debug(`Starting background refresh for key: ${key}`);
-      pendingRequests[key] = refreshRequest()
+      pendingRequests.set(key, refreshRequest()
         .then(data => {
           logger.debug(`Background refresh completed for key: ${key}`);
           if (data !== undefined) {
             cacheEngine.storeInCache(key, { date: new Date(), data: data });
           }
           return data;
         })
         .catch(err => {
           logger.error({ message: `Error making background cache refresh`, error: err });
           return cachedObject.data;
         })
         .finally(() => {
-          delete pendingRequests[key];
+          pendingRequests.delete(key);
           logger.debug(`Removed pending request for key: ${key}`);
-        });
+        }));
     }
-  pendingRequests[key] =
-    pendingRequests[key] ||
-    refreshRequest()
+  pendingRequests.set(
+    key,
+    pendingRequests.get(key) ||
+      refreshRequest()
         .then(data => {
           logger.debug(`New request completed for key: ${key}`);
           if (data !== undefined) {
             cacheEngine.storeInCache(key, { date: new Date(), data: data });
           }
           return data;
         })
         .catch(err => {
           throw err;
         })
         .finally(() => {
-          delete pendingRequests[key];
+          pendingRequests.delete(key);
           logger.debug(`Removed pending request for key: ${key}`);
-        });
+        })
+  );
 
-  return (await pendingRequests[key]) as T;
+  return (await pendingRequests.get(key)!) as T;

Also applies to: 58-80, 90-110

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 856e8b3 and 68e4e5c.

📒 Files selected for processing (2)
  • apps/api/src/caching/helpers.spec.ts (1 hunks)
  • apps/api/src/caching/helpers.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/api/src/caching/helpers.spec.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/general.mdc)

Never use type any or cast to type any. Always define the proper TypeScript types.

Files:

  • apps/api/src/caching/helpers.ts
**/*.{js,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/general.mdc)

**/*.{js,ts,tsx}: Never use deprecated methods from libraries.
Don't add unnecessary comments to the code

Files:

  • apps/api/src/caching/helpers.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: test-build
  • GitHub Check: test-build
  • GitHub Check: test-build
  • GitHub Check: validate / validate-app
  • GitHub Check: test-build
  • GitHub Check: validate / validate-app
  • GitHub Check: test-build
  • GitHub Check: validate / validate-app
  • GitHub Check: validate / validate-app
  • GitHub Check: test-build
  • GitHub Check: test-build
🔇 Additional comments (3)
apps/api/src/caching/helpers.ts (3)

53-84: Stale-while-revalidate path looks solid.

  • Returns cached data immediately.
  • Starts a guarded background refresh.
  • Only overwrites cache on defined data.
  • Cleans up pendingRequests in finally.

89-109: Cold-start concurrency guard is correct and prevents duplicate refreshes.

Using pendingRequests[key] = pendingRequests[key] || refreshRequest() ensures only one in-flight request per key. Cleanup in finally is correct; callers await the shared promise.


40-42: Cache engine API is compatible – no changes needed

Verified that MemoryCacheEngine.getFromCache returns false on cache misses and that storeInCache(key, data, duration?) accepts two-argument calls (with duration optional). All existing invocations match this signature.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (5)
apps/api/src/caching/helpers.ts (5)

46-50: TTL boundary condition: use >= instead of > for expiry

Using > seconds means entries live one extra second beyond TTL. Recommend >= for conventional TTL semantics.

Apply:

-  let isExpired = true;
+  let isExpired = false;
   if (hasCachedData) {
     const timeDiff = differenceInSeconds(new Date(), cachedObject.date);
-    isExpired = timeDiff > seconds;
+    isExpired = timeDiff >= seconds;
   }

90-114: Cold-start: remove redundant catch that just rethrows

The catch at Line 102 rethrows without adding context or action. It’s redundant and can be dropped to keep the promise chain simpler.

Apply:

-    pendingRequest = refreshRequest()
+    pendingRequest = refreshRequest()
       .then(data => {
         logger.debug(`New request completed for key: ${key}`);
         // Only store in cache if we have valid data
         if (data !== undefined) {
           cacheEngine.storeInCache(key, { date: new Date(), data: data });
         }
         return data;
       })
-      .catch(err => {
-        throw err;
-      })
       .finally(() => {
         pendingRequests.delete(key);
         logger.debug(`Removed pending request for key: ${key}`);
       });
 
-    pendingRequests.set(key, pendingRequest);
+    pendingRequests.set(key, pendingRequest);
   }
 
-  return (await pendingRequest) as T;
+  return (await pendingRequest) as T;

Additionally, you can slightly improve typing here to avoid the final cast by narrowing the local variable:

-  let pendingRequest = pendingRequests.get(key);
+  let pendingRequest = pendingRequests.get(key) as Promise<T> | undefined;

Then the final line can be:

-  return (await pendingRequest) as T;
+  return await pendingRequest;

27-33: Memoize key generation ignores non-string args — potential collisions

Only string args are considered; calls with different non-string args will collide to the same key. Consider supporting numbers/booleans and a safe serialization for simple objects, or allow a custom argKeyFn in options.

Minimal improvement that avoids JSON-heavy keys but handles common primitives:

-    const argsKey =
-      args.length > 0
-        ? `${cacheKey}#${args
-            .map(arg => (typeof arg === "string" ? arg : null))
-            .filter(Boolean)
-            .join("#")}`
-        : cacheKey;
+    const argsKey =
+      args.length > 0
+        ? `${cacheKey}#${args
+            .map(arg => {
+              if (typeof arg === "string" || typeof arg === "number" || typeof arg === "boolean") return String(arg);
+              return null;
+            })
+            .filter(Boolean)
+            .join("#")}`
+        : cacheKey;

If you want full control, extend MemoizeOptions with an argKeyFn: (args: unknown[]) => string and use it when provided.


40-44: Avoid the cast by making cache engine generic

The getFromCache result is cast to CachedObject | false. If MemoryCacheEngine.getFromCache is changed to be generic, this cast becomes unnecessary.

Proposed change in memoryCacheEngine.ts (not this file):

// before
getFromCache(key: string): unknown | false

// after
getFromCache<T>(key: string): { date: Date; data: T } | false

Then here you can write:

const cachedObject = cacheEngine.getFromCache<T>(key);

9-9: pendingRequests typing is fine; minor ergonomic improvement available

Keeping Map<string, Promise> at module scope is appropriate since T varies per key. Locally casting the retrieved promise to Promise (as shown in the cold-path comment) helps avoid the final return cast and keeps types tighter within the function.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 68e4e5c and 6edcb09.

📒 Files selected for processing (1)
  • apps/api/src/caching/helpers.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/general.mdc)

Never use type any or cast to type any. Always define the proper TypeScript types.

Files:

  • apps/api/src/caching/helpers.ts
**/*.{js,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/general.mdc)

**/*.{js,ts,tsx}: Never use deprecated methods from libraries.
Don't add unnecessary comments to the code

Files:

  • apps/api/src/caching/helpers.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: validate / validate-app
  • GitHub Check: validate / validate-app
🔇 Additional comments (3)
apps/api/src/caching/helpers.ts (3)

56-81: Stale-while-revalidate path and in-flight dedupe look solid

Returns stale data immediately, dedupes refreshes with pendingRequests, avoids clobbering cache with undefined, and cleans up in finally. Good work.


65-67: Confirm storing null vs undefined semantics

You guard against undefined before storing; null will be stored. Is null intended as a valid “no data” marker for callers? If not, also guard against null to avoid overwriting cache with an empty payload.

If null should not be stored, apply:

-          if (data !== undefined) {
+          if (data !== undefined && data !== null) {
             cacheEngine.storeInCache(key, { date: new Date(), data: data });
           }

And similarly in the cold path store.

Also applies to: 97-99


39-41: Breaking API change addressed: cacheResponse call sites updated

– No instances of cacheResponse with four arguments found
– No references to the removed keepData parameter detected

All call sites now use the new three-parameter signature.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (4)
apps/api/test/setup-functional-tests.ts (2)

10-10: Global cache cleanup before setup and after teardown looks correct.

This avoids test bleed-through across suites. Minor improvement: consider wrapping teardown in try/finally to guarantee cache clearing even if teardown throws.

Apply this diff to make cleanup resilient:

 afterAll(async () => {
-  await dbService.teardown();
-  cacheEngine.clearAllKeyInCache();
+  try {
+    await dbService.teardown();
+  } finally {
+    cacheEngine.clearAllKeyInCache();
+  }
 }, 10000);

Also applies to: 16-16


19-21: Per-test clear in both beforeEach and afterEach is redundant.

Clearing in afterEach is sufficient (it runs even when tests fail), and avoids duplicate work. If you prefer belt-and-suspenders, keep as-is; otherwise, drop one to reduce overhead.

Here’s a minimal change retaining afterEach only:

-beforeEach(() => {
-  cacheEngine.clearAllKeyInCache();
-});
-
 afterEach(() => {
   cacheEngine.clearAllKeyInCache();
 });

Also applies to: 23-25

apps/api/src/caching/memoryCacheEngine.ts (2)

8-8: Consider returning T | undefined instead of using a boolean sentinel.

A boolean sentinel complicates typing and prevents caching boolean false distinctly. Returning undefined on miss is idiomatic and plays well with optional chaining and nullish coalescing. This would require small call-site adjustments.

Proposed signature:

- getFromCache<T>(key: string): T | false {
+ getFromCache<T>(key: string): T | undefined {
   const cachedBody = mcache.get(key);
-  if (cachedBody !== null && cachedBody !== undefined) {
-    return cachedBody as T;
-  }
-  return false;
+  return cachedBody === null || cachedBody === undefined ? undefined : (cachedBody as T);
 }

If you want, I can follow through with a codemod to update call sites that rely on === false.


4-13: Tighten JSDoc to reflect actual types and behavior.

Docs say “memcache” and use {*} which isn’t helpful. Align comments with the typed signature and nullish behavior.

   /**
-   * Used to retrieve data from memcache
-   * @param {*} key
+   * Retrieve a cached value.
+   * @param {string} key Cache key
+   * @returns {T|false} Cached value when present, otherwise false
    */
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6edcb09 and 67c037e.

📒 Files selected for processing (3)
  • apps/api/src/caching/helpers.ts (2 hunks)
  • apps/api/src/caching/memoryCacheEngine.ts (1 hunks)
  • apps/api/test/setup-functional-tests.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/api/src/caching/helpers.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/general.mdc)

Never use type any or cast to type any. Always define the proper TypeScript types.

Files:

  • apps/api/test/setup-functional-tests.ts
  • apps/api/src/caching/memoryCacheEngine.ts
**/*.{js,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/general.mdc)

**/*.{js,ts,tsx}: Never use deprecated methods from libraries.
Don't add unnecessary comments to the code

Files:

  • apps/api/test/setup-functional-tests.ts
  • apps/api/src/caching/memoryCacheEngine.ts
🧠 Learnings (2)
📚 Learning: 2025-07-21T08:25:07.474Z
Learnt from: CR
PR: akash-network/console#0
File: .cursor/rules/setup-instead-of-before-each.mdc:0-0
Timestamp: 2025-07-21T08:25:07.474Z
Learning: Applies to **/*.spec.{ts,tsx} : Use `setup` function instead of `beforeEach` in test files

Applied to files:

  • apps/api/test/setup-functional-tests.ts
📚 Learning: 2025-07-21T08:25:07.474Z
Learnt from: CR
PR: akash-network/console#0
File: .cursor/rules/setup-instead-of-before-each.mdc:0-0
Timestamp: 2025-07-21T08:25:07.474Z
Learning: Applies to **/*.spec.{ts,tsx} : `setup` function creates an object under test and returns it

Applied to files:

  • apps/api/test/setup-functional-tests.ts
🧬 Code Graph Analysis (1)
apps/api/test/setup-functional-tests.ts (1)
apps/api/src/caching/helpers.ts (1)
  • cacheEngine (8-8)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: validate / validate-app
  • GitHub Check: test-build
  • GitHub Check: validate / validate-app
  • GitHub Check: validate / validate-app
  • GitHub Check: test-build
  • GitHub Check: test-build
  • GitHub Check: test-build
  • GitHub Check: test-build
  • GitHub Check: validate / validate-app
  • GitHub Check: test-build
  • GitHub Check: test-build
🔇 Additional comments (1)
apps/api/test/setup-functional-tests.ts (1)

3-3: Good call centralizing cache control via the singleton.

Importing cacheEngine from helpers ensures a single in-process cache is cleared consistently across tests.

Comment on lines 9 to 13
const cachedBody = mcache.get(key);
if (cachedBody) {
return cachedBody;
return cachedBody as T;
}
return false;
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Fix falsy-value bug in cache hit detection.

Using a truthiness check causes valid cached values like 0, "" or false to be treated as a cache miss. Use a nullish check instead.

Apply this diff:

   getFromCache<T>(key: string): T | false {
     const cachedBody = mcache.get(key);
-    if (cachedBody) {
+    if (cachedBody !== null && cachedBody !== undefined) {
       return cachedBody as T;
     }
     return false;
   }

To confirm no callers intentionally store falsy primitives (which would still be fine after this fix, but worth validating expectations), run:


🏁 Script executed:

#!/bin/bash
# Find storeInCache calls and flag obviously falsy literals
rg -n --no-heading $'storeInCache\\s*\\(' -A 2 | sed -n '1,200p'

# Spot calls explicitly passing false/0/""/null as data
rg -n --no-heading $'storeInCache\\s*\\([^,]+,\\s*(false|0|""|\'\'|null)\\b' -A 0

Length of output: 2071


Fix falsy-value bug in cache hit detection

This change ensures that valid cached values such as 0, "", or false aren’t treated as cache misses. We’ve checked all storeInCache calls and found no cases where primitives like false, 0, "", or null are intentionally stored, so this update is safe.

Apply this diff in apps/api/src/caching/memoryCacheEngine.ts:

   getFromCache<T>(key: string): T | false {
     const cachedBody = mcache.get(key);
-    if (cachedBody) {
+    if (cachedBody !== null && cachedBody !== undefined) {
       return cachedBody as T;
     }
     return false;
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const cachedBody = mcache.get(key);
if (cachedBody) {
return cachedBody;
return cachedBody as T;
}
return false;
const cachedBody = mcache.get(key);
if (cachedBody !== null && cachedBody !== undefined) {
return cachedBody as T;
}
return false;
🤖 Prompt for AI Agents
In apps/api/src/caching/memoryCacheEngine.ts around lines 9 to 13, the current
cache hit test uses a truthy check (if (cachedBody)) which treats valid cached
values like 0, "", or false as misses; change the condition to an explicit
undefined check (e.g., if (cachedBody !== undefined)) so any stored value
(including falsy primitives) is returned, otherwise return false to indicate a
miss.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🔭 Outside diff range comments (1)
apps/api/src/middlewares/userMiddleware.ts (1)

8-20: Fix falsy-miss bug, avoid double cache read, and improve typing

  • Using if (!result) treats "", 0, and false as cache misses. Prefer a nullish check (result == null) to only treat undefined/null as misses.
  • Avoid the second cache read; return the already-fetched result.
  • Tighten types to avoid implicit any and make the API clearer. Also annotate put’s return type.

Apply this diff:

-  async get(key: string, format: string) {
-    const result = cacheEngine.getFromCache(key);
-    if (!result) {
-      return null;
-    } else if (format === "json" && typeof result === "string") {
-      return JSON.parse(result);
-    } else {
-      return cacheEngine.getFromCache(key);
-    }
+  async get<T = unknown>(key: string, format?: "json"): Promise<T | null> {
+    const result: unknown = cacheEngine.getFromCache(key);
+    if (result == null) {
+      return null;
+    } else if (format === "json" && typeof result === "string") {
+      try {
+        return JSON.parse(result) as T;
+      } catch {
+        // Treat malformed JSON as a cache miss to allow fresh fetch
+        return null;
+      }
+    } else {
+      return result as T;
+    }
   },
-  async put(key: string, value: unknown) {
+  async put(key: string, value: unknown): Promise<void> {
     cacheEngine.storeInCache(key, value);
   }

Notes:

  • If the narrower signature (format?: "json") or the generic return type causes type incompatibilities with verifyRsaJwt’s kvStore shape, keep format: string but still return Promise<unknown | null> and retain the nullish check/double-read fix:
-  async get(key: string, format: string) {
+  async get(key: string, format: string): Promise<unknown | null> {
-    const result = cacheEngine.getFromCache(key);
+    const result: unknown = cacheEngine.getFromCache(key);
-    if (!result) {
+    if (result == null) {
       return null;
     } else if (format === "json" && typeof result === "string") {
-      return JSON.parse(result);
+      try { return JSON.parse(result) as unknown; } catch { return null; }
     } else {
-      return cacheEngine.getFromCache(key);
+      return result;
     }
   }
🧹 Nitpick comments (1)
apps/api/src/middlewares/userMiddleware.ts (1)

25-25: Use property shorthand for kvStore

Minor readability nit: use object property shorthand.

-  kvStore: kvStore
+  kvStore
-  kvStore: kvStore,
+  kvStore,

Also applies to: 30-30

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 67c037e and 3d8ea71.

📒 Files selected for processing (1)
  • apps/api/src/middlewares/userMiddleware.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/general.mdc)

Never use type any or cast to type any. Always define the proper TypeScript types.

Files:

  • apps/api/src/middlewares/userMiddleware.ts
**/*.{js,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/general.mdc)

**/*.{js,ts,tsx}: Never use deprecated methods from libraries.
Don't add unnecessary comments to the code

Files:

  • apps/api/src/middlewares/userMiddleware.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
  • GitHub Check: validate / validate-app
  • GitHub Check: test-build
  • GitHub Check: test-build
  • GitHub Check: validate / validate-app
  • GitHub Check: test-build
  • GitHub Check: validate / validate-app
  • GitHub Check: test-build
  • GitHub Check: test-build
  • GitHub Check: validate / validate-app
  • GitHub Check: validate / validate-app
  • GitHub Check: test-build
  • GitHub Check: test-build
🔇 Additional comments (1)
apps/api/src/middlewares/userMiddleware.ts (1)

12-13: Safer JSON.parse guard — good change

Adding typeof result === "string" before JSON.parse prevents runtime errors when non-string values are cached. This aligns with the broader caching refactor.

stalniy
stalniy previously approved these changes Aug 14, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
apps/api/test/functional/provider-graph-data.spec.ts (1)

291-293: Move timer restoration into a finally block

Restoring timers only at the end of the happy path can leak timers on failure. Co-locate this with the setup in a try/finally as suggested above.

🧹 Nitpick comments (1)
apps/api/test/functional/market-data.spec.ts (1)

12-15: Persisted nock interceptors: add net-connect guard and ensure cleanup

Persisting interceptors is fine for repeated calls, and you clean them later. To further harden tests:

  • Disable real network connections to fail fast on missing mocks.
  • Optionally re-enable in afterAll if other suites rely on network.

Suggested additions:

   beforeAll(async () => {
     // Clean up any existing nock interceptors
     nock.cleanAll();
+    // Prevent real HTTP calls during this suite
+    nock.disableNetConnect();

     const coinGeckoApiUrl = "https://api.coingecko.com";

     // More permissive nock setup that matches any headers
     nock(coinGeckoApiUrl)
       .persist()

And in afterAll:

   afterAll(() => {
     nock.cleanAll();
+    nock.enableNetConnect();
   });

Also applies to: 31-32

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these settings in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3d8ea71 and f36774b.

📒 Files selected for processing (3)
  • apps/api/test/functional/lease-flow.spec.ts (5 hunks)
  • apps/api/test/functional/market-data.spec.ts (3 hunks)
  • apps/api/test/functional/provider-graph-data.spec.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.spec.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/no-jest-mock.mdc)

Don't use jest.mock() to mock dependencies in test files. Instead, use jest-mock-extended to create mocks and pass mocks as dependencies to the service under test.

**/*.spec.{ts,tsx}: Use setup function instead of beforeEach in test files
setup function must be at the bottom of the root describe block in test files
setup function creates an object under test and returns it
setup function should accept a single parameter with inline type definition
Don't use shared state in setup function
Don't specify return type of setup function

Files:

  • apps/api/test/functional/provider-graph-data.spec.ts
  • apps/api/test/functional/market-data.spec.ts
  • apps/api/test/functional/lease-flow.spec.ts
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/general.mdc)

Never use type any or cast to type any. Always define the proper TypeScript types.

Files:

  • apps/api/test/functional/provider-graph-data.spec.ts
  • apps/api/test/functional/market-data.spec.ts
  • apps/api/test/functional/lease-flow.spec.ts
**/*.{js,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/general.mdc)

**/*.{js,ts,tsx}: Never use deprecated methods from libraries.
Don't add unnecessary comments to the code

Files:

  • apps/api/test/functional/provider-graph-data.spec.ts
  • apps/api/test/functional/market-data.spec.ts
  • apps/api/test/functional/lease-flow.spec.ts
🧠 Learnings (1)
📚 Learning: 2025-07-21T08:24:24.269Z
Learnt from: CR
PR: akash-network/console#0
File: .cursor/rules/no-jest-mock.mdc:0-0
Timestamp: 2025-07-21T08:24:24.269Z
Learning: Applies to **/*.spec.{ts,tsx} : Don't use `jest.mock()` to mock dependencies in test files. Instead, use `jest-mock-extended` to create mocks and pass mocks as dependencies to the service under test.

Applied to files:

  • apps/api/test/functional/provider-graph-data.spec.ts
🧬 Code Graph Analysis (1)
apps/api/test/functional/lease-flow.spec.ts (1)
apps/api/src/caching/helpers.ts (1)
  • cacheEngine (8-8)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: test-build
  • GitHub Check: test-build
  • GitHub Check: validate / validate-app
  • GitHub Check: test-build
  • GitHub Check: validate / validate-app
  • GitHub Check: test-build
  • GitHub Check: test-build
  • GitHub Check: test-build
  • GitHub Check: validate / validate-app
  • GitHub Check: validate / validate-app
  • GitHub Check: test-build
🔇 Additional comments (4)
apps/api/test/functional/lease-flow.spec.ts (2)

74-74: Allowance increase looks correct

Updating deploymentAllowance to 10_000_000 aligns with the subsequent deposit flow and reduces the risk of insufficiency during the test.


158-160: No need to await cacheEngine.clearAllKeyInCache (it's synchronous)

After inspecting MemoryCacheEngine.clearAllKeyInCache(), it simply calls mcache.clear() and does not return a Promise. Awaiting it has no effect, so the current calls at lines 158–160, 217–219, and 261–263 in lease-flow.spec.ts (and the similar calls in setup-functional-tests.ts and helpers.spec.ts) are correct as-is. If you ever introduce an async cache-clearing implementation in the future, you can revisit and add await.

apps/api/test/functional/market-data.spec.ts (2)

7-9: Good isolation: cleaning interceptors before tests start

Resetting nock interceptors up-front prevents carry-over from previous suites and helps ensure deterministic runs.


48-50: Solid teardown

Cleaning interceptors after the suite avoids cross-suite interference.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (3)
apps/api/test/functional/lease-flow.spec.ts (3)

19-19: Good: decoupled test cache clearing via helper

Importing clearCache from @test/setup-functional-tests removes the direct dependency on the cache engine and aligns with previous feedback. This improves maintainability and test isolation.


217-219: Same as above: use wildcard or a shared constant for cache key/prefix

Make the prefix intent explicit and avoid magic strings.

-    clearCache("WalletController#getBalances");
+    clearCache("WalletController#getBalances*");

261-263: Same as above: use wildcard or a shared constant for cache key/prefix

Repeat the explicit prefix clearing here for consistency.

-    clearCache("WalletController#getBalances");
+    clearCache("WalletController#getBalances*");
🧹 Nitpick comments (1)
apps/api/test/functional/lease-flow.spec.ts (1)

158-160: Prefer explicit prefix clearing (use wildcard) or extract a named constant

clearCache("WalletController#getBalances") relies on the helper’s heuristic to decide between key vs prefix clearing. Make it explicit and less brittle by using a wildcard to guarantee prefix invalidation, or extract a constant to avoid repeating a magic string.

Apply this change here:

-    clearCache("WalletController#getBalances");
+    clearCache("WalletController#getBalances*");

Optionally, define a constant near the imports and reuse in all three places to reduce duplication:

// near imports
const BALANCES_CACHE_PREFIX = "WalletController#getBalances*";
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these settings in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f36774b and 879701c.

📒 Files selected for processing (5)
  • apps/api/src/caching/memoryCacheEngine.ts (2 hunks)
  • apps/api/test/functional/deployment-setting.spec.ts (1 hunks)
  • apps/api/test/functional/lease-flow.spec.ts (5 hunks)
  • apps/api/test/functional/provider-graph-data.spec.ts (1 hunks)
  • apps/api/test/setup-functional-tests.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/api/test/functional/deployment-setting.spec.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/api/src/caching/memoryCacheEngine.ts
  • apps/api/test/functional/provider-graph-data.spec.ts
  • apps/api/test/setup-functional-tests.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.spec.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/no-jest-mock.mdc)

Don't use jest.mock() to mock dependencies in test files. Instead, use jest-mock-extended to create mocks and pass mocks as dependencies to the service under test.

**/*.spec.{ts,tsx}: Use setup function instead of beforeEach in test files
setup function must be at the bottom of the root describe block in test files
setup function creates an object under test and returns it
setup function should accept a single parameter with inline type definition
Don't use shared state in setup function
Don't specify return type of setup function

Files:

  • apps/api/test/functional/lease-flow.spec.ts
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/general.mdc)

Never use type any or cast to type any. Always define the proper TypeScript types.

Files:

  • apps/api/test/functional/lease-flow.spec.ts
**/*.{js,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/general.mdc)

**/*.{js,ts,tsx}: Never use deprecated methods from libraries.
Don't add unnecessary comments to the code

Files:

  • apps/api/test/functional/lease-flow.spec.ts
🧬 Code Graph Analysis (1)
apps/api/test/functional/lease-flow.spec.ts (1)
apps/api/test/setup-functional-tests.ts (1)
  • clearCache (13-31)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: test-build
  • GitHub Check: test-build
  • GitHub Check: validate / validate-app
  • GitHub Check: test-build
  • GitHub Check: test-build
  • GitHub Check: test-build
  • GitHub Check: validate / validate-app
  • GitHub Check: test-build
  • GitHub Check: validate / validate-app
  • GitHub Check: test-build
  • GitHub Check: validate / validate-app
🔇 Additional comments (1)
apps/api/test/functional/lease-flow.spec.ts (1)

74-74: Verify increased deploymentAllowance value and units

Bumping deploymentAllowance to 10_000_000 provides ample headroom, but please confirm the unit matches the deposit inputs used in this flow (e.g., AKT vs uAKT) so we’re not masking potential unit mismatches or regressions. If feasible, consider deriving this from a shared constant to avoid divergence with production defaults.

@baktun14 baktun14 merged commit bb1185e into main Aug 14, 2025
62 checks passed
@baktun14 baktun14 deleted the fix/api-memoize branch August 14, 2025 10:36
stalniy pushed a commit that referenced this pull request Nov 20, 2025
* fix: improve caching function

* fix: return cache data if error

* fix: improve concurrency check

* refactor: replace pendingRequests object with Map for improved performance and clarity

* refactor: enhance type safety in caching functions and improve cache expiration logic

* fix: ensure JSON parsing only occurs for string results in cache retrieval

* test: enhance lease flow tests with cache clearing and update deployment allowance

* feat: add cache management functions and enhance test cache clearing logic
@coderabbitai coderabbitai bot mentioned this pull request Jan 28, 2026
9 tasks
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.

2 participants

Comments