カテゴリー : Node.js

[Mongoose] stream を使ってバッチ処理するときは noCursorTimeout: true オプションを設定すると幸せになれるかも

Express.js(Node.js) + Mongoose(MongoDB) という構成で、バッチ処理を長時間実行すると途中で終了してしまう問題が発生しました。

状況としては、まだ cursor が次のデータを取得できるはずなのに、stream.on ‘data’ 内では次のデータが渡ってこなくて、stream.on ‘error’ が呼ばれることなく、stream.on ‘close’ が呼ばれている感じです。cursor がタイムアウトしてしまっているのが原因らしいです。

解決方法としては、第三引数に noCursorTimeout: true を指定することで、途中で終了せずにバッチ処理を最後まで実行することができました。

var stream = User.find(
  {},
  {},
  { noCursorTimeout: true }
).stream();
 
stream.on('data', function (doc) {
  // do something with the mongoose document
}).on('error', function (err) {
  // handle the error
}).on('close', function () {
  // the stream is closed
});

参考情報

[Node.js] RangeError: Maximum call stack size exceeded

users がめっちゃいると RangeError: Maximum call stack size exceeded が発生して、途中で死ぬ。

async.eachSeries users, (user, next) ->
  user.save (error)->
    return next()
, (error) ->
  // ...

下記のように process.nextTick や setImmediate で囲ってあげると解決する。

async.eachSeries users, (user, next) ->
  process.nextTick ->
    user.save (error)->
      return next()
, (error) ->
  // ...

参考情報

Node.js – Maximum call stack size exceeded – Stack Overflow

'Maximum call stack size exceeded' using async.forEachLimit · Issue #75 · caolan/async

Node.js は実行環境とライブラリの2つから成っているもの

Nodeビギナーズブックを読んで、“Node.jsは実行環境とライブラリの2つから成っている” という表現が個人的にしっくりきたので、忘れないようにメモしておきます。

サーバサイドJavaScript

最初にJavaScriptが日の目を見たのは、ブラウザ上でした。 しかしこれは単なるコンテキストに過ぎません。 コンテキストによってその言語でできることは決まってきますが、 それはその言語自体ができることとイコールというわけではありません。 JavaScriptは”完全な”言語であり、様々なコンテキストで使えます。 他の言語でやっていることは、すべてJavaScriptでもできます。

Node.jsもまた、ひとつのコンテキストに過ぎません。 Node.jsによって、JavaScriptはバックエンド、ブラウザの外で動作できるのです。

バックエンドでJavaScriptが動作するには、インタープリターで変換され、 そして実行されなければなりません。これをNode.jsが行います。 内部ではGoogleのV8 VMが利用されています。 V8 VMはGoogle Chromeが使用しているJavaScriptの実行環境そのものと同じです。

それに加えて、Node.jsにはたくさんの便利なモジュールが同梱されています。 全てを1から作る必要はないのです。例えばコンソールに文字列を出力するモジュールなどがあります。

つまりNode.jsは、実行環境とライブラリの2つから成っているのです。

[Node.js] node-wkhtmltopdf を使う

Node.js で wkhtmltopdf を使う方法をメモ。
(最終更新日:2014/11/28)

node-wkhtmltopdf

A Node.js wrapper for the wkhtmltopdf command line tool.

wkhtmltopdf

ということで、npm module だけだと使えないので wkhtmltopdf はインストールする必要があります。

wkhtmltopdf のインストール

# the wkhtmltopdf formula is now inactive but still available in the boneyard repository
brew tap homebrew/boneyard
 
# Install wkhtmltopdf
brew install wkhtmltopdf

wkhtmltopdf コマンドで、PDFを生成できることを確認します。

wkhtmltopdf "http://google.com" google.pdf

以上です。

[Node.js] ファイルの拡張子を取得する path.extname

Node.js の path module を使って、下記のようにファイルの拡張子を取得することができます。

path.extname('index.html')
// returns
'.html'
 
path.extname('index.coffee.md')
// returns
'.md'
 
path.extname('index.')
// returns
'.'
 
path.extname('index')
// returns
''

参考情報

Path Node.js v0.10.26 Manual & Documentation

[Node.js] RangeError: Maximum call stack size exceeded

Node.js で、RangeError: Maximum call stack size exceeded エラーが発生したときの対応方法をメモ。

% node -v
v0.10.26
 
% node --v8-options | grep -B0 -A1 stack_size
  --stack_size (default size of stack region v8 is allowed to use (in kBytes))
        type: int  default: 984

Node.js version 0.10.26 は、デフォルトの stack size は 984 KB です。

% node -h 
...
 
Options:
  --max-stack-size=val set max v8 stack size (bytes)
 
...

ヘルプには –max-stack-size で指定すると書いてありますが、

node --max-stack-size=val

v0.10.x 以上だと

node --stack-size=val

–stack-size で指定するっぽいです。


参考情報

What is the default stack size in Node.js? – Stack Overflow

javascript – How can I increase the maximum call stack size in Node.js – Stack Overflow

[Node.js] ファイルの存在チェック

Node.js で、viewファイルの存在チェックして、無ければリダイレクトさせる処理をメモ。

var fs = require('fs');
 
var template = 'novels/'+ title + '.jade';
var templateFilePath = 'views/' + template;
 
