Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 1 addition & 36 deletions .github/workflows/slo-report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,42 +16,7 @@ jobs:
pull-requests: write
steps:
- name: Publish YDB SLO Report
uses: ydb-platform/ydb-slo-action/report@13c687b7d4b2879da79dd12932dee0ed2b65dd1c
uses: ydb-platform/ydb-slo-action/report@v2
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
github_run_id: ${{ github.event.workflow_run.id }}

remove-slo-label:
if: always() && github.event.workflow_run.event == 'pull_request'
name: Remove SLO Label
needs: ydb-slo-action-report
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Remove SLO label from PR
uses: actions/github-script@v7
with:
script: |
const pullRequests = context.payload.workflow_run.pull_requests;
if (pullRequests && pullRequests.length > 0) {
for (const pr of pullRequests) {
try {
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
name: 'SLO'
});
console.log(`Removed SLO label from PR #${pr.number}`);
} catch (error) {
if (error.status === 404) {
console.log(`SLO label not found on PR #${pr.number}, skipping`);
} else {
throw error;
}
}
}
} else {
console.log('No pull requests associated with this workflow run');
}
155 changes: 30 additions & 125 deletions .github/workflows/slo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,14 @@ jobs:
strategy:
fail-fast: false
matrix:
include:
- id: sync-table
prefix: table
workload: sync-table
- id: sync-query
prefix: table
workload: sync-query
sdk:
- name: sync-table
command: "--read-rps ${{ inputs.slo_workload_read_max_rps || '1000' }} --write-rps ${{ inputs.slo_workload_write_max_rps || '100' }}"
- name: sync-query
command: "--read-rps ${{ inputs.slo_workload_read_max_rps || '1000' }} --write-rps ${{ inputs.slo_workload_write_max_rps || '100' }}"

concurrency:
group: slo-${{ github.ref }}-${{ matrix.workload }}
group: slo-${{ github.ref }}-${{ matrix.sdk.name }}
cancel-in-progress: true

steps:
Expand Down Expand Up @@ -141,125 +139,32 @@ jobs:
-t "ydb-app-baseline" \
"$GITHUB_WORKSPACE/baseline"

- name: Initialize YDB SLO
id: ydb_slo
uses: ydb-platform/ydb-slo-action/init@13c687b7d4b2879da79dd12932dee0ed2b65dd1c
- name: Run SLO Tests
uses: ydb-platform/ydb-slo-action/init@v2
timeout-minutes: 30
with:
github_issue: ${{ github.event.pull_request.number || inputs.github_issue }}
github_issue: ${{ github.event.inputs.github_issue }}
github_token: ${{ secrets.GITHUB_TOKEN }}
workload_name: ydb-python-${{ matrix.workload }}
workload_name: ${{ matrix.sdk.name }}
workload_duration: ${{ inputs.slo_workload_duration_seconds || '600' }}
workload_current_ref: ${{ github.head_ref || github.ref_name }}
workload_current_image: ydb-app-current
workload_current_command: ${{ matrix.sdk.command }}
workload_baseline_ref: ${{ steps.baseline.outputs.ref }}

- name: Prepare SLO Database
run: |
docker run --rm \
--network ydb_ydb-net \
--add-host "ydb:172.28.0.11" \
--add-host "ydb:172.28.0.12" \
--add-host "ydb:172.28.0.13" \
--add-host "ydb:172.28.0.99" \
-e "WORKLOAD=${{ matrix.workload }}" \
-e "REF=${{ github.head_ref || github.ref_name }}" \
ydb-app-current \
${{ matrix.prefix }}-create grpc://ydb:2136 /Root/testdb

- name: Run SLO Tests (current + baseline in parallel)
timeout-minutes: 15
env:
WORKLOAD: ${{ matrix.workload }}
DURATION: ${{ inputs.slo_workload_duration_seconds || 600 }}
READ_RPS: ${{ inputs.slo_workload_read_max_rps || 1000 }}
WRITE_RPS: ${{ inputs.slo_workload_write_max_rps || 100 }}
CURRENT_REF: ${{ github.head_ref || github.ref_name }}
BASELINE_REF: ${{ steps.baseline.outputs.ref }}
run: |
ARGS="${{ matrix.prefix }}-run grpc://ydb:2136 /Root/testdb \
--otlp-endpoint http://prometheus:9090/api/v1/otlp/v1/metrics \
--report-period 250 \
--time ${DURATION} \
--read-rps ${READ_RPS} \
--write-rps ${WRITE_RPS} \
--read-timeout 1000 \
--write-timeout 1000"

echo "Starting current workload (ref=${CURRENT_REF}, workload=${WORKLOAD})..."
docker run -d \
--name ydb-app-current \
--network ydb_ydb-net \
--add-host "ydb:172.28.0.11" \
--add-host "ydb:172.28.0.12" \
--add-host "ydb:172.28.0.13" \
--add-host "ydb:172.28.0.99" \
-e "REF=${CURRENT_REF}" \
-e "WORKLOAD=${WORKLOAD}" \
ydb-app-current \
$ARGS

echo "Starting baseline workload (ref=${BASELINE_REF}, workload=${WORKLOAD})..."
docker run -d \
--name ydb-app-baseline \
--network ydb_ydb-net \
--add-host "ydb:172.28.0.11" \
--add-host "ydb:172.28.0.12" \
--add-host "ydb:172.28.0.13" \
--add-host "ydb:172.28.0.99" \
-e "REF=${BASELINE_REF}" \
-e "WORKLOAD=${WORKLOAD}" \
ydb-app-baseline \
$ARGS

