0
Posted on Tuesday, September 04, 2018 by 醉·醉·鱼 and labeled under
最新的5.11.3 minitest不知道为什么,在verbose模式下,把对应的测试 用例的名字给抹掉了。这样的话,导致分析测试用例performance的时候,非常得不友好。

https://github.com/seattlerb/minitest/blob/master/lib/minitest.rb#L617


    def record result # :nodoc:
      io.print "%.2f s = " % [result.time] if options[:verbose]
      io.print result.result_code
      io.puts if options[:verbose]
    end
  end

将上述代码替换成下面代码,就可以看到某个test用掉多少时间。

    def record result # :nodoc:
      io.print "%s#%s = %.2f s = " % [result.klass, result.name, result.time] if options[:verbose]
      io.print result.result_code
      io.puts if options[:verbose]
    end

输出
Run options: -n test_viewing_transaction --verbose --seed 39465

# Running:

TransactionsControllerTest#test_viewing_transaction = 6.13 s = .

Finished in 7.851562s, 0.1274 runs/s, 0.1274 assertions/s.

1 runs, 1 assertions, 0 failures, 0 errors, 0 skips
0
Posted on Thursday, August 30, 2018 by 醉·醉·鱼 and labeled under

罗列所有container的IP地址

docker ps -q | xargs -n 1 docker inspect --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}} {{ .Name }}'

单独monitor某个contrainer的logs


docker ps -a | grep payments | sed 's/ .*//' | xargs docker logs -f
0
Posted on Tuesday, August 21, 2018 by 醉·醉·鱼 and labeled under , ,
DatabaseCleaner上面的example过时了。按照示例去做的话,会报错 unknown method 'before'。在github上找到了答案,还是比较简单的。


class ActiveSupport::TestCase
  include FactoryGirl::Syntax::Methods

  ActiveRecord::Migration.check_pending!
  DatabaseCleaner.strategy = :truncation
  DatabaseCleaner.logger = Rails.logger
  setup { DatabaseCleaner.start }
  teardown { DatabaseCleaner.clean }
end

配置完成以后,就可以在MYSQL去monitor query,看具体是如何操作数据库的。

mysql> SHOW VARIABLES LIKE "general_log%";

+------------------+----------------------------+
| Variable_name    | Value                      |
+------------------+----------------------------+
| general_log      | OFF                        |
| general_log_file | /var/run/mysqld/mysqld.log |
+------------------+----------------------------+

mysql> SET GLOBAL general_log = 'ON';

观察log

tail -f -n300 /var/run/mysqld/mysqld.log

最后,重置改动。

mysql> SET GLOBAL general_log = 'OFF';

  1. https://stackoverflow.com/questions/568564/how-can-i-view-live-mysql-queries
  2. https://github.com/metaskills/minitest-spec-rails/issues/44#issuecomment-244155657
0
Posted on Monday, January 22, 2018 by 醉·醉·鱼 and labeled under
之前分析过SQL SERVER的死锁,但基本都是基于READ COMMITTED下的死锁。玩得高级点的,就是key lookup lock。最近不幸玩了MySQL,拿原来的理解去尝试分析,结果不对,然后才发现,MySQL的默认隔离级别是REPEATABLE READ。呵呵~

在RR级别下,除了常规的RECORD LOCK,还有一个GAP LOCK。即两条记录之前的间隙。这样的话,就不会允许在范围内插入数据了。http://blog.csdn.net/wanghai__/article/details/7067118 这里有个很好的例子去模拟死锁。

至于分析锁,首先执行
set global innodb_status_output_locks=on;

然后,再执行
SHOW ENGINE INNODB STATUS \G
就可以拿到所有session的锁了。

-- session 1
mysql> start transaction;
mysql> delete from game_summaries where game_id = 2;

-- session 2
mysql> start transaction;
mysql> delete from game_summaries where game_id = 3;

-- session 1
mysql> insert into game_summaries(game_id, score) values (2, 0);
-- waiting

-- session 2
mysql> insert into game_summaries(game_id, score) values(3, 0);
-- deadlock occurs


Deadlock info

------------------------
LATEST DETECTED DEADLOCK
------------------------
2018-02-11 02:19:51 0x7ff5b7b83700
*** (1) TRANSACTION:
TRANSACTION 365986, ACTIVE 59 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 16, OS thread handle 140693325752064, query id 1184 172.18.0.1 root update
insert into game_summaries(game_id, score) values (2, 0)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 3445 page no 4 n bits 72 index index_game_summaries_on_game_id of table `TEST`.`game_summaries` trx id 365986 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 80000009; asc     ;;
 1: len 4; hex 80000002; asc     ;;

*** (2) TRANSACTION:
TRANSACTION 365987, ACTIVE 45 sec inserting
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 17, OS thread handle 140693326018304, query id 1186 172.18.0.1 root update
insert into game_summaries(game_id, score) values(3, 0)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 3445 page no 4 n bits 72 index index_game_summaries_on_game_id of table `TEST`.`game_summaries` trx id 365987 lock_mode X locks gap before rec
Record lock, heap no 3 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 80000009; asc     ;;
 1: len 4; hex 80000002; asc     ;;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 3445 page no 4 n bits 72 index index_game_summaries_on_game_id of table `TEST`.`game_summaries` trx id 365987 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 80000009; asc     ;;
 1: len 4; hex 80000002; asc     ;;

