"일꾼이 일을 잘하려면 먼저 도구를 갈고 닦아야 한다." - 공자, 『논어』.
첫 장 > 프로그램 작성 > 소파GO! — Go로 작성된 쿼리 서버로 CouchDB 강화

소파GO! — Go로 작성된 쿼리 서버로 CouchDB 강화

2024-08-06에 게시됨
검색:195

CouchGO! — Enhancing CouchDB with Query Server Written in Go

지난 한 달 동안 CouchDB와 관련된 개념 증명 프로젝트를 활발히 진행하며 기능을 탐색하고 향후 작업을 준비했습니다. 이 기간 동안 나는 모든 것이 어떻게 작동하는지 이해하기 위해 CouchDB 문서를 여러 번 검토했습니다. 문서를 읽는 동안 JavaScript로 작성된 기본 쿼리 서버와 함께 CouchDB가 제공됨에도 불구하고 사용자 정의 구현을 만드는 것이 비교적 간단하며 사용자 정의 솔루션이 이미 존재한다는 진술을 발견했습니다.

몇 가지 빠른 조사를 통해 Python, Ruby 또는 Clojure로 작성된 구현을 찾았습니다. 전체 구현이 그리 길지 않아 보였기 때문에 나만의 사용자 정의 쿼리 서버를 작성하여 CouchDB를 실험해 보기로 결정했습니다. 이를 위해 언어로 Go를 선택했습니다. Helm의 차트에서 Go 템플릿을 사용하는 것 외에는 이 언어에 대한 경험이 많지 않았지만 새로운 것을 시도하고 싶었고 이 프로젝트가 좋은 기회가 될 것이라고 생각했습니다.

쿼리 서버 이해

작업을 시작하기 전, Query Server가 실제로 어떻게 작동하는지 이해하기 위해 CouchDB 문서를 한 번 더 살펴봤습니다. 문서에 따르면 쿼리 서버의 상위 수준 개요는 매우 간단합니다.

쿼리 서버는 stdio 인터페이스를 통해 JSON 프로토콜을 통해 CouchDB와 통신하고 모든 디자인 함수 호출을 처리하는 외부 프로세스입니다 [...].

CouchDB가 쿼리 서버로 보내는 명령의 구조는 [, ] 또는 ["ddoc", , [, ], [ , , …]] 설계 문서의 경우.

그래서 기본적으로 제가 해야 했던 일은 STDIO에서 이러한 종류의 JSON을 구문 분석하고, 예상되는 작업을 수행하고, 문서에 지정된 대로 응답을 반환할 수 있는 애플리케이션을 작성하는 것이었습니다. Go 코드에서는 광범위한 명령을 처리하기 위해 많은 유형 캐스팅이 필요했습니다. 각 명령에 대한 구체적인 세부 정보는 설명서의 쿼리 서버 프로토콜 섹션에서 확인할 수 있습니다.

여기서 직면한 한 가지 문제는 쿼리 서버가 디자인 문서에 제공된 임의의 코드를 해석하고 실행할 수 있어야 한다는 것이었습니다. Go가 컴파일된 언어라는 것을 알았기 때문에 이 시점에서 정체될 것으로 예상했습니다. 다행히 Go 코드를 쉽게 해석할 수 있는 Yeagi 패키지를 빨리 찾았습니다. 이를 통해 샌드박스를 생성하고 해석된 코드로 가져올 수 있는 패키지에 대한 액세스를 제어할 수 있습니다. 내 경우에는 Couchgo라는 패키지만 노출하기로 했지만, 다른 표준 패키지도 쉽게 추가할 수 있다.

CouchGO를 소개합니다!

작업의 결과 CouchGO!라는 애플리케이션이 탄생했습니다. 등장했다. Query Server Protocol을 따르지만 디자인 문서 기능을 처리하는 자체 접근 방식이 있으므로 JavaScript 버전을 일대일로 다시 구현하는 것은 아닙니다.

예를 들어 CouchGO!에는 내보내기와 같은 도우미 기능이 없습니다. 값을 내보내려면 지도 함수에서 값을 반환하기만 하면 됩니다. 또한 디자인 문서의 각 함수는 동일한 패턴을 따릅니다. 즉, 함수별 속성을 포함하는 개체인 인수가 하나만 있고 결과로 하나의 값만 반환해야 합니다. 이 값은 기본 값일 필요는 없습니다. 기능에 따라 객체일 수도 있고, 지도일 수도 있고, 심지어 오류일 수도 있습니다.

CouchGO! 작업을 시작하려면 내 GitHub 저장소에서 실행 가능한 바이너리를 다운로드하여 CouchDB 인스턴스 어딘가에 배치하고 CouchDB가 CouchGO!를 시작할 수 있도록 하는 환경 변수를 추가하면 됩니다. 프로세스.

