Forum: Ferret Ferret and Rails transaction

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
91308e9bc88cb069fd1bcf88e910d042?d=identicon&s=25 Nick Snels (nicksnels)
on 2005-12-21 21:03
(Received via mailing list)
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
This topic is locked and can not be replied to.