Screaming Loud

日々是精進

rubyのyieldでのGCを調べてみた

rubyのyieldでGCってどうなるのかちょっと気になったので調べてみました。

コード

gc.rb

def opener1
  Array.new(3).each do |_e|
    a = Array.new(1_000_000) do
      'hello'
    end
    yield a
  end
end

def opener2
  Array.new(3).map do |_e|
    Array.new(1_000_000) do
      'hello'
    end
  end
end

def check_gc(p)
  p.length
  GC.start
  puts "did gc: #{GC.stat.slice(:minor_gc_count, :major_gc_count, :heap_free_slots, :heap_live_slots, :old_objects)}"
end

GC::Profiler.enable
puts "start: #{GC.stat.slice(:minor_gc_count, :major_gc_count, :heap_free_slots, :heap_live_slots, :old_objects)}"

# opener1 do |p|
#   check_gc(p)
# end

opener2.each do |p|
  check_gc(p)
end

puts "total_time: #{GC::Profiler.total_time.round(3)}ms"

結果

yieldで返すopener1の場合、loopごとにheap free slotが増えold objectが消えてるので使い終わったらGCされていそうです。

$ ruby gc.rb 
start: {:minor_gc_count=>9, :major_gc_count=>2, :heap_free_slots=>70, :heap_live_slots=>23163, :old_objects=>14939}
did gc: {:minor_gc_count=>17, :major_gc_count=>6, :heap_free_slots=>229, :heap_live_slots=>1015920, :old_objects=>1015702}
did gc: {:minor_gc_count=>18, :major_gc_count=>7, :heap_free_slots=>1000068, :heap_live_slots=>1015922, :old_objects=>15702}
did gc: {:minor_gc_count=>18, :major_gc_count=>8, :heap_free_slots=>1000067, :heap_live_slots=>1015923, :old_objects=>15702}
total_time: 0.149ms

通常のArrayを返すopener2では、配列を生成してそれを読み込んでいるので、GCされずにold_objectとして保持されています。

$ ruby gc.rb
start: {:minor_gc_count=>9, :major_gc_count=>2, :heap_free_slots=>77, :heap_live_slots=>23155, :old_objects=>14940}
did gc: {:minor_gc_count=>19, :major_gc_count=>6, :heap_free_slots=>725, :heap_live_slots=>3015923, :old_objects=>3015705}
did gc: {:minor_gc_count=>19, :major_gc_count=>7, :heap_free_slots=>724, :heap_live_slots=>3015924, :old_objects=>3015705}
did gc: {:minor_gc_count=>19, :major_gc_count=>8, :heap_free_slots=>723, :heap_live_slots=>3015925, :old_objects=>3015705}
total_time: 0.343ms

dockerを使って別のサーバのredisのメトリクスを取る

Redisのメトリクス、特にElasticacheのメトリクスを見たいときにCloudwatchのメトリクスだけだと足りない場合があると思います。

redis-infoのメトリクスを取る場合、別のサーバにdatadog-agentを立ち上げてそのサーバからredisへmetricsを取りに行きます。

今回はdocker-composeでlocalからアクセスする場合です。

ディレクトリ構成

├── conf.d
│   └── redisdb.d
│       └── config.yaml
├── datadog.yaml
└── docker-compose.yaml

以上のような構成にしています。

conf.d 以下はredisなどの各種メトリクス用のconfをディレクトリを切ります。 redisの場合は redisdb.d など名前が決まっているので、注意しましょう。

参考: Redis monitor - datadog

rootにおいているdatadog.yamlはdatadog-agent自体の設定です。 dockerで動かしているので環境変数でも利用できますが、ゴチャゴチャするので切り出すときれいになるかと思います。

各種設定yaml

yamlもそれぞれ見ていきます。

docker-compose.yaml

version: "3"
services:
  datadog:
    image: datadog/agent:7.21.1
    environment:
      DD_API_KEY: xxxx
      DD_TAGS: "hostname:redis-monitor-test name:datadog-test"
      DD_PROCESS_AGENT_ENABLED: "false"
      DD_HOSTNAME: "redis-monitor-test"
    volumes:
      - "./conf.d:/conf.d"
      - "./datadog.yaml:/etc/datadog-agent/datadog.yaml"

DD_API_KEYだけは必須のパラメータになっているので、設定しましょう。

またprocess agentもいらないので、環境変数で設定します。

datadog-agent/config.go at master · DataDog/datadog-agent · GitHub

datadog.yaml

# enable_payloads:
#   series: false
#   events: false
#   service_checks: false
#   sketches: false

上記設定をすると、datadog-agentからメトリクスを送らなくなります。 他にもいろいろな設定がができます。 datadog.yamlに書いても反映されないパラメータもあるので、それらはdockerの環境変数に書きましょう。

今回redisのメトリクスを送るので、この設定は使わないのでコメントアウトしてあります。 logsだけ使う場合などはコメントアウト外します。 参考: How to set up only Logs

redisdb.d/config.yaml

init_config:

instances:
  - host: hoge.cache.amazonaws.com
    port: 6379
    tags:
      - enviroment:production
      - region:ap-northeast-1
      - apps:redis

redisの設定はinstanceを並べるだけです。 tagは適当に好きなのをつけます。

実行

あとは実行するだけです。  

$ docker-compose up

「魯珈」芳醇チキンカレー

今回は魯珈の芳醇チキンカレーです。

f:id:yuutookun:20200904203744j:plain

魯珈は早朝から並ばないと食べられない超人気店ですが、そのカレーをレトルトで食べられます。

様々なスパイスの味を楽しめて、もうやんカレーに似てるなという印象でした。

自分は店舗の魯珈に行ったことがないのでぜひ行ってみたいのですが、それをレトルトで楽しめるので便利な世の中ですね。

www.amazon.co.jp

十勝モッツァレラチーズカレー

久しぶりにカレーブログです。 今回は「十勝モッツァレラチーズカレー」です。

f:id:yuutookun:20200702202924j:plain

チーズカレーですが、モッツァレラのせいか粘り気が少なめな感じがしました。 比較的チーズ感はありつつも、少し玉ねぎの甘みがあるカレーです。

www.amazon.co.jp