2016/06/28

Memcached 1.4.19 to 1.4.28: Eviction 不具合による Slab OOM

はじめに



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 不能) となります。

不具合特定の方法


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 を引き起こす不具合が存在します