Ruby - Redis based mutex with expiration implementation - ruby

I'm trying to implement a memory based, multi process shared mutex, which supports timeout, using Redis.
I need the mutex to be non-blocking, meaning that I just need to be able to know if I was able to fetch the mutex or not, and if not - simply continue with execution of fallback code.
something along these lines:
if lock('my_lock_key', timeout: 1.minute)
# Do some job
else
# exit
end
An un-expiring mutex could be implemented using redis's setnx mutex 1:
if redis.setnx('#{mutex}', '1')
# Do some job
redis.delete('#{mutex}')
else
# exit
end
But what if I need a mutex with a timeout mechanism (In order to avoid a situation where the ruby code fails before the redis.delete command, resulting the mutex being locked forever, for example, but not for this reason only).
Doing something like this obviously doesn't work:
redis.multi do
redis.setnx('#{mutex}', '1')
redis.expire('#{mutex}', key_timeout)
end
since I'm re-setting an expiration to the mutex EVEN if I wasn't able to set the mutex (setnx returns 0).
Naturally, I would've expected to have something like setnxex which atomically sets a key's value with an expiration time, but only if the key does not exist already. Unfortunately, Redis does not support this as far as I know.
I did however, find renamenx key otherkey, which lets you rename a key to some other key, only if the other key does not already exist.
I came up with something like this (for demonstration purposes, I wrote it down monolithically, and didn't break it down to methods):
result = redis.multi do
dummy_key = "mutex:dummy:#{Time.now.to_f}#{key}"
redis.setex dummy_key, key_timeout, 0
redis.renamenx dummy_key, key
end
if result.length > 1 && result.second == 1
# do some job
redis.delete key
else
# exit
end
Here, i'm setting an expiration for a dummy key, and try to rename it to the real key (in one transaction).
If the renamenx operation fails, then we weren't able to obtain the mutex, but no harm done: the dummy key will expire (it can be optionally deleted immediately by adding one line of code) and the real key's expiration time will remain intact.
If the renamenx operation succeeded, then we were able to obtain the mutex, and the mutex will get the desired expiration time.
Can anyone see any flaw with the above solution? Is there a more standard solution for this problem? I would really hate using an external gem in order to solve this problem...

If you're using Redis 2.6+, you can do this much more simply with the Lua scripting engine. The Redis documentation says:
A Redis script is transactional by definition, so everything you can do with a Redis transaction, you can also do with a script, and usually the script will be both simpler and faster.
Implementing it is trivial:
LUA_ACQUIRE = "return redis.call('setnx', KEYS[1], 1) == 1 and redis.call('expire', KEYS[1], KEYS[2]) and 1 or 0"
def lock(key, timeout = 3600)
if redis.eval(LUA_ACQUIRE, key, timeout) == 1
begin
yield
ensure
redis.del key
end
end
end
Usage:
lock("somejob") { do_exclusive_job }

Starting from redis 2.6.12 you can do: redis.set(key, 1, nx: true, ex: 3600) which is actually SET key 1 NX EX 3600.
I was inspired by the simplicity that of both Chris's and Mickey's solutions, and created gem - simple_redis_lock with this code(and some features and rspec):
def lock(key, timeout)
if #redis.set(key, Time.now, nx: true, px: timeout)
begin
yield
ensure
release key
end
end
end
I explored some other awesome alternatives:
mlanett/redis-lock
PatrickTulskie/redis-lock
leandromoreira/redlock-rb
dv/redis-semaphore
but they had too many features of blocking to acquire lock and didn't use this single SET KEY 1 NX EX 3600 atomic redis statement.

Related

How is CopyFileEx's pbCancel parameter safe?

There are several questions posted (like Send flag to cancel CopyFileEx after it has started) that reference the ability to use the pbCancel parameter of the Win32 CopyFileEx() function to cancel an in-progress copy. What is not clear to me, is why is it safe to set that boolean in another thread without any sort of synchronization mechanism (mutex, etc.)? This functionality is really only useful if another thread sets that boolean to true, as CopyFileEx() blocks until the file copy is finished.
Is this relying on a peculiarity of the Windows platform?
in case simply set boolean by sense variable (which can be 0 or not 0) without any connection with other data - not need any synchronization - for what ?!
one thread set variable to true, another thread read from variable or 0 or not 0. even if you do write and read to variable in critical section - what this change ? nothing ! thread which will be read - anyway load or 0 or not 0 from variable.
synchronization need in another cases. usually when we write to another memory locations, before store true in variable, and want that after another thread read true from variable - all another memory modification will be visible already. but in case cancel callback - no any another data
or if we write complex data to variable (not only or 0 or not 0) and this write not atomic - synchronization need for prevent read partial state. but here impossible any "partial state" by design
For those who not understand:
no matter are write to or read from pbCancel will be atomic.
in any case finally some value will be read from pbCancel.
if this value will be interpreted as TRUE during the copy operation, the operation is canceled. Otherwise, the copy operation will continue to completion.
If this flag is set to TRUE during the copy operation, the operation is canceled. Otherwise, the copy operation will continue to
completion.
even if some "transient state" will be read - and so what -any way this value will be used in if/else expression and as result copy operation will be canceled or continue.
Nowhere is not specified (and this contradicts common sense) that a strict checking of 0 (false) and 1(true) will be performed and in case of any other value there will be an exception or UB.
on the contrary - it is clearly indicated that Otherwise (ie flag not set to TRUE), the copy operation will continue to completion.. no any words about exception, UB, etc
if look on declaration of CopyFileExW, here visible in more details how pbCancel value is interpreted:
_When_(pbCancel != NULL, _Pre_satisfies_(*pbCancel == FALSE))
_Inout_opt_ LPBOOL pbCancel,
so check is (and this is most native)
if (pbCancel == NULL || *pbCancel == FALSE)
// continue copy
else
// cancel copy
here no any "transient state". here or 0 or not 0. even if you write 1 to pbCancel but another thread read from it say 0x5FD38FCA - this will be interpreted as TRUE and copy operation will be canceled.
anyway - if you write true (in strict sense 1) to variable - another thread sooner or later read 1 from this variable. do this in critical section - nothing change - again only sooner or later another thread read this value. not faster.

Why am I not allowed to break a Promise?

The following simple Promise is vowed and I am not allowed to break it.
my $my_promise = start {
loop {} # or sleep x;
'promise response'
}
say 'status : ', $my_promise.status; # status : Planned
$my_promise.break('promise broke'); # Access denied to keep/break this Promise; already vowed
# in block <unit> at xxx line xxx
Why is that?
Because the Promise is vowed, you cannot change it: only something that actually has the vow, can break the Promise. That is the intent of the vow functionality.
What are you trying to achieve by breaking the promise as you showed? Is it to stop the work being done inside of the start block? Breaking the Promise would not do that. And the vow mechanism was explicitly added to prevent you from thinking it can somehow stop the work inside a start block.
If you want work inside a start block to be interruptible, you will need to add some kind of semaphore that is regularly checked, for instance:
my int $running = 1;
my $my_promise = start {
while $running {
# do stuff
}
$running
}
# do other stuff
$running = 0;
await $my_promise;
Hope this made sense.
The reason why you cannot directly keep/break Promise from outside or stop it on Thread Pool are explained here in Jonathans comment.
Common misuse of Promises comes from timeout pattern.
await Promise.anyof(
start { sleep 4; say "finished"; },
Promise.in( 1 )
);
say "moving on...";
sleep;
This will print "finished". And when user realize that the next logical step for him is to try to kill obsolete Promise. While the only correct way to solve it is to make Promise aware that its work is no longer needed. For example through periodically checking some shared variable.
Things gets complicated if you have blocking code on Promise (for example database query) that runs for too long and you want to terminate it from main thread. That is not doable on Promises. All you can do is to ensure Promise will run in finite time (for example on MySQL by setting MAX_EXECUTION_TIME before running query). And then you have choice:
You can grind your teeth and patiently wait for Promise to finish. For example if you really must disconnect database in main thread.
Or you can move on immediately and allow "abandoned" Promise to finish on its own, without ever receiving its result. In this case you should control how many of those Promises can stack up in background by using Semaphore or running them on dedicated ThreadPoolScheduler.

Mutex for ActiveRecord Model

My User model has a nasty method that should not be called simultaneously for two instances of the same record. I need to execute two http requests in a row and at the same time make sure that any other thread does not execute the same method for the same record at the same time.
class User
...
def nasty_long_running_method
// something nasty will happen if this method is called simultaneously
// for two instances of the same record and the later one finishes http_request_1
// before the first one finishes http_request_2.
http_request_1 // Takes 1-3 seconds.
http_request_2 // Takes 1-3 seconds.
update_model
end
end
For example this would break everything:
user = User.first
Thread.new { user.nasty_long_running_method }
Thread.new { user.nasty_long_running_method }
But this would be ok and it should be allowed:
user1 = User.find(1)
user2 = User.find(2)
Thread.new { user1.nasty_long_running_method }
Thread.new { user2.nasty_long_running_method }
What would be the best way to make sure the method is not called simultaneously for two instances of the same record?
I found a gem Remote lock when searching for a solution for my problem. It is a mutex solution that uses Redis in the backend.
It:
is accessible for all processes
does not lock the database
is in memory -> fast and no IO
The method looks like this now
def nasty
$lock = RemoteLock.new(RemoteLock::Adapters::Redis.new(REDIS))
$lock.synchronize("capi_lock_#{user_id}") do
http_request_1
http_request_2
update_user
end
end
I would start with adding a mutex or semaphore. Read about mutex: http://www.ruby-doc.org/core-2.1.2/Mutex.html
class User
...
def nasty
#semaphore ||= Mutex.new
#semaphore.synchronize {
# only one thread at a time can enter this block...
}
end
end
If your class is an ActiveRecord object you might want to use Rails' locking and database transactions. See: http://api.rubyonrails.org/classes/ActiveRecord/Locking/Pessimistic.html
def nasty
User.transaction do
lock!
...
save!
end
end
Update: You updated your question with more details. And it seems like my solutions do not really fit anymore. The first solutions does not work if you have multiple instances running. The second locks only the database row, it does not prevent multiple thread from entering the code block at the same time.
Therefore if would think about building a database based semaphore.
class Semaphore < ActiveRecord::Base
belongs_to :item, :polymorphic => true
def self.get_lock(item, identifier)
# may raise invalid key exception from unique key contraints in db
create(:item => item) rescue false
end
def release
destroy
end
end
The database should have an unique index covering the rows for the polymorphic association to item. That should protect multiple thread from getting a lock for the same item at the same time. Your method would look like this:
def nasty
until semaphore
semaphore = Semaphore.get_lock(user)
end
...
semaphore.release
end
There are a couple of problems to solve around this: How long do you want to wait to get the semaphore? What happens if the external http requests take ages? Do you need to store additional pieces of information (hostname, pid) to identifier what thread lock an item? You will need some kind of cleanup task the removes locks that still exist after a certain period of time or after restarting the server.
Furthermore I think it is a terrible idea to have something like this in a web server. At least you should move all that stuff into background jobs. What might solve your problem, if your app is small and needs just one background job to get everything done.
You state that this is an ActiveRecord model, in which case the usual approach would be to use a database lock on that record. No need for additional locking mechanisms as far as I can see.
Take a look at the short (one page) Rails Guides section on pessimistic locking - http://guides.rubyonrails.org/active_record_querying.html#pessimistic-locking
Basically you can get a lock on a single record or a whole table (if you were updating a lot of things)
In your case something like this should do the trick...
class User < ActiveRecord::Base
...
def nasty_long_running_method
with_lock do
// something nasty will happen if this method is called simultaneously
// for two instances of the same record and the later one finishes http_request_1
// before the first one finishes http_request_2.
http_request_1 // Takes 1-3 seconds.
http_request_2 // Takes 1-3 seconds.
update_model
end
end
end
I recently created a gem called szymanskis_mutex. It is a module that you can include in the class User and provides the method mutual_exclusion(concern) to provide the functionality you want.
It doesnt rely on databases and doesn't depend on how many processes want to enter the critical section at any given moment.
Note that if the class is initialized in different servers it will not work.
I may suite your needs if your app is small enough. Your code would look like this:
class User
include SzymanskisMutex
...
def nasty_long_running_method
mutual_exclusion(:nasty_long) do
http_request_1 // Takes 1-3 seconds.
http_request_2 // Takes 1-3 seconds.
end
update_model
end
end
I suggest rethinking your architecture as this is not going to be scalable - imagine having multiple ruby processes, failing processes, timeouts etc. Also in-process locking and spawning threads is quite dangerous for application servers.
If you want to sleep well with production then try some async background processing framework for long running tasks with serial queue which will ensure order of running tasks. Just simple RabbitMQ or check this QA Best practice for Rails App to run a long task in the background? , eventually try DB but Optimistic Locking.

What is the best way to periodically export a counter from a loop in Ruby

I have created a daemon in Ruby which has a counter incrementing inside of a loop. The loop does its business, then sleeps for 1 second, then continues. Simplified it's something like:
loop do
response = send_command
if response == 1
counter += 1
end
sleep(1)
end
Every 5 minutes I would like to call a method to database the counter value. I figure there are a few ways to do this. The initial way I considered was calling Time.now in the loop, examining it to match 5 minutes, 0 seconds, and if that matched, call the sql function. That seems terribly inefficient, however, and it could also miss a record if send_command took some time.
Another possibility may be to make available the counter variable, which could be called (and reset) via a socket. I briefly took a look at the Socket class, and that seems possible.
Is there an obvious/best way to do this that I'm missing?
If you just want to save every 5 minutes, you could just use a Thread. Something like:
Thread.new do
save_value_in_the_db(counter)
sleep 5*60
end
Note that the thread have access to counter if it is defined in the same block as the loop. you could also use an object and have the #counter declared insidd.
If you prefer to access remotely, you can do it with a socket or use a drb approach, that is probably easier. This drb tutorial seem to fit your requirements: http://ruby.about.com/od/advancedruby/a/drb.htm
I'd have the counter be updated every time through the loop, then periodically have something read that and update the database.
That makes a simpler main loop because it doesn't have to pay attention to how long it's needed to wait before exporting the value.
And, it's very common and normal to have a periodic task that samples a value and does something with it.
Creating a simple socket would work well. Ruby's Socket code RDoc has some samples for echo servers that might get you started.

How to use ruby fibers to avoid blocking IO

I need to upload a bunch of files in a directory to S3. Since more than 90% of the time required to upload is spent waiting for the http request to finish, I want to execute several of them at once somehow.
Can Fibers help me with this at all? They are described as a way to solve this sort of problem, but I can't think of any way I can do any work while an http call blocks.
Any way I can solve this problem without threads?
I'm not up on fibers in 1.9, but regular Threads from 1.8.6 can solve this problem. Try using a Queue http://ruby-doc.org/stdlib/libdoc/thread/rdoc/classes/Queue.html
Looking at the example in the documentation, your consumer is the part that does the upload. It 'consumes' a URL and a file, and uploads the data. The producer is the part of your program that keeps working and finds new files to upload.
If you want to upload multiple files at once, simply launch a new Thread for each file:
t = Thread.new do
upload_file(param1, param2)
end
#all_threads << t
Then, later on in your 'producer' code (which, remember, doesn't have to be in its own Thread, it could be the main program):
#all_threads.each do |t|
t.join if t.alive?
end
The Queue can either be a #member_variable or a $global.
To answer your actual questions:
Can Fibers help me with this at all?
No they can't. Jörg W Mittag explains why best.
No, you cannot do concurrency with Fibers. Fibers simply aren't a concurrency construct, they are a control-flow construct, like Exceptions. That's the whole point of Fibers: they never run in parallel, they are cooperative and they are deterministic. Fibers are coroutines. (In fact, I never understood why they aren't simply called Coroutines.)
The only concurrency construct in Ruby is Thread.
When he says that the only concurrency contruct in Ruby is Thread, remember that there are many different implimentations of Ruby and that they vary in their threading implementations. Jörg once again provides a great answer to these differences; and correctly concludes that only something like JRuby (that uses JVM threads mapped to native threads) or forking your process is how you can achieve true parallelism.
Any way I can solve this problem without threads?
Other than forking your process, I would also suggest that you look at EventMachine and something like em-http-request. It's an event driven, non-blocking, reactor pattern based HTTP client that is asynchronous and does not incur the overhead of threads.
Aaron Patterson (#tenderlove) uses an example almost exactly like yours to describe exactly why you can and should use threads to achieve concurrency in your situation.
Most I/O libraries are now smart enough to release the GVL (Global VM Lock, or most people know it as the GIL or Global Interpreter Lock) when doing IO. There is a simple function call in C to do this. You don't need to worry about the C code, but for you this means that most IO libraries worth their salt are going to release the GVL and allow other threads to execute while the thread that is doing the IO waits for the data to return.
If what I just said was confusing, you don't need to worry about it too much. The main thing that you need to know is that if you are using a decent library to do your HTTP requests (or any other I/O operation for that matter... database, interprocess communication, whatever), the Ruby interpreter (MRI) is smart enough to be able to release the lock on the interpreter and allow other threads to execute while one thread awaits IO to return. If the next thread has its own IO to grab, the Ruby interpreter will do the same thing (assuming that the IO library is built to utilize this feature of Ruby, which I believe most are these days).
So, to sum up what I am saying, use threads! You should see the performance benefit. If not, check to see whether your http library is using the rb_thread_blocking_region() function in C and, if not, find out why not. Maybe there is a good reason, maybe you need to consider using a better library.
The link to the Aaron Patterson video is here: http://www.youtube.com/watch?v=kufXhNkm5WU
It is worth a watch, even if just for the laughs, as Aaron Patterson is one of the funniest people on the internet.
You could use separate processes for this instead of threads:
#!/usr/bin/env ruby
$stderr.sync = true
# Number of children to use for uploading
MAX_CHILDREN = 5
# Hash of PIDs for children that are working along with which file
# they're working on.
#child_pids = {}
# Keep track of uploads that failed
#failed_files = []
# Get the list of files to upload as arguments to the program
#files = ARGV
### Wait for a child to finish, adding the file to the list of those
### that failed if the child indicates there was a problem.
def wait_for_child
$stderr.puts " waiting for a child to finish..."
pid, status = Process.waitpid2( 0 )
file = #child_pids.delete( pid )
#failed_files << file unless status.success?
end
### Here's where you'd put the particulars of what gets uploaded and
### how. I'm just sleeping for the file size in bytes * milliseconds
### to simulate the upload, then returning either +true+ or +false+
### based on a random factor.
def upload( file )
bytes = File.size( file )
sleep( bytes * 0.00001 )
return rand( 100 ) > 5
end
### Start a child uploading the specified +file+.
def start_child( file )
if pid = Process.fork
$stderr.puts "%s: uploaded started by child %d" % [ file, pid ]
#child_pids[ pid ] = file
else
if upload( file )
$stderr.puts "%s: done." % [ file ]
exit 0 # success
else
$stderr.puts "%s: failed." % [ file ]
exit 255
end
end
end
until #files.empty?
# If there are already the maximum number of children running, wait
# for one to finish
wait_for_child() if #child_pids.length >= MAX_CHILDREN
# Start a new child working on the next file
start_child( #files.shift )
end
# Now we're just waiting on the final few uploads to finish
wait_for_child() until #child_pids.empty?
if #failed_files.empty?
exit 0
else
$stderr.puts "Some files failed to upload:",
#failed_files.collect {|file| " #{file}" }
exit 255
end

Resources