@oneuptime/common 10.5.28 → 10.5.30

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.
Files changed (26) hide show
  1. package/Models/AnalyticsModels/MetricBaselineHourly.ts +30 -0
  2. package/Models/AnalyticsModels/MetricItemAggMV1m.ts +35 -5
  3. package/Models/AnalyticsModels/MetricItemAggMV1mByHost.ts +28 -0
  4. package/Server/Services/UserNotificationRuleService.ts +3 -3
  5. package/Types/Dashboard/DashboardTemplates.ts +27 -26
  6. package/Types/Metrics/MetricQueryConfigData.ts +11 -0
  7. package/UI/Components/ModelTable/BaseModelTable.tsx +38 -0
  8. package/UI/Utils/Navigation.ts +39 -0
  9. package/UI/Utils/TableFilterUrlState.ts +92 -0
  10. package/build/dist/Models/AnalyticsModels/MetricBaselineHourly.js +30 -0
  11. package/build/dist/Models/AnalyticsModels/MetricBaselineHourly.js.map +1 -1
  12. package/build/dist/Models/AnalyticsModels/MetricItemAggMV1m.js +35 -5
  13. package/build/dist/Models/AnalyticsModels/MetricItemAggMV1m.js.map +1 -1
  14. package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByHost.js +28 -0
  15. package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByHost.js.map +1 -1
  16. package/build/dist/Server/Services/UserNotificationRuleService.js +3 -3
  17. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  18. package/build/dist/Types/Dashboard/DashboardTemplates.js +27 -26
  19. package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
  20. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +29 -0
  21. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  22. package/build/dist/UI/Utils/Navigation.js +30 -0
  23. package/build/dist/UI/Utils/Navigation.js.map +1 -1
  24. package/build/dist/UI/Utils/TableFilterUrlState.js +67 -0
  25. package/build/dist/UI/Utils/TableFilterUrlState.js.map +1 -0
  26. package/package.json +1 -1
@@ -171,6 +171,36 @@ export default class MetricBaselineHourly extends AnalyticsBaseModel {
171
171
  maxObsStateColumn,
172
172
  ],
173
173
  projections: [],
