From f7387cd8d1af627e5919da371cac923bbc59ef6a Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 1 Dec 2010 10:34:32 +0000 Subject: add basic support for the Trivial Database (TDB) TDB supports multiple simultaneous readers and writer *processes*, not just threads. --- lib/metropolis.rb | 5 ++++ lib/metropolis/tdb.rb | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 lib/metropolis/tdb.rb (limited to 'lib') 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 -- cgit v1.2.3-24-ge0c7