echo ""
echo "==================== INITIAL CURRENT LOGS ===================="
docker logs -n 15 ydb-app-current 2>&1 || echo "No current container"
echo ""
echo "==================== INITIAL BASELINE LOGS ===================="
docker logs -n 15 ydb-app-baseline 2>&1 || echo "No baseline container"
echo ""

echo "Waiting for workloads to complete (${DURATION}s)..."
sleep ${DURATION}

echo "Stopping containers after ${DURATION}s..."
docker stop --timeout=30 ydb-app-current ydb-app-baseline 2>&1 || true

# Force kill if still running
docker kill ydb-app-current ydb-app-baseline 2>&1 || true

# Check exit codes
CURRENT_EXIT=$(docker inspect ydb-app-current --format='{{.State.ExitCode}}' 2>/dev/null || echo "1")
BASELINE_EXIT=$(docker inspect ydb-app-baseline --format='{{.State.ExitCode}}' 2>/dev/null || echo "1")

echo "Current exit code: ${CURRENT_EXIT}"
echo "Baseline exit code: ${BASELINE_EXIT}"

echo ""
echo "==================== FINAL CURRENT LOGS ===================="
docker logs -n 15 ydb-app-current 2>&1 || echo "No current container"
echo ""
echo "==================== FINAL BASELINE LOGS ===================="
docker logs -n 15 ydb-app-baseline 2>&1 || echo "No baseline container"
echo ""

if [[ "${CURRENT_EXIT}" != "0" || "${BASELINE_EXIT}" != "0" ]]; then
echo "One or both workloads failed."
exit 0
fi

echo "SUCCESS: Workloads completed successfully"

- if: always()
name: Store logs
run: |
docker logs ydb-app-current > current.log 2>&1 || echo "No current container" > current.log
docker logs ydb-app-baseline > baseline.log 2>&1 || echo "No baseline container" > baseline.log

- if: always()
name: Upload logs
uses: actions/upload-artifact@v4
workload_baseline_image: ydb-app-current
workload_baseline_command: ${{ matrix.sdk.command }}

ydb-slo-action-report:
runs-on: ubuntu-latest
name: Publish YDB SLO Report
needs: ydb-slo-action
permissions:
checks: write
contents: read
pull-requests: write
steps:
- name: Publish YDB SLO Report
uses: ydb-platform/ydb-slo-action/report@v2
with:
name: ydb-python-${{ matrix.workload }}-logs
path: |
./current.log
./baseline.log
retention-days: 1
github_token: ${{ secrets.GITHUB_TOKEN }}
github_run_id: ${{ github.run_id }}
6 changes: 6 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
[tool.ty.environment]
extra-paths = ["tests/slo/src"]

[tool.ruff]
line-length = 120

[tool.black]
line-length = 120

Expand Down
18 changes: 14 additions & 4 deletions tests/slo/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
# syntax=docker/dockerfile:1

# This image packages the Python SLO workload runner.
# It expects to be run with arguments like:
# docker run --rm <image> table-run <endpoint> <db> --otlp-endpoint http://prometheus:9090/api/v1/otlp/v1/metrics ...
#
# Connection and workload identity are configured via environment variables:
# YDB_ENDPOINT grpc://ydb:2136
# YDB_DATABASE /Root/testdb
# WORKLOAD_DURATION 600
# WORKLOAD_NAME sync-query | sync-table | topic
# WORKLOAD_REF <branch/ref label for metrics>
# OTEL_EXPORTER_OTLP_METRICS_ENDPOINT http://ydb-prometheus:9090/api/v1/otlp/v1/metrics
#
# Additional tuning flags (read/write RPS, timeouts, thread counts) are passed via
# the Docker CMD, e.g.:
# docker run --rm --env-file ... <image> --read-rps 1000 --write-rps 100
#
# Notes:
# - OpenTelemetry 1.39.x requires Python >= 3.9.
# - The entrypoint is `python ./tests/slo/src`, i.e. it runs the `__main__.py`
# from that directory (same as `python tests/slo/src ...` in CI).

FROM python:3.11-slim AS build

Expand Down Expand Up @@ -36,3 +44,5 @@ COPY --from=build /opt/venv /opt/venv
COPY --from=build /src/tests/slo/src /app/tests/slo/src

ENTRYPOINT ["python", "./tests/slo/src"]

CMD ["--read-rps", "1000", "--write-rps", "100"]
2 changes: 1 addition & 1 deletion tests/slo/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
requests==2.33.0
aiolimiter==1.1.0
quantile-estimator==0.1.2
hdrhistogram==0.10.3

# OpenTelemetry (OTLP/HTTP exporter)
# NOTE: OpenTelemetry 1.39.1 requires Python >= 3.9.
Expand Down
5 changes: 2 additions & 3 deletions tests/slo/src/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
import logging

from options import parse_options
from root_runner import run_from_args

from root_runner import run_all

if __name__ == "__main__":
args = parse_options()
Expand All @@ -12,4 +11,4 @@
log_level = logging.DEBUG if args.debug else logging.INFO
logging.basicConfig(level=log_level, format="%(asctime)s %(levelname)-8s %(message)s")

run_from_args(args)
run_all(args)
Loading
Loading