@apibara/plugin-mongo 2.1.0-beta.5 → 2.1.0-beta.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -39,17 +39,42 @@ async function finalize(db, session, cursor, collections) {
39
39
  );
40
40
  }
41
41
  }
42
+ async function cleanupStorage(db, session, collections) {
43
+ for (const collection of collections) {
44
+ try {
45
+ await db.collection(collection).deleteMany({}, { session });
46
+ } catch (error) {
47
+ throw new Error(`Failed to clean up collection ${collection}`, {
48
+ cause: error
49
+ });
50
+ }
51
+ }
52
+ }
53
+
54
+ class MongoStorageError extends Error {
55
+ constructor(message, options) {
56
+ super(message, options);
57
+ this.name = "MongoStorageError";
58
+ }
59
+ }
60
+ async function withTransaction(client, cb) {
61
+ return await client.withSession(async (session) => {
62
+ return await session.withTransaction(
63
+ async (session2) => {
64
+ return await cb(session2);
65
+ },
66
+ {
67
+ retryWrites: false
68
+ }
69
+ );
70
+ });
71
+ }
42
72
 
43
73
  const checkpointCollectionName = "checkpoints";
44
74
  const filterCollectionName = "filters";
45
75
  async function initializePersistentState(db, session) {
46
- const checkpoint = await db.createCollection(
47
- checkpointCollectionName,
48
- { session }
49
- );
50
- const filter = await db.createCollection(filterCollectionName, {
51
- session
52
- });
76
+ const checkpoint = db.collection(checkpointCollectionName);
77
+ const filter = db.collection(filterCollectionName);
53
78
  await checkpoint.createIndex({ id: 1 }, { session });
54
79
  await filter.createIndex({ id: 1, fromBlock: 1 }, { session });
55
80
  }
@@ -61,7 +86,7 @@ async function persistState(props) {
61
86
  {
62
87
  $set: {
63
88
  orderKey: Number(endCursor.orderKey),
64
- uniqueKey: endCursor.uniqueKey
89
+ uniqueKey: endCursor.uniqueKey ? endCursor.uniqueKey : null
65
90
  }
66
91
  },
67
92
  { upsert: true, session }
@@ -134,6 +159,17 @@ async function finalizeState(props) {
134
159
  { session }
135
160
  );
136
161
  }
162
+ async function resetPersistence(props) {
163
+ const { db, session, indexerId } = props;
164
+ try {
165
+ await db.collection(checkpointCollectionName).deleteMany({ id: indexerId }, { session });
166
+ await db.collection(filterCollectionName).deleteMany({ id: indexerId }, { session });
167
+ } catch (error) {
168
+ throw new MongoStorageError("Failed to reset persistence state", {
169
+ cause: error
170
+ });
171
+ }
172
+ }
137
173
 
