Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deduplicate various Active Record schema cache structures #35891

Merged
merged 1 commit into from Jun 19, 2019

Conversation

Projects
None yet
3 participants
@casperisfine
Copy link

commented Apr 8, 2019

Superseeds: #35860 and #35855
Also #35875 is important for limiting the performance impact of the deduplication.

Summary

Real world database schemas contain a lot of duplicated data.
Some column names like id, created_at etc can easily be repeated
hundreds of times. Same for SqlTypeMetada, most database will contain
only a limited number of possible combinations.

This result in a lot of wasted memory.

The idea here is to make these data sctructures immutable, use a registry
to substitute similar instances with pre-existing ones.

Implementation details

This is mostly a refactor of #35860, instead of using the constructor arguments as cache key, I first instantiate the structure, and then look them up in the registry.

This makes for much cleaner code, and also provide a hook for deduplicating the internal values like #35860 did, which save a lot of CPU compared to deduplicating them on instantiation.

I also update the SchemaCache class to trigger the deduplication after the YAML schema has been loaded, this is because YAML use the allocate.init_with(coder) interface, hence bypassing the default constructor.

I'll add more comments on the diff.

Efficiency

I posted various memory and CPU profiles in the related PRs. Today I shipped this PR as monkey patch on our app, and the resident memory difference is absolutely massive.

@rafaelfranca @kaspth @Edouard-chin @csfrancis

@rails-bot rails-bot bot added the activerecord label Apr 8, 2019

@@ -26,6 +28,17 @@ def initialize(name, default, sql_type_metadata = nil, null = true, table_name =
@comment = comment
end

def deduplicated
@name = -name
@table_name = -table_name if table_name

This comment has been minimized.

Copy link
@casperisfine

casperisfine Apr 8, 2019

Author

There are some test which instantiate a column without a table_name, I haven't been able to figure out if that's a realistic scenario or not. It seem to be related to dynamic columns when using AR#select().

This makes me wonder if there's a risk of memory leak, some applications could possibly generate very dynamic queries and create new columns over and over.

@@ -77,7 +90,7 @@ def hash
protected

def attributes_for_hash
[self.class, name, default, sql_type_metadata, null, table_name, default_function, collation]
[self.class, name, name.encoding, default, sql_type_metadata, null, table_name, default_function, collation]

This comment has been minimized.

Copy link
@casperisfine

casperisfine Apr 8, 2019

Author

This made a column encoding test fail. Turns out String#hash totally ignores the String#encoding.

>> "foo".force_encoding('UTF-8').hash == "foo".force_encoding('EUC-JP').hash
=> true

Sounds a bit like an MRI bug to me.

attr_reader :extra

def initialize(type_metadata, extra: "")
def initialize(type_metadata, extra: nil)

This comment has been minimized.

Copy link
@casperisfine

casperisfine Apr 8, 2019

Author

The vast majority of columns won't have an extra, nil seems a much better default value as we won't have to deduplicate an empty string.

end.to_h
@primary_keys = @primary_keys.map { |table_name, column_name| [-table_name, -column_name] }.to_h
@data_sources = @data_sources.transform_keys { |table_name| -table_name }
@indexes = @indexes.map { |table_name, indexes| [-table_name, indexes] }.to_h

This comment has been minimized.

Copy link
@casperisfine

casperisfine Apr 8, 2019

Author

I think we should make IndexDefinition Deduplicable as well, but it doesn't have a == nor hash method, so I think it would be better handled in a followup.

This comment has been minimized.

Copy link
@kaspth

kaspth Apr 12, 2019

Member

Agreed on IndexDefinition!

I find this code quite hard to parse. The meaning is hidden behind the processing and the individual - are lost — plus the differences for when to use deduplicate. Would something like this be clearer?

  @columns      = deep_deduplicate(@columns) { |columns| columns.map(&:deduplicate) }
  @columns_hash = deep_deduplicate(@columns_hash) { |columns| deep_deduplicate(columns, &:deduplicate) }
  @primary_keys = deep_deduplicate(@primary_keys)
  @data_sources = deep_deduplicate(@data_sources, &:itself)
  @indexes      = deep_deduplicate(@indexes, &:itself)
end

def deep_deduplicate(values, &block)
  block ||= :-@.to_proc
  values.transform_keys(&:-@).transform_values(&block)
end

The :-@.to_proc should probably be cleaned up a bit somehow. Also not sure how wasteful it is to loop through the probably gargantuan hashes twice.

Another thing we could do is make the deduplicateable objects respond to -@ instead of deduplicate. Then we don't have to make the difference between them and follow Ruby's new semantics.

This comment has been minimized.

Copy link
@casperisfine

casperisfine Apr 13, 2019

Author

Another thing we could do is make the deduplicateable objects respond to -@ instead of deduplicate. Then we don't have to make the difference between them and follow Ruby's new semantics.

That's what I wanted to do initially, however I though that since numeric types respond to that same interface for a very different meaning made me reconsider it. So I'm split on this.

This comment has been minimized.

Copy link
@casperisfine

casperisfine Apr 13, 2019

Author

Agreed on IndexDefinition!

Actually I wonder about these. Turns out our internal SchemaCache implementation doesn't persist them, our prod workers have 0 of them in memory. So I wonder how useful they actually are.

This comment has been minimized.

Copy link
@kaspth

kaspth Jun 19, 2019

Member

iirc, indexes were added to the cache in Rails 6.

@@ -32,7 +32,9 @@ def merge_column(table_name, name, sql_type = nil, options = {})
name.to_s,
options[:default],
fetch_type_metadata(sql_type),
options[:null])
options[:null],
table_name,

This comment has been minimized.

Copy link
@casperisfine

casperisfine Apr 8, 2019

Author

This test wan't passing the table name, resulting in a quite unrealistic scenario (IMHO)

c2 = Post.connection.schema_cache.columns("posts")
c1.each_with_index do |v, i|
assert_not_same v, c2[i]

This comment has been minimized.

Copy link
@casperisfine

casperisfine Apr 8, 2019

Author

The interning made this way of testing the schema was reloaded fail.

I refactored the test, I believe it still offer the same confidence.

column :created_at, 'datetime'
column :awesome, 'boolean'
column :preferences, 'string'
column :alternative_id, 'integer'

This comment has been minimized.

Copy link
@casperisfine

casperisfine Apr 8, 2019

Author

I might be wrong but I believe sql_type is never a symbol in real world scenario, so I took the liberty to refactor this test data.

@casperisfine casperisfine force-pushed the Shopify:schema-cache-deduplication branch 2 times, most recently from 7853c1d to 019b712 Apr 9, 2019

@casperisfine

This comment has been minimized.

Copy link
Author

commented Apr 9, 2019

Rebased to fix conflict with #35890

Now that Column instance no longer contain the table name, the deduplication should be even more efficient.

@casperisfine

This comment has been minimized.

Copy link
Author

commented Apr 9, 2019

So with #35890 profiles show that retained ActiveRecord::ConnectionAdapters::MySQL::Column instances have been further reduced from 7768 (807.89 kB) to 2550 (265.22 kB). Not too bad!

end

def deduplicate
self.class.registry[self] ||= self.deduplicated

This comment has been minimized.

Copy link
@kaspth

kaspth Apr 12, 2019

Member

self.deduplicated can just be deduplicated, no? Also don't see why deduplicated should be a public method.

This comment has been minimized.

Copy link
@casperisfine

casperisfine Apr 13, 2019

Author

Yeah good point, I missed that.

@casperisfine casperisfine force-pushed the Shopify:schema-cache-deduplication branch 3 times, most recently from 5585c3a to 4d722f2 Apr 15, 2019

@casperisfine

This comment has been minimized.

Copy link
Author

commented Apr 15, 2019

I rebased following @kamipo 's refactor in 59d32a6 and also based on @kaspth suggestions.

@casperisfine casperisfine force-pushed the Shopify:schema-cache-deduplication branch 3 times, most recently from 46ee966 to 0fccfce Apr 15, 2019

@indexes ||= {}

@columns = deep_deduplicate(@columns)
@columns_hash = deep_deduplicate(@columns_hash)

This comment has been minimized.

Copy link
@casperisfine

casperisfine Apr 15, 2019

Author

@kaspth another thing: I think we should simply stop storing columns_hash in the schema_cache dump, it's very easily rebuilt from columns.

That would make parsing much faster, and would save lots of allocations.

This comment has been minimized.

Copy link
@kaspth

kaspth Jun 19, 2019

Member

I’d definitely be game to see a PR for that!

Deduplicate various Active Record schema cache structures
Real world database schemas contain a lot of duplicated data.
Some column names like `id`, `created_at` etc can easily be repeated
hundreds of times. Same for SqlTypeMetada, most database will contain
only a limited number of possible combinations.

This result in a lot of wasted memory.

The idea here is to make these data sctructures immutable, use a registry
to substitute similar instances with pre-existing ones.

@casperisfine casperisfine force-pushed the Shopify:schema-cache-deduplication branch from 0fccfce to 17acb77 Jun 3, 2019

@kaspth kaspth merged commit aae270d into rails:master Jun 19, 2019

2 checks passed

buildkite/rails Build #61459 passed (8 minutes, 50 seconds)
Details
codeclimate All good!
Details
@kaspth

This comment has been minimized.

Copy link
Member

commented Jun 19, 2019

Love it!

yahonda added a commit to yahonda/oracle-enhanced that referenced this pull request Jun 19, 2019

