cloudtasker 0.10.rc6 → 0.10.rc7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 796e69d0470947fe0af6ea530cfd6c5b69e95b6a1be42b307e84b27dd9246198
4
- data.tar.gz: 5c51dd25b3033546a7115d83d638123e80a9ee26fb74845b88a02247edb39461
3
+ metadata.gz: 7c5440326c0fa695e469a341d58b976bd4027179440a607ce34c15c9dd4d697e
4
+ data.tar.gz: 671c2bcf90c199c68136a14efa62410d17e3a850dad1e6b5bf6a02b083169d75
5
5
  SHA512:
6
- metadata.gz: 134e6d344e75a9500850b135ea215c2b64d31cf366892acc0aab1b37c47e758dfa7e2bd00015ec6d8bb47556aba002bb578f2365ea34196d3343b24cee0f2156
7
- data.tar.gz: 73bd3cbd91fa1938b97df2cd7f5125a3f90205fb31b5d12efe5b64c4c0aa03e74f6f83cd8c9267b95b5553be93395a45db4f365bbb49b5623a97bf982bf9d2d7
6
+ metadata.gz: f6adad797eedb6ff0d589ea2dfa6b109709f629ad82cf64e9755e7da235b4a4fb902d4c48794981dcd527a6933196686a72fce46b2572912fd84b1a3396bcd33
7
+ data.tar.gz: 78fa73bafda04c16b46bed2bd8930e5e5ef3badf046f2e665b41d156e9644c20468788a0ffa173fcb6585bdb24a1bdcff599be11f41a5420c1302747e209bc52
@@ -40,4 +40,7 @@ Style/Documentation:
40
40
  - 'spec/**/*'
41
41
 
42
42
  Metrics/ParameterLists:
43
- CountKeywordArgs: false
43
+ CountKeywordArgs: false
44
+
45
+ RSpec/MessageSpies:
46
+ Enabled: false
data/README.md CHANGED
@@ -6,11 +6,11 @@ Background jobs for Ruby using Google Cloud Tasks.
6
6
 
7
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.
8
8
 
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.
9
+ Cloudtasker is particularly suited for serverless applications only responding to HTTP requests and where running a dedicated job processing server 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.
10
10
 
11
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).
12
12
 
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.
13
+ A local processing server is also available for development. This local server processes jobs in lieu of Cloud Tasks and allows you to work offline.
14
14
 
15
15
  ## Summary
16
16
 
