Skip to content

Commit 80eaa25

Browse files
Nana-ECAlfredoG87
andauthored
Adding a configurable TTL cache for get_ethBalance to eth.ts (#1064) (#1076)
* Adding a configurable TTL cache for get_ethBalance to eth.ts * Added logs for cache * Added logs for cache for blockNumber aswell --------- Signed-off-by: Alfredo Gutierrez <[email protected]> Co-authored-by: Alfredo Gutierrez <[email protected]>
1 parent f549a69 commit 80eaa25

File tree

6 files changed

+63
-2
lines changed

6 files changed

+63
-2
lines changed

.env.example

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,9 @@ SUBSCRIPTIONS_ENABLED=
3333
SERVER_PORT=
3434
WEB_SOCKET_PORT=
3535
CONNECTION_LIMIT=
36+
WS_MAX_CONNECTION_TTL=
37+
WS_CONNECTION_LIMIT_PER_IP=
38+
WS_SUBSCRIPTION_LIMIT=
39+
WS_MULTIPLE_ADDRESSES_ENABLED=
40+
ETH_BLOCK_NUMBER_CACHE_TTL_MS=
41+
ETH_GET_BALANCE_CACHE_TTL_MS=

docs/configuration.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ Unless you need to set a non-default value, it is recommended to only populate o
4949
| `DEFAULT_RATE_LIMIT` | "200" | default fallback rate limit, if no other is configured. |
5050
| `ETH_CALL_CACHE_TTL` | "200" | Maximum time in ms to cache an eth_call response. |
5151
| `ETH_BLOCK_NUMBER_CACHE_TTL_MS` | "1000" | Time in ms to cache response from mirror node |
52+
| `ETH_GET_BALANCE_CACHE_TTL_MS` | "1000" | Time in ms to cache balance returned |
5253
| `ETH_CALL_DEFAULT_TO_CONSENSUS_NODE ` | "false" | Flag to set if eth_call logic should first query the mirror node. |
5354
| `ETH_GET_LOGS_BLOCK_RANGE_LIMIT` | "1000" | The maximum block number range to consider during an eth_getLogs call. |
5455
| `FEE_HISTORY_MAX_RESULTS` | "10" | The maximum number of results to returns as part of `eth_feeHistory`. |

k6/.envexample

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,3 @@ DEFAULT_GRACEFUL_STOP=
1010
FILTER_TEST=
1111
DEBUG_MODE=
1212
SIGNED_TXS=
13-
ETH_BLOCK_NUMBER_CACHE_TTL_MS=

packages/relay/src/lib/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ enum CACHE_KEY {
2323
FEE_HISTORY = 'fee_history',
2424
GET_CONTRACT_RESULT = 'getContractResult',
2525
ETH_BLOCK_NUMBER = 'eth_block_number',
26+
ETH_GET_BALANCE = 'eth_get_balance',
2627
}
2728

2829
enum CACHE_TTL {

packages/relay/src/lib/eth.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ export class EthImpl implements Eth {
6767
static invalidEVMInstruction = '0xfe';
6868
static ethCallCacheTtl = process.env.ETH_CALL_CACHE_TTL || 200;
6969
static ethBlockNumberCacheTtlMs = process.env.ETH_BLOCK_NUMBER_CACHE_TTL_MS || 1000;
70+
static ethGetBalanceCacheTtlMs = process.env.ETH_GET_BALANCE_CACHE_TTL_MS || 1000;
7071

7172
// endpoint metric callerNames
7273
static ethCall = 'eth_call';
@@ -309,6 +310,7 @@ export class EthImpl implements Eth {
309310
const blockNumberCached = this.cache.get(cacheKey);
310311

311312
if(blockNumberCached) {
313+
this.logger.trace(`${requestIdPrefix} returning cached value ${cacheKey}:${JSON.stringify(blockNumberCached)}`);
312314
return blockNumberCached;
313315
}
314316

@@ -318,6 +320,7 @@ export class EthImpl implements Eth {
318320
const currentBlock = EthImpl.numberTo0x(blocks[0].number);
319321
// save the latest block number in cache
320322
this.cache.set(cacheKey, currentBlock, { ttl: EthImpl.ethBlockNumberCacheTtlMs });
323+
this.logger.trace(`${requestIdPrefix} caching ${cacheKey}:${JSON.stringify(currentBlock)} for ${EthImpl.ethBlockNumberCacheTtlMs} ms`);
321324

322325
return currentBlock;
323326
}
@@ -577,6 +580,15 @@ export class EthImpl implements Eth {
577580
}
578581
}
579582

583+
// check cache first
584+
// create a key for the cache
585+
const cacheKey = `${constants.CACHE_KEY.ETH_GET_BALANCE}-${account}-${blockNumberOrTag}`;
586+
const cachedBalance = this.cache.get(cacheKey);
587+
if (cachedBalance) {
588+
this.logger.trace(`${requestIdPrefix} returning cached value ${cacheKey}:${JSON.stringify(cachedBalance)}`);
589+
return cachedBalance;
590+
}
591+
580592
let blockNumber = null;
581593
let balanceFound = false;
582594
let weibars: BigInt = BigInt(0);
@@ -649,6 +661,10 @@ export class EthImpl implements Eth {
649661
return EthImpl.zeroHex;
650662
}
651663

664+
// save in cache the current balance for the account and blockNumberOrTag
665+
this.cache.set(cacheKey, EthImpl.numberTo0x(weibars), {ttl: EthImpl.ethGetBalanceCacheTtlMs});
666+
this.logger.trace(`${requestIdPrefix} caching ${cacheKey}:${JSON.stringify(cachedBalance)} for ${EthImpl.ethGetBalanceCacheTtlMs} ms`);
667+
652668
return EthImpl.numberTo0x(weibars);
653669
} catch (error: any) {
654670
throw this.genericErrorHandler(error, `${requestIdPrefix} Error raised during getBalance for account ${account}`);

packages/relay/tests/lib/eth.spec.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1318,6 +1318,44 @@ describe('Eth calls using MirrorNode', async function () {
13181318
expect(resBalance).to.equal(defHexBalance);
13191319
});
13201320

1321+
it('should return balance for latest block from cache', async () => {
1322+
restMock.onGet(`blocks?limit=1&order=desc`).reply(200, {
1323+
blocks: [{
1324+
number: 10000
1325+
}]
1326+
});
1327+
restMock.onGet(`accounts/${contractAddress1}`).reply(200, {
1328+
account: contractAddress1,
1329+
balance: {
1330+
balance: defBalance
1331+
}
1332+
});
1333+
1334+
const resBalance = await ethImpl.getBalance(contractAddress1, null);
1335+
expect(resBalance).to.equal(defHexBalance);
1336+
1337+
// next call should use cache
1338+
restMock.onGet(`accounts/${contractAddress1}`).reply(404, {});
1339+
1340+
const resBalanceCached = await ethImpl.getBalance(contractAddress1, null);
1341+
expect(resBalanceCached).to.equal(resBalance);
1342+
1343+
// Third call should return new number using mirror node
1344+
const newBalance = 55555;
1345+
const newBalanceHex = EthImpl.numberTo0x(BigInt(newBalance) * TINYBAR_TO_WEIBAR_COEF_BIGINT);
1346+
restMock.onGet(`accounts/${contractAddress1}`).reply(200, {
1347+
account: contractAddress1,
1348+
balance: {
1349+
balance: newBalance
1350+
}
1351+
});
1352+
// expire cache, instead of waiting for ttl we clear it to simulate expiry faster.
1353+
cache.clear();
1354+
1355+
const resBalanceNew = await ethImpl.getBalance(contractAddress1, null);
1356+
expect(newBalanceHex).to.equal(resBalanceNew);
1357+
});
1358+
13211359
it('should return balance from mirror node with block number passed as param the same as latest', async () => {
13221360
const blockNumber = "0x2710";
13231361
restMock.onGet(`blocks?limit=1&order=desc`).reply(200, {
@@ -1394,7 +1432,7 @@ describe('Eth calls using MirrorNode', async function () {
13941432

13951433
const resCached = await ethImpl.getBalance(contractAddress1, null);
13961434
expect(resNoCache).to.equal(defHexBalance);
1397-
expect(resCached).to.equal(EthImpl.zeroHex);
1435+
expect(resCached).to.equal(defHexBalance);
13981436
});
13991437

14001438
describe('with blockNumberOrTag filter', async function() {

0 commit comments

Comments
 (0)