Skip to content

Commit 096bd38

Browse files
committed
refactor: changed indexes approach and graphql resolvers to make transfers query faster for all use cases
1 parent 4a5447d commit 096bd38

File tree

8 files changed

+320
-79
lines changed

8 files changed

+320
-79
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
'use strict';
2+
3+
/** @type {import('sequelize-cli').Migration} */
4+
module.exports = {
5+
async up(queryInterface) {
6+
await queryInterface.removeIndex('Transfers', 'transfers_creationtime_id_idx');
7+
await queryInterface.removeIndex('Transfers', 'transfers_from_acct_creationtime_id_idx');
8+
await queryInterface.removeIndex('Transfers', 'transfers_to_acct_creationtime_id_idx');
9+
await queryInterface.removeIndex('Transfers', 'transfers_type_idx');
10+
await queryInterface.removeIndex('Transfers', 'transfers_from_acct_modulename_idx');
11+
await queryInterface.removeIndex('Transfers', 'transfers_chainid_from_acct_modulename_idx');
12+
await queryInterface.removeIndex('Transfers', 'transfers_chainid_to_acct_modulename_idx');
13+
},
14+
15+
async down(queryInterface) {
16+
await queryInterface.addIndex('Transfers', ['creationtime', 'id'], {
17+
name: 'transfers_creationtime_id_idx',
18+
});
19+
await queryInterface.addIndex('Transfers', ['from_acct', 'creationtime', 'id'], {
20+
name: 'transfers_from_acct_creationtime_id_idx',
21+
});
22+
await queryInterface.addIndex('Transfers', ['to_acct', 'creationtime', 'id'], {
23+
name: 'transfers_to_acct_creationtime_id_idx',
24+
});
25+
await queryInterface.addIndex('Transfers', ['type'], {
26+
name: 'transfers_type_idx',
27+
});
28+
await queryInterface.addIndex('Transfers', ['from_acct', 'modulename'], {
29+
name: 'transfers_from_acct_modulename_idx',
30+
});
31+
await queryInterface.addIndex('Transfers', ['chainId', 'from_acct', 'modulename'], {
32+
name: 'transfers_chainid_from_acct_modulename_idx',
33+
});
34+
await queryInterface.addIndex('Transfers', ['chainId', 'to_acct', 'modulename'], {
35+
name: 'transfers_chainid_to_acct_modulename_idx',
36+
});
37+
},
38+
};
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
'use strict';
2+
3+
/** @type {import('sequelize-cli').Migration} */
4+
module.exports = {
5+
async up(queryInterface, Sequelize) {
6+
// For use case 1: where hasTokenId = ...
7+
await queryInterface.sequelize.query(
8+
'ALTER INDEX "transfers_hasTokenId_idx" RENAME TO "transfers_hasTokenId_count_idx";',
9+
);
10+
await queryInterface.addIndex('Transfers', ['hasTokenId', 'creationtime', 'id'], {
11+
name: 'transfers_hasTokenId_creationtime_id_idx',
12+
});
13+
14+
// For use case 2: where modulename = ...
15+
await queryInterface.sequelize.query(
16+
'ALTER INDEX transfers_modulename_idx RENAME TO transfers_modulename_count_idx;',
17+
);
18+
await queryInterface.addIndex('Transfers', ['modulename', 'creationtime', 'id'], {
19+
name: 'transfers_modulename_creationtime_id_idx',
20+
});
21+
22+
// For use case 3: where (from_acct = ... OR to_acct = ...) and hasTokenId = ...
23+
await queryInterface.addIndex('Transfers', ['from_acct', 'hasTokenId'], {
24+
name: 'transfers_from_acct_hasTokenId_count_idx',
25+
});
26+
await queryInterface.addIndex('Transfers', ['to_acct', 'hasTokenId'], {
27+
name: 'transfers_to_acct_hasTokenId_count_idx',
28+
});
29+
await queryInterface.addIndex('Transfers', ['from_acct', 'hasTokenId', 'creationtime', 'id'], {
30+
name: 'transfers_from_acct_hasTokenId_creationtime_id_idx',
31+
});
32+
await queryInterface.addIndex('Transfers', ['to_acct', 'hasTokenId', 'creationtime', 'id'], {
33+
name: 'transfers_to_acct_hasTokenId_creationtime_id_idx',
34+
});
35+
36+
// -- For use case 4: where chainId = ... and hasTokenId = ...
37+
await queryInterface.addIndex('Transfers', ['chainId', 'hasTokenId'], {
38+
name: 'transfers_chainId_hasTokenId_idx',
39+
});
40+
await queryInterface.addIndex('Transfers', ['chainId', 'hasTokenId', 'creationtime', 'id'], {
41+
name: 'transfers_chainId_hasTokenId_creationtime_id_idx',
42+
});
43+
44+
// -- For use case 5: where (from_acct = ... OR to_acct = ...) and hasTokenId = ... and chainId = ...
45+
await queryInterface.addIndex('Transfers', ['from_acct', 'hasTokenId', 'chainId'], {
46+
name: 'transfers_from_acct_hasTokenId_chainId_count_idx',
47+
});
48+
await queryInterface.addIndex('Transfers', ['to_acct', 'hasTokenId', 'chainId'], {
49+
name: 'transfers_to_acct_hasTokenId_chainId_count_idx',
50+
});
51+
await queryInterface.addIndex(
52+
'Transfers',
53+
['from_acct', 'hasTokenId', 'chainId', 'creationtime', 'id'],
54+
{
55+
name: 'transfers_from_acct_hasTokenId_chainId_creationtime_id_idx',
56+
},
57+
);
58+
await queryInterface.addIndex(
59+
'Transfers',
60+
['to_acct', 'hasTokenId', 'chainId', 'creationtime', 'id'],
61+
{
62+
name: 'transfers_to_acct_hasTokenId_chainId_creationtime_id_idx',
63+
},
64+
);
65+
66+
// -- For use case 6: where (from_acct = ... OR to_acct = ...) ... and modulename = ...
67+
await queryInterface.addIndex('Transfers', ['from_acct', 'modulename'], {
68+
name: 'transfers_from_acct_modulename_count_idx',
69+
});
70+
await queryInterface.addIndex('Transfers', ['to_acct', 'modulename'], {
71+
name: 'transfers_to_acct_modulename_count_idx',
72+
});
73+
await queryInterface.addIndex('Transfers', ['from_acct', 'modulename', 'creationtime', 'id'], {
74+
name: 'transfers_from_acct_modulename_creationtime_id_idx',
75+
});
76+
await queryInterface.addIndex('Transfers', ['to_acct', 'modulename', 'creationtime', 'id'], {
77+
name: 'transfers_to_acct_modulename_creationtime_id_idx',
78+
});
79+
80+
// -- For use case 7: where (from_acct = ... OR to_acct = ...) ... and chainId = ... and modulename = ...
81+
await queryInterface.addIndex('Transfers', ['from_acct', 'chainId', 'modulename'], {
82+
name: 'transfers_from_acct_chainId_modulename_count_idx',
83+
});
84+
await queryInterface.addIndex('Transfers', ['to_acct', 'chainId', 'modulename'], {
85+
name: 'transfers_to_acct_chainId_modulename_count_idx',
86+
});
87+
await queryInterface.addIndex(
88+
'Transfers',
89+
['from_acct', 'chainId', 'modulename', 'creationtime', 'id'],
90+
{
91+
name: 'transfers_from_acct_chainId_modulename_creationtime_id_idx',
92+
},
93+
);
94+
await queryInterface.addIndex(
95+
'Transfers',
96+
['to_acct', 'chainId', 'modulename', 'creationtime', 'id'],
97+
{
98+
name: 'transfers_to_acct_chainId_modulename_creationtime_id_idx',
99+
},
100+
);
101+
},
102+
103+
async down(queryInterface) {
104+
// For use case 1: where hasTokenId = ...
105+
await queryInterface.sequelize.query(
106+
'ALTER INDEX transfers_hasTokenId_count_idx RENAME TO transfers_hasTokenId_idx;',
107+
);
108+
await queryInterface.removeIndex('Transfers', 'transfers_hasTokenId_creationtime_id_idx');
109+
110+
// For use case 2: where modulename = ...
111+
await queryInterface.sequelize.query(
112+
'ALTER INDEX transfers_modulename_count_idx RENAME TO transfers_modulename_idx;',
113+
);
114+
await queryInterface.removeIndex('Transfers', 'transfers_modulename_creationtime_id_idx');
115+
116+
// For use case 3: where (from_acct = ... OR to_acct = ...) and hasTokenId = ...
117+
await queryInterface.removeIndex('Transfers', 'transfers_from_acct_hasTokenId_count_idx');
118+
await queryInterface.removeIndex('Transfers', 'transfers_to_acct_hasTokenId_count_idx');
119+
await queryInterface.removeIndex(
120+
'Transfers',
121+
'transfers_from_acct_hasTokenId_creationtime_id_idx',
122+
);
123+
await queryInterface.removeIndex(
124+
'Transfers',
125+
'transfers_to_acct_hasTokenId_creationtime_id_idx',
126+
);
127+
128+
// For use case 4: where chainId = ... and hasTokenId = ...
129+
await queryInterface.removeIndex('Transfers', 'transfers_chainId_hasTokenId_idx');
130+
await queryInterface.removeIndex(
131+
'Transfers',
132+
'transfers_chainId_hasTokenId_creationtime_id_idx',
133+
);
134+
135+
// For use case 5: where (from_acct = ... OR to_acct = ...) and hasTokenId = ... and chainId = ...
136+
await queryInterface.removeIndex(
137+
'Transfers',
138+
'transfers_from_acct_hasTokenId_chainId_count_idx',
139+
);
140+
await queryInterface.removeIndex('Transfers', 'transfers_to_acct_hasTokenId_chainId_count_idx');
141+
await queryInterface.removeIndex(
142+
'Transfers',
143+
'transfers_from_acct_hasTokenId_chainId_creationtime_id_idx',
144+
);
145+
await queryInterface.removeIndex(
146+
'Transfers',
147+
'transfers_to_acct_hasTokenId_chainId_creationtime_id_idx',
148+
);
149+
150+
// For use case 6: where (from_acct = ... OR to_acct = ...) and modulename = ...
151+
await queryInterface.removeIndex('Transfers', 'transfers_from_acct_modulename_count_idx');
152+
await queryInterface.removeIndex('Transfers', 'transfers_to_acct_modulename_count_idx');
153+
await queryInterface.removeIndex(
154+
'Transfers',
155+
'transfers_from_acct_modulename_creationtime_id_idx',
156+
);
157+
await queryInterface.removeIndex(
158+
'Transfers',
159+
'transfers_to_acct_modulename_creationtime_id_idx',
160+
);
161+
162+
// For use case 7: where (from_acct = ... OR to_acct = ...) and chainId = ... and modulename = ...
163+
await queryInterface.removeIndex(
164+
'Transfers',
165+
'transfers_from_acct_chainId_modulename_count_idx',
166+
);
167+
await queryInterface.removeIndex('Transfers', 'transfers_to_acct_chainId_modulename_count_idx');
168+
await queryInterface.removeIndex(
169+
'Transfers',
170+
'transfers_from_acct_chainId_modulename_creationtime_id_idx',
171+
);
172+
await queryInterface.removeIndex(
173+
'Transfers',
174+
'transfers_to_acct_chainId_modulename_creationtime_id_idx',
175+
);
176+
},
177+
};

