はじめに
Memcached を運用中に、Request の傾向は変わっていないにもかかわらず、徐々に Item 数が増加し始め、 ある時を境に Item が 一切 Eviction/Expire されなくなり、Memory が枯渇し Slab OOM Error が起こる、という不具合に遭遇しました。不具合の原因については特定し、1.4.29 で修正がマージされました (Pull-Request: fix zero hash items eviction , ReleaseNote1.4.29) 。不具合が発生する条件、原因、回避策を簡単にまとめておきます(Pull-Request にはより詳しく書いてあります)。
不具合が発生する条件
- Memcached Version : 1.4.19 から 1.4.28
- SET した Key を GET しない場合がある
- Item を入れ替える Command (APPEND, PREPEND, INCR, DECR) を使用していない
不具合の原因
Memcached は Item を双方向リストで管理しています。GET, SET などの Command を処理するタイミングで各 Slab Class の双方向リストの末尾から 5 つの Item を捜査し、 Eviction/Expire させます(1.4.26 時点 lru_pull_tail@items.c)。この Item Eviction/Expire 処理に不具合があり、 Item の Key の hash 値が偶然に 0 の時に、Item が Eviction/Expire されず、双方向リストの末尾に滞留し続けます。
再現スクリプト
# set the items whose jenkins hash(ENDIAN_LITTLE) values are zero with exptime 1 sec printf "set 0wYuLiaUdfgTZCUsz8mRR1WJVk 0 1 4\r\ndata\r\n" | nc localhost 11211 printf "set 9NxjWkBnSfD0LShqUBZAqv3jKI 0 1 4\r\ndata\r\n" | nc localhost 11211 printf "set wRL2lvUMyPnbiImZdi9CTXbsJ6 0 1 4\r\ndata\r\n" | nc localhost 11211 printf "set vOgpzFNo2pGpYLKxUWXfI6PDXq 0 1 4\r\ndata\r\n" | nc localhost 11211 printf "set aTogASrWNRQSA1uh2ySgv6HwaU 0 1 4\r\ndata\r\n" | nc localhost 11211 # wait for exptime sleep 2 # another innocent key set # normally, the five items expire here printf "set innocentkey 0 1 4\r\ndata\r\n" | nc localhost 11211 echo "stats items" | nc localhost 11211
スクリプト実行結果
STORED STORED STORED STORED STORED STORED STAT items:1:number 6 STAT items:1:age 2 STAT items:1:evicted 0 STAT items:1:evicted_nonzero 0 STAT items:1:evicted_time 0 STAT items:1:outofmemory 0 STAT items:1:tailrepairs 0 STAT items:1:reclaimed 0 STAT items:1:expired_unfetched 0 STAT items:1:evicted_unfetched 0 STAT items:1:crawler_reclaimed 0 STAT items:1:crawler_items_checked 0 STAT items:1:lrutail_reflocked 0 END
本来であれば、6度目の SET が完了した時点で、 Expire すべき Item は全て回収されるはずですが、滞留してしまっています。
さらに後続の SET が続くと、 Item が一切回収されないまま Memory を消費し続け、-M option で指定した上限にあたるところで OOM Error(SET 不能) となります。
さらに後続の SET が続くと、 Item が一切回収されないまま Memory を消費し続け、-M option で指定した上限にあたるところで OOM Error(SET 不能) となります。
不具合特定の方法
Coredump を取得し、コードを読んで可能性を絞りつつ gdb で追いました。
現状の回避策
- Memcached 1.4.19 - 1.4.28 の使用を避ける
- [再起動不要, コード理解している人向け] Item 滞留を起こしうる全ての Slab Class を対象に、時折 Item を入れ替える Command (APPEND, PREPEND, INCR, DECR) を発行する
- lru_pull_tail の引数 cur_hv が 0 では無くなるため
まとめ
- Memcached 1.4.19 以降には突然 Slab OOM を引き起こす不具合が存在します
最新の upstream でも未修正です(修正提案中)- [8/30 追記] 1.4.29 で修正がマージされました