移动数据库后存储过程卡住

时间:2017-07-17 10:01:04

标签: sql database postgresql stored-procedures

我只是将数据从生产服务器转储到本地服务器以进行测试。我们的应用程序使用大约300行sql的存储过程。此过程在服务器数据库中正常工作,但每次在我的本地数据库上运行它时都会卡住(永远运行)。有没有人对它为何陷入困境有任何想法。 这是我转储数据的命令。

CREATE OR REPLACE FUNCTION public.insert_hm_ticket_statistic(v_sprint_id bigint)
  RETURNS integer
LANGUAGE plpgsql
AS $$

DECLARE

  v_ticket              INT;
  v_startsprint_date    DATE;
  v_end_date            DATE;
  v_current_date        DATE;
  v_first_activity_date DATE;
  v_started             DATE;

  v_sprint_setting      VARCHAR(255);

  v_time_spent_second   FLOAT;
  v_remaining_estimate  FLOAT;
  v_time_logged_tmp     FLOAT;

  v_has_log             INT;
  v_time_logged_tk      FLOAT;
  v_oe_tk               INT;
  v_has_past_sprint     INT;

  v_oe_sprint           INT;
  v_complete_of_sprint  FLOAT;
  v_oe_tk_burnt         FLOAT;
  v_oe_sprint_burnt     FLOAT;
  v_project_complete    FLOAT;
  v_complete_tk         FLOAT;

    rl_cursor CURSOR FOR SELECT
                           tk.id,
                           tk.has_past_sprint,
                           cast(first_activity_date AS DATE)
                         FROM hm_ticket tk
                         WHERE sprint = v_sprint_id AND deleted = 0;

