@checkstack/healthcheck-postgres-backend 0.1.14 → 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 +24 -0
- package/package.json +1 -1
- package/src/query-collector.test.ts +3 -3
- package/src/query-collector.ts +20 -34
- package/src/strategy.test.ts +5 -5
- package/src/strategy.ts +32 -64
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
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
|
+
|
|
3
27
|
## 0.1.14
|
|
4
28
|
|
|
5
29
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -89,8 +89,8 @@ describe("QueryCollector", () => {
|
|
|
89
89
|
let aggregated = collector.mergeResult(undefined, runs[0]);
|
|
90
90
|
aggregated = collector.mergeResult(aggregated, runs[1]);
|
|
91
91
|
|
|
92
|
-
expect(aggregated.avgExecutionTimeMs).toBe(75);
|
|
93
|
-
expect(aggregated.successRate).toBe(100);
|
|
92
|
+
expect(aggregated.avgExecutionTimeMs.avg).toBe(75);
|
|
93
|
+
expect(aggregated.successRate.rate).toBe(100);
|
|
94
94
|
});
|
|
95
95
|
|
|
96
96
|
it("should calculate success rate correctly", () => {
|
|
@@ -117,7 +117,7 @@ describe("QueryCollector", () => {
|
|
|
117
117
|
let aggregated = collector.mergeResult(undefined, runs[0]);
|
|
118
118
|
aggregated = collector.mergeResult(aggregated, runs[1]);
|
|
119
119
|
|
|
120
|
-
expect(aggregated.successRate).toBe(50);
|
|
120
|
+
expect(aggregated.successRate.rate).toBe(50);
|
|
121
121
|
});
|
|
122
122
|
});
|
|
123
123
|
|
package/src/query-collector.ts
CHANGED
|
@@ -6,10 +6,10 @@ import {
|
|
|
6
6
|
type CollectorStrategy,
|
|
7
7
|
mergeAverage,
|
|
8
8
|
mergeRate,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
type
|
|
9
|
+
VersionedAggregated,
|
|
10
|
+
aggregatedAverage,
|
|
11
|
+
aggregatedRate,
|
|
12
|
+
type InferAggregatedResult,
|
|
13
13
|
} from "@checkstack/backend-api";
|
|
14
14
|
import {
|
|
15
15
|
healthResultNumber,
|
|
@@ -51,29 +51,24 @@ const queryResultSchema = healthResultSchema({
|
|
|
51
51
|
|
|
52
52
|
export type QueryResult = z.infer<typeof queryResultSchema>;
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
// Aggregated result fields definition
|
|
55
|
+
const queryAggregatedFields = {
|
|
56
|
+
avgExecutionTimeMs: aggregatedAverage({
|
|
56
57
|
"x-chart-type": "line",
|
|
57
58
|
"x-chart-label": "Avg Execution Time",
|
|
58
59
|
"x-chart-unit": "ms",
|
|
59
60
|
}),
|
|
60
|
-
successRate:
|
|
61
|
+
successRate: aggregatedRate({
|
|
61
62
|
"x-chart-type": "gauge",
|
|
62
63
|
"x-chart-label": "Success Rate",
|
|
63
64
|
"x-chart-unit": "%",
|
|
64
65
|
}),
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const queryAggregatedInternalSchema = z.object({
|
|
68
|
-
_executionTime: averageStateSchema.optional(),
|
|
69
|
-
_success: rateStateSchema.optional(),
|
|
70
|
-
});
|
|
66
|
+
};
|
|
71
67
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
export type QueryAggregatedResult = z.infer<typeof queryAggregatedSchema>;
|
|
68
|
+
// Type inferred from field definitions
|
|
69
|
+
export type QueryAggregatedResult = InferAggregatedResult<
|
|
70
|
+
typeof queryAggregatedFields
|
|
71
|
+
>;
|
|
77
72
|
|
|
78
73
|
// ============================================================================
|
|
79
74
|
// QUERY COLLECTOR
|
|
@@ -99,9 +94,9 @@ export class QueryCollector implements CollectorStrategy<
|
|
|
99
94
|
|
|
100
95
|
config = new Versioned({ version: 1, schema: queryConfigSchema });
|
|
101
96
|
result = new Versioned({ version: 1, schema: queryResultSchema });
|
|
102
|
-
aggregatedResult = new
|
|
97
|
+
aggregatedResult = new VersionedAggregated({
|
|
103
98
|
version: 1,
|
|
104
|
-
|
|
99
|
+
fields: queryAggregatedFields,
|
|
105
100
|
});
|
|
106
101
|
|
|
107
102
|
async execute({
|
|
@@ -133,21 +128,12 @@ export class QueryCollector implements CollectorStrategy<
|
|
|
133
128
|
): QueryAggregatedResult {
|
|
134
129
|
const metadata = run.metadata;
|
|
135
130
|
|
|
136
|
-
const executionTimeState = mergeAverage(
|
|
137
|
-
existing?._executionTime as AverageState | undefined,
|
|
138
|
-
metadata?.executionTimeMs,
|
|
139
|
-
);
|
|
140
|
-
|
|
141
|
-
const successState = mergeRate(
|
|
142
|
-
existing?._success as RateState | undefined,
|
|
143
|
-
metadata?.success,
|
|
144
|
-
);
|
|
145
|
-
|
|
146
131
|
return {
|
|
147
|
-
avgExecutionTimeMs:
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
132
|
+
avgExecutionTimeMs: mergeAverage(
|
|
133
|
+
existing?.avgExecutionTimeMs,
|
|
134
|
+
metadata?.executionTimeMs,
|
|
135
|
+
),
|
|
136
|
+
successRate: mergeRate(existing?.successRate, metadata?.success),
|
|
151
137
|
};
|
|
152
138
|
}
|
|
153
139
|
}
|
package/src/strategy.test.ts
CHANGED
|
@@ -162,9 +162,9 @@ describe("PostgresHealthCheckStrategy", () => {
|
|
|
162
162
|
let aggregated = strategy.mergeResult(undefined, runs[0]);
|
|
163
163
|
aggregated = strategy.mergeResult(aggregated, runs[1]);
|
|
164
164
|
|
|
165
|
-
expect(aggregated.avgConnectionTime).toBe(75);
|
|
166
|
-
expect(aggregated.successRate).toBe(100);
|
|
167
|
-
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);
|
|
168
168
|
});
|
|
169
169
|
|
|
170
170
|
it("should count errors", () => {
|
|
@@ -184,8 +184,8 @@ describe("PostgresHealthCheckStrategy", () => {
|
|
|
184
184
|
|
|
185
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,23 +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,
|
|
11
|
-
|
|
12
|
-
mergeRate,
|
|
13
|
-
mergeCounter,
|
|
14
|
-
mergeMinMax,
|
|
15
|
-
averageStateSchema,
|
|
16
|
-
minMaxStateSchema,
|
|
17
|
-
rateStateSchema,
|
|
18
|
-
counterStateSchema,
|
|
19
|
-
type AverageState,
|
|
20
|
-
type RateState,
|
|
21
|
-
type CounterState,
|
|
22
|
-
type MinMaxState,
|
|
20
|
+
type InferAggregatedResult,
|
|
23
21
|
} from "@checkstack/backend-api";
|
|
24
22
|
import {
|
|
25
23
|
healthResultBoolean,
|
|
@@ -82,45 +80,32 @@ const postgresResultSchema = healthResultSchema({
|
|
|
82
80
|
|
|
83
81
|
type PostgresResult = z.infer<typeof postgresResultSchema>;
|
|
84
82
|
|
|
85
|
-
/**
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
// UI-visible aggregated fields (for charts)
|
|
89
|
-
const postgresAggregatedDisplaySchema = healthResultSchema({
|
|
90
|
-
avgConnectionTime: healthResultNumber({
|
|
83
|
+
/** Aggregated field definitions for bucket merging */
|
|
84
|
+
const postgresAggregatedFields = {
|
|
85
|
+
avgConnectionTime: aggregatedAverage({
|
|
91
86
|
"x-chart-type": "line",
|
|
92
87
|
"x-chart-label": "Avg Connection Time",
|
|
93
88
|
"x-chart-unit": "ms",
|
|
94
89
|
}),
|
|
95
|
-
maxConnectionTime:
|
|
90
|
+
maxConnectionTime: aggregatedMinMax({
|
|
96
91
|
"x-chart-type": "line",
|
|
97
92
|
"x-chart-label": "Max Connection Time",
|
|
98
93
|
"x-chart-unit": "ms",
|
|
99
94
|
}),
|
|
100
|
-
successRate:
|
|
95
|
+
successRate: aggregatedRate({
|
|
101
96
|
"x-chart-type": "gauge",
|
|
102
97
|
"x-chart-label": "Success Rate",
|
|
103
98
|
"x-chart-unit": "%",
|
|
104
99
|
}),
|
|
105
|
-
errorCount:
|
|
100
|
+
errorCount: aggregatedCounter({
|
|
106
101
|
"x-chart-type": "counter",
|
|
107
102
|
"x-chart-label": "Errors",
|
|
108
103
|
}),
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Internal state for incremental aggregation
|
|
112
|
-
const postgresAggregatedInternalSchema = z.object({
|
|
113
|
-
_connectionTime: averageStateSchema.optional(),
|
|
114
|
-
_maxConnectionTime: minMaxStateSchema.optional(),
|
|
115
|
-
_success: rateStateSchema.optional(),
|
|
116
|
-
_errors: counterStateSchema.optional(),
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
const postgresAggregatedSchema = postgresAggregatedDisplaySchema.merge(
|
|
120
|
-
postgresAggregatedInternalSchema,
|
|
121
|
-
);
|
|
104
|
+
};
|
|
122
105
|
|
|
123
|
-
type PostgresAggregatedResult =
|
|
106
|
+
type PostgresAggregatedResult = InferAggregatedResult<
|
|
107
|
+
typeof postgresAggregatedFields
|
|
108
|
+
>;
|
|
124
109
|
|
|
125
110
|
// ============================================================================
|
|
126
111
|
// DATABASE CLIENT INTERFACE (for testability)
|
|
@@ -162,7 +147,7 @@ export class PostgresHealthCheckStrategy implements HealthCheckStrategy<
|
|
|
162
147
|
PostgresConfig,
|
|
163
148
|
PostgresTransportClient,
|
|
164
149
|
PostgresResult,
|
|
165
|
-
|
|
150
|
+
typeof postgresAggregatedFields
|
|
166
151
|
> {
|
|
167
152
|
id = "postgres";
|
|
168
153
|
displayName = "PostgreSQL Health Check";
|
|
@@ -200,9 +185,9 @@ export class PostgresHealthCheckStrategy implements HealthCheckStrategy<
|
|
|
200
185
|
],
|
|
201
186
|
});
|
|
202
187
|
|
|
203
|
-
aggregatedResult
|
|
188
|
+
aggregatedResult = new VersionedAggregated({
|
|
204
189
|
version: 1,
|
|
205
|
-
|
|
190
|
+
fields: postgresAggregatedFields,
|
|
206
191
|
});
|
|
207
192
|
|
|
208
193
|
mergeResult(
|
|
@@ -211,40 +196,23 @@ export class PostgresHealthCheckStrategy implements HealthCheckStrategy<
|
|
|
211
196
|
): PostgresAggregatedResult {
|
|
212
197
|
const metadata = run.metadata;
|
|
213
198
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
existing?._connectionTime as AverageState | undefined,
|
|
199
|
+
const avgConnectionTime = mergeAverage(
|
|
200
|
+
existing?.avgConnectionTime,
|
|
217
201
|
metadata?.connectionTimeMs,
|
|
218
202
|
);
|
|
219
203
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
existing?._maxConnectionTime as MinMaxState | undefined,
|
|
204
|
+
const maxConnectionTime = mergeMinMax(
|
|
205
|
+
existing?.maxConnectionTime,
|
|
223
206
|
metadata?.connectionTimeMs,
|
|
224
207
|
);
|
|
225
208
|
|
|
226
|
-
|
|
227
|
-
const
|
|
228
|
-
existing?._success as RateState | undefined,
|
|
229
|
-
metadata?.connected,
|
|
230
|
-
);
|
|
209
|
+
const isSuccess = metadata?.connected ?? false;
|
|
210
|
+
const successRate = mergeRate(existing?.successRate, isSuccess);
|
|
231
211
|
|
|
232
|
-
|
|
233
|
-
const
|
|
234
|
-
existing?._errors as CounterState | undefined,
|
|
235
|
-
metadata?.error !== undefined,
|
|
236
|
-
);
|
|
212
|
+
const hasError = metadata?.error !== undefined;
|
|
213
|
+
const errorCount = mergeCounter(existing?.errorCount, hasError);
|
|
237
214
|
|
|
238
|
-
return {
|
|
239
|
-
avgConnectionTime: connectionTimeState.avg,
|
|
240
|
-
maxConnectionTime: maxConnectionTimeState.max,
|
|
241
|
-
successRate: successState.rate,
|
|
242
|
-
errorCount: errorState.count,
|
|
243
|
-
_connectionTime: connectionTimeState,
|
|
244
|
-
_maxConnectionTime: maxConnectionTimeState,
|
|
245
|
-
_success: successState,
|
|
246
|
-
_errors: errorState,
|
|
247
|
-
};
|
|
215
|
+
return { avgConnectionTime, maxConnectionTime, successRate, errorCount };
|
|
248
216
|
}
|
|
249
217
|
|
|
250
218
|
async createClient(
|