s3db.js 8.2.0 → 9.2.0

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.
@@ -84,6 +84,7 @@
84
84
  * - Tags are useful for cache invalidation and monitoring
85
85
  * - Case sensitivity affects key matching and storage efficiency
86
86
  */
87
+ import zlib from 'node:zlib';
87
88
  import { Cache } from "./cache.class.js"
88
89
 
89
90
  export class MemoryCache extends Cache {
@@ -93,6 +94,18 @@ export class MemoryCache extends Cache {
93
94
  this.meta = {};
94
95
  this.maxSize = config.maxSize !== undefined ? config.maxSize : 1000;
95
96
  this.ttl = config.ttl !== undefined ? config.ttl : 300000;
97
+
98
+ // Compression configuration
99
+ this.enableCompression = config.enableCompression !== undefined ? config.enableCompression : false;
100
+ this.compressionThreshold = config.compressionThreshold !== undefined ? config.compressionThreshold : 1024;
101
+
102
+ // Stats for compression
103
+ this.compressionStats = {
104
+ totalCompressed: 0,
105
+ totalOriginalSize: 0,
106
+ totalCompressedSize: 0,
107
+ compressionRatio: 0
108
+ };
96
109
  }
97
110
 
98
111
  async _set(key, data) {
@@ -106,13 +119,59 @@ export class MemoryCache extends Cache {
106
119
  delete this.meta[oldestKey];
107
120
  }
108
121
  }
109
- this.cache[key] = data;
110
- this.meta[key] = { ts: Date.now() };
122
+
123
+ // Prepare data for storage
124
+ let finalData = data;
125
+ let compressed = false;
126
+ let originalSize = 0;
127
+ let compressedSize = 0;
128
+
129
+ // Apply compression if enabled
130
+ if (this.enableCompression) {
131
+ try {
132
+ // Serialize data to measure size
133
+ const serialized = JSON.stringify(data);
134
+ originalSize = Buffer.byteLength(serialized, 'utf8');
135
+
136
+ // Compress only if over threshold
137
+ if (originalSize >= this.compressionThreshold) {
138
+ const compressedBuffer = zlib.gzipSync(Buffer.from(serialized, 'utf8'));
139
+ finalData = {
140
+ __compressed: true,
141
+ __data: compressedBuffer.toString('base64'),
142
+ __originalSize: originalSize
143
+ };
144
+ compressedSize = Buffer.byteLength(finalData.__data, 'utf8');
145
+ compressed = true;
146
+
147
+ // Update compression stats
148
+ this.compressionStats.totalCompressed++;
149
+ this.compressionStats.totalOriginalSize += originalSize;
150
+ this.compressionStats.totalCompressedSize += compressedSize;
151
+ this.compressionStats.compressionRatio =
152
+ (this.compressionStats.totalCompressedSize / this.compressionStats.totalOriginalSize).toFixed(2);
153
+ }
154
+ } catch (error) {
155
+ // If compression fails, store uncompressed
156
+ console.warn(`[MemoryCache] Compression failed for key '${key}':`, error.message);
157
+ }
158
+ }
159
+
160
+ this.cache[key] = finalData;
161
+ this.meta[key] = {
162
+ ts: Date.now(),
163
+ compressed,
164
+ originalSize,
165
+ compressedSize: compressed ? compressedSize : originalSize
166
+ };
167
+
111
168
  return data;
112
169
  }
113
170
 
114
171
  async _get(key) {
115
172
  if (!Object.prototype.hasOwnProperty.call(this.cache, key)) return null;
173
+
174
+ // Check TTL expiration
116
175
  if (this.ttl > 0) {
117
176
  const now = Date.now();
118
177
  const meta = this.meta[key];
@@ -123,7 +182,27 @@ export class MemoryCache extends Cache {
123
182
  return null;
124
183
  }
125
184
  }
126
- return this.cache[key];
185
+
186
+ const rawData = this.cache[key];
187
+
188
+ // Check if data is compressed
189
+ if (rawData && typeof rawData === 'object' && rawData.__compressed) {
190
+ try {
191
+ // Decompress data
192
+ const compressedBuffer = Buffer.from(rawData.__data, 'base64');
193
+ const decompressed = zlib.gunzipSync(compressedBuffer).toString('utf8');
194
+ return JSON.parse(decompressed);
195
+ } catch (error) {
196
+ console.warn(`[MemoryCache] Decompression failed for key '${key}':`, error.message);
197
+ // If decompression fails, remove corrupted entry
198
+ delete this.cache[key];
199
+ delete this.meta[key];
200
+ return null;
201
+ }
202
+ }
203
+
204
+ // Return uncompressed data
205
+ return rawData;
127
206
  }
128
207
 
129
208
  async _del(key) {
@@ -159,6 +238,36 @@ export class MemoryCache extends Cache {
159
238
  async keys() {
160
239
  return Object.keys(this.cache);
161
240
  }
241
+
242
+ /**
243
+ * Get compression statistics
244
+ * @returns {Object} Compression stats including total compressed items, ratios, and space savings
245
+ */
246
+ getCompressionStats() {
247
+ if (!this.enableCompression) {
248
+ return { enabled: false, message: 'Compression is disabled' };
249
+ }
250
+
251
+ const spaceSavings = this.compressionStats.totalOriginalSize > 0
252
+ ? ((this.compressionStats.totalOriginalSize - this.compressionStats.totalCompressedSize) / this.compressionStats.totalOriginalSize * 100).toFixed(2)
253
+ : 0;
254
+
255
+ return {
256
+ enabled: true,
257
+ totalItems: Object.keys(this.cache).length,
258
+ compressedItems: this.compressionStats.totalCompressed,
259
+ compressionThreshold: this.compressionThreshold,
260
+ totalOriginalSize: this.compressionStats.totalOriginalSize,
261
+ totalCompressedSize: this.compressionStats.totalCompressedSize,
262
+ averageCompressionRatio: this.compressionStats.compressionRatio,
263
+ spaceSavingsPercent: spaceSavings,
264
+ memoryUsage: {
265
+ uncompressed: `${(this.compressionStats.totalOriginalSize / 1024).toFixed(2)} KB`,
266
+ compressed: `${(this.compressionStats.totalCompressedSize / 1024).toFixed(2)} KB`,
267
+ saved: `${((this.compressionStats.totalOriginalSize - this.compressionStats.totalCompressedSize) / 1024).toFixed(2)} KB`
268
+ }
269
+ };
270
+ }
162
271
  }
163
272
 
164
273
  export default MemoryCache
@@ -4,9 +4,12 @@ export { default as Plugin } from './plugin.class.js'
4
4
 
5
5
  // plugins:
6
6
  export * from './audit.plugin.js'
7
+ export * from './backup.plugin.js'
7
8
  export * from './cache.plugin.js'
8
9
  export * from './costs.plugin.js'
9
10
  export * from './fulltext.plugin.js'
10
11
  export * from './metrics.plugin.js'
11
12
  export * from './queue-consumer.plugin.js'
12
13
  export * from './replicator.plugin.js'
14
+ export * from './scheduler.plugin.js'
15
+ export * from './state-machine.plugin.js'