cloudtasker 0.7.0 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.rubocop.yml +5 -0
  4. data/.travis.yml +3 -3
  5. data/CHANGELOG.md +41 -0
  6. data/README.md +145 -25
  7. data/_config.yml +1 -0
  8. data/app/controllers/cloudtasker/worker_controller.rb +21 -5
  9. data/cloudtasker.gemspec +2 -2
  10. data/docs/BATCH_JOBS.md +28 -3
  11. data/docs/CRON_JOBS.md +3 -1
  12. data/exe/cloudtasker +13 -1
  13. data/gemfiles/google_cloud_tasks_1.0.gemfile.lock +26 -9
  14. data/gemfiles/google_cloud_tasks_1.1.gemfile.lock +26 -9
  15. data/gemfiles/google_cloud_tasks_1.2.gemfile.lock +27 -10
  16. data/gemfiles/google_cloud_tasks_1.3.gemfile.lock +26 -9
  17. data/gemfiles/rails_5.2.gemfile.lock +28 -11
  18. data/gemfiles/rails_6.0.gemfile.lock +29 -12
  19. data/lib/cloudtasker.rb +1 -1
  20. data/lib/cloudtasker/backend/google_cloud_task.rb +65 -12
  21. data/lib/cloudtasker/backend/memory_task.rb +5 -3
  22. data/lib/cloudtasker/backend/redis_task.rb +24 -13
  23. data/lib/cloudtasker/batch/batch_progress.rb +11 -2
  24. data/lib/cloudtasker/batch/job.rb +18 -4
  25. data/lib/cloudtasker/cli.rb +6 -5
  26. data/lib/cloudtasker/cloud_task.rb +6 -2
  27. data/lib/cloudtasker/config.rb +33 -9
  28. data/lib/cloudtasker/cron/job.rb +2 -2
  29. data/lib/cloudtasker/cron/schedule.rb +26 -14
  30. data/lib/cloudtasker/local_server.rb +44 -22
  31. data/lib/cloudtasker/max_task_size_exceeded_error.rb +14 -0
  32. data/lib/cloudtasker/redis_client.rb +10 -7
  33. data/lib/cloudtasker/unique_job/job.rb +2 -2
  34. data/lib/cloudtasker/version.rb +1 -1
  35. data/lib/cloudtasker/worker.rb +45 -10
  36. data/lib/cloudtasker/worker_handler.rb +7 -5
  37. data/lib/cloudtasker/worker_logger.rb +1 -1
  38. data/lib/cloudtasker/worker_wrapper.rb +52 -0
  39. data/lib/tasks/setup_queue.rake +12 -2
  40. metadata +7 -6
  41. data/Gemfile.lock +0 -280
  42. data/lib/cloudtasker/railtie.rb +0 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 92abbab630f4e60c7ae8a6b2e249a5e19d49ad9730788381790463cfda136aea
4
- data.tar.gz: c71fcf022efd34d3bba1e11341583b18581d1ae93bcd128b769b2e871e5e76f6
3
+ metadata.gz: dc46ef6ccfab1437c91a90c3cf3e4ee7b401fe5b453d8e61c7d8af09607f6695
4
+ data.tar.gz: 4a8192b395ff86ff1dba2d06d677237273cef952533e41b199256db3bad2cc0d
5
5
  SHA512:
6
- metadata.gz: 4a0f52436da444c75530ceb49ac16ce4c76392383011774accb31418447b6d0b2677e1289cb5768be39bb51b2665b46f347e0496c98c9552bf8c1a82a324f6e1
7
- data.tar.gz: 5597abc655cef50010bbb38850cc26a5f440404aa8e3d0b645d49fe6bf34f4222d8402e1d61d3e99cf4a0093f7ccd66f561258a3b02595de7a7774cb21cda643
6
+ metadata.gz: 387f5202bb42b3903cf7cede2fcc84978f8ca0bf0e61eadd73d8e8fbf2d4c29ce5bb15caa749b22386ec3e5363936afb93f9600b6fd0886734909937512303bd
7
+ data.tar.gz: 6b3a4a77d35d1c885e623e7e737ee9008fd16e05568e3f5f2bc8e82b9ce39feecfcaf875c4b94c6dc94ecb0a68855e651759980488b1eb633bb5ca645f254c73
data/.gitignore CHANGED
@@ -12,5 +12,8 @@
12
12
  /spec/dummy/tmp/
