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

Expose primary_abstract_class public API #41258

Merged
merged 1 commit into from Feb 4, 2021

Conversation

eileencodes
Copy link
Member

followup to #41030, cc @rafaelfranca

We went with primary_abstract_class because we want that to set the class that is the primary abstract class, not have to look for which one is set to true. We'd have to store the class name somewhere, so figured better on the caller.


Previously Rails was treating ApplicationRecord as special in the
primary_class? check. The reason we need to treat it differently than
other connection classes is that ActiveRecord::Base will establish a
connection to the primary database on boot. The established connection
is to your primary database, or the first database defined in your
configuration. We need to do this so that 2 connections aren't opened to
the same database since ActiveRecord::Base and ApplicationRecord
are different classes, on connection the connection_specification_name
would be different.

However, there is no guarantee that an application is using
ApplicationRecord as it's primary abstract class. This exposes a
public method for setting a class to a primary_abstract_class like
this:

class PrimaryApplicationRecord < ActiveRecord::Base
  self.primary_abstract_class
end

Calling primary_abstract_class will automatically set
self.abstract_class = true. This change is backwards compatible
because we weren't supporting multiple application records previously,
and if you had an ApplicationRecord we assumed that was the primary
class. This change continues to assume that ApplicationRecord is your
primary class. You only need to set primary_abstract_class if your
application record is not ApplicationRecord and you're using multiple
databases.

Co-authored-by: John Crepezzi john.crepezzi@gmail.com

Previously Rails was treating `ApplicationRecord` as special in the
`primary_class?` check. The reason we need to treat it differtently than
other connection classes is that `ActiveRecord::Base` will establish a
connection to the primary database on boot. The established connection
is to your primary database, or the first database defined in your
configuration. We need to do this so that 2 connections aren't opened to
the same database since `ActiveRecord::Base` and `AppliationRecord`
are different classes, on connection the connection_speicification_name
would be different.

However, there is no guarantee that an application is using
`ApplicationRecord` as it's primary abstract class. This exposes a
public method for setting a class to a `primary_abstract_class` like
this:

```
class PrimaryApplicationRecord < ActiveRecord::Base
  self.primary_abstract_class
end
```

Calling `primary_abstract_class` will automatically set
`self.abstract_class = true`. This change is backwards compatible
because we weren't supporting multiple application records previously,
and if you had an `ApplicationRecord` we assumed that was the primary
class. This change continues to assume that `ApplicationRecord` is your
primary class. You only need to set `primary_abstract_class` if your
application record is not ApplicationRecord and you're using multiple
databases.

Co-authored-by: John Crepezzi <john.crepezzi@gmail.com>
@eileencodes eileencodes merged commit 5a47789 into rails:main Feb 4, 2021
@eileencodes eileencodes deleted the primary-class branch February 4, 2021 14:47
if Base.application_record_class
self == Base.application_record_class
else
if defined?(ApplicationRecord) && self == ApplicationRecord
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is here only for backward compatibility right? Should we deprecate?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's less backwards compatibility and more "most likely to be the default". I was thinking that I didn't want to force any existing application using ApplicationRecord to have to change anything. We could update the generators to add primary_abstract_class to ApplicationRecord and deprecate this to get everyone on board, but it only matters in apps that are using multiple databases AND have a non-application record application record. I don't think the number of apps doing this is high enough to warrant everyone changing their existing applications. What do you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with what you said. I'd still update the generator to use primary_abstract_class, but no need to deprecate or force apps to change.

@rafaelfranca
Copy link
Member

The reason we need to treat it differently than
other connection classes is that ActiveRecord::Base will establish a
connection to the primary database on boot

One thing to investigate is if we can remove this connection on boot. Connection on boot is a huge resiliency problem because it means that you application that could be serving request that don't depend on the primary database will not be able to boot if the primary database is down.

@eileencodes
Copy link
Member Author

One thing to investigate is if we can remove this connection on boot. Connection on boot is a huge resiliency problem because it means that you application that could be serving request that don't depend on the primary database will not be able to boot if the primary database is down.

This has existed for a very long time so changing it will need to take that into account. But I agree it would be great if Rails didn't need to connect to a db on boot. It's something on my list but I don't know exactly when I'll get to it.

eileencodes added a commit to eileencodes/rails that referenced this pull request Feb 5, 2021
Followup on rails#41258 (comment)

This makes sure that newly generated applications get their
`ApplicationRecord` set to `primary_abstract_class`. Existing applications
can opt-in to this if they're using multiple databases or have changed
their `ApplicationRecord` to a class with a different name.

Co-authored-by: John Crepezzi <john.crepezzi@gmail.com>
eileencodes added a commit to eileencodes/rails that referenced this pull request May 17, 2021
In rails#41258 we exposed a public API for changing the default application
record - ie the class used to connect to the database on boot in a
non-eager loaded environment. After attempting to implement this
in GitHub we realized that this won't work unless the class that is the
primary class is eager loaded. Instead of marking a class as the
`primary_abstract_class` this moves it to a config setting. The config
setting must use a string because the class is not loaded yet. When the
database connects it will be constantized. Without this change
application envs that don't eager load like development would see an
error that ApplicationRecord doesn't exist - because the custom
application record class is not loaded yet.

Co-authored-by: John Crepezzi <john.crepezzi@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants