class PseudoCleaner::RedisMonitorCleaner
I’m not a huge fan of sleeps. In the non-rails world, I used to be able to do a sleep(0) to signal the system to check if somebody else needed to do some work. Testing with Rails, I find I have to actually sleep, so I do a very short time like 0.01.
Constants
- FLUSH_COMMANDS
SUITE_KEY = “PseudoDelete::RedisMonitorCleaner:initial_redis_state”
- READ_COMMANDS
- WRITE_COMMANDS
Attributes
initial_keys[R]
monitor_thread[R]
options[RW]
Public Class Methods
new(start_method, end_method, table, options)
click to toggle source
# File lib/pseudo_cleaner/redis_monitor_cleaner.rb, line 315 def initialize(start_method, end_method, table, options) @initial_keys = SortedSet.new @monitor_thread = nil @redis_name = nil @suite_altered_keys = SortedSet.new unless PseudoCleaner::MasterCleaner::VALID_START_METHODS.include?(start_method) raise "You must specify a valid start function from: #{PseudoCleaner::MasterCleaner::VALID_START_METHODS}." end unless PseudoCleaner::MasterCleaner::VALID_END_METHODS.include?(end_method) raise "You must specify a valid end function from: #{PseudoCleaner::MasterCleaner::VALID_END_METHODS}." end @options = options @options[:table_start_method] ||= start_method @options[:table_end_method] ||= end_method @options[:output_diagnostics] ||= PseudoCleaner::Configuration.current_instance.output_diagnostics || PseudoCleaner::Configuration.current_instance.post_transaction_analysis @redis = table end
Public Instance Methods
<=>(right_object)
click to toggle source
# File lib/pseudo_cleaner/redis_monitor_cleaner.rb, line 338 def <=>(right_object) if (right_object.is_a?(PseudoCleaner::RedisMonitorCleaner)) return 0 elsif (right_object.is_a?(PseudoCleaner::TableCleaner)) return 1 else if right_object.respond_to?(:<=>) comparison = (right_object <=> self) if comparison return -1 * comparison end end end return 1 end
ignore_key(key)
click to toggle source
# File lib/pseudo_cleaner/redis_monitor_cleaner.rb, line 431 def ignore_key(key) ignore_regexes.detect { |ignore_regex| key =~ ignore_regex } end
ignore_regexes()
click to toggle source
# File lib/pseudo_cleaner/redis_monitor_cleaner.rb, line 427 def ignore_regexes [] end
peek_values()
click to toggle source
# File lib/pseudo_cleaner/redis_monitor_cleaner.rb, line 456 def peek_values synchronize_test_values do |updated_values| if updated_values && !updated_values.empty? output_values = false if PseudoCleaner::MasterCleaner.report_table Cornucopia::Util::ReportTable.new(nested_table: PseudoCleaner::MasterCleaner.report_table, nested_table_label: redis_name, suppress_blank_table: true) do |report_table| updated_values.each do |updated_value| unless ignore_key(updated_value) output_values = true report_table.write_stats updated_value, report_record(updated_value) end end end else PseudoCleaner::Logger.write(" #{redis_name}") updated_values.each do |updated_value| unless ignore_key(updated_value) output_values = true PseudoCleaner::Logger.write(" #{updated_value}: #{report_record(updated_value)}") end end end PseudoCleaner::MasterCleaner.report_error if output_values end end end
queue()
click to toggle source
# File lib/pseudo_cleaner/redis_monitor_cleaner.rb, line 529 def queue @queue ||= Queue.new end
redis()
click to toggle source
# File lib/pseudo_cleaner/redis_monitor_cleaner.rb, line 355 def redis @redis ||= Redis.current end
redis_name()
click to toggle source
# File lib/pseudo_cleaner/redis_monitor_cleaner.rb, line 435 def redis_name unless @redis_name redis_options = redis.client.options.with_indifferent_access @redis_name = "#{redis_options[:host]}:#{redis_options[:port]}/#{redis_options[:db]}" end @redis_name end
report_dirty_values(message, test_values)
click to toggle source
# File lib/pseudo_cleaner/redis_monitor_cleaner.rb, line 635 def report_dirty_values message, test_values if test_values && !test_values.empty? output_values = false if PseudoCleaner::MasterCleaner.report_table Cornucopia::Util::ReportTable.new(nested_table: PseudoCleaner::MasterCleaner.report_table, nested_table_label: redis_name, suppress_blank_table: true) do |report_table| report_table.write_stats "action", message test_values.each_with_index do |key_name, index| unless ignore_key(key_name) output_values = true report_table.write_stats index, report_record(key_name) end end end else PseudoCleaner::Logger.write("********* RedisMonitorCleaner - #{message}".red.on_light_white) test_values.each do |key_name| unless ignore_key(key_name) output_values = true PseudoCleaner::Logger.write(" #{key_name}: #{report_record(key_name)}".red.on_light_white) end end end PseudoCleaner::MasterCleaner.report_error if output_values end end
report_end_of_suite_state(report_reason)
click to toggle source
# File lib/pseudo_cleaner/redis_monitor_cleaner.rb, line 496 def report_end_of_suite_state report_reason current_keys = SortedSet.new(redis.keys) deleted_keys = initial_keys - current_keys new_keys = current_keys - initial_keys # filter out values we inserted that will go away on their own. new_keys = new_keys.select { |key| (key =~ /redis_cleaner::synchronization_(?:end_)?key_[0-9]+_[0-9]+/).nil? } report_dirty_values "new values as of #{report_reason}", new_keys report_dirty_values "values deleted before #{report_reason}", deleted_keys report_dirty_values "initial values changed during suite run", @suite_altered_keys @suite_altered_keys = SortedSet.new new_keys.each do |key_value| redis.del key_value end end
report_record(key_name)
click to toggle source
# File lib/pseudo_cleaner/redis_monitor_cleaner.rb, line 609 def report_record(key_name) key_hash = { key: key_name, type: redis.type(key_name), ttl: redis.ttl(key_name) } case key_hash[:type] when "string" key_hash[:value] = redis.get(key_name) when "list" key_hash[:list] = { len: redis.llen(key_name), values: redis.lrange(key_name, 0, -1) } when "set" key_hash[:set] = redis.smembers(key_name) when "zset" key_hash[:sorted_set] = redis.smembers(key_name) when "hash" key_hash[:list] = { len: redis.hlen(key_name), values: redis.hgetall(key_name) } end if key_hash[:value].nil? && key_hash[:list].nil? && key_hash[:set].nil? && key_hash[:sorted_set].nil? && key_hash[:hash].nil? key_hash[:value] = "[[DELETED]]" end key_hash end
reset_suite()
click to toggle source
# File lib/pseudo_cleaner/redis_monitor_cleaner.rb, line 417 def reset_suite report_end_of_suite_state "reset suite" if monitor_thread monitor_thread.kill @monitor_thread = nil start_monitor end end
review_rows(&block)
click to toggle source
# File lib/pseudo_cleaner/redis_monitor_cleaner.rb, line 444 def review_rows(&block) synchronize_test_values do |updated_values| if updated_values && !updated_values.empty? updated_values.each do |updated_value| unless ignore_key(updated_value) block.yield redis_name, report_record(updated_value) end end end end end
start_monitor()
click to toggle source
# File lib/pseudo_cleaner/redis_monitor_cleaner.rb, line 533 def start_monitor cleaner_class = self @initial_keys = SortedSet.new(redis.keys) # @initial_keys.add(PseudoCleaner::RedisMonitorCleaner::SUITE_KEY) # @initial_keys.each do |key_value| # redis.sadd(PseudoCleaner::RedisMonitorCleaner::SUITE_KEY, key_value) # end if @options[:output_diagnostics] if PseudoCleaner::MasterCleaner.report_table Cornucopia::Util::ReportTable.new(nested_table: PseudoCleaner::MasterCleaner.report_table, nested_table_label: redis_name, suppress_blank_table: true) do |report_table| report_table.write_stats "initial keys count", @initial_keys.count end else PseudoCleaner::Logger.write("#{redis_name}") PseudoCleaner::Logger.write(" Initial keys count - #{@initial_keys.count}") end end unless @monitor_thread @monitor_thread = Thread.new do in_redis_cleanup = false updated_keys = SortedSet.new monitor_redis = Redis.new(cleaner_class.redis.client.options) redis_options = monitor_redis.client.options.with_indifferent_access cleaner_class_db = redis_options[:db] monitor_redis.monitor do |message| redis_message = RedisMessage.new message if redis_message.db == cleaner_class_db process_command = true if redis_message.command == "setex" if redis_message.keys[0] == cleaner_class.synchronize_key process_command = false in_redis_cleanup = true return_values = updated_keys updated_keys = SortedSet.new cleaner_class.queue << return_values elsif redis_message.keys[0] == cleaner_class.synchronize_end_key in_redis_cleanup = false cleaner_class.monitor_thread[:updated] = nil process_command = false end elsif redis_message.command == "del" if in_redis_cleanup process_command = false end end if process_command # flush... if PseudoCleaner::RedisMonitorCleaner::WRITE_COMMANDS.include? redis_message.command updated_keys.merge(redis_message.keys) elsif PseudoCleaner::RedisMonitorCleaner::FLUSH_COMMANDS.include? redis_message.command # Not sure I can get the keys at this point... # updated_keys.merge(cleaner_class.redis.keys) end end elsif "flushall" == redis_message.command # Not sure I can get the keys at this point... # updated_keys.merge(cleaner_class.redis.keys) end end end sleep(0.01) redis.get(synchronize_key) end end
suite_end(test_strategy)
click to toggle source
# File lib/pseudo_cleaner/redis_monitor_cleaner.rb, line 408 def suite_end test_strategy report_end_of_suite_state "suite end" if monitor_thread monitor_thread.kill @monitor_thread = nil end end
suite_start(test_strategy)
click to toggle source
# File lib/pseudo_cleaner/redis_monitor_cleaner.rb, line 359 def suite_start test_strategy @test_strategy ||= test_strategy # if redis.type(PseudoCleaner::RedisMonitorCleaner::SUITE_KEY) == "set" # @initial_keys = SortedSet.new(redis.smembers(PseudoCleaner::RedisMonitorCleaner::SUITE_KEY)) # report_end_of_suite_state "before suite start" # end # redis.del PseudoCleaner::RedisMonitorCleaner::SUITE_KEY start_monitor end
synchronize_end_key()
click to toggle source
# File lib/pseudo_cleaner/redis_monitor_cleaner.rb, line 492 def synchronize_end_key @synchronize_end_key ||= "redis_cleaner::synchronization_end_key_#{rand(1..1_000_000_000_000_000_000)}_#{rand(1..1_000_000_000_000_000_000)}" end
synchronize_key()
click to toggle source
# File lib/pseudo_cleaner/redis_monitor_cleaner.rb, line 488 def synchronize_key @synchronize_key ||= "redis_cleaner::synchronization_key_#{rand(1..1_000_000_000_000_000_000)}_#{rand(1..1_000_000_000_000_000_000)}" end
synchronize_test_values(&block)
click to toggle source
# File lib/pseudo_cleaner/redis_monitor_cleaner.rb, line 516 def synchronize_test_values(&block) updated_values = nil if monitor_thread redis.setex(synchronize_key, 1, true) updated_values = queue.pop end block.yield updated_values redis.setex(synchronize_end_key, 1, true) end
test_end(test_strategy)
click to toggle source
# File lib/pseudo_cleaner/redis_monitor_cleaner.rb, line 385 def test_end test_strategy synchronize_test_values do |updated_values| if updated_values && !updated_values.empty? report_keys = [] if @options[:output_diagnostics] report_dirty_values "updated values", updated_values end updated_values.each do |value| if initial_keys.include?(value) report_keys << value @suite_altered_keys << value else redis.del(value) end end report_dirty_values "initial values altered by test", report_keys end end end
test_start(test_strategy)
click to toggle source
# File lib/pseudo_cleaner/redis_monitor_cleaner.rb, line 371 def test_start test_strategy @test_strategy ||= test_strategy synchronize_test_values do |test_values| if test_values && !test_values.empty? report_dirty_values "values altered before the test started", test_values test_values.each do |value| redis.del value unless initial_keys.include?(value) end end end end