Version not found. Please check the version and try again.

@owlmeans/redis 0.1.0

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.
Files changed (46) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +517 -0
  3. package/build/.gitkeep +0 -0
  4. package/build/consts.d.ts +2 -0
  5. package/build/consts.d.ts.map +1 -0
  6. package/build/consts.js +3 -0
  7. package/build/consts.js.map +1 -0
  8. package/build/index.d.ts +4 -0
  9. package/build/index.d.ts.map +1 -0
  10. package/build/index.js +3 -0
  11. package/build/index.js.map +1 -0
  12. package/build/service.d.ts +9 -0
  13. package/build/service.d.ts.map +1 -0
  14. package/build/service.js +64 -0
  15. package/build/service.js.map +1 -0
  16. package/build/types.d.ts +6 -0
  17. package/build/types.d.ts.map +1 -0
  18. package/build/types.js +2 -0
  19. package/build/types.js.map +1 -0
  20. package/build/utils/cluster.d.ts +5 -0
  21. package/build/utils/cluster.d.ts.map +1 -0
  22. package/build/utils/cluster.js +186 -0
  23. package/build/utils/cluster.js.map +1 -0
  24. package/build/utils/config.d.ts +9 -0
  25. package/build/utils/config.d.ts.map +1 -0
  26. package/build/utils/config.js +31 -0
  27. package/build/utils/config.js.map +1 -0
  28. package/build/utils/index.d.ts +4 -0
  29. package/build/utils/index.d.ts.map +1 -0
  30. package/build/utils/index.js +4 -0
  31. package/build/utils/index.js.map +1 -0
  32. package/build/utils/instance.d.ts +4 -0
  33. package/build/utils/instance.d.ts.map +1 -0
  34. package/build/utils/instance.js +13 -0
  35. package/build/utils/instance.js.map +1 -0
  36. package/package.json +40 -0
  37. package/src/consts.ts +3 -0
  38. package/src/index.ts +4 -0
  39. package/src/service.ts +94 -0
  40. package/src/types.ts +6 -0
  41. package/src/utils/cluster.ts +215 -0
  42. package/src/utils/config.ts +35 -0
  43. package/src/utils/index.ts +4 -0
  44. package/src/utils/instance.ts +16 -0
  45. package/tsconfig.json +14 -0
  46. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cluster.d.ts","sourceRoot":"","sources":["../../src/utils/cluster.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAIjC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAE5C,eAAO,MAAM,aAAa,WAAkB,QAAQ,CAAC,SAAS,CAAC,KAAG,OAAO,CAAC,OAAO,CAuLhF,CAAA"}
