Evalml: Таймауты модульного тестирования (нестабильность Dask)

Созданный на 9 июн. 2021  ·  11Комментарии  ·  Источник: alteryx/evalml

В настоящее время мы видим, что модульные тесты выходят на ограничение GH Actions, равное 6 часам. Это нехорошо по понятным причинам.

3.8 core deps, тайм-аут 6 часов (в процессе)
build_conda_pkg, 3.8 core deps, 3.7 non-core deps, 6 hr timeout (в процессе)
3.7 непрофильный тайм-аут 6 часов
3.8 непрофильная глубина 6 часов тайм-аут
3,7 непрофильные от 1,5 часов
build_conda_pkg
3.7 непрофильные депс
3,8

blocker bug testing

Самый полезный комментарий

Теперь я вижу следующую трассировку стека в build_conda_pkg

[gw3] linux -- Python 3.7.10 $PREFIX/bin/python

X_y_binary_cls = (          0         1         2   ...        17        18        19
0  -0.039268  0.131912 -0.211206  ...  1.976989  ...ns], 0     0
1     0
2     1
3     1
4     1
     ..
95    1
96    1
97    1
98    1
99    0
Length: 100, dtype: int64)
cluster = LocalCluster(15c4b3ad, 'tcp://127.0.0.1:45201', workers=0, threads=0, memory=0 B)

    def test_submit_training_jobs_multiple(X_y_binary_cls, cluster):
        """Test that training multiple pipelines using the parallel engine produces the
        same results as the sequential engine."""
        X, y = X_y_binary_cls
        with Client(cluster) as client:
            pipelines = [
                BinaryClassificationPipeline(
                    component_graph=["Logistic Regression Classifier"],
                    parameters={"Logistic Regression Classifier": {"n_jobs": 1}},
                ),
                BinaryClassificationPipeline(component_graph=["Baseline Classifier"]),
                BinaryClassificationPipeline(component_graph=["SVM Classifier"]),
            ]

            def fit_pipelines(pipelines, engine):
                futures = []
                for pipeline in pipelines:
                    futures.append(
                        engine.submit_training_job(
                            X=X, y=y, automl_config=automl_data, pipeline=pipeline
                        )
                    )
                results = [f.get_result() for f in futures]
                return results

            # Verify all pipelines are trained and fitted.
            seq_pipelines = fit_pipelines(pipelines, SequentialEngine())
            for pipeline in seq_pipelines:
                assert pipeline._is_fitted

            # Verify all pipelines are trained and fitted.
>           par_pipelines = fit_pipelines(pipelines, DaskEngine(client=client))

evalml/tests/automl_tests/dask_tests/test_dask_engine.py:103: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
evalml/tests/automl_tests/dask_tests/test_dask_engine.py:94: in fit_pipelines
    results = [f.get_result() for f in futures]
evalml/tests/automl_tests/dask_tests/test_dask_engine.py:94: in <listcomp>
    results = [f.get_result() for f in futures]
evalml/automl/engine/dask_engine.py:30: in get_result
    return self.work.result()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Future: cancelled, key: train_pipeline-4bd4a99325cd3cc91144f86b64d6503c>
timeout = None

    def result(self, timeout=None):
        """Wait until computation completes, gather result to local process.

        If *timeout* seconds are elapsed before returning, a
        ``dask.distributed.TimeoutError`` is raised.
        """
        if self.client.asynchronous:
            return self.client.sync(self._result, callback_timeout=timeout)

        # shorten error traceback
        result = self.client.sync(self._result, callback_timeout=timeout, raiseit=False)
        if self.status == "error":
            typ, exc, tb = result
            raise exc.with_traceback(tb)
        elif self.status == "cancelled":
>           raise result
E           concurrent.futures._base.CancelledError: train_pipeline-4bd4a99325cd3cc91144f86b64d6503c

Кажется, это известная проблема в dask https://github.com/dask/distributed/issues/4612

Все 11 Комментарий

Просто добавив некоторые данные из этой серии проверок

github_unittests.txt

Думаю, я заметил одну вещь: все они останавливаются на отметке 91-93%. Я сомневаюсь, что есть какая-то ценность в выяснении, какие это тесты, но это может быть путь, которым нужно следовать.

Вот еще для 3,9 непрофильных дпс.

github_unittests_2.txt

Спасибо за подачу @chukarsten

К счастью, мы можем исключить conda как причину, поскольку это происходит для наших обычных сборок модульных тестов, а не только для build_conda_pkg

Есть ли другая информация, которую мы должны собрать, которая могла бы помочь нам в этом разобраться? Несколько идей ниже

  • Насколько надежно мы можем воспроизвести тайм-аут? Происходит ли это в 50% случаев, когда мы запускаем задание модульного тестирования, больше или меньше?
  • Какой тест или тесты не проходят должным образом? Если мы можем заставить pytest регистрировать начало и конец каждого теста, мы можем посмотреть журналы и определить, какой тест не закончился, когда происходит зависание. Это выглядело потенциально полезным.
  • Будем ли мы видеть эти тайм-ауты, если запускаем тесты без какого-либо распараллеливания pytest?
  • Это всего лишь догадка, но что будет, если мы отключим тесты движка dask? Я знаю, что недавно мы видели несколько хлопьев с этими # 2341
  • Как выглядит загрузка ЦП и памяти во время выполнения тестов?

