1use mz_catalog::memory::objects::{Cluster, ClusterReplica};
11use mz_ore::sql;
12use mz_ore::sql::Sql;
13
14use super::sql::SqlRequest;
15
16#[derive(Debug)]
17pub(crate) struct PrometheusSqlQuery<'a> {
18 pub(crate) metric_name: &'a str,
19 pub(crate) help: &'a str,
20 pub(crate) query: &'static str,
21 pub(crate) value_column_name: &'a str,
22 pub(crate) per_replica: bool,
23}
24
25impl<'a> PrometheusSqlQuery<'a> {
26 pub(crate) fn to_sql_request(
27 &self,
28 cluster: Option<(&Cluster, &ClusterReplica)>,
29 ) -> SqlRequest {
30 let query = Sql::new(self.query);
31 let query = if let Some((cluster, replica)) = cluster {
32 sql!(
33 "SET auto_route_catalog_queries = false; SET CLUSTER = {}; SET CLUSTER_REPLICA = {}; {}",
34 Sql::literal(&cluster.name),
35 Sql::literal(&replica.name),
36 query
37 )
38 } else {
39 sql!(
40 "SET auto_route_catalog_queries = true; RESET CLUSTER; RESET CLUSTER_REPLICA; {}",
41 query
42 )
43 };
44 SqlRequest::Simple { query }
45 }
46}
47
48pub(crate) static FRONTIER_METRIC_QUERIES: &[PrometheusSqlQuery] = &[
49 PrometheusSqlQuery {
50 metric_name: "mz_write_frontier",
51 help: "The global write frontiers of compute and storage collections.",
52 query: "SELECT
53 object_id AS collection_id,
54 coalesce(write_frontier::text::uint8, 18446744073709551615::uint8) AS write_frontier
55 FROM mz_internal.mz_frontiers
56 WHERE object_id NOT LIKE 't%';",
57 value_column_name: "write_frontier",
58 per_replica: false,
59 },
60 PrometheusSqlQuery {
61 metric_name: "mz_read_frontier",
62 help: "The global read frontiers of compute and storage collections.",
63 query: "SELECT
64 object_id AS collection_id,
65 coalesce(read_frontier::text::uint8, 18446744073709551615::uint8) AS read_frontier
66 FROM mz_internal.mz_frontiers
67 WHERE object_id NOT LIKE 't%';",
68 value_column_name: "read_frontier",
69 per_replica: false,
70 },
71 PrometheusSqlQuery {
72 metric_name: "mz_replica_write_frontiers",
73 help: "The per-replica write frontiers of compute and storage collections.",
74 query: "SELECT
75 object_id AS collection_id,
76 coalesce(write_frontier::text::uint8, 18446744073709551615::uint8) AS write_frontier,
77 cluster_id AS instance_id,
78 replica_id AS replica_id
79 FROM mz_catalog.mz_cluster_replica_frontiers
80 JOIN mz_cluster_replicas ON (id = replica_id)
81 WHERE object_id NOT LIKE 't%';",
82 value_column_name: "write_frontier",
83 per_replica: false,
84 },
85 PrometheusSqlQuery {
86 metric_name: "mz_replica_write_frontiers",
87 help: "The per-replica write frontiers of compute and storage collections.",
88 query: "SELECT
89 object_id AS collection_id,
90 coalesce(write_frontier::text::uint8, 18446744073709551615::uint8) AS write_frontier,
91 cluster_id AS instance_id,
92 replica_id AS replica_id
93 FROM mz_catalog.mz_cluster_replica_frontiers
94 JOIN mz_cluster_replicas ON (id = replica_id)
95 WHERE object_id NOT LIKE 't%';",
96 value_column_name: "write_frontier",
97 per_replica: false,
98 },
99];
100
101pub(crate) static USAGE_METRIC_QUERIES: &[PrometheusSqlQuery] = &[
102 PrometheusSqlQuery {
103 metric_name: "mz_compute_cluster_status",
104 help: "Reports the name, ID, size, and availability zone of each cluster replica. Value is always 1.",
105 query: "SELECT
106 1 AS status,
107 cr.cluster_id AS compute_cluster_id,
108 cr.id AS compute_replica_id,
109 c.name AS compute_cluster_name,
110 cr.name AS compute_replica_name,
111 COALESCE(cr.size, '') AS size,
112 COALESCE(cr.availability_zone, '') AS availability_zone,
113 mz_version() AS mz_version
114 FROM
115 mz_cluster_replicas AS cr
116 JOIN mz_clusters AS c
117 ON cr.cluster_id = c.id",
118 value_column_name: "status",
119 per_replica: false,
120 },
121 PrometheusSqlQuery {
122 metric_name: "mz_workload_clusters",
123 help: "Reports the workload_class of each user cluster. Value is always 1.",
124 query: "select
125 c.id as cluster_id,
126 c.name as cluster_name,
127 coalesce(wc.workload_class,'false') as workload_class,
128 1 as value
129 from mz_clusters c
130 join mz_internal.mz_cluster_workload_classes wc
131 on c.id = wc.id",
132 value_column_name: "value",
133 per_replica: false,
134 },
135 PrometheusSqlQuery {
136 metric_name: "mz_clusters_count",
137 help: "Number of active clusters in the instance.",
138 query: "select
139 count(id) as clusters
140 from mz_clusters
141 where id like 'u%'",
142 value_column_name: "clusters",
143 per_replica: false,
144 },
145 PrometheusSqlQuery {
146 metric_name: "mz_cluster_reps_count",
147 help: "Number of active cluster replicas in the instance, by replica size.",
148 query: "select COALESCE(size, '') as size
151 , count(id) as replicas
152 from mz_cluster_replicas
153 where cluster_id like 'u%'
154 group by size",
155 value_column_name: "replicas",
156 per_replica: false,
157 },
158 PrometheusSqlQuery {
159 metric_name: "mz_indexes_count",
160 help: "Number of active indexes in the instance, by the type of relation on which the index is built.",
161 query: "select
162 o.type as relation_type
163 , count(i.id) as indexes
164 from mz_catalog.mz_indexes i
165 join mz_catalog.mz_objects o
166 on i.on_id = o.id
167 where i.id like 'u%'
168 group by relation_type",
169 value_column_name: "indexes",
170 per_replica: false,
171 },
172 PrometheusSqlQuery {
173 metric_name: "mz_sources_count",
174 help: "Number of active sources in the instance, by type, envelope type, and size of source.",
175 query: "SELECT
176 type,
177 COALESCE(envelope_type, '<none>') AS envelope_type,
178 COALESCE(mz_cluster_replicas.size, '') AS size,
179 count(mz_sources.id) AS sources
180 FROM
181 mz_sources, mz_cluster_replicas
182 WHERE mz_sources.id LIKE 'u%'
183 AND mz_sources.type != 'subsource'
184 AND mz_sources.cluster_id = mz_cluster_replicas.cluster_id
185 GROUP BY
186 type, envelope_type, mz_cluster_replicas.size",
187 value_column_name: "sources",
188 per_replica: false,
189 },
190 PrometheusSqlQuery {
191 metric_name: "mz_views_count",
192 help: "Number of active views in the instance.",
193 query: "select count(id) as views from mz_views where id like 'u%'",
194 value_column_name: "views",
195 per_replica: false,
196 },
197 PrometheusSqlQuery {
198 metric_name: "mz_mzd_views_count",
199 help: "Number of active materialized views in the instance.",
200 query: "select count(id) as mzd_views from mz_materialized_views where id like 'u%'",
201 value_column_name: "mzd_views",
202 per_replica: false,
203 },
204 PrometheusSqlQuery {
205 metric_name: "mz_secrets_count",
206 help: "Number of active secrets in the instance.",
207 query: "select count(id) as secrets from mz_secrets where id like 'u%'",
208 value_column_name: "secrets",
209 per_replica: false,
210 },
211 PrometheusSqlQuery {
212 metric_name: "mz_sinks_count",
213 help: "Number of active sinks in the instance, by type, envelope type, and size.",
214 query: "SELECT
215 type,
216 COALESCE(envelope_type, '<none>') AS envelope_type,
217 COALESCE(mz_cluster_replicas.size, '') AS size,
218 count(mz_sinks.id) AS sinks
219 FROM
220 mz_sinks, mz_cluster_replicas
221 WHERE mz_sinks.id LIKE 'u%'
222 AND mz_sinks.cluster_id = mz_cluster_replicas.cluster_id
223 GROUP BY type, envelope_type, mz_cluster_replicas.size",
224 value_column_name: "sinks",
225 per_replica: false,
226 },
227 PrometheusSqlQuery {
228 metric_name: "mz_connections_count",
229 help: "Number of active connections in the instance, by type.",
230 query: "select
231 type
232 , count(id) as connections
233 from mz_connections
234 where id like 'u%'
235 group by type",
236 value_column_name: "connections",
237 per_replica: false,
238 },
239 PrometheusSqlQuery {
240 metric_name: "mz_tables_count",
241 help: "Number of active tables in the instance.",
242 query: "select count(id) as tables from mz_tables where id like 'u%'",
243 value_column_name: "tables",
244 per_replica: false,
245 },
246 PrometheusSqlQuery {
247 metric_name: "mz_catalog_items",
248 help: "Mapping internal id for catalog item.",
249 query: "SELECT
250 concat(d.name, '.', s.name, '.', o.name) AS label,
251 0 AS value
252 FROM mz_objects o
253 JOIN mz_schemas s ON (o.schema_id = s.id)
254 JOIN mz_databases d ON (s.database_id = d.id)
255 WHERE o.id LIKE 'u%'",
256 value_column_name: "value",
257 per_replica: false,
258 },
259 PrometheusSqlQuery {
260 metric_name: "mz_object_id",
261 help: "Mapping external name for catalog item.",
262 query: "SELECT
263 o.id AS label1,
264 concat(d.name, '.', s.name, '.', o.name) AS label2,
265 0 AS value
266 FROM mz_objects o
267 JOIN mz_schemas s ON (o.schema_id = s.id)
268 JOIN mz_databases d ON (s.database_id = d.id)
269 WHERE o.id LIKE 'u%'",
270 value_column_name: "value",
271 per_replica: false,
272 },
273];
274
275pub(crate) static COMPUTE_METRIC_QUERIES: &[PrometheusSqlQuery] = &[
276 PrometheusSqlQuery {
277 metric_name: "mz_arrangement_count",
278 help: "The number of arrangements in a dataflow.",
279 query: "WITH
280 arrangements AS (
281 SELECT DISTINCT operator_id AS id
282 FROM mz_internal.mz_arrangement_records_raw
283 ),
284 collections AS (
285 SELECT
286 id,
287 CASE
288 WHEN starts_with(export_id, 't') THEN 'transient'
289 ELSE export_id
290 END AS export_id
291 FROM
292 mz_internal.mz_dataflow_addresses,
293 mz_internal.mz_compute_exports
294 WHERE address[1] = dataflow_id
295 )
296 SELECT
297 COALESCE(export_id, 'none') AS collection_id,
298 count(*) as count
299 FROM arrangements
300 LEFT JOIN collections USING (id)
301 GROUP BY export_id",
302 value_column_name: "count",
303 per_replica: true,
304 },
305 PrometheusSqlQuery {
306 metric_name: "mz_arrangement_record_count",
307 help: "The number of records in all arrangements in a dataflow.",
308 query: "WITH
309 collections AS (
310 SELECT
311 id,
312 CASE
313 WHEN starts_with(export_id, 't') THEN 'transient'
314 ELSE export_id
315 END AS export_id
316 FROM
317 mz_internal.mz_dataflow_addresses,
318 mz_internal.mz_compute_exports
319 WHERE address[1] = dataflow_id
320 )
321 SELECT
322 worker_id,
323 COALESCE(export_id, 'none') AS collection_id,
324 count(*) as count
325 FROM mz_internal.mz_arrangement_records_raw
326 LEFT JOIN collections ON (operator_id = id)
327 GROUP BY worker_id, export_id",
328 value_column_name: "count",
329 per_replica: true,
330 },
331 PrometheusSqlQuery {
332 metric_name: "mz_arrangement_batch_count",
333 help: "The number of batches in all arrangements in a dataflow.",
334 query: "WITH
335 collections AS (
336 SELECT
337 id,
338 CASE
339 WHEN starts_with(export_id, 't') THEN 'transient'
340 ELSE export_id
341 END AS export_id
342 FROM
343 mz_internal.mz_dataflow_addresses,
344 mz_internal.mz_compute_exports
345 WHERE address[1] = dataflow_id
346 )
347 SELECT
348 worker_id,
349 COALESCE(export_id, 'none') AS collection_id,
350 count(*) as count
351 FROM mz_internal.mz_arrangement_batches_raw
352 LEFT JOIN collections ON (operator_id = id)
353 GROUP BY worker_id, export_id",
354 value_column_name: "count",
355 per_replica: true,
356 },
357 PrometheusSqlQuery {
358 metric_name: "mz_arrangement_size_bytes",
359 help: "The size of all arrangements in a dataflow.",
360 query: "WITH
361 collections AS (
362 SELECT
363 id,
364 CASE
365 WHEN starts_with(export_id, 't') THEN 'transient'
366 ELSE export_id
367 END AS export_id
368 FROM
369 mz_internal.mz_dataflow_addresses,
370 mz_internal.mz_compute_exports
371 WHERE address[1] = dataflow_id
372 )
373 SELECT
374 worker_id,
375 COALESCE(export_id, 'none') AS collection_id,
376 count(*) as count
377 FROM mz_internal.mz_arrangement_heap_size_raw
378 LEFT JOIN collections ON (operator_id = id)
379 GROUP BY worker_id, export_id",
380 value_column_name: "count",
381 per_replica: true,
382 },
383 PrometheusSqlQuery {
384 metric_name: "mz_arrangement_capacity_bytes",
385 help: "The capacity of all arrangements in all dataflows.",
386 query: "SELECT
387 worker_id,
388 count(*) as count
389 FROM mz_internal.mz_arrangement_heap_capacity_raw
390 GROUP BY worker_id",
391 value_column_name: "count",
392 per_replica: true,
393 },
394 PrometheusSqlQuery {
395 metric_name: "mz_arrangement_allocation_count",
396 help: "The number of allocations in all arrangements in all dataflows.",
397 query: "SELECT
398 worker_id,
399 count(*) as count
400 FROM mz_internal.mz_arrangement_heap_allocations_raw
401 GROUP BY worker_id",
402 value_column_name: "count",
403 per_replica: true,
404 },
405 PrometheusSqlQuery {
406 metric_name: "mz_compute_replica_park_duration_seconds_total",
407 help: "The total time workers were parked since restart.",
408 query: "SELECT
409 worker_id,
410 sum(slept_for_ns * count)::float8 / 1000000000 AS duration_s
411 FROM mz_internal.mz_scheduling_parks_histogram_per_worker
412 GROUP BY worker_id",
413 value_column_name: "duration_s",
414 per_replica: true,
415 },
416 PrometheusSqlQuery {
417 metric_name: "mz_compute_replica_peek_count",
418 help: "The number of pending peeks.",
419 query: "SELECT worker_id, count(*) as count
420 FROM mz_internal.mz_active_peeks_per_worker
421 GROUP BY worker_id",
422 value_column_name: "count",
423 per_replica: true,
424 },
425 PrometheusSqlQuery {
426 metric_name: "mz_dataflow_elapsed_seconds_total",
427 help: "The total time spent computing a dataflow.",
428 query: "SELECT
429 worker_id,
430 CASE
431 WHEN starts_with(export_id, 't') THEN 'transient'
432 ELSE export_id
433 END AS collection_id,
434 sum(elapsed_ns)::float8 / 1000000000 AS elapsed_s
435 FROM
436 mz_internal.mz_scheduling_elapsed_per_worker AS s,
437 mz_internal.mz_dataflow_operators AS o,
438 mz_internal.mz_dataflow_addresses AS a,
439 mz_internal.mz_compute_exports AS e
440 WHERE
441 o.id = s.id AND
442 o.id = a.id AND
443 list_length(a.address) = 1 AND
444 e.dataflow_id = a.address[1]
445 GROUP BY worker_id, collection_id",
446 value_column_name: "elapsed_s",
447 per_replica: true,
448 },
449 PrometheusSqlQuery {
450 metric_name: "mz_dataflow_error_count",
451 help: "The number of errors in a dataflow",
452 query: "SELECT
453 CASE
454 WHEN starts_with(export_id, 't') THEN 'transient'
455 ELSE export_id
456 END AS collection_id,
457 count::uint8 as count
458 FROM mz_internal.mz_compute_error_counts",
459 value_column_name: "count",
460 per_replica: true,
461 },
462];
463
464pub(crate) static STORAGE_METRIC_QUERIES: &[PrometheusSqlQuery] = &[
465 PrometheusSqlQuery {
466 metric_name: "mz_storage_objects",
467 help: "Nicely labeled information about existing sources and sinks.",
468 value_column_name: "value",
469 per_replica: false,
470 query: "
471 WITH
472 -- All user, non- progress or subsource sources.
473 top_level_sources AS (
474 SELECT id, connection_id, type, envelope_type, cluster_id
475 FROM mz_sources
476 WHERE id LIKE 'u%' AND type NOT IN ('progress', 'subsource')
477 ),
478 -- Sources enriched with the type of the core connection.
479 source_and_conns AS (
480 SELECT top_level_sources.*, mc.type AS connection_type
481 FROM top_level_sources
482 LEFT OUTER JOIN mz_connections mc ON mc.id = top_level_sources.connection_id
483 ),
484
485 -- All user sinks.
486 top_level_sinks AS (
487 SELECT id, connection_id, type, envelope_type, cluster_id
488 FROM mz_sinks
489 WHERE id LIKE 'u%'
490 ),
491 -- Sinks enriched with the type of the core connection.
492 sink_and_conns AS (
493 SELECT top_level_sinks.*, mc.type AS connection_type
494 FROM top_level_sinks
495 LEFT OUTER JOIN mz_connections mc ON mc.id = top_level_sinks.connection_id
496 ),
497
498 -- All objects we care about
499 object_and_conns AS (
500 SELECT * FROM source_and_conns
501 UNION ALL
502 SELECT * FROM sink_and_conns
503 ),
504
505 -- The networking the object connection uses, if any.
506 networking AS (
507 SELECT object_and_conns.id, mc.id AS networking_id, mc.type AS networking_type
508 FROM object_and_conns
509 JOIN mz_internal.mz_object_dependencies mod ON object_and_conns.connection_id = mod.object_id
510 JOIN mz_connections mc ON mc.id = mod.referenced_object_id
511 -- Not required but made explicit
512 WHERE object_and_conns.connection_id IS NOT NULL
513 ),
514 -- The connection the format of the object uses, if any. This uses `mz_object_dependencies`
515 -- and a filter to find non-core objects the object depends on.
516 format_conns AS (
517 SELECT object_and_conns.id, mc.id AS format_connection_id, mc.type AS format_connection
518 FROM mz_internal.mz_object_dependencies mod
519 JOIN object_and_conns ON mod.object_id = object_and_conns.id
520 JOIN mz_connections mc ON mc.id = mod.referenced_object_id
521 WHERE mc.id NOT IN (SELECT connection_id FROM top_level_sources UNION ALL SELECT connection_id FROM top_level_sinks)
522 -- Not required but made explicit
523 AND object_and_conns.connection_id IS NOT NULL
524 ),
525 -- The networking used by `format_conns`, if any.
526 format_conn_deps AS (
527 SELECT format_conns.id, mc.id AS format_connection_networking_id, mc.type AS format_connection_networking
528 FROM format_conns
529 JOIN mz_internal.mz_object_dependencies mod ON mod.object_id = format_conns.format_connection_id
530 JOIN mz_connections mc
531 ON mc.id = mod.referenced_object_id
532 ),
533
534 -- When aggregating values that are known to be the same, we just use `MAX` for simplicity.
535
536 -- source_and_conns LEFT JOINed with the networking and format connection information.
537 -- Missing networking/format connections are coalesced to `none`, and aggregated with a comma.
538 -- This is because sources can have multiple type of networking (i.e. different kafka brokers with
539 -- different configuration), and multiple connections for formats (for key and value formats).
540 --
541 -- The actual format type is not yet included, as it depends on https://github.com/MaterializeInc/materialize/pull/23880
542 sources AS (
543 SELECT
544 -- Whether its a source or sink
545 'source' AS type,
546 1 AS value,
547 source_and_conns.id AS id,
548 -- What type of source/sink it is
549 MAX(type) AS object_type,
550 COALESCE(MAX(connection_type), 'none') AS connection_type,
551 COALESCE(MAX(envelope_type), 'none') AS envelope_type,
552 STRING_AGG(
553 DISTINCT COALESCE(networking_type, 'none'),
554 ','
555 ORDER BY COALESCE(networking_type, 'none') ASC
556 ) AS networking_type,
557 STRING_AGG(
558 DISTINCT COALESCE(format_connection, 'none'),
559 ','
560 ORDER BY COALESCE(format_connection, 'none') ASC
561 ) AS format_connection,
562 STRING_AGG(
563 DISTINCT COALESCE(format_connection_networking, 'none'),
564 ','
565 ORDER BY COALESCE(format_connection_networking, 'none') ASC
566 ) AS format_connection_networking,
567 MAX(cluster_id) AS cluster_id
568 FROM source_and_conns
569 LEFT OUTER JOIN networking ON networking.id = source_and_conns.id
570 LEFT OUTER JOIN format_conns ON format_conns.id = source_and_conns.id
571 LEFT OUTER JOIN format_conn_deps ON format_conn_deps.id = source_and_conns.id
572 GROUP BY source_and_conns.id
573 ),
574
575 sinks AS (
576 SELECT
577 'sink' AS type,
578 1 AS value,
579 sink_and_conns.id AS id,
580 MAX(type) AS object_type,
581 COALESCE(MAX(connection_type), 'none') AS connection_type,
582 COALESCE(MAX(envelope_type), 'none') AS envelope_type,
583 STRING_AGG(
584 DISTINCT COALESCE(networking_type, 'none'),
585 ','
586 ORDER BY COALESCE(networking_type, 'none') ASC
587 ) AS networking_type,
588 -- Sinks can only have 1 format connection but we aggregate
589 -- for consistency.
590 STRING_AGG(
591 DISTINCT COALESCE(format_connection, 'none'),
592 ','
593 ORDER BY COALESCE(format_connection, 'none') ASC
594 ) AS format_connection,
595 STRING_AGG(
596 DISTINCT COALESCE(format_connection_networking, 'none'),
597 ','
598 ORDER BY COALESCE(format_connection_networking, 'none') ASC
599 ) AS format_connection_networking,
600 MAX(cluster_id) AS cluster_id
601 FROM sink_and_conns
602 LEFT OUTER JOIN networking ON networking.id = sink_and_conns.id
603 LEFT OUTER JOIN format_conns ON format_conns.id = sink_and_conns.id
604 LEFT OUTER JOIN format_conn_deps ON format_conn_deps.id = sink_and_conns.id
605 GROUP BY sink_and_conns.id
606 ),
607
608 -- everything without replicas
609 together AS (
610 SELECT * FROM sources
611 UNION ALL
612 SELECT * from sinks
613 ),
614
615 with_cluster_replicas AS (
616 SELECT
617 -- `together.*` doesn't work because we need to aggregate the columns :(
618 MAX(together.id) AS id,
619 MAX(together.type) AS type,
620 MAX(together.object_type) AS object_type,
621 -- We just report 1 to the gauge. The `replica_id` labels aggregates the 0-many replicas associated
622 -- with this object.
623 MAX(together.value) AS value,
624 MAX(together.connection_type) AS connection_type,
625 MAX(together.envelope_type) AS envelope_type,
626 MAX(together.networking_type) AS networking_type,
627 MAX(together.format_connection) AS format_connection,
628 MAX(together.format_connection_networking) AS format_connection_networking,
629 MAX(together.cluster_id) AS cluster_id,
630 mcr.id AS replica_id,
631 -- Coalesce to 0 when there is no replica. This ensures `with_pod` below will generate
632 -- 1 row for the object. Also, -1 because `generate_series` is inclusive.
633 COALESCE((mcrs.processes - 1)::int, 0) AS processes
634 FROM together
635 LEFT OUTER JOIN mz_cluster_replicas mcr ON together.cluster_id = mcr.cluster_id
636 LEFT OUTER JOIN mz_catalog.mz_cluster_replica_sizes mcrs ON mcr.size = mcrs.size
637 GROUP BY together.id, mcr.id, mcrs.processes
638 ),
639
640 with_pod AS (
641 SELECT
642 id,
643 type,
644 object_type,
645 value,
646 connection_type,
647 envelope_type,
648 networking_type,
649 format_connection,
650 format_connection_networking,
651 cluster_id,
652 COALESCE(replica_id, 'none') AS replica_id,
653 -- When `replica_id` is NULL`, this coalesces to `none`.
654 COALESCE('cluster-' || cluster_id || '-replica-' || replica_id || '-' || generated, 'none') AS synthesized_pod
655 FROM with_cluster_replicas, generate_series(0, processes) generated
656 )
657
658 SELECT * FROM with_pod;
659 ",
660 },
661];