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
Add the ability to ignore counter cache columns while they are backfilling #51453
Conversation
What if instead of having to modify the model, we simply assume the cache is inactive if its value is less than 0. Then the cache can be active/inactive on a per row basis rather than for an entire model/table. Additionally this allows the cache to automatically activate itself simply by being updated, rather than requiring a code change. |
While this is simpler to implement, unfortunately, this approach has some problems. For example:
|
I think this I was thinking of @jmarchello's comment, though, and wondering if we could allow the counter cache column to be nullable, which (if null) would essentially make that record's Another related idea might be to leverage the belongs_to :post, counter_cache: Proc.new { |post| post.created_at >= ENV["COMMENTS_COUNTER_CACHE_CREATED_AT"] } In any case, I think this PR is a great idea, I just wanted to share some idle chatter since I was wondering if there could be a simpler way, but I think the |
I like this as a conceptual design. SQL Null propagation semantics would mean that things like relative increments would all Just Work (read: remain null), and the only thing you need to do to start using the column is set it -- even incrementally, in the case of a large backfill. All you would need to do would be to add the column with a null default, and then change its default to 0 -- old rows get null, new rows start at 0. And then backfill as the opportunity arises (maybe even automatically, if we're writing the row for some other reason?) (But I'm saying this without having looked at the code, so I'm not sure how straightforward it would be to implement.) |
This may work on postgres, and perhaps might work with MySQL new online alters, but for people using LHM / gh-ost etc, this isn't nice. Because if you need a week to introduce the nullable column, you'll need another week to change it's default. It then runs into schema cache issues, you'll at least need a deploy for the change to be noticed by Rails. And I'm not even looking at the backward compatibility issue, because right now counter caches treat In the end disabling it in the model seem simpler to me, and is akin to |
38c5c84
to
f15e986
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Come minor comments.
f15e986
to
e79455f
Compare
Updated 👍 |
See the full feature discussion at https://discuss.rubyonrails.org/t/new-feature-to-make-introducing-counter-caches-safer-and-easier/85456/2
Starting to use counter caches on existing large tables can be troublesome, because the column values must be backfilled separately of the column addition (to not lock the table for too long) and before the use of
:counter_cache
(otherwise methods likesize
/any?
/etc, which use counter caches internally, can produce incorrect results). People usually use database triggers or callbacks on child associations while backfilling before introducing a counter cacheconfiguration to the association.
Now, to safely backfill the column, while keeping the column updated with child records added/removed, use:
While the counter cache is not "active", the methods like
size
/any?
/etc will not use it, but get the results directly from the database. After the counter cache column is backfilled, simply remove the{ active: false }
part from the counter cache definition, and it will now be used by the mentioned methods.cc @byroot