MongoDB Javaドライバーが条件付きで乱数ジェネレーターを使用するのはなぜですか?


211

このMongoDBのJava接続ドライバーのコミットで次のコードを見て、最初はある種の冗談のように見えます。次のコードは何をしますか?

if (!((_ok) ? true : (Math.random() > 0.1))) {
    return res;
}

(編集:この質問を投稿してからコードが更新されました


13
それのどの部分があなたを混乱させていますか?
オリバーチャールズワース2013年

4
わかりにくいと思います。このコードはcatchブロックで実行されます!
2013年

11
@MarkoTopolnik:そうですか?それは、より明確にif (!ok || Math.random() < 0.1)(または同様の何か)書くことができます。
オリバーチャールズワース2013年

5
github.com/mongodb/mongo-java-driver/commit/…最初ではありません。その行へのコメントを参照してください
msangel

3
@msangelこれらの人たちはコーディングスタイルではなくロジックを批判しているようです。
Marko Topolnik 2013年

回答:


279

その行の履歴を調べた後、私の主な結論は、いくつかの無能なプログラミングが機能しているということです。

  1. その行は不自然に複雑です。一般的な形

    a? true : b

    for boolean a, bは単純な

    a || b
  2. 周囲の否定と過度の括弧は、物事をさらに複雑にします。De Morganの法則を覚えておくと、このコードの断片が

    if (!_ok && Math.random() <= 0.1)
      return res;
  3. このロジック最初に導入したコミットには、

    if (_ok == true) {
      _logger.log( Level.WARNING , "Server seen down: " + _addr, e );
    } else if (Math.random() < 0.1) {
      _logger.log( Level.WARNING , "Server seen down: " + _addr );
    }

    無能の-another例は、コードが、気づく逆ロジック:ここでイベントが記録される場合のいずれか_ok、または他の症例の10%2のコードに対し、戻り時間とログ倍の90%の10%。したがって、後のコミットは、明快さだけでなく正しさ自体も台無しにしました。

    私はあなたが投稿したコードで、著者がオリジナルをif-then何らかの形で文字通り初期のreturn状態に必要な否定にどのように変換したかを実際に見ることができると思います。しかし、その後、彼はめちゃくちゃにして、不等号を反転させることにより、効果的な「二重否定」を挿入しました。

  4. コーディングスタイルの問題は別として、確率論的ロギングは、それ自体がかなり疑わしい手法です。特に、ログエントリには独自の固有の動作が記載されていないためです。その意図は、明らかに、同じ事実の言い直しを減らすことです:サーバーが現在ダウンしているということです。適切な解決策は、サーバーの状態の変化のみをログに記録することであり、その観察は記録せず、そのような観察を10%ランダムに選択することは言うまでもありません。はい、少し手間がかかるので、いくつか見てみましょう。

たった3行のコードを調べただけで蓄積されたこの無能さの証拠がすべてプロジェクト全体について公平に語らず、この作業ができるだけ早くクリーンアップされることを願うだけです。


26
さらに、これは、私の知る限りでは、MongoDBの公式の10gen Javaドライバーのようです。そのため、Javaドライバーについて意見を述べるだけでなく、MongoDBのコードについての意見も得られると思います
Chris Travers

5
ほんの数行のコードの優れた分析、それをインタビューの質問に変えるかもしれません!4番目のポイントは、このプロジェクトに根本的に何か問題がある理由です(その他は不幸なプログラマーのバグとして却下される可能性があります)。
アベル

1
@ChrisTraversそれはある Mongoのための公式のmongo Javaドライバ。
assylias 2013年

17

https://github.com/mongodb/mongo-java-driver/commit/d51b3648a8e1bf1a7b7886b7ceb343064c9e2225#commitcomment-3315694

gareth-reesによる11時間前:

おそらく、カウンターやタイマーを維持するためのコストをかけずに、サーバー障害の約1/10だけをログに記録する(したがって、大量のログを大量送信しないようにする)ことが考えられます。(しかし、タイマーを維持することは確かに手頃な価格でしょうか?)


13
nitpickするのではなく、1/10の時間で解像度が返されるため、他の9/10回のログが記録されます。
2013年

23
@Supericyそれは間違いなくつまらないことではありません。これは、この人のひどいコーディングプラクティスの証拠です。
Anorov 2013年

7

負の値1に初期化されたクラスメンバーを追加します。

  private int logit = -1;

tryブロックで、テストを行います。

 if( !ok && (logit = (logit + 1 ) % 10)  == 0 ) { //log error

これは常に最初のエラーをログに記録し、その後10回ごとにエラーをログに記録します。論理演算子は「ショートサーキット」であるため、logitは実際のエラーでのみ増加します。

接続に関係なく、すべてのエラーの最初と10番目が必要な場合は、メンバーではなく、logitクラスを静的にします。

すでに述べたように、これはスレッドセーフである必要があります。

private synchronized int getLogit() {
   return (logit = (logit + 1 ) % 10);
}

tryブロックで、テストを行います。

 if( !ok && getLogit() == 0 ) { //log error

注:エラーの90%を破棄することは良い考えではないと思います。


1

私はこのようなものを見たことがあります。

別の「ブラックボックス」コードの一部からの特定の「質問」に答えることができるコードの一部がありました。応答できない場合は、非常に遅い別の「ブラックボックス」コードに転送します。

そのため、以前は目に見えなかった新しい「質問」が表示され、100個が連続して表示されるようなバッチで表示される場合がありました。

プログラマーはプログラムがどのように機能しているかに満足していましたが、可能であれば新しい質問が発見された場合、将来的にソフトウェアを改善する何らかの方法を望んでいました。

したがって、解決策は未知の質問をログに記録することでしたが、結局のところ、数千の異なる質問がありました。ログが大きくなりすぎて、明確な答えがなかったため、これらを高速化するメリットはありませんでした。しかし、時々、答えられる可能性のある一連の質問が表示されます。

ログが大きくなりすぎて、ロギングがこのソリューションで得た本当に重要なもののロギングを妨げていたので、

ランダムに5%だけログに記録します。これによりログがクリーンアップされますが、長期的には追加できる質問/回答が表示されます。

したがって、不明なイベントが発生した場合、これらのケースのランダムな量で、それがログに記録されます。

これはあなたがここで見ているものに似ていると思います。

私はこの作業方法が気に入らなかったので、このコードを削除し、これらのメッセージを別のファイル記録しただけで、すべて存在していましたが、一般的なログファイルを壊していませんでした。


3
ここでデータベースドライバーについて話していることを除いて... IMO!
Steven Schlansker 2013

@StevenSchlansker私はこれが良い習慣だとは言っていません。このコードを削除し、これらのメッセージを別のファイルに記録しました。
Jens Timmerman 2013年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.