ソースを参照

refactor(core): Replace instances of TtlCache with CacheService

Relates to #3043
Michael Bromley 1 年間 前
コミット
75feeea19b

+ 2 - 27
packages/core/src/cache/cache.service.ts

@@ -5,32 +5,7 @@ import { ConfigService } from '../config/config.service';
 import { Logger } from '../config/index';
 import { CacheStrategy, SetCacheKeyOptions } from '../config/system/cache-strategy';
 
-import { Cache } from './cache';
-
-/**
- * @description
- * Configuration for a new {@link Cache} instance.
- */
-export interface CacheConfig {
-    /**
-     * @description
-     * A function which generates a cache key from the given id.
-     * This key will be used to store the value in the cache.
-     *
-     * By convention, the key should be namespaced to avoid conflicts.
-     *
-     * @example
-     * ```ts
-     * getKey: id => `MyStrategy.getProductVariantIds.${id}`,
-     * ```
-     */
-    getKey: (id: string | number) => string;
-    /**
-     * @description
-     * Options available when setting the value in the cache.
-     */
-    options?: SetCacheKeyOptions;
-}
+import { Cache, CacheConfig } from './cache';
 
 /**
  * @description
@@ -58,7 +33,7 @@ export class CacheService {
      * @example
      * ```ts
      * const cache = cacheService.createCache({
-     *   getKey: id => `ProductVariantIds.${id}`,
+     *   getKey: id => `ProductVariantIds:${id}`,
      *   options: {
      *     ttl: 1000 * 60 * 60,
      *     tags: ['products'],

+ 28 - 1
packages/core/src/cache/cache.ts

@@ -1,6 +1,33 @@
 import { JsonCompatible } from '@vendure/common/lib/shared-types';
 
-import { CacheConfig, CacheService } from './cache.service';
+import { SetCacheKeyOptions } from '../config/system/cache-strategy';
+
+import { CacheService } from './cache.service';
+
+/**
+ * @description
+ * Configuration for a new {@link Cache} instance.
+ */
+export interface CacheConfig {
+    /**
+     * @description
+     * A function which generates a cache key from the given id.
+     * This key will be used to store the value in the cache.
+     *
+     * By convention, the key should be namespaced to avoid conflicts.
+     *
+     * @example
+     * ```ts
+     * getKey: id => `MyStrategy:getProductVariantIds:${id}`,
+     * ```
+     */
+    getKey: (id: string | number) => string;
+    /**
+     * @description
+     * Options available when setting the value in the cache.
+     */
+    options?: SetCacheKeyOptions;
+}
 
 /**
  * @description

+ 1 - 1
packages/core/src/cache/index.ts

@@ -1,3 +1,3 @@
 export * from './request-context-cache.service';
 export * from './cache.service';
-export { Cache } from './cache';
+export * from './cache';

+ 3 - 0
packages/core/src/common/ttl-cache.ts

@@ -1,8 +1,11 @@
 /**
+ * @description
  * An in-memory cache with a configurable TTL (time to live) which means cache items
  * expire after they have been in the cache longer than that time.
  *
  * The `ttl` config option is in milliseconds. Defaults to 30 seconds TTL and a cache size of 1000.
+ *
+ * @deprecated Use the CacheService instead
  */
 export class TtlCache<K, V> {
     private cache = new Map<K, { value: V; expires: number }>();

+ 16 - 13
packages/core/src/config/promotion/conditions/customer-group-condition.ts

@@ -1,18 +1,18 @@
 import { LanguageCode } from '@vendure/common/lib/generated-types';
-import { ID } from '@vendure/common/lib/shared-types';
+import ms from 'ms';
 import { Subscription } from 'rxjs';
 
-import { TtlCache } from '../../../common/ttl-cache';
+import { Cache } from '../../../cache/index';
 import { idsAreEqual } from '../../../common/utils';
 import { EventBus } from '../../../event-bus/event-bus';
 import { CustomerGroupChangeEvent } from '../../../event-bus/events/customer-group-change-event';
 import { PromotionCondition } from '../promotion-condition';
 
 let customerService: import('../../../service/services/customer.service').CustomerService;
+let cacheService: import('../../../cache/cache.service').CacheService;
 let subscription: Subscription | undefined;
 
-const fiveMinutes = 5 * 60 * 1000;
-const cache = new TtlCache<ID, ID[]>({ ttl: fiveMinutes });
+let groupIdCache: Cache;
 
 export const customerGroup = new PromotionCondition({
     code: 'customer_group',
@@ -27,16 +27,20 @@ export const customerGroup = new PromotionCondition({
     async init(injector) {
         // Lazily-imported to avoid circular dependency issues.
         const { CustomerService } = await import('../../../service/services/customer.service.js');
+        const { CacheService } = await import('../../../cache/cache.service.js');
         customerService = injector.get(CustomerService);
+        cacheService = injector.get(CacheService);
+        groupIdCache = cacheService.createCache({
+            getKey: id => `PromotionCondition:customer_group:${id}`,
+            options: { ttl: ms('1 week') },
+        });
         subscription = injector
             .get(EventBus)
             .ofType(CustomerGroupChangeEvent)
-            .subscribe(event => {
+            .subscribe(async event => {
                 // When a customer is added to or removed from a group, we need
                 // to invalidate the cache for that customer id
-                for (const customer of event.customers) {
-                    cache.delete(customer.id);
-                }
+                await groupIdCache.delete(event.customers.map(c => c.id));
             });
     },
     destroy() {
@@ -47,12 +51,11 @@ export const customerGroup = new PromotionCondition({
             return false;
         }
         const customerId = order.customer.id;
-        let groupIds = cache.get(customerId);
-        if (!groupIds) {
+        const groupIds = await groupIdCache.get(customerId, async () => {
             const groups = await customerService.getCustomerGroups(ctx, customerId);
-            groupIds = groups.map(g => g.id);
-            cache.set(customerId, groupIds);
-        }
+            return groups.map(g => g.id);
+        });
+
         return !!groupIds.find(id => idsAreEqual(id, args.customerGroupId));
     },
 });

+ 13 - 6
packages/core/src/config/shipping-method/shipping-eligibility-checker.ts

@@ -3,14 +3,15 @@ import { Json } from '@vendure/common/lib/shared-types';
 import { createHash } from 'crypto';
 
 import { RequestContext } from '../../api/common/request-context';
+import { CacheService } from '../../cache/index';
 import {
     ConfigArgs,
     ConfigArgValues,
     ConfigurableOperationDef,
     ConfigurableOperationDefOptions,
 } from '../../common/configurable-operation';
-import { TtlCache } from '../../common/ttl-cache';
-import { ShippingMethod, Order } from '../../entity';
+import { Injector } from '../../common/index';
+import { Order, ShippingMethod } from '../../entity';
 
 /**
  * @description
@@ -51,7 +52,7 @@ export class ShippingEligibilityChecker<
 > extends ConfigurableOperationDef<T> {
     private readonly checkFn: CheckShippingEligibilityCheckerFn<T>;
     private readonly shouldRunCheckFn?: ShouldRunCheckFn<T>;
-    private shouldRunCheckCache = new TtlCache({ cacheSize: 5000, ttl: 1000 * 60 * 60 * 5 });
+    private cacheService: CacheService;
 
     constructor(config: ShippingEligibilityCheckerConfig<T>) {
         super(config);
@@ -59,6 +60,11 @@ export class ShippingEligibilityChecker<
         this.shouldRunCheckFn = config.shouldRunCheck;
     }
 
+    async init(injector: Injector) {
+        await super.init(injector);
+        this.cacheService = injector.get(CacheService);
+    }
+
     /**
      * @description
      * Check the given Order to determine whether it is eligible.
@@ -86,7 +92,8 @@ export class ShippingEligibilityChecker<
         method: ShippingMethod,
     ): Promise<boolean> {
         if (typeof this.shouldRunCheckFn === 'function') {
-            const cacheKey = ctx.session?.id;
+            const cacheKey =
+                ctx.session?.id && `ShippingEligibilityChecker:shouldRunCheck:${this.code}:${ctx.session.id}`;
             if (cacheKey) {
                 const checkResult = await this.shouldRunCheckFn(
                     ctx,
@@ -97,8 +104,8 @@ export class ShippingEligibilityChecker<
                 const checkResultHash = createHash('sha1')
                     .update(JSON.stringify(checkResult))
                     .digest('base64');
-                const lastResultHash = this.shouldRunCheckCache.get(cacheKey);
-                this.shouldRunCheckCache.set(cacheKey, checkResultHash);
+                const lastResultHash = await this.cacheService.get(cacheKey);
+                await this.cacheService.set(cacheKey, checkResultHash, { ttl: 1000 * 60 * 60 * 5 });
                 if (checkResultHash === lastResultHash) {
                     return false;
                 }