cache.service.ts 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. import { Injectable } from '@nestjs/common';
  2. import { JsonCompatible } from '@vendure/common/lib/shared-types';
  3. import { getActiveSpan } from '@vendure/telemetry';
  4. import { ConfigService } from '../config/config.service';
  5. import { Logger } from '../config/index';
  6. import { CacheStrategy, SetCacheKeyOptions } from '../config/system/cache-strategy';
  7. import { Span } from '../instrumentation';
  8. import { Cache, CacheConfig } from './cache';
  9. /**
  10. * @description
  11. * The CacheService is used to cache data in order to optimize performance.
  12. *
  13. * Internally it makes use of the configured {@link CacheStrategy} to persist
  14. * the cache into a key-value store.
  15. *
  16. * @since 3.1.0
  17. * @docsCategory cache
  18. */
  19. @Injectable()
  20. export class CacheService {
  21. protected cacheStrategy: CacheStrategy;
  22. constructor(private configService: ConfigService) {
  23. this.cacheStrategy = this.configService.systemOptions.cacheStrategy;
  24. }
  25. /**
  26. * @description
  27. * Creates a new {@link Cache} instance with the given configuration.
  28. *
  29. * The `Cache` instance provides a convenience wrapper around the `CacheService`
  30. * methods.
  31. */
  32. @Span('vendure.cache.create-cache')
  33. createCache(config: CacheConfig): Cache {
  34. const span = getActiveSpan();
  35. span?.setAttribute('cache.config', JSON.stringify(config));
  36. return new Cache(config, this);
  37. }
  38. /**
  39. * @description
  40. * Gets an item from the cache, or returns undefined if the key is not found, or the
  41. * item has expired.
  42. */
  43. @Span('vendure.cache.get')
  44. async get<T extends JsonCompatible<T>>(key: string): Promise<T | undefined> {
  45. const span = getActiveSpan();
  46. span?.setAttribute('cache.key', key);
  47. try {
  48. const result = await this.cacheStrategy.get(key);
  49. if (result) {
  50. span?.setAttribute('cache.hit', true);
  51. Logger.debug(`CacheService hit for key [${key}]`);
  52. }
  53. span?.end();
  54. return result as T;
  55. } catch (e: any) {
  56. Logger.error(`Could not get key [${key}] from CacheService`, undefined, e.stack);
  57. }
  58. }
  59. /**
  60. * @description
  61. * Sets a key-value pair in the cache. The value must be serializable, so cannot contain
  62. * things like functions, circular data structures, class instances etc.
  63. *
  64. * Optionally a "time to live" (ttl) can be specified, which means that the key will
  65. * be considered stale after that many milliseconds.
  66. */
  67. @Span('vendure.cache.set')
  68. async set<T extends JsonCompatible<T>>(
  69. key: string,
  70. value: T,
  71. options?: SetCacheKeyOptions,
  72. ): Promise<void> {
  73. const span = getActiveSpan();
  74. span?.setAttribute('cache.key', key);
  75. try {
  76. await this.cacheStrategy.set(key, value, options);
  77. span?.setAttribute('cache.set', true);
  78. span?.end();
  79. Logger.debug(`Set key [${key}] in CacheService`);
  80. } catch (e: any) {
  81. span?.setAttribute('cache.set', false);
  82. span?.end();
  83. Logger.error(`Could not set key [${key}] in CacheService`, undefined, e.stack);
  84. }
  85. }
  86. /**
  87. * @description
  88. * Deletes an item from the cache.
  89. */
  90. @Span('vendure.cache.delete')
  91. async delete(key: string): Promise<void> {
  92. const span = getActiveSpan();
  93. span?.setAttribute('cache.key', key);
  94. try {
  95. await this.cacheStrategy.delete(key);
  96. span?.setAttribute('cache.deleted', true);
  97. span?.end();
  98. Logger.debug(`Deleted key [${key}] from CacheService`);
  99. } catch (e: any) {
  100. span?.setAttribute('cache.deleted', false);
  101. span?.end();
  102. Logger.error(`Could not delete key [${key}] from CacheService`, undefined, e.stack);
  103. }
  104. }
  105. /**
  106. * @description
  107. * Deletes all items from the cache which contain at least one matching tag.
  108. */
  109. @Span('vendure.cache.invalidate-tags')
  110. async invalidateTags(tags: string[]): Promise<void> {
  111. const span = getActiveSpan();
  112. span?.setAttribute('cache.tags', tags.join(', '));
  113. try {
  114. await this.cacheStrategy.invalidateTags(tags);
  115. span?.setAttribute('cache.invalidated', true);
  116. span?.end();
  117. Logger.debug(`Invalidated tags [${tags.join(', ')}] from CacheService`);
  118. } catch (e: any) {
  119. span?.setAttribute('cache.invalidated', false);
  120. span?.end();
  121. Logger.error(
  122. `Could not invalidate tags [${tags.join(', ')}] from CacheService`,
  123. undefined,
  124. e.stack,
  125. );
  126. }
  127. }
  128. }