Рубрики
Без рубрики

Запустите MongoDB Atlas локально для тестирования

Что происходит с вашим набором реплик MongoDB, когда дело доходит до сбоев, таких как разделение сети, перезагрузка… Помечен как mongodb, java, тестовые контейнеры, докер.

Что происходит с вашим набором реплик MongoDB, когда дело доходит до сбоев, таких как разделение сети, перезапуск, перенастройка существующей топологии и т.д.? Этот вопрос особенно важен в наши дни из-за популярности, которую приобрела модель с несколькими облаками , где шансы на эти сценарии вполне реалистичны.

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

Взглянув на MongoDB Atlas и их бесплатные Общие кластеры , мы не можем настроить набор реплик, заданный по умолчанию, который является основным с двумя вторичными элементами (далее P-S-S), не говоря уже о тестировании некоторых функций, таких как restartPrimaries . Все это предназначено для выделенных кластеров, требующих оплаты по почасовой ставке.

В то время как облачные решения, по-видимому, являются фактическим выбором для производственных систем, некоторые разработчики программного обеспечения, скорее всего, будут нуждаться в бесплатных локальных альтернативах, что подтверждается такими проектами, как LocalStack, act и др.

Учитывая это, как насчет разработки решения для локального запуска некоторых функций, которые выполняет платная версия MongoDB Atlas? Одним из возможных способов является выбор в пользу следующего:

  • Докер для запуска контейнеров MongoDB;
  • Тестовые контейнеры для обработки контейнеров с помощью языка программирования, такого как Java;
  • Foxyproxy для моделирования сетевых условий.

Все хорошо, но для создания кластера из более чем одного узла MongoDB требуется, чтобы “каждый член набора реплик был доступен с помощью разрешаемого DNS или имен хостов” . Если вы запускаете Docker версии 18.03+ на хостах, таких как Mac и Win, или Docker 20.04+ на Linux, вы можете свободно использовать специальное DNS-имя хост.докер.внутренний . Во время установки Docker может добавить его в файл хоста вашей операционной системы. Однако для тех, кто по какой-либо причине не может обновить свой докер, мы можем использовать специальный контейнер, который перенаправляет трафик на хост, например Qoomon докер-хост . Аналогично, мы можем добавить docker host в файл хоста ОС, а также запустить контейнер с NET_ADMIN и NET_RAW возможностями ядра.

Хорошей новостью является то, что нам не нужно соединять все вышеупомянутые части вместе вручную, а вместо этого мы можем воспользоваться бесплатным Набор реплик MongoDB .

Давайте начнем с создания нового проекта Gradle с Java и добавления некоторых зависимостей от Maven central:

testCompile("com.github.silaev:mongodb-replica-set:0.4.3")
testImplementation("org.mongodb:mongodb-driver-sync:4.2.1")

1) Обратите внимание, что вам решать, какой драйвер MongoDB использовать здесь, примером является mongodb-driver-sync .

Затем нам нужно проверить и, возможно, изменить файл хоста нашей операционной системы. Что касается host.docker.internal , он может быть уже там, в противном случае добавьте 127.0.0.1 host.docker.internal . Согласно dockerhost , добавьте 127.0.0.1 dockerhost . Вы должны выбрать только один из них и использовать его при выполнении теста.

Наше путешествие начинается, и мы готовы написать тест для имитации разделения сети в P-S-S. Вот его описание:

try (
  final MongoDbReplicaSet mongoReplicaSet = MongoDbReplicaSet.builder()
    .mongoDockerImageName("mongo:4.4.4")
    .useHostDockerInternal(true)
    .addToxiproxy(true)
    .replicaSetNumber(3)
    .commandLineOptions(Arrays.asList("--oplogSize", "50"))
    .build()
) {

1) Используйте mongo:4.4.4 в качестве последнего образа докера MongoDB на момент написания; 2) Если внутренний докер хоста пользователя верно, используйте host.docker.internal докера, в противном случае используйте докерхост общего хоста докера; 3) Поместите контейнер ToxiproxyContainer. ContainerProxy для каждого узла MongoDB; 4) Установите 3 (возможно до 7) элемента для построения ПРОШЛОГО; 5) При необходимости добавьте некоторые параметры командной строки, например, установите 50 МБ в качестве журнала операций репликации; 6) Автоматическое закрытие всех контейнеров с помощью try-with-resources в случае любого исключения или завершения.

