cloudtasker 0.10.rc2 → 0.10.rc3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -4
- data/README.md +7 -2
- data/gemfiles/google_cloud_tasks_1.0.gemfile +3 -5
- data/gemfiles/google_cloud_tasks_1.1.gemfile +3 -5
- data/gemfiles/google_cloud_tasks_1.2.gemfile +3 -5
- data/gemfiles/google_cloud_tasks_1.3.gemfile +3 -5
- data/gemfiles/rails_5.2.gemfile +3 -5
- data/gemfiles/rails_6.0.gemfile +3 -5
- data/lib/cloudtasker/backend/redis_task.rb +1 -1
- data/lib/cloudtasker/config.rb +20 -2
- data/lib/cloudtasker/local_server.rb +4 -1
- data/lib/cloudtasker/version.rb +1 -1
- data/lib/cloudtasker/worker.rb +54 -14
- data/lib/cloudtasker/worker_logger.rb +3 -2
- metadata +2 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 4b2692b6e4fe2fbd98cf70fb2fe7a2027992d83be63188108113d523cf410160
         | 
| 4 | 
            +
              data.tar.gz: 892910f2b59d84e2f516478926fefb63bd913bb4db4507133cde614eca933482
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 3f7a4bc94036af569901cde01fe7596acc9eea3019d9216db033de2833eb471668f231c6dbf00ac231247f599291fdd0bec0ed4eb700cf06521b01ea2a753739
         | 
| 7 | 
            +
              data.tar.gz: f37e39c7ce6fff9a755a457694f5b46aca1c64b9a46b8b1fa74e10208e19c6c6beffc7e5c4e50a74040d1cca8032088478be2168f80f17e2be4d1845d4d69306
         | 
    
        data/.rubocop.yml
    CHANGED
    
    | @@ -8,6 +8,9 @@ AllCops: | |
| 8 8 | 
             
            Metrics/ClassLength:
         | 
| 9 9 | 
             
              Max: 150
         | 
| 10 10 |  | 
| 11 | 
            +
            Metrics/ModuleLength:
         | 
| 12 | 
            +
              Max: 150
         | 
| 13 | 
            +
             | 
| 11 14 | 
             
            Metrics/AbcSize:
         | 
| 12 15 | 
             
              Max: 20
         | 
| 13 16 |  | 
| @@ -34,7 +37,4 @@ Metrics/BlockLength: | |
| 34 37 | 
             
            Style/Documentation:
         | 
| 35 38 | 
             
              Exclude:
         | 
| 36 39 | 
             
                - 'examples/**/*'
         | 
| 37 | 
            -
                - 'spec/**/*'
         | 
| 38 | 
            -
             | 
| 39 | 
            -
            RSpec/RepeatedExampleGroupBody:
         | 
| 40 | 
            -
              Enabled: false
         | 
| 40 | 
            +
                - 'spec/**/*'
         | 
    
        data/README.md
    CHANGED
    
    | @@ -246,6 +246,8 @@ Cloudtasker.configure do |config| | |
| 246 246 | 
             
              # You can set this configuration parameter to a KB value if you want to store jobs
         | 
| 247 247 | 
             
              # args in redis only if the JSONified arguments payload exceeds that threshold.
         | 
| 248 248 | 
             
              #
         | 
| 249 | 
            +
              # Supported since: v0.10.rc1
         | 
| 250 | 
            +
              #
         | 
| 249 251 | 
             
              # Default: false
         | 
| 250 252 | 
             
              #
         | 
| 251 253 | 
             
              # Store all job payloads in Redis:
         | 
| @@ -503,7 +505,7 @@ See the [Cloudtasker::Worker class](lib/cloudtasker/worker.rb) for more informat | |
| 503 505 |  | 
| 504 506 | 
             
            ## Error Handling
         | 
| 505 507 |  | 
| 506 | 
            -
            Jobs failing will automatically return an HTTP error to Cloud Task and trigger a retry at a later time. The number of retries Cloud Task will  | 
| 508 | 
            +
            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.
         | 
| 507 509 |  | 
| 508 510 | 
             
            ### HTTP Error codes
         | 
| 509 511 |  | 
| @@ -549,6 +551,8 @@ By default jobs are retried 25 times - using an exponential backoff - before bei | |
| 549 551 |  | 
| 550 552 | 
             
            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.
         | 
| 551 553 |  | 
| 554 | 
            +
            **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.
         | 
| 555 | 
            +
             | 
| 552 556 | 
             
            E.g. Set max number of retries globally via the cloudtasker initializer.
         | 
