# # Copyright 2024-present ScyllaDB # # SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0 # import pytest from datetime import timedelta from uuid import uuid1 from test.nodetool.rest_api_mock import expected_request def create_task(i, num_pending_tasks): return {"ks": "ks", "cf": f"cf{i}", "task": num_pending_tasks} def create_compaction_stats(ks, cf): return {"id": str(uuid1()), "ks": ks, "cf": cf, "unit": "keys", "completed": 42, "total": 10240, "task_type": "COMPACTION"} def format_task(task): ks = task["ks"] cf = task["cf"] num = task["task"] return f"- {ks}.{cf}: {num}\n" def format_compaction(compaction, progress=None): task_id = compaction['id'] task_type = compaction['task_type'] keyspace = compaction['ks'] table = compaction['cf'] completed = compaction['completed'] total = compaction['total'] unit = compaction['unit'] if progress is None: percent = completed * 100 / total progress = f"{percent:.2f}%" return f'{task_id:<36} {task_type:<15} {keyspace:<8} {table:<5} {completed:<9} {total:<5} {unit:<4} {progress:<8}\n' @pytest.mark.parametrize("num_compactions", [0, 1, 2]) @pytest.mark.parametrize("throughput", [0, 1024]) def test_compactionstats(nodetool, request, num_compactions, throughput): pending_tasks = [create_task(i, 1) for i in range(num_compactions)] compactions = [create_compaction_stats(task["ks"], task["cf"]) for task in pending_tasks] expected_requests = [ expected_request("GET", "/compaction_manager/metrics/pending_tasks_by_table", response=pending_tasks), expected_request("GET", "/compaction_manager/compactions", response=compactions), ] if request.config.getoption("nodetool") == "cassandra" or len(compactions) > 0: # scylla nodetool does not bother reading throughput if there is no # pending compaction. but cassandra's nodetool always does. expected_requests.append( expected_request("GET", "/storage_service/compaction_throughput", response=throughput)) res = nodetool("compactionstats", expected_requests=expected_requests) actual_output = res.stdout expected_output = f"pending tasks: {num_compactions}\n" for task in pending_tasks: expected_output += format_task(task) expected_output += "\n" if num_compactions > 0: header = {"id": "id", "task_type": "compaction type", "ks": "keyspace", "cf": "table", "completed": "completed", "total": "total", "unit": "unit"} expected_output += format_compaction(header, progress="progress") remaining_bytes = 0 for compaction in compactions: expected_output += format_compaction(compaction) remaining_bytes += compaction['total'] - compaction['completed'] remaining_time = 'n/a' if throughput > 0: remaining_secs = remaining_bytes // (throughput * 1024 * 1024) hours, remainder = divmod(remaining_secs, timedelta(hours=1).seconds) minutes, seconds = divmod(remainder, timedelta(minutes=1).seconds) remaining_time = f'{hours}h{minutes:0>2}m{seconds:0>2}s' expected_output += f'Active compaction remaining time : {remaining_time:>10}\n' assert actual_output == expected_output