@@ -0,0 +1,186 @@
1
+ import { Cluster } from 'ioredis';
2
+ import { prepareClusterRedisOptions } from './config.js';
3
+ import { createClient } from './instance.js';
4
+ export const ensuerCluster = async (config) => {
5
+ if (!Array.isArray(config.host)) {
6
+ throw new SyntaxError('We may connect to redis cluster only knowing its nodes');
7
+ }
8
+ const setup = prepareClusterRedisOptions(config);
9
+ // 1. Create single clients for testing
10
+ const clients = await Promise.all(setup.nodes.map(async (node) => {
11
+ if (typeof node !== 'object') {
12
+ throw new SyntaxError('Cluster node must be an object after cluster options initialization');
13
+ }
14
+ if (node.host == null) {
15
+ throw new SyntaxError('Cluster node must have a host property after cluster options initialization');
16
+ }
17
+ const client = await createClient({ ...config, host: node.host });
18
+ return client;
19
+ }));
20
+ // 2. Test clients and ensure they are configured
21
+ try {
22
+ const masters = [];
23
+ const slaves = [];
24
+ const masterQty = config.meta?.masterNumber
25
+ ?? Math.round(clients.length / (1 + (config.meta?.slaveNumber ?? 2)));
26
+ const slaveQty = config.meta?.slaveNumber ??
27
+ Math.round((clients.length - masterQty) / masterQty);
28
+ const requiredNodes = (slaveQty + 1) * masterQty;
29
+ if (requiredNodes !== clients.length) {
30
+ throw new SyntaxError(`Number of nodes in redis cluster does not match the configuration requirements: ${clients.length} / ${requiredNodes}`);
31
+ }
32
+ for (const client of clients) {
33
+ let nodesInfo = await client.cluster('NODES');
34
+ let nodes = parseClusterNodeInfo(nodesInfo);
35
+ // Remove nodes that we don't nkow
36
+ for (const node of nodes) {
37
+ // We do not remove ourselves
38
+ if (node.flags.includes('myself')) {
39
+ continue;
40
+ }
41
+ if (!config.host.includes(node.addr)) {
42
+ await client.cluster('FORGET', node.nodeId);
43
+ }
44
+ }
45
+ const otherNodes = nodes.filter(node => !node.flags.includes('myself'));
46
+ // Add nodes that are missing
47
+ for (const host of config.host) {
48
+ if (!otherNodes.some(node => node.addr === host)) {
49
+ await client.cluster('MEET', host, config.port ?? 6379);
50
+ }
51
+ }
52
+ client.disconnect(true);
53
+ nodesInfo = await client.cluster('NODES');
54
+ nodes = parseClusterNodeInfo(nodesInfo);
55
+ const myself = nodes.find(node => node.flags.includes('myself'));
56
+ if (myself == null) {
57
+ throw new SyntaxError('Cluster node does not contain information about itself');
58
+ }
59
+ if (myself.flags.includes('master')) {
60
+ masters.push({ info: myself, node: client });
61
+ }
62
+ else {
63
+ slaves.push({ info: myself, node: client });
64
+ }
65
+ }
66
+ await Promise.all(clients.map(client => client.disconnect(true)));
67
+ const realMasters = masters.filter(master => master.info.slots != null);
68
+ const newcommers = masters.filter(master => master.info.slots == null);
69
+ const slotsChunk = Math.floor(16384 / masterQty);
70
+ const slotsSurplus = 16384 % masterQty;
71
+ const resetNode = async (node, flush = false) => {
72
+ flush && await node.node.flushall();
73
+ await node.node.cluster('RESET');
74
+ node.info.slots = null;
75
+ node.info.master = '-';
76
+ newcommers.push(node);
77
+ };
78
+ const configureMaster = async (node, idx) => {
79
+ const chunk = idx + 1 == masterQty ? slotsChunk + slotsSurplus : slotsChunk;
80
+ const firstSlot = idx * slotsChunk;
81
+ const lastSlot = firstSlot + chunk - 1;
82
+ await node.node.flushall();
83
+ await node.node.cluster('RESET');
84
+ await node.node.cluster('ADDSLOTSRANGE', firstSlot, lastSlot);
85
+ node.info.slots = [[firstSlot, lastSlot]];
86
+ const nodeSlaves = slaves.filter(slave => slave.info.master === node.info.nodeId);
87
+ while (nodeSlaves.length > slaveQty) {
88
+ const slave = nodeSlaves.pop();
89
+ if (slave == null) {
90
+ throw new SyntaxError('Not enough slaves to forget in redis cluster');
91
+ }
92
+ slaves.splice(slaves.indexOf(slave), 1);
93
+ await resetNode(slave);
94
+ }
95
+ };
96
+ if (realMasters.length === 0) {
97
+ for (let i = 0; i < masterQty; ++i) {
98
+ const newcommer = newcommers.shift();
99
+ if (newcommer == null) {
100
+ throw new SyntaxError('(clean) Not enough nodes to add to redis cluster as master');
101
+ }
102
+ await configureMaster(newcommer, i);
103
+ realMasters.push(newcommer);
104
+ }
105
+ }
106
+ else if (realMasters.length !== masterQty) {
107
+ while (realMasters.length > masterQty) {
108
+ const renegade = realMasters.pop();
109
+ if (renegade == null) {
110
+ throw new SyntaxError('Not enough nodes to forget in redis cluster');
111
+ }
112
+ const renegadeSlaves = slaves.filter(slave => slave.info.master === renegade.info.nodeId);
113
+ for (const slave of renegadeSlaves) {
114
+ slaves.splice(slaves.indexOf(slave), 1);
115
+ await resetNode(slave);
116
+ }
117
+ await resetNode(renegade, true);
118
+ }
119
+ while (realMasters.length < masterQty) {
120
+ const newcommer = newcommers.shift();
121
+ if (newcommer == null) {
122
+ throw new SyntaxError('(dirty) Not enough nodes to add to redis cluster as master');
123
+ }
124
+ realMasters.push(newcommer);
125
+ }
126
+ for (let i = 0; i < masterQty; ++i) {
127
+ await configureMaster(realMasters[i], i);
128
+ }
129
+ }
130
+ if (slaves.length !== slaveQty * masterQty) {
131
+ for (let master of realMasters) {
132
+ const masterSlaves = slaves.filter(slave => slave.info.master === master.info.nodeId);
133
+ while (masterSlaves.length > slaveQty) {
134
+ const slave = masterSlaves.pop();
135
+ if (slave == null) {
136
+ throw new SyntaxError('Not enough slaves to forget in redis cluster');
137
+ }
138
+ slaves.splice(slaves.indexOf(slave), 1);
139
+ await resetNode(slave);
140
+ }
141
+ while (masterSlaves.length < slaveQty) {
142
+ const newcommer = newcommers.shift();
143
+ if (newcommer == null) {
144
+ throw new SyntaxError('Not enough slaves to add to redis cluster');
145
+ }
146
+ newcommer.info.master = master.info.nodeId;
147
+ await newcommer.node.cluster('REPLICATE', master.info.nodeId);
148
+ masterSlaves.push(newcommer);
149
+ slaves.push(newcommer);
150
+ }
151
+ }
152
+ }
153
+ if (newcommers.length !== 0) {
154
+ throw new SyntaxError('There are nodes that are not configured in redis cluster');
155
+ }
156
+ if (realMasters.length !== masterQty) {
157
+ throw new SyntaxError('Not enough master nodes in redis cluster after configuration');
158
+ }
159
+ if (slaves.length !== slaveQty * masterQty) {
160
+ throw new SyntaxError('Not enough slave nodes in redis cluster after configuration');
161
+ }
162
+ }
163
+ catch (e) {
164
+ console.error(e);
165
+ throw e;
166
+ }
167
+ // 3. Disconnect single clients
168
+ await Promise.all(clients.map(client => client.disconnect()));
169
+ // 4. Connect to cluster
170
+ return new Cluster(setup.nodes, setup.options);
171
+ };
172
+ const parseClusterNodeInfo = (clusterNodes) => {
173
+ const nodesInfo = clusterNodes.split('\n').filter(line => line.trim() !== '');
174
+ return nodesInfo.map(line => {
175
+ const [nodeId, addr, flags, master, , , , state, ...slots] = line.split(' ');
176
+ return {
177
+ nodeId,
178
+ addr: addr.trim().split(':')[0],
179
+ flags: flags.trim().split(','),
180
+ master,
181
+ state,
182
+ slots: slots != null && slots.length > 0 ? slots.map(slot => (slot.includes('-') ? slot.split('-', 2) : [slot, slot]).map(v => parseInt(v))) : null
183
+ };
184
+ });
185
+ };
186
+ //# sourceMappingURL=cluster.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cluster.js","sourceRoot":"","sources":["../../src/utils/cluster.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { DbConfig } from '@owlmeans/resource';
2
+ import type { RedisOptions, ClusterNode, ClusterOptions } from 'ioredis';
3
+ import type { RedisMeta } from '../types.js';
4
+ export declare const prepareSingleRedisOptions: (config: DbConfig<RedisMeta>, host?: string) => RedisOptions;
5
+ export declare const prepareClusterRedisOptions: (config: DbConfig<RedisMeta>) => {
6
+ nodes: ClusterNode[];
7
+ options: ClusterOptions;
8
+ };
9
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AACxE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAE5C,eAAO,MAAM,yBAAyB,WAAY,QAAQ,CAAC,SAAS,CAAC,SAAS,MAAM,KAAG,YAWtF,CAAA;AAED,eAAO,MAAM,0BAA0B,WAAY,QAAQ,CAAC,SAAS,CAAC,KAAG;IAAE,KAAK,EAAE,WAAW,EAAE,CAAC;IAAC,OAAO,EAAE,cAAc,CAAA;CAiBvH,CAAA"}
@@ -0,0 +1,31 @@
1
+ export const prepareSingleRedisOptions = (config, host) => {
2
+ host = (host != null ? host : config.host);
3
+ if (typeof host !== 'string') {
4
+ throw new SyntaxError('Single redis options can be created only from config referencing single host');
5
+ }
6
+ return {
7
+ host: host,
8
+ port: config.port ?? 6379,
9
+ password: config.secret,
10
+ ...config.meta
11
+ };
12
+ };
13
+ export const prepareClusterRedisOptions = (config) => {
14
+ if (!Array.isArray(config.host)) {
15
+ throw new SyntaxError('Cluster redis options can be created only from config referencing multiple hosts');
16
+ }
17
+ return {
18
+ nodes: config.host.map(host => ({ host, port: config.port })), options: {
19
+ dnsLookup: (address, callback) => {
20
+ callback(null, address);
21
+ },
22
+ slotsRefreshTimeout: 20000,
23
+ redisOptions: {
24
+ // tls: { rejectUnauthorized: false }, // @TODO check if it's working
25
+ password: config.secret,
26
+ ...config.meta
27
+ }
28
+ }
29
+ };
30
+ };
31
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,MAA2B,EAAE,IAAa,EAAgB,EAAE;IACpG,IAAI,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAW,CAAA;IACpD,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,MAAM,IAAI,WAAW,CAAC,8EAA8E,CAAC,CAAA;IACvG,CAAC;IACD,OAAO;QACL,IAAI,EAAE,IAAI;QACV,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,IAAI;QACzB,QAAQ,EAAE,MAAM,CAAC,MAAM;QACvB,GAAG,MAAM,CAAC,IAAI;KACf,CAAA;AACH,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,MAA2B,EAAqD,EAAE;IAC3H,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,WAAW,CAAC,kFAAkF,CAAC,CAAA;IAC3G,CAAC;IACD,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE;YACtE,SAAS,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE;gBAC/B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;YACzB,CAAC;YACD,mBAAmB,EAAE,KAAK;YAC1B,YAAY,EAAE;gBACZ,qEAAqE;gBACrE,QAAQ,EAAE,MAAM,CAAC,MAAM;gBACvB,GAAG,MAAM,CAAC,IAAI;aACf;SACF;KACF,CAAA;AACH,CAAC,CAAA"}
@@ -0,0 +1,4 @@
1
+ export * from './cluster.js';
2
+ export * from './config.js';
3
+ export * from './instance.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AACA,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA"}
@@ -0,0 +1,4 @@
1
+ export * from './cluster.js';
2
+ export * from './config.js';
3
+ export * from './instance.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AACA,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA"}
@@ -0,0 +1,4 @@
1
+ import type { DbConfig } from '@owlmeans/resource';
2
+ import type { RedisClient } from '@owlmeans/redis-resource';
3
+ export declare const createClient: (config: DbConfig) => Promise<RedisClient>;
4
+ //# sourceMappingURL=instance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instance.d.ts","sourceRoot":"","sources":["../../src/utils/instance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAK3D,eAAO,MAAM,YAAY,WAAkB,QAAQ,KAAG,OAAO,CAAC,WAAW,CASxE,CAAA"}
@@ -0,0 +1,13 @@
1
+ import { Redis } from 'ioredis';
2
+ import { prepareSingleRedisOptions } from './config.js';
3
+ import { ensuerCluster } from './cluster.js';
4
+ export const createClient = async (config) => {
5
+ if (Array.isArray(config.host) && config.host.length === 1) {
6
+ config.host = config.host[0];
7
+ }
8
+ if (typeof config.host === 'string') {
9
+ return new Redis(prepareSingleRedisOptions(config));
10
+ }
11
+ return await ensuerCluster(config);
12
+ };
13
+ //# sourceMappingURL=instance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instance.js","sourceRoot":"","sources":["../../src/utils/instance.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,KAAK,EAAC,MAAM,SAAS,CAAA;AAC7B,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAA;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAE5C,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAAE,MAAgB,EAAwB,EAAE;IAC3E,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3D,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IAC9B,CAAC;IACD,IAAI,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QACpC,OAAO,IAAI,KAAK,CAAC,yBAAyB,CAAC,MAAM,CAAC,CAAC,CAAA;IACrD,CAAC;IAED,OAAO,MAAM,aAAa,CAAC,MAAM,CAAC,CAAA;AACpC,CAAC,CAAA"}
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@owlmeans/redis",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "scripts": {
6
+ "build": "tsc -b",
7
+ "dev": "sleep 240 && nodemon -e ts,tsx,json --watch src --exec \"tsc -p ./tsconfig.json\"",
8
+ "watch": "tsc -b -w --preserveWatchOutput --pretty"
9
+ },
10
+ "main": "build/index.js",
11
+ "module": "build/index.js",
12
+ "types": "build/index.d.ts",
13
+ "exports": {
14
+ ".": {
15
+ "import": "./build/index.js",
16
+ "require": "./build/index.js",
17
+ "default": "./build/index.js",
18
+ "module": "./build/index.js",
19
+ "types": "./build/index.d.ts"
20
+ }
21
+ },
22
+ "devDependencies": {
23
+ "@types/node": "^22.7.8",
24
+ "nodemon": "^3.1.7",
25
+ "typescript": "^5.6.3"
26
+ },
27
+ "dependencies": {
28
+ "@noble/hashes": "^1.5.0",
29
+ "@owlmeans/context": "^0.1.0",
30
+ "@owlmeans/redis-resource": "^0.1.0",
31
+ "@owlmeans/resource": "^0.1.0",
32
+ "@owlmeans/server-context": "^0.1.0",
33
+ "@scure/base": "^1.1.9",
34
+ "ioredis": "^5.4.1"
35
+ },
36
+ "private": false,
37
+ "publishConfig": {
38
+ "access": "public"
39
+ }
40
+ }
package/src/consts.ts ADDED
@@ -0,0 +1,3 @@
1
+ import { DEFAULT_DB_ALIAS } from '@owlmeans/redis-resource'
2
+
3
+ export const DEFAULT_ALIAS = DEFAULT_DB_ALIAS
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+
2
+ export type * from './types.js'
3
+ export * from './consts.js'
4
+ export * from './service.js'
package/src/service.ts ADDED
@@ -0,0 +1,94 @@
1
+ import type { RedisDbService, RedisClient, RedisDb } from '@owlmeans/redis-resource'
2
+ import { DEFAULT_ALIAS } from './consts.js'
3
+ import { createDbService } from '@owlmeans/resource'
4
+ import { assertContext, Layer } from '@owlmeans/context'
5
+ import type { BasicContext } from '@owlmeans/context'
6
+ import type { ServerContext, ServerConfig } from '@owlmeans/server-context'
7
+ import { createClient } from './utils/index.js'
8
+
9
+ type Config = ServerConfig
10
+ interface Context<C extends Config = Config> extends ServerContext<C> { }
11
+
12
+ export const makeRedisService = (alias: string = DEFAULT_ALIAS): RedisDbService => {
13
+ const location = `redis:${alias}`
14
+
15
+ const service: RedisDbService = createDbService<RedisDb, RedisClient, RedisDbService>(
16
+ alias, {
17
+ db: async configAlias => {
18
+ const client = await service.client(configAlias)
19
+
20
+ const name = await service.name(configAlias)
21
+
22
+ /**
23
+ * @TODO we need to think how we can reuse the initail
24
+ * one instead of duplication for some cases
25
+ */
26
+ return { client: client.duplicate(), prefix: name }
27
+ },
28
+
29
+ initialize: async configAlias => {
30
+ configAlias = service.ensureConfigAlias(configAlias)
31
+ const config = service.config(configAlias)
32
+
33
+ if (service.clients[configAlias] != null) {
34
+ return
35
+ }
36
+
37
+ if (service.layers == null) {
38
+ service.layers = [Layer.Global]
39
+ }
40
+ if (config.serviceSensitive && service.layers.includes(Layer.Service)) {
41
+ service.layers.push(Layer.Service)
42
+ }
43
+ if (config.entitySensitive && service.layers.includes(Layer.Entity)) {
44
+ service.layers.push(Layer.Entity)
45
+ }
46
+
47
+ let client = await createClient(config)
48
+
49
+ // we need to check all hosts for replication consistancy
50
+
51
+ if (service.clients[configAlias] != null) {
52
+ throw new SyntaxError(`Cannot replace existing redis client: ${configAlias} - ${service.alias}`)
53
+ }
54
+
55
+ process.on('SIGTERM', () => {
56
+ client.quit()
57
+ })
58
+
59
+ service.clients[configAlias] = client
60
+ },
61
+
62
+ reinitializeContext: <T>(context: BasicContext<ServerConfig>) => {
63
+ const _service = makeRedisService(alias)
64
+
65
+ _service.ctx = context
66
+
67
+ _service.layers = service.layers
68
+
69
+ return _service as T
70
+ }
71
+ }, service => async () => {
72
+ const context = assertContext<Config, Context>(service.ctx as Context, location)
73
+
74
+ // Try to initialize all connections
75
+ await context.cfg.dbs?.filter(dbConfig => dbConfig.service === alias).reduce(async (prev, dbConfig) => {
76
+ await prev
77
+ await service.config(dbConfig.alias)
78
+ }, Promise.resolve())
79
+
80
+ service.initialized = true
81
+ })
82
+
83
+ return service
84
+ }
85
+
86
+ export const appendRedis = <C extends Config, T extends Context<C> = Context<C>>(
87
+ context: T, alias: string = DEFAULT_ALIAS
88
+ ): T => {
89
+ const service = makeRedisService(alias)
90
+
91
+ context.registerService(service)
92
+
93
+ return context
94
+ }
package/src/types.ts ADDED
@@ -0,0 +1,6 @@
1
+ import type { RedisOptions } from 'ioredis'
2
+
3
+ export interface RedisMeta extends RedisOptions {
4
+ masterNumber?: number
5
+ slaveNumber?: number
6
+ }