Skip to content
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

Concurrency problems in specs #239

Open
dbil25 opened this issue Dec 21, 2023 · 7 comments
Open

Concurrency problems in specs #239

dbil25 opened this issue Dec 21, 2023 · 7 comments
Assignees
Labels
bug Something isn't working

Comments

@dbil25
Copy link

dbil25 commented Dec 21, 2023

Steps to reproduce

In rspec, especially in feature specs, multiple threads can run concurrently ( for example the test itself and the test server) but they both use the same ActiveRecord database connection. Thats makes it that when a spec changes the schema by doig a SET search_path TO, then it can also affects the other threads causing unexpected behaviors. At least thats what i understood after debugging some very flaky behavior in the feature specs.

This does not happen in a normal context (development and production), it only happens during specs, especially feature specs.

Is there an existing solution for this kind of bugs? meanwhile, if other people encounter the same problem i have a working fix by adding a monkey-patch in a spec/support file:

# patch to fix concurrency problem in test mode. In test mode, you can have multiple threads
# that uses the same ActiveRecord connection. Each thread has its own instance of apartment
# and can do a Tenant.switch!. This cause other threads to start using the wrong schema.
# with this patch, before every single sql query, we validate that we are doing it in the
# correct schema. If we are not in the correct schema, we switch. This is not optimal and causes
# a lot of overhead, but luckily its a test-only problem.

module ApartmentExtension
  def exec_query(sql, name = "SQL", binds = [], prepare: false, async: false)
    raise unless Rails.env.test? # this patch is for tests only, do not use in dev/prod!
    @lock.synchronize do
      current_schema_name = get_current_schema
      if current_schema_name && current_schema_name != Apartment::Tenant.current
        Apartment::Tenant.switch!(Apartment::Tenant.current)
      end
      super(sql, name, binds, prepare: prepare, async: async)
    end
  end

  def get_current_schema
    current_schema
  rescue
    nil
  end
end

class ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
  prepend ApartmentExtension
end

System configuration

  • Database: postgres 14

  • Apartment version: 2.11

  • Apartment config (in config/initializers/apartment.rb or so):

    • use_schemas: true
  • Rails (or ActiveRecord) version: 7.0.8

  • Ruby version: 3.2.1

@dlupu
Copy link

dlupu commented Mar 15, 2024

@dbil25 just run into the same issue and your solution helped me fix the issue! Thank you very much :)

@otagi
Copy link

otagi commented Oct 22, 2024

I have a similar issue when upgrading Rails from 7.0 to 7.1. When running the full spec suite, rspec eventually stops without exiting (in a model spec). It runs fine with Rails 7.0.

The main thread backtrace reports:

/.../.rbenv/versions/3.3.4/lib/ruby/gems/3.3.0/gems/activesupport-7.1.4.1/lib/active_support/concurrency/load_interlock_aware_monitor.rb:55:in `lock'
# ...
/.../.rbenv/versions/3.3.4/lib/ruby/3.3.0/forwardable.rb:240:in `switch!'
/.../spec/support/apartment.rb:22:in `block (2 levels) in <main>'
# spec/support/apartment.rb

RSpec.configure do |config|
  config.before(:each) do |example|
    Apartment::Tenant.switch! @tenant_name
  end
end
# active_support/concurrency/load_interlock_aware_monitor.rb

def mon_enter
  @mutex.lock if @owner != Thread.current
  # …
end

The PostgreSQLAdapter patch above does not resolve the lock.

System configuration

  • Database: PostgreSQL 14
  • Pg version: 1.5.8
  • Rspec-rails version: 6.1.2
  • Apartment version: 3.1.0
  • Rails version: 7.1.4.1
  • Ruby version: 3.3.4
  • use_schemas: true

@mnovelo mnovelo self-assigned this Oct 29, 2024
Copy link

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.

@github-actions github-actions bot added the stale label Nov 29, 2024
@github-actions github-actions bot closed this as not planned Won't fix, can't repro, duplicate, stale Dec 6, 2024
@dlupu
Copy link

dlupu commented Dec 10, 2024

@otagi I run into the same problem and managed to update the patch suggested by @dbil25 :

# patch for Rails 7.1

module ApartmentExtension
  def exec_query(sql, name = "SQL", binds = [], prepare: false, async: false)
    raise unless Rails.env.test? # this patch is for tests only, do not use in dev/prod!
    @lock.synchronize do
      current_schema_name = get_current_schema
      if current_schema_name && current_schema_name != Apartment::Tenant.current
        Apartment::Tenant.switch!(Apartment::Tenant.current)
      end
      super(sql, name, binds, prepare: prepare, async: async)
    end
  end

  def get_current_schema
    current_schema
  rescue
    nil
  end
end

class ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
  prepend ApartmentExtension
end

@mnovelo thanks for the work you are doing on this gem ! Would be great if these patches were included by default in the gem

@mnovelo
Copy link
Collaborator

mnovelo commented Dec 10, 2024

I'm finally done with school so I'll have more time to look into this, especially 'cause I've got some major refactoring that I want to continue.

In particular, I'm planning to use CurrentAttributes to make Apartment Thread and Fiber safe https://api.rubyonrails.org/classes/ActiveSupport/CurrentAttributes.html

@mnovelo
Copy link
Collaborator

mnovelo commented Jan 9, 2025

This has been an ongoing issue for us as well but wasn't addressed in v3.2.0, but I hope to add such support in v4.0.0. Your help would be greatly appreciated! #312

Copy link

github-actions bot commented Feb 9, 2025

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.

@github-actions github-actions bot added the stale label Feb 9, 2025
@mnovelo mnovelo added bug Something isn't working and removed stale labels Feb 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants