Как проверить поведение перезапуска GenServer?

В моем приложении у меня есть GenServer. Он создает резервные копии данных, необходимых для повторного запуска в агенте. Я хочу проверить, правильно ли мой GenServer выполняет резервное копирование и восстановление, поэтому я хотел запустить агент резервного копирования, затем перезапустить GenServer и посмотреть, работает ли он (помнит конфигурацию перед перезапуском).

Прямо сейчас я настроил и запустил GenServer (с start_supervised!) в тестовой настройке. Мне нужно как-то перезапустить этот GenServer.

Есть ли хороший способ сделать это? Должен ли я делать это совершенно по-другому? Есть ли другой, правильный способ тестирования поведения перезапуска?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
773
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Супервизор решает, когда перезапустить процесс, находящийся под его наблюдением, через child_spec этого дочернего процесса. По умолчанию, когда вы определяете свой модуль GenServer и используете use GenServer на модуле, по умолчанию (значения перезапуска) будет :permanent, что означает, что всегда перезапускайте этот процесс, если он завершается.

Учитывая это, должно быть достаточно отправить ему сигнал выхода с помощью Process.exit(your_gen_server_pid, :kill) (:kill гарантирует, что даже если процесс перехватывает выходы, он будет убит), а затем супервизор должен снова запустить процесс, и вы можете затем делать свои утверждения .

Вам понадобится способ обращения к «новому» процессу genserver, так как он будет убит, при перезапуске его pid не будет таким же, как было изначально, обычно вы делаете это, указав имя при его запуске.

Если ваш генератор загружает состояние как часть своего init, вам не обязательно контролировать его, чтобы проверить поведение резервного копирования, вы можете просто запустить его отдельно, убить его, а затем запустить снова.

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

Обновлено:

Чтобы справиться как с завершением процесса, так и с его повторным запуском, вы можете написать 2 вспомогательные функции, специально предназначенные для этого.

def ensure_exited(pid, timeout \\ 1_000) do
  true = Process.alive?(pid)
  ref = Process.monitor(pid)
  Process.exit(pid, :kill)
  receive do
     {:DOWN, ^ref, :process, ^pid, _reason} -> :ok
  after
    timeout -> :timeout
  end
end

Вы могли бы вместо этого взять name и сделать GenServer.whereis для получения pid, но идея та же.

Чтобы убедиться, что он жив:

def is_back_up?(name, max \\ 200, tries \\ 0) when tries <= max do
    case GenServer.whereis(name) do
       nil ->
         Process.sleep(5)
         is_back_up?(name, max, tries + 1)
       pid -> true
    end
end

def is_back_up?(_, _, _), do: false

Основная идея такова. Не уверен, что уже есть помощники для подобных вещей.

Затем вы просто используете это (вы можете написать 3-го помощника, который берет живой pid, имя и делает все это за один «шаг»), или написать:

:ok = ensure_exited(pid)
true = is_back_up?(name)

Это решило бы мою проблему, за исключением того, что ни убийство, ни перезапуск не происходят мгновенно, поэтому мне нужен способ дождаться завершения перезапуска. С другой стороны, PID не проблема, потому что все начинается с имени модуля. В жизни моего приложения нет момента, когда у актера было бы 2 экземпляра. Кроме того, у меня есть актеры, начинающиеся с setup (перед каждым тестом) с start_supervised, так что есть ли способ пропустить настройку для одного теста, чтобы я мог делать все вручную?

VOID404 27.07.2019 16:47

@ VOID404 да, я добавлю еще несколько деталей по вашим первым вопросам. Я не использовал ExUnit экстенсивно, поэтому для этого уже могут быть некоторые помощники, но легко написать 2, чтобы делать именно такие вещи. Что касается части setup, я не понимаю, что вы имеете в виду.

m3characters 27.07.2019 17:07

Я запускаю все, что контролируется, в обратном вызове установки ExUnit, поэтому ручное управление (как вы предложили - без присмотра) означало бы, что мне придется пропустить обратный вызов установки для одного теста.

VOID404 27.07.2019 17:19

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

m3characters 27.07.2019 17:24

Кроме того, обратите внимание, что проблема, о которой вы упомянули, также показывает «проблему», которую тест не покрывает, то есть, если при запуске фактической программы сервер завершает работу между «взаимодействиями» с ней, это приведет к сбою, поэтому вам нужно как-то учитывать и такую ​​возможность.

m3characters 27.07.2019 17:31

У меня почему-то не перезапускается процесс... Я начинаю так: start_supervised!(Bot.Heart, restart: :permanent), а останавливаю так: Process.exit(heart, :kill). У меня есть функция, которая ждет смерти, и функция, которая ждет, пока не найдет PID для заданного имени, и Process.alive?(pid). Последний работает вечно.

VOID404 27.07.2019 17:33

Ой. Ты прав. Я делаю всю эту «актерскую штучку» неправильно? Не могли бы вы порекомендовать некоторые ресурсы для самообразования?

VOID404 27.07.2019 17:36

Особо об эликсире я ничего не читал. Документация по Elixir и Erlang и множество тем в Интернете. В качестве книги я бы предложил «Программирование на Erlang» Джо Армстронга, она написана на языке erlang, но должна дать вам хорошее представление о семантике. У Sasã Juric также есть Эликсир в действии, который явно находится в Эликсире (у меня он есть, но я еще не читал), и я слышал о нем хорошие отзывы. Вы также можете проверить темы на elixirforum, там есть несколько тем о книгах и обзорах.

m3characters 27.07.2019 21:55

Другие вопросы по теме