174
+ /*
175
+ * Baseline materialized view. Canonical definition applied
176
+ * idempotently by the analytics schema-sync on every boot (see
177
+ * AnalyticsTableManagement.createMaterializedViews), so a
178
+ * wiped/recreated ClickHouse volume self-heals. Aggregates each
179
+ * sample into a (calendar day, hour-of-week) cell.
180
+ */
181
+ materializedViews: [
182
+ {
183
+ name: "MetricBaselineHourly_mv",
184
+ query: `CREATE MATERIALIZED VIEW IF NOT EXISTS MetricBaselineHourly_mv
185
+ TO MetricBaselineHourly
186
+ AS
187
+ SELECT
188
+ projectId,
189
+ name,
190
+ serviceId,
191
+ toDate(time) AS day,
192
+ toUInt8((toDayOfWeek(time, 1) - 1) * 24 + toHour(time)) AS hourOfWeek,
193
+ countState(toFloat64(coalesce(value, sum, 0))) AS sampleCountState,
194
+ avgState(toFloat64(coalesce(value, sum, 0))) AS meanState,
195
+ stddevPopState(toFloat64(coalesce(value, sum, 0))) AS stddevState,
196
+ quantileState(0.5)(toFloat64(coalesce(value, sum, 0))) AS medianState,
197
+ quantileState(0.95)(toFloat64(coalesce(value, sum, 0))) AS p95State,
198
+ minState(toFloat64(coalesce(value, sum, 0))) AS minObsState,
199
+ maxState(toFloat64(coalesce(value, sum, 0))) AS maxObsState
200
+ FROM MetricItemV2
201
+ GROUP BY projectId, name, serviceId, day, hourOfWeek`,
202
+ },
203
+ ],
174
204
  /*
175
205
  * Sort key prefix matches the read-side WHERE clause of
176
206
  * MetricBaselineService.getBaseline so lookups touch a tight
@@ -8,11 +8,12 @@ import TableColumnType from "../../Types/AnalyticsDatabase/TableColumnType";
8
8
  * Per-minute pre-aggregated rollup of `MetricItemV2` value samples.
9
9
  *
10
10
  * Populated by the attached materialized view `MetricItemAggMV1m_mv`
11
- * (defined in the `AddMetricMinuteAggregateMaterializedView` data
12
- * migration), which fires on every insert into `MetricItemV2`. Each
13
- * row holds AggregateFunction *states* partial intermediate values
14
- * combined by background merges and finalized at read time via
15
- * `*Merge()` (e.g. `sumMerge(valueSumState)`).
11
+ * (declared below in `materializedViews` and applied idempotently by
12
+ * the analytics schema-sync on every boot), which fires on every
13
+ * insert into `MetricItemV2`. Each row holds AggregateFunction
14
+ * *states* partial intermediate values combined by background merges
15
+ * and finalized at read time via `*Merge()` (e.g.
16
+ * `sumMerge(valueSumState)`).
16
17
  *
17
18
  * Read access goes through `MetricService.tryBuildMinuteAggregateMVStatement`
18
19
  * for chart aggregation queries that span ≥ 1 minute and don't filter
@@ -130,6 +131,35 @@ export default class MetricItemAggMV1m extends AnalyticsBaseModel {
130
131
  retentionDateColumn,
131
132
  ],
132
133
  projections: [],
134
+ /*
135
+ * Materialized view that pre-rolls MetricItemV2 value samples into
136
+ * 1-minute buckets. This is the canonical definition: the analytics
137
+ * schema-sync (AnalyticsTableManagement.createMaterializedViews)
138
+ * creates it on every boot if missing, so a wiped/recreated
139
+ * ClickHouse volume self-heals even when the one-time DataMigration
140
+ * is already recorded as executed in Postgres. Kept idempotent with
141
+ * `IF NOT EXISTS`.
142
+ */
143
+ materializedViews: [
144
+ {
145
+ name: "MetricItemAggMV1m_mv",
146
+ query: `CREATE MATERIALIZED VIEW IF NOT EXISTS MetricItemAggMV1m_mv
147
+ TO MetricItemAggMV1m
148
+ AS
149
+ SELECT
150
+ projectId,
151
+ name,
152
+ serviceId,
153
+ toStartOfMinute(time) AS bucketTime,
154
+ sumState(toFloat64(coalesce(value, sum, 0))) AS valueSumState,
155
+ countState(toFloat64(coalesce(value, sum, 0))) AS valueCountState,
156
+ minState(toFloat64(coalesce(value, sum, 0))) AS valueMinState,
157
+ maxState(toFloat64(coalesce(value, sum, 0))) AS valueMaxState,
158
+ max(retentionDate) AS retentionDate
159
+ FROM MetricItemV2
160
+ GROUP BY projectId, name, serviceId, bucketTime`,
161
+ },
162
+ ],
133
163
  sortKeys: ["projectId", "name", "serviceId", "bucketTime"],
134
164
  primaryKeys: ["projectId", "name", "serviceId", "bucketTime"],
135
165
  partitionKey: "sipHash64(projectId) % 16",
@@ -131,6 +131,34 @@ export default class MetricItemAggMV1mByHost extends AnalyticsBaseModel {
131
131
  retentionDateColumn,
132
132
  ],
133
133
  projections: [],
134
+ /*
135
+ * Per-host materialized view. Canonical definition applied
136
+ * idempotently by the analytics schema-sync on every boot (see
137
+ * AnalyticsTableManagement.createMaterializedViews), so a
138
+ * wiped/recreated ClickHouse volume self-heals. Rows without a
139
+ * host identifier are filtered out so the per-host MV stays small.
140
+ */
141
+ materializedViews: [
142
+ {
143
+ name: "MetricItemAggMV1mByHost_mv",
144
+ query: `CREATE MATERIALIZED VIEW IF NOT EXISTS MetricItemAggMV1mByHost_mv
145
+ TO MetricItemAggMV1mByHost
146
+ AS
147
+ SELECT
148
+ projectId,
149
+ name,
150
+ attributes['resource.host.name'] AS hostIdentifier,
151
+ toStartOfMinute(time) AS bucketTime,
152
+ sumState(toFloat64(coalesce(value, sum, 0))) AS valueSumState,
153
+ countState(toFloat64(coalesce(value, sum, 0))) AS valueCountState,
154
+ minState(toFloat64(coalesce(value, sum, 0))) AS valueMinState,
155
+ maxState(toFloat64(coalesce(value, sum, 0))) AS valueMaxState,
156
+ max(retentionDate) AS retentionDate
157
+ FROM MetricItemV2
158
+ WHERE attributes['resource.host.name'] != ''
159
+ GROUP BY projectId, name, hostIdentifier, bucketTime`,
160
+ },
161
+ ],
134
162
  sortKeys: ["projectId", "name", "hostIdentifier", "bucketTime"],
