Kouhei Sutou 2019-03-28 12:40:09 +0900 (Thu, 28 Mar 2019) Revision: ebf8a6d9bcdc8038ad877f280b789b07189a3ac4 https://github.com/ranguba/chupa-text/commit/ebf8a6d9bcdc8038ad877f280b789b07189a3ac4 Message: external-command: add support for soft limit Modified files: lib/chupa-text/external-command.rb test/test-external-command.rb Modified: lib/chupa-text/external-command.rb (+40 -3) =================================================================== --- lib/chupa-text/external-command.rb 2019-03-28 12:22:17 +0900 (7d50eb0) +++ lib/chupa-text/external-command.rb 2019-03-28 12:40:09 +0900 (2a2d524) @@ -25,7 +25,9 @@ module ChupaText @default_timeout = nil @default_soft_timeout = nil @default_limit_cpu = nil + @default_soft_limit_cpu = nil @default_limit_as = nil + @default_soft_limit_as = nil class << self def default_timeout @default_timeout || ENV["CHUPA_TEXT_EXTERNAL_COMMAND_TIMEOUT"] @@ -51,6 +53,14 @@ module ChupaText @default_limit_cpu = cpu end + def default_soft_limit_cpu + @default_soft_limit_cpu + end + + def default_soft_limit_cpu=(cpu) + @default_soft_limit_cpu = cpu + end + def default_limit_as @default_limit_as || limit_env("AS") end @@ -59,6 +69,14 @@ module ChupaText @default_limit_as = as end + def default_soft_limit_as + @default_soft_limit_as + end + + def default_soft_limit_as=(as) + @default_soft_limit_as = as + end + private def limit_env(name) ENV["CHUPA_TEXT_EXTERNAL_COMMAND_LIMIT_#{name}"] || @@ -110,7 +128,7 @@ module ChupaText private def spawn_options(user_options) options = (user_options || {}).dup - apply_default_spawn_limit(options, :cpu, :int) + apply_default_spawn_limit(options, :cpu, :time) apply_default_spawn_limit(options, :as, :size) options end @@ -120,16 +138,26 @@ module ChupaText case key when :cpu option_key = :rlimit_cpu + unit = "s" when :as option_key = :rlimit_as + unit = "" else option_key = :"rlimit_#{key}" + unit = "" end return if options[option_key] tag = "[limit][#{key}]" value = self.class.__send__("default_limit_#{key}") value = __send__("parse_#{type}", tag, value) + soft_value = self.class.__send__("default_soft_limit_#{key}") + soft_value = __send__("parse_#{type}", tag, soft_value) + if value + value = soft_value if soft_value and soft_value < value + else + value = soft_value + end return if value.nil? rlimit_number = Process.const_get("RLIMIT_#{key.to_s.upcase}") soft_limit, hard_limit = Process.getrlimit(rlimit_number) @@ -138,7 +166,7 @@ module ChupaText return nil end limit_info = "soft-limit:#{soft_limit}, hard-limit:#{hard_limit}" - info("#{log_tag}#{tag}[set] <#{value}>(#{limit_info})") + info("#{log_tag}#{tag}[set] <#{value}#{unit}>(#{limit_info})") options[option_key] = value end @@ -178,12 +206,21 @@ module ChupaText scale = 1 case value when /GB?\z/i + scale = 1000 ** 3 + number = $PREMATCH + when /GiB?\z/i scale = 1024 ** 3 number = $PREMATCH when /MB?\z/i + scale = 1000 ** 2 + number = $PREMATCH + when /MiB?\z/i scale = 1024 ** 2 number = $PREMATCH - when /KB?\z/i + when /[kK]B?\z/i + scale = 1000 ** 1 + number = $PREMATCH + when /KiB?\z/i scale = 1024 ** 1 number = $PREMATCH when /B?\z/i Modified: test/test-external-command.rb (+174 -0) =================================================================== --- test/test-external-command.rb 2019-03-28 12:22:17 +0900 (a07cd40) +++ test/test-external-command.rb 2019-03-28 12:40:09 +0900 (54d69a7) @@ -215,4 +215,178 @@ class TestExternalCommand < Test::Unit::TestCase messages) end end + + class TestLimitCPU < self + def setup + limit_cpu = ChupaText::ExternalCommand.default_limit_cpu + soft_limit_cpu = ChupaText::ExternalCommand.default_soft_limit_cpu + begin + yield + ensure + ChupaText::ExternalCommand.default_limit_cpu = limit_cpu + ChupaText::ExternalCommand.default_soft_limit_cpu = soft_limit_cpu + end + end + + def run_command(spawn_options={}) + command = create_command(ruby) + command.run("-e", "true", + spawn_options: spawn_options) + end + + def test_default + ChupaText::ExternalCommand.default_limit_cpu = "60s" + messages = capture_log do + run_command + end + soft_limit, hard_limit = Process.getrlimit(Process::RLIMIT_CPU) + assert_equal([ + [ + :info, + "[external-command][limit][cpu][set] <60.0s>" + + "(soft-limit:#{soft_limit}, hard-limit:#{hard_limit})", + ] + ], + messages) + end + + def test_default_soft_not_use + ChupaText::ExternalCommand.default_limit_cpu = "60s" + ChupaText::ExternalCommand.default_soft_limit_cpu = "90s" + messages = capture_log do + run_command + end + soft_limit, hard_limit = Process.getrlimit(Process::RLIMIT_CPU) + assert_equal([ + [ + :info, + "[external-command][limit][cpu][set] <60.0s>" + + "(soft-limit:#{soft_limit}, hard-limit:#{hard_limit})", + ] + ], + messages) + end + + def test_default_soft_use + ChupaText::ExternalCommand.default_limit_cpu = "60s" + ChupaText::ExternalCommand.default_soft_limit_cpu = "30s" + messages = capture_log do + run_command + end + soft_limit, hard_limit = Process.getrlimit(Process::RLIMIT_CPU) + assert_equal([ + [ + :info, + "[external-command][limit][cpu][set] <30.0s>" + + "(soft-limit:#{soft_limit}, hard-limit:#{hard_limit})", + ] + ], + messages) + end + + def test_default_soft_only + ChupaText::ExternalCommand.default_soft_limit_cpu = "30s" + messages = capture_log do + run_command + end + soft_limit, hard_limit = Process.getrlimit(Process::RLIMIT_CPU) + assert_equal([ + [ + :info, + "[external-command][limit][cpu][set] <30.0s>" + + "(soft-limit:#{soft_limit}, hard-limit:#{hard_limit})", + ] + ], + messages) + end + end + + class TestLimitAS < self + def setup + limit_as = ChupaText::ExternalCommand.default_limit_as + soft_limit_as = ChupaText::ExternalCommand.default_soft_limit_as + begin + yield + ensure + ChupaText::ExternalCommand.default_limit_as = limit_as + ChupaText::ExternalCommand.default_soft_limit_as = soft_limit_as + end + end + + def run_command(spawn_options={}) + command = create_command(ruby) + command.run("-e", "true", + spawn_options: spawn_options) + end + + def test_default + ChupaText::ExternalCommand.default_limit_as = "100MiB" + messages = capture_log do + run_command + end + soft_limit, hard_limit = Process.getrlimit(Process::RLIMIT_AS) + assert_equal([ + [ + :info, + "[external-command][limit][as][set] " + + "<#{100 * 1024 * 1024}>" + + "(soft-limit:#{soft_limit}, hard-limit:#{hard_limit})", + ] + ], + messages) + end + + def test_default_soft_not_use + ChupaText::ExternalCommand.default_limit_as = "100MiB" + ChupaText::ExternalCommand.default_soft_limit_as = "150MiB" + messages = capture_log do + run_command + end + soft_limit, hard_limit = Process.getrlimit(Process::RLIMIT_AS) + assert_equal([ + [ + :info, + "[external-command][limit][as][set] " + + "<#{100 * 1024 * 1024}>" + + "(soft-limit:#{soft_limit}, hard-limit:#{hard_limit})", + ] + ], + messages) + end + + def test_default_soft_use + ChupaText::ExternalCommand.default_limit_as = "100MiB" + ChupaText::ExternalCommand.default_soft_limit_as = "50MiB" + messages = capture_log do + run_command + end + soft_limit, hard_limit = Process.getrlimit(Process::RLIMIT_AS) + assert_equal([ + [ + :info, + "[external-command][limit][as][set] " + + "<#{50 * 1024 * 1024}>" + + "(soft-limit:#{soft_limit}, hard-limit:#{hard_limit})", + ] + ], + messages) + end + + def test_default_soft_only + ChupaText::ExternalCommand.default_soft_limit_as = "50MiB" + messages = capture_log do + run_command + end + soft_limit, hard_limit = Process.getrlimit(Process::RLIMIT_AS) + assert_equal([ + [ + :info, + "[external-command][limit][as][set] " + + "<#{50 * 1024 * 1024}>" + + "(soft-limit:#{soft_limit}, hard-limit:#{hard_limit})", + ] + ], + messages) + end + end end -------------- next part -------------- An HTML attachment was scrubbed... URL: <https://lists.osdn.me/mailman/archives/groonga-commit/attachments/20190328/4609574f/attachment-0001.html>