Skip to content

Commit

Permalink
cache polish (#1039)
Browse files Browse the repository at this point in the history
* cache polish

* fix

* update

* fix

* polish

* fix

* fix

* fix

* polish

* encapsulate api cache to a class

* add cache tests

* fix
  • Loading branch information
shunjizhan authored Nov 5, 2024
1 parent df76528 commit 6398220
Show file tree
Hide file tree
Showing 9 changed files with 304 additions and 243 deletions.
41 changes: 0 additions & 41 deletions packages/eth-providers/src/__tests__/evm-rpc-provider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,47 +55,6 @@ describe('getReceiptAtBlock', async () => {
});
});

// TODO: maybe setup a subway to test
describe.skip('all cache', async () => {
const provider = EvmRpcProvider.from(ACALA_NODE_URL);
await provider.isReady();

afterAll(async () => await provider.disconnect());

it('getBlockHeader at latest block => header cache', async () => {
const { time: time1, res: header1 } = await runWithTiming(() => provider._getBlockHeader('latest'), 1);
const { time: time2, res: header2 } = await runWithTiming(() => provider._getBlockHeader('latest'), 1);

// latest header should already be cached at the start
console.log('latest header:', { time1, time2 });
expect(time1).to.be.lt(10);
expect(time2).to.be.lt(10);
expect(header1.toJSON()).to.deep.equal(header2.toJSON());
});

it('getBlockHeader at random block => header cache', async () => {
const { time: time1, res: header1 } = await runWithTiming(() => provider._getBlockHeader(1234567), 1);
const { time: time2, res: header2 } = await runWithTiming(() => provider._getBlockHeader(1234567), 1);

// second time should be 100x faster with cache, in poor network 800ms => 0.5ms
console.log('getBlockHeader:', { time1, time2 });
expect(time2).to.be.lt(time1 / 20); // conservative multiplier
expect(time2).to.be.lt(10); // no async call
expect(header1.toJSON()).to.deep.equal(header2.toJSON());
});

it('getBlockData at random block => header cache + storage cache + receipt cache', async () => {
const { time: time1, res: blockData1 } = await runWithTiming(() => provider.getBlockData(1234321), 1);
const { time: time2, res: blockData2 } = await runWithTiming(() => provider.getBlockData(1234321), 1);

// second time should be 100x faster with cache, usually 1500ms => 3ms
console.log('getBlockData: ', { time1, time2 });
expect(time2).to.be.lt(time1 / 20); // conservative multiplier
expect(time2).to.be.lt(30); // no async call
expect(blockData1).to.deep.equal(blockData2);
});
});

