@checkstack/healthcheck-redis-backend 0.1.13 → 0.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/CHANGELOG.md +54 -0
- package/package.json +6 -6
- package/src/command-collector.test.ts +8 -6
- package/src/command-collector.ts +32 -35
- package/src/strategy.test.ts +30 -31
- package/src/strategy.ts +46 -59
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,59 @@
|
|
|
1
1
|
# @checkstack/healthcheck-redis-backend
|
|
2
2
|
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 3dd1914: Migrate health check strategies to VersionedAggregated with \_type discriminator
|
|
8
|
+
|
|
9
|
+
All 13 health check strategies now use `VersionedAggregated` for their `aggregatedResult` property, enabling automatic bucket merging with 100% mathematical fidelity.
|
|
10
|
+
|
|
11
|
+
**Key changes:**
|
|
12
|
+
|
|
13
|
+
- **`_type` discriminator**: All aggregated state objects now include a required `_type` field (`"average"`, `"rate"`, `"counter"`, `"minmax"`) for reliable type detection
|
|
14
|
+
- The `HealthCheckStrategy` interface now requires `aggregatedResult` to be a `VersionedAggregated<AggregatedResultShape>`
|
|
15
|
+
- Strategy/collector `mergeResult` methods return state objects with `_type` (e.g., `{ _type: "average", _sum, _count, avg }`)
|
|
16
|
+
- `mergeAggregatedBucketResults`, `combineBuckets`, and `reaggregateBuckets` now require `registry` and `strategyId` parameters
|
|
17
|
+
- `HealthCheckService` constructor now requires both `registry` and `collectorRegistry` parameters
|
|
18
|
+
- Frontend `extractComputedValue` now uses `_type` discriminator for robust type detection
|
|
19
|
+
|
|
20
|
+
**Breaking Change**: State objects now require `_type`. Merge functions automatically add `_type` to output. The bucket merging functions and `HealthCheckService` now require additional required parameters.
|
|
21
|
+
|
|
22
|
+
### Patch Changes
|
|
23
|
+
|
|
24
|
+
- Updated dependencies [3dd1914]
|
|
25
|
+
- @checkstack/backend-api@0.7.0
|
|
26
|
+
|
|
27
|
+
## 0.1.14
|
|
28
|
+
|
|
29
|
+
### Patch Changes
|
|
30
|
+
|
|
31
|
+
- 48c2080: Migrate aggregation from batch to incremental (`mergeResult`)
|
|
32
|
+
|
|
33
|
+
### Breaking Changes (Internal)
|
|
34
|
+
|
|
35
|
+
- Replaced `aggregateResult(runs[])` with `mergeResult(existing, run)` interface across all HealthCheckStrategy and CollectorStrategy implementations
|
|
36
|
+
|
|
37
|
+
### New Features
|
|
38
|
+
|
|
39
|
+
- Added incremental aggregation utilities in `@checkstack/backend-api`:
|
|
40
|
+
- `mergeCounter()` - track occurrences
|
|
41
|
+
- `mergeAverage()` - track sum/count, compute avg
|
|
42
|
+
- `mergeRate()` - track success/total, compute %
|
|
43
|
+
- `mergeMinMax()` - track min/max values
|
|
44
|
+
- Exported Zod schemas for internal state: `averageStateSchema`, `rateStateSchema`, `minMaxStateSchema`, `counterStateSchema`
|
|
45
|
+
|
|
46
|
+
### Improvements
|
|
47
|
+
|
|
48
|
+
- Enables O(1) storage overhead by maintaining incremental aggregation state
|
|
49
|
+
- Prepares for real-time hourly aggregation without batch accumulation
|
|
50
|
+
|
|
51
|
+
- Updated dependencies [f676e11]
|
|
52
|
+
- Updated dependencies [48c2080]
|
|
53
|
+
- @checkstack/common@0.6.2
|
|
54
|
+
- @checkstack/backend-api@0.6.0
|
|
55
|
+
- @checkstack/healthcheck-common@0.8.2
|
|
56
|
+
|
|
3
57
|
## 0.1.13
|
|
4
58
|
|
|
5
59
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@checkstack/healthcheck-redis-backend",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"scripts": {
|
|
@@ -9,15 +9,15 @@
|
|
|
9
9
|
"lint:code": "eslint . --max-warnings 0"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@checkstack/backend-api": "0.5.
|
|
13
|
-
"@checkstack/common": "0.6.
|
|
14
|
-
"@checkstack/healthcheck-common": "0.
|
|
12
|
+
"@checkstack/backend-api": "0.5.2",
|
|
13
|
+
"@checkstack/common": "0.6.1",
|
|
14
|
+
"@checkstack/healthcheck-common": "0.8.1",
|
|
15
15
|
"ioredis": "^5.3.0"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
18
|
"@types/bun": "^1.0.0",
|
|
19
19
|
"typescript": "^5.0.0",
|
|
20
|
-
"@checkstack/tsconfig": "0.0.
|
|
21
|
-
"@checkstack/scripts": "0.1.
|
|
20
|
+
"@checkstack/tsconfig": "0.0.3",
|
|
21
|
+
"@checkstack/scripts": "0.1.1"
|
|
22
22
|
}
|
|
23
23
|
}
|
|
@@ -79,7 +79,7 @@ describe("CommandCollector", () => {
|
|
|
79
79
|
});
|
|
80
80
|
});
|
|
81
81
|
|
|
82
|
-
describe("
|
|
82
|
+
describe("mergeResult", () => {
|
|
83
83
|
it("should calculate average response time and success rate", () => {
|
|
84
84
|
const collector = new CommandCollector();
|
|
85
85
|
const runs = [
|
|
@@ -101,10 +101,11 @@ describe("CommandCollector", () => {
|
|
|
101
101
|
},
|
|
102
102
|
];
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
let aggregated = collector.mergeResult(undefined, runs[0]);
|
|
105
|
+
aggregated = collector.mergeResult(aggregated, runs[1]);
|
|
105
106
|
|
|
106
|
-
expect(aggregated.avgResponseTimeMs).toBe(10);
|
|
107
|
-
expect(aggregated.successRate).toBe(100);
|
|
107
|
+
expect(aggregated.avgResponseTimeMs.avg).toBe(10);
|
|
108
|
+
expect(aggregated.successRate.rate).toBe(100);
|
|
108
109
|
});
|
|
109
110
|
|
|
110
111
|
it("should calculate success rate correctly", () => {
|
|
@@ -128,9 +129,10 @@ describe("CommandCollector", () => {
|
|
|
128
129
|
},
|
|
129
130
|
];
|
|
130
131
|
|
|
131
|
-
|
|
132
|
+
let aggregated = collector.mergeResult(undefined, runs[0]);
|
|
133
|
+
aggregated = collector.mergeResult(aggregated, runs[1]);
|
|
132
134
|
|
|
133
|
-
expect(aggregated.successRate).toBe(50);
|
|
135
|
+
expect(aggregated.successRate.rate).toBe(50);
|
|
134
136
|
});
|
|
135
137
|
});
|
|
136
138
|
|
package/src/command-collector.ts
CHANGED
|
@@ -4,6 +4,12 @@ import {
|
|
|
4
4
|
type HealthCheckRunForAggregation,
|
|
5
5
|
type CollectorResult,
|
|
6
6
|
type CollectorStrategy,
|
|
7
|
+
mergeAverage,
|
|
8
|
+
mergeRate,
|
|
9
|
+
VersionedAggregated,
|
|
10
|
+
aggregatedAverage,
|
|
11
|
+
aggregatedRate,
|
|
12
|
+
type InferAggregatedResult,
|
|
7
13
|
} from "@checkstack/backend-api";
|
|
8
14
|
import {
|
|
9
15
|
healthResultNumber,
|
|
@@ -53,20 +59,24 @@ const commandResultSchema = healthResultSchema({
|
|
|
53
59
|
|
|
54
60
|
export type CommandResult = z.infer<typeof commandResultSchema>;
|
|
55
61
|
|
|
56
|
-
|
|
57
|
-
|
|
62
|
+
// Aggregated result fields definition
|
|
63
|
+
const commandAggregatedFields = {
|
|
64
|
+
avgResponseTimeMs: aggregatedAverage({
|
|
58
65
|
"x-chart-type": "line",
|
|
59
66
|
"x-chart-label": "Avg Response Time",
|
|
60
67
|
"x-chart-unit": "ms",
|
|
61
68
|
}),
|
|
62
|
-
successRate:
|
|
69
|
+
successRate: aggregatedRate({
|
|
63
70
|
"x-chart-type": "gauge",
|
|
64
71
|
"x-chart-label": "Success Rate",
|
|
65
72
|
"x-chart-unit": "%",
|
|
66
73
|
}),
|
|
67
|
-
}
|
|
74
|
+
};
|
|
68
75
|
|
|
69
|
-
|
|
76
|
+
// Type inferred from field definitions
|
|
77
|
+
export type CommandAggregatedResult = InferAggregatedResult<
|
|
78
|
+
typeof commandAggregatedFields
|
|
79
|
+
>;
|
|
70
80
|
|
|
71
81
|
// ============================================================================
|
|
72
82
|
// COMMAND COLLECTOR
|
|
@@ -76,15 +86,12 @@ export type CommandAggregatedResult = z.infer<typeof commandAggregatedSchema>;
|
|
|
76
86
|
* Built-in Redis command collector.
|
|
77
87
|
* Executes Redis commands and checks results.
|
|
78
88
|
*/
|
|
79
|
-
export class CommandCollector
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
CommandAggregatedResult
|
|
86
|
-
>
|
|
87
|
-
{
|
|
89
|
+
export class CommandCollector implements CollectorStrategy<
|
|
90
|
+
RedisTransportClient,
|
|
91
|
+
CommandConfig,
|
|
92
|
+
CommandResult,
|
|
93
|
+
CommandAggregatedResult
|
|
94
|
+
> {
|
|
88
95
|
id = "command";
|
|
89
96
|
displayName = "Redis Command";
|
|
90
97
|
description = "Execute a Redis command and check the result";
|
|
@@ -95,9 +102,9 @@ export class CommandCollector
|
|
|
95
102
|
|
|
96
103
|
config = new Versioned({ version: 1, schema: commandConfigSchema });
|
|
97
104
|
result = new Versioned({ version: 1, schema: commandResultSchema });
|
|
98
|
-
aggregatedResult = new
|
|
105
|
+
aggregatedResult = new VersionedAggregated({
|
|
99
106
|
version: 1,
|
|
100
|
-
|
|
107
|
+
fields: commandAggregatedFields,
|
|
101
108
|
});
|
|
102
109
|
|
|
103
110
|
async execute({
|
|
@@ -127,28 +134,18 @@ export class CommandCollector
|
|
|
127
134
|
};
|
|
128
135
|
}
|
|
129
136
|
|
|
130
|
-
|
|
131
|
-
|
|
137
|
+
mergeResult(
|
|
138
|
+
existing: CommandAggregatedResult | undefined,
|
|
139
|
+
run: HealthCheckRunForAggregation<CommandResult>,
|
|
132
140
|
): CommandAggregatedResult {
|
|
133
|
-
const
|
|
134
|
-
.map((r) => r.metadata?.responseTimeMs)
|
|
135
|
-
.filter((v): v is number => typeof v === "number");
|
|
136
|
-
|
|
137
|
-
const successes = runs
|
|
138
|
-
.map((r) => r.metadata?.success)
|
|
139
|
-
.filter((v): v is boolean => typeof v === "boolean");
|
|
140
|
-
|
|
141
|
-
const successCount = successes.filter(Boolean).length;
|
|
141
|
+
const metadata = run.metadata;
|
|
142
142
|
|
|
143
143
|
return {
|
|
144
|
-
avgResponseTimeMs:
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
successRate:
|
|
149
|
-
successes.length > 0
|
|
150
|
-
? Math.round((successCount / successes.length) * 100)
|
|
151
|
-
: 0,
|
|
144
|
+
avgResponseTimeMs: mergeAverage(
|
|
145
|
+
existing?.avgResponseTimeMs,
|
|
146
|
+
metadata?.responseTimeMs,
|
|
147
|
+
),
|
|
148
|
+
successRate: mergeRate(existing?.successRate, metadata?.success),
|
|
152
149
|
};
|
|
153
150
|
}
|
|
154
151
|
}
|
package/src/strategy.test.ts
CHANGED
|
@@ -12,17 +12,17 @@ describe("RedisHealthCheckStrategy", () => {
|
|
|
12
12
|
pingResponse?: string;
|
|
13
13
|
infoResponse?: string;
|
|
14
14
|
pingError?: Error;
|
|
15
|
-
} = {}
|
|
15
|
+
} = {},
|
|
16
16
|
): RedisConnection => ({
|
|
17
17
|
ping: mock(() =>
|
|
18
18
|
config.pingError
|
|
19
19
|
? Promise.reject(config.pingError)
|
|
20
|
-
: Promise.resolve(config.pingResponse ?? "PONG")
|
|
20
|
+
: Promise.resolve(config.pingResponse ?? "PONG"),
|
|
21
21
|
),
|
|
22
22
|
info: mock(() =>
|
|
23
23
|
Promise.resolve(
|
|
24
|
-
config.infoResponse ?? "redis_version:7.0.0\r\nrole:master\r\n"
|
|
25
|
-
)
|
|
24
|
+
config.infoResponse ?? "redis_version:7.0.0\r\nrole:master\r\n",
|
|
25
|
+
),
|
|
26
26
|
),
|
|
27
27
|
get: mock(() => Promise.resolve(undefined)),
|
|
28
28
|
quit: mock(() => Promise.resolve("OK")),
|
|
@@ -35,12 +35,12 @@ describe("RedisHealthCheckStrategy", () => {
|
|
|
35
35
|
infoResponse?: string;
|
|
36
36
|
pingError?: Error;
|
|
37
37
|
connectError?: Error;
|
|
38
|
-
} = {}
|
|
38
|
+
} = {},
|
|
39
39
|
): RedisClient => ({
|
|
40
40
|
connect: mock(() =>
|
|
41
41
|
config.connectError
|
|
42
42
|
? Promise.reject(config.connectError)
|
|
43
|
-
: Promise.resolve(createMockConnection(config))
|
|
43
|
+
: Promise.resolve(createMockConnection(config)),
|
|
44
44
|
),
|
|
45
45
|
});
|
|
46
46
|
|
|
@@ -63,7 +63,7 @@ describe("RedisHealthCheckStrategy", () => {
|
|
|
63
63
|
|
|
64
64
|
it("should throw for connection error", async () => {
|
|
65
65
|
const strategy = new RedisHealthCheckStrategy(
|
|
66
|
-
createMockClient({ connectError: new Error("Connection refused") })
|
|
66
|
+
createMockClient({ connectError: new Error("Connection refused") }),
|
|
67
67
|
);
|
|
68
68
|
|
|
69
69
|
await expect(
|
|
@@ -71,7 +71,7 @@ describe("RedisHealthCheckStrategy", () => {
|
|
|
71
71
|
host: "localhost",
|
|
72
72
|
port: 6379,
|
|
73
73
|
timeout: 5000,
|
|
74
|
-
})
|
|
74
|
+
}),
|
|
75
75
|
).rejects.toThrow("Connection refused");
|
|
76
76
|
});
|
|
77
77
|
});
|
|
@@ -94,7 +94,7 @@ describe("RedisHealthCheckStrategy", () => {
|
|
|
94
94
|
|
|
95
95
|
it("should return error for ping failure", async () => {
|
|
96
96
|
const strategy = new RedisHealthCheckStrategy(
|
|
97
|
-
createMockClient({ pingError: new Error("NOAUTH") })
|
|
97
|
+
createMockClient({ pingError: new Error("NOAUTH") }),
|
|
98
98
|
);
|
|
99
99
|
const connectedClient = await strategy.createClient({
|
|
100
100
|
host: "localhost",
|
|
@@ -128,7 +128,7 @@ describe("RedisHealthCheckStrategy", () => {
|
|
|
128
128
|
});
|
|
129
129
|
});
|
|
130
130
|
|
|
131
|
-
describe("
|
|
131
|
+
describe("mergeResult", () => {
|
|
132
132
|
it("should calculate averages correctly", () => {
|
|
133
133
|
const strategy = new RedisHealthCheckStrategy();
|
|
134
134
|
const runs = [
|
|
@@ -160,35 +160,34 @@ describe("RedisHealthCheckStrategy", () => {
|
|
|
160
160
|
},
|
|
161
161
|
];
|
|
162
162
|
|
|
163
|
-
|
|
163
|
+
let aggregated = strategy.mergeResult(undefined, runs[0]);
|
|
164
|
+
aggregated = strategy.mergeResult(aggregated, runs[1]);
|
|
164
165
|
|
|
165
|
-
expect(aggregated.avgConnectionTime).toBe(10);
|
|
166
|
-
expect(aggregated.successRate).toBe(100);
|
|
167
|
-
expect(aggregated.errorCount).toBe(0);
|
|
166
|
+
expect(aggregated.avgConnectionTime.avg).toBe(10);
|
|
167
|
+
expect(aggregated.successRate.rate).toBe(100);
|
|
168
|
+
expect(aggregated.errorCount.count).toBe(0);
|
|
168
169
|
});
|
|
169
170
|
|
|
170
171
|
it("should count errors", () => {
|
|
171
172
|
const strategy = new RedisHealthCheckStrategy();
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
error: "Connection refused",
|
|
184
|
-
},
|
|
173
|
+
const run = {
|
|
174
|
+
id: "1",
|
|
175
|
+
status: "unhealthy" as const,
|
|
176
|
+
latencyMs: 100,
|
|
177
|
+
checkId: "c1",
|
|
178
|
+
timestamp: new Date(),
|
|
179
|
+
metadata: {
|
|
180
|
+
connected: false,
|
|
181
|
+
connectionTimeMs: 100,
|
|
182
|
+
pingSuccess: false,
|
|
183
|
+
error: "Connection refused",
|
|
185
184
|
},
|
|
186
|
-
|
|
185
|
+
};
|
|
187
186
|
|
|
188
|
-
const aggregated = strategy.
|
|
187
|
+
const aggregated = strategy.mergeResult(undefined, run);
|
|
189
188
|
|
|
190
|
-
expect(aggregated.errorCount).toBe(1);
|
|
191
|
-
expect(aggregated.successRate).toBe(0);
|
|
189
|
+
expect(aggregated.errorCount.count).toBe(1);
|
|
190
|
+
expect(aggregated.successRate.rate).toBe(0);
|
|
192
191
|
});
|
|
193
192
|
});
|
|
194
193
|
});
|
package/src/strategy.ts
CHANGED
|
@@ -3,11 +3,21 @@ import {
|
|
|
3
3
|
HealthCheckStrategy,
|
|
4
4
|
HealthCheckRunForAggregation,
|
|
5
5
|
Versioned,
|
|
6
|
+
VersionedAggregated,
|
|
7
|
+
aggregatedAverage,
|
|
8
|
+
aggregatedMinMax,
|
|
9
|
+
aggregatedRate,
|
|
10
|
+
aggregatedCounter,
|
|
11
|
+
mergeAverage,
|
|
12
|
+
mergeRate,
|
|
13
|
+
mergeCounter,
|
|
14
|
+
mergeMinMax,
|
|
6
15
|
z,
|
|
7
16
|
configString,
|
|
8
17
|
configNumber,
|
|
9
18
|
configBoolean,
|
|
10
19
|
type ConnectedClient,
|
|
20
|
+
type InferAggregatedResult,
|
|
11
21
|
} from "@checkstack/backend-api";
|
|
12
22
|
import {
|
|
13
23
|
healthResultBoolean,
|
|
@@ -75,32 +85,32 @@ const redisResultSchema = healthResultSchema({
|
|
|
75
85
|
|
|
76
86
|
type RedisResult = z.infer<typeof redisResultSchema>;
|
|
77
87
|
|
|
78
|
-
/**
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const redisAggregatedSchema = healthResultSchema({
|
|
82
|
-
avgConnectionTime: healthResultNumber({
|
|
88
|
+
/** Aggregated field definitions for bucket merging */
|
|
89
|
+
const redisAggregatedFields = {
|
|
90
|
+
avgConnectionTime: aggregatedAverage({
|
|
83
91
|
"x-chart-type": "line",
|
|
84
92
|
"x-chart-label": "Avg Connection Time",
|
|
85
93
|
"x-chart-unit": "ms",
|
|
86
94
|
}),
|
|
87
|
-
maxConnectionTime:
|
|
95
|
+
maxConnectionTime: aggregatedMinMax({
|
|
88
96
|
"x-chart-type": "line",
|
|
89
97
|
"x-chart-label": "Max Connection Time",
|
|
90
98
|
"x-chart-unit": "ms",
|
|
91
99
|
}),
|
|
92
|
-
successRate:
|
|
100
|
+
successRate: aggregatedRate({
|
|
93
101
|
"x-chart-type": "gauge",
|
|
94
102
|
"x-chart-label": "Success Rate",
|
|
95
103
|
"x-chart-unit": "%",
|
|
96
104
|
}),
|
|
97
|
-
errorCount:
|
|
105
|
+
errorCount: aggregatedCounter({
|
|
98
106
|
"x-chart-type": "counter",
|
|
99
107
|
"x-chart-label": "Errors",
|
|
100
108
|
}),
|
|
101
|
-
}
|
|
109
|
+
};
|
|
102
110
|
|
|
103
|
-
type RedisAggregatedResult =
|
|
111
|
+
type RedisAggregatedResult = InferAggregatedResult<
|
|
112
|
+
typeof redisAggregatedFields
|
|
113
|
+
>;
|
|
104
114
|
|
|
105
115
|
// ============================================================================
|
|
106
116
|
// REDIS CLIENT INTERFACE (for testability)
|
|
@@ -160,15 +170,12 @@ const defaultRedisClient: RedisClient = {
|
|
|
160
170
|
// STRATEGY
|
|
161
171
|
// ============================================================================
|
|
162
172
|
|
|
163
|
-
export class RedisHealthCheckStrategy
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
RedisAggregatedResult
|
|
170
|
-
>
|
|
171
|
-
{
|
|
173
|
+
export class RedisHealthCheckStrategy implements HealthCheckStrategy<
|
|
174
|
+
RedisConfig,
|
|
175
|
+
RedisTransportClient,
|
|
176
|
+
RedisResult,
|
|
177
|
+
typeof redisAggregatedFields
|
|
178
|
+
> {
|
|
172
179
|
id = "redis";
|
|
173
180
|
displayName = "Redis Health Check";
|
|
174
181
|
description = "Redis server connectivity and health monitoring";
|
|
@@ -205,58 +212,38 @@ export class RedisHealthCheckStrategy
|
|
|
205
212
|
],
|
|
206
213
|
});
|
|
207
214
|
|
|
208
|
-
aggregatedResult
|
|
215
|
+
aggregatedResult = new VersionedAggregated({
|
|
209
216
|
version: 1,
|
|
210
|
-
|
|
217
|
+
fields: redisAggregatedFields,
|
|
211
218
|
});
|
|
212
219
|
|
|
213
|
-
|
|
214
|
-
|
|
220
|
+
mergeResult(
|
|
221
|
+
existing: RedisAggregatedResult | undefined,
|
|
222
|
+
run: HealthCheckRunForAggregation<RedisResult>,
|
|
215
223
|
): RedisAggregatedResult {
|
|
216
|
-
const
|
|
224
|
+
const metadata = run.metadata;
|
|
217
225
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
successRate: 0,
|
|
223
|
-
errorCount: 0,
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
+
const avgConnectionTime = mergeAverage(
|
|
227
|
+
existing?.avgConnectionTime,
|
|
228
|
+
metadata?.connectionTimeMs,
|
|
229
|
+
);
|
|
226
230
|
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
231
|
+
const maxConnectionTime = mergeMinMax(
|
|
232
|
+
existing?.maxConnectionTime,
|
|
233
|
+
metadata?.connectionTimeMs,
|
|
234
|
+
);
|
|
230
235
|
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
? Math.round(
|
|
234
|
-
connectionTimes.reduce((a, b) => a + b, 0) / connectionTimes.length
|
|
235
|
-
)
|
|
236
|
-
: 0;
|
|
236
|
+
const isSuccess = metadata?.connected ?? false;
|
|
237
|
+
const successRate = mergeRate(existing?.successRate, isSuccess);
|
|
237
238
|
|
|
238
|
-
const
|
|
239
|
-
|
|
239
|
+
const hasError = metadata?.error !== undefined;
|
|
240
|
+
const errorCount = mergeCounter(existing?.errorCount, hasError);
|
|
240
241
|
|
|
241
|
-
|
|
242
|
-
(r) => r.metadata?.connected === true
|
|
243
|
-
).length;
|
|
244
|
-
const successRate = Math.round((successCount / validRuns.length) * 100);
|
|
245
|
-
|
|
246
|
-
const errorCount = validRuns.filter(
|
|
247
|
-
(r) => r.metadata?.error !== undefined
|
|
248
|
-
).length;
|
|
249
|
-
|
|
250
|
-
return {
|
|
251
|
-
avgConnectionTime,
|
|
252
|
-
maxConnectionTime,
|
|
253
|
-
successRate,
|
|
254
|
-
errorCount,
|
|
255
|
-
};
|
|
242
|
+
return { avgConnectionTime, maxConnectionTime, successRate, errorCount };
|
|
256
243
|
}
|
|
257
244
|
|
|
258
245
|
async createClient(
|
|
259
|
-
config: RedisConfigInput
|
|
246
|
+
config: RedisConfigInput,
|
|
260
247
|
): Promise<ConnectedClient<RedisTransportClient>> {
|
|
261
248
|
const validatedConfig = this.config.validate(config);
|
|
262
249
|
|