20 サブリクエストの使用

この章では、Oracle Enterprise Schedulerサブリクエストを使用し、特に、並列リクエスト数が変動する動的コンテキストにおいてデータを並行して処理する方法について説明します。

この章の内容は次のとおりです。

20.1 サブリクエストの使用の概要

Oracle Enterprise Schedulerサブリクエストは、データを並行して処理する場合に役立ちます。実行中のジョブから発行されるリクエストをサブリクエストと呼びます。

単一の親リクエストから複数のサブリクエストを発行できます。Oracle Enterprise Schedulerにおける通常の方法でのパラレル実行はジョブ・セット概念によるものですが、パラレル処理の数が一定にならない場合があります。たとえば、100万行ごとに1つのリクエストを割り当て、先週は、累積された処理対象が970万行となったとします。この場合は、10個のリクエストを割り当てます。460万行が累積された週では5個となります。

Oracle Enterprise Schedulerでは、指定した実行中のリクエスト(ジョブ・リクエスト)によりサブリクエストを発行し、このようなリクエストの完了を待機してから続行できるよう、サブリクエスト機能がサポートされています。

Oracle Enterprise Schedulerでは、オーバーロードされたサブリクエストのメソッドsubmitRequest()を公開することによって、サブリクエストがサポートされています。ジョブ・リクエストを発行するアプリケーションでは、このAPIを起動してサブリクエストを発行できます。

サブリクエストには、次の制限事項が適用されます。

  • サブリクエストは1回かぎりの実行としてのみ発行できます。スケジュールは指定できません。通常、サブリクエストは即時実行リクエストとして処理されます。

  • 非定型サブリクエストはサポートされていません。サブリクエストは、アプリケーション内の既存のJobDefinitionオブジェクトに対して発行される必要があります。

  • ジョブ・セットはサブリクエストに対してサポートされていません。サブリクエストはJobDefinitionオブジェクトに対してのみ発行できます。ただし、実行中の任意のジョブ(ジョブ・セットに含まれる場合があります)によって、サブリクエストを発行できます。

これらの制限によってサブリクエストの実行が簡略化され、発行元のリクエスト自体を実行する場合の混乱や遅延が回避されます。

Oracle Enterprise Schedulerには様々な親リクエストが存在しますが、この章の説明においては、親リクエストはサブリクエストを発行するリクエストのことを指します。

サブリクエストは、標準的な1回かぎりのリクエストの通常フローに従います。ただし、サブリクエストの処理は、親リクエストがその実行を一時停止した場合にのみ開始されます。これを示すために、Oracle Enterprise Schedulerでは、PAUSED状態が使用されます。この状態は、親リクエストが一時停止され、サブリクエストの終了を待機していることを意味します。

親リクエストによりサブリクエストが発行された後、この親リクエストはそのジョブ・タイプに適切な方法でコントロールをOracle Enterprise Schedulerに戻す必要があり、この状態は親リクエストが実行を一時停止したことを示します。次に、Oracle Enterprise Schedulerにより親の状態がPAUSEDに設定され、サブリクエストの処理が開始されます。サブリクエストの終了後、親リクエストは、適切なリクエスト・プロセッサによって取得されるまで、PAUSEDのままで準備完了キューに置かれます。その後、この親はRUNNING状態に設定され、再開されたリクエストとして再実行されます。

20.2 サブリクエストの作成と管理

20.2.1 サブリクエストの発行方法

サブリクエストは、submitRequest APIをコールすることにより発行できます。サブリクエストはWAIT状態に設定されますが、親リクエストが実行中の間は、Oracle Enterprise Schedulerによりこのリクエストが処理されることはありません。親リクエストが一時停止された後にのみ、サブリクエストを処理できます。

20.2.2 サブリクエストの取消し方法

