mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2026-10-12 15:02:32 +00:00
* ci: update Agent and Traces benchmark runtime Signed-off-by: WenyXu <wenymedia@gmail.com> * ci: use unrestricted benchmark runtimes by default Signed-off-by: WenyXu <wenymedia@gmail.com> * ci: retry benchmark image pulls before generating datasets Signed-off-by: WenyXu <wenymedia@gmail.com> * ci: increase tracesbench defaults to 8 CPU and 32 GiB Signed-off-by: WenyXu <wenymedia@gmail.com> * ci: configure tracesbench query timeout and update runtime Signed-off-by: WenyXu <wenymedia@gmail.com> * ci: update tracesbench runtime and configure import scan workers Signed-off-by: WenyXu <wenymedia@gmail.com> * ci: use shared parallel tracesbench import preparation Signed-off-by: WenyXu <wenymedia@gmail.com> * ci: update tracesbench runtime to preserve Tempo trace requests Signed-off-by: WenyXu <wenymedia@gmail.com> * ci: summarize tracesbench query results and lifecycle Signed-off-by: WenyXu <wenymedia@gmail.com> * ci: show benchmark resource usage and update parallel loader runtime Signed-off-by: WenyXu <wenymedia@gmail.com> * fix(ci): resolve shared summary module in existing test harness Signed-off-by: WenyXu <wenymedia@gmail.com> * ci: select tracesbench engines and GreptimeDB image version Signed-off-by: WenyXu <wenymedia@gmail.com> * ci: update benchmark runtime with parallel Agent preparation Signed-off-by: WenyXu <wenymedia@gmail.com> * ci: select benchmark runtime registry by cloud provider Signed-off-by: WenyXu <wenymedia@gmail.com> * ci: retry long-range benchmark image pulls Signed-off-by: WenyXu <wenymedia@gmail.com> * ci: compare baseline and candidate GreptimeDB benchmarks Signed-off-by: WenyXu <wenymedia@gmail.com> * ci: pin published dual-version benchmark runtime Signed-off-by: WenyXu <wenymedia@gmail.com> * ci: prepare agent benchmark indexes with updated runtime Signed-off-by: WenyXu <wenymedia@gmail.com> * ci: validate agent admission and render traces version comparison Signed-off-by: WenyXu <wenymedia@gmail.com> * ci: use runtime with traces index preparation Signed-off-by: WenyXu <wenymedia@gmail.com> * ci: use consolidated runtime for benchmark version reports Signed-off-by: WenyXu <wenymedia@gmail.com> * test: add license header to benchmark comparison tests Signed-off-by: WenyXu <wenymedia@gmail.com> * fix(ci): read legacy Tempo ingestion metrics in traces summaries Signed-off-by: WenyXu <wenymedia@gmail.com> * ci: require tracesbench query plan artifacts Signed-off-by: WenyXu <wenymedia@gmail.com> * ci: use optimized tracesbench report runtime Signed-off-by: WenyXu <wenymedia@gmail.com> * ci: update tracesbench runtime to merged report validation Signed-off-by: WenyXu <wenymedia@gmail.com> --------- Signed-off-by: WenyXu <wenymedia@gmail.com>
100 lines
4.5 KiB
Python
100 lines
4.5 KiB
Python
# Copyright 2023 Greptime Team
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
"""Render shared O11yBench sampled resource artifacts for lifecycle summaries."""
|
|
import json
|
|
import math
|
|
|
|
|
|
def number(value):
|
|
return isinstance(value, (int, float)) and not isinstance(value, bool) and math.isfinite(value) and value >= 0
|
|
|
|
|
|
def metric(values, key, statistic):
|
|
entry = values.get(key, {})
|
|
value = entry.get(statistic)
|
|
return value if number(entry.get('samples')) and entry['samples'] > 0 and number(value) else None
|
|
|
|
|
|
def decimal(value, suffix=''):
|
|
return f'{value:.2f}{suffix}' if number(value) else '—'
|
|
|
|
|
|
def memory(value):
|
|
if not number(value):
|
|
return '—'
|
|
for unit in ('B', 'KiB', 'MiB', 'GiB', 'TiB'):
|
|
if value < 1024 or unit == 'TiB':
|
|
return decimal(value, f' {unit}')
|
|
value /= 1024
|
|
|
|
|
|
def capacities(path):
|
|
# Quotas are metadata, not recomputed CPU statistics. Bound reads on long runs.
|
|
result = {}
|
|
try:
|
|
with path.open() as source:
|
|
for index, line in enumerate(source):
|
|
if index == 256:
|
|
break
|
|
record = json.loads(line)
|
|
cores = record.get('host', {}).get('cpu_count')
|
|
if number(cores) and cores > 0:
|
|
result.setdefault('host', cores)
|
|
for role, values in record.get('containers', {}).items():
|
|
quota = values.get('configured_cpu_limit_cores')
|
|
if number(quota):
|
|
result.setdefault(role, quota)
|
|
if all(role in result for role in ('host', 'runtime', 'db')):
|
|
break
|
|
except (OSError, ValueError, TypeError, AttributeError):
|
|
pass
|
|
return result
|
|
|
|
|
|
def render_resources(scopes, table):
|
|
rows = []
|
|
for scope, directory in scopes:
|
|
try:
|
|
summary = json.loads((directory / 'resources.summary.json').read_text())
|
|
phases = summary.get('phases', {}) if summary.get('schema') == 'o11ybench-resources-v1' else {}
|
|
except (OSError, ValueError, TypeError, AttributeError):
|
|
phases = {}
|
|
if not isinstance(phases, dict) or not phases:
|
|
rows.append([scope, 'unavailable', *['—'] * 9])
|
|
continue
|
|
limits = capacities(directory / 'resources.jsonl')
|
|
for phase, data in phases.items():
|
|
for role in ('host', 'runtime', 'db'):
|
|
values = data.get('host', {}) if role == 'host' else data.get('containers', {}).get(role, {})
|
|
if role == 'db' and scope == 'dataset':
|
|
continue
|
|
quota = limits.get(role)
|
|
cpu_samples = values.get('cpu_used_cores', {}).get('samples')
|
|
rows.append([scope, phase, role, cpu_samples,
|
|
decimal(metric(values, 'cpu_used_cores', 'mean')),
|
|
decimal(metric(values, 'cpu_used_cores', 'max')),
|
|
'unlimited' if quota == 0 else decimal(quota),
|
|
memory(metric(values, 'memory_used_bytes' if role == 'host' else 'memory_usage_bytes', 'max')),
|
|
decimal(metric(values, 'io_wait_percent', 'mean'), '%'),
|
|
decimal(metric(values, 'io_wait_percent', 'max'), '%'),
|
|
memory(metric(values, 'swap_used_bytes', 'max'))])
|
|
return '\n'.join(['## Sampled resources', '',
|
|
'CPU is measured in cores. Mean/max summarize available samples, not continuous peaks; '
|
|
'short phases may have no CPU sample. CPU capacity is host cores or the configured container quota. '
|
|
'Host memory is total minus available; Docker memory excludes cache where supported. '
|
|
'Swap is host-wide usage, not swap activity. Missing evidence stays —.', '',
|
|
table(['Scope', 'Phase', 'Component', 'CPU samples', 'Mean CPU cores', 'Max CPU cores',
|
|
'CPU capacity', 'Max memory', 'Mean I/O wait', 'Max I/O wait', 'Max swap'], rows), ''])
|