Skip to content

Commit 2b2d79e

Browse files
committed
Merge remote-tracking branch 'origin/main' into feat/integrate-ensdb-with-ensapi
2 parents 76fa60c + 410f937 commit 2b2d79e

17 files changed

Lines changed: 301 additions & 78 deletions

.changeset/better-bats-switch.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"ensindexer": minor
3+
---
4+
5+
Improved developer experience by skipping validation step in ENSDb Writer Worker while in dev mode.

.changeset/old-seals-draw.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@ensnode/ponder-sdk": minor
3+
---
4+
5+
Introduced `PonderAppContext` data model to capture the internal context of a local Ponder app.

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ import {
1212
type OmnichainIndexingStatusSnapshot,
1313
PluginName,
1414
} from "@ensnode/ensnode-sdk";
15+
import type { LocalPonderClient } from "@ensnode/ponder-sdk";
1516

17+
import { EnsDbWriterWorker } from "@/lib/ensdb-writer-worker/ensdb-writer-worker";
1618
import type { IndexingStatusBuilder } from "@/lib/indexing-status-builder";
1719
import type { PublicConfigBuilder } from "@/lib/public-config-builder";
1820

@@ -103,3 +105,37 @@ export function createMockCrossChainSnapshot(
103105
...overrides,
104106
};
105107
}
108+
109+
export function createMockLocalPonderClient(
110+
overrides: { isInDevMode?: boolean } = {},
111+
): LocalPonderClient {
112+
const isInDevMode = overrides.isInDevMode ?? false;
113+
114+
return {
115+
isInDevMode,
116+
} as unknown as LocalPonderClient;
117+
}
118+
119+
export function createMockEnsDbWriterWorker(
120+
overrides: {
121+
ensDbClient?: EnsDbWriter;
122+
publicConfigBuilder?: PublicConfigBuilder;
123+
indexingStatusBuilder?: IndexingStatusBuilder;
124+
isInDevMode?: boolean;
125+
} = {},
126+
) {
127+
const ensDbClient = overrides.ensDbClient ?? createMockEnsDbWriter();
128+
const publicConfigBuilder = overrides.publicConfigBuilder ?? createMockPublicConfigBuilder();
129+
const indexingStatusBuilder =
130+
overrides.indexingStatusBuilder ?? createMockIndexingStatusBuilder();
131+
const localPonderClient = createMockLocalPonderClient({
132+
isInDevMode: overrides.isInDevMode ?? false,
133+
});
134+
135+
return new EnsDbWriterWorker(
136+
ensDbClient,
137+
publicConfigBuilder,
138+
indexingStatusBuilder,
139+
localPonderClient,
140+
);
141+
}

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

Lines changed: 58 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ import {
66
validateEnsIndexerPublicConfigCompatibility,
77
} from "@ensnode/ensnode-sdk";
88

9-
import { EnsDbWriterWorker } from "@/lib/ensdb-writer-worker/ensdb-writer-worker";
109
import type { IndexingStatusBuilder } from "@/lib/indexing-status-builder/indexing-status-builder";
1110
import type { PublicConfigBuilder } from "@/lib/public-config-builder/public-config-builder";
1211