サブリクエストを取り消す方法は主に2つあり、ユーザーがサブリクエストを直接取り消すか、または親リクエストを取り消すことによって結果的に取り消します。いずれの方法の場合でも、サブリクエストの取消しプロセスは、他の実行可能なリクエストと同じ方法で処理されます。異なる点は、保留中のサブリクエストがすべて完了して終了状態に到達した後に、親リクエストがOracle Enterprise Schedulerで処理される方法です。

Oracle Enterprise Schedulerにより、WAITまたはREADY状態にあるサブリクエストが、直接CANCELLEDに設定されます。サブリクエストが現在実行中である場合、そのサブリクエストはCANCELLINGに設定され、次に、Oracle Enterprise Schedulerにより、そのジョブ・タイプに適切な方法で、実行中の実行可能ファイルの取消しが試行されます。通常、サブリクエストはCANCELLED状態で終了しますが、そのサブリクエストがライフ・サイクルのどのステージに位置するかによって、他の終了状態で終了する場合があります。親リクエストは、すべてのサブリクエストが終了状態に到達するまで、PAUSEDまたはCANCELLING状態のままとなります。

ユーザーがサブリクエストを取り消すと、前述のように、Oracle Enterprise Schedulerによりそのサブリクエストが取り消されます。親リクエストは、すべてのサブリクエストが完了し、その時点でOracle Enterprise Schedulerにより親リクエストが再開または再起動されるまで、PAUSED状態のままとなります。これにより、親リクエストはサブリクエストの完了を、取り消されたものとするなどして、適切な方法で処理できます。したがって、サブリクエストの取消しが上位に伝播されることはありません。

ユーザーが親リクエストを取り消すと、Oracle Enterprise Schedulerによって親リクエストがCANCELLING状態に設定され、次に、前述の方法で、すべての保留中のサブリクエストに対する取消しが開始されます。すべてのサブリクエストが完了した後、親リクエストは、Oracle Enterprise SchedulerによりCANCELLEDに設定され、再開されません。親リクエストの取消しは、そのサブリクエストに伝播されます。

20.2.3 サブリクエストの保留方法

サブリクエストのライフ・サイクルは通常のリクエストのライフ・サイクルと同じであり、WAITまたはREADY状態である場合、保留にできます。親リクエストは、サブリクエストが保留になっている間、PAUSED状態のままとなります。

20.2.4 複数のサブリクエストの発行方法

Oracle Enterprise Schedulerを使用すると、リクエストにより複数のサブリクエストを発行できます。実行中のリクエストにより複数のサブリクエストが発行される場合があります。これらすべてのサブリクエストは、親リクエストが一時停止してPAUSED状態になると、Oracle Enterprise Schedulerによって処理されます。

このような複数のサブリクエストの場合、親リクエストは、すべてのサブリクエストが終了した場合にのみ再開されます。

また、サブリクエストは、任意の深さまで発行することもできます。これにより、ネストされたサブリクエストが作成されます。したがって、このようなサブリクエストの発行の深さについて制限はありません。これは、スタックのプッシュ操作とポップ操作に似ています。

20.2.5 一時停止されたサブリクエストの管理方法

20.2.5.1 一時停止ステータスの指定

Java実行可能ファイルにより、RuntimeService.submitRequestを使用してサブリクエストを発行できます。サブリクエストが発行された後、親リクエストは、サブリクエストを処理できるようにするために一時停止することを、Oracle Enterprise Schedulerに示す必要があります。これは、リクエストをPAUSED状態に遷移させるExecutionPausedExcpetionをスローする親によって実行されます。サブリクエストが完了した後、親リクエストは再開されたリクエストとして再度実行されます。RequestExecutionContextを使用すると、実行可能ファイルが再開されたリクエストとして実行中かどうかを判別できます。

20.2.5.2 親リクエストの一時停止状態の格納

