Skip to content

Commit 6dbdbfd

Browse files
committed
Change the queries of getTransactions()
This patch also changes the indexes of the AssetTypeLogs and AddressLogs tables for the API performance.
1 parent 6c486aa commit 6dbdbfd

File tree

5 files changed

+183
-72
lines changed

5 files changed

+183
-72
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"use strict";
2+
3+
const tableName = "AddressLogs";
4+
module.exports = {
5+
up: async (queryInterface, Sequelize) => {
6+
await queryInterface.addIndex(tableName, [
7+
"address",
8+
"blockNumber",
9+
"transactionIndex"
10+
]);
11+
await queryInterface.removeIndex(tableName, ["blockNumber"]);
12+
await queryInterface.removeIndex(tableName, ["address"]);
13+
},
14+
15+
down: async (queryInterface, Sequelize) => {
16+
await queryInterface.addIndex(tableName, ["address"]);
17+
await queryInterface.addIndex(tableName, ["blockNumber"]);
18+
await queryInterface.removeIndex(tableName, [
19+
"address",
20+
"blockNumber",
21+
"transactionIndex"
22+
]);
23+
}
24+
};
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
"use strict";
2+
3+
const tableName = "AssetTypeLogs";
4+
module.exports = {
5+
up: async (queryInterface, Sequelize) => {
6+
await queryInterface.addIndex(tableName, [
7+
"assetType",
8+
"blockNumber",
9+
"transactionIndex"
10+
]);
11+
await queryInterface.removeIndex(tableName, ["assetType"]);
12+
},
13+
14+
down: async (queryInterface, Sequelize) => {
15+
await queryInterface.addIndex(tableName, ["assetType"]);
16+
await queryInterface.removeIndex(tableName, [
17+
"assetType",
18+
"blockNumber",
19+
"transactionIndex"
20+
]);
21+
}
22+
};

src/models/logic/transaction.ts

