Testing concurrency functions

How would you test Ruby code with some concurrency functions? For example, suppose I have a synchronization mechanism that is expected to prevent deadlocks. Is there a viable way to verify what it actually does? Can controlled performance in fibers be a direction?

+4
source share
3 answers

I had the same problem and implemented a simple gem for synchronizing subprocesses using breakpoints: http://github.com/remen/fork_break

I also documented an extended use case for rails3 at http://www.hairoftheyak.com/testing-concurrency-in-rails/

+3
source

I needed to make sure that the gems I created ( redis-native_hash ) could handle simultaneous writes to the same Redis hash, detect race conditions and recover elegantly. I found that I did not need to use threads at all to verify this.

 it "should respect changes made since last read from redis" do concurrent_edit = Redis::NativeHash.find :test => @hash.key concurrent_edit["foo"] = "race value" concurrent_edit.save @hash["yin"] = "yang" @hash["foo"] = "bad value" @hash.save hash = Redis::NativeHash.find :test => @hash.key hash["foo"].should == "race value" hash["yin"].should == "yang" end 

In this test case, I simply created an instance of another object, which is editing a Redis hash at the same time if it makes a change, and then save an existing object that points to the same hash that respects these changes.

Not all problems involving concurrency can be tested without actually using concurrency, but in this case it was possible. You might want to try something similar to testing your concurrency solutions. If possible, this is by far the easier way.

+1
source

This is definitely a difficult problem. I started writing my test using threads and realized that they, like the code I tested, were implemented, I need process identifiers (PIDs) to be different. Threads are started using the same PID as the process that started with Thread. Lesson learned.

It was at that moment that I began to study forks and stumbled upon a thread of Qaru and played with fork_break. Pretty cool and easy to set up. Although I don't need breakpoints for what I was doing, I just wanted the processes to run in parallel, using breakpoints could be very useful in the future. The problem I ran into was that I kept getting EOFError and I did not know why. So I started implementing forking, instead of going through fork_break, and found out that the exception occurred in the test code. It is sad that the stack trace was hidden from me by EOFError, although I understand that the child process ended abruptly and something like that.

The next issue I ran into was with DatabaseCleaner. No matter what strategy he used (truncation or transaction), the data of the child process was truncated / rolled back when the child process was completed, so the data that was inserted by the child processes disappeared and the parent process could not select and make sure that it was right.

After I hit my head about it and tried many other unsuccessful things, I came across this post http://makandracards.com/makandra/556-test-concurrent-ruby-code , which was almost the same as I was already to do, with one small addition. Call "Process.exit!" at the end of the fork. My best guess (based on my rather limited understanding of forking) is that this leads to the process terminating abruptly enough that it completely bypasses any type of database cleanup when the child process ends. Therefore, my parent process, the actual test, can continue and verify the data that needs to be verified. Then, during the normal test after hooks (in this case, cucumber, but it can also be easy rspec too), clearing the database starts and cleans the data, as usual, for the test.

So, I just thought I would share some of my own lessons learned in this discussion of how to test parallel functions.

+1
source

Source: https://habr.com/ru/post/1381592/


All Articles