diff --git a/di/cache/.cache.q.swo b/di/cache/.cache.q.swo new file mode 100644 index 0000000..7ccb982 Binary files /dev/null and b/di/cache/.cache.q.swo differ diff --git a/di/cache/cache.md b/di/cache/cache.md new file mode 100644 index 0000000..e69de29 diff --git a/di/cache/cache.q b/di/cache/cache.q new file mode 100644 index 0000000..2682279 --- /dev/null +++ b/di/cache/cache.q @@ -0,0 +1,95 @@ +/ Library to provide a mechanism for storing function results in a cache and returning them from the cache if they are available and non stale. + +/ return timestamp function +cp:{.z.p}; + +/ the maximum size of the cache in MB +maxsize:10; + +/ the maximum size of any individual result set in MB +maxindividual:50; + +/ make sure the maxindividual isn't bigger than maxsize +maxindividual:maxsize&maxindividual; + +MB:2 xexp 20; + +/ a table to store the cache values in memory +cache:([id:`u#`long$()] lastrun:`timestamp$();lastaccess:`timestamp$();size:`long$()); + +/ a dictionary of the functions +funcs:(`u#`long$())!(); +/ the results of the functions +results:(`u#`long$())!(); + +/ table to track the cache performance +perf:([]time:`timestamp$();id:`long$();status:`symbol$()); + +id:0j; +getid:{:id+::1}; + +/ add to cache +add:{[function;id;status] + / Don't trap the error here - if it throws an error, we want it to be propagated out + res:value function; + $[(maxindividual*MB)>size:-22!res; + / check if we need more space to store this item + [now:cp[]; + if[0>requiredsize:(maxsize*MB) - size+sum exec size from cache; evict[neg requiredsize;now]]; + / Insert to the cache table + `cache upsert (id;now;now;size); + / and insert to the function and results dictionary + funcs[id]:enlist function; + results[id]:enlist res; + / Update the performance + trackperf[id;status;now]]; + / Otherwise just log it as an addfail - the result set is too big + trackperf[id;`fail;cp[]]]; + / Return the result + res}; + +// Drop some ids from the cache +drop:{[ids] + ids,:(); + delete from `cache where id in ids; + `results : ids _ `results; + } + +// evict some items from the cache - need to clear enough space for the new item +// evict the least recently accessed items which make up the total size +// feel free to write a more intelligent cache eviction policy ! +evict:{[reqsize;currenttime] + r:select from + (update totalsize:sums size from `lastaccess xasc select lastaccess,id,size from cache) + where prev[totalsize] (now:.proc.cp[]) - r`lastrun; + // update the cache stats, return the cached result + [update lastaccess:now from `.cache.cache where id=r`id; + trackperf[r`id;`hit;now]; + first results[r`id]]; + // value found, but too old - re-run it under the same id + [drop[r`id]; + add[func;r`id;`rerun]]]]; + // it's not in the cache, so add it + add[func;getid[];`add]]} + +// get the cache performance +getperf:{update function:.cache.funcs[id] from .cache.perf} + diff --git a/di/cache/init.q b/di/cache/init.q new file mode 100644 index 0000000..7ea6d4d --- /dev/null +++ b/di/cache/init.q @@ -0,0 +1,5 @@ +/ Load core functionality into root module namespace +\l ::cache.q + +export:([add]); + diff --git a/di/cache/test.csv b/di/cache/test.csv new file mode 100644 index 0000000..e69de29