yahonda added a commit to yahonda/oracle-enhanced that referenced this pull request Jun 20, 2019

yahonda added a commit to yahonda/oracle-enhanced that referenced this pull request Jun 20, 2019

yahonda added a commit to yahonda/oracle-enhanced that referenced this pull request Jun 20, 2019

Deduplicate various Active Record schema cache structures
Refer rails/rails#35891

This pulll request addresses 28 failures. Here is the one of them:

You can see entire failures at https://travis-ci.org/rsim/oracle-enhanced/jobs/547963138

```
$ bundle exec rspec ./spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb:62
==> Loading config from ENV or use default
==> Running specs with MRI version 2.6.3
==> Effective ActiveRecord version 6.1.0.alpha
Run options: include {:locations=>{"./spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb"=>[62]}}
F

Failures:

  1) OracleEnhancedAdapter cache table columns without column caching should identify virtual columns as such
     Failure/Error: delegate :virtual, to: :sql_type_metadata, allow_nil: true

     NoMethodError:
       undefined method `virtual' for #<ActiveRecord::ConnectionAdapters::SqlTypeMetadata:0x00005564d0332fb8>
     # ./lib/active_record/connection_adapters/oracle_enhanced/column.rb:7:in `virtual'
     # ./lib/active_record/connection_adapters/oracle_enhanced/column.rb:14:in `virtual?'
     # ./spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb:64:in `each'
     # ./spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb:64:in `detect'
     # ./spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb:64:in `block (4 levels) in <top (required)>'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/example.rb:254:in `instance_exec'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/example.rb:254:in `block in run'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/example.rb:500:in `block in with_around_and_singleton_context_hooks'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/example.rb:457:in `block in with_around_example_hooks'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/hooks.rb:464:in `block in run'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/hooks.rb:602:in `run_around_example_hooks_for'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/hooks.rb:464:in `run'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/example.rb:457:in `with_around_example_hooks'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/example.rb:500:in `with_around_and_singleton_context_hooks'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/example.rb:251:in `run'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/example_group.rb:629:in `block in run_examples'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/example_group.rb:625:in `map'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/example_group.rb:625:in `run_examples'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/example_group.rb:591:in `run'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/example_group.rb:592:in `block in run'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/example_group.rb:592:in `map'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/example_group.rb:592:in `run'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/example_group.rb:592:in `block in run'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/example_group.rb:592:in `map'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/example_group.rb:592:in `run'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/runner.rb:116:in `block (3 levels) in run_specs'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/runner.rb:116:in `map'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/runner.rb:116:in `block (2 levels) in run_specs'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/configuration.rb:2008:in `with_suite_hooks'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/runner.rb:111:in `block in run_specs'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/reporter.rb:74:in `report'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/runner.rb:110:in `run_specs'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/runner.rb:87:in `run'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/runner.rb:71:in `run'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/runner.rb:45:in `invoke'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/exe/rspec:4:in `<top (required)>'
     # /home/yahonda/.rbenv/versions/2.6.3/bin/rspec:23:in `load'
     # /home/yahonda/.rbenv/versions/2.6.3/bin/rspec:23:in `<top (required)>'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/bundler-1.17.3/lib/bundler/cli/exec.rb:74:in `load'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/bundler-1.17.3/lib/bundler/cli/exec.rb:74:in `kernel_load'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/bundler-1.17.3/lib/bundler/cli/exec.rb:28:in `run'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/bundler-1.17.3/lib/bundler/cli.rb:463:in `exec'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/bundler-1.17.3/lib/bundler/vendor/thor/lib/thor/command.rb:27:in `run'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/bundler-1.17.3/lib/bundler/vendor/thor/lib/thor/invocation.rb:126:in `invoke_command'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/bundler-1.17.3/lib/bundler/vendor/thor/lib/thor.rb:387:in `dispatch'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/bundler-1.17.3/lib/bundler/cli.rb:27:in `dispatch'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/bundler-1.17.3/lib/bundler/vendor/thor/lib/thor/base.rb:466:in `start'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/bundler-1.17.3/lib/bundler/cli.rb:18:in `start'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/bundler-1.17.3/exe/bundle:30:in `block in <top (required)>'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/bundler-1.17.3/lib/bundler/friendly_errors.rb:124:in `with_friendly_errors'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/bundler-1.17.3/exe/bundle:22:in `<top (required)>'
     # /home/yahonda/.rbenv/versions/2.6.3/bin/bundle:23:in `load'
     # /home/yahonda/.rbenv/versions/2.6.3/bin/bundle:23:in `<main>'

