Source: components/client-request-cache.js

  1. /**
  2. * Caches requests in memory and handles expiring them.
  3. */
  4. class ClientRequestCache {
  5. /**
  6. * @param ttl {Number} How old a result can be before it gets expired.
  7. * @param size {Number} How many results to store before we trim.
  8. * @param requestFunc The function to use on cache miss.
  9. */
  10. constructor (ttl, size, requestFunc) {
  11. if (!Number.isInteger(ttl) || ttl <= 0) {
  12. throw Error("'ttl' must be greater than 0");
  13. }
  14. if (!Number.isInteger(size) || ttl <= 0) {
  15. throw Error("'size' must be greater than 0");
  16. }
  17. if (typeof(requestFunc) !== "function") {
  18. throw Error("'requestFunc' must be a function");
  19. }
  20. this._requestContent = new Map(); // key => {ts, content}
  21. this.requestFunc = requestFunc;
  22. this.ttl = ttl;
  23. this.maxSize = size;
  24. }
  25. /**
  26. * Gets a result of a request from the cache, or otherwise
  27. * tries to fetch the the result with this.requestFunc
  28. *
  29. * @param {string}} key Key of the item to get/store.
  30. * @param {any[]} args A set of arguments to pass to the request func.
  31. * @returns {Promise} The request, or undefined if not retrievable.
  32. * @throws {Error} If the key is not a string.
  33. */
  34. get(key, ...args) {
  35. if (typeof(key) !== "string") {
  36. throw Error("'key' must be a string");
  37. }
  38. const cachedResult = this._requestContent.get(key);
  39. if (cachedResult !== undefined && cachedResult.ts >= Date.now() - this.ttl) {
  40. return cachedResult.content;
  41. }
  42. // Delete the old req.
  43. this._requestContent.delete(key);
  44. return new Promise((resolve, reject) => {
  45. resolve(this.requestFunc.apply(null, [key].concat(args)))
  46. }).then((result) => {
  47. if (result !== undefined) {
  48. this._requestContent.set(key, {
  49. ts: Date.now(),
  50. content: result,
  51. });
  52. if (this._requestContent.size > this.maxSize) {
  53. const oldKey = this._requestContent.keys().next().value;
  54. this._requestContent.delete(oldKey);
  55. }
  56. }
  57. return result;
  58. });
  59. // Not catching here because we want to pass
  60. // through any failures.
  61. }
  62. /**
  63. * Clone the current request result cache, mapping keys to their cache records.
  64. * @returns {Map<string,any>}
  65. */
  66. getCachedResults() {
  67. return new Map(this._requestContent);
  68. }
  69. /**
  70. * @callback requestFunc
  71. * @param {any[]} args A set of arguments passed from get().
  72. * @param {string} key The key for the cached item.
  73. */
  74. }
  75. module.exports = ClientRequestCache;