From 61120b1268679bb8ffa157736e91e6846fd2a372 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Tue, 23 Nov 2010 19:45:52 -0800 Subject: tc/hdb: add exclusive mode, lock disabling The :exclusive mode behaves like TokyoTyrant and keeps the database opened in read-write mode, preventing other processes from accessing the database. This will be useful on Rubies without a GVL. :readonly no longer disables locking by default instead "rdlock=false" must be passed in the query parameter. Write locks may also be disabled with the "wrlock=false" query parameter. --- lib/metropolis/common.rb | 4 ++++ lib/metropolis/tc/hdb.rb | 27 ++++++++++++++++++++++++--- lib/metropolis/tc/hdb/ex.rb | 18 ++++++++++++++++++ lib/metropolis/tc/hdb/ro.rb | 1 - test/rack_read_write.rb | 14 ++++++++++++-- test/test_tc_hdb.rb | 26 ++++++++++++++++++++++++++ 6 files changed, 84 insertions(+), 6 deletions(-) create mode 100644 lib/metropolis/tc/hdb/ex.rb diff --git a/lib/metropolis/common.rb b/lib/metropolis/common.rb index 13a02d8..2a3b841 100644 --- a/lib/metropolis/common.rb +++ b/lib/metropolis/common.rb @@ -9,6 +9,10 @@ module Metropolis::Common @headers.merge!(opts[:response_headers] || {}) @nr_slots = opts[:nr_slots] || 3 @readonly = !!opts[:readonly] + @exclusive = !!opts[:exclusive] + if @readonly && @exclusive + raise ArgumentError, ":readonly and :exclusive may not be used together" + end end def r(code, body = nil) diff --git a/lib/metropolis/tc/hdb.rb b/lib/metropolis/tc/hdb.rb index ebd7a17..ce6d13a 100644 --- a/lib/metropolis/tc/hdb.rb +++ b/lib/metropolis/tc/hdb.rb @@ -4,6 +4,7 @@ # local machine so there is never anything that needs yielding to threads. module Metropolis::TC::HDB autoload :RO, 'metropolis/tc/hdb/ro' + autoload :EX, 'metropolis/tc/hdb/ex' TCHDB = TokyoCabinet::HDB # :nodoc include Metropolis::Common @@ -13,13 +14,34 @@ module Metropolis::TC::HDB path_pattern = opts[:path_pattern] path_pattern.scan(/%\d*x/).size == 1 or raise ArgumentError, "only one '/%\d*x/' may appear in #{path_pattern}" + + @rd_flags = TCHDB::OREADER + @wr_flags = TCHDB::OWRITER + @optimize = nil if query = opts[:query] + case query['rdlock'] + when 'true', nil + when 'false' + @rd_flags |= TCHDB::ONOLCK + else + raise ArgumentError, "'rdlock' must be 'true' or 'false'" + end + + case query['wrlock'] + when 'true', nil + when 'false' + @wr_flags |= TCHDB::ONOLCK + else + raise ArgumentError, "'wrlock' must be 'true' or 'false'" + end + flags = 0 @optimize = %w(bnum apow fpow).map do |x| v = query[x] v ? v.to_i : nil end + case large = query['large'] when 'false', nil when 'true' @@ -27,6 +49,7 @@ module Metropolis::TC::HDB else raise ArgumentError, "invalid 'large' value: #{large}" end + case compress = query['compress'] when nil when 'deflate', 'bzip', 'tcbs' @@ -48,12 +71,10 @@ module Metropolis::TC::HDB end [ hdb, path ] end - @rd_flags = TCHDB::OREADER - @wr_flags = TCHDB::OWRITER extend(RO) if @readonly + extend(EX) if @exclusive end - def ex!(msg, hdb) raise "#{msg}: #{hdb.errmsg(hdb.ecode)}" end diff --git a/lib/metropolis/tc/hdb/ex.rb b/lib/metropolis/tc/hdb/ex.rb new file mode 100644 index 0000000..5bc7f39 --- /dev/null +++ b/lib/metropolis/tc/hdb/ex.rb @@ -0,0 +1,18 @@ +module Metropolis::TC::HDB::EX + def self.extended(obj) + obj.instance_eval do + @wr_flags |= @rd_flags + @rd_flags = nil + @dbv.each { |(hdb, path)| + hdb.open(path, @wr_flags) or ex!(:open, hdb) + } + @ex_dbv = @dbv.map { |(hdb,_)| hdb } + end + end + + def reader(key) + yield @ex_dbv[key.hash % @nr_slots] + end + + alias_method :writer, :reader +end diff --git a/lib/metropolis/tc/hdb/ro.rb b/lib/metropolis/tc/hdb/ro.rb index fddd73c..62ededc 100644 --- a/lib/metropolis/tc/hdb/ro.rb +++ b/lib/metropolis/tc/hdb/ro.rb @@ -6,7 +6,6 @@ module Metropolis::TC::HDB::RO def self.extended(obj) obj.instance_eval do @wr_flags = nil - @rd_flags |= TokyoCabinet::HDB::ONOLCK @dbv.each { |(hdb, path)| hdb.open(path, @rd_flags) or ex!(:open, hdb) } diff --git a/test/rack_read_write.rb b/test/rack_read_write.rb index 8cbeb04..b3a8a1f 100644 --- a/test/rack_read_write.rb +++ b/test/rack_read_write.rb @@ -5,8 +5,14 @@ require 'tempfile' require 'rack' module TestRackReadWrite + attr_reader :app + def test_rack_read_write - app = Metropolis.new(:uri => uri) + @app = Metropolis.new(:uri => uri) + basic_rest + end + + def basic_rest o = { :lint => true, :fatal => true } req = Rack::MockRequest.new(app) @@ -42,7 +48,11 @@ module TestRackReadWrite def test_rack_readonly tmp = Metropolis.new(:uri => uri) tmp.close! - app = Metropolis.new(:uri => uri, :readonly => true) + @app = Metropolis.new(:uri => uri, :readonly => true) + basic_rest_readonly + end + + def basic_rest_readonly o = { :lint => true, :fatal => true } req = Rack::MockRequest.new(app) diff --git a/test/test_tc_hdb.rb b/test/test_tc_hdb.rb index 7a9d548..e2229e8 100644 --- a/test/test_tc_hdb.rb +++ b/test/test_tc_hdb.rb @@ -196,4 +196,30 @@ class Test_TC_HDB < Test::Unit::TestCase assert sum <= nr_bytes, "#{sum} > #{nr_bytes}" obj.close! end + + def test_exclusive + @app = Metropolis.new(:uri => uri, :exclusive => true) + assert_equal(app.method(:reader), app.method(:writer)) + basic_rest + end + + def test_no_rdlock + @app = Metropolis.new(:uri => "#{uri}?rdlock=false") + nolck = ::TokyoCabinet::HDB::ONOLCK + flags = @app.instance_variable_get(:@rd_flags) + assert((flags & nolck) == nolck) + flags = @app.instance_variable_get(:@wr_flags) + assert((flags & nolck) == 0) + basic_rest + end + + def test_no_wrlock + @app = Metropolis.new(:uri => "#{uri}?wrlock=false") + nolck = ::TokyoCabinet::HDB::ONOLCK + flags = @app.instance_variable_get(:@wr_flags) + assert((flags & nolck) == nolck) + flags = @app.instance_variable_get(:@rd_flags) + assert((flags & nolck) == 0) + basic_rest + end end -- cgit v1.2.3-24-ge0c7