specbandit 1.1.1 → 1.2.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: 70efc56f9de13cd1e141cbb1ad39ff22cf1a07cdfbcd2c1257cefc55f5cb73fe
4
- data.tar.gz: 6070e8b72aec64162a3e37190e787abbd23bf464faf74d06212e401c7c312fed
3
+ metadata.gz: 24148d640548adc9f3bd6714ad39f4bfd380eeaf8e904f0fffd4b777a9b13d4c
4
+ data.tar.gz: '082673e08f9b40e9c8babae73f658d91b7627a40002b1e326d62fb49c5242d4f'
5
5
  SHA512:
6
- metadata.gz: c7fc604a65cba2387b1177fef33ad2994e13677777d6d87b53f68aa29a00616c864dc874b5571947c4e837d0c3e25b24cc4d99d0841c94b79433258e8f5faba7
7
- data.tar.gz: 7c6acd6833e1f082e2c764de7d32ab67407726d6905cc6246cbbe0921410f2ad3db4a503ee5932240414ebf413538830db54e8275adb4cd6c1f3d9bc71a195e4
6
+ metadata.gz: 06ced4b5b9530b9f836d90a0e05a663acf88d16f008c6f181acc19a1711ee6260465f42b7e27c76d3e16ec8d28ae621bfd4a614d2ec2aae26312927cb40fd3fb
7
+ data.tar.gz: 6f1578c3db2a446bce8677d16020014e4fa1c30e2483789f41311c1d18ba75567875a65728fca9f48136951b722b91631d674e995d2a66b1ebe26b99b910e128
data/README.md CHANGED
@@ -319,7 +319,7 @@ The full decision table:
319
319
  | Yes | Drained / empty | Empty | **OK, exit 0.** Worker arriving late -- everything was already taken by peers and this runner has no re-run memory. |
320
320
  | Yes | Has data | Empty | **Steal.** Classic run: pop batches from the shared queue (and record them to the rerun key if one is configured). |
321
321
  | Yes | Drained / empty | Has data | **Replay.** Classic re-run: ignore the shared queue and re-run exactly the recorded files. |
322
- | Yes | Has data | Has data | **Crash** (exit 1). Inconsistent state -- refuses to run to avoid double-executing. |
322
+ | Yes | Has data | Has data | **Full rerun.** The queue was re-pushed while this runner still holds rerun memory from a previous run. The stale rerun key is deleted and the runner steals from the shared queue like a classic run, re-recording as it goes. |
323
323
 
324
324
  > **Empty key names count as "not provided".** A `--key-rerun` whose value is an empty string -- e.g. `--key-rerun "$VAR"` where `$VAR` is unset in CI -- is treated exactly like `--key-rerun` being absent. The same applies to `--key-failed`: an empty/unset name is treated as "not configured" and no failed files are recorded.
325
325
 
@@ -351,7 +351,7 @@ The full decision table:
351
351
 
352
352
  Key details:
353
353
 
354
- - **Replay reads non-destructively** (`LRANGE`, not `LPOP`). The rerun key is never consumed. If you re-run the same runner multiple times, it replays the same files every time.
354
+ - **Replay reads non-destructively** (`LRANGE`, not `LPOP`). Replay never consumes the rerun key, so re-running the same runner multiple times replays the same files every time. The one case where the rerun key is deleted (`DEL`) is a **full rerun** -- the shared queue has data again while the rerun key still holds files from a previous run -- where the stale memory is reset and rebuilt from the newly stolen batches.
355
355
  - **The shared queue is never touched in replay mode**. Other runners are unaffected.
356
356
  - **Each runner has its own rerun key**. Only the re-run runner enters replay mode; runners that aren't re-run don't start at all.
357
357
 
@@ -429,6 +429,7 @@ specbandit push --key "pr-42-run-100" --key-ttl 259200 --pattern 'spec/**/*_spec
429
429
  - **Steal** uses `LPOP key count` (Redis 6.2+), which atomically pops up to N elements. No Lua scripts, no locks, no race conditions.