indexer/src/kadena-server/config/schema.graphql

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -353,15 +353,17 @@ type Query {
353353
Retrieve transfers. Default page size is 20.
354354
"""
355355
transfers(
356-
accountName: String
357356
after: String
358357
before: String
359-
blockHash: String
360-
chainId: String
361358
first: Int
362-
fungibleName: String
363359
last: Int
360+
361+
blockHash: String
364362
requestKey: String
363+
364+
accountName: String
365+
chainId: String
366+
fungibleName: String
365367
isNFT: Boolean
366368
): QueryTransfersConnection! @complexity(value: 1, multipliers: ["first", "last"])
367369

indexer/src/kadena-server/repository/infra/repository/transfer-db-repository.ts

Lines changed: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,10 @@ export default class TransferDbRepository implements TransferRepository {
103103
queryParams.push(fungibleName);
104104
const op = operator(queryParams.length);
105105
conditions += `\n${op} transfers."modulename" = $${queryParams.length}`;
106+
} else {
107+
queryParams.push(hasTokenId ?? false);
108+
const op = operator(queryParams.length);
109+
conditions += `\n${op} transfers."hasTokenId" = $${queryParams.length}`;
106110
}
107111

108112
if (orderIndex) {
@@ -129,10 +133,6 @@ export default class TransferDbRepository implements TransferRepository {
129133
conditions += `\n${op} b.hash = $${queryParams.length}`;
130134
}
131135

132-
queryParams.push(hasTokenId ?? false);
133-
const op = operator(queryParams.length);
134-
conditions += `\n${op} transfers."hasTokenId" = $${queryParams.length}`;
135-
136136
let query = '';
137137
if (accountName) {
138138
queryParams.push(accountName);
@@ -210,7 +210,7 @@ export default class TransferDbRepository implements TransferRepository {
210210

211211
const { rows } = await rootPgPool.query(query, queryParams);
212212

213-
const edges = rows.map(row => ({
213+
const edges = rows.map((row: any) => ({
214214
cursor: `${row.creationTime.toString()}:${row.id.toString()}`,
215215
node: transferSchemaValidator.validate(row),
216216
}));
@@ -308,12 +308,6 @@ export default class TransferDbRepository implements TransferRepository {
308308

309309
const localOperator = (length: number) => (length > 1 ? `\nAND` : 'WHERE');
310310

311-
if (accountName) {
312-
queryParams.push(accountName);
313-
const op = localOperator(queryParams.length);
314-
conditions += `\n${op} (trans.from_acct = $${queryParams.length} OR trans.to_acct = $${queryParams.length})`;
315-
}
316-
317311
if (blockHash) {
318312
queryParams.push(blockHash);
319313
const op = localOperator(queryParams.length);
@@ -323,7 +317,7 @@ export default class TransferDbRepository implements TransferRepository {
323317
if (chainId) {
324318
queryParams.push(chainId);
325319
const op = localOperator(queryParams.length);
326-
conditions += `${op} b."chainId" = $${queryParams.length}`;
320+
conditions += `${op} trans."chainId" = $${queryParams.length}`;
327321
}
328322

329323
if (transactionId) {
@@ -344,18 +338,57 @@ export default class TransferDbRepository implements TransferRepository {
344338
conditions += `${op} t.requestkey = $${queryParams.length}`;
345339
}
346340

347-
queryParams.push(hasTokenId ?? false);
348-
const op = localOperator(queryParams.length);
349-
conditions += `${op} trans."hasTokenId" = $${queryParams.length}`;
341+
if (!fungibleName) {
342+
queryParams.push(hasTokenId ?? false);
343+
const op = localOperator(queryParams.length);
344+
conditions += `${op} trans."hasTokenId" = $${queryParams.length}`;
345+
}
346+
347+
const hasJoinBlocks = blockHash;
348+
const hasJoinTransactions = transactionId || requestKey || hasJoinBlocks;
349+
350+
let totalCountQuery = '';
351+
if (accountName) {
352+
queryParams.push(accountName);
350353

351-
const totalCountQuery = `
354+
const queryOne = `
355+
SELECT COUNT(*)
356+
FROM "Transfers" trans
357+
${conditions}
358+
AND trans."from_acct" = $${queryParams.length}
359+
`;
360+
const queryTwo = `
361+
SELECT COUNT(*)
362+
FROM "Transfers" trans
363+
${conditions}
364+
AND trans."to_acct" = $${queryParams.length}
365+
`;
366+
367+
const promises = await Promise.all([
368+
rootPgPool.query(queryOne, queryParams),
369+
rootPgPool.query(queryTwo, queryParams),
370+
]);
371+
372+
const countResultOne = parseInt(promises[0].rows[0].count, 10);
373+
const countResultTwo = parseInt(promises[1].rows[0].count, 10);
374+
return countResultOne + countResultTwo;
375+
}
376+
377+
totalCountQuery = `
352378
SELECT COUNT(*) as count
353379
FROM "Transfers" trans
354-
join "Transactions" t on t.id = trans."transactionId"
355-
join "Blocks" b on b.id = t."blockId"
356-
${conditions}
357380
`;
358381

382+
if (hasJoinTransactions) {
383+
totalCountQuery += `\nJOIN "Transactions" t on t.id = trans."transactionId"`;
384+
}
385+
386+
if (hasJoinBlocks) {
387+
totalCountQuery += `\nJOIN "Blocks" b on b.id = t."blockId"`;
388+
}
389+
390+
totalCountQuery += `\n${conditions}`;
391+
359392
const { rows: countResult } = await rootPgPool.query(totalCountQuery, queryParams);
360393

361394
const totalCount = parseInt(countResult[0].count, 10);

0 commit comments

Comments
 (0)