specbandit 1.0.1 → 1.1.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9444599b748d46d177475926a3b5f3740a6534c54a0e540c7663ee4eb4208d40
4
- data.tar.gz: f7e5787ee93b270c2ba121d7edae8a2ccac5881f1bc873d65e5afcb481958e35
3
+ metadata.gz: 70efc56f9de13cd1e141cbb1ad39ff22cf1a07cdfbcd2c1257cefc55f5cb73fe
4
+ data.tar.gz: 6070e8b72aec64162a3e37190e787abbd23bf464faf74d06212e401c7c312fed
5
5
  SHA512:
6
- metadata.gz: 147b0eb446363ece19f086bbdedc375f6f6a15afc8d128628cdb64881396bd7c0ad01b8fc04b2af72b8db47aa89d83af0b48c86772ea57615582e412ec80a0d6
7
- data.tar.gz: ae4357cd2cfcb1a12f539c5a7f43dacfa838a5810540ca41270c8e8fb1efb672189d898d0b4182106bb2719308b0f0ecf74495aade0f904f25d7253c48c96ed9
6
+ metadata.gz: c7fc604a65cba2387b1177fef33ad2994e13677777d6d87b53f68aa29a00616c864dc874b5571947c4e837d0c3e25b24cc4d99d0841c94b79433258e8f5faba7
7
+ data.tar.gz: 7c6acd6833e1f082e2c764de7d32ab67407726d6905cc6246cbbe0921410f2ad3db4a503ee5932240414ebf413538830db54e8275adb4cd6c1f3d9bc71a195e4
@@ -131,6 +131,10 @@ module Specbandit
131
131
  Specbandit.configuration.report = v
132
132
  end
133
133
 
134
+ opts.on('--redis-max-attempts N', Integer, 'Redis connection retry attempts (default: 5)') do |v|
135
+ Specbandit.configuration.redis_max_attempts = v
136
+ end
137
+
134
138
  opts.on('--verbose', 'Show per-batch file list and full command output (default: quiet)') do
135
139
  Specbandit.configuration.verbose = true
136
140
  end
@@ -5,7 +5,9 @@ module Specbandit
5
5
  attr_accessor :redis_url, :batch_size, :key, :rspec_opts, :key_ttl,
6
6
  :key_rerun, :verbose,
7
7
  :adapter, :command, :command_opts,
8
- :key_failed, :report
8
+ :key_failed, :report,
9
+ :redis_max_attempts, :redis_connect_timeout, :redis_timeout,
10
+ :redis_reconnect_attempts
9
11
 
10
12
  DEFAULT_REDIS_URL = 'redis://localhost:6379'
11
13
  DEFAULT_BATCH_SIZE = 5
@@ -16,6 +18,17 @@ module Specbandit
16
18
  DEFAULT_KEY_TTL = 604_800 # 1 week in seconds
17
19
  DEFAULT_ADAPTER = 'cli'
18
20
 
21
+ # Redis connection resilience. Redis is a best-effort coordination store for
22
+ # a distributed test run, and CI runners can sit a WAN hop away from it
23
+ # (e.g. cross-datacenter mesh), so a transient blip must not red the build.
24
+ # We retry a handful of times with capped exponential backoff, and give the
25
+ # underlying client explicit connect/read/write timeouts + reconnects rather
26
+ # than relying on library defaults.
27
+ DEFAULT_REDIS_MAX_ATTEMPTS = 5
28
+ DEFAULT_REDIS_CONNECT_TIMEOUT = 3.0
29
+ DEFAULT_REDIS_TIMEOUT = 5.0
30
+ DEFAULT_REDIS_RECONNECT_ATTEMPTS = 3
31
+
19
32
  def initialize
20
33
  @redis_url = ENV.fetch('SPECBANDIT_REDIS_URL', DEFAULT_REDIS_URL)
21
34
  @batch_size = Integer(ENV.fetch('SPECBANDIT_BATCH_SIZE', DEFAULT_BATCH_SIZE))
@@ -29,12 +42,18 @@ module Specbandit
29
42
  @command_opts = parse_space_separated(ENV.fetch('SPECBANDIT_COMMAND_OPTS', nil))
