まっしろけっけ

めもてきなやーつ

AWS ElastiCache for Memcached の AutoDiscovery についての調査

はじめに

ElastiCache for Memecached is これ
AutoDiscovery is これ

最近転職して SRE として生きているのですが、週末に ElastiCache for Memcached の空きメモリのアラートが上がっておりこれってスケールアップするのがいいのか node 増やすのがいいのか?という話になりました。
そこでそもそもどうやって書き込む node が決まっとるんだっけ?となったのが調査の発端。

前提条件

ElastiCache for Memcached を使っているアプリケーションは下記

Ruby on Rails
Dalli を使っている

Rails.cache の設定や Dalli::Client には AWS console で確認できる `設定エンドポイント` を渡している。

調査

僕の想像では設定エンドポイントを指定するとなんかいい感じに(key を元に?) node に書き込みデータを分散させて、取得の際もいい感じに(key を元に?) node からデータを取得してくれるのでは?もしくは node と言いつつ同じデータを書き込んだりしている?(でもそれって node として意味なくない?)と思っていた。

後者に関しては、その予想通りなら複数 node はメトリクス上同じような波形になる項目が多いはずだが実際にはバラバラなのでそうじゃないっぽいというのが理解できる。
その他にも転職したばかりなので cache 周りの構成や仕様などを把握出来ていないので色々見ながらふむふむ〜となっていたら同僚が下記の issue を見つけてくれた。

github.com


要するに書き込むデータを各 node に分散したければ Rails.cache の設定や Dalli::Client には複数の node の hostname を渡さないといけないのだけれど、node は増やしたくなったり減らしたくなったりするよね。そのために AutoDiscovery という機能を使って node の一覧を取得できるようになっているから Dalli を使う場合は dalli-elasticache を使って node 一覧を取ってきてからそれを Dalli::Client に渡してねという話

つまり、僕が想像していた設定エンドポイントを指定するとなんかいい感じにしてくれるのではなく、複数の memcached server の hostname を Dalli に渡すと Dalli がいい感じに分散してくれるようだ。(確かに Dalli::Client に複数の hostname を渡せるようになってるのなぜ?となっていた)

検証

$ bundle exec rails c

nodes = %w[0001-hostname 0002-hostname]

node01 = Dalli::Client.new(nodes[0])
node02 = Dalli::Client.new(nodes[1])
cluster = Dalli::Client.new('cluseter endpoint')

cluster.set("abc", "1")
Dalli::Server#connect cluseter-endpoint.apne1.cache.amazonaws.com:11211 # cluster endpoint に書き込まれたログ

cluster.set("bcde", "2")

node01.get("abc")
"1"
node02.get("abc")
nil
node01.get("bcde")
"2"
node02.get("bcde")
nil

# ここまででどうやら 0001 にのみ書き込みが行われていそうなことがわかる

100.times { |i| cluster.set("abc#{i}", i) }
100.times { |i| pp node01.get("abc#{i}") } # 1 ~ 100 まで取得できる

# 掃除
cluster.delete("abc")
cluster.delete("bcde")

100.times { |i| node01.delete("abc#{i}") }
100.times { |i| node02.delete("abc#{i}") }

自分の最初の想像だとこれで node01 と node02 にいい感じに ElastiCache 側が分散してくれている想定だったが 1 つの node にのみ書き込みが行われるというのがわかる

dalli-elasticache を使用した際の挙動確認

$ bundle exec rails c

nodes = %w[0001-hostname 0002-hostname]

node01 = Dalli::Client.new(nodes[0])
node02 = Dalli::Client.new(nodes[1])
elasticache = Dalli::ElastiCache.new('cluseter endpoint')
cluster = Dalli::Client.new(elasticache.servers)

cluster.set("abc", "1")
Dalli::Server#connect 0002-hostname.apne1.cache.amazonaws.com:11211 # 0002 に書き込みされたログ

cluster.set("bcde", "2")
Dalli::Server#connect 0001-hostname.apne1.cache.amazonaws.com:11211 # 0001 に書き込みされたログ

# 取得

cluster.get("abc")
"1"
cluster.get("bcde")
"2"

node01.get("bcde")
"2"
node02.get("bcde")
nil

node01.get("abc")
nil
node02.get("abc")
"1"

# ここまででどうやら key によって書き込み先が分散していることがわかる

100.times { |i| cluster.set("abc#{i}", i) }
100.times { |i| pp node01.get("abc#{i}") } # 1 ~ 100 の間で取得できない値が存在する
100.times { |i| pp cluster.get("abc#{i}") } # 1 ~ 100 まで取得できる

# 掃除
cluster.delete("abc")
cluster.delete("bcde")

100.times { |i| node01.delete("abc#{i}") }
100.times { |i| node02.delete("abc#{i}") }

key によって書き込む node が分散されていることがわかる

他にも下記などで memcached のデータを調査

$ echo "stats items" | nc 0001-hostname.apne1.cache.amazonaws.com 11211
$ echo "stats cachedump 5 100" | nc 0001-hostname.apne1.cache.amazonaws.com 11211

さいごに

ということでいい感じに node に書き込みを分散させたいなら dalli-elasticache などを使うと良さそう。
JavaPHP では公式の client があるみたいですよ

ElastiCache for Memcached をはじめて使っていた現場で既に設定エンドポイントを直接指定するコードになっていたので ElastiCache 側がなんかいい感じにしてるんや〜と想像していたがどうやら違ったようでちゃんと検証する機会が持てたのでよかったという感想

とブログを書いて前の職場に今も副業で関わっているのでコードを見てみたら普通に dalli-elasticache 使ってたわ〜となったオチ