diff options
author | Eric Wong <normalperson@yhbt.net> | 2010-11-23 18:52:08 -0800 |
---|---|---|
committer | Eric Wong <normalperson@yhbt.net> | 2010-11-23 18:52:08 -0800 |
commit | 0f3c1c14630fda58363ffd7d3a942041ca2419eb (patch) | |
tree | df1340b1702ac2d8a7b234a1c6e426445fc5d1a1 /lib/metropolis | |
parent | 2bb7406db018e6902aacaf495e63d69cf9b93174 (diff) | |
download | metropolis-0f3c1c14630fda58363ffd7d3a942041ca2419eb.tar.gz |
add plain Hash database support
Useful as a proof-of-concept and for benchmark base.
Diffstat (limited to 'lib/metropolis')
-rw-r--r-- | lib/metropolis/common.rb | 8 | ||||
-rw-r--r-- | lib/metropolis/hash.rb | 75 |
2 files changed, 83 insertions, 0 deletions
diff --git a/lib/metropolis/common.rb b/lib/metropolis/common.rb index 738a511..13a02d8 100644 --- a/lib/metropolis/common.rb +++ b/lib/metropolis/common.rb @@ -37,4 +37,12 @@ module Metropolis::Common r(405) end end + + # generic HEAD implementation, some databases can optimize this by + # not retrieving the value + def head(key) + r = get(key) + r[2].clear + r + end end diff --git a/lib/metropolis/hash.rb b/lib/metropolis/hash.rb new file mode 100644 index 0000000..698aef0 --- /dev/null +++ b/lib/metropolis/hash.rb @@ -0,0 +1,75 @@ +# -*- encoding: binary -*- +require 'tempfile' + +# use a Ruby hash as a plain data store +# It can unmarshal a hash from disk +module Metropolis::Hash + include Metropolis::Common + + def setup(opts) + super + if @path = opts[:path] + begin + @db = Marshal.load(File.open(@path, "rb") { |fp| fp.read }) + Hash === @db or raise ArgumentError, "#@path is not a marshaled Hash" + rescue Errno::ENOENT + @db = {} + end + else + @db = {} + end + if @readonly + extend Metropolis::Common::RO + else + args = [ @db, @path, !!opts[:fsync] ] + @clean_proc = Metropolis::Hash.finalizer_callback(args) + ObjectSpace.define_finalizer(self, @clean_proc) + end + end + + def close! + unless @readonly + @clean_proc.call + ObjectSpace.undefine_finalizer(self) + end + @db = @path = nil + end + + def get(key) + value = @db[key] or return r(404) + [ 200, { 'Content-Length' => value.size.to_s }.merge!(@headers), [ value ] ] + end + + def put(key, env) + value = env["rack.input"].read + case env['HTTP_X_TT_PDMODE'] + when '1' + @db.exists?(key) and r(409) + @db[key] = value + when '2' + (tmp = @db[key] ||= "") << value + else + @db[key] = value + end + r(201) + end + + def delete(key) + r(@db.delete(key) ? 200 : 404) + end + + def self.finalizer_callback(data) + lambda { + db, path, fsync = data + dir = File.dirname(path) + tmp = Tempfile.new('hash_save', dir) + tmp.binmode + tmp.sync = true + tmp.write(Marshal.dump(db)) + tmp.fsync if fsync + File.rename(tmp.path, path) + File.open(dir) { |d| d.fsync } if fsync + tmp.close! + } + end +end |