From 2777db22fb1cba9a0bdd9f4fba82c62f74c65016 Mon Sep 17 00:00:00 2001 From: Cristhian Garcia Date: Fri, 27 Sep 2024 09:45:21 -0500 Subject: [PATCH 01/10] feat(metrics): sort metrics by clickhouse query time --- .../pythonpath/performance_metrics.py | 61 ++++++++++--------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/tutoraspects/templates/aspects/apps/superset/pythonpath/performance_metrics.py b/tutoraspects/templates/aspects/apps/superset/pythonpath/performance_metrics.py index c361b9e85..a6116f928 100644 --- a/tutoraspects/templates/aspects/apps/superset/pythonpath/performance_metrics.py +++ b/tutoraspects/templates/aspects/apps/superset/pythonpath/performance_metrics.py @@ -42,7 +42,10 @@ "Result rows: {result_rows}\n" "Memory Usage (MB): {memory_usage_mb}\n" "Row count (superset) {rowcount:}\n" - "Filters: {filters}\n\n" + "Filters: {filters}\n" + "SQL:\n" + "{sql}\n\n\n" + ) @click.command() @@ -123,7 +126,7 @@ def performance_metrics(course_key, dashboard_slug, slice_name, print_sql, return report logger.info("Waiting for clickhouse log...") - time.sleep(20) + time.sleep(30) get_query_log_from_clickhouse(report, query_contexts, print_sql, fail_on_error) return report @@ -184,10 +187,12 @@ def measure_chart(slice, query_context, fail_on_error): query_context = ChartDataQueryContextSchema().load(query_context) command = ChartDataCommand(query_context) - start_time = datetime.now() try: + start_time = datetime.now() result = command.run() - + end_time = datetime.now() + result["time_elapsed"] = (end_time - start_time).total_seconds() + result["slice"] = slice for query in result["queries"]: if "error" in query and query["error"]: raise query["error"] @@ -197,11 +202,6 @@ def measure_chart(slice, query_context, fail_on_error): raise e return - end_time = datetime.now() - - result["time_elapsed"] = (end_time - start_time).total_seconds() - result["slice"] = slice - return result @@ -231,39 +231,44 @@ def get_query_log_from_clickhouse(report, query_contexts, print_sql, fail_on_err logger.info("ClickHouse SQL: ") logger.info(parsed_sql) + + for k, chart_result in enumerate(report): + for query in chart_result["queries"]: + parsed_sql = ( + str(sqlparse.parse(query["query"])[0]).replace(";", "") + + "\n FORMAT Native" + ) + chart_result['sql'] = parsed_sql + clickhouse_report = clickhouse_queries.get(parsed_sql, {}) + chart_result.update( + clickhouse_report + ) + chart_result.update({ + "query_duration_ms": chart_result.get("query_duration_ms", 0) + }) + # Sort report by slowest queries - report = sorted(report, key=lambda x: x["time_elapsed"], reverse=True) + report = sorted(report, key=lambda x: x["query_duration_ms"], reverse=True) report_str = f"\nSuperset Reports: {RUN_ID}\n\n" - for i, chart_result in enumerate(report): + for k, chart_result in enumerate(report): report_str += ( report_format.format( - i=(i + 1), + i=(k + 1), dashboard=chart_result["dashboard"], slice=chart_result["slice"], superset_time=chart_result["time_elapsed"] ) ) - for i, query in enumerate(chart_result["queries"]): - parsed_sql = ( - str(sqlparse.parse(query["query"])[0]).replace(";", "") - + "\n FORMAT Native" - ) - - if print_sql: - logger.info("Superset SQL: ") - logger.info(parsed_sql) - - clickhouse_report = clickhouse_queries.get(parsed_sql, {}) + for query in chart_result["queries"]: report_str += ( query_format.format( - query_duration_ms=clickhouse_report.get( - "query_duration_ms", 0 - ) / 1000, - memory_usage_mb=clickhouse_report.get("memory_usage_mb"), - result_rows=clickhouse_report.get("result_rows"), + query_duration_ms=chart_result.get('query_duration_ms') / 1000, + memory_usage_mb=chart_result.get("memory_usage_mb"), + result_rows=chart_result.get("result_rows"), rowcount=query["rowcount"], filters=query["applied_filters"], + sql=chart_result['sql'] if print_sql else '', ) ) logger.info(report_str) From c71a90e390b92c6c57ce63f0f59c2fdf53ff09ec Mon Sep 17 00:00:00 2001 From: Cristhian Garcia Date: Fri, 27 Sep 2024 12:41:28 -0500 Subject: [PATCH 02/10] fix(metrics): use course name filter --- tutoraspects/commands_v1.py | 8 ++++---- .../apps/superset/pythonpath/performance_metrics.py | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tutoraspects/commands_v1.py b/tutoraspects/commands_v1.py index dffdbb23e..2723b6f58 100644 --- a/tutoraspects/commands_v1.py +++ b/tutoraspects/commands_v1.py @@ -149,9 +149,9 @@ def init_clickhouse() -> list[tuple[str, str]]: # Ex: "tutor local do performance-metrics " @click.command(context_settings={"ignore_unknown_options": True}) @click.option( - "--course_key", + "--course_name", default="", - help="A course_key to apply as a filter, you must include the 'course-v1:'.", + help="A course_name to apply as a filter.", ) @click.option( "--dashboard_slug", default="", help="Only run charts for the given dashboard." @@ -169,12 +169,12 @@ def init_clickhouse() -> list[tuple[str, str]]: "--fail_on_error", is_flag=True, default=False, help="Allow errors to fail the run." ) def performance_metrics( - course_key, dashboard_slug, slice_name, print_sql, fail_on_error + course_name, dashboard_slug, slice_name, print_sql, fail_on_error ) -> (list)[tuple[str, str]]: """ Job to measure performance metrics of charts and its queries in Superset and ClickHouse. """ - options = f"--course_key {course_key}" if course_key else "" + options = f"--course_name '{course_name}'" if course_name else "" options += f" --dashboard_slug {dashboard_slug}" if dashboard_slug else "" options += f' --slice_name "{slice_name}"' if slice_name else "" options += " --print_sql" if print_sql else "" diff --git a/tutoraspects/templates/aspects/apps/superset/pythonpath/performance_metrics.py b/tutoraspects/templates/aspects/apps/superset/pythonpath/performance_metrics.py index a6116f928..a1a88733a 100644 --- a/tutoraspects/templates/aspects/apps/superset/pythonpath/performance_metrics.py +++ b/tutoraspects/templates/aspects/apps/superset/pythonpath/performance_metrics.py @@ -50,9 +50,9 @@ @click.command() @click.option( - "--course_key", + "--course_name", default="", - help="A course_key to apply as a filter, you must include the 'course-v1:'.") + help="A course_name to apply as a filter, you must include the 'course-v1:'.") @click.option( "--dashboard_slug", default="", @@ -71,7 +71,7 @@ @click.option( "--fail_on_error", is_flag=True, default=False, help="Allow errors to fail the run." ) -def performance_metrics(course_key, dashboard_slug, slice_name, print_sql, +def performance_metrics(course_name, dashboard_slug, slice_name, print_sql, fail_on_error): """ Measure the performance of the dashboard. @@ -79,8 +79,8 @@ def performance_metrics(course_key, dashboard_slug, slice_name, print_sql, # Mock the client name to identify the queries in the clickhouse system.query_log # table by by the http_user_agent field. extra_filters = [] - if course_key: - extra_filters += [{"col": "course_key", "op": "==", "val": course_key}] + if course_name: + extra_filters += [{"col": "course_name", "op": "IN", "val": course_name}] with patch("clickhouse_connect.common.build_client_name") as mock_build_client_name: mock_build_client_name.return_value = RUN_ID From a42888262cc121e9fb55eab3d147163a15d042d6 Mon Sep 17 00:00:00 2001 From: Cristhian Garcia Date: Fri, 27 Sep 2024 12:41:53 -0500 Subject: [PATCH 03/10] fix(metrics): set global form data to allow prewhere filters --- .../apps/superset/pythonpath/performance_metrics.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tutoraspects/templates/aspects/apps/superset/pythonpath/performance_metrics.py b/tutoraspects/templates/aspects/apps/superset/pythonpath/performance_metrics.py index a1a88733a..bd983f784 100644 --- a/tutoraspects/templates/aspects/apps/superset/pythonpath/performance_metrics.py +++ b/tutoraspects/templates/aspects/apps/superset/pythonpath/performance_metrics.py @@ -126,7 +126,7 @@ def performance_metrics(course_name, dashboard_slug, slice_name, print_sql, return report logger.info("Waiting for clickhouse log...") - time.sleep(30) + time.sleep(20) get_query_log_from_clickhouse(report, query_contexts, print_sql, fail_on_error) return report @@ -170,6 +170,10 @@ def get_slice_query_context(slice, query_contexts, extra_filters=None): } ) + query_context["form_data"]["extra_form_data"] = { + "filters": extra_filters + } + if extra_filters: for query in query_context["queries"]: query["filters"] += extra_filters @@ -177,16 +181,17 @@ def get_slice_query_context(slice, query_contexts, extra_filters=None): return query_context -def measure_chart(slice, query_context, fail_on_error): +def measure_chart(slice, query_context_dict, fail_on_error): """ Measure the performance of a chart and return the results. """ logger.info(f"Fetching slice data: {slice}") g.user = security_manager.find_user(username="{{SUPERSET_ADMIN_USERNAME}}") - query_context = ChartDataQueryContextSchema().load(query_context) + query_context = ChartDataQueryContextSchema().load(query_context_dict) command = ChartDataCommand(query_context) - + command.validate() + g.form_data = query_context.form_data try: start_time = datetime.now() result = command.run() From 6753a448d55d63cc00bd400fe40f5c0ddcb38117 Mon Sep 17 00:00:00 2001 From: Cristhian Garcia Date: Fri, 27 Sep 2024 12:48:29 -0500 Subject: [PATCH 04/10] chore(metrics): remove unnecessary code --- .../aspects/apps/superset/pythonpath/performance_metrics.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tutoraspects/templates/aspects/apps/superset/pythonpath/performance_metrics.py b/tutoraspects/templates/aspects/apps/superset/pythonpath/performance_metrics.py index bd983f784..7ac99b070 100644 --- a/tutoraspects/templates/aspects/apps/superset/pythonpath/performance_metrics.py +++ b/tutoraspects/templates/aspects/apps/superset/pythonpath/performance_metrics.py @@ -174,10 +174,6 @@ def get_slice_query_context(slice, query_contexts, extra_filters=None): "filters": extra_filters } - if extra_filters: - for query in query_context["queries"]: - query["filters"] += extra_filters - return query_context @@ -273,7 +269,7 @@ def get_query_log_from_clickhouse(report, query_contexts, print_sql, fail_on_err result_rows=chart_result.get("result_rows"), rowcount=query["rowcount"], filters=query["applied_filters"], - sql=chart_result['sql'] if print_sql else '', + sql=chart_result['sql'] if print_sql else '', ) ) logger.info(report_str) From d26555bc6b3d4a32a2e51d402f9e93484c621ec8 Mon Sep 17 00:00:00 2001 From: Cristhian Garcia Date: Fri, 27 Sep 2024 13:05:00 -0500 Subject: [PATCH 05/10] chore(metrics): remove unnecesary logging --- .../aspects/apps/superset/pythonpath/performance_metrics.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tutoraspects/templates/aspects/apps/superset/pythonpath/performance_metrics.py b/tutoraspects/templates/aspects/apps/superset/pythonpath/performance_metrics.py index 7ac99b070..c190dc831 100644 --- a/tutoraspects/templates/aspects/apps/superset/pythonpath/performance_metrics.py +++ b/tutoraspects/templates/aspects/apps/superset/pythonpath/performance_metrics.py @@ -228,10 +228,6 @@ def get_query_log_from_clickhouse(report, query_contexts, print_sql, fail_on_err parsed_sql = str(sqlparse.parse(row.pop("query"))[0]) clickhouse_queries[parsed_sql] = row - if print_sql: - logger.info("ClickHouse SQL: ") - logger.info(parsed_sql) - for k, chart_result in enumerate(report): for query in chart_result["queries"]: From 4cb45adf3f3a623c6e6ff20d3f2f3f800ff6dccd Mon Sep 17 00:00:00 2001 From: Cristhian Garcia Date: Fri, 27 Sep 2024 13:12:48 -0500 Subject: [PATCH 06/10] fix(metrics): restore extra filter per query --- .../aspects/apps/superset/pythonpath/performance_metrics.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tutoraspects/templates/aspects/apps/superset/pythonpath/performance_metrics.py b/tutoraspects/templates/aspects/apps/superset/pythonpath/performance_metrics.py index c190dc831..ba681a3a7 100644 --- a/tutoraspects/templates/aspects/apps/superset/pythonpath/performance_metrics.py +++ b/tutoraspects/templates/aspects/apps/superset/pythonpath/performance_metrics.py @@ -174,6 +174,10 @@ def get_slice_query_context(slice, query_contexts, extra_filters=None): "filters": extra_filters } + if extra_filters: + for query in query_context["queries"]: + query["filters"] += extra_filters + return query_context From f5a7533e7fdaefa7ee5ddad51205a55a0a27320b Mon Sep 17 00:00:00 2001 From: Cristhian Garcia Date: Mon, 30 Sep 2024 10:06:22 -0500 Subject: [PATCH 07/10] feat(metrics): add organization filter option --- tutoraspects/commands_v1.py | 11 +++++++++-- .../apps/superset/pythonpath/performance_metrics.py | 8 +++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/tutoraspects/commands_v1.py b/tutoraspects/commands_v1.py index 2723b6f58..9dbf55c6d 100644 --- a/tutoraspects/commands_v1.py +++ b/tutoraspects/commands_v1.py @@ -148,6 +148,11 @@ def init_clickhouse() -> list[tuple[str, str]]: # Ex: "tutor local do performance-metrics " @click.command(context_settings={"ignore_unknown_options": True}) +@click.option( + "--org", + default="", + help="An organization to apply as a filter.", +) @click.option( "--course_name", default="", @@ -169,12 +174,14 @@ def init_clickhouse() -> list[tuple[str, str]]: "--fail_on_error", is_flag=True, default=False, help="Allow errors to fail the run." ) def performance_metrics( - course_name, dashboard_slug, slice_name, print_sql, fail_on_error + org, course_name, dashboard_slug, slice_name, print_sql, fail_on_error ) -> (list)[tuple[str, str]]: """ Job to measure performance metrics of charts and its queries in Superset and ClickHouse. """ - options = f"--course_name '{course_name}'" if course_name else "" + options = "" + options += f"--org '{org}' " if org else "" + options += f"--course_name '{course_name}' " if course_name else "" options += f" --dashboard_slug {dashboard_slug}" if dashboard_slug else "" options += f' --slice_name "{slice_name}"' if slice_name else "" options += " --print_sql" if print_sql else "" diff --git a/tutoraspects/templates/aspects/apps/superset/pythonpath/performance_metrics.py b/tutoraspects/templates/aspects/apps/superset/pythonpath/performance_metrics.py index ba681a3a7..3d709fce7 100644 --- a/tutoraspects/templates/aspects/apps/superset/pythonpath/performance_metrics.py +++ b/tutoraspects/templates/aspects/apps/superset/pythonpath/performance_metrics.py @@ -49,6 +49,10 @@ ) @click.command() +@click.option( + "--org", + default="", + help="An organization to apply as a filter.") @click.option( "--course_name", default="", @@ -71,7 +75,7 @@ @click.option( "--fail_on_error", is_flag=True, default=False, help="Allow errors to fail the run." ) -def performance_metrics(course_name, dashboard_slug, slice_name, print_sql, +def performance_metrics(org, course_name, dashboard_slug, slice_name, print_sql, fail_on_error): """ Measure the performance of the dashboard. @@ -81,6 +85,8 @@ def performance_metrics(course_name, dashboard_slug, slice_name, print_sql, extra_filters = [] if course_name: extra_filters += [{"col": "course_name", "op": "IN", "val": course_name}] + if org: + extra_filters += [{"col": "org", "op": "IN", "val": org}] with patch("clickhouse_connect.common.build_client_name") as mock_build_client_name: mock_build_client_name.return_value = RUN_ID From 85f881f37cc61862f6d90b516c17fbc3fe008bca Mon Sep 17 00:00:00 2001 From: Cristhian Garcia Date: Mon, 30 Sep 2024 10:11:08 -0500 Subject: [PATCH 08/10] chore: run black and isort on performance metrics --- .../pythonpath/performance_metrics.py | 89 ++++++++----------- 1 file changed, 38 insertions(+), 51 deletions(-) diff --git a/tutoraspects/templates/aspects/apps/superset/pythonpath/performance_metrics.py b/tutoraspects/templates/aspects/apps/superset/pythonpath/performance_metrics.py index 3d709fce7..e21577d85 100644 --- a/tutoraspects/templates/aspects/apps/superset/pythonpath/performance_metrics.py +++ b/tutoraspects/templates/aspects/apps/superset/pythonpath/performance_metrics.py @@ -7,9 +7,6 @@ across Superset installations. """ -from create_assets import BASE_DIR, ASSET_FOLDER_MAPPING, app - -import json import logging import os import time @@ -20,10 +17,12 @@ import click import sqlparse import yaml +from create_assets import app + from flask import g from superset import security_manager -from superset.commands.chart.data.get_data_command import ChartDataCommand from superset.charts.schemas import ChartDataQueryContextSchema +from superset.commands.chart.data.get_data_command import ChartDataCommand from superset.extensions import db from superset.models.dashboard import Dashboard from superset.models.slice import Slice @@ -45,38 +44,34 @@ "Filters: {filters}\n" "SQL:\n" "{sql}\n\n\n" - ) + @click.command() -@click.option( - "--org", - default="", - help="An organization to apply as a filter.") +@click.option("--org", default="", help="An organization to apply as a filter.") @click.option( "--course_name", default="", - help="A course_name to apply as a filter, you must include the 'course-v1:'.") + help="A course_name to apply as a filter, you must include the 'course-v1:'.", +) @click.option( - "--dashboard_slug", - default="", - help="Only run charts for the given dashboard.") + "--dashboard_slug", default="", help="Only run charts for the given dashboard." +) @click.option( "--slice_name", default="", help="Only run charts for the given slice name, if the name appears in more than " - "one dashboard it will be run for each.") + "one dashboard it will be run for each.", +) @click.option( - "--print_sql", - is_flag=True, - default=False, - help="Whether to print the SQL run." + "--print_sql", is_flag=True, default=False, help="Whether to print the SQL run." ) @click.option( "--fail_on_error", is_flag=True, default=False, help="Allow errors to fail the run." ) -def performance_metrics(org, course_name, dashboard_slug, slice_name, print_sql, - fail_on_error): +def performance_metrics( + org, course_name, dashboard_slug, slice_name, print_sql, fail_on_error +): """ Measure the performance of the dashboard. """ @@ -90,7 +85,9 @@ def performance_metrics(org, course_name, dashboard_slug, slice_name, print_sql, with patch("clickhouse_connect.common.build_client_name") as mock_build_client_name: mock_build_client_name.return_value = RUN_ID - target_dashboards = [dashboard_slug] if dashboard_slug else {{SUPERSET_EMBEDDABLE_DASHBOARDS}} + target_dashboards = ( + [dashboard_slug] if dashboard_slug else {{SUPERSET_EMBEDDABLE_DASHBOARDS}} + ) dashboards = ( db.session.query(Dashboard) @@ -107,14 +104,13 @@ def performance_metrics(org, course_name, dashboard_slug, slice_name, print_sql, logger.info(f"Dashboard: {dashboard.slug}") for slice in dashboard.slices: if slice_name and not slice_name == slice.slice_name: - logger.info(f"{slice.slice_name} doesn't match {slice_name}, " - f"skipping.") + logger.info( + f"{slice.slice_name} doesn't match {slice_name}, " f"skipping." + ) continue query_context = get_slice_query_context( - slice, - query_contexts, - extra_filters + slice, query_contexts, extra_filters ) result = measure_chart(slice, query_context, fail_on_error) if not result: @@ -176,9 +172,7 @@ def get_slice_query_context(slice, query_contexts, extra_filters=None): } ) - query_context["form_data"]["extra_form_data"] = { - "filters": extra_filters - } + query_context["form_data"]["extra_form_data"] = {"filters": extra_filters} if extra_filters: for query in query_context["queries"]: @@ -238,45 +232,38 @@ def get_query_log_from_clickhouse(report, query_contexts, print_sql, fail_on_err parsed_sql = str(sqlparse.parse(row.pop("query"))[0]) clickhouse_queries[parsed_sql] = row - for k, chart_result in enumerate(report): for query in chart_result["queries"]: parsed_sql = ( str(sqlparse.parse(query["query"])[0]).replace(";", "") + "\n FORMAT Native" ) - chart_result['sql'] = parsed_sql + chart_result["sql"] = parsed_sql clickhouse_report = clickhouse_queries.get(parsed_sql, {}) + chart_result.update(clickhouse_report) chart_result.update( - clickhouse_report + {"query_duration_ms": chart_result.get("query_duration_ms", 0)} ) - chart_result.update({ - "query_duration_ms": chart_result.get("query_duration_ms", 0) - }) # Sort report by slowest queries report = sorted(report, key=lambda x: x["query_duration_ms"], reverse=True) report_str = f"\nSuperset Reports: {RUN_ID}\n\n" for k, chart_result in enumerate(report): - report_str += ( - report_format.format( - i=(k + 1), - dashboard=chart_result["dashboard"], - slice=chart_result["slice"], - superset_time=chart_result["time_elapsed"] - ) + report_str += report_format.format( + i=(k + 1), + dashboard=chart_result["dashboard"], + slice=chart_result["slice"], + superset_time=chart_result["time_elapsed"], ) for query in chart_result["queries"]: - report_str += ( - query_format.format( - query_duration_ms=chart_result.get('query_duration_ms') / 1000, - memory_usage_mb=chart_result.get("memory_usage_mb"), - result_rows=chart_result.get("result_rows"), - rowcount=query["rowcount"], - filters=query["applied_filters"], - sql=chart_result['sql'] if print_sql else '', - ) + report_str += query_format.format( + query_duration_ms=chart_result.get("query_duration_ms") / 1000, + memory_usage_mb=chart_result.get("memory_usage_mb"), + result_rows=chart_result.get("result_rows"), + rowcount=query["rowcount"], + filters=query["applied_filters"], + sql=chart_result["sql"] if print_sql else "", ) logger.info(report_str) From 858f3c269f1783b0691635b3cfdfbfba80e1348c Mon Sep 17 00:00:00 2001 From: Cristhian Garcia Date: Mon, 30 Sep 2024 10:26:58 -0500 Subject: [PATCH 09/10] chore: quality fixes --- tutoraspects/commands_v1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutoraspects/commands_v1.py b/tutoraspects/commands_v1.py index 9dbf55c6d..9d5e32cfc 100644 --- a/tutoraspects/commands_v1.py +++ b/tutoraspects/commands_v1.py @@ -173,7 +173,7 @@ def init_clickhouse() -> list[tuple[str, str]]: @click.option( "--fail_on_error", is_flag=True, default=False, help="Allow errors to fail the run." ) -def performance_metrics( +def performance_metrics( # pylint: disable=too-many-arguments org, course_name, dashboard_slug, slice_name, print_sql, fail_on_error ) -> (list)[tuple[str, str]]: """ From 20b4f531e21d1cddd9517ecaabee1ed6aa282866 Mon Sep 17 00:00:00 2001 From: Cristhian Garcia Date: Mon, 30 Sep 2024 11:32:20 -0500 Subject: [PATCH 10/10] chore: quality fixes --- tutoraspects/commands_v1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutoraspects/commands_v1.py b/tutoraspects/commands_v1.py index 9d5e32cfc..03623b9ca 100644 --- a/tutoraspects/commands_v1.py +++ b/tutoraspects/commands_v1.py @@ -173,7 +173,7 @@ def init_clickhouse() -> list[tuple[str, str]]: @click.option( "--fail_on_error", is_flag=True, default=False, help="Allow errors to fail the run." ) -def performance_metrics( # pylint: disable=too-many-arguments +def performance_metrics( # pylint: disable=too-many-arguments,too-many-positional-arguments org, course_name, dashboard_slug, slice_name, print_sql, fail_on_error ) -> (list)[tuple[str, str]]: """