Computer >> 컴퓨터 >  >> 프로그래밍 >> Ruby

Ruby on Rails의 병렬 테스트:위험 및 완화 전략

누군가 테스트가 너무 빠르다고 불평하는 것을 들어본 적이 있습니까? 나도 마찬가지야.

빠른 테스트는 빠른 피드백을 의미합니다. 로컬에서 실행하든 지속적 통합 파이프라인에서 실행하든 관계없이 테스트가 일찍 완료될수록 더 일찍 실패에 대응하고 코드를 개선할 수 있습니다. 생산성 향상 외에도 느린 테스트가 개발자를 짜증나게 한다는 것은 잘 알려져 있습니다. 개발자가 심술궂은 것을 좋아하는 사람은 없습니다.

그렇긴 하지만, 빛처럼 빠른 테스트 스위트를 만드는 것이 항상 원하는 만큼 쉬운 것은 아닙니다. 다행히 Rails 6에는 병렬 테스트라는 흥미로운 기능이 도입되었습니다. . 시작하기가 쉽고 테스트 속도를 크게 높일 수 있습니다. 그러나 주의해야 할 몇 가지 함정이 있습니다.

병렬 테스팅이란 무엇인가요?

병렬 테스트는 무엇을 의미하나요?

테스트 스위트를 실행할 때 테스트 실행기는 일반적으로 테스트를 차례로 또는 직렬 실행하기 위해 단일 프로세스를 생성합니다. . 단일 테스트 프로세스에서는 단일 CPU 코어를 사용합니다. 아마도 상상할 수 있듯이 이 접근 방식은 종종 수십 개의 CPU 코어를 사용하는 최신 하드웨어를 최대한 활용하지 않습니다.

CPU 코어가 10개 있는 멋진 MacBook을 갖고 있을 수도 있지만 안타깝게도 그렇다고 해서 테스트가 더 빨라지는 것은 아닙니다!

개별 테스트를 여러 작업자 프로세스에 배포하여 이를 변경할 수 있습니다. 테스트는 더 이상 순차적으로 실행되지 않고 나란히 — 병렬로 실행됩니다. . 두 작업자에서 테스트 모음을 실행하는 것은 단일 작업자에서 동일한 테스트 모음을 실행하는 것보다 두 배 빠릅니다.

머신에 코어가 많을수록 더 많은 작업자 프로세스가 가능하므로 테스트 스위트가 더 빨리 완료됩니다. 테스트를 실행하는 데 일반적으로 8분이 걸린다고 가정해 보겠습니다. 4개의 작업자 프로세스를 사용하여 동일한 테스트를 실행하면 런타임이 약 2분으로 단축됩니다!

16명의 작업자를 생성할 수 있는 멋지고 두꺼운 16개 코어 머신에서 동일한 작업을 수행한다고 상상해 보세요. 정말 좋아요!

Rails에서 병렬 테스트 구성

그러면 우리는 거기에 어떻게 갑니까? 최근까지 타사 gem을 사용하여 테스트 스위트를 병렬화할 수 있었지만 Rails 6부터 병렬 테스트가 표준으로 제공됩니다. parallelize만 추가하면 됩니다. 테스트를 위해:

 

이 구성을 사용하면 Rails는 시스템의 프로세서 수에 따라 작업자 프로세스를 자동으로 생성합니다. Rails는 또한 네임스페이스화된 데이터베이스(예:database-test-0)를 생성합니다. , database-test-1 등)에 대해 테스트를 실행합니다.

시작하는 데 필요한 전부입니다! 물론 필요한 경우 몇 가지 추가 구성 옵션도 있습니다.

때로는 병렬 테스트를 위해 특정 설정이나 정리를 수행해야 할 수도 있습니다. Rails는 여러분이 사용할 수 있는 두 가지 후크를 제공합니다 — parallelize_setupparallelize_teardown . 이는 새로운 작업자 프로세스가 생성되기 전후에 호출됩니다:

 

작업자 수를 수동으로 설정할 수도 있습니다:

 

또는 PARALLEL_WORKERS을 사용하세요. 기존 구성을 재정의하는 환경 변수:

 

