카우치베이스 아키텍처

기능 및 통합 테스트(FIT) 프레임워크

이 블로그에서는 분산 환경에서의 Couchbase 트랜잭션을 위한 테스트 프레임워크, 즉 FIT 프레임워크의 설계 및 개발에 대해 설명합니다. 먼저 높은 수준의 아키텍처 인사이트를 소개한 다음 프레임워크의 개발 과정을 안내해 드리겠습니다.

트랜잭션 SDK 테스트와 관련된 다양한 문제와 그에 대한 해결책을 살펴보고, 프레임워크 개발 과정에서 관련 예제를 사용할 것입니다. 이 블로그에서 프레임워크의 모든 기술적 세부 사항을 언급하지는 않지만, 프레임워크에 대한 전체적인 그림을 제공하려고 노력했습니다.

카우치베이스는 여러 SDK로 트랜잭션을 제공합니다: 현재는 Java, Dotnet, CXX를 지원하며 조만간 다른 SDK도 지원할 계획입니다. 동일한 기능을 제공하는 SDK를 테스트하면 테스트 자동화 중에 여러 가지 문제가 발생할 수 있습니다. 테스트 자동화 중복성은 누구나 가장 먼저 떠올릴 수 있는 문제입니다. 중복성 외에도 모든 SDK의 Couchbase 트랜잭션 구현이 유사해야 합니다. 예: 오류 처리는 모든 SDK에서 정확히 동일하게 수행됩니다. 이는 몇 가지 문제일 뿐입니다. 이 블로그에서는 트랜잭션에 중점을 두고 여러 SDK를 테스트하는 동안 직면하게 되는 다양한 문제와 Couchbase에서 이를 어떻게 해결했는지에 대해 설명합니다.

카우치베이스 트랜잭션 소개

분산 ACID 트랜잭션은 여러 문서를 수정해야 하는 경우 모든 수정이 성공적으로 수행되거나 수정이 전혀 수행되지 않는 경우 모두의 수정이 성공해야만 일부의 수정이 정당화되도록 보장합니다. ACID 속성을 준수하는 카우치베이스는 다음에서 확인할 수 있습니다. 여기.

분산 환경에서의 트랜잭션: 

단일 노드 클러스터: 카우치베이스 트랜잭션은 단일 노드 클러스터뿐만 아니라 멀티 노드 클러스터에서도 작동합니다. 그러나 클러스터 구성은 다음에서 지원되어야 합니다. 카우치베이스.

트랜잭션은 N1QL 쿼리를 지원합니다: 클러스터의 노드 중 하나 이상에 쿼리 서비스가 있는지 확인합니다.

카우치베이스 트랜잭션 SDK 테스트:

프레임워크의 설계 단계에서 테스트 계획과 자동화를 심층적으로 분석하는 과정에서 여러 가지 도전과제에 직면했습니다. 다음은 몇 가지 주요 과제와 그 해결 방법입니다. 앞으로 이 문제와 그 해결책을 논의하겠습니다. 또한 이러한 문제 논의와 함께 프레임워크의 개발 진행 상황도 살펴볼 것입니다.

문제1: 중복성 문제:

카우치베이스에서는 현재 3가지 SDK로 트랜잭션을 지원합니다: Java , 닷넷, CXX입니다. 조만간 Golang을 포함한 몇 가지 SDK를 더 지원할 예정입니다. 이로 인해 각 SDK마다 동일한 테스트 사례를 여러 번 자동화해야 하는 중복 문제가 발생할 수 있습니다. 

해상도: 각 테스트 케이스는 크게 세 부분으로 분류할 수 있습니다:

  1. 테스트 준비: 테스트 데이터, 테스트 인프라 등 
  2. 테스트 실행 예: 트랜잭션 작업 실행(예: 삽입, 바꾸기 등) 및 
  3. 결과 유효성 검사.

