@textile/datastore-ttl
Advanced tools
Comparing version 0.0.2 to 0.0.3
/// <reference types="node" /> | ||
import { Datastore, Key, Batch, Query } from 'interface-datastore'; | ||
export { Duration } from './duration'; | ||
import { Duration } from './duration'; | ||
export { Duration }; | ||
export interface TTLDatastoreOptions { | ||
@@ -26,3 +27,3 @@ /** | ||
constructor(ttl: number, batch: Batch<Value>, ttlOn: TTLOnFunction, ttlOff: TTLOffFunction); | ||
put(key: Key, value: Value, ttl?: number): void; | ||
put(key: Key, value: Value): void; | ||
delete(key: Key): void; | ||
@@ -43,5 +44,5 @@ commit(): Promise<void>; | ||
private meta; | ||
readonly options: TTLDatastoreOptions; | ||
private lock; | ||
private interval; | ||
readonly options: TTLDatastoreOptions; | ||
/** | ||
@@ -48,0 +49,0 @@ * TTLDatastore creates a new datastore that supports TTL expirations. |
@@ -24,6 +24,6 @@ "use strict"; | ||
const lexicographic_integer_1 = require("lexicographic-integer"); | ||
const duration_1 = require("./duration"); | ||
exports.Duration = duration_1.Duration; | ||
const ttlPrefix = new interface_datastore_1.Key('ttl'); | ||
const expPrefix = new interface_datastore_1.Key('exp'); | ||
var duration_1 = require("./duration"); | ||
exports.Duration = duration_1.Duration; | ||
class TTLBatch { | ||
@@ -38,3 +38,3 @@ constructor(ttl, batch, ttlOn, ttlOff) { | ||
} | ||
put(key, value, ttl) { | ||
put(key, value) { | ||
this.on.push(key); | ||
@@ -75,7 +75,11 @@ return this.batch.put(key, value); | ||
*/ | ||
constructor(store, meta = new datastore_core_1.NamespaceDatastore(store, ttlPrefix), options = { ttl: 0, frequency: 10000 }) { | ||
constructor(store, meta = new datastore_core_1.NamespaceDatastore(store, ttlPrefix), options) { | ||
var _a, _b; | ||
this.store = store; | ||
this.meta = meta; | ||
this.options = options; | ||
this.interval = 0; | ||
this.options = { | ||
frequency: ((_a = options) === null || _a === void 0 ? void 0 : _a.frequency) || duration_1.Duration.Second * 10, | ||
ttl: ((_b = options) === null || _b === void 0 ? void 0 : _b.ttl) || 0, | ||
}; | ||
this.lock = new async_rwlock_1.RWLock(); | ||
@@ -94,6 +98,7 @@ this.startTTL(); | ||
// @note: ttlPrefix is required 'hack' because NamespaceDatastore doesn't take this into account | ||
// @fixme: Assuming ttlPrefix is not a good idea, we should extract the prefix from the meta store | ||
const lte = ttlPrefix.child(expPrefix.child(new interface_datastore_1.Key(exp))); | ||
const query = { | ||
prefix: expPrefix.toString(), | ||
filters: [item => item.key.toString() <= lte.toString()], | ||
filters: [item => item.key.less(lte)], | ||
}; | ||
@@ -124,2 +129,3 @@ const meta = this.meta.batch(); | ||
} | ||
return; | ||
}); | ||
@@ -126,0 +132,0 @@ } |
@@ -11,6 +11,12 @@ "use strict"; | ||
}; | ||
var __asyncValues = (this && this.__asyncValues) || function (o) { | ||
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); | ||
var m = o[Symbol.asyncIterator], i; | ||
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); | ||
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } | ||
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const chai_1 = require("chai"); | ||
const interface_datastore_1 = require("interface-datastore"); | ||
const datastore_core_1 = require("datastore-core"); | ||
const index_1 = require("./index"); | ||
@@ -25,4 +31,3 @@ const sleep = (ms) => new Promise(r => setTimeout(r, ms)); | ||
setup() { | ||
const store = new interface_datastore_1.MemoryDatastore(); | ||
return new index_1.TTLDatastore(store); | ||
return new index_1.TTLDatastore(new interface_datastore_1.MemoryDatastore()); | ||
}, | ||
@@ -37,4 +42,3 @@ teardown() { | ||
beforeEach(() => { | ||
const store = new interface_datastore_1.MemoryDatastore(); | ||
ttl = new index_1.TTLDatastore(store, new datastore_core_1.NamespaceDatastore(store, new interface_datastore_1.Key('ttl')), { ttl: 100, frequency: 20 }); | ||
ttl = new index_1.TTLDatastore(new interface_datastore_1.MemoryDatastore(), undefined, { ttl: 100, frequency: 20 }); | ||
}); | ||
@@ -128,2 +132,43 @@ it('should have default options', () => { | ||
})); | ||
it('should correctly expire batched items', () => __awaiter(void 0, void 0, void 0, function* () { | ||
var e_1, _a, e_2, _b; | ||
const batch = ttl.batch(100); | ||
batch.put(key.child(new interface_datastore_1.Key('1')), value); | ||
batch.put(key.child(new interface_datastore_1.Key('2')), value); | ||
batch.put(key.child(new interface_datastore_1.Key('3')), value); | ||
yield batch.commit(); | ||
let list = []; | ||
try { | ||
for (var _c = __asyncValues(ttl.query({})), _d; _d = yield _c.next(), !_d.done;) { | ||
const { key } = _d.value; | ||
list.push(key); | ||
} | ||
} | ||
catch (e_1_1) { e_1 = { error: e_1_1 }; } | ||
finally { | ||
try { | ||
if (_d && !_d.done && (_a = _c.return)) yield _a.call(_c); | ||
} | ||
finally { if (e_1) throw e_1.error; } | ||
} | ||
chai_1.expect(list).to.have.length(3); | ||
ttl.ttl(key.child(new interface_datastore_1.Key('3')), 300); | ||
yield sleep(200); | ||
list = []; | ||
const it = ttl.query({ prefix: key.toString() }); | ||
try { | ||
for (var it_1 = __asyncValues(it), it_1_1; it_1_1 = yield it_1.next(), !it_1_1.done;) { | ||
const kv = it_1_1.value; | ||
list.push(kv.key); | ||
} | ||
} | ||
catch (e_2_1) { e_2 = { error: e_2_1 }; } | ||
finally { | ||
try { | ||
if (it_1_1 && !it_1_1.done && (_b = it_1.return)) yield _b.call(it_1); | ||
} | ||
finally { if (e_2) throw e_2.error; } | ||
} | ||
chai_1.expect(list).to.have.length(1); | ||
})); | ||
}); | ||
@@ -130,0 +175,0 @@ }); |
{ | ||
"name": "@textile/datastore-ttl", | ||
"version": "0.0.2", | ||
"version": "0.0.3", | ||
"description": "An implementation of the Datastore interface that supports a time-to-live for key-value pairs.", | ||
@@ -5,0 +5,0 @@ "main": "dist/index", |
@@ -54,3 +54,5 @@ # Time to Live Datastore _(datastore-ttl)_ | ||
```typescript | ||
import { Buffer } from 'buffer' | ||
import { MemoryDatastore, Key } from 'interface-datastore' | ||
import { TTLDatastore } from '@textile/datastore-ttl' | ||
@@ -62,6 +64,11 @@ // Use any compliant Datastore | ||
await ttl.put(key, Buffer.from('bar'), 1000) | ||
// Wait 900 milliseconds... | ||
await ttl.ttl(key, 100) // Keep alive for another 100 milliseconds | ||
// Wait 900 ms... | ||
await sleep(900) | ||
// Keep alive for another 100 ms from now | ||
await ttl.ttl(key, 100) | ||
await ttl.has(key) // true | ||
// Wait 110 milliseconds | ||
await ttl.expiration(key) // <unix-timestamp> | ||
await ttl.get(key) // <Buffer> | ||
// Wait 110 ms | ||
await sleep(110) | ||
await ttl.has(key) // false | ||
@@ -68,0 +75,0 @@ ``` |
import { expect } from 'chai' | ||
import { MemoryDatastore, Key } from 'interface-datastore' | ||
import { NamespaceDatastore } from 'datastore-core' | ||
import { TTLDatastore } from './index' | ||
@@ -15,4 +14,3 @@ | ||
setup() { | ||
const store = new MemoryDatastore() | ||
return new TTLDatastore(store) | ||
return new TTLDatastore(new MemoryDatastore()) | ||
}, | ||
@@ -27,4 +25,3 @@ teardown() { | ||
beforeEach(() => { | ||
const store = new MemoryDatastore() | ||
ttl = new TTLDatastore(store, new NamespaceDatastore(store, new Key('ttl')), { ttl: 100, frequency: 20 }) | ||
ttl = new TTLDatastore(new MemoryDatastore(), undefined, { ttl: 100, frequency: 20 }) | ||
}) | ||
@@ -116,4 +113,24 @@ it('should have default options', () => { | ||
}) | ||
it('should correctly expire batched items', async () => { | ||
const batch = ttl.batch(100) | ||
batch.put(key.child(new Key('1')), value) | ||
batch.put(key.child(new Key('2')), value) | ||
batch.put(key.child(new Key('3')), value) | ||
await batch.commit() | ||
let list = [] | ||
for await (const { key } of ttl.query({})) { | ||
list.push(key) | ||
} | ||
expect(list).to.have.length(3) | ||
ttl.ttl(key.child(new Key('3')), 300) | ||
await sleep(200) | ||
list = [] | ||
const it = ttl.query({ prefix: key.toString() }) | ||
for await (const kv of it) { | ||
list.push(kv.key) | ||
} | ||
expect(list).to.have.length(1) | ||
}) | ||
}) | ||
}) | ||
}) |
@@ -6,2 +6,3 @@ import { Buffer } from 'buffer' | ||
import { pack, unpack } from 'lexicographic-integer' | ||
import { Duration } from './duration' | ||
@@ -11,3 +12,3 @@ const ttlPrefix = new Key('ttl') | ||
export { Duration } from './duration' | ||
export { Duration } | ||
@@ -39,3 +40,3 @@ export interface TTLDatastoreOptions { | ||
) {} | ||
put(key: Key, value: Value, ttl?: number) { | ||
put(key: Key, value: Value) { | ||
this.on.push(key) | ||
@@ -67,2 +68,3 @@ return this.batch.put(key, value) | ||
private interval: number | NodeJS.Timeout = 0 | ||
readonly options: TTLDatastoreOptions | ||
/** | ||
@@ -80,4 +82,8 @@ * TTLDatastore creates a new datastore that supports TTL expirations. | ||
private meta: Datastore<Buffer> = new NamespaceDatastore(store, ttlPrefix), | ||
readonly options: TTLDatastoreOptions = { ttl: 0, frequency: 10000 }, | ||
options?: TTLDatastoreOptions, | ||
) { | ||
this.options = { | ||
frequency: options?.frequency || Duration.Second * 10, | ||
ttl: options?.ttl || 0, | ||
} | ||
this.lock = new RWLock() | ||
@@ -95,6 +101,7 @@ this.startTTL() | ||
// @note: ttlPrefix is required 'hack' because NamespaceDatastore doesn't take this into account | ||
// @fixme: Assuming ttlPrefix is not a good idea, we should extract the prefix from the meta store | ||
const lte = ttlPrefix.child(expPrefix.child(new Key(exp))) | ||
const query: Query<Buffer> = { | ||
prefix: expPrefix.toString(), | ||
filters: [item => item.key.toString() <= lte.toString()], | ||
filters: [item => item.key.less(lte)], | ||
} | ||
@@ -114,2 +121,3 @@ const meta = this.meta.batch() | ||
} | ||
return | ||
} | ||
@@ -116,0 +124,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
91108
1187
98