Evalml: ユニットテストのタイムアウト(夕暮れの不安定性)

作成日 2021年06月09日  ·  11コメント  ·  ソース: alteryx/evalml

現在、ユニットテストがGHアクションの制限である6時間に達するのを確認しています。 これは明らかな理由で良くありません。

3.8コアデップ6時間タイムアウト(進行中)
build_conda_pkg、3.8コア深度、3.7非コア深度6時間のタイムアウト(進行中)
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件

この3.8コア部門からいくつかのデータを追加するだけで、一連のチェックが

github_unittests.txt

私が気づいたことの1つは、それらがすべて91〜93%の完了マークの周りで一時停止していることだと思います。 それらがどのテストであるかを理解することに価値があるとは思えませんが、それは追求するルートかもしれません。

3.9の非コア部門もう1つです。

github_unittests_2.txt

@chukarstenを提出していただきありがとうございます

ありがたいことに、これはbuild_conda_pkgだけでなく、通常の単体テストビルドで発生するため、原因としてcondaを除外できます。

これを理解するのに役立つ、収集する必要のある他の情報はありますか? 以下のいくつかのアイデア

  • タイムアウトをどの程度確実に再現できますか? ユニットテストジョブを実行する時間の50%は、多かれ少なかれ起こっていますか?
  • どのテストが正しく完了していませんか? pytestに各テストの開始と終了をログに記録させることができれば、ログを調べて、ハングが発生したときにどのテストが終了していないかを推測できます。 これは潜在的に有用に見えました。
  • pytestの並列化なしでテストを実行した場合でも、これらのタイムアウトが表示されますか?
  • これは単なる予感ですが、daskエンジンテストを無効にするとどうなりますか? 最近#2341でフレークがいくつか見られたのは知っています
  • テストの実行中のCPUとメモリの使用率はどのようになりますか?

@freddyaboultonこれは#2298と#1815に接続するので、ここに追加しました)

pytestを使用して詳細なロギングを行うようにMakefileを変更すると、次のログが得られ
。 これは、最後に実行されたテストが「evalml / tuners / random_search_tuner.py :: evalml.tuners.random_search_tuner.RandomSearchTuner」であることを示しています。

@freddyaboultonは確かにここで何かにDaskをしっかりと指しています。 daskユニットテストを分離するためにこのPRを作成しました。 失敗してもマージを妨げないという選択肢があると思います。 この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'

なぜこれが起こるのですか? まあ作用を受けるのデータは、そのデータへの参照を失っている場所にいるようです。 さらに、「労働者:[]」は、おそらく乳母のプロセスが労働者を殺していることを示唆しています。 データがどのように分散しているかに何かが起こっているのではないかと思いますが、これら4つのジョブが疑似並列/直列で一緒に実行されていることで何が起こっているのかについても疑わしいです。

このdask分散の問題は、クラスターの適応スケーリングを無効にすることを示唆しています。 残念ながら、アダプティブクラスターは使用せず、通常のローカルの静的クラスターのみを使用するため、問題はありません。 この問題は、ワーカーが放棄されているという問題の潜在的な原因としてデータが分散していることを示していますが、同じ接続エラーは発生していません。

#2376でdaskジョブを分離しようとし、DaskEngineのクライアントにbroadcast=Falseを設定した後、デフォルトで、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@freddyaboultonのようです。

この問題は、[このPR]ごとにdaskジョブ(https://github.com/alteryx/evalml/pull/2376)を分離するためにブロックされなくなり、このPRはdaskジョブをリファクタリングしてフレークを削減するようになりました。このPRは、メインへのマージをブロックしない個別のdaskジョブを作成し、このPRは、病理学的なdaskテストが最終的にGHアクションによってキャンセルされるのに6時間かかるのを防ぐためのタイムアウトを追加します。

dask関連のタイムアウトはもはや問題ではなく、予見可能な将来には問題にならないため、これをクローズに移動します。 ただし、根本的な原因はまだ不明です。

このページは役に立ちましたか?
0 / 5 - 0 評価