이 세 부분을 자세히 살펴보면 SDK 테스트는 테스트 실행 단계에만 관여하고 테스트 준비 및 결과 검증은 실제로 SDK와 무관하므로 어떤 SDK를 사용하든 크게 상관없다는 것을 알 수 있습니다. 이에 따라 드라이버와 수행자의 두 부분으로 구성된 프레임워크를 설계하게 되었습니다. 드라이버는 완전한 테스트 준비와 결과 유효성 검사를 처리합니다. 드라이버는 테스트 실행을 추상적으로만 주도하지만(아래에서 자세히 알아보겠습니다.) 수행자에게 명령을 내리고 수행자는 이를 받아 실제 테스트 실행을 수행합니다.

FIT 프레임워크 드라이버가 클라이언트 역할을 하고 퍼포머가 서버 역할을 하는 클라이언트-서버 모델입니다.

FIT Framework Architecture

드라이버: 모든 테스트 준비 및 결과 유효성 검사로 구성됩니다. 모든 테스트는 고전적인 Junit 테스트이며 단일 개별 테스트 또는 특정 테스트 스위트 또는 전체 테스트 스위트로 실행할 수 있습니다. 모든 테스트는 한 번만 작성됩니다. 이러한 테스트는 모든 SDK에 재사용할 수 있습니다.

수행자: 이것은 각 SDK에 대해 한 번만 작성되는 간단한 애플리케이션입니다. 드라이버 내부에서 각 테스트는 Java 객체 형태로 몰딩되어 gRPC 레이어. gRPC 프로토콜은 이 Java 객체를 언어별 테스트 객체로 변환하여 수행자에게 전송하는 작업을 수행합니다. 수행자는 이 테스트 객체를 가져와서 지침을 읽고 필요한 트랜잭션 작업을 실행합니다. 트랜잭션이 완료되면 수행자는 결과를 검색하여 gRPC 프로토콜을 통해 드라이버로 다시 보냅니다.

드라이버가 결과 객체를 받으면 드라이버는 결과 유효성 검사를 진행합니다.테스트 개발 프로세스: 이제 FIT 프레임워크 내에서 드라이버와 퍼포머가 어떻게 작동하는지에 대한 개략적인 아이디어를 얻었으니, 몇 가지 간단한 예제 테스트를 통해 기술적 측면과 서로 상호 작용하는 방식을 살펴보겠습니다.

예1: 단일 작업으로 트랜잭션 테스트하기: 기본 '바꾸기' 작업 

드라이버 코드: 

보시다시피 모든 테스트는 항상 Junit 테스트로 한 번만 작성됩니다.

테스트 준비 및 결과 유효성 검사는 SDK와 독립적이므로 Junit 테스트 자체에서 수행됩니다. 

그러나 테스트 실행 부분은 추상적인 방식으로 수행됩니다. 겉으로 보기에는 드라이버 자체에서 실행되는 것처럼 보입니다. 하지만 원격 프로시저 호출에 따라 분산 컴퓨팅에 참여합니다. 전체 테스트는 트랜잭션 빌더 클래스를 사용하여 FIT 프레임워크에서 "TransactionBuilder" 객체로 명명된 Java 객체로 변환된 다음 "sendToPerfomer" 메서드를 사용하여 gRPC 계층을 통해 수행자에게 전송됩니다. 

트랜잭션 바꾸기 작업을 테스트하려는 이 예제에서는 모든 세부 정보를 포함하는 Java 객체를 만듭니다:

  1. 트랜잭션이 실행되어야 하는 문서 ID 
  2. 업데이트된 값, 즉 트랜잭션이 문서에 부과할 새 값입니다.
  3. 트랜잭션 작업, 이 경우 "교체"

이러한 자바 객체를 생성하면 sendToPerformer는 단지 gRPC 호출하여 서버로 전송합니다.

Java 퍼포머 코드 를 참조하세요: 기본 수행자

따라서 첫 번째 단계에서 수행자는 테스트 객체를 읽고 실행해야 하는 연산이 있는지 확인합니다. 이 예제에서는 바꾸기 연산이므로 op.hasReplace()는 참을 반환하고 op.hasInsert(), op.hasRemove() 등은 거짓을 반환합니다.

대체 코드 블록 내에서 수행자는 문서 ID, 문서 위치, 문서에 대한 업데이트된 값을 검색합니다. 모든 관련 정보가 검색되면 수행자는 트랜잭션, 즉 ctx.replace() 작업을 실행합니다.

