Skip to content

Commit

Permalink
improve TTLCache
Browse files Browse the repository at this point in the history
  • Loading branch information
vinikjkkj committed Dec 19, 2024
1 parent 1b9a1bc commit 2c96bb0
Show file tree
Hide file tree
Showing 2 changed files with 142 additions and 124 deletions.
180 changes: 96 additions & 84 deletions src/caches/ttl.ts
Original file line number Diff line number Diff line change
@@ -1,84 +1,96 @@
import { Cache } from './cache'
import { TTLParams, Keyable } from '../types'

/**
* ### About
* This cache removes values based on TTL (Time-To-Live).
*
* If you store a key with a TTL of 1 second, that key will be removed after 1 second.
*
* ### Example
* ```typescript
* const cache = new TTLCache({maxsize: 2, ttl: 1000})
*
* //store some keys
*
* //default ttl will be used (1000)
* cache['foo'] = 'bar'
*
* //custom ttl will be used (500)
* cache.set('bar', 'foo', 500)
*
* //store another key
* cache['baz'] = 'foo'
* //throws SizeError, you need to delete some key to store another key
*
* //will log: undefined
* setTimeout(() => console.log(cache['bar']), 1000)
*
* ```
*/
export class TTLCache extends Cache {
protected _timeouts: Map<Keyable, NodeJS.Timeout>
protected _defaultTTL?: number

/**
* Creates a new TTLCache.
*/
constructor(params: TTLParams = {}){
super({
maxsize: params.maxsize,
useClones: params.useClones
})

this._timeouts = new Map()
if (params.ttl) {
this._defaultTTL = params.ttl
}
}

set(key: Keyable, value: unknown, ttl = 0){
super.set(key, value)

if (this._timeouts.has(key)) {
clearTimeout(
this._timeouts.get(key)
)

this._timeouts.delete(key)
}

if (ttl || this._defaultTTL){
this._timeouts.set(
key,
setTimeout(
() => {
this.del(key)
this.emit('expired', key)
},
ttl || this._defaultTTL
)
)
}
}

del(key: Keyable){
super.del(key)
if (this._timeouts.has(key)) {
clearTimeout(
this._timeouts.get(key)
)
this._timeouts.delete(key)
}
}
}
import { Cache } from './cache'
import { TTLParams, Keyable } from '../types'

/**
* ### About
* This cache removes values based on TTL (Time-To-Live).
*
* If you store a key with a TTL of 1 second, that key will be removed after 1 second.
*
* ### Example
* ```typescript
* const cache = new TTLCache({maxsize: 2, ttl: 1000})
*
* //store some keys
*
* //default ttl will be used (1000)
* cache['foo'] = 'bar'
*
* //custom ttl will be used (500)
* cache.set('bar', 'foo', 500)
*
* //store another key
* cache['baz'] = 'foo'
* //throws SizeError, you need to delete some key to store another key
*
* //will log: undefined
* setTimeout(() => console.log(cache['bar']), 1000)
*
* ```
*/
export class TTLCache extends Cache {
protected _timeouts: Map<Keyable, number>
protected _defaultTTL?: number
protected _checkInterval?: number
private _intervalId?: NodeJS.Timeout

/**
* Creates a new TTLCache.
*/
constructor(params: TTLParams = {}){
super({
maxsize: params.maxsize,
useClones: params.useClones
})

this._timeouts = new Map()
this._checkInterval = params.checkPeriod ?? 60000
if (params.ttl) {
this._defaultTTL = params.ttl
}

setInterval(() => this._checkExpired(), this._checkInterval)
}

set(key: Keyable, value: unknown, ttl = 0){
super.set(key, value)

if (this._timeouts.has(key)) {
this._timeouts.delete(key)
}

if (ttl || this._defaultTTL){
this._timeouts.set(
key,
Date.now() + (ttl ?? this._defaultTTL)
)
}
}

del(key: Keyable){
super.del(key)
if (this._timeouts.has(key)) {
this._timeouts.delete(key)
}
}

private _clearInterval() {
if (this._intervalId) {
clearInterval(this._intervalId)
}
}

destroy() {
this._clearInterval()
}

private _checkExpired() {
const now = Date.now()
for (const [key, expirationTime] of this._timeouts.entries()) {
if (expirationTime <= now) {
this.del(key)
this.emit('expired', key)
}
}
}
}
86 changes: 46 additions & 40 deletions src/types/params.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,46 @@
export interface CacheParams {
/**
* Defines the maximum number of keys that will be stored.
* In some caches, this parameter is required; the default is `undefined`.
*/
maxsize?: number

/**
* If true, a **deep copy** of all data will be created before storing.
* The default is `false`.
*/
useClones?: boolean
}

export interface TTLParams extends CacheParams {
/**
* The **Time to Live** of the cache in **ms**.
* If you don't provide the `ttl` parameter in the `set` function, this `ttl` will be used.
* If not provided, the cache will not delete keys.
*/
ttl?: number
}

export interface RRParams extends CacheParams {
/**
* Random logic for the deletion of keys in the cache.
* The default is `Math.floor(Math.random() * length)`.
*/
randomLogic?: (length: number) => number
}

export type ParamsLike = CacheParams | TTLParams | RRParams

export interface CachePoolParams {
/**
* Defines the maximum number of caches that will be stored.
* This parameter is optional; the default is `undefined`.
*/
maxsize?: number
}
export interface CacheParams {
/**
* Defines the maximum number of keys that will be stored.
* In some caches, this parameter is required; the default is `undefined`.
*/
maxsize?: number

/**
* If true, a **deep copy** of all data will be created before storing.
* The default is `false`.
*/
useClones?: boolean
}

export interface TTLParams extends CacheParams {
/**
* The **Time to Live** of the cache in **ms**.
* If you don't provide the `ttl` parameter in the `set` function, this `ttl` will be used.
* If not provided, the cache will not delete keys.
*/
ttl?: number

/**
* The **Check Period** to verify if key is expired or not.
* **default:** 60000 - 1 minute
*/
checkPeriod?: number
}

export interface RRParams extends CacheParams {
/**
* Random logic for the deletion of keys in the cache.
* The default is `Math.floor(Math.random() * length)`.
*/
randomLogic?: (length: number) => number
}

export type ParamsLike = CacheParams | TTLParams | RRParams

export interface CachePoolParams {
/**
* Defines the maximum number of caches that will be stored.
* This parameter is optional; the default is `undefined`.
*/
maxsize?: number
}

0 comments on commit 2c96bb0

Please sign in to comment.