sehawq.db 4.0.2 → 4.0.5
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/.github/workflows/npm-publish.yml +30 -30
- package/LICENSE +21 -21
- package/index.js +1 -1
- package/package.json +36 -36
- package/readme.md +413 -413
- package/src/core/Database.js +294 -294
- package/src/core/Events.js +285 -285
- package/src/core/IndexManager.js +813 -813
- package/src/core/Persistence.js +375 -375
- package/src/core/QueryEngine.js +447 -447
- package/src/core/Storage.js +321 -321
- package/src/core/Validator.js +324 -324
- package/src/index.js +115 -115
- package/src/performance/Cache.js +338 -338
- package/src/performance/LazyLoader.js +354 -354
- package/src/performance/MemoryManager.js +495 -495
- package/src/server/api.js +687 -687
- package/src/server/websocket.js +527 -527
- package/src/utils/benchmark.js +51 -51
- package/src/utils/dot-notation.js +247 -247
- package/src/utils/helpers.js +275 -275
- package/src/utils/profiler.js +70 -70
- package/src/version.js +37 -37
package/src/core/Database.js
CHANGED
|
@@ -1,295 +1,295 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SehawqDB - The main database class
|
|
3
|
-
*
|
|
4
|
-
* Started as a simple JSON store, now with performance optimizations
|
|
5
|
-
* Because waiting for databases to load is boring 😴
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const EventEmitter = require('events');
|
|
9
|
-
const path = require('path');
|
|
10
|
-
const fs = require('fs').promises;
|
|
11
|
-
|
|
12
|
-
class SehawqDB extends EventEmitter {
|
|
13
|
-
constructor(options = {}) {
|
|
14
|
-
super();
|
|
15
|
-
|
|
16
|
-
// Config with some sensible defaults
|
|
17
|
-
this.config = {
|
|
18
|
-
path: './sehawq-data.json',
|
|
19
|
-
autoSave: true,
|
|
20
|
-
saveInterval: 5000,
|
|
21
|
-
cacheEnabled: true,
|
|
22
|
-
cacheSize: 1000,
|
|
23
|
-
...options
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
// Performance optimizations
|
|
27
|
-
this.data = new Map(); // Using Map for better performance
|
|
28
|
-
this.cache = new Map(); // Hot data cache
|
|
29
|
-
this.indexes = new Map(); // Query indexes
|
|
30
|
-
|
|
31
|
-
// Runtime stats (for debugging)
|
|
32
|
-
this.stats = {
|
|
33
|
-
reads: 0,
|
|
34
|
-
writes: 0,
|
|
35
|
-
cacheHits: 0,
|
|
36
|
-
cacheMisses: 0
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
this._initialized = false;
|
|
40
|
-
this._saveTimeout = null;
|
|
41
|
-
|
|
42
|
-
// Initialize - but don't block the constructor
|
|
43
|
-
this._init().catch(error => {
|
|
44
|
-
console.error('🚨 SehawqDB init failed:', error);
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Async initialization
|
|
50
|
-
* Because sometimes we need to wait for things...
|
|
51
|
-
*/
|
|
52
|
-
async _init() {
|
|
53
|
-
if (this._initialized) return;
|
|
54
|
-
|
|
55
|
-
try {
|
|
56
|
-
// Try to load existing data
|
|
57
|
-
await this._loadFromDisk();
|
|
58
|
-
|
|
59
|
-
// Start auto-save if enabled
|
|
60
|
-
if (this.config.autoSave) {
|
|
61
|
-
this._startAutoSave();
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
this._initialized = true;
|
|
65
|
-
this.emit('ready');
|
|
66
|
-
|
|
67
|
-
if (this.config.debug) {
|
|
68
|
-
console.log('✅ SehawqDB ready - Performance mode: ON');
|
|
69
|
-
}
|
|
70
|
-
} catch (error) {
|
|
71
|
-
this.emit('error', error);
|
|
72
|
-
throw error;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Set a value - the bread and butter
|
|
78
|
-
*/
|
|
79
|
-
set(key, value) {
|
|
80
|
-
if (!this._initialized) {
|
|
81
|
-
throw new Error('Database not initialized. Wait for ready event.');
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const oldValue = this.data.get(key);
|
|
85
|
-
this.data.set(key, value);
|
|
86
|
-
|
|
87
|
-
// Cache the hot data
|
|
88
|
-
if (this.config.cacheEnabled) {
|
|
89
|
-
this._updateCache(key, value);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Update indexes if any
|
|
93
|
-
this._updateIndexes(key, value, oldValue);
|
|
94
|
-
|
|
95
|
-
this.stats.writes++;
|
|
96
|
-
this.emit('set', { key, value, oldValue });
|
|
97
|
-
|
|
98
|
-
// Immediate save for important data, otherwise batch it
|
|
99
|
-
if (this.config.autoSave) {
|
|
100
|
-
this._queueSave();
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return this;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Get a value - faster than your morning coffee
|
|
108
|
-
*/
|
|
109
|
-
get(key) {
|
|
110
|
-
if (!this._initialized) {
|
|
111
|
-
throw new Error('Database not initialized.');
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
this.stats.reads++;
|
|
115
|
-
|
|
116
|
-
// Check cache first (hot path)
|
|
117
|
-
if (this.config.cacheEnabled && this.cache.has(key)) {
|
|
118
|
-
this.stats.cacheHits++;
|
|
119
|
-
return this.cache.get(key);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
this.stats.cacheMisses++;
|
|
123
|
-
const value = this.data.get(key);
|
|
124
|
-
|
|
125
|
-
// Cache it for next time
|
|
126
|
-
if (this.config.cacheEnabled && value !== undefined) {
|
|
127
|
-
this._updateCache(key, value);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return value;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Delete a key - poof! gone.
|
|
135
|
-
*/
|
|
136
|
-
delete(key) {
|
|
137
|
-
if (!this.data.has(key)) return false;
|
|
138
|
-
|
|
139
|
-
const oldValue = this.data.get(key);
|
|
140
|
-
this.data.delete(key);
|
|
141
|
-
this.cache.delete(key);
|
|
142
|
-
this._removeFromIndexes(key, oldValue);
|
|
143
|
-
|
|
144
|
-
this.emit('delete', { key, oldValue });
|
|
145
|
-
this._queueSave();
|
|
146
|
-
|
|
147
|
-
return true;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Check if key exists - no guessing games
|
|
152
|
-
*/
|
|
153
|
-
has(key) {
|
|
154
|
-
return this.data.has(key);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Get all data - use carefully!
|
|
159
|
-
*/
|
|
160
|
-
all() {
|
|
161
|
-
return Object.fromEntries(this.data);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Clear everything - the nuclear option
|
|
166
|
-
*/
|
|
167
|
-
clear() {
|
|
168
|
-
const size = this.data.size;
|
|
169
|
-
this.data.clear();
|
|
170
|
-
this.cache.clear();
|
|
171
|
-
this.indexes.clear();
|
|
172
|
-
|
|
173
|
-
this.emit('clear', { size });
|
|
174
|
-
this._queueSave();
|
|
175
|
-
|
|
176
|
-
return this;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Smart cache management
|
|
181
|
-
*/
|
|
182
|
-
_updateCache(key, value) {
|
|
183
|
-
// Simple LRU-like cache eviction
|
|
184
|
-
if (this.cache.size >= this.config.cacheSize) {
|
|
185
|
-
const firstKey = this.cache.keys().next().value;
|
|
186
|
-
this.cache.delete(firstKey);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
this.cache.set(key, value);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Index management for faster queries
|
|
194
|
-
*/
|
|
195
|
-
_updateIndexes(key, newValue, oldValue) {
|
|
196
|
-
// TODO: Implement in IndexManager.js
|
|
197
|
-
// This will make queries lightning fast ⚡
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
_removeFromIndexes(key, oldValue) {
|
|
201
|
-
// TODO: Remove from indexes
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* File operations with error handling
|
|
206
|
-
*/
|
|
207
|
-
async _loadFromDisk() {
|
|
208
|
-
try {
|
|
209
|
-
const data = await fs.readFile(this.config.path, 'utf8');
|
|
210
|
-
const parsed = JSON.parse(data);
|
|
211
|
-
|
|
212
|
-
// Convert object to Map for better performance
|
|
213
|
-
for (const [key, value] of Object.entries(parsed)) {
|
|
214
|
-
this.data.set(key, value);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
if (this.config.debug) {
|
|
218
|
-
console.log(`📁 Loaded ${this.data.size} records from disk`);
|
|
219
|
-
}
|
|
220
|
-
} catch (error) {
|
|
221
|
-
if (error.code === 'ENOENT') {
|
|
222
|
-
// File doesn't exist yet - that's fine
|
|
223
|
-
if (this.config.debug) {
|
|
224
|
-
console.log('📁 No existing data file - starting fresh');
|
|
225
|
-
}
|
|
226
|
-
} else {
|
|
227
|
-
throw error;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
async _saveToDisk() {
|
|
233
|
-
try {
|
|
234
|
-
const data = JSON.stringify(Object.fromEntries(this.data), null, 2);
|
|
235
|
-
|
|
236
|
-
// Atomic write - prevent corruption
|
|
237
|
-
const tempPath = this.config.path + '.tmp';
|
|
238
|
-
await fs.writeFile(tempPath, data);
|
|
239
|
-
await fs.rename(tempPath, this.config.path);
|
|
240
|
-
|
|
241
|
-
if (this.config.debug) {
|
|
242
|
-
console.log(`💾 Saved ${this.data.size} records to disk`);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
this.emit('save', { recordCount: this.data.size });
|
|
246
|
-
} catch (error) {
|
|
247
|
-
this.emit('error', error);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* Batch save operations for performance
|
|
253
|
-
*/
|
|
254
|
-
_queueSave() {
|
|
255
|
-
if (this._saveTimeout) clearTimeout(this._saveTimeout);
|
|
256
|
-
|
|
257
|
-
this._saveTimeout = setTimeout(() => {
|
|
258
|
-
this._saveToDisk();
|
|
259
|
-
}, 100); // Batch saves within 100ms window
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
_startAutoSave() {
|
|
263
|
-
setInterval(() => {
|
|
264
|
-
if (this._initialized) {
|
|
265
|
-
this._saveToDisk();
|
|
266
|
-
}
|
|
267
|
-
}, this.config.saveInterval);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Performance monitoring
|
|
272
|
-
*/
|
|
273
|
-
getStats() {
|
|
274
|
-
return {
|
|
275
|
-
...this.stats,
|
|
276
|
-
cacheHitRate: this.stats.reads > 0
|
|
277
|
-
? (this.stats.cacheHits / this.stats.reads * 100).toFixed(2) + '%'
|
|
278
|
-
: '0%',
|
|
279
|
-
totalRecords: this.data.size,
|
|
280
|
-
cacheSize: this.cache.size
|
|
281
|
-
};
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
/**
|
|
285
|
-
* Clean shutdown - be nice to your data
|
|
286
|
-
*/
|
|
287
|
-
async close() {
|
|
288
|
-
if (this._saveTimeout) clearTimeout(this._saveTimeout);
|
|
289
|
-
await this._saveToDisk();
|
|
290
|
-
this._initialized = false;
|
|
291
|
-
this.emit('close');
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
1
|
+
/**
|
|
2
|
+
* SehawqDB - The main database class
|
|
3
|
+
*
|
|
4
|
+
* Started as a simple JSON store, now with performance optimizations
|
|
5
|
+
* Because waiting for databases to load is boring 😴
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const EventEmitter = require('events');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const fs = require('fs').promises;
|
|
11
|
+
|
|
12
|
+
class SehawqDB extends EventEmitter {
|
|
13
|
+
constructor(options = {}) {
|
|
14
|
+
super();
|
|
15
|
+
|
|
16
|
+
// Config with some sensible defaults
|
|
17
|
+
this.config = {
|
|
18
|
+
path: './sehawq-data.json',
|
|
19
|
+
autoSave: true,
|
|
20
|
+
saveInterval: 5000,
|
|
21
|
+
cacheEnabled: true,
|
|
22
|
+
cacheSize: 1000,
|
|
23
|
+
...options
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// Performance optimizations
|
|
27
|
+
this.data = new Map(); // Using Map for better performance
|
|
28
|
+
this.cache = new Map(); // Hot data cache
|
|
29
|
+
this.indexes = new Map(); // Query indexes
|
|
30
|
+
|
|
31
|
+
// Runtime stats (for debugging)
|
|
32
|
+
this.stats = {
|
|
33
|
+
reads: 0,
|
|
34
|
+
writes: 0,
|
|
35
|
+
cacheHits: 0,
|
|
36
|
+
cacheMisses: 0
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
this._initialized = false;
|
|
40
|
+
this._saveTimeout = null;
|
|
41
|
+
|
|
42
|
+
// Initialize - but don't block the constructor
|
|
43
|
+
this._init().catch(error => {
|
|
44
|
+
console.error('🚨 SehawqDB init failed:', error);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Async initialization
|
|
50
|
+
* Because sometimes we need to wait for things...
|
|
51
|
+
*/
|
|
52
|
+
async _init() {
|
|
53
|
+
if (this._initialized) return;
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
// Try to load existing data
|
|
57
|
+
await this._loadFromDisk();
|
|
58
|
+
|
|
59
|
+
// Start auto-save if enabled
|
|
60
|
+
if (this.config.autoSave) {
|
|
61
|
+
this._startAutoSave();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
this._initialized = true;
|
|
65
|
+
this.emit('ready');
|
|
66
|
+
|
|
67
|
+
if (this.config.debug) {
|
|
68
|
+
console.log('✅ SehawqDB ready - Performance mode: ON');
|
|
69
|
+
}
|
|
70
|
+
} catch (error) {
|
|
71
|
+
this.emit('error', error);
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Set a value - the bread and butter
|
|
78
|
+
*/
|
|
79
|
+
set(key, value) {
|
|
80
|
+
if (!this._initialized) {
|
|
81
|
+
throw new Error('Database not initialized. Wait for ready event.');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const oldValue = this.data.get(key);
|
|
85
|
+
this.data.set(key, value);
|
|
86
|
+
|
|
87
|
+
// Cache the hot data
|
|
88
|
+
if (this.config.cacheEnabled) {
|
|
89
|
+
this._updateCache(key, value);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Update indexes if any
|
|
93
|
+
this._updateIndexes(key, value, oldValue);
|
|
94
|
+
|
|
95
|
+
this.stats.writes++;
|
|
96
|
+
this.emit('set', { key, value, oldValue });
|
|
97
|
+
|
|
98
|
+
// Immediate save for important data, otherwise batch it
|
|
99
|
+
if (this.config.autoSave) {
|
|
100
|
+
this._queueSave();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return this;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get a value - faster than your morning coffee
|
|
108
|
+
*/
|
|
109
|
+
get(key) {
|
|
110
|
+
if (!this._initialized) {
|
|
111
|
+
throw new Error('Database not initialized.');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
this.stats.reads++;
|
|
115
|
+
|
|
116
|
+
// Check cache first (hot path)
|
|
117
|
+
if (this.config.cacheEnabled && this.cache.has(key)) {
|
|
118
|
+
this.stats.cacheHits++;
|
|
119
|
+
return this.cache.get(key);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
this.stats.cacheMisses++;
|
|
123
|
+
const value = this.data.get(key);
|
|
124
|
+
|
|
125
|
+
// Cache it for next time
|
|
126
|
+
if (this.config.cacheEnabled && value !== undefined) {
|
|
127
|
+
this._updateCache(key, value);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return value;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Delete a key - poof! gone.
|
|
135
|
+
*/
|
|
136
|
+
delete(key) {
|
|
137
|
+
if (!this.data.has(key)) return false;
|
|
138
|
+
|
|
139
|
+
const oldValue = this.data.get(key);
|
|
140
|
+
this.data.delete(key);
|
|
141
|
+
this.cache.delete(key);
|
|
142
|
+
this._removeFromIndexes(key, oldValue);
|
|
143
|
+
|
|
144
|
+
this.emit('delete', { key, oldValue });
|
|
145
|
+
this._queueSave();
|
|
146
|
+
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Check if key exists - no guessing games
|
|
152
|
+
*/
|
|
153
|
+
has(key) {
|
|
154
|
+
return this.data.has(key);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Get all data - use carefully!
|
|
159
|
+
*/
|
|
160
|
+
all() {
|
|
161
|
+
return Object.fromEntries(this.data);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Clear everything - the nuclear option
|
|
166
|
+
*/
|
|
167
|
+
clear() {
|
|
168
|
+
const size = this.data.size;
|
|
169
|
+
this.data.clear();
|
|
170
|
+
this.cache.clear();
|
|
171
|
+
this.indexes.clear();
|
|
172
|
+
|
|
173
|
+
this.emit('clear', { size });
|
|
174
|
+
this._queueSave();
|
|
175
|
+
|
|
176
|
+
return this;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Smart cache management
|
|
181
|
+
*/
|
|
182
|
+
_updateCache(key, value) {
|
|
183
|
+
// Simple LRU-like cache eviction
|
|
184
|
+
if (this.cache.size >= this.config.cacheSize) {
|
|
185
|
+
const firstKey = this.cache.keys().next().value;
|
|
186
|
+
this.cache.delete(firstKey);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
this.cache.set(key, value);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Index management for faster queries
|
|
194
|
+
*/
|
|
195
|
+
_updateIndexes(key, newValue, oldValue) {
|
|
196
|
+
// TODO: Implement in IndexManager.js
|
|
197
|
+
// This will make queries lightning fast ⚡
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
_removeFromIndexes(key, oldValue) {
|
|
201
|
+
// TODO: Remove from indexes
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* File operations with error handling
|
|
206
|
+
*/
|
|
207
|
+
async _loadFromDisk() {
|
|
208
|
+
try {
|
|
209
|
+
const data = await fs.readFile(this.config.path, 'utf8');
|
|
210
|
+
const parsed = JSON.parse(data);
|
|
211
|
+
|
|
212
|
+
// Convert object to Map for better performance
|
|
213
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
214
|
+
this.data.set(key, value);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (this.config.debug) {
|
|
218
|
+
console.log(`📁 Loaded ${this.data.size} records from disk`);
|
|
219
|
+
}
|
|
220
|
+
} catch (error) {
|
|
221
|
+
if (error.code === 'ENOENT') {
|
|
222
|
+
// File doesn't exist yet - that's fine
|
|
223
|
+
if (this.config.debug) {
|
|
224
|
+
console.log('📁 No existing data file - starting fresh');
|
|
225
|
+
}
|
|
226
|
+
} else {
|
|
227
|
+
throw error;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
async _saveToDisk() {
|
|
233
|
+
try {
|
|
234
|
+
const data = JSON.stringify(Object.fromEntries(this.data), null, 2);
|
|
235
|
+
|
|
236
|
+
// Atomic write - prevent corruption
|
|
237
|
+
const tempPath = this.config.path + '.tmp';
|
|
238
|
+
await fs.writeFile(tempPath, data);
|
|
239
|
+
await fs.rename(tempPath, this.config.path);
|
|
240
|
+
|
|
241
|
+
if (this.config.debug) {
|
|
242
|
+
console.log(`💾 Saved ${this.data.size} records to disk`);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
this.emit('save', { recordCount: this.data.size });
|
|
246
|
+
} catch (error) {
|
|
247
|
+
this.emit('error', error);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Batch save operations for performance
|
|
253
|
+
*/
|
|
254
|
+
_queueSave() {
|
|
255
|
+
if (this._saveTimeout) clearTimeout(this._saveTimeout);
|
|
256
|
+
|
|
257
|
+
this._saveTimeout = setTimeout(() => {
|
|
258
|
+
this._saveToDisk();
|
|
259
|
+
}, 100); // Batch saves within 100ms window
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
_startAutoSave() {
|
|
263
|
+
setInterval(() => {
|
|
264
|
+
if (this._initialized) {
|
|
265
|
+
this._saveToDisk();
|
|
266
|
+
}
|
|
267
|
+
}, this.config.saveInterval);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Performance monitoring
|
|
272
|
+
*/
|
|
273
|
+
getStats() {
|
|
274
|
+
return {
|
|
275
|
+
...this.stats,
|
|
276
|
+
cacheHitRate: this.stats.reads > 0
|
|
277
|
+
? (this.stats.cacheHits / this.stats.reads * 100).toFixed(2) + '%'
|
|
278
|
+
: '0%',
|
|
279
|
+
totalRecords: this.data.size,
|
|
280
|
+
cacheSize: this.cache.size
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Clean shutdown - be nice to your data
|
|
286
|
+
*/
|
|
287
|
+
async close() {
|
|
288
|
+
if (this._saveTimeout) clearTimeout(this._saveTimeout);
|
|
289
|
+
await this._saveToDisk();
|
|
290
|
+
this._initialized = false;
|
|
291
|
+
this.emit('close');
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
295
|
module.exports = SehawqDB;
|