트랜잭션이 성공적으로 실행되면 결과가 드라이버로 다시 전송되고 드라이버는 마찬가지로 결과 객체에서 관련 정보를 검색하고 결과 유효성 검사를 수행합니다.

기능 테스트의 예 이 프레임워크의 기능은 문서 콘텐츠뿐만 아니라 트랜잭션 메타데이터, 즉 필요한 곳에 예상 메타데이터가 존재하고 필요한 곳에 메타데이터가 제거되는 트랜잭션 SDK를 테스트하는 데 도움이 되었습니다.

이제 FIT 프레임워크에 대한 기술적 인사이트를 얻었으니 조금 더 자세히 살펴보겠습니다:

예2: 둘 이상의 작업으로 트랜잭션 테스트하기:

드라이버 코드:

이 테스트에서 트랜잭션은 docId1이 있는 문서에 삽입과 docId2가 있는 문서에 바꾸기를 수행합니다. 따라서 테스트 개체에 "삽입"과 "바꾸기"를 추가해야 하며 이러한 각 작업에 필요한 모든 정보가 수행자에게 전송됩니다.

Java 퍼포머 코드 를 참조하세요: 수행자지원TwoOps

삽입과 교체가 있으므로 op.Insert가 참을 반환하고 수행자가 필요한 정보를 검색하여 삽입을 수행한 다음 op.replace()가 참을 반환하고 수행자가 교체 작업을 실행하고 결과를 드라이버에 다시 반환합니다.

기능 테스트의 예 처음에는 동일한 트랜잭션에서 동일한 문서에 대한 모든 유효한 다중 트랜잭션 작업을 지원하지 않았지만, 프레임워크의 이러한 동작을 추가하여 해당 기능을 테스트할 수 있었습니다. 또한 서로 다른 문서에 대한 트랜잭션의 일반적인 다중 작업도 테스트되었습니다. 트랜잭션이 문서를 교체/제거하지 못하거나 만료되는 등의 문제가 이 지원으로 잘 테스트되었습니다.

두 예시에서 트랜잭션이 성공할 것으로 예상되는 경우를 보았습니다. 그러나 부정적인 경우의 시나리오에서는 트랜잭션이 오류/예외를 발생시킬 것으로 예상했습니다. 이러한 오류/예외는 SDK에 따라 다르므로 수행자에서 처리해야 합니다. 따라서 드라이버는 예외 처리할 오류/예외를 퍼포머에게 알려주고 퍼포머는 이 유효성 검사를 수행해야 합니다.

Problem2 오류 확인:

  1. 다양한 원인에 대해 트랜잭션은 원인을 파악하고 관련 오류/예외를 던져야 합니다. 따라서 트랜잭션의 기능뿐만 아니라 트랜잭션이 던지는 오류 코드와 예외도 테스트해야 했습니다.
  2. 트랜잭션 예외 처리는 각 오류/예외에 따라 다릅니다: 문서를 찾을 수 없음 예외는 일부 일시적인 예외와 다르게 처리해야 합니다. 
  3. 동일한 예외라도 예외가 발생하는 트랜잭션 단계에 따라 다르게 처리됩니다. 예: 삽입/교체 작업의 쓰기-쓰기 충돌은 가져오기 작업과 다르게 처리됩니다.

해상도: 드라이버는 원인 및 예외에 대한 코드를 수행자에게 보내야 합니다. 수행자는 실패 원인에 대한 코드를 읽고 Hook을 사용하여 이를 유도합니다.

Hook은 실패 시나리오를 테스트하는 데 도움이 되는 내부 Couchbase 구현입니다. 아래 예제에서는 문서를 삽입하기 전에 만료를 생성하려고 합니다.

일단 실패가 유도되면 수행자는 이 트랜잭션이 던져야 하는 오류/예상 예외도 예상하게 됩니다. 따라서 수행자는 예외를 검색하고 유효성을 검사합니다. 예외가 발생하지 않거나 잘못된 예상이 발생하면 수행자는 테스트에 실패하고 결과 객체에 실패를 드라이버에 보냅니다. 드라이버는 이 결과 객체를 읽고 예상 실패와 실제 실패를 출력으로 제공합니다.

