diff --git a/apps/ensapi/src/handlers/api/explore/registrar-actions-api.ts b/apps/ensapi/src/handlers/api/explore/registrar-actions-api.ts index 1e14060ab..89a0093a6 100644 --- a/apps/ensapi/src/handlers/api/explore/registrar-actions-api.ts +++ b/apps/ensapi/src/handlers/api/explore/registrar-actions-api.ts @@ -1,3 +1,5 @@ +import { trace } from "@opentelemetry/api"; + import { buildPageContext, type Node, @@ -10,6 +12,7 @@ import { } from "@ensnode/ensnode-sdk"; import { createApp } from "@/lib/hono-factory"; +import { withActiveSpanAsync } from "@/lib/instrumentation/auto-span"; import { makeLogger } from "@/lib/logger"; import { findRegistrarActions } from "@/lib/registrar-actions/find-registrar-actions"; import { indexingStatusMiddleware } from "@/middleware/indexing-status.middleware"; @@ -24,56 +27,69 @@ import { const app = createApp({ middlewares: [indexingStatusMiddleware, registrarActionsApiMiddleware] }); const logger = makeLogger("registrar-actions-api"); +const tracer = trace.getTracer("registrar-actions-api"); // Shared business logic for fetching registrar actions async function fetchRegistrarActions(parentNode: Node | undefined, query: RegistrarActionsQuery) { - const { - orderBy, - page, - recordsPerPage, - withReferral, - decodedReferrer, - beginTimestamp, - endTimestamp, - } = query; - - const filters: RegistrarActionsFilter[] = []; - - if (parentNode) { - filters.push(registrarActionsFilter.byParentNode(parentNode)); - } - - if (withReferral) { - filters.push(registrarActionsFilter.withReferral(true)); - } - - if (decodedReferrer) { - filters.push(registrarActionsFilter.byDecodedReferrer(decodedReferrer)); - } - - if (beginTimestamp) { - filters.push(registrarActionsFilter.beginTimestamp(beginTimestamp)); - } - - if (endTimestamp) { - filters.push(registrarActionsFilter.endTimestamp(endTimestamp)); - } - - // Calculate offset from page and recordsPerPage - const offset = (page - 1) * recordsPerPage; - - // Find the latest "logical registrar actions" with pagination - const { registrarActions, totalRecords } = await findRegistrarActions({ - filters, - orderBy, - limit: recordsPerPage, - offset, - }); - - // Build page context - const pageContext = buildPageContext(page, recordsPerPage, totalRecords); - - return { registrarActions, pageContext }; + return withActiveSpanAsync( + tracer, + "fetchRegistrarActions", + { + parentNode: parentNode ?? "undefined", + page: query.page, + recordsPerPage: query.recordsPerPage, + orderBy: query.orderBy, + }, + async () => { + const { + orderBy, + page, + recordsPerPage, + withReferral, + decodedReferrer, + beginTimestamp, + endTimestamp, + } = query; + + const filters: RegistrarActionsFilter[] = []; + + if (parentNode) { + filters.push(registrarActionsFilter.byParentNode(parentNode)); + } + + if (withReferral) { + filters.push(registrarActionsFilter.withReferral(true)); + } + + if (decodedReferrer) { + filters.push(registrarActionsFilter.byDecodedReferrer(decodedReferrer)); + } + + if (beginTimestamp) { + filters.push(registrarActionsFilter.beginTimestamp(beginTimestamp)); + } + + if (endTimestamp) { + filters.push(registrarActionsFilter.endTimestamp(endTimestamp)); + } + + // Calculate offset from page and recordsPerPage + const offset = (page - 1) * recordsPerPage; + + // Find the latest "logical registrar actions" with pagination + const { registrarActions, totalRecords } = await findRegistrarActions({ + filters, + orderBy, + limit: recordsPerPage, + offset, + }); + + // Build page context + const pageContext = buildPageContext(page, recordsPerPage, totalRecords); + + return { registrarActions, pageContext }; + }, + ); } /** diff --git a/apps/ensapi/src/lib/registrar-actions/find-registrar-actions.ts b/apps/ensapi/src/lib/registrar-actions/find-registrar-actions.ts index faee5c0ec..91bc5b402 100644 --- a/apps/ensapi/src/lib/registrar-actions/find-registrar-actions.ts +++ b/apps/ensapi/src/lib/registrar-actions/find-registrar-actions.ts @@ -1,3 +1,4 @@ +import { trace } from "@opentelemetry/api"; import { and, count, desc, eq, gte, isNotNull, lte, not, type SQL } from "drizzle-orm/sql"; import { @@ -21,6 +22,9 @@ import { } from "@ensnode/ensnode-sdk"; import { ensDb, ensIndexerSchema } from "@/lib/ensdb/singleton"; +import { withSpanAsync } from "@/lib/instrumentation/auto-span"; + +const tracer = trace.getTracer("registrar-actions"); /** * Build SQL for order clause from provided order param. @@ -97,33 +101,30 @@ interface FindRegistrarActionsOptions { export async function _countRegistrarActions( filters: RegistrarActionsFilter[] | undefined, ): Promise { - const countQuery = ensDb - .select({ - count: count(), - }) - .from(ensIndexerSchema.registrarActions) - // join Registration Lifecycles associated with Registrar Actions - .innerJoin( - ensIndexerSchema.registrationLifecycles, - eq(ensIndexerSchema.registrarActions.node, ensIndexerSchema.registrationLifecycles.node), - ) - // join Domains associated with Registration Lifecycles - .innerJoin( - ensIndexerSchema.subgraph_domain, - eq(ensIndexerSchema.registrationLifecycles.node, ensIndexerSchema.subgraph_domain.id), - ) - // join Subregistries associated with Registration Lifecycles - .innerJoin( - ensIndexerSchema.subregistries, - eq( - ensIndexerSchema.registrationLifecycles.subregistryId, - ensIndexerSchema.subregistries.subregistryId, - ), - ) - .where(and(...buildWhereClause(filters))); - - const result = await countQuery; - return result[0].count; + return withSpanAsync( + tracer, + "registrarActions.count", + { filterCount: filters?.length ?? 0 }, + async () => { + const result = await ensDb + .select({ count: count() }) + .from(ensIndexerSchema.registrarActions) + .innerJoin( + ensIndexerSchema.registrationLifecycles, + eq(ensIndexerSchema.registrarActions.node, ensIndexerSchema.registrationLifecycles.node), + ) + .innerJoin( + ensIndexerSchema.subregistries, + eq( + ensIndexerSchema.registrationLifecycles.subregistryId, + ensIndexerSchema.subregistries.subregistryId, + ), + ) + .where(and(...buildWhereClause(filters))); + + return result[0].count; + }, + ); } /** @@ -131,40 +132,47 @@ export async function _countRegistrarActions( * build a list of {@link NamedRegistrarAction} objects. */ export async function _findRegistrarActions(options: FindRegistrarActionsOptions) { - const query = ensDb - .select({ - registrarActions: ensIndexerSchema.registrarActions, - registrationLifecycles: ensIndexerSchema.registrationLifecycles, - subregistries: ensIndexerSchema.subregistries, - domain: ensIndexerSchema.subgraph_domain, - }) - .from(ensIndexerSchema.registrarActions) - // join Registration Lifecycles associated with Registrar Actions - .innerJoin( - ensIndexerSchema.registrationLifecycles, - eq(ensIndexerSchema.registrarActions.node, ensIndexerSchema.registrationLifecycles.node), - ) - // join Domains associated with Registration Lifecycles - .innerJoin( - ensIndexerSchema.subgraph_domain, - eq(ensIndexerSchema.registrationLifecycles.node, ensIndexerSchema.subgraph_domain.id), - ) - // join Subregistries associated with Registration Lifecycles - .innerJoin( - ensIndexerSchema.subregistries, - eq( - ensIndexerSchema.registrationLifecycles.subregistryId, - ensIndexerSchema.subregistries.subregistryId, - ), - ) - .where(and(...buildWhereClause(options.filters))) - .orderBy(buildOrderByClause(options.orderBy)) - .limit(options.limit) - .offset(options.offset); - - const records = await query; - - return records; + return withSpanAsync( + tracer, + "registrarActions.find", + { + filterCount: options.filters?.length ?? 0, + orderBy: options.orderBy, + limit: options.limit, + offset: options.offset, + }, + () => + ensDb + .select({ + registrarActions: ensIndexerSchema.registrarActions, + registrationLifecycles: ensIndexerSchema.registrationLifecycles, + subregistries: ensIndexerSchema.subregistries, + domain: ensIndexerSchema.subgraph_domain, + }) + .from(ensIndexerSchema.registrarActions) + // join Registration Lifecycles associated with Registrar Actions + .innerJoin( + ensIndexerSchema.registrationLifecycles, + eq(ensIndexerSchema.registrarActions.node, ensIndexerSchema.registrationLifecycles.node), + ) + // join Domains associated with Registration Lifecycles + .innerJoin( + ensIndexerSchema.subgraph_domain, + eq(ensIndexerSchema.registrationLifecycles.node, ensIndexerSchema.subgraph_domain.id), + ) + // join Subregistries associated with Registration Lifecycles + .innerJoin( + ensIndexerSchema.subregistries, + eq( + ensIndexerSchema.registrationLifecycles.subregistryId, + ensIndexerSchema.subregistries.subregistryId, + ), + ) + .where(and(...buildWhereClause(options.filters))) + .orderBy(buildOrderByClause(options.orderBy)) + .limit(options.limit) + .offset(options.offset), + ); } type MapToNamedRegistrarActionArgs = Awaited>[0]; @@ -176,7 +184,7 @@ type MapToNamedRegistrarActionArgs = Awaited