138
174
  class MongoStorage {
139
175
  constructor(db, session, endCursor) {
@@ -305,20 +341,6 @@ class MongoCollection {
305
341
  }
306
342
  }
307
343
 
308
- class MongoStorageError extends Error {
309
- constructor(message) {
310
- super(message);
311
- this.name = "MongoStorageError";
312
- }
313
- }
314
- async function withTransaction(client, cb) {
315
- return await client.withSession(async (session) => {
316
- return await session.withTransaction(async (session2) => {
317
- return await cb(session2);
318
- });
319
- });
320
- }
321
-
322
344
  const MONGO_PROPERTY = "_mongo";
323
345
  function useMongoStorage() {
324
346
  const context = indexer.useIndexerContext();
@@ -339,14 +361,27 @@ function mongoStorage({
339
361
  }) {
340
362
  return plugins.defineIndexerPlugin((indexer) => {
341
363
  let indexerId = "";
342
- indexer.hooks.hook("run:before", async () => {
364
+ const alwaysReindex = process.env["APIBARA_ALWAYS_REINDEX"] === "true";
365
+ let prevFinality;
366
+ indexer.hooks.hook("plugins:init", async () => {
343
367
  const { indexerName } = plugins$1.useInternalContext();
344
368
  indexerId = internal.generateIndexerId(indexerName, identifier);
369
+ const logger = plugins.useLogger();
345
370
  await withTransaction(client, async (session) => {
346
371
  const db = client.db(dbName, dbOptions);
347
372
  if (enablePersistence) {
348
373
  await initializePersistentState(db, session);
349
374
  }
375
+ if (alwaysReindex) {
376
+ logger.warn(
377
+ `Reindexing: Deleting all data from collections - ${collections.join(", ")}`
378
+ );
379
+ await cleanupStorage(db, session, collections);
380
+ if (enablePersistence) {
381
+ await resetPersistence({ db, session, indexerId });
382
+ }
383
+ logger.success("All data has been cleaned up for reindexing");
384
+ }
350
385
  });
351
386
  });
352
387
  indexer.hooks.hook("connect:before", async ({ request }) => {
@@ -399,7 +434,7 @@ function mongoStorage({
399
434
  });
400
435
  });
401
436
  indexer.hooks.hook("message:finalize", async ({ message }) => {
402
- const { cursor } = message.finalize;
437
+ const { cursor } = message;
403
438
  if (!cursor) {
404
439
  throw new MongoStorageError("finalized cursor is undefined");
405
440
  }
@@ -412,7 +447,7 @@ function mongoStorage({
412
447
  });
413
448
  });
414
449
  indexer.hooks.hook("message:invalidate", async ({ message }) => {
415
- const { cursor } = message.invalidate;
450
+ const { cursor } = message;
416
451
  if (!cursor) {
417
452
  throw new MongoStorageError("invalidate cursor is undefined");
418
453
  }
@@ -426,16 +461,19 @@ function mongoStorage({
426
461
  });
427
462
  indexer.hooks.hook("handler:middleware", async ({ use }) => {
428
463
  use(async (context, next) => {
429
- const { endCursor } = context;
464
+ const { endCursor, finality, cursor } = context;
430
465
  if (!endCursor) {
431
466
  throw new MongoStorageError("end cursor is undefined");
432
467
  }
433
468
  await withTransaction(client, async (session) => {
434
469
  const db = client.db(dbName, dbOptions);
435
470
  context[MONGO_PROPERTY] = new MongoStorage(db, session, endCursor);
471
+ if (prevFinality === "pending") {
472
+ await invalidate(db, session, cursor, collections);
473
+ }
436
474
  await next();
437
475
  delete context[MONGO_PROPERTY];
438
- if (enablePersistence) {
476
+ if (enablePersistence && finality !== "pending") {
439
477
  await persistState({
440
478
  db,
441
479
  endCursor,
@@ -443,6 +481,7 @@ function mongoStorage({
443
481
  indexerId
444
482
  });
445
483
  }
484
+ prevFinality = finality;
446
485
  });
447
486
  });
448
487
  });
@@ -453,3 +492,4 @@ exports.MongoCollection = MongoCollection;
453
492
  exports.MongoStorage = MongoStorage;
454
493
  exports.mongoStorage = mongoStorage;
455
494
  exports.useMongoStorage = useMongoStorage;
495
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../src/mongo.ts","../src/utils.ts","../src/persistence.ts","../src/storage.ts","../src/index.ts"],"sourcesContent":["import type { Cursor } from \"@apibara/protocol\";\nimport type { ClientSession, Db } from \"mongodb\";\n\nexport async function invalidate(\n db: Db,\n session: ClientSession,\n cursor: Cursor,\n collections: string[],\n) {\n const orderKeyValue = Number(cursor.orderKey);\n for (const collection of collections) {\n // Delete documents where the lower bound of _cursor is greater than the invalidate cursor\n await db.collection(collection).deleteMany(\n {\n \"_cursor.from\": {\n $gt: orderKeyValue,\n },\n },\n { session },\n );\n\n // Update documents where the upper bound of _cursor is greater than the invalidate cursor\n await db.collection(collection).updateMany(\n { \"_cursor.to\": { $gt: orderKeyValue } },\n {\n $set: {\n \"_cursor.to\": null,\n },\n },\n { session },\n );\n }\n}\n\nexport async function finalize(\n db: Db,\n session: ClientSession,\n cursor: Cursor,\n collections: string[],\n) {\n const orderKeyValue = Number(cursor.orderKey);\n for (const collection of collections) {\n // Delete documents where the upper bound of _cursor is less than the finalize cursor\n await db.collection(collection).deleteMany(\n {\n \"_cursor.to\": { $lte: orderKeyValue },\n },\n { session },\n );\n }\n}\n\nexport async function cleanupStorage(\n db: Db,\n session: ClientSession,\n collections: string[],\n) {\n for (const collection of collections) {\n try {\n // Delete all documents in the collection\n await db.collection(collection).deleteMany({}, { session });\n } catch (error) {\n throw new Error(`Failed to clean up collection ${collection}`, {\n cause: error,\n });\n }\n }\n}\n","import type { ClientSession, MongoClient } from \"mongodb\";\n\nexport class MongoStorageError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"MongoStorageError\";\n }\n}\n\nexport async function withTransaction<T>(\n client: MongoClient,\n cb: (session: ClientSession) => Promise<T>,\n) {\n return await client.withSession(async (session) => {\n return await session.withTransaction(\n async (session) => {\n return await cb(session);\n },\n {\n retryWrites: false,\n },\n );\n });\n}\n","import { type Cursor, normalizeCursor } from \"@apibara/protocol\";\nimport type { ClientSession, Db } from \"mongodb\";\nimport { MongoStorageError } from \"./utils\";\n\nexport type CheckpointSchema = {\n id: string;\n orderKey: number;\n uniqueKey: string | null;\n};\n\nexport type FilterSchema = {\n id: string;\n filter: Record<string, unknown>;\n fromBlock: number;\n toBlock: number | null;\n};\n\nexport const checkpointCollectionName = \"checkpoints\";\nexport const filterCollectionName = \"filters\";\n\nexport async function initializePersistentState(\n db: Db,\n session: ClientSession,\n) {\n const checkpoint = db.collection<CheckpointSchema>(checkpointCollectionName);\n const filter = db.collection<FilterSchema>(filterCollectionName);\n\n await checkpoint.createIndex({ id: 1 }, { session });\n await filter.createIndex({ id: 1, fromBlock: 1 }, { session });\n}\n\nexport async function persistState<TFilter>(props: {\n db: Db;\n session: ClientSession;\n endCursor: Cursor;\n filter?: TFilter;\n indexerId: string;\n}) {\n const { db, session, endCursor, filter, indexerId } = props;\n\n if (endCursor) {\n await db.collection<CheckpointSchema>(checkpointCollectionName).updateOne(\n { id: indexerId },\n {\n $set: {\n orderKey: Number(endCursor.orderKey),\n uniqueKey: endCursor.uniqueKey ? endCursor.uniqueKey : null,\n },\n },\n { upsert: true, session },\n );\n\n if (filter) {\n // Update existing filter's to_block\n await db\n .collection<FilterSchema>(filterCollectionName)\n .updateMany(\n { id: indexerId, toBlock: null },\n { $set: { toBlock: Number(endCursor.orderKey) } },\n { session },\n );\n\n // Insert new filter\n await db.collection<FilterSchema>(filterCollectionName).updateOne(\n {\n id: indexerId,\n fromBlock: Number(endCursor.orderKey),\n },\n {\n $set: {\n filter: filter as Record<string, unknown>,\n fromBlock: Number(endCursor.orderKey),\n toBlock: null,\n },\n },\n { upsert: true, session },\n );\n }\n }\n}\n\nexport async function getState<TFilter>(props: {\n db: Db;\n session: ClientSession;\n indexerId: string;\n}): Promise<{ cursor?: Cursor; filter?: TFilter }> {\n const { db, session, indexerId } = props;\n\n let cursor: Cursor | undefined;\n let filter: TFilter | undefined;\n\n const checkpointRow = await db\n .collection<CheckpointSchema>(checkpointCollectionName)\n .findOne({ id: indexerId }, { session });\n\n if (checkpointRow) {\n cursor = normalizeCursor({\n orderKey: BigInt(checkpointRow.orderKey),\n uniqueKey: checkpointRow.uniqueKey,\n });\n }\n\n const filterRow = await db\n .collection<FilterSchema>(filterCollectionName)\n .findOne(\n {\n id: indexerId,\n toBlock: null,\n },\n { session },\n );\n\n if (filterRow) {\n filter = filterRow.filter as TFilter;\n }\n\n return { cursor, filter };\n}\n\nexport async function invalidateState(props: {\n db: Db;\n session: ClientSession;\n cursor: Cursor;\n indexerId: string;\n}) {\n const { db, session, cursor, indexerId } = props;\n\n await db\n .collection<FilterSchema>(filterCollectionName)\n .deleteMany(\n { id: indexerId, fromBlock: { $gt: Number(cursor.orderKey) } },\n { session },\n );\n\n await db\n .collection<FilterSchema>(filterCollectionName)\n .updateMany(\n { id: indexerId, toBlock: { $gt: Number(cursor.orderKey) } },\n { $set: { toBlock: null } },\n { session },\n );\n}\n\nexport async function finalizeState(props: {\n db: Db;\n session: ClientSession;\n cursor: Cursor;\n indexerId: string;\n}) {\n const { db, session, cursor, indexerId } = props;\n\n await db.collection<FilterSchema>(filterCollectionName).deleteMany(\n {\n id: indexerId,\n toBlock: { $lte: Number(cursor.orderKey) },\n },\n { session },\n );\n}\n\nexport async function resetPersistence(props: {\n db: Db;\n session: ClientSession;\n indexerId: string;\n}) {\n const { db, session, indexerId } = props;\n\n try {\n // Delete all checkpoints for this indexer\n await db\n .collection<CheckpointSchema>(checkpointCollectionName)\n .deleteMany({ id: indexerId }, { session });\n\n // Delete all filters for this indexer\n await db\n .collection<FilterSchema>(filterCollectionName)\n .deleteMany({ id: indexerId }, { session });\n } catch (error) {\n throw new MongoStorageError(\"Failed to reset persistence state\", {\n cause: error,\n });\n }\n}\n","import type { Cursor } from \"@apibara/protocol\";\nimport type {\n BulkWriteOptions,\n ClientSession,\n Collection,\n CollectionOptions,\n Db,\n DeleteOptions,\n Document,\n Filter,\n FindCursor,\n FindOneAndUpdateOptions,\n FindOptions,\n InsertManyResult,\n InsertOneOptions,\n InsertOneResult,\n MatchKeysAndValues,\n OptionalUnlessRequiredId,\n UpdateFilter,\n UpdateOptions,\n UpdateResult,\n WithId,\n} from \"mongodb\";\n\nexport class MongoStorage {\n constructor(\n private db: Db,\n private session: ClientSession,\n private endCursor?: Cursor,\n ) {}\n\n collection<TSchema extends Document = Document>(\n name: string,\n options?: CollectionOptions,\n ) {\n const collection = this.db.collection<TSchema>(name, options);\n\n return new MongoCollection<TSchema>(\n this.session,\n collection,\n this.endCursor,\n );\n }\n}\n\nexport type MongoCursor = {\n from: number | null;\n to: number | null;\n};\n\nexport type CursoredSchema<TSchema extends Document> = TSchema & {\n _cursor: MongoCursor;\n};\n\nexport class MongoCollection<TSchema extends Document> {\n constructor(\n private session: ClientSession,\n private collection: Collection<TSchema>,\n private endCursor?: Cursor,\n ) {}\n\n async insertOne(\n doc: OptionalUnlessRequiredId<TSchema>,\n options?: InsertOneOptions,\n ): Promise<InsertOneResult<TSchema>> {\n return await this.collection.insertOne(\n {\n ...doc,\n _cursor: {\n from: Number(this.endCursor?.orderKey),\n to: null,\n } as MongoCursor,\n },\n { ...options, session: this.session },\n );\n }\n\n async insertMany(\n docs: ReadonlyArray<OptionalUnlessRequiredId<TSchema>>,\n options?: BulkWriteOptions,\n ): Promise<InsertManyResult<TSchema>> {\n return await this.collection.insertMany(\n docs.map((doc) => ({\n ...doc,\n _cursor: {\n from: Number(this.endCursor?.orderKey),\n to: null,\n } as MongoCursor,\n })),\n { ...options, session: this.session },\n );\n }\n\n async updateOne(\n filter: Filter<TSchema>,\n update: UpdateFilter<TSchema>,\n options?: UpdateOptions,\n ): Promise<UpdateResult<TSchema>> {\n // 1. Find and update the document, getting the old version\n const oldDoc = await this.collection.findOneAndUpdate(\n {\n ...filter,\n \"_cursor.to\": null,\n },\n {\n ...update,\n $set: {\n ...update.$set,\n \"_cursor.from\": Number(this.endCursor?.orderKey),\n } as unknown as MatchKeysAndValues<TSchema>,\n },\n {\n ...options,\n session: this.session,\n returnDocument: \"before\",\n } as FindOneAndUpdateOptions,\n );\n\n // 2. If we found and updated a document, insert its old version\n if (oldDoc) {\n const { _id, ...doc } = oldDoc;\n await this.collection.insertOne(\n {\n ...doc,\n _cursor: {\n ...oldDoc._cursor,\n to: Number(this.endCursor?.orderKey),\n },\n } as unknown as OptionalUnlessRequiredId<TSchema>,\n { session: this.session },\n );\n }\n\n // 3. Return an UpdateResult-compatible object\n return {\n acknowledged: true,\n modifiedCount: oldDoc ? 1 : 0,\n upsertedId: null,\n upsertedCount: 0,\n matchedCount: oldDoc ? 1 : 0,\n };\n }\n\n async updateMany(\n filter: Filter<TSchema>,\n update: UpdateFilter<TSchema>,\n options?: UpdateOptions,\n ): Promise<UpdateResult<TSchema>> {\n // 1. Find all documents matching the filter that are latest (to: null)\n const oldDocs = await this.collection\n .find(\n {\n ...filter,\n \"_cursor.to\": null,\n },\n { session: this.session },\n )\n .toArray();\n\n // 2. Update to the new values with updateMany\n // (setting _cursor.from to endCursor, leaving _cursor.to unchanged)\n const updateResult = await this.collection.updateMany(\n {\n ...filter,\n \"_cursor.to\": null,\n },\n {\n ...update,\n $set: {\n ...update.$set,\n \"_cursor.from\": Number(this.endCursor?.orderKey),\n } as unknown as MatchKeysAndValues<TSchema>,\n },\n { ...options, session: this.session },\n );\n\n // 3. Adjust the cursor.to of the old values\n const oldDocsWithUpdatedCursor = oldDocs.map(({ _id, ...doc }) => ({\n ...doc,\n _cursor: {\n ...doc._cursor,\n to: Number(this.endCursor?.orderKey),\n },\n }));\n\n // 4. Insert the old values back into the db\n if (oldDocsWithUpdatedCursor.length > 0) {\n await this.collection.insertMany(\n oldDocsWithUpdatedCursor as unknown as OptionalUnlessRequiredId<TSchema>[],\n { session: this.session },\n );\n }\n\n return updateResult;\n }\n\n async deleteOne(\n filter: Filter<TSchema>,\n options?: DeleteOptions,\n ): Promise<UpdateResult<TSchema>> {\n return await this.collection.updateOne(\n {\n ...filter,\n \"_cursor.to\": null,\n },\n {\n $set: {\n \"_cursor.to\": Number(this.endCursor?.orderKey),\n } as unknown as MatchKeysAndValues<TSchema>,\n },\n { ...options, session: this.session },\n );\n }\n\n async deleteMany(\n filter?: Filter<TSchema>,\n options?: DeleteOptions,\n ): Promise<UpdateResult<TSchema>> {\n return await this.collection.updateMany(\n {\n ...((filter ?? {}) as Filter<TSchema>),\n \"_cursor.to\": null,\n },\n {\n $set: {\n \"_cursor.to\": Number(this.endCursor?.orderKey),\n } as unknown as MatchKeysAndValues<TSchema>,\n },\n { ...options, session: this.session },\n );\n }\n\n async findOne(\n filter: Filter<TSchema>,\n options?: Omit<FindOptions, \"timeoutMode\">,\n ): Promise<WithId<TSchema> | null> {\n return await this.collection.findOne(\n {\n ...filter,\n \"_cursor.to\": null,\n },\n { ...options, session: this.session },\n );\n }\n\n find(\n filter: Filter<TSchema>,\n options?: FindOptions,\n ): FindCursor<WithId<TSchema>> {\n return this.collection.find(\n {\n ...filter,\n \"_cursor.to\": null,\n },\n { ...options, session: this.session },\n );\n }\n}\n","import { useIndexerContext } from \"@apibara/indexer\";\nimport { defineIndexerPlugin, useLogger } from \"@apibara/indexer/plugins\";\nimport type { DbOptions, MongoClient } from \"mongodb\";\n\nimport { generateIndexerId } from \"@apibara/indexer/internal\";\nimport { useInternalContext } from \"@apibara/indexer/internal/plugins\";\nimport type { Cursor, DataFinality } from \"@apibara/protocol\";\nimport { cleanupStorage, finalize, invalidate } from \"./mongo\";\nimport {\n finalizeState,\n getState,\n initializePersistentState,\n invalidateState,\n persistState,\n resetPersistence,\n} from \"./persistence\";\nimport { MongoStorage } from \"./storage\";\nimport { MongoStorageError, withTransaction } from \"./utils\";\n\nexport { MongoCollection, MongoStorage } from \"./storage\";\n\nconst MONGO_PROPERTY = \"_mongo\";\n\nexport function useMongoStorage(): MongoStorage {\n const context = useIndexerContext();\n\n if (!context[MONGO_PROPERTY]) {\n throw new MongoStorageError(\n \"mongo storage is not available. Did you register the plugin?\",\n );\n }\n\n return context[MONGO_PROPERTY] as MongoStorage;\n}\n\nexport interface MongoStorageOptions {\n client: MongoClient;\n dbName: string;\n dbOptions?: DbOptions;\n collections: string[];\n persistState?: boolean;\n indexerName?: string;\n}\n/**\n * Creates a plugin that uses MongoDB as the storage layer.\n *\n * Supports storing the indexer's state and provides a simple Key-Value store.\n * @param options.client - The MongoDB client instance.\n * @param options.dbName - The name of the database.\n * @param options.dbOptions - The database options.\n * @param options.collections - The collections to use.\n * @param options.persistState - Whether to persist the indexer's state. Defaults to true.\n * @param options.indexerName - The name of the indexer. Defaults value is 'default'.\n */\nexport function mongoStorage<TFilter, TBlock>({\n client,\n dbName,\n dbOptions,\n collections,\n persistState: enablePersistence = true,\n indexerName: identifier = \"default\",\n}: MongoStorageOptions) {\n return defineIndexerPlugin<TFilter, TBlock>((indexer) => {\n let indexerId = \"\";\n const alwaysReindex = process.env[\"APIBARA_ALWAYS_REINDEX\"] === \"true\";\n let prevFinality: DataFinality | undefined;\n\n indexer.hooks.hook(\"plugins:init\", async () => {\n const { indexerName } = useInternalContext();\n indexerId = generateIndexerId(indexerName, identifier);\n const logger = useLogger();\n\n await withTransaction(client, async (session) => {\n const db = client.db(dbName, dbOptions);\n if (enablePersistence) {\n await initializePersistentState(db, session);\n }\n\n if (alwaysReindex) {\n logger.warn(\n `Reindexing: Deleting all data from collections - ${collections.join(\", \")}`,\n );\n\n await cleanupStorage(db, session, collections);\n\n if (enablePersistence) {\n await resetPersistence({ db, session, indexerId });\n }\n\n logger.success(\"All data has been cleaned up for reindexing\");\n }\n });\n });\n\n indexer.hooks.hook(\"connect:before\", async ({ request }) => {\n if (!enablePersistence) {\n return;\n }\n\n await withTransaction(client, async (session) => {\n const db = client.db(dbName, dbOptions);\n const { cursor, filter } = await getState<TFilter>({\n db,\n session,\n indexerId,\n });\n\n if (cursor) {\n request.startingCursor = cursor;\n }\n\n if (filter) {\n request.filter[1] = filter;\n }\n });\n });\n\n indexer.hooks.hook(\"connect:after\", async ({ request }) => {\n // On restart, we need to invalidate data for blocks that were processed but not persisted.\n const cursor = request.startingCursor;\n\n if (!cursor) {\n return;\n }\n\n await withTransaction(client, async (session) => {\n const db = client.db(dbName, dbOptions);\n await invalidate(db, session, cursor, collections);\n\n if (enablePersistence) {\n await invalidateState({ db, session, cursor, indexerId });\n }\n });\n });\n\n indexer.hooks.hook(\"connect:factory\", async ({ request, endCursor }) => {\n if (!enablePersistence) {\n return;\n }\n await withTransaction(client, async (session) => {\n const db = client.db(dbName, dbOptions);\n if (endCursor && request.filter[1]) {\n await persistState({\n db,\n endCursor,\n session,\n filter: request.filter[1],\n indexerId,\n });\n }\n });\n });\n\n indexer.hooks.hook(\"message:finalize\", async ({ message }) => {\n const { cursor } = message;\n\n if (!cursor) {\n throw new MongoStorageError(\"finalized cursor is undefined\");\n }\n\n await withTransaction(client, async (session) => {\n const db = client.db(dbName, dbOptions);\n await finalize(db, session, cursor, collections);\n\n if (enablePersistence) {\n await finalizeState({ db, session, cursor, indexerId });\n }\n });\n });\n\n indexer.hooks.hook(\"message:invalidate\", async ({ message }) => {\n const { cursor } = message;\n\n if (!cursor) {\n throw new MongoStorageError(\"invalidate cursor is undefined\");\n }\n\n await withTransaction(client, async (session) => {\n const db = client.db(dbName, dbOptions);\n await invalidate(db, session, cursor, collections);\n\n if (enablePersistence) {\n await invalidateState({ db, session, cursor, indexerId });\n }\n });\n });\n\n indexer.hooks.hook(\"handler:middleware\", async ({ use }) => {\n use(async (context, next) => {\n const { endCursor, finality, cursor } = context as {\n cursor: Cursor;\n endCursor: Cursor;\n finality: DataFinality;\n };\n\n if (!endCursor) {\n throw new MongoStorageError(\"end cursor is undefined\");\n }\n\n await withTransaction(client, async (session) => {\n const db = client.db(dbName, dbOptions);\n context[MONGO_PROPERTY] = new MongoStorage(db, session, endCursor);\n\n if (prevFinality === \"pending\") {\n // invalidate if previous block's finality was \"pending\"\n await invalidate(db, session, cursor, collections);\n }\n\n await next();\n\n delete context[MONGO_PROPERTY];\n\n if (enablePersistence && finality !== \"pending\") {\n await persistState({\n db,\n endCursor,\n session,\n indexerId,\n });\n }\n\n prevFinality = finality;\n });\n });\n });\n });\n}\n"],"names":["session","normalizeCursor","useIndexerContext","defineIndexerPlugin","useInternalContext","generateIndexerId","useLogger"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { useIndexerContext } from '@apibara/indexer';
2
- import { defineIndexerPlugin } from '@apibara/indexer/plugins';
2
+ import { defineIndexerPlugin, useLogger } from '@apibara/indexer/plugins';
3
3
  import { generateIndexerId } from '@apibara/indexer/internal';
4
4
  import { useInternalContext } from '@apibara/indexer/internal/plugins';
5
5
  import { normalizeCursor } from '@apibara/protocol';
@@ -37,17 +37,42 @@ async function finalize(db, session, cursor, collections) {
37
37
  );
38
38
  }
39
39
  }
40
+ async function cleanupStorage(db, session, collections) {
41
+ for (const collection of collections) {
42
+ try {
43
+ await db.collection(collection).deleteMany({}, { session });
44
+ } catch (error) {
45
+ throw new Error(`Failed to clean up collection ${collection}`, {
46
+ cause: error
47
+ });
48
+ }
49
+ }
50
+ }
51
+
52
+ class MongoStorageError extends Error {
53
+ constructor(message, options) {
54
+ super(message, options);
55
+ this.name = "MongoStorageError";
56
+ }
57
+ }
58
+ async function withTransaction(client, cb) {
59
+ return await client.withSession(async (session) => {
60
+ return await session.withTransaction(
61
+ async (session2) => {
62
+ return await cb(session2);
63
+ },
64
+ {
65
+ retryWrites: false
66
+ }
67
+ );
68
+ });
69
+ }
40
70
 
41
71
  const checkpointCollectionName = "checkpoints";
42
72
  const filterCollectionName = "filters";
43
73
  async function initializePersistentState(db, session) {
44
- const checkpoint = await db.createCollection(
45
- checkpointCollectionName,
46
- { session }
47
- );
48
- const filter = await db.createCollection(filterCollectionName, {
49
- session
50
- });
74
+ const checkpoint = db.collection(checkpointCollectionName);
75
+ const filter = db.collection(filterCollectionName);
51
76
  await checkpoint.createIndex({ id: 1 }, { session });
52
77
  await filter.createIndex({ id: 1, fromBlock: 1 }, { session });
53
78
  }
@@ -59,7 +84,7 @@ async function persistState(props) {
59
84
  {
60
85
  $set: {
61
86
  orderKey: Number(endCursor.orderKey),
62
- uniqueKey: endCursor.uniqueKey
87
+ uniqueKey: endCursor.uniqueKey ? endCursor.uniqueKey : null
63
88
  }
64
89
  },
65
90
  { upsert: true, session }
@@ -132,6 +157,17 @@ async function finalizeState(props) {
132
157
  { session }
133
158
  );
134
159
  }
160
+ async function resetPersistence(props) {
161
+ const { db, session, indexerId } = props;
162
+ try {
163
+ await db.collection(checkpointCollectionName).deleteMany({ id: indexerId }, { session });
164
+ await db.collection(filterCollectionName).deleteMany({ id: indexerId }, { session });
165
+ } catch (error) {
166
+ throw new MongoStorageError("Failed to reset persistence state", {
167
+ cause: error
168
+ });
169
+ }
170
+ }
135
171
 
136
172
  class MongoStorage {
137
173
  constructor(db, session, endCursor) {
@@ -303,20 +339,6 @@ class MongoCollection {
303
339
  }
304
340
  }
305
341
 
306
- class MongoStorageError extends Error {
307
- constructor(message) {
308
- super(message);
309
- this.name = "MongoStorageError";
310
- }
311
- }
312
- async function withTransaction(client, cb) {
313
- return await client.withSession(async (session) => {
314
- return await session.withTransaction(async (session2) => {
315
- return await cb(session2);
316
- });
317
- });
318
- }
319
-
320
342
  const MONGO_PROPERTY = "_mongo";
321
343
  function useMongoStorage() {
322
344
  const context = useIndexerContext();
@@ -337,14 +359,27 @@ function mongoStorage({
337
359
  }) {
338
360
  return defineIndexerPlugin((indexer) => {
339
361
  let indexerId = "";
340
- indexer.hooks.hook("run:before", async () => {
362
+ const alwaysReindex = process.env["APIBARA_ALWAYS_REINDEX"] === "true";
363
+ let prevFinality;
364
+ indexer.hooks.hook("plugins:init", async () => {
341
365
  const { indexerName } = useInternalContext();
342
366
  indexerId = generateIndexerId(indexerName, identifier);
367
+ const logger = useLogger();
343
368
  await withTransaction(client, async (session) => {
344
369
  const db = client.db(dbName, dbOptions);
345
370
  if (enablePersistence) {
346
371
  await initializePersistentState(db, session);
347
372
  }
373
+ if (alwaysReindex) {
374
+ logger.warn(
375
+ `Reindexing: Deleting all data from collections - ${collections.join(", ")}`
376
+ );
377
+ await cleanupStorage(db, session, collections);
378
+ if (enablePersistence) {
379
+ await resetPersistence({ db, session, indexerId });
380
+ }
381
+ logger.success("All data has been cleaned up for reindexing");
382
+ }
348
383
  });
349
384
  });
350
385
  indexer.hooks.hook("connect:before", async ({ request }) => {
@@ -397,7 +432,7 @@ function mongoStorage({
397
432
  });
398
433
  });
399
434
  indexer.hooks.hook("message:finalize", async ({ message }) => {
400
- const { cursor } = message.finalize;
435
+ const { cursor } = message;
401
436
  if (!cursor) {
402
437
  throw new MongoStorageError("finalized cursor is undefined");
403
438
  }
@@ -410,7 +445,7 @@ function mongoStorage({
410
445
  });
411
446
  });
412
447
  indexer.hooks.hook("message:invalidate", async ({ message }) => {
413
- const { cursor } = message.invalidate;
448
+ const { cursor } = message;
414
449
  if (!cursor) {
415
450
  throw new MongoStorageError("invalidate cursor is undefined");
416
451
  }
@@ -424,16 +459,19 @@ function mongoStorage({
424
459
  });
425
460
  indexer.hooks.hook("handler:middleware", async ({ use }) => {
426
461
  use(async (context, next) => {
427
- const { endCursor } = context;
462
+ const { endCursor, finality, cursor } = context;
428
463
  if (!endCursor) {
429
464
  throw new MongoStorageError("end cursor is undefined");
430
465
  }
431
466
  await withTransaction(client, async (session) => {
432
467
  const db = client.db(dbName, dbOptions);
433
468
  context[MONGO_PROPERTY] = new MongoStorage(db, session, endCursor);
469
+ if (prevFinality === "pending") {
470
+ await invalidate(db, session, cursor, collections);
471
+ }
434
472
  await next();
435
473
  delete context[MONGO_PROPERTY];
436
- if (enablePersistence) {
474
+ if (enablePersistence && finality !== "pending") {
437
475
  await persistState({
438
476
  db,
439
477
  endCursor,
@@ -441,6 +479,7 @@ function mongoStorage({
441
479
  indexerId
442
480
  });
443
481
  }
482
+ prevFinality = finality;
444
483
  });
445
484
  });
446
485
  });
@@ -448,3 +487,4 @@ function mongoStorage({
448
487
  }
449
488
 
450
489
  export { MongoCollection, MongoStorage, mongoStorage, useMongoStorage };
490
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","sources":["../src/mongo.ts","../src/utils.ts","../src/persistence.ts","../src/storage.ts","../src/index.ts"],"sourcesContent":["import type { Cursor } from \"@apibara/protocol\";\nimport type { ClientSession, Db } from \"mongodb\";\n\nexport async function invalidate(\n db: Db,\n session: ClientSession,\n cursor: Cursor,\n collections: string[],\n) {\n const orderKeyValue = Number(cursor.orderKey);\n for (const collection of collections) {\n // Delete documents where the lower bound of _cursor is greater than the invalidate cursor\n await db.collection(collection).deleteMany(\n {\n \"_cursor.from\": {\n $gt: orderKeyValue,\n },\n },\n { session },\n );\n\n // Update documents where the upper bound of _cursor is greater than the invalidate cursor\n await db.collection(collection).updateMany(\n { \"_cursor.to\": { $gt: orderKeyValue } },\n {\n $set: {\n \"_cursor.to\": null,\n },\n },\n { session },\n );\n }\n}\n\nexport async function finalize(\n db: Db,\n session: ClientSession,\n cursor: Cursor,\n collections: string[],\n) {\n const orderKeyValue = Number(cursor.orderKey);\n for (const collection of collections) {\n // Delete documents where the upper bound of _cursor is less than the finalize cursor\n await db.collection(collection).deleteMany(\n {\n \"_cursor.to\": { $lte: orderKeyValue },\n },\n { session },\n );\n }\n}\n\nexport async function cleanupStorage(\n db: Db,\n session: ClientSession,\n collections: string[],\n) {\n for (const collection of collections) {\n try {\n // Delete all documents in the collection\n await db.collection(collection).deleteMany({}, { session });\n } catch (error) {\n throw new Error(`Failed to clean up collection ${collection}`, {\n cause: error,\n });\n }\n }\n}\n","import type { ClientSession, MongoClient } from \"mongodb\";\n\nexport class MongoStorageError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"MongoStorageError\";\n }\n}\n\nexport async function withTransaction<T>(\n client: MongoClient,\n cb: (session: ClientSession) => Promise<T>,\n) {\n return await client.withSession(async (session) => {\n return await session.withTransaction(\n async (session) => {\n return await cb(session);\n },\n {\n retryWrites: false,\n },\n );\n });\n}\n","import { type Cursor, normalizeCursor } from \"@apibara/protocol\";\nimport type { ClientSession, Db } from \"mongodb\";\nimport { MongoStorageError } from \"./utils\";\n\nexport type CheckpointSchema = {\n id: string;\n orderKey: number;\n uniqueKey: string | null;\n};\n\nexport type FilterSchema = {\n id: string;\n filter: Record<string, unknown>;\n fromBlock: number;\n toBlock: number | null;\n};\n\nexport const checkpointCollectionName = \"checkpoints\";\nexport const filterCollectionName = \"filters\";\n\nexport async function initializePersistentState(\n db: Db,\n session: ClientSession,\n) {\n const checkpoint = db.collection<CheckpointSchema>(checkpointCollectionName);\n const filter = db.collection<FilterSchema>(filterCollectionName);\n\n await checkpoint.createIndex({ id: 1 }, { session });\n await filter.createIndex({ id: 1, fromBlock: 1 }, { session });\n}\n\nexport async function persistState<TFilter>(props: {\n db: Db;\n session: ClientSession;\n endCursor: Cursor;\n filter?: TFilter;\n indexerId: string;\n}) {\n const { db, session, endCursor, filter, indexerId } = props;\n\n if (endCursor) {\n await db.collection<CheckpointSchema>(checkpointCollectionName).updateOne(\n { id: indexerId },\n {\n $set: {\n orderKey: Number(endCursor.orderKey),\n uniqueKey: endCursor.uniqueKey ? endCursor.uniqueKey : null,\n },\n },\n { upsert: true, session },\n );\n\n if (filter) {\n // Update existing filter's to_block\n await db\n .collection<FilterSchema>(filterCollectionName)\n .updateMany(\n { id: indexerId, toBlock: null },\n { $set: { toBlock: Number(endCursor.orderKey) } },\n { session },\n );\n\n // Insert new filter\n await db.collection<FilterSchema>(filterCollectionName).updateOne(\n {\n id: indexerId,\n fromBlock: Number(endCursor.orderKey),\n },\n {\n $set: {\n filter: filter as Record<string, unknown>,\n fromBlock: Number(endCursor.orderKey),\n toBlock: null,\n },\n },\n { upsert: true, session },\n );\n }\n }\n}\n\nexport async function getState<TFilter>(props: {\n db: Db;\n session: ClientSession;\n indexerId: string;\n}): Promise<{ cursor?: Cursor; filter?: TFilter }> {\n const { db, session, indexerId } = props;\n\n let cursor: Cursor | undefined;\n let filter: TFilter | undefined;\n\n const checkpointRow = await db\n .collection<CheckpointSchema>(checkpointCollectionName)\n .findOne({ id: indexerId }, { session });\n\n if (checkpointRow) {\n cursor = normalizeCursor({\n orderKey: BigInt(checkpointRow.orderKey),\n uniqueKey: checkpointRow.uniqueKey,\n });\n }\n\n const filterRow = await db\n .collection<FilterSchema>(filterCollectionName)\n .findOne(\n {\n id: indexerId,\n toBlock: null,\n },\n { session },\n );\n\n if (filterRow) {\n filter = filterRow.filter as TFilter;\n }\n\n return { cursor, filter };\n}\n\nexport async function invalidateState(props: {\n db: Db;\n session: ClientSession;\n cursor: Cursor;\n indexerId: string;\n}) {\n const { db, session, cursor, indexerId } = props;\n\n await db\n .collection<FilterSchema>(filterCollectionName)\n .deleteMany(\n { id: indexerId, fromBlock: { $gt: Number(cursor.orderKey) } },\n { session },\n );\n\n await db\n .collection<FilterSchema>(filterCollectionName)\n .updateMany(\n { id: indexerId, toBlock: { $gt: Number(cursor.orderKey) } },\n { $set: { toBlock: null } },\n { session },\n );\n}\n\nexport async function finalizeState(props: {\n db: Db;\n session: ClientSession;\n cursor: Cursor;\n indexerId: string;\n}) {\n const { db, session, cursor, indexerId } = props;\n\n await db.collection<FilterSchema>(filterCollectionName).deleteMany(\n {\n id: indexerId,\n toBlock: { $lte: Number(cursor.orderKey) },\n },\n { session },\n );\n}\n\nexport async function resetPersistence(props: {\n db: Db;\n session: ClientSession;\n indexerId: string;\n}) {\n const { db, session, indexerId } = props;\n\n try {\n // Delete all checkpoints for this indexer\n await db\n .collection<CheckpointSchema>(checkpointCollectionName)\n .deleteMany({ id: indexerId }, { session });\n\n // Delete all filters for this indexer\n await db\n .collection<FilterSchema>(filterCollectionName)\n .deleteMany({ id: indexerId }, { session });\n } catch (error) {\n throw new MongoStorageError(\"Failed to reset persistence state\", {\n cause: error,\n });\n }\n}\n","import type { Cursor } from \"@apibara/protocol\";\nimport type {\n BulkWriteOptions,\n ClientSession,\n Collection,\n CollectionOptions,\n Db,\n DeleteOptions,\n Document,\n Filter,\n FindCursor,\n FindOneAndUpdateOptions,\n FindOptions,\n InsertManyResult,\n InsertOneOptions,\n InsertOneResult,\n MatchKeysAndValues,\n OptionalUnlessRequiredId,\n UpdateFilter,\n UpdateOptions,\n UpdateResult,\n WithId,\n} from \"mongodb\";\n\nexport class MongoStorage {\n constructor(\n private db: Db,\n private session: ClientSession,\n private endCursor?: Cursor,\n ) {}\n\n collection<TSchema extends Document = Document>(\n name: string,\n options?: CollectionOptions,\n ) {\n const collection = this.db.collection<TSchema>(name, options);\n\n return new MongoCollection<TSchema>(\n this.session,\n collection,\n this.endCursor,\n );\n }\n}\n\nexport type MongoCursor = {\n from: number | null;\n to: number | null;\n};\n\nexport type CursoredSchema<TSchema extends Document> = TSchema & {\n _cursor: MongoCursor;\n};\n\nexport class MongoCollection<TSchema extends Document> {\n constructor(\n private session: ClientSession,\n private collection: Collection<TSchema>,\n private endCursor?: Cursor,\n ) {}\n\n async insertOne(\n doc: OptionalUnlessRequiredId<TSchema>,\n options?: InsertOneOptions,\n ): Promise<InsertOneResult<TSchema>> {\n return await this.collection.insertOne(\n {\n ...doc,\n _cursor: {\n from: Number(this.endCursor?.orderKey),\n to: null,\n } as MongoCursor,\n },\n { ...options, session: this.session },\n );\n }\n\n async insertMany(\n docs: ReadonlyArray<OptionalUnlessRequiredId<TSchema>>,\n options?: BulkWriteOptions,\n ): Promise<InsertManyResult<TSchema>> {\n return await this.collection.insertMany(\n docs.map((doc) => ({\n ...doc,\n _cursor: {\n from: Number(this.endCursor?.orderKey),\n to: null,\n } as MongoCursor,\n })),\n { ...options, session: this.session },\n );\n }\n\n async updateOne(\n filter: Filter<TSchema>,\n update: UpdateFilter<TSchema>,\n options?: UpdateOptions,\n ): Promise<UpdateResult<TSchema>> {\n // 1. Find and update the document, getting the old version\n const oldDoc = await this.collection.findOneAndUpdate(\n {\n ...filter,\n \"_cursor.to\": null,\n },\n {\n ...update,\n $set: {\n ...update.$set,\n \"_cursor.from\": Number(this.endCursor?.orderKey),\n } as unknown as MatchKeysAndValues<TSchema>,\n },\n {\n ...options,\n session: this.session,\n returnDocument: \"before\",\n } as FindOneAndUpdateOptions,\n );\n\n // 2. If we found and updated a document, insert its old version\n if (oldDoc) {\n const { _id, ...doc } = oldDoc;\n await this.collection.insertOne(\n {\n ...doc,\n _cursor: {\n ...oldDoc._cursor,\n to: Number(this.endCursor?.orderKey),\n },\n } as unknown as OptionalUnlessRequiredId<TSchema>,\n { session: this.session },\n );\n }\n\n // 3. Return an UpdateResult-compatible object\n return {\n acknowledged: true,\n modifiedCount: oldDoc ? 1 : 0,\n upsertedId: null,\n upsertedCount: 0,\n matchedCount: oldDoc ? 1 : 0,\n };\n }\n\n async updateMany(\n filter: Filter<TSchema>,\n update: UpdateFilter<TSchema>,\n options?: UpdateOptions,\n ): Promise<UpdateResult<TSchema>> {\n // 1. Find all documents matching the filter that are latest (to: null)\n const oldDocs = await this.collection\n .find(\n {\n ...filter,\n \"_cursor.to\": null,\n },\n { session: this.session },\n )\n .toArray();\n\n // 2. Update to the new values with updateMany\n // (setting _cursor.from to endCursor, leaving _cursor.to unchanged)\n const updateResult = await this.collection.updateMany(\n {\n ...filter,\n \"_cursor.to\": null,\n },\n {\n ...update,\n $set: {\n ...update.$set,\n \"_cursor.from\": Number(this.endCursor?.orderKey),\n } as unknown as MatchKeysAndValues<TSchema>,\n },\n { ...options, session: this.session },\n );\n\n // 3. Adjust the cursor.to of the old values\n const oldDocsWithUpdatedCursor = oldDocs.map(({ _id, ...doc }) => ({\n ...doc,\n _cursor: {\n ...doc._cursor,\n to: Number(this.endCursor?.orderKey),\n },\n }));\n\n // 4. Insert the old values back into the db\n if (oldDocsWithUpdatedCursor.length > 0) {\n await this.collection.insertMany(\n oldDocsWithUpdatedCursor as unknown as OptionalUnlessRequiredId<TSchema>[],\n { session: this.session },\n );\n }\n\n return updateResult;\n }\n\n async deleteOne(\n filter: Filter<TSchema>,\n options?: DeleteOptions,\n ): Promise<UpdateResult<TSchema>> {\n return await this.collection.updateOne(\n {\n ...filter,\n \"_cursor.to\": null,\n },\n {\n $set: {\n \"_cursor.to\": Number(this.endCursor?.orderKey),\n } as unknown as MatchKeysAndValues<TSchema>,\n },\n { ...options, session: this.session },\n );\n }\n\n async deleteMany(\n filter?: Filter<TSchema>,\n options?: DeleteOptions,\n ): Promise<UpdateResult<TSchema>> {\n return await this.collection.updateMany(\n {\n ...((filter ?? {}) as Filter<TSchema>),\n \"_cursor.to\": null,\n },\n {\n $set: {\n \"_cursor.to\": Number(this.endCursor?.orderKey),\n } as unknown as MatchKeysAndValues<TSchema>,\n },\n { ...options, session: this.session },\n );\n }\n\n async findOne(\n filter: Filter<TSchema>,\n options?: Omit<FindOptions, \"timeoutMode\">,\n ): Promise<WithId<TSchema> | null> {\n return await this.collection.findOne(\n {\n ...filter,\n \"_cursor.to\": null,\n },\n { ...options, session: this.session },\n );\n }\n\n find(\n filter: Filter<TSchema>,\n options?: FindOptions,\n ): FindCursor<WithId<TSchema>> {\n return this.collection.find(\n {\n ...filter,\n \"_cursor.to\": null,\n },\n { ...options, session: this.session },\n );\n }\n}\n","import { useIndexerContext } from \"@apibara/indexer\";\nimport { defineIndexerPlugin, useLogger } from \"@apibara/indexer/plugins\";\nimport type { DbOptions, MongoClient } from \"mongodb\";\n\nimport { generateIndexerId } from \"@apibara/indexer/internal\";\nimport { useInternalContext } from \"@apibara/indexer/internal/plugins\";\nimport type { Cursor, DataFinality } from \"@apibara/protocol\";\nimport { cleanupStorage, finalize, invalidate } from \"./mongo\";\nimport {\n finalizeState,\n getState,\n initializePersistentState,\n invalidateState,\n persistState,\n resetPersistence,\n} from \"./persistence\";\nimport { MongoStorage } from \"./storage\";\nimport { MongoStorageError, withTransaction } from \"./utils\";\n\nexport { MongoCollection, MongoStorage } from \"./storage\";\n\nconst MONGO_PROPERTY = \"_mongo\";\n\nexport function useMongoStorage(): MongoStorage {\n const context = useIndexerContext();\n\n if (!context[MONGO_PROPERTY]) {\n throw new MongoStorageError(\n \"mongo storage is not available. Did you register the plugin?\",\n );\n }\n\n return context[MONGO_PROPERTY] as MongoStorage;\n}\n\nexport interface MongoStorageOptions {\n client: MongoClient;\n dbName: string;\n dbOptions?: DbOptions;\n collections: string[];\n persistState?: boolean;\n indexerName?: string;\n}\n/**\n * Creates a plugin that uses MongoDB as the storage layer.\n *\n * Supports storing the indexer's state and provides a simple Key-Value store.\n * @param options.client - The MongoDB client instance.\n * @param options.dbName - The name of the database.\n * @param options.dbOptions - The database options.\n * @param options.collections - The collections to use.\n * @param options.persistState - Whether to persist the indexer's state. Defaults to true.\n * @param options.indexerName - The name of the indexer. Defaults value is 'default'.\n */\nexport function mongoStorage<TFilter, TBlock>({\n client,\n dbName,\n dbOptions,\n collections,\n persistState: enablePersistence = true,\n indexerName: identifier = \"default\",\n}: MongoStorageOptions) {\n return defineIndexerPlugin<TFilter, TBlock>((indexer) => {\n let indexerId = \"\";\n const alwaysReindex = process.env[\"APIBARA_ALWAYS_REINDEX\"] === \"true\";\n let prevFinality: DataFinality | undefined;\n\n indexer.hooks.hook(\"plugins:init\", async () => {\n const { indexerName } = useInternalContext();\n indexerId = generateIndexerId(indexerName, identifier);\n const logger = useLogger();\n\n await withTransaction(client, async (session) => {\n const db = client.db(dbName, dbOptions);\n if (enablePersistence) {\n await initializePersistentState(db, session);\n }\n\n if (alwaysReindex) {\n logger.warn(\n `Reindexing: Deleting all data from collections - ${collections.join(\", \")}`,\n );\n\n await cleanupStorage(db, session, collections);\n\n if (enablePersistence) {\n await resetPersistence({ db, session, indexerId });\n }\n\n logger.success(\"All data has been cleaned up for reindexing\");\n }\n });\n });\n\n indexer.hooks.hook(\"connect:before\", async ({ request }) => {\n if (!enablePersistence) {\n return;\n }\n\n await withTransaction(client, async (session) => {\n const db = client.db(dbName, dbOptions);\n const { cursor, filter } = await getState<TFilter>({\n db,\n session,\n indexerId,\n });\n\n if (cursor) {\n request.startingCursor = cursor;\n }\n\n if (filter) {\n request.filter[1] = filter;\n }\n });\n });\n\n indexer.hooks.hook(\"connect:after\", async ({ request }) => {\n // On restart, we need to invalidate data for blocks that were processed but not persisted.\n const cursor = request.startingCursor;\n\n if (!cursor) {\n return;\n }\n\n await withTransaction(client, async (session) => {\n const db = client.db(dbName, dbOptions);\n await invalidate(db, session, cursor, collections);\n\n if (enablePersistence) {\n await invalidateState({ db, session, cursor, indexerId });\n }\n });\n });\n\n indexer.hooks.hook(\"connect:factory\", async ({ request, endCursor }) => {\n if (!enablePersistence) {\n return;\n }\n await withTransaction(client, async (session) => {\n const db = client.db(dbName, dbOptions);\n if (endCursor && request.filter[1]) {\n await persistState({\n db,\n endCursor,\n session,\n filter: request.filter[1],\n indexerId,\n });\n }\n });\n });\n\n indexer.hooks.hook(\"message:finalize\", async ({ message }) => {\n const { cursor } = message;\n\n if (!cursor) {\n throw new MongoStorageError(\"finalized cursor is undefined\");\n }\n\n await withTransaction(client, async (session) => {\n const db = client.db(dbName, dbOptions);\n await finalize(db, session, cursor, collections);\n\n if (enablePersistence) {\n await finalizeState({ db, session, cursor, indexerId });\n }\n });\n });\n\n indexer.hooks.hook(\"message:invalidate\", async ({ message }) => {\n const { cursor } = message;\n\n if (!cursor) {\n throw new MongoStorageError(\"invalidate cursor is undefined\");\n }\n\n await withTransaction(client, async (session) => {\n const db = client.db(dbName, dbOptions);\n await invalidate(db, session, cursor, collections);\n\n if (enablePersistence) {\n await invalidateState({ db, session, cursor, indexerId });\n }\n });\n });\n\n indexer.hooks.hook(\"handler:middleware\", async ({ use }) => {\n use(async (context, next) => {\n const { endCursor, finality, cursor } = context as {\n cursor: Cursor;\n endCursor: Cursor;\n finality: DataFinality;\n };\n\n if (!endCursor) {\n throw new MongoStorageError(\"end cursor is undefined\");\n }\n\n await withTransaction(client, async (session) => {\n const db = client.db(dbName, dbOptions);\n context[MONGO_PROPERTY] = new MongoStorage(db, session, endCursor);\n\n if (prevFinality === \"pending\") {\n // invalidate if previous block's finality was \"pending\"\n await invalidate(db, session, cursor, collections);\n }\n\n await next();\n\n delete context[MONGO_PROPERTY];\n\n if (enablePersistence && finality !== \"pending\") {\n await persistState({\n db,\n endCursor,\n session,\n indexerId,\n });\n }\n\n prevFinality = finality;\n });\n });\n });\n });\n}\n"],"names":["session"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apibara/plugin-mongo",
3
- "version": "2.1.0-beta.5",
3
+ "version": "2.1.0-beta.51",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -35,7 +35,7 @@
35
35
  "mongodb": "^6.12.0"
36
36
  },
37
37
  "dependencies": {
38
- "@apibara/indexer": "2.1.0-beta.5",
39
- "@apibara/protocol": "2.1.0-beta.5"
38
+ "@apibara/indexer": "2.1.0-beta.52",
39
+ "@apibara/protocol": "2.1.0-beta.52"
40
40
  }
41
41
  }
package/src/index.ts CHANGED
@@ -1,16 +1,18 @@
1
1
  import { useIndexerContext } from "@apibara/indexer";
2
- import { defineIndexerPlugin } from "@apibara/indexer/plugins";
2
+ import { defineIndexerPlugin, useLogger } from "@apibara/indexer/plugins";
3
3
  import type { DbOptions, MongoClient } from "mongodb";
4
4
 
5
5
  import { generateIndexerId } from "@apibara/indexer/internal";
6
6
  import { useInternalContext } from "@apibara/indexer/internal/plugins";
7
- import { finalize, invalidate } from "./mongo";
7
+ import type { Cursor, DataFinality } from "@apibara/protocol";
8
+ import { cleanupStorage, finalize, invalidate } from "./mongo";
8
9
  import {
9
10
  finalizeState,
10
11
  getState,
11
12
  initializePersistentState,
12
13
  invalidateState,
13
14
  persistState,
15
+ resetPersistence,
14
16
  } from "./persistence";
15
17
  import { MongoStorage } from "./storage";
16
18
  import { MongoStorageError, withTransaction } from "./utils";
@@ -60,16 +62,33 @@ export function mongoStorage<TFilter, TBlock>({
60
62
  }: MongoStorageOptions) {
61
63
  return defineIndexerPlugin<TFilter, TBlock>((indexer) => {
62
64
  let indexerId = "";
65
+ const alwaysReindex = process.env["APIBARA_ALWAYS_REINDEX"] === "true";
66
+ let prevFinality: DataFinality | undefined;
63
67
 
64
- indexer.hooks.hook("run:before", async () => {
68
+ indexer.hooks.hook("plugins:init", async () => {
65
69
  const { indexerName } = useInternalContext();
66
70
  indexerId = generateIndexerId(indexerName, identifier);
71
+ const logger = useLogger();
67
72
 
68
73
  await withTransaction(client, async (session) => {
69
74
  const db = client.db(dbName, dbOptions);
70
75
  if (enablePersistence) {
71
76
  await initializePersistentState(db, session);
72
77
  }
78
+
79
+ if (alwaysReindex) {
80
+ logger.warn(
81
+ `Reindexing: Deleting all data from collections - ${collections.join(", ")}`,
82
+ );
83
+
84
+ await cleanupStorage(db, session, collections);
85
+
86
+ if (enablePersistence) {
87
+ await resetPersistence({ db, session, indexerId });
88
+ }
89
+
90
+ logger.success("All data has been cleaned up for reindexing");
91
+ }
73
92
  });
74
93
  });
75
94
 
@@ -133,7 +152,7 @@ export function mongoStorage<TFilter, TBlock>({
133
152
  });
134
153
 
135
154
  indexer.hooks.hook("message:finalize", async ({ message }) => {
136
- const { cursor } = message.finalize;
155
+ const { cursor } = message;
137
156
 
138
157
  if (!cursor) {
139
158
  throw new MongoStorageError("finalized cursor is undefined");
@@ -150,7 +169,7 @@ export function mongoStorage<TFilter, TBlock>({
150
169
  });
151
170
 
152
171
  indexer.hooks.hook("message:invalidate", async ({ message }) => {
153
- const { cursor } = message.invalidate;
172
+ const { cursor } = message;
154
173
 
155
174
  if (!cursor) {
156
175
  throw new MongoStorageError("invalidate cursor is undefined");
@@ -168,7 +187,11 @@ export function mongoStorage<TFilter, TBlock>({
168
187
 
169
188
  indexer.hooks.hook("handler:middleware", async ({ use }) => {
170
189
  use(async (context, next) => {
171
- const { endCursor } = context;
190
+ const { endCursor, finality, cursor } = context as {
191
+ cursor: Cursor;
192
+ endCursor: Cursor;
193
+ finality: DataFinality;
194
+ };
172
195
 
173
196
  if (!endCursor) {
174
197
  throw new MongoStorageError("end cursor is undefined");
@@ -178,10 +201,16 @@ export function mongoStorage<TFilter, TBlock>({
178
201
  const db = client.db(dbName, dbOptions);
179
202
  context[MONGO_PROPERTY] = new MongoStorage(db, session, endCursor);
180
203
 
204
+ if (prevFinality === "pending") {
205
+ // invalidate if previous block's finality was "pending"
206
+ await invalidate(db, session, cursor, collections);
207
+ }
208
+
181
209
  await next();
210
+
182
211
  delete context[MONGO_PROPERTY];
183
212
 
184
- if (enablePersistence) {
213
+ if (enablePersistence && finality !== "pending") {
185
214
  await persistState({
186
215
  db,
187
216
  endCursor,
@@ -189,6 +218,8 @@ export function mongoStorage<TFilter, TBlock>({
189
218
  indexerId,
190
219
  });
191
220
  }
221
+
222
+ prevFinality = finality;
192
223
  });
193
224
  });
194
225
  });
package/src/mongo.ts CHANGED
@@ -49,3 +49,20 @@ export async function finalize(
49
49
  );
50
50
  }
51
51
  }
52
+
53
+ export async function cleanupStorage(
54
+ db: Db,
55
+ session: ClientSession,
56
+ collections: string[],
57
+ ) {
58
+ for (const collection of collections) {
59
+ try {
60
+ // Delete all documents in the collection
61
+ await db.collection(collection).deleteMany({}, { session });
62
+ } catch (error) {
63
+ throw new Error(`Failed to clean up collection ${collection}`, {
64
+ cause: error,
65
+ });
66
+ }
67
+ }
68
+ }
@@ -1,5 +1,6 @@
1
1
  import { type Cursor, normalizeCursor } from "@apibara/protocol";
2
2
  import type { ClientSession, Db } from "mongodb";
3
+ import { MongoStorageError } from "./utils";
3
4
 
4
5
  export type CheckpointSchema = {
5
6
  id: string;
@@ -21,13 +22,8 @@ export async function initializePersistentState(
21
22
  db: Db,
22
23
  session: ClientSession,
23
24
  ) {
24
- const checkpoint = await db.createCollection<CheckpointSchema>(
25
- checkpointCollectionName,
26
- { session },
27
- );
28
- const filter = await db.createCollection<FilterSchema>(filterCollectionName, {
29
- session,
30
- });
25
+ const checkpoint = db.collection<CheckpointSchema>(checkpointCollectionName);
26
+ const filter = db.collection<FilterSchema>(filterCollectionName);
31
27
 
32
28
  await checkpoint.createIndex({ id: 1 }, { session });
33
29
  await filter.createIndex({ id: 1, fromBlock: 1 }, { session });
@@ -48,7 +44,7 @@ export async function persistState<TFilter>(props: {
48
44
  {
49
45
  $set: {
50
46
  orderKey: Number(endCursor.orderKey),
51
- uniqueKey: endCursor.uniqueKey,
47
+ uniqueKey: endCursor.uniqueKey ? endCursor.uniqueKey : null,
52
48
  },
53
49
  },
54
50
  { upsert: true, session },
@@ -161,3 +157,27 @@ export async function finalizeState(props: {
161
157
  { session },
162
158
  );
163
159
  }
160
+
161
+ export async function resetPersistence(props: {
162
+ db: Db;
163
+ session: ClientSession;
164
+ indexerId: string;
165
+ }) {
166
+ const { db, session, indexerId } = props;
167
+
168
+ try {
169
+ // Delete all checkpoints for this indexer
170
+ await db
171
+ .collection<CheckpointSchema>(checkpointCollectionName)
172
+ .deleteMany({ id: indexerId }, { session });
173
+
174
+ // Delete all filters for this indexer
175
+ await db
176
+ .collection<FilterSchema>(filterCollectionName)
177
+ .deleteMany({ id: indexerId }, { session });
178
+ } catch (error) {
179
+ throw new MongoStorageError("Failed to reset persistence state", {
180
+ cause: error,
181
+ });
182
+ }
183
+ }
package/src/utils.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import type { ClientSession, MongoClient } from "mongodb";
2
2
 
3
3
  export class MongoStorageError extends Error {
4
- constructor(message: string) {
5
- super(message);
4
+ constructor(message: string, options?: ErrorOptions) {
5
+ super(message, options);
6
6
  this.name = "MongoStorageError";
7
7
  }
8
8
  }
@@ -12,8 +12,13 @@ export async function withTransaction<T>(
12
12
  cb: (session: ClientSession) => Promise<T>,
13
13
  ) {
14
14
  return await client.withSession(async (session) => {
15
- return await session.withTransaction(async (session) => {
16
- return await cb(session);
17
- });
15
+ return await session.withTransaction(
16
+ async (session) => {
17
+ return await cb(session);
18
+ },
19
+ {
20
+ retryWrites: false,
21
+ },
22
+ );
18
23
  });
19
24
  }