( @freddyaboulton Я добавил вас сюда, так как это

Изменив Makefile для ведения подробного журнала с помощью pytest, мы получим следующий журнал
. Это показывает, что последним выполненным тестом будет "evalml / tuners / random_search_tuner.py :: evalml.tuners.random_search_tuner.RandomSearchTuner"

После добавления тайм-аута я видел такой же тайм-аут на test_dask_sends_woodwork_schema как минимум три раза:

  1. https://github.com/alteryx/evalml/pull/2374/checks?check_run_id=2804775673
  2. https://github.com/alteryx/evalml/pull/2374/checks?check_run_id=2804202831#step : 9: 92
  3. https://github.com/alteryx/evalml/runs/2804668851?check_suite_focus=true

Я думаю, что @freddyaboulton определенно что-то здесь этот пиар для отделения юнит-тестов dask. Я думаю, у нас есть возможность не предотвращать слияние в случае их неудачи. Этот PR не прошел на test_automl_immediate_quit, который все еще находится в массиве тестов dask.

Выяснение первопричины сбоев модульных тестов dask вызывает недоумение. Журналы генерируют много такого:

distributed.worker - WARNING - Could not find data: {'Series-32a3ef2ca4739b46a6acc2ac58638b32': ['tcp://127.0.0.1:45587']} on workers: [] (who_has: {'Series-32a3ef2ca4739b46a6acc2ac58638b32': ['tcp://127.0.0.1:45587']})
distributed.scheduler - WARNING - Communication failed during replication: {'status': 'missing-data', 'keys'

Почему это происходит? Кажется, что везде, где обрабатываются данные, теряется ссылка на эти данные. Кроме того, «рабочие: []» предполагает, что, возможно, процесс няни убивает рабочих. Я подозреваю, что что-то происходит с разбросом данных, но я также с подозрением отношусь к тому, что происходит под прикрытием, когда эти четыре задания работают вместе в псевдопараллельном / последовательном режиме.

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

После попытки # 2376 разделить задания dask и установки broadcast=False для клиента DaskEngine по умолчанию у меня возникла нестабильная ошибка теста с test_automl_immediate_quit. Документировано здесь .

Теперь я вижу следующую трассировку стека в build_conda_pkg

[gw3] linux -- Python 3.7.10 $PREFIX/bin/python

X_y_binary_cls = (          0         1         2   ...        17        18        19
0  -0.039268  0.131912 -0.211206  ...  1.976989  ...ns], 0     0
1     0
2     1
3     1
4     1
     ..
95    1
96    1
97    1
98    1
99    0
Length: 100, dtype: int64)
cluster = LocalCluster(15c4b3ad, 'tcp://127.0.0.1:45201', workers=0, threads=0, memory=0 B)

    def test_submit_training_jobs_multiple(X_y_binary_cls, cluster):
        """Test that training multiple pipelines using the parallel engine produces the
        same results as the sequential engine."""
        X, y = X_y_binary_cls
        with Client(cluster) as client:
            pipelines = [
                BinaryClassificationPipeline(
                    component_graph=["Logistic Regression Classifier"],
                    parameters={"Logistic Regression Classifier": {"n_jobs": 1}},
                ),
                BinaryClassificationPipeline(component_graph=["Baseline Classifier"]),
                BinaryClassificationPipeline(component_graph=["SVM Classifier"]),
            ]

            def fit_pipelines(pipelines, engine):
                futures = []
                for pipeline in pipelines:
                    futures.append(
                        engine.submit_training_job(
                            X=X, y=y, automl_config=automl_data, pipeline=pipeline
                        )
                    )
                results = [f.get_result() for f in futures]
                return results

            # Verify all pipelines are trained and fitted.
            seq_pipelines = fit_pipelines(pipelines, SequentialEngine())
            for pipeline in seq_pipelines:
                assert pipeline._is_fitted

            # Verify all pipelines are trained and fitted.
>           par_pipelines = fit_pipelines(pipelines, DaskEngine(client=client))

evalml/tests/automl_tests/dask_tests/test_dask_engine.py:103: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
evalml/tests/automl_tests/dask_tests/test_dask_engine.py:94: in fit_pipelines
    results = [f.get_result() for f in futures]
evalml/tests/automl_tests/dask_tests/test_dask_engine.py:94: in <listcomp>
    results = [f.get_result() for f in futures]
evalml/automl/engine/dask_engine.py:30: in get_result
    return self.work.result()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Future: cancelled, key: train_pipeline-4bd4a99325cd3cc91144f86b64d6503c>
timeout = None

    def result(self, timeout=None):
        """Wait until computation completes, gather result to local process.

        If *timeout* seconds are elapsed before returning, a
        ``dask.distributed.TimeoutError`` is raised.
        """
        if self.client.asynchronous:
            return self.client.sync(self._result, callback_timeout=timeout)

        # shorten error traceback
        result = self.client.sync(self._result, callback_timeout=timeout, raiseit=False)
        if self.status == "error":
            typ, exc, tb = result
            raise exc.with_traceback(tb)
        elif self.status == "cancelled":
>           raise result
E           concurrent.futures._base.CancelledError: train_pipeline-4bd4a99325cd3cc91144f86b64d6503c

Кажется, это известная проблема в dask https://github.com/dask/distributed/issues/4612

Мой старый пост удален, но вот красный: https://github.com/alteryx/evalml/actions/runs/939673304 , похоже, та же трассировка стека @freddyaboulton, опубликованная выше.

Я считаю, что эта проблема больше не блокируется на [этот PR], чтобы отделить задания dask (https://github.com/alteryx/evalml/pull/2376), этот PR для рефакторинга заданий dask, чтобы сократить количество хлопьев и этот PR, чтобы отдельные задания dask не блокировались для слияния с main, и этот PR, чтобы добавить тайм-аут, чтобы патологические тесты dask не заняли 6 часов и в конечном итоге были отменены GH Actions.

Собираюсь переместить это в состояние closed, потому что таймауты, связанные с dask, теперь больше не являются проблемой и не должны быть в обозримом будущем. Однако основная причина до сих пор неизвестна.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги