中年engineerの独り言 - crumbjp

LinuxとApacheの憂鬱

Mongo threads 日本語版

この記事はmongoのソースを読み解きたい人向けです。
それ以外の人には多分有意な情報は無いかと。。

また私自身がコードリーディングした時に書き散らしたメモを纏めただけなので
精確じゃなかったり諦めたりした部分もありますので、ご了承ください。

スタートアップ・シーケンス


スレッド

スレッド名はログファイルに現れている。

Tue Feb  5 19:15:33.544 [rsBackgroundSync] replSet syncing to: 192.168.159.134:27017

例えば、このログは[rsBackgroundSync]というスレッドが出力したという事。

main

メインスレッドは少し特殊で名前が変わる。

//github.com/mongodb/mongo/blob/r2.3.2/src/mongo/db/db.cpp#L740">(noname): 最初は名前が無い。
//github.com/mongodb/mongo/blob/r2.3.2/src/mongo/db/db.cpp#L568">initandlisten: イニシャル処理中の名前
//github.com/mongodb/mongo/blob/r2.3.2/src/mongo/util/net/listen.cpp#L208">conn(class MyMessageHandler): サービス開始後はソケットサーバスレッドに変わる
interruptThread

シグナルハンドラ専門スレッド

デーモンプロセスの定石

DataFileSync

定期的にデータファイルのmmapをmsync()する。

sleep
[--syncdelay]*1000 (60*1000 is default , 0 is never)

ちなみにsyncdelayオプションはオンラインで変更可能。

  admin db.runCommand({'setParameter':1,syncdelay:1})

しかし実際はわざわざmsync()しなくてもkernelが定期的にsyncしている。
しかもsyncdelayは秒指定で長すぎてほとんど意味がない。

多分このスレッドは設計ミスで全然効いてない(要らない)

journal

MongoDBではパフォーマンス確保の為、一定期間のアップデートを貯めて置きグループコミットする。

sleep
(journalCommitInterval/3) + 1
    • journalCommitIntervalオプション値の1/3の時間毎にuncommitted bytesのリミットをチェックする。

実は"--journalCommitInterval"の範囲が2から300である理由かも
2って半端だよな!って思ってたw

Implements:

  1. ジャーナルに書く(fsync)
  2. getlasterrorへ『書いたよ』通知
  3. データファイルへ書く(その内msync()される)
indexRebuilder : (since 2.4)

スタートアップ時の一時的なスレッド。

sleep
修復が終わったら死ぬのでsleepしない

前回終了がクラッシュで『作りかけのインデックス』がある場合に修復する。
("--noIndexBuildRetry"オプションには従う )

SnapshotThread

ロギングするだけ?

sleep
4000

このログ

   cpu: elapsed: 4000  writelock: 0%"

重要な事は何もしてない気がする・・・

clientcursormon

カーソルの数を数えたり、タイムアウトしたカーソルを回収したりする。

sleep
4000


カーソル数が100000を超えるとこんなログを出す

warning number of open cursors is very large: ??

回収した時のログ

  killing old cursor [id] [ns] idle: ??ms
timeout of cursor
600000 msec
PeriodicTask::Runner

1分毎タスクを走らせるフレームワーク

sleep
60000

mongos と mongo-client で使われてる模様。

  • Cleaner (writeback query cleaner)
  • DBConnectionPool (staled connection cleaner)

mongosでは動作確認したが(この辺)
mongodではシチュエーションが良く解らない。。。まあ動きは解ってるし放置しとく。

TTLMonitor : (since 2.2)

新機能。期限付きドキュメントを回収するスレッド

sleep
60000
   db.foo.ensureIndex({"status":1},{expireAfterSeconds:3600})
rsStart

実処理はこの辺 startThreads()

スタートアップ時の一時的なスレッド
単にレプリカに必要なスレッドを立てて回るだけ。

sleep
修復が終わったら死ぬのでsleepしない
rsHealthPoll

Mongod間の生死チェックメッセージ用のスレッド

sleep
2000

何か変わってたらrsMgrに投げるだけ

  1. Heartbeatリクエスト&レスポンス
  2. Send "update heartbeat" message to rsMgr
  3. Send "check new state" message to rsMgr
rsMgr on on task::Server

非同期メッセージングフレームワーク

sleep
by mutex cond wait

ラムダ式を直接キューイングする変わった仕組み。

実際はrsHealthPollからしか使われて無いんじゃないか?

rsSync

レプリカの肝処理だが、以下のスレッドと複雑に絡み合って動作する。

  • rsBackgroundSync
  • rsSyncNotifier
  • rsGhostSync

正直複雑すぎて良く解らない・・・

sleep
1000

まず、プライマリでは何もしない。(誰からも同期しない)

スレーブの場合

  1. 必要なら初期同期する。
  2. サービス中はループ
  3. 内部キューのOPQueueを読んで自分に書き込んでいく

in SyncTail::oplogApplication()

  1. replBatchLimitBytesやslaveDelayを考慮しつつOPQueueから必要な量を読む。
  2. Multi apply
  3. 処理したOplogを記録。(次回何処から処理するのか?)
  4. rsBackgroundSync と rsSyncNotifierへ通知
rsBackgroundSync

同期先(普通はPrimary)のOplogを読んでOPQueueに貯める

sleep
no wait
  1. 同期対象を決める。(普通はPrimary)
  2. Oplogを読む
  3. OPQueueに積む
rsSyncNotifier

役割が良く解らない・・・

sleep
no wait
  1. rsSyncからの通知を受け取るまで待機
  2. _oplogMarker.more()を叩く。

多分、rsBackgroundSyncのOplogと比べてるんだろう・・・
ただ実際はログを吐いているだけで、特に重要な処理はしてなさそう・・・にしては複雑すぎる仕組みなので何かありそう。

結局はoplogMarkerの役割は不明

rsGhostSync on task::Server

GhostSync用っぽい。こいつも良く解らない。

sleep
no wait

自分がスレーブの時、別のスレーブからOplogを読まれる場合(Ghost sync)にGhostSync::percolate()が呼ばれる。
percolate() では、循環同期を検出する為、自分の同期先が相手に向いていないか否かをチェックする。

ただ、循環同期を検出してもログを吐いているだけに見える。。

slaveTracking

スレーブ構成をウォッチするスレッド

sleep
1000
  1. スレーブ構成変更の通知を受ける
  2. local.slavesに書く
  3. 別スレッドに通知 (多分mongosだけの処理)

mongodではlocal.slavesに書くだけっぽい。

クライアント接続時

Threads

conn%d

Mongodはクライアント接続毎に専用スレッドを振る模様。
スレッド名はconn1 , conn2 , ... となっていく。

ASIOビルドの場合はその限りでは無いが、まだ正式サポートしてないので無視。