Я создаю сервис поверх хранилища данных Google App Engine, которое в конечном итоге становится единым хранилищем данных. Для моего приложения это нормально.
Тем не менее, я разрабатываю тесты, которые делают такие вещи, как объект PUT, а затем объект GET и проверяют свойства возвращаемого объекта. К сожалению, поскольку хранилище данных в конечном итоге непротиворечиво, эти простые тесты не воспроизводимы.
Как вы тестируете в конечном итоге непротиворечивый сервис?
google-app-engine
eventual-consistency
google-cloud-datastore
Дуг Ричардсон
источник
источник
How can I reproducibly test an eventually consistent service?
- Ты не можешь. Вы должны удалить слово «воспроизводимо» или слово «в конце концов»; Вы не можете иметь оба.Ответы:
При разработке функциональных тестов учитывайте нефункциональные требования - если у вашей службы есть нефункциональное требование «Согласовано в течение x (секунд / минут / и т. Д.)», Просто запустите запросы PUT, подождите x, затем запустите запросы GET.
В этот момент, если данные еще не «поступили», вы можете считать, что запрос PUT не соответствует вашим требованиям.
источник
Вы действительно хотите, чтобы ваши тесты были быстрыми и последовательными. Если вы начнете создавать тесты, которые могут иногда давать сбой из-за возможной непротиворечивости, вы проигнорируете тест, если он потерпит неудачу, и тогда зачем он нужен?
Создайте поддельный сервис, который обрабатывает запросы PUT и GET, но имеет дополнительную операцию для обеспечения его согласованности. Ваш тест тогда:
Это позволяет вам проверить поведение вашего программного обеспечения, когда GET успешно получает объект PUT. Это также позволяет вам протестировать поведение вашего программного обеспечения, когда GET не находит объект (или правильный объект) из-за того, что сервис еще не согласован. Просто пропустите звонок
make_consistent()
.Все еще стоит иметь тесты, которые взаимодействуют с реальным сервисом, но они должны запускаться вне вашего обычного рабочего процесса разработки, поскольку они никогда не будут на 100% надежными (например, если сервис не работает). Эти тесты должны использоваться для:
источник
Итак. «Что ты тестируешь» - ключевой вопрос.
В этом случае вы должны издеваться над сервисами Google и всегда возвращать ответ.
В этом случае вы должны издеваться над сервисами Google и всегда возвращать временную ошибку до правильного ответа
Вы должны ввести настоящие сервисы Google и запустить тест. Но! Код, который вы тестируете, должен иметь встроенную обработку повторных ошибок (повтор). Так что вы должны получить последовательный ответ. (если Google очень плохо себя ведет)
источник
Используйте одно из следующего:
К сожалению, вы должны выбрать магические значения (N или продолжительность сна) для обеих этих техник.
источник
Насколько я понимаю, хранилище данных Google Cloud допускает как строго согласованные, так и в конечном итоге согласованные запросы .
Компромисс в том, что строго согласованные запросы довольно сильно ограничены по скорости (что-то, с чем вы можете жить во время тестирования).
Одной из возможностей может быть помещение ваших запросов в хранилище данных в оболочке, которая может обеспечить строгую согласованность для целей тестирования.
Например, вы можете вызывать методы
start_debug_strong_consistency()
иend_debug_strong_consistency()
.Метод start создает ключ, который можно использовать в качестве ключа предка для всех последующих запросов, а метод end удаляет ключ.
Единственное изменение в реальных запросах, которые вы тестируете, - это вызов,
setAncestor(your_debug_key)
если этот ключ существует.источник
Один подход, который хорош в теории, но не всегда может быть практичным, состоит в том, чтобы сделать все операции записи в тестируемой системе идемпотентными . Это означает, что, предполагая, что ваш тестовый код тестирует все в фиксированном последовательном порядке, вы можете повторить все чтения и все записи по отдельности, пока не получите ожидаемый результат, повторяя попытки до истечения некоторого времени ожидания, которое вы определяете в тестовом коде. То есть выполните операцию А1, повторяя, если необходимо, до тех пор, пока результат не станет В1, затем выполните операцию А2, повторяя попытку, если необходимо, до результата В2, и так далее.
Тогда вам не нужно беспокоиться о проверке предварительных условий операций записи, потому что операции записи уже будут проверять их для вас, и вы просто повторяете их, пока они не завершатся успешно!
Максимально используйте те же тайм-ауты «по умолчанию», которые можно увеличить, если вся система работает медленнее, и индивидуально переопределить значения по умолчанию при повторных попытках особенно медленных операций.
источник
Сервис, такой как хранилище данных Google App Engine, основан на репликации данных в нескольких точках присутствия (POP). Любой интеграционный тест для в конечном итоге непротиворечивой службы на самом деле является тестом скорости репликации этой службы в его наборе POP. Скорость, с которой контент распространяется на каждую POP в данной услуге, не будет одинаковой для каждой POP в пределах службы, в зависимости от ряда факторов, таких как метод репликации и различные проблемы Интернет-транспорта - это два примера которые составляют большинство отчетов в любой в конечном итоге непротиворечивой службе хранилища данных (по крайней мере, таков был мой опыт, пока я работал в крупной CDN).
Чтобы эффективно протестировать репликацию объекта на данной платформе, вам нужно настроить тест так, чтобы он запрашивал один и тот же недавно размещенный объект от конкретно каждой из POPs службы. Я предлагаю проверять список POP от одного до пяти раз или до тех пор, пока все отчеты POP в вашем списке POP не будут содержать объект. Вот набор интервалов для выполнения теста, который вы можете настроить: 1, 5, 60 минут, 12 часов, 25 часов после помещения его в хранилище данных. Ключом является регистрация результатов в каждом интервале для последующего просмотра и анализа, чтобы почувствовать способность данного сервиса глобально реплицировать объекты. Часто службы хранилища данных извлекают локальную копию в POP только после того, как она была запрошена локально [маршрутизация выполняется по протоколу BGP, поэтому ваш тест должен запросить объект из каждой конкретной POP, чтобы он был глобально действительным для данной платформы] , В случае хранилища данных Google вам нужно будет настроить тест для запроса данного объекта из «более 70 точек присутствия в 33 странах»; вам, скорее всего, придется получить список URL адресов, специфичных для POP, из службы поддержки Google [ref:https://cloud.google.com/about/locations/ ] или, если Google использует Fastly для репликации, Fastly Support [ https://www.fastly.com/resources ].
Несколько преимуществ этого метода: 1) Вы почувствуете платформу репликации данного сервиса, узнаете его сильные и слабые стороны в целом в глобальном масштабе [как это было во время интеграционного теста]. 2) Для любого объекта, который вы тестируете, у вас будет доступный инструмент для подогрева контента [сделайте первый запрос, который создаст копию в заданном локальном POP] - таким образом, вы обеспечите способ распространения контента по всему миру, прежде чем ваши клиенты запросят его у где-нибудь на земле.
источник
У меня есть опыт работы с Google App Engine Datastore. Удивительно, что он работает локально и часто более «в конечном итоге», чем «последовательный». Простейший пример: создать новую сущность, а затем извлечь ее. За последние 5 лет я часто видел, как локально работающий SDK не находил новую сущность сразу, а находил ее примерно через полсекунды.
Однако, работая с реальными серверами Google, я не видел такого поведения. Они стараются, чтобы ваш клиент Datastore всегда работал на одном и том же сервере, поэтому обычно любые изменения немедленно отражаются в запросах.
Мой совет для интеграционных тестов - запускать их на реальных серверах, и тогда вам, вероятно, не нужно будет вводить какие-либо фиктивные опросы или задержки для получения ваших результатов.
источник