Skip to content

Commit 41060d0

Browse files
committed
Simplify initIndexingOnchainEvents logic
Refactor logic into `IndexingMetadataContextBuilder` class and `StackInfoBuilder` class
1 parent 1ff960e commit 41060d0

9 files changed

Lines changed: 225 additions & 155 deletions

File tree

apps/ensindexer/src/lib/ensdb-writer-worker/ensdb-writer-worker.ts

Lines changed: 25 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,9 @@
1-
import { getUnixTime, secondsToMilliseconds } from "date-fns";
1+
import { secondsToMilliseconds } from "date-fns";
22
import type { Duration } from "enssdk";
33

44
import type { EnsDbWriter } from "@ensnode/ensdb-sdk";
5-
import {
6-
buildCrossChainIndexingStatusSnapshotOmnichain,
7-
buildIndexingMetadataContextInitialized,
8-
type IndexingMetadataContext,
9-
IndexingMetadataContextStatusCodes,
10-
} from "@ensnode/ensnode-sdk";
115

12-
import type { IndexingStatusBuilder } from "@/lib/indexing-status-builder/indexing-status-builder";
6+
import type { IndexingMetadataContextBuilder } from "@/lib/indexing-metadata-context-builder/indexing-metadata-context-builder";
137
import { logger } from "@/lib/logger";
148

159
/**
@@ -36,17 +30,20 @@ export class EnsDbWriterWorker {
3630
private ensDbClient: EnsDbWriter;
3731

3832
/**
39-
* Indexing Status Builder instance used by the worker to read ENSIndexer Indexing Status.
33+
* Indexing Metadata Context Builder instance used by the worker to read {@link IndexingMetadataContext}.
4034
*/
41-
private indexingStatusBuilder: IndexingStatusBuilder;
35+
private indexingMetadataContextBuilder: IndexingMetadataContextBuilder;
4236

4337
/**
4438
* @param ensDbClient ENSDb Writer instance used by the worker to interact with ENSDb.
45-
* @param indexingStatusBuilder Indexing Status Builder instance used by the worker to read ENSIndexer Indexing Status.
39+
* @param indexingMetadataContextBuilder Indexing Metadata Context Builder instance used by the worker to read {@link IndexingMetadataContext}.
4640
*/
47-
constructor(ensDbClient: EnsDbWriter, indexingStatusBuilder: IndexingStatusBuilder) {
41+
constructor(
42+
ensDbClient: EnsDbWriter,
43+
indexingMetadataContextBuilder: IndexingMetadataContextBuilder,
44+
) {
4845
this.ensDbClient = ensDbClient;
49-
this.indexingStatusBuilder = indexingStatusBuilder;
46+
this.indexingMetadataContextBuilder = indexingMetadataContextBuilder;
5047
}
5148

5249
/**
@@ -63,9 +60,9 @@ export class EnsDbWriterWorker {
6360
throw new Error("EnsDbWriterWorker is already running");
6461
}
6562

66-
// Recurring upsert of Indexing Metadata Context into ENSDb.
63+
// Recurring update of the Indexing Metadata Context record in ENSDb.
6764
this.indexingStatusInterval = setInterval(
68-
() => this.upsertIndexingMetadataContext(),
65+
() => this.updateIndexingMetadataContext(),
6966
secondsToMilliseconds(INDEXING_STATUS_RECORD_UPDATE_INTERVAL),
7067
);
7168
}
@@ -90,40 +87,29 @@ export class EnsDbWriterWorker {
9087
}
9188

9289
/**
93-
* Upsert the current Indexing Status Snapshot into ENSDb.
90+
* Update the current Indexing Status Snapshot into ENSDb.
9491
*
9592
* This method is called by the scheduler at regular intervals.
9693
* Errors are logged but not thrown, to keep the worker running.
9794
*/
98-
private async upsertIndexingMetadataContext(): Promise<void> {
95+
private async updateIndexingMetadataContext(): Promise<void> {
9996
try {
100-
// get system timestamp for the current iteration
101-
const snapshotTime = getUnixTime(new Date());
102-
const indexingMetadataContext = await this.ensDbClient.getIndexingMetadataContext();
97+
const indexingMetadataContext =
98+
await this.indexingMetadataContextBuilder.getIndexingMetadataContext();
10399

104-
if (indexingMetadataContext.statusCode === IndexingMetadataContextStatusCodes.Uninitialized) {
105-
throw new Error(
106-
`Cannot upsert Indexing Status Snapshot into ENSDb because Indexing Metadata Context should be be initialized first`,
107-
);
108-
}
109-
110-
const omnichainSnapshot =
111-
await this.indexingStatusBuilder.getOmnichainIndexingStatusSnapshot();
112-
113-
const updatedIndexingMetadataContext = buildIndexingMetadataContextInitialized(
114-
buildCrossChainIndexingStatusSnapshotOmnichain(omnichainSnapshot, snapshotTime),
115-
indexingMetadataContext.stackInfo,
116-
);
117-
118-
await this.ensDbClient.upsertIndexingMetadataContext(updatedIndexingMetadataContext);
100+
await this.ensDbClient.upsertIndexingMetadataContext(indexingMetadataContext);
119101
} catch (error) {
102+
// If any error happens during the update of indexing metadata context record in ENSDb,
103+
// we want to log the error and exit the process with a non-zero exit code,
104+
// since this is a critical failure that prevents the ENSIndexer instance from functioning properly.
120105
logger.error({
121-
msg: "Failed to upsert indexing metadata context",
122-
error,
106+
msg: "Failed to update indexing metadata context record in ENSDb",
123107
module: "EnsDbWriterWorker",
108+
error,
124109
});
125-
// Do not throw the error, as failure to retrieve the Indexing Status
126-
// should not cause the ENSDb Writer Worker to stop functioning.
110+
111+
process.exitCode = 1;
112+
throw error;
127113
}
128114
}
129115
}

apps/ensindexer/src/lib/ensdb-writer-worker/singleton.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ensDbClient } from "@/lib/ensdb/singleton";
2-
import { indexingStatusBuilder } from "@/lib/indexing-status-builder/singleton";
2+
import { indexingMetadataContextBuilder } from "@/lib/indexing-metadata-context-builder/singleton";
33
import { logger } from "@/lib/logger";
44