예를 들어, Couchgo 실행 파일을 /opt/couchdb/bin 디렉토리에 배치하는 경우 다음 환경 변수를 추가하여 작동할 수 있습니다.

export COUCHDB_QUERY_SERVER_GO="/opt/couchdb/bin/couchgo"

CouchGO!로 함수 작성하기

CouchGO!로 함수를 작성하는 방법을 빠르게 이해하기 위해 다음 함수 인터페이스를 살펴보겠습니다.

func Func(args couchgo.FuncInput) couchgo.FuncOutput { ... }

CouchGO!의 각 기능 이 패턴을 따르며 Func는 적절한 함수 이름으로 대체됩니다. 현재 카우치GO! 다음 기능 유형을 지원합니다:

  • 지도
  • 줄이다
  • 필터
  • 업데이트
  • 검증(validate_doc_update)

map 및 축소 기능과 함께 유효성 검사_doc_update 기능을 사용하여 뷰를 지정하는 예제 디자인 문서를 살펴보겠습니다. 또한 Go를 언어로 사용하고 있음을 지정해야 합니다.

{
  "_id": "_design/ddoc-go",
  "views": {
    "view": {
      "map": "func Map(args couchgo.MapInput) couchgo.MapOutput {\n\tout := couchgo.MapOutput{}\n\tout = append(out, [2]interface{}{args.Doc[\"_id\"], 1})\n\tout = append(out, [2]interface{}{args.Doc[\"_id\"], 2})\n\tout = append(out, [2]interface{}{args.Doc[\"_id\"], 3})\n\t\n\treturn out\n}",
      "reduce": "func Reduce(args couchgo.ReduceInput) couchgo.ReduceOutput {\n\tout := 0.0\n\n\tfor _, value := range args.Values {\n\t\tout  = value.(float64)\n\t}\n\n\treturn out\n}"
    }
  },
  "validate_doc_update": "func Validate(args couchgo.ValidateInput) couchgo.ValidateOutput {\n\tif args.NewDoc[\"type\"] == \"post\" {\n\t\tif args.NewDoc[\"title\"] == nil || args.NewDoc[\"content\"] == nil {\n\t\t\treturn couchgo.ForbiddenError{Message: \"Title and content are required\"}\n\t\t}\n\n\t\treturn nil\n\t}\n\n\tif args.NewDoc[\"type\"] == \"comment\" {\n\t\tif args.NewDoc[\"post\"] == nil || args.NewDoc[\"author\"] == nil || args.NewDoc[\"content\"] == nil {\n\t\t\treturn couchgo.ForbiddenError{Message: \"Post, author, and content are required\"}\n\t\t}\n\n\t\treturn nil\n\t}\n\n\tif args.NewDoc[\"type\"] == \"user\" {\n\t\tif args.NewDoc[\"username\"] == nil || args.NewDoc[\"email\"] == nil {\n\t\t\treturn couchgo.ForbiddenError{Message: \"Username and email are required\"}\n\t\t}\n\n\t\treturn nil\n\t}\n\n\treturn couchgo.ForbiddenError{Message: \"Invalid document type\"}\n}",
  "language": "go"
}

이제 지도 기능으로 시작하는 각 기능을 분석해 보겠습니다.

func Map(args couchgo.MapInput) couchgo.MapOutput {
  out := couchgo.MapOutput{}
  out = append(out, [2]interface{}{args.Doc["_id"], 1})
  out = append(out, [2]interface{}{args.Doc["_id"], 2})
  out = append(out, [2]interface{}{args.Doc["_id"], 3})

  return out
}

CouchGO!에는 방출 기능이 없습니다. 대신, 키와 값이 모두 어떤 유형이든 될 수 있는 키-값 튜플 조각을 반환합니다. 문서 객체는 JavaScript처럼 함수에 직접 전달되지 않습니다. 오히려 객체에 싸여 있습니다. 문서 자체는 단순히 다양한 값의 해시맵입니다.

다음으로 축소 기능을 살펴보겠습니다.

func Reduce(args couchgo.ReduceInput) couchgo.ReduceOutput {
  out := 0.0
  for _, value := range args.Values {
    out  = value.(float64)
  }
  return out
}

JavaScript와 유사하게 CouchGO의 축소 기능! 키, 값, rereduce 매개변수를 모두 단일 객체로 래핑합니다. 이 함수는 축소 작업의 결과를 나타내는 모든 유형의 단일 값을 반환해야 합니다.

마지막으로 verify_doc_update 속성에 해당하는 Validate 함수를 살펴보겠습니다.

