-1

Let's say I have 3 requests:

@app.route('/schedulejob1/')
def schedulejob1():
   for pram in prams1:
     deferred.defer(scheduletjob1Task,pram)
   return 'Running'
@app.route('/schedulejob1/')
def schedulejob1():
   for pram in prams2:
     deferred.defer(scheduletjob1Task,pram)
   return 'Running'
@app.route('/schedulejob1/')
def schedulejob1():
   for pram in prams3:
     deferred.defer(scheduletjob1Task,pram)
   return 'Running'

So my cron.yaml is

cron:
- description: Run schedule job1
  url: /schedulejob1/
  schedule: every 1 minutes
- description: Run schedule job2
  url: /schedulejob2/
  schedule: every 1 minutes
- description: Run schedule job3
  url: /schedulejob3/
  schedule: every 1 minutes

This approach will have 3 requests per minutes so that there is a big chance gae will dynamically create more instances which increase cost of (instances x frondend/backend hours).

My plan is to put 3 requests into a single request:

@app.route('/schedulejobs/')
def schedulejobs():
   for pram in prams1
     deferred.defer(scheduletjob1Task,pram)
   for pram in prams2  
     deferred.defer(scheduletjob2Task,pram)
   for pram in prams3 
     deferred.defer(scheduletjob3Task,pram)

and then

 cron:
    - description: Run schedule jobs
      url: /schedulejobs/

Does the second approach help to reduce the number of requests? therefore reduce cost?

My objective is to break a request into multiple task queues, each taskqueue run in less than 1 minutes, and it can fail permanently (or can retry 1 time). I also will put a a half of cron jobs on backend instances to take advantage s of 9 free hours.

If u have any idea how to reduce the instance hours, please advice.

John
  • 3,724
  • 10
  • 40
  • 80
  • Typically no. Google pricing for per instance per hour, so running 3 instances in 1 min ~= running 1 instances in 3 mins. – MT-FreeHK Aug 03 '18 at 09:07
  • There's no guarantee that App Engine won't start a new instance for one of the deferred tasks - we actually use this pattern to decrease the risk of a single instance exceeding its memory quota for big cron jobs. – snakecharmerb Aug 03 '18 at 09:11

1 Answers1

1

Yes, cron jobs with overlapping schedules can cause potentially unnecessary peaks of activity which can drive up the instance hours. Not only that you'd have an average of 3 requests per minute, you'd have 3 requests in your app's request queue simultaneously, at the beginning of that minute. If you use automatic/basic scaling configs GAE may decide to spawn additional instances to keep the request latency low, even if a single instance could easily handle the average of 3 requests per minute.

Side note: you already have the cron request coming in to trigger the job, you're kinda unnecessarily creating yet another (task queue) request to actually execute the job (thus amounting to 6 requests per minute in your case), why not just execute the job directly from the cron handler?

Now, if each of the requests takes close to ~1 minute to complete staggering them won't help much - @MatrixTai's comment applies.

But if the combined execution time of all 3 jobs is below 1 minute staggering them will help keep the costs down. Maybe a better approach in this case would be to not create 3 more requests - which has the disadvantage that you still need to carefully configure the delays to ensure they don't overlap, but simply sequentially execute the 3 jobs, without enqueueing new requests (this is what I do):

@app.route('/per_minute_jobs/')
def handle_per_minute_jobs():  # OK if the combined execution time is < 1 min
     execute_job_1()
     execute_job_2()
     execute_job_3()

With this approach you have exactly 1 request per minute, which, by itself, can't trigger spawning new instances as the other approaches did.

Now the combined job execution could take significantly longer than your typical (non-cron) request handling. If you handle them both in the same GAE service, you'll still end up with at least 2 instances running, even if overall your app has low enough average traffic that could apparently be handled with just one instance.

UPDATE:

OK, since you have to split the work in subsequent tasks, just use the deferred.defer()'s optional countdown or eta args to stagger those tasks in time and smooth those activity peaks.

Dan Cornilescu
  • 38,757
  • 12
  • 59
  • 95
  • Hi Dan, thank for your answer. I am sorry that I forgot 1 important part of code when I saw your sitenote. I can not excecute the job directly from the cron handler because it will take 10-20 minutes. Therefore, I have to break it into multiple small task queues. Each task queue will run less than 1 minute. So can u update your answer/advice? Thanks! – John Aug 03 '18 at 20:31
  • Updated. But if a once per minute request generates 10-20 minutes worth of work (if I understood correctly) then you have a kinda exploding situation, it'll be difficult to convince GAE to not spawn a lot of instances. You may need to come up with a "rate limiting" scheme for these tasks (maybe using a progressively longer/cumulative countdown delay for the deferred tasks?) And that'll work only if the per-minute jobs run just for a while, after which they stop creating new work items - to allow the already enqueued tasks to drain. – Dan Cornilescu Aug 04 '18 at 02:35
  • does deferred.defer ( push task ) run one by one or at the same time? – John Aug 04 '18 at 10:57
  • They run based on their eta, which is "immediately" by default, unless you specify `countdown` or `eta`. – Dan Cornilescu Aug 04 '18 at 11:59