Теперь мы можем запустить набор реплик с помощью mongo Replica Set.start() , получить его URL-адрес и сделать некоторые утверждения:

final String replicaSetUrl = mongoReplicaSet.getReplicaSetUrl();
assertThat(
  mongoReplicaSet.nodeStates(mongoReplicaSet.getMongoRsStatus().getMembers())
).containsExactlyInAnyOrder(PRIMARY, SECONDARY, SECONDARY);

1) Внутренне getMongoRsStatus() вызывает rs.status() в оболочке MongoDB.

Тогда мы можем, например, создать MongoClient для вставки некоторых данных и последующего их утверждения (см. полный пример на Github ).

try (
  final MongoClient mongoSyncClient = MongoClients.create(new ConnectionString(replicaSetUrl))
) {

1) Обратите внимание, что мы также можем использовать более удобные настройки MongoClient в качестве параметра метода create для установки тайм-аутов, проблем с чтением/записью, отключите повторные попытки на уровне соединения.

Первый сбой происходит здесь, поэтому позвольте нашему набору реплик пережить отключение главного узла:

// TODO: Insert a document here to assert total number at the end
final MongoNode masterNodeBeforeFailure1 = mongoReplicaSet.getMasterMongoNode(
  mongoReplicaSet.getMongoRsStatus().getMembers()
);
mongoReplicaSet.disconnectNodeFromNetwork(masterNodeBeforeFailure1);
mongoReplicaSet.waitForMasterReelection(masterNodeBeforeFailure1);
assertThat(
  mongoReplicaSet.nodeStates(mongoReplicaSet.getMongoRsStatus().getMembers())
).containsExactlyInAnyOrder(PRIMARY, SECONDARY, DOWN);

1) Нам нужно дождаться нового главного узла, выбранного путем предоставления предыдущего главного узла методу waitForMasterReelection(...) .

Идя дальше, следующая авария приводит к отключению главного узла новичка:

// TODO: Insert a document here to assert total number at the end
final MongoNode masterNodeBeforeFailure2 = mongoReplicaSet.getMasterMongoNode(
  mongoReplicaSet.getMongoRsStatus().getMembers()
);
mongoReplicaSet.disconnectNodeFromNetwork(masterNodeBeforeFailure2);
mongoReplicaSet.waitForMongoNodesDown(2);
assertThat(
  mongoReplicaSet.nodeStates(mongoReplicaSet.getMongoRsStatus().getMembers())
).containsExactlyInAnyOrder(SECONDARY, DOWN, DOWN);

1) Мы ждем момента, когда наш единственный вторичный узел обнаружит, что остальные 2 узла не работают.

Наше путешествие подходит к концу, так что давайте вернем все отсоединенные узлы обратно в виде счастливого конца:

// TODO: Insert a document here to assert total number at the end
mongoReplicaSet.connectNodeToNetwork(masterNodeBeforeFailure1);
mongoReplicaSet.connectNodeToNetwork(masterNodeBeforeFailure2);
mongoReplicaSet.waitForAllMongoNodesUp();
mongoReplicaSet.waitForMaster();
assertThat(
  mongoReplicaSet.nodeStates(mongoReplicaSet.getMongoRsStatus().getMembers())
).containsExactlyInAnyOrder(PRIMARY, SECONDARY, SECONDARY);

1) Метод подождите, пока все узлы Монго будут запущены (...) ожидает, пока все отключенные узлы будут запущены и запущены; 2) Затем метод wait For Master() ожидает завершения выборов.

Вывод

Зачем писать такие тесты? Чтобы ответить на этот вопрос, давайте установим проблему записи как большинство с включенным ведением журнала и проблемой чтения как большинство также. Затем мы можем заменить //TODO:... в вышеупомянутых примерах кода с помощью mongoSyncClient.insertOne(…)

Связи:

  1. Найдите пример из этой статьи здесь ;
  2. Набор реплик MongoDB на Github .

Оригинал: “https://dev.to/silaev/run-mongodb-atlas-locally-for-testing-1mp9”