feat(cache-map): Promote makeLRUCacheMap into a new package#2822
feat(cache-map): Promote makeLRUCacheMap into a new package#2822
Conversation
| @@ -0,0 +1 @@ | |||
| export * from './src/cachemap.js'; | |||
There was a problem hiding this comment.
Am I doing this right?
There was a problem hiding this comment.
I dunno about endo these days, but I think agoric-sdk does it different. In general I don't like explicit types conditions in package.json, and prefer tsc to generate the .d.ts files as siblings much as possible.
@boneskull might have some more feedback here.
There was a problem hiding this comment.
I’ve commandeered the branch and aligned this with the new policy.
There was a problem hiding this comment.
And it transpires that the types.d.ts was doing a better job of exporting the types than the implicit index.d.ts so I have gone back to @gibson042’s original solution in hopes that we can find a better resolution eventually.
| // * liveTouchStats/evictedTouchStats { count, sum, mean, min, max } | ||
| // * p50/p90/p95/p99 via Ben-Haim/Tom-Tov streaming histograms |
There was a problem hiding this comment.
They’re pretty ubiquitous, to be sure.
kriskowal
left a comment
There was a problem hiding this comment.
I wish to rely on @erights review for the implementation. The packaging and compatibility story are in order.
Please change the version number to 1.0.0. When this lands, I can do a one-off publish so Agoric integration CI will continue without interruption.
mhofman
left a comment
There was a problem hiding this comment.
This looks reasonable.
Someday we'll have to make this durable compatible somehow, but we can cross that bridge then (an explicit linked list may not be appropriate in that case)
| @@ -0,0 +1 @@ | |||
| export * from './src/cachemap.js'; | |||
There was a problem hiding this comment.
I dunno about endo these days, but I think agoric-sdk does it different. In general I don't like explicit types conditions in package.json, and prefer tsc to generate the .d.ts files as siblings much as possible.
@boneskull might have some more feedback here.
Done. @kriskowal this should be ready to merge, which I'd like to leave in your hands for that initial publish. |
dfba2e1 to
582f79f
Compare
packages/cache-map/src/cachemap.js
Outdated
| throw Error('internal: Unexpected non-singular cell data entry count'); | ||
| } | ||
| const cellData = /** @type {Map<K, V>} */ (cell.data); | ||
| oldKey = /** @type {K} */ (cellData.keys().next().value); |
There was a problem hiding this comment.
Just thought of an alternative, not sure which is better
| oldKey = /** @type {K} */ (cellData.keys().next().value); | |
| ([oldKey] = cellData.keys()); |
But regardless I'm also wondering if we shouldn't just call cellData.clear() and not fall through?
There was a problem hiding this comment.
I originally had something like that, and it took me a minute to remember why I switched away so I added a comment explaining the paranoia. But I definitely want to use delete(oldKey) rather than clear(), primarily to use the bare minimum of the non-weak Map API—to which end, I've now made explicit that SingleEntryMap extends WeakMapAPI by at most size and keys.
There was a problem hiding this comment.
Thinking more of it, I would prefer if the minimum WeakMap-ish API was an optional clear() instead of a keys(). The former is technically implementable by a something that keeps weak keys, where the latter isn't.
Was there a reason to prefer keys over clear ?
There was a problem hiding this comment.
Okay, I'm convinced. Done.
3017f39 to
e4d7a80
Compare
da41c9b to
d916f3b
Compare
boneskull
left a comment
There was a problem hiding this comment.
nothing blocking. I care most about the macro type 😜
packages/cache-map/package.json
Outdated
| "name": "@endo/cache-map", | ||
| "version": "1.0.0", | ||
| "description": "bounded-size caches having WeakMap-compatible methods", | ||
| "keywords": [], |
There was a problem hiding this comment.
"cache", "lru", "weakmap", etc. 😄
| "files": [ | ||
| "test/**/*.test.*" | ||
| ], | ||
| "timeout": "2m" |
There was a problem hiding this comment.
I realize this is probably the default in skel, but I imagine this could be significantly reduced 😄
| /** | ||
| * @param {import('ava').ExecutionContext} t | ||
| * @param {object} config | ||
| * @param {any} [config.makeMap] | ||
| * @param {string} config.expectTag | ||
| * @param {[unknown, unknown, unknown]} config.keys | ||
| * @param {unknown[]} [config.badKeys] | ||
| */ |
There was a problem hiding this comment.
This should be a docstring on the function definition on L15, not on the macro itself.
suggestions:
- Might be nicer to just use
anyinstead ofunknownto avoid extra type assertions - Use
@import {x} from 'y'instead ofimport(y).x ExecutionContextis generic on a context object, so you might want to use@template [T=unknown]/ExecutionContext<T>- There are a lot of assertions here; consider using
t.plan()
There was a problem hiding this comment.
I took the middle two bullet-point suggestions, but moving the docstring produced a lot of errors that I'd rather not deal with.
|
|
||
| /** @type {WeakMapAPI<K, CacheMapCell<K, V>>} */ | ||
| const keyToCell = makeMap(); | ||
| // @ts-expect-error this sentinel head is special |
There was a problem hiding this comment.
I'd rather not; this is a singular exception.
d916f3b to
3403bee
Compare
3403bee to
0dc492e
Compare
|
@kriskowal this should again be ready to merge, possibly pending re-review from @boneskull. |
… redundant operations
adf8a20 to
9e3fa1c
Compare
Description
@endo/cache-map.getMetrics()facet for evaluating cache efficiency (currently rudimentary).Security Considerations
This package sits beneath SES, and so must be defensive without relying upon
harden/assert/etc.Scaling Considerations
The implementation remains a doubly-linked ring with permanent head and LRU eviction policy. All operations are O(1), mutating up to five nodes for promotion/insertion/removal (the target node and its former and new neighbors) but minimizing that when possible (e.g., bailing out early in
moveCellAfterwhen the target is already in the right position). Cells are created up to the specified capacity and never destroyed, although their single-entry weak maps are necessarily replaced when evicting a tail inset. Cells are currently created lazily, although that could be made eager for better memory locality (probably with a booleanlazyoption for opting out). All in all, it's probably about as good as an object-based implementation can be (and a bit better than themakeLRUCacheMapfrom which it is derived).Documentation Considerations
Includes a README.md with explanation and example usage.
Testing Considerations
Existing tests have been ported and expanded. A future improvement could add GC assertions.
Compatibility Considerations
This continues to support SES, just now as a package that can additionally be used elsewhere.
Upgrade Considerations
None known; this is a new package.