【问题标题】:How to wait for dbms_scheduler jobs to finish如何等待 dbms_scheduler 作业完成
【发布时间】:2015-04-17 06:32:23
【问题描述】:

使用 Oracle 11.2

嗨,

这是我想要做的:我正在使用 dbms_scheduler 调度作业。要调度的作业数量不固定,最多应同时运行 4 个作业。调度作业的过程应该等到所有作业都完成。如果一个作业失败,“调度”过程也应该失败,并且应该从调度程序中删除所有剩余的调度作业。

目前我不得不在循环中休眠和轮询表 user_scheduler_jobs。

我是 PL/SQL 新手,经验不足,所以请不要对我太苛刻 ;)

这是我目前的代码。

首先是调度作业的sn-p:

BEGIN
  FOR r IN (SELECT p_values FROM some_table WHERE flag = 0 )
  LOOP
    --  count running jobs
    SELECT count(*) INTO v_cnt
    FROM user_scheduler_jobs
    WHERE job_name LIKE 'something%';

    /*
    If max number of parallel jobs is reached, then wait before starting a new one.
    */
    WHILE v_cnt >= l_max_parallel_jobs
    LOOP

      dbms_lock.sleep(10);

      SELECT count(*) INTO v_cnt
      FROM user_scheduler_jobs
      WHERE job_name LIKE 'something%' AND state = 'RUNNING';

      SELECT count(*) INTO v_cnt_failures
      FROM user_scheduler_jobs
      WHERE job_name LIKE 'something%' AND state = 'FAILED' OR state = 'BROKEN';

      IF v_cnt_failures > 0 THEN RAISE some_exception; END IF;

    END LOOP;

    -- Start a new Job
    v_job_name := 'something_someting_' || p_values;
    v_job_action := 'begin user.some_procedure(''' || r.p_values || '''); end;';

    dbms_scheduler.create_job(job_name   => v_job_name,
                              job_type   => 'PLSQL_BLOCK',
                              job_action => v_job_action,
                              comments   => 'Some comment ' || v_job_name,
                              enabled    => FALSE,
                              auto_drop  => FALSE);

    dbms_scheduler.set_attribute(NAME => v_job_name,
                                 ATTRIBUTE => 'max_failures',
                                 VALUE => '1');

    dbms_scheduler.set_attribute(NAME => v_job_name,
                                 ATTRIBUTE => 'max_runs',
                                 VALUE => '1');

    dbms_scheduler.enable(v_job_name);

    v_job_count := v_job_count + 1;

    -- array for all jobs
    v_jobs_aat(v_job_count) := v_job_name;

  END LOOP;

  -- ... Wait till all jobs have finisched. 

  check_queue_completion(v_jobs_aat); -- see procedure below
END;

等待最后四个作业完成的程序:

PROCEDURE check_queue_completion(p_jobs_aat IN OUT t_jobs_aat) AS
  v_state user_scheduler_jobs.state%TYPE;
  v_index PLS_INTEGER;
  v_done BOOLEAN := TRUE;

  -- Exceptions
  e_job_failure EXCEPTION;
BEGIN

  WHILE v_done
  LOOP

    v_done := FALSE;

    FOR i IN p_jobs_aat.first..p_jobs_aat.last
    LOOP

      SELECT state INTO v_state FROM user_scheduler_jobs WHERE job_name = p_jobs_aat(i);

      --dbms_output.put_line('Status: ' || v_state);

      CASE

        WHEN v_state = 'SUCCEEDED' OR v_state = 'COMPLETED' THEN
        dbms_output.put_line(p_jobs_aat(i) || ' SUCCEEDED');
        dbms_scheduler.drop_job(job_name => p_jobs_aat(i), force => TRUE);
        p_jobs_aat.delete(i);

        WHEN v_state = 'FAILED' OR v_state = 'BROKEN' THEN
        --Exception auslösen
        dbms_output.put_line(p_jobs_aat(i) || ' FAILED');
        RAISE e_job_failure;

        WHEN v_state = 'RUNNING' OR v_state = 'RETRY SCHEDULED' THEN
        NULL;
        dbms_output.put_line(p_jobs_aat(i) || ' RUNNING or RETRY SCHEDULED');
        v_done := TRUE;

      /*DISABLED, SCHEDULED, REMOTE, CHAIN_STALLED*/
      ELSE
        dbms_output.put_line(p_jobs_aat(i) || ' ELSE');
        dbms_scheduler.drop_job(job_name => p_jobs_aat(i), force => TRUE);
        p_jobs_aat.delete(i);
      END CASE;

    END LOOP;

    hifa.gen_sleep(30);

  END LOOP;

  IF p_jobs_aat.count > 0 THEN delete_jobs_in_queue(p_jobs_aat); END IF;

  EXCEPTION WHEN e_job_failure THEN
  delete_jobs_in_queue(p_jobs_aat);
  RAISE_APPLICATION_ERROR(-20500, 'some error message');

END check_queue_completion;

它成功了,但它看起来像一些可怕的黑客。

有没有更好的方法:

  1. 等到所有作业都完成。
  2. 一次只运行四个作业,并在其中一个正在运行的作业完成后立即启动一个新作业。
  3. 如果一项作业失败或中断,则引发异常。

【问题讨论】:

  • 我会利用 dbms_scheduler 的现有功能。对于 #1,你看过链 (docs.oracle.com/cd/B28359_01/appdev.111/b28419/…) 吗? #2 您是否考虑过修改 job_queue_processes?
  • Patrick 所说的 - 查看调度程序链机制
  • 感谢您的 cmets。不幸的是我不能使用job_queue_processes,因为我在一个共享数据库上,他们不会让我使用“alter system.....”。我对工作链的理解是,它允许我定义诸如“做前 4 个工作”->“下 4 个工作”->“下 4 个工作”等等,但不像“开始 4 个工作并且尽快一个完成了,开始下一个”。我的目标是在任何给定时间都有四个作业正在运行。我还考虑过使用 DBMS_ALERT 等待作业,这至少会减少一点编码开销。

标签: oracle oracle11gr2 dbms-scheduler


【解决方案1】:
DECLARE
  cnt NUMBER:=1;
BEGIN
  WHILE cnt>=1
  LOOP
    SELECT count(1) INTO cnt FROM dba_scheduler_running_jobs srj
    WHERE srj.job_name IN ('TEST_JOB1','TEST_JOB2');
    IF cnt>0 THEN
      dbms_lock.sleep (5);
    END IF;
  END LOOP;
  dbms_output.put_line('ASASA');  
END;

【讨论】:

    【解决方案2】:

    使用 dbms_alert 或 dbms_pipe 发送/接收有关作业开始/完成的信息。仅当您未在预期时间内收到信息时才查询作业表。

    【讨论】:

    • 此管道或警报仅在发送者和接收者在同一实例中时才有效。 AQ 会是更好的选择,让它在 RAC 中工作。
    【解决方案3】:

    Oracle 调度器大量使用 Oracle 资源管理器,只需提交您的作业,定义一个结束通知,并有一个任务等待您的事件 Q,它计算提交的作业和完成的作业。

    您使用 Oracle 资源管理器来控制同时运行的最大作业数。这也将基于数据库的总负载,保护其他用户免受系统被作业淹没。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-09-02
      • 1970-01-01
      • 2020-01-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-02
      相关资源
      最近更新 更多