Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions indexer/packages/postgres/src/stores/fill-table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import { knexReadReplica } from '../helpers/knex';
import { setupBaseQuery, verifyAllRequiredFields } from '../helpers/stores-helpers';
import Transaction from '../helpers/transaction';
import { getUuid } from '../helpers/uuid';
import { getSubaccountQueryForParent } from '../lib/parent-subaccount-helpers';
import FillModel from '../models/fill-model';
import {
FillColumns,
Expand All @@ -31,6 +30,7 @@ import {
QueryableField,
QueryConfig,
} from '../types';
import { findIdsForParentSubaccount } from './subaccount-table';

export function uuid(eventId: Buffer, liquidity: Liquidity): string {
// TODO(IND-483): Fix all uuid string substitutions to use Array.join.
Expand Down Expand Up @@ -147,10 +147,11 @@ export async function findAll(
if (subaccountId !== undefined) {
baseQuery = baseQuery.whereIn(FillColumns.subaccountId, subaccountId);
} else if (parentSubaccount !== undefined) {
baseQuery = baseQuery.whereIn(
FillColumns.subaccountId,
getSubaccountQueryForParent(parentSubaccount),
);
// PERFORMANCE CRITICAL: Resolve subaccountIds to concrete UUIDs before querying.
// Using IN (subquery) causes Postgres to misestimate cardinality and scan millions
// of rows. With explicit UUIDs, Postgres uses optimal index scans per subaccount.
const subaccountIds = await findIdsForParentSubaccount(parentSubaccount);
baseQuery = baseQuery.whereIn(FillColumns.subaccountId, subaccountIds);
}

if (side !== undefined) {
Expand Down
34 changes: 33 additions & 1 deletion indexer/packages/postgres/src/stores/subaccount-table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { IndexerSubaccountId } from '@dydxprotocol-indexer/v4-protos';
import { PartialModelObject, QueryBuilder } from 'objection';

import config from '../config';
import { BUFFER_ENCODING_UTF_8, DEFAULT_POSTGRES_OPTIONS } from '../constants';
import { BUFFER_ENCODING_UTF_8, DEFAULT_POSTGRES_OPTIONS, MAX_PARENT_SUBACCOUNTS } from '../constants';
import {
verifyAllRequiredFields,
setupBaseQuery,
Expand Down Expand Up @@ -201,3 +201,35 @@ export async function deleteById(
Transaction.get(options.txId),
).deleteById(id);
}

/**
* Retrieves all subaccount IDs associated with a parent subaccount.
* A subaccount is considered a child of the parent if it has the same address
* and its subaccount number follows the modulo relationship with the parent.
*
* @param parentSubaccount The parent subaccount object with address and subaccountNumber
* @param options Query options including transaction ID
* @returns A promise that resolves to an array of subaccount ID strings
*/
export async function findIdsForParentSubaccount(
parentSubaccount: {
address: string,
subaccountNumber: number,
},
options: Options = DEFAULT_POSTGRES_OPTIONS,
): Promise<string[]> {
// Get all subaccounts for the address
const subaccounts = await findAll(
{ address: parentSubaccount.address },
[],
options,
);

// Filter for subaccounts that match the parent relationship
// (subaccountNumber - parentSubaccountNumber) % MAX_PARENT_SUBACCOUNTS = 0
return subaccounts
.filter((subaccount) => (subaccount.subaccountNumber - parentSubaccount.subaccountNumber) %
MAX_PARENT_SUBACCOUNTS === 0,
)
.map((subaccount) => subaccount.id);
}
Loading