Skip to Content
Technical Articles
Author's profile photo Carlos Roggan

Using Job Scheduler in SAP Cloud Platform [5]: Long-Running (Async) Jobs

This tutorial is part of a little series about SAP Cloud Platform Job Scheduler

Quicklinks:
Intro Blog
Tutorial 1
Sample Code (Trial)
Sample Code (Prod)
Quick Guide

In previous tutorials, we’ve learned how to configure Jobscheduler and how to write apps which are protected with OAuth 2.0

Basically, what we’ve done was to create a web app with a REST endpoint and ask Jobscheduler to call that endpoint.
That endpoint hopefully returns a success response and Jobscheduler is happy to mark the jobrun with green letters

With other words:
Jobscheduler WAITS for the response.
It is like a patient cab driver…. but his patience is not endless:
To be concrete: patience ends after 15 seconds
Yes:
Jobscheduler has a timeout: 15 seconds

But what can we do, if we have operations that require more time?

In this blog, let’s discuss an important scenario: Long running jobs
They are required in scenarios like e.g. data replication running overnight, etc
In such case, Jobscheduler does NOT wait for the response of the called endpoint.
Such scenario has to be executed in ASYNCHRONOUS way
This means:
Jobscheduler triggers an app’s endpoint
Once the app is done, it calls sort of callback to inform Jobscheduler about the finished job

Let’s learn how it is done

Prerequisites

* You should have had a look at the previous tutorials
* You should be familiar with Node.js (otherwise check here)
* You need an account in SAP Cloud Platform
As usual, our tutorial is based on the Trial account (Jobscheduler ‘lite’), such that everybody can follw it.
In productive account (Jobscheduler ‘standard’), the code looks a bit different, but this is explicitly described in a section below

Overview:

1. See the timeout
2. Implement the required app behavior
2.1. Endpoint
2.2. Backend operation
2.3. Update status
2.4. Update status prod
3. Test it

1. Normal synch job

Let‘s start with the negative scenario, then we repair it in a second step
We create a little node application which represents a long-running operation.
To simulate it, we just wait for 18 seconds

1.1. Create Jobscheduler instance

See description here

1.2. Create XSUAA instance

See description here

1.3. Create app

const express = require('express');
const app = express();

app.get('/runjob', function(req, res){
   setTimeout(() => {
      res.send('Finished job after 18 seconds waiting');
   }, 18000);
});

app.listen(process.env.PORT, function(){})

1.4. Deploy

You know how to deply your app

1.5. Try it in browser

Invoke the endpoint in a browser window,e.g.
https://longrun.cfapps.eu10.hana.ondemand.com/runjob
after 18 seconds, you get the response with the message as coded above

1.6. Try JobScheduler

Create job, check logs. There you’ll find the error message:

Error: ESOCKETTIMEDOUT – Request timeout after 15 seconds

OK, this was expected because in our app, we wait 18 seconds, which is longer than the allowed 15 seconds
Yes, our wait-18-seconds is our long-running operation
Let’s see the next chapter to learn how to repair it

Summary:
Jobscheduler has a built-in timeout of 15 seconds

 

2. The solution: Async Job

What needs to be done?

1. Our endpoint has to respond immediately with success status 202
2. We have to trigger our long-running operation
3. We have to manually set the result in Jobscheduler

OK, let’s see in detail

2.1. The endpoint

The Jobscheduler doesn’t know much about us:
It just knows the endpoint which it is supposed to call
So we have to inform Jobscheduler about our plans of running long:
for that purpose we use our endpoint
Concrete: Our endpoint has to send a response with status code 202
Like that, we tell Jobscheduler not to wait for us

Jobscheduler interprets status codes as follows:
* Error code: endpoint operation had an error -> job is completed and will be marked with error
* 200 : endpoint operation finished successfully -> job is completed with success
* 202: endpoint has been triggered and has responded with 202 -> job is not completed, it is on hold until further notice

