@fjall/components-infrastructure 16.0.1 → 18.0.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/dist/lib/config/aws/scpPreset.js +103 -17
- package/dist/lib/patterns/aws/clickhouseDatabase.d.ts +103 -8
- package/dist/lib/patterns/aws/clickhouseDatabase.js +51 -12
- package/dist/lib/patterns/aws/database.d.ts +1 -1
- package/dist/lib/patterns/aws/database.js +1 -1
- package/dist/lib/patterns/aws/interfaces/domain.d.ts +1 -0
- package/dist/lib/patterns/aws/storage.d.ts +8 -0
- package/dist/lib/patterns/aws/storage.js +10 -3
- package/dist/lib/patterns/aws/targets/fjallTargets.d.ts +7 -5
- package/dist/lib/patterns/aws/targets/fjallTargets.js +9 -6
- package/dist/lib/patterns/aws/targets/targetResolution.d.ts +8 -5
- package/dist/lib/patterns/aws/targets/targetResolution.js +11 -8
- package/dist/lib/resources/aws/database/clickhouseBackupScript.d.ts +103 -0
- package/dist/lib/resources/aws/database/clickhouseBackupScript.js +123 -0
- package/dist/lib/resources/aws/database/clickhouseConstants.d.ts +132 -20
- package/dist/lib/resources/aws/database/clickhouseConstants.js +126 -15
- package/dist/lib/resources/aws/database/clickhouseSchemas.d.ts +33 -1
- package/dist/lib/resources/aws/database/clickhouseSchemas.js +31 -0
- package/dist/lib/resources/aws/database/clickhouseStorage.d.ts +58 -0
- package/dist/lib/resources/aws/database/clickhouseStorage.js +87 -0
- package/dist/lib/resources/aws/database/clickhouseTuning.d.ts +5 -6
- package/dist/lib/resources/aws/database/clickhouseTuning.js +39 -16
- package/dist/lib/resources/aws/database/clickhouseUserData.d.ts +21 -1
- package/dist/lib/resources/aws/database/clickhouseUserData.js +22 -6
- package/dist/lib/resources/aws/database/clickhouseXmlRenderer.js +22 -1
- package/dist/lib/resources/aws/monitoring/clickhouseAlarms.d.ts +54 -5
- package/dist/lib/resources/aws/monitoring/clickhouseAlarms.js +106 -10
- package/dist/lib/resources/aws/storage/s3.d.ts +19 -7
- package/dist/lib/resources/aws/storage/s3.js +15 -3
- package/package.json +3 -3
|
@@ -23,6 +23,19 @@ export type ClickHouseSchemaAdmin = z.infer<typeof ClickHouseSchemaAdminSchema>;
|
|
|
23
23
|
*/
|
|
24
24
|
export declare const ManagedPasswordNameSchema: z.ZodString;
|
|
25
25
|
export type ManagedPasswordName = z.infer<typeof ManagedPasswordNameSchema>;
|
|
26
|
+
/**
|
|
27
|
+
* One setting's constraint inside a profile's `<constraints>` block.
|
|
28
|
+
* `max`/`min` bound what a statement-level `SETTINGS` clause may set the
|
|
29
|
+
* value to; `readonly` forbids changing it at all. A statement that tries
|
|
30
|
+
* to exceed a bound fails with 452 SETTING_CONSTRAINT_VIOLATION instead of
|
|
31
|
+
* silently widening its own budget.
|
|
32
|
+
*/
|
|
33
|
+
export declare const SettingConstraintSchema: z.ZodObject<{
|
|
34
|
+
min: z.ZodOptional<z.ZodNumber>;
|
|
35
|
+
max: z.ZodOptional<z.ZodNumber>;
|
|
36
|
+
readonly: z.ZodOptional<z.ZodLiteral<true>>;
|
|
37
|
+
}, z.core.$strict>;
|
|
38
|
+
export type SettingConstraint = z.infer<typeof SettingConstraintSchema>;
|
|
26
39
|
/**
|
|
27
40
|
* Per-profile resource-cap shape. Field names mirror the ClickHouse XML
|
|
28
41
|
* element names (snake_case at the wire; camelCase here for TS ergonomics).
|
|
@@ -31,8 +44,18 @@ export type ManagedPasswordName = z.infer<typeof ManagedPasswordNameSchema>;
|
|
|
31
44
|
* `renderUsersXml` maps each present field to the snake_case XML element
|
|
32
45
|
* via `camelToSnakeCase`. Unknown fields are rejected by `.strict()` so a
|
|
33
46
|
* typo at the consumer doesn't silently ship a no-op profile.
|
|
47
|
+
*
|
|
48
|
+
* `constraints` is the one non-scalar member: it renders as the profile's
|
|
49
|
+
* `<constraints>` block (keys camelCase here, snake_case at the wire, same
|
|
50
|
+
* as the scalar settings), making the named ceilings unraisable from a
|
|
51
|
+
* statement-level `SETTINGS` clause.
|
|
34
52
|
*/
|
|
35
53
|
export declare const ProfileSpecSchema: z.ZodObject<{
|
|
54
|
+
constraints: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
55
|
+
min: z.ZodOptional<z.ZodNumber>;
|
|
56
|
+
max: z.ZodOptional<z.ZodNumber>;
|
|
57
|
+
readonly: z.ZodOptional<z.ZodLiteral<true>>;
|
|
58
|
+
}, z.core.$strict>>>;
|
|
36
59
|
maxThreads: z.ZodOptional<z.ZodNumber>;
|
|
37
60
|
maxInsertThreads: z.ZodOptional<z.ZodNumber>;
|
|
38
61
|
maxConcurrentQueriesForUser: z.ZodOptional<z.ZodNumber>;
|
|
@@ -62,6 +85,15 @@ export declare const ProfileSpecSchema: z.ZodObject<{
|
|
|
62
85
|
materializeTtlAfterModify: z.ZodOptional<z.ZodBoolean>;
|
|
63
86
|
}, z.core.$strict>;
|
|
64
87
|
export type ProfileSpec = z.infer<typeof ProfileSpecSchema>;
|
|
88
|
+
/**
|
|
89
|
+
* Deep-frozen `ProfileSpec` — matches the `Object.freeze` depth of the
|
|
90
|
+
* derived defaults so the type system forbids what the runtime freeze
|
|
91
|
+
* throws on. `Readonly<ProfileSpec>` alone is shallow: it left the nested
|
|
92
|
+
* constraint objects typed mutable while frozen at runtime.
|
|
93
|
+
*/
|
|
94
|
+
export type FrozenProfileSpec = Readonly<Omit<ProfileSpec, "constraints">> & {
|
|
95
|
+
readonly constraints?: Readonly<Record<string, Readonly<SettingConstraint>>>;
|
|
96
|
+
};
|
|
65
97
|
/**
|
|
66
98
|
* Re-exported for the construct's Stage 1 validation. Profile keys MUST
|
|
67
99
|
* match the same lowercase snake_case shape as user names — they emit as
|
|
@@ -88,4 +120,4 @@ export declare const PROFILE_NAME_PATTERN: RegExp;
|
|
|
88
120
|
* consumer cannot mutate the defaults in place. Pass your own `profiles:`
|
|
89
121
|
* map to the construct to extend / override; never mutate this constant.
|
|
90
122
|
*/
|
|
91
|
-
export declare const ClickHouseDefaultProfiles: Readonly<Record<string,
|
|
123
|
+
export declare const ClickHouseDefaultProfiles: Readonly<Record<string, FrozenProfileSpec>>;
|
|
@@ -9,6 +9,12 @@ import { deriveClickHouseDefaultProfiles } from "./clickhouseTuning.js";
|
|
|
9
9
|
* surfaces as a parse failure in the runner). Keeps the XML element names
|
|
10
10
|
* emit-safe (no escaping needed). */
|
|
11
11
|
const NAME_PATTERN = /^[a-z][a-z0-9_]*$/;
|
|
12
|
+
/** Constraint-record keys — camelCase (or snake_case) setting names with a
|
|
13
|
+
* leading lowercase letter. Record keys are the one profile surface
|
|
14
|
+
* `.strict()` cannot police, and they emit as XML element names via
|
|
15
|
+
* `camelToSnakeCase`, so the emit-safety NAME_PATTERN guarantees for
|
|
16
|
+
* user/profile names must come from the key schema itself here. */
|
|
17
|
+
const CONSTRAINT_KEY_PATTERN = /^[a-z][a-zA-Z0-9_]*$/;
|
|
12
18
|
/**
|
|
13
19
|
* Schema-admin user — the framework's bootstrap-privileged identity that
|
|
14
20
|
* customer SQL runs as. Owns DDL, schema migrations, GRANTs. Rendered into
|
|
@@ -41,6 +47,21 @@ export const ManagedPasswordNameSchema = z
|
|
|
41
47
|
.min(1)
|
|
42
48
|
.max(63)
|
|
43
49
|
.regex(NAME_PATTERN, "Must be lowercase snake_case");
|
|
50
|
+
/**
|
|
51
|
+
* One setting's constraint inside a profile's `<constraints>` block.
|
|
52
|
+
* `max`/`min` bound what a statement-level `SETTINGS` clause may set the
|
|
53
|
+
* value to; `readonly` forbids changing it at all. A statement that tries
|
|
54
|
+
* to exceed a bound fails with 452 SETTING_CONSTRAINT_VIOLATION instead of
|
|
55
|
+
* silently widening its own budget.
|
|
56
|
+
*/
|
|
57
|
+
export const SettingConstraintSchema = z
|
|
58
|
+
.object({
|
|
59
|
+
min: z.number().int().nonnegative().optional(),
|
|
60
|
+
max: z.number().int().nonnegative().optional(),
|
|
61
|
+
readonly: z.literal(true).optional()
|
|
62
|
+
})
|
|
63
|
+
.strict()
|
|
64
|
+
.refine((c) => c.min !== undefined || c.max !== undefined || c.readonly === true, "A setting constraint must declare min, max or readonly");
|
|
44
65
|
/**
|
|
45
66
|
* Per-profile resource-cap shape. Field names mirror the ClickHouse XML
|
|
46
67
|
* element names (snake_case at the wire; camelCase here for TS ergonomics).
|
|
@@ -49,9 +70,19 @@ export const ManagedPasswordNameSchema = z
|
|
|
49
70
|
* `renderUsersXml` maps each present field to the snake_case XML element
|
|
50
71
|
* via `camelToSnakeCase`. Unknown fields are rejected by `.strict()` so a
|
|
51
72
|
* typo at the consumer doesn't silently ship a no-op profile.
|
|
73
|
+
*
|
|
74
|
+
* `constraints` is the one non-scalar member: it renders as the profile's
|
|
75
|
+
* `<constraints>` block (keys camelCase here, snake_case at the wire, same
|
|
76
|
+
* as the scalar settings), making the named ceilings unraisable from a
|
|
77
|
+
* statement-level `SETTINGS` clause.
|
|
52
78
|
*/
|
|
53
79
|
export const ProfileSpecSchema = z
|
|
54
80
|
.object({
|
|
81
|
+
constraints: z
|
|
82
|
+
.record(z
|
|
83
|
+
.string()
|
|
84
|
+
.regex(CONSTRAINT_KEY_PATTERN, "Constraint keys must be camelCase (or snake_case) setting names"), SettingConstraintSchema)
|
|
85
|
+
.optional(),
|
|
55
86
|
maxThreads: z.number().int().positive().optional(),
|
|
56
87
|
maxInsertThreads: z.number().int().positive().optional(),
|
|
57
88
|
maxConcurrentQueriesForUser: z.number().int().positive().optional(),
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default critical free-space floor for a volume of `storageGb`.
|
|
3
|
+
*
|
|
4
|
+
* The larger of the absolute floor and a share of the volume — neither alone
|
|
5
|
+
* is right at both ends of the size range. See
|
|
6
|
+
* `CLICKHOUSE_DISK_FREE_CRITICAL_GIB` for the reasoning; rounded to whole GiB
|
|
7
|
+
* so the alarm description reads as a number an operator would say out loud.
|
|
8
|
+
*/
|
|
9
|
+
export declare function defaultDiskFreeCriticalGib(storageGb: number): number;
|
|
10
|
+
export interface ClickHouseStorageResolution {
|
|
11
|
+
/** EBS volume size in GiB, defaulted and validated. */
|
|
12
|
+
storageGb: number;
|
|
13
|
+
/** Critical free-space floor in GiB, defaulted and validated. */
|
|
14
|
+
diskFreeCriticalGib: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Resolve and validate the two values that together decide when a ClickHouse
|
|
18
|
+
* volume is considered nearly full.
|
|
19
|
+
*
|
|
20
|
+
* They are validated as a PAIR because neither is checkable alone: a 100 GiB
|
|
21
|
+
* volume is fine until someone sets a 60 GiB free-space floor, and a 60 GiB
|
|
22
|
+
* floor is fine until someone shrinks the volume under it. Split across two
|
|
23
|
+
* validators, each would pass on its own and the deployed stack would carry a
|
|
24
|
+
* critical alarm that can never leave ALARM.
|
|
25
|
+
*
|
|
26
|
+
* Throws rather than annotating: both are numbers the caller typed, and a
|
|
27
|
+
* warning on a synth that still emits a permanently-breaching pager is worse
|
|
28
|
+
* than a failed synth with the arithmetic in the message.
|
|
29
|
+
*/
|
|
30
|
+
export declare function resolveClickHouseStorage(storageGb: number | undefined, diskFreeCriticalGib: number | undefined): ClickHouseStorageResolution;
|
|
31
|
+
export interface ClickHouseVolumePerformanceInput {
|
|
32
|
+
/** Validated volume size from `resolveClickHouseStorage` — the IOPS ceiling
|
|
33
|
+
* is a function of it. */
|
|
34
|
+
storageGb: number;
|
|
35
|
+
iops: number | undefined;
|
|
36
|
+
throughputMbps: number | undefined;
|
|
37
|
+
}
|
|
38
|
+
export interface ClickHouseVolumePerformanceResolution {
|
|
39
|
+
/** Provisioned IOPS, defaulted and validated. */
|
|
40
|
+
iops: number;
|
|
41
|
+
/** Provisioned throughput in MiB/s, defaulted and validated. */
|
|
42
|
+
throughputMbps: number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Resolve and validate the gp3 performance pair for the data volume.
|
|
46
|
+
*
|
|
47
|
+
* Same shape as `resolveClickHouseStorage`: each value has a band of its own,
|
|
48
|
+
* but EBS also constrains them against each other and against the size —
|
|
49
|
+
* throughput needs four IOPS per MiB/s, and IOPS needs a GiB per 500 — so a
|
|
50
|
+
* value legal on its own is rejected by CreateVolume mid-deploy once its
|
|
51
|
+
* partner is in view. Checking the pair at synth turns that rollback into a
|
|
52
|
+
* message that names the figure the other half needs.
|
|
53
|
+
*
|
|
54
|
+
* Both checks are the ones aws-cdk-lib's `Volume` applies too; this resolver
|
|
55
|
+
* exists so the failure names `ClickHouseDatabase` props and their cure
|
|
56
|
+
* rather than a CDK-internal field.
|
|
57
|
+
*/
|
|
58
|
+
export declare function resolveClickHouseVolumePerformance(input: ClickHouseVolumePerformanceInput): ClickHouseVolumePerformanceResolution;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { CLICKHOUSE_DISK_FREE_CRITICAL_GIB, CLICKHOUSE_DISK_FREE_CRITICAL_SHARE, CLICKHOUSE_EBS_IOPS, CLICKHOUSE_EBS_THROUGHPUT_MBPS, CLICKHOUSE_EBS_VOLUME_SIZE_GB, CLICKHOUSE_IOPS_PER_THROUGHPUT_MBPS, CLICKHOUSE_MAX_IOPS, CLICKHOUSE_MAX_IOPS_PER_GB, CLICKHOUSE_MAX_STORAGE_GB, CLICKHOUSE_MAX_THROUGHPUT_MBPS, CLICKHOUSE_MIN_IOPS, CLICKHOUSE_MIN_STORAGE_GB, CLICKHOUSE_MIN_THROUGHPUT_MBPS } from "./clickhouseConstants.js";
|
|
2
|
+
/**
|
|
3
|
+
* Largest share of the volume the critical free-space floor may claim. A
|
|
4
|
+
* floor at half the disk would page from the day the cluster passed 50 %
|
|
5
|
+
* used, which is a capacity-planning fact, not an incident; at a fifth, the
|
|
6
|
+
* alarm means what its description says — the disk is nearly gone.
|
|
7
|
+
*/
|
|
8
|
+
const MAX_FLOOR_SHARE_OF_VOLUME = 0.2;
|
|
9
|
+
/**
|
|
10
|
+
* Default critical free-space floor for a volume of `storageGb`.
|
|
11
|
+
*
|
|
12
|
+
* The larger of the absolute floor and a share of the volume — neither alone
|
|
13
|
+
* is right at both ends of the size range. See
|
|
14
|
+
* `CLICKHOUSE_DISK_FREE_CRITICAL_GIB` for the reasoning; rounded to whole GiB
|
|
15
|
+
* so the alarm description reads as a number an operator would say out loud.
|
|
16
|
+
*/
|
|
17
|
+
export function defaultDiskFreeCriticalGib(storageGb) {
|
|
18
|
+
return Math.max(CLICKHOUSE_DISK_FREE_CRITICAL_GIB, Math.round(storageGb * CLICKHOUSE_DISK_FREE_CRITICAL_SHARE));
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Resolve and validate the two values that together decide when a ClickHouse
|
|
22
|
+
* volume is considered nearly full.
|
|
23
|
+
*
|
|
24
|
+
* They are validated as a PAIR because neither is checkable alone: a 100 GiB
|
|
25
|
+
* volume is fine until someone sets a 60 GiB free-space floor, and a 60 GiB
|
|
26
|
+
* floor is fine until someone shrinks the volume under it. Split across two
|
|
27
|
+
* validators, each would pass on its own and the deployed stack would carry a
|
|
28
|
+
* critical alarm that can never leave ALARM.
|
|
29
|
+
*
|
|
30
|
+
* Throws rather than annotating: both are numbers the caller typed, and a
|
|
31
|
+
* warning on a synth that still emits a permanently-breaching pager is worse
|
|
32
|
+
* than a failed synth with the arithmetic in the message.
|
|
33
|
+
*/
|
|
34
|
+
export function resolveClickHouseStorage(storageGb, diskFreeCriticalGib) {
|
|
35
|
+
const size = storageGb ?? CLICKHOUSE_EBS_VOLUME_SIZE_GB;
|
|
36
|
+
if (!Number.isInteger(size) ||
|
|
37
|
+
size < CLICKHOUSE_MIN_STORAGE_GB ||
|
|
38
|
+
size > CLICKHOUSE_MAX_STORAGE_GB) {
|
|
39
|
+
throw new Error(`ClickHouseDatabase: storageGb must be an integer between ${CLICKHOUSE_MIN_STORAGE_GB} and ${CLICKHOUSE_MAX_STORAGE_GB} GiB; got ${size}.`);
|
|
40
|
+
}
|
|
41
|
+
const floor = diskFreeCriticalGib ?? defaultDiskFreeCriticalGib(size);
|
|
42
|
+
if (!Number.isFinite(floor) || floor <= 0) {
|
|
43
|
+
throw new Error(`ClickHouseDatabase: alarms.diskFreeCriticalGib must be a positive number of GiB; got ${floor}.`);
|
|
44
|
+
}
|
|
45
|
+
const ceiling = size * MAX_FLOOR_SHARE_OF_VOLUME;
|
|
46
|
+
if (floor > ceiling) {
|
|
47
|
+
throw new Error(`ClickHouseDatabase: alarms.diskFreeCriticalGib (${floor} GiB) exceeds a fifth of storageGb (${size} GiB → ${ceiling} GiB). A floor that large keeps the disk-critical alarm in ALARM on a healthy cluster; raise storageGb or lower the floor.`);
|
|
48
|
+
}
|
|
49
|
+
return { storageGb: size, diskFreeCriticalGib: floor };
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Resolve and validate the gp3 performance pair for the data volume.
|
|
53
|
+
*
|
|
54
|
+
* Same shape as `resolveClickHouseStorage`: each value has a band of its own,
|
|
55
|
+
* but EBS also constrains them against each other and against the size —
|
|
56
|
+
* throughput needs four IOPS per MiB/s, and IOPS needs a GiB per 500 — so a
|
|
57
|
+
* value legal on its own is rejected by CreateVolume mid-deploy once its
|
|
58
|
+
* partner is in view. Checking the pair at synth turns that rollback into a
|
|
59
|
+
* message that names the figure the other half needs.
|
|
60
|
+
*
|
|
61
|
+
* Both checks are the ones aws-cdk-lib's `Volume` applies too; this resolver
|
|
62
|
+
* exists so the failure names `ClickHouseDatabase` props and their cure
|
|
63
|
+
* rather than a CDK-internal field.
|
|
64
|
+
*/
|
|
65
|
+
export function resolveClickHouseVolumePerformance(input) {
|
|
66
|
+
const iops = input.iops ?? CLICKHOUSE_EBS_IOPS;
|
|
67
|
+
if (!Number.isInteger(iops) ||
|
|
68
|
+
iops < CLICKHOUSE_MIN_IOPS ||
|
|
69
|
+
iops > CLICKHOUSE_MAX_IOPS) {
|
|
70
|
+
throw new Error(`ClickHouseDatabase: iops must be an integer between ${CLICKHOUSE_MIN_IOPS} and ${CLICKHOUSE_MAX_IOPS}; got ${iops}.`);
|
|
71
|
+
}
|
|
72
|
+
const iopsCeilingForSize = input.storageGb * CLICKHOUSE_MAX_IOPS_PER_GB;
|
|
73
|
+
if (iops > iopsCeilingForSize) {
|
|
74
|
+
throw new Error(`ClickHouseDatabase: iops (${iops}) exceeds ${CLICKHOUSE_MAX_IOPS_PER_GB} per GiB of storageGb (${input.storageGb} GiB → ${iopsCeilingForSize}). EBS rejects the volume; raise storageGb to at least ${Math.ceil(iops / CLICKHOUSE_MAX_IOPS_PER_GB)} GiB or lower iops.`);
|
|
75
|
+
}
|
|
76
|
+
const throughputMbps = input.throughputMbps ?? CLICKHOUSE_EBS_THROUGHPUT_MBPS;
|
|
77
|
+
if (!Number.isInteger(throughputMbps) ||
|
|
78
|
+
throughputMbps < CLICKHOUSE_MIN_THROUGHPUT_MBPS ||
|
|
79
|
+
throughputMbps > CLICKHOUSE_MAX_THROUGHPUT_MBPS) {
|
|
80
|
+
throw new Error(`ClickHouseDatabase: throughputMbps must be an integer between ${CLICKHOUSE_MIN_THROUGHPUT_MBPS} and ${CLICKHOUSE_MAX_THROUGHPUT_MBPS} MiB/s; got ${throughputMbps}.`);
|
|
81
|
+
}
|
|
82
|
+
const iopsNeeded = throughputMbps * CLICKHOUSE_IOPS_PER_THROUGHPUT_MBPS;
|
|
83
|
+
if (iops < iopsNeeded) {
|
|
84
|
+
throw new Error(`ClickHouseDatabase: throughputMbps (${throughputMbps} MiB/s) needs ${CLICKHOUSE_IOPS_PER_THROUGHPUT_MBPS} IOPS per MiB/s, so at least ${iopsNeeded} iops; got ${iops}. EBS rejects the volume; raise iops to ${iopsNeeded} or lower throughputMbps to ${Math.floor(iops / CLICKHOUSE_IOPS_PER_THROUGHPUT_MBPS)}.`);
|
|
85
|
+
}
|
|
86
|
+
return { iops, throughputMbps };
|
|
87
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { FrozenProfileSpec } from "./clickhouseSchemas.js";
|
|
2
2
|
/**
|
|
3
3
|
* Server-level tuning values derived from the instance type. Consumed by
|
|
4
4
|
* `generateServerConfigXml` — every value here lands in the rendered
|
|
@@ -50,10 +50,9 @@ export declare const SYSTEM_LOG_TTL = "event_date + INTERVAL 14 DAY DELETE";
|
|
|
50
50
|
export declare function deriveClickHouseServerTuning(instanceType: string): ClickHouseServerTuning;
|
|
51
51
|
/**
|
|
52
52
|
* Derive the four workload-class default profiles from the instance type.
|
|
53
|
-
* Memory caps, external-spill thresholds and
|
|
54
|
-
* instance memory; thread caps scale with vCPUs.
|
|
55
|
-
*
|
|
56
|
-
* (pinned by clickhouseTuning.test.ts).
|
|
53
|
+
* Memory caps, external-spill thresholds and constraint ceilings scale with
|
|
54
|
+
* instance memory; thread caps scale with vCPUs. The full m7g.medium
|
|
55
|
+
* shape is pinned by clickhouseTuning.test.ts.
|
|
57
56
|
*
|
|
58
57
|
* `high_throughput_ingest.maxConcurrentQueriesForUser` deliberately does
|
|
59
58
|
* NOT scale: it mirrors the webapp's client-side FIFO gate
|
|
@@ -61,4 +60,4 @@ export declare function deriveClickHouseServerTuning(instanceType: string): Clic
|
|
|
61
60
|
* parity test on the webapp side enforces the pair, so the value is a
|
|
62
61
|
* client contract, not an instance-capacity knob.
|
|
63
62
|
*/
|
|
64
|
-
export declare function deriveClickHouseDefaultProfiles(instanceType: string): Readonly<Record<string,
|
|
63
|
+
export declare function deriveClickHouseDefaultProfiles(instanceType: string): Readonly<Record<string, FrozenProfileSpec>>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CLICKHOUSE_INSTANCE_SPECS } from "./clickhouseConstants.js";
|
|
1
|
+
import { clickHouseInstanceSpec, CLICKHOUSE_INSTANCE_SPECS } from "./clickhouseConstants.js";
|
|
2
2
|
/** 4 GiB / 1 vCPU (m7g.medium) baseline the scaling formulas anchor to.
|
|
3
3
|
* These are the proven prod values the construct shipped hand-tuned before
|
|
4
4
|
* derivation existed — `deriveClickHouseServerTuning("m7g.medium")` must
|
|
@@ -47,7 +47,7 @@ export const MAX_SERVER_MEMORY_RAM_RATIO = 0.75;
|
|
|
47
47
|
* change is a single edit. */
|
|
48
48
|
export const SYSTEM_LOG_TTL = "event_date + INTERVAL 14 DAY DELETE";
|
|
49
49
|
function resolveSpec(instanceType) {
|
|
50
|
-
const spec =
|
|
50
|
+
const spec = clickHouseInstanceSpec(instanceType);
|
|
51
51
|
if (spec === undefined) {
|
|
52
52
|
throw new Error(`ClickHouseDatabase: unknown instance type '${instanceType}' — cannot ` +
|
|
53
53
|
"derive server tuning. Supported: " +
|
|
@@ -110,10 +110,9 @@ export function deriveClickHouseServerTuning(instanceType) {
|
|
|
110
110
|
}
|
|
111
111
|
/**
|
|
112
112
|
* Derive the four workload-class default profiles from the instance type.
|
|
113
|
-
* Memory caps, external-spill thresholds and
|
|
114
|
-
* instance memory; thread caps scale with vCPUs.
|
|
115
|
-
*
|
|
116
|
-
* (pinned by clickhouseTuning.test.ts).
|
|
113
|
+
* Memory caps, external-spill thresholds and constraint ceilings scale with
|
|
114
|
+
* instance memory; thread caps scale with vCPUs. The full m7g.medium
|
|
115
|
+
* shape is pinned by clickhouseTuning.test.ts.
|
|
117
116
|
*
|
|
118
117
|
* `high_throughput_ingest.maxConcurrentQueriesForUser` deliberately does
|
|
119
118
|
* NOT scale: it mirrors the webapp's client-side FIFO gate
|
|
@@ -124,12 +123,27 @@ export function deriveClickHouseServerTuning(instanceType) {
|
|
|
124
123
|
export function deriveClickHouseDefaultProfiles(instanceType) {
|
|
125
124
|
const spec = resolveSpec(instanceType);
|
|
126
125
|
const scale = memoryScale(spec);
|
|
126
|
+
// The four unraisable ceilings — one source for both the ingest profile's
|
|
127
|
+
// scalar defaults and its <constraints> block, so a retuned default can
|
|
128
|
+
// never drift from (or exceed) its own ceiling.
|
|
129
|
+
const ingestCeilings = Object.freeze({
|
|
130
|
+
maxThreads: spec.vcpus,
|
|
131
|
+
maxMemoryUsage: 1610612736 * scale, // 1.5 GiB baseline
|
|
132
|
+
maxMemoryUsageForUser: 2147483648 * scale, // 2 GiB baseline
|
|
133
|
+
maxExecutionTime: 30
|
|
134
|
+
});
|
|
127
135
|
return Object.freeze({
|
|
128
136
|
high_throughput_ingest: Object.freeze({
|
|
129
|
-
maxThreads:
|
|
137
|
+
maxThreads: ingestCeilings.maxThreads,
|
|
130
138
|
maxInsertThreads: spec.vcpus,
|
|
131
139
|
maxConcurrentQueriesForUser: 6,
|
|
132
|
-
|
|
140
|
+
// MUST stay 0: any non-zero value drops ALL ExceptionBeforeStart rows
|
|
141
|
+
// (quota / concurrency / limit refusals) and every QueryStart row from
|
|
142
|
+
// system.query_log unconditionally (executeQuery.cpp gates on elapsed
|
|
143
|
+
// time, which refused queries never accrue) — failed product queries
|
|
144
|
+
// become invisible. If log volume ever needs a lever it is
|
|
145
|
+
// log_queries_min_type=QUERY_FINISH, never a duration floor.
|
|
146
|
+
logQueriesMinQueryDurationMs: 0,
|
|
133
147
|
optimizeMoveToPrewhere: true,
|
|
134
148
|
useQueryConditionCache: true,
|
|
135
149
|
useSkipIndexesIfFinal: true,
|
|
@@ -146,16 +160,25 @@ export function deriveClickHouseDefaultProfiles(instanceType) {
|
|
|
146
160
|
inputFormatParallelParsing: false,
|
|
147
161
|
outputFormatParallelFormatting: false,
|
|
148
162
|
queryPlanOptimizeLazyMaterialization: true,
|
|
149
|
-
maxMemoryUsage:
|
|
150
|
-
maxMemoryUsageForUser:
|
|
163
|
+
maxMemoryUsage: ingestCeilings.maxMemoryUsage,
|
|
164
|
+
maxMemoryUsageForUser: ingestCeilings.maxMemoryUsageForUser,
|
|
151
165
|
maxBytesBeforeExternalSort: 536870912 * scale, // 512 MiB baseline
|
|
152
166
|
maxBytesBeforeExternalGroupBy: 536870912 * scale,
|
|
153
|
-
maxExecutionTime:
|
|
154
|
-
//
|
|
155
|
-
//
|
|
156
|
-
//
|
|
157
|
-
//
|
|
158
|
-
|
|
167
|
+
maxExecutionTime: ingestCeilings.maxExecutionTime,
|
|
168
|
+
// Deliberately no maxRowsToRead: the planning-time estimate refuses
|
|
169
|
+
// legitimate in-order LIMIT reads, and any statement can override it.
|
|
170
|
+
// Per-box protection is the unraisable ceilings below; per-tenant
|
|
171
|
+
// fairness is the org-keyed quota (webapp clickhouse-init/030).
|
|
172
|
+
constraints: Object.freeze({
|
|
173
|
+
maxExecutionTime: Object.freeze({
|
|
174
|
+
max: ingestCeilings.maxExecutionTime
|
|
175
|
+
}),
|
|
176
|
+
maxMemoryUsage: Object.freeze({ max: ingestCeilings.maxMemoryUsage }),
|
|
177
|
+
maxMemoryUsageForUser: Object.freeze({
|
|
178
|
+
max: ingestCeilings.maxMemoryUsageForUser
|
|
179
|
+
}),
|
|
180
|
+
maxThreads: Object.freeze({ max: ingestCeilings.maxThreads })
|
|
181
|
+
})
|
|
159
182
|
}),
|
|
160
183
|
audit_append: Object.freeze({
|
|
161
184
|
maxThreads: 1,
|
|
@@ -18,6 +18,17 @@ export interface BuildClickHouseUserDataOptions {
|
|
|
18
18
|
* as `clickHouseTaskMemoryMiB`).
|
|
19
19
|
*/
|
|
20
20
|
instanceType: string;
|
|
21
|
+
/**
|
|
22
|
+
* Declared data-volume size in GiB, embedded into the emitted script as a
|
|
23
|
+
* comment line. Load-bearing, not documentation: user data is part of the
|
|
24
|
+
* launch template, so embedding the size means a `storageGb` change
|
|
25
|
+
* produces a new launch-template version — which is what triggers the ASG
|
|
26
|
+
* instance refresh whose boot runs the `resize2fs` below. Without it, a
|
|
27
|
+
* volume increase modifies the EBS device in place, nothing relaunches,
|
|
28
|
+
* and the grown capacity is inert until some unrelated deploy happens to
|
|
29
|
+
* replace the instance. Same apply mechanism `instanceType` rides.
|
|
30
|
+
*/
|
|
31
|
+
dataVolumeSizeGb: number;
|
|
21
32
|
/**
|
|
22
33
|
* Optional S3 cold-tier configuration.
|
|
23
34
|
* When omitted, single-tier hot storage on the EC2 EBS volume is used.
|
|
@@ -52,7 +63,16 @@ export interface BuildClickHouseUserDataOptions {
|
|
|
52
63
|
serverSecretArn: string;
|
|
53
64
|
};
|
|
54
65
|
}
|
|
55
|
-
|
|
66
|
+
/**
|
|
67
|
+
* The subset of the user-data options that config.xml generation actually
|
|
68
|
+
* reads. `dataVolumeSizeGb` is deliberately absent: it shapes the boot
|
|
69
|
+
* SCRIPT (the launch-template refresh trigger), not the rendered server
|
|
70
|
+
* config — demanding it here forced the XML parity tests to invent a value
|
|
71
|
+
* the render never consumes. `tls` is read for presence only (it toggles
|
|
72
|
+
* the secure-listener block); the ARNs inside it are boot-script concerns.
|
|
73
|
+
*/
|
|
74
|
+
export type ServerConfigXmlOptions = Pick<BuildClickHouseUserDataOptions, "backupBucketName" | "backupBucketRegion" | "instanceType" | "coldTier" | "tls">;
|
|
75
|
+
export declare function generateServerConfigXml(options: ServerConfigXmlOptions): string;
|
|
56
76
|
export interface GenerateUsersConfigXmlOptions {
|
|
57
77
|
schemaAdmin: ClickHouseSchemaAdmin;
|
|
58
78
|
profiles: Record<string, ProfileSpec>;
|
|
@@ -362,9 +362,14 @@ chmod 644 "$MOUNT_POINT/server-certs/client.xml"
|
|
|
362
362
|
`
|
|
363
363
|
: "";
|
|
364
364
|
// The CWAgent-namespace metrics have no producer without this — no
|
|
365
|
-
// CloudWatch Agent is installed. disk_used_percent feeds the
|
|
366
|
-
//
|
|
367
|
-
//
|
|
365
|
+
// CloudWatch Agent is installed. disk_used_percent feeds the disk WARN
|
|
366
|
+
// alarm (trajectory) and disk_free_gib the disk CRITICAL alarm (imminent
|
|
367
|
+
// breakage — merges need free bytes, not free percent, so the paging signal
|
|
368
|
+
// is absolute and the warn one is not); mem_used_percent is dashboards-only
|
|
369
|
+
// (the CWAgent memory alarm is retired — memory alarming lives on ECS
|
|
370
|
+
// MemoryUtilization). Free space is read in MiB and divided rather than
|
|
371
|
+
// taken from `--block-size=1G`, whose integer truncation would quantise the
|
|
372
|
+
// metric to 1 GiB steps against a floor measured in tens. The ASG name
|
|
368
373
|
// (the alarms' dimension) is unknowable at synth (LaunchTemplate → ASG is a
|
|
369
374
|
// CFN cycle), so it resolves at run time: IMDS instance tags first, with a
|
|
370
375
|
// describe-auto-scaling-instances fallback. AL2023 ECS AMIs ship no cron;
|
|
@@ -375,10 +380,10 @@ chmod 644 "$MOUNT_POINT/server-certs/client.xml"
|
|
|
375
380
|
// timer never re-triggers a still-activating unit, so ONE wedged run (hung
|
|
376
381
|
// IMDS or AWS call) would silence the publisher forever — every curl carries
|
|
377
382
|
// --max-time and the unit a TimeoutStartSec under the 60s cadence. The disk
|
|
378
|
-
//
|
|
383
|
+
// metrics publish only when the data volume is actually mounted: `df` on
|
|
379
384
|
// an unmounted path reports the ROOT filesystem, a false-healthy number
|
|
380
385
|
// that would defeat the disk-critical alarm's missing-data=BREACHING flip.
|
|
381
|
-
const { namespace, memoryMetric, diskMetric, asgDimension } = CLICKHOUSE_HOST_METRICS;
|
|
386
|
+
const { namespace, memoryMetric, diskMetric, diskFreeMetric, asgDimension } = CLICKHOUSE_HOST_METRICS;
|
|
382
387
|
const metricsPublisher = `
|
|
383
388
|
cat > /usr/local/bin/fjall-ch-metrics.sh << 'METRICSEOF'
|
|
384
389
|
#!/bin/bash
|
|
@@ -401,8 +406,10 @@ METRICS=("MetricName=${memoryMetric},Dimensions=[{Name=${asgDimension},Value=$AS
|
|
|
401
406
|
if mountpoint -q ${CLICKHOUSE_DATA_MOUNT_PATH}; then
|
|
402
407
|
DISK=$(df --output=pcent ${CLICKHOUSE_DATA_MOUNT_PATH} | tail -n 1 | tr -dc '0-9')
|
|
403
408
|
METRICS+=("MetricName=${diskMetric},Dimensions=[{Name=${asgDimension},Value=$ASG}],Value=$DISK,Unit=Percent")
|
|
409
|
+
FREE=$(df --output=avail --block-size=1M ${CLICKHOUSE_DATA_MOUNT_PATH} | tail -n 1 | tr -dc '0-9' | awk '{printf "%.2f", $1/1024}')
|
|
410
|
+
METRICS+=("MetricName=${diskFreeMetric},Dimensions=[{Name=${asgDimension},Value=$ASG}],Value=$FREE,Unit=Gigabytes")
|
|
404
411
|
else
|
|
405
|
-
echo "fjall-ch-metrics: ${CLICKHOUSE_DATA_MOUNT_PATH} not mounted, skipping disk
|
|
412
|
+
echo "fjall-ch-metrics: ${CLICKHOUSE_DATA_MOUNT_PATH} not mounted, skipping disk metrics" >&2
|
|
406
413
|
fi
|
|
407
414
|
aws cloudwatch put-metric-data --region "$REGION" --namespace ${namespace} --metric-data "\${METRICS[@]}"
|
|
408
415
|
METRICSEOF
|
|
@@ -490,6 +497,15 @@ fi
|
|
|
490
497
|
mkdir -p "$MOUNT_POINT"
|
|
491
498
|
mount "$DEVICE" "$MOUNT_POINT"
|
|
492
499
|
|
|
500
|
+
# The declared size below is load-bearing, and it MUST be a statement, not a
|
|
501
|
+
# comment — compactUserDataScript strips comments from the emitted script.
|
|
502
|
+
# User data is part of the launch template, so the size appearing verbatim in
|
|
503
|
+
# the emitted script is what turns a storageGb change into a new template
|
|
504
|
+
# version -> instance refresh -> the boot-time resize2fs below. Without it a
|
|
505
|
+
# volume increase never relaunches anything and the grown capacity stays
|
|
506
|
+
# unusable until an unrelated deploy replaces the instance.
|
|
507
|
+
DECLARED_DATA_VOLUME_GIB=${options.dataVolumeSizeGb}
|
|
508
|
+
#
|
|
493
509
|
# Grow the filesystem to fill the volume. Raising the CDK volume size issues a
|
|
494
510
|
# ModifyVolume against the block device only — ext4 keeps its old size until
|
|
495
511
|
# resize2fs runs, so without this line every capacity increase is INERT and the
|
|
@@ -1,15 +1,36 @@
|
|
|
1
1
|
function camelToSnakeCase(input) {
|
|
2
2
|
return input.replace(/[A-Z]/g, (m) => "_" + m.toLowerCase());
|
|
3
3
|
}
|
|
4
|
+
function renderConstraintsBlock(constraints) {
|
|
5
|
+
const settings = Object.entries(constraints).map(([key, constraint]) => {
|
|
6
|
+
const xmlName = camelToSnakeCase(key);
|
|
7
|
+
const children = [];
|
|
8
|
+
if (constraint.min !== undefined) {
|
|
9
|
+
children.push(` <min>${String(constraint.min)}</min>`);
|
|
10
|
+
}
|
|
11
|
+
if (constraint.max !== undefined) {
|
|
12
|
+
children.push(` <max>${String(constraint.max)}</max>`);
|
|
13
|
+
}
|
|
14
|
+
if (constraint.readonly === true) {
|
|
15
|
+
children.push(` <readonly/>`);
|
|
16
|
+
}
|
|
17
|
+
return ` <${xmlName}>\n${children.join("\n")}\n </${xmlName}>`;
|
|
18
|
+
});
|
|
19
|
+
return ` <constraints>\n${settings.join("\n")}\n </constraints>`;
|
|
20
|
+
}
|
|
4
21
|
function renderProfileSpec(spec) {
|
|
22
|
+
const { constraints, ...scalars } = spec;
|
|
5
23
|
const lines = [];
|
|
6
|
-
for (const [key, value] of Object.entries(
|
|
24
|
+
for (const [key, value] of Object.entries(scalars)) {
|
|
7
25
|
if (value === undefined)
|
|
8
26
|
continue;
|
|
9
27
|
const xmlName = camelToSnakeCase(key);
|
|
10
28
|
const xmlValue = typeof value === "boolean" ? (value ? "1" : "0") : String(value);
|
|
11
29
|
lines.push(` <${xmlName}>${xmlValue}</${xmlName}>`);
|
|
12
30
|
}
|
|
31
|
+
if (constraints !== undefined && Object.keys(constraints).length > 0) {
|
|
32
|
+
lines.push(renderConstraintsBlock(constraints));
|
|
33
|
+
}
|
|
13
34
|
return lines.join("\n");
|
|
14
35
|
}
|
|
15
36
|
function renderDefaultUserBlock(access) {
|
|
@@ -23,10 +23,39 @@ export interface ClickHouseAlarmThresholds {
|
|
|
23
23
|
* CWAgent host-memory alarm is retired (its threshold meant a different
|
|
24
24
|
* headroom per instance size; the timer metric remains for dashboards). */
|
|
25
25
|
memoryThreshold?: number;
|
|
26
|
-
/** ClickHouse data volume disk % used. Default 70
|
|
26
|
+
/** ClickHouse data volume disk % used. Default 70. The TRAJECTORY signal —
|
|
27
|
+
* "this volume is filling up, plan growth" — which is the one disk
|
|
28
|
+
* question a percentage answers well at any volume size. The imminent-
|
|
29
|
+
* breakage question is answered in GiB by `diskFreeCriticalGib`.
|
|
30
|
+
*
|
|
31
|
+
* The nightly restore-verify transiently doubles the database's on-disk
|
|
32
|
+
* footprint, so a database above ~35 % of the volume crosses a 70 % warn
|
|
33
|
+
* during every verify window. That is the knob's honest reading — the
|
|
34
|
+
* volume genuinely is that full while the scratch copy exists — but when
|
|
35
|
+
* the nightly warn becomes noise, the cures are a larger `storageGb`, a
|
|
36
|
+
* higher threshold here, or `backupVerify: false`; the critical floor is
|
|
37
|
+
* protected from the same transient by the verify gate's ×2 margin. */
|
|
27
38
|
diskWarnThreshold?: number;
|
|
28
|
-
/** ClickHouse data volume
|
|
29
|
-
|
|
39
|
+
/** Free space on the ClickHouse data volume, in GiB, below which the
|
|
40
|
+
* critical alarm pages. Defaults to `defaultDiskFreeCriticalGib(storageGb)`
|
|
41
|
+
* — the larger of an absolute floor and a share of the volume.
|
|
42
|
+
*
|
|
43
|
+
* Absolute rather than a percentage BECAUSE the failure it guards is
|
|
44
|
+
* absolute: a merge needs free bytes proportional to the parts it is
|
|
45
|
+
* merging, not to the volume, and the server stops accepting inserts on a
|
|
46
|
+
* fixed reserve. A percentage critical was either redundant (on a small
|
|
47
|
+
* volume the free-space floor is crossed first) or noise (on a large one
|
|
48
|
+
* 85 % used still leaves hundreds of usable GiB) — never uniquely useful.
|
|
49
|
+
*
|
|
50
|
+
* Must stay under a fifth of `storageGb`, so the floor is a slice of the
|
|
51
|
+
* disk rather than most of it; `resolveClickHouseStorage` enforces the
|
|
52
|
+
* pair together, since neither value is checkable alone.
|
|
53
|
+
*
|
|
54
|
+
* Replaces `diskCriticalThreshold` (a percentage of the volume used). The
|
|
55
|
+
* rename carries a unit change, so there is no value that means the same
|
|
56
|
+
* thing in both — a declaration on the old key fails to compile rather
|
|
57
|
+
* than deploying a threshold read as the wrong quantity. */
|
|
58
|
+
diskFreeCriticalGib?: number;
|
|
30
59
|
/** `<Error>`/`<Fatal>` server-log lines per 5 min. Default 30. */
|
|
31
60
|
serverErrorsPer5Min?: number;
|
|
32
61
|
/** Failed `executeQuery:` error lines per 5 min. Default 10 — absolute, not
|
|
@@ -52,7 +81,7 @@ export interface ClickHouseAlarmThresholds {
|
|
|
52
81
|
* backup heartbeat window. The remaining fields are consumed by the pattern's
|
|
53
82
|
* service-alarm and logAlarms wiring (see `ClickHouseAlarmThresholds`).
|
|
54
83
|
*/
|
|
55
|
-
export type ClickHouseHostAlarmThresholds = Pick<ClickHouseAlarmThresholds, "cpuThreshold" | "diskWarnThreshold" | "
|
|
84
|
+
export type ClickHouseHostAlarmThresholds = Pick<ClickHouseAlarmThresholds, "cpuThreshold" | "diskWarnThreshold" | "backupHeartbeatWindowHours">;
|
|
56
85
|
export declare const BACKUP_HEARTBEAT_DEFAULT_WINDOW_HOURS = 26;
|
|
57
86
|
/**
|
|
58
87
|
* Ceiling derivation — an ORDERING bound, not CloudWatch expressibility.
|
|
@@ -97,7 +126,25 @@ export interface ClickHouseAlarmsProps {
|
|
|
97
126
|
* Omitted when `backupSchedule: false` — no backup task, no log group.
|
|
98
127
|
*/
|
|
99
128
|
backupTaskLogGroup?: ILogGroup;
|
|
129
|
+
/**
|
|
130
|
+
* Whether the backup task runs the restore-verify (the pattern's
|
|
131
|
+
* `backupVerify` prop, already resolved). `false` withholds the two verify
|
|
132
|
+
* alarms so a deliberately disabled verify never leaves marker-counting
|
|
133
|
+
* alarms armed against a script that emits no markers — the alarm half and
|
|
134
|
+
* the script half of the contract disable together. The backup-failure
|
|
135
|
+
* alarm and the success heartbeat stay: they monitor the backup itself.
|
|
136
|
+
*/
|
|
137
|
+
backupVerifyEnabled?: boolean;
|
|
100
138
|
config?: ClickHouseHostAlarmThresholds;
|
|
139
|
+
/**
|
|
140
|
+
* Critical free-space floor in GiB, ALREADY RESOLVED. Not read off `config`
|
|
141
|
+
* because its default depends on the volume size and its legality depends
|
|
142
|
+
* on the volume size — both of which live on the pattern, not here. The
|
|
143
|
+
* pattern resolves the pair once (`resolveClickHouseStorage`) and hands the
|
|
144
|
+
* answer down; re-deriving it here would be a second source for a number
|
|
145
|
+
* that must agree with the volume it guards.
|
|
146
|
+
*/
|
|
147
|
+
diskFreeCriticalGib: number;
|
|
101
148
|
/** Application ID for webhook-to-application alarm mapping. */
|
|
102
149
|
applicationId?: string;
|
|
103
150
|
}
|
|
@@ -107,7 +154,9 @@ export interface ClickHouseAlarmsProps {
|
|
|
107
154
|
* (never installed on these hosts) but by the lightweight put-metric-data
|
|
108
155
|
* timer the user-data installs (see `buildClickHouseUserData`), which publishes
|
|
109
156
|
* `mem_used_percent` / `disk_used_percent` under the same names and
|
|
110
|
-
* `AutoScalingGroupName` dimension the agent would use
|
|
157
|
+
* `AutoScalingGroupName` dimension the agent would use, plus `disk_free_gib`,
|
|
158
|
+
* which has no CloudWatch Agent equivalent and exists because the disk
|
|
159
|
+
* question that pages must be answered in bytes rather than percent — plus the
|
|
111
160
|
* backup-failure log alarm when a backup-task log group is supplied:
|
|
112
161
|
*
|
|
113
162
|
* - **Backup failures** — `AccessDenied` or `S3Exception` from the backup
|