1312
import {
1413
createMockCrossChainSnapshot,
1514
createMockEnsDbWriter,
15+
createMockEnsDbWriterWorker,
1616
createMockIndexingStatusBuilder,
1717
createMockOmnichainSnapshot,
1818
createMockPublicConfigBuilder,
@@ -51,10 +51,10 @@ describe("EnsDbWriterWorker", () => {
5151
vi.mocked(buildCrossChainIndexingStatusSnapshotOmnichain).mockReturnValue(snapshot);
5252

5353
const ensDbClient = createMockEnsDbWriter();
54-
const publicConfigBuilder = createMockPublicConfigBuilder();
55-
const indexingStatusBuilder = createMockIndexingStatusBuilder(omnichainSnapshot);
56-
57-
const worker = new EnsDbWriterWorker(ensDbClient, publicConfigBuilder, indexingStatusBuilder);
54+
const worker = createMockEnsDbWriterWorker({
55+
ensDbClient,
56+
indexingStatusBuilder: createMockIndexingStatusBuilder(omnichainSnapshot),
57+
});
5858

5959
// act
6060
await worker.run();
@@ -81,31 +81,58 @@ describe("EnsDbWriterWorker", () => {
8181

8282
it("throws when stored config is incompatible", async () => {
8383
// arrange
84-
const incompatibleError = new Error("incompatible");
8584
vi.mocked(validateEnsIndexerPublicConfigCompatibility).mockImplementation(() => {
86-
throw incompatibleError;
85+
throw new Error("incompatible");
8786
});
8887

8988
const ensDbClient = createMockEnsDbWriter({
9089
getEnsIndexerPublicConfig: vi.fn().mockResolvedValue(mockPublicConfig),
9190
});
92-
const publicConfigBuilder = createMockPublicConfigBuilder(mockPublicConfig);
93-
const indexingStatusBuilder = createMockIndexingStatusBuilder();
94-
95-
const worker = new EnsDbWriterWorker(ensDbClient, publicConfigBuilder, indexingStatusBuilder);
91+
const worker = createMockEnsDbWriterWorker({
92+
ensDbClient,
93+
publicConfigBuilder: createMockPublicConfigBuilder(mockPublicConfig),
94+
});
9695

9796
// act & assert
9897
await expect(worker.run()).rejects.toThrow("incompatible");
9998
expect(ensDbClient.upsertEnsDbVersion).not.toHaveBeenCalled();
10099
});
101100

102-
it("throws error when worker is already running", async () => {
101+
it("skips config validation when in dev mode", async () => {
103102
// arrange
104-
const ensDbClient = createMockEnsDbWriter();
105-
const publicConfigBuilder = createMockPublicConfigBuilder();
106-
const indexingStatusBuilder = createMockIndexingStatusBuilder();
103+
vi.mocked(validateEnsIndexerPublicConfigCompatibility).mockImplementation(() => {
104+
throw new Error("incompatible");
105+
});
106+
107+
const snapshot = createMockCrossChainSnapshot();
108+
vi.mocked(buildCrossChainIndexingStatusSnapshotOmnichain).mockReturnValue(snapshot);
107109

108-
const worker = new EnsDbWriterWorker(ensDbClient, publicConfigBuilder, indexingStatusBuilder);
110+
const ensDbClient = createMockEnsDbWriter({
111+
getEnsIndexerPublicConfig: vi.fn().mockResolvedValue(mockPublicConfig),
112+
});
113+
const worker = createMockEnsDbWriterWorker({
114+
ensDbClient,
115+
publicConfigBuilder: createMockPublicConfigBuilder(mockPublicConfig),
116+
isInDevMode: true,
117+
});
118+
119+
// act - should not throw even though configs are incompatible
120+
await worker.run();
121+
122+
// assert - validation should not have been called
123+
expect(validateEnsIndexerPublicConfigCompatibility).not.toHaveBeenCalled();
124+
expect(ensDbClient.upsertEnsDbVersion).toHaveBeenCalledWith(
125+
mockPublicConfig.versionInfo.ensDb,
126+
);
127+
expect(ensDbClient.upsertEnsIndexerPublicConfig).toHaveBeenCalledWith(mockPublicConfig);
128+
129+
// cleanup
130+
worker.stop();
131+
});
132+
133+
it("throws error when worker is already running", async () => {
134+
// arrange
135+
const worker = createMockEnsDbWriterWorker();
109136

110137
// act - first run
111138
await worker.run();
@@ -119,14 +146,11 @@ describe("EnsDbWriterWorker", () => {
119146

120147
it("throws error when config fetch fails", async () => {
121148
// arrange
122-
const networkError = new Error("Network failure");
123-
const ensDbClient = createMockEnsDbWriter();
124149
const publicConfigBuilder = {
125-
getPublicConfig: vi.fn().mockRejectedValue(networkError),
150+
getPublicConfig: vi.fn().mockRejectedValue(new Error("Network failure")),
126151
} as unknown as PublicConfigBuilder;
127-
const indexingStatusBuilder = createMockIndexingStatusBuilder();
128-
129-
const worker = new EnsDbWriterWorker(ensDbClient, publicConfigBuilder, indexingStatusBuilder);
152+
const ensDbClient = createMockEnsDbWriter();
153+
const worker = createMockEnsDbWriterWorker({ ensDbClient, publicConfigBuilder });
130154

131155
// act & assert
132156
await expect(worker.run()).rejects.toThrow("Network failure");
@@ -136,14 +160,10 @@ describe("EnsDbWriterWorker", () => {
136160

137161
it("throws error when stored config fetch fails", async () => {
138162
// arrange
139-
const dbError = new Error("Database connection lost");
140163
const ensDbClient = createMockEnsDbWriter({
141-
getEnsIndexerPublicConfig: vi.fn().mockRejectedValue(dbError),
164+
getEnsIndexerPublicConfig: vi.fn().mockRejectedValue(new Error("Database connection lost")),
142165
});
143-
const publicConfigBuilder = createMockPublicConfigBuilder();
144-
const indexingStatusBuilder = createMockIndexingStatusBuilder();
145-
146-
const worker = new EnsDbWriterWorker(ensDbClient, publicConfigBuilder, indexingStatusBuilder);
166+
const worker = createMockEnsDbWriterWorker({ ensDbClient });
147167

148168
// act & assert
149169
await expect(worker.run()).rejects.toThrow("Database connection lost");
@@ -152,17 +172,16 @@ describe("EnsDbWriterWorker", () => {
152172

153173
it("fetches stored and in-memory configs concurrently", async () => {
154174
// arrange
155-
vi.mocked(validateEnsIndexerPublicConfigCompatibility).mockImplementation(() => {
156-
// validation passes
157-
});
175+
vi.mocked(validateEnsIndexerPublicConfigCompatibility).mockImplementation(() => {});
158176

159177
const ensDbClient = createMockEnsDbWriter({
160178
getEnsIndexerPublicConfig: vi.fn().mockResolvedValue(mockPublicConfig),
161179
});
162180
const publicConfigBuilder = createMockPublicConfigBuilder(mockPublicConfig);
163-
const indexingStatusBuilder = createMockIndexingStatusBuilder();
164-
165-
const worker = new EnsDbWriterWorker(ensDbClient, publicConfigBuilder, indexingStatusBuilder);
181+
const worker = createMockEnsDbWriterWorker({
182+
ensDbClient,
183+
publicConfigBuilder,
184+
});
166185

167186
// act
168187
await worker.run();
@@ -182,9 +201,7 @@ describe("EnsDbWriterWorker", () => {
182201

183202
const ensDbClient = createMockEnsDbWriter();
184203
const publicConfigBuilder = createMockPublicConfigBuilder();
185-
const indexingStatusBuilder = createMockIndexingStatusBuilder();
186-
187-
const worker = new EnsDbWriterWorker(ensDbClient, publicConfigBuilder, indexingStatusBuilder);
204+
const worker = createMockEnsDbWriterWorker({ ensDbClient, publicConfigBuilder });
188205

189206
// act
190207
await worker.run();
@@ -203,10 +220,7 @@ describe("EnsDbWriterWorker", () => {
203220
// arrange
204221
const upsertIndexingStatusSnapshot = vi.fn().mockResolvedValue(undefined);
205222
const ensDbClient = createMockEnsDbWriter({ upsertIndexingStatusSnapshot });
206-
const publicConfigBuilder = createMockPublicConfigBuilder();
207-
const indexingStatusBuilder = createMockIndexingStatusBuilder();
208-
209-
const worker = new EnsDbWriterWorker(ensDbClient, publicConfigBuilder, indexingStatusBuilder);
223+
const worker = createMockEnsDbWriterWorker({ ensDbClient });
210224

211225
// act
212226
await worker.run();
@@ -227,11 +241,7 @@ describe("EnsDbWriterWorker", () => {
227241
describe("isRunning - worker state", () => {
228242
it("indicates isRunning status correctly", async () => {
229243
// arrange
230-
const ensDbClient = createMockEnsDbWriter();
231-
const publicConfigBuilder = createMockPublicConfigBuilder();
232-
const indexingStatusBuilder = createMockIndexingStatusBuilder();
233-
234-
const worker = new EnsDbWriterWorker(ensDbClient, publicConfigBuilder, indexingStatusBuilder);
244+
const worker = createMockEnsDbWriterWorker();
235245

236246
// assert - not running initially
237247
expect(worker.isRunning).toBe(false);
@@ -268,15 +278,13 @@ describe("EnsDbWriterWorker", () => {
268278
vi.mocked(buildCrossChainIndexingStatusSnapshotOmnichain).mockReturnValue(crossChainSnapshot);
269279

270280
const ensDbClient = createMockEnsDbWriter();
271-
const publicConfigBuilder = createMockPublicConfigBuilder();
272281
const indexingStatusBuilder = {
273282
getOmnichainIndexingStatusSnapshot: vi
274283
.fn()
275284
.mockResolvedValueOnce(unstartedSnapshot)
276285
.mockResolvedValueOnce(validSnapshot),
277286
} as unknown as IndexingStatusBuilder;
278-
279-
const worker = new EnsDbWriterWorker(ensDbClient, publicConfigBuilder, indexingStatusBuilder);
287+
const worker = createMockEnsDbWriterWorker({ ensDbClient, indexingStatusBuilder });
280288

281289
// act - run returns immediately
282290
await worker.run();
@@ -324,16 +332,14 @@ describe("EnsDbWriterWorker", () => {
324332
.mockRejectedValueOnce(new Error("DB error"))
325333
.mockResolvedValueOnce(undefined),
326334
});
327-
const publicConfigBuilder = createMockPublicConfigBuilder();
328335
const indexingStatusBuilder = {
329336
getOmnichainIndexingStatusSnapshot: vi
330337
.fn()
331338
.mockResolvedValueOnce(snapshot1)
332339
.mockResolvedValueOnce(snapshot2)
333340
.mockResolvedValueOnce(snapshot2),
334341
} as unknown as IndexingStatusBuilder;
335-
336-
const worker = new EnsDbWriterWorker(ensDbClient, publicConfigBuilder, indexingStatusBuilder);
342+
const worker = createMockEnsDbWriterWorker({ ensDbClient, indexingStatusBuilder });
337343

338344
// act
339345
await worker.run();

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

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
type OmnichainIndexingStatusSnapshot,
1212
validateEnsIndexerPublicConfigCompatibility,
1313
} from "@ensnode/ensnode-sdk";
14+
import type { LocalPonderClient } from "@ensnode/ponder-sdk";
1415

1516
import type { IndexingStatusBuilder } from "@/lib/indexing-status-builder/indexing-status-builder";
1617
import type { PublicConfigBuilder } from "@/lib/public-config-builder/public-config-builder";
@@ -50,19 +51,29 @@ export class EnsDbWriterWorker {
5051
*/
5152
private publicConfigBuilder: PublicConfigBuilder;
5253

54+
/**
55+
* Local Ponder Client instance
56+
*
57+
* Used to get local Ponder app command.
58+
*/
59+
private localPonderClient: LocalPonderClient;
60+
5361
/**
5462
* @param ensDbClient ENSDb Writer instance used by the worker to interact with ENSDb.
5563
* @param publicConfigBuilder ENSIndexer Public Config Builder instance used by the worker to read ENSIndexer Public Config.
5664
* @param indexingStatusBuilder Indexing Status Builder instance used by the worker to read ENSIndexer Indexing Status.
65+
* @param localPonderClient Local Ponder Client instance, used to get local Ponder app command.
5766
*/
5867
constructor(
5968
ensDbClient: EnsDbWriter,
6069
publicConfigBuilder: PublicConfigBuilder,
6170
indexingStatusBuilder: IndexingStatusBuilder,
71+
localPonderClient: LocalPonderClient,
6272
) {
6373
this.ensDbClient = ensDbClient;
6474
this.publicConfigBuilder = publicConfigBuilder;
6575
this.indexingStatusBuilder = indexingStatusBuilder;
76+
this.localPonderClient = localPonderClient;
6677
}
6778

6879
/**
@@ -133,14 +144,16 @@ export class EnsDbWriterWorker {
133144
* - stored config in ENSDb, if available, and
134145
* - in-memory config from ENSIndexer Client.
135146
*
136-
* If, and only if, a stored config is available in ENSDb, then the function
137-
* validates the compatibility of the in-memory config object against
138-
* the stored one. Validation criteria are defined in the function body.
147+
* If a stored config exists **and** the local Ponder app is **not** in dev
148+
* mode, the in-memory config is validated for compatibility against the
149+
* stored one. Validation is skipped if the local Ponder app is in dev mode,
150+
* allowing to override the stored config in ENSDb with the current in-memory
151+
* config, without having to keep them compatible.
139152
*
140-
* @returns In-memory config object, if the validation is successful or
141-
* if there is no stored config.
142-
* @throws Error if the in-memory config object cannot be fetched or,
143-
* got fetched and is incompatible with the stored config object.
153+
* @returns The in-memory config when validation passes or no stored config
154+
* exists.
155+
* @throws Error if either fetch fails, or if the in-memory config is
156+
* incompatible with the stored config.
144157
*/
145158
private async getValidatedEnsIndexerPublicConfig(): Promise<EnsIndexerPublicConfig> {
146159
/**
@@ -181,8 +194,12 @@ export class EnsDbWriterWorker {
181194
}
182195

183196
// Validate in-memory config object compatibility with the stored one,
184-
// if the stored one is available
185-
if (storedConfig) {
197+
// if the stored one is available.
198+
// The validation is skipped if the local Ponder app is running in dev mode.
199+
// This is to improve the development experience during ENSIndexer
200+
// development, by allowing to override the stored config in ENSDb with
201+
// the current in-memory config, without having to keep them compatible.
202+
if (storedConfig && !this.localPonderClient.isInDevMode) {
186203
try {
187204
validateEnsIndexerPublicConfigCompatibility(storedConfig, inMemoryConfig);
188205
} catch (error) {

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { ensDbClient } from "@/lib/ensdb/singleton";
22
import { indexingStatusBuilder } from "@/lib/indexing-status-builder/singleton";
3+
import { localPonderClient } from "@/lib/local-ponder-client";
34
import { publicConfigBuilder } from "@/lib/public-config-builder/singleton";
45

56
import { EnsDbWriterWorker } from "./ensdb-writer-worker";
@@ -24,6 +25,7 @@ export function startEnsDbWriterWorker() {
2425
ensDbClient,
2526
publicConfigBuilder,
2627
indexingStatusBuilder,
28+
localPonderClient,
2729
);
2830

2931
ensDbWriterWorker

0 commit comments

Comments
 (0)