| 553 557 | 
             
            ```ruby
         | 
| 554 558 | 
             
            # config/initializers/cloudtasker.rb
         | 
| @@ -583,7 +587,6 @@ end | |
| 583 587 | 
             
            ```
         | 
| 584 588 |  | 
| 585 589 |  | 
| 586 | 
            -
             | 
| 587 590 | 
             
            ## Best practices building workers
         | 
| 588 591 |  | 
| 589 592 | 
             
            Below are recommendations and notes about creating workers.
         | 
| @@ -658,6 +661,8 @@ Google Cloud Tasks enforces a limit of 100 KB for job payloads. Taking into acco | |
| 658 661 | 
             
            Any excessive job payload (> 100 KB) will raise a `Cloudtasker::MaxTaskSizeExceededError`, both in production and development mode.
         | 
| 659 662 |  | 
| 660 663 | 
             
            #### Option 1: Use Cloudtasker optional support for payload storage in Redis
         | 
| 664 | 
            +
            **Supported since**: `0.10.rc1`
         | 
| 665 | 
            +
             | 
| 661 666 | 
             
            Cloudtasker provides optional support for storing argument payloads in Redis instead of sending them to Google Cloud Tasks.
         | 
| 662 667 |  | 
| 663 668 | 
             
            To enable it simply put the following in your Cloudtasker initializer:
         | 
    
        data/gemfiles/rails_5.2.gemfile
    CHANGED
    
    
    
        data/gemfiles/rails_6.0.gemfile
    CHANGED
    
    
| @@ -248,7 +248,7 @@ module Cloudtasker | |
| 248 248 | 
             
                      req = Net::HTTP::Post.new(uri.path, http_request[:headers])
         | 
| 249 249 |  | 
| 250 250 | 
             
                      # Add retries header
         | 