Finished in 2.7 seconds (files took 1.1 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb:62 # OracleEnhancedAdapter cache table columns without column caching should identify virtual columns as such

Coverage report generated for RSpec to /home/yahonda/git/oracle-enhanced/coverage. 1019 / 2186 LOC (46.61%) covered.
$
```

yahonda added a commit to yahonda/oracle-enhanced that referenced this pull request Jun 20, 2019

Deduplicate various Active Record schema cache structures
Refer rails/rails#35891

This pulll request addresses 28 failures. Here is the one of them:

You can see entire failures at https://travis-ci.org/rsim/oracle-enhanced/jobs/547963138

```
$ bundle exec rspec ./spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb:62
==> Loading config from ENV or use default
==> Running specs with MRI version 2.6.3
==> Effective ActiveRecord version 6.1.0.alpha
Run options: include {:locations=>{"./spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb"=>[62]}}
F

Failures:

  1) OracleEnhancedAdapter cache table columns without column caching should identify virtual columns as such
     Failure/Error: delegate :virtual, to: :sql_type_metadata, allow_nil: true

     NoMethodError:
       undefined method `virtual' for #<ActiveRecord::ConnectionAdapters::SqlTypeMetadata:0x00005564d0332fb8>
     # ./lib/active_record/connection_adapters/oracle_enhanced/column.rb:7:in `virtual'
     # ./lib/active_record/connection_adapters/oracle_enhanced/column.rb:14:in `virtual?'
     # ./spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb:64:in `each'
     # ./spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb:64:in `detect'
     # ./spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb:64:in `block (4 levels) in <top (required)>'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/example.rb:254:in `instance_exec'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/example.rb:254:in `block in run'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/example.rb:500:in `block in with_around_and_singleton_context_hooks'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/example.rb:457:in `block in with_around_example_hooks'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/hooks.rb:464:in `block in run'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/hooks.rb:602:in `run_around_example_hooks_for'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/hooks.rb:464:in `run'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/example.rb:457:in `with_around_example_hooks'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/example.rb:500:in `with_around_and_singleton_context_hooks'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/example.rb:251:in `run'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/example_group.rb:629:in `block in run_examples'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/example_group.rb:625:in `map'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/example_group.rb:625:in `run_examples'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/example_group.rb:591:in `run'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/example_group.rb:592:in `block in run'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/example_group.rb:592:in `map'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/example_group.rb:592:in `run'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/example_group.rb:592:in `block in run'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/example_group.rb:592:in `map'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/example_group.rb:592:in `run'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/runner.rb:116:in `block (3 levels) in run_specs'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/runner.rb:116:in `map'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/runner.rb:116:in `block (2 levels) in run_specs'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/configuration.rb:2008:in `with_suite_hooks'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/runner.rb:111:in `block in run_specs'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/reporter.rb:74:in `report'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/runner.rb:110:in `run_specs'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/runner.rb:87:in `run'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/runner.rb:71:in `run'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/lib/rspec/core/runner.rb:45:in `invoke'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/rspec-core-3.8.1/exe/rspec:4:in `<top (required)>'
     # /home/yahonda/.rbenv/versions/2.6.3/bin/rspec:23:in `load'
     # /home/yahonda/.rbenv/versions/2.6.3/bin/rspec:23:in `<top (required)>'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/bundler-1.17.3/lib/bundler/cli/exec.rb:74:in `load'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/bundler-1.17.3/lib/bundler/cli/exec.rb:74:in `kernel_load'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/bundler-1.17.3/lib/bundler/cli/exec.rb:28:in `run'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/bundler-1.17.3/lib/bundler/cli.rb:463:in `exec'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/bundler-1.17.3/lib/bundler/vendor/thor/lib/thor/command.rb:27:in `run'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/bundler-1.17.3/lib/bundler/vendor/thor/lib/thor/invocation.rb:126:in `invoke_command'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/bundler-1.17.3/lib/bundler/vendor/thor/lib/thor.rb:387:in `dispatch'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/bundler-1.17.3/lib/bundler/cli.rb:27:in `dispatch'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/bundler-1.17.3/lib/bundler/vendor/thor/lib/thor/base.rb:466:in `start'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/bundler-1.17.3/lib/bundler/cli.rb:18:in `start'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/bundler-1.17.3/exe/bundle:30:in `block in <top (required)>'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/bundler-1.17.3/lib/bundler/friendly_errors.rb:124:in `with_friendly_errors'
     # /home/yahonda/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/bundler-1.17.3/exe/bundle:22:in `<top (required)>'
     # /home/yahonda/.rbenv/versions/2.6.3/bin/bundle:23:in `load'
     # /home/yahonda/.rbenv/versions/2.6.3/bin/bundle:23:in `<main>'

Finished in 2.7 seconds (files took 1.1 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb:62 # OracleEnhancedAdapter cache table columns without column caching should identify virtual columns as such

Coverage report generated for RSpec to /home/yahonda/git/oracle-enhanced/coverage. 1019 / 2186 LOC (46.61%) covered.
$
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.