Lines changed: 111 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
import * as assert from "assert";
22
import { SDK } from "codechain-sdk";
3-
import { H160, H256, SignedTransaction } from "codechain-sdk/lib/core/classes";
3+
import {
4+
AssetAddress,
5+
H256,
6+
PlatformAddress,
7+
SignedTransaction
8+
} from "codechain-sdk/lib/core/classes";
49
import * as _ from "lodash";
5-
import { Transaction } from "sequelize";
610
import * as Sequelize from "sequelize";
11+
import { Transaction } from "sequelize";
712
import * as Exception from "../../exception";
813
import models from "../index";
9-
import { TransactionAttribute, TransactionInstance } from "../transaction";
14+
import { TransactionInstance } from "../transaction";
1015
import { createAddressLog } from "./addressLog";
1116
import { updateAssetScheme } from "./assetscheme";
1217
import { createChangeAssetScheme } from "./changeAssetScheme";
@@ -239,7 +244,7 @@ export async function applyTransaction(
239244

240245
export async function getPendingTransactions(params: {
241246
address?: string | null;
242-
assetType?: H160 | null;
247+
assetType?: string | null;
243248
type?: string[] | null;
244249
page: number;
245250
itemsPerPage: number;
@@ -386,65 +391,129 @@ export async function removeOutdatedPendings(
386391
}
387392
}
388393

389-
async function getTransactionHashes(params: {
390-
address?: string | null;
391-
assetType?: H160 | null;
394+
async function getHashesByPlatformAddress(params: {
395+
address: string;
392396
page: number;
393397
itemsPerPage: number;
394-
type?: string[] | null;
395-
includePending?: boolean | null;
396398
}): Promise<string[]> {
397-
const where: Sequelize.WhereOptions<TransactionAttribute> = {};
398-
if (params.type != null) {
399-
where.type = { [Sequelize.Op.in]: params.type };
399+
const { address, page, itemsPerPage } = params;
400+
try {
401+
return models.AddressLog.findAll({
402+
attributes: ["transactionHash"],
403+
where: {
404+
address
405+
},
406+
order: [["blockNumber", "DESC"], ["transactionIndex", "DESC"]],
407+
limit: itemsPerPage,
408+
offset: (page - 1) * itemsPerPage
409+
}).map(r => r.get("transactionHash"));
410+
} catch (err) {
411+
console.error(err);
412+
throw Exception.DBError();
400413
}
401-
if (params.includePending !== true) {
402-
where.isPending = false;
414+
}
415+
416+
async function getHashesByAssetAddress(params: {
417+
address: string;
418+
assetType?: string | null;
419+
page: number;
420+
itemsPerPage: number;
421+
}): Promise<string[]> {
422+
const { address, assetType, page, itemsPerPage } = params;
423+
try {
424+
return models.AssetAddressLog.findAll({
425+
attributes: ["transactionHash"],
426+
where: {
427+
address,
428+
...(assetType && { assetType })
429+
},
430+
order: [["blockNumber", "DESC"], ["transactionIndex", "DESC"]],
431+
limit: itemsPerPage,
432+
offset: (page - 1) * itemsPerPage
433+
}).map(r => r.get("transactionHash"));
434+
} catch (err) {
435+
console.error(err);
436+
throw Exception.DBError();
403437
}
404-
const { page, itemsPerPage, address, assetType } = params;
438+
}
439+
440+
async function getHashesByAssetType(params: {
441+
assetType: string;
442+
page: number;
443+
itemsPerPage: number;
444+
}): Promise<string[]> {
445+
const { assetType, page, itemsPerPage } = params;
405446
try {
406-
return await models.Transaction.findAll({
407-
subQuery: false,
408-
attributes: ["hash"],
409-
where,
447+
return models.AssetTypeLog.findAll({
448+
attributes: ["transactionHash"],
449+
where: {
450+
assetType
451+
},
410452
order: [["blockNumber", "DESC"], ["transactionIndex", "DESC"]],
411453
limit: itemsPerPage,
412-
offset: (page - 1) * itemsPerPage,
413-
...(address || assetType
414-
? {
415-
group: [
416-
"Transaction.hash",
417-
"Transaction.blockNumber",
418-
"Transaction.transactionIndex"
419-
],
420-
include: buildIncludeArray({
421-
address,
422-
assetType
423-
})
424-
}
425-
: {})
426-
}).map(result => result.get("hash"));
454+
offset: (page - 1) * itemsPerPage
455+
}).map(r => r.get("transactionHash"));
427456
} catch (err) {
428457
console.error(err);
429-
return [];
458+
throw Exception.DBError();
459+
}
460+
}
461+
462+
async function getHashes(params: {
463+
address?: string | null;
464+
assetType?: string | null;
465+
page: number;
466+
itemsPerPage: number;
467+
type?: string[] | null;
468+
includePending?: boolean | null;
469+
}): Promise<string[]> {
470+
const { address, assetType, page, itemsPerPage } = params;
471+
if (address != null && assetType != null) {
472+
return getHashesByAssetAddress({
473+
address,
474+
assetType,
475+
page,
476+
itemsPerPage
477+
});
478+
} else if (address != null) {
479+
if (AssetAddress.check(address)) {
480+
return getHashesByAssetAddress({ address, page, itemsPerPage });
481+
} else if (PlatformAddress.check(address)) {
482+
return getHashesByPlatformAddress({ address, page, itemsPerPage });
483+
}
484+
throw Error(`Invalid address: ${address}`);
485+
} else if (assetType != null) {
486+
return getHashesByAssetType({ assetType, page, itemsPerPage });
430487
}
488+
return models.Transaction.findAll({
489+
attributes: ["hash"],
490+
where: {
491+
...(params.type != null
492+
? { type: { [Sequelize.Op.in]: params.type } }
493+
: {}),
494+
...(params.includePending !== true ? { isPending: false } : {})
495+
},
496+
order: [["blockNumber", "DESC"], ["transactionIndex", "DESC"]],
497+
limit: itemsPerPage,
498+
offset: (page - 1) * itemsPerPage
499+
}).map(result => result.get("hash"));
431500
}
432501

433502
export async function getTransactions(params: {
434503
address?: string | null;
435-
assetType?: H160 | null;
504+
assetType?: string | null;
436505
type?: string[] | null;
437506
page: number;
438507
itemsPerPage: number;
439508
includePending?: boolean | null;
440509
onlyConfirmed?: boolean | null;
441510
confirmThreshold?: number | null;
442511
}) {
443-
const { address, assetType, itemsPerPage } = params;
512+
const { itemsPerPage } = params;
444513
try {
445514
// TODO: Querying twice will waste IO bandwidth and take longer time as long as the response time
446515
// Find a way to merge these queries.
447-
const hashes = await getTransactionHashes(params);
516+
const hashes = await getHashes(params);
448517
assert(
449518
hashes.length <= itemsPerPage,
450519
`The number of hashes(${
@@ -453,14 +522,10 @@ export async function getTransactions(params: {
453522
);
454523
return models.Transaction.findAll({
455524
where: {
456-
hash: hashes,
457-
...buildQueryForTransactions(params)
525+
hash: hashes
458526
},
459527
order: [["blockNumber", "DESC"], ["transactionIndex", "DESC"]],
460-
include: [
461-
...fullIncludeArray,
462-
...buildIncludeArray({ address, assetType })
463-
]
528+
include: [...fullIncludeArray]
464529
});
465530
} catch (err) {
466531
console.error(err);
@@ -511,17 +576,6 @@ export async function getNumberOfEachTransactionType(
511576
}
512577
}
513578

514-
function buildQueryForTransactions(params: {
515-
type?: string[] | null;
516-
includePending?: boolean | null;
517-
}) {
518-
return {
519-
...(params.type ? { type: { [Sequelize.Op.in]: params.type } } : {}),
520-
...(params.includePending !== true ? { isPending: false } : {})
521-
/* FIXME: onlyConfirmed, confirmThreshold */
522-
};
523-
}
524-
525579
// @ts-ignore
526580
export async function deleteByHash(hash: H256) {
527581
try {
@@ -556,7 +610,7 @@ export async function getSuccessfulByTracker(
556610

557611
function buildIncludeArray(params: {
558612
address?: string | null;
559-
assetType?: H160 | null;
613+
assetType?: string | null;
560614
}): Sequelize.IncludeOptions[] {
561615
const { address, assetType } = params;
562616
return [
@@ -578,9 +632,7 @@ function buildIncludeArray(params: {
578632
model: models.AssetTypeLog,
579633
as: "assetTypeLogs",
580634
attributes: [],
581-
where: {
582-
assetType: assetType.value
583-
},
635+
where: { assetType },
584636
required: true
585637
}
586638
])

src/routers/transaction.ts

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -113,14 +113,11 @@ export function handle(context: IndexerContext, router: Router) {
113113
const confirmThreshold =
114114
req.query.confirmThreshold &&
115115
parseInt(req.query.confirmThreshold, 10);
116-
let assetType;
117116
try {
118-
if (assetTypeString) {
119-
assetType = new H160(assetTypeString);
120-
}
121117
const txInsts = await TxModel.getTransactions({
122118
address,
123-
assetType,
119+
assetType:
120+
assetTypeString && H160.ensure(assetTypeString).value,
124121
type:
125122
typeof type === "string" ? type.split(",") : undefined,
126123
page,
@@ -280,13 +277,10 @@ export function handle(context: IndexerContext, router: Router) {
280277
const page = req.query.page || 1;
281278
const itemsPerPage = req.query.itemsPerPage || 15;
282279
try {
283-
let assetType;
284-
if (assetTypeString) {
285-
assetType = new H160(assetTypeString);
286-
}
287280
const pendingTxInsts = await TxModel.getPendingTransactions({
288281
address,
289-
assetType,
282+
assetType:
283+
assetTypeString && H160.ensure(assetTypeString).value,
290284
type:
291285
typeof type === "string" ? type.split(",") : undefined,
292286
page,

test/api/transaction.spec.ts

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,13 +139,32 @@ describe("transaction-api", function() {
139139
getBestBlockNumberStub.restore();
140140
});
141141

142-
it("api /tx with args", async function() {
142+
it("api /tx with assetType filter", async function() {
143143
const assetType = mintRubyTx.getMintedAsset().assetType;
144144
await request(app)
145-
.get(`/api/tx?assetType=${assetType}&type=mintAsset`)
145+
.get(`/api/tx?assetType=${assetType}`)
146146
.expect(200)
147147
.expect(res =>
148-
expect(Object.keys(JSON.parse(res.text)).length).equal(1)
148+
expect(Object.keys(JSON.parse(res.text)).length).gte(1)
149+
);
150+
});
151+
152+
it("api /tx with address filter", async function() {
153+
await request(app)
154+
.get(`/api/tx?address=${aliceAddress.value}`)
155+
.expect(200)
156+
.expect(res =>
157+
expect(Object.keys(JSON.parse(res.text)).length).gte(1)
158+
);
159+
});
160+
161+
it("api /tx with both assetType and address filters", async function() {
162+
const assetType = mintRubyTx.getMintedAsset().assetType;
163+
await request(app)
164+
.get(`/api/tx?address=${aliceAddress.value}&assetType=${assetType}`)
165+
.expect(200)
166+
.expect(res =>
167+
expect(Object.keys(JSON.parse(res.text)).length).gte(1)
149168
);
150169
});
151170

0 commit comments

Comments
 (0)