Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Random replacement cache #131

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions jest/__snapshots__/bundles-snapshot.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -370,12 +370,67 @@ exports[`Dist bundle is unchanged 1`] = `
return LruMapCache;
}();

var RrMapCache = /*#__PURE__*/function () {
function RrMapCache(_temp) {
var _ref = _temp === void 0 ? {} : _temp,
cacheSize = _ref.cacheSize;

validateCacheSize(cacheSize);
this.clear();
this._cacheSize = cacheSize;
}

var _proto = RrMapCache.prototype;

_proto.set = function set(key, selectorFn) {
if (this._cache.size >= this._cacheSize) {
this._randomReplace(key, selectorFn);
} else {
this._cache.set(key, selectorFn);

this._cacheKeys[this._cache.size] = key;
}
};

_proto.get = function get(key) {
return this._cache.get(key);
};

_proto.remove = function remove(key) {
var index = this._cacheKeys.indexOf(key); // O(1)


if (index > -1) {
delete this._cache[\\"delete\\"](key);
this._cacheKeys[index] = this._cacheKeys[this._cache.size];
}
};

_proto.clear = function clear() {
this._cache = new Map();
this._cacheKeys = [];
};

_proto._randomReplace = function _randomReplace(newKey, newValue) {
var index = Math.floor(Math.random() * this._cache.size);

this._cache[\\"delete\\"](this._cacheKeys[index]);

this._cacheKeys[index] = newKey;

this._cache.set(newKey, newValue);
};

return RrMapCache;
}();

exports.FifoMapCache = FifoMapCache;
exports.FifoObjectCache = FifoObjectCache;
exports.FlatMapCache = FlatMapCache;
exports.FlatObjectCache = FlatObjectCache;
exports.LruMapCache = LruMapCache;
exports.LruObjectCache = LruObjectCache;
exports.RrMapCache = RrMapCache;
exports.createCachedSelector = createCachedSelector;
exports.createStructuredCachedSelector = createStructuredCachedSelector;
exports[\\"default\\"] = createCachedSelector;
Expand Down
4 changes: 3 additions & 1 deletion src/cache/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

## Available cache objects

`re-reselect` ships with **6 ready-to-use cache object constructors**:
`re-reselect` ships with **7 ready-to-use cache object constructors**:

| name | accepted cacheKey | type | storage |
| :---------------------------------------: | :---------------: | :-----------------------------------: | :----------------------------: |
Expand All @@ -14,6 +14,7 @@
| [`FlatMapCache`](./FlatMapCache.js) | any | flat unlimited | [Map object][docs-mozilla-map] |
| [`FifoMapCache`](./FifoMapCache.js) | any | [first in first out][docs-fifo-cache] | [Map object][docs-mozilla-map] |
| [`LruMapCache`](./LruMapCache.js) | any | [least recently used][docs-lru-cache] | [Map object][docs-mozilla-map] |
| [`RrMapCache`](./RrMapCache.js) | any | [random replacement][docs-rr-cache] | [Map object][docs-mozilla-map] |

<!-- prettier-ignore -->
```js
Expand Down Expand Up @@ -53,4 +54,5 @@ interface ICacheObject {
[wiki-strategy-pattern]: https://en.wikipedia.org/wiki/Strategy_pattern
[docs-fifo-cache]: https://en.wikipedia.org/wiki/Cache_replacement_policies#First_in_first_out_(FIFO)
[docs-lru-cache]: https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_recently_used_(LRU)
[docs-rr-cache]: https://en.wikipedia.org/wiki/Cache_replacement_policies#Random_replacement_(RR)
[docs-mozilla-map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
37 changes: 37 additions & 0 deletions src/cache/RrMapCache.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import validateCacheSize from './util/validateCacheSize';

export default class RrMapCache {
constructor({cacheSize} = {}) {
validateCacheSize(cacheSize);
this.clear();
this._cacheSize = cacheSize;
}
set(key, selectorFn) {
if (this._cache.size >= this._cacheSize) {
this._randomReplace(key, selectorFn);
} else {
this._cacheKeys[this._cache.size] = key;
this._cache.set(key, selectorFn);
}
}
get(key) {
return this._cache.get(key);
}
remove(key) {
const index = this._cacheKeys.indexOf(key); // O(1)
if (index > -1) {
delete this._cache.delete(key);
this._cacheKeys[index] = this._cacheKeys[this._cache.size];
}
}
clear() {
this._cache = new Map();
this._cacheKeys = [];
toomuchdesign marked this conversation as resolved.
Show resolved Hide resolved
}
_randomReplace(newKey, newValue) {
const index = Math.floor(Math.random() * this._cache.size);
this._cache.delete(this._cacheKeys[index]);
this._cacheKeys[index] = newKey;
this._cache.set(newKey, newValue);
}
}
12 changes: 12 additions & 0 deletions src/cache/__tests__/RrMapCache.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {RrMapCache as CacheObject} from '../../../src/index';
import testBasicBehavior from '../__util__/testBasicBehavior';
import testRrBehavior from '../__util__/testRrBehavior';
import testCacheSizeOptionValidation from '../__util__/testCacheSizeOptionValidation';
import testMapCacheKeyBehavior from '../__util__/testMapCacheKeyBehavior';

describe('RrMapCache', () => {
testBasicBehavior(CacheObject, {cacheSize: 10});
testRrBehavior(CacheObject);
testCacheSizeOptionValidation(CacheObject);
testMapCacheKeyBehavior(CacheObject, {cacheSize: 10});
});
5 changes: 5 additions & 0 deletions src/cache/__util__/testBasicBehavior.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ function testBasicBehavior(CacheObject, options) {
expect(cache.get(entry)).toBe(undefined);
});
});

it('removes non-existant keys', () => {
const cache = new CacheObject({cacheSize: 5});
expect(() => cache.remove('foo')).not.toThrow();
});
});
}

Expand Down
30 changes: 30 additions & 0 deletions src/cache/__util__/testRrBehavior.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import fillCacheWith from './fillCacheWith';

function testRrBehavior(CacheObject) {
describe('RR cache behavior', () => {
it('limits cache size by removing a random item', () => {
const cache = new CacheObject({cacheSize: 5});
const entries = [1, 2, 3, 4, 5, 6];
const get = cache.get.bind(cache);
fillCacheWith(cache, entries);
expect(entries.every(get)).toBe(false);
});

it('shrinks when removing existing items manually', () => {
const cache = new CacheObject({cacheSize: 5});
const entries = [1, 2, 3, 4, 5];
const get = cache.get.bind(cache);
fillCacheWith(cache, entries);
expect(entries.every(get)).toBe(true);
cache.remove('non-existant');
cache.remove(5);
expect(cache.get(5)).toBeUndefined();
cache.set(5, 5);
expect(entries.every(get)).toBe(true);
cache.set(6, 6);
expect(entries.every(get)).toBe(false);
});
});
}

export default testRrBehavior;
8 changes: 8 additions & 0 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4350,6 +4350,14 @@ export class LruMapCache implements ICacheObject {
clear(): void;
}

export class RrMapCache implements ICacheObject {
constructor(options: {cacheSize: number});
set(key: any, selectorFn: any): void;
get(key: any): any;
remove(key: any): void;
clear(): void;
}

/*
* Key selector creators
*/
Expand Down
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ export {default as LruObjectCache} from './cache/LruObjectCache';
export {default as FlatMapCache} from './cache/FlatMapCache';
export {default as FifoMapCache} from './cache/FifoMapCache';
export {default as LruMapCache} from './cache/LruMapCache';
export {default as RrMapCache} from './cache/RrMapCache';
Loading