@checkstack/healthcheck-postgres-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/query-collector.test.ts +8 -6
- package/src/query-collector.ts +32 -35
- package/src/strategy.test.ts +29 -29
- package/src/strategy.ts +46 -59
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,59 @@
|
|
|
1
1
|
# @checkstack/healthcheck-postgres-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-postgres-backend",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"scripts": {
|
|
@@ -9,16 +9,16 @@
|
|
|
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
|
"pg": "^8.11.0"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
18
|
"@types/bun": "^1.0.0",
|
|
19
19
|
"@types/pg": "^8.11.0",
|
|
20
20
|
"typescript": "^5.0.0",
|
|
21
|
-
"@checkstack/tsconfig": "0.0.
|
|
22
|
-
"@checkstack/scripts": "0.1.
|
|
21
|
+
"@checkstack/tsconfig": "0.0.3",
|
|
22
|
+
"@checkstack/scripts": "0.1.1"
|
|
23
23
|
}
|
|
24
24
|
}
|
|
@@ -64,7 +64,7 @@ describe("QueryCollector", () => {
|
|
|
64
64
|
});
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
-
describe("
|
|
67
|
+
describe("mergeResult", () => {
|
|
68
68
|
it("should calculate average execution time and success rate", () => {
|
|
69
69
|
const collector = new QueryCollector();
|
|
70
70
|
const runs = [
|
|
@@ -86,10 +86,11 @@ describe("QueryCollector", () => {
|
|
|
86
86
|
},
|
|
87
87
|
];
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
let aggregated = collector.mergeResult(undefined, runs[0]);
|
|
90
|
+
aggregated = collector.mergeResult(aggregated, runs[1]);
|
|
90
91
|
|
|
91
|
-
expect(aggregated.avgExecutionTimeMs).toBe(75);
|
|
92
|
-
expect(aggregated.successRate).toBe(100);
|
|
92
|
+
expect(aggregated.avgExecutionTimeMs.avg).toBe(75);
|
|
93
|
+
expect(aggregated.successRate.rate).toBe(100);
|
|
93
94
|
});
|
|
94
95
|
|
|
95
96
|
it("should calculate success rate correctly", () => {
|
|
@@ -113,9 +114,10 @@ describe("QueryCollector", () => {
|
|
|
113
114
|
},
|
|
114
115
|
];
|
|
115
116
|
|
|
116
|
-
|
|
117
|
+
let aggregated = collector.mergeResult(undefined, runs[0]);
|
|
118
|
+
aggregated = collector.mergeResult(aggregated, runs[1]);
|
|
117
119
|
|
|
118
|
-
expect(aggregated.successRate).toBe(50);
|
|
120
|
+
expect(aggregated.successRate.rate).toBe(50);
|
|
119
121
|
});
|
|
120
122
|
});
|
|
121
123
|
|
package/src/query-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,
|
|
@@ -45,20 +51,24 @@ const queryResultSchema = healthResultSchema({
|
|
|
45
51
|
|
|
46
52
|
export type QueryResult = z.infer<typeof queryResultSchema>;
|
|
47
53
|
|
|
48
|
-
|
|
49
|
-
|
|
54
|
+
// Aggregated result fields definition
|
|
55
|
+
const queryAggregatedFields = {
|
|
56
|
+
avgExecutionTimeMs: aggregatedAverage({
|
|
50
57
|
"x-chart-type": "line",
|
|
51
58
|
"x-chart-label": "Avg Execution Time",
|
|
52
59
|
"x-chart-unit": "ms",
|
|
53
60
|
}),
|
|
54
|
-
successRate:
|
|
61
|
+
successRate: aggregatedRate({
|
|
55
62
|
"x-chart-type": "gauge",
|
|
56
63
|
"x-chart-label": "Success Rate",
|
|
57
64
|
"x-chart-unit": "%",
|
|
58
65
|
}),
|
|
59
|
-
}
|
|
66
|
+
};
|
|
60
67
|
|
|
61
|
-
|
|
68
|
+
// Type inferred from field definitions
|
|
69
|
+
export type QueryAggregatedResult = InferAggregatedResult<
|
|
70
|
+
typeof queryAggregatedFields
|
|
71
|
+
>;
|
|
62
72
|
|
|
63
73
|
// ============================================================================
|
|
64
74
|
// QUERY COLLECTOR
|
|
@@ -68,15 +78,12 @@ export type QueryAggregatedResult = z.infer<typeof queryAggregatedSchema>;
|
|
|
68
78
|
* Built-in PostgreSQL query collector.
|
|
69
79
|
* Executes SQL queries and checks results.
|
|
70
80
|
*/
|
|
71
|
-
export class QueryCollector
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
QueryAggregatedResult
|
|
78
|
-
>
|
|
79
|
-
{
|
|
81
|
+
export class QueryCollector implements CollectorStrategy<
|
|
82
|
+
PostgresTransportClient,
|
|
83
|
+
QueryConfig,
|
|
84
|
+
QueryResult,
|
|
85
|
+
QueryAggregatedResult
|
|
86
|
+
> {
|
|
80
87
|
id = "query";
|
|
81
88
|
displayName = "SQL Query";
|
|
82
89
|
description = "Execute a SQL query and check the result";
|
|
@@ -87,9 +94,9 @@ export class QueryCollector
|
|
|
87
94
|
|
|
88
95
|
config = new Versioned({ version: 1, schema: queryConfigSchema });
|
|
89
96
|
result = new Versioned({ version: 1, schema: queryResultSchema });
|
|
90
|
-
aggregatedResult = new
|
|
97
|
+
aggregatedResult = new VersionedAggregated({
|
|
91
98
|
version: 1,
|
|
92
|
-
|
|
99
|
+
fields: queryAggregatedFields,
|
|
93
100
|
});
|
|
94
101
|
|
|
95
102
|
async execute({
|
|
@@ -115,28 +122,18 @@ export class QueryCollector
|
|
|
115
122
|
};
|
|
116
123
|
}
|
|
117
124
|
|
|
118
|
-
|
|
119
|
-
|
|
125
|
+
mergeResult(
|
|
126
|
+
existing: QueryAggregatedResult | undefined,
|
|
127
|
+
run: HealthCheckRunForAggregation<QueryResult>,
|
|
120
128
|
): QueryAggregatedResult {
|
|
121
|
-
const
|
|
122
|
-
.map((r) => r.metadata?.executionTimeMs)
|
|
123
|
-
.filter((v): v is number => typeof v === "number");
|
|
124
|
-
|
|
125
|
-
const successes = runs
|
|
126
|
-
.map((r) => r.metadata?.success)
|
|
127
|
-
.filter((v): v is boolean => typeof v === "boolean");
|
|
128
|
-
|
|
129
|
-
const successCount = successes.filter(Boolean).length;
|
|
129
|
+
const metadata = run.metadata;
|
|
130
130
|
|
|
131
131
|
return {
|
|
132
|
-
avgExecutionTimeMs:
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
successRate:
|
|
137
|
-
successes.length > 0
|
|
138
|
-
? Math.round((successCount / successes.length) * 100)
|
|
139
|
-
: 0,
|
|
132
|
+
avgExecutionTimeMs: mergeAverage(
|
|
133
|
+
existing?.avgExecutionTimeMs,
|
|
134
|
+
metadata?.executionTimeMs,
|
|
135
|
+
),
|
|
136
|
+
successRate: mergeRate(existing?.successRate, metadata?.success),
|
|
140
137
|
};
|
|
141
138
|
}
|
|
142
139
|
}
|
package/src/strategy.test.ts
CHANGED
|
@@ -8,7 +8,7 @@ describe("PostgresHealthCheckStrategy", () => {
|
|
|
8
8
|
rowCount?: number;
|
|
9
9
|
queryError?: Error;
|
|
10
10
|
connectError?: Error;
|
|
11
|
-
} = {}
|
|
11
|
+
} = {},
|
|
12
12
|
): DbClient => ({
|
|
13
13
|
connect: mock(() =>
|
|
14
14
|
config.connectError
|
|
@@ -17,10 +17,10 @@ describe("PostgresHealthCheckStrategy", () => {
|
|
|
17
17
|
query: mock(() =>
|
|
18
18
|
config.queryError
|
|
19
19
|
? Promise.reject(config.queryError)
|
|
20
|
-
: Promise.resolve({ rowCount: config.rowCount ?? 1 })
|
|
20
|
+
: Promise.resolve({ rowCount: config.rowCount ?? 1 }),
|
|
21
21
|
),
|
|
22
22
|
end: mock(() => Promise.resolve()),
|
|
23
|
-
})
|
|
23
|
+
}),
|
|
24
24
|
),
|
|
25
25
|
});
|
|
26
26
|
|
|
@@ -46,7 +46,7 @@ describe("PostgresHealthCheckStrategy", () => {
|
|
|
46
46
|
|
|
47
47
|
it("should throw for connection error", async () => {
|
|
48
48
|
const strategy = new PostgresHealthCheckStrategy(
|
|
49
|
-
createMockClient({ connectError: new Error("Connection refused") })
|
|
49
|
+
createMockClient({ connectError: new Error("Connection refused") }),
|
|
50
50
|
);
|
|
51
51
|
|
|
52
52
|
await expect(
|
|
@@ -57,7 +57,7 @@ describe("PostgresHealthCheckStrategy", () => {
|
|
|
57
57
|
user: "postgres",
|
|
58
58
|
password: "secret",
|
|
59
59
|
timeout: 5000,
|
|
60
|
-
})
|
|
60
|
+
}),
|
|
61
61
|
).rejects.toThrow("Connection refused");
|
|
62
62
|
});
|
|
63
63
|
});
|
|
@@ -85,7 +85,7 @@ describe("PostgresHealthCheckStrategy", () => {
|
|
|
85
85
|
|
|
86
86
|
it("should return error for query error", async () => {
|
|
87
87
|
const strategy = new PostgresHealthCheckStrategy(
|
|
88
|
-
createMockClient({ queryError: new Error("Syntax error") })
|
|
88
|
+
createMockClient({ queryError: new Error("Syntax error") }),
|
|
89
89
|
);
|
|
90
90
|
const connectedClient = await strategy.createClient({
|
|
91
91
|
host: "localhost",
|
|
@@ -107,7 +107,7 @@ describe("PostgresHealthCheckStrategy", () => {
|
|
|
107
107
|
|
|
108
108
|
it("should return custom row count", async () => {
|
|
109
109
|
const strategy = new PostgresHealthCheckStrategy(
|
|
110
|
-
createMockClient({ rowCount: 5 })
|
|
110
|
+
createMockClient({ rowCount: 5 }),
|
|
111
111
|
);
|
|
112
112
|
const connectedClient = await strategy.createClient({
|
|
113
113
|
host: "localhost",
|
|
@@ -128,8 +128,8 @@ describe("PostgresHealthCheckStrategy", () => {
|
|
|
128
128
|
});
|
|
129
129
|
});
|
|
130
130
|
|
|
131
|
-
describe("
|
|
132
|
-
it("should calculate averages correctly", () => {
|
|
131
|
+
describe("mergeResult", () => {
|
|
132
|
+
it("should calculate averages correctly through incremental merging", () => {
|
|
133
133
|
const strategy = new PostgresHealthCheckStrategy();
|
|
134
134
|
const runs = [
|
|
135
135
|
{
|
|
@@ -158,34 +158,34 @@ describe("PostgresHealthCheckStrategy", () => {
|
|
|
158
158
|
},
|
|
159
159
|
];
|
|
160
160
|
|
|
161
|
-
|
|
161
|
+
// Merge incrementally
|
|
162
|
+
let aggregated = strategy.mergeResult(undefined, runs[0]);
|
|
163
|
+
aggregated = strategy.mergeResult(aggregated, runs[1]);
|
|
162
164
|
|
|
163
|
-
expect(aggregated.avgConnectionTime).toBe(75);
|
|
164
|
-
expect(aggregated.successRate).toBe(100);
|
|
165
|
-
expect(aggregated.errorCount).toBe(0);
|
|
165
|
+
expect(aggregated.avgConnectionTime.avg).toBe(75);
|
|
166
|
+
expect(aggregated.successRate.rate).toBe(100);
|
|
167
|
+
expect(aggregated.errorCount.count).toBe(0);
|
|
166
168
|
});
|
|
167
169
|
|
|
168
170
|
it("should count errors", () => {
|
|
169
171
|
const strategy = new PostgresHealthCheckStrategy();
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
error: "Connection refused",
|
|
181
|
-
},
|
|
172
|
+
const run = {
|
|
173
|
+
id: "1",
|
|
174
|
+
status: "unhealthy" as const,
|
|
175
|
+
latencyMs: 100,
|
|
176
|
+
checkId: "c1",
|
|
177
|
+
timestamp: new Date(),
|
|
178
|
+
metadata: {
|
|
179
|
+
connected: false,
|
|
180
|
+
connectionTimeMs: 100,
|
|
181
|
+
error: "Connection refused",
|
|
182
182
|
},
|
|
183
|
-
|
|
183
|
+
};
|
|
184
184
|
|
|
185
|
-
const aggregated = strategy.
|
|
185
|
+
const aggregated = strategy.mergeResult(undefined, run);
|
|
186
186
|
|
|
187
|
-
expect(aggregated.errorCount).toBe(1);
|
|
188
|
-
expect(aggregated.successRate).toBe(0);
|
|
187
|
+
expect(aggregated.errorCount.count).toBe(1);
|
|
188
|
+
expect(aggregated.successRate.rate).toBe(0);
|
|
189
189
|
});
|
|
190
190
|
});
|
|
191
191
|
});
|
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,
|
|
@@ -70,32 +80,32 @@ const postgresResultSchema = healthResultSchema({
|
|
|
70
80
|
|
|
71
81
|
type PostgresResult = z.infer<typeof postgresResultSchema>;
|
|
72
82
|
|
|
73
|
-
/**
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const postgresAggregatedSchema = healthResultSchema({
|
|
77
|
-
avgConnectionTime: healthResultNumber({
|
|
83
|
+
/** Aggregated field definitions for bucket merging */
|
|
84
|
+
const postgresAggregatedFields = {
|
|
85
|
+
avgConnectionTime: aggregatedAverage({
|
|
78
86
|
"x-chart-type": "line",
|
|
79
87
|
"x-chart-label": "Avg Connection Time",
|
|
80
88
|
"x-chart-unit": "ms",
|
|
81
89
|
}),
|
|
82
|
-
maxConnectionTime:
|
|
90
|
+
maxConnectionTime: aggregatedMinMax({
|
|
83
91
|
"x-chart-type": "line",
|
|
84
92
|
"x-chart-label": "Max Connection Time",
|
|
85
93
|
"x-chart-unit": "ms",
|
|
86
94
|
}),
|
|
87
|
-
successRate:
|
|
95
|
+
successRate: aggregatedRate({
|
|
88
96
|
"x-chart-type": "gauge",
|
|
89
97
|
"x-chart-label": "Success Rate",
|
|
90
98
|
"x-chart-unit": "%",
|
|
91
99
|
}),
|
|
92
|
-
errorCount:
|
|
100
|
+
errorCount: aggregatedCounter({
|
|
93
101
|
"x-chart-type": "counter",
|
|
94
102
|
"x-chart-label": "Errors",
|
|
95
103
|
}),
|
|
96
|
-
}
|
|
104
|
+
};
|
|
97
105
|
|
|
98
|
-
type PostgresAggregatedResult =
|
|
106
|
+
type PostgresAggregatedResult = InferAggregatedResult<
|
|
107
|
+
typeof postgresAggregatedFields
|
|
108
|
+
>;
|
|
99
109
|
|
|
100
110
|
// ============================================================================
|
|
101
111
|
// DATABASE CLIENT INTERFACE (for testability)
|
|
@@ -133,15 +143,12 @@ const defaultDbClient: DbClient = {
|
|
|
133
143
|
// STRATEGY
|
|
134
144
|
// ============================================================================
|
|
135
145
|
|
|
136
|
-
export class PostgresHealthCheckStrategy
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
PostgresAggregatedResult
|
|
143
|
-
>
|
|
144
|
-
{
|
|
146
|
+
export class PostgresHealthCheckStrategy implements HealthCheckStrategy<
|
|
147
|
+
PostgresConfig,
|
|
148
|
+
PostgresTransportClient,
|
|
149
|
+
PostgresResult,
|
|
150
|
+
typeof postgresAggregatedFields
|
|
151
|
+
> {
|
|
145
152
|
id = "postgres";
|
|
146
153
|
displayName = "PostgreSQL Health Check";
|
|
147
154
|
description = "PostgreSQL database connectivity and query health check";
|
|
@@ -178,58 +185,38 @@ export class PostgresHealthCheckStrategy
|
|
|
178
185
|
],
|
|
179
186
|
});
|
|
180
187
|
|
|
181
|
-
aggregatedResult
|
|
188
|
+
aggregatedResult = new VersionedAggregated({
|
|
182
189
|
version: 1,
|
|
183
|
-
|
|
190
|
+
fields: postgresAggregatedFields,
|
|
184
191
|
});
|
|
185
192
|
|
|
186
|
-
|
|
187
|
-
|
|
193
|
+
mergeResult(
|
|
194
|
+
existing: PostgresAggregatedResult | undefined,
|
|
195
|
+
run: HealthCheckRunForAggregation<PostgresResult>,
|
|
188
196
|
): PostgresAggregatedResult {
|
|
189
|
-
const
|
|
197
|
+
const metadata = run.metadata;
|
|
190
198
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
successRate: 0,
|
|
196
|
-
errorCount: 0,
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
+
const avgConnectionTime = mergeAverage(
|
|
200
|
+
existing?.avgConnectionTime,
|
|
201
|
+
metadata?.connectionTimeMs,
|
|
202
|
+
);
|
|
199
203
|
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
204
|
+
const maxConnectionTime = mergeMinMax(
|
|
205
|
+
existing?.maxConnectionTime,
|
|
206
|
+
metadata?.connectionTimeMs,
|
|
207
|
+
);
|
|
203
208
|
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
? Math.round(
|
|
207
|
-
connectionTimes.reduce((a, b) => a + b, 0) / connectionTimes.length
|
|
208
|
-
)
|
|
209
|
-
: 0;
|
|
209
|
+
const isSuccess = metadata?.connected ?? false;
|
|
210
|
+
const successRate = mergeRate(existing?.successRate, isSuccess);
|
|
210
211
|
|
|
211
|
-
const
|
|
212
|
-
|
|
212
|
+
const hasError = metadata?.error !== undefined;
|
|
213
|
+
const errorCount = mergeCounter(existing?.errorCount, hasError);
|
|
213
214
|
|
|
214
|
-
|
|
215
|
-
(r) => r.metadata?.connected === true
|
|
216
|
-
).length;
|
|
217
|
-
const successRate = Math.round((successCount / validRuns.length) * 100);
|
|
218
|
-
|
|
219
|
-
const errorCount = validRuns.filter(
|
|
220
|
-
(r) => r.metadata?.error !== undefined
|
|
221
|
-
).length;
|
|
222
|
-
|
|
223
|
-
return {
|
|
224
|
-
avgConnectionTime,
|
|
225
|
-
maxConnectionTime,
|
|
226
|
-
successRate,
|
|
227
|
-
errorCount,
|
|
228
|
-
};
|
|
215
|
+
return { avgConnectionTime, maxConnectionTime, successRate, errorCount };
|
|
229
216
|
}
|
|
230
217
|
|
|
231
218
|
async createClient(
|
|
232
|
-
config: PostgresConfigInput
|
|
219
|
+
config: PostgresConfigInput,
|
|
233
220
|
): Promise<ConnectedClient<PostgresTransportClient>> {
|
|
234
221
|
const validatedConfig = this.config.validate(config);
|
|
235
222
|
|