@apibara/plugin-drizzle 2.1.0-beta.1 → 2.1.0-beta.11
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 +139 -15
- package/dist/index.d.cts +131 -3
- package/dist/index.d.mts +131 -3
- package/dist/index.d.ts +131 -3
- package/dist/index.mjs +123 -5
- package/dist/shared/plugin-drizzle.e884ca32.cjs +7 -0
- package/dist/shared/plugin-drizzle.f8d1b186.mjs +4 -0
- package/dist/testing.cjs +13 -0
- package/dist/testing.d.cts +6 -0
- package/dist/testing.d.mts +6 -0
- package/dist/testing.d.ts +6 -0
- package/dist/testing.mjs +11 -0
- package/package.json +9 -3
- package/src/constants.ts +2 -0
- package/src/helper.ts +192 -0
- package/src/index.ts +64 -3
- package/src/persistence.ts +21 -0
- package/src/storage.ts +42 -0
- package/src/testing.ts +13 -0
package/src/index.ts
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
|
|
|
4
4
|
import type {
|
|
5
5
|
ExtractTablesWithRelations,
|
|
@@ -14,14 +14,18 @@ import type {
|
|
|
14
14
|
PgQueryResultHKT,
|
|
15
15
|
PgTransaction,
|
|
16
16
|
} from "drizzle-orm/pg-core";
|
|
17
|
+
import { DRIZZLE_PROPERTY, DRIZZLE_STORAGE_DB_PROPERTY } from "./constants";
|
|
18
|
+
import { type MigrateOptions, migrate } from "./helper";
|
|
17
19
|
import {
|
|
18
20
|
finalizeState,
|
|
19
21
|
getState,
|
|
20
22
|
initializePersistentState,
|
|
21
23
|
invalidateState,
|
|
22
24
|
persistState,
|
|
25
|
+
resetPersistence,
|
|
23
26
|
} from "./persistence";
|
|
24
27
|
import {
|
|
28
|
+
cleanupStorage,
|
|
25
29
|
finalize,
|
|
26
30
|
initializeReorgRollbackTable,
|
|
27
31
|
invalidate,
|
|
@@ -30,7 +34,8 @@ import {
|
|
|
30
34
|
} from "./storage";
|
|
31
35
|
import { DrizzleStorageError, sleep, withTransaction } from "./utils";
|
|
32
36
|
|
|
33
|
-
|
|
37
|
+
export * from "./helper";
|
|
38
|
+
|
|
34
39
|
const MAX_RETRIES = 5;
|
|
35
40
|
|
|
36
41
|
export type DrizzleStorage<
|
|
@@ -67,11 +72,30 @@ export interface DrizzleStorageOptions<
|
|
|
67
72
|
TSchema extends
|
|
68
73
|
TablesRelationalConfig = ExtractTablesWithRelations<TFullSchema>,
|
|
69
74
|
> {
|
|
75
|
+
/**
|
|
76
|
+
* The Drizzle database instance.
|
|
77
|
+
*/
|
|
70
78
|
db: PgDatabase<TQueryResult, TFullSchema, TSchema>;
|
|
79
|
+
/**
|
|
80
|
+
* Whether to persist the indexer's state. Defaults to true.
|
|
81
|
+
*/
|
|
71
82
|
persistState?: boolean;
|
|
83
|
+
/**
|
|
84
|
+
* The name of the indexer. Default value is 'default'.
|
|
85
|
+
*/
|
|
72
86
|
indexerName?: string;
|
|
87
|
+
/**
|
|
88
|
+
* The schema of the database.
|
|
89
|
+
*/
|
|
73
90
|
schema?: Record<string, unknown>;
|
|
91
|
+
/**
|
|
92
|
+
* The column to use as the id. Defaults to 'id'.
|
|
93
|
+
*/
|
|
74
94
|
idColumn?: string;
|
|
95
|
+
/**
|
|
96
|
+
* The options for the database migration. When provided, the database will automatically run migrations before the indexer runs.
|
|
97
|
+
*/
|
|
98
|
+
migrate?: MigrateOptions;
|
|
75
99
|
}
|
|
76
100
|
|
|
77
101
|
/**
|
|
@@ -83,6 +107,7 @@ export interface DrizzleStorageOptions<
|
|
|
83
107
|
* @param options.indexerName - The name of the indexer. Defaults value is 'default'.
|
|
84
108
|
* @param options.schema - The schema of the database.
|
|
85
109
|
* @param options.idColumn - The column to use as the id. Defaults to 'id'.
|
|
110
|
+
* @param options.migrate - The options for the database migration. when provided, the database will automatically run migrations before the indexer runs.
|
|
86
111
|
*/
|
|
87
112
|
export function drizzleStorage<
|
|
88
113
|
TFilter,
|
|
@@ -97,10 +122,12 @@ export function drizzleStorage<
|
|
|
97
122
|
indexerName: identifier = "default",
|
|
98
123
|
schema,
|
|
99
124
|
idColumn = "id",
|
|
125
|
+
migrate: migrateOptions,
|
|
100
126
|
}: DrizzleStorageOptions<TQueryResult, TFullSchema, TSchema>) {
|
|
101
127
|
return defineIndexerPlugin<TFilter, TBlock>((indexer) => {
|
|
102
128
|
let tableNames: string[] = [];
|
|
103
129
|
let indexerId = "";
|
|
130
|
+
const alwaysReindex = process.env["APIBARA_ALWAYS_REINDEX"] === "true";
|
|
104
131
|
|
|
105
132
|
try {
|
|
106
133
|
tableNames = Object.values((schema as TSchema) ?? db._.schema ?? {}).map(
|
|
@@ -113,15 +140,46 @@ export function drizzleStorage<
|
|
|
113
140
|
}
|
|
114
141
|
|
|
115
142
|
indexer.hooks.hook("run:before", async () => {
|
|
143
|
+
const internalContext = useInternalContext();
|
|
144
|
+
const context = useIndexerContext();
|
|
145
|
+
const logger = useLogger();
|
|
146
|
+
|
|
147
|
+
// For testing purposes using vcr.
|
|
148
|
+
context[DRIZZLE_STORAGE_DB_PROPERTY] = db;
|
|
149
|
+
|
|
116
150
|
const { indexerName: indexerFileName, availableIndexers } =
|
|
117
|
-
|
|
151
|
+
internalContext;
|
|
118
152
|
|
|
119
153
|
indexerId = generateIndexerId(indexerFileName, identifier);
|
|
120
154
|
|
|
155
|
+
if (alwaysReindex) {
|
|
156
|
+
logger.warn(
|
|
157
|
+
`Reindexing: Deleting all data from tables - ${tableNames.join(", ")}`,
|
|
158
|
+
);
|
|
159
|
+
await withTransaction(db, async (tx) => {
|
|
160
|
+
await cleanupStorage(tx, tableNames, indexerId);
|
|
161
|
+
|
|
162
|
+
if (enablePersistence) {
|
|
163
|
+
await resetPersistence({ tx, indexerId });
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
logger.success("Tables have been cleaned up for reindexing");
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
121
170
|
let retries = 0;
|
|
122
171
|
|
|
172
|
+
// incase the migrations are already applied, we don't want to run them again
|
|
173
|
+
let migrationsApplied = false;
|
|
174
|
+
|
|
123
175
|
while (retries <= MAX_RETRIES) {
|
|
124
176
|
try {
|
|
177
|
+
if (migrateOptions && !migrationsApplied) {
|
|
178
|
+
// @ts-ignore type mismatch for db
|
|
179
|
+
await migrate(db, migrateOptions);
|
|
180
|
+
migrationsApplied = true;
|
|
181
|
+
logger.success("Migrations applied");
|
|
182
|
+
}
|
|
125
183
|
await withTransaction(db, async (tx) => {
|
|
126
184
|
await initializeReorgRollbackTable(tx, indexerId);
|
|
127
185
|
if (enablePersistence) {
|
|
@@ -131,6 +189,9 @@ export function drizzleStorage<
|
|
|
131
189
|
break;
|
|
132
190
|
} catch (error) {
|
|
133
191
|
if (retries === MAX_RETRIES) {
|
|
192
|
+
if (error instanceof DrizzleStorageError) {
|
|
193
|
+
throw error;
|
|
194
|
+
}
|
|
134
195
|
throw new DrizzleStorageError(
|
|
135
196
|
"Initialization failed after 5 retries",
|
|
136
197
|
{
|
package/src/persistence.ts
CHANGED
|
@@ -297,3 +297,24 @@ export async function finalizeState<
|
|
|
297
297
|
});
|
|
298
298
|
}
|
|
299
299
|
}
|
|
300
|
+
|
|
301
|
+
export async function resetPersistence<
|
|
302
|
+
TQueryResult extends PgQueryResultHKT,
|
|
303
|
+
TFullSchema extends Record<string, unknown> = Record<string, never>,
|
|
304
|
+
TSchema extends
|
|
305
|
+
TablesRelationalConfig = ExtractTablesWithRelations<TFullSchema>,
|
|
306
|
+
>(props: {
|
|
307
|
+
tx: PgTransaction<TQueryResult, TFullSchema, TSchema>;
|
|
308
|
+
indexerId: string;
|
|
309
|
+
}) {
|
|
310
|
+
const { tx, indexerId } = props;
|
|
311
|
+
|
|
312
|
+
try {
|
|
313
|
+
await tx.delete(checkpoints).where(eq(checkpoints.id, indexerId));
|
|
314
|
+
await tx.delete(filters).where(eq(filters.id, indexerId));
|
|
315
|
+
} catch (error) {
|
|
316
|
+
throw new DrizzleStorageError("Failed to reset persistence state", {
|
|
317
|
+
cause: error,
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
}
|
package/src/storage.ts
CHANGED
|
@@ -316,3 +316,45 @@ export async function finalize<
|
|
|
316
316
|
});
|
|
317
317
|
}
|
|
318
318
|
}
|
|
319
|
+
|
|
320
|
+
export async function cleanupStorage<
|
|
321
|
+
TQueryResult extends PgQueryResultHKT,
|
|
322
|
+
TFullSchema extends Record<string, unknown> = Record<string, never>,
|
|
323
|
+
TSchema extends
|
|
324
|
+
TablesRelationalConfig = ExtractTablesWithRelations<TFullSchema>,
|
|
325
|
+
>(
|
|
326
|
+
tx: PgTransaction<TQueryResult, TFullSchema, TSchema>,
|
|
327
|
+
tables: string[],
|
|
328
|
+
indexerId: string,
|
|
329
|
+
) {
|
|
330
|
+
try {
|
|
331
|
+
for (const table of tables) {
|
|
332
|
+
await tx.execute(
|
|
333
|
+
sql.raw(
|
|
334
|
+
`DROP TRIGGER IF EXISTS ${getReorgTriggerName(table, indexerId)} ON ${table};`,
|
|
335
|
+
),
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
await tx.execute(
|
|
340
|
+
sql.raw(`
|
|
341
|
+
DELETE FROM __reorg_rollback
|
|
342
|
+
WHERE indexer_id = '${indexerId}'
|
|
343
|
+
`),
|
|
344
|
+
);
|
|
345
|
+
|
|
346
|
+
for (const table of tables) {
|
|
347
|
+
try {
|
|
348
|
+
await tx.execute(sql.raw(`TRUNCATE TABLE ${table} CASCADE;`));
|
|
349
|
+
} catch (error) {
|
|
350
|
+
throw new DrizzleStorageError(`Failed to truncate table ${table}`, {
|
|
351
|
+
cause: error,
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
} catch (error) {
|
|
356
|
+
throw new DrizzleStorageError("Failed to clean up storage", {
|
|
357
|
+
cause: error,
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
}
|
package/src/testing.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { VcrResult } from "@apibara/indexer/testing";
|
|
2
|
+
import type { PgDatabase, PgQueryResultHKT } from "drizzle-orm/pg-core";
|
|
3
|
+
import { DRIZZLE_STORAGE_DB_PROPERTY } from "./constants";
|
|
4
|
+
|
|
5
|
+
export function getTestDatabase(context: VcrResult) {
|
|
6
|
+
const db = context[DRIZZLE_STORAGE_DB_PROPERTY];
|
|
7
|
+
|
|
8
|
+
if (!db) {
|
|
9
|
+
throw new Error("Drizzle database not found in context");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return db as PgDatabase<PgQueryResultHKT>;
|
|
13
|
+
}
|