Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Implement custom LRU cache with protected keys #3097

Closed

Conversation

victor-yanev
Copy link
Contributor

Description:

This PR implements a custom LRUCache implementation with the ability to protect certain keys from being deleted. The cache is designed to handle specific key prefixes that are associated with pre-configured spending plans, ensuring that these keys are not removed from the cache.

Changes

  • CustomLRUCache Class:
    • Added a new class CustomLRUCache that extends the LRUCache class.
    • Implemented methods to load and parse spending plans configuration from a JSON file.
    • Added logic to protect keys with specific prefixes (hbarSpendingPlan:{{id}}, ethAddressHbarSpendingPlan:{{ethAddress}}, ipAddressHbarSpendingPlan:{{ipAddress}}).
    • Overridden the delete method to prevent deletion of protected keys.

Unit Tests:

  • Added unit tests in customLRUCache.spec.ts to verify the behavior of the CustomLRUCache.
  • Tests include scenarios for deleting non-protected keys, and ensuring protected keys are not deleted.

Related issue(s):

Fixes #3096

Notes for reviewer:

Checklist

  • Documented (Code comments, README, etc.)
  • Tested (unit, integration, etc.)

@victor-yanev victor-yanev added enhancement New feature or request P1 labels Oct 14, 2024
@victor-yanev victor-yanev added this to the 0.59.0 milestone Oct 14, 2024
@victor-yanev victor-yanev self-assigned this Oct 14, 2024
Copy link

github-actions bot commented Oct 14, 2024

🚨 Memory Leak Detected 🚨

A potential memory leak has been detected in the test titled validates enforcement of request id. This may impact the application's performance and stability.

Details

📊 Memory Leak Detection Report 📊

GC Type: MarkSweepCompact
Cost: 23,653.4 ms

Heap Statistics (before vs after executing the test):

  • Total Heap Size: increased with 1.46 MB
  • Total Heap Size Executable: no changes
  • Total Physical Size: decreased with 348.16 KB
  • Total Available Size: decreased with 8.91 MB
  • Total Global Handles Size: no changes
  • Used Global Handles Size: decreased with 64.00 bytes
  • Used Heap Size: decreased with 3.43 MB
  • Heap Size Limit: no changes
  • Malloced Memory: no changes
  • External Memory: no changes
  • Peak Malloced Memory: no changes

Heap Space Statistics (before vs after executing the test):

  • Old Space:

    • Space Size: increased with 1.84 MB
    • Space Used Size: increased with 2.09 MB
    • Space Available Size: decreased with 11.84 MB
    • Physical Space Size: increased with 1.84 MB
  • Large Object Space:

    • Space Size: increased with 835.58 KB
    • Space Used Size: increased with 813.50 KB
    • Space Available Size: no changes
    • Physical Space Size: increased with 835.58 KB

Recommendations

Please investigate the memory allocations in this test, focusing on objects that are not being properly deallocated.

Copy link

github-actions bot commented Oct 14, 2024

Tests

       3 files     402 suites   18s ⏱️
1 442 tests 1 441 ✔️ 1 💤 0
1 451 runs  1 450 ✔️ 1 💤 0

Results for commit 3a48fee.

♻️ This comment has been updated with latest results.

Copy link

github-actions bot commented Oct 14, 2024

Acceptance Tests

  16 files  221 suites   29m 18s ⏱️
599 tests 595 ✔️ 4 💤 0
615 runs  611 ✔️ 4 💤 0

Results for commit 231d379.

♻️ This comment has been updated with latest results.

Copy link
Collaborator

@Nana-EC Nana-EC left a comment

Choose a reason for hiding this comment

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

Nice work,
some suggestions