ジョブ実行がサブリクエストの発行後に一時停止すると、実装において実行スレッドを一時停止するという概念がないため、Oracle Enterprise Schedulerでは、その実行があらゆる意味で完了したとみなされます。したがって、このような一時停止されたジョブを再開するには、Oracle Enterprise Schedulerによってジョブを再起動する必要があります。このような場合、実行が一時停止された時点から動作を続行することが必要であるのに対し、ジョブ実行は最初から再起動されます。これにより、ジョブ実行において、一時停止された時点を表すなんらかの実行状態を格納することが必要となります。再開したとき、ジョブはこのような状態を取得して一時停止された時点に移動し、ここから続行できます。

通常、ビジネス・ロジック全体における一時停止されたそれぞれの時点から、確定的な方法でジョブを再開できるようにするための実行状態は、個々のジョブで定義する必要があります(ジョブが一時停止された時点は複数存在する可能性があります)。これには、ステップ番号を格納して、再開時にその特定のステップに移動するという簡単な場合と、一時停止時にビジネス・ロジックにとって重要な状態を大規模なデータ・セットに格納する場合があります。Oracle Enterprise Schedulerでは、状態全体を格納するための完全なソリューションまたはフレームワークは提供できません。

Oracle Enterprise Schedulerでは、ジョブがその一時停止された時点を文字列の形式で格納するための簡易的な方法を提供しており、この文字列は、親ジョブがその実行を一時停止するときに指定できます。親ジョブが再開するとき、親では、必要に応じて使用するために、一時停止状態の値を取得できます。

Javaジョブにより、一時停止状態文字列を特別なExecutionPausedExceptionコンストラクタを使用して指定できます。状態パラメータは、親リクエストがPAUSED状態に設定されたときにOracle Enterprise Schedulerによって保存された一時停止状態文字列を表します。

public ExecutionPausedException(String message, String state)

再開された親は、親実行可能ファイルに渡されたRequestExecutionContextに対してgetPausedState()をコールすることにより、一時停止状態の値を取得できます。

単一の文字列値では不十分な場合は、親ジョブにより、setRequestParameter()を使用して任意の数のプロパティをOracle Enterprise Schedulerに書き込むことができ、再開時にはgetRequestParameter()を使用してこれらのプロパティを取得できます。

20.2.6 サブリクエストの処理方法

サブリクエストが発行されると、Oracle Enterprise Schedulerによりリクエストの状態がWAITに設定されますが、遅延モードでは、親リクエストが一時停止するまでディスパッチされません。

Javaジョブの親リクエストは、ExecutionPausedExceptionをスローすることによって、サブリクエストを処理する準備が整ったことを示します。Oracle Enterprise Schedulerでは、このような例外を受信すると、親リクエストの状態をPAUSEDに設定し、親が一時停止したという内容のシステム・イベント・メッセージを発行した後、その親に対する待機中のすべてのサブリクエストを準備完了キューにディスパッチします。

サブリクエストは、Oracle Enterprise Scheduler内の通常のライフ・サイクルに従って実行されます。指定された親リクエストのすべてのサブリクエストが終了した後、親リクエストを再開できます。

親の再開する準備が整うと、Oracle Enterprise Schedulerによって、親リクエストは準備完了キューに置かれます。親の状態は、取得を待機している間はPAUSEDのままとなります。Oracle Enterprise Schedulerにより親リクエストが準備完了キューから取得された後、リクエストの状態はRUNNINGに設定され、リクエストの実行可能ファイルは再開されたリクエストとしてコールされます。

サブリクエストが発行されることなくリクエストが一時停止された場合は、すべてのサブリクエストが終了したものとして処理されます。つまり、これはPAUSED状態で準備完了キューに置かれ、再開されたリクエストとして処理するために取得されます。

サブリクエストの最終状態は、その親実行可能ファイルが完了した後にOracle Enterprise Schedulerが親リクエストを処理する方法、および親リクエストの最終状態には影響を及ぼしません。親リクエストが再開すると、親リクエストのジョブ・ロジックはサブリクエストに関する情報を取得できるようになり、このデータを必要に応じて使用して、後続のアクションを決定します。親リクエストの最終状態は、親リクエストが完了した状態(成功、エラー、警告または取消し)のみに基づきます。

