deepbase-sqlite 3.1.2 → 3.1.6
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/package.json +3 -3
- package/src/SqliteDriver.js +37 -9
- package/test/test.js +97 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deepbase-sqlite",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.6",
|
|
4
4
|
"description": "⚡ DeepBase SQLite - SQLite database driver",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.cjs",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"better-sqlite3": "^11.8.1"
|
|
16
16
|
},
|
|
17
17
|
"peerDependencies": {
|
|
18
|
-
"deepbase": "^3.1.
|
|
18
|
+
"deepbase": "^3.1.6"
|
|
19
19
|
},
|
|
20
20
|
"scripts": {
|
|
21
21
|
"test": "mocha test/test.js"
|
|
@@ -42,5 +42,5 @@
|
|
|
42
42
|
"bugs": {
|
|
43
43
|
"url": "https://github.com/clasen/DeepBase/issues"
|
|
44
44
|
},
|
|
45
|
-
"homepage": "https://github.com/clasen/DeepBase
|
|
45
|
+
"homepage": "https://github.com/clasen/DeepBase/tree/main/packages/driver-sqlite"
|
|
46
46
|
}
|
package/src/SqliteDriver.js
CHANGED
|
@@ -57,6 +57,10 @@ export class SqliteDriver extends DeepBaseDriver {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
async get(...args) {
|
|
60
|
+
return this._getSync(args);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
_getSync(args) {
|
|
60
64
|
if (args.length === 0) {
|
|
61
65
|
// Get root object
|
|
62
66
|
return this._getRootObject();
|
|
@@ -106,14 +110,23 @@ export class SqliteDriver extends DeepBaseDriver {
|
|
|
106
110
|
return [];
|
|
107
111
|
}
|
|
108
112
|
|
|
113
|
+
return this._setSync(args);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
_setSync(args) {
|
|
109
117
|
const keys = args.slice(0, -1);
|
|
110
118
|
const value = args[args.length - 1];
|
|
111
119
|
const key = this._pathToKey(keys);
|
|
112
120
|
|
|
113
|
-
//
|
|
114
|
-
this.
|
|
121
|
+
// Use transaction to make expansion and set atomic
|
|
122
|
+
const transaction = this.db.transaction(() => {
|
|
123
|
+
// Check if any parent path exists as an object that needs to be expanded
|
|
124
|
+
this._expandParentObjects(keys);
|
|
125
|
+
|
|
126
|
+
this.setStmt.run(key, JSON.stringify(value));
|
|
127
|
+
});
|
|
115
128
|
|
|
116
|
-
|
|
129
|
+
transaction();
|
|
117
130
|
return keys;
|
|
118
131
|
}
|
|
119
132
|
|
|
@@ -126,12 +139,17 @@ export class SqliteDriver extends DeepBaseDriver {
|
|
|
126
139
|
|
|
127
140
|
const key = this._pathToKey(keys);
|
|
128
141
|
|
|
129
|
-
//
|
|
130
|
-
this.
|
|
142
|
+
// Use transaction to make deletion atomic
|
|
143
|
+
const transaction = this.db.transaction(() => {
|
|
144
|
+
// Delete the key itself
|
|
145
|
+
this.delStmt.run(key);
|
|
146
|
+
|
|
147
|
+
// Delete all children
|
|
148
|
+
const childKey = key + '.';
|
|
149
|
+
this.db.prepare('DELETE FROM deepbase WHERE key LIKE ?').run(childKey + '%');
|
|
150
|
+
});
|
|
131
151
|
|
|
132
|
-
|
|
133
|
-
const childKey = key + '.';
|
|
134
|
-
this.db.prepare('DELETE FROM deepbase WHERE key LIKE ?').run(childKey + '%');
|
|
152
|
+
transaction();
|
|
135
153
|
}
|
|
136
154
|
|
|
137
155
|
async inc(...args) {
|
|
@@ -153,7 +171,17 @@ export class SqliteDriver extends DeepBaseDriver {
|
|
|
153
171
|
|
|
154
172
|
async upd(...args) {
|
|
155
173
|
const func = args.pop();
|
|
156
|
-
|
|
174
|
+
const keys = args;
|
|
175
|
+
|
|
176
|
+
// Use transaction to make get+set atomic
|
|
177
|
+
const transaction = this.db.transaction(() => {
|
|
178
|
+
const currentValue = this._getSync(keys);
|
|
179
|
+
const newValue = func(currentValue);
|
|
180
|
+
this._setSync([...keys, newValue]);
|
|
181
|
+
return keys;
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
return transaction();
|
|
157
185
|
}
|
|
158
186
|
|
|
159
187
|
_getRootObject() {
|
package/test/test.js
CHANGED
|
@@ -500,6 +500,103 @@ describe('SqliteDriver', function() {
|
|
|
500
500
|
});
|
|
501
501
|
});
|
|
502
502
|
});
|
|
503
|
+
|
|
504
|
+
describe('Race Conditions', function() {
|
|
505
|
+
it('should handle 100 concurrent increments correctly', async function() {
|
|
506
|
+
this.timeout(5000);
|
|
507
|
+
|
|
508
|
+
await db.set('counter', 0);
|
|
509
|
+
|
|
510
|
+
// Run 100 concurrent increments
|
|
511
|
+
const promises = Array.from({ length: 100 }, () => db.inc('counter', 1));
|
|
512
|
+
await Promise.all(promises);
|
|
513
|
+
|
|
514
|
+
const result = await db.get('counter');
|
|
515
|
+
assert.strictEqual(result, 100, 'All increments should be applied atomically');
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
it('should handle concurrent read-modify-write operations', async function() {
|
|
519
|
+
this.timeout(5000);
|
|
520
|
+
|
|
521
|
+
await db.set('data', { counter: 0, items: [] });
|
|
522
|
+
|
|
523
|
+
// Run 50 concurrent updates that read, modify, and write
|
|
524
|
+
const promises = Array.from({ length: 50 }, (_, i) =>
|
|
525
|
+
db.upd('data', (current) => ({
|
|
526
|
+
counter: current.counter + 1,
|
|
527
|
+
items: [...current.items, `item-${current.counter}`]
|
|
528
|
+
}))
|
|
529
|
+
);
|
|
530
|
+
|
|
531
|
+
await Promise.all(promises);
|
|
532
|
+
|
|
533
|
+
const finalData = await db.get('data');
|
|
534
|
+
assert.strictEqual(finalData.counter, 50, 'Counter should be exactly 50');
|
|
535
|
+
assert.strictEqual(finalData.items.length, 50, 'Should have exactly 50 items');
|
|
536
|
+
|
|
537
|
+
// Check that all items have unique values
|
|
538
|
+
const uniqueItems = new Set(finalData.items);
|
|
539
|
+
assert.strictEqual(uniqueItems.size, 50, 'All items should be unique');
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
it('should handle concurrent sets on different keys', async function() {
|
|
543
|
+
this.timeout(5000);
|
|
544
|
+
|
|
545
|
+
// Run 50 concurrent set operations on different keys
|
|
546
|
+
const promises = Array.from({ length: 50 }, (_, i) =>
|
|
547
|
+
db.set('users', `user${i}`, { name: `User ${i}`, value: i })
|
|
548
|
+
);
|
|
549
|
+
|
|
550
|
+
await Promise.all(promises);
|
|
551
|
+
|
|
552
|
+
// Verify all keys exist and have correct values
|
|
553
|
+
const users = await db.get('users');
|
|
554
|
+
assert.strictEqual(Object.keys(users).length, 50, 'Should have 50 users');
|
|
555
|
+
|
|
556
|
+
for (let i = 0; i < 50; i++) {
|
|
557
|
+
assert.deepStrictEqual(
|
|
558
|
+
users[`user${i}`],
|
|
559
|
+
{ name: `User ${i}`, value: i },
|
|
560
|
+
`User ${i} should have correct data`
|
|
561
|
+
);
|
|
562
|
+
}
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
it('should handle concurrent add operations with unique IDs', async function() {
|
|
566
|
+
this.timeout(5000);
|
|
567
|
+
|
|
568
|
+
await db.set('items', {});
|
|
569
|
+
|
|
570
|
+
// Run 50 concurrent add operations
|
|
571
|
+
const promises = Array.from({ length: 50 }, (_, i) =>
|
|
572
|
+
db.add('items', { value: i })
|
|
573
|
+
);
|
|
574
|
+
|
|
575
|
+
const results = await Promise.all(promises);
|
|
576
|
+
|
|
577
|
+
// Verify all items were added with unique IDs
|
|
578
|
+
const items = await db.get('items');
|
|
579
|
+
assert.strictEqual(Object.keys(items).length, 50, 'Should have 50 items');
|
|
580
|
+
|
|
581
|
+
// Check that all IDs are unique
|
|
582
|
+
const ids = results.map(r => r[r.length - 1]);
|
|
583
|
+
const uniqueIds = new Set(ids);
|
|
584
|
+
assert.strictEqual(uniqueIds.size, 50, 'All IDs should be unique');
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
it('should handle concurrent decrements correctly', async function() {
|
|
588
|
+
this.timeout(5000);
|
|
589
|
+
|
|
590
|
+
await db.set('inventory', 1000);
|
|
591
|
+
|
|
592
|
+
// Run 100 concurrent decrements
|
|
593
|
+
const promises = Array.from({ length: 100 }, () => db.dec('inventory', 5));
|
|
594
|
+
await Promise.all(promises);
|
|
595
|
+
|
|
596
|
+
const result = await db.get('inventory');
|
|
597
|
+
assert.strictEqual(result, 500, 'All decrements should be applied atomically');
|
|
598
|
+
});
|
|
599
|
+
});
|
|
503
600
|
});
|
|
504
601
|
|
|
505
602
|
|