Ferret and Rails transaction


#1

Hi,

following the discussion about acts_as_ferret on the Rails mailinglist,
there was an issue about transactions, which could result in beind the
database and ferret out of sync. I have taken a different approach from
acts_as_ferret trying to resolve the transaction problem. Instead of
adding
things to the ferret index in the model, I have added it in the
controller.
I have only the create part for now and it’s very rough, but it works.

def create
if request.get?
redirect_to :action => ‘new’
else
@user = User.find(session[:user_id])
@ad = Ad.new(params[:ad])
@listing = Listing.new(params[:listing])
@listing.listing_type = “ads”

  index ||= Index::Index.new(:key => [:id, :table],
                                    :path =>

“#{RAILS_ROOT}/db/index.test”,
:auto_flush => true)

  begin
    Listing.transaction(@ad) do
      @ad.listings << @listing

      if @ad.save
        session[:last_addition] = @ad.id

        #ferret create a new entry in the index

        doc = Ferret::Document::Document.new
        doc << Ferret::Document::Field.new("id", @ad.id,

Document::Field::Store::YES, Document::Field::Index::UNTOKENIZED)
doc << Ferret::Document::Field.new(“table”, @
listing.listing_type, Document::Field::Store::YES,
Document::Field::Index::UNTOKENIZED)
doc << Ferret::Document::Field.new(“content”, @ad.title + "
" +
@ad.text, Document::Field::Store::NO,
Document::Field::Index::TOKENIZED)
index << doc
index.flush

        flash[:notice] = 'Ad was successfully created.'
        redirect_to :action => 'list'
      else
        index.close
        render :action => 'new'
      end
    end
  rescue
    if session[:last_addition]
      logger.error "We verwijderen ad met id:

#{session[:last_addition]}"
index.query_delete("+id:#{session[:last_addition]}
+table:ads")
index.flush
session[:last_addition] = nil
end

    flash[:notice] = 'An error occurred.'
    redirect_to :action => 'new'
  end
end

end

How does it work, or at least how do I think it works. If everything
goes
well, the entry gets in the Ferret index. I had to use index.flush,
because
without I got a lock. Don’t know why this happens, because I set
autoflush
to true.
When I have a validation error (field of form is empty, or…) in my
model,
the else part of @ad.save is taken, there I close the index. I don’t
know if
this is necessary, but just to be sure.
When their is another error: ferret index is not available, trying to
add
wrong field or something. Transaction - rescue kicks in. If there are no
validation errors, the ad should get saved, so I set a session variable
last_addition to hold the id of the ad that is being saved, when an
error
occurs I have the id of the ad just added. In the rescue block I check
if
the session variable last_addition is set. If it is, I search ferret and
delete the addition, afterwhich I flush index (had to add it, otherwise
I
would get a lock).

Is my solution waterproof or am I missing something. All feedback is
welcome.

Kind regards,

Nick