55
import { EnsDbWriterWorker } from "./ensdb-writer-worker";
@@ -20,7 +20,7 @@ export function startEnsDbWriterWorker() {
2020
throw new Error("EnsDbWriterWorker has already been initialized");
2121
}
2222

23-
ensDbWriterWorker = new EnsDbWriterWorker(ensDbClient, indexingStatusBuilder);
23+
ensDbWriterWorker = new EnsDbWriterWorker(ensDbClient, indexingMetadataContextBuilder);
2424

2525
ensDbWriterWorker
2626
.run()

apps/ensindexer/src/lib/indexing-engines/init-indexing-onchain-events.ts

Lines changed: 15 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -23,28 +23,16 @@
2323
* in this module, which is executed in step 5 b) and is guaranteed to be executed on every ENSIndexer instance startup,
2424
* regardless of the state of Ponder Checkpoints or whether any setup handlers were registered.
2525
*/
26-
import { getUnixTime } from "date-fns";
27-
28-
import {
29-
buildCrossChainIndexingStatusSnapshotOmnichain,
30-
buildEnsIndexerStackInfo,
31-
buildIndexingMetadataContextInitialized,
32-
IndexingMetadataContextStatusCodes,
33-
OmnichainIndexingStatusIds,
34-
validateEnsIndexerPublicConfigCompatibility,
35-
} from "@ensnode/ensnode-sdk";
3626

3727
import { migrateEnsNodeSchema } from "@/lib/ensdb/migrate-ensnode-schema";
3828
import { ensDbClient } from "@/lib/ensdb/singleton";
3929
import { startEnsDbWriterWorker } from "@/lib/ensdb-writer-worker/singleton";
4030
import {
41-
ensRainbowClient,
4231
waitForEnsRainbowToBeHealthy,
4332
waitForEnsRainbowToBeReady,
4433
} from "@/lib/ensrainbow/singleton";
45-
import { indexingStatusBuilder } from "@/lib/indexing-status-builder/singleton";
34+
import { indexingMetadataContextBuilder } from "@/lib/indexing-metadata-context-builder/singleton";
4635
import { logger } from "@/lib/logger";
47-
import { publicConfigBuilder } from "@/lib/public-config-builder/singleton";
4836

