Skip to content

Stub `ForkedStorageCheck.storage_available?` by default in all specs

Robert Speicher requested to merge rs-stub-storage-availability-check into master

While looking to see what I could do to speed up the spec/features/security/* specs, I ran them with bin/rspec-stackprof and noticed this:

gitlab-ce master % ES bin/rspec-stackprof spec/features/security/project/public_access_spec.rb

Finished in 2 minutes 6.4 seconds (files took 5.16 seconds to load)
306 examples, 0 failures

Randomized with seed 22643

==================================
  Mode: wall(1000)
  Samples: 107374 (12.97% miss rate)
  GC: 10300 (9.59%)
==================================
     TOTAL    (pct)     SAMPLES    (pct)     FRAME
     19766  (18.4%)       19718  (18.4%)     Gitlab::Git::Storage::ForkedStorageCheck#timeout_check
     10278   (9.6%)        5730   (5.3%)     ActionView::PathResolver#find_template_paths
      8443   (7.9%)        4217   (3.9%)     ActionDispatch::Journey::Formatter#missing_keys
      8590   (8.0%)        2956   (2.8%)     Sprockets::Cache::FileStore#get
      2811   (2.6%)        2530   (2.4%)     Peek::PGInstrumented#async_exec
      2757   (2.6%)        2289   (2.1%)     Peek::PGInstrumented#exec_prepared
      6627   (6.2%)        1828   (1.7%)     ActiveSupport::FileUpdateChecker#max_mtime
      1775   (1.7%)        1698   (1.6%)     GRPC::ActiveCall#request_response
      1629   (1.5%)        1629   (1.5%)     #<Module:0x007ffcc0812ee0>.distance
      1615   (1.5%)        1516   (1.4%)     GRPC::ActiveCall#remote_read
      1522   (1.4%)        1167   (1.1%)     Time#to_time
       969   (0.9%)         969   (0.9%)     URI::RFC3986_Parser#split
      1150   (1.1%)         935   (0.9%)     ActiveRecord::LazyAttributeHash#[]
      1016   (0.9%)         865   (0.8%)     ActionDispatch::Journey::Path::Pattern#match
       760   (0.7%)         760   (0.7%)     ThreadSafe::NonConcurrentCacheBackend#[]
       695   (0.6%)         695   (0.6%)     #<Module:0x007ffcc00a66c0>.load_with_autoloading
       608   (0.6%)         608   (0.6%)     ActiveSupport::PerThreadRegistry#instance
       587   (0.5%)         587   (0.5%)     Gon.public_method_name?
       793   (0.7%)         565   (0.5%)     ActiveSupport::FileUpdateChecker#watched
       560   (0.5%)         560   (0.5%)     Sprockets::PathUtils#stat

So I benchmarked the method with 250 iterations, and then compared it against a stub of the method that just returns true straight away:

n = 250

path = File.expand_path("../tmp/tests/repositories", __dir__)

Benchmark.bm(20) do |x|
  x.report("unstubbed") do
    n.times { Gitlab::Git::Storage::ForkedStorageCheck.storage_available?(path, 5) }
  end
  x.report("stubbed") do
    allow(Gitlab::Git::Storage::ForkedStorageCheck)
      .to receive(:storage_available?).and_return(true)
    n.times { Gitlab::Git::Storage::ForkedStorageCheck.storage_available?(path, 5) }
  end
end

#                           user     system      total        real
# unstubbed              0.410000   0.350000  90.710000 ( 93.097250)
# stubbed                0.010000   0.000000   0.010000 (  0.011941)

Obviously a pretty big difference.

This MR stubs that method globally in the test suite unless an example includes the check_storage_availability metadata.

Here's the "after" for that file:

Finished in 1 minute 39.1 seconds (files took 5.75 seconds to load)
306 examples, 0 failures

Randomized with seed 4500

==================================
  Mode: wall(1000)
  Samples: 81576 (16.17% miss rate)
  GC: 8437 (10.34%)
==================================
     TOTAL    (pct)     SAMPLES    (pct)     FRAME
     10197  (12.5%)        5662   (6.9%)     ActionView::PathResolver#find_template_paths
      8651  (10.6%)        4321   (5.3%)     ActionDispatch::Journey::Formatter#missing_keys
      7985   (9.8%)        2705   (3.3%)     Sprockets::Cache::FileStore#get
      2674   (3.3%)        2359   (2.9%)     Peek::PGInstrumented#async_exec
      2610   (3.2%)        2159   (2.6%)     Peek::PGInstrumented#exec_prepared
      6240   (7.6%)        1734   (2.1%)     ActiveSupport::FileUpdateChecker#max_mtime
      1710   (2.1%)        1637   (2.0%)     GRPC::ActiveCall#request_response
      1532   (1.9%)        1532   (1.9%)     #<Module:0x007fae2180aee0>.distance
      1575   (1.9%)        1500   (1.8%)     GRPC::ActiveCall#remote_read
      1406   (1.7%)        1054   (1.3%)     Time#to_time
       929   (1.1%)         929   (1.1%)     URI::RFC3986_Parser#split
       971   (1.2%)         829   (1.0%)     ActionDispatch::Journey::Path::Pattern#match
       983   (1.2%)         790   (1.0%)     ActiveRecord::LazyAttributeHash#[]
       692   (0.8%)         692   (0.8%)     #<Module:0x007fae208a66c0>.load_with_autoloading
       682   (0.8%)         682   (0.8%)     ThreadSafe::NonConcurrentCacheBackend#[]
       790   (1.0%)         566   (0.7%)     ActiveSupport::FileUpdateChecker#watched
       554   (0.7%)         554   (0.7%)     Sprockets::PathUtils#stat
       533   (0.7%)         533   (0.7%)     Gon.public_method_name?
      4147   (5.1%)         532   (0.7%)     Sprockets::Cache::FileStore#safe_open
       564   (0.7%)         529   (0.6%)     ActiveRecord::Inheritance::ClassMethods#base_class

This should provide a not-insignificant improvement to the speed of any test that uses an on-disk repository.

@reprazent What do you think about this? Should we be stubbing something further up the call chain, like disabling the Feature.enabled? check, which we globally set to true?

Edited by Robert Speicher

Merge request reports