unhurried

コンピュータ関連ネタがほとんど、ときどき趣味も…

MongoDB Node.JS Driver コネクション管理

Java + MySQLではどのフレームワークにも当たり前にあるコネクションプールのようなコネクション管理機能ですが、Node.js + MongoDBではどのように実装するのが常套手段なのかを調べました。

ポイント

  • MongoClient.connectは1度のみ呼び出し、databaseインスタンスを再利用する。
  • コネクション管理はDriverが行ってくれて、MongoClient.connectのオプションにコネクションプール数を指定できる。

サンプルコード

  • db.js
// Keep the mongoDB connection while the application is running.
var db = null;
module.exports.initialize = co.wrap(function*() {
    db = yield MongoClient.connect("mongodb://...");
});

module.exports.getById = co.wrap(function*(id) {
var col = db.collection("collection");
    var row = yield col.findOne({_id:id});
    return row.property;
});
  • app.js
var db = require('./db');

co(function*(){
    db.initialize();
    var result = db.getById(0);
    ...
});

参考

Callback地獄対策 (3) aa generator

今回は、Qiitaで人気のあるCallback地獄対策の記事で紹介されているaaについて調べてみました。

aaもcoと同様にGeneratorの仕組みを利用して簡単に並列・直列処理フローを記述できるようにするライブラリです。 aaはcoと比べるとthunkify/promisifyが同じライブラリにパッケージされていることと、thunkifyAll/promisifyAllでオブジェクト全体にかけれられるの良いところと思います。 一方で、co.wrapに相当する機能はなく、並列・直列処理フローを利用したyieldableな関数を作るときに少し不便と感じました。

const aa = require('aa');

// callbackを引数にとる関数を持つオブジェクト
var callbackObject = {
    func: function(message, timeout, callback) {
        setTimeout(function() {
            console.log(message);
            callback(null, message);
        }, timeout);
    }
};

// aa.promisifyAllはオブジェクト全体に対してpromisifyを適用する
// promisifyはcallbackを引数にとる関数をPromiseを返却する関数に変換する
// Promiseoにはgenerator内でyeild式を適用できる(yeildable)
var promisifiedObject = aa.promisifyAll(callbackObject);

// aa関数はcoと同様にGenerator(function*(){...})を渡すと、
// yeild式に指定されたPromiseの完了を待ってから処理を再開させる
aa(function*() {
    console.log('--- aa start ---');
    
    // 2つの関数を並列処理する
    // 並列処理をするにはyeildにArrayもしくはObjectを指定する
    var response = yield [
        promisifiedObject.funcAsync('aa two', 800),
        promisifiedObject.funcAsync('aa one', 600),
    ];
    console.log("response: " + JSON.stringify(response));

    // 2つの関数を直列処理する
    yield promisifiedObject.funcAsync('aa three', 200);
    yield promisifiedObject.funcAsync('aa four', 400);
    
    // aaを利用した関数の呼び出し(後述)
    yield wrappedFunction();

    console.log('--- aa end ---');

    // Generatorでreturnするとaa.then()に指定した関数が実行される
    return "Generator finished.";

    // いずれかのPromiseでreject関数が呼ばれると
    // aa.error()に指定した関数が実行される
    yield Promise.reject("An error happened.");

}).then(function(value){
    console.log('--- then start ---');
    console.log(value);

}).catch((error) => {
    console.log('--- error start ---');
    console.log(error);
});

// aaにはco.wrapに相当する機能がないため、
// yieldを利用した関数を定義するにはaa(function*(){})を返却する関数を作る
var wrappedFunction = function() {
    return aa(function* () {
        console.log('--- wrap start ---');

        var response = yield [
            promisifiedObject.funcAsync('wrap two', 800),
            promisifiedObject.funcAsync('wrap one', 600),
        ];
        console.log("wrap response: " + JSON.stringify(response));
        yield promisifiedObject.funcAsync('wrap three', 200);
        yield promisifiedObject.funcAsync('wrap four', 400);

        console.log('--- wrap end ---');

        return yield Promise.resolve(null);
    });
};

実行結果

$ node aa_generator.js
--- aa start ---
aa one
aa two
response: ["aa two","aa one"]
aa three
aa four
--- wrap start ---
wrap one
wrap two
wrap response: ["wrap two","wrap one"]
wrap three
wrap four
--- wrap end ---
--- aa end ---
--- then start ---
Generator finished.

参考

シリコンバレー周辺のお土産

最近にそれなりの量のお土産を買う機会があったので、お土産に良さそうなものを調べてみました。

探す条件

  • 多人数に配れるように個包装されている食べ物や飲み物
  • シリコンバレー周辺で購入できるもの
  • カリフォルニア、アメリカ、もしくは北米地域発祥で日本では入手しにくいもの

