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'
7 autoload :EX, 'metropolis/tc/hdb/ex'
9 TCHDB = TokyoCabinet::HDB # :nodoc
10 include Metropolis::Common
14 path_pattern = opts[:path_pattern]
15 path_pattern.scan(/%\d*x/).size == 1 or
16 raise ArgumentError, "only one '/%\d*x/' may appear in #{path_pattern}"
18 @rd_flags = TCHDB::OREADER
19 @wr_flags = TCHDB::OWRITER
22 if query = opts[:query]
26 @rd_flags |= TCHDB::ONOLCK
28 raise ArgumentError, "'rdlock' must be 'true' or 'false'"
34 @wr_flags |= TCHDB::ONOLCK
36 raise ArgumentError, "'wrlock' must be 'true' or 'false'"
40 @optimize = %w(bnum apow fpow).map do |x|
45 case large = query['large']
48 flags |= TCHDB::TLARGE
50 raise ArgumentError, "invalid 'large' value: #{large}"
53 case compress = query['compress']
55 when 'deflate', 'bzip', 'tcbs'
56 flags |= TCHDB.const_get("T#{compress.upcase}")
58 raise ArgumentError, "invalid 'compress' value: #{compress}"
62 @dbv = (0...@nr_slots).to_a.map do |slot|
63 path = sprintf(path_pattern, slot)
66 hdb.open(path, TCHDB::OWRITER | TCHDB::OCREAT) or ex!(:open, hdb)
68 hdb.optimize(*@optimize) or ex!(:optimize, hdb)
70 hdb.close or ex!(:close, hdb)
74 extend(RO) if @readonly
75 extend(EX) if @exclusive
79 raise "#{msg}: #{hdb.errmsg(hdb.ecode)}"
82 def writer(key, &block)
83 hdb, path = @dbv[key.hash % @nr_slots]
84 hdb.open(path, @wr_flags) or ex!(:open, hdb)
87 hdb.close or ex!(:close, hdb)
91 hdb, path = @dbv[key.hash % @nr_slots]
92 hdb.open(path, @rd_flags) or ex!(:open, hdb)
95 hdb.close or ex!(:close, hdb)
99 value = env["rack.input"].read
101 case env['HTTP_X_TT_PDMODE']
103 unless hdb.putkeep(key, value)
104 TCHDB::EKEEP == hdb.ecode and return r(409)
108 hdb.putcat(key, value) or ex!(:putcat, hdb)
110 # ttserver does not care for other PDMODE values, so we don't, either
111 hdb.put(key, value) or ex!(:put, hdb)
119 unless hdb.delete(key)
120 TCHDB::ENOREC == hdb.ecode and return r(404)
128 size = reader(key) { |hdb| hdb.vsiz(key) or ex!(:vsiz, hdb) }
129 0 > size and return r(404, "")
130 [ 200, { 'Content-Length' => size.to_s }.merge!(@headers), [] ]
136 unless value = hdb.get(key)
137 TCHDB::ENOREC == hdb.ecode and return r(404)
141 [ 200, { 'Content-Length' => value.size.to_s }.merge!(@headers), [ value ] ]
145 @dbv.each { |(hdb,_)| hdb.close }