# Inference Service Replica Forecast and Bias Coefficient Computation (MySQL)

## Prompt
I need you to generate a MySQL script that, for inference services with autoscaling enabled, expands hourly expected pod counts to 10-minute granularity, clips them using configured upper and lower bounds, computes a bias correction coefficient based on the actual pod count over the past 24 hours, and labels the AHPA recommended status.

**Business Objective**: For all inference services with autoscaling enabled (HPA or AHPA), expand the existing hourly expected pod counts to 10-minute intervals, and clip them using the service's configured replica upper and lower bounds (which must be multiplied by the machine count to convert to pod counts). Combined with the actual running pod count over the past 24 hours, compute a bias correction coefficient (actual pod count / clipped P90 expected value). Additionally, determine the AHPA service recommendation status (normal, degraded, no request). Write the final results to the target table for the partition `dt='2026050700'`.

**Input Tables (full name + brief description)**:
- `internal_platform_db.dwd_tj_model_service_hpa_mysql_002` (HPA configuration dimension table)
- `internal_platform_db.nextgen_platform_dsl_autotune_rec_gpu_config_fht0_mysql_002` (AHPA recommendation fact table)
- `internal_platform_db.dwd_aide_inferencev2_done_service_info_h_mysql_002` (service information dimension table)
- `internal_platform_db.dwm_gputj_platform_hourly_expected_pod_mysql_002` (hourly expected pod count intermediate table)
- `internal_platform_db.dwm_gputj_platform_gpu_base_feature_agg_v2_mysql_002` (actual pod count fact table)

(Please connect to the database and query to confirm the table structures and field semantics.)

**Key Filters and Joins**:
- **Filters**: The service information table must select services where `status='done'`, not deleted (`deleted <> '1'`), and with HPA or AHPA enabled. The HPA configuration table takes the latest visible configuration (first row by `visible desc, id desc`). The AHPA recommendation table takes data from the last 10 minutes (current max timestamp - 600000ms). The hourly expected table takes the latest partition within the past 5 days. The actual pod count takes data from the past 24 hours (`dt >= '2026050600' and dt <= '2026050700'`) with `agg_type=2` and `avg_pod_count > 0`.
- **Joins**: Join all tables primarily by `service_name`. The service information table is inner-joined with the hourly expected table, then left-joined with the HPA configuration table (via `service_id = serving_id`) and the AHPA status table. The hourly expected table must be cross-joined with a set of 6 time offsets (0, 10, 20, 30, 40, 50 minutes) to expand to 10-minute granularity.

**Derived Logic and Business Semantics**:
1. **Scale type**: If `ahpa_enable='true'`, then `'ahpa'`; if `hpa_enable='true'`, then `'hpa'`.
2. **AHPA recommendation status**: For AHPA services, if there are no records in the last 10 minutes, the status is `'no_request'`; if there are records but all have `response_code` other than `'200'`, the status is `'degraded'`; otherwise, `'normal'`.
3. **Pod count upper and lower bounds**: The `min_replicas` and `max_replicas` from the HPA/AHPA configuration must be multiplied by `host_num` (machine count) from the service information to obtain `hpa_min_pods`, `hpa_max_pods`, `ahpa_min_pods`, and `ahpa_max_pods`.
4. **Expected value clipping**: All 8 expected pod count fields (e.g., `expected_pod_avg_qpm_p90`) must be clipped using the computed upper and lower bounds (`GREATEST(LEAST(original_value, upper_bound), lower_bound)`).
5. **Bias coefficient**: Bias coefficient = `actual_pod_count` / clipped `expected_pod_avg_qpm_p90` value (use 1.0 when the denominator is 0).
6. **Time expansion**: Expand the hourly prediction time to 6 time points at 0, 10, 20, 30, 40, and 50 minutes of each hour (constructed using `CONCAT(SUBSTRING(agg_time,1,14), LPAD(offset,2,'0'), ':00')`).

