# Data Quality Check Pipeline Instance GPU Card-Hour Analysis (PrestoSQL)

## Prompt
**Task Objective**: Analyze the runtime of data quality check pipeline instances. For some abnormally terminated instances, their completion records need to be backfilled. Compute the runtime duration of each instance per date shard, and combined with GPU monitoring data, compute the GPU card-hour consumption, ultimately outputting a detail table.

**Business Background**:
The data quality check pipeline produces various span records, including check.start, check.completed, check.aborted, data.scan, rule.evaluate, resource.allocate, etc. Some instances only record check.aborted without check.completed. For these instances, a synthetic check.completed record must be constructed based on their start and aborted times to correctly compute the runtime duration.

GPU monitoring data is collected in 5-minute windows and must be mapped to instances via Pod names. Combined with the GPU count from the task configuration, the card-hours are computed (gpu_hour = host_gpu_num * total_run_minutes / 60.0).

**Input Tables**:
- `internal_platform_db.t_quality_check_span_prestosql_012` (quality check span table)
  - `databus_imp_date` STRING — data date
  - `trace_id` STRING — trace ID
  - `span_name` STRING — span name
  - `start_time` STRING — start time (epoch milliseconds)
  - `end_time` STRING — end time (epoch milliseconds)
  - `status_code` INT — status code
  - `project_id` STRING — project ID
  - `task_id` STRING — task ID
  - `check_type` STRING — check type

- `internal_platform_db.t_quality_gpu_monitor_prestosql_012` (GPU monitoring table)
  - `pod_name` STRING — Pod name
  - `pkg_time` STRING — collection time (epoch seconds)
  - `gpu_name` STRING — GPU model
  - `metric` STRING — monitoring metric
  - `value` STRING — metric value
  - `dt` STRING — date partition

- `internal_platform_db.dwd_quality_podname_prestosql_012` (Pod-to-instance mapping table)
  - `dt` STRING — date partition
  - `instance_uuid` STRING — instance UUID
  - `pod_name` STRING — Pod name
  - `pod_phase` STRING — Pod phase

- `internal_platform_db.dwd_quality_task_config_prestosql_012` (task GPU configuration table 1)
  - `databus_imp_date` STRING — data date
  - `instance_uuid` STRING — instance UUID
  - `host_gpu_num` DOUBLE — GPU count
  - `host_num` DOUBLE — host count
  - `last_modify` DOUBLE — last modification time
  - `gpu_name` STRING — GPU model
  - `resource_id` STRING — resource ID
  - `is_dedicated` STRING — whether dedicated

- `internal_platform_db.quality_task_config_prestosql_012` (task GPU configuration table 2)
  - `databus_imp_date` STRING — data date
  - `instance_uuid` STRING — instance UUID
  - `host_gpu_num` DOUBLE — GPU count
  - `host_num` DOUBLE — host count
  - `last_modify` DOUBLE — last modification time
  - `gpu_name` STRING — GPU model
  - `resource_id` STRING — resource ID
  - `is_dedicated` STRING — whether dedicated
  - `scan_type` STRING — scan type

**Key Logic Notes**:
1. Identify trace_ids that have only check.aborted without check.completed, and synthesize a check.completed record for them
2. Spans running across days must be split by day, generating one record per day
3. GPU monitoring data must be deduplicated by 5-minute windows to compute running minutes, and mapped to instances via Pod mapping
4. The two GPU configuration tables must be combined with UNION ALL, then use ROW_NUMBER to take the latest configuration per instance
5. gpu_hour = host_gpu_num * total_run_minutes / 60.0

**Output Requirements**:
- Target table: `internal_platform_db.t_quality_check_gpu_instance_detail_cand_prestosql_012`
- Output fields: `p_date` STRING, `trace_id` STRING, `project_id` STRING, `task_id` STRING, `check_type` STRING, `status_code` INT, `instance_run_time` INT, `code_run_time` INT, `gpu_hour` DOUBLE, `resource_id` STRING, `is_dedicated` BOOLEAN
- If the target table does not exist, first create the table, then write the data
- Use Presto/Trino SQL syntax; do not use Hive/Spark SQL dialects

**Environment and Execution Notes**:
- Presto is running, connected via the Hive catalog
- Execute SQL: `presto-cli --catalog hive --schema internal_platform_db -f /tmp_workspace/result.sql`
- After writing `result.sql`, you must execute it yourself to verify that it runs successfully and produces correct data

