自作のごくシンプルなウェブアプリを memcached 対応(要は「データベースが memcached でも動くようにする」対応)させようとしてハマってしまった時の様子と、なんとか対応できた結果をブログにまとめました:
2022022300


そのシンプルなウェブアプリではデータの CRUD 処理を行います。その CRUD 処理の中に「全データを取得」という API を作っていました。データベースが MySQL なら MySQL の特定テーブルの全データを、MongoDB なら MongoDB 内の全データを取り出す、という API でした。これの memcached 版を作ろうとした時の話です。ちなみにアプリケーションは Node.js で作っていたので、Node.js で実装できる必要があります。memcached を扱うためのライブラリとしてはおそらく最も一般的なライブラリである npm memcached を使っていました(必ずしもこのライブラリでなくてもいいです)。

memcached はいわゆる Key-Value 型のデータストアです。key の値を頼りにして value を取り出す、というものです。key さえ分かれば value が取り出せるので、「全データを取り出す」のは「全キーが分かれば全データがわかる」ということになります、、、のですが、npm memcached の解説のどこを見てもそんな API は提供されていないようでした。当初は「えー!? ただ全キーを取り出したいだけなのに、その機能がないの!?」と思いました。ちなみに memcached とメモリDBの双璧をなすもうひとつの Redis では keys() というキーの検索メソッドが用意されていて、keys( '*' ) のようにキーにワイルドカードを指定して実行することで全キーを取り出すことができていました。memcached にも同様の機能があるものと期待していたのですが、、、StackOverflow など英語圏の情報も含めていろいろ調べてみたのですが、どうも一筋縄ではいかないらしいことが分かってきました。。さてどうしよう。。


ここで手助けになったのはネットの情報というよりも、実際に telnet コマンドを叩いて調べてみる方法でした。「試行錯誤」というやつです。そうしているうちに「CLI コマンドであれば、コマンドを何回か繰り返して実行する必要はあるが、キー一覧を取り出すことはできそう」だと分かってきました:

2022022301

2022022302


結論としてのコマンド順序は、
 1. "stats items" コマンドを実行し、各 slab に何件のデータが登録されているのかを調べ、
 2. 1. で調べた結果に従って、"stats cachedump" コマンドで slab 内をダンプする
 3. key 一覧だけでなく value の一覧が必要な場合は getMulti() 関数で value も取り出す
という感じでできそうでした。


参考: 
https://shim0mura.hatenadiary.jp/entry/20140125/1390647044


後はこの 1, 2, 3 の処理を Node.js と memcached ライブラリで実装すればよい、ということになります。こんな感じで作ってみました:
var Memcached = require( 'memcached' );
var memcached = new Memcached( "localhost:11211" );   // memcached が localhost:11211 で稼働している場合

async function getAll(){
  return new Promise( async ( resolve, reject ) => {
    if( memcached ){
      memcached.items( function( err, results ){
        if( err ){
          resolve( { status: false, error: err } );
        }else{
          var cnt = 0;
          var values = [];
          results.forEach( function( result ){
            var obj_keys = Object.keys( result );
            if( obj_keys.length == 0 ){
              resolve( { status: true, results: [] } );
            }else{
              Object.keys( result ).forEach( function( slabid ){
                if( slabid != 'server' ){
                  memcached.cachedump( result.server, parseInt( slabid ), result[slabid].number, function( err, key_results ){
                    var keys = [];
                    if( 'length' in key_results ){
                      key_results.forEach( function( key_result ){
                        var key = key_result['key'];
                        keys.push( key );
                      });
                    }else{
                      var key = key_results['key'];
                      keys.push( key );
                    }
  
                    memcached.getMulti( keys, function( err, data ){
                      if( !err ){
                        keys.forEach( function( key ){
                          values.push( data[key] );
                        });
                      }

                      cnt ++;
                      if( cnt == results.length ){
                        resolve( { status: true, results: values } );
                      }
                    });
                  });
                }
              });
            }
          });
        }
      });
    }else{
      resolve( { status: false, error: 'no db' } );
    }
  });
};

これで、
var result = await getAll();

といった感じで全データ( result.results )が取得できるようになりました。

自分であちこち見て回った上で、「難しそう」とか「できないわけではない・・・」といった情報は得られたのですが、実際に取得できる Node.js サンプルソースコードを見つけることができなかったので、同じように悩んでいる人のお役にたてば。