Browse Source

feat(core): Expose config for internal TaxRate cache

Relates to #1856
Michael Bromley 3 years ago
parent
commit
454e9052c7

+ 1 - 0
packages/core/src/config/default-config.ts

@@ -113,6 +113,7 @@ export const defaultConfig: RuntimeVendureConfig = {
     entityOptions: {
         channelCacheTtl: 30000,
         zoneCacheTtl: 30000,
+        taxRateCacheTtl: 30000,
         metadataModifiers: [],
     },
     promotionOptions: {

+ 11 - 0
packages/core/src/config/vendure-config.ts

@@ -886,6 +886,17 @@ export interface EntityOptions {
      * @default 30000
      */
     zoneCacheTtl?: number;
+    /**
+     * @description
+     * TaxRates get cached in-memory as they are accessed very frequently. This
+     * setting determines how long the cache lives (in ms) until it is considered stale and
+     * refreshed. For multi-instance deployments (e.g. serverless, load-balanced), a
+     * smaller value here will prevent data inconsistencies between instances.
+     *
+     * @since 1.9.0
+     * @default 30000
+     */
+    taxRateCacheTtl?: number;
     /**
      * @description
      * Allows the metadata of the built-in TypeORM entities to be manipulated. This allows you

+ 3 - 0
packages/core/src/service/initializer.service.ts

@@ -11,6 +11,7 @@ import { ChannelService } from './services/channel.service';
 import { GlobalSettingsService } from './services/global-settings.service';
 import { RoleService } from './services/role.service';
 import { ShippingMethodService } from './services/shipping-method.service';
+import { TaxRateService } from './services/tax-rate.service';
 import { ZoneService } from './services/zone.service';
 
 /**
@@ -27,6 +28,7 @@ export class InitializerService {
         private administratorService: AdministratorService,
         private shippingMethodService: ShippingMethodService,
         private globalSettingsService: GlobalSettingsService,
+        private taxRateService: TaxRateService,
         private eventBus: EventBus,
     ) {}
 
@@ -44,6 +46,7 @@ export class InitializerService {
         await this.roleService.initRoles();
         await this.administratorService.initAdministrators();
         await this.shippingMethodService.initShippingMethods();
+        await this.taxRateService.initTaxRates();
         this.eventBus.publish(new InitializerEvent());
     }
 

+ 30 - 7
packages/core/src/service/services/tax-rate.service.ts

@@ -1,4 +1,4 @@
-import { CACHE_MANAGER, Inject, Injectable } from '@nestjs/common';
+import { Injectable } from '@nestjs/common';
 import {
     CreateTaxRateInput,
     DeletionResponse,
@@ -9,10 +9,11 @@ import { ID, PaginatedList } from '@vendure/common/lib/shared-types';
 
 import { RequestContext } from '../../api/common/request-context';
 import { RelationPaths } from '../../api/index';
-import { RequestContextCacheService } from '../../cache';
 import { EntityNotFoundError } from '../../common/error/errors';
+import { createSelfRefreshingCache, SelfRefreshingCache } from '../../common/index';
 import { ListQueryOptions } from '../../common/types/common-types';
 import { assertFound } from '../../common/utils';
+import { ConfigService } from '../../config/index';
 import { TransactionalConnection } from '../../connection/transactional-connection';
 import { CustomerGroup } from '../../entity/customer-group/customer-group.entity';
 import { TaxCategory } from '../../entity/tax-category/tax-category.entity';
@@ -24,8 +25,6 @@ import { TaxRateModificationEvent } from '../../event-bus/events/tax-rate-modifi
 import { ListQueryBuilder } from '../helpers/list-query-builder/list-query-builder';
 import { patchEntity } from '../helpers/utils/patch-entity';
 
-const activeTaxRatesKey = 'active-tax-rates';
-
 /**
  * @description
  * Contains methods relating to {@link TaxRate} entities.
@@ -40,14 +39,23 @@ export class TaxRateService {
         name: 'No configured tax rate',
         id: '0',
     });
+    private activeTaxRates: SelfRefreshingCache<TaxRate[], [RequestContext]>;
 
     constructor(
         private connection: TransactionalConnection,
         private eventBus: EventBus,
         private listQueryBuilder: ListQueryBuilder,
-        private cacheService: RequestContextCacheService,
+        private configService: ConfigService,
     ) {}
 
+    /**
+     * When the app is bootstrapped, ensure the tax rate cache gets created
+     * @internal
+     */
+    async initTaxRates() {
+        await this.ensureCacheExists();
+    }
+
     findAll(
         ctx: RequestContext,
         options?: ListQueryOptions<TaxRate>,
@@ -153,11 +161,11 @@ export class TaxRateService {
     }
 
     private async getActiveTaxRates(ctx: RequestContext): Promise<TaxRate[]> {
-        return this.cacheService.get(ctx, activeTaxRatesKey, () => this.findActiveTaxRates(ctx));
+        return this.activeTaxRates.value(ctx);
     }
 
     private async updateActiveTaxRates(ctx: RequestContext) {
-        this.cacheService.set(ctx, activeTaxRatesKey, await this.findActiveTaxRates(ctx));
+        await this.activeTaxRates.refresh(ctx);
     }
 
     private async findActiveTaxRates(ctx: RequestContext): Promise<TaxRate[]> {
@@ -168,4 +176,19 @@ export class TaxRateService {
             },
         });
     }
+
+    /**
+     * Ensures taxRate cache exists. If not, this method creates one.
+     */
+    private async ensureCacheExists() {
+        if (this.activeTaxRates) {
+            return;
+        }
+
+        this.activeTaxRates = await createSelfRefreshingCache({
+            name: 'TaxRateService.activeTaxRates',
+            ttl: this.configService.entityOptions.taxRateCacheTtl,
+            refresh: { fn: ctx => this.findActiveTaxRates(ctx), defaultArgs: [RequestContext.empty()] },
+        });
+    }
 }