13
13
  /tmp/
14
14
 
15
+ # Ignore lock files (e.g. Gemfile.lock)
16
+ *.lock
17
+
15
18
  # rspec failure tracking
16
19
  .rspec_status
@@ -1,5 +1,10 @@
1
1
  require: rubocop-rspec
2
2
 
3
+ AllCops:
4
+ Exclude:
5
+ - 'gemfiles/vendor/**/*'
6
+ - 'vendor/**/*'
7
+
3
8
  Metrics/ClassLength:
4
9
  Max: 150
5
10
 
@@ -2,11 +2,11 @@
2
2
  language: ruby
3
3
  cache: bundler
4
4
  rvm:
5
- - 2.3
6
- - 2.4
7
5
  - 2.5.5
6
+ services:
7
+ - redis-server
8
8
  before_install: gem install bundler -v 2.0.2
9
- before_script: rubocop
9
+ before_script: bundle exec rubocop
10
10
  gemfile:
11
11
  - gemfiles/google_cloud_tasks_1.0.gemfile
12
12
  - gemfiles/google_cloud_tasks_1.1.gemfile
@@ -1,5 +1,46 @@
1
1
  # Changelog
2
2
 
3
+ ## [v0.9.1](https://github.com/keypup-io/cloudtasker/tree/v0.9.1) (2020-02-11)
4
+
5
+ [Full Changelog](https://github.com/keypup-io/cloudtasker/compare/v0.9.0...v0.9.1)
6
+
7
+ **Fixed bugs:**
8
+ - Cloud Task: raise `Cloudtasker::MaxTaskSizeExceededError` if job payload exceeds 100 KB. This is mainly to have production parity in development when running the local processing server.
9
+
10
+ ## [v0.9.0](https://github.com/keypup-io/cloudtasker/tree/v0.9.0) (2020-01-23)
11
+
12
+ [Full Changelog](https://github.com/keypup-io/cloudtasker/compare/v0.8.2...v0.9.0)
13
+
14
+ **Fixed bugs:**
15
+ - Cloud Task: Base64 encode task body to support UTF-8 characters (e.g. emojis).
16
+ - Redis: Restrict to one connection (class level) to avoid too many DNS lookups
17
+
18
+ **Migration**
19
+ For Sinatra applications please update your Cloudtasker controller according to [this diff](https://github.com/keypup-io/cloudtasker/commit/311fa8f9beec91fbae012164a25b2ee6e261a2e4#diff-c2a0ea6c6e6c31c749d2e1acdc574f0f).
20
+
21
+ ## [v0.8.2](https://github.com/keypup-io/cloudtasker/tree/v0.8.2) (2019-12-05)
22
+
23
+ [Full Changelog](https://github.com/keypup-io/cloudtasker/compare/v0.8.1...v0.8.2)
24
+
25
+ **Fixed bugs:**
26
+ - Config: do not add processor host to `Rails.application.config.hosts` if originally empty.
27
+
28
+ ## [v0.8.1](https://github.com/keypup-io/cloudtasker/tree/v0.8.1) (2019-12-03)
29
+
30
+ [Full Changelog](https://github.com/keypup-io/cloudtasker/compare/v0.8.0...v0.8.1)
31
+
32
+ **Fixed bugs:**
33
+ - Local dev server: ensure job queue name is kept when taks is retried
34
+ - Rails/Controller: bypass Rails munge logic to preserve nil values inside job arguments.
35
+
36
+ ## [v0.8.0](https://github.com/keypup-io/cloudtasker/tree/v0.8.0) (2019-11-27)
37
+
38
+ [Full Changelog](https://github.com/keypup-io/cloudtasker/compare/v0.7.0...v0.8.0)
39
+
40
+ ## [v0.7.0](https://github.com/keypup-io/cloudtasker/tree/v0.7.0) (2019-11-25)
41
+
42
+ [Full Changelog](https://github.com/keypup-io/cloudtasker/compare/v0.6.0...v0.7.0)
43
+
3
44
  ## [v0.6.0](https://github.com/keypup-io/cloudtasker/tree/v0.6.0) (2019-11-25)
4
45
 
5
46
  [Full Changelog](https://github.com/keypup-io/cloudtasker/compare/v0.5.0...v0.6.0)
data/README.md CHANGED
@@ -1,14 +1,16 @@
1
+ [![Build Status](https://travis-ci.org/keypup-io/cloudtasker.svg?branch=master)](https://travis-ci.org/keypup-io/cloudtasker) [![Gem Version](https://badge.fury.io/rb/cloudtasker.svg)](https://badge.fury.io/rb/cloudtasker)
2
+
1
3
  # Cloudtasker
2
4
 
3
5
  Background jobs for Ruby using Google Cloud Tasks.
4
6
 
5
7
  Cloudtasker provides an easy to manage interface to Google Cloud Tasks for background job processing. Workers can be defined programmatically using the Cloudtasker DSL and enqueued for processing using a simple to use API.
6
8
 
7
- Cloudtasker is particularly suited for serverless applications only responding to HTTP requests and where running a dedicated job processing is not an option (e.g. deploy via [Cloud Run](https://cloud.google.com/run)). All jobs enqueued in Cloud Tasks via Cloudtasker eventually gets processed by your application via HTTP requests.
9
+ Cloudtasker is particularly suited for serverless applications only responding to HTTP requests and where running a dedicated job processing is not an option (e.g. deploy via [Cloud Run](https://cloud.google.com/run)). All jobs enqueued in Cloud Tasks via Cloudtasker eventually get processed by your application via HTTP requests.
8
10
 
9
11
  Cloudtasker also provides optional modules for running [cron jobs](docs/CRON_JOBS.md), [batch jobs](docs/BATCH_JOBS.md) and [unique jobs](docs/UNIQUE_JOBS.md).
10
12
 
11
- A local processing server is also available in development. This local server processes jobs in lieu of Cloud Tasks and allow you to work offline.
13
+ A local processing server is also available in development. This local server processes jobs in lieu of Cloud Tasks and allows you to work offline.
12
14
 
13
15
  ## Summary
14
16
 
@@ -18,18 +20,21 @@ A local processing server is also available in development. This local server pr
18
20
  1. [Cloud Tasks authentication & permissions](#cloud-tasks-authentication--permissions)
19
21
  2. [Cloudtasker initializer](#cloudtasker-initializer)
20
22
  4. [Enqueuing jobs](#enqueuing-jobs)
21
- 5. [Extensions](#extensions)
22
- 6. [Working locally](#working-locally)
23
+ 5. [Managing worker queues](#managing-worker-queues)
24
+ 1. [Creating queues](#creating-queues)
25
+ 2. [Assigning queues to workers](#assigning-queues-to-workers)
26
+ 6. [Extensions](#extensions)
27
+ 7. [Working locally](#working-locally)
23
28
  1. [Option 1: Cloudtasker local server](#option-1-cloudtasker-local-server)
24
29
  2. [Option 2: Using ngrok](#option-2-using-ngrok)
25
- 7. [Logging](#logging)
30
+ 8. [Logging](#logging)
26
31
  1. [Configuring a logger](#configuring-a-logger)
27
32
  2. [Logging context](#logging-context)
28
- 8. [Error Handling](#error-handling)
33
+ 9. [Error Handling](#error-handling)
29
34
  1. [HTTP Error codes](#http-error-codes)
30
35
  2. [Error callbacks](#error-callbacks)
31
36
  3. [Max retries](#max-retries)
32
- 9. [Best practices building workers](#best-practices-building-workers)
37
+ 10. [Best practices building workers](#best-practices-building-workers)
33
38
 
34
39
  ## Installation
35
40
 
@@ -104,7 +109,7 @@ Open a Rails console and enqueue some jobs
104
109
  DummyWorker.perform_async('foo')
105
110
 
106
111
  # Process job in 60 seconds
107
- DummyWorker.perform_in(10, 'foo')
112
+ DummyWorker.perform_in(60, 'foo')
108
113
  ```
109
114
 
110
115
  Your Rails logs should display the following:
@@ -157,13 +162,31 @@ Cloudtasker.configure do |config|
157
162
  # config.secret = 'some-long-token'
158
163
 
159
164
  #
160
- # Specify the details of your Google Cloud Task queue.
165
+ # Specify the details of your Google Cloud Task location.
161
166
  #
162
167
  # This not required in development using the Cloudtasker local server.
163
168
  #
164
169
  config.gcp_location_id = 'us-central1' # defaults to 'us-east1'
165
170
  config.gcp_project_id = 'my-gcp-project'
166
- config.gcp_queue_id = 'my-queue'
171
+
172
+ #
173
+ # Specify the namespace for your Cloud Task queues.
174
+ #
175
+ # The gem assumes that a least a default queue named 'my-app-default'
176
+ # exists in Cloud Tasks. You can create this default queue using the
177
+ # gcloud SDK or via the `rake cloudtasker:setup_queue` task if you use Rails.
178
+ #
179
+ # Workers can be scheduled on different queues. The name of the queue
180
+ # in Cloud Tasks is always assumed to be prefixed with the prefix below.
181
+ #
182
+ # E.g.
183
+ # Setting `cloudtasker_options queue: 'critical'` on a worker means that
184
+ # the worker will be pushed to 'my-app-critical' in Cloud Tasks.
185
+ #
186
+ # Specific queues can be created in Cloud Tasks using the gcloud SDK or
187
+ # via the `rake cloudtasker:setup_queue name=<queue_name>` task.
188
+ #
189
+ config.gcp_queue_prefix = 'my-app'
167
190
 
168
191
  #
169
192
  # Specify the publicly accessible host for your application
@@ -215,7 +238,7 @@ Cloudtasker.configure do |config|
215
238
  end
216
239
  ```
217
240
 
218
- If your queue does not exist in Cloud Tasks you should [create it using the gcloud sdk](https://cloud.google.com/tasks/docs/creating-queues).
241
+ If the default queue `<gcp_queue_prefix>-default` does not exist in Cloud Tasks you should [create it using the gcloud sdk](https://cloud.google.com/tasks/docs/creating-queues).
219
242
 
220
243
  Alternatively with Rails you can simply run the following rake task if you have queue admin permissions (`cloudtasks.queues.get` and `cloudtasks.queues.create`).
221
244
  ```bash
@@ -235,10 +258,15 @@ MyWorker.perform_in(5 * 60, arg1, arg2)
235
258
  # or with Rails
236
259
  MyWorker.perform_in(5.minutes, arg1, arg2)
237
260
 
238
- # Worker will be processed on specific date
261
+ # Worker will be processed on a specific date
239
262
  MyWorker.perform_at(Time.parse('2025-01-01 00:50:00Z'), arg1, arg2)
240
263
  # also with Rails
241
264
  MyWorker.perform_at(3.days.from_now, arg1, arg2)
265
+
266
+ # With all options, including which queue to run the worker on.
267
+ MyWorker.schedule(args: [arg1, arg2], time_at: Time.parse('2025-01-01 00:50:00Z'), queue: 'critical')
268
+ # or
269
+ MyWorker.schedule(args: [arg1, arg2], time_in: 5 * 60, queue: 'critical')
242
270
  ```
243
271
 
244
272
  Cloudtasker also provides a helper for re-enqueuing jobs. Re-enqueued jobs keep the same worker id. Some middlewares may rely on this to track the fact that that a job didn't actually complete (e.g. Cloustasker batch). This is optional and you can always fallback to using exception management (raise an error) to retry/re-enqueue jobs.
@@ -262,6 +290,52 @@ class FetchResourceWorker
262
290
  end
263
291
  ```
264
292
 
293
+ ## Managing worker queues
294
+
295
+ Cloudtasker allows you to manage several queues and distribute workers across them based on job priority. By default jobs are pushed to the `default` queue, which is `<gcp_queue_prefix>-default` in Cloud Tasks.
296
+
297
+ ### Creating queues
298
+
299
+ More queues can be created using the gcloud sdk or the `cloudtasker:setup_queue` rake task.
300
+
301
+ E.g. Create a `critical` queue with a concurrency of 5 via the gcloud SDK
302
+ ```bash
303
+ gcloud tasks queues create <gcp_queue_prefix>-critical --max-concurrent-dispatches=5
304
+ ```
305
+
306
+ E.g. Create a `real-time` queue with a concurrency of 15 via the rake task (Rails only)
307
+ ```bash
308
+ rake cloudtasker:setup_queue name=real-time concurrency=15
309
+ ```
310
+
311
+ When running the Cloudtasker local processing server, you can specify the concurrency for each queue using:
312
+ ```bash
313
+ cloudtasker -q critical,5 -q important,4 -q default,3
314
+ ```
315
+
316
+ ### Assigning queues to workers
317
+
318
+ Queues can be assigned to workers via the `cloudtasker_options` directive on the worker class:
319
+
320
+ ```ruby
321
+ # app/workers/critical_worker.rb
322
+
323
+ class CriticalWorker
324
+ include Cloudtasker::Worker
325
+
326
+ cloudtasker_options queue: :critical
327
+
328
+ def perform(some_arg)
329
+ logger.info("This is a critical job run with arg=#{some_arg}.")
330
+ end
331
+ end
332
+ ```
333
+
334
+ Queues can also be assigned at runtime when scheduling a job:
335
+ ```ruby
336
+ CriticalWorker.schedule(args: [1], queue: :important)
337
+ ```
338
+
265
339
  ## Extensions
266
340
  Cloudtasker comes with three optional features:
267
341
  - Cron Jobs [[docs](docs/CRON_JOBS.md)]: Run jobs at fixed intervals.
@@ -277,7 +351,7 @@ When working locally on your application it is usually not possible to have a pu
277
351
  ### Option 1: Cloudtasker local server
278
352
  The Cloudtasker local server is a ruby daemon that looks for jobs pushed to Redis and sends them to your application via HTTP POST requests. The server mimics the way Google Cloud Tasks works, but locally!
279
353
 
280
- You can configure your applicatiion to use the Cloudtasker local server using the following initializer:
354
+ You can configure your application to use the Cloudtasker local server using the following initializer:
281
355
  ```ruby
282
356
  # config/initializers/cloudtasker.rb
283
357
 
@@ -299,15 +373,20 @@ bundle exec cloudtasker
299
373
  You can as well define a Procfile to manage the cloudtasker process via foreman. Then use `foreman start` to launch both your Rails server and the Cloudtasker local server.
300
374
  ```yaml
301
375
  # Procfile
302
- web: rails s
303
- worker: cloudtasker
376
+ web: bundle exec rails s
377
+ worker: bundle exec cloudtasker
378
+ ```
379
+
380
+ Note that the local development server runs with `5` concurrent threads by default. You can tune the number of threads per queue by running `cloudtasker` the following options:
381
+ ```bash
382
+ bundle exec cloudtasker -q critical,5 -q important,4 -q default,3
304
383
  ```
305
384
 
306
385
  ### Option 2: Using ngrok
307
386
 
308
387
  Want to test your application end to end with Google Cloud Task? Then [ngrok](https://ngrok.io) is the way to go.
309
388
 
310
- First start your ngrok tunnel and take note of the :
389
+ First start your ngrok tunnel:
311
390
  ```bash
312
391
  ngrok http 3000
313
392
  ```
@@ -318,9 +397,9 @@ Take note of your ngrok domain and configure Cloudtasker to use Google Cloud Tas
318
397
 
319
398
  Cloudtasker.configure do |config|
320
399
  # Specify your Google Cloud Task queue configuration
321
- # config.gcp_location_id = 'us-central1'
322
- # config.gcp_project_id = 'my-gcp-project'
323
- # config.gcp_queue_id = 'my-queue'
400
+ config.gcp_location_id = 'us-central1'
401
+ config.gcp_project_id = 'my-gcp-project'
402
+ config.gcp_queue_prefix = 'my-app'
324
403
 
325
404
  # Use your ngrok domain as the processor host
326
405
  config.processor_host = 'https://your-tunnel-id.ngrok.io'
@@ -332,7 +411,7 @@ end
332
411
 
333
412
  Finally start Rails to accept jobs from Google Cloud Tasks
334
413
  ```bash
335
- rails s
414
+ bundle exec rails s
336
415
  ```
337
416
 
338
417
  ## Logging
@@ -341,7 +420,7 @@ There are several options available to configure logging and logging context.
341
420
  ### Configuring a logger
342
421
  Cloudtasker uses `Rails.logger` if Rails is available and falls back on a plain ruby logger `Logger.new(STDOUT)` if not.
343
422
 
344
- It is also possible to configure your own logger. For example you can setup Cloudtasker with [semantic_logger](http://rocketjob.github.io/semantic_logger) by doing the following your initializer:
423
+ It is also possible to configure your own logger. For example you can setup Cloudtasker with [semantic_logger](http://rocketjob.github.io/semantic_logger) by doing the following in your initializer:
345
424
  ```ruby
346
425
  # config/initializers/cloudtasker.rb
347
426
 
@@ -387,7 +466,7 @@ Cloudtasker::WorkerLogger.log_context_processor = lambda { |worker|
387
466
  }
388
467
  ```
389
468
 
390
- You could also decide to log all available context (including arguments passed to perform) for specific workers only:
469
+ You could also decide to log all available context - including arguments passed to `perform` - for specific workers only:
391
470
  ```ruby
392
471
  # app/workers/full_context_worker.rb
393
472
 
@@ -402,7 +481,7 @@ class FullContextWorker
402
481
  end
403
482
  ```
404
483
 
405
- See the [Cloudtasker::Worker class](blob/master/lib/cloudtasker/worker.rb) for more information on attributes available to be logged in your `log_context_processor` proc.
484
+ See the [Cloudtasker::Worker class](lib/cloudtasker/worker.rb) for more information on attributes available to be logged in your `log_context_processor` proc.
406
485
 
407
486
  ## Error Handling
408
487
 
@@ -534,7 +613,7 @@ MyWorker.new.perform({ 'foo' => 'bar', 'baz' => { 'key' => 'value' } })
534
613
  ```
535
614
 
536
615
  ### Be careful with default arguments
537
- Default arguments passed to the `perform` method are not actually considered as job arguments. Default arguments will therefore be ignored in contextual logging and by extensions relying on arguments such as the `unique-job` extension.
616
+ Default arguments passed to the `perform` method are not actually considered as job arguments. Default arguments will therefore be ignored in contextual logging and by extensions relying on arguments such as the [unique job](docs/UNIQUE_JOBS.md) extension.
538
617
 
539
618
  Consider the following worker:
540
619
  ```ruby
@@ -556,7 +635,9 @@ If you enqueue this worker by omitting the second argument `MyWorker.perform_asy
556
635
  - The `time_at` argument will be ignored by the `unique-job` extension, meaning that job uniqueness will be only based on the `user_id` argument.
557
636
 
558
637
  ### Handling big job payloads
559
- Keep in mind that jobs are pushed to Google Cloud Tasks via API and then delivered to your application via API as well. Therefore any excessive job payload will slow down the enqueuing of jobs and create additional processing when receiving the job.
638
+ Google Cloud Tasks enforces a limit of 100 KB for job payloads. Taking into accounts Cloudtasker authentication headers and meta information this leave ~85 KB of free space for JSONified job arguments.
639
+
640
+ Any excessive job payload (> 100 KB) will raise a `Cloudtasker::MaxTaskSizeExceededError`, both in production and development mode.
560
641
 
561
642
  If you feel that a job payload is going to get big, prefer to store the payload using a datastore (e.g. Redis) and pass a reference to the job to retrieve the payload inside your job `perform` method.
562
643
 
@@ -585,6 +666,45 @@ Rails.cache.write(payload_id, data)
585
666
  BigPayloadWorker.perform_async(payload_id)
586
667
  ```
587
668
 
669
+ ### Sizing the concurrency of your queues
670
+
671
+ When defining the max concurrency of your queues (`max_concurrent_dispatches` in Cloud Tasks) you must keep in mind the maximum number of threads that your application provides. Otherwise your application threads may eventually get exhausted and your users will experience outages if all your web threads are busy running jobs.
672
+
673
+ #### With server based applications
674
+
675
+ Let's consider an application deployed in production with 3 instances, each having `RAILS_MAX_THREADS` set to `20`. This gives us a total of `60` threads available.
676
+
677
+ Now let's say that we distribute jobs across two queues: `default` and `critical`. We can set the concurrency of each queue depending on the profile of the application:
678
+
679
+ E.g. 1: The application serves requests from web users and runs backgrounds jobs in a balanced way
680
+ ```
681
+ concurrency for default queue: 20
682
+ concurrency for critical queue: 10
683
+
684
+ Total threads consumed by jobs at most: 30
685
+ Total threads always available to web users at worst: 30
686
+ ```
687
+
688
+ E.g. 2: The application is a micro-service API heavily focused on running jobs (e.g. data processing)
689
+ ```
690
+ concurrency for default queue: 35
691
+ concurrency for critical queue: 15
692
+
693
+ Total threads consumed by jobs at most: 50
694
+ Total threads always available to API clients at worst: 10
695
+ ```
696
+
697
+ Also always ensure that your total number of threads does not exceed the available number of database connections (if you use any).
698
+
699
+ #### With serverless applications
700
+
701
+ In a serverless context your application will be scaled up/down based on traffic. When we say 'traffic' this includes requests from Cloud Tasks to run jobs.
702
+
703
+ Because your application is auto-scaled - and assuming you haven't set a maximum - your job processing capacity if theoretically unlimited. The main limiting factor in a serverless context becomes external constraints such as the number of database connections available.
704
+
705
+ To size the concurrency of your queues you should therefore take the most limiting factor - which is often the database connection pool size of relational databases - and use the calculations of the previous section with this limiting factor as the capping parameter instead of threads.
706
+
707
+
588
708
  ## Development
589
709
 
590
710
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -0,0 +1 @@
1
+ theme: jekyll-theme-slate
@@ -16,11 +16,6 @@ module Cloudtasker
16
16
  # Run a worker from a Cloud Task payload
17
17
  #
18
18
  def run
19
- # Build payload
20
- payload = request.params
21
- .slice(:worker, :job_id, :job_args, :job_meta)
22
- .merge(job_retries: job_retries)
23
-
24
19
  # Process payload
25
20
  WorkerHandler.execute_from_payload!(payload)
26
21
  head :no_content
@@ -39,6 +34,27 @@ module Cloudtasker
39
34
 
40
35
  private
41
36
 
37
+ #
38
+ # Parse the request body and return the actual job
39
+ # payload.
40
+ #
41
+ # @return [Hash] The job payload
42
+ #
43
+ def payload
44
+ @payload ||= begin
45
+ # Get raw body
46
+ content = request.body.read
47
+
48
+ # Decode content if the body is Base64 encoded
49
+ if request.headers[Cloudtasker::Config::ENCODING_HEADER].to_s.downcase == 'base64'
50
+ content = Base64.decode64(content)
51
+ end
52
+
53
+ # Return content parsed as JSON and add job retries count
54
+ JSON.parse(content).merge(job_retries: job_retries)
55
+ end
56
+ end
57
+
42
58
  #
43
59
  # Extract the number of times this task failed at runtime.
44
60
  #
@@ -10,8 +10,8 @@ Gem::Specification.new do |spec|
10
10
  spec.authors = ['Arnaud Lachaume']
11
11
  spec.email = ['arnaud.lachaume@keypup.io']
12
12
 
13
- spec.summary = 'Background jobs for Ruby using Google Cloud Tasks (alpha)'
14
- spec.description = 'Background jobs for Ruby using Google Cloud Tasks (alpha)'
13
+ spec.summary = 'Background jobs for Ruby using Google Cloud Tasks (beta)'
14
+ spec.description = 'Background jobs for Ruby using Google Cloud Tasks (beta)'
15
15
  spec.homepage = 'https://github.com/keypup-io/cloudtasker'
16
16
  spec.license = 'MIT'
17
17