As per definition, status code 202 means “Accepted”
See appendix for explanation of status code 202

For our endpoint implementation this means:
as soon as our endpoint is invoked, we respond with 202

app.get('/runjob', function(req, res){       
   res.status(202).send('Accepted async job, but long-running operation still running.')

As you’ve already noticed, I’m distinguishing:
– The term “job” belongs to the Jobscheduler, it is the trigger in the cloud
– The term “operation” indicates the actual work, which is done e.g. in a backend

2.2. The backend-operation

After responding to the Jobscheduler, our endpoint will trigger the long-running backend-operation
In our little sample app, we just wait for 3 seconds:

const doLongRunningOperation = function (doFail) {
   return new Promise((resolve, reject)=>{
      const letItFail = doFail
      setTimeout(() => {     
         if(letItFail === 'true'){
            reject({message: 'Backend operation failed with error code 123'});
         }else{
            resolve({message: 'Successfully finished long-running backend operation'})   
         }
      }, 3000); // wait... wait...
   })
}

Note:
To make testing easier, our little sample app can be configured:
Depending on a URL parameter (which is set by us), the long-running backend operation will fail or will succeed

2.3. The Status

Now comes the interesting part:
How to inform the Jobscheduler about the result of our backend operation?
We cannot expect that Jobscheduler calls us again to ask us
So now it is vice versa: WE have to call Jobscheduler

How do we call Jobscheduler?
Jobscheduler provides a REST API for remote control
This is a big feature and here we cover a small subset:
How to use the REST API to set the status of a job execution
See the REST API documentation for an overview of other features (create jobs, view results, etc)

In order to access a particular job execution and set the status, we need some information:
1. We need the internal info about the job and the URL to call
2. We need authentication info
3. We need to know the structure of the endpoint and the data which we have to send

And this is how we get the required info:

2.3.1. How to get the internal info of concrete job to update?

This information is sent to us by Jobscheduler when it calls our endpoint.
It is hidden in the headers
And here are the headers which we need:

Use case Header name
Which job? x-sap-job-id
Which schedule? x-sap-job-schedule-id
Which particular job run? x-sap-job-run-id
Which URL to call? x-sap-scheduler-host

Example values:

x-sap-job-id 12345
x-sap-job-schedule-id cfca1190-faf5-476d-bc03-b70bfa82dba3
x-sap-job-run-id b2cde24e-5479-46da-b3a1-1f1da93823d0
x-sap-scheduler-host https://jobscheduler-rest.cfapps…hana.ondemand.com

2.3.2. How to authenticate?

There are credentials for us, we only need to find them:
As usual in the cloud platform, our app gets credentials in the application environment, when it is bound to a service instance.
We can see it either in the cloud cockpit, or in the command line (as described in previous blog)
Now we need to distinguish:
Authentication mechanism is different in Trial and productive landscape, or with other words, it is different depending on the service plan used to create the instance of Jobscheduler service

Account Service Plan Authentication Type
trial lite Basic Auth
prod standard OAuth 2.0

Authentication in Trial

How does the environment look like?

{
  "VCAP_SERVICES": {
    "jobscheduler": [
    {
      "credentials": {
        "user": "sbss_gkokz3ayc90hyyyvqat3chwzvkhzkwrqrvby2zw6pjapxxmecgtrcifezrfzjm294xc=",
        "password": "aa_rr/YIrbkHGVneWSchRfbnFSJjc8=",

To access the values, we can traverse through the JSON

const VCAP_SERVICES = JSON.parse(process.env.VCAP_SERVICES)
const CREDENTIALS = VCAP_SERVICES.jobscheduler[0].credentials
const USER = CREDENTIALS.user
const PWD = CREDENTIALS.password

Note:
You can use a little node module to access the environment variables: @sap/xsenv (see here)

2.3.3. How to call the udpateStatus endpoint?

Now that we have all the required info, we can go ahead with composing the request to the REST API (see the documentation)

How to build the URL?
This is the template

{host}/scheduler/jobs/{jobId}/schedules/{scheduleId}/runs/{runId}

Which HTTP Verb?
For update the status, we use PUT

Which payload?
As stated in the documentation, the payload has to be a (stringified) json object with mandatory parameter “success” and optional “message”

{
  "success": true,
  "message": "Successful finished long running operation"
}

Which code?

Now, to programmatically execute that request to the REST endpoint of Jobscheduler, I’ve decided to use the native node module, but you can use any other convenient module of your choice

The code shows what we learned above:
1. how we access the information from headers
2. how we compose the authorization from the env variables
3. how we compose the URL with the information extracted from the headers

And at the end we execute the request

const doUpdateStatus = function(headers, success, message){
      jobId = headers['x-sap-job-id']
      scheduleId = headers['x-sap-job-schedule-id']
      runId = headers['x-sap-job-run-id']
      host = headers['x-sap-scheduler-host']
   
      const data = JSON.stringify({success: success, message: message}) 
      const options = {
         host:  host.replace('https://', ''),
         path:  `/scheduler/jobs/${jobId}/schedules/${scheduleId}/runs/${runId}`,
         method: 'PUT',
         headers: {
            Authorization: "Basic " + Buffer.from(USER + ':' + PWD).toString("base64")
         }
      };
      
      const req = https.request(options, (res) => {
      ...
      });     
      ...   
      req.write(data)
      req.end()   
   })
}

Note:
Above code works only in SAP Cloud Platform Trial account, where the jobscheduler service instance can only be created with service plan lite

Please refer to the appendix section for the full application code

2.4. Update status in prod, service plan ‘standard’

In productive landscape, where the jobscheduler service instance is created with service plan ‘standard’, the REST API is protected with OAuth 2.0
(remember: above code sample was based on Trial landscape, where only basic authentication is required)

As such, updating the status takes some more lines of code
In order to use the REST endpoint, we have to send a valid JWT token
So we need to obtain a token before we execute the REST request

2.4.1. Fetch JWT token

We get the token from XSUAA
The credentials required for that call are again provided by jobscheduler in the environment, but this time we don’t get user and password, but instead we get the clientid and clientsecret.
Furthermore we need to know the URL which we have to call in order to get the token.
It is the oauth authorization server URL provided by xsuaa. We get that url in the environment as well
More info about OAuth can be found here
So we can go ahead and create a helper method for fetching the JWT token

const fetchJwtToken = function() {
   return new Promise ((resolve, reject) => {

      // VCAP variables containing the oauth credentials
      const UAA = CREDENTIALS.uaa
      const OA_CLIENTID = UAA.clientid; 
      const OA_SECRET = UAA.clientsecret;
      const OA_ENDPOINT = UAA.url;
      
      const options = {
         host:  OA_ENDPOINT.replace('https://', ''),
         path: '/oauth/token?grant_type=client_credentials&response_type=token',
         headers: {
            Authorization: "Basic " + Buffer.from(OA_CLIENTID + ':' + OA_SECRET).toString("base64")
         }
      }

      https.get(options, res => {
      . . .

The response is a JSON-structured string containing several properties, so we can get the JWT token as follows:

const responseAsJson = JSON.parse(response)
const jwtToken = responseAsJson.access_token            

So here it is, the token
Now can call the REST api to update the status

2.4.2. Update status

Calling the REST endpoint to change the status of a job run is all the same like realized above, just one small difference:
The authentication has to be done with OAuth 2.0 instead of Basic Authentication
We have to send the JWT token in the “Authorization” header as follows:

headers: {
   Authorization: 'Bearer ' + jwtToken

See Appendix for the full code

3. Test

Deploy the application to your account in SAP Cloud Platform
Create a job and enter the action URL similar like this:

https://longrun.cfapps….hana.ondemand.com/runjob

Let it run and quickly navigate to view the run log.
You can see that the endpoint has responded and that Jobscheduler has set a yellow status to indicate that the endpoint has responded but the real status of the long-running operation is not clear yet
Note:
If you don’t see the yellow status, you have to be more quick

After our application has invoked the updateStatus-endpoint of jobschedulers REST api, you can see that the yellow status in the run log has changed accordingly.

To test a negative result, create a job and enter the following action URL;
https://longrun.cfapps…..hana.ondemand.com/runjob?doFail=true
It will show the expected error in the log (like coded by us):

Note:
But what if the long-running operation NEVER ends?
What if the endpoint fails while trying to set the status?
Jobscheduler needs to be aware of that, there must be a fallback.
And yes, in such cases, Jobscheduler has (another) built-in timeout for async jobs

Summary

Any job which runs more than 15 seconds is considered long-running
The endpoint of your application has to respond immediately with status 202
Once the long-running operation has finished, your app has to update the status of the jobscheduler
To update the status, you have to do REST request with PUT and required info
The required info can be found in headers and app-environment
Authentication for REST api: basic auth in lite, oauth in prod

The configuration of a job in Jobscheduler is same as usual

Appendix 1: HTTP Status Code 202

For your convenience, I’ve copied the definition from here:
https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html

10.2.3 202 Accepted

The request has been accepted for processing, but the processing has not been completed. The request might or might not eventually be acted upon, as it might be disallowed when processing actually takes place. There is no facility for re-sending a status code from an asynchronous operation such as this.

The 202 response is intentionally non-committal. Its purpose is to allow a server to accept a request for some other process (perhaps a batch-oriented process that is only run once per day) without requiring that the user agent’s connection to the server persist until the process is completed. The entity returned with this response SHOULD include an indication of the request’s current status and either a pointer to a status monitor or some estimate of when the user can expect the request to be fulfilled.

Appendix 2: Full sample code for Trial

This project can be deployed to Trial account, it works with Jobscheduler instance created with service plan ‘lite’

manifest.yml

---
applications:
- name: longrun
  host: longrun
  memory: 128M
  buildpacks:
    - nodejs_buildpack
  services:
    - xsuaainstance
    - jobschedulerinstance

package.json

{
  "name": "myapp",
  "main": "server.js",
  "dependencies": {
    "express": "^4.16.3"
  }
}

server.js

const express = require('express')
const app = express()
const https = require('https');


// access credentials from environment variable
const VCAP_SERVICES = JSON.parse(process.env.VCAP_SERVICES);
const CREDENTIALS = VCAP_SERVICES.jobscheduler[0].credentials;
const USER = CREDENTIALS.user; 
const PWD = CREDENTIALS.password;

// our endpoint which is called by Jobscheduler 
app.get('/runjob', function(req, res){       
   // always return success status for async job
   res.status(202).send('Accepted async job, but long-running operation still running.');

   // afterwards the actual processing
   handleAsyncJob(req.headers, req.query.doFail)
});

// our server
app.listen(process.env.PORT || 3000, ()=>{})

// helper
const handleAsyncJob = function (headers, doFail) {
   doLongRunningOperation(doFail)
   .then((result) => {
      doUpdateStatus(headers, true, result.message)
   })
   .catch((error) => {
      doUpdateStatus(headers, false, error.message)
      .then(()=>{
         console.log("Successfully called REST api of Jobscheduler")
      }).catch((error)=>{
         console.log('Error occurred while calling REST api of Jobscheduler ' + error)
      })
   })
}

// our backend operation
const doLongRunningOperation = function (doFail) {
   return new Promise((resolve, reject)=>{
      const letItFail = doFail
      setTimeout(() => {     
         if(letItFail === 'true'){
            reject({message: 'Backend operation failed with error code 123'});
         }else{
            resolve({message: 'Successfully finished long-running backend operation'})   
         }
      }, 3000); // for testing purpose, 3 seconds are long-running enough
   })
}

// our helper method to set the status in Jobscheduler
const doUpdateStatus = function(headers, success, message){
   return new Promise((resolve, reject) => {
      jobId = headers['x-sap-job-id']
      scheduleId = headers['x-sap-job-schedule-id']
      runId = headers['x-sap-job-run-id']
      host = headers['x-sap-scheduler-host']
   
      const data = JSON.stringify({success: success, message: message}) 
      const options = {
         host:  host.replace('https://', ''),
         path:  `/scheduler/jobs/${jobId}/schedules/${scheduleId}/runs/${runId}`,
         method: 'PUT',
         headers: {
            'Content-Type': 'application/json',
            'Content-Length': data.length,
            Authorization: "Basic " + Buffer.from(USER + ':' + PWD).toString("base64")
         }
      };
      
      const req = https.request(options, (res) => {
         res.setEncoding('utf8')
         if (res.statusCode !== 200 && res.statusCode !== 201) {
           return reject(new Error(`Failed to update status of job ${jobId}`))
         }
   
         res.on('data', () => {
            resolve()
         })
      });
      
      req.on('error', (error) => {
         return reject({error: error})
      });
   
      req.write(data)
      req.end()   
   })
}

Appendix 3: Sample code for Prod

Use this javascript file for scenarios in productive account, where Jobscheduler instance is created with service plan ‘standard’
The difference to previous app is only in the code:
Calling the REST api with OAuth flow

server.js

const express = require('express')
const app = express()
const https = require('https');

// access credentials from environment variable (alternatively use xsenv)
const VCAP_SERVICES = JSON.parse(process.env.VCAP_SERVICES)
const CREDENTIALS = VCAP_SERVICES.jobscheduler[0].credentials
//oauth
const UAA = CREDENTIALS.uaa
const OA_CLIENTID = UAA.clientid; 
const OA_SECRET = UAA.clientsecret;
const OA_ENDPOINT = UAA.url;

// our endpoint which is called by Jobscheduler 
app.get('/runjob', function(req, res){       
   // always return success status for async job
   res.status(202).send('Accepted async job, but long-running operation still running.');

   // afterwards the actual processing
   handleAsyncJob(req.headers, req.query.doFail)
});

// our server
app.listen(process.env.PORT || 3000, ()=>{})

// helper
const handleAsyncJob = function (headers, doFail) { 
   doLongRunningOperation(doFail)
   .then((result) => {
      doUpdateStatus(headers, true, result.message)
   })
   .catch((error) => {
      doUpdateStatus(headers, false, error.message)
      .then(()=>{
         console.log("Successfully called REST api of Jobscheduler")
      }).catch((error)=>{
         console.log('Error occurred while calling REST api of Jobscheduler ' + error)
      })
   })
}

// our backend operation
const doLongRunningOperation = function (doFail) {
   return new Promise((resolve, reject)=>{
      const letItFail = doFail
      setTimeout(() => {     
         if(letItFail === 'true'){
            reject({message: 'Backend operation failed with error code 123'});
         }else{
            resolve({message: 'Successfully finished long-running backend operation'})   
         }
      }, 3000); // for testing purpose, 3 seconds are long-running enough
   })
}

// jwt token required for calling REST api
const fetchJwtToken = function() {
   return new Promise ((resolve, reject) => {
      const options = {
         host:  OA_ENDPOINT.replace('https://', ''),
         path: '/oauth/token?grant_type=client_credentials&response_type=token',
         headers: {
            Authorization: "Basic " + Buffer.from(OA_CLIENTID + ':' + OA_SECRET).toString("base64")
         }
      }

      https.get(options, res => {
         res.setEncoding('utf8')
         let response = ''
         res.on('data', chunk => {
           response += chunk
         })

         res.on('end', () => {
            try {
               const responseAsJson = JSON.parse(response)
               const jwtToken = responseAsJson.access_token            
               if (!jwtToken) {
                  return reject(new Error('Error while fetching JWT token'))
               }
               resolve(jwtToken)
            } catch (error) {
               return reject(new Error('Error while fetching JWT token'))               
            }
         })
      })
      .on("error", (error) => {
         console.log("Error: " + error.message);
         return reject({error: error})
      });
   })   
}

// our helper method to set the status in Jobscheduler
const doUpdateStatus = function(headers, success, message){
   return new Promise((resolve, reject) => {
      return fetchJwtToken()
         .then((jwtToken) => {

            const jobId = headers['x-sap-job-id']
            const scheduleId = headers['x-sap-job-schedule-id']
            const runId = headers['x-sap-job-run-id']
            const host = headers['x-sap-scheduler-host']
         
            const data = JSON.stringify({success: success, message: message})             
            const options = {
               host:  host.replace('https://', ''),
               path:  `/scheduler/jobs/${jobId}/schedules/${scheduleId}/runs/${runId}`,
               method: 'PUT',
               headers: {
                  'Content-Type': 'application/json',
                  'Content-Length': data.length,
                  Authorization: 'Bearer ' + jwtToken
               }
            }
            
            const req = https.request(options, (res) => {
               res.setEncoding('utf8')
               const status = res.statusCode 
               if (status !== 200 && status !== 201) {
                  return reject(new Error(`Failed to update status of job ${jobId}. Error: ${status} - ${res.statusMessage}`))
               }
         
               res.on('data', () => {
                  resolve()
               })
            });
            
            req.on('error', (error) => {
               return reject({error: error})
            });
         
            req.write(data)
            req.end()   
      })
      .catch((error) => {
         console.log('ERROR: failed to fetch JWT token: ' + error)
         reject(error)
      })
   })
}

 

Assigned Tags

      17 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Fabiano Rosa
      Fabiano Rosa

      Hi Carlos, great blog post, thanks for your contribution!

      I'm facing some challenges with SCP Job Scheduler now as I have a similar scenario with async calls inside the node.js and I need to update the status of the job in SCP Job Scheduler.

      But in my case, I'm using TASK instead of JOB, with Node.js, and I need to know how the get the information from the header (x-sap-job-id, x-sap-job-schedule-id, x-sap-job-run-id, x-sap-scheduler-host) that you described, but in my case the Task is scheduled as a node.js (ex: node task.js) and I don't have REST endpoints inside the node.js file to receive the header.

      Do you know how I can get these parameters with Tasks?

       

      Regards,

      Fabiano Rosa

       

      Author's profile photo Carlos Roggan
      Carlos Roggan
      Blog Post Author

      ​Hello Fabiano Rosa

      sorry for the late reply (I had to check with the experts)
      ​In case of Task, Jobscheduler will set the status automatically. It is not possible to set the status from the node.js code.
      ​So in this case it is different from what is done in case of async "Jobs"
      ​Kind Regards,
      ​Carlos
      And thanks very much for the nice feedback! 😉

      Author's profile photo Fabiano Rosa
      Fabiano Rosa

      Hi Carlos, thanks for checked it internally in SAP. I already moved from Task to Job, and it worked!

      Also, I validated the Cedric's approach from this blog post and worked as expect with XSUAA:

      https://blogs.sap.com/2020/10/01/application-to-multiple-xsuaa-services-replace-whitelisting-of-sap_jwt_trust_acl/

      Regards,

      Fabiano Rosa

      Author's profile photo Carlos Roggan
      Carlos Roggan
      Blog Post Author

      Hi Fabiano Rosa  Great !!

      Thanks, good to know

      Author's profile photo Jason Muzzy
      Jason Muzzy

      Hi Carlos Roggan and thank you for such a great blog series!  I wonder, do you have an example of using CAP to work with a long-running job?  The event handlers for the "on" event don't pass in the res object, so I'm not sure how to return a 202.

      Additionally, is it possible to leverage the @sap/jobs-client package to reduce the amount of coding necessary?  Something like the example shown in this GitHub repo?

      Author's profile photo Carlos Roggan
      Carlos Roggan
      Blog Post Author

      Hi Jason, strange, I thought I had replied to your comment, sorry ;(
      Anyways, I'm not familiar with CAP, but I'm pretty sure that there is a way to get hold of the internal context objects.
      About the job scheduler client, yes, sure it is possible to use it.
      I've used it in some other blog post.
      Typically, I'm avoiding the use of helper libs in the samples.
      Reason is that the libs might get deprecated or updated and then, after some time, the sample code wouldn't work anymore.
      Furthermore, I believe native code makes more clear what is happening.
      After understanding, the use of helpers is obviously useful
      But you're right, in case of Jobscheduler, the client lib is part of the product 😉

      Author's profile photo Gregor Wolf
      Gregor Wolf

      Hi Jason,

      you can checkout my example srv/admin-service.js#L177 where I use the @sap/jobs-client package in a CAP project.

      CU
      Gregor

      Author's profile photo Jia-Min Seah
      Jia-Min Seah

      Hi Jason Muzzy , did you find a way to return a 202 in the event handlers "on" event?

      Best Regards,

      Jia-Min

      Author's profile photo Jason Muzzy
      Jason Muzzy

      Yes, in my "on" handler I used this code:

                  // Respond with 202 so the job scheduling service knows that the job has started asynchronously
                  req.res.set("Content-Type", "text/plain");
                  req.res.status(202).send("ACCEPTED"); // 202 not 200
      Author's profile photo Jia-Min Seah
      Jia-Min Seah

      Thank you Jason Muzzy!

      Author's profile photo Mustafa Bensan
      Mustafa Bensan

      Hi Carlos Roggan,

      In this example, a node app is used to simulate a long running job by using a timer.  However, in a real world scenario, we may call an S/4HANA Cloud API from this node app to perform a mass data upload for example, in which case the call to the S/4HANA Cloud API may take a long time to process, resulting in a timeout in the node app.  How would we address this to ensure that the long running call to the S/4HANA Cloud API actually completes successfully?

      Regards,

      Mustafa.

      Author's profile photo Carlos Roggan
      Carlos Roggan
      Blog Post Author

      Hi Mustafa Bensan ,
      From Jobscheduler perspective, it gives a means to decouple the job trigger from the bound app. So the Jobscheduler triggers the job and then does not wait.
      Instead, it is the task of the app to notify the Jobscheduler after the long-running job is completed.
      It is up to the app to decide when and how to notify the Jobscheduler.
      If the app itself cannot handle long-running long-third-party-tasks, because too long, then it needs to think about workarounds, like controlling the timeout-settings of app, or e.g. polling for the status of the hana-process?
      Kind Regards,
      Carlos

      Author's profile photo Mustafa Bensan
      Mustafa Bensan

      Okay, thanks Carlos.  That makes sense.

      Author's profile photo Carlos Roggan
      Carlos Roggan
      Blog Post Author

      BTW, on Jobscheduler-side, there's also a configuration setting related to timeout for async jobs, on the menu, configuration screen. Just for your info, in case you need it

      Author's profile photo Mustafa Bensan
      Mustafa Bensan

      Thanks. I'll check it out.

      Author's profile photo Soni Deepak
      Soni Deepak

      Thanks Carlos for the document. It is really helpful for us.

      Only one small correction is required in the PUT method of the job status update. Payload should have runId added

      "{ success: success, message: message, runId: runId }"

      Author's profile photo Carlos Roggan
      Carlos Roggan
      Blog Post Author

      Hello Soni Deepak, 
      Thank you for the feedback, I'm glad that this has been helpful for you!

      Regarding your proposal, I'm not fully getting it.
      The runId is already contained in the URL, together with the scheduleId.
      Therefore, it is not required in the payload of the body of the PUT request.
      Did you face any issue, which you could solve by adding it? This would be surprising...hum
      The documentation for Update Run Log describes the properties of the payload.

      Kind Regards,

      Carlos