In your current setup, if read_at gives an error message, it will still continue to execute code that executes thread.status, for example.
You want to use ActiveRecord transactions :
def read!
transaction do
self.read_at = Time.now
self.save
self.thread.status = Status.find_by_name("read")
self.thread.save
end
end
Using transactions, you can be sure that either all database calls (inside the transaction block) will be stored in the database, or not at all.