Skip to content

Commit bf26fb3

Browse files
perf(evm): read commits in batches (#1334)
* conditional storage compression * add tests * rename CompressedBincode to CompactBincode * add TransactionKey wrapper * tests * add `getCommitsByBlockRange` * use new evm to read commits * pass maxBytes * fix * ensure no negative start --------- Co-authored-by: oXtxNt9U <120286271+oXtxNt9U@users.noreply.github.com>
1 parent e6a0af2 commit bf26fb3

10 files changed

Lines changed: 267 additions & 55 deletions

File tree

packages/api-sync/source/restore.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ export class Restore {
324324
const fromBlockNumber = Math.min(currentBlockNumber, mostRecentCommit.block.number);
325325
const toBlockNumber = Math.min(currentBlockNumber + BATCH_SIZE - 1, mostRecentCommit.block.number);
326326

327-
const commits = this.databaseService.readCommits(fromBlockNumber, toBlockNumber);
327+
const commits = this.databaseService.readCommits(fromBlockNumber, toBlockNumber, Number.MAX_SAFE_INTEGER);
328328

329329
const blocks: Models.Block[] = [];
330330
const transactions: Models.Transaction[] = [];

packages/contracts/source/contracts/database.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ export interface DatabaseService extends CommitHandler {
1414

1515
getLastCommit(): Promise<Commit>;
1616
hasCommitByHash(blockHash: string): Promise<boolean>;
17-
findCommitBuffers(start: number, end: number): Promise<Buffer[]>;
18-
readCommits(start: number, end: number): AsyncGenerator<Commit>;
17+
findCommitBuffers(start: number, end: number, maxBytes: number): Promise<Buffer[]>;
18+
readCommits(start: number, end: number, maxBytes: number): AsyncGenerator<Commit>;
1919

2020
getBlock(blockNumber: number): Promise<Block | undefined>;
2121
getBlockByHash(blockHash: string): Promise<Block | undefined>;

packages/contracts/source/contracts/evm/storage.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ export interface Storage {
5555
getBlockHeaderData(blockNumber: number): Promise<BlockHeaderStorageData | undefined>;
5656
getBlockNumberByHash(blockHash: string): Promise<number | undefined>;
5757
getCommitData(blockNumber: number): Promise<CommitStorageData | undefined>;
58+
getCommitsByBlockRange(
59+
fromBlockNumber: number,
60+
toBlockNumber: number,
61+
maxBytes: number,
62+
): Promise<CommitStorageData[]>;
5863
getTransactionData(key: string): Promise<TransactionStorageData | undefined>;
5964
getTransactionKeyByHash(txHash: string): Promise<string | undefined>;
6065
isEmpty(): Promise<boolean>;

packages/database/source/database-service.test.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,13 @@ describe<{
8383
});
8484

8585
it("findCommitBuffers - should be ok", async ({ databaseService }) => {
86-
const commits = await databaseService.findCommitBuffers(1, 2);
86+
const commits = await databaseService.findCommitBuffers(1, 2, Number.MAX_SAFE_INTEGER);
8787
assert.empty(commits);
8888
});
8989

9090
it("readCommits - should be ok", async ({ databaseService }) => {
9191
const commits = [];
92-
for await (const commit of databaseService.readCommits(1, 2)) {
92+
for await (const commit of databaseService.readCommits(1, 2, Number.MAX_SAFE_INTEGER)) {
9393
commits.push(commit);
9494
}
9595

@@ -197,7 +197,6 @@ describe<{
197197
from: transaction.from,
198198
gasLimit: BigInt(transaction.gasLimit),
199199
gasPrice: BigInt(transaction.gasPrice),
200-
index: transaction.transactionIndex,
201200
nonce: transaction.nonce,
202201
specId: Enums.Evm.SpecId.LATEST,
203202
to: transaction.to,
@@ -237,7 +236,9 @@ describe<{
237236
});
238237

239238
it("#findCommitBuffers - should return commit buffer", async ({ databaseService, genesisCommit }) => {
240-
assert.equal(await databaseService.findCommitBuffers(0, 1), [Buffer.from(genesisCommit.serialized, "hex")]);
239+
assert.equal(await databaseService.findCommitBuffers(0, 1, Number.MAX_SAFE_INTEGER), [
240+
Buffer.from(genesisCommit.serialized, "hex"),
241+
]);
241242
});
242243

243244
it("#getBlock - should return block", async ({ databaseService, genesisCommit }) => {
@@ -269,7 +270,7 @@ describe<{
269270

270271
it("#readCommits - should return commits", async ({ databaseService, genesisCommit }) => {
271272
const commits = [];
272-
for await (const commit of databaseService.readCommits(0, 1)) {
273+
for await (const commit of databaseService.readCommits(0, 1, Number.MAX_SAFE_INTEGER)) {
273274
commits.push(commit);
274275
}
275276
assert.equal(

packages/database/source/database-service.ts

Lines changed: 30 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -38,26 +38,14 @@ export class DatabaseService implements Contracts.Database.DatabaseService {
3838
return blockNumber !== undefined;
3939
}
4040

41-
public async findCommitBuffers(start: number, end: number): Promise<Buffer[]> {
42-
const blockNumbers: number[] = [];
41+
public async findCommitBuffers(start: number, end: number, maxBytes: number): Promise<Buffer[]> {
42+
const buffers: Buffer[] = [];
4343

44-
for (const blockNumber of this.#range(start, end)) {
45-
blockNumbers.push(blockNumber);
44+
for await (const commit of this.readCommits(start, end, maxBytes)) {
45+
buffers.push(Buffer.from(commit.serialized, "hex"));
4646
}
4747

48-
const buffers = await Promise.all(
49-
blockNumbers.map(async (blockNumber: number) => {
50-
const commitStorage = await this.#readCommitStorage(blockNumber);
51-
if (!commitStorage) {
52-
return;
53-
}
54-
55-
const commit = await this.commitFactory.fromStorage(commitStorage);
56-
return Buffer.from(commit.serialized, "hex");
57-
}),
58-
);
59-
60-
return buffers.filter((commit) => !!commit);
48+
return buffers;
6149
}
6250

6351
public async getBlock(blockNumber: number): Promise<Contracts.Crypto.Block | undefined> {
@@ -106,12 +94,13 @@ export class DatabaseService implements Contracts.Database.DatabaseService {
10694
}
10795

10896
public async findBlocks(start: number, end: number): Promise<Contracts.Crypto.Block[]> {
109-
const commitBuffers = await this.findCommitBuffers(start, end);
97+
const blocks: Contracts.Crypto.Block[] = [];
98+
99+
for await (const commit of this.readCommits(start, end, Number.MAX_SAFE_INTEGER)) {
100+
blocks.push(commit.block);
101+
}
110102

111-
return await this.#map(
112-
commitBuffers,
113-
async (buffer: Buffer) => (await this.commitFactory.fromBytes(buffer)).block,
114-
);
103+
return blocks;
115104
}
116105

117106
public async getTransactionByHash(transactionHash: string): Promise<Contracts.Crypto.BlockTransaction | undefined> {
@@ -143,16 +132,29 @@ export class DatabaseService implements Contracts.Database.DatabaseService {
143132
return this.#readTransaction(`${blockNumber}-${index}`);
144133
}
145134

146-
public async *readCommits(start: number, end: number): AsyncGenerator<Contracts.Crypto.Commit> {
147-
for (let blockNumber = start; blockNumber <= end; blockNumber++) {
148-
const data = await this.#readCommitStorage(blockNumber);
135+
public async *readCommits(start: number, end: number, maxBytes: number): AsyncGenerator<Contracts.Crypto.Commit> {
136+
let from = Math.max(0, start);
137+
let remainingBytes = maxBytes;
149138

150-
if (!data) {
139+
while (from <= end) {
140+
const commitsData = await this.storage.getCommitsByBlockRange(from, end, remainingBytes);
141+
if (commitsData.length === 0) {
151142
return;
152143
}
153144

154-
const commit = await this.commitFactory.fromStorage(data);
155-
yield commit;
145+
let lastBlockNumber = from;
146+
for (const data of commitsData) {
147+
const commit = await this.commitFactory.fromStorage(data);
148+
lastBlockNumber = commit.block.number;
149+
yield commit;
150+
151+
remainingBytes -= commit.serialized.length / 2;
152+
if (remainingBytes <= 0) {
153+
return;
154+
}
155+
}
156+
157+
from = lastBlockNumber + 1;
156158
}
157159
}
158160

@@ -201,19 +203,4 @@ export class DatabaseService implements Contracts.Database.DatabaseService {
201203

202204
return this.transactionFactory.fromStorage({ ...transactionStorageData, blockHash: blockHeaderData.hash });
203205
}
204-
205-
async #map<T, U>(data: U[], callback: (...arguments_: U[]) => Promise<T>): Promise<T[]> {
206-
const result: T[] = [];
207-
for (const [index, datum] of data.entries()) {
208-
result[index] = await callback(datum);
209-
}
210-
211-
return result;
212-
}
213-
214-
*#range(start: number, end: number): Generator<number> {
215-
for (let index = start; index <= end; index++) {
216-
yield index;
217-
}
218-
}
219206
}

packages/evm-service/source/instances/evm.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,14 @@ export class EvmInstance implements Contracts.Evm.Instance, Contracts.Evm.Storag
208208
return result;
209209
}
210210

211+
public async getCommitsByBlockRange(
212+
fromBlockNumber: number,
213+
toBlockNumber: number,
214+
maxBytes: number,
215+
): Promise<Contracts.Evm.CommitStorageData[]> {
216+
return this.#evm.getCommitsByBlockRange(BigInt(fromBlockNumber), BigInt(toBlockNumber), BigInt(maxBytes));
217+
}
218+
211219
public async getTransactionData(key: string): Promise<Contracts.Evm.TransactionStorageData | undefined> {
212220
const result = await this.#evm.getTransactionData(key);
213221
if (result === null || result === undefined) {

packages/evm/bindings/src/lib.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -915,6 +915,27 @@ impl EvmInner {
915915
Ok(Some((proof, header, txs)))
916916
}
917917

918+
pub fn get_commits_by_block_range(
919+
&mut self,
920+
from_block_number: u64,
921+
to_block_number: u64,
922+
max_bytes: u64,
923+
) -> std::result::Result<
924+
Vec<(ProofData, BlockHeaderData, Vec<TransactionData>)>,
925+
EVMError<String>,
926+
> {
927+
match self.persistent_db.get_commits_by_block_range(
928+
from_block_number,
929+
to_block_number,
930+
max_bytes,
931+
) {
932+
Ok(commits) => Ok(commits),
933+
Err(err) => Err(EVMError::Database(
934+
format!("failed reading commits by block range: {}", err).into(),
935+
)),
936+
}
937+
}
938+
918939
pub fn get_transaction_data(
919940
&mut self,
920941
key: String,
@@ -1625,6 +1646,33 @@ impl JsEvmWrapper {
16251646
)
16261647
}
16271648

1649+
#[napi]
1650+
pub fn get_commits_by_block_range<'env>(
1651+
&mut self,
1652+
env: &'env Env,
1653+
from_block_number: BigInt,
1654+
to_block_number: BigInt,
1655+
max_bytes: BigInt,
1656+
) -> Result<PromiseRaw<'env, Vec<JsCommitData>>> {
1657+
let from_block_number = from_block_number.get_u64().1;
1658+
let to_block_number = to_block_number.get_u64().1;
1659+
let max_bytes = max_bytes.get_u64().1;
1660+
env.spawn_future_with_callback(
1661+
Self::get_commits_by_block_range_async(
1662+
self.evm.clone(),
1663+
from_block_number,
1664+
to_block_number,
1665+
max_bytes,
1666+
),
1667+
|_, result| {
1668+
Ok(result
1669+
.into_iter()
1670+
.map(|(proof, header, txs)| JsCommitData::new(proof, header, txs))
1671+
.collect())
1672+
},
1673+
)
1674+
}
1675+
16281676
#[napi]
16291677
pub fn get_transaction_data<'env>(
16301678
&mut self,
@@ -2050,6 +2098,21 @@ impl JsEvmWrapper {
20502098
}
20512099
}
20522100

2101+
async fn get_commits_by_block_range_async(
2102+
evm: Arc<tokio::sync::Mutex<EvmInner>>,
2103+
from_block_number: u64,
2104+
to_block_number: u64,
2105+
max_bytes: u64,
2106+
) -> Result<Vec<(ProofData, BlockHeaderData, Vec<TransactionData>)>> {
2107+
let mut lock = evm.lock().await;
2108+
let result = lock.get_commits_by_block_range(from_block_number, to_block_number, max_bytes);
2109+
2110+
match result {
2111+
Ok(result) => Result::Ok(result),
2112+
Err(err) => Result::Err(serde::de::Error::custom(err)),
2113+
}
2114+
}
2115+
20532116
async fn get_transaction_data_async(
20542117
evm: Arc<tokio::sync::Mutex<EvmInner>>,
20552118
key: String,

0 commit comments

Comments
 (0)