strapi-cache 1.8.3 → 1.8.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,7 +5,6 @@ const zlib = require("zlib");
5
5
  const rawBody = require("raw-body");
6
6
  const lruCache = require("lru-cache");
7
7
  const ioredis = require("ioredis");
8
- const iovalkey = require("iovalkey");
9
8
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
10
9
  const Stream__default = /* @__PURE__ */ _interopDefault(Stream);
11
10
  const rawBody__default = /* @__PURE__ */ _interopDefault(rawBody);
@@ -544,7 +543,8 @@ const config = {
544
543
  autoPurgeCache: true,
545
544
  autoPurgeCacheOnStart: true,
546
545
  disableAdminPopups: false,
547
- disableAdminButtons: false
546
+ disableAdminButtons: false,
547
+ redisScanDeleteCount: 100
548
548
  }),
549
549
  validator: (config2) => {
550
550
  if (typeof config2.debug !== "boolean") {
@@ -624,6 +624,9 @@ const config = {
624
624
  if (typeof config2.disableAdminButtons !== "boolean") {
625
625
  throw new Error(`Invalid config: disableAdminButtons must be a boolean`);
626
626
  }
627
+ if (typeof config2.redisScanDeleteCount !== "number") {
628
+ throw new Error(`Invalid config: redisScanDeleteCount must be a number`);
629
+ }
627
630
  }
628
631
  };
629
632
  const contentTypes = {};
@@ -856,34 +859,20 @@ class RedisCacheProvider {
856
859
  this.strapi.plugin("strapi-cache").config("cacheGetTimeoutInMs")
857
860
  );
858
861
  this.keyPrefix = this.strapi.plugin("strapi-cache").config("redisConfig")?.["keyPrefix"] ?? "";
859
- if (provider === "valkey") {
860
- if (redisClusterNodes.length) {
861
- const redisClusterOptions = this.strapi.plugin("strapi-cache").config("redisClusterOptions") ?? {};
862
- const clusterOptions = { ...redisClusterOptions };
863
- if (!clusterOptions["redisOptions"]) {
864
- clusterOptions["redisOptions"] = redisConfig;
865
- }
866
- this.client = new iovalkey.Cluster(
867
- redisClusterNodes,
868
- clusterOptions
869
- );
870
- } else {
871
- this.client = new iovalkey.Redis(redisConfig);
862
+ this.redisScanDeleteCount = Number(
863
+ this.strapi.plugin("strapi-cache").config("redisScanDeleteCount")
864
+ );
865
+ if (redisClusterNodes.length) {
866
+ const redisClusterOptions = this.strapi.plugin("strapi-cache").config("redisClusterOptions");
867
+ if (!redisClusterOptions["redisOptions"]) {
868
+ redisClusterOptions.redisOptions = redisConfig;
872
869
  }
873
- loggy.info("Valkey provider initialized");
870
+ this.client = new ioredis.Redis.Cluster(redisClusterNodes, redisClusterOptions);
874
871
  } else {
875
- if (redisClusterNodes.length) {
876
- const redisClusterOptions = this.strapi.plugin("strapi-cache").config("redisClusterOptions");
877
- if (!redisClusterOptions["redisOptions"]) {
878
- redisClusterOptions.redisOptions = redisConfig;
879
- }
880
- this.client = new ioredis.Redis.Cluster(redisClusterNodes, redisClusterOptions);
881
- } else {
882
- this.client = new ioredis.Redis(redisConfig);
883
- }
884
- loggy.info("Redis provider initialized");
872
+ this.client = new ioredis.Redis(redisConfig);
885
873
  }
886
874
  this.initialized = true;
875
+ loggy.info(`${provider === "valkey" ? "Valkey" : "Redis"} provider initialized`);
887
876
  } catch (error) {
888
877
  loggy.error(error);
889
878
  }
@@ -931,18 +920,6 @@ class RedisCacheProvider {
931
920
  return null;
932
921
  }
933
922
  }
934
- /**
935
- * Deletes all given keys in Redis pipeline.
936
- * @param keys to delete from cache
937
- */
938
- async delAll(keys) {
939
- const pipeline = this.client.pipeline();
940
- keys.forEach((key) => {
941
- const relativeKey = key.slice(this.keyPrefix.length);
942
- pipeline.del(relativeKey);
943
- });
944
- await pipeline.exec();
945
- }
946
923
  async keys() {
947
924
  if (!this.ready) return null;
948
925
  try {
@@ -972,13 +949,37 @@ class RedisCacheProvider {
972
949
  return null;
973
950
  }
974
951
  }
952
+ async deleteBatch(client, batch, regExps) {
953
+ const toDelete = batch.filter((key) => regExps.some((re) => re.test(key)));
954
+ if (toDelete.length === 0) return;
955
+ const pipeline = client.pipeline();
956
+ for (const key of toDelete) {
957
+ const relativeKey = key.startsWith(this.keyPrefix) ? key.slice(this.keyPrefix.length) : key;
958
+ pipeline.del(relativeKey);
959
+ }
960
+ await pipeline.exec();
961
+ }
962
+ scanAndDelete(client, regExps) {
963
+ return new Promise((resolve, reject) => {
964
+ const stream = client.scanStream({ match: `${this.keyPrefix}*`, count: this.redisScanDeleteCount });
965
+ stream.on("data", (batch) => {
966
+ stream.pause();
967
+ this.deleteBatch(client, batch, regExps).then(() => stream.resume()).catch(reject);
968
+ });
969
+ stream.on("end", resolve);
970
+ stream.on("error", reject);
971
+ });
972
+ }
973
+ /**
974
+ * ScanStream keys and batch delete for Redis,
975
+ * iterates over master nodes in case of Cluster.
976
+ */
975
977
  async clearByRegexp(regExps) {
976
- const keys = await this.keys();
977
- if (!keys) {
978
- return;
978
+ if (this.client instanceof ioredis.Redis) {
979
+ return this.scanAndDelete(this.client, regExps);
979
980
  }
980
- const toDelete = keys.filter((key) => regExps.some((re) => re.test(key)));
981
- await this.delAll(toDelete);
981
+ const nodes = this.client.nodes("master");
982
+ await Promise.all(nodes.map((node) => this.scanAndDelete(node, regExps)));
982
983
  }
983
984
  }
984
985
  const resolveCacheProvider = (strapi2) => {
@@ -3,8 +3,7 @@ import Stream, { Readable } from "stream";
3
3
  import { createInflate, createBrotliDecompress, createGunzip } from "zlib";
4
4
  import rawBody from "raw-body";
5
5
  import { LRUCache } from "lru-cache";
6
- import { Redis as Redis$1 } from "ioredis";
7
- import { Cluster, Redis } from "iovalkey";
6
+ import { Redis } from "ioredis";
8
7
  const loggy = {
9
8
  info: (msg) => {
10
9
  const shouldDebug = strapi.plugin("strapi-cache").config("debug") ?? false;
@@ -540,7 +539,8 @@ const config = {
540
539
  autoPurgeCache: true,
541
540
  autoPurgeCacheOnStart: true,
542
541
  disableAdminPopups: false,
543
- disableAdminButtons: false
542
+ disableAdminButtons: false,
543
+ redisScanDeleteCount: 100
544
544
  }),
545
545
  validator: (config2) => {
546
546
  if (typeof config2.debug !== "boolean") {
@@ -620,6 +620,9 @@ const config = {
620
620
  if (typeof config2.disableAdminButtons !== "boolean") {
621
621
  throw new Error(`Invalid config: disableAdminButtons must be a boolean`);
622
622
  }
623
+ if (typeof config2.redisScanDeleteCount !== "number") {
624
+ throw new Error(`Invalid config: redisScanDeleteCount must be a number`);
625
+ }
623
626
  }
624
627
  };
625
628
  const contentTypes = {};
@@ -852,34 +855,20 @@ class RedisCacheProvider {
852
855
  this.strapi.plugin("strapi-cache").config("cacheGetTimeoutInMs")
853
856
  );
854
857
  this.keyPrefix = this.strapi.plugin("strapi-cache").config("redisConfig")?.["keyPrefix"] ?? "";
855
- if (provider === "valkey") {
856
- if (redisClusterNodes.length) {
857
- const redisClusterOptions = this.strapi.plugin("strapi-cache").config("redisClusterOptions") ?? {};
858
- const clusterOptions = { ...redisClusterOptions };
859
- if (!clusterOptions["redisOptions"]) {
860
- clusterOptions["redisOptions"] = redisConfig;
861
- }
862
- this.client = new Cluster(
863
- redisClusterNodes,
864
- clusterOptions
865
- );
866
- } else {
867
- this.client = new Redis(redisConfig);
858
+ this.redisScanDeleteCount = Number(
859
+ this.strapi.plugin("strapi-cache").config("redisScanDeleteCount")
860
+ );
861
+ if (redisClusterNodes.length) {
862
+ const redisClusterOptions = this.strapi.plugin("strapi-cache").config("redisClusterOptions");
863
+ if (!redisClusterOptions["redisOptions"]) {
864
+ redisClusterOptions.redisOptions = redisConfig;
868
865
  }
869
- loggy.info("Valkey provider initialized");
866
+ this.client = new Redis.Cluster(redisClusterNodes, redisClusterOptions);
870
867
  } else {
871
- if (redisClusterNodes.length) {
872
- const redisClusterOptions = this.strapi.plugin("strapi-cache").config("redisClusterOptions");
873
- if (!redisClusterOptions["redisOptions"]) {
874
- redisClusterOptions.redisOptions = redisConfig;
875
- }
876
- this.client = new Redis$1.Cluster(redisClusterNodes, redisClusterOptions);
877
- } else {
878
- this.client = new Redis$1(redisConfig);
879
- }
880
- loggy.info("Redis provider initialized");
868
+ this.client = new Redis(redisConfig);
881
869
  }
882
870
  this.initialized = true;
871
+ loggy.info(`${provider === "valkey" ? "Valkey" : "Redis"} provider initialized`);
883
872
  } catch (error) {
884
873
  loggy.error(error);
885
874
  }
@@ -927,18 +916,6 @@ class RedisCacheProvider {
927
916
  return null;
928
917
  }
929
918
  }
930
- /**
931
- * Deletes all given keys in Redis pipeline.
932
- * @param keys to delete from cache
933
- */
934
- async delAll(keys) {
935
- const pipeline = this.client.pipeline();
936
- keys.forEach((key) => {
937
- const relativeKey = key.slice(this.keyPrefix.length);
938
- pipeline.del(relativeKey);
939
- });
940
- await pipeline.exec();
941
- }
942
919
  async keys() {
943
920
  if (!this.ready) return null;
944
921
  try {
@@ -968,13 +945,37 @@ class RedisCacheProvider {
968
945
  return null;
969
946
  }
970
947
  }
948
+ async deleteBatch(client, batch, regExps) {
949
+ const toDelete = batch.filter((key) => regExps.some((re) => re.test(key)));
950
+ if (toDelete.length === 0) return;
951
+ const pipeline = client.pipeline();
952
+ for (const key of toDelete) {
953
+ const relativeKey = key.startsWith(this.keyPrefix) ? key.slice(this.keyPrefix.length) : key;
954
+ pipeline.del(relativeKey);
955
+ }
956
+ await pipeline.exec();
957
+ }
958
+ scanAndDelete(client, regExps) {
959
+ return new Promise((resolve, reject) => {
960
+ const stream = client.scanStream({ match: `${this.keyPrefix}*`, count: this.redisScanDeleteCount });
961
+ stream.on("data", (batch) => {
962
+ stream.pause();
963
+ this.deleteBatch(client, batch, regExps).then(() => stream.resume()).catch(reject);
964
+ });
965
+ stream.on("end", resolve);
966
+ stream.on("error", reject);
967
+ });
968
+ }
969
+ /**
970
+ * ScanStream keys and batch delete for Redis,
971
+ * iterates over master nodes in case of Cluster.
972
+ */
971
973
  async clearByRegexp(regExps) {
972
- const keys = await this.keys();
973
- if (!keys) {
974
- return;
974
+ if (this.client instanceof Redis) {
975
+ return this.scanAndDelete(this.client, regExps);
975
976
  }
976
- const toDelete = keys.filter((key) => regExps.some((re) => re.test(key)));
977
- await this.delAll(toDelete);
977
+ const nodes = this.client.nodes("master");
978
+ await Promise.all(nodes.map((node) => this.scanAndDelete(node, regExps)));
978
979
  }
979
980
  }
980
981
  const resolveCacheProvider = (strapi2) => {
@@ -22,6 +22,7 @@ declare const _default: {
22
22
  autoPurgeCacheOnStart: boolean;
23
23
  disableAdminPopups: boolean;
24
24
  disableAdminButtons: boolean;
25
+ redisScanDeleteCount: number;
25
26
  };
26
27
  validator: (config: any) => void;
27
28
  };
@@ -31,6 +31,7 @@ declare const _default: {
31
31
  autoPurgeCacheOnStart: boolean;
32
32
  disableAdminPopups: boolean;
33
33
  disableAdminButtons: boolean;
34
+ redisScanDeleteCount: number;
34
35
  };
35
36
  validator: (config: any) => void;
36
37
  };
@@ -6,18 +6,20 @@ export declare class RedisCacheProvider implements CacheProvider {
6
6
  private client;
7
7
  private cacheGetTimeoutInMs;
8
8
  private keyPrefix;
9
+ private redisScanDeleteCount;
9
10
  constructor(strapi: Core.Strapi);
10
11
  init(): void;
11
12
  get ready(): boolean;
12
13
  get(key: string): Promise<any | null>;
13
14
  set(key: string, val: any): Promise<any | null>;
14
15
  del(key: string): Promise<any | null>;
15
- /**
16
- * Deletes all given keys in Redis pipeline.
17
- * @param keys to delete from cache
18
- */
19
- delAll(keys: string[]): Promise<void>;
20
16
  keys(): Promise<string[] | null>;
21
17
  reset(): Promise<any | null>;
18
+ private deleteBatch;
19
+ private scanAndDelete;
20
+ /**
21
+ * ScanStream keys and batch delete for Redis,
22
+ * iterates over master nodes in case of Cluster.
23
+ */
22
24
  clearByRegexp(regExps: RegExp[]): Promise<void>;
23
25
  }
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.8.3",
2
+ "version": "1.8.4",
3
3
  "keywords": [
4
4
  "strapi cache",
5
5
  "strapi rest cache",
@@ -42,7 +42,6 @@
42
42
  "@strapi/design-system": "^2.0.1",
43
43
  "@strapi/icons": "^2.0.1",
44
44
  "ioredis": "^5.6.1",
45
- "iovalkey": "^0.3.3",
46
45
  "lru-cache": "^11.1.0",
47
46
  "raw-body": "^3.0.0",
48
47
  "react-intl": "^7.1.10"