20.2.7 サブリクエストの識別方法

Oracle Enterprise Schedulerでは、リクエストごとにRequestType属性が含まれています。この属性は、リクエストがシングルトン、ジョブ・セットの一部、再帰リクエスト、サブリクエストなどのいずれかであるかを示します。

サブリクエストのRequestTypeは、SUB_REQUESTまたはUNVALIDATED_SUB_REQUESTです。UNVALIDATED_SUB_REQUESTは、Oracle Enterprise SchedulerのPL/SQLインタフェースを使用して発行されたが、まだ検証されていないサブリクエストを表します。親リクエストのRequestTypeは、SINGLETONRECUR_CHILDJOBSET_STEPまたはSUBREQUESTのいずれかです。その他すべてのリクエスト・タイプは、サブリクエストの親になることができないリクエストを表します。

サブリクエストに対する親リクエストのID属性は、そのサブリクエストを発行したリクエストです。

20.2.8 サブリクエストと非互換性の管理方法

通常、リクエストがREADYからRUNNING状態に遷移する場合、リクエストにより非互換性ロックが取得されます。これらのロックは、リクエストが終了して終了状態(SUCCEEDEDERRORWARNINGCANCELLEDなど)になるまで解放されません。

サブリクエストの親によって取得された非互換性ロックは、親リクエストがPAUSED状態の間も引き続き有効です。サブリクエストの親によってブロックされたリクエストはどれも、サブリクエストが実行される間、および親リクエストが再開されて終了するまでブロックされたままとなります。

サブリクエストは非互換性のすべてのルールに従います。したがって、Oracle Enterprise Schedulerでサブリクエストを実行する準備が整っているときに、互換性のない任意のリクエストが現在実行中である場合は、サブリクエストがブロックされることがあります。このような時間ウィンドウでは、親リクエストはPAUSED状態のままとなり、一方、サブリクエストはBLOCKED状態に遷移します。

20.3 サブリクエストを発行するJavaプロシージャの作成

これは、サブリクエストを発行するJavaジョブ・タイプのJavaクラスの例です。プロシージャによって2つのサブリクエストが発行され、それぞれの間に一時停止します。各サブリクエストでは同じJobDefinitionが使用されますが、リクエスト・パラメータSubRequestDataには異なる値が指定されます。

親リクエストのoracle.as.scheduler.Executable.executeメソッドは、指定されたOracle Enterprise Schedulerリクエストに対して合計3回コールされ、それぞれのコールに対する条件とアクションは、次のサマリーに示すように想定されます。

再開されないリクエストとしてメソッドを実行する最初のコールは、次のようになります。

エントリ条件:

  • RequestExecutionContext.isResumed()falseです

  • RequestExecutionContext.getPausedState()nullです

メソッドのアクション:

  • リクエスト・パラメータ値MyData1を使用したサブリクエストを発行します。

  • 値がMyPausedState1のpausedStateを使用したExecutionPausedExceptionをスローします。

Oracle Enterprise Schedulerによって、リクエストがPAUSED状態に遷移し、サブリクエストが実行され、そのサブリクエストが完了した後、リクエストが再開されます。

再開されたリクエストとしてメソッドを実行する最初のコールは、次のようになります。

エントリ条件:

  • RequestExecutionContext.isResumed()trueです

  • RequestExecutionContext.getPausedState()は'MyPausedState1'です

メソッドのアクション:

  • リクエスト・パラメータ値MyData2を使用したサブリクエストを発行します。

  • 値がMyPausedState2のpausedStateを使用したExecutionPausedExceptionをスローします。

Oracle Enterprise Schedulerによって、リクエストがPAUSED状態に遷移し、サブリクエストが実行され、そのサブリクエストが完了した後、リクエストが再開されます。

