event-storage 0.7.2 → 0.9.1
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 +51 -392
- package/index.js +2 -1
- package/package.json +28 -19
- package/src/Clock.js +20 -8
- package/src/Consumer.js +68 -18
- package/src/EventStore.js +305 -94
- package/src/EventStream.js +171 -17
- package/src/Index/ReadableIndex.js +33 -13
- package/src/Index/WritableIndex.js +33 -17
- package/src/IndexEntry.js +5 -1
- package/src/JoinEventStream.js +32 -30
- package/src/Partition/ReadOnlyPartition.js +1 -0
- package/src/Partition/ReadablePartition.js +201 -49
- package/src/Partition/WritablePartition.js +134 -61
- package/src/Storage/ReadOnlyStorage.js +6 -3
- package/src/Storage/ReadableStorage.js +147 -19
- package/src/Storage/WritableStorage.js +205 -27
- package/src/Watcher.js +38 -29
- package/src/WatchesFile.js +9 -8
- package/src/metadataUtil.js +79 -0
- package/src/util.js +102 -65
- package/test/Consumer.spec.js +0 -268
- package/test/EventStore.spec.js +0 -591
- package/test/EventStream.spec.js +0 -120
- package/test/Index.spec.js +0 -590
- package/test/JoinEventStream.spec.js +0 -113
- package/test/Partition.spec.js +0 -384
- package/test/Storage.spec.js +0 -955
- package/test/Watcher.spec.js +0 -131
package/test/Partition.spec.js
DELETED
|
@@ -1,384 +0,0 @@
|
|
|
1
|
-
const expect = require('expect.js');
|
|
2
|
-
const fs = require('fs-extra');
|
|
3
|
-
const Partition = require('../src/Partition');
|
|
4
|
-
|
|
5
|
-
const dataDirectory = __dirname + '/data';
|
|
6
|
-
|
|
7
|
-
describe('Partition', function() {
|
|
8
|
-
|
|
9
|
-
/** @type {WritablePartition} */
|
|
10
|
-
let partition;
|
|
11
|
-
/** @type {Array<ReadOnlyPartition>} */
|
|
12
|
-
let readers = [];
|
|
13
|
-
|
|
14
|
-
beforeEach(function () {
|
|
15
|
-
fs.emptyDirSync(dataDirectory);
|
|
16
|
-
partition = new Partition('.part', { dataDirectory, readBufferSize: 4*1024 });
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
afterEach(function () {
|
|
20
|
-
if (partition) partition.close();
|
|
21
|
-
for (let reader of readers) reader.close();
|
|
22
|
-
readers = [];
|
|
23
|
-
partition = null;
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* @returns {ReadOnlyPartition}
|
|
28
|
-
*/
|
|
29
|
-
function createReader() {
|
|
30
|
-
const reader = new Partition.ReadOnly(partition.name, { dataDirectory });
|
|
31
|
-
readers[readers.length] = reader;
|
|
32
|
-
return reader;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function fillPartition(num, documentBuilder) {
|
|
36
|
-
let lastPosition;
|
|
37
|
-
for (let i = 1; i <= num; i++) {
|
|
38
|
-
lastPosition = partition.write(documentBuilder && documentBuilder(i) || 'foobar', i);
|
|
39
|
-
}
|
|
40
|
-
partition.flush();
|
|
41
|
-
return lastPosition;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
it('creates the storage directory if it does not exist', function() {
|
|
45
|
-
fs.removeSync(dataDirectory);
|
|
46
|
-
partition = new Partition('.part', { dataDirectory });
|
|
47
|
-
expect(fs.existsSync(dataDirectory)).to.be(true);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('is not opened automatically on construct', function() {
|
|
51
|
-
expect(partition.isOpen()).to.be(false);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('does nothing on reopening', function() {
|
|
55
|
-
partition.open();
|
|
56
|
-
expect(partition.isOpen()).to.be(true);
|
|
57
|
-
expect(() => partition.open()).to.not.throwError();
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('throws when not providing partition name', function() {
|
|
61
|
-
expect(() => new Partition()).to.throwError();
|
|
62
|
-
expect(() => new Partition([])).to.throwError();
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('throws on opening non-partition file', function() {
|
|
66
|
-
let fd = fs.openSync('test/data/.part', 'w');
|
|
67
|
-
fs.writeSync(fd, 'foobar');
|
|
68
|
-
fs.closeSync(fd);
|
|
69
|
-
|
|
70
|
-
expect(() => partition.open()).to.throwError();
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('throws when mismatching header version', function() {
|
|
74
|
-
let fd = fs.openSync('test/data/.part', 'w');
|
|
75
|
-
fs.writeSync(fd, 'nesprt00');
|
|
76
|
-
fs.closeSync(fd);
|
|
77
|
-
|
|
78
|
-
expect(() => partition.open()).to.throwError();
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it('can be opened and closed multiple times', function() {
|
|
82
|
-
partition.open();
|
|
83
|
-
expect(partition.open()).to.be(true);
|
|
84
|
-
partition.close();
|
|
85
|
-
partition.close();
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
describe('write', function() {
|
|
89
|
-
|
|
90
|
-
it('returns false when partition is not open', function() {
|
|
91
|
-
expect(partition.write('foo')).to.be(false);
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it('calls callback eventually', function(done) {
|
|
95
|
-
partition.open();
|
|
96
|
-
partition.write('foo', done);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it('writes data sequentially', function() {
|
|
100
|
-
partition.open();
|
|
101
|
-
fillPartition(50, i => 'foo-' + i.toString());
|
|
102
|
-
partition.close();
|
|
103
|
-
partition.open();
|
|
104
|
-
let i = 1;
|
|
105
|
-
for (let data of partition.readAll()) {
|
|
106
|
-
expect(data).to.be('foo-' + i.toString());
|
|
107
|
-
i++;
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it('writes utf-8 data correctly', function() {
|
|
112
|
-
partition.open();
|
|
113
|
-
let doc1 = partition.write('foo-üöälß');
|
|
114
|
-
let doc2 = partition.write('bar-日本語');
|
|
115
|
-
partition.close();
|
|
116
|
-
partition.open();
|
|
117
|
-
expect(partition.readFrom(doc1)).to.be('foo-üöälß');
|
|
118
|
-
expect(partition.readFrom(doc2)).to.be('bar-日本語');
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it('returns file position of written document', function() {
|
|
122
|
-
partition.open();
|
|
123
|
-
let positions = [];
|
|
124
|
-
for (let i = 1; i <= 10; i++) {
|
|
125
|
-
positions.push(partition.write('foo-' + i.toString()));
|
|
126
|
-
}
|
|
127
|
-
partition.close();
|
|
128
|
-
partition.open();
|
|
129
|
-
for (let i = 1; i <= positions.length; i++) {
|
|
130
|
-
expect(partition.readFrom(positions[i - 1])).to.be('foo-' + i.toString());
|
|
131
|
-
}
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it('can write large documents', function(done) {
|
|
135
|
-
partition.open();
|
|
136
|
-
let blob = 'foobar'.repeat(100000);
|
|
137
|
-
partition.write(blob, () => {
|
|
138
|
-
let stat = fs.statSync('test/data/.part');
|
|
139
|
-
expect(stat.size).to.be.greaterThan(blob.length);
|
|
140
|
-
done();
|
|
141
|
-
});
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
it('works with small write buffer', function() {
|
|
145
|
-
partition = new Partition('.part', { dataDirectory, writeBufferSize: 64 });
|
|
146
|
-
partition.open();
|
|
147
|
-
fillPartition(50, i => 'foo-' + i.toString());
|
|
148
|
-
partition.close();
|
|
149
|
-
partition.open();
|
|
150
|
-
let i = 1;
|
|
151
|
-
for (let data of partition.readAll()) {
|
|
152
|
-
expect(data).to.be('foo-' + i.toString());
|
|
153
|
-
i++;
|
|
154
|
-
}
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
describe('readFrom', function() {
|
|
160
|
-
|
|
161
|
-
it('returns false when partition is not open', function() {
|
|
162
|
-
expect(partition.readFrom(0)).to.be(false);
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
it('returns false when reading beyond file size', function() {
|
|
166
|
-
partition.open();
|
|
167
|
-
partition.write('foobar');
|
|
168
|
-
expect(partition.readFrom(5000)).to.be(false);
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
it('can read unflushed documents', function() {
|
|
172
|
-
partition.open();
|
|
173
|
-
let position = partition.write('foobar');
|
|
174
|
-
expect(partition.readFrom(position, 6)).to.be('foobar');
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
it('can disable dirty reads', function() {
|
|
178
|
-
partition = new Partition('.part', { dataDirectory, dirtyReads: false });
|
|
179
|
-
partition.open();
|
|
180
|
-
let position = partition.write('foobar');
|
|
181
|
-
expect(partition.readFrom(position, 6)).to.be(false);
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
it('validates document size', function() {
|
|
185
|
-
partition.open();
|
|
186
|
-
fillPartition(10);
|
|
187
|
-
partition.close();
|
|
188
|
-
partition.open();
|
|
189
|
-
|
|
190
|
-
expect(partition.readFrom(0, 6)).to.be('foobar');
|
|
191
|
-
expect(() => partition.readFrom(0, 4)).to.throwError((e) => {
|
|
192
|
-
expect(e).to.be.a(Partition.InvalidDataSizeError);
|
|
193
|
-
});
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
it('throws when reading from an invalid position', function() {
|
|
197
|
-
partition.open();
|
|
198
|
-
fillPartition(10);
|
|
199
|
-
partition.close();
|
|
200
|
-
partition.open();
|
|
201
|
-
|
|
202
|
-
expect(() => console.log(partition.readFrom(3))).to.throwError();
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
it('throws when reading unexpected document size', function() {
|
|
206
|
-
partition.open();
|
|
207
|
-
fillPartition(10);
|
|
208
|
-
partition.close();
|
|
209
|
-
partition.open();
|
|
210
|
-
|
|
211
|
-
expect(() => partition.readFrom(4, 6)).to.throwError();
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
it('throws when an unfinished write is found', function() {
|
|
215
|
-
partition.open();
|
|
216
|
-
const position = partition.write('foobar');
|
|
217
|
-
partition.close();
|
|
218
|
-
|
|
219
|
-
let fd = fs.openSync('test/data/.part', 'r+');
|
|
220
|
-
fs.ftruncateSync(fd, partition.headerSize + position + partition.documentWriteSize('foobar'.length) - 4);
|
|
221
|
-
fs.closeSync(fd);
|
|
222
|
-
|
|
223
|
-
partition.open();
|
|
224
|
-
|
|
225
|
-
expect(() => partition.readFrom(0)).to.throwError((e) => {
|
|
226
|
-
expect(e).to.be.a(Partition.CorruptFileError);
|
|
227
|
-
});
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
it('can read more documents than fit into a single read buffer', function() {
|
|
231
|
-
partition.open();
|
|
232
|
-
fillPartition(1000);
|
|
233
|
-
partition.close();
|
|
234
|
-
partition.open();
|
|
235
|
-
|
|
236
|
-
let pos = 0;
|
|
237
|
-
for (let i = 0; i < 1000; i++) {
|
|
238
|
-
expect(partition.readFrom(pos)).to.be('foobar');
|
|
239
|
-
pos += partition.documentWriteSize('foobar'.length);
|
|
240
|
-
}
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
it('can read large documents', function() {
|
|
244
|
-
partition.open();
|
|
245
|
-
let blob = 'foobar'.repeat(100000);
|
|
246
|
-
partition.write(blob);
|
|
247
|
-
|
|
248
|
-
partition.close();
|
|
249
|
-
partition.open();
|
|
250
|
-
|
|
251
|
-
let read = partition.readFrom(0);
|
|
252
|
-
expect(read).to.be(blob);
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
describe('truncate', function() {
|
|
258
|
-
|
|
259
|
-
it('does nothing if truncating beyond filesize', function() {
|
|
260
|
-
partition.open();
|
|
261
|
-
let lastposition = fillPartition(10);
|
|
262
|
-
let size = partition.size;
|
|
263
|
-
partition.truncate(lastposition + 1000);
|
|
264
|
-
expect(partition.size).to.be(size);
|
|
265
|
-
|
|
266
|
-
partition.close();
|
|
267
|
-
partition.open();
|
|
268
|
-
expect(partition.size).to.be(size);
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
it('truncating whole file if negative position given', function() {
|
|
272
|
-
partition.open();
|
|
273
|
-
fillPartition(10);
|
|
274
|
-
partition.close();
|
|
275
|
-
partition.open();
|
|
276
|
-
|
|
277
|
-
partition.truncate(-3);
|
|
278
|
-
expect(partition.size).to.be(0);
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
it('throws when truncating outside document boundary', function() {
|
|
282
|
-
partition.open();
|
|
283
|
-
let lastposition = fillPartition(10);
|
|
284
|
-
partition.close();
|
|
285
|
-
partition.open();
|
|
286
|
-
|
|
287
|
-
expect(() => partition.truncate(lastposition - 3)).to.throwError();
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
it('truncates after the given position', function() {
|
|
291
|
-
partition.open();
|
|
292
|
-
let lastposition = fillPartition(10);
|
|
293
|
-
partition.close();
|
|
294
|
-
partition.open();
|
|
295
|
-
|
|
296
|
-
partition.truncate(lastposition);
|
|
297
|
-
expect(partition.size).to.be(lastposition);
|
|
298
|
-
|
|
299
|
-
partition.close();
|
|
300
|
-
partition.open();
|
|
301
|
-
expect(partition.size).to.be(lastposition);
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
it('correctly truncates after unflushed writes', function() {
|
|
305
|
-
partition.open();
|
|
306
|
-
let lastposition = fillPartition(10);
|
|
307
|
-
partition.truncate(lastposition);
|
|
308
|
-
expect(partition.size).to.be(lastposition);
|
|
309
|
-
|
|
310
|
-
partition.close();
|
|
311
|
-
partition.open();
|
|
312
|
-
expect(partition.size).to.be(lastposition);
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
describe('concurrency', function(){
|
|
318
|
-
|
|
319
|
-
it('allows multiple readers for a partition', function(){
|
|
320
|
-
partition.open();
|
|
321
|
-
fillPartition(10);
|
|
322
|
-
expect(partition.size).to.be.greaterThan(0);
|
|
323
|
-
|
|
324
|
-
let reader1 = createReader();
|
|
325
|
-
reader1.open();
|
|
326
|
-
expect(reader1.size).to.be(partition.size);
|
|
327
|
-
let reader2 = createReader();
|
|
328
|
-
reader2.open();
|
|
329
|
-
expect(reader2.size).to.be(partition.size);
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
it('updates reader when writer appends', function(done){
|
|
333
|
-
partition.open();
|
|
334
|
-
fillPartition(10);
|
|
335
|
-
const size = partition.size;
|
|
336
|
-
|
|
337
|
-
let reader = createReader();
|
|
338
|
-
reader.open();
|
|
339
|
-
reader.on('append', (prevSize, newSize) => {
|
|
340
|
-
expect(prevSize).to.be(size);
|
|
341
|
-
expect(newSize).to.be(partition.size);
|
|
342
|
-
done();
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
partition.write('foo');
|
|
346
|
-
expect(partition.size).to.be.greaterThan(reader.size);
|
|
347
|
-
partition.flush();
|
|
348
|
-
fs.fdatasync(partition.fd);
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
it('updates reader when writer truncates', function(done){
|
|
352
|
-
partition.open();
|
|
353
|
-
fillPartition(10);
|
|
354
|
-
const size = partition.size;
|
|
355
|
-
|
|
356
|
-
let reader = createReader();
|
|
357
|
-
reader.open();
|
|
358
|
-
reader.on('truncate', (prevSize, newSize) => {
|
|
359
|
-
expect(prevSize).to.be(size);
|
|
360
|
-
expect(newSize).to.be(partition.size);
|
|
361
|
-
reader.close();
|
|
362
|
-
done();
|
|
363
|
-
});
|
|
364
|
-
|
|
365
|
-
partition.truncate(0);
|
|
366
|
-
fs.fdatasync(partition.fd);
|
|
367
|
-
});
|
|
368
|
-
|
|
369
|
-
it('recognizes file renames and closes', function(done){
|
|
370
|
-
partition.open();
|
|
371
|
-
fillPartition(10);
|
|
372
|
-
partition.close();
|
|
373
|
-
|
|
374
|
-
let reader = createReader();
|
|
375
|
-
reader.open();
|
|
376
|
-
fs.rename(reader.fileName, reader.fileName + '2', () => {
|
|
377
|
-
setTimeout(() => {
|
|
378
|
-
expect(reader.isOpen()).to.be(false);
|
|
379
|
-
done();
|
|
380
|
-
}, 5);
|
|
381
|
-
});
|
|
382
|
-
});
|
|
383
|
-
});
|
|
384
|
-
});
|