Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

Parallel Testing ใน Ruby on Rails: Risks & Mitigation Strategies

เคยได้ยินใครร้องเรียนว่า Tests ของพวกเราเร็วเกินไป? ผมไม่เคย

Fast Tests หมายถึง Fast Feedback ไม่ว่าคุณ Run Locally หรือใน CI Pipeline คุณ React Failures และ Improve Code ได้เร็วขึ้น นอกจาก Productivity Gains แล้ว Well-known ว่า Slow Tests ทำให้ Developers Grumpy ไม่มีใครชอบ Developers Grumpy

ด้วยที่กล่าวมา สร้าง Lightning-fast Test Suite ไม่ง่ายเท่าที่คาด Luckily Rails 6 Introduce Feature ชื่อ Parallel Testing เริ่มต้นง่ายมาก และ Speed Up Tests มาก แต่มี Pitfalls ที่ต้องระวัง

Parallel Testing คืออะไร?

Parallel Testing หมายถึงอะไร?

ตอน Run Test Suite Test Runner ปกติ Spawn Single Process Execute Tests ทีละอัน — หรือ Serially Single Test Process ใช้ Single CPU Core ตามที่คุณจินตนาการ Approach นี้ไม่ Take Full Advantage ของ Modern Hardware ที่มักมี Dozens CPU Cores

คุณอาจมี MacBook 10 Cores แตเศร้าที่มันไม่ทำให้ Tests เร็วขึ้น!

เราเปลี่ยนได้โดย Distribute Individual Tests ไป Multiple Worker Processes Tests ไม่ Run After Each Other แต Run Next to Each Other — In Parallel Run Test Suite บน 2 Workers เร็วกว่า Single Worker 2 เท่า

Cores มากเท่าไหร่ Worker Processes Feasible มากเท่านั้น Test Suite จบเร็วขึ้น สมมติ Tests ปกติ 8 นาที ใช้ 4 Workers จะเหลือ ~2 นาที!

จินตนาการบน Machine 16 Cores Spawn 16 Workers Very Nice!

Configuring Parallel Testing ใน Rails

ทำยังไง? จนก่อนหน้านี้ Third-party Gems Parallelize Test Suite แตตั้งแต่ Rails 6 Parallel Tests มา Standard ง่ายแค่เพิ่ม parallelize ใน Tests:


ด้วย Config นี้ Rails Auto Spawn Worker Processes ตาม Processors ใน Machine Rails ยังสร้าง Namespaced Databases (เช่น database-test-0, database-test-1, etc.) สำหรับ Run Tests

แค่นี้ก็เริ่มได้! มี Additional Config Options หากต้องการ

บางครั้งต้อง Specific Setup/Teardown สำหรับ Parallel Tests Rails Provide 2 Hooks — parallelize_setup และ parallelize_teardown เรียกก่อน/หลัง Worker Processes Spawn:


คุณตั้ง Number of Workers Manual ได้:


หรือใช้ Environment Variable PARALLEL_WORKERS Override Existing Config:


มี Option ใช้ Threads แทน Workers Parallelize Test Suite


with: :threads เป็น Default Option ตอนใช้ JRuby หรือ TruffleRuby Threads ทฤษฎีให้ Performance 好กว่า Process เนื่องจาก Overhead น้อยกว่า ใน Practice ผมไม่คิด Threads 有用 มาก แค่ Stick กับ Process-based Parallelization ส่วนใหญ่

Beware the Pitfalls (ระวัง Pitfalls)

เพิ่ม parallelize ใน Existing Tests ได้ Incredible Speedup? ง่ายขนาดนี้!

ถ้าคุณโชคดี จริงๆ แต่มักจะ Hit Unexpected Snags ตอนเพิ่ม Parallelization เข้า Existing Test Suite กรณีผมเป็นเช่นนั้น!

ชี้แจงก่อน: ถ้าใช้ RSpec แทน Minitest คุณ Out of Luck RSpec ไม่ Support Rails 6 Built-in Parallel Testing มี Ongoing Discussion แต่ไม่มี Progress มานาน หากอยาก Parallel Tests กับ RSpec Best Bet ยังคือ Third-party Gems เช่น grosser/parallel_tests

Unexpected Issue อีกอย่าง Run Small Number ของ Tests ใน Parallel จบ Slower แล้ว Serially Setup Parallel Tests มี Overhead มาก — สร้าง Multiple Databases — Eliminate Gains จาก Parallelization

คุณอาจ Disable Parallel Tests สำหรับ Small Number Tests ด้วย PARALLEL_WORKERS Environment Variable:


