views:

200

answers:

2

I have built a pretty simple REST service in Sinatra, on Rack. It's backed by 3 Tokyo Cabinet/Table datastores, which have connections that need to be opened and closed. I have two model classes written in straight Ruby that currently simply connect, get or put what they need, and then disconnect. Obviously, this isn't going to work long-term.

I also have some Rack middleware like Warden that rely on these model classes.

What's the best way to manage opening and closing the connections? Rack doesn't provide startup/shutdown hooks as I'm aware. I thought about inserting a piece of middleware that provides reference to the TC/TT object in env, but then I'd have to pipe that through Sinatra to the models, which doesn't seem efficient either; and that would only get be a per-request connection to TC. I'd imagine that per-server-instance-lifecycle would be a more appropriate lifespan.

Thanks!

A: 

Have you considered using Sinatra's configure blocks to set up your connections?

configure do
  Connection.initialize_for_development
end

configure :production do
  Connection.initialize_for_production
end

That's a pretty common idiom while using things like DataMapper with Sinatra

Check out the "Configuration" section at http://www.sinatrarb.com/intro

chrismetcalf
+1  A: 

If you have other Rack middleware that depend on these connections (by way of a dependence on your model classes), then I wouldn't put the connection logic in Sinatra -- what happens if you rip out Sinatra and put in another endpoint?

Since you want connection-per-application rather than connection-per-request, you could easily write a middleware that initialized and cleaned up connections (sort of the Guard Idiom as applied to Rack) and install it ahead of any other middleware that need the connections.

class TokyoCabinetConnectionManagerMiddleware
  class <<self
    attr_accessor :connection
  end

  def initialize(app)
    @app = app
  end

  def call(env)
    open_connection_if_necessary!
    @app.call(env)
  end

  protected

  def open_connection_if_necessary!
    self.class.connection ||= begin
      ... initialize the connection ..
      add_finalizer_hook!
    end
  end

  def add_finalizer_hook!
    at_exit do
      begin
        TokyoCabinetConnectionManagerMiddleware.connection.close!
      rescue WhateverTokyoCabinetCanRaise => e
        puts "Error closing Tokyo Cabinet connection. You might have to clean up manually."
      end
    end
  end
end

If you later decide you want connection-per-thread or connection-per-request, you can change this middleware to put the connection in the env Hash, but you'll need to change your models as well. Perhaps this middleware could set a connection variable in each model class instead of storing it internally? In that case, you might want to do more checking about the state of the connection in the at_exit hook because another thread/request might have closed it.

James A. Rosen
Awesome. Thanks!
Clint Tseng