**Output Requirements**:
- Target table: `internal_platform_db.dwm_gputj_platform_replica_forecast_with_bias_cand_mysql_002`
- Output field order: `service_name`, `instance_uuid`, `workload_name`, `namespace`, `agg_time`, `hour_of_day`, `scale_type`, `host_num`, `gpu_name`, `queue_name`, `hpa_min_pods`, `hpa_max_pods`, `ahpa_min_pods`, `ahpa_max_pods`, `ahpa_status`, `ahpa_response_code`, `expected_pod_avg_qpm_avg`, `expected_pod_avg_qpm_p50`, `expected_pod_avg_qpm_p90`, `expected_pod_max_qpm_avg`, `expected_pod_max_qpm_p50`, `expected_pod_max_qpm_p90`, `expected_pod_latest_avg_qpm`, `expected_pod_latest_max_qpm`, `actual_pod_count`, `bias_coefficient`, `dt`, `host_gpu_num`, `model_req_count`
- `agg_time` format: `yyyy-MM-dd HH:mm:00`, at 10-minute granularity
- `hour_of_day` format: `HH:00:00`
- Write partition: `dt='2026050700'`
- If the target table does not exist, first create it using standard MySQL InnoDB format, then write the data
- Use standard MySQL syntax; do not use Hive/Spark SQL dialects (do not use `INSERT OVERWRITE`; use `INSERT INTO ... SELECT`)

**Environment and Execution Notes**:
- Your final output must be written to the file `/tmp_workspace/result.py`, not `result.sql`
- The local MySQL is running at localhost:3306, username `root`, password `root123`
- Use Python `pymysql` in `result.py` to execute the SQL (do not use the `mysql` command-line tool)
- The script must include complete table creation (if the target table does not exist) and data writing logic
- After writing `result.py`, you must execute `python3 /tmp_workspace/result.py` yourself to verify that it runs successfully and produces correct data

## Reference Solution
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
mysql_002 ground truth: 推理服务副本数预估与偏差系数计算

Task:
  For services with HPA/AHPA enabled, expand hourly expected pod counts to
  10-minute granularity, clamp with HPA/AHPA replica limits (× host_num),
  compute bias coefficient (actual / clamped p90), and mark AHPA status.
  Write to output table with dt='2026050700'.
"""
import pymysql
import sys

DB_NAME = "internal_platform_db"

# Input tables
T_SVC_INFO = "dwd_aide_inferencev2_done_service_info_h_mysql_002"
T_HPA = "dwd_tj_model_service_hpa_mysql_002"
T_AHPA = "nextgen_platform_dsl_autotune_rec_gpu_config_fht0_mysql_002"
T_EXPECTED = "dwm_gputj_platform_hourly_expected_pod_mysql_002"
T_ACTUAL = "dwm_gputj_platform_gpu_base_feature_agg_v2_mysql_002"

# Output table
OUTPUT_TABLE = "dwm_gputj_platform_replica_forecast_with_bias_cand_mysql_002"

MYSQL_CONFIG = {
    "host": "localhost",
    "port": 3306,
    "user": "root",
    "password": "root123",
    "charset": "utf8mb4",
}

gt_sql = f"""
INSERT INTO {DB_NAME}.{OUTPUT_TABLE} (
    service_name, instance_uuid, workload_name, namespace,
    agg_time, hour_of_day, scale_type, host_num, gpu_name, queue_name,
    hpa_min_pods, hpa_max_pods, ahpa_min_pods, ahpa_max_pods,
    ahpa_status, ahpa_response_code,
    expected_pod_avg_qpm_avg, expected_pod_avg_qpm_p50, expected_pod_avg_qpm_p90,
    expected_pod_max_qpm_avg, expected_pod_max_qpm_p50, expected_pod_max_qpm_p90,
    expected_pod_latest_avg_qpm, expected_pod_latest_max_qpm,
    actual_pod_count, bias_coefficient, dt, host_gpu_num, model_req_count
)
WITH
-- 1. 从服务信息表获取 hpa/ahpa 类型、host_num 和 service_id
service_info AS (
    SELECT
        name AS service_name,
        instance_uuid,
        service_id,
        CAST(host_num AS SIGNED) AS host_num,
        CAST(host_gpu_num AS SIGNED) AS host_gpu_num,
        gpu_name,
        queue_name,
        hpa_enable,
        ahpa_enable,
        CASE
            WHEN ahpa_enable = 'true' THEN 'ahpa'
            WHEN hpa_enable = 'true' THEN 'hpa'
            ELSE NULL
        END AS scale_type
    FROM {DB_NAME}.{T_SVC_INFO}
    WHERE dt = '2026050700'
      AND status = 'done'
      AND deleted <> '1'
      AND (hpa_enable = 'true' OR ahpa_enable = 'true')
),