*** WE ROLL BACK TRANSACTION (2)


然后就按照下面两篇文章去分析锁就行了。


  1. http://keithlan.github.io/2017/06/21/innodb_locks_algorithms/
  2. http://keithlan.github.io/2017/06/05/innodb_locks_1/
如果你有SQL SERVER的背景知识,简单来说,就是基本的record lock(以及相关的index),加上gap lock。一旦有gap lock,这个范围内是不允许插入数据的。这就增加了死锁发生的几率。这种情况更多是发生在DELETE & INSERT 组合情况下。
在上面的例子里面,两个delete statement所加的gap lock是不会相互冲突的。但是会阻止后续的插入。

0
Posted on Monday, October 30, 2017 by 醉·醉·鱼 and labeled under
故事是这样的,如果我在创建一个实例以后,再去编辑类并增加一个方法,这个实例是能够发现新的方法的。

class Dog
  def name
    
  end
end

a_dog = Dog.new

p a_dog.methods

class Dog
  def age
    
  end
end

p a_dog.methods

同理,在已经included 的module里增加一个新的方法。

module Professor
  def lectures
    
  end
  
end

class Mathematician
  attr_accessor :first_name, :last_name
  include Professor
end

fett = Mathematician.new

p fett.methods


module Professor
  def primary_classroom
    
  end
  
end

p fett.methods # this will have new method

但是,如果在已经included的module里面include一个新的module,这样就不行了。

module Employee
  def hired_date
    
  end
  
end

module Professor
  include Employee
end

p fett.methods # this will not have hired_date method until Mathematician included Professor again

原因在于,前两者影响的是方法表而已,而实例对应的klass里面留下的只是方法表pointer,而不是具体的方法。所以,在方法表里面增加方法是可行的。
但是,include 一个新的module,是会改变super pointer。在第一次include的时候,就已经“复制”好module并设置好了super pointer,不会再次改变。除非,重新打开类再include一次。


0
Posted on Friday, October 20, 2017 by 醉·醉·鱼 and labeled under
给指定的硬币类型,用最少的硬币个数,找出指定的amount。比如,现在有[2, 5, 10, 20, 50]这几种硬币,找出21块钱出来。

这个其实是算法导论里面动态规划。无意间,发现还有其他的实现方法。记录下来。

首先是动态规划。原来是,21块钱,可以拆分成19+2, 16+5, 11+10, 1+20。19又可以继续拆分成17+2, 14+5, 9+10。以此类推下去。里面有个技术点就是,还可以用block去定义Hash,厉害了。代码如下:

 def change(coins, amount, results = [])
    return [] if amount == 0

    coins.sort! { |a, b| a <=> b }

    optimal_change = Hash.new do |hash, key|

      if key < coins.min
        hash[key] = []
      elsif coins.include?(key)
        hash[key] = [key]
      else
        hash[key] = coins
            .reject { |coin| coin > key }
            .map { |coin| [coin] + hash[key - coin] }
            .reject { |change| change.inject(&:+) != key }
            .min { |a, b| a.size <=> b.size } || []
      end

      puts hash
      hash[key]
    end

    optimal_change[amount].empty? ? -1 : optimal_change[amount].sort
end



另外一种方法是,创建一个数组,从1到amount,然后逐一用coin去替代,最后一个就是amount对应的coin了。


  def change(coins, total_change)
    return -1 if total_change < 0 || coins.any? { |x| x < 1 }
    m = [[]] + [nil] * total_change
    coins.size
         .times
         .to_a
         .product((1..m.size - 1).to_a)
         .each { |c, t|
           if coins[c] == t
             m[t] = [coins[c]]
           else
             (1..t - 1).select { |t2| coins[c] + t2 == t }
                       .reject { |t2| m[t2].nil? }
                       .each { |t2| 
                         m[t] = m[t2] + [coins[c]] if m[t].nil? || m[t2].size + 1 < m[t].size 
                       }
           end
         }
    m[-1].nil? ? -1 : m[-1]
  end
0
Posted on Tuesday, October 17, 2017 by 醉·醉·鱼 and labeled under
Ruby的include和prepend有一个重要的知识点,就是多重包含的时候,后面的Module会被ignore掉,只会包含一次。


module C
  
end

module M
  
end

class B
  include M
  include C
end

p B.ancestors
# [B, C, M, Object, Kernel, BasicObject]

class A
  prepend M
  prepend C
end

p A.ancestors
# [C, M, A, Object, Kernel, BasicObject]


class D
  include M
  prepend C
end

p D.ancestors
# [C, D, M, Object, Kernel, BasicObject]

module M
  include C
end

class E
  prepend C
  include M
end

p E.ancestors
# [C, E, M, Object, Kernel, BasicObject]