jexidb 2.0.3 → 2.1.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.
Files changed (67) hide show
  1. package/.babelrc +13 -0
  2. package/.gitattributes +2 -0
  3. package/CHANGELOG.md +132 -101
  4. package/LICENSE +21 -21
  5. package/README.md +301 -639
  6. package/babel.config.json +5 -0
  7. package/dist/Database.cjs +3896 -0
  8. package/docs/API.md +1051 -390
  9. package/docs/EXAMPLES.md +701 -177
  10. package/docs/README.md +194 -184
  11. package/examples/iterate-usage-example.js +157 -0
  12. package/examples/simple-iterate-example.js +115 -0
  13. package/jest.config.js +24 -0
  14. package/package.json +63 -54
  15. package/scripts/README.md +47 -0
  16. package/scripts/clean-test-files.js +75 -0
  17. package/scripts/prepare.js +31 -0
  18. package/scripts/run-tests.js +80 -0
  19. package/src/Database.mjs +4130 -0
  20. package/src/FileHandler.mjs +1101 -0
  21. package/src/OperationQueue.mjs +279 -0
  22. package/src/SchemaManager.mjs +268 -0
  23. package/src/Serializer.mjs +511 -0
  24. package/src/managers/ConcurrencyManager.mjs +257 -0
  25. package/src/managers/IndexManager.mjs +1403 -0
  26. package/src/managers/QueryManager.mjs +1273 -0
  27. package/src/managers/StatisticsManager.mjs +262 -0
  28. package/src/managers/StreamingProcessor.mjs +429 -0
  29. package/src/managers/TermManager.mjs +278 -0
  30. package/test/$not-operator-with-and.test.js +282 -0
  31. package/test/README.md +8 -0
  32. package/test/close-init-cycle.test.js +256 -0
  33. package/test/critical-bugs-fixes.test.js +1069 -0
  34. package/test/index-persistence.test.js +306 -0
  35. package/test/index-serialization.test.js +314 -0
  36. package/test/indexed-query-mode.test.js +360 -0
  37. package/test/iterate-method.test.js +272 -0
  38. package/test/query-operators.test.js +238 -0
  39. package/test/regex-array-fields.test.js +129 -0
  40. package/test/score-method.test.js +238 -0
  41. package/test/setup.js +17 -0
  42. package/test/term-mapping-minimal.test.js +154 -0
  43. package/test/term-mapping-simple.test.js +257 -0
  44. package/test/term-mapping.test.js +514 -0
  45. package/test/writebuffer-flush-resilience.test.js +204 -0
  46. package/dist/FileHandler.js +0 -688
  47. package/dist/IndexManager.js +0 -353
  48. package/dist/IntegrityChecker.js +0 -364
  49. package/dist/JSONLDatabase.js +0 -1333
  50. package/dist/index.js +0 -617
  51. package/docs/MIGRATION.md +0 -295
  52. package/examples/auto-save-example.js +0 -158
  53. package/examples/cjs-usage.cjs +0 -82
  54. package/examples/close-vs-delete-example.js +0 -71
  55. package/examples/esm-usage.js +0 -113
  56. package/examples/example-columns.idx.jdb +0 -0
  57. package/examples/example-columns.jdb +0 -9
  58. package/examples/example-options.idx.jdb +0 -0
  59. package/examples/example-options.jdb +0 -0
  60. package/examples/example-users.idx.jdb +0 -0
  61. package/examples/example-users.jdb +0 -5
  62. package/examples/simple-test.js +0 -55
  63. package/src/FileHandler.js +0 -674
  64. package/src/IndexManager.js +0 -363
  65. package/src/IntegrityChecker.js +0 -379
  66. package/src/JSONLDatabase.js +0 -1391
  67. package/src/index.js +0 -608