| 251 | 
            -
                      req[ | 
| 251 | 
            +
                      req[Cloudtasker::Config::RETRY_HEADER] = retries
         | 
| 252 252 |  | 
| 253 253 | 
             
                      # Set job payload
         | 
| 254 254 | 
             
                      req.body = http_request[:body]
         | 
    
        data/lib/cloudtasker/config.rb
    CHANGED
    
    | @@ -13,7 +13,17 @@ module Cloudtasker | |
| 13 13 | 
             
                MAX_TASK_SIZE = 100 * 1024 # 100 KB
         | 
| 14 14 |  | 
| 15 15 | 
             
                # Retry header in Cloud Task responses
         | 
| 16 | 
            -
                 | 
| 16 | 
            +
                #
         | 
| 17 | 
            +
                # TODO: use 'X-CloudTasks-TaskExecutionCount' instead of 'X-CloudTasks-TaskRetryCount'
         | 
| 18 | 
            +
                #   'X-CloudTasks-TaskExecutionCount' is currently bugged and remains at 0 even on retries.
         | 
| 19 | 
            +
                #
         | 
| 20 | 
            +
                # See bug: https://issuetracker.google.com/issues/154532072
         | 
| 21 | 
            +
                #
         | 
| 22 | 
            +
                # Definitions:
         | 
| 23 | 
            +
                #   X-CloudTasks-TaskRetryCount: total number of retries (including 504 "instance unreachable")
         | 
| 24 | 
            +
                #   X-CloudTasks-TaskExecutionCount: number of non-503 retries (= actual number of job failures)
         | 
| 25 | 
            +
                #
         | 
| 26 | 
            +
                RETRY_HEADER = 'X-CloudTasks-TaskRetryCount'
         | 
| 17 27 |  | 
| 18 28 | 
             
                # Content-Transfer-Encoding header in Cloud Task responses
         | 
| 19 29 | 
             
                ENCODING_HEADER = 'Content-Transfer-Encoding'
         | 
| @@ -33,7 +43,15 @@ module Cloudtasker | |
| 33 43 | 
             
                DEFAULT_QUEUE_CONCURRENCY = 10
         | 
| 34 44 | 
             
                DEFAULT_QUEUE_RETRIES = -1 # unlimited
         | 
| 35 45 |  | 
| 36 | 
            -
                # The number of times jobs will be attempted before declaring them dead
         | 
| 46 | 
            +
                # The number of times jobs will be attempted before declaring them dead.
         | 
| 47 | 
            +
                #
         | 
| 48 | 
            +
                # With the default retry configuration (maxDoublings = 16 and minBackoff = 0.100s)
         | 
| 49 | 
            +
                # it means that jobs will be declared dead after 20h of consecutive failing.
         | 
| 50 | 
            +
                #
         | 
| 51 | 
            +
                # Note that this configuration parameter is internal to Cloudtasker and does not
         | 
| 52 | 
            +
                # affect the Cloud Task queue configuration. The number of retries configured
         | 
| 53 | 
            +
                # on the Cloud Task queue should be higher than the number below to also cover
         | 
| 54 | 
            +
                # failures due to the instance being unreachable.
         | 
| 37 55 | 
             
                DEFAULT_MAX_RETRY_ATTEMPTS = 25
         | 
| 38 56 |  | 
| 39 57 | 
             
                PROCESSOR_HOST_MISSING = <<~DOC
         | 
| @@ -12,6 +12,9 @@ module Cloudtasker | |
| 12 12 | 
             
                # Default number of threads to allocate to process a specific queue
         | 
| 13 13 | 
             
                QUEUE_CONCURRENCY = 1
         | 
| 14 14 |  | 
| 15 | 
            +
                # Job Polling. How frequently to poll jobs in redis.
         | 
| 16 | 
            +
                JOB_POLLING_FREQUENCY = 0.5 # seconds
         | 
| 17 | 
            +
             | 
| 15 18 | 
             
                #
         | 
| 16 19 | 
             
                # Stop the local server.
         | 
| 17 20 | 
             
                #
         | 
| @@ -46,7 +49,7 @@ module Cloudtasker | |
| 46 49 | 
             
                  @start ||= Thread.new do
         | 
| 47 50 | 
             
                    until @done
         | 
| 48 51 | 
             
                      queues.each { |(n, c)| process_jobs(n, c) }
         | 
| 49 | 
            -
                      sleep  | 
| 52 | 
            +
                      sleep JOB_POLLING_FREQUENCY
         | 
| 50 53 | 
             
                    end
         | 
| 51 54 | 
             
                    Cloudtasker.logger.info('[Cloudtasker/Server] Local server exiting...')
         | 
| 52 55 | 
             
                  end
         | 
    
        data/lib/cloudtasker/version.rb
    CHANGED
    
    
    
        data/lib/cloudtasker/worker.rb
    CHANGED
    
    | @@ -7,7 +7,8 @@ module Cloudtasker | |
| 7 7 | 
             
                def self.included(base)
         | 
| 8 8 | 
             
                  base.extend(ClassMethods)
         | 
| 9 9 | 
             
                  base.attr_writer :job_queue
         | 
| 10 | 
            -
                  base.attr_accessor :job_args, :job_id, :job_meta, :job_reenqueued, :job_retries
         | 
| 10 | 
            +
                  base.attr_accessor :job_args, :job_id, :job_meta, :job_reenqueued, :job_retries,
         | 
| 11 | 
            +
                                     :perform_started_at, :perform_ended_at
         | 
| 11 12 | 
             
                end
         | 
| 12 13 |  | 
| 13 14 | 
             
                #
         | 
| @@ -181,21 +182,19 @@ module Cloudtasker | |
| 181 182 | 
             
                #
         | 
| 182 183 | 
             
                def execute
         | 
| 183 184 | 
             
                  logger.info('Starting job...')
         | 
| 184 | 
            -
                  resp = Cloudtasker.config.server_middleware.invoke(self) do
         | 
| 185 | 
            -
                    begin
         | 
| 186 | 
            -
                      perform(*job_args)
         | 
| 187 | 
            -
                    rescue StandardError => e
         | 
| 188 | 
            -
                      try(:on_error, e)
         | 
| 189 | 
            -
                      return raise(e) unless job_dead?
         | 
| 190 185 |  | 
| 191 | 
            -
             | 
| 192 | 
            -
             | 
| 193 | 
            -
             | 
| 194 | 
            -
             | 
| 195 | 
            -
             | 
| 196 | 
            -
                  end
         | 
| 197 | 
            -
                  logger.info('Job done')
         | 
| 186 | 
            +
                  # Perform job logic
         | 
| 187 | 
            +
                  resp = execute_middleware_chain
         | 
| 188 | 
            +
             | 
| 189 | 
            +
                  # Log job completion and return result
         | 
| 190 | 
            +
                  logger.info("Job done after #{job_duration}s") { { duration: job_duration } }
         | 
| 198 191 | 
             
                  resp
         | 
| 192 | 
            +
                rescue DeadWorkerError => e
         | 
| 193 | 
            +
                  logger.info("Job dead after #{job_duration}s and #{job_retries} retries") { { duration: job_duration } }
         | 
| 194 | 
            +
                  raise(e)
         | 
| 195 | 
            +
                rescue StandardError => e
         | 
| 196 | 
            +
                  logger.info("Job failed after #{job_duration}s") { { duration: job_duration } }
         | 
| 197 | 
            +
                  raise(e)
         | 
| 199 198 | 
             
                end
         | 
| 200 199 |  | 
| 201 200 | 
             
                #
         | 
| @@ -286,5 +285,46 @@ module Cloudtasker | |
| 286 285 | 
             
                def job_dead?
         | 
| 287 286 | 
             
                  job_retries >= Cloudtasker.config.max_retries
         | 
| 288 287 | 
             
                end
         | 
| 288 | 
            +
             | 
| 289 | 
            +
                #
         | 
| 290 | 
            +
                # Return the time taken (in seconds) to perform the job. This duration
         | 
| 291 | 
            +
                # includes the middlewares and the actual perform method.
         | 
| 292 | 
            +
                #
         | 
| 293 | 
            +
                # @return [Float] The time taken in seconds as a floating point number.
         | 
| 294 | 
            +
                #
         | 
| 295 | 
            +
                def job_duration
         | 
| 296 | 
            +
                  return 0.0 unless perform_ended_at && perform_started_at
         | 
| 297 | 
            +
             | 
| 298 | 
            +
                  (perform_ended_at - perform_started_at).ceil(3)
         | 
| 299 | 
            +
                end
         | 
| 300 | 
            +
             | 
| 301 | 
            +
                #=============================
         | 
| 302 | 
            +
                # Private
         | 
| 303 | 
            +
                #=============================
         | 
| 304 | 
            +
                private
         | 
| 305 | 
            +
             | 
| 306 | 
            +
                #
         | 
| 307 | 
            +
                # Execute the worker perform method through the middleware chain.
         | 
| 308 | 
            +
                #
         | 
| 309 | 
            +
                # @return [Any] The result of the perform method.
         | 
| 310 | 
            +
                #
         | 
| 311 | 
            +
                def execute_middleware_chain
         | 
| 312 | 
            +
                  self.perform_started_at = Time.now
         | 
| 313 | 
            +
             | 
| 314 | 
            +
                  Cloudtasker.config.server_middleware.invoke(self) do
         | 
| 315 | 
            +
                    begin
         | 
| 316 | 
            +
                      perform(*job_args)
         | 
| 317 | 
            +
                    rescue StandardError => e
         | 
| 318 | 
            +
                      try(:on_error, e)
         | 
| 319 | 
            +
                      return raise(e) unless job_dead?
         | 
| 320 | 
            +
             | 
| 321 | 
            +
                      # Flag job as dead
         | 
| 322 | 
            +
                      try(:on_dead, e)
         | 
| 323 | 
            +
                      raise(DeadWorkerError, e)
         | 
| 324 | 
            +
                    end
         | 
| 325 | 
            +
                  end
         | 
| 326 | 
            +
                ensure
         | 
| 327 | 
            +
                  self.perform_ended_at = Time.now
         | 
| 328 | 
            +
                end
         | 
| 289 329 | 
             
              end
         | 
| 290 330 | 
             
            end
         | 
| @@ -59,7 +59,7 @@ module Cloudtasker | |
| 59 59 | 
             
                # @return [String] The formatted log message
         | 
| 60 60 | 
             
                #
         | 
| 61 61 | 
             
                def formatted_message(msg)
         | 
| 62 | 
            -
                  "[Cloudtasker][#{worker.job_id}] #{msg}"
         | 
| 62 | 
            +
                  "[Cloudtasker][#{worker.class}][#{worker.job_id}] #{msg}"
         | 
| 63 63 | 
             
                end
         | 
| 64 64 |  | 
| 65 65 | 
             
                #
         | 
| @@ -141,7 +141,8 @@ module Cloudtasker | |
| 141 141 | 
             
                # @param [Proc] &block Optional context block.
         | 
| 142 142 | 
             
                #
         | 
| 143 143 | 
             
                def log_message(level, msg, &block)
         | 
| 144 | 
            -
                   | 
| 144 | 
            +
                  # Merge log-specific context into worker-specific context
         | 
| 145 | 
            +
                  payload_block = -> { log_block.call.merge(block&.call || {}) }
         | 
| 145 146 |  | 
| 146 147 | 
             
                  # ActiveSupport::Logger does not support passing a payload through a block on top
         | 
| 147 148 | 
             
                  # of a message.
         | 
    
        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. | 
| 4 | 
            +
              version: 0.10.rc3
         | 
| 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-04- | 
| 11 | 
            +
            date: 2020-04-21 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: activesupport
         |