135
163
  primaryKeys: ["projectId", "name", "hostIdentifier", "bucketTime"],
136
164
  partitionKey: "sipHash64(projectId) % 16",
@@ -1838,7 +1838,7 @@ export class Service extends DatabaseService<Model> {
1838
1838
  to: to,
1839
1839
  data: [
1840
1840
  {
1841
- sayMessage: "This is a call from OneUptime",
1841
+ sayMessage: "This is a call from One Uptime",
1842
1842
  },
1843
1843
  {
1844
1844
  sayMessage: "A new alert has been created",
@@ -1894,7 +1894,7 @@ export class Service extends DatabaseService<Model> {
1894
1894
  to: to,
1895
1895
  data: [
1896
1896
  {
1897
- sayMessage: "This is a call from OneUptime",
1897
+ sayMessage: "This is a call from One Uptime",
1898
1898
  },
1899
1899
  {
1900
1900
  sayMessage: "A new incident has been created",
@@ -1951,7 +1951,7 @@ export class Service extends DatabaseService<Model> {
1951
1951
  to: to,
1952
1952
  data: [
1953
1953
  {
1954
- sayMessage: "This is a call from OneUptime",
1954
+ sayMessage: "This is a call from One Uptime",
1955
1955
  },
1956
1956
  {
1957
1957
  sayMessage: "A new alert episode has been created",
@@ -933,12 +933,13 @@ function createKubernetesDashboardConfig(): DashboardViewConfig {
933
933
  * first-class percent metric we replace it with a Value widget that
934
934
  * renders the absolute usage via ValueFormatter (e.g. "8.3 GB").
935
935
  *
936
- * - CPU widgets use OTel's k8s.*.cpu.utilization, which the collector
937
- * emits as a [0, 1] ratio with unit "1". DashboardValueComponent /
938
- * DashboardGaugeComponent now scale that to a percent at render time
939
- * when the metric name carries the `.utilization` suffix, so "0.05"
940
- * reads as "5.00%" and gauge thresholds in the natural 0-100 scale work
941
- * as expected.
936
+ * - CPU widgets show cores, not a percent. OTel's k8s.*.cpu.utilization
937
+ * is a misnamed cores gauge (cores in use, NOT a [0, 1] ratio), and
938
+ * this templated dashboard's renderer can't divide by per-node
939
+ * allocatable CPU to form a true percentage. So we use the `.usage`
940
+ * metrics and label them in cores ("2.3 cores"). The Kubernetes
941
+ * cluster overview page — which fetches `k8s.node.allocatable_cpu` —
942
+ * is where CPU is shown as a real "% of capacity".
942
943
  */
943
944
  const components: Array<DashboardBaseComponent> = [
944
945
  // Row 0: Title
@@ -953,16 +954,16 @@ function createKubernetesDashboardConfig(): DashboardViewConfig {
953
954
 
954
955
  /*
955
956
  * Row 1: Key cluster metrics — averages render with proper units via
956
- * ValueFormatter (CPU utilization"%", memory.usage → "MB"/"GB").
957
+ * ValueFormatter (CPU usagecores, memory.usage → "MB"/"GB").
957
958
  * All four are "higher = worse" (closer to capacity = bad).
958
959
  */
959
960
  createValueComponent({
960
- title: "Pod CPU (avg)",
961
+ title: "Pod CPU (cores, avg)",
961
962
  top: 1,
962
963
  left: 0,
963
964
  width: 3,
964
965
  metricConfig: {
965
- metricName: "k8s.pod.cpu.utilization",
966
+ metricName: "k8s.pod.cpu.usage",
966
967
  aggregationType: MetricsAggregationType.Avg,
967
968
  },
968
969
  trendDirection: DashboardValueTrendDirection.HigherIsWorse,
@@ -979,12 +980,12 @@ function createKubernetesDashboardConfig(): DashboardViewConfig {
979
980
  trendDirection: DashboardValueTrendDirection.HigherIsWorse,
980
981
  }),
981
982
  createValueComponent({
982
- title: "Node CPU (avg)",
983
+ title: "Node CPU (cores, avg)",
983
984
  top: 1,
984
985
  left: 6,
985
986
  width: 3,
986
987
  metricConfig: {
987
- metricName: "k8s.node.cpu.utilization",
988
+ metricName: "k8s.node.cpu.usage",
988
989
  aggregationType: MetricsAggregationType.Avg,
989
990
  },
990
991
  trendDirection: DashboardValueTrendDirection.HigherIsWorse,
@@ -1003,16 +1004,16 @@ function createKubernetesDashboardConfig(): DashboardViewConfig {
1003
1004
 
1004
1005
  // Row 2-4: Resource usage charts
1005
1006
  createChartComponent({
1006
- title: "CPU Usage Over Time",
1007
+ title: "Pod CPU Cores Over Time",
1007
1008
  chartType: DashboardChartType.Line,
1008
1009
  top: 2,
1009
1010
  left: 0,
1010
1011
  width: 6,
1011
1012
  height: 3,
1012
1013
  metricConfig: {
1013
- metricName: "k8s.pod.cpu.utilization",
1014
+ metricName: "k8s.pod.cpu.usage",
1014
1015
  aggregationType: MetricsAggregationType.Avg,
1015
- legend: "CPU Utilization",
1016
+ legend: "CPU Cores",
1016
1017
  },
1017
1018
  }),
1018
1019
  createChartComponent({
@@ -1072,24 +1073,24 @@ function createKubernetesDashboardConfig(): DashboardViewConfig {
1072
1073
  }),
1073
1074
 
1074
1075
  /*
1075
- * Row 11-13: CPU gauge (auto-scaled from [0,1] to percent), and the
1076
- * network throughput chart. The old "Memory Utilization" gauge over
1077
- * raw bytes is gone see top-of-function comment.
1076
+ * Row 11-13: cluster CPU cores tile and the network throughput
1077
+ * chart. This was a 0-100 CPU gauge, but the cores-valued
1078
+ * `k8s.node.cpu.utilization` pinned it to nonsense (e.g. 711%) and
1079
+ * the templated renderer can't divide by allocatable CPU to make a
1080
+ * real percentage — so we show total cores in use instead. The old
1081
+ * "Memory Utilization" gauge over raw bytes is gone — see
1082
+ * top-of-function comment.
1078
1083
  */
1079
- createGaugeComponent({
1080
- title: "Cluster CPU Utilization",
1084
+ createValueComponent({
1085
+ title: "Cluster CPU (cores in use)",
1081
1086
  top: 11,
1082
1087
  left: 0,
1083
1088
  width: 4,
1084
- height: 3,
1085
- minValue: 0,
1086
- maxValue: 100,
1087
- warningThreshold: 70,
1088
- criticalThreshold: 90,
1089
1089
  metricConfig: {
1090
- metricName: "k8s.node.cpu.utilization",
1091
- aggregationType: MetricsAggregationType.Avg,
1090
+ metricName: "k8s.node.cpu.usage",
1091
+ aggregationType: MetricsAggregationType.Sum,
1092
1092
  },
1093
+ trendDirection: DashboardValueTrendDirection.HigherIsWorse,
1093
1094
  }),
1094
1095
  createChartComponent({
1095
1096
  title: "Network I/O",
@@ -18,6 +18,17 @@ export default interface MetricQueryConfigData {
18
18
  getSeries?: ((data: AggregatedModel) => ChartSeries) | undefined;
19
19
  chartType?: MetricChartType | undefined;
20
20
  yAxisValueFormatter?: ((value: number) => string) | undefined;
21
+ /*
22
+ * Optional post-aggregation transform of each datapoint's plotted
23
+ * value. Runs before `transformAsRate`. Receives the raw aggregated
24
+ * value plus the full datapoint (so the transform can read grouped
25
+ * attributes like `resource.k8s.node.name`). Used, e.g., to turn a
26
+ * Kubernetes CPU *cores* value into "% of its node's allocatable CPU"
27
+ * by dividing each point by the node's capacity. Unset = no change.
28
+ */
29
+ transformValue?:
30
+ | ((value: number, dataPoint: AggregatedModel) => number)
31
+ | undefined;
21
32
  warningThreshold?: number | undefined;
22
33
  criticalThreshold?: number | undefined;
23
34
  /*
@@ -12,6 +12,7 @@ import Sort from "../../../Types/BaseDatabase/Sort";
12
12
  import Select from "../../../Types/BaseDatabase/Select";
13
13
  import { Logger } from "../../Utils/Logger";
14
14
  import Navigation from "../../Utils/Navigation";
15
+ import TableFilterUrlState from "../../Utils/TableFilterUrlState";
15
16
  import PermissionUtil from "../../Utils/Permission";
16
17
  import ProjectUtil from "../../Utils/Project";
17
18
  import User from "../../Utils/User";
@@ -1862,6 +1863,43 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
1862
1863
  setQuery({ ...newQuery });
1863
1864
  };
1864
1865
 
1866
+ /*
1867
+ * URL persistence for classic (column) filters, keyed by the table's
1868
+ * `saveFilterProps.tableId`. Mirrors the facet persistence in
1869
+ * `useResourceOwners` so filters survive navigating to a detail page and
1870
+ * back, and so a filtered view is shareable. `hasRestoredUrlFilters` is
1871
+ * state (not a ref) so the persist effect only runs after the restore has
1872
+ * been applied — never clobbering the snapshot with the empty default.
1873
+ */
1874
+ const [hasRestoredUrlFilters, setHasRestoredUrlFilters] =
1875
+ useState<boolean>(false);
1876
+
1877
+ useEffect(() => {
1878
+ const restored: JSONObject | null = TableFilterUrlState.read(
1879
+ props.saveFilterProps?.tableId,
1880
+ "filter",
1881
+ );
1882
+ if (restored) {
1883
+ /*
1884
+ * Re-run through onFilterChanged so the derived query is rebuilt and the
1885
+ * first fetch uses the restored filters.
1886
+ */
1887
+ onFilterChanged(restored as unknown as FilterData<TBaseModel>);
1888
+ }
1889
+ setHasRestoredUrlFilters(true);
1890
+ }, []);
1891
+
1892
+ useEffect(() => {
1893
+ if (!hasRestoredUrlFilters) {
1894
+ return;
1895
+ }
1896
+ TableFilterUrlState.write(
1897
+ props.saveFilterProps?.tableId,
1898
+ "filter",
1899
+ filterData as unknown as JSONObject,
1900
+ );
1901
+ }, [hasRestoredUrlFilters, filterData]);
1902
+
1865
1903
  type GetDeleteBulkActionFunction = () => BulkActionButtonSchema<TBaseModel>;
1866
1904
 
1867
1905
  const getDeleteBulkAction: GetDeleteBulkActionFunction =
@@ -66,6 +66,45 @@ abstract class Navigation {
66
66
  return null;
67
67
  }
68
68
 
69
+ /**
70
+ * Update (or remove) query-string params on the current URL *in place*,
71
+ * without pushing a new browser-history entry and without triggering a
72
+ * react-router navigation/re-render. Pass `null` (or "") as a value to
73
+ * delete that param.
74
+ *
75
+ * This is used to keep table filter/facet state in the URL so it survives
76
+ * navigating to a detail page and back, and so a filtered view is
77
+ * shareable/bookmarkable. We use `replaceState` (not push) so changing a
78
+ * filter doesn't flood the back-button history.
79
+ */
80
+ public static setQueryString(params: Dictionary<string | null>): void {
81
+ const urlSearchParams: URLSearchParams = new URLSearchParams(
82
+ window.location.search,
83
+ );
84
+
85
+ for (const paramName in params) {
86
+ const value: string | null =
87
+ params[paramName] === undefined
88
+ ? null
89
+ : (params[paramName] as string | null);
90
+
91
+ if (value === null || value === "") {
92
+ urlSearchParams.delete(paramName);
93
+ } else {
94
+ urlSearchParams.set(paramName, value);
95
+ }
96
+ }
97
+
98
+ const queryString: string = urlSearchParams.toString();
99
+
100
+ const newRelativeUrl: string =
101
+ window.location.pathname +
102
+ (queryString ? `?${queryString}` : "") +
103
+ window.location.hash;
104
+
105
+ window.history.replaceState(window.history.state, "", newRelativeUrl);
106
+ }
107
+
69
108
  public static getParamByName(
70
109
  paramName: string,
71
110
  routeTemplate: Route,
@@ -0,0 +1,92 @@
1
+ import Navigation from "./Navigation";
2
+ import { JSONObject } from "../../Types/JSON";
3
+ import JSONFunctions from "../../Types/JSONFunctions";
4
+
5
+ export type TableFilterUrlStateKind = "facets" | "filter";
6
+
7
+ /**
8
+ * Persists a table's filter/facet selections in the URL query string so they:
9
+ * - survive navigating to a detail page and clicking the browser "Back" button
10
+ * (the list page remounts with the params still on the URL), and
11
+ * - are shareable/bookmarkable (the URL fully describes the filtered view).
12
+ *
13
+ * The snapshot is run through {@link JSONFunctions} so OneUptime's typed query
14
+ * values (Search, Includes, InBetween, ...) round-trip as real class instances
15
+ * rather than plain objects. Params are namespaced by `tableId` so two tables on
16
+ * the same route don't clobber each other.
17
+ */
18
+ export default class TableFilterUrlState {
19
+ private static getParamName(
20
+ tableId: string,
21
+ kind: TableFilterUrlStateKind,
22
+ ): string {
23
+ return `${tableId}-${kind}`;
24
+ }
25
+
26
+ /*
27
+ * Read a previously-persisted snapshot from the URL. Returns null when absent,
28
+ * empty, or unparseable (e.g. a hand-edited param) so callers fall back to
29
+ * their defaults.
30
+ */
31
+ public static read(
32
+ tableId: string | undefined,
33
+ kind: TableFilterUrlStateKind,
34
+ ): JSONObject | null {
35
+ if (!tableId) {
36
+ return null;
37
+ }
38
+
39
+ try {
40
+ const raw: string | null = Navigation.getQueryStringByName(
41
+ this.getParamName(tableId, kind),
42
+ );
43
+
44
+ if (!raw) {
45
+ return null;
46
+ }
47
+
48
+ const deserialized: JSONObject = JSONFunctions.deserialize(
49
+ JSONFunctions.parseJSONObject(raw),
50
+ );
51
+
52
+ if (deserialized && Object.keys(deserialized).length > 0) {
53
+ return deserialized;
54
+ }
55
+
56
+ return null;
57
+ } catch {
58
+ return null;
59
+ }
60
+ }
61
+
62
+ /*
63
+ * Persist a snapshot to the URL, or remove the param when the state is empty
64
+ * (`null`, or an object with no keys). Does not add a browser-history entry.
65
+ */
66
+ public static write(
67
+ tableId: string | undefined,
68
+ kind: TableFilterUrlStateKind,
69
+ state: JSONObject | null,
70
+ ): void {
71
+ if (!tableId) {
72
+ return;
73
+ }
74
+
75
+ try {
76
+ const hasState: boolean = Boolean(state && Object.keys(state).length > 0);
77
+
78
+ const value: string | null = hasState
79
+ ? JSON.stringify(JSONFunctions.serialize(state as JSONObject))
80
+ : null;
81
+
82
+ Navigation.setQueryString({
83
+ [this.getParamName(tableId, kind)]: value,
84
+ });
85
+ } catch {
86
+ /*
87
+ * Serialization failure (shouldn't happen for filter data) — skip the
88
+ * URL sync rather than break the table.
89
+ */
90
+ }
91
+ }
92
+ }
@@ -148,6 +148,36 @@ export default class MetricBaselineHourly extends AnalyticsBaseModel {
148
148
  maxObsStateColumn,
149
149
  ],
150
150
  projections: [],
151
+ /*
152
+ * Baseline materialized view. Canonical definition applied
153
+ * idempotently by the analytics schema-sync on every boot (see
154
+ * AnalyticsTableManagement.createMaterializedViews), so a
155
+ * wiped/recreated ClickHouse volume self-heals. Aggregates each
156
+ * sample into a (calendar day, hour-of-week) cell.
157
+ */
158
+ materializedViews: [
159
+ {
160
+ name: "MetricBaselineHourly_mv",
161
+ query: `CREATE MATERIALIZED VIEW IF NOT EXISTS MetricBaselineHourly_mv
162
+ TO MetricBaselineHourly
163
+ AS
164
+ SELECT
165
+ projectId,
166
+ name,
167
+ serviceId,
168
+ toDate(time) AS day,
169
+ toUInt8((toDayOfWeek(time, 1) - 1) * 24 + toHour(time)) AS hourOfWeek,
170
+ countState(toFloat64(coalesce(value, sum, 0))) AS sampleCountState,
171
+ avgState(toFloat64(coalesce(value, sum, 0))) AS meanState,
172
+ stddevPopState(toFloat64(coalesce(value, sum, 0))) AS stddevState,
173
+ quantileState(0.5)(toFloat64(coalesce(value, sum, 0))) AS medianState,
174
+ quantileState(0.95)(toFloat64(coalesce(value, sum, 0))) AS p95State,
175
+ minState(toFloat64(coalesce(value, sum, 0))) AS minObsState,
176
+ maxState(toFloat64(coalesce(value, sum, 0))) AS maxObsState
177
+ FROM MetricItemV2
178
+ GROUP BY projectId, name, serviceId, day, hourOfWeek`,
179
+ },
180
+ ],
151
181
  /*
152
182
  * Sort key prefix matches the read-side WHERE clause of
153
183
  * MetricBaselineService.getBaseline so lookups touch a tight
@@ -1 +1 @@
1
- {"version":3,"file":"MetricBaselineHourly.js","sourceRoot":"","sources":["../../../../Models/AnalyticsModels/MetricBaselineHourly.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"MetricBaselineHourly.js","sourceRoot":"","sources":["../../../../Models/AnalyticsModels/MetricBaselineHourly.ts"],"names":[],"mappings":"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"}
@@ -7,11 +7,12 @@ import TableColumnType from "../../Types/AnalyticsDatabase/TableColumnType";
7
7
  * Per-minute pre-aggregated rollup of `MetricItemV2` value samples.
8
8
  *
9
9
  * Populated by the attached materialized view `MetricItemAggMV1m_mv`
10
- * (defined in the `AddMetricMinuteAggregateMaterializedView` data
11
- * migration), which fires on every insert into `MetricItemV2`. Each
12
- * row holds AggregateFunction *states* partial intermediate values
13
- * combined by background merges and finalized at read time via
14
- * `*Merge()` (e.g. `sumMerge(valueSumState)`).
10
+ * (declared below in `materializedViews` and applied idempotently by
11
+ * the analytics schema-sync on every boot), which fires on every
12
+ * insert into `MetricItemV2`. Each row holds AggregateFunction
13
+ * *states* partial intermediate values combined by background merges
14
+ * and finalized at read time via `*Merge()` (e.g.
15
+ * `sumMerge(valueSumState)`).
15
16
  *
16
17
  * Read access goes through `MetricService.tryBuildMinuteAggregateMVStatement`
17
18
  * for chart aggregation queries that span ≥ 1 minute and don't filter
@@ -113,6 +114,35 @@ export default class MetricItemAggMV1m extends AnalyticsBaseModel {
113
114
  retentionDateColumn,
114
115
  ],
115
116
  projections: [],
117
+ /*
118
+ * Materialized view that pre-rolls MetricItemV2 value samples into
119
+ * 1-minute buckets. This is the canonical definition: the analytics
120
+ * schema-sync (AnalyticsTableManagement.createMaterializedViews)
121
+ * creates it on every boot if missing, so a wiped/recreated
122
+ * ClickHouse volume self-heals even when the one-time DataMigration
123
+ * is already recorded as executed in Postgres. Kept idempotent with
124
+ * `IF NOT EXISTS`.
125
+ */
126
+ materializedViews: [
127
+ {
128
+ name: "MetricItemAggMV1m_mv",
129
+ query: `CREATE MATERIALIZED VIEW IF NOT EXISTS MetricItemAggMV1m_mv
130
+ TO MetricItemAggMV1m
131
+ AS
132
+ SELECT
133
+ projectId,
134
+ name,
135
+ serviceId,
136
+ toStartOfMinute(time) AS bucketTime,
137
+ sumState(toFloat64(coalesce(value, sum, 0))) AS valueSumState,
138
+ countState(toFloat64(coalesce(value, sum, 0))) AS valueCountState,
139
+ minState(toFloat64(coalesce(value, sum, 0))) AS valueMinState,
140
+ maxState(toFloat64(coalesce(value, sum, 0))) AS valueMaxState,
141
+ max(retentionDate) AS retentionDate
142
+ FROM MetricItemV2
143
+ GROUP BY projectId, name, serviceId, bucketTime`,
144
+ },
145
+ ],
116
146
  sortKeys: ["projectId", "name", "serviceId", "bucketTime"],
117
147
  primaryKeys: ["projectId", "name", "serviceId", "bucketTime"],
118
148
  partitionKey: "sipHash64(projectId) % 16",
@@ -1 +1 @@
1
- {"version":3,"file":"MetricItemAggMV1m.js","sourceRoot":"","sources":["../../../../Models/AnalyticsModels/MetricItemAggMV1m.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"MetricItemAggMV1m.js","sourceRoot":"","sources":["../../../../Models/AnalyticsModels/MetricItemAggMV1m.ts"],"names":[],"mappings":"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"}
@@ -111,6 +111,34 @@ export default class MetricItemAggMV1mByHost extends AnalyticsBaseModel {
111
111
  retentionDateColumn,
112
112
  ],
113
113
  projections: [],
114
+ /*
115
+ * Per-host materialized view. Canonical definition applied
116
+ * idempotently by the analytics schema-sync on every boot (see
117
+ * AnalyticsTableManagement.createMaterializedViews), so a
118
+ * wiped/recreated ClickHouse volume self-heals. Rows without a
119
+ * host identifier are filtered out so the per-host MV stays small.
120
+ */
121
+ materializedViews: [
122
+ {
123
+ name: "MetricItemAggMV1mByHost_mv",
124
+ query: `CREATE MATERIALIZED VIEW IF NOT EXISTS MetricItemAggMV1mByHost_mv
125
+ TO MetricItemAggMV1mByHost
126
+ AS
127
+ SELECT
128
+ projectId,
129
+ name,
130
+ attributes['resource.host.name'] AS hostIdentifier,
131
+ toStartOfMinute(time) AS bucketTime,
132
+ sumState(toFloat64(coalesce(value, sum, 0))) AS valueSumState,
133
+ countState(toFloat64(coalesce(value, sum, 0))) AS valueCountState,
134
+ minState(toFloat64(coalesce(value, sum, 0))) AS valueMinState,
135
+ maxState(toFloat64(coalesce(value, sum, 0))) AS valueMaxState,
136
+ max(retentionDate) AS retentionDate
137
+ FROM MetricItemV2
138
+ WHERE attributes['resource.host.name'] != ''
139
+ GROUP BY projectId, name, hostIdentifier, bucketTime`,
140
+ },
141
+ ],
114
142
  sortKeys: ["projectId", "name", "hostIdentifier", "bucketTime"],
115
143
  primaryKeys: ["projectId", "name", "hostIdentifier", "bucketTime"],
116
144
  partitionKey: "sipHash64(projectId) % 16",
@@ -1 +1 @@
1
- {"version":3,"file":"MetricItemAggMV1mByHost.js","sourceRoot":"","sources":["../../../../Models/AnalyticsModels/MetricItemAggMV1mByHost.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"MetricItemAggMV1mByHost.js","sourceRoot":"","sources":["../../../../Models/AnalyticsModels/MetricItemAggMV1mByHost.ts"],"names":[],"mappings":"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"}