rails/activerecord/test/cases
eileencodes a2a525bbb6 Add an intermediary called RoleManager to manage connections
This PR is an alternate solution to #37388. While there are benefits
to merging #37388 it changes the public API and swaps around existing
concepts for how connection management works. The changes are
backwards-incompatible and pretty major. This will have a negative impact
on gems and applications relying on how conn management currently works.

**Background:**

Shopify and other applications need sharding but Rails has
made it impossible to do this because a handler can only hold one
connection pool per class. Sharded apps need to hold multiple
connections per handler per class.

This PR aims to solve only that problem.

**What this PR does:**

In this PR we've added a `RoleManager` class that can hold multiple
`Roles`. Each `Role` holds the `db_config`,
`connection_specification_name`, `schema_cache` and `pool`. By default
the `RoleManager` holds a single reference from a `default` key to the
`Role` instance. A sharded/multi-tenant app can pass an optional second
argument to `remove_connection`, `retrieve_connection_pool`,
`establish_connection` and `connected?` on the handler, thus allowing
for multiple connections belonging to the same class/handler without
breaking backwards compatibility.

By using the `RoleManager` we can avoid altering the public API, moving
around handler/role concepts, and achieve the internal needs for
establishing multiple connections per handler per class.

**A note about why we opened this PR:**

We very much appreciate the work that went into #37388 and in no way mean
to diminish that work. However, it breaks the following public APIs:

* `#retrieve_connection`, `#connected?`, and `#remove_connection` are
public methods on handler and can't be changed from taking a spec to a
role.
* The knowledge that the handler keys are symbols relating to a role
(`:writing`/`:reading`) is public - changing how handlers are accessed
will break apps/libraries.

In addition it doesn't solve the problem of mapping a single connection
to a single class since it has a 1:1 mapping of `class (handler) -> role
(writing) -> db_config`. Multiple pools in a writing role can't exist
in that implementation.

The new PR solves this by using the `RoleManager` to hold multiple connection
objects for the same class. This lets a handler hold a role manager
which can hold as many roles for that writer as the app needs.

**Regarding the `Role` name:**

