<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Shinichi Maeshima</title>
    <description>The latest articles on DEV Community by Shinichi Maeshima (@willnet).</description>
    <link>https://dev.to/willnet</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F101957%2Fa1d7a6e8-17ff-4152-b375-d106dd450943.jpg</url>
      <title>DEV Community: Shinichi Maeshima</title>
      <link>https://dev.to/willnet</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/willnet"/>
    <language>en</language>
    <item>
      <title>SolidQueue now runs with multiple threads</title>
      <dc:creator>Shinichi Maeshima</dc:creator>
      <pubDate>Fri, 23 Jan 2026 06:45:26 +0000</pubDate>
      <link>https://dev.to/willnet/solidqueue-now-runs-with-multiple-threads-29co</link>
      <guid>https://dev.to/willnet/solidqueue-now-runs-with-multiple-threads-29co</guid>
      <description>&lt;p&gt;Recently, I’ve personally been using SolidQueue more and more as a background worker. Around that time, SolidQueue v1.3.0 was released, and it included a feature that I found particularly interesting, so I’d like to introduce it here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;SolidQueue defines multiple roles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Supervisor&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dispatcher&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scheduler&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Worker&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the early days of SolidQueue, there was an &lt;a href="https://github.com/rails/solid_queue/pull/269" rel="noopener noreferrer"&gt;async mode where each role could run as a thread within a single process&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;However, in order to simplify the implementation, &lt;a href="https://github.com/rails/solid_queue/pull/308" rel="noopener noreferrer"&gt;async mode was removed starting with v0.4.0, and SolidQueue began assigning one or more processes to each role&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  SolidQueue Used More Memory Than Sidekiq
&lt;/h2&gt;

&lt;p&gt;Because each role runs in its own process, even the minimum SolidQueue setup ends up spawning 3–4 processes (the Scheduler can be omitted if recurring jobs are not needed). Sidekiq, on the other hand, runs in a single process, so a server that works fine with Sidekiq can easily run into memory constraints when using SolidQueue.&lt;/p&gt;

&lt;p&gt;I personally ran workers on Heroku using a 512 MB dyno, but when migrating from Sidekiq to SolidQueue, memory was no longer sufficient and I had no choice but to upgrade to a 1 GB dyno.&lt;/p&gt;

&lt;p&gt;Rails is very convenient for individual developers because it can be easily deployed and operated on inexpensive VPSs. However, when using SolidQueue, a relatively larger amount of memory (around 2 GB or more) was required.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Return of Async Mode
&lt;/h2&gt;

&lt;p&gt;Due to this memory usage issue, some people started to want async mode to come back. Simply saying “we want to reduce memory usage” wasn’t enough to move things forward, but the discussion gained momentum when it became clear that &lt;a href="https://github.com/rails/solid_queue/issues/679" rel="noopener noreferrer"&gt;people on JRuby and Windows—who couldn’t use SolidQueue because &lt;code&gt;fork&lt;/code&gt; is unavailable—also wanted to be able to use it&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Eventually, the &lt;a href="https://github.com/rails/solid_queue/pull/644" rel="noopener noreferrer"&gt;PR that brings back async mode&lt;/a&gt; was merged. This change is included in the recently released &lt;a href="https://github.com/rails/solid_queue/releases/tag/v1.3.0" rel="noopener noreferrer"&gt;v1.3.0&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can enable it by starting SolidQueue with &lt;code&gt;bin/jobs --async&lt;/code&gt; or by setting &lt;code&gt;SOLID_QUEUE_SUPERVISOR_MODE=async&lt;/code&gt;, which makes SolidQueue use threads instead of processes.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/rails/solid_queue?tab=readme-ov-file#fork-vs-async-mode" rel="noopener noreferrer"&gt;README&lt;/a&gt; says the following, so using processes (&lt;code&gt;fork&lt;/code&gt;) is generally more stable. Still, async mode seems quite convenient for limited environments such as personal projects.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The recommended and default mode is &lt;code&gt;fork&lt;/code&gt;. Only use &lt;code&gt;async&lt;/code&gt; if you know what you're doing and have strong reasons to.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In my own environment, memory usage dropped from over 600 MB to around 260 MB. Nice.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fstp1fw1wqc3dvxms21gv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fstp1fw1wqc3dvxms21gv.png" alt="memory usage of my heroku worker" width="800" height="205"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Released Gon v7.0.0</title>
      <dc:creator>Shinichi Maeshima</dc:creator>
      <pubDate>Sun, 11 Jan 2026 06:29:04 +0000</pubDate>
      <link>https://dev.to/willnet/released-gon-v700-34k6</link>
      <guid>https://dev.to/willnet/released-gon-v700-34k6</guid>
      <description>&lt;p&gt;I am a current maintainer of Gon. We have released Gon v7.0.0.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/gazay/gon/releases/tag/v7.0.0" rel="noopener noreferrer"&gt;Release v7.0.0 · gazay/gon&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can tell from the major version bump, this release includes &lt;strong&gt;breaking changes&lt;/strong&gt;.&lt;br&gt;
These breaking changes were introduced by the following PR created by &lt;a href="https://github.com/krororo" rel="noopener noreferrer"&gt;@krororo&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/gazay/gon/pull/296" rel="noopener noreferrer"&gt;Breaking: Make request_store an optional dependency by krororo · Pull Request #296 · gazay/gon&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In gon, when you set a value like &lt;code&gt;gon.foo = 'bar'&lt;/code&gt;, the value used to be stored internally using &lt;a href="https://github.com/steveklabnik/request_store" rel="noopener noreferrer"&gt;request_store&lt;/a&gt;.&lt;br&gt;
In the early days of Rails application development, &lt;code&gt;request_store&lt;/code&gt; was almost the only practical option when you wanted to use a global value that was automatically reset for each request.&lt;/p&gt;

&lt;p&gt;However, since Rails 5.2, Rails has provided an official alternative:&lt;br&gt;
&lt;a href="https://api.rubyonrails.org/classes/ActiveSupport/CurrentAttributes.html" rel="noopener noreferrer"&gt;ActiveSupport::CurrentAttributes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The PR above removes the dependency on &lt;code&gt;request_store&lt;/code&gt; and switches gon to use &lt;code&gt;ActiveSupport::CurrentAttributes&lt;/code&gt; instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is good
&lt;/h2&gt;

&lt;p&gt;I believe this change improves stability.&lt;/p&gt;

&lt;p&gt;Currently, &lt;code&gt;request_store&lt;/code&gt; uses &lt;a href="https://github.com/steveklabnik/request_store/blob/ded0d62b3636844ddb00b04a98994eaf16ec6922/lib/request_store.rb#L7" rel="noopener noreferrer"&gt;&lt;code&gt;Thread#[]&lt;/code&gt;&lt;/a&gt; internally.&lt;br&gt;
As documented in the &lt;a href="https://docs.ruby-lang.org/en/3.4/Thread.html#method-i-5B-5D" rel="noopener noreferrer"&gt;&lt;code&gt;Thread#[]&lt;/code&gt; documentation&lt;/a&gt;, &lt;code&gt;Thread#[]&lt;/code&gt; actually refers to &lt;strong&gt;fiber-local variables&lt;/strong&gt;, which means the value cannot be accessed once execution switches to another Fiber.&lt;/p&gt;