再開されたリクエストとしてメソッドを実行する2回目のコールは、次のようになります。

エントリ条件:

  • RequestExecutionContext.isResumed()trueです

  • RequestExecutionContext.getPausedState()は'MyPausedState2'です

メソッドのアクション:

  • 正常に終了し、例外は発生しません。

Oracle Enterprise Schedulerによって、リクエストがSUCCEEDED状態に遷移します。

例20-1に、サブリクエストを含むJavaプロシージャを示します。

例20-1 サブリクエストを含むJavaプロシージャ

// constants for the pausedState values
private final static String PAUSED_STATE_1 = "MyPausedState1";
private final static String PAUSED_STATE_2 = "MyPausedState2";
 
public class SubRequestSubmittor implements Executable {
 
    // method called by Oracle Enterprise Scheduler when the request is executed
    public void execute( RequestExecutionContext execCtx,
                         RequestParameters props )
           throws     ExecutionWarningException, 
                      ExecutionErrorException,
                      ExecutionPausedException, 
                      ExecutionCancelledException {
 
        long requestId =     execCtx.getRequestId();
        boolean isResumed =  execCtx.isResumed();
        String pausedState = execCtx.getPausedState();
 
        if (!isResumed) {
 
         // Method being called for first time, as non-resumed request.
         // Submit first subrequest.
         submitSubRequest(execCtx, "MyData1");
         throw new ExecutionPausedException("first subrequest", PAUSED_STATE_1);
 
        } else if (PAUSED_STATE_1.equals(pausedState)) {
 
          // Method being called for a resumed request.
          // Submit next subrequest.
          submitSubRequest(execCtx, "MyData2");
          throw new 
                ExecutionPausedException("second subrequest", PAUSED_STATE_2);
 
        } else if (PAUSED_STATE_2.equals(pausedState)) {
 
            // Method being called for a resumed request.
            // All done, just return.
 
        } else {
 
          // Method being called for a resumed request.
          // Unknown paused state (should never happen).
          String msg = "Request " + requestId + 
                       " was resumed with unexpected pause state " + pausedState;
            throw new ExecutionErrorException(msg);
 
        }
    }
 
    // Submit subrequest with request parameter having the given value.
    private void submitSubRequest( RequestExecutionContext execCtx,
                                   String paramValue )
        throws ExecutionErrorException{
 
        RuntimeService        rs = null;
        RuntimeServiceHandle  rh = null;
 
        try {
            rs = getRuntimeService();
 
            // Retrieve MetadataObjectId of the subrequest job definition
            String jobDef = "MySubRequestJobDef";
            MetadataObjectId jobDefId = getJobDefinition(jobDef);
 
             // Set value for the request parameter used by subrequest.
             RequestParameters rp = new RequestParameters();
             rp.add("SubRequestData", paramValue);
 
             // Submit the subrequest
             rh = rs.open();
 
             long subReqId = rs.submitRequest(rh,  execCtx, 
                                              "subrequest submitter", 
                                              jobDefId, rp);
 
        } catch (Exception e) {
 
            String msg = "Error while submitting subrequest for request " +
                          ExecCtx.getRequestId();
            throw new ExecutionErrorException(msg, e);
 
        } finally {
 
            if (null != rh) { 
                try {
                    rs.close(rh); 
                } catch (Exception e) {
                    String msg = "Error while submitting subrequest for request "
                                 + ExecCtx.getRequestId();
                    throw new ExecutionErrorException(msg, e);
                }
            }
        }
    }
 
    // Get RuntimeService.
    private RuntimeService getRuntime()
        throws ExecutionErrorException {
        // implementation not shown
    }
 
    // Retrieve MetadataObjectId for a given job definition name.
    private MetadataObjectId getJobDefinition( String jobDef )
        throws ExecutionErrorException {
        // implementation not shown
    }
 
}

20.4 サブリクエストを発行するPL/SQLプロシージャの作成