fs.stat(templateFilePath, function(e) {
  if (e) {
    console.error(templateFilePath + " file does't exist.");
    return res.redirect('/novels');
  }
 
  return res.render(template);
});

ここでは、テンプレートエンジンは jade を使っているという前提で、動的に生成したファイル名に対応する jade ファイルが存在するかチェックしています。

[Node.js][node-csv] Error: Invalid closing quote at line 1; found ” ” instead of delimiter “\t”

Node.js で使える CSV Parser node-csv でタブ区切りテキストの中にダブルクオーテーション ” を含めていたら下記のようなエラーが発生しました。

wdavidw/node-csv

Error: Invalid closing quote at line 1; found " " instead of delimiter "\t"
  at [object Object].Parser.write (/u/apps/com/shared/node_modules/csv/lib/parser.js:104:29)
  at [object Object].CSV.write (/u/apps/com/shared/node_modules/csv/lib/index.js:275:17)
  at write (_stream_readable.js:583:24)
  at flow (_stream_readable.js:592:7)
  at ReadStream.pipeOnReadable (_stream_readable.js:624:5)
  at ReadStream.EventEmitter.emit (events.js:92:17)
  at emitReadable_ (_stream_readable.js:408:10)
  at emitReadable (_stream_readable.js:404:5)
  at readableAddChunk (_stream_readable.js:165:9)
  at ReadStream.Readable.push (_stream_readable.js:127:10)
  at onread (fs.js:1561:12)
  at Object.wrapper [as oncomplete] (fs.js:454:17)

一部、手動で作成したCSVファイルだったので、ダブルクオーテーションを消して解決しました。


参考情報

double quotes in a tab-delimited file throws error · Issue #46 · wdavidw/node-csv

[Node.js][Express] Error: Request Entity Too Large

Express で大きいサイズのファイルをPOSTしたら Request Entity Too Large というエラーが発生した。

エラーメッセージ

Error: Request Entity Too Large
  at Object.exports.error (/u/apps/com/shared/node_modules/express/node_modules/connect/lib/utils.js:63:13)
  at limit (/u/apps/com/shared/node_modules/express/node_modules/connect/lib/middleware/limit.js:51:47)
  at multipart (/u/apps/com/shared/node_modules/express/node_modules/connect/lib/middleware/multipart.js:102:5)
  at /u/apps/com/shared/node_modules/express/node_modules/connect/lib/middleware/bodyParser.js:57:9
  at urlencoded (/u/apps/com/shared/node_modules/express/node_modules/connect/lib/middleware/urlencoded.js:52:72)
  at /u/apps/com/shared/node_modules/express/node_modules/connect/lib/middleware/bodyParser.js:55:7
  at json (/u/apps/com/shared/node_modules/express/node_modules/connect/lib/middleware/json.js:54:55)
  at Object.bodyParser [as handle] (/u/apps/com/shared/node_modules/express/node_modules/connect/lib/middleware/bodyParser.js:53:5)
  at next (/u/apps/com/shared/node_modules/express/node_modules/connect/lib/proto.js:193:15)
  at Object.cookieParser [as handle] (/u/apps/com/shared/node_modules/express/node_modules/connect/lib/middleware/cookieParser.js:60:5)
  at next (/u/apps/com/shared/node_modules/express/node_modules/connect/lib/proto.js:193:15)
  at Object.expressInit [as handle] (/u/apps/com/shared/node_modules/express/lib/middleware.js:30:5)
  at next (/u/apps/com/shared/node_modules/express/node_modules/connect/lib/proto.js:193:15)
  at Object.query [as handle] (/u/apps/com/shared/node_modules/express/node_modules/connect/lib/middleware/query.js:44:5)
  at next (/u/apps/com/shared/node_modules/express/node_modules/connect/lib/proto.js:193:15)
  at Function.app.handle (/u/apps/com/shared/node_modules/express/node_modules/connect/lib/proto.js:201:3)
  at Server.app (/u/apps/com/shared/node_modules/express/node_modules/connect/lib/connect.js:65:37)
  at Server.EventEmitter.emit (events.js:98:17)
  at HTTPParser.parser.onIncoming (http.js:2108:12)
  at HTTPParser.parserOnHeadersComplete [as onHeadersComplete] (http.js:121:23)
  at Socket.socket.ondata (http.js:1966:22)
  at TCP.onread (net.js:527:27)

bodyParser の limit オプションの値を変更して、アップロードするファイルサイズ制限を上げればよいです。

全 routing に適用する書き方

app.use(express.bodyParser({
  limit: '1024mb'
}));

/upload に適用する書き方

app.use('/upload', express.bodyParser({
  limit: '1024mb'
}));

特定の URL だけ limit を変更する書き方

var defaultBodyParser = express.bodyParser({
  limit: "5mb"
});
var extendedBodyParser = express.bodyParser({
  limit: "1024mb"
});
 
app.use(function(req, res, next) {
  if (req.url === '/upload') {
    return defaultBodyParser(req, res, next);
  } else {
    return extendedBodyParser(req, res, next);
  }
});

以上です。


参考情報

[Node.js] Express でホスト名 (host name) を取得する

Express (Node.js) でホスト名を取得するには、

request.headers.host

HTTP リクエストヘッダーを見ればいい。

別に、Express に限ったことじゃないけど。


参考情報

node.js – Node JS get server hostname of current process – Stack Overflow