When I originally designed the API for multiple databases, it wasn't
accidental that handler and role are the same concept. Handler is the
internal concept (since that's what was there already) and Role was the
public external concept. Meaning, role and handler were meant to
be the same thing. The concept here means that when you switch a
handler/role, Rails automatically can pick up the connection on the
other role by knowing the specification name. Changing this would mean not
just that we need to rework how GitHub and many many gems work, but also
means retraining users of Rails 6.0 that all these concepts changed.

Since this PR doesn't move around the concepts in connection
management and instead creates an intermediary between `handler` and
`role` to manage the connection data (`db_config`, `schema_cache`,
`pool`, and `connection_specification`) we think that `Role` and
`RoleManager` are the wrong name.

We didn't change it yet in this PR because we wanted to keep change
churn low for initial review. We also haven't come up with a better
name yet. 😄

**What this PR does not solve:**

Our PR here solves a small portion of the problem - it allows models to
have multiple connections on a class. It doesn't aim to solve any other
problems than that. Going forward we'll need to still solve the
following problems:

* `DatabaseConfig` doesn't support a sharding configuration
* `connects_to`/`connected_to` still needs a way to switch connections for shards
* Automatic switching of shards
* `connection_specification_name` still exists

**The End**

Thanks for reading this far. These problems aren't easy to solve. John
and I spent a lot of time trying different things and so I hope that
this doesn't come across as if we think we know better. I would have
commented on the other PR what changes to make but we needed to try out
different solutions in order to get here.

Ultimately we're aiming to change as little as the API as possible. Even
if the handler/role -> manager -> db_config/pool/etc isn't how we'd
design connection management if we could start over, we also don't want
to break public APIs. It's important that we make things better while
maintaining compatibility.

The `RoleManager` class makes it possible for us to fix the underlying
problem while maintaining all the backwards compatibility in the public
API.

We all have the same goal; to add sharding support to Rails. Let me know
your thoughts on this change in lieu of #37388 and if you have questions.

Co-authored-by: John Crepezzi <seejohnrun@github.com>
2019-11-05 16:27:56 -05:00
..
adapters Add supports_common_table_expressions? for CTE testing 2019-10-23 20:29:54 +09:00
arel Merge pull request #37358 from louim/doc/update-mysql-doc-to-current 2019-10-05 00:43:55 +09:00
associations Fix random CI failure due to non-deterministic sorting order 2019-10-31 05:55:57 +09:00
attribute_methods Add AR::Base.base_class? predicate 2018-04-02 14:34:32 +03:00
coders Use frozen-string-literal in ActiveRecord 2017-07-19 22:27:07 +03:00
connection_adapters Add an intermediary called RoleManager to manage connections 2019-11-05 16:27:56 -05:00
connection_specification Merge ConnectionSpecification + Role -> Role 2019-10-18 10:26:08 -05:00
database_configurations Fix defaults for database configs 2019-09-25 11:15:34 -04:00
fixture_set Use frozen-string-literal in ActiveRecord 2017-07-19 22:27:07 +03:00
migration Use FileUpdateChecker for Migration::CheckPending 2019-10-08 16:29:12 -07:00
relation Improve deprecation message for nested where.not condition 2019-10-31 05:31:02 +09:00
scoping Association loading isn't to be affected by null relation scoping 2019-04-06 11:37:34 +09:00
tasks Make DatabaseTasks adapters use DatabaseConfig objects 2019-09-25 13:52:14 -04:00
type Fix year value when casting a multiparameter time hash 2019-01-21 09:23:12 +00:00
validations Move the ActiveModel:Errors#full_message method to the Error class: 2019-07-16 14:28:38 +02:00
adapter_test.rb Add supports_common_table_expressions? for CTE testing 2019-10-23 20:29:54 +09:00
aggregations_test.rb No need to handle if FrozenError is available 2018-12-23 13:26:20 +00:00
annotate_test.rb Should Regexp.escape quoted table name in regex 2019-07-08 09:32:30 +09:00
ar_schema_test.rb Move SchemaMigration to migration_context 2019-06-14 11:15:08 -04:00
associations_test.rb Add Relation#annotate for SQL commenting 2019-03-21 20:30:56 -07:00
attribute_decorators_test.rb Use frozen-string-literal in ActiveRecord 2017-07-19 22:27:07 +03:00
attribute_methods_test.rb Enable Layout/EmptyLinesAroundAccessModifier cop 2019-06-13 12:00:45 +09:00
attributes_test.rb Should find last created record 2019-06-15 08:19:31 +09:00
autosave_association_test.rb Ensure Contextual validations fire on associations 2019-10-04 12:17:53 +01:00
base_test.rb Better double checked locking in load_schema 2019-09-24 18:00:05 +02:00
batches_test.rb Fix random CI fail due to non-deterministic order 2019-10-17 14:43:06 -05:00
binary_test.rb Enable Performance/UnfreezeString cop 2018-09-23 08:56:55 +09:00
bind_parameter_test.rb Address test_statement_cache_with_in_clause failure due to nondeterministic sort order 2019-06-20 15:07:58 +00:00
boolean_test.rb Type cast falsy boolean symbols on boolean attribute as false 2019-03-30 04:18:25 +09:00
cache_key_test.rb PostgreSQL: Use native timestamp decoders of pg-1.1 2019-01-26 20:30:43 +01:00
calculations_test.rb Fix random CI failure due to non-deterministic sorting order 2019-10-11 22:01:20 +09:00
callbacks_test.rb Don't call after_commit callbacks despite a record isn't saved 2019-04-12 09:19:03 +09:00
clone_test.rb Fix CustomCops/AssertNot to allow it to have failure message 2018-05-13 11:32:47 +09:00
collection_cache_key_test.rb Revert "Deprecate collection_cache_key which is private API" 2019-04-19 23:54:25 +09:00
column_alias_test.rb Use frozen-string-literal in ActiveRecord 2017-07-19 22:27:07 +03:00
column_definition_test.rb Use frozen-string-literal in ActiveRecord 2017-07-19 22:27:07 +03:00
comment_test.rb Fix invalid schema dump when primary key column has a comment 2019-06-03 08:35:36 -03:00
connection_management_test.rb Change the empty block style to have space inside of the block 2018-09-25 13:19:35 -04:00
connection_pool_test.rb Merge ConnectionSpecification + Role -> Role 2019-10-18 10:26:08 -05:00
core_test.rb Update for Time#inspect 2019-09-21 16:57:39 +09:00
counter_cache_test.rb Enable Lint/UselessAssignment cop to avoid unused variable warnings (#34904) 2019-01-09 18:09:01 +09:00
custom_locking_test.rb Use frozen-string-literal in ActiveRecord 2017-07-19 22:27:07 +03:00
database_configurations_test.rb Deprecate to_h and to_legacy_hash 2019-09-17 20:49:38 -04:00
database_selector_test.rb Extend test_can_write_while_reading_from_replicas_if_explicit 2019-08-30 17:46:21 +03:00
database_statements_test.rb Enable Layout/EmptyLinesAroundAccessModifier cop 2019-06-13 12:00:45 +09:00
date_test.rb Rails DateTime type is mapped to Oracle TIMESTAMP since Rails 5.0 2019-03-03 04:55:17 +00:00
date_time_precision_test.rb Raise ArgumentError for invalid :limit and :precision like as other options 2019-04-07 16:14:42 +09:00
date_time_test.rb Use frozen-string-literal in ActiveRecord 2017-07-19 22:27:07 +03:00
defaults_test.rb Update MySQL links to the current version of the manual [ci skip] 2019-10-03 20:22:29 -04:00
dirty_test.rb Serialize symbols to strings in ImmutableString serialize method 2019-08-10 23:37:37 +01:00
disconnected_test.rb Merge ConnectionSpecification + Role -> Role 2019-10-18 10:26:08 -05:00
dup_test.rb Fix CustomCops/AssertNot to allow it to have failure message 2018-05-13 11:32:47 +09:00
enum_test.rb Add silence_warnings for defining 'not_' prefix enum elements 2019-07-30 04:54:55 +09:00
errors_test.rb Enable Style/RedundantBegin cop to avoid newly adding redundant begin block 2018-12-21 06:12:42 +09:00
explain_subscriber_test.rb Permit list usage cleanup and clearer documentation 2018-08-27 09:51:46 -04:00
explain_test.rb Enable Layout/EmptyLinesAroundAccessModifier cop 2019-06-13 12:00:45 +09:00
filter_attributes_test.rb Enable Style/RedundantBegin cop to avoid newly adding redundant begin block 2018-12-21 06:12:42 +09:00
finder_respond_to_test.rb Enable Layout/EmptyLinesAroundAccessModifier cop 2019-06-13 12:00:45 +09:00
finder_test.rb Correctly find nested association reflections for #37356 2019-10-11 16:20:11 +09:00
fixtures_test.rb Add an intermediary called RoleManager to manage connections 2019-11-05 16:27:56 -05:00
forbidden_attributes_protection_test.rb Remove duplicated protected params definitions 2019-02-24 19:50:53 +09:00
habtm_destroy_order_test.rb Enable Style/RedundantBegin cop to avoid newly adding redundant begin block 2018-12-21 06:12:42 +09:00
helper.rb Instantiate ConnectionPool with a DatabaseConfig rather than a ConnectionSpecification 2019-09-24 15:12:22 +02:00
hot_compatibility_test.rb Enable Layout/EmptyLinesAroundAccessModifier cop 2019-06-13 12:00:45 +09:00
i18n_test.rb Use frozen-string-literal in ActiveRecord 2017-07-19 22:27:07 +03:00
inheritance_test.rb Should Regexp.escape quoted table name in regex 2019-07-08 09:32:30 +09:00
insert_all_test.rb Auto-correct rubocop offences 2019-09-16 05:46:26 +09:00
instrumentation_test.rb Include connection in cached query notifications 2019-10-01 00:35:32 +01:00
integration_test.rb Remove ability to specify a timestamp name for #cache_key 2019-01-17 16:08:32 -05:00
invalid_connection_test.rb Use frozen-string-literal in ActiveRecord 2017-07-19 22:27:07 +03:00
invertible_migration_test.rb make change_column_comment and change_table_comment invertible 2019-04-15 08:43:22 +09:00
json_attribute_test.rb Use :string instead of :text for JsonAttributeTest 2017-12-06 08:42:51 +09:00
json_serialization_test.rb Merge pull request #35891 from Shopify/schema-cache-deduplication 2019-06-19 13:04:32 +02:00
json_shared_test_cases.rb Deprecate update_attributes and update_attributes! 2018-02-17 13:58:54 -08:00
locking_test.rb Enable Layout/EmptyLinesAroundAccessModifier cop 2019-06-13 12:00:45 +09:00
log_subscriber_test.rb Omit BEGIN/COMMIT statements for empty transactions 2018-08-13 16:51:54 +01:00
migration_test.rb Accept columns passed with options in remove_index 2019-09-10 22:21:20 +01:00
migrator_test.rb Move SchemaMigration to migration_context 2019-06-14 11:15:08 -04:00
mixin_test.rb Merge pull request #29860 from georgeclaghorn/travel-back-automatically 2017-07-24 01:01:25 -04:00
modules_test.rb Fix CustomCops/AssertNot to allow it to have failure message 2018-05-13 11:32:47 +09:00
multi_db_migrator_test.rb Move SchemaMigration to migration_context 2019-06-14 11:15:08 -04:00
multiparameter_attributes_test.rb Autocorrect refute RuboCop violations 2018-04-03 22:35:49 -04:00
multiple_db_test.rb Enable Style/RedundantBegin cop to avoid newly adding redundant begin block 2018-12-21 06:12:42 +09:00
nested_attributes_test.rb Enable Layout/EmptyLinesAroundAccessModifier cop 2019-06-13 12:00:45 +09:00
nested_attributes_with_callbacks_test.rb Use assert_predicate and assert_not_predicate 2018-01-25 23:32:59 -05:00
null_relation_test.rb Call load_schema before assert_no_queries 2018-10-10 09:50:22 +09:00
numeric_data_test.rb Ensure casting by decimal attribute when querying 2018-11-12 03:39:28 +09:00
persistence_test.rb Assert deprecation 2019-03-31 22:59:12 +08:00
pooled_connections_test.rb Enable Layout/EmptyLinesAroundAccessModifier cop 2019-06-13 12:00:45 +09:00
prepared_statement_status_test.rb Make prepared statement status thread and instance-specific 2019-08-16 02:11:19 -04:00
primary_keys_test.rb PERF: 20% faster pk attribute access 2019-04-22 14:41:00 +09:00
query_cache_test.rb Add an intermediary called RoleManager to manage connections 2019-11-05 16:27:56 -05:00
quoting_test.rb Normalize time value not to be affected by summer time 2018-07-25 09:46:39 +09:00
readonly_test.rb Replace assert ! with assert_not 2018-04-19 08:11:33 -04:00
reaper_test.rb Merge ConnectionSpecification + Role -> Role 2019-10-18 10:26:08 -05:00
reflection_test.rb Silence method redefined warnings 2019-09-17 15:53:36 +09:00
relation_test.rb Use assert_queries since we are only checking for the count 2019-09-30 22:38:00 -03:00
relations_test.rb Fix random CI failure due to non-deterministic sorting order 2019-10-17 11:21:46 +09:00
reload_models_test.rb Merge remote-tracking branch 'origin/master' into unlock-minitest 2017-08-01 17:34:14 -04:00
reserved_word_test.rb Use assert_predicate and assert_not_predicate 2018-01-25 23:32:59 -05:00
result_test.rb Deprecate ActiveRecord::Result#to_hash in favor of #to_a 2018-09-18 16:40:10 -04:00
sanitize_test.rb Quote empty ranges like other empty enumerables 2019-03-07 19:22:29 -05:00
schema_dumper_test.rb Revert schema dumper to use strings rather than integers 2019-06-20 14:00:42 +02:00
schema_loading_test.rb Enable Layout/EmptyLinesAroundAccessModifier cop 2019-06-13 12:00:45 +09:00
secure_token_test.rb Allow token length configuration for has_secure_token method 2019-04-09 17:25:50 -04:00
serialization_test.rb Replace assert ! with assert_not 2018-04-19 08:11:33 -04:00
serialized_attribute_test.rb define_attribute takes keyword arguments 2019-09-15 03:05:52 +09:00
statement_cache_test.rb activerecord: Fix statement cache for strictly cast attributes 2019-01-23 14:49:42 -05:00
statement_invalid_test.rb Redact SQL in errors 2018-11-22 13:53:23 -05:00
store_test.rb Add saved changes helpers for store accessors 2019-03-25 18:53:07 -04:00
suppressor_test.rb Change the empty block style to have space inside of the block 2018-09-25 13:19:35 -04:00
test_case.rb Make has_many inversing opt-in 2019-10-11 15:55:46 -04:00
test_fixtures_test.rb Use frozen-string-literal in ActiveRecord 2017-07-19 22:27:07 +03:00
time_precision_test.rb Raise ArgumentError for invalid :limit and :precision like as other options 2019-04-07 16:14:42 +09:00
timestamp_test.rb Fix dirty tracking for touch 2019-04-15 13:53:58 +09:00
touch_later_test.rb Fix typo for touch later test description. laster -> later 2019-04-05 19:33:19 +05:30
transaction_callbacks_test.rb Rollback in after_commit should not rollback state that already been succeeded 2019-09-20 12:44:35 +09:00
transaction_isolation_test.rb Change the empty block style to have space inside of the block 2018-09-25 13:19:35 -04:00
transactions_test.rb Enable Layout/EmptyLinesAroundAccessModifier cop 2019-06-13 12:00:45 +09:00
type_test.rb Use frozen-string-literal in ActiveRecord 2017-07-19 22:27:07 +03:00
types_test.rb Use frozen-string-literal in ActiveRecord 2017-07-19 22:27:07 +03:00
unconnected_test.rb Revert "Revert "Merge pull request #37296 from Shopify/db-config-pool"" 2019-10-14 13:28:42 -04:00
unsafe_raw_sql_test.rb Allow column name with function (e.g. length(title)) as safe SQL string 2019-06-10 07:36:58 +09:00
validations_repair_helper.rb Use frozen-string-literal in ActiveRecord 2017-07-19 22:27:07 +03:00
validations_test.rb Add validation to subclass in tests to avoid polluting parent class 2019-04-13 22:20:56 +09:00
view_test.rb Bump the minimum version of PostgreSQL to 9.3 2018-11-25 13:13:08 +00:00
yaml_serialization_test.rb Enable Layout/EmptyLinesAroundAccessModifier cop 2019-06-13 12:00:45 +09:00