{"id":19234560,"url":"https://github.com/smitelli/leroux-cache","last_synced_at":"2026-06-14T18:35:46.061Z","repository":{"id":30149995,"uuid":"33700200","full_name":"smitelli/leroux-cache","owner":"smitelli","description":"A cache object that *actually* deletes the least-recently-used items.","archived":false,"fork":false,"pushed_at":"2017-01-19T18:46:07.000Z","size":24,"stargazers_count":2,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-02-05T02:27:54.028Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/smitelli.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-04-10T00:47:20.000Z","updated_at":"2017-01-19T18:18:29.000Z","dependencies_parsed_at":"2022-07-21T20:49:09.682Z","dependency_job_id":null,"html_url":"https://github.com/smitelli/leroux-cache","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smitelli%2Fleroux-cache","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smitelli%2Fleroux-cache/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smitelli%2Fleroux-cache/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smitelli%2Fleroux-cache/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/smitelli","download_url":"https://codeload.github.com/smitelli/leroux-cache/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240317421,"owners_count":19782389,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-09T16:14:17.636Z","updated_at":"2026-06-14T18:35:41.013Z","avatar_url":"https://github.com/smitelli.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# leroux-cache\n\nA cache object that *actually* deletes the least-recently-used items.\n\nby [Scott Smitelli](mailto:scott@smitelli.com)\n\n[![Build Status](https://travis-ci.org/smitelli/leroux-cache.svg?branch=master)](https://travis-ci.org/smitelli/leroux-cache) [![Coverage Status](https://coveralls.io/repos/smitelli/leroux-cache/badge.svg?branch=master)](https://coveralls.io/r/smitelli/leroux-cache?branch=master)\n\n## Basic Usage\n\n```js\nvar cache = require('leroux-cache')();\n\ncache.set('one', \"Item One\");\ncache.set('two', {value: \"Item Two\"});\n\ncache.get('one');\n// 'Item One'\n\ncache.get('two');\n// { value: 'Item Two' }\n\ncache.del('two');\ncache.get('two');\n// undefined\n```\n\n## Initialization\n\nA cache instance can be obtained with or without the use of `new`. The following\ntwo caches are equivalent:\n\n```js\nvar leroux = require('leroux-cache'),\n    cache1 = leroux(),\n    cache2 = new leroux();\n```\n\n`cache1` and `cache2` are separate, but otherwise equivalent cache instances. No\ndata is shared between instances.\n\nAn `options` object can be passed to the initialization function. This is a\nplain Object. See the **Options** section for information on the available keys\nthat can be used here.\n\n```js\nvar cache = require('leroux-cache')({\n    maxAge  : 60 * 1000,\n    maxSize : 1000\n});\n```\n\n## Options\n\nEvery option can be set by passing an `options` object to the initialization\nfunction. Every option can also be read through the corresponding property on\na cache instance. Many of these options can also be set through the instance\nproperties, and the cache will respond accordingly.\n\nIf an attempt is made to set an option to an invalid value, the request will be\nsilently dropped and the previous value (possibly the default) will prevail.\n\n* **maxAge:** (Defaults to `null`) Can be any number greater than zero, or\n`null` to disable the behavior. The unit is milliseconds. If a cached item has\nnot been used (via the `get()` or `set()` methods) within the last `maxAge`\nmilliseconds, it will be dropped from the cache. This attribute can be read and\nwritten on the fly.\n\n* **maxSize:** (Defaults to `null`) Any number greater than zero, or `null` to\ndisable the behavior. The unit varies. If the cache's total `size` exceeds\n`maxSize` at any point, items will be dropped from the cache until the `size` is\nsmall enough again. This is a rather exceptional condition, and the choice of\nwhich items are dropped is not predictable. This attribute can be read and\nwritten on the fly.\n\n* **sizeFn:** (Defaults to a counting function where each item equals exactly\n`1`) Any JS function. This function will be invoked with a single argument, the\nvalue of a cache item. This function should return a number representing the\n\"size\" of the cache item's value. The cache will sum all of the returned sizes\nto determine the total size of the cache, and compare this to the `maxSize`\noption to determine if items need to start being dropped. This attribute can be\nread and written on the fly, and changes will result in the entire cache being\nre-measured.\n\n* **sweepDelay:** (Defaults to 2500 ms) Any number greater than zero. The unit\nis milliseconds. This option controls the interval between sweep operations.\nEach sweep operation drops expired items from the cache and makes sure that the\noverall size is not greater than `maxSize`.\n\n## Cache API\n\n* **size:** A read-only property indicating the current size of all the cache\nitems. This count may include items that have expired but not yet been swept,\nso set `sweepDelay` to occur more frequently to improve the accuracy of this\ncount.\n\n* **set(key, value):** Adds a new item to the cache, or replaces an existing\nitem. The `key` and `value` can be any type, but using an object reference as\na key may hold unwanted references that could impede garbage collection. Each\ncall to `set()` resets the last-used timestamp.\n\n* **del(key):** Removes `key` from the cache. If such an item does not exist,\nthis method is a no-op.\n\n* **reset():** Drops all cache items and resets the cache to its initial state.\nAny options that were already set will be preserved.\n\n* **get(key):** Return the stored value for `key`. If such an item does not\nexist, this method returns `undefined`. Each call to `get()` resets the\nlast-used timestamp.\n\n* **has(key):** Return `true` if the cache currently holds a value for `key`, or\n`false` otherwise.\n\n* **forEach(fn, [context]):** Invoke the supplied function for each item in the\ncache. `fn` is called with arguments (`value, key, cache`). `key` and `value`\nare self-explanatory. `cache` is a reference to the cache instance. The optional\n`context` will be used as `this` inside the invoked function. If not specified,\n`context` is a reference to the cache instance.\n\n## Theory of Operation\n\nThe cache object is a simple JS Object instance with a `null` prototype. Entries\nare keyed by the `key` supplied through the public API methods. Each entry is\nrepresented by an `Entry` instance that holds the entry's key, value, size, and\nlast-used timestamp. When an item is created or updated, the last-used timestamp\nis set to the current time, and the entry's size is computed by running the\nvalue through the `sizeFn` and taking the returned number. A running total size\ncount is kept at the cache level.\n\nWhen cache items are used via `get()`, a quick check is done to make sure that\nthe entry is not too old. If it is too old, `undefined` is returned, otherwise\nthe last-used timestamp is updated to the current time and the true value is\nreturned.\n\nThe novel part of the implementation is centered around the `Sweeper`. The\ncache, at any given time, has one sweeper instance open. Any time an entry's\nlast-used timestamp is set or updated, the entry's `key` is appended to a list\ncontained within the currently-open sweeper. During each scheduled sweep\ninterval, the current sweeper instance is taken out of service and pushed onto\nthe end of a queue. A fresh, empty `Sweeper` instance is put in its place.\n\nThis queue of `Sweepers` is consumed in a FIFO manner. Each sweeper contains\na snapshot of every cache key that was read or written during the slice of time\nwhen that particular sweeper was open. When the oldest sweeper in the queue\nbecomes older than `maxAge`, we know it's possible that some of the keys it\nreferences are at least that old too. The old sweeper is shifted off, the keys\ncontained inside are iterated over, and any of those cache entries that have\nnot been touched are deleted in one batch.\n\nThe beauty of this approach is that it consolidates all the deletions into a\nsingle tick, and spreads these ticks out over a predictable and configurable\nspan of time. Compare this to a scheme where expired entries are deleted on\n(attempted) use -- not only does it leave a lot of dead data hanging around in\nmemory that could be better used elsewhere, but it opens up the possibility to\nhave a lot of GC thrash during `get()` or `has()` operations.\n\nIs this approach measurably better? No idea. But there was no harm in trying.\n\n## Acknowledgements\n\nThis module was heavily inspired by [node-lru-cache](https://github.com/isaacs/node-lru-cache).\nWhile it doesn't maintain strict compatibility with the original API anymore, it\nshould still be pretty close.\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmitelli%2Fleroux-cache","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsmitelli%2Fleroux-cache","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmitelli%2Fleroux-cache/lists"}