Skip to content

Commit 2637cca

Browse files
majectyjoojis
authored andcommitted
Use an index to find rows in the next page in the BlockTxs query
The indexer was using SQL's `skip` for the pagination. The DB should scan the number of skipped rows, to get the page result. For the performance enhancement, we concluded that change the pagination method. Finding a particular row using an index and get the following `n` rows is much faster than using `skip`. This commit uses `firstEvaluatedKey` and `lastEvaluatedKey` for the pagination in the BlockTxs query. The BlockTxs query will find a row using `firstEvaluatedKey` or `lastEvaluatedKey` and returns next `n` rows.
1 parent 8ed669e commit 2637cca

File tree

4 files changed

+110
-8
lines changed

4 files changed

+110
-8
lines changed

src/models/logic/transaction.ts

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
pendingTxPagination,
1616
txPagination
1717
} from "../../routers/pagination";
18+
import { blockTxPagination } from "../../routers/pagination";
1819
import { AddressLogType } from "../addressLog";
1920
import models from "../index";
2021
import { TransactionAttribute, TransactionInstance } from "../transaction";
@@ -811,15 +812,41 @@ export async function getTransactionsOfBlock(params: {
811812
page: number;
812813
itemsPerPage: number;
813814
blockNumber: number;
815+
firstEvaluatedKey?: number[] | null;
816+
lastEvaluatedKey?: number[] | null;
814817
}) {
815818
try {
816-
const { page, itemsPerPage, blockNumber } = params;
819+
const {
820+
page,
821+
itemsPerPage,
822+
blockNumber,
823+
firstEvaluatedKey,
824+
lastEvaluatedKey
825+
} = params;
826+
827+
const whereCond: any[] = [{ blockNumber }];
828+
if (firstEvaluatedKey || lastEvaluatedKey) {
829+
whereCond.push(
830+
blockTxPagination.where({
831+
firstEvaluatedKey,
832+
lastEvaluatedKey
833+
})
834+
);
835+
}
817836

818837
return models.Transaction.findAll({
819-
where: { blockNumber },
820-
order: [["transactionIndex", "DESC"]],
838+
where: {
839+
[Sequelize.Op.and]: whereCond
840+
},
841+
order: blockTxPagination.orderby({
842+
firstEvaluatedKey,
843+
lastEvaluatedKey
844+
}),
821845
limit: itemsPerPage,
822-
offset: (page - 1) * itemsPerPage,
846+
offset:
847+
firstEvaluatedKey || lastEvaluatedKey
848+
? 0
849+
: (page - 1) * itemsPerPage,
823850
include: [...fullIncludeArray]
824851
});
825852
} catch (err) {
@@ -828,6 +855,10 @@ export async function getTransactionsOfBlock(params: {
828855
}
829856
}
830857

858+
export function createBlockTxEvaluatedKey(tx: TransactionAttribute) {
859+
return JSON.stringify([tx.transactionIndex]);
860+
}
861+
831862
export async function getNumberOfEachTransactionType(
832863
params: {
833864
blockNumber: number;

src/routers/block.ts

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { createPaginationResult } from "./pagination";
1212
import {
1313
blockPaginationSchema,
1414
blockSchema,
15+
blockTxPaginationSchema,
1516
paginationSchema,
1617
syncSchema,
1718
validate
@@ -209,6 +210,16 @@ export function handle(context: IndexerContext, router: Router) {
209210
* in: query
210211
* required: false
211212
* type: number
213+
* - name: firstEvaluatedKey
214+
* description: the evaulated key of the first item in the previous page. It will be used for the pagination
215+
* in: query
216+
* required: false
217+
* type: string
218+
* - name: lastEvaluatedKey
219+
* description: the evaulated key of the last item in the previous page. It will be used for the pagination
220+
* in: query
221+
* required: false
222+
* type: string
212223
* responses:
213224
* 200:
214225
* description: Transactions
@@ -219,10 +230,12 @@ export function handle(context: IndexerContext, router: Router) {
219230
*/
220231
router.get(
221232
"/block/:hashOrNumber/tx",
233+
parseEvaluatedKey,
222234
validate({
223235
// FIXME: Throw an error if hashOrNumber is not hash or number
224236
query: {
225-
...paginationSchema
237+
...paginationSchema,
238+
...blockTxPaginationSchema
226239
}
227240
}),
228241
async (req, res, next) => {
@@ -232,17 +245,32 @@ export function handle(context: IndexerContext, router: Router) {
232245
(req.query.itemsPerPage &&
233246
parseInt(req.query.itemsPerPage, 10)) ||
234247
15;
248+
const firstEvaluatedKey = req.query.firstEvaluatedKey;
249+
const lastEvaluatedKey = req.query.lastEvaluatedKey;
235250

236251
try {
237252
const block =
238253
H256.check(hashOrNumber) &&
239254
(await BlockModel.getByHash(new H256(hashOrNumber)));
240255
const txs = await TransactionModel.getTransactionsOfBlock({
241256
page,
242-
itemsPerPage,
243-
blockNumber: block ? block.get().number : hashOrNumber
257+
itemsPerPage: itemsPerPage + 1,
258+
blockNumber: block ? block.get().number : hashOrNumber,
259+
firstEvaluatedKey,
260+
lastEvaluatedKey
244261
});
245-
res.json(txs.map(tx => tx.get({ plain: true })));
262+
res.json(
263+
createPaginationResult({
264+
query: {
265+
firstEvaluatedKey,
266+
lastEvaluatedKey
267+
},
268+
rows: txs.map(tx => tx.get({ plain: true })),
269+
getEvaluatedKey:
270+
TransactionModel.createBlockTxEvaluatedKey,
271+
itemsPerPage
272+
})
273+
);
246274
} catch (e) {
247275
next(e);
248276
}

src/routers/pagination.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,44 @@ export const blockPagination = {
144144
}
145145
};
146146

147+
export const blockTxPagination = {
148+
forwardOrder: [["transactionIndex", "DESC"]],
149+
reverseOrder: [["transactionIndex", "ASC"]],
150+
orderby: (params: {
151+
firstEvaluatedKey?: number[] | null;
152+
lastEvaluatedKey?: number[] | null;
153+
}) => {
154+
const order = queryOrder(params);
155+
if (order === "forward") {
156+
return blockTxPagination.forwardOrder;
157+
} else if (order === "reverse") {
158+
return blockTxPagination.reverseOrder;
159+
}
160+
},
161+
where: (params: {
162+
firstEvaluatedKey?: number[] | null;
163+
lastEvaluatedKey?: number[] | null;
164+
}) => {
165+
const order = queryOrder(params);
166+
const { firstEvaluatedKey, lastEvaluatedKey } = params;
167+
if (order === "forward") {
168+
const transactionIndex = lastEvaluatedKey![0];
169+
return {
170+
transactionIndex: {
171+
[Sequelize.Op.lt]: transactionIndex
172+
}
173+
};
174+
} else if (order === "reverse") {
175+
const transactionIndex = firstEvaluatedKey![0];
176+
return {
177+
transactionIndex: {
178+
[Sequelize.Op.gt]: transactionIndex
179+
}
180+
};
181+
}
182+
}
183+
};
184+
147185
export const aggsUTXOPagination = {
148186
byAssetType: {
149187
forwardOrder: [["assetType", "DESC"]],

src/routers/validator.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,11 @@ export const blockPaginationSchema = {
8888
firstEvaluatedKey: blockEvaluationKey
8989
};
9090

91+
export const blockTxPaginationSchema = {
92+
firstEvaluatedKey: Joi.array().items(Joi.number()),
93+
lastEvaluatedKey: Joi.array().items(Joi.number())
94+
};
95+
9196
export const aggsUTXOPaginationSchema = {
9297
firstEvaluatedKey: Joi.array().items(Joi.string()),
9398
lastEvaluatedKey: Joi.array().items(Joi.string())

0 commit comments

Comments
 (0)