«Если рабочий хочет хорошо выполнять свою работу, он должен сначала заточить свои инструменты» — Конфуций, «Аналитики Конфуция. Лу Лингун»
титульная страница > программирование > Диван, ВПЕРЕД! — Улучшение CouchDB с помощью сервера запросов, написанного на Go.

Диван, ВПЕРЕД! — Улучшение CouchDB с помощью сервера запросов, написанного на Go.

Опубликовано 6 августа 2024 г.
Просматривать:898

CouchGO! — Enhancing CouchDB with Query Server Written in Go

В течение последнего месяца я активно работал над экспериментальными проектами, связанными с CouchDB, изучая его возможности и готовясь к будущим задачам. За это время я несколько раз просматривал документацию CouchDB, чтобы убедиться, что я понимаю, как все работает. Читая документацию, я наткнулся на заявление о том, что, несмотря на то, что CouchDB поставляется с сервером запросов по умолчанию, написанным на JavaScript, создание собственной реализации относительно просто, и специальные решения уже существуют в природе.

Я провел небольшое исследование и нашел реализации, написанные на Python, Ruby или Clojure. Поскольку вся реализация не показалась мне слишком длинной, я решил поэкспериментировать с CouchDB, попытавшись написать собственный сервер запросов. Для этого я выбрал в качестве языка Go. Раньше у меня не было большого опыта работы с этим языком, за исключением использования шаблонов Go в чартах Helm, но мне хотелось попробовать что-то новое, и я подумал, что этот проект станет для этого отличной возможностью.

Понимание сервера запросов

Прежде чем приступить к работе, я еще раз просмотрел документацию CouchDB, чтобы понять, как на самом деле работает сервер запросов. Согласно документации, общий обзор Query Server довольно прост:

Сервер запросов — это внешний процесс, который взаимодействует с CouchDB через протокол JSON через интерфейс stdio и обрабатывает все вызовы функций проектирования […].

Структура команд, отправляемых CouchDB на сервер запросов, может быть выражена как [, ] или ["ddoc", , [, ], [ , , …]] в случае проектной документации.

Итак, по сути, мне нужно было написать приложение, способное анализировать этот тип JSON из STDIO, выполнять ожидаемые операции и возвращать ответы, как указано в документации. Для обработки широкого спектра команд в коде Go потребовалось много приведения типов. Подробную информацию о каждой команде можно найти в разделе документации «Протокол сервера запросов».

Одна из проблем, с которой я столкнулся здесь, заключалась в том, что сервер запросов должен иметь возможность интерпретировать и выполнять произвольный код, представленный в проектной документации. Зная, что Go — компилируемый язык, я ожидал, что застряну на этом этапе. К счастью, я быстро нашел пакет Yeagi, который позволяет легко интерпретировать код Go. Он позволяет создать песочницу и контролировать доступ к тому, какие пакеты можно импортировать в интерпретируемый код. В моем случае я решил предоставить доступ только к моему пакету под названием «couchgo», но можно легко добавить и другие стандартные пакеты.

Представляем CouchGO!

В результате моей работы появилось приложение CouchGO! появился. Хотя он следует протоколу сервера запросов, он не является однозначной перереализацией версии 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 заменяется соответствующим именем функции. В настоящее время CouchGO! поддерживает следующие типы функций:

  • Карта
  • Уменьшать
  • Фильтр
  • Обновлять
  • Подтвердить (validate_doc_update)

Давайте рассмотрим пример дизайнерского документа, в котором указано представление с функциями карты и сокращения, а также функцией validate_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, заключенные в один объект. Эта функция должна возвращать одно значение любого типа, представляющее результат операции сокращения.

Наконец, давайте посмотрим на функцию Validate, которая соответствует свойству validate_doc_update:

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. Если документ можно обновить, мы должны вернуть ноль.

Более подробные примеры можно найти в моем репозитории на GitHub. Важно отметить, что имена функций не являются произвольными; они всегда должны соответствовать типу функции, которую они представляют, например Map, уменьшить, фильтровать и т. д.

Диван, ВПЕРЕД! Производительность

Хотя написание собственного сервера запросов было действительно увлекательным занятием, в этом не было бы особого смысла, если бы я не сравнил его с существующими решениями. Итак, я подготовил несколько простых тестов в Docker-контейнере, чтобы проверить, насколько быстрее CouchGO! может:

  • Индексировать 100 тысяч документов (индексирование в CouchDB означает выполнение картографических функций из представлений)
  • Выполнить функцию сокращения для 100 тыс. документов
  • Фильтр изменения ленты для 100 тыс. документов
  • Выполнить функцию обновления для 1 тыс. запросов

Я заполнил базу данных ожидаемым количеством документов и измерил время ответа или журналы с дифференцированными метками времени из контейнера Docker, используя специальные сценарии оболочки. Подробности реализации можно найти в моем репозитории GitHub. Результаты представлены в таблице ниже.

Тест Диван, ВПЕРЕД! CouchJS Способствовать росту
Индексирование 141,713 с 421,529 с 2,97x
Сокращение 7672 мс 15642 мс 2,04x
Фильтрация 28,928 с 80,594 с 2,79x
Обновление 7,742 с 9,661 с 1,25x

Как видите, прирост по сравнению с реализацией JavaScript значителен: почти в три раза быстрее в случае индексации, более чем в два раза быстрее для функций сокращения и фильтрации. Прирост относительно невелик для функций обновления, но все же быстрее, чем в JavaScript.

Заключение

Как и обещал автор документации, написать собственный сервер запросов не так уж сложно, если следовать протоколу сервера запросов. Несмотря на то, что CouchGO! в целом отсутствует несколько устаревших функций, он обеспечивает значительный прирост по сравнению с версией JavaScript даже на этой ранней стадии разработки. Я считаю, что еще есть много возможностей для улучшений.

Если вам нужен весь код из этой статьи в одном месте, вы можете найти его в моем репозитории GitHub.

Спасибо, что прочитали эту статью. Я хотел бы услышать ваши мысли об этом решении. Вы бы использовали его со своим экземпляром CouchDB или, может быть, вы уже используете какой-то собственный сервер запросов? Буду рад услышать об этом в комментариях.

Не забудьте прочитать другие мои статьи, чтобы узнать больше советов, идей и других частей этой серии по мере их создания. Удачного взлома!

Заявление о выпуске Эта статья воспроизведена по адресу: https://dev.to/kishieel/couchgo-enhancing-couchdb-with-query-server-writing-in-go-304n?1 Если есть какие-либо нарушения, пожалуйста, свяжитесь с [email protected] удалить его
Последний учебник Более>

Изучайте китайский

Отказ от ответственности: Все предоставленные ресурсы частично взяты из Интернета. В случае нарушения ваших авторских прав или других прав и интересов, пожалуйста, объясните подробные причины и предоставьте доказательства авторских прав или прав и интересов, а затем отправьте их по электронной почте: [email protected]. Мы сделаем это за вас как можно скорее.

Copyright© 2022 湘ICP备2022001581号-3