@ouim/vectoriadb-server 0.1.3 → 0.1.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/README.md +63 -7
- package/package.json +1 -1
- package/vectoriadb-server.js +116 -0
package/README.md
CHANGED
|
@@ -113,13 +113,69 @@ const bobDocs = await db.query('my-collection', 'farewell', {
|
|
|
113
113
|
|
|
114
114
|
### Server Options
|
|
115
115
|
|
|
116
|
-
| Option
|
|
117
|
-
|
|
|
118
|
-
| `port`
|
|
119
|
-
| `host`
|
|
120
|
-
| `apiKey`
|
|
121
|
-
| `cors`
|
|
122
|
-
| `streamChunkSize`
|
|
116
|
+
| Option | Description | Default |
|
|
117
|
+
| :------------------------ | :------------------------------------------------------------------------------------- | :--------- |
|
|
118
|
+
| `port` | Server listening port | `3001` |
|
|
119
|
+
| `host` | Server host address | `0.0.0.0` |
|
|
120
|
+
| `apiKey` | Optional key for client authentication | `null` |
|
|
121
|
+
| `cors` | Allowed origins (array) | `[]` (All) |
|
|
122
|
+
| `streamChunkSize` | Max results per chunk for streaming | `500` |
|
|
123
|
+
| `autoSaveOnMutationBurst` | Enable automatic saveToStorage after a burst of mutation calls + idle | `true` |
|
|
124
|
+
| `autoSaveOnInactivity` | Save to storage after `mutationInactivityMs` of no mutations (suitable for small apps) | `true` |
|
|
125
|
+
| `mutationBurstThreshold` | Number of mutation calls within `mutationBurstWindowMs` to consider a burst | `5` |
|
|
126
|
+
| `mutationBurstWindowMs` | Time window (ms) used to count burst mutations | `120000` |
|
|
127
|
+
| `mutationInactivityMs` | Inactivity window (ms) after last mutation that triggers save | `30000` |
|
|
128
|
+
| `minSaveIntervalMs` | Minimum time (ms) between automatic saves to avoid thrashing | `10000` |
|
|
129
|
+
|
|
130
|
+
Auto-save on mutation bursts
|
|
131
|
+
|
|
132
|
+
A configurable mechanism that detects "mutation bursts" and automatically calls `saveToStorage()` after activity stops. This minimizes write amplification during heavy write periods while ensuring state is persisted shortly after the burst finishes.
|
|
133
|
+
|
|
134
|
+
How it works:
|
|
135
|
+
|
|
136
|
+
- A "burst" is detected when at least `mutationBurstThreshold` mutation operations occur within the `mutationBurstWindowMs` time window.
|
|
137
|
+
- When a burst has been detected, the server starts (or resets) an inactivity timer of `mutationInactivityMs` after the last mutation.
|
|
138
|
+
- If the inactivity timer expires and the recent mutation count still meets the threshold, the server calls `saveToStorage()` once.
|
|
139
|
+
- `minSaveIntervalMs` prevents repeated auto-saves from occurring too frequently; overlapping saves are also avoided by an internal in-progress flag.
|
|
140
|
+
- After the inactivity handler runs the mutation history is cleared until new activity occurs.
|
|
141
|
+
|
|
142
|
+
Behavior timeline (concrete example):
|
|
143
|
+
|
|
144
|
+
- Settings: `mutationBurstThreshold = 100`, `mutationBurstWindowMs = 120000` (2 min), `mutationInactivityMs = 30000` (30s).
|
|
145
|
+
- If 120 mutations arrive between t=0 and t=90s (burst detected), and no further mutations occur after t=90s, the server waits 30s (inactivity window) and calls `saveToStorage()` at ~t=120s (provided `minSaveIntervalMs` allows it).
|
|
146
|
+
|
|
147
|
+
Configuration example:
|
|
148
|
+
|
|
149
|
+
```javascript
|
|
150
|
+
const server = new VectoriaDBServer({
|
|
151
|
+
port: 3001,
|
|
152
|
+
autoSaveOnMutationBurst: true,
|
|
153
|
+
mutationBurstThreshold: 100, // # mutations within the window to mark a burst
|
|
154
|
+
mutationBurstWindowMs: 2 * 60 * 1000, // window length used to count mutations
|
|
155
|
+
mutationInactivityMs: 30 * 1000, // wait this long after last mutation before saving
|
|
156
|
+
minSaveIntervalMs: 10 * 1000, // never auto-save more often than this
|
|
157
|
+
})
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Log & testing notes:
|
|
161
|
+
|
|
162
|
+
- When the auto-save runs the server logs: `[VectoriaDBServer] auto-saved storage (<reason>)` (e.g. `mutation-burst-inactivity` or `inactivity`).
|
|
163
|
+
- To test: simulate mutation calls and assert `saveToStorage()` is invoked after `mutationInactivityMs`; verify `minSaveIntervalMs` prevents frequent saves.
|
|
164
|
+
|
|
165
|
+
Inactivity-only mode (simpler)
|
|
166
|
+
|
|
167
|
+
If your application is small or you prefer a simpler policy, enable `autoSaveOnInactivity: true`. With this enabled the server will call `saveToStorage()` after `mutationInactivityMs` of no mutation activity — regardless of how many mutations occurred before the idle period. The inactivity timer resets on each mutation.
|
|
168
|
+
|
|
169
|
+
Example:
|
|
170
|
+
|
|
171
|
+
```javascript
|
|
172
|
+
const server = new VectoriaDBServer({
|
|
173
|
+
autoSaveOnInactivity: true,
|
|
174
|
+
mutationInactivityMs: 30 * 1000, // save 30s after last mutation
|
|
175
|
+
})
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Client Options
|
|
123
179
|
|
|
124
180
|
### Client Options
|
|
125
181
|
|
package/package.json
CHANGED
package/vectoriadb-server.js
CHANGED
|
@@ -14,6 +14,35 @@ export default class VectoriaDBServer {
|
|
|
14
14
|
this.vectoriadbConfig = opts.vectoriadbConfig || {}
|
|
15
15
|
this.streamChunkSize = opts.streamChunkSize || 500
|
|
16
16
|
|
|
17
|
+
// --- auto-save / burst-detection (configurable) ---
|
|
18
|
+
this.autoSaveOnMutationBurst = opts.autoSaveOnMutationBurst !== undefined ? !!opts.autoSaveOnMutationBurst : true
|
|
19
|
+
// when true the server will save after any inactivity period (see `mutationInactivityMs`) — useful for small apps
|
|
20
|
+
this.autoSaveOnInactivity = opts.autoSaveOnInactivity !== undefined ? !!opts.autoSaveOnInactivity : false
|
|
21
|
+
this.mutationBurstThreshold = Number(opts.mutationBurstThreshold) || 5
|
|
22
|
+
this.mutationBurstWindowMs = Number(opts.mutationBurstWindowMs) || 2 * 60 * 1000 // 2 minutes
|
|
23
|
+
this.mutationInactivityMs = Number(opts.mutationInactivityMs) || 30 * 1000 // 30s inactivity to trigger save
|
|
24
|
+
this.minSaveIntervalMs = Number(opts.minSaveIntervalMs) || 10 * 1000 // minimum time between auto-saves
|
|
25
|
+
|
|
26
|
+
// internal mutation-tracking state
|
|
27
|
+
this._mutationMethods = new Set([
|
|
28
|
+
'add',
|
|
29
|
+
'addMany',
|
|
30
|
+
'update',
|
|
31
|
+
'remove',
|
|
32
|
+
'removeMany',
|
|
33
|
+
'clear',
|
|
34
|
+
'insert',
|
|
35
|
+
'upsert',
|
|
36
|
+
'replace',
|
|
37
|
+
'put',
|
|
38
|
+
'delete',
|
|
39
|
+
])
|
|
40
|
+
this._mutationTimestamps = []
|
|
41
|
+
this._inactivityTimer = null
|
|
42
|
+
this._lastBurstAt = 0
|
|
43
|
+
this._savingInProgress = false
|
|
44
|
+
this._lastSaveAt = 0
|
|
45
|
+
|
|
17
46
|
this._http = null
|
|
18
47
|
this._io = null
|
|
19
48
|
this._vectoria = null
|
|
@@ -148,6 +177,15 @@ export default class VectoriaDBServer {
|
|
|
148
177
|
new Promise((_, reject) => setTimeout(() => reject(new Error('ServerTimeout')), 30000)),
|
|
149
178
|
])
|
|
150
179
|
|
|
180
|
+
// record mutation activity (used to auto-flush after a burst + inactivity)
|
|
181
|
+
if (this.autoSaveOnMutationBurst && this._isMutationMethod(method)) {
|
|
182
|
+
try {
|
|
183
|
+
this._recordMutation()
|
|
184
|
+
} catch (e) {
|
|
185
|
+
/* swallow tracking errors */
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
151
189
|
const took = Date.now() - start
|
|
152
190
|
|
|
153
191
|
// streaming support for very large arrays
|
|
@@ -169,8 +207,86 @@ export default class VectoriaDBServer {
|
|
|
169
207
|
}
|
|
170
208
|
}
|
|
171
209
|
|
|
210
|
+
// --- Auto-save on mutation bursts (configurable) ---
|
|
211
|
+
_isMutationMethod(method) {
|
|
212
|
+
return this._mutationMethods.has(method)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
_recordMutation() {
|
|
216
|
+
const now = Date.now()
|
|
217
|
+
this._mutationTimestamps.push(now)
|
|
218
|
+
// purge old timestamps outside the burst window
|
|
219
|
+
const cutoff = now - this.mutationBurstWindowMs
|
|
220
|
+
while (this._mutationTimestamps.length && this._mutationTimestamps[0] < cutoff) {
|
|
221
|
+
this._mutationTimestamps.shift()
|
|
222
|
+
}
|
|
223
|
+
if (this._mutationTimestamps.length >= this.mutationBurstThreshold) {
|
|
224
|
+
this._lastBurstAt = now
|
|
225
|
+
}
|
|
226
|
+
this._scheduleInactivityTimer()
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
_scheduleInactivityTimer() {
|
|
230
|
+
if (this._inactivityTimer) {
|
|
231
|
+
clearTimeout(this._inactivityTimer)
|
|
232
|
+
}
|
|
233
|
+
this._inactivityTimer = setTimeout(() => {
|
|
234
|
+
// fire-and-forget async
|
|
235
|
+
this._onInactivityTimeout().catch(err => console.warn('Inactivity flush error:', err.message))
|
|
236
|
+
}, this.mutationInactivityMs)
|
|
237
|
+
if (this._inactivityTimer.unref) this._inactivityTimer.unref()
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
async _onInactivityTimeout() {
|
|
241
|
+
this._inactivityTimer = null
|
|
242
|
+
const now = Date.now()
|
|
243
|
+
|
|
244
|
+
// Simple "inactivity-only" mode: save after any inactivity period if enabled.
|
|
245
|
+
if (this.autoSaveOnInactivity) {
|
|
246
|
+
if (this._mutationTimestamps.length > 0) {
|
|
247
|
+
await this._saveToStorage('inactivity')
|
|
248
|
+
}
|
|
249
|
+
// reset history and return early
|
|
250
|
+
this._mutationTimestamps = []
|
|
251
|
+
this._lastBurstAt = 0
|
|
252
|
+
return
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// count mutations within the burst window (existing burst-detection behavior)
|
|
256
|
+
const cutoff = now - this.mutationBurstWindowMs
|
|
257
|
+
const recentCount = this._mutationTimestamps.filter(ts => ts >= cutoff).length
|
|
258
|
+
if (recentCount >= this.mutationBurstThreshold) {
|
|
259
|
+
await this._saveToStorage('mutation-burst-inactivity')
|
|
260
|
+
}
|
|
261
|
+
// reset history (we only track bursts between inactivity windows)
|
|
262
|
+
this._mutationTimestamps = []
|
|
263
|
+
this._lastBurstAt = 0
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
async _saveToStorage(reason = 'manual') {
|
|
267
|
+
if (!this._vectoria || typeof this._vectoria.saveToStorage !== 'function') return
|
|
268
|
+
const now = Date.now()
|
|
269
|
+
if (this._savingInProgress) return
|
|
270
|
+
if (now - this._lastSaveAt < this.minSaveIntervalMs) return
|
|
271
|
+
this._savingInProgress = true
|
|
272
|
+
try {
|
|
273
|
+
await this._vectoria.saveToStorage()
|
|
274
|
+
this._lastSaveAt = Date.now()
|
|
275
|
+
console.log(`[VectoriaDBServer] auto-saved storage (${reason})`)
|
|
276
|
+
} catch (err) {
|
|
277
|
+
console.warn('[VectoriaDBServer] auto-save failed:', err.message)
|
|
278
|
+
} finally {
|
|
279
|
+
this._savingInProgress = false
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
172
283
|
async close() {
|
|
173
284
|
if (!this._started) return
|
|
285
|
+
// stop any pending auto-save timer
|
|
286
|
+
if (this._inactivityTimer) {
|
|
287
|
+
clearTimeout(this._inactivityTimer)
|
|
288
|
+
this._inactivityTimer = null
|
|
289
|
+
}
|
|
174
290
|
// disconnect sockets
|
|
175
291
|
for (const s of Array.from(this._sockets)) {
|
|
176
292
|
try {
|