1 # -*- encoding: binary -*-
3 # this module is NOT thread-safe, all performance is dependent on the
4 # local machine so there is never anything that needs yielding to threads.
5 module Metropolis::TC::HDB
6 autoload :RO, 'metropolis/tc/hdb/ro'
8 TCHDB = TokyoCabinet::HDB # :nodoc
9 include Metropolis::Common
12 @headers = { 'Content-Type' => 'application/octet-stream' }
13 @headers.merge!(opts[:response_headers] || {})
14 @nr_slots = opts[:nr_slots] || 3
15 path_pattern = opts[:path_pattern]
16 path_pattern.scan(/%\d*x/).size == 1 or
17 raise ArgumentError, "only one '/%\d*x/' may appear in #{path_pattern}"
19 if query = opts[:query]
21 @optimize = %w(bnum apow fpow).map do |x|
25 case large = query['large']
28 flags |= TCHDB::TLARGE
30 raise ArgumentError, "invalid 'large' value: #{large}"
32 case compress = query['compress']
34 when 'deflate', 'bzip', 'tcbs'
35 flags |= TCHDB.const_get("T#{compress.upcase}")
37 raise ArgumentError, "invalid 'compress' value: #{compress}"
41 @dbv = (0...@nr_slots).to_a.map do |slot|
42 path = sprintf(path_pattern, slot)
44 unless opts[:read_only]
45 hdb.open(path, TCHDB::OWRITER | TCHDB::OCREAT) or ex!(:open, hdb)
47 hdb.optimize(*@optimize) or ex!(:optimize, hdb)
49 hdb.close or ex!(:close, hdb)
53 @rd_flags = TCHDB::OREADER
54 @wr_flags = TCHDB::OWRITER
62 raise "#{msg}: #{hdb.errmsg(hdb.ecode)}"
65 def writer(key, &block)
66 hdb, path = @dbv[key.hash % @nr_slots]
67 hdb.open(path, @wr_flags) or ex!(:open, hdb)
70 hdb.close or ex!(:close, hdb)
74 hdb, path = @dbv[key.hash % @nr_slots]
75 hdb.open(path, @rd_flags) or ex!(:open, hdb)
78 hdb.close or ex!(:close, hdb)
82 value = env["rack.input"].read
84 case env['HTTP_X_TT_PDMODE']
86 unless hdb.putkeep(key, value)
87 TCHDB::EKEEP == hdb.ecode and return r(409)
91 hdb.putcat(key, value) or ex!(:putcat, hdb)
93 # ttserver does not care for other PDMODE values, so we don't, either
94 hdb.put(key, value) or ex!(:put, hdb)
102 unless hdb.delete(key)
103 TCHDB::ENOREC == hdb.ecode and return r(404)
111 size = reader(key) { |hdb| hdb.vsiz(key) or ex!(:vsiz, hdb) }
112 0 > size and return r(404)
114 'Content-Length' => size.to_s,
115 }.merge!(@headers), [] ]
121 unless value = hdb.get(key)
122 TCHDB::ENOREC == hdb.ecode and return r(404)
127 'Content-Length' => value.size.to_s,
128 }.merge!(@headers), [ value ] ]
132 @dbv.each { |(hdb,_)| hdb.close }