@@ -34,7 +34,11 @@ A local processing server is also available in development. This local server pr
34
34
  1. [HTTP Error codes](#http-error-codes)
35
35
  2. [Error callbacks](#error-callbacks)
36
36
  3. [Max retries](#max-retries)
37
- 10. [Best practices building workers](#best-practices-building-workers)
37
+ 10. [Testing](#testing)
38
+ 1. [Test helper setup](#test-helper-setup)
39
+ 2. [In-memory queues](#in-memory-queues)
40
+ 3. [Unit tests](#unit-tests)
41
+ 11. [Best practices building workers](#best-practices-building-workers)
38
42
 
39
43
  ## Installation
40
44
 
@@ -48,7 +52,7 @@ And then execute:
48
52
 
49
53
  $ bundle
50
54
 
51
- Or install it yourself as:
55
+ Or install it yourself with:
52
56
 
53
57
  $ gem install cloudtasker
54
58
 
@@ -218,7 +222,7 @@ Cloudtasker.configure do |config|
218
222
 
219
223
  #
220
224
  # Specify how many retries are allowed on jobs. This number of retries excludes any
221
- # connectivity error that would be due to the application being down or unreachable.
225
+ # connectivity error due to the application being down or unreachable.
222
226
  #
223
227
  # Default: 25
224
228
  #
@@ -289,7 +293,7 @@ MyWorker.schedule(args: [arg1, arg2], time_at: Time.parse('2025-01-01 00:50:00Z'
289
293
  MyWorker.schedule(args: [arg1, arg2], time_in: 5 * 60, queue: 'critical')
290
294
  ```
291
295
 
292
- 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.
296
+ Cloudtasker also provides a helper for re-enqueuing jobs. Re-enqueued jobs keep the same job 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.
293
297
 
294
298
  E.g.
295
299
  ```ruby
@@ -474,7 +478,7 @@ The way contextual information is displayed depends on the logger itself. For ex
474
478
 
475
479
  Contextual information can be customised globally and locally using a log context_processor. By default the `Cloudtasker::WorkerLogger` is configured the following way:
476
480
  ```ruby
477
- Cloudtasker::WorkerLogger.log_context_processor = ->(worker) { worker.to_h.slice(:worker, :job_id, :job_meta) }
481
+ Cloudtasker::WorkerLogger.log_context_processor = ->(worker) { worker.to_h.slice(:worker, :job_id, :job_meta, :job_queue, :task_id) }
478
482
  ```
479
483
 
480
484
  You can decide to add a global identifier for your worker logs using the following:
@@ -482,7 +486,7 @@ You can decide to add a global identifier for your worker logs using the followi
482
486
  # config/initializers/cloudtasker.rb
483
487
 
484
488
  Cloudtasker::WorkerLogger.log_context_processor = lambda { |worker|
485
- worker.to_h.slice(:worker, :job_id, :job_meta).merge(app: 'my-app')
489
+ worker.to_h.slice(:worker, :job_id, :job_meta, :job_queue, :task_id).merge(app: 'my-app')
486
490
  }
487
491
  ```
488
492
 
@@ -520,7 +524,7 @@ The Google Cloud Task UI (GCP console) lists all the tasks pending/retrying and
520
524
 
521
525
  ## Error Handling
522
526
 
523
- Jobs failing will automatically return an HTTP error to Cloud Task and trigger a retry at a later time. The number of Cloud Task retries Cloud Task will depend on the configuration of your queue in Cloud Tasks.
527
+ Jobs failures will return an HTTP error to Cloud Task and trigger a retry at a later time. The number of Cloud Task retries depends on the configuration of your queue in Cloud Tasks.
524
528
 
525
529
  ### HTTP Error codes
526
530
 
@@ -528,6 +532,7 @@ Jobs failing will automatically return the following HTTP error code to Cloud Ta
528
532
 
529
533
  | Code | Description |
530
534
  |------|-------------|
535
+ | 204 | The job was processed successfully |
531
536
  | 205 | The job is dead and has been removed from the queue |
532
537
  | 404 | The job has specified an incorrect worker class. |
533
538
  | 422 | An error happened during the execution of the worker (`perform` method) |
@@ -566,7 +571,7 @@ By default jobs are retried 25 times - using an exponential backoff - before bei
566
571
 
567
572
  Note that the number of retries set on your Cloud Task queue should be many times higher than the number of retries configured in Cloudtasker because Cloud Task also includes failures to connect to your application. Ideally set the number of retries to `unlimited` in Cloud Tasks.
568
573
 
569
- **Note**: The `X-CloudTasks-TaskExecutionCount` header sent by Google Cloud Tasks and providing the number of retries outside of `HTTP 503` (instance not reachable) is currently bugged and remains at `0` all the time. Starting with `0.10.rc3` Cloudtasker uses the `X-CloudTasks-TaskRetryCount` header to detect the number of retries. This header includes `HTTP 503` errors which means that if your application is down at some point, jobs will fail and these failures will be counted toward the maximum number of retries. A [bug report](https://issuetracker.google.com/issues/154532072) has been raised with GCP to address this issue. Once fixed we will revert to using `X-CloudTasks-TaskExecutionCount` to avoid counting `HTTP 503` as job failures.
574
+ **Note**: The `X-CloudTasks-TaskExecutionCount` header sent by Google Cloud Tasks and providing the number of retries outside of `HTTP 503` (instance not reachable) is currently bugged and remains at `0` all the time. Starting with `v0.10.rc3` Cloudtasker uses the `X-CloudTasks-TaskRetryCount` header to detect the number of retries. This header includes `HTTP 503` errors which means that if your application is down at some point, jobs will fail and these failures will be counted toward the maximum number of retries. A [bug report](https://issuetracker.google.com/issues/154532072) has been raised with GCP to address this issue. Once fixed we will revert to using `X-CloudTasks-TaskExecutionCount` to avoid counting `HTTP 503` as job failures.
570
575
 
571
576
  E.g. Set max number of retries globally via the cloudtasker initializer.
572
577
  ```ruby
@@ -601,6 +606,97 @@ class SomeErrorWorker
601
606
  end
602
607
  ```
603
608
 
609
+ ## Testing
610
+ Cloudtasker provides several options to test your workers.
611
+
612
+ ### Test helper setup
613
+ Require `cloudtasker/testing` in your `rails_helper.rb` (Rspec Rails) or `spec_helper.rb` (Rspec) or test unit helper file then enable one of the three modes:
614
+
615
+ ```ruby
616
+ require 'cloudtasker/testing'
617
+
618
+ # Mode 1 (default): Push jobs to Google Cloud Tasks (env != development) or Redis (env == development)
619
+ Cloudtasker::Testing.enable!
620
+
621
+ # Mode 2: Push jobs to an in-memory queue. Jobs will not be processed until you call
622
+ # Cloudtasker::Worker.drain_all (process all jobs) or MyWorker.drain (process jobs for specific worker)
623
+ Cloudtasker::Testing.fake!
624
+
625
+ # Mode 3: Push jobs to an in-memory queue. Jobs will be processed immediately.
626
+ Cloudtasker::Testing.inline!
627
+ ```
628
+
629
+ You can query the current testing mode with:
630
+ ```ruby
631
+ Cloudtasker::Testing.enabled?
632
+ Cloudtasker::Testing.fake?
633
+ Cloudtasker::Testing.inline?
634
+ ```
635
+
636
+ Each testing mode accepts a block argument to temporarily switch to it:
637
+ ```ruby
638
+ # Enable fake mode for all tests
639
+ Cloudtasker::Testing.fake!
640
+
641
+ # Enable inline! mode temporarily for a given test
642
+ Cloudtasker.inline! do
643
+ MyWorker.perform_async(1,2)
644
+ end
645
+ ```
646
+
647
+ Note that extension middlewares - e.g. unique job, batch job etc. - run in test mode. You can disable middlewares in your tests by adding the following to your test helper:
648
+ ```ruby
649
+ # Remove all middlewares
650
+ Cloudtasker.configure do |c|
651
+ c.client_middleware.clear
652
+ c.server_middleware.clear
653
+ end
654
+
655
+ # Remove all unique job middlewares
656
+ Cloudtasker.configure do |c|
657
+ c.client_middleware.remove(Cloudtasker::UniqueJob::Middleware::Client)
658
+ c.server_middleware.remove(Cloudtasker::UniqueJob::Middleware::Server)
659
+ end
660
+ ```
661
+
662
+ ### In-memory queues
663
+ The `fake!` or `inline!` modes use in-memory queues, which can be queried and controlled using the following methods:
664
+
665
+ ```ruby
666
+ # Perform all jobs in queue
667
+ Cloudtasker::Worker.drain_all
668
+
669
+ # Remove all jobs in queue
670
+ Cloudtasker::Worker.clear_all
671
+
672
+ # Perform all jobs in queue for a specific worker type
673
+ MyWorker.drain
674
+
675
+ # Return the list of jobs in queue for a specific worker type
676
+ MyWorker.jobs
677
+ ```
678
+
679
+ ### Unit tests
680
+ Below are examples of rspec tests. It is assumed that `Cloudtasker::Testing.fake!` has been set in the test helper.
681
+
682
+ **Example 1**: Testing that a job is scheduled
683
+ ```ruby
684
+ describe 'worker scheduling'
685
+ subject(:enqueue_job) { MyWorker.perform_async(1,2) }
686
+
687
+ it { expect { enqueue_job }.to change(MyWorker.jobs, :size).by(1) }
688
+ end
689
+ ```
690
+
691
+ **Example 2**: Testing job execution logic
692
+ ```ruby
693
+ describe 'worker calls api'
694
+ subject { Cloudtasker::Testing.inline! { MyApiWorker.perform_async(1,2) } }
695
+
696
+ before { expect(MyApi).to receive(:fetch).and_return([]) }
697
+ it { is_expected.to be_truthy }
698
+ end
699
+ ```
604
700
 
605
701
  ## Best practices building workers
606
702
 
@@ -8,6 +8,15 @@ module Cloudtasker
8
8
  attr_accessor :job_retries
9
9
  attr_reader :id, :http_request, :schedule_time, :queue
10
10
 
11
+ #
12
+ # Return true if we are in test inline execution mode.
13
+ #
14
+ # @return [Boolean] True if inline mode enabled.
15
+ #
16
+ def self.inline_mode?
17
+ defined?(Cloudtasker::Testing) && Cloudtasker::Testing.inline?
18
+ end
19
+
11
20
  #
12
21
  # Return the task queue. A worker class name
13
22
  #
@@ -57,7 +66,7 @@ module Cloudtasker
57
66
  queue << task
58
67
 
59
68
  # Execute task immediately if in testing and inline mode enabled
60
- task.execute if defined?(Cloudtasker::Testing) && Cloudtasker::Testing.inline?
69
+ task.execute if inline_mode?
61
70
 
62
71
  task
63
72
  end
@@ -157,8 +166,9 @@ module Cloudtasker
157
166
  # Delete task
158
167
  self.class.delete(id)
159
168
  resp
160
- rescue StandardError
169
+ rescue StandardError => e
161
170
  self.job_retries += 1
171
+ raise(e) if self.class.inline_mode?
162
172
  end
163
173
 
164
174
  #
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Cloudtasker
4
- VERSION = '0.10.rc6'
4
+ VERSION = '0.10.rc7'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cloudtasker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.rc6
4
+ version: 0.10.rc7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Arnaud Lachaume
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-05-08 00:00:00.000000000 Z
11
+ date: 2020-05-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport