Persistent data

Brian Candler <[email protected]> Thu, 14 Oct 2004 16:13:04 +0100
Newsgroups gmane.comp.apache.mod-ruby
Message-ID <[email protected]>

Here are a couple of suggested additions for the mod_ruby FAQ.

-------------------------------------------------------------------------
Q. How do I keep an object instance between invocations of a page, for
example, a persistent database handle?

A. You can keep it in a global variable. Or, you can access a class in
another file, and use a class variable or class instance variable.

Example 1:

  r = Apache.request

  $count ||= 0
  $count += 1

  r.content_type = 'text/html'
  r.send_http_header
  r << "<html>Pid #{Process.pid}, global count #{$count}</html>"

Example 2:

  require 'memory'

  r = Apache.request

  Memory.memory[:count] ||= 0
  Memory.memory[:count] += 1

  r.content_type = 'text/html'
  r.send_http_header
  r << "<html>Pid #{Process.pid}, memory count #{Memory.memory[:count]}</html>"

[file memory.rb]
  class Memory
    def self.memory
      @memory ||= {}
    end
  end

This example demonstrates that each Apache httpd worker process keeps its
own state (its own value of 'counter' in this case). If you did

  $db_handle = DBI.new(...)

and had 5 worker processes, you would end up with 5 database handles, one
for each worker. This is probably what you want anyway, so that each worker
can perform a concurrent operation on the database.

-------------------------------------------------------------------------
Q. So how can I have an object which is shared between all the worker
processes?

A. If you do want to share data between all the worker processes, e.g.
session information, one way is to keep the object in another process and
access it using DRb (distributed Ruby).

Example:

  # Access a DRb shared memory object [DRb client]
  require 'drb'
  memory = DRbObject.new(nil, 'druby://127.0.0.1:9000')

  r = Apache.request

  memory[:count] ||= 0
  memory[:count] += 1

  r.content_type = 'text/html'
  r.send_http_header
  r << "<html>Pid #{Process.pid}, drb count #{memory[:count]}</html>"

For this to work, you also run a standalone Ruby program containing the
object and share it using DRb [a DRb server]:

  #!/usr/local/bin/ruby -w
  require 'drb'
  memory = {}
  DRb.start_service('druby://127.0.0.1:9000', memory)
  DRb.thread.join

This has the big advantage that even if you stop and restart apache, the
state stored in the DRb object still remains.

If you have multiple web servers in a cluster, the DRb object can be held on
a different machine and accessed over the network. If you do that, you will
want to make sure that the DRb object is hidden from the outside world (e.g.
on a private network or protected by firewall rules) so that other people
can't access it directly!



require 'memory'

r = Apache.request

Memory.memory[:count] ||= 0
Memory.memory[:count] += 1

r.content_type = 'text/html'
r.send_http_header
r << "<html>Pid #{Process.pid}, memory count #{Memory.memory[:count]}</html>"



class Memory
  def self.memory
    @memory ||= {}
  end
end