@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.
- package/LICENSE +21 -0
- package/README.md +517 -0
- package/build/.gitkeep +0 -0
- package/build/consts.d.ts +2 -0
- package/build/consts.d.ts.map +1 -0
- package/build/consts.js +3 -0
- package/build/consts.js.map +1 -0
- package/build/index.d.ts +4 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +3 -0
- package/build/index.js.map +1 -0
- package/build/service.d.ts +9 -0
- package/build/service.d.ts.map +1 -0
- package/build/service.js +64 -0
- package/build/service.js.map +1 -0
- package/build/types.d.ts +6 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +2 -0
- package/build/types.js.map +1 -0
- package/build/utils/cluster.d.ts +5 -0
- package/build/utils/cluster.d.ts.map +1 -0
- package/build/utils/cluster.js +186 -0
- package/build/utils/cluster.js.map +1 -0
- package/build/utils/config.d.ts +9 -0
- package/build/utils/config.d.ts.map +1 -0
- package/build/utils/config.js +31 -0
- package/build/utils/config.js.map +1 -0
- package/build/utils/index.d.ts +4 -0
- package/build/utils/index.d.ts.map +1 -0
- package/build/utils/index.js +4 -0
- package/build/utils/index.js.map +1 -0
- package/build/utils/instance.d.ts +4 -0
- package/build/utils/instance.d.ts.map +1 -0
- package/build/utils/instance.js +13 -0
- package/build/utils/instance.js.map +1 -0
- package/package.json +40 -0
- package/src/consts.ts +3 -0
- package/src/index.ts +4 -0
- package/src/service.ts +94 -0
- package/src/types.ts +6 -0
- package/src/utils/cluster.ts +215 -0
- package/src/utils/config.ts +35 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/instance.ts +16 -0
- package/tsconfig.json +14 -0
- package/tsconfig.tsbuildinfo +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 OwlMeans Common — Fullstack typescript framework
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
# @owlmeans/redis
|
|
2
|
+
|
|
3
|
+
Redis service integration for OwlMeans Common server applications. This package provides a server-side Redis service implementation with clustering support, connection management, and multi-layer support designed for caching, session storage, and high-performance data operations.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The `@owlmeans/redis` package extends the OwlMeans resource system to provide Redis-specific functionality including:
|
|
8
|
+
|
|
9
|
+
- **Redis Service Integration**: Factory functions for creating Redis services with connection management
|
|
10
|
+
- **Cluster Support**: Automatic Redis cluster and sentinel configuration
|
|
11
|
+
- **Connection Pooling**: Efficient Redis connection management with proper cleanup
|
|
12
|
+
- **Multi-Layer Support**: Integration with OwlMeans context layer system for proper data isolation
|
|
13
|
+
- **Prefix Management**: Automatic key prefixing for namespace isolation
|
|
14
|
+
|
|
15
|
+
This package follows the OwlMeans "quadra" pattern as a server-side implementation complementing the basic `@owlmeans/redis-resource` package.
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @owlmeans/redis
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Dependencies
|
|
24
|
+
|
|
25
|
+
This package requires Redis client library and integrates with:
|
|
26
|
+
- `@owlmeans/redis-resource`: Base Redis resource definitions
|
|
27
|
+
- `@owlmeans/server-context`: Server context management
|
|
28
|
+
- `@owlmeans/resource`: Base resource service patterns
|
|
29
|
+
- `ioredis`: High-performance Redis client library
|
|
30
|
+
|
|
31
|
+
## Core Concepts
|
|
32
|
+
|
|
33
|
+
### Redis Service
|
|
34
|
+
|
|
35
|
+
The Redis service provides connection management and extends the base database service with Redis-specific functionality like clustering and prefix management.
|
|
36
|
+
|
|
37
|
+
### Connection Management
|
|
38
|
+
|
|
39
|
+
Manages Redis connections with support for clustering, sentinels, and proper connection lifecycle management including graceful shutdown.
|
|
40
|
+
|
|
41
|
+
### Layer Integration
|
|
42
|
+
|
|
43
|
+
Supports multi-layer data isolation through the OwlMeans context layer system, allowing service-specific and entity-specific Redis configurations.
|
|
44
|
+
|
|
45
|
+
## API Reference
|
|
46
|
+
|
|
47
|
+
### Types
|
|
48
|
+
|
|
49
|
+
#### `RedisMeta`
|
|
50
|
+
Metadata interface for Redis-specific configuration extending ioredis options.
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
interface RedisMeta extends RedisOptions {
|
|
54
|
+
masterNumber?: number // Master node count for cluster
|
|
55
|
+
slaveNumber?: number // Slave node count for cluster
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Factory Functions
|
|
60
|
+
|
|
61
|
+
#### `makeRedisService(alias?: string): RedisDbService`
|
|
62
|
+
|
|
63
|
+
Creates a Redis service instance with connection management and clustering capabilities.
|
|
64
|
+
|
|
65
|
+
**Parameters:**
|
|
66
|
+
- `alias` (optional): Service alias (default: `DEFAULT_ALIAS`)
|
|
67
|
+
|
|
68
|
+
**Returns:** `RedisDbService` instance
|
|
69
|
+
|
|
70
|
+
**Methods:**
|
|
71
|
+
- **`db(configAlias?: string): Promise<RedisDb>`**: Gets Redis database instance with prefix for the configuration
|
|
72
|
+
- **`initialize(configAlias?: string): Promise<void>`**: Initializes Redis connection with cluster setup
|
|
73
|
+
- **`reinitializeContext<T>(context: BasicContext<ServerConfig>): T`**: Reinitializes service with new context
|
|
74
|
+
|
|
75
|
+
**Example:**
|
|
76
|
+
```typescript
|
|
77
|
+
import { makeRedisService } from '@owlmeans/redis'
|
|
78
|
+
|
|
79
|
+
const redisService = makeRedisService('cache')
|
|
80
|
+
|
|
81
|
+
// Initialize with configuration
|
|
82
|
+
await redisService.initialize('prod-config')
|
|
83
|
+
|
|
84
|
+
// Get database instance with prefix
|
|
85
|
+
const db = await redisService.db('prod-config')
|
|
86
|
+
|
|
87
|
+
// Use Redis operations
|
|
88
|
+
await db.client.set('user:123', JSON.stringify(userData))
|
|
89
|
+
const user = await db.client.get('user:123')
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
#### `appendRedis<C, T>(context: T, alias?: string): T`
|
|
93
|
+
|
|
94
|
+
Convenience function to create and register a Redis service with a context.
|
|
95
|
+
|
|
96
|
+
**Parameters:**
|
|
97
|
+
- `context`: Server context instance
|
|
98
|
+
- `alias` (optional): Service alias (default: `DEFAULT_ALIAS`)
|
|
99
|
+
|
|
100
|
+
**Returns:** The context with Redis service registered
|
|
101
|
+
|
|
102
|
+
**Example:**
|
|
103
|
+
```typescript
|
|
104
|
+
import { appendRedis } from '@owlmeans/redis'
|
|
105
|
+
import { makeServerContext } from '@owlmeans/server-context'
|
|
106
|
+
|
|
107
|
+
const context = makeServerContext(serverConfig)
|
|
108
|
+
const contextWithRedis = appendRedis(context, 'session-store')
|
|
109
|
+
|
|
110
|
+
await contextWithRedis.configure().init()
|
|
111
|
+
|
|
112
|
+
// Access Redis service
|
|
113
|
+
const redisService = context.service('session-store')
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Redis Database Operations
|
|
117
|
+
|
|
118
|
+
The Redis service provides a database interface with automatic prefixing:
|
|
119
|
+
|
|
120
|
+
#### `RedisDb`
|
|
121
|
+
Database instance with client and prefix management.
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
interface RedisDb {
|
|
125
|
+
client: RedisClient // ioredis client instance (duplicated for isolation)
|
|
126
|
+
prefix: string // Key prefix for namespace isolation
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Usage:**
|
|
131
|
+
```typescript
|
|
132
|
+
const db = await redisService.db('config-alias')
|
|
133
|
+
|
|
134
|
+
// Keys are automatically prefixed
|
|
135
|
+
await db.client.set('session:user123', sessionData)
|
|
136
|
+
await db.client.get('session:user123')
|
|
137
|
+
|
|
138
|
+
// Use Redis operations with prefix isolation
|
|
139
|
+
await db.client.hmset('user:profile', profileData)
|
|
140
|
+
const profile = await db.client.hgetall('user:profile')
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Cluster Support
|
|
144
|
+
|
|
145
|
+
The service automatically handles Redis cluster and sentinel configuration:
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
// Configuration with cluster hosts
|
|
149
|
+
const config = {
|
|
150
|
+
alias: 'cluster-config',
|
|
151
|
+
host: ['redis1.example.com', 'redis2.example.com', 'redis3.example.com'],
|
|
152
|
+
port: 6379,
|
|
153
|
+
database: 'app-cache',
|
|
154
|
+
// Additional cluster options in meta
|
|
155
|
+
meta: {
|
|
156
|
+
masterNumber: 3,
|
|
157
|
+
slaveNumber: 6,
|
|
158
|
+
enableReadyCheck: true,
|
|
159
|
+
maxRetriesPerRequest: 3
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Service automatically detects cluster and sets up connections
|
|
164
|
+
await redisService.initialize('cluster-config')
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Constants
|
|
168
|
+
|
|
169
|
+
#### `DEFAULT_ALIAS`
|
|
170
|
+
Default service alias for Redis services.
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
const DEFAULT_ALIAS = DEFAULT_DB_ALIAS // From @owlmeans/redis-resource
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Usage Examples
|
|
177
|
+
|
|
178
|
+
### Basic Redis Service Setup
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
import { makeRedisService } from '@owlmeans/redis'
|
|
182
|
+
import { makeServerContext } from '@owlmeans/server-context'
|
|
183
|
+
|
|
184
|
+
// Create server context with Redis configuration
|
|
185
|
+
const context = makeServerContext({
|
|
186
|
+
service: 'my-app',
|
|
187
|
+
type: AppType.Backend,
|
|
188
|
+
layer: Layer.Service,
|
|
189
|
+
dbs: [{
|
|
190
|
+
alias: 'main-cache',
|
|
191
|
+
service: 'redis',
|
|
192
|
+
host: 'localhost',
|
|
193
|
+
port: 6379,
|
|
194
|
+
database: 'app-cache'
|
|
195
|
+
}]
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
// Create and register Redis service
|
|
199
|
+
const redisService = makeRedisService('redis')
|
|
200
|
+
context.registerService(redisService)
|
|
201
|
+
|
|
202
|
+
// Initialize context
|
|
203
|
+
await context.configure().init()
|
|
204
|
+
|
|
205
|
+
// Use Redis
|
|
206
|
+
const db = await redisService.db('main-cache')
|
|
207
|
+
await db.client.set('key', 'value')
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Using appendRedis Helper
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
import { appendRedis } from '@owlmeans/redis'
|
|
214
|
+
|
|
215
|
+
const context = makeServerContext(config)
|
|
216
|
+
const contextWithRedis = appendRedis(context)
|
|
217
|
+
|
|
218
|
+
await contextWithRedis.configure().init()
|
|
219
|
+
|
|
220
|
+
const redisService = context.service('redis')
|
|
221
|
+
const db = await redisService.db()
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Session Storage
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
// Configure Redis for session storage
|
|
228
|
+
const sessionConfig = {
|
|
229
|
+
alias: 'session-store',
|
|
230
|
+
service: 'redis',
|
|
231
|
+
host: 'localhost',
|
|
232
|
+
port: 6379,
|
|
233
|
+
database: 'sessions'
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const redisService = makeRedisService('redis')
|
|
237
|
+
await redisService.initialize('session-store')
|
|
238
|
+
|
|
239
|
+
const db = await redisService.db('session-store')
|
|
240
|
+
|
|
241
|
+
// Store session data
|
|
242
|
+
const sessionId = 'sess_123456'
|
|
243
|
+
const sessionData = {
|
|
244
|
+
userId: 'user_789',
|
|
245
|
+
loginTime: Date.now(),
|
|
246
|
+
permissions: ['read', 'write']
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
await db.client.setex(`session:${sessionId}`, 3600, JSON.stringify(sessionData))
|
|
250
|
+
|
|
251
|
+
// Retrieve session data
|
|
252
|
+
const rawSession = await db.client.get(`session:${sessionId}`)
|
|
253
|
+
const session = JSON.parse(rawSession)
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Caching with TTL
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
const cacheService = makeRedisService('cache')
|
|
260
|
+
await cacheService.initialize('cache-config')
|
|
261
|
+
|
|
262
|
+
const db = await cacheService.db('cache-config')
|
|
263
|
+
|
|
264
|
+
// Cache with expiration
|
|
265
|
+
await db.client.setex('user:profile:123', 300, JSON.stringify(userProfile)) // 5 minutes
|
|
266
|
+
|
|
267
|
+
// Cache with complex data structures
|
|
268
|
+
await db.client.hmset('stats:daily', {
|
|
269
|
+
visitors: 1234,
|
|
270
|
+
pageViews: 5678,
|
|
271
|
+
lastUpdated: Date.now()
|
|
272
|
+
})
|
|
273
|
+
await db.client.expire('stats:daily', 86400) // 24 hours
|
|
274
|
+
|
|
275
|
+
// Get cached data
|
|
276
|
+
const cachedProfile = await db.client.get('user:profile:123')
|
|
277
|
+
const stats = await db.client.hgetall('stats:daily')
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Multi-Configuration Setup
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
const context = makeServerContext({
|
|
284
|
+
service: 'multi-redis-app',
|
|
285
|
+
type: AppType.Backend,
|
|
286
|
+
layer: Layer.Service,
|
|
287
|
+
dbs: [
|
|
288
|
+
{
|
|
289
|
+
alias: 'session-store',
|
|
290
|
+
service: 'redis',
|
|
291
|
+
host: 'sessions.redis.example.com',
|
|
292
|
+
database: 'sessions',
|
|
293
|
+
meta: { db: 0 }
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
alias: 'cache-store',
|
|
297
|
+
service: 'redis',
|
|
298
|
+
host: 'cache.redis.example.com',
|
|
299
|
+
database: 'cache',
|
|
300
|
+
meta: { db: 1 }
|
|
301
|
+
}
|
|
302
|
+
]
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
const redisService = makeRedisService('redis')
|
|
306
|
+
context.registerService(redisService)
|
|
307
|
+
|
|
308
|
+
await context.configure().init()
|
|
309
|
+
|
|
310
|
+
// Use different Redis instances
|
|
311
|
+
const sessionDb = await redisService.db('session-store')
|
|
312
|
+
const cacheDb = await redisService.db('cache-store')
|
|
313
|
+
|
|
314
|
+
// Operations are isolated by configuration
|
|
315
|
+
await sessionDb.client.set('user:session', sessionData)
|
|
316
|
+
await cacheDb.client.set('user:profile', profileData)
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Cluster Configuration
|
|
320
|
+
|
|
321
|
+
```typescript
|
|
322
|
+
const clusterConfig = {
|
|
323
|
+
service: 'cluster-app',
|
|
324
|
+
type: AppType.Backend,
|
|
325
|
+
layer: Layer.Service,
|
|
326
|
+
dbs: [{
|
|
327
|
+
alias: 'cluster-cache',
|
|
328
|
+
service: 'redis',
|
|
329
|
+
host: [
|
|
330
|
+
'redis1.cluster.example.com',
|
|
331
|
+
'redis2.cluster.example.com',
|
|
332
|
+
'redis3.cluster.example.com'
|
|
333
|
+
],
|
|
334
|
+
port: 6379,
|
|
335
|
+
database: 'clustered-cache',
|
|
336
|
+
meta: {
|
|
337
|
+
enableReadyCheck: true,
|
|
338
|
+
maxRetriesPerRequest: 3,
|
|
339
|
+
retryDelayOnFailover: 100,
|
|
340
|
+
masterNumber: 3,
|
|
341
|
+
slaveNumber: 6
|
|
342
|
+
}
|
|
343
|
+
}]
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const context = makeServerContext(clusterConfig)
|
|
347
|
+
const redisService = makeRedisService('redis')
|
|
348
|
+
context.registerService(redisService)
|
|
349
|
+
|
|
350
|
+
// Service automatically handles cluster setup
|
|
351
|
+
await context.configure().init()
|
|
352
|
+
|
|
353
|
+
const db = await redisService.db('cluster-cache')
|
|
354
|
+
// Operations work transparently across cluster
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Service Reinitialization
|
|
358
|
+
|
|
359
|
+
```typescript
|
|
360
|
+
// Original context
|
|
361
|
+
const originalContext = makeServerContext(config)
|
|
362
|
+
const redisService = makeRedisService()
|
|
363
|
+
originalContext.registerService(redisService)
|
|
364
|
+
|
|
365
|
+
// Later, reinitialize with new context
|
|
366
|
+
const newContext = makeServerContext(newConfig)
|
|
367
|
+
const reinitializedService = redisService.reinitializeContext(newContext)
|
|
368
|
+
|
|
369
|
+
// Service now uses new context configuration
|
|
370
|
+
await reinitializedService.initialize()
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Pub/Sub Operations
|
|
374
|
+
|
|
375
|
+
```typescript
|
|
376
|
+
const redisService = makeRedisService('pubsub')
|
|
377
|
+
await redisService.initialize('pubsub-config')
|
|
378
|
+
|
|
379
|
+
const db = await redisService.db('pubsub-config')
|
|
380
|
+
|
|
381
|
+
// Subscribe to channels
|
|
382
|
+
await db.client.subscribe('notifications')
|
|
383
|
+
db.client.on('message', (channel, message) => {
|
|
384
|
+
console.log(`Received from ${channel}: ${message}`)
|
|
385
|
+
})
|
|
386
|
+
|
|
387
|
+
// Publish messages
|
|
388
|
+
await db.client.publish('notifications', JSON.stringify({
|
|
389
|
+
type: 'user-login',
|
|
390
|
+
userId: '123',
|
|
391
|
+
timestamp: Date.now()
|
|
392
|
+
}))
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
## Configuration
|
|
396
|
+
|
|
397
|
+
Redis service configuration is handled through the server context's database configuration:
|
|
398
|
+
|
|
399
|
+
```typescript
|
|
400
|
+
interface DatabaseConfig {
|
|
401
|
+
alias: string // Configuration alias
|
|
402
|
+
service: string // Service name ('redis')
|
|
403
|
+
host: string | string[] // Redis host(s)
|
|
404
|
+
port?: number // Redis port
|
|
405
|
+
database: string // Database prefix/namespace
|
|
406
|
+
username?: string // Authentication username
|
|
407
|
+
password?: string // Authentication password
|
|
408
|
+
serviceSensitive?: boolean // Enable service-layer isolation
|
|
409
|
+
entitySensitive?: boolean // Enable entity-layer isolation
|
|
410
|
+
meta?: RedisMeta // Redis-specific options
|
|
411
|
+
}
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### Redis-Specific Meta Options
|
|
415
|
+
|
|
416
|
+
```typescript
|
|
417
|
+
interface RedisMeta extends RedisOptions {
|
|
418
|
+
masterNumber?: number // Master node count for cluster
|
|
419
|
+
slaveNumber?: number // Slave node count for cluster
|
|
420
|
+
// Plus all ioredis options:
|
|
421
|
+
db?: number // Redis database number
|
|
422
|
+
connectTimeout?: number // Connection timeout
|
|
423
|
+
commandTimeout?: number // Command timeout
|
|
424
|
+
retryDelayOnFailover?: number // Failover retry delay
|
|
425
|
+
enableReadyCheck?: boolean // Enable ready check
|
|
426
|
+
maxRetriesPerRequest?: number // Max retries per request
|
|
427
|
+
// ... and many more ioredis options
|
|
428
|
+
}
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
## Error Handling
|
|
432
|
+
|
|
433
|
+
The package provides descriptive error messages for common issues:
|
|
434
|
+
|
|
435
|
+
- **Client replacement**: Thrown when attempting to replace existing Redis client
|
|
436
|
+
- **Context assertion**: Thrown when service context is invalid
|
|
437
|
+
- **Connection errors**: Propagated from ioredis client
|
|
438
|
+
|
|
439
|
+
```typescript
|
|
440
|
+
try {
|
|
441
|
+
await redisService.initialize('config-alias')
|
|
442
|
+
} catch (error) {
|
|
443
|
+
if (error.message.includes('Cannot replace existing redis client')) {
|
|
444
|
+
// Handle client replacement error
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
## Performance Considerations
|
|
450
|
+
|
|
451
|
+
- **Connection Pooling**: ioredis handles connection pooling automatically
|
|
452
|
+
- **Client Duplication**: Each db() call creates a duplicate client for isolation
|
|
453
|
+
- **Cluster Latency**: Cluster setup may add initialization time
|
|
454
|
+
- **Memory Usage**: Multiple Redis connections increase memory usage
|
|
455
|
+
- **Key Prefixing**: Adds minimal overhead for namespace isolation
|
|
456
|
+
|
|
457
|
+
## Security Considerations
|
|
458
|
+
|
|
459
|
+
### Connection Security
|
|
460
|
+
- Use authentication credentials for production Redis instances
|
|
461
|
+
- Configure TLS/SSL for Redis connections when needed
|
|
462
|
+
- Restrict Redis access to necessary IP addresses
|
|
463
|
+
|
|
464
|
+
### Data Security
|
|
465
|
+
- Be cautious with sensitive data in Redis (consider encryption)
|
|
466
|
+
- Use appropriate TTL values to limit data exposure time
|
|
467
|
+
- Implement proper access controls at the application level
|
|
468
|
+
|
|
469
|
+
### Namespace Isolation
|
|
470
|
+
- Use layer-sensitive configurations for multi-tenant applications
|
|
471
|
+
- Leverage prefix management for data isolation between services
|
|
472
|
+
|
|
473
|
+
## Integration with OwlMeans Ecosystem
|
|
474
|
+
|
|
475
|
+
This package integrates with:
|
|
476
|
+
|
|
477
|
+
- **@owlmeans/redis-resource**: Base Redis resource types and interfaces
|
|
478
|
+
- **@owlmeans/server-context**: Server context and configuration management
|
|
479
|
+
- **@owlmeans/resource**: Base resource service patterns
|
|
480
|
+
- **@owlmeans/mongo**: For multi-database architectures
|
|
481
|
+
|
|
482
|
+
## Best Practices
|
|
483
|
+
|
|
484
|
+
1. **Use appropriate TTL values** for cached data to prevent memory bloat
|
|
485
|
+
2. **Configure clustering** for production high-availability
|
|
486
|
+
3. **Separate Redis configurations** by use case (cache vs sessions vs pub/sub)
|
|
487
|
+
4. **Monitor Redis memory usage** and implement eviction policies
|
|
488
|
+
5. **Use connection pooling** efficiently with proper client management
|
|
489
|
+
|
|
490
|
+
## Related Packages
|
|
491
|
+
|
|
492
|
+
- **@owlmeans/redis-resource**: Base Redis resource definitions
|
|
493
|
+
- **@owlmeans/mongo**: MongoDB service implementation
|
|
494
|
+
- **@owlmeans/server-context**: Server context management
|
|
495
|
+
- **@owlmeans/resource**: Base resource service patterns
|
|
496
|
+
|
|
497
|
+
## Redis Use Cases
|
|
498
|
+
|
|
499
|
+
### Caching
|
|
500
|
+
- API response caching
|
|
501
|
+
- Database query result caching
|
|
502
|
+
- Computed value caching
|
|
503
|
+
|
|
504
|
+
### Session Storage
|
|
505
|
+
- User session management
|
|
506
|
+
- Authentication token storage
|
|
507
|
+
- Shopping cart persistence
|
|
508
|
+
|
|
509
|
+
### Real-time Features
|
|
510
|
+
- Pub/Sub messaging
|
|
511
|
+
- Live notifications
|
|
512
|
+
- Real-time analytics
|
|
513
|
+
|
|
514
|
+
### Rate Limiting
|
|
515
|
+
- API rate limiting
|
|
516
|
+
- Request throttling
|
|
517
|
+
- Usage tracking
|
package/build/.gitkeep
ADDED
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,aAAa,UAAmB,CAAA"}
|
package/build/consts.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.js","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAE3D,MAAM,CAAC,MAAM,aAAa,GAAG,gBAAgB,CAAA"}
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,mBAAmB,YAAY,CAAA;AAC/B,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA"}
|
package/build/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { RedisDbService } from '@owlmeans/redis-resource';
|
|
2
|
+
import type { ServerContext, ServerConfig } from '@owlmeans/server-context';
|
|
3
|
+
type Config = ServerConfig;
|
|
4
|
+
interface Context<C extends Config = Config> extends ServerContext<C> {
|
|
5
|
+
}
|
|
6
|
+
export declare const makeRedisService: (alias?: string) => RedisDbService;
|
|
7
|
+
export declare const appendRedis: <C extends Config, T extends Context<C> = Context<C>>(context: T, alias?: string) => T;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAwB,MAAM,0BAA0B,CAAA;AAKpF,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAG3E,KAAK,MAAM,GAAG,YAAY,CAAA;AAC1B,UAAU,OAAO,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,CAAE,SAAQ,aAAa,CAAC,CAAC,CAAC;CAAI;AAEzE,eAAO,MAAM,gBAAgB,WAAW,MAAM,KAAmB,cAwEhE,CAAA;AAED,eAAO,MAAM,WAAW,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,wBACvD,CAAC,UAAS,MAAM,KACxB,CAMF,CAAA"}
|
package/build/service.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { DEFAULT_ALIAS } from './consts.js';
|
|
2
|
+
import { createDbService } from '@owlmeans/resource';
|
|
3
|
+
import { assertContext, Layer } from '@owlmeans/context';
|
|
4
|
+
import { createClient } from './utils/index.js';
|
|
5
|
+
export const makeRedisService = (alias = DEFAULT_ALIAS) => {
|
|
6
|
+
const location = `redis:${alias}`;
|
|
7
|
+
const service = createDbService(alias, {
|
|
8
|
+
db: async (configAlias) => {
|
|
9
|
+
const client = await service.client(configAlias);
|
|
10
|
+
const name = await service.name(configAlias);
|
|
11
|
+
/**
|
|
12
|
+
* @TODO we need to think how we can reuse the initail
|
|
13
|
+
* one instead of duplication for some cases
|
|
14
|
+
*/
|
|
15
|
+
return { client: client.duplicate(), prefix: name };
|
|
16
|
+
},
|
|
17
|
+
initialize: async (configAlias) => {
|
|
18
|
+
configAlias = service.ensureConfigAlias(configAlias);
|
|
19
|
+
const config = service.config(configAlias);
|
|
20
|
+
if (service.clients[configAlias] != null) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (service.layers == null) {
|
|
24
|
+
service.layers = [Layer.Global];
|
|
25
|
+
}
|
|
26
|
+
if (config.serviceSensitive && service.layers.includes(Layer.Service)) {
|
|
27
|
+
service.layers.push(Layer.Service);
|
|
28
|
+
}
|
|
29
|
+
if (config.entitySensitive && service.layers.includes(Layer.Entity)) {
|
|
30
|
+
service.layers.push(Layer.Entity);
|
|
31
|
+
}
|
|
32
|
+
let client = await createClient(config);
|
|
33
|
+
// we need to check all hosts for replication consistancy
|
|
34
|
+
if (service.clients[configAlias] != null) {
|
|
35
|
+
throw new SyntaxError(`Cannot replace existing redis client: ${configAlias} - ${service.alias}`);
|
|
36
|
+
}
|
|
37
|
+
process.on('SIGTERM', () => {
|
|
38
|
+
client.quit();
|
|
39
|
+
});
|
|
40
|
+
service.clients[configAlias] = client;
|
|
41
|
+
},
|
|
42
|
+
reinitializeContext: (context) => {
|
|
43
|
+
const _service = makeRedisService(alias);
|
|
44
|
+
_service.ctx = context;
|
|
45
|
+
_service.layers = service.layers;
|
|
46
|
+
return _service;
|
|
47
|
+
}
|
|
48
|
+
}, service => async () => {
|
|
49
|
+
const context = assertContext(service.ctx, location);
|
|
50
|
+
// Try to initialize all connections
|
|
51
|
+
await context.cfg.dbs?.filter(dbConfig => dbConfig.service === alias).reduce(async (prev, dbConfig) => {
|
|
52
|
+
await prev;
|
|
53
|
+
await service.config(dbConfig.alias);
|
|
54
|
+
}, Promise.resolve());
|
|
55
|
+
service.initialized = true;
|
|
56
|
+
});
|
|
57
|
+
return service;
|
|
58
|
+
};
|
|
59
|
+
export const appendRedis = (context, alias = DEFAULT_ALIAS) => {
|
|
60
|
+
const service = makeRedisService(alias);
|
|
61
|
+
context.registerService(service);
|
|
62
|
+
return context;
|
|
63
|
+
};
|
|
64
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
|
package/build/types.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAE3C,MAAM,WAAW,SAAU,SAAQ,YAAY;IAC7C,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB"}
|
package/build/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|