เหตุใดไดรเวอร์ MongoDB Java จึงใช้ตัวสร้างตัวเลขแบบสุ่มในเงื่อนไข


211

ฉันเห็นโค้ดต่อไปนี้ในคอมมิชชันนี้สำหรับไดรเวอร์ Java Connection ของ MongoDBและในตอนแรกมันจะเป็นเรื่องตลกสำหรับบางประเภท รหัสต่อไปนี้ทำอะไร?

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

(แก้ไข: รหัสได้รับการปรับปรุงตั้งแต่โพสต์คำถามนี้)


13
ส่วนไหนของคุณที่ทำให้คุณสับสน?
Oliver Charlesworth

4
ฉันคิดว่ามันสับสน รหัสนี้ถูกเรียกใช้งานในบล็อก catch!
Proviste

11
@MarkoTopolnik: มันคืออะไร? มันสามารถเขียนได้ชัดเจนยิ่งขึ้นเป็นif (!ok || Math.random() < 0.1)(หรือสิ่งที่คล้ายกัน)
Oliver Charlesworth

5
github.com/mongodb/mongo-java-driver/commit/ …คุณยังไม่ได้เป็นคนแรกดูความคิดเห็นในบรรทัดนั้น
msangel

3
@msangel พวกนั้นดูเหมือนจะวิจารณ์เหตุผลไม่ใช่สไตล์การเข้ารหัส
Marko Topolnik

คำตอบ:


279

หลังจากตรวจสอบประวัติของบรรทัดนั้นข้อสรุปหลักของฉันคือมีการเขียนโปรแกรมที่ไร้ความสามารถบางอย่างในที่ทำงาน

  1. บรรทัดนั้นซับซ้อนอย่างไม่มีเหตุผล รูปแบบทั่วไป

    a? true : b

    เพราะboolean a, bเทียบเท่ากับวิธีง่าย ๆ

    a || b
  2. การปฏิเสธโดยรอบและวงเล็บที่มากเกินไปทำให้เกิดสิ่งต่อไป โปรดจำไว้ว่ากฎหมายของเดอมอร์แกนเป็นการสังเกตเล็กน้อยที่รหัสนี้มีค่า

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

    - ตัวอย่างอื่น ๆ ของการเข้ารหัสที่ไร้ความสามารถ แต่สังเกตเห็นตรรกะย้อนกลับ : นี่คือเหตุการณ์ที่ถูกบันทึกไว้ถ้าอย่างใดอย่างหนึ่ง_okหรือใน 10% ของกรณีอื่น ๆ ในขณะที่รหัสใน 2กลับมา 10% ของเวลาและบันทึก 90% ของเวลา ดังนั้นการกระทำในเวลาต่อมาได้ทำลายไม่เพียง แต่ความชัดเจนเท่านั้น แต่ความถูกต้องนั้นเองด้วย

    ฉันคิดว่าในรหัสที่คุณโพสต์เราสามารถเห็นได้ว่าผู้เขียนตั้งใจที่จะเปลี่ยนต้นฉบับif-thenให้เป็นรูปแบบของการปฏิเสธอย่างแท้จริงในreturnสภาพเริ่มแรก แต่แล้วเขาก็ทำผิดพลาดและแทรก "การลบสองครั้ง" ที่มีประสิทธิภาพโดยการกลับเครื่องหมายความไม่เท่าเทียมกัน

  4. การเข้ารหัสปัญหาด้านสไตล์การบันทึก stochastic ค่อนข้างเป็นการปฏิบัติที่น่าสงสัยทั้งหมดด้วยตัวเองโดยเฉพาะอย่างยิ่งเนื่องจากรายการบันทึกไม่ได้บันทึกพฤติกรรมที่ผิดปกติของตัวเอง เห็นได้ชัดว่ามีความตั้งใจที่จะลดการคืนค่าในความเป็นจริงเดียวกัน: เซิร์ฟเวอร์อยู่ในขณะนี้ ทางออกที่เหมาะสมคือการบันทึกการเปลี่ยนแปลงของสถานะเซิร์ฟเวอร์เท่านั้นไม่ใช่การสังเกตแต่ละครั้งให้เลือกสุ่ม 10% จากการสังเกตดังกล่าว ใช่นั่นต้องใช้ความพยายามอีกเล็กน้อยดังนั้นเรามาดูกันดีกว่า

ฉันสามารถหวังได้ว่าหลักฐานทั้งหมดของการไร้ความสามารถนี้สะสมจากการตรวจสอบโค้ดเพียงสามบรรทัดไม่ได้พูดอย่างเป็นธรรมเกี่ยวกับโครงการโดยรวมและงานชิ้นนี้จะได้รับการทำความสะอาดโดยเร็ว


26
นอกจากนี้ดูเหมือนว่าจะเป็นเท่าที่ฉันสามารถบอกได้ว่าไดรเวอร์ 10gen Java อย่างเป็นทางการสำหรับ MongoDB ดังนั้นนอกเหนือจากการมีความคิดเห็นเกี่ยวกับไดรเวอร์ Java ฉันคิดว่ามันให้ความเห็นเกี่ยวกับรหัสของ MongoDB
Chris Travers

5
การวิเคราะห์ที่ยอดเยี่ยมของรหัสเพียงไม่กี่บรรทัดฉันอาจเปลี่ยนเป็นคำถามสัมภาษณ์! ประเด็นที่สี่ของคุณคือกุญแจสำคัญที่แท้จริงว่าทำไมมีบางสิ่งผิดปกติกับโครงการนี้
Abel

1
@ChrisTravers มันเป็นไดรเวอร์ java mongo อย่างเป็นทางการสำหรับ mongo
assylias

17

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

11 ชั่วโมงที่แล้วโดย gareth-rees:

ความคิดที่น่าจะเป็นไปได้คือการบันทึกเพียงประมาณ 1/10 ของเซิร์ฟเวอร์ที่ล้มเหลว (และเพื่อหลีกเลี่ยงการบันทึกไฟล์ขยะอย่างหนาแน่น) โดยไม่เกิดค่าใช้จ่ายในการบำรุงรักษาเคาน์เตอร์หรือตัวจับเวลา (แต่แน่นอนว่าการบำรุงรักษาตัวจับเวลาจะเหมาะสมหรือไม่)


13
ไม่ใช่เพื่อ nitpick แต่: 1 / 10th ของเวลามันจะกลับ res ดังนั้นมันจะเข้าสู่ระบบอีก 9/10 ครั้ง
Supericy

23
@ ยอดเยี่ยมนั่นไม่ใช่ nitpicking แน่นอน นั่นเป็นเพียงหลักฐานเพิ่มเติมเกี่ยวกับวิธีการเข้ารหัสที่แย่มากของคนนี้
Anorov

7

เพิ่มสมาชิกคลาสที่เริ่มต้นเป็นลบ 1:

  private int logit = -1;

ในบล็อกลองทำการทดสอบ:

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

ซึ่งจะบันทึกข้อผิดพลาดแรกเสมอจากนั้นทุกข้อผิดพลาดที่ตามมาสิบ ตัวดำเนินการเชิงตรรกะ "ลัดวงจร" ดังนั้น logit จะได้รับเพิ่มขึ้นเมื่อเกิดข้อผิดพลาดจริง

ถ้าคุณต้องการที่หนึ่งและที่สิบของทั้งหมดข้อผิดพลาดโดยไม่คำนึงถึงการเชื่อมต่อให้ทำการ logit class static แทนสมาชิก aa

ตามที่ได้รับการตั้งข้อสังเกตนี้ควรปลอดภัยกระทู้:

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

ในบล็อกลองทำการทดสอบ:

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

หมายเหตุ: ฉันไม่คิดว่าการทิ้ง 90% ของข้อผิดพลาดเป็นความคิดที่ดี


1

ฉันเคยเห็นสิ่งแบบนี้มาก่อน

มีรหัสบางส่วนที่สามารถตอบ 'คำถาม' บางอย่างที่มาจากอีกส่วนหนึ่งของรหัส 'กล่องดำ' ในกรณีที่ไม่สามารถตอบคำถามได้มันจะส่งต่อไปยังโค้ด 'กล่องดำ' อีกชิ้นที่ช้ามาก

ดังนั้นบางครั้งก่อนหน้านี้ 'คำถาม' ที่มองไม่เห็นจะปรากฏขึ้นและพวกเขาจะแสดงเป็นชุดเช่นเดียวกับ 100 คำถามในแถว

โปรแกรมเมอร์มีความสุขกับการทำงานของโปรแกรม แต่เขาต้องการวิธีปรับปรุงซอฟต์แวร์ในอนาคตหากมีคำถามใหม่ที่อาจถูกค้นพบ

ดังนั้นทางออกคือการบันทึกคำถามที่ไม่รู้จัก แต่เมื่อมันปรากฏออกมามี 1,000 ของคำถามที่แตกต่างกัน บันทึกมีขนาดใหญ่เกินไปและไม่มีประโยชน์ในการเร่งให้ดีขึ้นเนื่องจากไม่มีคำตอบที่ชัดเจน แต่ในบางครั้งคำถามจะปรากฏขึ้นซึ่งสามารถตอบได้

เนื่องจากบันทึกมีขนาดใหญ่เกินไปและการบันทึกได้รับในทางของการบันทึกสิ่งสำคัญที่แท้จริงที่เขาได้รับจากวิธีแก้ไขปัญหานี้:

บันทึกเพียงสุ่ม 5% นี้จะล้างบันทึกในขณะที่ในระยะยาวยังคงแสดงคำถาม / คำตอบที่สามารถเพิ่ม

ดังนั้นหากมีเหตุการณ์ที่ไม่รู้จักเกิดขึ้นในกรณีแบบสุ่มจำนวนนี้จะถูกบันทึกไว้

ฉันคิดว่านี่คล้ายกับสิ่งที่คุณเห็นที่นี่

ฉันไม่ชอบวิธีการทำงานนี้ดังนั้นฉันจึงลบรหัสชิ้นนี้และเพิ่งบันทึกข้อความเหล่านี้ไปยังไฟล์อื่นดังนั้นจึงมีอยู่ทั้งหมด แต่ไม่ได้บล็อกไฟล์บันทึกทั่วไป


3
ยกเว้นว่าเรากำลังพูดถึงไดรเวอร์ฐานข้อมูลที่นี่ ... พื้นที่ปัญหาผิด IMO!
Steven Schlansker

@StevenSchlansker ฉันไม่เคยบอกว่านี่เป็นวิธีปฏิบัติที่ดี ฉันลบรหัสชิ้นส่วนนี้และบันทึกข้อความเหล่านี้ไปยังไฟล์อื่น
Jens Timmerman
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.