func Validate(args couchgo.ValidateInput) couchgo.ValidateOutput {
  if args.NewDoc["type"] == "post" {
    if args.NewDoc["title"] == nil || args.NewDoc["content"] == nil {
      return couchgo.ForbiddenError{Message: "Title and content are required"}
    }

    return nil
  }

  if args.NewDoc["type"] == "comment" {
    if args.NewDoc["post"] == nil || args.NewDoc["author"] == nil || args.NewDoc["content"] == nil {
      return couchgo.ForbiddenError{Message: "Post, author, and content are required"}
    }

    return nil
  }

  return nil
}

이 함수에서는 새 문서, 이전 문서, 사용자 컨텍스트, 보안 객체와 같은 매개변수를 수신하며 모두 함수 인수로 전달된 하나의 객체로 래핑됩니다. 여기서는 문서를 업데이트할 수 있는지 확인하고 그렇지 않은 경우 오류를 반환해야 합니다. JavaScript 버전과 유사하게 ForbiddenError 또는 UnauthorizedError라는 두 가지 유형의 오류를 반환할 수 있습니다. 문서를 업데이트할 수 있으면 nil을 반환해야 합니다.

더 자세한 예는 내 GitHub 저장소에서 찾을 수 있습니다. 주목해야 할 한 가지 중요한 점은 함수 이름이 임의적이지 않다는 것입니다. Map, Reduce, Filter 등과 같이 표현하는 함수 유형과 항상 일치해야 합니다.

소파GO! 성능

쿼리 서버를 직접 작성하는 것은 정말 재미있는 경험이었지만 기존 솔루션과 비교하지 않으면 의미가 없습니다. 그래서 CouchGO!가 얼마나 빨라졌는지 확인하기 위해 Docker 컨테이너에서 몇 가지 간단한 테스트를 준비했습니다. 할 수 있다:

  • 100,000개 문서 색인화(CouchDB의 색인화는 뷰에서 지도 기능 실행을 의미함)
  • 100,000개 문서에 대한 축소 기능 실행
  • 100,000개의 문서에 대한 필터 변경 피드
  • 1,000개 요청에 대한 업데이트 기능 수행

예상되는 문서 수로 데이터베이스를 시드하고 전용 셸 스크립트를 사용하여 Docker 컨테이너에서 측정된 응답 시간 또는 차별화된 타임스탬프 로그를 사용했습니다. 구현 세부 사항은 내 GitHub 저장소에서 확인할 수 있습니다. 그 결과는 아래 표와 같습니다.

시험 소파GO! CouchJS 후원
색인 생성 141.713초 421.529초 2.97x
감소 7672ms 15642ms 2.04x
필터링 28.928초 80.594초 2.79x
업데이트 중 7.742초 9.661초 1.25x

보시다시피 JavaScript 구현에 비해 성능이 크게 향상되었습니다. 인덱싱의 경우 거의 3배 빠르고, 축소 및 필터 기능의 경우 2배 이상 빠릅니다. 업데이트 기능의 경우 부스트가 상대적으로 작지만 JavaScript보다 여전히 빠릅니다.

결론

문서 작성자가 약속한 대로 쿼리 서버 프로토콜을 따르면 사용자 지정 쿼리 서버를 작성하는 것이 그리 어렵지 않았습니다. 비록 CouchGO! 일반적으로 더 이상 사용되지 않는 몇 가지 기능이 부족하므로 개발 초기 단계에서도 JavaScript 버전에 비해 상당한 향상을 제공합니다. 아직 개선의 여지가 많다고 생각합니다.

이 기사의 모든 코드가 한곳에 필요하다면 내 GitHub 저장소에서 찾을 수 있습니다.

이 글을 읽어주셔서 감사합니다. 이 솔루션에 대한 귀하의 생각을 듣고 싶습니다. CouchDB 인스턴스와 함께 사용하시겠습니까, 아니면 이미 맞춤형 쿼리 서버를 사용하고 계십니까? 댓글로 들어주시면 감사하겠습니다.

더 많은 팁, 통찰력, 이 시리즈의 다른 부분이 작성되면 내 다른 기사를 확인하는 것을 잊지 마세요. 즐거운 해킹되세요!

릴리스 선언문 이 글은 https://dev.to/kishieel/couchgo-enhancing-couchdb-with-query-server-write-in-go-304n?1에서 복제됩니다.1 침해 내용이 있는 경우, [email protected]으로 문의하시기 바랍니다. 그것을 삭제하려면
최신 튜토리얼 더>

부인 성명: 제공된 모든 리소스는 부분적으로 인터넷에서 가져온 것입니다. 귀하의 저작권이나 기타 권리 및 이익이 침해된 경우 자세한 이유를 설명하고 저작권 또는 권리 및 이익에 대한 증거를 제공한 후 이메일([email protected])로 보내주십시오. 최대한 빨리 처리해 드리겠습니다.

Copyright© 2022 湘ICP备2022001581号-3