Skip to content

Commit b4f63dd

Browse files
joojisPark Juhyung
authored andcommitted
Improve the pagination performance of the Block API
1 parent 6937658 commit b4f63dd

File tree

4 files changed

+111
-21
lines changed

4 files changed

+111
-21
lines changed

src/models/logic/block.ts

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import { Block, H256, U64 } from "codechain-sdk/lib/core/classes";
33
import * as _ from "lodash";
44
import * as Sequelize from "sequelize";
55
import * as Exception from "../../exception";
6-
import { BlockInstance } from "../block";
6+
import { blockPagination } from "../../routers/pagination";
7+
import { BlockAttribute, BlockInstance } from "../block";
78
import models from "../index";
89
import * as AddressLogModel from "./addressLog";
910
import * as AssetAddressLogModel from "./assetAddressLog";
@@ -127,27 +128,46 @@ export async function getBlocks(params: {
127128
address?: string;
128129
page?: number | null;
129130
itemsPerPage?: number | null;
131+
firstEvaluatedKey?: [number] | null;
132+
lastEvaluatedKey?: [number] | null;
130133
}) {
131-
const { page = 1, itemsPerPage = 15, address } = params;
132-
let query = {};
133-
if (address) {
134-
query = {
135-
author: address
136-
};
137-
}
134+
const {
135+
address,
136+
page = 1,
137+
itemsPerPage = 15,
138+
firstEvaluatedKey,
139+
lastEvaluatedKey
140+
} = params;
138141
try {
139142
return await models.Block.findAll({
140-
order: [["number", "DESC"]],
143+
order: blockPagination.orderby({
144+
firstEvaluatedKey,
145+
lastEvaluatedKey
146+
}),
141147
limit: itemsPerPage!,
142-
offset: (page! - 1) * itemsPerPage!,
143-
where: query
148+
offset:
149+
firstEvaluatedKey || lastEvaluatedKey
150+
? 0
151+
: (page! - 1) * itemsPerPage!,
152+
where: {
153+
...(address && { author: address }),
154+
...((firstEvaluatedKey || lastEvaluatedKey) &&
155+
blockPagination.where({
156+
firstEvaluatedKey,
157+
lastEvaluatedKey
158+
}))
159+
}
144160
});
145161
} catch (err) {
146162
console.log(err);
147163
throw Exception.DBError();
148164
}
149165
}
150166

167+
export function createBlockEvaluatedKey(block: BlockAttribute): string {
168+
return JSON.stringify([block.number]);
169+
}
170+
151171
export async function getNumberOfBlocks(params: { address?: string }) {
152172
const { address } = params;
153173
try {

src/routers/block.ts

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,13 @@ import * as _ from "lodash";
44
import { IndexerContext } from "../context";
55
import * as BlockModel from "../models/logic/block";
66
import * as TransactionModel from "../models/logic/transaction";
7-
import { syncIfNeeded } from "../models/logic/utils/middleware";
87
import {
8+
parseEvaluatedKey,
9+
syncIfNeeded
10+
} from "../models/logic/utils/middleware";
11+
import { createPaginationResult } from "./pagination";
12+
import {
13+
blockPaginationSchema,
914
blockSchema,
1015
paginationSchema,
1116
syncSchema,
@@ -304,6 +309,16 @@ export function handle(context: IndexerContext, router: Router) {
304309
* in: query
305310
* required: false
306311
* type: number
312+
* - name: firstEvaluatedKey
313+
* description: the evaulated key of the first item in the previous page. It will be used for the pagination
314+
* in: query
315+
* required: false
316+
* type: string
317+
* - name: lastEvaluatedKey
318+
* description: the evaulated key of the last item in the previous page. It will be used for the pagination
319+
* in: query
320+
* required: false
321+
* type: string
307322
* - name: itemsPerPage
308323
* description: items per page for the pagination (default 15)
309324
* in: query
@@ -324,29 +339,44 @@ export function handle(context: IndexerContext, router: Router) {
324339
*/
325340
router.get(
326341
"/block",
342+
parseEvaluatedKey,
327343
validate({
328344
query: {
329345
...blockSchema,
330-
...paginationSchema
346+
...paginationSchema,
347+
...blockPaginationSchema
331348
}
332349
}),
333350
syncIfNeeded(context),
334351
async (req, res, next) => {
335352
const address = req.query.address;
336353
const page = req.query.page && parseInt(req.query.page, 10);
337354
const itemsPerPage =
338-
req.query.itemsPerPage && parseInt(req.query.itemsPerPage, 10);
339-
355+
req.query.itemsPerPage &&
356+
parseInt(req.query.itemsPerPage, 10) + 1;
357+
const lastEvaluatedKey = req.query.lastEvaluatedKey;
358+
const firstEvaluatedKey = req.query.firstEvaluatedKey;
340359
try {
341-
const blockInsts = await BlockModel.getBlocks({
360+
const blocks = await BlockModel.getBlocks({
342361
address,
343362
page,
344-
itemsPerPage
345-
});
346-
const blocks = blockInsts.map(blockInst =>
347-
blockInst.get({ plain: true })
363+
itemsPerPage,
364+
firstEvaluatedKey,
365+
lastEvaluatedKey
366+
}).then(instances =>
367+
instances.map(i => i.get({ plain: true }))
368+
);
369+
res.json(
370+
createPaginationResult({
371+
query: {
372+
firstEvaluatedKey,
373+
lastEvaluatedKey
374+
},
375+
rows: blocks,
376+
getEvaluatedKey: BlockModel.createBlockEvaluatedKey,
377+
itemsPerPage
378+
})
348379
);
349-
res.json(blocks);
350380
} catch (e) {
351381
next(e);
352382
}

src/routers/pagination.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,37 @@ export const utxoPagination = {
109109
}
110110
}
111111
};
112+
113+
type BlockEvaluationKey = [number];
114+
export const blockPagination = {
115+
orderby: (params: {
116+
firstEvaluatedKey?: BlockEvaluationKey | null;
117+
lastEvaluatedKey?: BlockEvaluationKey | null;
118+
}) => {
119+
switch (queryOrder(params)) {
120+
case "forward":
121+
return [["number", "DESC"]];
122+
case "reverse":
123+
return [["number", "ASC"]];
124+
default:
125+
throw Error("Unreachable");
126+
}
127+
},
128+
where: (params: {
129+
firstEvaluatedKey?: BlockEvaluationKey | null;
130+
lastEvaluatedKey?: BlockEvaluationKey | null;
131+
}) => {
132+
switch (queryOrder(params)) {
133+
case "forward": {
134+
const [blockNumber] = params.lastEvaluatedKey!;
135+
return { number: { [Sequelize.Op.lt]: blockNumber } };
136+
}
137+
case "reverse": {
138+
const [blockNumber] = params.firstEvaluatedKey!;
139+
return { number: { [Sequelize.Op.gt]: blockNumber } };
140+
}
141+
default:
142+
throw Error("Unreachable");
143+
}
144+
}
145+
};

src/routers/validator.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ export const utxoPaginationSchema = {
8282
)
8383
};
8484

85+
const blockEvaluationKey = Joi.array().items(Joi.number());
86+
export const blockPaginationSchema = {
87+
lastEvaluatedKey: blockEvaluationKey,
88+
firstEvaluatedKey: blockEvaluationKey
89+
};
90+
8591
export const txSchema = {
8692
address,
8793
assetType: assetTypeSchema,

0 commit comments

Comments
 (0)