Переглянути джерело

fix(core): Fix transaction-related issues with in-memory caching

Michael Bromley 4 роки тому
батько
коміт
d35306fa26

+ 16 - 12
packages/core/src/common/self-refreshing-cache.ts

@@ -6,7 +6,7 @@ import { Logger } from '../config/logger/vendure-logger';
  * @description
  * A cache which automatically refreshes itself if the value is found to be stale.
  */
-export interface SelfRefreshingCache<V> {
+export interface SelfRefreshingCache<V, RefreshArgs extends any[] = []> {
     /**
      * @description
      * The current value of the cache. If the value is stale, the data will be refreshed and then
@@ -28,13 +28,16 @@ export interface SelfRefreshingCache<V> {
      * Force a refresh of the value, e.g. when it is known that the value has changed such as after
      * an update operation to the source data in the database.
      */
-    refresh(): Promise<V>;
+    refresh(...args: RefreshArgs): Promise<V>;
 }
 
-export interface SelfRefreshingCacheConfig<V> {
+export interface SelfRefreshingCacheConfig<V, RefreshArgs extends any[]> {
     name: string;
     ttl: number;
-    refreshFn: () => Promise<V>;
+    refresh: {
+        fn: (...args: RefreshArgs) => Promise<V>;
+        defaultArgs: RefreshArgs;
+    };
 }
 
 /**
@@ -47,18 +50,19 @@ export interface SelfRefreshingCacheConfig<V> {
  * value has expired, it will still be returned and the `refreshFn` will be triggered to update the value in the
  * background.
  */
-export async function createSelfRefreshingCache<V>(
-    config: SelfRefreshingCacheConfig<V>,
-): Promise<SelfRefreshingCache<V>> {
-    const { ttl, name, refreshFn } = config;
-    const initialValue = await refreshFn();
+export async function createSelfRefreshingCache<V, RefreshArgs extends any[]>(
+    config: SelfRefreshingCacheConfig<V, RefreshArgs>,
+): Promise<SelfRefreshingCache<V, RefreshArgs>> {
+    const { ttl, name, refresh } = config;
+    const initialValue = await refresh.fn(...refresh.defaultArgs);
     let value = initialValue;
     let expires = new Date().getTime() + ttl;
     const memoCache = new Map<string, any>();
     const hashArgs = (...args: any[]) => JSON.stringify([args, expires]);
-    const refreshValue = (): Promise<V> => {
+    const refreshValue = (...args: RefreshArgs): Promise<V> => {
         Logger.debug(`Refreshing the SelfRefreshingCache "${name}"`);
-        return refreshFn()
+        return refresh
+            .fn(...args)
             .then(newValue => {
                 value = newValue;
                 expires = new Date().getTime() + ttl;
@@ -77,7 +81,7 @@ export async function createSelfRefreshingCache<V>(
     const getValue = async (): Promise<V> => {
         const now = new Date().getTime();
         if (expires < now) {
-            return refreshValue();
+            return refreshValue(...refresh.defaultArgs);
         }
         return value;
     };

+ 4 - 4
packages/core/src/service/services/channel.service.ts

@@ -33,7 +33,7 @@ import { GlobalSettingsService } from './global-settings.service';
 
 @Injectable()
 export class ChannelService {
-    private allChannels: SelfRefreshingCache<Channel[]>;
+    private allChannels: SelfRefreshingCache<Channel[], [RequestContext]>;
 
     constructor(
         private connection: TransactionalConnection,
@@ -51,7 +51,7 @@ export class ChannelService {
         this.allChannels = await createSelfRefreshingCache({
             name: 'ChannelService.allChannels',
             ttl: this.configService.entityOptions.channelCacheTtl,
-            refreshFn: () => this.findAll(RequestContext.empty()),
+            refresh: { fn: ctx => this.findAll(ctx), defaultArgs: [RequestContext.empty()] },
         });
     }
 
@@ -174,7 +174,7 @@ export class ChannelService {
         }
         const newChannel = await this.connection.getRepository(ctx, Channel).save(channel);
         await this.customFieldRelationService.updateRelations(ctx, Channel, input, newChannel);
-        await this.allChannels.refresh();
+        await this.allChannels.refresh(ctx);
         return channel;
     }
 
@@ -207,7 +207,7 @@ export class ChannelService {
         }
         await this.connection.getRepository(ctx, Channel).save(updatedChannel, { reload: false });
         await this.customFieldRelationService.updateRelations(ctx, Channel, input, updatedChannel);
-        await this.allChannels.refresh();
+        await this.allChannels.refresh(ctx);
         return assertFound(this.findOne(ctx, channel.id));
     }
 

+ 13 - 10
packages/core/src/service/services/zone.service.ts

@@ -26,17 +26,20 @@ export class ZoneService {
     /**
      * We cache all Zones to avoid hitting the DB many times per request.
      */
-    private zones: SelfRefreshingCache<Zone[]>;
+    private zones: SelfRefreshingCache<Zone[], [RequestContext]>;
     constructor(private connection: TransactionalConnection, private configService: ConfigService) {}
 
     async initZones() {
         this.zones = await createSelfRefreshingCache({
             name: 'ZoneService.zones',
             ttl: this.configService.entityOptions.zoneCacheTtl,
-            refreshFn: () =>
-                this.connection.getRepository(Zone).find({
-                    relations: ['members'],
-                }),
+            refresh: {
+                fn: ctx =>
+                    this.connection.getRepository(ctx, Zone).find({
+                        relations: ['members'],
+                    }),
+                defaultArgs: [RequestContext.empty()],
+            },
         });
     }
 
@@ -69,7 +72,7 @@ export class ZoneService {
             zone.members = await this.getCountriesFromIds(ctx, input.memberIds);
         }
         const newZone = await this.connection.getRepository(ctx, Zone).save(zone);
-        await this.zones.refresh();
+        await this.zones.refresh(ctx);
         return assertFound(this.findOne(ctx, newZone.id));
     }
 
@@ -77,7 +80,7 @@ export class ZoneService {
         const zone = await this.connection.getEntityOrThrow(ctx, Zone, input.id);
         const updatedZone = patchEntity(zone, input);
         await this.connection.getRepository(ctx, Zone).save(updatedZone, { reload: false });
-        await this.zones.refresh();
+        await this.zones.refresh(ctx);
         return assertFound(this.findOne(ctx, zone.id));
     }
 
@@ -115,7 +118,7 @@ export class ZoneService {
             };
         } else {
             await this.connection.getRepository(ctx, Zone).remove(zone);
-            await this.zones.refresh();
+            await this.zones.refresh(ctx);
             return {
                 result: DeletionResult.DELETED,
                 message: '',
@@ -131,7 +134,7 @@ export class ZoneService {
         const members = unique(zone.members.concat(countries), 'id');
         zone.members = members;
         await this.connection.getRepository(ctx, Zone).save(zone, { reload: false });
-        await this.zones.refresh();
+        await this.zones.refresh(ctx);
         return assertFound(this.findOne(ctx, zone.id));
     }
 
@@ -144,7 +147,7 @@ export class ZoneService {
         });
         zone.members = zone.members.filter(country => !input.memberIds.includes(country.id));
         await this.connection.getRepository(ctx, Zone).save(zone, { reload: false });
-        await this.zones.refresh();
+        await this.zones.refresh(ctx);
         return assertFound(this.findOne(ctx, zone.id));
     }