4937
/**
5038
* Prepare for executing the "onchain" event handlers.
@@ -74,104 +62,36 @@ import { publicConfigBuilder } from "@/lib/public-config-builder/singleton";
7462
* 1. Make ENSDb instance "ready" for ENSDb clients to use.
7563
*/
7664
export async function initIndexingOnchainEvents(): Promise<void> {
77-
// TODO: wait for ENSDb instance to be healthy
78-
// Ensure the ENSNode Schema in ENSDb is up to date by running any pending migrations.
79-
await migrateEnsNodeSchema();
80-
81-
// Before calling `ensRainbowClient.config()`, we want to make sure that
82-
// the ENSRainbow instance is healthy and ready to serve requests.
83-
// This is a quick check, as we expect the ENSRainbow instance to be healthy
84-
// by the time ENSIndexer instance executes `initIndexingOnchainEvents`.
85-
await waitForEnsRainbowToBeHealthy();
86-
8765
try {
88-
const [
89-
inMemoryIndexingStatusSnapshot,
90-
inMemoryEnsDbPublicConfig,
91-
inMemoryEnsIndexerPublicConfig,
92-
inMemoryEnsRainbowPublicConfig,
93-
storedIndexingMetadataContext,
94-
] = await Promise.all([
95-
indexingStatusBuilder.getOmnichainIndexingStatusSnapshot(),
96-
ensDbClient.buildEnsDbPublicConfig(),
97-
publicConfigBuilder.getPublicConfig(),
98-
ensRainbowClient.config(),
99-
ensDbClient.getIndexingMetadataContext(),
100-
]);
101-
102-
if (
103-
storedIndexingMetadataContext.statusCode === IndexingMetadataContextStatusCodes.Uninitialized
104-
) {
105-
logger.info({
106-
msg: `Indexing Metadata Context is "uninitialized"`,
107-
});
108-
109-
// Invariant: indexing status must be "unstarted" when the indexing metadata context is uninitialized,
110-
// since we haven't started processing any onchain events yet
111-
if (inMemoryIndexingStatusSnapshot.omnichainStatus !== OmnichainIndexingStatusIds.Unstarted) {
112-
throw new Error(
113-
`Omnichain indexing status must be "unstarted" for "uninitialized" Indexing Metadata Context. Provided omnichain indexing status "${inMemoryIndexingStatusSnapshot.omnichainStatus}".`,
114-
);
115-
}
116-
} else {
117-
logger.info({
118-
msg: `Indexing Metadata Context is "initialized"`,
119-
});
120-
logger.debug({
121-
msg: `Indexing Metadata Context`,
122-
indexingStatus: storedIndexingMetadataContext.indexingStatus,
123-
stackInfo: storedIndexingMetadataContext.stackInfo,
124-
});
125-
// if (ensIndexerPublicConfig.ensIndexerBuildId !== storedIndexingMetadataContext.stackInfo.ensIndexer.ensIndexerBuildId) {
126-
// TODO: store the `ensIndexerPublicConfig` object in ENSDb so `storedIndexingMetadataContext.stackInfo.ensIndexer` is updated
127-
// }
128-
const { ensIndexer: storedEnsIndexerPublicConfig } = storedIndexingMetadataContext.stackInfo;
129-
validateEnsIndexerPublicConfigCompatibility(
130-
inMemoryEnsIndexerPublicConfig,
131-
storedEnsIndexerPublicConfig,
132-
);
133-
}
66+
// TODO: wait for ENSDb instance to be healthy before running any queries against it.
13467

135-
// Build the {@link CrossChainIndexingStatusSnapshot} with the current snapshot time.
136-
// This is important to make sure the `snapshotTime` is always up to date in
137-
// the indexing status snapshot stored in ENSDb.
138-
const now = getUnixTime(new Date());
139-
const crossChainIndexingStatusSnapshot = buildCrossChainIndexingStatusSnapshotOmnichain(
140-
inMemoryIndexingStatusSnapshot,
141-
now,
142-
);
68+
// Ensure the ENSNode Schema in ENSDb is up to date by running any pending migrations.
69+
await migrateEnsNodeSchema();
14370

144-
// Build EnsIndexerStackInfo based on the current state of in-memory public
145-
// config objects. It's unlikely, but possible, that after the ENSIndexer
146-
// instance restarts, some values in the public config objects have changed
147-
// compared to the previous instance before the restart. For example,
148-
// if the ENSIndexer instance is redeployed with a new version of the code that has different default values for some config parameters, or if there are changes in the environment variables used to build the public config objects.
149-
const updatedStackInfo = buildEnsIndexerStackInfo(
150-
inMemoryEnsDbPublicConfig,
151-
inMemoryEnsIndexerPublicConfig,
152-
inMemoryEnsRainbowPublicConfig,
153-
);
71+
// Before calling `ensRainbowClient.config()`, we want to make sure that
72+
// the ENSRainbow instance is healthy and ready to serve requests.
73+
// This is a quick check, as we expect the ENSRainbow instance to be healthy
74+
// by the time ENSIndexer instance executes `initIndexingOnchainEvents`.
75+
await waitForEnsRainbowToBeHealthy();
15476

155-
const updatedIndexingMetadataContext = buildIndexingMetadataContextInitialized(
156-
crossChainIndexingStatusSnapshot,
157-
updatedStackInfo,
158-
);
77+
const indexingMetadataContext =
78+
await indexingMetadataContextBuilder.getIndexingMetadataContext();
15979

16080
logger.info({
16181
msg: `Upserting Indexing Metadata Context Initialized`,
16282
});
16383
logger.debug({
16484
msg: `Indexing Metadata Context`,
165-
indexingStatus: updatedIndexingMetadataContext.indexingStatus,
166-
stackInfo: updatedIndexingMetadataContext.stackInfo,
85+
indexingStatus: indexingMetadataContext.indexingStatus,
86+
stackInfo: indexingMetadataContext.stackInfo,
16787
});
168-
await ensDbClient.upsertIndexingMetadataContext(updatedIndexingMetadataContext);
88+
await ensDbClient.upsertIndexingMetadataContext(indexingMetadataContext);
16989
logger.info({
17090
msg: `Successfully upserted Indexing Metadata Context Initialized`,
17191
});
17292

17393
// Before starting to process onchain events, we want to make sure that
174-
// ENSRainbow is ready and ready to serve the "heal" requests.
94+
// ENSRainbow is ready to serve the "heal" requests.
17595
await waitForEnsRainbowToBeReady();
17696

17797
// TODO: start Indexing Status Sync worker

apps/ensindexer/src/lib/indexing-engines/ponder.ts

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,6 @@ function buildEventTypeId(eventName: EventNames): EventTypeId {
127127
}
128128
}
129129

130-
let eventHandlerPreconditionsFullyExecuted = false;
131130
let indexingOnchainEventsPromise: Promise<void> | null = null;
132131

133132
/**
@@ -143,14 +142,6 @@ let indexingOnchainEventsPromise: Promise<void> | null = null;
143142
* "onchain" event.
144143
*/
145144
async function eventHandlerPreconditions(eventType: EventTypeId): Promise<void> {
146-
if (eventHandlerPreconditionsFullyExecuted) {
147-
// Preconditions have already been fully executed, so we can skip executing them again.
148-
// We can also reset the promises for indexing setup and onchain events to free up memory,
149-
// since they will never be used again after the preconditions have been fully executed.
150-
indexingOnchainEventsPromise = null;
151-
return;
152-
}
153-
154145
switch (eventType) {
155146
case EventTypeIds.Setup: {
156147
// For some ENSIndexer instances, the setup handlers are not defined at all,
@@ -168,19 +159,13 @@ async function eventHandlerPreconditions(eventType: EventTypeId): Promise<void>
168159
// since Ponder would not allow us to use static imports for modules
169160
// that internally rely on `ponder:api`. Using dynamic imports solves
170161
// this issue.
171-
indexingOnchainEventsPromise = import("./init-indexing-onchain-events")
172-
.then(({ initIndexingOnchainEvents }) =>
162+
indexingOnchainEventsPromise = import("./init-indexing-onchain-events").then(
163+
({ initIndexingOnchainEvents }) =>
173164
// Init the indexing of "onchain" events just once in order to
174165
// optimize the indexing "hot path", since these events are much
175166
// more frequent than setup events.
176167
initIndexingOnchainEvents(),
177-
)
178-
.then(() => {
179-
// Mark the preconditions as fully executed after the first time we execute
180-
// the preconditions for onchain events, since that's the "hot path" and we want to
181-
// minimize the overhead of this function in the long run.
182-
eventHandlerPreconditionsFullyExecuted = true;
183-
});
168+
);
184169
}
185170

186171
return await indexingOnchainEventsPromise;

0 commit comments

Comments
 (0)