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
Define adapter type maps statically when possible #42773
Conversation
a919c95
to
75a5547
Compare
Each type map can use a non trivial amount of memory (over 10KiB in our app). Currently each connection build its own type map from scratch, but except for postgresql which has extension types, all connections end up with the same maps. So the more connections you have the more memory it wastes. By defining the type map statically for MySQL and SQLite3 connections we save some memory, share caches, and allow that memory to be handled by Copy on Write for forking setups.
75a5547
to
d79fb96
Compare
So after digging a bit more, this might be a bit over reported since quite a few values are shared (e.g. frozen strings). But still, after loading a good chunks of our models:
So we're not that far off, but clearly a good part of that is the |
Which also begs the question of why we were even using these since each connection had its own private |
cc @yahonda since you seem to be the maintainer of https://github.com/rsim/oracle-enhanced, and I think this PR breaks it. cc @wpolicarpo since you seem to be the maintainer of https://github.com/rails-sqlserver/activerecord-sqlserver-adapter. I'm not sure this PR breaks it, but I'd recommend applying a similar change in your gem. not sure if there are other popular 3rd party adapters I should notify. |
Thanks for the heads-up. |
Nice. Thanks for letting us know. |
We had a few old v4.2 migrations that referenced a |
@chrisbloom7 I'm afraid I dont' quite understand what you are referring to. Would you have some more detailed example, or just point to the part of this patch you think introduced the breakage? |
@byroot we had a migration that looked like this:
After upgrading to a version of edge rails that included this change, that migration started failing in schema test builds that we run. It erred with |
Interestingly, |
Thank you for the extra info. I'll try to get to the bottom of this next week (I'm mostly AFK until Monday), unless someone beats me to it. |
I was able to get a reproduction script for this. There seem to be three conditions that cause this to fail:
To avoid setting up mysql2 in this reproduction script, I patched the sqlite3 adapter to have a parent TypeMap Reproduction Script# frozen_string_literal: true
require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
gem "rails", github: "rails/rails", branch: "main"
gem "sqlite3"
end
require "active_record"
require "minitest/autorun"
require "logger"
# This connection will do for database-independent bug reports.
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Schema.define do
create_table :payments, force: true
end
# Wrap TypeMap in a parent so it behaves roughly like the mysql2 TYPE_MAP_WITH_BOOLEAN
# https://github.com/rails/rails/blob/88ec15b850790a06bd8dcfe59ca05d865f458c7c/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb#L624
class ActiveRecord::ConnectionAdapters::SQLite3Adapter
private
def type_map
ActiveRecord::Type::TypeMap.new(super)
end
end
class Payment < ActiveRecord::Base
end
class ChangeAmountToAddScale < ActiveRecord::Migration[7.0]
def change
add_column :payments, :paid, :bool, default: false
end
end
class BugTest < Minitest::Test
def test_migration_up
ChangeAmountToAddScale.migrate(:up)
end
end |
@composerinteralia thank you! |
Opened #42786 |
#42773 introduced a regression where looking up an unregistered type on a TypeMap with a parent (like [mysql2 TYPE_MAP_WITH_BOOLEAN](https://github.com/rails/rails/blob/88ec15b850790a06bd8dcfe59ca05d865f458c7c/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb#L618) would cause a `LocalJumpError` This commit fixes the error by forwarding the default block when fetching from the parent TypeMap. Co-authored-by: Chris Bloom <chrisbloom7@gmail.com>
* Ruby 3.0.2 Released https://www.ruby-lang.org/en/news/2021/07/07/ruby-3-0-2-released/ * Ruby 2.7.4 Released https://www.ruby-lang.org/en/news/2021/07/07/ruby-2-7-4-released/ * Ruby 2.6.8 Released https://www.ruby-lang.org/en/news/2021/07/07/ruby-2-6-8-released/ Allow CI failures against Active Record main branch due to rails/rails#42773
Related to issue rails/rails#42773. Follow work done [here](rails/rails@d79fb96)
Related to issue rails/rails#42773. Follow work done [here](rails/rails@d79fb96)
In ActiveRecord 7, the `initialize_type_map` method has been moved to class method by rails/rails#42773 and armg no longer works correctly. Therefore, I have added a monkey patch to the `type_map` method. Because ActiveRecord 7 defines the type map statically when loaded, monkey patching class method does not make sense. https://github.com/rails/rails/blob/v7.0.3/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb#L616-L619 The `type_map` method is defined in the base class `ActiveRecord::ConnectionAdapters::AbstractAdapter`. It is the same for all versions of ActiveRecord supported by armg. And this fix works in any version.
In ActiveRecord 7, the `initialize_type_map` method has been moved to class method by rails/rails#42773 and armg no longer works correctly. Therefore, I have added a monkey patch to the `type_map` method. Because ActiveRecord 7 defines the type map statically when loaded, monkey patching class method does not make sense. https://github.com/rails/rails/blob/v7.0.3/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb#L616-L619 The `type_map` method is defined in the base class `ActiveRecord::ConnectionAdapters::AbstractAdapter`. It is the same for all versions of ActiveRecord supported by armg. And this fix works in any version.
Define adapter type maps statically when possible #42773 rails/rails#42773
Each type map can use a non trivial amount of memory (over 10KiB
in our app).
Currently each connection build its own type map from scratch, but
except for postgresql which has extension types, all connections
end up with the same maps.
So the more connections you have the more memory it wastes.
By defining the type map statically for MySQL and SQLite3 connections
we save some memory, share caches, and allow that memory to be handled
by Copy on Write for forking setups.