Again, good point. There are some workarounds ( https://evrim.io/invalidating-caches-when-using-many-to-many-associations-in-rails/), but the absence of a touch: true option on has_many relations makes the last ID approach less optimal.
Also, I'm noting that support for the LAST_VERSION function didn't get added to SQLite3 until version 3.25.0 (2018). As much as I liked the idea of the last ID approach, I'm not seeing a clean way to implement that doesn't require Rails devs to update their development environment. I'm going to continue to pursue a hash-based approach. Thanks again for your insights, Daniel! On Mon, Nov 4, 2019 at 5:58 PM Daniel <dan...@heath.cc> wrote: > The last ID isn't stable if you replace an item within the collection > though, right (assuming it's via a HABTM, so the associated record doesn't > get touched)? > > > On 11/5/19 2:21 AM, Aaron Lipman wrote: > > Been thinking on this a few days, here are a couple potential solutions > I've been considering: > > Hash-based: Sum the MD5 hashes of the IDs and append the last 32 > hexadecimal digits of the sum to the cache version. Alternatively, use a > mathematical hashing function like Fibonacci Hashing > <https://probablydance.com/2018/06/16/fibonacci-hashing-the-optimization-that-the-world-forgot-or-a-better-alternative-to-integer-modulo/>. > (The second approach wouldn't accommodate non-numeric ID columns, but may > be easier to implement across various SQL flavors without any conditional > logic.) Both approaches need to account for the various 64-bit limits found > in MySQL and Postgres, e.g. MySQL's CONV() function. > > Last ID based: It occurs to me that when a record within a collection is > destroyed, either the size of the collection or the ID of its last item > will change. If we can reliably get that last ID, we can use it in > conjunction with collection size to generate more robust cache keys. The > LAST_VALUE sql function might provide this. However, MySQL didn't add > LAST_VALUE until version 8 (2018), but I think we can emulate the same > functionality via MySQL session variables. > > I'm leaning towards the "Last ID" path. I think it's a little more elegant > and probably more efficient than calculating a sum when it comes to really > large collections. Starting work on a pull request, but welcome other > ideas/directions/considerations. > > > On Wed, Oct 30, 2019 at 6:19 PM Daniel Heath <dan...@heath.cc> > <dan...@heath.cc> wrote: > >> I think it’s worth considering implementing per database, as the major >> ones all have something that’ll work and the keys don’t need to be stable >> across different data store implementations. >> >> Thanks, >> Daniel Heath >> >> On 31 Oct 2019, at 6:17 am, Aaron Lipman <alipma...@gmail.com> wrote: >> >> >> Thanks for the flag, Daniel. On my first read of the code, I didn't catch >> that an aggregate query was used to determine the max updated_at timestamp >> (without fetching individual records) if a relation wasn't loaded. >> >> Figuring out a database-side hashing function that works with all the >> flavors of SQL supported by Rails may prove tricky. I'm going to consider >> this some more, but an alternative approach might be to update the >> documentation to acknowledge this issue and explain how to customize a >> model's cache_version method according to one's needs. >> >> On Mon, Oct 28, 2019 at 4:31 PM Daniel Heath <dan...@heath.cc> >> <dan...@heath.cc> wrote: >> >>> The full collection could be millions of records. Fetching the ids to >>> hash might not be an option either, unless they can be hashed on the >>> database side. >>> >>> Thanks, >>> Daniel Heath >>> >>> On 29 Oct 2019, at 4:22 am, Aaron Lipman <alipma...@gmail.com> wrote: >>> >>> >>> Hi Marc & Richard, >>> >>> I'd categorize this as a bug. When generating a cache_version for a >>> collection, one solution might be to include a hash of the IDs of all items >>> in the collection. This way, the cache_version will change should an item >>> be removed. >>> >>> I believe modifying the compute_cache_version method defined in >>> activerecord/lib/active_record/relation.rb >>> <https://github.com/rails/rails/blob/master/activerecord/lib/active_record/relation.rb> >>> is the way to go about this. >>> >>> Marc, please let me know if you intend to submit a pull request. While >>> I'm not on the Rails team, I'd be happy to offer any assistance and lobby >>> for implementing a fix. (If you're not interested in submitting a PR, I'd >>> be excited to pick this up myself.) >>> >>> On Sun, Oct 27, 2019 at 10:41 AM Marc Köhlbrugge < >>> marckohlbru...@gmail.com> wrote: >>> >>>> 👍 https://github.com/rails/rails/issues/37555 >>>> >>>> I did find a few similar issues, but they all got marked as stale >>>> >>>> https://github.com/rails/rails/issues/34408 >>>> https://github.com/rails/rails/issues/31996 >>>> https://github.com/rails/rails/issues/34093 >>>> >>>> I'm surprised this doesn't appear to be a bigger problem. Perhaps I'm >>>> not supposed to fragment cache my paginated results. Using collection >>>> caching (`render collection: @posts, cached: true`) might be performant >>>> enough. >>>> >>>> On Sunday, October 27, 2019 at 2:55:18 PM UTC+1, richard schneeman >>>> wrote: >>>>> >>>>> Sounds like a bug. Can you move into an issue? >>>>> >>>>> >>>>> >>>>> On Thu, Oct 24, 2019 at 7:52 PM Marc Köhlbrugge < >>>>> h...@marckohlbrugge.com> wrote: >>>>> >>>>>> I'm running into a problem when using fragment caching with active >>>>>> record collections. >>>>>> >>>>>> Rails 6 uses a combination of collection.cache_key and >>>>>> collection.cache_version to determine whether a fragment cache is fresh >>>>>> or >>>>>> not. However, there is a scenario where the collection might change while >>>>>> collection.cache_key and collection.cache_version stay the same. >>>>>> >>>>>> It happens when you use a limit on the collection and then destroy >>>>>> one of those records, as long as it's not the most recent one. This way >>>>>> collection.cache_key will stay the same, because the query does not >>>>>> change. >>>>>> And collection.cache_version will stay the same as well, because the >>>>>> collection count will remain unchanged as does the maximum updated_at >>>>>> timestamp of the most recent record. >>>>>> >>>>>> I've build a sample app for demonstration purposes: >>>>>> >>>>>> https://github.com/marckohlbrugge/rails-6-collection-caching-bug >>>>>> >>>>>> The readme describes a way to reproduce the issue via the website >>>>>> itself, or the Rails console. >>>>>> >>>>>> Would this be considered a bug or expected behavior? Are there any >>>>>> known work arounds? >>>>>> -- >>>>>> You received this message because you are subscribed to the Google >>>>>> Groups "Ruby on Rails: Core" group. >>>>>> To unsubscribe from this group and stop receiving emails from it, >>>>>> send an email to rubyonra...@googlegroups.com. >>>>>> To view this discussion on the web visit >>>>>> https://groups.google.com/d/msgid/rubyonrails-core/4cc0ae69-c736-48d0-bd84-8b3f44dc879d%40googlegroups.com >>>>>> <https://groups.google.com/d/msgid/rubyonrails-core/4cc0ae69-c736-48d0-bd84-8b3f44dc879d%40googlegroups.com?utm_medium=email&utm_source=footer> >>>>>> . >>>>>> >>>>> -- >>>>> Richard Schneeman >>>>> https://www.schneems.com >>>>> >>>> -- >>>> You received this message because you are subscribed to the Google >>>> Groups "Ruby on Rails: Core" group. >>>> To unsubscribe from this group and stop receiving emails from it, send >>>> an email to rubyonrails-core+unsubscr...@googlegroups.com. >>>> To view this discussion on the web visit >>>> https://groups.google.com/d/msgid/rubyonrails-core/6590103e-3528-4896-bc07-e6ff6a194bef%40googlegroups.com >>>> <https://groups.google.com/d/msgid/rubyonrails-core/6590103e-3528-4896-bc07-e6ff6a194bef%40googlegroups.com?utm_medium=email&utm_source=footer> >>>> . >>>> >>> -- >>> You received this message because you are subscribed to the Google >>> Groups "Ruby on Rails: Core" group. >>> To unsubscribe from this group and stop receiving emails from it, send >>> an email to rubyonrails-core+unsubscr...@googlegroups.com. >>> To view this discussion on the web visit >>> https://groups.google.com/d/msgid/rubyonrails-core/CAEJZ43iCipyOZddZAT1WCrtQ7g7VRLeokgLRGcYroK-TvGoGWw%40mail.gmail.com >>> <https://groups.google.com/d/msgid/rubyonrails-core/CAEJZ43iCipyOZddZAT1WCrtQ7g7VRLeokgLRGcYroK-TvGoGWw%40mail.gmail.com?utm_medium=email&utm_source=footer> >>> . >>> >>> -- >>> You received this message because you are subscribed to the Google >>> Groups "Ruby on Rails: Core" group. >>> To unsubscribe from this group and stop receiving emails from it, send >>> an email to rubyonrails-core+unsubscr...@googlegroups.com. >>> To view this discussion on the web visit >>> https://groups.google.com/d/msgid/rubyonrails-core/4A85F762-275C-4796-9A47-0586833DADFA%40heath.cc >>> <https://groups.google.com/d/msgid/rubyonrails-core/4A85F762-275C-4796-9A47-0586833DADFA%40heath.cc?utm_medium=email&utm_source=footer> >>> . >>> >> -- >> You received this message because you are subscribed to the Google Groups >> "Ruby on Rails: Core" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to rubyonrails-core+unsubscr...@googlegroups.com. >> To view this discussion on the web visit >> https://groups.google.com/d/msgid/rubyonrails-core/CAEJZ43h%2BOsd6b_1H2a%3DGScC8dpp%3DMw9d1mO0id6Fdjy2igASUg%40mail.gmail.com >> <https://groups.google.com/d/msgid/rubyonrails-core/CAEJZ43h%2BOsd6b_1H2a%3DGScC8dpp%3DMw9d1mO0id6Fdjy2igASUg%40mail.gmail.com?utm_medium=email&utm_source=footer> >> . >> >> -- >> You received this message because you are subscribed to the Google Groups >> "Ruby on Rails: Core" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to rubyonrails-core+unsubscr...@googlegroups.com. >> To view this discussion on the web visit >> https://groups.google.com/d/msgid/rubyonrails-core/FD13AF4B-ADBA-4ABC-9F56-D63F181B653D%40heath.cc >> <https://groups.google.com/d/msgid/rubyonrails-core/FD13AF4B-ADBA-4ABC-9F56-D63F181B653D%40heath.cc?utm_medium=email&utm_source=footer> >> . >> > -- > You received this message because you are subscribed to the Google Groups > "Ruby on Rails: Core" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to rubyonrails-core+unsubscr...@googlegroups.com. > To view this discussion on the web visit > https://groups.google.com/d/msgid/rubyonrails-core/CAEJZ43hUknLmyi_dWmP2TW3Uspcvw5m7jWAUO%3DiPg_u_bWJFDg%40mail.gmail.com > <https://groups.google.com/d/msgid/rubyonrails-core/CAEJZ43hUknLmyi_dWmP2TW3Uspcvw5m7jWAUO%3DiPg_u_bWJFDg%40mail.gmail.com?utm_medium=email&utm_source=footer> > . > > -- > You received this message because you are subscribed to the Google Groups > "Ruby on Rails: Core" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to rubyonrails-core+unsubscr...@googlegroups.com. > To view this discussion on the web visit > https://groups.google.com/d/msgid/rubyonrails-core/89cf81d9-d107-f976-572c-caf1a095561e%40heath.cc > <https://groups.google.com/d/msgid/rubyonrails-core/89cf81d9-d107-f976-572c-caf1a095561e%40heath.cc?utm_medium=email&utm_source=footer> > . > -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group. To unsubscribe from this group and stop receiving emails from it, send an email to rubyonrails-core+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/rubyonrails-core/CAEJZ43hXHE9NOeoJoHif4FFuxK1YLsAc%2BkQNiEBxckpTYjULKA%40mail.gmail.com.