Module: RSpec::OpenAPI::ParallelRecords
- Defined in:
- lib/rspec/openapi/parallel_records.rb
Overview
Collects records across processes when the test framework forks parallel
workers, like Rails' parallelize. Records accumulate in each worker's own
memory, so without this the main process would write the schema from an empty
set. Each worker dumps its records to a shared directory on exit, and the
main process merges every dump back in before recording results.
The dumps go through YAML.safe_load with an allowlist rather than Marshal, so reading them cannot instantiate arbitrary objects even if the directory were tampered with.
Constant Summary collapse
- MAIN_PID =
The pid of the process that loaded this gem. Forked workers inherit the constant, which is how they know they are not the main process.
Process.pid
- RUN_ID =
Unpredictable per-run token. Workers inherit it across fork, while other local users cannot guess it to pre-create or plant files in the dump directory, which lives in the world-writable system tmpdir.
SecureRandom.hex(16)
- UPLOADED_FILE_MARKER =
Marks the stand-in a multipart upload leaves in a dump. The schema builder only ever asks an upload for its class and metadata, never for its content, so the file itself does not need to survive the trip.
'__rspec_openapi_uploaded_file'
Class Method Summary collapse
- .dump! ⇒ Object
- .merge! ⇒ Object
-
.schedule_dump! ⇒ Object
Called when a worker records an example.
- .worker? ⇒ Boolean
Class Method Details
.dump! ⇒ Object
50 51 52 53 54 55 56 57 58 |
# File 'lib/rspec/openapi/parallel_records.rb', line 50 def dump! return if RSpec::OpenAPI.path_records.empty? FileUtils.mkdir_p(dump_dir, mode: 0o700) encoded = RSpec::OpenAPI.path_records.transform_values do |records| records.map { |record| transform_record(record) { |value| encode(value) } } end File.write(File.join(dump_dir, "#{Process.pid}.yaml"), YAML.dump(encoded)) end |
.merge! ⇒ Object
60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/rspec/openapi/parallel_records.rb', line 60 def merge! Dir.glob(File.join(dump_dir, '*.yaml')).sort.each do |file| records_by_path = YAML.safe_load(File.read(file), permitted_classes: permitted_classes, aliases: true) records_by_path.each do |path, records| decoded = records.map { |record| transform_record(record) { |value| decode(value) } } RSpec::OpenAPI.path_records[path].concat(decoded) end end ensure FileUtils.rm_rf(dump_dir) end |
.schedule_dump! ⇒ Object
Called when a worker records an example. Registered lazily from inside the worker because an at_exit inherited from the main process has already run there by the time workers fork: minitest executes the whole suite from an at_exit hook, so handlers the gem registered at load time are popped before any fork happens.
43 44 45 46 47 48 |
# File 'lib/rspec/openapi/parallel_records.rb', line 43 def schedule_dump! return if @dump_scheduled || !worker? @dump_scheduled = true at_exit { dump! } end |
.worker? ⇒ Boolean
34 35 36 |
# File 'lib/rspec/openapi/parallel_records.rb', line 34 def worker? Process.pid != MAIN_PID end |