Pourquoi le pilote Java MongoDB utilise-t-il un générateur de nombres aléatoires au conditionnel?

211

J'ai vu le code suivant dans cette validation pour le pilote de connexion Java de MongoDB , et il semble d'abord être une blague en quelque sorte. Que fait le code suivant?

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

(EDIT: le code a été mis à jour depuis la publication de cette question)

Monstieur
la source
13
Quelle partie de cela vous déroute?
Oliver Charlesworth
4
je pense que c'est déroutant. ce code est exécuté dans un bloc catch!
Proviste
11
@MarkoTopolnik: C'est vrai? Il pourrait être écrit beaucoup plus clairement if (!ok || Math.random() < 0.1)(ou quelque chose de similaire).
Oliver Charlesworth
5
github.com/mongodb/mongo-java-driver/commit/… vous n'êtes pas le premier, voir le commentaire de cette ligne
msangel
3
@msangel Ces gars-là semblent critiquer la logique, pas le style de codage.
Marko Topolnik

Réponses:

279

Après avoir inspecté l'historique de cette ligne, ma principale conclusion est qu'il y a eu une programmation incompétente au travail.

  1. Cette ligne est alambiquée gratuitement. La forme générale

    a? true : b

    car boolean a, best équivalent au simple

    a || b
  2. La négation environnante et les parenthèses excessives compliquent encore les choses. En gardant à l'esprit les lois de De Morgan, c'est une observation banale que ce morceau de code équivaut à

    if (!_ok && Math.random() <= 0.1)
      return res;
  3. Le commit qui a initialement introduit cette logique avait

    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 );
    }

    —Un autre exemple de codage incompétent, mais notez la logique inversée : ici l'événement est enregistré si soit _okou dans 10% des autres cas, alors que le code en 2. renvoie 10% des fois et enregistre 90% des fois. Ainsi, le dernier engagement a ruiné non seulement la clarté, mais l'exactitude elle-même.

    Je pense que dans le code que vous avez publié, nous pouvons réellement voir comment l'auteur avait l'intention de transformer l'original en if-thenquelque sorte littéralement en sa négation requise pour la première returncondition. Mais ensuite il a gâché et inséré un "double négatif" efficace en inversant le signe de l'inégalité.

  4. Mis à part les problèmes de style de codage, la journalisation stochastique est une pratique assez douteuse en soi, d'autant plus que l'entrée de journal ne documente pas son propre comportement particulier. L'intention est évidemment de réduire les retraitements du même fait: que le serveur est actuellement en panne. La solution appropriée consiste à consigner uniquement les modifications de l'état du serveur, et non chacune son observation, sans parler d'une sélection aléatoire de 10% de ces observations. Oui, cela demande un peu plus d'efforts, alors voyons-en.

Je ne peux qu'espérer que toutes ces preuves d'incompétence, accumulées en inspectant seulement trois lignes de code , ne parlent pas du projet dans son ensemble et que ce travail sera nettoyé dès que possible.

Marko Topolnik
la source
26
De plus, cela semble être, pour autant que je sache, le pilote Java 10gen officiel pour MongoDB, donc en plus d'avoir une opinion sur le pilote Java, je pense que cela me donne une opinion sur le code de MongoDB
Chris Travers
5
Excellente analyse de quelques lignes de code, je pourrais simplement en faire une question d'entrevue! Votre quatrième point est la vraie clé pour laquelle il y a quelque chose de fondamentalement mal avec ce projet (les autres pourraient être rejetés comme des bugs de programmeur malheureux).
Abel
1
@ChrisTravers Il est le pilote java officiel mongo pour mongo.
assylias
17

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

Il y a 11 heures par gareth-rees:

L'idée est probablement de ne consigner qu'environ 1/10 des défaillances du serveur (et donc d'éviter de spammer massivement le journal), sans encourir le coût de maintenance d'un compteur ou d'une minuterie. (Mais le maintien d'une minuterie serait sûrement abordable?)

msangel
la source
13
Pas au nitpick mais: 1 / 10ème du temps il reviendra res, donc il enregistrera les 9/10 autres fois.
Supericy
23
@Supericy Ce n'est certainement pas tatillon. C'est juste une preuve de plus des terribles pratiques de codage de cette personne.
Anorov
7

Ajouter un membre de classe initialisé à négatif 1:

  private int logit = -1;

Dans le bloc try, faites le test:

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

Cela enregistre toujours la première erreur, puis une erreur sur dix. Les opérateurs logiques "court-circuitent", donc logit n'est incrémenté qu'en cas d'erreur réelle.

Si vous voulez le premier et le dixième de tous erreurs, quelle que soit la connexion, rendez la classe logit statique au lieu d'un membre.

Comme cela avait été noté, cela devrait être thread-safe:

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

Dans le bloc try, faites le test:

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

Remarque: Je ne pense pas que jeter 90% des erreurs soit une bonne idée.

tpdi
la source
1

J'ai déjà vu ce genre de choses auparavant.

Il y avait un morceau de code qui pouvait répondre à certaines «questions» qui provenaient d'un autre morceau de code «boîte noire». Dans le cas où il ne pourrait pas y répondre, il les transmettrait à un autre morceau de code «boîte noire» qui était vraiment lent.

Ainsi, parfois, de nouvelles «questions» inédites apparaissaient et apparaissaient en lot, comme 100 d'entre elles d'affilée.

Le programmeur était satisfait du fonctionnement du programme, mais il voulait peut-être un moyen d'améliorer le logiciel à l'avenir, si de nouvelles questions étaient découvertes.

Donc, la solution était de consigner des questions inconnues, mais il s'est avéré qu'il y avait des milliers de questions différentes. Les journaux sont devenus trop gros et il n'y avait aucun avantage à les accélérer, car ils n'avaient pas de réponses évidentes. Mais de temps en temps, un lot de questions apparaissait et pouvait être répondu.

Étant donné que les journaux devenaient trop volumineux et que la journalisation gênait la journalisation des choses vraiment importantes qu'il avait apportées à cette solution:

N'enregistrez que 5% au hasard, cela nettoiera les journaux, tout en montrant à long terme quelles questions / réponses pourraient être ajoutées.

Donc, si un événement inconnu se produisait, dans un nombre aléatoire de ces cas, il serait enregistré.

Je pense que cela ressemble à ce que vous voyez ici.

Je n'aimais pas cette façon de travailler, j'ai donc supprimé ce morceau de code et j'ai simplement consigné ces messages dans un fichier différent , ils étaient donc tous présents, mais sans encombrer le fichier journal général.

Jens Timmerman
la source
3
Sauf que nous parlons ici d'un pilote de base de données ... mauvais espace de problème, OMI!
Steven Schlansker
@StevenSchlansker Je n'ai jamais dit que c'était une bonne pratique. J'ai supprimé ce morceau de code et j'ai simplement enregistré ces messages dans un fichier différent.
Jens Timmerman