about summary refs log tree commit
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2010-12-01 10:34:32 +0000
committerEric Wong <normalperson@yhbt.net>2010-12-01 10:47:53 +0000
commitf7387cd8d1af627e5919da371cac923bbc59ef6a (patch)
treef18212e18ce20cedbc734bbac05c0c40825bd02c
parent27eb2d7ebd29239a5043a528c97c6dd218d03217 (diff)
downloadmetropolis-f7387cd8d1af627e5919da371cac923bbc59ef6a.tar.gz
add basic support for the Trivial Database (TDB)
TDB supports multiple simultaneous readers and writer
*processes*, not just threads.
-rw-r--r--lib/metropolis.rb5
-rw-r--r--lib/metropolis/tdb.rb74
-rw-r--r--test/test_tdb.rb20
3 files changed, 99 insertions, 0 deletions
diff --git a/lib/metropolis.rb b/lib/metropolis.rb
index 3afc3ec..e9810d7 100644
--- a/lib/metropolis.rb
+++ b/lib/metropolis.rb
@@ -8,6 +8,7 @@ module Metropolis
   autoload :Gzip, 'metropolis/gzip'
   autoload :TC, 'metropolis/tc'
   autoload :Hash, 'metropolis/hash'
+  autoload :TDB, 'metropolis/tdb'
 
   def self.new(opts = {})
     opts = opts.dup
@@ -17,6 +18,10 @@ module Metropolis
     when 'hash'
       opts[:path] = uri.path if uri.path != '/'
       rv.extend Metropolis::Hash
+    when 'tdb'
+      opts[:path_pattern] = uri.path
+      opts[:query] = Rack::Utils.parse_query(uri.query) if uri.query
+      rv.extend Metropolis::TDB
     when 'tc'
       opts[:path_pattern] = uri.path
       opts[:query] = Rack::Utils.parse_query(uri.query) if uri.query
diff --git a/lib/metropolis/tdb.rb b/lib/metropolis/tdb.rb
new file mode 100644
index 0000000..14aa3aa
--- /dev/null
+++ b/lib/metropolis/tdb.rb
@@ -0,0 +1,74 @@
+# -*- encoding: binary -*-
+
+require 'tdb'
+
+module Metropolis::TDB
+  include Metropolis::Common
+
+  def setup(opts)
+    super
+    path_pattern = opts[:path_pattern]
+    path_pattern.scan(/%\d*x/).size == 1 or
+      raise ArgumentError, "only one '/%\d*x/' may appear in #{path_pattern}"
+    @tdb_opts = { :tdb_flags => 0 }
+    if @readonly
+      @tdb_opts[:open_flags] = IO::RDONLY
+      extend Metropolis::Common::RO
+    end
+    if query = opts[:query]
+      size = query['hash_size'] and @tdb_opts[:hash_size] = size.to_i
+      hash = query['hash'] and @tdb_opts[:hash] = hash.to_sym
+
+      case query['volatile']
+      when 'true'; @tdb_opts[:tdb_flags] |= TDB::VOLATILE
+      when 'false', nil
+      else
+        raise ArgumentError, "'volatile' must be 'true' or 'false'"
+      end
+
+      case query['sync']
+      when 'true', nil
+      when 'false'; @tdb_opts[:tdb_flags] |= TDB::NOSYNC
+      else
+        raise ArgumentError, "'sync' must be 'true' or 'false'"
+      end
+    end
+
+    @dbv = (0...@nr_slots).to_a.map do |slot|
+      path = sprintf(path_pattern, slot)
+      ::TDB.new(path, @tdb_opts)
+    end
+  end
+
+  def db(key, &block)
+    yield @dbv[key.hash % @nr_slots]
+  end
+
+  def put(key, env)
+    value = env["rack.input"].read
+    db(key) do |tdb|
+      case env['HTTP_X_TT_PDMODE']
+      when '1'
+        # TODO: make this atomic
+        return r(409) if tdb.include?(key)
+      when '2'
+        value = (tdb.get(key) || "") << value
+      end
+      tdb.store(key, value)
+    end
+    r(201)
+  end
+
+  def delete(key)
+    r(db(key) { |tdb| tdb.nuke!(key) } ? 200 : 404)
+  end
+
+  def get(key, env)
+    value = db(key) { |tdb| tdb.fetch(key) } or return r(404)
+    [ 200, { 'Content-Length' => value.size.to_s }.merge!(@headers), [ value ] ]
+  end
+
+  def close!
+    @dbv.each { |tdb| tdb.close }
+  end
+end
diff --git a/test/test_tdb.rb b/test/test_tdb.rb
new file mode 100644
index 0000000..c5b3245
--- /dev/null
+++ b/test/test_tdb.rb
@@ -0,0 +1,20 @@
+# -*- encoding: binary -*-
+require './test/rack_read_write.rb'
+$-w = true
+require 'metropolis'
+
+class Test_TDB < Test::Unit::TestCase
+  attr_reader :tmp, :o, :uri
+  include TestRackReadWrite
+
+  def setup
+    tmp = Tempfile.new('tdb')
+    @path_pattern = tmp.path + ".%01x.tdb"
+    tmp.close!
+    @uri = "tdb://#{@path_pattern}"
+  end
+
+  def teardown
+    Dir[@path_pattern.sub!(/%\d*x/, '*')].each { |x| File.unlink(x) }
+  end
+end