430
430
  - **Record** (when `--key-rerun` is set): after each steal, the batch is also `RPUSH`ed to the per-runner rerun key.
431
431
  - **Replay** (when the rerun key has data and the shared queue is drained): reads all files from the rerun key via `LRANGE` (non-destructive), splits into batches, and runs them locally. The shared queue is never touched.
432
+ - **Full rerun** (when both the shared queue and the rerun key have data): the stale rerun key is removed with `DEL`, then the runner steals from the shared queue and re-records as in a classic run.
432
433
  - **Mode selection** is derived entirely from Redis (published marker + queue/rerun state) -- see the decision table above. There is no re-run flag or environment variable.
433
434
  - **Run** delegates to the configured adapter:
434
435
  - **CLI adapter**: spawns a shell command per batch via `Open3`, appending file paths as arguments. Works with any test runner.
@@ -88,6 +88,12 @@ module Specbandit
88
88
  with_retries { redis.lrange(key, 0, -1) }
89
89
  end
90
90
 
91
+ # Remove a key entirely. Used to discard stale rerun memory when a
92
+ # full rerun starts over from the shared queue.
93
+ def delete(key)
94
+ with_retries { redis.del(key) }
95
+ end
96
+
91
97
  def close
92
98
  redis.close
93
99
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Specbandit
4
- VERSION = '1.1.1'
4
+ VERSION = '1.2.0'
5
5
  end
@@ -76,7 +76,7 @@ module Specbandit
76
76
  # Yes | empty/drained | empty | OK: worker arriving late (0)
77
77
  # Yes | has data | empty | Steal (record if rerun key set)
78
78
  # Yes | empty/drained | has data | Replay recorded files
79
- # Yes | has data | has data | Crash: inconsistent (weird case)
79
+ # Yes | has data | has data | Full rerun: reset rerun key, steal
80
80
  #
81
81
  # "Published" is a durable marker written by `specbandit push`; it is the
82
82
  # only reliable signal that work was ever enqueued, because Redis
@@ -89,7 +89,7 @@ module Specbandit
89
89
  rerun_files = key_present?(key_rerun) ? queue.read_all(key_rerun) : []
90
90
 
91
91
  if key_has_data && rerun_files.any?
92
- fail_inconsistent_state
92
+ run_full_rerun
93
93
  elsif rerun_files.any?
94
94
  run_replay(rerun_files)
95
95
  elsif key_has_data
@@ -119,14 +119,16 @@ module Specbandit
119
119
  1
120
120
  end
121
121
 
122
- # Both the shared queue and this runner's rerun key hold files at once.
123
- # That should never happen: a fresh run has no rerun memory yet, and a
124
- # re-run reads from a drained queue. Crash instead of double-executing.
125
- def fail_inconsistent_state
126
- output.puts "[specbandit] ERROR: inconsistent state — shared queue '#{key}' still has files " \
127
- "while rerun key '#{key_rerun}' also has recorded files."
128
- output.puts '[specbandit] Refusing to run to avoid double-execution / undefined behavior.'
129
- 1
122
+ # Both the shared queue and this runner's rerun key hold files: the queue
123
+ # was re-pushed while this runner still carries rerun memory from a
124
+ # previous run (a full rerun). The stored memory is stale -- discard it
125
+ # and steal from the shared queue like a classic run, re-recording each
126
+ # stolen batch as we go.
127
+ def run_full_rerun
128
+ output.puts "[specbandit] Shared queue '#{key}' and rerun key '#{key_rerun}' both have files. " \
129
+ "Full rerun: resetting '#{key_rerun}' and working from '#{key}'."
130
+ queue.delete(key_rerun)
131
+ run_steal(record: true)
130
132
  end
131
133
 
132
134
  # Replay mode: run a known list of files in local batches.
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.1.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ferran Basora