@@ -0,0 +1,257 @@
1
+ /**
2
+ * ConcurrencyManager - Handles all concurrency control and synchronization
3
+ *
4
+ * Responsibilities:
5
+ * - _acquireMutexWithTimeout()
6
+ * - Mutex and fileMutex management
7
+ * - Concurrent operations control
8
+ */
9
+
10
+ export class ConcurrencyManager {
11
+ constructor(database) {
12
+ this.database = database
13
+ this.opts = database.opts
14
+ this.mutex = database.mutex
15
+ this.fileMutex = database.fileMutex
16
+ this.operationQueue = database.operationQueue
17
+ this.pendingOperations = database.pendingOperations || 0
18
+ this.pendingPromises = database.pendingPromises || new Set()
19
+ }
20
+
21
+ /**
22
+ * Acquire mutex with timeout
23
+ * @param {Mutex} mutex - Mutex to acquire
24
+ * @param {number} timeout - Timeout in milliseconds
25
+ * @returns {Promise<Function>} - Release function
26
+ */
27
+ async _acquireMutexWithTimeout(mutex, timeout = null) {
28
+ const timeoutMs = timeout || this.opts.mutexTimeout
29
+ const startTime = Date.now()
30
+
31
+ try {
32
+ const release = await Promise.race([
33
+ mutex.acquire(),
34
+ new Promise((_, reject) =>
35
+ setTimeout(() => reject(new Error(`Mutex acquisition timeout after ${timeoutMs}ms`)), timeoutMs)
36
+ )
37
+ ])
38
+
39
+ if (this.opts.debugMode) {
40
+ const acquireTime = Date.now() - startTime
41
+ if (acquireTime > 1000) {
42
+ console.warn(`⚠️ Slow mutex acquisition: ${acquireTime}ms`)
43
+ }
44
+ }
45
+
46
+ // Wrap release function to track mutex usage
47
+ const originalRelease = release
48
+ return () => {
49
+ try {
50
+ originalRelease()
51
+ } catch (error) {
52
+ console.error(`❌ Error releasing mutex: ${error.message}`)
53
+ }
54
+ }
55
+ } catch (error) {
56
+ if (this.opts.debugMode) {
57
+ console.error(`❌ Mutex acquisition failed: ${error.message}`)
58
+ }
59
+ throw error
60
+ }
61
+ }
62
+
63
+
64
+ /**
65
+ * Execute operation with queue management
66
+ * @param {Function} operation - Operation to execute
67
+ * @returns {Promise} - Operation result
68
+ */
69
+ async executeWithQueue(operation) {
70
+ if (!this.operationQueue) {
71
+ return operation()
72
+ }
73
+
74
+ return this.operationQueue.enqueue(operation)
75
+ }
76
+
77
+ /**
78
+ * Wait for all pending operations to complete
79
+ * @returns {Promise<void>}
80
+ */
81
+ async waitForPendingOperations() {
82
+ if (this.pendingOperations === 0) {
83
+ return
84
+ }
85
+
86
+ const pendingPromisesArray = Array.from(this.pendingPromises)
87
+
88
+ if (pendingPromisesArray.length === 0) {
89
+ return
90
+ }
91
+
92
+ try {
93
+ await Promise.allSettled(pendingPromisesArray)
94
+ this.pendingPromises.clear()
95
+ this.pendingOperations = 0
96
+ } catch (error) {
97
+ console.warn('Error waiting for pending operations:', error)
98
+ this.pendingPromises.clear()
99
+ this.pendingOperations = 0
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Get concurrency statistics
105
+ * @returns {Object} - Concurrency statistics
106
+ */
107
+ getConcurrencyStats() {
108
+ return {
109
+ pendingOperations: this.pendingOperations,
110
+ pendingPromises: this.pendingPromises.size,
111
+ mutexTimeout: this.opts.mutexTimeout,
112
+ hasOperationQueue: !!this.operationQueue
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Check if system is under high concurrency load
118
+ * @returns {boolean} - True if under high load
119
+ */
120
+ isUnderHighLoad() {
121
+ const maxOperations = this.opts.maxConcurrentOperations || 10
122
+ return this.pendingOperations >= maxOperations * 0.8 // 80% of max capacity
123
+ }
124
+
125
+ /**
126
+ * Get recommended timeout based on current load
127
+ * @returns {number} - Recommended timeout in milliseconds
128
+ */
129
+ getRecommendedTimeout() {
130
+ const baseTimeout = this.opts.mutexTimeout || 15000 // Reduced from 30000 to 15000
131
+ const loadFactor = this.pendingOperations / 10 // Use fixed limit of 10
132
+
133
+ // Increase timeout based on load
134
+ return Math.min(baseTimeout * (1 + loadFactor), baseTimeout * 3)
135
+ }
136
+
137
+ /**
138
+ * Acquire multiple mutexes in order to prevent deadlocks
139
+ * @param {Array<Mutex>} mutexes - Mutexes to acquire in order
140
+ * @param {number} timeout - Timeout in milliseconds
141
+ * @returns {Promise<Array<Function>>} - Array of release functions
142
+ */
143
+ async acquireMultipleMutexes(mutexes, timeout = null) {
144
+ const releases = []
145
+
146
+ try {
147
+ for (const mutex of mutexes) {
148
+ const release = await this._acquireMutexWithTimeout(mutex, timeout)
149
+ releases.push(release)
150
+ }
151
+
152
+ return releases
153
+ } catch (error) {
154
+ // Release already acquired mutexes on error
155
+ for (const release of releases) {
156
+ try {
157
+ release()
158
+ } catch (releaseError) {
159
+ console.warn('Error releasing mutex:', releaseError)
160
+ }
161
+ }
162
+ throw error
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Execute operation with automatic mutex management
168
+ * @param {Function} operation - Operation to execute
169
+ * @param {Object} options - Options for execution
170
+ * @returns {Promise} - Operation result
171
+ */
172
+ async executeWithMutex(operation, options = {}) {
173
+ const {
174
+ mutex = this.mutex,
175
+ timeout = null,
176
+ retries = 0,
177
+ retryDelay = 100
178
+ } = options
179
+
180
+ let lastError = null
181
+
182
+ for (let attempt = 0; attempt <= retries; attempt++) {
183
+ try {
184
+ const release = await this._acquireMutexWithTimeout(mutex, timeout)
185
+
186
+ try {
187
+ const result = await operation()
188
+ return result
189
+ } finally {
190
+ release()
191
+ }
192
+ } catch (error) {
193
+ lastError = error
194
+
195
+ if (attempt < retries) {
196
+ // Wait before retry with exponential backoff
197
+ const delay = retryDelay * Math.pow(2, attempt)
198
+ await new Promise(resolve => setTimeout(resolve, delay))
199
+ }
200
+ }
201
+ }
202
+
203
+ throw lastError
204
+ }
205
+
206
+ /**
207
+ * Create a semaphore for limiting concurrent operations
208
+ * @param {number} limit - Maximum concurrent operations
209
+ * @returns {Object} - Semaphore object
210
+ */
211
+ createSemaphore(limit) {
212
+ let current = 0
213
+ const queue = []
214
+
215
+ return {
216
+ async acquire() {
217
+ return new Promise((resolve) => {
218
+ if (current < limit) {
219
+ current++
220
+ resolve()
221
+ } else {
222
+ queue.push(resolve)
223
+ }
224
+ })
225
+ },
226
+
227
+ release() {
228
+ if (queue.length > 0) {
229
+ const next = queue.shift()
230
+ next()
231
+ } else {
232
+ current--
233
+ }
234
+ },
235
+
236
+ getCurrent() {
237
+ return current
238
+ },
239
+
240
+ getQueueLength() {
241
+ return queue.length
242
+ }
243
+ }
244
+ }
245
+
246
+ /**
247
+ * Cleanup concurrency resources
248
+ */
249
+ cleanup() {
250
+ this.pendingPromises.clear()
251
+ this.pendingOperations = 0
252
+
253
+ if (this.opts.debugMode) {
254
+ console.log('🧹 Concurrency manager cleaned up')
255
+ }
256
+ }
257
+ }