Perché il driver Java MongoDB utilizza un generatore di numeri casuali in un condizionale?


211

Ho visto il seguente codice in questo commit per il driver Java Connection di MongoDB , e all'inizio sembra essere uno scherzo di qualche tipo. Cosa fa il seguente codice?

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

(EDIT: il codice è stato aggiornato da quando ho pubblicato questa domanda)


13
Quale parte ti confonde?
Oliver Charlesworth,

4
penso che sia confuso. questo codice viene eseguito in un blocco catch!
Proviste

11
@MarkoTopolnik: vero? Potrebbe essere scritto molto più chiaramente come if (!ok || Math.random() < 0.1)(o qualcosa di simile).
Oliver Charlesworth,

5
github.com/mongodb/mongo-java-driver/commit/… non sei il primo, vedi commento a quella riga
msangel,

3
@msangel Quei ragazzi sembrano criticare la logica, non lo stile di programmazione.
Marko Topolnik,

Risposte:


279

Dopo aver esaminato la storia di quella linea, la mia conclusione principale è che c'è stata una programmazione incompetente al lavoro.

  1. Quella linea è contorta gratuitamente. La forma generale

    a? true : b

    perché boolean a, bequivale al semplice

    a || b
  2. La negazione circostante e le parentesi eccessive contorcono ulteriormente le cose. Tenendo a mente le leggi di De Morgan è una banale osservazione che questo pezzo di codice equivale a

    if (!_ok && Math.random() <= 0.1)
      return res;
    
  3. L'impegno che aveva originariamente introdotto questa logica aveva

    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 altro esempio di codifica incompetente, ma si noti la logica inversa : qui l'evento viene registrato se uno _oko nel 10% degli altri casi, mentre il codice in 2. restituisce il 10% delle volte e registra il 90% delle volte. Quindi l'impegno successivo ha rovinato non solo la chiarezza, ma la correttezza stessa.

    Penso che nel codice che hai pubblicato possiamo effettivamente vedere come l'autore intendesse trasformare l'originale in if-thenqualche modo letteralmente nella sua negazione richiesta per la returncondizione precoce . Ma poi ha sbagliato e ha inserito un "doppio negativo" efficace invertendo il segno di disuguaglianza.

  4. A parte i problemi di stile del codice, la registrazione stocastica è una pratica piuttosto dubbia da sola, soprattutto perché la voce di registro non documenta il suo comportamento peculiare. L'intenzione, ovviamente, riduce le riaffermazioni dello stesso fatto: che il server è attualmente inattivo. La soluzione appropriata è quella di registrare solo i cambiamenti dello stato del server, e non ciascuno delle sue osservazioni, e tanto meno una selezione casuale del 10% di tali osservazioni. Sì, ci vuole solo un po 'più di sforzo, quindi vediamo alcuni.

Posso solo sperare che tutta questa prova di incompetenza, accumulata dall'ispezione di sole tre righe di codice , non parli in modo equo del progetto nel suo insieme e che questo lavoro venga ripulito al più presto.


26
Inoltre questo sembra essere, per quanto posso dire, il driver Java 10gen ufficiale per MongoDB, quindi oltre ad avere un'opinione sul driver Java, penso che mi dia un'opinione sul codice di MongoDB
Chris Travers

5
Eccellente analisi di poche righe di codice, potrei semplicemente trasformarlo in una domanda di intervista! Il tuo quarto punto è la vera chiave per cui c'è qualcosa di fondamentalmente sbagliato in questo progetto (gli altri potrebbero essere liquidati come bug del programmatore sfortunato).
Abel,

1
@ChrisTravers E è il pilota ufficiale mongo java per Mongo.
assylias,

17

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

11 ore fa di gareth-rees:

Presumibilmente l'idea è quella di registrare solo circa 1/10 degli errori del server (e quindi evitare in modo massiccio lo spamming del registro), senza sostenere i costi di gestione di un contatore o di un timer. (Ma sicuramente mantenere un timer sarebbe conveniente?)


13
Non per nitpick ma: 1/10 delle volte restituirà res, quindi registrerà le altre 9/10 volte.
Supericy,

23
@Supericy Questo sicuramente non è nitpicking. Questa è solo un'ulteriore prova delle terribili pratiche di codifica di questa persona.
Anorov

7

Aggiungi un membro della classe inizializzato al negativo 1:

  private int logit = -1;

Nel blocco try, esegui il test:

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

Ciò registra sempre il primo errore, quindi ogni decimo errore successivo. Gli operatori logici "cortocircuiscono", pertanto il logit viene incrementato solo in caso di errore reale.

Se si desidera il primo e il decimo di tutti gli errori, indipendentemente dalla connessione, rendere la classe logit statica anziché un membro.

Come notato, questo dovrebbe essere sicuro per i thread:

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

Nel blocco try, esegui il test:

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

Nota: non credo che eliminare il 90% degli errori sia una buona idea.


1

Ho visto questo genere di cose prima.

C'era un pezzo di codice che poteva rispondere ad alcune "domande" che provenivano da un altro pezzo di codice "scatola nera". Nel caso in cui non potesse rispondere, li avrebbe inoltrati a un altro pezzo di codice "scatola nera" che era molto lento.

Quindi a volte si presentavano nuove "domande" mai viste prima e si presentavano in una serie, come 100 di loro di fila.

Il programmatore era contento di come funzionava il programma, ma voleva un modo per forse migliorare il software in futuro, se possibile venivano scoperte nuove domande.

Quindi, la soluzione era quella di registrare domande sconosciute, ma alla fine c'erano migliaia di domande diverse. I registri sono diventati troppo grandi e non vi è stato alcun vantaggio nel velocizzarli, poiché non avevano risposte ovvie. Ma ogni tanto, una serie di domande venivano visualizzate a cui era possibile rispondere.

Dato che i registri stavano diventando troppo grandi e la registrazione stava ostacolando la registrazione delle cose veramente importanti, è arrivato a questa soluzione:

Registra solo un 5% casuale, questo pulirà i registri, mentre a lungo termine continuerà a mostrare quali domande / risposte potrebbero essere aggiunte.

Quindi, se si verificasse un evento sconosciuto, in un numero casuale di questi casi, verrebbe registrato.

Penso che questo sia simile a quello che stai vedendo qui.

Non mi piaceva questo modo di lavorare, quindi ho rimosso questo pezzo di codice e ho appena registrato questi messaggi in un file diverso , quindi erano tutti presenti, ma non ostruivano il file di registro generale.


3
Solo che stiamo parlando di un driver di database qui ... spazio di problema sbagliato, IMO!
Steven Schlansker,

@StevenSchlansker Non ho mai detto che fosse una buona pratica. Ho rimosso questo pezzo di codice e ho appena registrato questi messaggi in un altro file.
Jens Timmerman,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.