0

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?

akarollil
  • 435
  • 4
  • 12
  • 1
    Consider using so-called "advisory lock" (check postgres documentation on explicit locking for the details) - should be enough for your case (and way simpler that the approach in the article you linked). – Konstantin Strukov Jun 06 '21 at 19:28

1 Answers1

1

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

akarollil
  • 435
  • 4
  • 12