&lt;p&gt;Because of this, if your request-handling code uses Fibers, gon may behave incorrectly.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ActiveSupport::CurrentAttributes&lt;/code&gt; also initially relied on fiber-local variables when it was first introduced&lt;br&gt;
(see &lt;a href="https://github.com/rails/rails/blob/375a4143cf5caeb6159b338be824903edfd62836/activesupport/lib/active_support/current_attributes.rb#L142-L142" rel="noopener noreferrer"&gt;this implementation&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;However, starting with Rails 7.0, Rails can automatically switch between fiber-local and thread-local storage depending on the type of server being used&lt;br&gt;
(see &lt;a href="https://github.com/Shopify/rails/commit/540d2f41f6913cd6c5a71301540bfe1551c2acc5" rel="noopener noreferrer"&gt;this commit by Shopify&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;As a result, if you are using &lt;strong&gt;Rails 7.0 or later&lt;/strong&gt;, &lt;code&gt;ActiveSupport::CurrentAttributes&lt;/code&gt; should be more stable than &lt;code&gt;request_store&lt;/code&gt;.&lt;br&gt;
That is why we decided to merge this PR.&lt;/p&gt;

&lt;h2&gt;
  
  
  Concerns
&lt;/h2&gt;

&lt;p&gt;Most users of gon probably don’t consciously think about whether it uses &lt;code&gt;request_store&lt;/code&gt; or &lt;code&gt;ActiveSupport::CurrentAttributes&lt;/code&gt; internally.&lt;/p&gt;

&lt;p&gt;Given that, the new behavior is as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you are using &lt;strong&gt;Rails 5.2 or later&lt;/strong&gt; and &lt;code&gt;ActiveSupport::CurrentAttributes&lt;/code&gt; is available, gon will use it automatically.&lt;/li&gt;
&lt;li&gt;Otherwise, you will need to explicitly add &lt;code&gt;request_store&lt;/code&gt; to your Gemfile:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'request_store'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I believe there are very few cases where you would want to explicitly use &lt;code&gt;request_store&lt;/code&gt; with modern versions of Rails, which is why we made this decision.&lt;br&gt;
That said, if you run into any issues, please let us know!&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
    </item>
    <item>
      <title>What is config.active_support.hash_digest_class = OpenSSL::Digest::SHA256?</title>
      <dc:creator>Shinichi Maeshima</dc:creator>
      <pubDate>Tue, 26 Nov 2024 09:13:07 +0000</pubDate>
      <link>https://dev.to/willnet/what-is-configactivesupporthashdigestclass-openssldigestsha256-4na7</link>
      <guid>https://dev.to/willnet/what-is-configactivesupporthashdigestclass-openssldigestsha256-4na7</guid>
      <description>&lt;h2&gt;
  
  
  What is &lt;code&gt;config.active_support.hash_digest_class = OpenSSL::Digest::SHA256&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;I occasionally update the &lt;code&gt;config.load_defaults&lt;/code&gt; version at work in Rails applications. This involves understanding the purpose of each new configuration in the target version, assessing its impact, and creating individual pull requests for each change. Based on my experience, this process can be quite challenging, and I suspect few projects carry it out with a thorough understanding of the impact. I decided to write this blog post to assist others planning to update &lt;code&gt;config.load_defaults&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Today, I’ll focus on &lt;code&gt;config.active_support.hash_digest_class = OpenSSL::Digest::SHA256&lt;/code&gt;, which is included in &lt;code&gt;config.load_defaults 7.0&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Relevant Commit: &lt;a href="https://github.com/rails/rails/commit/ba9207f301332b6c3e748eb8fe46b7f6c9ced667" rel="noopener noreferrer"&gt;Change the default digest for new apps to SHA256 · rails/rails@ba9207f&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Related Documentation: &lt;a href="https://guides.rubyonrails.org/upgrading_ruby_on_rails.html#digest-class-for-activesupport-digest-changing-to-sha256" rel="noopener noreferrer"&gt;Upgrading Ruby on Rails — Ruby on Rails Guides&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This setting changes the default hash function used for cache keys to SHA256. The default was originally MD5, then updated to SHA1 in Rails 5.2, and now to SHA256 in Rails 7.0.&lt;/p&gt;

&lt;p&gt;Since this hash function primarily applies to cache keys, the change wasn’t prompted by security issues with SHA1. Instead, the shift to SHA256 aligns Rails with modern security standards, simplifying compliance checks.&lt;/p&gt;




&lt;h2&gt;
  
  
  Related Areas in Rails
&lt;/h2&gt;

&lt;p&gt;The following are the main areas in Rails where this hash function is used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/rails/rails/blob/fc734f28e65ef8829a1a939ee6702c1f349a1d5a/actionview/lib/action_view/digestor.rb#L93-L95" rel="noopener noreferrer"&gt;Determining keys for fragment cache templates&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Used to compute the template tree digest in the &lt;a href="https://api.rubyonrails.org/classes/ActionView/Helpers/CacheHelper.html#method-i-cache" rel="noopener noreferrer"&gt;&lt;code&gt;cache&lt;/code&gt; method&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/rails/rails/blob/fc734f28e65ef8829a1a939ee6702c1f349a1d5a/activerecord/lib/active_record/relation.rb#L326" rel="noopener noreferrer"&gt;Setting cache keys when using &lt;code&gt;cache(User.all)&lt;/code&gt; with an &lt;code&gt;ActiveRecord::Relation&lt;/code&gt;&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Specifically, it computes the &lt;code&gt;id&lt;/code&gt; portion of the key.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/rails/rails/blob/fc734f28e65ef8829a1a939ee6702c1f349a1d5a/actionpack/lib/action_dispatch/http/cache.rb#L134-L136" rel="noopener noreferrer"&gt;Generating strong ETags&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Reference: &lt;a href="https://guides.rubyonrails.org/caching_with_rails.html#strong-v-s-weak-etags" rel="noopener noreferrer"&gt;Rails Caching Guide&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;a href="https://github.com/rails/rails/blob/522c86f35ccc80453ed9fb6ca8b394db321f9a69/activesupport/lib/active_support/cache/redis_cache_store.rb#L435" rel="noopener noreferrer"&gt;Shortening long cache keys&lt;/a&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;You can safely apply this setting change if your project doesn’t rely on these caching features.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Verify Affected Areas
&lt;/h2&gt;

&lt;p&gt;You can verify whether caching is used by adding a monkey patch like the following, which logs whenever &lt;code&gt;ActiveSupport::Digest.hexdigest&lt;/code&gt; is called. This works for all related areas except "shortening long cache keys." Merge logs appropriately if your CI runs tests in parallel.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/application.rb or config/initializers/something.rb&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;MonkeyPatch&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;monkeypatch_logger&lt;/span&gt;
    &lt;span class="vi"&gt;@monkeypatch_logger&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="no"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'log/monkeypatch.log'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;monkeypatch_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"ActiveSupport::Digest.hexdigest is called from: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;caller&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ActiveSupport::Digest&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;
    &lt;span class="n"&gt;prepend&lt;/span&gt; &lt;span class="no"&gt;MonkeyPatch&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before running tests, enable caching and set the cache store to &lt;code&gt;:null_store&lt;/code&gt; to avoid unexpected behavior:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/environments/test.rb&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;action_controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform_caching&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cache_store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:null_store&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you find any areas using caching, note that this change will invalidate all related caches after deployment. This could increase server or database load, so consider temporarily scaling up your application servers and database resources to handle the extra traffic.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
    </item>
    <item>
      <title>Summary of changes in behavior of create_association method in Rails 7.0.5 and later</title>
      <dc:creator>Shinichi Maeshima</dc:creator>
      <pubDate>Tue, 04 Jul 2023 07:11:53 +0000</pubDate>
      <link>https://dev.to/willnet/summary-of-changes-in-behavior-of-createassociation-method-in-rails-705-and-later-dhk</link>
      <guid>https://dev.to/willnet/summary-of-changes-in-behavior-of-createassociation-method-in-rails-705-and-later-dhk</guid>
      <description>&lt;p&gt;This article is an English translation from &lt;a href="https://blog.willnet.in/entry/2023/07/04/113321"&gt;https://blog.willnet.in/entry/2023/07/04/113321&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are several related PRs and Issues on this matter and comments are scattered, and it is complicated to explain to people, so this is a summary as a blog. Please comment if you find any mistakes or opinions!&lt;/p&gt;

&lt;h2&gt;
  
  
  Update 2023/08/02
&lt;/h2&gt;

&lt;p&gt;A commit has been made to revert this change to the 7-0-stable branch. It seems that the policy is to revert back and start over because some people had trouble with the new behavior.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/rails/rails/pull/48809"&gt;[7-0-stable] Revert singular association breaking changes by zzak · Pull Request #48809 · rails/rails&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I don't know when 7.0.7 will be released, but if it is released in its current state, it will revert to 7.0.4 behavior; those who are having trouble with changes after 7.0.5 may want to point to 7-0-stable once it is released.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Behavior of create_association method generated when defining has_one association is different in Rails 7.0.4 and 7.0.5 or later.&lt;/li&gt;
&lt;li&gt;If you think that the behavior of create_association is only "create a new association record", you may need to modify your code to use 7.0.5 or later.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Assumptions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;If you try to create a new model via has_one association when the association is already persisted, the existing association will be deleted(behavior of &lt;code&gt;association=&lt;/code&gt; is written in &lt;a href="http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_one"&gt;http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_one&lt;/a&gt; but &lt;code&gt;build_association&lt;/code&gt; and &lt;code&gt;create_association&lt;/code&gt; do not seem to be described, including in the Rails guide).&lt;/li&gt;
&lt;li&gt;The way the deletion is done depends on the dependent option of has_one (This is where the code is applicable. &lt;a href="https://github.com/rails/rails/blob/89508b95d8d289b430cca7db05109d8171ff7a5f/activerecord/lib/active_record/associations/has_one_association.rb#L94-L116"&gt;https://github.com/rails/rails/blob/89508b95d8d289b430cca7db05109d8171ff7a5f/activerecord/lib/active_record/associations/has_one_association.rb#L94-L116&lt;/a&gt; )

&lt;ul&gt;
&lt;li&gt;When &lt;code&gt;:nullify&lt;/code&gt; or &lt;code&gt;:dependent&lt;/code&gt; option is not set, set NULL to the foreign key and UPDATE&lt;/li&gt;
&lt;li&gt;When &lt;code&gt;:delete&lt;/code&gt;, delete with the &lt;code&gt;destroy&lt;/code&gt; method&lt;/li&gt;
&lt;li&gt;When &lt;code&gt;:delete&lt;/code&gt;, delete with &lt;code&gt;delete&lt;/code&gt; method.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, in all of (1)~(3) in the following code, if there is a record associated with an existing associate, it will be deleted.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;has_one&lt;/span&gt; &lt;span class="ss"&gt;:address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;dependent: :destroy&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Address&lt;/span&gt;
  &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:user&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="c1"&gt;# (1)&lt;/span&gt;
&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_address&lt;/span&gt; &lt;span class="c1"&gt;# (2)&lt;/span&gt;
&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build_address&lt;/span&gt; &lt;span class="c1"&gt;# (3)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Behavior up to Rails 7.0.4
&lt;/h2&gt;

&lt;p&gt;Previously, the behavior of create_association was to insert and then delete in separate transactions as described in &lt;a href="https://github.com/rails/rails/pull/46386"&gt;this PR&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# begin transaction
INSERT new_record; # commit transaction
# commit transaction

# begin transaction
DELETE old_record; # commit transaction
# commit transaction
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Furthermore, even if the preceding insert (save method) fails due to a validation error, the subsequent delete will still be executed. This behavior caused a bug in which a validation error would result in the deletion of an existing record when execute &lt;code&gt;create_association!&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Related Issue: &lt;a href="https://github.com/rails/rails/issues/46737"&gt;has_one association getting deleted on using create_association &amp;amp; validation fails - Issue #46737 - rails/rails&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There was another issue where deletion of existing records did not work in the following way&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;create a new record when the validation passes&lt;/li&gt;
&lt;li&gt;then delete the existing record&lt;/li&gt;
&lt;li&gt;retrieving an existing record for deletion is done via has_one related method.&lt;/li&gt;
&lt;li&gt;when the order by related to has_one is not set (in most cases, order by is not set for has_one), the newly created record is retrieved in rare cases. &lt;/li&gt;
&lt;li&gt;if this newly created record is retrieved, it is treated as if there are no records to delete&lt;/li&gt;
&lt;li&gt;as a result, there are two or more records without deletion.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Related Issue: &lt;a href="https://github.com/rails/rails/issues/47554"&gt;Sometimes &lt;code&gt;create_association&lt;/code&gt; does not delete existing records - Issue #47554 - rails/rails&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Behavior since Rails 7.0.5
&lt;/h2&gt;

&lt;p&gt;In 7.0.5, the behavior has changed to delete and then insert in the same transaction, as shown below, and the above bug has been mostly resolved.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# begin transaction
DELETE old_record;
INSERT new_record; # commit transaction
# commit transaction
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, the code would need to be modified for an application that originally had a unique constraint on the has_one foreign key and wanted to make a validation error if create_association was performed when there was an existing record.&lt;/p&gt;

&lt;p&gt;Related Issue: &lt;a href="https://github.com/rails/rails/issues/48330"&gt;has_one relation start deleting existing record even when new record fails passing validation - Issue #48330 - rails/rails&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Thoughts
&lt;/h2&gt;

&lt;p&gt;I personally feel that the changes in 7.0.5 were necessary for the consistency of the behavior described in the "Assumptions" section, but on the other hand, I understand that it may be surprising to people who do not know that &lt;code&gt;association=&lt;/code&gt;, &lt;code&gt;build_association&lt;/code&gt;, or &lt;code&gt;create_association&lt;/code&gt; will delete existing records. On the other hand, I can understand why some people might find it hard to modify their code to accommodate this change, as it might be a surprise to those who are not familiar with this behavior.&lt;/p&gt;

&lt;p&gt;I wonder what kind of behavior would make everyone happy...?&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
    </item>
  </channel>
</rss>
