Skip to content

Merge upstream metadata without an intermediate filtered hash - #32

Open
OskarEichler wants to merge 1 commit into
standardrb:mainfrom
OskarEichler:codex/stream-upstream-metadata
Open

Merge upstream metadata without an intermediate filtered hash#32
OskarEichler wants to merge 1 commit into
standardrb:mainfrom
OskarEichler:codex/stream-upstream-metadata

Conversation

@OskarEichler

Copy link
Copy Markdown

Summary

  • Merge shared upstream metadata directly into a copy of the plugin configuration, avoiding an intermediate filtered hash.
  • Keep the plugin value on nested conflicts without repeating key?: Hash#merge only invokes that block for a conflict.
  • Preserve rule selection, plugin precedence (including explicit nil/false), shallow merge semantics, key order and input ownership.

Why / measurement

The current implementation filters the full upstream hash, then merges the filtered hash, and rechecks membership for every nested conflict. The replacement removes that temporary hash and redundant callbacks/lookups.

Ruby 4.0.6 with YJIT, three alternating sequential processes per revision; each process takes the median of five batches of 1,000 merges. Synthetic configurations have two plugin keys and four upstream keys per rule:

Rules Before (s) After (s) Before / after allocations
10 0.002011 0.001894 12,000 / 11,000
100 0.019083 0.017071 102,000 / 101,000
1,000 0.193973 0.177861 1,002,000 / 1,001,000

This is a metadata-merge microbenchmark, not a claim about end-to-end linter speed.

A standalone benchmark input (run the same code on base and branch):

require "lint_roller"
plugin = {}; upstream = {}
1000.times do |i|
  plugin["Rule/#{i}"] = {"Enabled" => false, "Description" => nil}
  upstream["Rule/#{i}"] = {"Enabled" => true, "Description" => "description",
    "VersionAdded" => "1.0", "StyleGuide" => "https://example.com"}
end
merger = LintRoller::Support::MergesUpstreamMetadata.new
100.times { merger.merge(plugin, upstream) }
GC.start
allocations = GC.stat(:total_allocated_objects)
started = Process.clock_gettime(Process::CLOCK_MONOTONIC)
1000.times { merger.merge(plugin, upstream) }
p seconds: Process.clock_gettime(Process::CLOCK_MONOTONIC) - started,
  allocations: GC.stat(:total_allocated_objects) - allocations

Verification

  • Existing main suite: 10 tests / 20 assertions, zero failures/errors/skips before and after; bundle exec rake test.
  • bundle exec standardrb lib passes. Full non-correcting rake test standard retains the baseline Gemspec/DeprecatedAttributeAssignment offense at lint_roller.gemspec:22; I did not run the default auto-correcting task or edit tests.
  • 15,855 focused checks compare with the original implementation over seeded (602118) configurations: explicit false/nil, nested hashes/arrays, missing rules, frozen inputs, defaults, identity hashes and key order; also exercise the released plugin/context/rules API.
  • Release-based 1.1.0 backport: existing 9 tests / 19 assertions and all focused checks pass on Ruby 4.0.6 and 3.2.11. All eight runtime files compile on both. Gem packaging retains the published release's 15 paths and metadata; only this implementation and changelog differ.
  • No new or modified tests: the consuming project's task policy prohibits test changes. Focused verification was run in scratch scripts outside either repository.

Compatibility and limitations

Breaking changes: none intended for the Hash/YAML configuration API. The result remains a new outer hash, unselected rules are excluded, plugin values win, and nested values retain existing shallow ownership semantics. No dependencies, Ruby requirement or public API are changed here. Arbitrary Hash subclasses overriding core methods, Ruby 2.6/2.7 and non-MRI engines were not executed. Unreleased plugin-comparison and gemspec changes are excluded from the consumer backport.

@jasonkarns

Copy link
Copy Markdown
Contributor

I'll be looking at this soon. Can you provide a more human digestable rationale for what problem this is solving?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants