specwrk 0.15.9 → 0.16.0

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: 8afbccd28740ac63fbdbd2f71a6f60442664a2d19e6743c337266fd14cffe081
4
- data.tar.gz: 5b2b645f3b93abcb320f9fb4e93ebac94df591bb4336dac8af946d5f771575a4
3
+ metadata.gz: 1736573f9c3fc84992518a7a2ea486c03eefe4885b8b857a79b15551533deec0
4
+ data.tar.gz: 7106e3b4c856bfd9ff89e5fe562ba3da45ad5981356b295e24b55579aa48609f
5
5
  SHA512:
6
- metadata.gz: eb05bb7995ebd500c6935e0325f00d58ad7cd0d78f3f1fe133615258f53a8a3ec8832f43e4c383bfb6d09577c1d9e5e9830bc9043e5c3cc7a3d5e4a376986c52
7
- data.tar.gz: 4bfbc09dde0c764df5ad01869b1ba86bb9acde760ab49ab46fcd509f9eba8db849237e272e46a3baf5e7c458c054a00450d3c9b1f657ecbeb84e81f2da9781c7
6
+ metadata.gz: 067222d93a38d847886d40b5a5121e24d68f633115ecfddccdd7c199adf598fd780cb8ca5fd1110028679828436ac3bc8a4335ca54c7a481aeaa7dc85938d0a4
7
+ data.tar.gz: 5d10c755bb71d7ace592e33c5fa8b2c4a2d9a349805870914ca0c9ac3410306765324211820c1283c8c2d8846ddbbcf5d1c88f89f2d5b89899f554a6e5c187dd
data/CHANGELOG.md CHANGED
@@ -1,22 +1,57 @@
1
- ## [Unreleased]
2
-
3
1
  # Changelog
4
2
 
