Accessing session data from a background worker

Hi guys,

I have been trying to access session data (I am using the
ActiveRecordStore option) from a background process (Workling in my
case - but I suspect it should be the same for others). It seems to me
it should be fairly obvious since I can pass the session_id to the
background and the session data is in the DB anyway. But I can't make
it to work.

I looked around and saw a note on this group from Fred:
"you'll need to instantiate the appropriate subclass of
ActionController::SessionStore" but I don't understand this.

Could someone point me in the right direction?

Thanks!
Pierre

How about just passing in the session data hash when you call the
particular workling ob method, something like:

  ### in ./app/workers/foo_worker.rb
  class FooWorker < Workling::Base
    def bar(options)
      session_data = options[:session_data])
      # do something with session.data hash ...
    end
  end

  ### in some_meth in ./app/controllers/some_controller.rb
  ...
  FooWorker.asynch_bar(:session_data=>session.data)
  ...

Jeff

Jeff, thanks a lot.

Just one thing though: if I do this, does it mean I am taking the
session data "out of" ActiveRecord? I was thinking that by only
passing the session ID and by letting the worker retrieve the record
in the DB, consistency would be guaranteed by ActiveRecord. But maybe
I am getting this wrong?

Indeed, I need to do quite a lot of things on this session data in the
background: several workers are going to work on it at the same time,
and one of them will have to create some threads… I am struggling to
understand whether there will be concurrency risks - therefore I
thought I would try and rely on ActiveRecord as much as possible.

Here is in pseudo-code what I had in mind:

### in ./app/workers/foo_worker.rb
  class FooWorker < Workling::Base
    def bar(options)
      session = Session.find(options[:session_id])
      # do something with session.data hash ...
    end
  end

### in ./app/workers/foo_worker2.rb
  class FooWorker2 < Workling::Base
    def bar(options)
      for i in 1..10
        Thread.new(options[:session_id]) { |session_id| retrieve
session data and do stuff }
      end
    end
  end

### in some_meth in ./app/controllers/some_controller.rb
  ...
  FooWorker.asynch_bar(:session_id =>session.id)
  FooWorker2.asynch_bar(:session_id =>session.id)
  ...

Am I getting it wrong?

Thanks a lot!
Pierre

A couple of problems that jump out regarding accessing/using session
data in that strategy you outlined:

- the background threads/processes might end up using invalid session
data if the session data changes during the time from when you pass in
that session_id and when some background thread/process fetches that
session data from the db.

- you're making multiple round-trips to the db by having each of the
background threads/processes fetch the session data from the db when
they could just access the data by having it passed in at the time
they are called/launched.

A safer/more-robust alternative would be to just pass in a copy of the
user's session data to each of the background workers to work with:

  ...
  ### in ./app/workers/foo_worker.rb
  class FooWorker < Workling::Base
    def bar(options)
      session_data = Session.find(options[:session_data])
      # do something with session_data hash ...
    end

    ...
  end

  ### in ./app/workers/biz_worker.rb
  class BizWorker < Workling::Base
    def baz(options)
      session_data = Session.find(options[:session_data])
      # do something with session_data hash ...
    end

    ...
  end

  ### in some_meth in ./app/controllers/some_controller.rb
  ...
  sd = session.data
  FooWorker.asynch_bar(:session_data =>sd.merge({}))
  BizWorker.asynch_baz(:session_data =>sd.merge({}))
  ...

Jeff

in the case of active record store you can do
ActiveRecord::SessionStore::Session.find_by_session_id( session_id).data
If you wanted a version that would work no matter what the session
store then you probably want to follow the trail of
ActionController::AbstractStore

Fred

Fred and Jeff: thanks a lot I got it to "work".

Thing is… I ran some tests and concurrency is in fact a big problem:
my background workers all need to read and write from/to the session
data. I was thinking that using a lock would help (each process would
have to wait for the other to release the data):

ActiveRecord::SessionStore::Session.find(:all, :conditions =>
["session_id = ?", lsession_id], :lock => true)

but I am in fact getting an ActiveRecord::ConnectionTimeoutError (and
yet I am trying to release the session as quickly as possible in each
process).

So I was wondering: is there a recommended design to get several
workers to pass info between each other? Say for example:

- a bunch of workers A do a simple task
- a daemon worker B is "listening"
- each time a process A returns a value, it "sends" it to B
- B accumulates these values

I thought that Workling return store would do the trick but it
doesn't: each time you "set" a value it erases the previous one. I
guess each A could store their return value as a row in a DB - but it
seems to me like an overkill?

For some reason I struggle to find info on this on the web.

Thanks a lot,
Pierre

Not sure what you are attempting to accomplish in those background
threads/processes, or what the reasoning is behind even using multiple
background threads/processes in the first place in light of what you
seem to be trying to do, but the basic strategy you're attempting to
pursue where you have multiple concurrent threads/processes all
reading from and writing back to some user's session data is just
asking for trouble on a number of levels: overwriting of data by one
of the background threads/processes or the user; resource contention
and deadlock; .....

Even if you didn't use the user's session data to work with / store
such data, if you really want/need to pursue this multi-thread/-
process strategy for processing some common data, then you better
design that processing to deal for concurrency issues, which is not a
simple task.

Other alternatives include bailing entirely on the background threads/
processes and just perform the data processing sequentially and reset
the final val(s?) in the user's session once processing is completed,
or you could pass off such sequential processing to be done in some
background process that performs that data processing sequentially in
that background process and persists the final results somewhere else
to be picked up later on and made available to the user in some future
request.

Jeff