お土産リスト

  • Ghirardelli Chocolate

    • サンフランシスコ発祥の1852年から続く老舗チョコレートメーカー
    • 正方形のチョコレートが定番で色々なフレーバーがある
    • Great Mallにアウトレット、West Field Valley Fairに小さな販売ブースがある他、スーパーマーケット等でも購入できる
    • (アメリカの定番お土産になっているので新鮮味は少ないかも)
  • See's Candies

    • ロサンゼルスにて1921年に開業したチョコレート、キャンディメーカー
    • 棒つきのキャンディやチョコレートが人気がある
    • シリコンバレー周辺にお店が5,6店舗ある
  • Sugarfina

    • ロサンゼルスにて2012年に設立されたおしゃれなキャンディショップ
    • 箱に色々な種類のキャンディを組み合わせて詰めて
    • Santana Rowにお店がある
  • スーパーマーケットで購入できるお菓子(キャンディ、チョコレートなど)

    • Whole Foods、Trader Joe's、Safewayなどで購入できる
    • (Trader Joe'sのリテールブランドのお菓子は個包装のものが少ない)
  • DAVIDs TEA

    • カナダのトロントに2008年に開店した紅茶ショップ
    • アメリカにも多くの店舗を出店していて人気がある
    • Palo Altoにお店がある

How to cope with Callback Hell #2 co generator

The second countermeasure of Callback Hell is using co. co is a library which utilizes Generator mechanism and make it easy to write serial and parallel flows.

const co = require('co');
const thunkify = require('thunkify')

// a function which has a callback in its argument
var callbackFunction = function(message, timeout, callback) {
    setTimeout(function() {
        console.log(message);
        callback(null, message);
    }, timeout);
};

// thunkify converts a function with a callback to one returning a thunk.
// yield can be applied to thunk in generator. (yeildable)
var thunkifiedFunction = thunkify(callbackFunction);

// a function which returns a Promise
var promiseFunction = function(message, timeout) {
    return new Promise(function(resolve, reject){
        setTimeout(function() {
            console.log(message);
            resolve(message);
        }, timeout);
    });
};

// When a Generator (function*(){...}) is passed to co,
// co will proceed the process of the Generator until a yeild statement appear,
// and resume the process after the Promise object is finished.
co(function*() {
    console.log('--- co start ---');
    
    // Process two functions in parallel.
    // Specify an array or an object to yeild.
    var response = yield [
        thunkifiedFunction('co two', 800),
        promiseFunction('co one', 600),
    ];
    console.log("response: " + JSON.stringify(response));

    // Process two functions serially.
    yield thunkifiedFunction('co three', 200);
    yield promiseFunction('co four', 400);

    // Functions defined using co.wrap can be used in co generator.
    yield wrappedFunction();

    console.log('--- co end ---');

    // When return is called, co.then() is executed.
    return "Generator finished.";

    // When a reject function is called in one of Promise objects, 
    // co.error() is executed.
    // yield Promise.reject("An error happened.");

}).then(function(value){
    console.log('--- then start ---');
    console.log(value);

}).catch((error) => {
    console.log('--- error start ---');
    console.log(error);
});

// Functions using co generator can be defined by using co.wrap.
// They are also yieldable because they also return a Promise object.
var wrappedFunction = co.wrap(function* () {
    console.log('--- co.wrap start ---');

    var response = yield [
        thunkifiedFunction('co.wrap two', 800),
        thunkifiedFunction('co.wrap one', 600),
    ];
    console.log("co.wrap response: " + JSON.stringify(response));
    yield thunkifiedFunction('co.wrap three', 200);
    yield thunkifiedFunction('co.wrap four', 400);

    console.log('--- co.wrap end ---');

    return yield Promise.resolve(null);
});

Result

$ node co_generator.js
--- co start ---
co one
co two
response: ["co two","co one"]
co three
co four
--- co.wrap start ---
co.wrap one
co.wrap two
co.wrap response: ["co.wrap two","co.wrap one"]
co.wrap three
co.wrap four
--- co.wrap end ---
--- co end ---
--- then start ---
Generator finished.

Callback地獄対策 (2) co generator

Callback地獄対策の2つ目はcoについてまとめました。
coはGeneratorの仕組みを利用して簡単に並列・直列処理フローを記述できるようにするライブラリです。

const co = require('co');
const thunkify = require('thunkify')

// callbackを引数にとる一般的な関数
var callbackFunction = function(message, timeout, callback) {
    setTimeout(function() {
        console.log(message);
        callback(null, message);
    }, timeout);
};

// thunkifyはcallbackを引数にとる関数をthunkを返却する関数に変換する
// thunkにはgenerator内でyeild式を適用できる(yeildable)
var thunkifiedFunction = thunkify(callbackFunction);

// Promiseを返却する一般的な関数
var promiseFunction = function(message, timeout) {
    return new Promise(function(resolve, reject){
        setTimeout(function() {
            console.log(message);
            resolve(message);
        }, timeout);
    });
};

// co関数にGenerator(function*(){...})を渡すと、
// co関数はGeneratorの処理をyeild式まで進め、
// yeild式に指定されたPromiseの完了を待ってから処理を再開させる
co(function*() {
    console.log('--- co start ---');
    
    // 2つの関数を並列処理する
    // 並列処理をするにはyeildにArrayもしくはObjectを指定する
    var response = yield [
        thunkifiedFunction('co two', 800),
        promiseFunction('co one', 600),
    ];
    console.log("response: " + JSON.stringify(response));

    // 2つの関数を直列処理する
    yield thunkifiedFunction('co three', 200);
    yield promiseFunction('co four', 400);

    // co.wrapで定義した関数(後述)はco generator内で利用できる
    yield wrappedFunction();

    console.log('--- co end ---');

    // Generatorでreturnするとco.then()に指定した関数が実行される
    return "Generator finished.";

    // いずれかのPromiseでreject関数が呼ばれると
    // co.error()に指定した関数が実行される
    // yield Promise.reject("An error happened.");

}).then(function(value){
    console.log('--- then start ---');
    console.log(value);

}).catch((error) => {
    console.log('--- error start ---');
    console.log(error);
});

// co.wrapを使うことでco generatorを利用した関数を定義できる
// この関数もPromiseを返却するのでyeildableとなる
var wrappedFunction = co.wrap(function* () {
    console.log('--- co.wrap start ---');

    var response = yield [
        thunkifiedFunction('co.wrap two', 800),
        thunkifiedFunction('co.wrap one', 600),
    ];
    console.log("co.wrap response: " + JSON.stringify(response));
    yield thunkifiedFunction('co.wrap three', 200);
    yield thunkifiedFunction('co.wrap four', 400);

    console.log('--- co.wrap end ---');

    return yield Promise.resolve(null);
});

実行結果

$ node co_generator.js
--- co start ---
co one
co two
response: ["co two","co one"]
co three
co four
--- co.wrap start ---
co.wrap one
co.wrap two
co.wrap response: ["co.wrap two","co.wrap one"]
co.wrap three
co.wrap four
--- co.wrap end ---
--- co end ---
--- then start ---
Generator finished.

How to cope with Callback Hell #1 Promise

We often fall into what is called "Callback Hell" when we write some asynchronous processes with Node.js, which is a problem the hierarchy of the code becomes too deep.

There are many countermeasures and it looks a little messy, so I will sort out them in several posts. This time, I will introduce the way of returning Promise objects which hold the running state in an asynchronous process instead of making a callback an argument.

// An example of a function returning Promise.
var promiseFunction = function(message, timeout) {
    // Pass a function which has resolve and reject as arguments
    // when creating a Promise object.
    // * resolve: a function returning an object when the process succeeds
    // * reject: a function returning an error object when the process fails
    return new Promise(function(resolve, reject){
        setTimeout(function() {
            console.log(message);
            resolve(message);
            // Call reject function when an error occurs.
            // reject(error);
        }, timeout);
    });
};

// The usage of a Promise instance
var promise = promiseFunction('promise', 100);
// then: write post-processing
promise.then(function(result){
    console.log("result: " + JSON.stringify(result));
// catch: write error processing
}).catch(function(error){
    console.log("error: " + JSON.stringify(error));
});

Result

$ node promise.js
promise
result: "promise"

Reference

Callback地獄対策 (1) Promise

NodeJSで非同期処理を書いていくと必ずはまるのが、Callbackで階層が深くなりすぎるいわゆるCallback地獄です。対策を調べてみたところ色々ありすぎて混乱してきてしまったので、何回かに分けて整理してみようと思います。

今回は、非同期処理を行う関数でCallbackを引数にとる代わりに、実行状態を保持するPromiseオブジェクトを返却する方法をまとめてみました。

// Promiseを返却する関数の例
var promiseFunction = function(message, timeout) {
    // Promiseオブジェクト生成時の引数に
    // resolve、rejectを引数に持つFunctionを渡す
    // * resolve: 処理成功時の返却オブジェクトを渡す関数
    // * reject: 処理失敗時のエラーオブジェクトを渡す関数
    return new Promise(function(resolve, reject){
        setTimeout(function() {
            console.log(message);
            resolve(message);
            // エラー発生時はreject関数を呼び出す
            // reject(error);
        }, timeout);
    });
};

// Promiseインスタンスの使い方
var promise = promiseFunction('promise', 100);
// then関数:後処理を記述する
promise.then(function(result){
    console.log("result: " + JSON.stringify(result));
// catch関数:エラー処理を記述する
}).catch(function(error){
    console.log("error: " + JSON.stringify(error));
});

実行結果

$ node promise.js
promise
result: "promise"

参考: http://qiita.com/koki_cheese/items/c559da338a3d307c9d88