ESS_RUNTIME PL/SQLパッケージは、サブリクエストを発行するために、SQLジョブ・リクエストによって使用されます。これには、リクエストのプロシージャが再開されたリクエストとして実行されているかどうかの判別、および一時停止状態文字列の取得のためのサポートも含まれています。

Javaリクエストの場合、親リクエストがRuntimeService.submitRequestメソッドを使用してサブリクエストを発行し、その後、サブリクエストを実行できるように親リクエストが一時停止する準備が整うと、ExecutionPausedExceptionをスローします。

SQLリクエストの場合は、サブリクエストを発行するために、ess_runtime.submit_subrequestが使用されます。親リクエストは、サブリクエストの実行準備が整ったとき、ess_runtime.mark_pausedをコールし、遷移をコミットして、例外を発生させることなく正常に返す必要があります。mark_pausedメソッドは、親リクエストのプロシージャから正常に返されると、親リクエストをPAUSEDに設定してサブリクエストを実行できるようにする必要があることをOracle Enterprise Schedulerに通知します。mark_pausedメソッドでは、オプションの引数がサポートされており、この引数を使用して、一時停止状態文字列を指定できます。

サブリクエストは、親リクエストが例外を発生させることなくmark_pausedをコールし、コミットして、正常に返されるまで実行されることに注意してください。例外が発生した場合、Oracle Enterprise Schedulerでは親リクエストをPAUSED状態に設定せず、かわりに、SQLエラー・コードに応じて親の状態がERRORまたはWARNINGに設定されます。さらに、サブリクエストは自動的にCANCELLEDとなり、実行されません。

サブリクエストが終了した後、親リクエストのPL/SQLプロシージャは、Java実行可能ファイルのときと同じように、再開されたリクエストとして再実行されます。

Java実行可能ファイルの場合、RequestExecutionContextはリクエストが再開中かどうかを示し、親リクエストが一時停止したときにスローされるExecutionPausedExceptionを使用して指定された、一時停止状態文字列も示します。

SQLリクエストの場合、ess_runtime.is_resumedは、リクエストのプロシージャが再開されたリクエストに対して実行中であるかどうかを示します。メソッドess_runtime.get_paused_stateは、リクエストが一時停止された場合、ess_runtime.mark_pausedプロシージャを使用して指定された一時停止状態文字列を返します。

これは、ESS_RUNTIMEパッケージを使用してサブリクエストを発行するSQLジョブ・タイプのPL/SQLストアド・プロシージャの例です。プロシージャによって2つのサブリクエストが発行され、それぞれの間に一時停止します。各サブリクエストでは同じJobDefinitionが使用されますが、リクエスト・パラメータSubRequestDataには異なる値が指定されます。PL/SQLストアド・プロシージャは、指定されたOracle Enterprise Schedulerリクエストに対して合計3回コールされ、それぞれのコールに対する条件とアクションは、次のサマリーに示すように想定されます。

再開されないリクエストとしてのプロシージャへの最初のコールは、次のようになります。

エントリ条件:

  • ess_runtime.is_resumedfalseです。

  • ess_runtime.get_paused_statenullです。

プロシージャのアクション:

  • リクエスト・パラメータ値MyData1を使用したサブリクエストを発行します。

  • 一時停止状態を示すMyPausedState1を使用して、一時停止としてマークされます。

  • 正常に終了し、例外は発生しません。

Oracle Enterprise Schedulerによって、リクエストがPAUSED状態に遷移し、サブリクエストが実行され、そのサブリクエストが完了した後、リクエストが再開されます。

再開されたリクエストとしてのプロシージャへの最初のコールは、次のようになります。

エントリ条件:

  • ess_runtime.is_resumedtrueです。

  • ess_runtime.get_paused_stateは'MyPausedState1'です。