예3: 부정 사례 시나리오 테스트 :

드라이버 코드:

따라서 이 테스트에서 드라이버는 수행자에게 삽입을 실행한 다음 이 삽입 작업 중에 트랜잭션이 만료될 것으로 예상하도록 지시하고 있습니다. 이를 수행자에게 전달하기 위해 "EXPECT_FAIL_EXPIRY" 코드를 전송합니다.

Java 퍼포머 코드 를 참조하세요: 수행자 지원 오류 처리

기능 테스트의 예 모든 오류/예외 처리 및 오류 코드가 테스트되었습니다. 오류 처리의 합의된 기능을 지원하지 않거나 동기화되지 않는 SDK와 관련된 기능 테스트가 수행되었습니다. 트랜잭션 만료 기능은 이 프레임워크의 지원과 함께 잘 테스트되었습니다.  

문제 3: 버전 관리: 다양한 라이브러리 버전의 트랜잭션을 테스트해야 하며, 이후 버전에는 이전 버전에서는 사용할 수 없는 새로운 기능이 추가될 수 있습니다. 버전을 지원하지 않습니다. 따라서 테스트 프레임워크는 지원되지 않는 기능을 파악하고 해당 테스트를 실행하지 않아야 했습니다. 

해상도: Junit5 조건 테스트 실행 확장을 사용했습니다. 각 테스트 스위트에는 "@IgnoreWhen" 조건이 주석 처리되어 있습니다. 여기에 언급된 모든 조건은 우리가 재정의하는 "ExecuteWhen" 메서드에서 검색되어 사용됩니다. 드라이버가 테스트 실행을 시작하기 전에 수행자에게 연락하여 지원되는 모든 기능을 가져옵니다. "ExecuteWhen" 메서드는 "@IgnoreWhen"에 제공된 정보와 수행자 기능을 사용하여 테스트 스위트를 실행할지 또는 무시해야 하는지 결정합니다. 

예3:

Java 드라이버 코드 를 참조하세요: 드라이버 지원 버전 관리

기능 테스트의 예 다른 SDK보다 조금 늦게 기능을 개발한 SDK는 기능을 구현한 후 FIT의 이 기능을 사용하여 이러한 테스트를 실행할 수 있습니다. 이는 테스트 중심 개발에 도움이 되었습니다.

문제 4: 여러 명의 출연자:  병렬 트랜잭션:

트랜잭션은 병렬로 실행할 수 있습니다. 카우치베이스 트랜잭션은 격리 모델을 확인합니다. 즉, 동일한 문서 세트에서 두 개 이상의 트랜잭션이 실행될 때 더티 쓰기/읽기로 이어지지 않아야 합니다. 이를 테스트하기 위해 무작위로 'n'개의 트랜잭션을 병렬로 실행하여 문서 손상이 발생하는 경우, 손상의 원인을 정확히 파악하기 어려울 것입니다. 각 트랜잭션에는 많은 작업이 있을 수 있고 각 작업에는 여러 단계가 있을 수 있습니다. 문제를 해결하려면 어떤 작업과 어떤 단계에서 이러한 트랜잭션이 충돌했는지 알아야 합니다.

해상도: 저희는 한 트랜잭션이 몇 가지 작업 또는 작업의 몇 단계를 실행하고 다른 트랜잭션이 시작되도록 신호를 보내는 래칭 메커니즘을 설계했습니다. 이제 첫 번째 트랜잭션은 두 번째 트랜잭션이 실행되어 원하는 단계에 도달할 때까지 기다립니다. 두 번째 트랜잭션이 특정 단계에 도달하면 첫 번째 트랜잭션에 진행을 알립니다. 이는 병렬 트랜잭션의 경우에도 마찬가지입니다. 그래서 저희는 쓰기-쓰기 충돌 또는 더티 읽기로 이어질 수 있는 일련의 충돌 지점을 생각해냈고 이러한 테스트 사례를 자동화하기 위해 래치를 사용했습니다.