## Reference Solution
INSERT INTO internal_platform_db.t_quality_check_gpu_instance_detail_prestosql_012
WITH base_trace AS (
    -- Step 1: Find trace_ids with check.aborted but NOT check.completed (HAVING inverted condition)
    SELECT trace_id
    FROM internal_platform_db.t_quality_check_span_prestosql_012
    WHERE databus_imp_date >= '2026060600'
      AND databus_imp_date <= '2026060800'
      AND span_name IN ('check.aborted', 'check.completed')
    GROUP BY trace_id
    HAVING COUNT(CASE WHEN span_name = 'check.aborted' THEN 1 END) > 0
       AND COUNT(CASE WHEN span_name = 'check.completed' THEN 1 END) = 0
),
combined_spans AS (
    -- Step 2: All original spans for ALL traces + synthetic check.completed for aborted-only traces
    SELECT
        trace_id,
        span_name,
        start_time,
        end_time,
        status_code,
        project_id,
        task_id,
        check_type
    FROM internal_platform_db.t_quality_check_span_prestosql_012
    WHERE databus_imp_date >= '2026060600'
      AND databus_imp_date <= '2026060800'
      AND span_name IN ('check.aborted', 'check.start', 'check.completed', 'data.scan', 'rule.evaluate', 'resource.allocate')

    UNION ALL

    SELECT
        trace_id,
        'check.completed' AS span_name,
        MAX(CASE WHEN span_name = 'check.start' THEN start_time END) AS start_time,
        MAX(CASE WHEN span_name = 'check.aborted' THEN end_time END) AS end_time,
        2 AS status_code,
        MAX(CASE WHEN span_name = 'check.aborted' THEN project_id END) AS project_id,
        MAX(CASE WHEN span_name = 'check.aborted' THEN task_id END) AS task_id,
        MAX(CASE WHEN span_name = 'check.aborted' THEN check_type END) AS check_type
    FROM internal_platform_db.t_quality_check_span_prestosql_012
    WHERE databus_imp_date >= '2026060600'
      AND databus_imp_date <= '2026060800'
      AND trace_id IN (SELECT trace_id FROM base_trace)
      AND span_name IN ('check.start', 'check.aborted')
    GROUP BY trace_id
),
base_data_time_fixed AS (
    -- Step 3: Fix check.aborted start_time -> earliest of code-related spans
    SELECT
        trace_id,
        span_name,
        CASE
            WHEN span_name = 'check.aborted'
            THEN MIN(CASE WHEN span_name IN ('data.scan', 'rule.evaluate', 'check.aborted') THEN start_time END)
                    OVER(PARTITION BY trace_id)
            ELSE start_time
        END AS start_time,
        end_time,
        status_code,
        project_id,
        task_id,
        check_type
    FROM combined_spans
),
base_data AS (
    -- Step 4: Convert timestamps, compute diff_days for cross-day splitting
    SELECT
        trace_id,
        span_name,
        start_time,
        end_time,
        status_code,
        project_id,
        task_id,
        check_type,
        from_unixtime(CAST(start_time AS BIGINT) / 1000) AS start_date,
        from_unixtime(CAST(end_time AS BIGINT) / 1000) AS end_date,
        date_diff('day', from_unixtime(CAST(start_time AS BIGINT) / 1000),
                 from_unixtime(CAST(end_time AS BIGINT) / 1000)) AS diff_days,
        CAST(CAST(start_time AS BIGINT) / 86400000 AS BIGINT) * 86400000 AS start_day_midnight_ms
    FROM base_data_time_fixed
),
pos_series AS (
    -- Step 5: Position series for cross-day expansion
    SELECT 0 AS pos UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
),
daily_split_spans AS (
    -- Step 6: INNER JOIN with pos_series, compute boundary timestamps per day
    SELECT
        trace_id,
        span_name,
        project_id,
        task_id,
        check_type,
        status_code,
        start_date,
        end_date,
        diff_days,
        date_add('day', s.pos, start_date) AS calc_date,
        CASE WHEN s.pos = 0 THEN start_time
             ELSE CAST(start_day_midnight_ms + s.pos * 86400000 AS VARCHAR)
        END AS split_start_time,
        CASE WHEN s.pos = diff_days THEN end_time
             ELSE CAST(start_day_midnight_ms + (s.pos + 1) * 86400000 - 1 AS VARCHAR)
        END AS split_end_time,
        start_time AS span_start_time,
        end_time AS span_end_time
    FROM base_data b
    INNER JOIN pos_series s ON s.pos <= b.diff_days
),
span_metrics AS (
    -- Step 7: GROUP BY trace_id, calc_date -> compute instance_run_time, code_run_time, status_code
    SELECT
        trace_id,
        calc_date,
        MAX(project_id) AS project_id,
        MAX(task_id) AS task_id,
        MAX(check_type) AS check_type,
        MAX(MAX(CASE WHEN span_name = 'check.completed' THEN status_code END))
            OVER(PARTITION BY trace_id) AS status_code,
        CAST(ROUND(
            (MAX(CASE WHEN span_name = 'check.completed' THEN CAST(split_end_time AS BIGINT) END) -
             MIN(CASE WHEN span_name = 'check.completed' THEN CAST(split_start_time AS BIGINT) END)) / 1000.0
        ) AS INT) AS instance_run_time,
        CAST(ROUND(
            (MAX(CASE WHEN span_name IN ('data.scan', 'rule.evaluate', 'check.aborted') THEN CAST(split_end_time AS BIGINT) END) -
             MIN(CASE WHEN span_name IN ('data.scan', 'rule.evaluate', 'check.aborted') THEN CAST(split_start_time AS BIGINT) END)) / 1000.0
        ) AS INT) AS code_run_time
    FROM daily_split_spans
    GROUP BY trace_id, calc_date
),
gpu_stats AS (
    -- Step 8: FLOOR 5-min window bucketing + pod mapping + ROW_NUMBER config dedup + gpu_hour
    SELECT
        ig.instance_uuid,
        lc.host_gpu_num * ig.total_run_time_m / 60.0 AS gpu_hour,
        ig.gpu_name,
        lc.resource_id,
        CASE WHEN lc.is_dedicated = 'true' THEN true ELSE false END AS is_dedicated
    FROM (
        -- Aggregate GPU run time per instance
        SELECT
            m.instance_uuid,
            MAX(p.gpu_name) AS gpu_name,
            SUM(p.run_time_m) AS total_run_time_m
        FROM (
            -- pod_run_time: count distinct minutes per (pod_name, time_5min)
            SELECT
                pod_name,
                MAX(gpu_name) AS gpu_name,
                time_5min,
                CAST(COUNT(DISTINCT minute_timestamp) AS DOUBLE) AS run_time_m
            FROM (
                -- pod_run_minutes: FLOOR bucketing dedup
                SELECT
                    pod_name,
                    gpu_name,
                    FLOOR(CAST(pkg_time AS BIGINT) / 60) * 60 AS minute_timestamp,
                    FLOOR(CAST(pkg_time AS BIGINT) / 300) * 300 AS time_5min
                FROM internal_platform_db.t_quality_gpu_monitor_prestosql_012
                WHERE metric IN ('k8s_container_vgpu_gpu_mem_usage', 'k8s_dcgm_fi_dev_fb_util')
                GROUP BY pod_name, gpu_name,
                         FLOOR(CAST(pkg_time AS BIGINT) / 60) * 60,
                         FLOOR(CAST(pkg_time AS BIGINT) / 300) * 300
            ) pod_run_minutes
            GROUP BY pod_name, time_5min
        ) p
        INNER JOIN internal_platform_db.dwd_quality_podname_prestosql_012 m
            ON p.pod_name = m.pod_name
        GROUP BY m.instance_uuid
    ) ig
    LEFT JOIN (
        -- latest_task_config: ROW_NUMBER dedup from UNION ALL of two config tables
        SELECT
            instance_uuid,
            host_gpu_num,
            resource_id,
            is_dedicated
        FROM (
            SELECT
                instance_uuid,
                host_gpu_num,
                resource_id,
                is_dedicated,
                ROW_NUMBER() OVER (PARTITION BY instance_uuid ORDER BY last_modify DESC) AS rn
            FROM (
                SELECT instance_uuid, host_gpu_num, last_modify, resource_id, is_dedicated
                FROM internal_platform_db.dwd_quality_task_config_prestosql_012
                WHERE databus_imp_date = (
                    SELECT MAX(databus_imp_date)
                    FROM internal_platform_db.dwd_quality_task_config_prestosql_012
                )
                UNION ALL
                SELECT instance_uuid, host_gpu_num, last_modify, resource_id, is_dedicated
                FROM internal_platform_db.quality_task_config_prestosql_012
                WHERE databus_imp_date = (
                    SELECT MAX(databus_imp_date)
                    FROM internal_platform_db.quality_task_config_prestosql_012
                )
            ) combined
        ) ranked
        WHERE rn = 1
    ) lc ON ig.instance_uuid = lc.instance_uuid
)
-- Final: JOIN span_metrics with gpu_stats
SELECT
    date_format(sm.calc_date, '%Y-%m-%d') AS p_date,
    sm.trace_id,
    sm.project_id,
    sm.task_id,
    sm.check_type,
    sm.status_code,
    sm.instance_run_time,
    sm.code_run_time,
    gs.gpu_hour,
    gs.resource_id,
    gs.is_dedicated
FROM span_metrics sm
LEFT JOIN gpu_stats gs ON sm.trace_id = gs.instance_uuid
;