Комментарии 6
Привет, Хабр! На связи главный адвокат MongoDB Всея Руси :D
А если серьезно, спасибо за проделанную работу и статью.
Не понял насчет writeMajority и компенсирующих действий - ретрай изначального запроса тут подходит ? Или если запрос был +100 папугаев на счет господина А, а потом мажорити не набралось и клиент (как-то) понимает что нужны эти компенсирующие действия - в таком случае повторный запрос даст +200 в итоге или нет ?
Отличный вопрос, "главный адвокат"!
Компенсирующие действия клиента будут зависеть от типа запроса. Полученную от сервера ошибку нужно интерпретировать как неопределенное состояние базы данных. Поэтому, если запрос к базе данных идемпотентный, то, конечно, его можно повторить. Если же запрос опирается на данные в базе, то перед выполнением ретрая необходимо выяснить реальное состояние данных: возможно, они уже изменились на сервере, и тогда следует отказаться от ретрая, чтобы на счету не оказалось +200 попугаев.
Однако есть «подводный камень», который неочевиден: ошибка может означать, что данные были применены на primary-узле, но потенциально могут исчезнуть при неблагоприятных условиях (см. раздел «Потеря данных в наборе реплик»). Поэтому перед ретраем неидемпотентного запроса потребуется проверить согласованность данных в наборе реплик, то есть прочитать данные дважды: сначала с primary-узла (параметры {readConcern: "local", readPreference: "primary"}), а затем — данные, подтверждённые большинством узлов (параметр {readConcern: "majority"}).
Если данные различаются, то сначала нужно дождаться репликации и достижения согласованного состояния всего набора реплик. Если же данные совпали, состояние набора реплик считается согласованным, и на основе этих данных можно принимать решение о повторной отправке запроса.
Я правильно понимаю, что при выполнении запросов к MongoDB с параметрами CP, если будет риск нарушения согласованности данных (например, в момент аварии на части узлов), то произойдет ошибка, но при этом сам запрос может изменить данные в MongoDB?
Да, все верно. Ошибка - это сигнал о неопределенном состоянии данных в системе. И прежде чем продолжать работу, нужно выяснить реальное состояние и возможно дождаться достижения согласованного состояния в узлах системы.
Как я отмечал в статье, при работе с любой базой данных, в том числе и Postgresql, могут произойти разные ситуации приводящие к одной и тоже ошибке. Например:
запрос клиента может дойти до сервера БД выполниться, но ответ об успешном выполнении операции может не дойти до клиента и клиент получит ошибку timeout,
запрос клиента может не дойти до сервера БД, соответственно не выполниться, и клиент получит точно такую же ошибку timeout.
Привет, Сергей, в примере с потерянными данными возможна ли настройка, при которой мастер не сможет вернуться кластер, чтобы не потерять данные? То есть руками затем как-то эти данные сохранить и вернуть ноду в кластер, избежав потери.
Такой настройки нет. Данные от "выпавшего" мастера не вернуть обратно в реплику, если реплика уже переизбрала другого мастера и ушла вперед.
Но если правильно установить параметры запросов и не игнорировать сообщения об ошибках, которые приходят из базы данных, то возвращать такие данные в реплику и не потребуется.
Тестирование CAP-теоремы на примере MongoDB: аварийные ситуации