@apibara/plugin-sqlite 2.1.0-beta.20 → 2.1.0-beta.22
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 +69 -26
- package/dist/index.d.cts +2 -1
- package/dist/index.d.mts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.mjs +70 -27
- package/package.json +3 -3
- package/src/index.ts +29 -5
- package/src/kv.ts +41 -21
- package/src/persistence.ts +16 -0
package/dist/index.cjs
CHANGED
|
@@ -49,45 +49,56 @@ function initializeKeyValueStore(db) {
|
|
|
49
49
|
db.exec(statements$1.createTable);
|
|
50
50
|
}
|
|
51
51
|
class KeyValueStore {
|
|
52
|
-
constructor(db, endCursor, finality, serialize, deserialize) {
|
|
52
|
+
constructor(db, endCursor, finality, serialize, deserialize, indexerId) {
|
|
53
53
|
this.db = db;
|
|
54
54
|
this.endCursor = endCursor;
|
|
55
55
|
this.finality = finality;
|
|
56
56
|
this.serialize = serialize;
|
|
57
57
|
this.deserialize = deserialize;
|
|
58
|
+
this.indexerId = indexerId;
|
|
58
59
|
assertInTransaction(db);
|
|
59
60
|
}
|
|
60
61
|
get(key) {
|
|
61
|
-
const row = this.db.prepare(statements$1.get).get(key);
|
|
62
|
+
const row = this.db.prepare(statements$1.get).get(key, this.indexerId);
|
|
62
63
|
return row ? this.deserialize(row.v) : void 0;
|
|
63
64
|
}
|
|
64
65
|
put(key, value) {
|
|
65
|
-
this.db.prepare(statements$1.updateToBlock).run(Number(this.endCursor.orderKey), key);
|
|
66
|
-
this.db.prepare(
|
|
66
|
+
this.db.prepare(statements$1.updateToBlock).run(Number(this.endCursor.orderKey), key, this.indexerId);
|
|
67
|
+
this.db.prepare(
|
|
68
|
+
statements$1.insertIntoKvs
|
|
69
|
+
).run(
|
|
67
70
|
Number(this.endCursor.orderKey),
|
|
68
71
|
key,
|
|
69
|
-
this.serialize(value)
|
|
72
|
+
this.serialize(value),
|
|
73
|
+
this.indexerId
|
|
70
74
|
);
|
|
71
75
|
}
|
|
72
76
|
del(key) {
|
|
73
|
-
this.db.prepare(statements$1.del).run(Number(this.endCursor.orderKey), key);
|
|
77
|
+
this.db.prepare(statements$1.del).run(Number(this.endCursor.orderKey), key, this.indexerId);
|
|
74
78
|
}
|
|
75
79
|
}
|
|
76
|
-
function finalizeKV(db, cursor) {
|
|
80
|
+
function finalizeKV(db, cursor, indexerId) {
|
|
77
81
|
assertInTransaction(db);
|
|
78
82
|
db.prepare(statements$1.finalize).run(
|
|
79
|
-
Number(cursor.orderKey)
|
|
83
|
+
Number(cursor.orderKey),
|
|
84
|
+
indexerId
|
|
80
85
|
);
|
|
81
86
|
}
|
|
82
|
-
function invalidateKV(db, cursor) {
|
|
87
|
+
function invalidateKV(db, cursor, indexerId) {
|
|
83
88
|
assertInTransaction(db);
|
|
84
89
|
db.prepare(statements$1.invalidateDelete).run(
|
|
85
|
-
Number(cursor.orderKey)
|
|
90
|
+
Number(cursor.orderKey),
|
|
91
|
+
indexerId
|
|
86
92
|
);
|
|
87
93
|
db.prepare(statements$1.invalidateUpdate).run(
|
|
88
|
-
Number(cursor.orderKey)
|
|
94
|
+
Number(cursor.orderKey),
|
|
95
|
+
indexerId
|
|
89
96
|
);
|
|
90
97
|
}
|
|
98
|
+
function cleanupKV(db, indexerId) {
|
|
99
|
+
assertInTransaction(db);
|
|
100
|
+
db.prepare(statements$1.cleanup).run(indexerId);
|
|
101
|
+
}
|
|
91
102
|
const statements$1 = {
|
|
92
103
|
createTable: `
|
|
93
104
|
CREATE TABLE IF NOT EXISTS kvs (
|
|
@@ -95,33 +106,37 @@ const statements$1 = {
|
|
|
95
106
|
to_block INTEGER,
|
|
96
107
|
k TEXT NOT NULL,
|
|
97
108
|
v BLOB NOT NULL,
|
|
98
|
-
|
|
109
|
+
id TEXT NOT NULL,
|
|
110
|
+
PRIMARY KEY (from_block, k, id)
|
|
99
111
|
);`,
|
|
100
112
|
get: `
|
|
101
113
|
SELECT v
|
|
102
114
|
FROM kvs
|
|
103
|
-
WHERE k = ? AND to_block IS NULL`,
|
|
115
|
+
WHERE k = ? AND id = ? AND to_block IS NULL`,
|
|
104
116
|
updateToBlock: `
|
|
105
117
|
UPDATE kvs
|
|
106
118
|
SET to_block = ?
|
|
107
|
-
WHERE k = ? AND to_block IS NULL`,
|
|
119
|
+
WHERE k = ? AND id = ? AND to_block IS NULL`,
|
|
108
120
|
insertIntoKvs: `
|
|
109
|
-
INSERT INTO kvs (from_block, to_block, k, v)
|
|
110
|
-
VALUES (?, NULL, ?, ?)`,
|
|
121
|
+
INSERT INTO kvs (from_block, to_block, k, v, id)
|
|
122
|
+
VALUES (?, NULL, ?, ?, ?)`,
|
|
111
123
|
del: `
|
|
112
124
|
UPDATE kvs
|
|
113
125
|
SET to_block = ?
|
|
114
|
-
WHERE k = ? AND to_block IS NULL`,
|
|
126
|
+
WHERE k = ? AND id = ? AND to_block IS NULL`,
|
|
115
127
|
finalize: `
|
|
116
128
|
DELETE FROM kvs
|
|
117
|
-
WHERE to_block <= ?`,
|
|
129
|
+
WHERE to_block <= ? AND id = ?`,
|
|
118
130
|
invalidateDelete: `
|
|
119
131
|
DELETE FROM kvs
|
|
120
|
-
WHERE from_block > ?`,
|
|
132
|
+
WHERE from_block > ? AND id = ?`,
|
|
121
133
|
invalidateUpdate: `
|
|
122
134
|
UPDATE kvs
|
|
123
135
|
SET to_block = NULL
|
|
124
|
-
WHERE to_block >
|
|
136
|
+
WHERE to_block > ? AND id = ?`,
|
|
137
|
+
cleanup: `
|
|
138
|
+
DELETE FROM kvs
|
|
139
|
+
WHERE id = ?`
|
|
125
140
|
};
|
|
126
141
|
|
|
127
142
|
function initializePersistentState(db) {
|
|
@@ -189,6 +204,12 @@ function invalidateState(props) {
|
|
|
189
204
|
Number(cursor.orderKey)
|
|
190
205
|
);
|
|
191
206
|
}
|
|
207
|
+
function resetPersistence(props) {
|
|
208
|
+
const { db, indexerId } = props;
|
|
209
|
+
assertInTransaction(db);
|
|
210
|
+
db.prepare(statements.resetCheckpoint).run(indexerId);
|
|
211
|
+
db.prepare(statements.resetFilter).run(indexerId);
|
|
212
|
+
}
|
|
192
213
|
const statements = {
|
|
193
214
|
createCheckpointsTable: `
|
|
194
215
|
CREATE TABLE IF NOT EXISTS checkpoints (
|
|
@@ -243,7 +264,13 @@ const statements = {
|
|
|
243
264
|
invalidateFilterUpdate: `
|
|
244
265
|
UPDATE filters
|
|
245
266
|
SET to_block = NULL
|
|
246
|
-
WHERE id = ? AND to_block >
|
|
267
|
+
WHERE id = ? AND to_block > ?`,
|
|
268
|
+
resetCheckpoint: `
|
|
269
|
+
DELETE FROM checkpoints
|
|
270
|
+
WHERE id = ?`,
|
|
271
|
+
resetFilter: `
|
|
272
|
+
DELETE FROM filters
|
|
273
|
+
WHERE id = ?`
|
|
247
274
|
};
|
|
248
275
|
|
|
249
276
|
const KV_PROPERTY = "_kv_sqlite";
|
|
@@ -268,10 +295,13 @@ function sqliteStorage({
|
|
|
268
295
|
return plugins.defineIndexerPlugin((indexer) => {
|
|
269
296
|
let indexerId = "";
|
|
270
297
|
let prevFinality;
|
|
298
|
+
const alwaysReindex = process.env["APIBARA_ALWAYS_REINDEX"] === "true";
|
|
271
299
|
indexer.hooks.hook("run:before", async () => {
|
|
272
300
|
const { indexerName: indexerFileName, availableIndexers } = plugins$1.useInternalContext();
|
|
301
|
+
const logger = plugins.useLogger();
|
|
273
302
|
indexerId = internal.generateIndexerId(indexerFileName, identifier);
|
|
274
303
|
let retries = 0;
|
|
304
|
+
let cleanupApplied = false;
|
|
275
305
|
while (retries <= MAX_RETRIES) {
|
|
276
306
|
try {
|
|
277
307
|
await withTransaction(database, async (db) => {
|
|
@@ -281,6 +311,18 @@ function sqliteStorage({
|
|
|
281
311
|
if (enableKeyValueStore) {
|
|
282
312
|
initializeKeyValueStore(db);
|
|
283
313
|
}
|
|
314
|
+
if (alwaysReindex && !cleanupApplied) {
|
|
315
|
+
if (enableKeyValueStore) {
|
|
316
|
+
logger.warn("Reindexing: Cleaning up key-value store");
|
|
317
|
+
cleanupKV(db, indexerId);
|
|
318
|
+
}
|
|
319
|
+
if (enablePersistState) {
|
|
320
|
+
logger.warn("Reindexing: Resetting persistence state");
|
|
321
|
+
resetPersistence({ db, indexerId });
|
|
322
|
+
}
|
|
323
|
+
cleanupApplied = true;
|
|
324
|
+
logger.success("All data has been cleaned up for reindexing");
|
|
325
|
+
}
|
|
284
326
|
});
|
|
285
327
|
break;
|
|
286
328
|
} catch (error) {
|
|
@@ -321,7 +363,7 @@ function sqliteStorage({
|
|
|
321
363
|
invalidateState({ db, cursor, indexerId });
|
|
322
364
|
}
|
|
323
365
|
if (enableKeyValueStore) {
|
|
324
|
-
invalidateKV(db, cursor);
|
|
366
|
+
invalidateKV(db, cursor, indexerId);
|
|
325
367
|
}
|
|
326
368
|
});
|
|
327
369
|
});
|
|
@@ -349,7 +391,7 @@ function sqliteStorage({
|
|
|
349
391
|
finalizeState({ db, cursor, indexerId });
|
|
350
392
|
}
|
|
351
393
|
if (enableKeyValueStore) {
|
|
352
|
-
finalizeKV(db, cursor);
|
|
394
|
+
finalizeKV(db, cursor, indexerId);
|
|
353
395
|
}
|
|
354
396
|
});
|
|
355
397
|
});
|
|
@@ -363,7 +405,7 @@ function sqliteStorage({
|
|
|
363
405
|
invalidateState({ db, cursor, indexerId });
|
|
364
406
|
}
|
|
365
407
|
if (enableKeyValueStore) {
|
|
366
|
-
invalidateKV(db, cursor);
|
|
408
|
+
invalidateKV(db, cursor, indexerId);
|
|
367
409
|
}
|
|
368
410
|
});
|
|
369
411
|
});
|
|
@@ -381,7 +423,7 @@ function sqliteStorage({
|
|
|
381
423
|
await withTransaction(database, async (db) => {
|
|
382
424
|
if (prevFinality === "pending") {
|
|
383
425
|
if (enableKeyValueStore) {
|
|
384
|
-
invalidateKV(db, cursor);
|
|
426
|
+
invalidateKV(db, cursor, indexerId);
|
|
385
427
|
}
|
|
386
428
|
}
|
|
387
429
|
if (enableKeyValueStore) {
|
|
@@ -390,7 +432,8 @@ function sqliteStorage({
|
|
|
390
432
|
endCursor,
|
|
391
433
|
finality,
|
|
392
434
|
serializeFn,
|
|
393
|
-
deserializeFn
|
|
435
|
+
deserializeFn,
|
|
436
|
+
indexerId
|
|
394
437
|
);
|
|
395
438
|
}
|
|
396
439
|
await next();
|
package/dist/index.d.cts
CHANGED
|
@@ -11,7 +11,8 @@ declare class KeyValueStore {
|
|
|
11
11
|
private readonly finality;
|
|
12
12
|
private readonly serialize;
|
|
13
13
|
private readonly deserialize;
|
|
14
|
-
|
|
14
|
+
private readonly indexerId;
|
|
15
|
+
constructor(db: Database, endCursor: Cursor, finality: DataFinality, serialize: SerializeFn, deserialize: DeserializeFn, indexerId: string);
|
|
15
16
|
get<T>(key: string): T | undefined;
|
|
16
17
|
put<T>(key: string, value: T): void;
|
|
17
18
|
del(key: string): void;
|
package/dist/index.d.mts
CHANGED
|
@@ -11,7 +11,8 @@ declare class KeyValueStore {
|
|
|
11
11
|
private readonly finality;
|
|
12
12
|
private readonly serialize;
|
|
13
13
|
private readonly deserialize;
|
|
14
|
-
|
|
14
|
+
private readonly indexerId;
|
|
15
|
+
constructor(db: Database, endCursor: Cursor, finality: DataFinality, serialize: SerializeFn, deserialize: DeserializeFn, indexerId: string);
|
|
15
16
|
get<T>(key: string): T | undefined;
|
|
16
17
|
put<T>(key: string, value: T): void;
|
|
17
18
|
del(key: string): void;
|
package/dist/index.d.ts
CHANGED
|
@@ -11,7 +11,8 @@ declare class KeyValueStore {
|
|
|
11
11
|
private readonly finality;
|
|
12
12
|
private readonly serialize;
|
|
13
13
|
private readonly deserialize;
|
|
14
|
-
|
|
14
|
+
private readonly indexerId;
|
|
15
|
+
constructor(db: Database, endCursor: Cursor, finality: DataFinality, serialize: SerializeFn, deserialize: DeserializeFn, indexerId: string);
|
|
15
16
|
get<T>(key: string): T | undefined;
|
|
16
17
|
put<T>(key: string, value: T): void;
|
|
17
18
|
del(key: string): void;
|
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';
|
|
@@ -47,45 +47,56 @@ function initializeKeyValueStore(db) {
|
|
|
47
47
|
db.exec(statements$1.createTable);
|
|
48
48
|
}
|
|
49
49
|
class KeyValueStore {
|
|
50
|
-
constructor(db, endCursor, finality, serialize, deserialize) {
|
|
50
|
+
constructor(db, endCursor, finality, serialize, deserialize, indexerId) {
|
|
51
51
|
this.db = db;
|
|
52
52
|
this.endCursor = endCursor;
|
|
53
53
|
this.finality = finality;
|
|
54
54
|
this.serialize = serialize;
|
|
55
55
|
this.deserialize = deserialize;
|
|
56
|
+
this.indexerId = indexerId;
|
|
56
57
|
assertInTransaction(db);
|
|
57
58
|
}
|
|
58
59
|
get(key) {
|
|
59
|
-
const row = this.db.prepare(statements$1.get).get(key);
|
|
60
|
+
const row = this.db.prepare(statements$1.get).get(key, this.indexerId);
|
|
60
61
|
return row ? this.deserialize(row.v) : void 0;
|
|
61
62
|
}
|
|
62
63
|
put(key, value) {
|
|
63
|
-
this.db.prepare(statements$1.updateToBlock).run(Number(this.endCursor.orderKey), key);
|
|
64
|
-
this.db.prepare(
|
|
64
|
+
this.db.prepare(statements$1.updateToBlock).run(Number(this.endCursor.orderKey), key, this.indexerId);
|
|
65
|
+
this.db.prepare(
|
|
66
|
+
statements$1.insertIntoKvs
|
|
67
|
+
).run(
|
|
65
68
|
Number(this.endCursor.orderKey),
|
|
66
69
|
key,
|
|
67
|
-
this.serialize(value)
|
|
70
|
+
this.serialize(value),
|
|
71
|
+
this.indexerId
|
|
68
72
|
);
|
|
69
73
|
}
|
|
70
74
|
del(key) {
|
|
71
|
-
this.db.prepare(statements$1.del).run(Number(this.endCursor.orderKey), key);
|
|
75
|
+
this.db.prepare(statements$1.del).run(Number(this.endCursor.orderKey), key, this.indexerId);
|
|
72
76
|
}
|
|
73
77
|
}
|
|
74
|
-
function finalizeKV(db, cursor) {
|
|
78
|
+
function finalizeKV(db, cursor, indexerId) {
|
|
75
79
|
assertInTransaction(db);
|
|
76
80
|
db.prepare(statements$1.finalize).run(
|
|
77
|
-
Number(cursor.orderKey)
|
|
81
|
+
Number(cursor.orderKey),
|
|
82
|
+
indexerId
|
|
78
83
|
);
|
|
79
84
|
}
|
|
80
|
-
function invalidateKV(db, cursor) {
|
|
85
|
+
function invalidateKV(db, cursor, indexerId) {
|
|
81
86
|
assertInTransaction(db);
|
|
82
87
|
db.prepare(statements$1.invalidateDelete).run(
|
|
83
|
-
Number(cursor.orderKey)
|
|
88
|
+
Number(cursor.orderKey),
|
|
89
|
+
indexerId
|
|
84
90
|
);
|
|
85
91
|
db.prepare(statements$1.invalidateUpdate).run(
|
|
86
|
-
Number(cursor.orderKey)
|
|
92
|
+
Number(cursor.orderKey),
|
|
93
|
+
indexerId
|
|
87
94
|
);
|
|
88
95
|
}
|
|
96
|
+
function cleanupKV(db, indexerId) {
|
|
97
|
+
assertInTransaction(db);
|
|
98
|
+
db.prepare(statements$1.cleanup).run(indexerId);
|
|
99
|
+
}
|
|
89
100
|
const statements$1 = {
|
|
90
101
|
createTable: `
|
|
91
102
|
CREATE TABLE IF NOT EXISTS kvs (
|
|
@@ -93,33 +104,37 @@ const statements$1 = {
|
|
|
93
104
|
to_block INTEGER,
|
|
94
105
|
k TEXT NOT NULL,
|
|
95
106
|
v BLOB NOT NULL,
|
|
96
|
-
|
|
107
|
+
id TEXT NOT NULL,
|
|
108
|
+
PRIMARY KEY (from_block, k, id)
|
|
97
109
|
);`,
|
|
98
110
|
get: `
|
|
99
111
|
SELECT v
|
|
100
112
|
FROM kvs
|
|
101
|
-
WHERE k = ? AND to_block IS NULL`,
|
|
113
|
+
WHERE k = ? AND id = ? AND to_block IS NULL`,
|
|
102
114
|
updateToBlock: `
|
|
103
115
|
UPDATE kvs
|
|
104
116
|
SET to_block = ?
|
|
105
|
-
WHERE k = ? AND to_block IS NULL`,
|
|
117
|
+
WHERE k = ? AND id = ? AND to_block IS NULL`,
|
|
106
118
|
insertIntoKvs: `
|
|
107
|
-
INSERT INTO kvs (from_block, to_block, k, v)
|
|
108
|
-
VALUES (?, NULL, ?, ?)`,
|
|
119
|
+
INSERT INTO kvs (from_block, to_block, k, v, id)
|
|
120
|
+
VALUES (?, NULL, ?, ?, ?)`,
|
|
109
121
|
del: `
|
|
110
122
|
UPDATE kvs
|
|
111
123
|
SET to_block = ?
|
|
112
|
-
WHERE k = ? AND to_block IS NULL`,
|
|
124
|
+
WHERE k = ? AND id = ? AND to_block IS NULL`,
|
|
113
125
|
finalize: `
|
|
114
126
|
DELETE FROM kvs
|
|
115
|
-
WHERE to_block <= ?`,
|
|
127
|
+
WHERE to_block <= ? AND id = ?`,
|
|
116
128
|
invalidateDelete: `
|
|
117
129
|
DELETE FROM kvs
|
|
118
|
-
WHERE from_block > ?`,
|
|
130
|
+
WHERE from_block > ? AND id = ?`,
|
|
119
131
|
invalidateUpdate: `
|
|
120
132
|
UPDATE kvs
|
|
121
133
|
SET to_block = NULL
|
|
122
|
-
WHERE to_block >
|
|
134
|
+
WHERE to_block > ? AND id = ?`,
|
|
135
|
+
cleanup: `
|
|
136
|
+
DELETE FROM kvs
|
|
137
|
+
WHERE id = ?`
|
|
123
138
|
};
|
|
124
139
|
|
|
125
140
|
function initializePersistentState(db) {
|
|
@@ -187,6 +202,12 @@ function invalidateState(props) {
|
|
|
187
202
|
Number(cursor.orderKey)
|
|
188
203
|
);
|
|
189
204
|
}
|
|
205
|
+
function resetPersistence(props) {
|
|
206
|
+
const { db, indexerId } = props;
|
|
207
|
+
assertInTransaction(db);
|
|
208
|
+
db.prepare(statements.resetCheckpoint).run(indexerId);
|
|
209
|
+
db.prepare(statements.resetFilter).run(indexerId);
|
|
210
|
+
}
|
|
190
211
|
const statements = {
|
|
191
212
|
createCheckpointsTable: `
|
|
192
213
|
CREATE TABLE IF NOT EXISTS checkpoints (
|
|
@@ -241,7 +262,13 @@ const statements = {
|
|
|
241
262
|
invalidateFilterUpdate: `
|
|
242
263
|
UPDATE filters
|
|
243
264
|
SET to_block = NULL
|
|
244
|
-
WHERE id = ? AND to_block >
|
|
265
|
+
WHERE id = ? AND to_block > ?`,
|
|
266
|
+
resetCheckpoint: `
|
|
267
|
+
DELETE FROM checkpoints
|
|
268
|
+
WHERE id = ?`,
|
|
269
|
+
resetFilter: `
|
|
270
|
+
DELETE FROM filters
|
|
271
|
+
WHERE id = ?`
|
|
245
272
|
};
|
|
246
273
|
|
|
247
274
|
const KV_PROPERTY = "_kv_sqlite";
|
|
@@ -266,10 +293,13 @@ function sqliteStorage({
|
|
|
266
293
|
return defineIndexerPlugin((indexer) => {
|
|
267
294
|
let indexerId = "";
|
|
268
295
|
let prevFinality;
|
|
296
|
+
const alwaysReindex = process.env["APIBARA_ALWAYS_REINDEX"] === "true";
|
|
269
297
|
indexer.hooks.hook("run:before", async () => {
|
|
270
298
|
const { indexerName: indexerFileName, availableIndexers } = useInternalContext();
|
|
299
|
+
const logger = useLogger();
|
|
271
300
|
indexerId = generateIndexerId(indexerFileName, identifier);
|
|
272
301
|
let retries = 0;
|
|
302
|
+
let cleanupApplied = false;
|
|
273
303
|
while (retries <= MAX_RETRIES) {
|
|
274
304
|
try {
|
|
275
305
|
await withTransaction(database, async (db) => {
|
|
@@ -279,6 +309,18 @@ function sqliteStorage({
|
|
|
279
309
|
if (enableKeyValueStore) {
|
|
280
310
|
initializeKeyValueStore(db);
|
|
281
311
|
}
|
|
312
|
+
if (alwaysReindex && !cleanupApplied) {
|
|
313
|
+
if (enableKeyValueStore) {
|
|
314
|
+
logger.warn("Reindexing: Cleaning up key-value store");
|
|
315
|
+
cleanupKV(db, indexerId);
|
|
316
|
+
}
|
|
317
|
+
if (enablePersistState) {
|
|
318
|
+
logger.warn("Reindexing: Resetting persistence state");
|
|
319
|
+
resetPersistence({ db, indexerId });
|
|
320
|
+
}
|
|
321
|
+
cleanupApplied = true;
|
|
322
|
+
logger.success("All data has been cleaned up for reindexing");
|
|
323
|
+
}
|
|
282
324
|
});
|
|
283
325
|
break;
|
|
284
326
|
} catch (error) {
|
|
@@ -319,7 +361,7 @@ function sqliteStorage({
|
|
|
319
361
|
invalidateState({ db, cursor, indexerId });
|
|
320
362
|
}
|
|
321
363
|
if (enableKeyValueStore) {
|
|
322
|
-
invalidateKV(db, cursor);
|
|
364
|
+
invalidateKV(db, cursor, indexerId);
|
|
323
365
|
}
|
|
324
366
|
});
|
|
325
367
|
});
|
|
@@ -347,7 +389,7 @@ function sqliteStorage({
|
|
|
347
389
|
finalizeState({ db, cursor, indexerId });
|
|
348
390
|
}
|
|
349
391
|
if (enableKeyValueStore) {
|
|
350
|
-
finalizeKV(db, cursor);
|
|
392
|
+
finalizeKV(db, cursor, indexerId);
|
|
351
393
|
}
|
|
352
394
|
});
|
|
353
395
|
});
|
|
@@ -361,7 +403,7 @@ function sqliteStorage({
|
|
|
361
403
|
invalidateState({ db, cursor, indexerId });
|
|
362
404
|
}
|
|
363
405
|
if (enableKeyValueStore) {
|
|
364
|
-
invalidateKV(db, cursor);
|
|
406
|
+
invalidateKV(db, cursor, indexerId);
|
|
365
407
|
}
|
|
366
408
|
});
|
|
367
409
|
});
|
|
@@ -379,7 +421,7 @@ function sqliteStorage({
|
|
|
379
421
|
await withTransaction(database, async (db) => {
|
|
380
422
|
if (prevFinality === "pending") {
|
|
381
423
|
if (enableKeyValueStore) {
|
|
382
|
-
invalidateKV(db, cursor);
|
|
424
|
+
invalidateKV(db, cursor, indexerId);
|
|
383
425
|
}
|
|
384
426
|
}
|
|
385
427
|
if (enableKeyValueStore) {
|
|
@@ -388,7 +430,8 @@ function sqliteStorage({
|
|
|
388
430
|
endCursor,
|
|
389
431
|
finality,
|
|
390
432
|
serializeFn,
|
|
391
|
-
deserializeFn
|
|
433
|
+
deserializeFn,
|
|
434
|
+
indexerId
|
|
392
435
|
);
|
|
393
436
|
}
|
|
394
437
|
await next();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apibara/plugin-sqlite",
|
|
3
|
-
"version": "2.1.0-beta.
|
|
3
|
+
"version": "2.1.0-beta.22",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"better-sqlite3": "^9.0.0"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@apibara/indexer": "2.1.0-beta.
|
|
40
|
-
"@apibara/protocol": "2.1.0-beta.
|
|
39
|
+
"@apibara/indexer": "2.1.0-beta.22",
|
|
40
|
+
"@apibara/protocol": "2.1.0-beta.22"
|
|
41
41
|
}
|
|
42
42
|
}
|
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
|
import type { Cursor, DataFinality } from "@apibara/protocol";
|
|
4
4
|
import type { Database as SqliteDatabase } from "better-sqlite3";
|
|
5
5
|
|
|
@@ -7,6 +7,7 @@ import { generateIndexerId } from "@apibara/indexer/internal";
|
|
|
7
7
|
import { useInternalContext } from "@apibara/indexer/internal/plugins";
|
|
8
8
|
import {
|
|
9
9
|
KeyValueStore,
|
|
10
|
+
cleanupKV,
|
|
10
11
|
finalizeKV,
|
|
11
12
|
initializeKeyValueStore,
|
|
12
13
|
invalidateKV,
|
|
@@ -17,6 +18,7 @@ import {
|
|
|
17
18
|
initializePersistentState,
|
|
18
19
|
invalidateState,
|
|
19
20
|
persistState,
|
|
21
|
+
resetPersistence,
|
|
20
22
|
} from "./persistence";
|
|
21
23
|
import {
|
|
22
24
|
type DeserializeFn,
|
|
@@ -77,15 +79,20 @@ export function sqliteStorage<TFilter, TBlock>({
|
|
|
77
79
|
return defineIndexerPlugin<TFilter, TBlock>((indexer) => {
|
|
78
80
|
let indexerId = "";
|
|
79
81
|
let prevFinality: DataFinality | undefined;
|
|
82
|
+
const alwaysReindex = process.env["APIBARA_ALWAYS_REINDEX"] === "true";
|
|
80
83
|
|
|
81
84
|
indexer.hooks.hook("run:before", async () => {
|
|
82
85
|
const { indexerName: indexerFileName, availableIndexers } =
|
|
83
86
|
useInternalContext();
|
|
84
87
|
|
|
88
|
+
const logger = useLogger();
|
|
89
|
+
|
|
85
90
|
indexerId = generateIndexerId(indexerFileName, identifier);
|
|
86
91
|
|
|
87
92
|
let retries = 0;
|
|
88
93
|
|
|
94
|
+
let cleanupApplied = false;
|
|
95
|
+
|
|
89
96
|
while (retries <= MAX_RETRIES) {
|
|
90
97
|
try {
|
|
91
98
|
await withTransaction(database, async (db) => {
|
|
@@ -96,6 +103,22 @@ export function sqliteStorage<TFilter, TBlock>({
|
|
|
96
103
|
if (enableKeyValueStore) {
|
|
97
104
|
initializeKeyValueStore(db);
|
|
98
105
|
}
|
|
106
|
+
|
|
107
|
+
if (alwaysReindex && !cleanupApplied) {
|
|
108
|
+
if (enableKeyValueStore) {
|
|
109
|
+
logger.warn("Reindexing: Cleaning up key-value store");
|
|
110
|
+
cleanupKV(db, indexerId);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (enablePersistState) {
|
|
114
|
+
logger.warn("Reindexing: Resetting persistence state");
|
|
115
|
+
resetPersistence({ db, indexerId });
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
cleanupApplied = true;
|
|
119
|
+
|
|
120
|
+
logger.success("All data has been cleaned up for reindexing");
|
|
121
|
+
}
|
|
99
122
|
});
|
|
100
123
|
break;
|
|
101
124
|
} catch (error) {
|
|
@@ -145,7 +168,7 @@ export function sqliteStorage<TFilter, TBlock>({
|
|
|
145
168
|
}
|
|
146
169
|
|
|
147
170
|
if (enableKeyValueStore) {
|
|
148
|
-
invalidateKV(db, cursor);
|
|
171
|
+
invalidateKV(db, cursor, indexerId);
|
|
149
172
|
}
|
|
150
173
|
});
|
|
151
174
|
});
|
|
@@ -182,7 +205,7 @@ export function sqliteStorage<TFilter, TBlock>({
|
|
|
182
205
|
}
|
|
183
206
|
|
|
184
207
|
if (enableKeyValueStore) {
|
|
185
|
-
finalizeKV(db, cursor);
|
|
208
|
+
finalizeKV(db, cursor, indexerId);
|
|
186
209
|
}
|
|
187
210
|
});
|
|
188
211
|
});
|
|
@@ -200,7 +223,7 @@ export function sqliteStorage<TFilter, TBlock>({
|
|
|
200
223
|
}
|
|
201
224
|
|
|
202
225
|
if (enableKeyValueStore) {
|
|
203
|
-
invalidateKV(db, cursor);
|
|
226
|
+
invalidateKV(db, cursor, indexerId);
|
|
204
227
|
}
|
|
205
228
|
});
|
|
206
229
|
});
|
|
@@ -227,7 +250,7 @@ export function sqliteStorage<TFilter, TBlock>({
|
|
|
227
250
|
if (prevFinality === "pending") {
|
|
228
251
|
// invalidate if previous block's finality was "pending"
|
|
229
252
|
if (enableKeyValueStore) {
|
|
230
|
-
invalidateKV(db, cursor);
|
|
253
|
+
invalidateKV(db, cursor, indexerId);
|
|
231
254
|
}
|
|
232
255
|
}
|
|
233
256
|
|
|
@@ -238,6 +261,7 @@ export function sqliteStorage<TFilter, TBlock>({
|
|
|
238
261
|
finality,
|
|
239
262
|
serializeFn,
|
|
240
263
|
deserializeFn,
|
|
264
|
+
indexerId,
|
|
241
265
|
);
|
|
242
266
|
}
|
|
243
267
|
|
package/src/kv.ts
CHANGED
|
@@ -19,64 +19,80 @@ export class KeyValueStore {
|
|
|
19
19
|
private readonly finality: DataFinality,
|
|
20
20
|
private readonly serialize: SerializeFn,
|
|
21
21
|
private readonly deserialize: DeserializeFn,
|
|
22
|
+
private readonly indexerId: string,
|
|
22
23
|
) {
|
|
23
24
|
assertInTransaction(db);
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
get<T>(key: string): T | undefined {
|
|
27
|
-
const row = this.db
|
|
28
|
+
const row = this.db
|
|
29
|
+
.prepare<[string, string], KeyValueRow>(statements.get)
|
|
30
|
+
.get(key, this.indexerId);
|
|
28
31
|
|
|
29
32
|
return row ? this.deserialize(row.v) : undefined;
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
put<T>(key: string, value: T) {
|
|
33
36
|
this.db
|
|
34
|
-
.prepare<[number, string], KeyValueRow>(statements.updateToBlock)
|
|
35
|
-
.run(Number(this.endCursor.orderKey), key);
|
|
37
|
+
.prepare<[number, string, string], KeyValueRow>(statements.updateToBlock)
|
|
38
|
+
.run(Number(this.endCursor.orderKey), key, this.indexerId);
|
|
36
39
|
|
|
37
40
|
this.db
|
|
38
|
-
.prepare<[number, string, string], KeyValueRow>(
|
|
41
|
+
.prepare<[number, string, string, string], KeyValueRow>(
|
|
42
|
+
statements.insertIntoKvs,
|
|
43
|
+
)
|
|
39
44
|
.run(
|
|
40
45
|
Number(this.endCursor.orderKey),
|
|
41
46
|
key,
|
|
42
47
|
this.serialize(value as Record<string, unknown>),
|
|
48
|
+
this.indexerId,
|
|
43
49
|
);
|
|
44
50
|
}
|
|
45
51
|
|
|
46
52
|
del(key: string) {
|
|
47
53
|
this.db
|
|
48
|
-
.prepare<[number, string], KeyValueRow>(statements.del)
|
|
49
|
-
.run(Number(this.endCursor.orderKey), key);
|
|
54
|
+
.prepare<[number, string, string], KeyValueRow>(statements.del)
|
|
55
|
+
.run(Number(this.endCursor.orderKey), key, this.indexerId);
|
|
50
56
|
}
|
|
51
57
|
}
|
|
52
58
|
|
|
53
|
-
export function finalizeKV(db: Database, cursor: Cursor) {
|
|
59
|
+
export function finalizeKV(db: Database, cursor: Cursor, indexerId: string) {
|
|
54
60
|
assertInTransaction(db);
|
|
55
61
|
|
|
56
|
-
db.prepare<[number], KeyValueRow>(statements.finalize).run(
|
|
62
|
+
db.prepare<[number, string], KeyValueRow>(statements.finalize).run(
|
|
57
63
|
Number(cursor.orderKey),
|
|
64
|
+
indexerId,
|
|
58
65
|
);
|
|
59
66
|
}
|
|
60
67
|
|
|
61
|
-
export function invalidateKV(db: Database, cursor: Cursor) {
|
|
68
|
+
export function invalidateKV(db: Database, cursor: Cursor, indexerId: string) {
|
|
62
69
|
assertInTransaction(db);
|
|
63
70
|
|
|
64
71
|
// Delete entries that started after the invalidation cursor
|
|
65
|
-
db.prepare<[number], KeyValueRow>(statements.invalidateDelete).run(
|
|
72
|
+
db.prepare<[number, string], KeyValueRow>(statements.invalidateDelete).run(
|
|
66
73
|
Number(cursor.orderKey),
|
|
74
|
+
indexerId,
|
|
67
75
|
);
|
|
68
76
|
|
|
69
77
|
// Update entries that were supposed to end after the invalidation cursor
|
|
70
|
-
db.prepare<[number], KeyValueRow>(statements.invalidateUpdate).run(
|
|
78
|
+
db.prepare<[number, string], KeyValueRow>(statements.invalidateUpdate).run(
|
|
71
79
|
Number(cursor.orderKey),
|
|
80
|
+
indexerId,
|
|
72
81
|
);
|
|
73
82
|
}
|
|
74
83
|
|
|
84
|
+
export function cleanupKV(db: Database, indexerId: string) {
|
|
85
|
+
assertInTransaction(db);
|
|
86
|
+
|
|
87
|
+
db.prepare<[string], KeyValueRow>(statements.cleanup).run(indexerId);
|
|
88
|
+
}
|
|
89
|
+
|
|
75
90
|
export type KeyValueRow = {
|
|
76
91
|
from_block: number;
|
|
77
92
|
to_block: number;
|
|
78
93
|
k: string;
|
|
79
94
|
v: string;
|
|
95
|
+
id: string;
|
|
80
96
|
};
|
|
81
97
|
|
|
82
98
|
const statements = {
|
|
@@ -86,31 +102,35 @@ const statements = {
|
|
|
86
102
|
to_block INTEGER,
|
|
87
103
|
k TEXT NOT NULL,
|
|
88
104
|
v BLOB NOT NULL,
|
|
89
|
-
|
|
105
|
+
id TEXT NOT NULL,
|
|
106
|
+
PRIMARY KEY (from_block, k, id)
|
|
90
107
|
);`,
|
|
91
108
|
get: `
|
|
92
109
|
SELECT v
|
|
93
110
|
FROM kvs
|
|
94
|
-
WHERE k = ? AND to_block IS NULL`,
|
|
111
|
+
WHERE k = ? AND id = ? AND to_block IS NULL`,
|
|
95
112
|
updateToBlock: `
|
|
96
113
|
UPDATE kvs
|
|
97
114
|
SET to_block = ?
|
|
98
|
-
WHERE k = ? AND to_block IS NULL`,
|
|
115
|
+
WHERE k = ? AND id = ? AND to_block IS NULL`,
|
|
99
116
|
insertIntoKvs: `
|
|
100
|
-
INSERT INTO kvs (from_block, to_block, k, v)
|
|
101
|
-
VALUES (?, NULL, ?, ?)`,
|
|
117
|
+
INSERT INTO kvs (from_block, to_block, k, v, id)
|
|
118
|
+
VALUES (?, NULL, ?, ?, ?)`,
|
|
102
119
|
del: `
|
|
103
120
|
UPDATE kvs
|
|
104
121
|
SET to_block = ?
|
|
105
|
-
WHERE k = ? AND to_block IS NULL`,
|
|
122
|
+
WHERE k = ? AND id = ? AND to_block IS NULL`,
|
|
106
123
|
finalize: `
|
|
107
124
|
DELETE FROM kvs
|
|
108
|
-
WHERE to_block <= ?`,
|
|
125
|
+
WHERE to_block <= ? AND id = ?`,
|
|
109
126
|
invalidateDelete: `
|
|
110
127
|
DELETE FROM kvs
|
|
111
|
-
WHERE from_block > ?`,
|
|
128
|
+
WHERE from_block > ? AND id = ?`,
|
|
112
129
|
invalidateUpdate: `
|
|
113
130
|
UPDATE kvs
|
|
114
131
|
SET to_block = NULL
|
|
115
|
-
WHERE to_block > ?`,
|
|
116
|
-
|
|
132
|
+
WHERE to_block > ? AND id = ?`,
|
|
133
|
+
cleanup: `
|
|
134
|
+
DELETE FROM kvs
|
|
135
|
+
WHERE id = ?`,
|
|
136
|
+
} as const;
|
package/src/persistence.ts
CHANGED
|
@@ -100,6 +100,16 @@ export function invalidateState(props: {
|
|
|
100
100
|
);
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
export function resetPersistence(props: {
|
|
104
|
+
db: Database;
|
|
105
|
+
indexerId: string;
|
|
106
|
+
}) {
|
|
107
|
+
const { db, indexerId } = props;
|
|
108
|
+
assertInTransaction(db);
|
|
109
|
+
db.prepare<[string]>(statements.resetCheckpoint).run(indexerId);
|
|
110
|
+
db.prepare<[string]>(statements.resetFilter).run(indexerId);
|
|
111
|
+
}
|
|
112
|
+
|
|
103
113
|
export type CheckpointRow = {
|
|
104
114
|
id: string;
|
|
105
115
|
order_key: number;
|
|
@@ -168,4 +178,10 @@ const statements = {
|
|
|
168
178
|
UPDATE filters
|
|
169
179
|
SET to_block = NULL
|
|
170
180
|
WHERE id = ? AND to_block > ?`,
|
|
181
|
+
resetCheckpoint: `
|
|
182
|
+
DELETE FROM checkpoints
|
|
183
|
+
WHERE id = ?`,
|
|
184
|
+
resetFilter: `
|
|
185
|
+
DELETE FROM filters
|
|
186
|
+
WHERE id = ?`,
|
|
171
187
|
};
|