akarollil
akarollil

Reputation: 509

Implementing mutual exclusion for a shared resource used by multiple Ruby rspec test runs

I am trying to fix a problem when running multiple tests that all login and search for emails in a shared IMAP email server account. Tests use a helper that uses NET::IMAP for login/search/delete and they occasionally fail when logging in or searching for emails. I think this is because of concurrent access of the mailbox by multiple rspec test runs in parallel.

I read about the MonitorMixin in Ruby but from what I understand that is for use by threads spawned within a process rather than between processes. I found this blog post that describes using Redis::Semaphore to implement a distributed lock: https://docs.knapsackpro.com/2017/when-distributed-locks-might-be-helpful-in-ruby-on-rails-application. But that needs Redis.

The tests are for a Ruby Sinatra app with postgres, so I do have postgres to use for shared locking amongst all rspec tests. So I guess I could build a simple database semaphore, like https://udby.com/archives/14 seems to describe.

Is there a simpler way to solve this problem of multiple processes needing to access a shared resource?

Upvotes: 0

Views: 281

Answers (1)

akarollil
akarollil

Reputation: 509

Thanks @konstantin-strukov. I couldn't get advisory locks to work using the with_advisory_lock Ruby gem. For whatever reason, after adding the gem to the project Gemfile, the Active Record model classes did not get extended to have the with_advisory_lock method. I am not sure if there is anything else I need to have done for the models to get extended.

I ended up using a file lock, which is included in the Ruby 'kernel' and that worked fine. Thanks to this other Stack Overflow answer. Here is a snippet:

class EmailChecker

    def initialize
        @lock_file_path = '/tmp/rspec-imap-flock'
    end

    def check_mail
        open(@lock_file_path, File::CREAT) do |f|
            puts 'Acquiring file lock to access IMAP server...'
            # This will block if another process/rspec using this class is
            # already using this method and thus has acquired the file lock
            f.flock(File::LOCK_EX)
            puts 'Acquired file lock!'
            
            # Do mail checking and processing

            # The end of the block will result in the file getting closed
            # and the lock getting released.
        end
    end

Upvotes: 1

Related Questions