#!/usr/bin/env ruby require 'active_record' require 'optparse' module Lhm class KillQueue def initialize @port = 3306 OptionParser.new do |opts| opts.on("-h", "--hostname HOSTNAME") { |v| @hostname = v } opts.on("-u", "--username USERNAME") { |v| @username = v } opts.on("-p", "--password PASSWORD") { |v| @password = v } opts.on("-d", "--database DATABASE") { |v| @database = v } opts.on("-m", "--mode MODE") { |v| @mode = v.to_sym } opts.on("-y", "--confirm") { |v| @confirm = true } end.parse! unless(@hostname && @username && @password && @database) abort usage end unless([:kill, :master, :slave].include?(@mode)) abort "specify -m kill OR -m master OR -m slave" end connect end def usage <<-desc.gsub(/^ /, '') kills queries on the given server after detecting 'lock table% -- lhm'. usage: lhm-kill-queue -h hostname -u username -p password -d database \\ (-m kill | -m master | -m slave) [--confirm] desc end def run case @mode when :kill then kill when :master then master when :slave then slave end end def kill lock = trip kill_process(lock) end def master lock = trip puts "starting to kill non lhm processes in 1 second" sleep(1.05) [list_non_lhm].flatten.each do |process| kill_process(process) sleep(0.05) end end def slave lock = trip puts "starting to kill non lhm SELECT processes in 1 second" sleep(1) [list_non_lhm].flatten.each do |process| if(select?(process)) kill_process(process) sleep(0.05) end end end private def connect ActiveRecord::Base.establish_connection({ :adapter => 'mysql', :host => @hostname, :port => @port, :username => @username, :password => @password, :database => @database }) end def connection ActiveRecord::Base.connection end def list_non_lhm select_processes("info not like '% -- lhm' and time > 0 and command = 'Query'") end def trip until res = select_processes("info like 'lock table% -- lhm'") sleep 0.2 print '.' end res end def kill_process(process_id) puts "killing #{ select_statement(process_id) }" if(@confirm) print "confirm ('y' to confirm): " if(gets.strip != 'y') puts "skipped." return end end connection.execute("kill #{ process_id }") puts "killed #{ process_id }" end def select?(process) if statement = select_statement(process) case statement when /delete/i then false when /update/i then false when /insert/i then false else !!statement.match(/select/i) end end end def select_statement(process) if process value %Q( select info from information_schema.processlist where id = #{ process } ) end end def select_processes(predicate) values %Q( select id from information_schema.processlist where db = '#{ @database }' and user = '#{ @username }' and #{ predicate } ) end def value(statement) connection.select_value(statement) end def values(statement) connection.select_values(statement) end end end killer = Lhm::KillQueue.new killer.run