@jointly/cache-candidate 1.5.3 → 1.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -0
- package/dist/index.js +1 -1
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -163,6 +163,7 @@ Please, refer to the [@jointly/cache-candidate-plugin-base](https://github.com/J
|
|
|
163
163
|
|
|
164
164
|
- [@jointly/cache-candidate-plugin-dependency-keys](https://github.com/JointlyTech/cache-candidate-plugin-dependency-keys): A plugin allowing to define dependency keys when setting the cache record.
|
|
165
165
|
This provides a mechanism to delete one or more cache records when a dependency key is invalidated.
|
|
166
|
+
- [@jointly/cache-candidate-plugin-invalidate-function](https://github.com/JointlyTech/cache-candidate-plugin-invalidate-function): A plugin providing an invalidation mechanism under specific conditions.
|
|
166
167
|
|
|
167
168
|
## Constraints
|
|
168
169
|
|
|
@@ -204,6 +205,25 @@ The cache key is composed based on the following criteria, allowing multiple fil
|
|
|
204
205
|
- `instanceIdentifier`: A uniqid generated for each instance of the class. It uses the instance properties to generate the id. (JSON.stringify)
|
|
205
206
|
- The arguments passed to the method. (JSON.stringify)
|
|
206
207
|
|
|
208
|
+
## Benchmarks
|
|
209
|
+
|
|
210
|
+
An overhead benchmark is available launching the `npm run benchmark` command.
|
|
211
|
+
The benchmark will run:
|
|
212
|
+
- A simple function without cache-candidate.
|
|
213
|
+
- A cache-candidate wrap of the same function of the first test with an unreachable requestsThreshold.
|
|
214
|
+
- A cache-candidate wrap of the same function of the first test with a candidateFunction that always returns false.
|
|
215
|
+
|
|
216
|
+
The results on a MacBook Pro M1 2021 with 16GB of RAM are:
|
|
217
|
+
|
|
218
|
+
```
|
|
219
|
+
cacheCandidate OFF - normalFunction x 47.48 ops/sec ±0.48% (74 runs sampled)
|
|
220
|
+
cacheCandidate ON - Request Threshold unreachable x 46.86 ops/sec ±0.76% (73 runs sampled)
|
|
221
|
+
cacheCandidate ON - Candidate Function set to false x 46.75 ops/sec ±0.47% (72 runs sampled)
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
The results show that the library has a very low overhead, even when the cache is not used.
|
|
225
|
+
Our suggestion therefore is to use the library on every function that could benefit from an hypothetical caching.
|
|
226
|
+
|
|
207
227
|
# Contributing
|
|
208
228
|
|
|
209
229
|
Please, refer to the [CONTRIBUTING.md](https://github.com/JointlyTech/cache-candidate/blob/main/CONTRIBUTING.md) file for more information.
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var B=Object.create;var A=Object.defineProperty;var z=Object.getOwnPropertyDescriptor;var G=Object.getOwnPropertyNames;var V=Object.getPrototypeOf,Y=Object.prototype.hasOwnProperty;var F=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),Z=(e,t)=>{for(var n in t)A(e,n,{get:t[n],enumerable:!0})},S=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of G(t))!Y.call(e,a)&&a!==n&&A(e,a,{get:()=>t[a],enumerable:!(r=z(t,a))||r.enumerable});return e};var P=(e,t,n)=>(n=e!=null?B(V(e)):{},S(t||!e||!e.__esModule?A(n,"default",{value:e,enumerable:!0}):n,e)),ee=e=>S(A({},"__esModule",{value:!0}),e);var x=F((Se,j)=>{"use strict";var H=Object.defineProperty,te=Object.getOwnPropertyDescriptor,ne=Object.getOwnPropertyNames,ae=Object.prototype.hasOwnProperty,re=(e,t)=>{for(var n in t)H(e,n,{get:t[n],enumerable:!0})},oe=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of ne(t))!ae.call(e,a)&&a!==n&&H(e,a,{get:()=>t[a],enumerable:!(r=te(t,a))||r.enumerable});return e},ie=e=>oe(H({},"__esModule",{value:!0}),e),I={};re(I,{Hooks:()=>M});j.exports=ie(I);var M=(e=>(e.SETUP="SETUP",e.INIT="INIT",e.EXECUTION_PRE="EXECUTION_PRE",e.EXECUTION_POST="EXECUTION_POST",e.DATACACHE_RECORD_ADD_PRE="DATACACHE_RECORD_ADD_PRE",e.DATACACHE_RECORD_ADD_POST="DATACACHE_RECORD_ADD_POST",e.DATACACHE_RECORD_DELETE_PRE="DATACACHE_RECORD_DELETE_PRE",e.DATACACHE_RECORD_DELETE_POST="DATACACHE_RECORD_DELETE_POST",e.CACHE_HIT="CACHE_HIT",e))(M||{})});var q=F((Ne,Q)=>{"use strict";var _=Object.defineProperty,ce=Object.getOwnPropertyDescriptor,se=Object.getOwnPropertyNames,ue=Object.prototype.hasOwnProperty,de=(e,t)=>{for(var n in t)_(e,n,{get:t[n],enumerable:!0})},le=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of se(t))!ue.call(e,a)&&a!==n&&_(e,a,{get:()=>t[a],enumerable:!(r=ce(t,a))||r.enumerable});return e},he=e=>le(_({},"__esModule",{value:!0}),e),U={};de(U,{Hook:()=>me,hook:()=>ge});Q.exports=he(U);var Ce={before:()=>{},after:()=>{}},fe={before:()=>{},after:()=>{}};function me(e){return function(t,n,r){let a={},o=r.value,{before:i,after:c}={...Ce,...e};return r.value=function(...s){i({context:a,args:s,target:t,propertyKey:n,descriptor:r});let d=o.apply(this,s);return c({context:a,args:s,target:t,propertyKey:n,descriptor:r,result:d}),d},r}}function ge(e){return function(t){let n={},{before:r,after:a}={...fe,...e};return function(...o){r({context:n,args:o});let i=t(...o);return a({context:n,args:o,result:i}),i}}}});var _e={};Z(_e,{CacheCandidate:()=>Oe,CacheCandidateOptionsDefault:()=>D,DataCacheRecordNotFound:()=>g,RunningQueryRecordNotFound:()=>R,cacheCandidate:()=>He});module.exports=ee(_e);var N=(e=new Map)=>({get:async t=>e.get(JSON.stringify(t)),has:async t=>e.has(JSON.stringify(t)),set:async(t,n)=>e.set(JSON.stringify(t),n),delete:async t=>e.delete(JSON.stringify(t))});var D={ttl:6e5,timeFrame:3e4,requestsThreshold:3,cache:N(),keepAlive:!1,plugins:[],events:{onCacheHit:e=>e,onCacheSet:e=>e,onCacheDelete:e=>e,onBeforeFunctionExecution:e=>e,onAfterFunctionExecution:e=>e}};var g=Symbol("DataCacheRecordNotFound"),R=Symbol("RunningQueryRecordNotFound");var w=P(x());var X=require("crypto");var J=P(x()),W=P(q());async function u(e,t,n){for(let r of t){let a=r.hooks.filter(i=>i.hook===e);if(a.length>1)throw new Error(`Only one hook instance per plugin is allowed. ${r.name} has ${a.length} instances of ${e}}`);if(a.length===0)continue;let o=a[0];if(pe(o))await o.action(n,r.additionalParameters);else throw new Error(`Hook ${e} for plugin ${r.name} is not actionable.`)}}function pe(e){return[e.action!==void 0,typeof e.action=="function"].every(t=>t===!0)}function $(e,t,n,r){return(0,W.hook)({before:()=>{u(e,n.plugins,r)},after:({result:a})=>{u(t,n.plugins,{...r,result:a})}})}function v({options:e}){e.plugins&&e.plugins.forEach(t=>{if(!t.hooks||!Array.isArray(t.hooks)||t.hooks.length===0)throw new Error(`Plugin ${t.name} has no hooks.`);t.hooks.forEach(n=>{if(!J.Hooks[n.hook])throw new Error(`Invalid hook ${n}`)})})}var h=P(x());function ye(e,t){return Date.now()<e+t.timeFrame}function Ee({key:e,timeframeCache:t,executionTime:n,executionEnd:r}){t.has(e)?t.get(e).push({executionTime:n,executionEnd:r}):t.set(e,[{executionEnd:r,executionTime:n}])}function Te({options:e,key:t,timeframeCache:n}){if(n.has(t)){let r=n.get(t),a=r.filter(({executionEnd:o})=>ye(o,e));r.length!==a.length&&n.set(t,a)}}function Ae({key:e,runningQueryCache:t}){return t.has(e)?t.get(e):R}function f(...e){return(0,X.createHash)("sha256").update(e.join("|")).digest("hex")}function y({birthTime:e,options:t}){return Date.now()-e>=t.ttl}async function p({options:e,key:t,HookPayload:n}){if(await e.cache.has(t)){let{result:r,birthTime:a}=await e.cache.get(t);return y({birthTime:a,options:e})?(await m({options:e,key:t,HookPayload:n}),g):r}return g}async function E({options:e,key:t,result:n,HookPayload:r}){return await u(h.Hooks.DATACACHE_RECORD_ADD_PRE,e.plugins,r),e.cache.set(t,{result:n,birthTime:Date.now()},e.ttl).then(async()=>{await u(h.Hooks.DATACACHE_RECORD_ADD_POST,e.plugins,{...r,result:n}),e.events.onCacheSet({key:t})})}async function m({options:e,key:t,HookPayload:n}){$(h.Hooks.DATACACHE_RECORD_DELETE_PRE,h.Hooks.DATACACHE_RECORD_DELETE_POST,e,n)(e.cache.delete)(t).then(()=>{e.events.onCacheDelete({key:t})})}async function L({result:e,runningQueryCache:t,key:n,executionStart:r,options:a,timeframeCache:o,timeoutCache:i,args:c,HookPayload:s}){let d=Date.now(),C=d-r;a.events.onAfterFunctionExecution({key:n,executionTime:C}),Ee({key:n,timeframeCache:o,executionTime:C,executionEnd:d}),await T({options:a,key:n,timeframeCache:o,executionTime:C,args:c})>=a.requestsThreshold?E({options:a,key:n,result:e,HookPayload:s}).finally(()=>{t.delete(n),i.set(n,setTimeout(()=>{m({options:a,key:n,HookPayload:s}),i.delete(n)},a.ttl).unref())}):t.delete(n)}async function T({options:e,key:t,timeframeCache:n,executionTime:r,args:a}){let o=0,i=n.get(t);return e.candidateFunction?o=await De(e,r,a,i):e.millisecondThreshold?o=Pe(r,e,i):o=i.length,o}function Pe(e,t,n){let r=0;return e>t.millisecondThreshold&&(r=n.filter(a=>a.executionTime>t.millisecondThreshold).length),r}async function De(e,t,n,r){let a=0;return await e.candidateFunction({timeFrameCacheRecords:r,options:e,args:n})&&(a=e.requestsThreshold),a}function Re({timeoutCache:e,key:t,options:n,HookPayload:r}){e.get(t)?.refresh()}function xe(e=10){return Math.random().toString(36).substring(2,e+2)}async function b({options:e,key:t,timeoutCache:n,runningQueryCache:r,timeframeCache:a,args:o,originalMethod:i}){let c={options:e,key:t,timeoutCache:n,runningQueryCache:r,timeframeCache:a,fnArgs:o,internals:{getDataCacheKey:f,getDataCacheRecord:p,addDataCacheRecord:E,deleteDataCacheRecord:m,isDataCacheRecordExpired:y,getExceedingAmount:T}};await u(h.Hooks.INIT,e.plugins,c);let s=await p({options:e,key:t,HookPayload:c});if(s!==g)return e.keepAlive&&Re({timeoutCache:n,key:t,options:e,HookPayload:c}),await u(h.Hooks.CACHE_HIT,e.plugins,{...c,result:s}),e.events.onCacheHit({key:t}),Promise.resolve(s);let d=Ae({key:t,runningQueryCache:r});if(d!==R)return await u(h.Hooks.CACHE_HIT,e.plugins,{...c,result:d}),d;Te({options:e,key:t,timeframeCache:a}),await u(h.Hooks.EXECUTION_PRE,e.plugins,c),e.events.onBeforeFunctionExecution({key:t});let C=Date.now(),l=i(...o);return await u(h.Hooks.EXECUTION_POST,e.plugins,{...c,result:l}),l instanceof Promise?(r.set(t,l),l.then(O=>L({result:O,runningQueryCache:r,key:t,executionStart:C,options:e,timeframeCache:a,timeoutCache:n,args:o,HookPayload:c})),l):(L({result:l,runningQueryCache:r,key:t,executionStart:C,options:e,timeframeCache:a,timeoutCache:n,args:o,HookPayload:c}),l)}function k(e){let t=new Map,n=new Map,r=xe(),a=new Map,o={...D,...e,events:{...D.events,...e.events||{}}};return{timeframeCache:t,runningQueryCache:n,uniqueIdentifier:r,timeoutCache:a,options:o}}function Oe(e={}){let{timeframeCache:t,runningQueryCache:n,uniqueIdentifier:r,timeoutCache:a,options:o}=k(e);return v({options:o}),u(w.Hooks.SETUP,o.plugins,{options:{...o,plugins:void 0},key:"",timeoutCache:a,runningQueryCache:n,timeframeCache:t,fnArgs:[],internals:{getDataCacheKey:f,getDataCacheRecord:p,addDataCacheRecord:E,deleteDataCacheRecord:m,isDataCacheRecordExpired:y,getExceedingAmount:T}}),function(c,s,d){let C=d.value;d.value=async function(...l){let O=c.constructor.name+JSON.stringify(this),K=f([s,r,O,JSON.stringify(l)]);return b({options:o,key:K,timeoutCache:a,runningQueryCache:n,timeframeCache:t,args:l,originalMethod:C})}}}function He(e,t={}){let{timeframeCache:n,runningQueryCache:r,uniqueIdentifier:a,timeoutCache:o,options:i}=k(t);return v({options:i}),u(w.Hooks.SETUP,i.plugins,{options:{...i,plugins:void 0},key:"",timeoutCache:o,runningQueryCache:r,timeframeCache:n,fnArgs:[],internals:{getDataCacheKey:f,getDataCacheRecord:p,addDataCacheRecord:E,deleteDataCacheRecord:m,isDataCacheRecordExpired:y,getExceedingAmount:T}}),function(...s){return b({options:i,key:f([a,JSON.stringify(s)]),timeoutCache:o,runningQueryCache:r,timeframeCache:n,args:s,originalMethod:e})}}0&&(module.exports={CacheCandidate,CacheCandidateOptionsDefault,DataCacheRecordNotFound,RunningQueryRecordNotFound,cacheCandidate});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jointly/cache-candidate",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "1.5.
|
|
4
|
+
"version": "1.5.5",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"dev": "jest --watch",
|
|
26
26
|
"test": "jest --no-cache",
|
|
27
27
|
"coverage": "jest --coverage",
|
|
28
|
+
"benchmark": "node benchmark.js",
|
|
28
29
|
"release:common": "npm run build && git push --follow-tags origin main && npm publish --access public",
|
|
29
30
|
"release:patch": "changelog -p && git add CHANGELOG.md && git commit -m 'docs: updated changelog' && npm version patch && npm run release:common",
|
|
30
31
|
"release:minor": "changelog -m && git add CHANGELOG.md && git commit -m 'docs: updated changelog' && npm version minor && npm run release:common",
|
|
@@ -40,6 +41,7 @@
|
|
|
40
41
|
"@types/redis": "^4.0.11",
|
|
41
42
|
"@typescript-eslint/eslint-plugin": "^5.46.0",
|
|
42
43
|
"@typescript-eslint/parser": "^5.46.0",
|
|
44
|
+
"benchmark": "^2.1.4",
|
|
43
45
|
"esbuild": "^0.16.4",
|
|
44
46
|
"eslint": "^8.29.0",
|
|
45
47
|
"generate-changelog": "^1.8.0",
|