-- 2. 从HPA表获取副本数上下限（按 serving_id 分组，取 visible desc, id desc 第一条）
hpa_limits AS (
    SELECT
        serving_id,
        min_replicas,
        max_replicas,
        status AS hpa_status,
        visible
    FROM (
        SELECT
            id,
            serving_id,
            min_replicas,
            max_replicas,
            status,
            visible,
            ROW_NUMBER() OVER (PARTITION BY serving_id ORDER BY visible DESC, id DESC) AS rn
        FROM {DB_NAME}.{T_HPA}
        WHERE dt = '2026050700'
    ) t
    WHERE rn = 1
),

-- 3. 从AHPA推荐状态表判断最近连续10分钟的推荐状态
-- 先用窗口函数取每个 service_name 最近一条的 response_code
ahpa_latest AS (
    SELECT
        service_name,
        cluster_id,
        namespace,
        workload_name,
        response_code,
        CAST(`timestamp` AS SIGNED) AS ts
    FROM (
        SELECT
            service_name,
            cluster_id,
            namespace,
            workload_name,
            response_code,
            `timestamp`,
            ROW_NUMBER() OVER (
                PARTITION BY service_name
                ORDER BY CAST(`timestamp` AS SIGNED) DESC
            ) AS rn
        FROM {DB_NAME}.{T_AHPA}
        WHERE databus_imp_date = '2026050700'
          AND CAST(`timestamp` AS SIGNED) >= (
              SELECT MAX(CAST(`timestamp` AS SIGNED)) - 600000
              FROM {DB_NAME}.{T_AHPA}
              WHERE databus_imp_date = '2026050700'
          )
    ) ranked
    WHERE rn = 1
),

ahpa_rec_raw AS (
    SELECT
        service_name,
        MAX(cluster_id) AS cluster_id,
        MAX(namespace) AS namespace,
        MAX(workload_name) AS workload_name,
        COUNT(1) AS total_count,
        SUM(CASE WHEN response_code = '200' THEN 1 ELSE 0 END) AS success_count,
        MAX(CASE WHEN response_code IS NOT NULL THEN response_code ELSE NULL END) AS any_response_code
    FROM {DB_NAME}.{T_AHPA}
    WHERE databus_imp_date = '2026050700'
      AND CAST(`timestamp` AS SIGNED) >= (
          SELECT MAX(CAST(`timestamp` AS SIGNED)) - 600000
          FROM {DB_NAME}.{T_AHPA}
          WHERE databus_imp_date = '2026050700'
      )
    GROUP BY service_name
),

-- 以 service_info 中 ahpa 服务为基准，LEFT JOIN 推荐状态
ahpa_status AS (
    SELECT
        si.service_name,
        r.cluster_id,
        r.namespace,
        r.workload_name,
        al.response_code,
        CASE
            WHEN r.service_name IS NULL THEN 'no_request'
            WHEN r.success_count = 0 THEN 'degraded'
            ELSE 'normal'
        END AS ahpa_rec_status
    FROM (
        SELECT DISTINCT service_name
        FROM service_info
        WHERE scale_type = 'ahpa'
    ) si
    LEFT JOIN ahpa_rec_raw r ON si.service_name = r.service_name
    LEFT JOIN ahpa_latest al ON si.service_name = al.service_name
),

-- 4. 从步骤三获取预期pod数（取近5天内最近有产出的天级分区）
latest_expected_dt AS (
    SELECT MAX(dt) AS latest_dt
    FROM {DB_NAME}.{T_EXPECTED}
    WHERE dt >= '20260502' AND dt <= '20260507'
),

expected_pod AS (
    SELECT
        ep.service_name,
        ep.instance_uuid,
        ep.workload_name,
        ep.namespace,
        ep.agg_time,
        ep.hour_of_day,
        ep.expected_pod_avg_qpm_avg,
        ep.expected_pod_avg_qpm_p50,
        ep.expected_pod_avg_qpm_p90,
        ep.expected_pod_max_qpm_avg,
        ep.expected_pod_max_qpm_p50,
        ep.expected_pod_max_qpm_p90,
        ep.expected_pod_latest_avg_qpm,
        ep.expected_pod_latest_max_qpm,
        ep.model_req_count
    FROM {DB_NAME}.{T_EXPECTED} ep
    JOIN latest_expected_dt led ON ep.dt = led.latest_dt
),