작업자 대신 스레드를 사용하여 테스트 모음을 병렬화하는 옵션도 있습니다.

 

with: :threads JRuby 또는 TruffleRuby를 사용할 때 기본 옵션입니다. 이론적으로 스레드를 사용하면 성능이 약간 향상됩니다. 결국 스레드는 프로세스보다 더 적은 오버헤드를 필요로 합니다. 그러나 실제로는 스레드를 사용하는 것이 그다지 유용하다고 생각하지 않으며 대부분의 경우 프로세스 기반 병렬화를 고수하는 것이 좋습니다.

함정을 조심하세요

따라서 parallelize를 추가하기만 하면 됩니다. 놀라운 속도 향상을 경험하려면 기존 테스트에 추가해야 합니까? 참 쉽죠!

운이 좋다면 그것은 정말 사실이다. 기존 테스트 스위트에 병렬화를 처음 추가할 때 예상치 못한 문제에 부딪힐 가능성이 더 높습니다. 저는 확실히 그랬습니다!

방해가되지 않는 한 가지를 살펴 보겠습니다. Minitest 대신 RSpec을 사용하면 운이 좋지 않습니다. RSpec은 Rails 6 내장 병렬 테스트를 지원하지 않습니다. 이를 변경하는 것에 대한 논의가 진행 중이지만 한동안 큰 진전이 없었습니다. RSpec을 사용한 병렬 테스트를 원한다면 여전히 Grosser/parallel_tests와 같은 타사 gem을 사용하는 것이 가장 좋습니다.

직면할 수 있는 또 다른 예상치 못한 문제는 소수의 테스트를 병렬로 실행하면 결국 느려진다는 것입니다. 그런 다음 직렬로 실행합니다. 병렬 테스트를 설정하면 다중 데이터베이스 생성과 같은 상당한 오버헤드가 발생하므로 병렬화로 얻을 수 있는 이점이 없어질 수 있습니다.

적은 수의 테스트에 대해서는 병렬 테스트를 비활성화하는 것이 더 나을 수도 있습니다. PARALLEL_WORKERS를 사용하여 그렇게 할 수 있습니다. 환경 변수:

 

Rails 7에서는 많은 테스트를 실행할 때만 병렬 실행을 활성화하여 이 문제를 해결했습니다. 따라서 이미 업그레이드했다면 이 문제가 발생하지 않습니다. 기본적으로 병렬화 임계값은 50으로 설정되어 있지만 다음과 같이 재정의할 수 있습니다.

 

마지막으로 강조하고 싶은 문제는 여러분이 직면할 가능성이 가장 높으며 가장 교활하고 다루기 어려운 문제입니다. 테스트 도구 모음에 대해 병렬 테스트를 활성화하면 무작위 오류가 나타날 수 있습니다. 병렬화로 인해 이러한 문제가 발생하는 방식을 이해하기 위해 간단한 테스트 사례를 살펴보겠습니다.

 

약간 쓸모가 없다는 것 외에도 이 테스트는 완벽하게 괜찮습니다. 순차적으로 실행되는 한 시간은 100% 통과합니다. 각 테스트는 격리되어 있으며 이러한 테스트를 무작위 순서로 실행해도 실패하지 않습니다. 여러 프로세스나 스레드를 혼합에 추가하면 상황이 달라집니다.

테스트를 병렬로 실행할 때 CPU 예약으로 인해 테스트의 개별 문의 실행 순서가 변경될 수 있습니다. 예시를 보면 다음과 같은 실행 순서를 볼 수 있습니다:

 

작업자 2는 작업자 1의 어설션이 실행되기 전에 파일을 삭제했기 때문에 첫 번째 테스트가 실패할 수도 있습니다. 부상을 가중시키기 위해 다른 실행 순서로 인해 때때로 두 번째 테스트는 실패하고 첫 번째 테스트는 통과하게 됩니다.