describe.concurrent('rpc test', async () => {
const provider = EvmRpcProvider.from(endpoint);

Expand Down
115 changes: 115 additions & 0 deletions packages/eth-providers/src/__tests__/provider-cache.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
import dotenv from 'dotenv';

import { EvmRpcProvider } from '../rpc-provider';
import { apiCache } from '../utils/ApiAtCache';
import { runWithTiming } from '../utils';

dotenv.config();

const ACALA_NODE_URL = 'wss://acala-rpc.dwellir.com';

describe.concurrent('provider cache', async () => {
let provider: EvmRpcProvider;
let provider2: EvmRpcProvider;

beforeAll(async () => {
provider = EvmRpcProvider.from(ACALA_NODE_URL);
provider2 = EvmRpcProvider.from(ACALA_NODE_URL); // provider 2 to query some info without affecting cache
await provider.isReady();
await provider2.isReady();
});

afterAll(async () => await Promise.all([
provider.disconnect(),
provider2.disconnect(),
]));

it('get apiAt', async() => {
const curBlock = await provider.getBlockNumber();
const randomBlock = curBlock - Math.floor(Math.random() * 100000);
const blockHash = await provider2._getBlockHash(randomBlock);

const { time: time1, res: apiAt1 } = await runWithTiming(() => apiCache.getApiAt(provider.api, blockHash), 1);
const { time: time2, res: apiAt2 } = await runWithTiming(() => apiCache.getApiAt(provider.api, blockHash), 1);

expect(time1).to.be.gt(0, 'first get apiAt failed!');
expect(time2).to.be.gt(0, 'second get apiAt failed!');
console.log('get random apiAt:', { time1, time2 });

expect(time2).to.be.lt(time1 / 20); // conservative multiplier

Check failure on line 40 in packages/eth-providers/src/__tests__/provider-cache.test.ts

View workflow job for this annotation

GitHub Actions / pkg-tests (eth-providers)

src/__tests__/provider-cache.test.ts > provider cache > get apiAt

AssertionError: expected 16.845100000000002 to be below 13.879432850000011 - Expected + Received - 13.879432850000011 + 16.845100000000002 ❯ src/__tests__/provider-cache.test.ts:40:25
expect(time2).to.be.lt(50); // no async call so should be almost instant
expect(apiAt1).to.equal(apiAt2); // should be the same instance
});

it('get block hash', async() => {
const curBlock = await provider.getBlockNumber();
const randomBlock = curBlock - Math.floor(Math.random() * 100000);

const { time: time1, res: hash1 } = await runWithTiming(() => provider._getBlockHash(randomBlock), 1);
const { time: time2, res: hash2 } = await runWithTiming(() => provider._getBlockHash(randomBlock), 1);

expect(time1).to.be.gt(0, 'first get block hash failed!');
expect(time2).to.be.gt(0, 'second get block hash failed!');
console.log('get random block hash:', { time1, time2 });

expect(time2).to.be.lt(time1 / 20); // conservative multiplier
expect(time2).to.be.lt(50); // no async call so should be almost instant
expect(hash1).to.deep.equal(hash2);
});

it('get block', async() => {
const curBlock = await provider.getBlockNumber();
const randomBlock = curBlock - Math.floor(Math.random() * 100000);
const blockHash = await provider2._getBlockHash(randomBlock);

const { time: time1, res: blockNumber1 } = await runWithTiming(() => provider._getBlockNumber(blockHash), 1);
const { time: time2, res: blockNumber2 } = await runWithTiming(() => provider._getBlockNumber(blockHash), 1);

expect(time1).to.be.gt(0, 'first get block number failed!');
expect(time2).to.be.gt(0, 'second get block number failed!');
console.log('get random block number:', { time1, time2 });

expect(time2).to.be.lt(time1 / 20); // conservative multiplier
expect(time2).to.be.lt(50); // no async call so should be almost instant
expect(blockNumber1).to.deep.equal(blockNumber2);
});

it('get block header', async() => {
const curBlock = await provider.getBlockNumber();
const randomBlock = curBlock - Math.floor(Math.random() * 100000);

const { time: time1, res: header1 } = await runWithTiming(() => provider._getBlockHeader(randomBlock), 1);
const { time: time2, res: header2 } = await runWithTiming(() => provider._getBlockHeader(randomBlock), 1);

expect(time1).to.be.gt(0, 'first get header failed!');
expect(time2).to.be.gt(0, 'second get header failed!');
console.log('get random header:', { time1, time2 });

expect(time2).to.be.lt(time1 / 20); // conservative multiplier
expect(time2).to.be.lt(50); // no async call so should be almost instant
expect(header1.toJSON()).to.deep.equal(header2.toJSON());
});

it('get block data', async () => {
const curBlock = await provider.getBlockNumber();
const randomBlock = curBlock - Math.floor(Math.random() * 100000);

const { time: time1, res: blockData1 } = await runWithTiming(() => provider.getBlockData(randomBlock), 1);
const { time: time2, res: blockData2 } = await runWithTiming(() => provider.getBlockData(randomBlock), 1);
const { time: time3, res: blockData3 } = await runWithTiming(() => provider.getBlockData(randomBlock, true), 1);

expect(time1).to.be.gt(0, 'first get blockData failed!');
expect(time2).to.be.gt(0, 'second get blockData failed!');
expect(time3).to.be.gt(0, 'third get blockData failed!');
console.log('get random blockData:', { time1, time2, time3 });

expect(time2).to.be.lt(time1 / 20); // conservative multiplier
expect(time2).to.be.lt(50); // no async call so should be almost instant
expect(time3).to.be.lt(time1 / 20); // conservative multiplier
expect(time3).to.be.lt(50); // no async call so should be almost instant
expect(blockData1).to.deep.equal(blockData2);
expect(blockData3.hash).to.deep.equal(blockData2.hash);
});
});

Loading

0 comments on commit 6398220

Please sign in to comment.