Java 드라이버 코드 를 참조하세요: 드라이버 병렬 트랜잭션

드라이버 코드:

기능 테스트/버그 발견 예시: 이 지원으로 동시 트랜잭션이 테스트되었습니다.

문제5: 다중 수행자: 서로 다른 SDK에 대한 병렬 트랜잭션:

여러 SDK에서 트랜잭션을 지원하기 때문에 서로 다른 SDK로 트랜잭션의 병렬 실행을 테스트하는 동안 동일한 로직을 사용할 수 있습니다. 예: Java 트랜잭션 대 CXX 트랜잭션. 위의 예제에서는 동일한 SDK에 대해 병렬 트랜잭션을 실행하고자 했기 때문에 동일한 수행자에 연결했습니다. 이 경우 TXN A는 퍼포머 A에 연결하고(퍼포머 A가 Java 트랜잭션을 사용한다고 가정), TXN B는 퍼포머 B에 연결합니다(CXX 트랜잭션 실행).

Java 드라이버 코드 를 참조하세요: 드라이버멀티퍼포머

기능 테스트/버그 발견 예시: 이 지원으로 다른 SDK 클라이언트와의 동시 트랜잭션이 테스트되었습니다. 또한 트랜잭션 메타데이터가 손상되지 않도록 하는 데에도 도움이 되었습니다.

결론:

이러한 FIT 프레임워크의 아키텍처 설계는 우리에게 제기된 문제를 해결하는 데 도움이 되었을 뿐만 아니라 효율적인 테스트 자동화에도 도움이 되었으며, 테스트 주도 개발(TDD) 모드에서 트랜잭션 개발에 큰 도움이 되었습니다. 

효율적인 테스트 자동화: 프레임워크를 하나의 드라이버와 여러 개의 퍼포머로 분할하여 프레임워크의 일부를 독립적으로 개발하는 데 도움이 되었습니다. 각 SDK의 개발자가 퍼포머를 제공했고 QE는 테스트 자동화, 즉 드라이버에 집중할 수 있었습니다. 또한 개발자는 드라이버에 단위 테스트를 추가하여 트랜잭션에 대한 모든 테스트가 이 단일 프레임워크에서 처리되도록 할 수 있었습니다.

테스트 주도 개발(TDD): 저희는 자바 퍼포머를 개발하고 초기 몇 가지 버전의 자바 SDK용 트랜잭션을 승인하는 데 필요한 모든 테스트를 작성했습니다.자바 SDK가 출시되고 다른 트랜잭션 SDK, 즉 CXX 및 닷넷 개발이 시작되자 개발팀은 동일한 드라이버 애플리케이션을 재사용하면서 퍼포머 애플리케이션을 개발해야 했습니다. 이는 TDD 방식으로 SDK를 개발하는 데 도움이 되었습니다.

이 블로그가 도움이 되셨기를 바랍니다. 저희는 이 프레임워크에 더 많은 기능을 추가하고 있으며 새로운 문제와 새로운 해결책을 설명하는 새로운 블로그를 곧 마련할 예정입니다. 그동안 FIT 프레임워크에 대해 자세히 알아보려면 다음 연락처로 문의하시기 바랍니다. praneeth.bokka@couchbase.com. 카우치베이스 트랜잭션에 대해 자세히 알아보려면 다음을 방문하세요. 카우치베이스 거래

이 문서 공유하기
받은 편지함에서 카우치베이스 블로그 업데이트 받기
이 필드는 필수 입력 사항입니다.

작성자

게시자 프레네스 보카

댓글 남기기

카우치베이스 카펠라를 시작할 준비가 되셨나요?

구축 시작

개발자 포털에서 NoSQL을 살펴보고, 리소스를 찾아보고, 튜토리얼을 시작하세요.

카펠라 무료 사용

클릭 몇 번으로 Couchbase를 직접 체험해 보세요. Capella DBaaS는 가장 쉽고 빠르게 시작할 수 있는 방법입니다.

연락하기

카우치베이스 제품에 대해 자세히 알고 싶으신가요? 저희가 도와드리겠습니다.