* @private
*/
private isPreconfiguredPlanKey(key: string, plan: SpendingPlanConfig): boolean {
return key.includes(`${HbarSpendingPlanRepository.collectionKey}:${plan.id}`);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit: This should work since we're working with fixed length keys. In general I would try to get an exact match, in the case that a key contains the substring of another key.

  const expectedKey = `${HbarSpendingPlanRepository.collectionKey}:${plan.id}`;
  return key === expectedKey;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is intentionally done here, because we want to also match the amountSpent and spendingHistory keys which contain the ${HbarSpendingPlanRepository.collectionKey}:${plan.id} prefix:

  • ${HbarSpendingPlanRepository.collectionKey}:${plan.id}:amountSpent
  • ${HbarSpendingPlanRepository.collectionKey}:${plan.id}:spendingHistory

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed to a more general approach where we initialize a set of protected keys in the constructor and then in delete we check for the existence of the key in this set of protected keys:

  delete(key: K): boolean {
    if (this.protectedKeys.has(key)) {
      this.logger.trace(`Deletion of key ${key} is ignored as it is protected.`);
      return false;
    }
    return super.delete(key);
  }

ebadiere
ebadiere previously approved these changes Oct 15, 2024
Copy link
Collaborator

@ebadiere ebadiere left a comment

Choose a reason for hiding this comment

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

Nice work. I have a couple of questions.

Copy link

sonarcloud bot commented Oct 22, 2024

@victor-yanev
Copy link
Contributor Author

Looks like this approach is still not working for scenarios where the LRUCache has reached the size limit:

    beforeEach(() => {
      cache = new CustomLRUCache<string, any>(logger, { ttl: 100, max: 3 });
    });
    
    ... 
    
    // ⚠️ ⚠️ ⚠️ THIS TEST IS FAILING ⚠️ ⚠️ ⚠️
    it('should not delete a protected key when the cache is full', () => {
      const protectedKey = `hbarSpendingPlan:${spendingPlansConfig[0].id}`;
      const keys = ['key1', 'key2', protectedKey];
      keys.forEach((key) => cache.set(key, 'value'));
      cache.get('key1');
      cache.get('key2');
      cache.set('key3', 'value');
      expect(cache.has(protectedKey)).to.be.true;
    });

And therefore, unfortunately, this PR is pointless...

Copy link

codecov bot commented Oct 22, 2024

Codecov Report

Attention: Patch coverage is 97.67442% with 1 line in your changes missing coverage. Please review.

Project coverage is 85.11%. Comparing base (afc3349) to head (fc1ef14).
Report is 8 commits behind head on main.

Files with missing lines Patch % Lines
...relay/src/lib/clients/cache/impl/customLRUCache.ts 96.87% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3097      +/-   ##
==========================================
+ Coverage   83.16%   85.11%   +1.94%     
==========================================
  Files          63       45      -18     
  Lines        4236     3372     -864     
  Branches      830      669     -161     
==========================================
- Hits         3523     2870     -653     
+ Misses        470      294     -176     
+ Partials      243      208      -35     
Flag Coverage Δ
relay 85.11% <97.67%> (-0.48%) ⬇️
server ?
ws-server ?

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...kages/relay/src/lib/clients/cache/localLRUCache.ts 87.27% <100.00%> (ø)
...barLimiter/ethAddressHbarSpendingPlanRepository.ts 81.81% <100.00%> (-10.49%) ⬇️
...sitories/hbarLimiter/hbarSpendingPlanRepository.ts 100.00% <100.00%> (ø)
...hbarLimiter/ipAddressHbarSpendingPlanRepository.ts 84.09% <100.00%> (-10.79%) ⬇️
...relay/src/lib/clients/cache/impl/customLRUCache.ts 96.87% <96.87%> (ø)

... and 26 files with indirect coverage changes

@victor-yanev
Copy link
Contributor Author

Replaced by #3140

@victor-yanev victor-yanev deleted the 3096-Implement-custom-LRU-cache-with-protected-keys branch October 22, 2024 12:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request P1
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[HBAR Rate Limit Redesign] Implement custom LRU cache with protected keys
3 participants