Rails 7 Addressed This โดย Enable Parallel Execution เฉพาะตอน Execute Many Tests Default Parallelization Threshold คือ 50 แต่ Override ได้:


ปัญหาสุดท้ายที่อยาก Highlight คือสิ่งที่ Most Likely Face และ Insidious/Hardest มาก คุณอาจเห็น Random Failures ตอน Enable Parallel Tests เพื่อเข้าใจ Parallelization Cause อะไร มาดู Simple Test Case:


นอกจาก Useless เล็กน้อย Test นี้ Fine Pass 100% ตราบใด Run Serially แต่ละ Test Isolated Execute Random Order ไม่ Cause Fail เปลี่ยนตอนเพิ่ม Multiple Processes/Threads

ตอน Run Tests ใน Parallel Execution Order ของ Individual Statements ใน Tests อาจเปลี่ยนจาก CPU Scheduling ดูตัวอย่าง Execution Orders แบบนี้:


เพราะ Worker 2 Delete File ก่อน Worker 1 Assertion Executed Test แรกจะ Fail — Sometimes To Add Insult to Injury Execution Order อื่นทำ Test ที่ 2 Fail และ Test แรก Pass

Simple Example นี้ Illustrate Issue ขยายไม่แค่ Files แต่ Singleton Resource ใดๆ ที่ Tests Access แบบ Non-thread-safe สมมติเขียน Redis หรือ Elasticsearch Index จะ Face Complications คล้ายกัน แย่กว่านั้น อาจใช้เวลานาน uncover Tests ที่ Cause Random Failures — และเวลามากกว่า Fix ทั้งหมด

No Silver Bullet สำหรับ Flaky Parallel Tests โดยทั่วไปต้อง Ensure Multiple Test Processes ไม่ Share Resources สำหรับ Files ใช้ Tempfiles ใช้ parallelize_setup สร้าง Namespaced Resources (เช่น Redis Databases) และไปเรื่อยๆ

Adding Parallel Testing เข้า Existing Rails Tests

สมมติคุณ Struggle กับ Random Test Failures เนื่องจาก Parallelization และไม่มีเวลา Fix แต่ยังอยาก Reap Benefits ของ Parallel Testing คุณอาจ Enable แค่ Subset ของ Tests

แค่ Tests ที่ Call parallelize จะ Parallelized และด้วย Concerns หรือ Parent Classes คุณเพิ่ม Parallel Testing เข้า Test Suite ทีละ Test Class ได้ สร้าง Module แบบนี้:


Test Class ใด Include Module นี้ จะ Run ใน Parallel:


หรือสร้าง Test Class ใหม่ ParallelTest:


แล้ว Inherit จากนั้นสำหรับ Tests ที่ควร Run Parallel — และเว้นไว้สำหรับที่ Problematic

Parallel Testing as a Bandaid

Parallel Testing ให้ Impressive Speed Gains ด้วย Effort น้อย อย่าหลอกลวง: มันไม่ Substitute สำหรับ Approaches อื่น Improve Test Suite Speed แตเป็น Addition

ถ้า Test Suite Slow และ Spare Effort ใช้เวลา Profile และ Address Root Cause ของ Slowness Slow Test Suite + Parallel Testing จะเร็วขึ้น แต่ไม่เร็วเท่า Fast Test Suite ที่ Run Parallel!

สรุป

ในบทความนี้ เราดู Parallel Testing คืออะไร Setup และ Config ยังไง หากต้องการ Way ทำ Tests เร็วขึ้น Parallel Testing ให้ Just That

คุณอาจ Face Obstacles ตอนเพิ่ม Parallel Testing อย่าประหลาดใจ Tests ที่ Run Stable หลายปีทีแดงเริ่ม Fail คุณ Work Around ได้โดย Parallelize Only Subset ของ Test Suite

ไม่ว่า Approach ไหน Parallel Testing เป็น Fantastic Tool Speed Up Tests!

Happy Coding!

P.S. หากต้องการอ่านบทความ Ruby Magic ทันทีที่เผยแพร่ สมัคร Newsletter Ruby Magic ของเราและจะไม่พลาดบทความเด็ดๆ ไป!

Parallel Testing ใน Ruby on Rails: Risks & Mitigation Strategies

Hans-Jörg Schnedlitz

ผู้เขียน אורח Hans เป็น Rails Engineer จาก Vienna, Austria เขาใช้เวลาส่วนใหญ่ Coding หรืออ่านเกี่ยว Coding และบางครั้งเขียนเกี่ยวกับมันบนบล็อก! ตอนไม่ได้นั่งหน้าจอ คุณคงหาเขาได้ข้างนอก ปีนเขาบางลูก

บทความทั้งหมดโดย Hans-Jörg Schnedlitz