diff --git a/index.js b/index.js index 36e03a3..9cc3efa 100644 --- a/index.js +++ b/index.js @@ -4,11 +4,15 @@ const kValues = require('./symbol') const stringify = require('safe-stable-stringify') const LRUCache = require('mnemonist/lru-cache') +const kCacheSize = Symbol('kCacheSize') +const kTTL = Symbol('kTTL') + class Cache { constructor (opts) { opts = opts || {} this[kValues] = {} - this._cacheSize = opts.cacheSize || 1024 + this[kCacheSize] = opts.cacheSize || 1024 + this[kTTL] = opts.ttl || 0 } define (key, opts, func) { @@ -28,23 +32,40 @@ class Cache { throw new TypeError('serialize must be a function') } - const cacheSize = opts.cacheSize || this._cacheSize + const cacheSize = opts.cacheSize || this[kCacheSize] + const ttl = opts.ttl || this[kTTL] - const wrapper = new Wrapper(func, key, serialize, cacheSize) + const wrapper = new Wrapper(func, key, serialize, cacheSize, ttl) this[kValues][key] = wrapper this[key] = wrapper.add.bind(wrapper) } } +let _currentSecond + +function currentSecond () { + if (_currentSecond !== undefined) { + return _currentSecond + } + _currentSecond = Math.floor(Date.now() / 1000) + setTimeout(_clearSecond, 1000).unref() + return _currentSecond +} + +function _clearSecond () { + _currentSecond = undefined +} + class Wrapper { - constructor (func, key, serialize, cacheSize) { + constructor (func, key, serialize, cacheSize, ttl) { this.ids = new LRUCache(cacheSize) this.error = null this.started = false this.func = func this.key = key this.serialize = serialize + this.ttl = ttl } add (args) { @@ -55,8 +76,15 @@ class Wrapper { if (!query) { query = new Query(id, args, this.func) this.ids.set(key, query) + } else if (this.ttl > 0) { + if (currentSecond() - query.lastAccessed > this.ttl) { + // restart + query.promise = this.func.call(null, args) + } } + query.touch() + return query.promise } } @@ -65,6 +93,11 @@ class Query { constructor (id, args, func) { this.id = id this.promise = func(args) + this.lastAccessed = null + } + + touch () { + this.lastAccessed = currentSecond() } } diff --git a/test.js b/test.js index 64f4219..c08355f 100644 --- a/test.js +++ b/test.js @@ -2,6 +2,9 @@ const { test } = require('tap') const { Cache } = require('.') +const { promisify } = require('util') + +const sleep = promisify(setTimeout) const kValues = require('./symbol') @@ -214,3 +217,23 @@ test('cacheSize on constructor', async (t) => { { k: 42 } ]) }) + +test('ttl', async (t) => { + t.plan(5) + + const cache = new Cache({ + ttl: 1 // seconds + }) + + cache.define('fetchSomething', async (query) => { + t.equal(query, 42) + return { k: query } + }) + + t.same(await cache.fetchSomething(42), { k: 42 }) + t.same(await cache.fetchSomething(42), { k: 42 }) + + await sleep(2000) + + t.same(await cache.fetchSomething(42), { k: 42 }) +})