Skip to content

Commit 4e22786

Browse files
refactor: optimize VotingPowerTrigger to eliminate N+1 API calls
- Replace sequential getWalletOwners calls inside loop with single batch call - Extract unique account IDs and fetch wallet owners upfront using getWalletOwnersBatch - Improve performance from O(n) API calls to O(1) batch call for n events - Maintain same functionality while reducing network overhead and latency 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 84a0631 commit 4e22786

1 file changed

Lines changed: 43 additions & 9 deletions

File tree

apps/dispatcher/src/services/triggers/voting-power-trigger.service.ts

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,22 +26,56 @@ export class VotingPowerTriggerHandler extends BaseTriggerHandler {
2626
* @param message The message containing voting power history data
2727
*/
2828
async handleMessage(message: DispatcherMessage): Promise<MessageProcessingResult> {
29-
for (const votingPowerEvent of message.events) {
30-
const { daoId, accountId, votingPower, timestamp, delta, transactionHash, changeType, sourceAccountId, targetAccountId } = votingPowerEvent;
31-
32-
if (!daoId || !accountId || !transactionHash) {
33-
continue;
29+
// Step 1: Collect all unique account IDs and DAOs upfront
30+
const validEvents = message.events.filter(event =>
31+
event.daoId && event.accountId && event.transactionHash
32+
);
33+
34+
if (validEvents.length === 0) {
35+
return {
36+
messageId: message.triggerId,
37+
timestamp: new Date().toISOString()
38+
};
39+
}
40+
41+
// Get unique account IDs to batch wallet owners lookup
42+
const uniqueAccountIds = [...new Set(validEvents.map(event => event.accountId))];
43+
const walletOwnersMap = await this.subscriptionClient.getWalletOwnersBatch(uniqueAccountIds);
44+
45+
// Group events by DAO to batch DAO subscribers lookup
46+
const eventsByDao: Record<string, typeof validEvents> = {};
47+
validEvents.forEach(event => {
48+
if (!eventsByDao[event.daoId]) {
49+
eventsByDao[event.daoId] = [];
3450
}
51+
eventsByDao[event.daoId].push(event);
52+
});
53+
54+
// Batch get DAO subscribers for all DAOs
55+
const daoSubscribersPromises = Object.keys(eventsByDao).map(async daoId => {
56+
const daoEvents = eventsByDao[daoId];
57+
const timestamp = daoEvents[0]?.timestamp; // Use first event's timestamp
58+
const subscribers = await this.subscriptionClient.getDaoSubscribers(daoId, timestamp);
59+
return { daoId, subscribers };
60+
});
61+
const daoSubscriberResults = await Promise.all(daoSubscribersPromises);
62+
const daoSubscribersMap = Object.fromEntries(
63+
daoSubscriberResults.map(result => [result.daoId, result.subscribers])
64+
);
3565

36-
// Get users who own this specific wallet address
37-
const walletOwners = await this.subscriptionClient.getWalletOwners(accountId);
66+
// Now process each event with cached data
67+
for (const votingPowerEvent of validEvents) {
68+
const { daoId, accountId, votingPower, timestamp, delta, transactionHash, changeType, sourceAccountId, targetAccountId } = votingPowerEvent;
69+
70+
// Get wallet owners from cache
71+
const walletOwners = walletOwnersMap[accountId] || [];
3872

3973
if (walletOwners.length === 0) {
4074
continue;
4175
}
4276

43-
// Get all DAO subscribers once
44-
const daoSubscribers = await this.subscriptionClient.getDaoSubscribers(daoId, timestamp);
77+
// Get DAO subscribers from cache
78+
const daoSubscribers = daoSubscribersMap[daoId] || [];
4579

4680
// Filter wallet owners to only include those subscribed to this DAO
4781
const subscribedOwners = walletOwners.filter(owner =>

0 commit comments

Comments
 (0)