UNPKG

4.7 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.CheckpointDB = void 0;
4const 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 */
9class 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}
140exports.CheckpointDB = CheckpointDB;
141//# sourceMappingURL=checkpointDb.js.map
\No newline at end of file