プロシージャのアクション:

  • リクエスト・パラメータ値MyData2を使用したサブリクエストを発行します。

  • 一時停止状態を示すMyPausedState2を使用して、一時停止としてマークされます。

  • 正常に終了し、例外は発生しません。

Oracle Enterprise Schedulerによって、リクエストがPAUSED状態に遷移し、サブリクエストが実行され、そのサブリクエストが完了した後、リクエストが再開されます。

再開されたリクエストとしてのプロシージャへの2回目のコールは、次のようになります。

エントリ条件:

  • ess_runtime.is_resumedtrueです。

  • ess_runtime.get_paused_stateは'MyPausedState2'です。

プロシージャのアクション:

  • 正常に終了し、例外は発生しません。

Oracle Enterprise Schedulerによって、リクエストがSUCCEEDED状態に遷移します。

例20-2に、サブリクエストを含むPL/SQLプロシージャを示します。

例20-2 サブリクエストを含むPL/SQLプロシージャ

-- Application stored procedure.       
procedure plsql_subreq_sample
( request_handle in varchar2 )
is
   v_reqid  number;
   v_is_resumed  boolean;
   v_paused_str  varchar2(100);
   v_paused_state1  varchar2(100) := 'MyPausedState1';
   v_paused_state2  varchar2(100) := 'MyPausedState2';
begin
   -- Request id of this subrequest parent.
   v_reqid := ess_runtime.get_request_id(request_handle);
 
   -- Check is this is a resumed request
   v_is_resumed := ess_runtime.is_resumed(request_handle);
  
   if (v_is_resumed = false) then
      -- This parent request is being run for the first time.
      -- Submit a subrequest and exit as success to allow ESS to pause this parent.
      submit_subrequest(request_handle, v_paused_state1, 'MyData1');
   else
      -- Parent is being run as resumed request.
      v_paused_str := ess_runtime.get_paused_state(v_reqid);
      if (v_paused_state1 = v_paused_str) then
         -- Request being resumed after first pause.
         -- Submit a subrequest and exit as success to allow ESS to pause this parent.
         submit_subrequest(request_handle, v_paused_state2, 'MyData2');
      elsif (v_paused_state2 = v_paused_str) then
         -- Request being resumed after second pause.
         -- Parent is done. Just return as success.
         null;
      end if;
   end if;
end;
 
-- Helper procedure to submit subrequest and call mark_paused.
-- Caller should exit normally, without an error, to allow
-- ESS mid-tier to pause the parent and execute the sub-request.
procedure submit_subrequest
( request_handle in varchar2,
  paused_state_value in varchar2,
  reqprop_value in varchar2 )
is
   v_sub_reqid  number := null;
   v_req_props  ess_runtime.request_prop_table_t := null;
   v_idx  pls_integer := 0;
begin
   v_req_props := ess_runtime.request_prop_table_t();
   v_idx := 0;
 
   v_idx := v_idx + 1;
   v_req_props.extend;
   v_req_props(v_idx).prop_name := 'SubRequestData';
   v_req_props(v_idx).prop_datatype := ess_runtime.STRING_DATATYPE;
   v_req_props(v_idx).prop_value := reqprop_value;
 
   -- Must commit or rollback work done in this block.
   begin
      -- Submit the subrequest.
      v_sub_reqid := ess_runtime.submit_subrequest(
            request_handle => request_handle,
            definition_name => 'sampleJob',
            definition_package => 'samplePkg',
            props => v_req_props);
 
      -- Indicate that the parent request should pause.
      -- The actual state change does not occur until ESS is notififed
      -- that this run of the parent executable finished.
      ess_runtime.mark_paused(request_handle, paused_state_value);
 
      -- This procedure is responsible for the txn commit.
      commit;
   exception
      -- Rollback txn on failure and raise an error.
      rollback;
      raise_application_error(-20000,
                              'Error submitting sub-request. '||
                              'SQLCODE='||SQLCODE || ', SQLERRM=['||SQLERRM||']',
                              true);
      end;
   end;
end;