1 | ;
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.CheckpointDB = void 0;
|
4 | const db_1 = require("./db");
|
5 | /**
|
6 | * DB is a thin wrapper around the underlying levelup db,
|
7 | * which validates inputs and sets encoding type.
|
8 | */
|
9 | class CheckpointDB extends db_1.DB {
|
10 | /**
|
11 | * Initialize a DB instance. If `leveldb` is not provided, DB
|
12 | * defaults to an [in-memory store](https://github.com/Level/memdown).
|
13 | * @param leveldb - An abstract-leveldown compliant store
|
14 | */
|
15 | constructor(leveldb) {
|
16 | super(leveldb);
|
17 | // Roots of trie at the moment of checkpoint
|
18 | this.checkpoints = [];
|
19 | }
|
20 | /**
|
21 | * Is the DB during a checkpoint phase?
|
22 | */
|
23 | get isCheckpoint() {
|
24 | return this.checkpoints.length > 0;
|
25 | }
|
26 | /**
|
27 | * Adds a new checkpoint to the stack
|
28 | * @param root
|
29 | */
|
30 | checkpoint(root) {
|
31 | this.checkpoints.push({ keyValueMap: new Map(), root });
|
32 | }
|
33 | /**
|
34 | * Commits the latest checkpoint
|
35 | */
|
36 | async commit() {
|
37 | const { keyValueMap } = this.checkpoints.pop();
|
38 | if (!this.isCheckpoint) {
|
39 | // This was the final checkpoint, we should now commit and flush everything to disk
|
40 | const batchOp = [];
|
41 | keyValueMap.forEach(function (value, key) {
|
42 | if (value === null) {
|
43 | batchOp.push({
|
44 | type: 'del',
|
45 | key: Buffer.from(key, 'binary'),
|
46 | });
|
47 | }
|
48 | else {
|
49 | batchOp.push({
|
50 | type: 'put',
|
51 | key: Buffer.from(key, 'binary'),
|
52 | value,
|
53 | });
|
54 | }
|
55 | });
|
56 | await this.batch(batchOp);
|
57 | }
|
58 | else {
|
59 | // dump everything into the current (higher level) cache
|
60 | const currentKeyValueMap = this.checkpoints[this.checkpoints.length - 1].keyValueMap;
|
61 | keyValueMap.forEach((value, key) => currentKeyValueMap.set(key, value));
|
62 | }
|
63 | }
|
64 | /**
|
65 | * Reverts the latest checkpoint
|
66 | */
|
67 | async revert() {
|
68 | const { root } = this.checkpoints.pop();
|
69 | return root;
|
70 | }
|
71 | /**
|
72 | * Retrieves a raw value from leveldb.
|
73 | * @param key
|
74 | * @returns A Promise that resolves to `Buffer` if a value is found or `null` if no value is found.
|
75 | */
|
76 | async get(key) {
|
77 | // Lookup the value in our cache. We return the latest checkpointed value (which should be the value on disk)
|
78 | for (let index = this.checkpoints.length - 1; index >= 0; index--) {
|
79 | const value = this.checkpoints[index].keyValueMap.get(key.toString('binary'));
|
80 | if (value !== undefined) {
|
81 | return value;
|
82 | }
|
83 | }
|
84 | // Nothing has been found in cache, look up from disk
|
85 | const value = await super.get(key);
|
86 | if (this.isCheckpoint) {
|
87 | // Since we are a checkpoint, put this value in cache, so future `get` calls will not look the key up again from disk.
|
88 | this.checkpoints[this.checkpoints.length - 1].keyValueMap.set(key.toString('binary'), value);
|
89 | }
|
90 | return value;
|
91 | }
|
92 | /**
|
93 | * Writes a value directly to leveldb.
|
94 | * @param key The key as a `Buffer`
|
95 | * @param value The value to be stored
|
96 | */
|
97 | async put(key, val) {
|
98 | if (this.isCheckpoint) {
|
99 | // put value in cache
|
100 | this.checkpoints[this.checkpoints.length - 1].keyValueMap.set(key.toString('binary'), val);
|
101 | }
|
102 | else {
|
103 | await super.put(key, val);
|
104 | }
|
105 | }
|
106 | /**
|
107 | * Removes a raw value in the underlying leveldb.
|
108 | * @param keys
|
109 | */
|
110 | async del(key) {
|
111 | if (this.isCheckpoint) {
|
112 | // delete the value in the current cache
|
113 | this.checkpoints[this.checkpoints.length - 1].keyValueMap.set(key.toString('binary'), null);
|
114 | }
|
115 | else {
|
116 | // delete the value on disk
|
117 | await this._leveldb.del(key, db_1.ENCODING_OPTS);
|
118 | }
|
119 | }
|
120 | /**
|
121 | * Performs a batch operation on db.
|
122 | * @param opStack A stack of levelup operations
|
123 | */
|
124 | async batch(opStack) {
|
125 | if (this.isCheckpoint) {
|
126 | for (const op of opStack) {
|
127 | if (op.type === 'put') {
|
128 | await this.put(op.key, op.value);
|
129 | }
|
130 | else if (op.type === 'del') {
|
131 | await this.del(op.key);
|
132 | }
|
133 | }
|
134 | }
|
135 | else {
|
136 | await super.batch(opStack);
|
137 | }
|
138 | }
|
139 | }
|
140 | exports.CheckpointDB = CheckpointDB;
|
141 | //# sourceMappingURL=checkpointDb.js.map |
\ | No newline at end of file |