-- 4.5 构造10分钟间隔的偏移量(0,10,20,30,40,50)
time_slots AS (
    SELECT 0 AS minute_offset UNION ALL
    SELECT 10 UNION ALL
    SELECT 20 UNION ALL
    SELECT 30 UNION ALL
    SELECT 40 UNION ALL
    SELECT 50
),

-- 4.6 将小时粒度的预期pod数按10分钟展开
expected_pod_10min AS (
    SELECT
        ep.service_name,
        ep.instance_uuid,
        ep.workload_name,
        ep.namespace,
        CONCAT(
            SUBSTRING(ep.agg_time, 1, 14),
            LPAD(CAST(ts.minute_offset AS CHAR), 2, '0'),
            ':00'
        ) AS agg_time,
        ep.hour_of_day,
        ep.expected_pod_avg_qpm_avg,
        ep.expected_pod_avg_qpm_p50,
        ep.expected_pod_avg_qpm_p90,
        ep.expected_pod_max_qpm_avg,
        ep.expected_pod_max_qpm_p50,
        ep.expected_pod_max_qpm_p90,
        ep.expected_pod_latest_avg_qpm,
        ep.expected_pod_latest_max_qpm,
        ep.model_req_count
    FROM expected_pod ep
    CROSS JOIN time_slots ts
),

-- 5. 从特征表取近24小时的实际pod数（按service+小时维度）
actual_pod AS (
    SELECT
        service_name,
        SUBSTRING_INDEX(agg_time, ' ', -1) AS hour_of_day,
        AVG(avg_pod_count) AS actual_pod_count
    FROM {DB_NAME}.{T_ACTUAL}
    WHERE dt >= '2026050600'
      AND dt <= '2026050700'
      AND agg_type = 2
      AND avg_pod_count > 0
    GROUP BY service_name, SUBSTRING_INDEX(agg_time, ' ', -1)
),

-- 6. 计算偏差系数
bias AS (
    SELECT
        a.service_name,
        a.hour_of_day,
        a.actual_pod_count,
        e.clamped_pod_p90,
        CASE
            WHEN e.clamped_pod_p90 > 0 THEN a.actual_pod_count / e.clamped_pod_p90
            ELSE 1.0
        END AS bias_coefficient
    FROM actual_pod a
    LEFT JOIN (
        SELECT
            ep.service_name,
            ep.hour_of_day,
            AVG(
                GREATEST(
                    LEAST(
                        ep.expected_pod_avg_qpm_p90,
                        CAST(hl.max_replicas AS DECIMAL(20,4)) * si.host_num
                    ),
                    CAST(hl.min_replicas AS DECIMAL(20,4)) * si.host_num
                )
            ) AS clamped_pod_p90
        FROM expected_pod_10min ep
        JOIN service_info si ON ep.service_name = si.service_name
        LEFT JOIN hpa_limits hl ON si.service_id = hl.serving_id
        GROUP BY ep.service_name, ep.hour_of_day
    ) e ON a.service_name = e.service_name AND a.hour_of_day = e.hour_of_day
)

