{"id":830,"date":"2021-12-20T15:33:12","date_gmt":"2021-12-20T19:33:12","guid":{"rendered":"http:\/\/www.michaelseneadza.com\/blog\/?p=830"},"modified":"2021-12-20T16:01:20","modified_gmt":"2021-12-20T20:01:20","slug":"rails-7s-load_async-doesnt-play-well-with-multi-tenant-apps","status":"publish","type":"post","link":"http:\/\/www.michaelseneadza.com\/blog\/2021\/12\/20\/rails-7s-load_async-doesnt-play-well-with-multi-tenant-apps\/","title":{"rendered":"Rails 7&#8217;s load_async Doesn&#8217;t Play Well with Multi-Tenant Apps"},"content":{"rendered":"\n<p><em>Disclaimer: I can&#8217;t speak for all multi-tenant apps but I suspect the vast majority of those which use separate DB schemas will run into the same issue I ran into.<\/em><\/p>\n\n\n\n<p>I was excited to try out the new <a href=\"https:\/\/blog.kiprosh.com\/rails-7-activerecord-relation-load_async\/\">Relation#load_async feature in Rails 7<\/a> to speed up some pages on <a href=\"https:\/\/swingtradebot.com\/\">SwingTradeBot<\/a>.  I upgraded the app to Rails 7 a few days ago and added some load_async calls to a few spots where I thought it would help.  <\/p>\n\n\n\n<p>All seemed well until earlier today when one of my users told me that he was getting some strange behavior one of my pages.  After a quick investigation I discovered that <strong>the load_async calls were the culprit<\/strong>.  <\/p>\n\n\n\n<p>SwingTradeBot is a multi-tenant app.  It has separate PostgreSQL schemas for different stock markets around the world.  I use the <a href=\"https:\/\/github.com\/rails-on-services\/apartment\">Apartment gem<\/a> to manage switching the schema per server request based on the subdomain of the requested URL.  So the Australian version of SwingTradeBot at <strong><a href=\"http:\/\/asx.swingtradebot.com\">asx.swingtradebot.com<\/a> switches to the PostgreSQL &#8216;asx&#8217; schema<\/strong>.  The person who contacted me was using the London (LSE) version of the site, which points to the &#8216;lse&#8217; schema.  The problem he was seeing was that <strong>stocks from the USA markets (NYSE &amp; NASDAQ) were appearing on the LSE page<\/strong>.  <\/p>\n\n\n\n<p>The USA markets is the default tenant \/ schema and that&#8217;s what the load_async calls were using.  So <strong>the load_async calls aren&#8217;t retaining the database schema context set by the Apartment gem<\/strong>.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">How to work around this?<\/h4>\n\n\n\n<p>My first thought of how to make load_async work with Apartment was to <strong>somehow pass the current schema into load_async<\/strong>.  Then load_async could switch to the proper schema.  (There&#8217;s <a href=\"https:\/\/github.com\/influitive\/apartment-sidekiq\">a gem to something similar in order to get Sidekiq to play well with Apartment<\/a>)  But I don&#8217;t think such a  change would be acceptable to the Rails core team. <\/p>\n\n\n\n<p>My next thought was to do explicitly specify the schema via ActiveRecord&#8217;s &#8216;from&#8217; and\/or &#8216;joins&#8217; clauses.  So something like:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote\"><p>Stock.select(:id).from( &#8220;#{target_schema}.stocks&#8221;)&#8230;<\/p><\/blockquote>\n\n\n\n<p>I think that could work in certain situations but it would be very messy for some of the queries on which I was using load_async.  So for now I&#8217;m just going to avoid using load_async.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">P.S.  <\/h4>\n\n\n\n<p>I use quite a bit of raw SQL (<em><strong>gasp!<\/strong><\/em>) in my app and was a bit disappointed to discover that<strong> load_async does not work with &#8216;find_by_sql&#8217;<\/strong>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Disclaimer: I can&#8217;t speak for all multi-tenant apps but I suspect the vast majority of those which use separate DB schemas will run into the same issue I ran into. I was excited to try out the new Relation#load_async feature in Rails 7 to speed up some pages on SwingTradeBot. I upgraded the app to&hellip; <a class=\"more-link\" href=\"http:\/\/www.michaelseneadza.com\/blog\/2021\/12\/20\/rails-7s-load_async-doesnt-play-well-with-multi-tenant-apps\/\">Continue reading <span class=\"screen-reader-text\">Rails 7&#8217;s load_async Doesn&#8217;t Play Well with Multi-Tenant Apps<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"spay_email":"","footnotes":""},"categories":[16],"tags":[22],"jetpack_featured_media_url":"","jetpack-related-posts":[{"id":557,"url":"http:\/\/www.michaelseneadza.com\/blog\/2014\/04\/06\/ive-finally-found-a-rails-4-x-blogging-engine-gem\/","url_meta":{"origin":830,"position":0},"title":"I&#8217;ve Finally Found a Rails 4.x Blogging Engine \/ Gem","date":"April 6, 2014","format":false,"excerpt":"I can't believe how difficult it's been to find a good solution for plugging a simple blog into an existing Rails app. I wanted to add a blog to SwingTradeBot, the new site I'm building but most answers to this question that I've found say to either use RefineryCMS or\u2026","rel":"","context":"In &quot;Blogging&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":566,"url":"http:\/\/www.michaelseneadza.com\/blog\/2014\/05\/02\/calculating-standard-deviations-in-ruby-on-rails-and-postgresql\/","url_meta":{"origin":830,"position":1},"title":"Calculating Standard Deviations in Ruby on Rails (and PostgreSQL)","date":"May 2, 2014","format":false,"excerpt":"I need to calculate some Bollinger Bands (BBs) for SwingTradeBot, which is built in Rails 4. Here's a quick definition of Bollinger Bands: Bollinger Bands\u00ae are volatility bands placed above and below a moving average. Volatility is based on the standard deviation, which changes as volatility increases and decreases. So\u2026","rel":"","context":"In &quot;Technology&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":561,"url":"http:\/\/www.michaelseneadza.com\/blog\/2014\/04\/17\/ruby-rails-memoization-gems-memoist-vs-memoizable\/","url_meta":{"origin":830,"position":2},"title":"Ruby \/ Rails Memoization Gems Memoist vs. Memoizable","date":"April 17, 2014","format":false,"excerpt":"I was just adding some memoization to a Rails app and I was exploring the available gems. I'd used Memoist in the past on another project but I couldn't remember why I chose it over other gems. While researching today I found the Memoizable gem and thought that it looked\u2026","rel":"","context":"In &quot;Technology&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":574,"url":"http:\/\/www.michaelseneadza.com\/blog\/2014\/08\/06\/slow-responses-from-the-braintree-ruby-gem-try-this-fix\/","url_meta":{"origin":830,"position":3},"title":"Slow Responses from the BrainTree Ruby Gem? Try This Fix.","date":"August 6, 2014","format":false,"excerpt":"A few weeks ago I was tasked with trying to mitigate some timeout issues in a client's Rails app making BrainTree calls. This was becoming more of a problem as the client's users built up more & more history in BrainTree. Apparently you can't paginate the results or ask BrainTree\u2026","rel":"","context":"In &quot;Technology&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":825,"url":"http:\/\/www.michaelseneadza.com\/blog\/2021\/09\/16\/my-digitally-nomadic-epic-road-trip\/","url_meta":{"origin":830,"position":4},"title":"My Digitally Nomadic Epic Road Trip","date":"September 16, 2021","format":false,"excerpt":"In about a week from now I'll be embarking on a 4 or 5 month long road trip. I've told a several people about my plans and I always seem to get at least one of the following in response: Where are you going?Take me with you! \/ I'd love\u2026","rel":"","context":"In &quot;Travel&quot;","img":{"alt_text":"","src":"https:\/\/i2.wp.com\/www.michaelseneadza.com\/blog\/wp-content\/uploads\/2021\/09\/PXL_20210914_172513967-scaled.jpg?resize=350%2C200","width":350,"height":200},"classes":[]},{"id":276,"url":"http:\/\/www.michaelseneadza.com\/blog\/2004\/02\/06\/whats_on_my_treo_600\/","url_meta":{"origin":830,"position":5},"title":"What&#8217;s on My Treo 600","date":"February 6, 2004","format":false,"excerpt":"I've now had my Treo 600 working for about a month and I'm really enjoying it. It's nice to be able to call, e-mail, IM, SMS, and browse the web from one device. My number port still isn't right, as some calls still ring on my land line, but that's\u2026","rel":"","context":"In &quot;Technology&quot;","img":{"alt_text":"Shop at Amazon.com","src":"https:\/\/i2.wp.com\/rcm-images.amazon.com\/images\/G\/01\/rcm\/468x336.gif?resize=350%2C200","width":350,"height":200},"classes":[]}],"_links":{"self":[{"href":"http:\/\/www.michaelseneadza.com\/blog\/wp-json\/wp\/v2\/posts\/830"}],"collection":[{"href":"http:\/\/www.michaelseneadza.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.michaelseneadza.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.michaelseneadza.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.michaelseneadza.com\/blog\/wp-json\/wp\/v2\/comments?post=830"}],"version-history":[{"count":4,"href":"http:\/\/www.michaelseneadza.com\/blog\/wp-json\/wp\/v2\/posts\/830\/revisions"}],"predecessor-version":[{"id":835,"href":"http:\/\/www.michaelseneadza.com\/blog\/wp-json\/wp\/v2\/posts\/830\/revisions\/835"}],"wp:attachment":[{"href":"http:\/\/www.michaelseneadza.com\/blog\/wp-json\/wp\/v2\/media?parent=830"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.michaelseneadza.com\/blog\/wp-json\/wp\/v2\/categories?post=830"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.michaelseneadza.com\/blog\/wp-json\/wp\/v2\/tags?post=830"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}