5
- ## v0.15.2 — 2025-08-15
6
- [Compare](https://github.com/danielwestendorf/specwrk/compare/v0.15.1...v0.15.2)
7
- - Fix bug where ruby objects were being written to ndjson files isntead of json — [#119](https://github.com/danielwestendorf/specwrk/pull/119) by @danielwestendorf
8
-
9
- ## v0.15.12025-08-14
10
- [Compare](https://github.com/danielwestendorf/specwrk/compare/v0.15.0...v0.15.1)
11
- - Add `watch` command to split spec files across processes as they change — [#117](https://github.com/danielwestendorf/specwrk/pull/117) by @danielwestendorf
12
- - Readme formatting tweaks @danielwestendorf
13
-
14
- ## v0.15.02025-08-08
15
- [Compare](https://github.com/danielwestendorf/specwrk/compare/v0.14.1...v0.15.0)
16
- - When output path is specified, write an `.ndjson` file per worker of all examples executed — [#113](https://github.com/danielwestendorf/specwrk/pull/113) (fixes [#10](https://github.com/danielwestendorf/specwrk/issues/10)) by @danielwestendorf
17
- - Print re-run commands for failures — [#114](https://github.com/danielwestendorf/specwrk/pull/114) (fixes [#7](https://github.com/danielwestendorf/specwrk/issues/7)) by @danielwestendorf
18
- - Show count of examples that did not execute; make runs resumable by only clearing stores on seed — [#115](https://github.com/danielwestendorf/specwrk/pull/115) by @danielwestendorf
19
- - Report flake counts and provide flake re-run commands — [#116](https://github.com/danielwestendorf/specwrk/pull/116) (fixes [#112](https://github.com/danielwestendorf/specwrk/issues/112)) by @danielwestendorf
3
+ ## v0.15.9 — 2025-08-28
4
+ [Compare](https://github.com/danielwestendorf/specwrk/compare/v0.15.8...v0.15.9)
5
+ - Support lock skipping for read-only endpoints — [#139](https://github.com/danielwestendorf/specwrk/pull/139) by [@danielwestendorf](https://github.com/danielwestendorf)
6
+ - Fix typo in README — [#137](https://github.com/danielwestendorf/specwrk/pull/137) by [@willnet](https://github.com/willnet)
7
+ - Support an array of arguments when seeding/starting [#141](https://github.com/danielwestendorf/specwrk/pull/141) by [@danielwestendorf](https://github.com/danielwestendorf)
8
+ - Load examples from processing before global lock — [#142](https://github.com/danielwestendorf/specwrk/pull/142) by [@danielwestendorf](https://github.com/danielwestendorf)
9
+
10
+ ## v0.15.82025-08-27
11
+ [Compare](https://github.com/danielwestendorf/specwrk/compare/v0.15.7...v0.15.8)
12
+ - Friendly helper message when Redis adapter is not available [@danielwestendorf](https://github.com/danielwestendorf)
13
+
14
+ ## v0.15.7 2025-08-26
15
+ [Compare](https://github.com/danielwestendorf/specwrk/compare/v0.15.6...v0.15.7)
16
+ - Change request locking from global to per-`run_id` — [#135](https://github.com/danielwestendorf/specwrk/pull/135) by [@danielwestendorf](https://github.com/danielwestendorf)
17
+ - `rspec-core` is a runtime dependency (not `rspec`) — [#128](https://github.com/danielwestendorf/specwrk/pull/128) by [@bquorning](https://github.com/bquorning)
18
+ - README typo fix — [#126](https://github.com/danielwestendorf/specwrk/pull/126) by [@brett-anderson](https://github.com/brett-anderson)
19
+
20
+ ## v0.15.6 — 2025-08-26
21
+ [Compare](https://github.com/danielwestendorf/specwrk/compare/v0.15.5...v0.15.6)
22
+ - Test `Store.adapter_klass` — [@danielwestendorf](https://github.com/danielwestendorf)
23
+
24
+ ## v0.15.5 — 2025-08-26
25
+ [Compare](https://github.com/danielwestendorf/specwrk/compare/v0.15.4...v0.15.5)
26
+ - Add support for Redis store adapters — [#133](https://github.com/danielwestendorf/specwrk/pull/133) by [@danielwestendorf](https://github.com/danielwestendorf)
27
+ - Remove support for Ruby 3.1.0 — [@danielwestendorf](https://github.com/danielwestendorf)
28
+ - Remove `gem-release` from dev dependencies — [@danielwestendorf](https://github.com/danielwestendorf)
29
+
30
+ ## v0.15.4 — 2025-08-23
31
+ [Compare](https://github.com/danielwestendorf/specwrk/compare/v0.15.3...v0.15.4)
32
+ - Split web endpoints into separate files for easier comprehension — [#129](https://github.com/danielwestendorf/specwrk/pull/129) by [@danielwestendorf](https://github.com/danielwestendorf)
33
+ - Write the title sequence as watch loops run — [#131](https://github.com/danielwestendorf/specwrk/pull/131) by [@danielwestendorf](https://github.com/danielwestendorf) (fixes [#130](https://github.com/danielwestendorf/specwrk/issues/130))
34
+
35
+ ## v0.15.3 — 2025-08-22
36
+ [Compare](https://github.com/danielwestendorf/specwrk/compare/v0.15.2...v0.15.3)
37
+ - Clear filter-manager inclusions/exclusions when listing examples — [#122](https://github.com/danielwestendorf/specwrk/pull/122) by [@danielwestendorf](https://github.com/danielwestendorf) (fixes [#121](https://github.com/danielwestendorf/specwrk/issues/121))
38
+ - Smarter completion-threshold calculation; make runs resumable — [#124](https://github.com/danielwestendorf/specwrk/pull/124) by [@danielwestendorf](https://github.com/danielwestendorf) (fixes [#121](https://github.com/danielwestendorf/specwrk/issues/121))
39
+
40
+ ## v0.15.2 — 2025-08-15
41
+ [Compare](https://github.com/danielwestendorf/specwrk/compare/v0.15.1...v0.15.2)
42
+ - Fix bug where Ruby objects were being written to NDJSON files instead of JSON — [#119](https://github.com/danielwestendorf/specwrk/pull/119) by [@danielwestendorf](https://github.com/danielwestendorf)
43
+
44
+ ## v0.15.1 — 2025-08-14
45
+ [Compare](https://github.com/danielwestendorf/specwrk/compare/v0.15.0...v0.15.1)
46
+ - Add `watch` command to split spec files across processes as they change — [#117](https://github.com/danielwestendorf/specwrk/pull/117) by [@danielwestendorf](https://github.com/danielwestendorf)
47
+ - README formatting tweaks — [@danielwestendorf](https://github.com/danielwestendorf)
48
+
49
+ ## v0.15.0 — 2025-08-08
50
+ [Compare](https://github.com/danielwestendorf/specwrk/compare/v0.14.1...v0.15.0)
51
+ - Per-worker NDJSON output when `--output` is specified — [#113](https://github.com/danielwestendorf/specwrk/pull/113) by [@danielwestendorf](https://github.com/danielwestendorf) (fixes [#10](https://github.com/danielwestendorf/specwrk/issues/10))
52
+ - Print re-run commands for failures — [#114](https://github.com/danielwestendorf/specwrk/pull/114) by [@danielwestendorf](https://github.com/danielwestendorf) (fixes [#7](https://github.com/danielwestendorf/specwrk/issues/7))
53
+ - Show number of examples that did not execute; make runs resumable — [#115](https://github.com/danielwestendorf/specwrk/pull/115) by [@danielwestendorf](https://github.com/danielwestendorf)
54
+ - Report flake counts and re-run commands — [#116](https://github.com/danielwestendorf/specwrk/pull/116) by [@danielwestendorf](https://github.com/danielwestendorf) (fixes [#112](https://github.com/danielwestendorf/specwrk/issues/112))
20
55
 
21
56
  ## v0.14.1 — 2025-08-07
22
57
  [Compare](https://github.com/danielwestendorf/specwrk/compare/v0.14.0...v0.14.1)
data/README.md CHANGED
@@ -34,7 +34,7 @@ Commands:
34
34
  Intended for quick ad-hoc local host development or single-node CI runs. This command starts a queue server, seeds it with examples from the `spec/` directory, and starts `8` worker processes. It will report the ultimate success or failure.
35
35
 
36
36
  ```sh
37
- $ start --help
37
+ $ specwrk start --help
38
38
  Command:
39
39
  specwrk start
40
40
 
@@ -52,6 +52,7 @@ Options:
52
52
  --key=VALUE, -k VALUE # Authentication key clients must use for access. Overrides SPECWRK_SRV_KEY, default: ""
53
53
  --run=VALUE, -r VALUE # The run identifier for this job execution. Overrides SPECWRK_RUN, default: "main"
54
54
  --timeout=VALUE, -t VALUE # The amount of time to wait for the server to respond. Overrides SPECWRK_TIMEOUT, default: "5"
55
+ --network-retries=VALUE # The number of times to retry in the event of a network failure. Overrides SPECWRK_NETWORK_RETRIES, default: "1"
55
56
  --id=VALUE # The identifier for this worker. Overrides SPECWRK_ID. If none provided one in the format of specwrk-worker-8_RAND_CHARS-COUNT_INDEX will be used
56
57
  --count=VALUE, -c VALUE # The number of worker processes you want to start, default: 1
57
58
  --output=VALUE, -o VALUE # Directory where worker output is stored. Overrides SPECWRK_OUT, default: ".specwrk/"
@@ -113,6 +114,7 @@ Options:
113
114
  --key=VALUE, -k VALUE # Authentication key clients must use for access. Overrides SPECWRK_SRV_KEY, default: ""
114
115
  --run=VALUE, -r VALUE # The run identifier for this job execution. Overrides SPECWRK_RUN, default: "main"
115
116
  --timeout=VALUE, -t VALUE # The amount of time to wait for the server to respond. Overrides SPECWRK_TIMEOUT, default: "5"
117
+ --network-retries=VALUE # The number of times to retry in the event of a network failure. Overrides SPECWRK_NETWORK_RETRIES, default: "1"
116
118
  --max-retries=VALUE # Number of times an example will be re-run should it fail, default: 0
117
119
  --help, -h # Print this help
118
120
  ```
@@ -140,6 +142,7 @@ Options:
140
142
  --key=VALUE, -k VALUE # Authentication key clients must use for access. Overrides SPECWRK_SRV_KEY, default: ""
141
143
  --run=VALUE, -r VALUE # The run identifier for this job execution. Overrides SPECWRK_RUN, default: "main"
142
144
  --timeout=VALUE, -t VALUE # The amount of time to wait for the server to respond. Overrides SPECWRK_TIMEOUT, default: "5"
145
+ --network-retries=VALUE # The number of times to retry in the event of a network failure. Overrides SPECWRK_NETWORK_RETRIES, default: "1"
143
146
  --help, -h # Print this help
144
147
  ```
145
148
 
@@ -164,13 +167,13 @@ Options:
164
167
  ```
165
168
 
166
169
  ## Configuring your test environment
167
- If you test suite tracks state, starts servers, etc. and you plan on running many processes on the same node, you'll need to make
170
+ If your test suite tracks state, starts servers, etc. and you plan on running many processes on the same node, you'll need to make
168
171
  adjustments to avoid conflicting port usage or database/state mutations.
169
172
 
170
173
  `specwrk` workers will have `TEST_ENV_NUMBER={i}` set to help you configure approriately.
171
174
 
172
175
  ### Rails
173
- Rails has had easy multi-process test setup for a while now by creating unique test databases per process. For my rails v7.2 app which uses PostgreSQL and Capyabara, I made these changes to my `spec/rails_helper.rb`:
176
+ Rails has had easy multi-process test setup for a while now by creating unique test databases per process. For my rails v7.2 app which uses PostgreSQL and Capybara, I made these changes to my `spec/rails_helper.rb`:
174
177
 
175
178
  ```diff
176
179
  ++ if ENV["TEST_ENV_NUMBER"]
@@ -200,7 +203,7 @@ Make sure to persist `$SPECWRK_OUT/report.json` between runs so that subsequent
200
203
  [CircleCI Example](https://github.com/danielwestendorf/specwrk/blob/main/.circleci/config.yml) (specwrk-single-node job)
201
204
 
202
205
  ### Multi-node, multi-process
203
- Multi-node, multi-process works best when have many nodes running tests. This distributes the test execution across the nodes until the queue is for the run is empty, optimizing for slowest specs first. This distributes test execution across all nodes evenly(-ish).
206
+ Multi-node, multi-process works best when you have many nodes running tests. This distributes the test execution across the nodes until the queue is for the run is empty, optimizing for slowest specs first. This distributes test execution across all nodes evenly(-ish).
204
207
 
205
208
  To accomplish this, a central queue server is required, examples must be explicitly seeded, and workers explicitly started.
206
209
 
@@ -221,7 +224,9 @@ Start a persistent Queue Server given one of the following methods
221
224
 
222
225
  ### Configuring your Queue Server
223
226
  - Secure your server with a key either with the `SPECWRK_SRV_KEY` environment variable or `--key` CLI option
224
- - Configure the server output to be a persisted volume so your timings survive between system restarts with the `SPECWRK_SRV_STORE_URI` environment variable or `--store-uri` CLI option. By default, `memory:///` will be used for the run's data stores (so run data will no survive server restarts) while `file://#{Dir.tmpdir}` will be used for run timings. Pass `--store-uri file:///whatever/absolute/path` to store all data on disk (required for multiple server processes).
227
+ - Configure the server output to be a persisted volume so your timings survive between system restarts with the `SPECWRK_SRV_STORE_URI` environment variable or `--store-uri` CLI option. By default, `memory:///` will be used for the run's data stores (so run data will not survive server restarts) while `file://#{Dir.tmpdir}` will be used for run timings. Pass `--store-uri file:///whatever/absolute/path` to store all data on disk (required for multiple server processes).
228
+
229
+ See [specwrk-store-redis_adapter](https://github.com/danielwestendorf/specwrk-store-redis_adapter) for Redis-compatible backed storage.
225
230
 
226
231
  See [specwrk serve --help](#specwrk-serve) for all possible configuration options.
227
232
 
@@ -243,7 +248,7 @@ map(/_spec\.rb$/) do |spec_path|
243
248
  spec_path
244
249
  end
245
250
 
246
- # If a file in lib changes, map it to the spec folder for it's spec file
251
+ # If a file in lib changes, map it to the spec folder for its spec file
247
252
  map(/lib\/.*\.rb$/) do |path|
248
253
  path.gsub(/lib\/(.+)\.rb/, "spec/\\1_spec.rb")
249
254
  end
@@ -253,7 +258,7 @@ end
253
258
  # path.gsub(/app\/models\/(.+)\.rb/, "spec/models/\\1_spec.rb")
254
259
  # end
255
260
  #
256
- # If a controlelr file changes (assuming rails app structure), run the controller and system specs file
261
+ # If a controller file changes (assuming rails app structure), run the controller and system specs file
257
262
  # map(/app\/controllers\/.*.rb$/) do |path|
258
263
  # [
259
264
  # path.gsub(/app\/controllers\/(.+)\.rb/, "spec/controllers/\\1_spec.rb"),
@@ -263,7 +268,7 @@ end
263
268
  ```
264
269
 
265
270
  ## Prior/other works
266
- There are many prior works for running rspec tests across multiple processes. Most of them combine process output making failures hard to grok. Some are good at running tests locally, but not on CI, while others are inversely true. Others are comercial or impactical without making a purchase.
271
+ There are many prior works for running rspec tests across multiple processes. Most of them combine process output making failures hard to grok. Some are good at running tests locally, but not on CI, while others are inversely true. Others are commercial or impractical without making a purchase.
267
272
 
268
273
  specwrk is different because it:
269
274
  1. Puts your developer experience first. Easy execution. No messy outputs. Retries built in. Easy(er) debugging of flaky tests.
@@ -273,14 +278,19 @@ specwrk is different because it:
273
278
  5. Is free.
274
279
 
275
280
  [parallel_rspec](https://github.com/willbryant/parallel_rspec)
281
+
276
282
  [Knapsack](https://github.com/KnapsackPro/knapsack)
283
+
277
284
  [parallel_tests](https://github.com/grosser/parallel_tests)
285
+
278
286
  [rspecq](https://github.com/skroutz/rspecq)
287
+
279
288
  [RSpec ABQ](https://github.com/rwx-research/rspec-abq)
280
289
 
290
+
281
291
  ## Contributing
282
292
 
283
- Bug reports and pull requests are welcome on GitHub at https://github.com/dwestendorf/specwrk.
293
+ Bug reports and pull requests are welcome on GitHub at https://github.com/danielwestendorf/specwrk.
284
294
 
285
295
  ## License
286
296
 
data/lib/specwrk/cli.rb CHANGED
@@ -20,13 +20,15 @@ module Specwrk
20
20
  base.unique_option :key, type: :string, default: ENV.fetch("SPECWRK_SRV_KEY", ""), aliases: ["-k"], desc: "Authentication key clients must use for access. Overrides SPECWRK_SRV_KEY"
21
21
  base.unique_option :run, type: :string, default: ENV.fetch("SPECWRK_RUN", "main"), aliases: ["-r"], desc: "The run identifier for this job execution. Overrides SPECWRK_RUN"
22
22
  base.unique_option :timeout, type: :integer, default: ENV.fetch("SPECWRK_TIMEOUT", "5"), aliases: ["-t"], desc: "The amount of time to wait for the server to respond. Overrides SPECWRK_TIMEOUT"
23
+ base.unique_option :network_retries, type: :integer, default: ENV.fetch("SPECWRK_NETWORK_RETRIES", "1"), desc: "The number of times to retry in the event of a network failure. Overrides SPECWRK_NETWORK_RETRIES"
23
24
  end
24
25
 
25
- on_setup do |uri:, key:, run:, timeout:, **|
26
+ on_setup do |uri:, key:, run:, timeout:, network_retries:, **|
26
27
  ENV["SPECWRK_SRV_URI"] = uri
27
28
  ENV["SPECWRK_SRV_KEY"] = key
28
29
  ENV["SPECWRK_RUN"] = run
29
30
  ENV["SPECWRK_TIMEOUT"] = timeout
31
+ ENV["SPECWRK_NETWORK_RETRIES"] = network_retries
30
32
  end
31
33
  end
32
34
 
@@ -131,9 +133,11 @@ module Specwrk
131
133
 
132
134
  desc "Seed the server with a list of specs for the run"
133
135
  option :max_retries, default: 0, desc: "Number of times an example will be re-run should it fail"
134
- argument :dir, type: :array, required: false, default: "spec", desc: "Relative spec directory to run against"
136
+ argument :dir, type: :array, required: false, desc: "Relative spec directory to run against, default: spec/"
135
137
 
136
138
  def call(max_retries:, dir:, **args)
139
+ dir = ["spec"] if dir.length.zero?
140
+
137
141
  self.class.setup(**args)
138
142
 
139
143
  require "specwrk/list_examples"
@@ -208,9 +212,11 @@ module Specwrk
208
212
 
209
213
  desc "Start a server and workers, monitor until complete"
210
214
  option :max_retries, default: 0, desc: "Number of times an example will be re-run should it fail"
211
- argument :dir, required: false, type: :array, default: "spec", desc: "Relative spec directory to run against"
215
+ argument :dir, type: :array, required: false, desc: "Relative spec directory to run against, default: spec/"
212
216
 
213
217
  def call(max_retries:, dir:, **args)
218
+ dir = ["spec"] if dir.length.zero?
219
+
214
220
  self.class.setup(**args)
215
221
  $stdout.sync = true
216
222
 
@@ -115,16 +115,6 @@ module Specwrk
115
115
  else
116
116
  raise UnhandledResponseError.new("#{response.code}: #{response.body}")
117
117
  end
118
- rescue Net::OpenTimeout, Net::ReadTimeout, Net::WriteTimeout => e
119
- @retry_count ||= 0
120
- @retry_count += 1
121
-
122
- raise e if @retry_count == 5
123
-
124
- warn e
125
- sleep @retry_count
126
-
127
- retry
128
118
  end
129
119
 
130
120
  def seed(examples, max_retries)
@@ -172,6 +162,16 @@ module Specwrk
172
162
  @worker_status = response["x-specwrk-status"].to_i if response["x-specwrk-status"]
173
163
  end
174
164
  end
165
+ rescue Net::ReadTimeout, Net::WriteTimeout => e
166
+ @retry_count ||= 0
167
+
168
+ raise e if @retry_count == ENV["SPECWRK_NETWORK_RETRIES"].to_i
169
+ @retry_count += 1
170
+
171
+ warn e
172
+ sleep @retry_count
173
+
174
+ retry
175
175
  end
176
176
 
177
177
  def default_headers
data/lib/specwrk/store.rb CHANGED
@@ -105,6 +105,47 @@ module Specwrk
105
105
  end
106
106
  end
107
107
 
108
+ class WorkerStore < Store
109
+ FIRST_SEEN_AT_KEY = :____first_seen_at_key
110
+ LAST_SEEN_AT_KEY = :____last_seen_at_key
111
+
112
+ def first_seen_at=(val)
113
+ @first_seen_at = nil
114
+
115
+ self[FIRST_SEEN_AT_KEY] = val.to_i
116
+ end
117
+
118
+ def first_seen_at
119
+ @first_seen_at ||= begin
120
+ value = self[FIRST_SEEN_AT_KEY]
121
+ return @first_seen_at = value unless value
122
+
123
+ @first_seen_at = Time.at(value.to_i)
124
+ end
125
+ end
126
+
127
+ def last_seen_at=(val)
128
+ @last_seen_at = nil
129
+
130
+ self[LAST_SEEN_AT_KEY] = val.to_i
131
+ end
132
+
133
+ def last_seen_at
134
+ @last_seen_at ||= begin
135
+ value = self[LAST_SEEN_AT_KEY]
136
+ return @last_seen_at = value unless value
137
+
138
+ @last_seen_at = Time.at(value.to_i)
139
+ end
140
+ end
141
+
142
+ def reload
143
+ @last_seen_at = nil
144
+ @first_seen_at = nil
145
+ super
146
+ end
147
+ end
148
+
108
149
  class PendingStore < Store
109
150
  RUN_TIME_BUCKET_MAXIMUM_KEY = :____run_time_bucket_maximum
110
151
  ORDER_KEY = :____order
@@ -234,22 +275,6 @@ module Specwrk
234
275
  end
235
276
 
236
277
  class ProcessingStore < Store
237
- def expired
238
- @expired ||= begin
239
- bucket = []
240
-
241
- keys.each_slice(24).each do |key_group|
242
- examples = multi_read(*key_group)
243
- examples.each do |id, example|
244
- next if example[:completion_threshold].nil?
245
-
246
- bucket << [id, example] if example[:completion_threshold] < Time.now.to_i
247
- end
248
- end
249
-
250
- bucket.to_h
251
- end
252
- end
253
278
  end
254
279
 
255
280
  class CompletedStore < Store
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Specwrk
4
- VERSION = "0.15.9"
4
+ VERSION = "0.16.0"
5
5
  end
@@ -21,8 +21,8 @@ module Specwrk
21
21
 
22
22
  before_lock
23
23
 
24
- worker[:first_seen_at] ||= Time.now.iso8601
25
- worker[:last_seen_at] = Time.now.iso8601
24
+ worker.first_seen_at ||= Time.now
25
+ worker.last_seen_at = Time.now
26
26
 
27
27
  final_response = with_lock do
28
28
  started_at = metadata[:started_at] ||= Time.now.iso8601
@@ -109,7 +109,11 @@ module Specwrk
109
109
  end
110
110
 
111
111
  def worker
112
- @worker ||= Store.new(ENV.fetch("SPECWRK_SRV_STORE_URI", "memory:///"), File.join(run_id, "workers", request.get_header("HTTP_X_SPECWRK_ID").to_s))
112
+ @worker ||= worker_store_for(worker_id)
113
+ end
114
+
115
+ def worker_id
116
+ request.get_header("HTTP_X_SPECWRK_ID").to_s
113
117
  end
114
118
 
115
119
  def worker_status
@@ -118,6 +122,10 @@ module Specwrk
118
122
  worker[:failed] || 1
119
123
  end
120
124
 
125
+ def worker_store_for(id)
126
+ WorkerStore.new(ENV.fetch("SPECWRK_SRV_STORE_URI", "memory:///"), File.join(run_id, "workers", id))
127
+ end
128
+
121
129
  def run_id
122
130
  request.get_header("HTTP_X_SPECWRK_RUN")
123
131
  end
@@ -15,9 +15,9 @@ module Specwrk
15
15
  [204, {"content-type" => "text/plain"}, ["Waiting for sample to be seeded."]]
16
16
  elsif completed.any? && processing.empty?
17
17
  [410, {"content-type" => "text/plain"}, ["That's a good lad. Run along now and go home."]]
18
- elsif processing.any? && processing.expired.keys.any?
19
- pending.merge!(processing.expired)
20
- processing.delete(*processing.expired.keys)
18
+ elsif expired_examples.length.positive?
19
+ pending.merge!(expired_examples.each { |_id, example| example[:worker_id] = worker_id })
20
+ processing.delete(*expired_examples.keys)
21
21
  @examples = nil
22
22
 
23
23
  [200, {"content-type" => "application/json"}, [JSON.generate(examples)]]
@@ -29,13 +29,10 @@ module Specwrk
29
29
  def examples
30
30
  @examples ||= begin
31
31
  examples = pending.shift_bucket
32
- bucket_run_time_total = examples.map { |example| example.fetch(:expected_run_time, 10.0) }.compact.sum * 2
33
- maximum_completion_threshold = (pending.run_time_bucket_maximum || 30.0) * 2
34
- completion_threshold = Time.now + [bucket_run_time_total, maximum_completion_threshold, 20.0].max
35
32
 
36
33
  processing_data = examples.map do |example|
37
34
  [
38
- example[:id], example.merge(completion_threshold: completion_threshold.to_f)
35
+ example[:id], example.merge(worker_id: worker_id, processing_started_at: Time.now.to_i)
39
36
  ]
40
37
  end
41
38
 
@@ -44,6 +41,27 @@ module Specwrk
44
41
  examples
45
42
  end
46
43
  end
44
+
45
+ def expired_examples
46
+ return unless processing.any?
47
+
48
+ @expired_examples ||= processing.to_h.select { |_id, example| expired?(example) }
49
+ end
50
+
51
+ # Has the worker missed two heartbeat check-ins?
52
+ def expired?(example)
53
+ return false unless example[:worker_id]
54
+ return false unless example[:processing_started_at]
55
+ return false unless example[:processing_started_at] < (Time.now - 20).to_i
56
+
57
+ workers_last_heartbeats[example[:worker_id]] < Time.now - 20
58
+ end
59
+
60
+ def workers_last_heartbeats
61
+ @workers_last_heartbeats ||= Hash.new do |h, k|
62
+ h[k] = worker_store_for(k).last_seen_at || Time.at(0)
63
+ end
64
+ end
47
65
  end
48
66
  end
49
67
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: specwrk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.9
4
+ version: 0.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Westendorf