CouchDB (не путать с Couchbase !) — документо-ориентированная СУБД, написанная на языке Erlang . В вышедшей недавно версии 2.0 в CouchDB добавили Riak -подобное шардирование, что наконец-то делает эту СУБД кому-то интересной. К тому же, в отличие от Riak, в CouchDB всегда была совершенно бесплатная (не в enterprise edition версии и т.д.) кросс-датацентровая master-slave и master-master репликация. Давайте попробуем разобраться, как же этим хозяйством вообще пользоваться, и подходит ли оно уже для продакшена.
Установка
Для своих страшных экспериментов я использовал Ubuntu 16.04. Готовых пакетов для различных дистрибутивов Linux разработчики CouchDB не предоставляют, поэтому имеет смысл один раз настроить его в Docker или LXC-контейнере , и потом использовать этот контейнер на всех узлах кластера. Для быстрой установки CouchDB мной был написан bash-скрипт . Имеет смысл сразу воспользоваться им. В ближайших двух параграфах я расскажу, как этот скрипт работает.
Ставим зависимости:
sudo apt-get —no-install-recommends -y install
build-essential pkg-config runit erlang
libicu-dev libmozjs185-dev libcurl4-openssl-dev
Качаем архив с исходниками с зеркала, которое предложат на официальном сайте. Затем говрим:
cd apache-couchdb-2.0.0 /
. / configure && make release
Создаем пользователя couchdb:
—no-create-home
—shell / bin / bash
—group —gecos
«CouchDB Administrator» couchdb
Копируем rel/couchdb в домашний каталог пользователя и выставляем правильные права:
sudo chown -R couchdb:couchdb / home / couchdb
sudo find / home / couchdb -type d -exec chmod 0770 { } ;
sudo sh -c ‘chmod 0644 /home/couchdb/etc/*’
Проверяем, что все было сделано правильно:
У меня в логи стали сыпаться ворнинги про отсутствующую базу данных _users. Не переживайте, мы это вот-вот пофиксим.
На http://localhost:5984 должны увидеть:
«couchdb»: «Welcome»,
«version»: «2.0.0»,
«vendor»: {
«name»: «The Apache Software Foundation»
}
}
Заметьте, что все общение клиентов с CouchDB происходит по REST API (иногда не REST, а просто какому-то кастомному HTTP API), со всеми вытекающими отсюда преимуществами и недостатками .
На http://localhost:5984/_utils/ увидим красивый веб-интерфейс вроде такого:
В веб-интерфейсе открываем Setup и создаем кластер из одной ноды (там все просто). После этого в логи перестанут сыпаться какие-либо ошибки.
Автозапуск через runit
Для автоматического запуска CouchDB при запуске системы разработчики рекомендуют использовать runit (как подсказал мне @listochkin , читается run-it, а не r-unit :).
Создаем директорию, куда будут писаться логи:
sudo chown couchdb:couchdb / var / log / couchdb
Создаем директории с конфигурацией для запуска couchdb:
sudo mkdir / etc / sv / couchdb / log
В /etc/sv/couchdb/log/run пишем:
exec svlogd -tt / var / log / couchdb
Опционально можно создать /etc/sv/couchdb/log/config c указанием, когда производить ротацию логов и подобного рода вешей, если дэфолты не устраивают. Формат конфига, а также дэфолты описаны в man svlogd
.
Также создаем скрипт /etc/sv/couchdb/run:
export HOME = / home / couchdb
exec 2 >& 1
exec chpst -u couchdb / home / couchdb / bin / couchdb
Делаем обоим скриптам chmod u+x
. Затем:
Через несколько секунд runit должен заметить новый сервис и запустить его. Управление:
sudo sv stop couchdb
sudo sv start couchdb
Можно проверить, что после рестарта системы CouchDB запускается автоматически.
Объединение нод в кластер
Важно! Максимальный размер кластера в CouchDB по умолчанию составляет 8 нод. Чтобы изменить это, нужно отредактировать значение параметра q в секции [cluster]
в etc/local.ini. Подробности см здесь .
Поднимаем три совершенно ненастроенные ноды. Если нода уже настроена, например, вы создали кластер в диалоге Setup, вот вам bash-скрипт , который все это откатывает.
Шаг 1. В /home/couchdb/etc/vm.args меняем параметры name и cookie. Кука должна быть супер секретной ( pwgen 16
) и одинаковой на всех нодах кластера; имена нод должны быть уникальны:
-setcookie eY2chohl4siecaib
Примечание: Узнать больше про распределенный Erlang и его куки можно из заметки Мои страшные эксперименты с Erlang и gen_server .
На всех нодах говорим:
Смотрим список известных нод:
Пример ответа:
«all_nodes»: [
«couchdb@10.110.2.4»
],
«cluster_nodes»: [
«couchdb@10.110.2.4»,
«couchdb@localhost»
]
}
Шаг 2. На каждой ноде указываем имя пользователя и пароль, а также какой IP-адрес должна слушать нода:
localhost: 5984 / _node / couchdb @ 10.110.2.4 / _config / admins / admin
-d ‘»password»‘
curl -X PUT
localhost: 5984 / _node / couchdb @ 10.110.2.4 / _config / chttpd / bind_address
-d ‘»0.0.0.0″‘ —user admin
Шаг 3. Объединяем ноды в кластер. Для этого на одной ноде для кадой ноды выполняем:
-H «Content-Type: application/json»
http: // localhost: 5984 / _cluster_setup
-d ‘{«action»:»enable_cluster»,»bind_address»:»0.0.0.0″,»username’
‘»:»admin»,»password»:»password»,»port»:5984,»remote_node»:»10.’
‘110.2.5″,»remote_current_user»:»admin»,»remote_current_password»:’
‘»password» }’
—user admin
curl -X POST
-H «Content-Type: application/json»
http: // localhost: 5984 / _cluster_setup
-d ‘{«action»: «add_node», «host»:»10.110.2.5″, «port»: «5984», ‘
‘»username»: «admin», «password»:»password»}’
—user admin
По окончании добавления нод говорим:
-H «Content-Type: application/json»
http: // localhost: 5984 / _cluster_setup
-d ‘{«action»: «finish_cluster»}’
—user admin
Проверяем, что ноды действительно объединились в кластер:
Список all_nodes — это ноды, которые сейчас онлайн, cluster_nodes — это вообще все существующие ноды.
Работа с документами
Итак, у нас есть настроенный кластер CouchDB. Попробуем сделать с ним что-нибудь сравнительно полезное.
Создаем пользователя для доступа к БД:
-H «Accept: application/json»
-H «Content-Type: application/json»
-d ‘{«name»: «afiskon», «password»: «secret», «roles»: [],’
‘ «type»: «user»}’
Заметьте, что пользователей в CouchDB может создавать кто угодно, как это делается на веб-сайтах. Тут пишут , что это такая фича.
Проверяем, что пользователь действительно создался:
-d ‘name=afiskon&password=secret’
Ответ:
Под админом создаем базу contacts (по аналогии с базой из заметки Пишем простой RESTful сервис с использованием Scotty ):
Даем права для работы с этой базой пользователю afiskon:
http: // localhost: 5984 / contacts / _security
-H «Content-Type: application/json»
-d ‘{«admins»: { «names»: [], «roles»: [] }, «members»: ‘
‘{ «names»: [«afiskon»], «roles»: [] } }’
—user admin
Проверяем, что пользователь теперь имеет доступ к базе:
В ответ приходит довольно большой документ с информацией о количестве документов в базе, сколько места на диске она занимает, и тд.
Проверяем, что пользователь не может удалить базу:
Для этого пользователь должен быть среди admins базы, а не members.
Создаем документ:
-d ‘{«name»:»Alex»,»phone»:»123″}’ —user afiskon
Ответ:
«ok»:true,
«id»:»contact_12345″,
«rev»:»1-85bda81e2e23351d07f0f9bbcb216032″
}
Читаем документ:
Ответ:
«_id»:»contact_12345″,
«_rev»:»1-85bda81e2e23351d07f0f9bbcb216032″,
«name»:»Alex»,
«phone»:»123″
}
Обновление и удаление документа выглядит так. Вы читаете весь документ, меняете его, и затем шлете запрос с указанием _rev считанного документа. Это нужно для ситуаций, когда один документ одновременно меняется несколькими клиентами. В сущности это означает, что вы всегда делаете CAS. Напомню, что в тех же Riak и Cassandra это не так. Поддержка CAS в частности означает, что поверх CouchDB можно написать распределенные транзакции .
Пример обновления документа:
http: // localhost: 5984 / contacts / contact_12345
-d ‘{«_rev»:»1-85bda81e2e23351d07f0f9bbcb216032″,»name»:»Alex»,’
‘»phone»:»456″}’
—user afiskon
В случае конфликта получим ответ:
Удаление документа:
‘http://localhost:5984/contacts/contact_12345’
‘?rev=2-972083a24122b6dc340e213ce50aa81e’
—user afiskon
А еще можно получить id всех документов в базе:
Пример ответа:
{
«id»:»contact_12345″,
«key»:»contact_12345″,
«value»:{«rev»:»6-4f271d4a106be6c1286b71a861c66bf1″}
}
]}
В качестве домашнего задания можете провести такой эксперимент. Уроните одну ноду. Убедитесь, что документ можно обновить, используя только оставшиеся две ноды. Затем поднимите упавшую ноду и быстро-быстро запросите с нее документ. Убедитесь, что никакого конфликта не происходит и вы получаете последнюю ревизию.
Разумеется, рассмотреть весь API CouchDB в рамках одной статьи не представляется возможным. Подробности вы найдете в официальной документации . Там еще немало интересного про аттачи, репликацию и views, а также специальный API поиска безо всякого MapReduce.
Заключение
Несмотря на то, что в первом приближении все вроде как здорово и замечательно, тащить эту систему в продакшен я бы лично не спешил.
Во-первых, никто не знает, сколько в ней еще никем не обнаруженных багов. Никогда не забуду, как в свое время мы радостно протащили в продакшен Couchbase, а спустя какое-то время выяснили, что кластер в нем может развалиться и не собраться обратно .
Во-вторых, внимательно прочитав документацию, я выявил несколько неприятных моментов. Оказывается, документы на самом деле никогда до конца не удаляются , сколько компакшенов не запускай. То есть, со временем база будет только расти. Что мешает вычищать такие документы спустя, скажем, месяц после их удаления, мне неясно. Кроме того, автоматического решардинга в CouchDB на данный момент нет , делать его придется вручную с scp и прочими плясками с бубнами. Наконец, я не обнаружил возможности указывать документам TTL, а это просто мастхев, если вы хотите класть в CouchDB сессии, коды капч, и подобного рода вещи.
Из приятных моментов хочется отметить, что CouchDB действительно пытается не терять данные. Например, он делает fsync на все записи. В отличие от него тот же Couchbase по умолчанию старается держать все в памяти, сбрасывая данные на диск лишь изредка. Впрочем, в Couchbase есть репликация, так что, возможно, это и не такая уж большая проблема. Еще из интересного — максимальный размер документа в CouchDB по умолчанию равен 64 Мб и может быть увеличен при помощи параметра max_document_size. Для сравнения, в MongoDB и Couchbase захардкожено, что докуметов размером больше 16 и 20 Мб соответственно не бывает. Также CouchDB из коробки поддерживает сжатие документов при помощи snappy и zlib , а также подписку на обновление документов при помощи change feeds, push/pull replication и возможности вызывать указанные программы при изменении документов.
Наконец, сообщество разработчиков CouchDB — пожалуй, самое открытое и дружелюбное из всех, что мне до сих пор встречались. Мейнтейнеры проекта отвечают на любые вопросы в IRC и охотно принимают посылаемые пулл реквесты (см раз , два ). Если вдруг вы ищите интересный open source проект для работы над ним в свободное время , лучше CouchDB вам будет сложно что-то найти.
Таковы мои впечатления от CouchDB. А вы что скажете?