-- 7. 最终结果
SELECT
    si.service_name,
    si.instance_uuid,
    COALESCE(ep.workload_name, ast.workload_name) AS workload_name,
    COALESCE(ep.namespace, ast.namespace) AS namespace,
    ep.agg_time,
    ep.hour_of_day,
    si.scale_type,
    si.host_num,
    si.gpu_name,
    si.queue_name,
    CAST(hl.min_replicas AS SIGNED) * si.host_num AS hpa_min_pods,
    CAST(hl.max_replicas AS SIGNED) * si.host_num AS hpa_max_pods,
    CAST(hl.min_replicas AS SIGNED) * si.host_num AS ahpa_min_pods,
    CAST(hl.max_replicas AS SIGNED) * si.host_num AS ahpa_max_pods,
    COALESCE(ast.ahpa_rec_status, 'no_request') AS ahpa_status,
    ast.response_code AS ahpa_response_code,
    GREATEST(LEAST(ep.expected_pod_avg_qpm_avg, CAST(hl.max_replicas AS DECIMAL(20,4)) * si.host_num), CAST(hl.min_replicas AS DECIMAL(20,4)) * si.host_num) AS expected_pod_avg_qpm_avg,
    GREATEST(LEAST(ep.expected_pod_avg_qpm_p50, CAST(hl.max_replicas AS DECIMAL(20,4)) * si.host_num), CAST(hl.min_replicas AS DECIMAL(20,4)) * si.host_num) AS expected_pod_avg_qpm_p50,
    GREATEST(LEAST(ep.expected_pod_avg_qpm_p90, CAST(hl.max_replicas AS DECIMAL(20,4)) * si.host_num), CAST(hl.min_replicas AS DECIMAL(20,4)) * si.host_num) AS expected_pod_avg_qpm_p90,
    GREATEST(LEAST(ep.expected_pod_max_qpm_avg, CAST(hl.max_replicas AS DECIMAL(20,4)) * si.host_num), CAST(hl.min_replicas AS DECIMAL(20,4)) * si.host_num) AS expected_pod_max_qpm_avg,
    GREATEST(LEAST(ep.expected_pod_max_qpm_p50, CAST(hl.max_replicas AS DECIMAL(20,4)) * si.host_num), CAST(hl.min_replicas AS DECIMAL(20,4)) * si.host_num) AS expected_pod_max_qpm_p50,
    GREATEST(LEAST(ep.expected_pod_max_qpm_p90, CAST(hl.max_replicas AS DECIMAL(20,4)) * si.host_num), CAST(hl.min_replicas AS DECIMAL(20,4)) * si.host_num) AS expected_pod_max_qpm_p90,
    GREATEST(LEAST(ep.expected_pod_latest_avg_qpm, CAST(hl.max_replicas AS DECIMAL(20,4)) * si.host_num), CAST(hl.min_replicas AS DECIMAL(20,4)) * si.host_num) AS expected_pod_latest_avg_qpm,
    GREATEST(LEAST(ep.expected_pod_latest_max_qpm, CAST(hl.max_replicas AS DECIMAL(20,4)) * si.host_num), CAST(hl.min_replicas AS DECIMAL(20,4)) * si.host_num) AS expected_pod_latest_max_qpm,
    b.actual_pod_count,
    b.bias_coefficient,
    '2026050700' AS dt,
    si.host_gpu_num,
    ep.model_req_count
FROM service_info si
JOIN expected_pod_10min ep ON si.service_name = ep.service_name
LEFT JOIN hpa_limits hl ON si.service_id = hl.serving_id
LEFT JOIN ahpa_status ast ON si.service_name = ast.service_name
LEFT JOIN bias b ON si.service_name = b.service_name AND ep.hour_of_day = b.hour_of_day
"""


def main():
    conn = pymysql.connect(**MYSQL_CONFIG)

    try:
        with conn.cursor() as cur:
            # Ensure output table exists
            cur.execute(f"""
                CREATE TABLE IF NOT EXISTS {DB_NAME}.{OUTPUT_TABLE} (
                    service_name VARCHAR(256),
                    instance_uuid VARCHAR(256),
                    workload_name VARCHAR(256),
                    namespace VARCHAR(256),
                    agg_time VARCHAR(256),
                    hour_of_day VARCHAR(256),
                    scale_type VARCHAR(256),
                    host_num BIGINT,
                    gpu_name VARCHAR(256),
                    queue_name VARCHAR(256),
                    hpa_min_pods BIGINT,
                    hpa_max_pods BIGINT,
                    ahpa_min_pods BIGINT,
                    ahpa_max_pods BIGINT,
                    ahpa_status VARCHAR(256),
                    ahpa_response_code VARCHAR(256),
                    expected_pod_avg_qpm_avg DOUBLE,
                    expected_pod_avg_qpm_p50 DOUBLE,
                    expected_pod_avg_qpm_p90 DOUBLE,
                    expected_pod_max_qpm_avg DOUBLE,
                    expected_pod_max_qpm_p50 DOUBLE,
                    expected_pod_max_qpm_p90 DOUBLE,
                    expected_pod_latest_avg_qpm DOUBLE,
                    expected_pod_latest_max_qpm DOUBLE,
                    actual_pod_count DOUBLE,
                    bias_coefficient DOUBLE,
                    dt VARCHAR(32),
                    host_gpu_num BIGINT,
                    model_req_count DOUBLE
                ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
            """)

            # Truncate + insert
            cur.execute(f"TRUNCATE TABLE {DB_NAME}.{OUTPUT_TABLE}")
            cur.execute(gt_sql)

        conn.commit()
        print("mysql_002 ground_truth done: rows written to output table")
    except Exception as e:
        print(f"ground_truth error: {e}", file=sys.stderr)
        conn.rollback()
        sys.exit(1)
    finally:
        conn.close()


if __name__ == "__main__":
    main()