BEGIN

  -- find start date and end date of sprint
  SELECT
    cast(start_date AS DATE),
    cast(end_date AS DATE)
  INTO v_startsprint_date, v_end_date
  FROM hm_sprint
  WHERE id = v_sprint_id AND status <> 'future';



  -- find sprint setting
  IF NOT exists(SELECT d.burn_down_statuses
                FROM hm_sprint x, hm_setting c, hm_burn_down_status d
                WHERE c.id = d.setting AND x.setting = c.id AND x.id = v_sprint_id)
  THEN
    v_sprint_setting :='xxx';
  ELSE
    SELECT string_agg(d.burn_down_statuses, ',')
    INTO v_sprint_setting
    FROM hm_sprint x, hm_setting c, hm_burn_down_status d
    WHERE c.id = d.setting AND x.setting = c.id AND x.id = v_sprint_id;
  END IF;

  raise notice 'v_sprint_setting %', v_sprint_setting;

  OPEN rl_cursor;
  LOOP

    FETCH rl_cursor INTO v_ticket, v_has_past_sprint, v_first_activity_date;
    raise notice 'v_ticket: %', v_ticket;

    EXIT WHEN NOT FOUND;

    /*select cast(min(started) as date) into v_started
        from hm_worklog
        where ticket=v_ticket and cast(started as date) between v_startsprint_date and v_end_date;

      if v_started is null then v_current_date:=v_startsprint_date;
        else
        v_current_date:=v_started;
        end if;*/

    v_current_date:=v_startsprint_date;

    --- calculate remaining estimate, time logged of ticket
    IF v_has_past_sprint = 0
    THEN
      SELECT
        coalesce(remaining_estimate, 0),
        coalesce(time_logged, 0)
      INTO v_remaining_estimate, v_time_logged_tk
      FROM hm_ticket
      WHERE id = v_ticket;

    ELSE
      SELECT coalesce(time_logged, 0)
      INTO v_time_logged_tmp
      FROM hm_ticket
      WHERE id = v_ticket;
      --------------------------------------
      SELECT
        coalesce(remaining_estimate, 0),
        v_time_logged_tmp - coalesce(time_logged, 0)
      INTO v_remaining_estimate, v_time_logged_tk
      FROM hm_ticket_past_sprint
      WHERE ticket = v_ticket;

    END IF;

    raise notice '----------v_has_past_sprint: %', v_has_past_sprint;
    raise notice '----------v_time_logged_tmp: %', v_time_logged_tmp;
    raise notice '----------v_time_logged_tk: %', v_time_logged_tk;
    raise notice '----------v_remaining_estimate: %', v_remaining_estimate;

    -- calculate oe of ticket
    IF v_has_past_sprint = 0
    THEN
      SELECT cast(CASE WHEN coalesce(original_estimate, 0) = 0
        THEN coalesce(time_logged, 0)
                  ELSE original_estimate
                  END
                  AS FLOAT)
      INTO v_oe_tk
      FROM hm_ticket
      WHERE id = v_ticket;
    ELSE
      SELECT coalesce(time_logged, 0)
      INTO v_time_logged_tmp
      FROM hm_ticket
      WHERE id = v_ticket;
      --------------------------------------
      SELECT cast(CASE WHEN coalesce(original_estimate, 0) = 0
        THEN v_time_logged_tmp - coalesce(time_logged, 0)
                  ELSE original_estimate
                  END
                  AS FLOAT)
      INTO v_oe_tk
      FROM hm_ticket_past_sprint
      WHERE ticket = v_ticket;

    END IF;

    raise notice 'v_oe_tk: %',v_oe_tk;


    /*########################################################################## START TO LOOP CURRENT DATE #############################################################################*/

    WHILE v_current_date <= v_end_date LOOP
      --- calculate time_spent_seconds from start sprint
      IF NOT exists(SELECT id
                    FROM hm_worklog
                    WHERE ticket = v_ticket AND cast(started AS DATE) BETWEEN v_startsprint_date AND v_current_date)
      THEN
        v_time_spent_second:=0;
      ELSE
        SELECT cast(sum(time_spent_seconds) AS FLOAT)
        INTO v_time_spent_second
        FROM hm_worklog
        WHERE ticket = v_ticket AND cast(started AS DATE) BETWEEN v_startsprint_date AND v_current_date
        GROUP BY ticket;
      END IF;

      raise notice 'v_time_spent_second: %', v_time_spent_second;

      --calculate % complete of ticket/day

      IF NOT v_current_date = v_end_date THEN
        IF NOT exists(SELECT date FROM hm_ticket_history WHERE ticket = v_ticket AND cast(date AS DATE) = v_current_date)
        then
          -- FIND STATUS OF TICKET IN SETTING. 0: NO STATUS EXIST -> FORMULAR SHOULD BE APPLIED , <>0 : EXIST--> SET COMPLETE TK = 0

          SELECT CASE WHEN (position(upper(trim(status)) IN upper(trim(v_sprint_setting))) = 0)
            THEN CASE WHEN v_remaining_estimate = 0 AND v_time_logged_tk <> 0 THEN  80
                 WHEN v_remaining_estimate = 0 AND v_time_logged_tk = 0 THEN 0
                 WHEN (v_remaining_estimate + v_time_logged_tk) = 0 THEN 0
                 ELSE round(cast(v_time_spent_second * 100 / (v_time_logged_tk + v_remaining_estimate) AS NUMERIC), 2)
                 END
                 ELSE 100
                 END AS complete
          INTO v_complete_tk
          FROM hm_ticket
          WHERE id = v_ticket;
        ELSE
          SELECT CASE WHEN (position(upper(trim(status)) IN upper(trim(v_sprint_setting))) = 0)
            THEN CASE WHEN v_remaining_estimate = 0 AND v_time_logged_tk <> 0 THEN  80
                 WHEN v_remaining_estimate = 0 AND v_time_logged_tk = 0 THEN 0
                 WHEN (v_remaining_estimate + v_time_logged_tk) = 0 THEN 0
                 ELSE round(cast(v_time_spent_second * 100 / (v_time_logged_tk + v_remaining_estimate) AS NUMERIC), 2)
                 END
                 ELSE 100
                 END AS complete
          INTO v_complete_tk
          FROM hm_ticket_history
          WHERE ticket = v_ticket AND cast("date" AS DATE)= v_current_date;
        END IF;
      ELSE
        --NOTE: IF V_CURRENT_DATE == END-DATE-OF-SPRINT, COMPLETE % WILL BE CALCULATED BASE ON LATEST TICKET STATUS, NOT LAST-DAY-OF-SPRINT TICKET STATUS
        IF NOT exists(SELECT date FROM hm_ticket_history WHERE ticket = v_ticket AND cast(date AS DATE) = v_current_date)
        then
          SELECT CASE WHEN (position(upper(trim(status)) IN upper(trim(v_sprint_setting))) = 0)
            THEN CASE WHEN v_remaining_estimate = 0 AND v_time_logged_tk <> 0 THEN  80
                 WHEN v_remaining_estimate = 0 AND v_time_logged_tk = 0 THEN 0
                 WHEN (v_remaining_estimate + v_time_logged_tk) = 0 THEN 0
                 ELSE round(cast(v_time_spent_second * 100 / (v_time_logged_tk + v_remaining_estimate) AS NUMERIC), 2)
                 END
                 ELSE 100
                 END AS complete
          INTO v_complete_tk
          FROM hm_ticket
          WHERE id = v_ticket;
        else
          SELECT CASE WHEN (position(upper(trim(status)) IN upper(trim(v_sprint_setting))) = 0)
            THEN CASE WHEN v_remaining_estimate = 0 AND v_time_logged_tk <> 0 THEN  80
                 WHEN v_remaining_estimate = 0 AND v_time_logged_tk = 0 THEN 0
                 WHEN (v_remaining_estimate + v_time_logged_tk) = 0 THEN 0
                 ELSE round(cast(v_time_spent_second * 100 / (v_time_logged_tk + v_remaining_estimate) AS NUMERIC), 2)
                 END
                 ELSE 100
                 END AS complete
          INTO v_complete_tk
          FROM hm_ticket_history
          WHERE ticket = v_ticket order by date desc limit 1;
        END IF;
      END IF;


      if v_complete_tk > 100 then v_complete_tk := 100;
      end if;

      raise notice 'v_sprint_setting: %', v_sprint_setting;
      raise notice 'v_complete_tk: %', v_complete_tk;
      raise notice '---------------------------------';

      -- check has log
      IF exists(SELECT id
                FROM hm_worklog
                WHERE cast(started AS DATE) = cast(v_current_date AS DATE) AND ticket = v_ticket)
      THEN
        v_has_log := 1;
      ELSE
        v_has_log := 0;
      END IF;

      --raise notice 'v_has_log: %', v_has_log;

      -- calculate oe of sprint
      SELECT sum(x.oe)
      INTO v_oe_sprint
      FROM
        (
          SELECT CASE WHEN coalesce(original_estimate, 0) = 0
            THEN coalesce(time_logged, 0)
                 ELSE coalesce(original_estimate, 0)
                 END oe
          FROM hm_ticket
          WHERE sprint = v_sprint_id AND cast(first_activity_date AS DATE) <= v_current_date
                AND has_past_sprint = 0

          UNION ALL

          SELECT CASE WHEN coalesce(b.original_estimate, 0) = 0
            THEN coalesce(a.time_logged - b.time_logged)
                 ELSE coalesce(b.original_estimate, 0)
                 END oe
          FROM hm_ticket a, hm_ticket_past_sprint b
          WHERE a.id = b.ticket AND a.sprint = v_sprint_id AND cast(a.first_activity_date AS DATE) <= v_current_date
                AND a.has_past_sprint = 1
        ) x;

      -- raise notice 'v_oe_sprint: %', v_oe_sprint;

      -- calculate v_oe_tk_burnt
      SELECT CASE WHEN v_time_spent_second = 0 OR v_oe_tk = 0
        THEN 0
             ELSE round(cast((v_time_spent_second * 100 / v_oe_tk) AS NUMERIC), 2)
             END
      INTO v_oe_tk_burnt;

      --raise notice 'v_oe_tk_burnt: %', v_oe_tk_burnt;

      -- calculate v_oe_sprint_burnt
      IF v_oe_sprint = 0
      THEN
        v_oe_sprint_burnt:=100;
      ELSE
        SELECT round(cast((v_time_spent_second * 100 / v_oe_sprint) AS NUMERIC), 2)
        INTO v_oe_sprint_burnt;
      END IF;

      --raise notice 'v_oe_sprint_burnt: %', v_oe_sprint_burnt;

      -- calculate v_project_complete
      IF v_oe_sprint = 0
      THEN
        v_project_complete:=100;
      ELSE
        SELECT round(cast((v_complete_tk * v_oe_tk / v_oe_sprint) AS NUMERIC), 2)
        INTO v_project_complete;
      END IF;

      -- raise notice 'v_project_complete: %', v_project_complete;

      IF v_current_date >= v_first_activity_date
      THEN
        INSERT INTO hm_ticket_statistic (complete, date, has_log, last_modified, original_estimate_burnt, original_estimate_project_burnt, project_complete, status, ticket, sprint)
        VALUES
          (v_complete_tk, cast(v_current_date AS DATE), v_has_log, current_timestamp, v_oe_tk_burnt, v_oe_sprint_burnt,
           v_project_complete, 1, v_ticket, v_sprint_id)
        ON CONFLICT (date, ticket, sprint)
          DO UPDATE
            SET complete                      = v_complete_tk,
              has_log                         = v_has_log,
              original_estimate_burnt         = v_oe_tk_burnt,
              original_estimate_project_burnt = v_oe_sprint_burnt,
              project_complete                = v_project_complete;
      END IF;

      v_current_date:= v_current_date + INTERVAL '24 hours';

      EXIT WHEN NOT found;

    END LOOP;

  END LOOP;
  CLOSE rl_cursor;

  RETURN 0;
END;

这是存储过程

{{1}}

此外,如果您在代码中看到任何可以改进的内容,请建议我。

2 个答案:

答案 0 :(得分:3)

恢复转储后,尝试在本地数据库上运行analyze。这会更新统计信息。

答案 1 :(得分:0)

终于发现了这个bug。这是一个死锁bug,我在功能方面使用了Synchronized太多了,它们开始卡住了。生产服务器仍然遇到同样的问题,我只是没有看到它。所以我尝试更有选择地使用Synchronized,减少Synchronized on上的函数的大小。然后就修好了。