이 간단한 예는 파일뿐만 아니라 테스트가 스레드로부터 안전하지 않은 방식으로 액세스하는 모든 싱글톤 리소스로 확장되는 문제를 보여줍니다. Redis 데이터베이스 또는 Elasticsearch 인덱스에 쓴다고 가정해 보겠습니다. 이 경우 비슷한 합병증을 경험할 가능성이 높습니다. 게다가 무작위 오류를 일으키는 모든 테스트를 찾아내는 데 시간이 걸릴 수 있으며, 모든 테스트를 수정하는 데는 더 많은 시간이 걸릴 수 있습니다.

불안정한 병렬 테스트를 해결할 수 있는 묘책은 없습니다. 일반적으로 여러 테스트 프로세스가 리소스를 공유하지 않는지 확인해야 합니다. 파일의 경우 Tempfiles를 사용하세요. parallelize_setup를 사용하세요. 네임스페이스화된 리소스(예:Redis 데이터베이스)를 생성합니다. 등등.

기존 Rails 테스트에 병렬 테스트 추가

병렬화로 인해 무작위 테스트 실패로 어려움을 겪고 있으며 이를 수정할 시간이 없다고 가정해 보겠습니다. 그러나 여전히 병렬 테스트의 이점을 누리고 싶습니다. 테스트의 하위 집합에 대해서만 활성화하는 것이 좋습니다.

parallelize을 호출하는 테스트만 결국 병렬화될 것이며 관심사 또는 상위 클래스를 사용하여 한 번에 하나의 테스트 클래스씩 테스트 스위트에 병렬 테스트를 추가할 수 있습니다. 다음과 같은 모듈을 만들 수 있습니다:

 

이제 이 모듈을 포함하는 모든 테스트 클래스가 병렬로 실행됩니다:

 

또는 ParallelTest과 같은 새 테스트 클래스를 만들 수도 있습니다. :

 

그런 다음 병렬로 실행되어야 하는 테스트를 위해 이를 상속하고 문제가 있는 테스트는 제외하세요.

Bandaid로서의 병렬 테스트

병렬 테스트는 적은 노력으로 놀라운 속도 향상을 제공합니다. 하지만 속지 마십시오. 이는 테스트 스위트의 속도를 향상시키기 위한 다른 접근법을 대체하는 것이 아니라 오히려 추가되는 것입니다.

테스트 스위트가 느리고 노력을 아끼지 않는다면 시간을 들여 프로파일링하고 속도 저하의 근본 원인을 해결하십시오. 병렬 테스트가 추가된 느린 테스트 모음은 더 빨라지지만 병렬로 실행되는 이미 빠른 테스트 모음만큼 빠르지는 않습니다!

마무리

이번 포스팅에서는 병렬 테스팅이 무엇인지, 어떻게 설정하고 구성하는지 살펴보았습니다. 테스트를 더 빠르게 진행하는 방법이 필요하다면 병렬 테스트가 바로 그 방법을 제공합니다.

테스트 스위트에 병렬 테스트를 추가할 때 몇 가지 장애물에 직면할 수 있습니다. 수년 동안 안정적으로 작동하던 테스트가 갑자기 실패하기 시작하더라도 놀라지 마십시오. 테스트 스위트의 하위 집합만 병렬화하여 이 문제를 해결할 수 있습니다.

어떤 접근 방식을 선택하든 병렬 테스트는 테스트 속도를 높이는 환상적인 도구입니다!

즐거운 코딩 되세요!

추신 Ruby Magic 게시물이 보도되는 즉시 읽으려면 Ruby Magic 뉴스레터를 구독하고 단 하나의 게시물도 놓치지 마세요!

Ruby on Rails의 병렬 테스트:위험 및 완화 전략

한스 외르크 슈네들리츠

객원 저자인 Hans는 오스트리아 비엔나 출신의 Rails 엔지니어입니다. 그는 대부분의 시간을 코딩하거나 코딩에 관한 독서를 하며 보내고 때로는 블로그에 이에 관한 글을 쓰기도 합니다! 그가 스크린 앞에 앉아 있지 않을 때는 아마도 밖에서 산을 오르고 있는 모습을 볼 수 있을 것입니다.

Hans-Jörg Schnedlitz의 모든 기사