FIX: Thread safety issues with `multisite:migrate` and `SeedFu`.

FIX: Thread safety issues with multisite:migrate and SeedFu.

diff --git a/db/fixtures/001_refresh.rb b/db/fixtures/001_refresh.rb
index 9d3a961..e340c8d 100644
--- a/db/fixtures/001_refresh.rb
+++ b/db/fixtures/001_refresh.rb
@@ -1,8 +1,19 @@
 # frozen_string_literal: true
 
-# fix any bust caches post initial migration
-ActiveRecord::Base.connection.tables.each do |table|
-  table.classify.constantize.reset_column_information rescue nil
+class SeedData::Refresher
+  def self.refresh!
+    return if @refreshed
+
+    # Fix any bust caches post initial migration
+    # Not that reset_column_information is not thread safe so we have to becareful
+    # not to run it concurrently within the same process.
+    ActiveRecord::Base.connection.tables.each do |table|
+      table.classify.constantize.reset_column_information rescue nil
+    end
+
+    @refreshed = true
+  end
 end
 
+SeedData::Refresher.refresh!
 SiteSetting.refresh!
diff --git a/lib/tasks/db.rake b/lib/tasks/db.rake
index 7b6c928..fdd463b 100644
--- a/lib/tasks/db.rake
+++ b/lib/tasks/db.rake
@@ -107,48 +107,63 @@ task 'multisite:migrate' => ['db:load_config', 'environment', 'set_locale'] do |
   puts "Multisite migrator is running using #{concurrency} threads"
   puts
 
-  queue = Queue.new
   exceptions = Queue.new
 
   old_stdout = $stdout
   $stdout = StdOutDemux.new($stdout)
 
-  RailsMultisite::ConnectionManagement.each_connection do |db|
-    queue << db
-  end
+  SeedFu.quiet = true
 
-  concurrency.times { queue << :done }
+  def execute_concurently(concurrency)
+    queue = Queue.new
 
-  SeedFu.quiet = true
+    RailsMultisite::ConnectionManagement.each_connection do |db|
+      queue << db
+    end
 
-  (1..concurrency).map do
-    Thread.new {
-      while true
-        db = queue.pop
-        break if db == :done
-
-        RailsMultisite::ConnectionManagement.with_connection(db) do
-          begin
-            puts "Migrating #{db}"
-            ActiveRecord::Tasks::DatabaseTasks.migrate
-            SeedFu.seed(DiscoursePluginRegistry.seed_paths)
-            if !Discourse.skip_post_deployment_migrations? && ENV['SKIP_OPTIMIZE_ICONS'] != '1'
-              SiteIconManager.ensure_optimized!
-            end
-          rescue => e
-            exceptions << [db, e]
-          ensure
+    concurrency.times { queue << :done }
+
+    (1..concurrency).map do
+      Thread.new {
+        while true
+          db = queue.pop
+          break if db == :done
+
+          RailsMultisite::ConnectionManagement.with_connection(db) do
             begin
-              $stdout.finish_chunk
-            rescue => ex
-              STDERR.puts ex.inspect
-              STDERR.puts ex.backtrace
+              yield(db) if block_given?
+            rescue => e
+              exceptions << [db, e]
+            ensure
+              begin
+                $stdout.finish_chunk
+              rescue => ex
+                STDERR.puts ex.inspect
+                STDERR.puts ex.backtrace
+              end
             end
           end
         end
-      end
-    }
-  end.each(&:join)
+      }
+    end.each(&:join)
+  end
+
+  execute_concurently(concurrency) do |db|
+    puts "Migrating #{db}"
+    ActiveRecord::Tasks::DatabaseTasks.migrate
+
+    if !Discourse.skip_post_deployment_migrations? && ENV['SKIP_OPTIMIZE_ICONS'] != '1'
+      SiteIconManager.ensure_optimized!
+    end
+  end
+
+  seed_paths = DiscoursePluginRegistry.seed_paths
+  SeedFu.seed(seed_paths, /001_refresh/)
+
+  execute_concurently(concurrency) do |db|
+    puts "Seeding #{db}"
+    SeedFu.seed(seed_paths)
+  end
 
   $stdout = old_stdout
 

GitHub sha: 45eb97c2

This commit has been mentioned on Discourse Meta. There might be relevant details there:

https://meta.discourse.org/t/rake-multisite-migrate-is-broken/155329/3