eyevinn-channel-engine 5.1.2 → 5.2.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/.github/workflows/nodejs.yml +1 -1
- package/.github/workflows/publish.yml +1 -1
- package/.github/workflows/rc.yml +1 -1
- package/CONTRIBUTING.md +40 -0
- package/Dockerfile +1 -1
- package/README.md +70 -8
- package/dist/engine/chaos_monkey.d.ts +1 -1
- package/dist/engine/chaos_monkey.js +1 -0
- package/dist/engine/event_stream.d.ts +1 -6
- package/dist/engine/event_stream.js +2 -0
- package/dist/engine/memcached_state_store.d.ts +1 -13
- package/dist/engine/memcached_state_store.js +4 -0
- package/dist/engine/memory_state_store.d.ts +1 -13
- package/dist/engine/memory_state_store.js +5 -0
- package/dist/engine/playhead_state.d.ts +0 -26
- package/dist/engine/playhead_state.js +9 -0
- package/dist/engine/preflight.d.ts +1 -1
- package/dist/engine/preflight.js +1 -0
- package/dist/engine/redis_state_store.d.ts +1 -19
- package/dist/engine/redis_state_store.js +5 -0
- package/dist/engine/server.d.ts +27 -0
- package/dist/engine/server.js +140 -6
- package/dist/engine/session.d.ts +1 -148
- package/dist/engine/session.js +398 -11
- package/dist/engine/session_live.d.ts +1 -181
- package/dist/engine/session_live.js +19 -4
- package/dist/engine/session_live_state.d.ts +0 -23
- package/dist/engine/session_live_state.js +5 -0
- package/dist/engine/session_state.d.ts +0 -45
- package/dist/engine/session_state.js +10 -0
- package/dist/engine/shared_state_store.d.ts +1 -25
- package/dist/engine/shared_state_store.js +10 -0
- package/dist/engine/state_store_types.d.ts +32 -0
- package/dist/engine/state_store_types.js +5 -0
- package/dist/engine/stream_switcher.d.ts +1 -40
- package/dist/engine/stream_switcher.js +108 -20
- package/dist/engine/subtitle_slicer.d.ts +1 -8
- package/dist/engine/subtitle_slicer.js +1 -0
- package/dist/engine/util.d.ts +1 -29
- package/dist/engine/util.js +2 -1
- package/dist/examples/adbreak.d.ts +1 -0
- package/dist/examples/adbreak.js +104 -0
- package/dist/index.d.ts +1 -1
- package/dist/package.json +20 -4
- package/docs/design/mixing-muxed-demuxed-audio.md +153 -0
- package/docs/design/subtitles-live-mix-demux.md +135 -0
- package/docs/reference.md +67 -0
- package/engine/chaos_monkey.ts +17 -0
- package/engine/{event_stream.js → event_stream.ts} +5 -3
- package/engine/{memcached_state_store.js → memcached_state_store.ts} +19 -12
- package/engine/memory_state_store.ts +71 -0
- package/engine/{playhead_state.js → playhead_state.ts} +92 -82
- package/engine/{preflight.js → preflight.ts} +1 -1
- package/engine/{redis_state_store.js → redis_state_store.ts} +216 -208
- package/engine/server.ts +210 -8
- package/engine/{session.js → session.ts} +421 -22
- package/engine/{session_live.js → session_live.ts} +54 -31
- package/engine/{session_live_state.js → session_live_state.ts} +129 -120
- package/engine/{session_state.js → session_state.ts} +269 -248
- package/engine/{shared_state_store.js → shared_state_store.ts} +31 -19
- package/engine/state_store_types.ts +35 -0
- package/engine/{stream_switcher.js → stream_switcher.ts} +133 -34
- package/engine/{subtitle_slicer.js → subtitle_slicer.ts} +10 -8
- package/engine/{util.js → util.ts} +28 -24
- package/examples/adbreak.ts +115 -0
- package/index.ts +2 -0
- package/package.json +20 -4
- package/spec/engine/custom_vod_request_params_spec.ts +99 -0
- package/spec/engine/drm_spec.ts +128 -0
- package/spec/engine/{engine_spec.js → engine_spec.ts} +44 -1
- package/spec/engine/{ha_spec.js → ha_spec.ts} +7 -4
- package/spec/engine/{init_switching_spec.js → init_switching_spec.ts} +10 -6
- package/spec/engine/{interface_spec.js → interface_spec.ts} +16 -4
- package/spec/engine/{playhead_live_spec.js → playhead_live_spec.ts} +2 -1
- package/spec/engine/{playhead_spec.js → playhead_spec.ts} +15 -8
- package/spec/engine/session_spec.ts +800 -0
- package/spec/engine/stream_switcher_audio_reconcile_spec.ts +158 -0
- package/spec/engine/{stream_switcher_spec.js → stream_switcher_spec.ts} +111 -13
- package/spec/engine/subtitle_missing_language_spec.ts +196 -0
- package/spec/engine/{subtitle_spec.js → subtitle_spec.ts} +28 -27
- package/spec/helpers/ts-resolve.js +65 -0
- package/spec/support/jasmine.json +1 -1
- package/spec/testvectors/hls_subs_no_language/config.subtitleTracks.json +12 -0
- package/spec/testvectors/hls_subs_no_language/evidence_dummy_segments_name_mismatch.m3u8 +24 -0
- package/spec/testvectors/hls_subs_no_language/evidence_real_cues_name_match.m3u8 +24 -0
- package/spec/testvectors/hls_subs_no_language/french.m3u8 +9 -0
- package/spec/testvectors/hls_subs_no_language/master.m3u8 +11 -0
- package/spec/testvectors/hls_subs_no_language/video.m3u8 +25 -0
- package/spec/testvectors/slate/master.m3u8 +3 -0
- package/spec/testvectors/slate/media.m3u8 +9 -0
- package/tsconfig.json +21 -4
- package/tsconfig.spec.json +16 -0
- package/engine/chaos_monkey.js +0 -17
- package/engine/memory_state_store.js +0 -63
- package/spec/engine/session_spec.js +0 -116
- /package/spec/engine/{session_live_spec.js → session_live_spec.ts} +0 -0
- /package/spec/engine/{util_spec.js → util_spec.ts} +0 -0
|
@@ -1,82 +1,92 @@
|
|
|
1
|
-
const SharedStateStore = require('./shared_state_store.js');
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return await this.store.
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
1
|
+
const SharedStateStore = require('./shared_state_store.js');
|
|
2
|
+
|
|
3
|
+
// Type-only migration (#373): a faithful port of the original JS.
|
|
4
|
+
// `SharedStateStore` is loaded via CommonJS `require` (erasable syntax) so the
|
|
5
|
+
// still-`.js` consumers keep resolving it unchanged; the base type is `any` at
|
|
6
|
+
// this interop boundary.
|
|
7
|
+
|
|
8
|
+
const PlayheadState = Object.freeze({
|
|
9
|
+
RUNNING: 1,
|
|
10
|
+
STOPPED: 2,
|
|
11
|
+
CRASHED: 3,
|
|
12
|
+
IDLE: 4
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
class SharedPlayheadState {
|
|
16
|
+
sessionId: string;
|
|
17
|
+
store: PlayheadStateStore;
|
|
18
|
+
state: number;
|
|
19
|
+
lastM3u8: any;
|
|
20
|
+
|
|
21
|
+
constructor(store: PlayheadStateStore, sessionId: string, opts?: any) {
|
|
22
|
+
this.sessionId = sessionId;
|
|
23
|
+
this.store = store;
|
|
24
|
+
this.state = PlayheadState.IDLE;
|
|
25
|
+
this.lastM3u8 = null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async get(key: string): Promise<any> {
|
|
29
|
+
return await this.store.get(this.sessionId, key);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async getState(): Promise<number> {
|
|
33
|
+
return this.state;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async getLastM3u8(): Promise<any> {
|
|
37
|
+
return this.lastM3u8;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async getValues(keys: string[]): Promise<Record<string, any>> {
|
|
41
|
+
return await this.store.getValues(this.sessionId, keys);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async set(key: string, value: any, isLeader?: boolean): Promise<any> {
|
|
45
|
+
if (isLeader) {
|
|
46
|
+
return await this.store.set(this.sessionId, key, value);
|
|
47
|
+
} else {
|
|
48
|
+
return await this.store.get(this.sessionId, key);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async setValues(keyValues: Record<string, any>, isLeader?: boolean): Promise<Record<string, any>> {
|
|
53
|
+
if (isLeader) {
|
|
54
|
+
return await this.store.setValues(this.sessionId, keyValues);
|
|
55
|
+
} else {
|
|
56
|
+
return await this.store.getValues(this.sessionId, Object.keys(keyValues));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async setState(newState: number): Promise<number> {
|
|
61
|
+
this.state = newState;
|
|
62
|
+
return this.state;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async setLastM3u8(m3u8: any): Promise<any> {
|
|
66
|
+
this.lastM3u8 = m3u8;
|
|
67
|
+
return this.lastM3u8;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
class PlayheadStateStore extends SharedStateStore {
|
|
72
|
+
constructor(opts?: any) {
|
|
73
|
+
super("playhead", opts, {
|
|
74
|
+
state: PlayheadState.IDLE,
|
|
75
|
+
tickInterval: opts ? (opts.averageSegmentDuration/1000) : 3,
|
|
76
|
+
mediaSeq: 0,
|
|
77
|
+
vodMediaSeqVideo: 0,
|
|
78
|
+
vodMediaSeqAudio: 0,
|
|
79
|
+
vodMediaSeqSubtitle: 0,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async create(sessionId: string): Promise<SharedPlayheadState> {
|
|
84
|
+
await this.init(sessionId);
|
|
85
|
+
return new SharedPlayheadState(this, sessionId);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
module.exports = {
|
|
90
|
+
PlayheadState,
|
|
91
|
+
PlayheadStateStore
|
|
92
|
+
};
|
|
@@ -30,7 +30,7 @@ const constants = {
|
|
|
30
30
|
STR_ORIGIN: 'origin',
|
|
31
31
|
HTTP_NO_CONTENT: 204
|
|
32
32
|
}
|
|
33
|
-
module.exports.handler = async function (req, res) {
|
|
33
|
+
module.exports.handler = async function (req: any, res: any) {
|
|
34
34
|
if (req.method !== 'OPTIONS') return;
|
|
35
35
|
res
|
|
36
36
|
.header(constants.AC_ALLOW_ORIGIN, '*')
|
|
@@ -1,208 +1,216 @@
|
|
|
1
|
-
const Redis = require("ioredis");
|
|
2
|
-
const debug = require("debug")("redis-state-store");
|
|
3
|
-
const { cloudWatchLog, timer } = require("./util.js");
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
this.
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
this.
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
1
|
+
const Redis = require("ioredis");
|
|
2
|
+
const debug = require("debug")("redis-state-store");
|
|
3
|
+
const { cloudWatchLog, timer } = require("./util.js");
|
|
4
|
+
|
|
5
|
+
import type { IStateStore } from "./state_store_types";
|
|
6
|
+
|
|
7
|
+
// Type-only migration (#373): a faithful port of the original JS.
|
|
8
|
+
|
|
9
|
+
const DEFAULT_VOLATILE_KEY_TTL = 5; // Timeout so it should not expire within one normal increment iteration (in seconds)
|
|
10
|
+
|
|
11
|
+
function isTrue(s: any): boolean {
|
|
12
|
+
const regex = /^\s*(true|1)\s*$/i;
|
|
13
|
+
return regex.test(s);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const REDIS_VERBOSE_LOG = process.env.REDIS_VERBOSE_LOG ? isTrue(process.env.REDIS_VERBOSE_LOG) : false;
|
|
17
|
+
const REDIS_POOL_SIZE = process.env.REDIS_POOL_SIZE ? parseInt(process.env.REDIS_POOL_SIZE) : 15;
|
|
18
|
+
|
|
19
|
+
class RedisStateStore implements IStateStore {
|
|
20
|
+
keyPrefix: string;
|
|
21
|
+
volatileKeyTTL: number;
|
|
22
|
+
pool: any[];
|
|
23
|
+
|
|
24
|
+
constructor(keyPrefix: string, opts: any) {
|
|
25
|
+
this.keyPrefix = keyPrefix;
|
|
26
|
+
if (opts.version) {
|
|
27
|
+
const prependPrefix = opts.version.replace(/\./g, "X");
|
|
28
|
+
this.keyPrefix = prependPrefix + this.keyPrefix;
|
|
29
|
+
debug(`Prepending keyprefix with ${prependPrefix} => ${this.keyPrefix}`);
|
|
30
|
+
}
|
|
31
|
+
this.volatileKeyTTL = DEFAULT_VOLATILE_KEY_TTL;
|
|
32
|
+
if (opts.volatileKeyTTL) {
|
|
33
|
+
debug(`Overriding default, volatileKeyTTL=${opts.volatileKeyTTL}s`);
|
|
34
|
+
this.volatileKeyTTL = opts.volatileKeyTTL;
|
|
35
|
+
}
|
|
36
|
+
this.pool = this.createRedisPool(REDIS_POOL_SIZE, opts.redisUrl);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
createRedisPool(size: number, redisUrl: string): any[] {
|
|
40
|
+
const pool: any[] = [];
|
|
41
|
+
for (let i = 0; i < size; i++) {
|
|
42
|
+
const client = new Redis(redisUrl);
|
|
43
|
+
pool.push(client);
|
|
44
|
+
}
|
|
45
|
+
return pool;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async getClientFromPool(maxWaitTime: number = 5000): Promise<any> {
|
|
49
|
+
const startTime = Date.now();
|
|
50
|
+
while (this.pool.length === 0) {
|
|
51
|
+
if (Date.now() - startTime > maxWaitTime) {
|
|
52
|
+
console.error("[!] Timeout: No available Redis clients in the pool");
|
|
53
|
+
}
|
|
54
|
+
await timer(100);
|
|
55
|
+
}
|
|
56
|
+
return this.pool.pop();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
returnClientToPool(client: any): void {
|
|
60
|
+
this.pool.push(client);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async initAsync(id: string, initData: Record<string, any>): Promise<Record<string, any>> {
|
|
64
|
+
const isInitiated = await this.getAsync(id, "_initiated");
|
|
65
|
+
let data: Record<string, any> = {};
|
|
66
|
+
if (!isInitiated) {
|
|
67
|
+
debug(`${this.keyPrefix}:${id}: Initiating keys ${Object.keys(initData)} with init data`);
|
|
68
|
+
await this.setValues(id, initData);
|
|
69
|
+
await this.setAsync(id, "_initiated", true);
|
|
70
|
+
} else {
|
|
71
|
+
debug(`${this.keyPrefix}:${id}: Already initiated, not initiating with init data`);
|
|
72
|
+
for (const key of Object.keys(initData)) {
|
|
73
|
+
debug(`${this.keyPrefix}:${id}: Initiating key ${key} with data from store`);
|
|
74
|
+
data[key] = await this.getAsync(id, key);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return data;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async resetAsync(id: string, initData: Record<string, any>): Promise<void> {
|
|
81
|
+
await this.setAsync(id, "_initiated", false);
|
|
82
|
+
await this.initAsync(id, initData);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async resetAllAsync(): Promise<void> {
|
|
86
|
+
const client = await this.getClientFromPool();
|
|
87
|
+
try {
|
|
88
|
+
await client.flushall();
|
|
89
|
+
console.log("Flushed Redis db");
|
|
90
|
+
} catch (err) {
|
|
91
|
+
console.error("Error flushing Redis db:", err);
|
|
92
|
+
} finally {
|
|
93
|
+
this.returnClientToPool(client);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async getValues(id: string, keys: string[]): Promise<Record<string, any>> {
|
|
98
|
+
const client = await this.getClientFromPool();
|
|
99
|
+
const pipeline = client.pipeline();
|
|
100
|
+
let data: Record<string, any> = {};
|
|
101
|
+
const startMs = Date.now();
|
|
102
|
+
|
|
103
|
+
for (const key of keys) {
|
|
104
|
+
const storeKey = `${this.keyPrefix}${id}${key}`;
|
|
105
|
+
pipeline.get(storeKey);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const results = await pipeline.exec();
|
|
109
|
+
const ops = pipeline.length;
|
|
110
|
+
|
|
111
|
+
results.forEach((result: any, index: number) => {
|
|
112
|
+
const reply = result[1];
|
|
113
|
+
const storeKey = `${this.keyPrefix}${id}${keys[index]}`;
|
|
114
|
+
debug(`REDIS get(pipeline) ${storeKey}:${reply ? reply.length + " chars" : "null"}`);
|
|
115
|
+
if (reply) {
|
|
116
|
+
try {
|
|
117
|
+
data[keys[index]] = JSON.parse(reply);
|
|
118
|
+
} catch (err) {
|
|
119
|
+
console.error(`REDIS get(pipeline): Failed to parse ${storeKey} data: '${reply}'`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const ioTimeMs = Date.now() - startMs;
|
|
125
|
+
cloudWatchLog(!REDIS_VERBOSE_LOG, "redis", { event: "getValues", operations: ops, ioTimeMs: ioTimeMs });
|
|
126
|
+
this.returnClientToPool(client);
|
|
127
|
+
return data;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async getAsync(id: string, key: string): Promise<any> {
|
|
131
|
+
const client = await this.getClientFromPool();
|
|
132
|
+
const startMs = Date.now();
|
|
133
|
+
const storeKey = `${this.keyPrefix}${id}${key}`;
|
|
134
|
+
const reply = await client.get(storeKey);
|
|
135
|
+
const ioTimeMs = Date.now() - startMs;
|
|
136
|
+
|
|
137
|
+
debug(`REDIS get ${storeKey}:${reply ? reply.length + " chars" : "null"} (${ioTimeMs}ms) ${ioTimeMs > 1000 ? "REDISSLOW!" : ""}`);
|
|
138
|
+
this.returnClientToPool(client);
|
|
139
|
+
|
|
140
|
+
if (reply) {
|
|
141
|
+
try {
|
|
142
|
+
cloudWatchLog(!REDIS_VERBOSE_LOG, "redis", { event: "get", operations: 1, ioTimeMs: ioTimeMs });
|
|
143
|
+
return JSON.parse(reply);
|
|
144
|
+
} catch (err) {
|
|
145
|
+
console.error(`REDIS get: Failed to parse ${storeKey} data: '${reply}'`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async setValues(id: string, data: Record<string, any>): Promise<Record<string, any>> {
|
|
152
|
+
const client = await this.getClientFromPool();
|
|
153
|
+
const returnData: Record<string, any> = {};
|
|
154
|
+
const startMs = Date.now();
|
|
155
|
+
const pipeline = client.pipeline();
|
|
156
|
+
|
|
157
|
+
for (const key of Object.keys(data)) {
|
|
158
|
+
const storeKey = `${this.keyPrefix}${id}${key}`;
|
|
159
|
+
const value = data[key];
|
|
160
|
+
pipeline.set(storeKey, JSON.stringify(value));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const results = await pipeline.exec();
|
|
164
|
+
const ops = pipeline.length;
|
|
165
|
+
|
|
166
|
+
results.forEach((result: any, index: number) => {
|
|
167
|
+
const storeKey = `${this.keyPrefix}${id}${Object.keys(data)[index]}`;
|
|
168
|
+
debug(`REDIS set(pipeline) ${storeKey}: ${result[1]}`);
|
|
169
|
+
returnData[Object.keys(data)[index]] = data[Object.keys(data)[index]];
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const ioTimeMs = Date.now() - startMs;
|
|
173
|
+
cloudWatchLog(!REDIS_VERBOSE_LOG, "redis", { event: "setValues", operations: ops, ioTimeMs: ioTimeMs });
|
|
174
|
+
this.returnClientToPool(client);
|
|
175
|
+
return returnData;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async setAsync(id: string, key: string, value: any): Promise<any> {
|
|
179
|
+
const client = await this.getClientFromPool();
|
|
180
|
+
const startMs = Date.now();
|
|
181
|
+
const storeKey = `${this.keyPrefix}${id}${key}`;
|
|
182
|
+
const res = await client.set(storeKey, JSON.stringify(value));
|
|
183
|
+
const ioTimeMs = Date.now() - startMs;
|
|
184
|
+
|
|
185
|
+
debug(`REDIS set ${storeKey}: ${res} (${ioTimeMs}ms) ${ioTimeMs > 1000 ? "REDISSLOW!" : ""}`);
|
|
186
|
+
cloudWatchLog(!REDIS_VERBOSE_LOG, "redis", { event: "set", operations: 1, ioTimeMs: ioTimeMs });
|
|
187
|
+
this.returnClientToPool(client);
|
|
188
|
+
return value;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async setVolatileAsync(id: string, key: string, value: any): Promise<any> {
|
|
192
|
+
const data = await this.setAsync(id, key, value);
|
|
193
|
+
const storeKey = `${this.keyPrefix}${id}${key}`;
|
|
194
|
+
const client = await this.getClientFromPool();
|
|
195
|
+
|
|
196
|
+
await client.expire(storeKey, this.volatileKeyTTL);
|
|
197
|
+
debug(`REDIS expire ${storeKey} ${this.volatileKeyTTL}s`);
|
|
198
|
+
this.returnClientToPool(client);
|
|
199
|
+
return data;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async removeAsync(id: string, key: string): Promise<any> {
|
|
203
|
+
const client = await this.getClientFromPool();
|
|
204
|
+
const startMs = Date.now();
|
|
205
|
+
const storeKey = `${this.keyPrefix}${id}${key}`;
|
|
206
|
+
const res = await client.del(storeKey);
|
|
207
|
+
const ioTimeMs = Date.now() - startMs;
|
|
208
|
+
|
|
209
|
+
debug(`REDIS remove ${storeKey}: (${ioTimeMs}ms) ${ioTimeMs > 1000 ? "REDISSLOW!" : ""}`);
|
|
210
|
+
cloudWatchLog(!REDIS_VERBOSE_LOG, "redis", { event: "remove", operations: 1, ioTimeMs: ioTimeMs });
|
|
211
|
+
this.returnClientToPool(client);
|
|
212
|
+
return res;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
module.exports = RedisStateStore;
|