30
43
  @key_failed = ENV.fetch('SPECBANDIT_KEY_FAILED', nil)
31
44
  @report = ENV.fetch('SPECBANDIT_REPORT', nil)
45
+
46
+ @redis_max_attempts = Integer(ENV.fetch('SPECBANDIT_REDIS_MAX_ATTEMPTS', DEFAULT_REDIS_MAX_ATTEMPTS))
47
+ @redis_connect_timeout = Float(ENV.fetch('SPECBANDIT_REDIS_CONNECT_TIMEOUT', DEFAULT_REDIS_CONNECT_TIMEOUT))
48
+ @redis_timeout = Float(ENV.fetch('SPECBANDIT_REDIS_TIMEOUT', DEFAULT_REDIS_TIMEOUT))
49
+ @redis_reconnect_attempts = Integer(ENV.fetch('SPECBANDIT_REDIS_RECONNECT_ATTEMPTS', DEFAULT_REDIS_RECONNECT_ATTEMPTS))
32
50
  end
33
51
 
34
52
  def validate!
35
53
  raise Error, 'key is required (set via --key or SPECBANDIT_KEY)' if key.nil? || key.empty?
36
54
  raise Error, 'batch_size must be a positive integer' unless batch_size.positive?
37
55
  raise Error, 'key_ttl must be a positive integer' unless key_ttl.positive?
56
+ raise Error, 'redis_max_attempts must be a positive integer' unless redis_max_attempts.positive?
38
57
  end
39
58
 
40
59
  private
@@ -6,12 +6,28 @@ module Specbandit
6
6
  class RedisQueue
7
7
  attr_reader :redis
8
8
 
9
- def initialize(redis_url: Specbandit.configuration.redis_url)
10
- @redis = Redis.new(url: redis_url)
9
+ # Cap the exponential backoff so a real outage degrades/fails within a
10
+ # bounded window instead of sleeping for minutes on the last attempts.
11
+ MAX_BACKOFF_SECONDS = 10
12
+
13
+ def initialize(
14
+ redis_url: Specbandit.configuration.redis_url,
15
+ connect_timeout: Specbandit.configuration.redis_connect_timeout,
16
+ read_timeout: Specbandit.configuration.redis_timeout,
17
+ write_timeout: Specbandit.configuration.redis_timeout,
18
+ reconnect_attempts: Specbandit.configuration.redis_reconnect_attempts,
19
+ max_attempts: Specbandit.configuration.redis_max_attempts
20
+ )
21
+ @max_attempts = max_attempts
22
+ @redis = Redis.new(
23
+ url: redis_url,
24
+ connect_timeout: connect_timeout,
25
+ read_timeout: read_timeout,
26
+ write_timeout: write_timeout,
27
+ reconnect_attempts: reconnect_attempts
28
+ )
11
29
  end
12
30
 
13
- MAX_ATTEMPTS = 3
14
-
15
31
  # Push file paths onto the queue and set an expiry on the key.
16
32
  # Returns the new length of the list.
17
33
  def push(key, files, ttl: nil)
@@ -83,7 +99,7 @@ module Specbandit
83
99
  "#{key}:published"
84
100
  end
85
101
 
86
- def with_retries(attempts: MAX_ATTEMPTS)
102
+ def with_retries(attempts: @max_attempts)
87
103
  retries = 0
88
104
  begin
89
105
  yield
@@ -91,7 +107,7 @@ module Specbandit
91
107
  retries += 1
92
108
  raise if retries >= attempts
93
109
 
94
- delay = 2**retries
110
+ delay = [2**retries, MAX_BACKOFF_SECONDS].min
95
111
  warn "[specbandit] Redis connection failed (attempt #{retries}/#{attempts}): #{e.message}. Retrying in #{delay}s..."
96
112
  sleep(delay)
97
113
  retry
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Specbandit
4
- VERSION = '1.0.1'
4
+ VERSION = '1.1.1'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: specbandit
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ferran Basora
@@ -109,7 +109,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
111
  requirements: []
112
- rubygems_version: 4.0.10
112
+ rubygems_version: 4.0.16
113
113
  specification_version: 4
114
114
  summary: Distributed test runner using Redis as a work queue
115
115
  test_files: []