Skip to content

Better web3 rpc handling & add caching for getBlockBeforeTime#4099

Open
kajoseph wants to merge 10 commits intobitpay:masterfrom
kajoseph:updateWeb3
Open

Better web3 rpc handling & add caching for getBlockBeforeTime#4099
kajoseph wants to merge 10 commits intobitpay:masterfrom
kajoseph:updateWeb3

Conversation

@kajoseph
Copy link
Collaborator

@kajoseph kajoseph commented Feb 1, 2026

Description

This PR improves the getWeb() by round-robin load-balancing RPCs as well as reduces memory leaks. Also adds caching to getBlockBeforeTime() to reduce redundant calls. Also a few various/random log typo fixes & testing improvements.

Changelog

  • Improves getWeb() by round-robin load-balancing RPCs as well as reduces memory leaks.
  • Adds caching to getBlockBeforeTime() to reduce redundant calls.
  • Fixes a few log errors/typos.
  • Removes the tsx dependency to run tests as js files.
  • Uses LRU caching with finite limits for block fee data caching.

Checklist

  • I have read CONTRIBUTING.md and verified that this PR follows the guidelines and requirements outlined in it.
  • I have added the appropriate package tag(s) (e.g. BWC if modifying the bitcore-wallet-client package, CLI if modifying the bitcore-cli package, etc.)
  • I have verified that this is not an existing PR (open or closed)

@kajoseph kajoseph added the BCN This pull request modifies the bitcore-node package label Feb 1, 2026
@kajoseph kajoseph requested a review from Copilot February 1, 2026 01:03
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR improves Web3 RPC handling by implementing round-robin load balancing and reducing memory leaks through proper connection lifecycle management. It also adds caching for getBlockBeforeTime() calls and fixes various logging issues.

Changes:

  • Refactored RPC initialization to use round-robin load balancing across multiple providers
  • Added proper RPC teardown methods to prevent memory leaks
  • Implemented LRU caching for getBlockBeforeTime() to reduce redundant database queries
  • Fixed logging typos and improved error message formatting

Reviewed changes

Copilot reviewed 13 out of 14 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/bitcore-node/src/providers/chain-state/evm/api/csp.ts Implements round-robin RPC selection and connection management
packages/bitcore-node/src/providers/chain-state/svm/api/csp.ts Implements similar RPC management for Solana chains
packages/bitcore-node/src/providers/chain-state/internal/internal.ts Adds LRU cache for block-at-time queries
packages/bitcore-node/src/modules/moralis/api/csp.ts Adds caching to Moralis provider's getBlockBeforeTime
packages/bitcore-node/src/routes/api/fee.ts Fixes logging typo (stackk → stack)
packages/bitcore-node/src/modules/moralis/p2p/p2p.ts Fixes missing parameters in error log
packages/bitcore-node/test/unit/modules/base/csp.test.ts Adds comprehensive tests for new RPC management
packages/bitcore-node/test/unit/modules/ethereum/csp.test.ts Updates tests to match new RPC structure
packages/bitcore-node/test/unit/modules/matic/csp.test.ts Updates tests to match new RPC structure
packages/bitcore-node/test/integration/ethereum/memory-leaks.test.ts Adds eslint disable comment for legitimate this alias
packages/bitcore-node/package.json Removes tsx dependency, adds lru-cache
packages/bitcore-node/src/types/Config.ts Adds ProviderDataType type alias
packages/bitcore-node/src/providers/chain-state/external/providers/provider.ts File deleted (functionality moved inline)
Files not reviewed (1)
  • packages/bitcore-node/package-lock.json: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

}
this.blockAtTimeCache[chainNetwork].set(date.toISOString(), block);
} else {
this.blockAtTimeCache[chainNetwork].set(date.toISOString(), block, { ttl: 10000 }); // cache nulls for 10 sec
Copy link
Contributor

Choose a reason for hiding this comment

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

Would reduce cognitive load to either directly set the cache to null here - or at the least a comment above - "block is null"

Comment on lines +64 to +65
static rpcs = {} as { [chainNetwork: string]: { historical: GetWeb3Response[]; realtime: GetWeb3Response[] } };
static rpcIndicies = {} as { [chainNetwork: string]: { historical: number; realtime: number } };
Copy link
Contributor

Choose a reason for hiding this comment

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

Be declarative - use explicit type annotations instead of type assertions

if (BaseEVMStateProvider.rpcInitialized[chain]) {
return;
}
BaseEVMStateProvider.rpcInitialized[chain] = true;
Copy link
Contributor

Choose a reason for hiding this comment

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

Setting flag after initialization seems right semantically, and provides (hopefully) redundant support if the initialization fails.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is meant to reduce race conditions. Though I suppose the "danger" of duplicate rpc's isn't very dangerous.

}
}
index = (index + 1) % BaseEVMStateProvider.rpcs[chainNetwork][type].length;
} while (index !== lastIndex + 1);
Copy link
Contributor

Choose a reason for hiding this comment

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

Check when lastIndex is length - 1.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not sure I understand this comment. lastIndex is a const so while (lastIndex != length - 1) would be a forever loop.

if (!this.blockAtTimeCache[chainNetwork]) {
this.blockAtTimeCache[chainNetwork] = new LRUCache<string, IBlock>({ max: 1000 });
}
this.blockAtTimeCache[chainNetwork].set(date.toISOString(), block);
Copy link
Contributor

Choose a reason for hiding this comment

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

How often will this be called w/o params.time? This is going to make a lot of churn. I know it's capped at 1k, but may be worth thinking about a bucket implementation depending on how it's usually called.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

BCN This pull request modifies the bitcore-node package

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants