0.0
Repository is archived
No commit activity in last 3 years
No release in over 3 years
thread_so_safe is a very simple gem to help keep multi-threaded environments synced.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

>= 1.3.0
 Project Readme

thread_so_safe¶ ↑

thread_so_safe is a very simple gem to help keep data accessed in multiple threads synced.

What is this thing?¶ ↑

Well the description pretty much says it all. thread_so_safe is a gem to help keep data accessed in multiple threads synced.

Why do I care?¶ ↑

That’s a great question! Your data is important. If your data changes during the lifecycle of your application, your application may not run properly. Oh no! That sounds terrible! Luckily we can make our code smart enough that we don’t have to worry about this.

Are you reinventing the wheel?¶ ↑

Absolutely not! thread_so_safe is not a wheel, but I might be reinventing a thread-safety mechanism. Some approaches to thread-safety lock the data from every other thread until the current thread is finished with the data. This has the potential to slow down your app quite a bit if you have many threads. Other approaches update the data found in each thread. This works when you know what you’re looking for. thread_so_safe gives you the means of checking, “has my data changed or is it still safe?” and, “I’ve changed my data, better send an update to any other threads that might be using this data.”

That sounds fancy, but how does it really work?¶ ↑

When you call register_token it creates a file in /tmp/thread_so_safe. This file contains the Time.now.to_i value at the time of creation. The time value is also stored in ThreadSoSafe‘s memory. The in_sync? method checks value stored in the ThreadSoSafe object against the value in the file. If they match the thread is safe, but if they don’t match in_sync? will return false to indicating the thread isn’t safe or synced anymore. The update! and reset! methods update the time value in the file.

At first I tried using only the timestamp of the file, but file timestamps only record hours, minutes and seconds. A lot can happen to a data source in one second. It’s because of this that I decided to store the Time.now.to_i value. Time.now returns a time value with much greater detail than jus seconds, so it looked like a good idea.

Example time¶ ↑

# It's time to register your application or data with ThreadSoSafe!
# Just pass in a unique string to the register_token method to generate
# a new thread-safety token.
ThreadSoSafe.register_token('My.App Users')

# Great! Now lets capture our data
users = User.find(:all)

# Lets make a change to our data set
users.last.destroy

# Our data has changed so let's send an update to ThreadSoSafe so that
# any other piece of code using ThreadSoSafe.register_token('My.App Users')
# will know the data has changed.
ThreadSoSafe.update!

# ...

# Well we've changed the data, but as anyone else in the meanwhile?
# We can use the in_sync? method to check.
if ThreadSoSafe.in_sync?
  puts "The data is great! Keep going!"
  # ...
end

###

# if you're working with multiple data sets that need to be in sync
# you can pass the name (in the first example's case, My.App Users)
# into the in_sync? and update!

ThreadSoSafe.register_token('Users')
users = User.find(:all)

ThreadSoSafe.register_token('Roles')
roles = Role.find(:all)

# ...

# Notice we're calling in_sync? but with a string now
unless ThreadSoSafe.in_sync?('Roles')
  users = User.find(:all)

  # Also notice we're calling update!, but with a string as well.
  ThreadSoSafe.update!('Roles)
end

###

# We can notify other threads that data has changed with update!,
# but that updates our thread token as well. I've hit a case where
# I want to notify other threads that my data has changed AND force
# a reload of data in my current thread. This is why I made a reset!
# method. Calling reset! updates the token that all threads reference,
# but does not update the token stored in the gem. This results in
# in_sync? returning false and your code can pull the appropriate data
# and call update! afterwards to re-sync the threads.

class Settings < ActiveRecord::Base
  class << self
    def [](key)
      load_collection
      @settings_collection[key]
    end

    def []=(key, value)
      item = find_or_create_by(:key => key)
      item.value = value
      item.save!

      ThreadSoSafe.reset!
      # We've reset the token. Next time Settings[:key] is called the
      # load_collection method will rebuild the @settings_collection
    end

    private
    def load_collection        
      if @settings_collection.nil?
        # Our @settings_collection variable doesn't exist so register your token
        ThreadSoSafe.register_token('MyAppSettings')
      elsif ThreadSoSafe.in_sync?
        # If our token is already registered and we're in-sync just return and
        # don't bother running the rest of the code
        return
      end  

      # We've just registered our token or we're not in-sync anymore. I guess
      # we had better reload our data

      @settings_collection = {}

      all.each { |s| @settings_collection[s.key] = s.value }

      # We reloaded our data so lets inform the thread that we're re-synced
      ThreadSoSafe.update!
    end
  end
end

So… I have to use this in my app?¶ ↑

Yes. thread_so_safe is just the mechanism used to keep track and inform whether or not data has changed. If have App1 using thread_so_safe, but App2 doesn’t – and manipulates the same data, your data wont be in sync. You’ll need to use thread_so_safe with both App1 and App2.

Questions/Comments¶ ↑

Feel free to send me a message on Github or on Twitter (@daneharrigan). Thanks!

Copyright © 2010 Dane Harrigan. See LICENSE for details.