Fix backfill_job_runner to work with custom executors - #32101

Closed
adh-wonolo wants to merge 1 commit into
apache:mainfrom
adh-wonolo:fix/backfill-custom-executor
Closed

Fix backfill_job_runner to work with custom executors#32101
adh-wonolo wants to merge 1 commit into
apache:mainfrom
adh-wonolo:fix/backfill-custom-executor

Conversation

@adh-wonolo

Copy link
Copy Markdown
Contributor

Backfill Job Runner pulls in the class name of your executor but doesn't pull in the full path so if you aren't using a default core executor you get an error like:

 File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/module_loading.py", line 32, in import_string module_path, class_name = dotted_path.rsplit(".", 1)
ValueError: not enough values to unpack (expected 2, got 1)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 106, in load_executor executor_cls, import_source = cls.import_executor_cls(executor_name)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 148, in import_executor_cls return _import_and_validate(executor_name), ConnectorSource.CUSTOM_PATH
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 129, in _import_and_validate executor = import_string(path)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/module_loading.py", line 34, in import_string raise ImportError(f"{dotted_path} doesn't look like a module path")
ImportError: CustomExecutor doesn't look like a module path

This can be fixed either by just passing in the actual class to executor_class or passing in #f"{self.job.executor.__class__.__module__}.{self.job.executor_class}" to ExecutorLoader.import_executor_cls or setting self.job.executor_class to be #f"{self.job.executor.__class__.__module__}.{self.job.executor.__class__.__name}"

I'm not sure which of these three is the best solution, though in my quick read through the code it seems like this isn't really called elsewhere besides in this specific file.

I ran the core tests and they all passed.


^ Add meaningful description above

Read the Pull Request Guidelines for more information.
In case of fundamental code changes, an Airflow Improvement Proposal (AIP) is needed.
In case of a new dependency, check compliance with the ASF 3rd Party License Policy.
In case of backwards incompatible changes please leave a note in a newsfragment file, named {pr_number}.significant.rst or {issue_number}.significant.rst, in newsfragments.

@boring-cyborgboring-cyborgBot added the area:Scheduler including HA (high availability) scheduler label Jun 23, 2023
@boring-cyborg

Copy link
Copy Markdown

Congratulations on your first Pull Request and welcome to the Apache Airflow community! If you have any issues or are unsure about any anything please check our Contribution Guide (https://github.com/apache/airflow/blob/main/CONTRIBUTING.rst)
Here are some useful points:

  • Pay attention to the quality of your code (ruff, mypy and type annotations). Our pre-commits will help you with that.
  • In case of a new feature add useful documentation (in docstrings or in docs/ directory). Adding a new operator? Check this short guide Consider adding an example DAG that shows how users should use it.
  • Consider using Breeze environment for testing locally, it's a heavy docker but it ships with a working Airflow and a lot of integrations.
  • Be patient and persistent. It might take some time to get a review or get the final approval from Committers.
  • Please follow ASF Code of Conduct for all communication including (but not limited to) comments on Pull Requests, Mailing list and Slack.
  • Be sure to read the Airflow Coding style.
    Apache Airflow is a community-driven project and together we are making it better 🚀.
    In case of doubts contact the developers at:
    Mailing List: dev@airflow.apache.org
    Slack: https://s.apache.org/airflow-slack

@potiuk
potiuk requested a review from o-nikolasJune 23, 2023 22:52
@o-nikolas

Copy link
Copy Markdown
Contributor

Hmm, this one is odd, because import_executor_class (see below) is written to be able to import default executors and executors from plugins. So the real fix is there if something is broken, not in the backfill job.

defimport_executor_cls(cls, executor_name: str) ->tuple[type[BaseExecutor], ConnectorSource]:
"""
Imports the executor class.
Supports the same formats as ExecutorLoader.load_executor.
:return: executor class via executor_name and executor import source
"""
def_import_and_validate(path: str) ->type[BaseExecutor]:
executor=import_string(path)
cls.validate_database_executor_compatibility(executor)
returnexecutor
ifexecutor_nameincls.executors:
return_import_and_validate(cls.executors[executor_name]), ConnectorSource.CORE
ifexecutor_name.count(".") ==1:
log.debug(
"The executor name looks like the plugin path (executor_name=%s). Trying to import a "
"executor from a plugin",
executor_name,
)
withsuppress(ImportError, AttributeError):
# Load plugins here for executors as at that time the plugins might not have been
# initialized yet
fromairflowimportplugins_manager
plugins_manager.integrate_executor_plugins()
return_import_and_validate(f"airflow.executors.{executor_name}"), ConnectorSource.PLUGIN
return_import_and_validate(executor_name), ConnectorSource.CUSTOM_PATH

@adh-wonolo can you include a full traceback (the one in the description has some strange formatting making it hard to read). And also an example of the executor path you're using? You can change the values if any of them are private, but try keep the formatting as similar as possible.

@o-nikolas

Copy link
Copy Markdown
Contributor

Also this backfill code has changed since I last touched it for AIP-51.
I'm confused why we're checking executor compatibility for the executor class on self.job (self.job.executor) instead of the executor that's passed into this method (executor) used on the line below (invalidating the above check for local anyway?):

executor_class, _=ExecutorLoader.import_executor_cls(
self.job.executor_class,
)
ifexecutor_class.is_local:
cfg_path=tmp_configuration_copy()
executor.queue_task_instance(

@potiuk It looks like you made that change in #30255, do you have any context on that?

@adh-wonolo

Copy link
Copy Markdown
ContributorAuthor

@o-nikolas the executor loads and functions normally for everything other than backfills which you can see in the logs too, its just that when executing

executor_class, _=ExecutorLoader.import_executor_cls(
self.job.executor_class,
)
ifexecutor_class.is_local:
cfg_path=tmp_configuration_copy()
executor.queue_task_instance(

All that's passed is the name of the Executor (in NomadExecutor) rather than the full path which the importer code can only resolve for core executors, hence why this could also be fixed by instead passing the path:
f"{self.job.executor.__class__.__module__}.{self.job.executor_class}" to that function instead or setting that as the value for self.job.executor_class more directly.

I'm storing the plugin in ./plugins/executor/nomad_executor.py

Here's the stacktrace:

$ airflow dags backfill clean_airflow_metadb --start-date 20230501 --end-date 20230612 --reset-dagruns
/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/cli/commands/dag_command.py:119 RemovedInAirflow3Warning: --ignore-first-depends-on-past is deprecated as the value is always set to True
[2023-06-27T09:54:21.489-0400] {dagbag.py:541} INFO - Filling up the DagBag from /Users/adh/repos/internal-tools-airflow/dags
You are about to delete these 2 tasks:
<TaskInstance: clean_airflow_metadb.clean_xcoms backfill__2023-05-01T00:00:00+00:00 [queued]>
<TaskInstance: clean_airflow_metadb.clean_xcoms backfill__2023-06-01T00:00:00+00:00 [scheduled]>
Are you sure? [y/n]
y
[2023-06-27T09:54:29.538-0400] {executor_loader.py:114} INFO - Loaded executor: plugins.executor.nomad_executor.NomadExecutor
/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/plugins_manager.py:258 RemovedInAirflow3Warning: This decorator is deprecated.
In previous versions, all subclasses of BaseOperator must use apply_default decorator for the `default_args` feature to work properly.
In current version, it is optional. The decorator is applied automatically using the metaclass.
2023-06-27 09:54:29,667 - [bugsnag] WARNING - No API key configured, couldn't notify
[2023-06-27T09:54:29.667-0400] {client.py:170} WARNING - No API key configured, couldn't notify
Traceback (most recent call last):
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/module_loading.py", line 32, in import_string
module_path, class_name = dotted_path.rsplit(".", 1)
ValueError: not enough values to unpack (expected 2, got 1)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/adh/.pyenv/versions/airflow310/bin/airflow", line 8, in <module>
sys.exit(main())
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/__main__.py", line 48, in main
args.func(args)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/cli/cli_config.py", line 52, in command
return func(*args, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/cli.py", line 112, in wrapper
return f(*args, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/cli/commands/dag_command.py", line 139, in dag_backfill
_run_dag_backfill(dags, args)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/cli/commands/dag_command.py", line 92, in _run_dag_backfill
dag.run(
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/models/dag.py", line 2490, in run
run_job(job=job, execute_callable=job_runner._execute)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/session.py", line 76, in wrapper
return func(*args, session=session, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/job.py", line 284, in run_job
return execute_job(job, execute_callable=execute_callable)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/job.py", line 313, in execute_job
ret = execute_callable()
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/session.py", line 76, in wrapper
return func(*args, session=session, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/backfill_job_runner.py", line 914, in _execute
self._execute_dagruns(
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/session.py", line 73, in wrapper
return func(*args, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/backfill_job_runner.py", line 801, in _execute_dagruns
processed_dag_run_dates = self._process_backfill_task_instances(
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/backfill_job_runner.py", line 643, in _process_backfill_task_instances
_per_task_process(key, ti, session)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/backfill_job_runner.py", line 539, in _per_task_process
executor_class, _ = ExecutorLoader.import_executor_cls(
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 148, in import_executor_cls
return _import_and_validate(executor_name), ConnectorSource.CUSTOM_PATH
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 129, in _import_and_validate
executor = import_string(path)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/module_loading.py", line 34, in import_string
raise ImportError(f"{dotted_path} doesn't look like a module path")
ImportError: NomadExecutor doesn't look like a module path

Happy to answer any other questions!

@potiuk

Copy link
Copy Markdown
Member

I looked at it and it loooks like a remnant from an old past that sneaked-in when I was refactoring BaseJob. Seems that the "executor_class" which was stored in the BaseJob was actually a different than "executor_class" that has been used in a number of places to check executor compatibility - and using it from job was simply a mistake.

But I also found out that the "job.executor_class" is something of a dead-relic. It has not been used anywhere else (only in one place in tests where it was not really needed any more). So I took the liberty to remove it altogether.

I also applied the same fix as you did here @adh-wonolo, but with a small twist (there is no need to get class to run the is_local property - the way python works, if you have an object of the class, you can run class method/property directly on the object and it will use the class one if there is no object property defined.

So I will close this one in favour of mine: #32219

@potiuk

Copy link
Copy Markdown
Member

I also made you co-author of that change @adh-wonolo . thanks for letting us know and providing the fix proposal!

@potiuk

Copy link
Copy Markdown
Member

And merged / marked for 2.6.3 -> once agin thans @adh-wonolo for raising it and providing proposed fix (and thanks @o-nikolas for raising my attantion :).

@adh-wonolo

Copy link
Copy Markdown
ContributorAuthor

Thanks so much @potiuk! Looking forward to collaborate again in the future :)

@adh-wonolo
adh-wonolo deleted the fix/backfill-custom-executor branch June 28, 2023 13:10
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:Schedulerincluding HA (high availability) scheduler

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@adh-wonolo@o-nikolas@potiuk
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Fix backfill_job_runner to work with custom executors - #32101

Closed
adh-wonolo wants to merge 1 commit into
apache:mainfrom
adh-wonolo:fix/backfill-custom-executor
Closed

Fix backfill_job_runner to work with custom executors#32101
adh-wonolo wants to merge 1 commit into
apache:mainfrom
adh-wonolo:fix/backfill-custom-executor

Conversation

@adh-wonolo

Copy link
Copy Markdown
Contributor

Backfill Job Runner pulls in the class name of your executor but doesn't pull in the full path so if you aren't using a default core executor you get an error like:

 File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/module_loading.py", line 32, in import_string module_path, class_name = dotted_path.rsplit(".", 1)
ValueError: not enough values to unpack (expected 2, got 1)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 106, in load_executor executor_cls, import_source = cls.import_executor_cls(executor_name)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 148, in import_executor_cls return _import_and_validate(executor_name), ConnectorSource.CUSTOM_PATH
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 129, in _import_and_validate executor = import_string(path)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/module_loading.py", line 34, in import_string raise ImportError(f"{dotted_path} doesn't look like a module path")
ImportError: CustomExecutor doesn't look like a module path

This can be fixed either by just passing in the actual class to executor_class or passing in #f"{self.job.executor.__class__.__module__}.{self.job.executor_class}" to ExecutorLoader.import_executor_cls or setting self.job.executor_class to be #f"{self.job.executor.__class__.__module__}.{self.job.executor.__class__.__name}"

I'm not sure which of these three is the best solution, though in my quick read through the code it seems like this isn't really called elsewhere besides in this specific file.

I ran the core tests and they all passed.


^ Add meaningful description above

Read the Pull Request Guidelines for more information.
In case of fundamental code changes, an Airflow Improvement Proposal (AIP) is needed.
In case of a new dependency, check compliance with the ASF 3rd Party License Policy.
In case of backwards incompatible changes please leave a note in a newsfragment file, named {pr_number}.significant.rst or {issue_number}.significant.rst, in newsfragments.

@boring-cyborgboring-cyborgBot added the area:Scheduler including HA (high availability) scheduler label Jun 23, 2023
@boring-cyborg

Copy link
Copy Markdown

Congratulations on your first Pull Request and welcome to the Apache Airflow community! If you have any issues or are unsure about any anything please check our Contribution Guide (https://github.com/apache/airflow/blob/main/CONTRIBUTING.rst)
Here are some useful points:

  • Pay attention to the quality of your code (ruff, mypy and type annotations). Our pre-commits will help you with that.
  • In case of a new feature add useful documentation (in docstrings or in docs/ directory). Adding a new operator? Check this short guide Consider adding an example DAG that shows how users should use it.
  • Consider using Breeze environment for testing locally, it's a heavy docker but it ships with a working Airflow and a lot of integrations.
  • Be patient and persistent. It might take some time to get a review or get the final approval from Committers.
  • Please follow ASF Code of Conduct for all communication including (but not limited to) comments on Pull Requests, Mailing list and Slack.
  • Be sure to read the Airflow Coding style.
    Apache Airflow is a community-driven project and together we are making it better 🚀.
    In case of doubts contact the developers at:
    Mailing List: dev@airflow.apache.org
    Slack: https://s.apache.org/airflow-slack

@potiuk
potiuk requested a review from o-nikolasJune 23, 2023 22:52
@o-nikolas

Copy link
Copy Markdown
Contributor

Hmm, this one is odd, because import_executor_class (see below) is written to be able to import default executors and executors from plugins. So the real fix is there if something is broken, not in the backfill job.

defimport_executor_cls(cls, executor_name: str) ->tuple[type[BaseExecutor], ConnectorSource]:
"""
Imports the executor class.
Supports the same formats as ExecutorLoader.load_executor.
:return: executor class via executor_name and executor import source
"""
def_import_and_validate(path: str) ->type[BaseExecutor]:
executor=import_string(path)
cls.validate_database_executor_compatibility(executor)
returnexecutor
ifexecutor_nameincls.executors:
return_import_and_validate(cls.executors[executor_name]), ConnectorSource.CORE
ifexecutor_name.count(".") ==1:
log.debug(
"The executor name looks like the plugin path (executor_name=%s). Trying to import a "
"executor from a plugin",
executor_name,
)
withsuppress(ImportError, AttributeError):
# Load plugins here for executors as at that time the plugins might not have been
# initialized yet
fromairflowimportplugins_manager
plugins_manager.integrate_executor_plugins()
return_import_and_validate(f"airflow.executors.{executor_name}"), ConnectorSource.PLUGIN
return_import_and_validate(executor_name), ConnectorSource.CUSTOM_PATH

@adh-wonolo can you include a full traceback (the one in the description has some strange formatting making it hard to read). And also an example of the executor path you're using? You can change the values if any of them are private, but try keep the formatting as similar as possible.

@o-nikolas

Copy link
Copy Markdown
Contributor

Also this backfill code has changed since I last touched it for AIP-51.
I'm confused why we're checking executor compatibility for the executor class on self.job (self.job.executor) instead of the executor that's passed into this method (executor) used on the line below (invalidating the above check for local anyway?):

executor_class, _=ExecutorLoader.import_executor_cls(
self.job.executor_class,
)
ifexecutor_class.is_local:
cfg_path=tmp_configuration_copy()
executor.queue_task_instance(

@potiuk It looks like you made that change in #30255, do you have any context on that?

@adh-wonolo

Copy link
Copy Markdown
ContributorAuthor

@o-nikolas the executor loads and functions normally for everything other than backfills which you can see in the logs too, its just that when executing

executor_class, _=ExecutorLoader.import_executor_cls(
self.job.executor_class,
)
ifexecutor_class.is_local:
cfg_path=tmp_configuration_copy()
executor.queue_task_instance(

All that's passed is the name of the Executor (in NomadExecutor) rather than the full path which the importer code can only resolve for core executors, hence why this could also be fixed by instead passing the path:
f"{self.job.executor.__class__.__module__}.{self.job.executor_class}" to that function instead or setting that as the value for self.job.executor_class more directly.

I'm storing the plugin in ./plugins/executor/nomad_executor.py

Here's the stacktrace:

$ airflow dags backfill clean_airflow_metadb --start-date 20230501 --end-date 20230612 --reset-dagruns
/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/cli/commands/dag_command.py:119 RemovedInAirflow3Warning: --ignore-first-depends-on-past is deprecated as the value is always set to True
[2023-06-27T09:54:21.489-0400] {dagbag.py:541} INFO - Filling up the DagBag from /Users/adh/repos/internal-tools-airflow/dags
You are about to delete these 2 tasks:
<TaskInstance: clean_airflow_metadb.clean_xcoms backfill__2023-05-01T00:00:00+00:00 [queued]>
<TaskInstance: clean_airflow_metadb.clean_xcoms backfill__2023-06-01T00:00:00+00:00 [scheduled]>
Are you sure? [y/n]
y
[2023-06-27T09:54:29.538-0400] {executor_loader.py:114} INFO - Loaded executor: plugins.executor.nomad_executor.NomadExecutor
/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/plugins_manager.py:258 RemovedInAirflow3Warning: This decorator is deprecated.
In previous versions, all subclasses of BaseOperator must use apply_default decorator for the `default_args` feature to work properly.
In current version, it is optional. The decorator is applied automatically using the metaclass.
2023-06-27 09:54:29,667 - [bugsnag] WARNING - No API key configured, couldn't notify
[2023-06-27T09:54:29.667-0400] {client.py:170} WARNING - No API key configured, couldn't notify
Traceback (most recent call last):
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/module_loading.py", line 32, in import_string
module_path, class_name = dotted_path.rsplit(".", 1)
ValueError: not enough values to unpack (expected 2, got 1)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/adh/.pyenv/versions/airflow310/bin/airflow", line 8, in <module>
sys.exit(main())
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/__main__.py", line 48, in main
args.func(args)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/cli/cli_config.py", line 52, in command
return func(*args, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/cli.py", line 112, in wrapper
return f(*args, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/cli/commands/dag_command.py", line 139, in dag_backfill
_run_dag_backfill(dags, args)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/cli/commands/dag_command.py", line 92, in _run_dag_backfill
dag.run(
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/models/dag.py", line 2490, in run
run_job(job=job, execute_callable=job_runner._execute)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/session.py", line 76, in wrapper
return func(*args, session=session, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/job.py", line 284, in run_job
return execute_job(job, execute_callable=execute_callable)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/job.py", line 313, in execute_job
ret = execute_callable()
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/session.py", line 76, in wrapper
return func(*args, session=session, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/backfill_job_runner.py", line 914, in _execute
self._execute_dagruns(
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/session.py", line 73, in wrapper
return func(*args, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/backfill_job_runner.py", line 801, in _execute_dagruns
processed_dag_run_dates = self._process_backfill_task_instances(
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/backfill_job_runner.py", line 643, in _process_backfill_task_instances
_per_task_process(key, ti, session)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/backfill_job_runner.py", line 539, in _per_task_process
executor_class, _ = ExecutorLoader.import_executor_cls(
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 148, in import_executor_cls
return _import_and_validate(executor_name), ConnectorSource.CUSTOM_PATH
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 129, in _import_and_validate
executor = import_string(path)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/module_loading.py", line 34, in import_string
raise ImportError(f"{dotted_path} doesn't look like a module path")
ImportError: NomadExecutor doesn't look like a module path

Happy to answer any other questions!

@potiuk

Copy link
Copy Markdown
Member

I looked at it and it loooks like a remnant from an old past that sneaked-in when I was refactoring BaseJob. Seems that the "executor_class" which was stored in the BaseJob was actually a different than "executor_class" that has been used in a number of places to check executor compatibility - and using it from job was simply a mistake.

But I also found out that the "job.executor_class" is something of a dead-relic. It has not been used anywhere else (only in one place in tests where it was not really needed any more). So I took the liberty to remove it altogether.

I also applied the same fix as you did here @adh-wonolo, but with a small twist (there is no need to get class to run the is_local property - the way python works, if you have an object of the class, you can run class method/property directly on the object and it will use the class one if there is no object property defined.

So I will close this one in favour of mine: #32219

@potiuk

Copy link
Copy Markdown
Member

I also made you co-author of that change @adh-wonolo . thanks for letting us know and providing the fix proposal!

@potiuk

Copy link
Copy Markdown
Member

And merged / marked for 2.6.3 -> once agin thans @adh-wonolo for raising it and providing proposed fix (and thanks @o-nikolas for raising my attantion :).

@adh-wonolo

Copy link
Copy Markdown
ContributorAuthor

Thanks so much @potiuk! Looking forward to collaborate again in the future :)

@adh-wonolo
adh-wonolo deleted the fix/backfill-custom-executor branch June 28, 2023 13:10
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:Schedulerincluding HA (high availability) scheduler

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@adh-wonolo@o-nikolas@potiuk
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix backfill_job_runner to work with custom executors - #32101

Closed
adh-wonolo wants to merge 1 commit into
apache:mainfrom
adh-wonolo:fix/backfill-custom-executor
Closed

Fix backfill_job_runner to work with custom executors#32101
adh-wonolo wants to merge 1 commit into
apache:mainfrom
adh-wonolo:fix/backfill-custom-executor

Conversation

@adh-wonolo

Copy link
Copy Markdown
Contributor

Backfill Job Runner pulls in the class name of your executor but doesn't pull in the full path so if you aren't using a default core executor you get an error like:

 File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/module_loading.py", line 32, in import_string module_path, class_name = dotted_path.rsplit(".", 1)
ValueError: not enough values to unpack (expected 2, got 1)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 106, in load_executor executor_cls, import_source = cls.import_executor_cls(executor_name)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 148, in import_executor_cls return _import_and_validate(executor_name), ConnectorSource.CUSTOM_PATH
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 129, in _import_and_validate executor = import_string(path)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/module_loading.py", line 34, in import_string raise ImportError(f"{dotted_path} doesn't look like a module path")
ImportError: CustomExecutor doesn't look like a module path

This can be fixed either by just passing in the actual class to executor_class or passing in #f"{self.job.executor.__class__.__module__}.{self.job.executor_class}" to ExecutorLoader.import_executor_cls or setting self.job.executor_class to be #f"{self.job.executor.__class__.__module__}.{self.job.executor.__class__.__name}"

I'm not sure which of these three is the best solution, though in my quick read through the code it seems like this isn't really called elsewhere besides in this specific file.

I ran the core tests and they all passed.


^ Add meaningful description above

Read the Pull Request Guidelines for more information.
In case of fundamental code changes, an Airflow Improvement Proposal (AIP) is needed.
In case of a new dependency, check compliance with the ASF 3rd Party License Policy.
In case of backwards incompatible changes please leave a note in a newsfragment file, named {pr_number}.significant.rst or {issue_number}.significant.rst, in newsfragments.

@boring-cyborgboring-cyborgBot added the area:Scheduler including HA (high availability) scheduler label Jun 23, 2023
@boring-cyborg

Copy link
Copy Markdown

Congratulations on your first Pull Request and welcome to the Apache Airflow community! If you have any issues or are unsure about any anything please check our Contribution Guide (https://github.com/apache/airflow/blob/main/CONTRIBUTING.rst)
Here are some useful points:

  • Pay attention to the quality of your code (ruff, mypy and type annotations). Our pre-commits will help you with that.
  • In case of a new feature add useful documentation (in docstrings or in docs/ directory). Adding a new operator? Check this short guide Consider adding an example DAG that shows how users should use it.
  • Consider using Breeze environment for testing locally, it's a heavy docker but it ships with a working Airflow and a lot of integrations.
  • Be patient and persistent. It might take some time to get a review or get the final approval from Committers.
  • Please follow ASF Code of Conduct for all communication including (but not limited to) comments on Pull Requests, Mailing list and Slack.
  • Be sure to read the Airflow Coding style.
    Apache Airflow is a community-driven project and together we are making it better 🚀.
    In case of doubts contact the developers at:
    Mailing List: dev@airflow.apache.org
    Slack: https://s.apache.org/airflow-slack

@potiuk
potiuk requested a review from o-nikolasJune 23, 2023 22:52
@o-nikolas

Copy link
Copy Markdown
Contributor

Hmm, this one is odd, because import_executor_class (see below) is written to be able to import default executors and executors from plugins. So the real fix is there if something is broken, not in the backfill job.

defimport_executor_cls(cls, executor_name: str) ->tuple[type[BaseExecutor], ConnectorSource]:
"""
Imports the executor class.
Supports the same formats as ExecutorLoader.load_executor.
:return: executor class via executor_name and executor import source
"""
def_import_and_validate(path: str) ->type[BaseExecutor]:
executor=import_string(path)
cls.validate_database_executor_compatibility(executor)
returnexecutor
ifexecutor_nameincls.executors:
return_import_and_validate(cls.executors[executor_name]), ConnectorSource.CORE
ifexecutor_name.count(".") ==1:
log.debug(
"The executor name looks like the plugin path (executor_name=%s). Trying to import a "
"executor from a plugin",
executor_name,
)
withsuppress(ImportError, AttributeError):
# Load plugins here for executors as at that time the plugins might not have been
# initialized yet
fromairflowimportplugins_manager
plugins_manager.integrate_executor_plugins()
return_import_and_validate(f"airflow.executors.{executor_name}"), ConnectorSource.PLUGIN
return_import_and_validate(executor_name), ConnectorSource.CUSTOM_PATH

@adh-wonolo can you include a full traceback (the one in the description has some strange formatting making it hard to read). And also an example of the executor path you're using? You can change the values if any of them are private, but try keep the formatting as similar as possible.

@o-nikolas

Copy link
Copy Markdown
Contributor

Also this backfill code has changed since I last touched it for AIP-51.
I'm confused why we're checking executor compatibility for the executor class on self.job (self.job.executor) instead of the executor that's passed into this method (executor) used on the line below (invalidating the above check for local anyway?):

executor_class, _=ExecutorLoader.import_executor_cls(
self.job.executor_class,
)
ifexecutor_class.is_local:
cfg_path=tmp_configuration_copy()
executor.queue_task_instance(

@potiuk It looks like you made that change in #30255, do you have any context on that?

@adh-wonolo

Copy link
Copy Markdown
ContributorAuthor

@o-nikolas the executor loads and functions normally for everything other than backfills which you can see in the logs too, its just that when executing

executor_class, _=ExecutorLoader.import_executor_cls(
self.job.executor_class,
)
ifexecutor_class.is_local:
cfg_path=tmp_configuration_copy()
executor.queue_task_instance(

All that's passed is the name of the Executor (in NomadExecutor) rather than the full path which the importer code can only resolve for core executors, hence why this could also be fixed by instead passing the path:
f"{self.job.executor.__class__.__module__}.{self.job.executor_class}" to that function instead or setting that as the value for self.job.executor_class more directly.

I'm storing the plugin in ./plugins/executor/nomad_executor.py

Here's the stacktrace:

$ airflow dags backfill clean_airflow_metadb --start-date 20230501 --end-date 20230612 --reset-dagruns
/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/cli/commands/dag_command.py:119 RemovedInAirflow3Warning: --ignore-first-depends-on-past is deprecated as the value is always set to True
[2023-06-27T09:54:21.489-0400] {dagbag.py:541} INFO - Filling up the DagBag from /Users/adh/repos/internal-tools-airflow/dags
You are about to delete these 2 tasks:
<TaskInstance: clean_airflow_metadb.clean_xcoms backfill__2023-05-01T00:00:00+00:00 [queued]>
<TaskInstance: clean_airflow_metadb.clean_xcoms backfill__2023-06-01T00:00:00+00:00 [scheduled]>
Are you sure? [y/n]
y
[2023-06-27T09:54:29.538-0400] {executor_loader.py:114} INFO - Loaded executor: plugins.executor.nomad_executor.NomadExecutor
/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/plugins_manager.py:258 RemovedInAirflow3Warning: This decorator is deprecated.
In previous versions, all subclasses of BaseOperator must use apply_default decorator for the `default_args` feature to work properly.
In current version, it is optional. The decorator is applied automatically using the metaclass.
2023-06-27 09:54:29,667 - [bugsnag] WARNING - No API key configured, couldn't notify
[2023-06-27T09:54:29.667-0400] {client.py:170} WARNING - No API key configured, couldn't notify
Traceback (most recent call last):
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/module_loading.py", line 32, in import_string
module_path, class_name = dotted_path.rsplit(".", 1)
ValueError: not enough values to unpack (expected 2, got 1)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/adh/.pyenv/versions/airflow310/bin/airflow", line 8, in <module>
sys.exit(main())
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/__main__.py", line 48, in main
args.func(args)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/cli/cli_config.py", line 52, in command
return func(*args, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/cli.py", line 112, in wrapper
return f(*args, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/cli/commands/dag_command.py", line 139, in dag_backfill
_run_dag_backfill(dags, args)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/cli/commands/dag_command.py", line 92, in _run_dag_backfill
dag.run(
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/models/dag.py", line 2490, in run
run_job(job=job, execute_callable=job_runner._execute)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/session.py", line 76, in wrapper
return func(*args, session=session, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/job.py", line 284, in run_job
return execute_job(job, execute_callable=execute_callable)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/job.py", line 313, in execute_job
ret = execute_callable()
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/session.py", line 76, in wrapper
return func(*args, session=session, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/backfill_job_runner.py", line 914, in _execute
self._execute_dagruns(
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/session.py", line 73, in wrapper
return func(*args, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/backfill_job_runner.py", line 801, in _execute_dagruns
processed_dag_run_dates = self._process_backfill_task_instances(
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/backfill_job_runner.py", line 643, in _process_backfill_task_instances
_per_task_process(key, ti, session)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/backfill_job_runner.py", line 539, in _per_task_process
executor_class, _ = ExecutorLoader.import_executor_cls(
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 148, in import_executor_cls
return _import_and_validate(executor_name), ConnectorSource.CUSTOM_PATH
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 129, in _import_and_validate
executor = import_string(path)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/module_loading.py", line 34, in import_string
raise ImportError(f"{dotted_path} doesn't look like a module path")
ImportError: NomadExecutor doesn't look like a module path

Happy to answer any other questions!

@potiuk

Copy link
Copy Markdown
Member

I looked at it and it loooks like a remnant from an old past that sneaked-in when I was refactoring BaseJob. Seems that the "executor_class" which was stored in the BaseJob was actually a different than "executor_class" that has been used in a number of places to check executor compatibility - and using it from job was simply a mistake.

But I also found out that the "job.executor_class" is something of a dead-relic. It has not been used anywhere else (only in one place in tests where it was not really needed any more). So I took the liberty to remove it altogether.

I also applied the same fix as you did here @adh-wonolo, but with a small twist (there is no need to get class to run the is_local property - the way python works, if you have an object of the class, you can run class method/property directly on the object and it will use the class one if there is no object property defined.

So I will close this one in favour of mine: #32219

@potiuk

Copy link
Copy Markdown
Member

I also made you co-author of that change @adh-wonolo . thanks for letting us know and providing the fix proposal!

@potiuk

Copy link
Copy Markdown
Member

And merged / marked for 2.6.3 -> once agin thans @adh-wonolo for raising it and providing proposed fix (and thanks @o-nikolas for raising my attantion :).

@adh-wonolo

Copy link
Copy Markdown
ContributorAuthor

Thanks so much @potiuk! Looking forward to collaborate again in the future :)

@adh-wonolo
adh-wonolo deleted the fix/backfill-custom-executor branch June 28, 2023 13:10
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:Schedulerincluding HA (high availability) scheduler

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@adh-wonolo@o-nikolas@potiuk
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix backfill_job_runner to work with custom executors - #32101

Closed
adh-wonolo wants to merge 1 commit into
apache:mainfrom
adh-wonolo:fix/backfill-custom-executor
Closed

Fix backfill_job_runner to work with custom executors#32101
adh-wonolo wants to merge 1 commit into
apache:mainfrom
adh-wonolo:fix/backfill-custom-executor

Conversation

@adh-wonolo

Copy link
Copy Markdown
Contributor

Backfill Job Runner pulls in the class name of your executor but doesn't pull in the full path so if you aren't using a default core executor you get an error like:

 File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/module_loading.py", line 32, in import_string module_path, class_name = dotted_path.rsplit(".", 1)
ValueError: not enough values to unpack (expected 2, got 1)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 106, in load_executor executor_cls, import_source = cls.import_executor_cls(executor_name)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 148, in import_executor_cls return _import_and_validate(executor_name), ConnectorSource.CUSTOM_PATH
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 129, in _import_and_validate executor = import_string(path)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/module_loading.py", line 34, in import_string raise ImportError(f"{dotted_path} doesn't look like a module path")
ImportError: CustomExecutor doesn't look like a module path

This can be fixed either by just passing in the actual class to executor_class or passing in #f"{self.job.executor.__class__.__module__}.{self.job.executor_class}" to ExecutorLoader.import_executor_cls or setting self.job.executor_class to be #f"{self.job.executor.__class__.__module__}.{self.job.executor.__class__.__name}"

I'm not sure which of these three is the best solution, though in my quick read through the code it seems like this isn't really called elsewhere besides in this specific file.

I ran the core tests and they all passed.


^ Add meaningful description above

Read the Pull Request Guidelines for more information.
In case of fundamental code changes, an Airflow Improvement Proposal (AIP) is needed.
In case of a new dependency, check compliance with the ASF 3rd Party License Policy.
In case of backwards incompatible changes please leave a note in a newsfragment file, named {pr_number}.significant.rst or {issue_number}.significant.rst, in newsfragments.

@boring-cyborgboring-cyborgBot added the area:Scheduler including HA (high availability) scheduler label Jun 23, 2023
@boring-cyborg

Copy link
Copy Markdown

Congratulations on your first Pull Request and welcome to the Apache Airflow community! If you have any issues or are unsure about any anything please check our Contribution Guide (https://github.com/apache/airflow/blob/main/CONTRIBUTING.rst)
Here are some useful points:

  • Pay attention to the quality of your code (ruff, mypy and type annotations). Our pre-commits will help you with that.
  • In case of a new feature add useful documentation (in docstrings or in docs/ directory). Adding a new operator? Check this short guide Consider adding an example DAG that shows how users should use it.
  • Consider using Breeze environment for testing locally, it's a heavy docker but it ships with a working Airflow and a lot of integrations.
  • Be patient and persistent. It might take some time to get a review or get the final approval from Committers.
  • Please follow ASF Code of Conduct for all communication including (but not limited to) comments on Pull Requests, Mailing list and Slack.
  • Be sure to read the Airflow Coding style.
    Apache Airflow is a community-driven project and together we are making it better 🚀.
    In case of doubts contact the developers at:
    Mailing List: dev@airflow.apache.org
    Slack: https://s.apache.org/airflow-slack

@potiuk
potiuk requested a review from o-nikolasJune 23, 2023 22:52
@o-nikolas

Copy link
Copy Markdown
Contributor

Hmm, this one is odd, because import_executor_class (see below) is written to be able to import default executors and executors from plugins. So the real fix is there if something is broken, not in the backfill job.

defimport_executor_cls(cls, executor_name: str) ->tuple[type[BaseExecutor], ConnectorSource]:
"""
Imports the executor class.
Supports the same formats as ExecutorLoader.load_executor.
:return: executor class via executor_name and executor import source
"""
def_import_and_validate(path: str) ->type[BaseExecutor]:
executor=import_string(path)
cls.validate_database_executor_compatibility(executor)
returnexecutor
ifexecutor_nameincls.executors:
return_import_and_validate(cls.executors[executor_name]), ConnectorSource.CORE
ifexecutor_name.count(".") ==1:
log.debug(
"The executor name looks like the plugin path (executor_name=%s). Trying to import a "
"executor from a plugin",
executor_name,
)
withsuppress(ImportError, AttributeError):
# Load plugins here for executors as at that time the plugins might not have been
# initialized yet
fromairflowimportplugins_manager
plugins_manager.integrate_executor_plugins()
return_import_and_validate(f"airflow.executors.{executor_name}"), ConnectorSource.PLUGIN
return_import_and_validate(executor_name), ConnectorSource.CUSTOM_PATH

@adh-wonolo can you include a full traceback (the one in the description has some strange formatting making it hard to read). And also an example of the executor path you're using? You can change the values if any of them are private, but try keep the formatting as similar as possible.

@o-nikolas

Copy link
Copy Markdown
Contributor

Also this backfill code has changed since I last touched it for AIP-51.
I'm confused why we're checking executor compatibility for the executor class on self.job (self.job.executor) instead of the executor that's passed into this method (executor) used on the line below (invalidating the above check for local anyway?):

executor_class, _=ExecutorLoader.import_executor_cls(
self.job.executor_class,
)
ifexecutor_class.is_local:
cfg_path=tmp_configuration_copy()
executor.queue_task_instance(

@potiuk It looks like you made that change in #30255, do you have any context on that?

@adh-wonolo

Copy link
Copy Markdown
ContributorAuthor

@o-nikolas the executor loads and functions normally for everything other than backfills which you can see in the logs too, its just that when executing

executor_class, _=ExecutorLoader.import_executor_cls(
self.job.executor_class,
)
ifexecutor_class.is_local:
cfg_path=tmp_configuration_copy()
executor.queue_task_instance(

All that's passed is the name of the Executor (in NomadExecutor) rather than the full path which the importer code can only resolve for core executors, hence why this could also be fixed by instead passing the path:
f"{self.job.executor.__class__.__module__}.{self.job.executor_class}" to that function instead or setting that as the value for self.job.executor_class more directly.

I'm storing the plugin in ./plugins/executor/nomad_executor.py

Here's the stacktrace:

$ airflow dags backfill clean_airflow_metadb --start-date 20230501 --end-date 20230612 --reset-dagruns
/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/cli/commands/dag_command.py:119 RemovedInAirflow3Warning: --ignore-first-depends-on-past is deprecated as the value is always set to True
[2023-06-27T09:54:21.489-0400] {dagbag.py:541} INFO - Filling up the DagBag from /Users/adh/repos/internal-tools-airflow/dags
You are about to delete these 2 tasks:
<TaskInstance: clean_airflow_metadb.clean_xcoms backfill__2023-05-01T00:00:00+00:00 [queued]>
<TaskInstance: clean_airflow_metadb.clean_xcoms backfill__2023-06-01T00:00:00+00:00 [scheduled]>
Are you sure? [y/n]
y
[2023-06-27T09:54:29.538-0400] {executor_loader.py:114} INFO - Loaded executor: plugins.executor.nomad_executor.NomadExecutor
/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/plugins_manager.py:258 RemovedInAirflow3Warning: This decorator is deprecated.
In previous versions, all subclasses of BaseOperator must use apply_default decorator for the `default_args` feature to work properly.
In current version, it is optional. The decorator is applied automatically using the metaclass.
2023-06-27 09:54:29,667 - [bugsnag] WARNING - No API key configured, couldn't notify
[2023-06-27T09:54:29.667-0400] {client.py:170} WARNING - No API key configured, couldn't notify
Traceback (most recent call last):
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/module_loading.py", line 32, in import_string
module_path, class_name = dotted_path.rsplit(".", 1)
ValueError: not enough values to unpack (expected 2, got 1)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/adh/.pyenv/versions/airflow310/bin/airflow", line 8, in <module>
sys.exit(main())
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/__main__.py", line 48, in main
args.func(args)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/cli/cli_config.py", line 52, in command
return func(*args, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/cli.py", line 112, in wrapper
return f(*args, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/cli/commands/dag_command.py", line 139, in dag_backfill
_run_dag_backfill(dags, args)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/cli/commands/dag_command.py", line 92, in _run_dag_backfill
dag.run(
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/models/dag.py", line 2490, in run
run_job(job=job, execute_callable=job_runner._execute)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/session.py", line 76, in wrapper
return func(*args, session=session, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/job.py", line 284, in run_job
return execute_job(job, execute_callable=execute_callable)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/job.py", line 313, in execute_job
ret = execute_callable()
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/session.py", line 76, in wrapper
return func(*args, session=session, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/backfill_job_runner.py", line 914, in _execute
self._execute_dagruns(
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/session.py", line 73, in wrapper
return func(*args, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/backfill_job_runner.py", line 801, in _execute_dagruns
processed_dag_run_dates = self._process_backfill_task_instances(
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/backfill_job_runner.py", line 643, in _process_backfill_task_instances
_per_task_process(key, ti, session)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/backfill_job_runner.py", line 539, in _per_task_process
executor_class, _ = ExecutorLoader.import_executor_cls(
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 148, in import_executor_cls
return _import_and_validate(executor_name), ConnectorSource.CUSTOM_PATH
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 129, in _import_and_validate
executor = import_string(path)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/module_loading.py", line 34, in import_string
raise ImportError(f"{dotted_path} doesn't look like a module path")
ImportError: NomadExecutor doesn't look like a module path

Happy to answer any other questions!

@potiuk

Copy link
Copy Markdown
Member

I looked at it and it loooks like a remnant from an old past that sneaked-in when I was refactoring BaseJob. Seems that the "executor_class" which was stored in the BaseJob was actually a different than "executor_class" that has been used in a number of places to check executor compatibility - and using it from job was simply a mistake.

But I also found out that the "job.executor_class" is something of a dead-relic. It has not been used anywhere else (only in one place in tests where it was not really needed any more). So I took the liberty to remove it altogether.

I also applied the same fix as you did here @adh-wonolo, but with a small twist (there is no need to get class to run the is_local property - the way python works, if you have an object of the class, you can run class method/property directly on the object and it will use the class one if there is no object property defined.

So I will close this one in favour of mine: #32219

@potiuk

Copy link
Copy Markdown
Member

I also made you co-author of that change @adh-wonolo . thanks for letting us know and providing the fix proposal!

@potiuk

Copy link
Copy Markdown
Member

And merged / marked for 2.6.3 -> once agin thans @adh-wonolo for raising it and providing proposed fix (and thanks @o-nikolas for raising my attantion :).

@adh-wonolo

Copy link
Copy Markdown
ContributorAuthor

Thanks so much @potiuk! Looking forward to collaborate again in the future :)

@adh-wonolo
adh-wonolo deleted the fix/backfill-custom-executor branch June 28, 2023 13:10
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:Schedulerincluding HA (high availability) scheduler

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@adh-wonolo@o-nikolas@potiuk
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Fix backfill_job_runner to work with custom executors - #32101

Closed
adh-wonolo wants to merge 1 commit into
apache:mainfrom
adh-wonolo:fix/backfill-custom-executor
Closed

Fix backfill_job_runner to work with custom executors#32101
adh-wonolo wants to merge 1 commit into
apache:mainfrom
adh-wonolo:fix/backfill-custom-executor

Conversation

@adh-wonolo

Copy link
Copy Markdown
Contributor

Backfill Job Runner pulls in the class name of your executor but doesn't pull in the full path so if you aren't using a default core executor you get an error like:

 File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/module_loading.py", line 32, in import_string module_path, class_name = dotted_path.rsplit(".", 1)
ValueError: not enough values to unpack (expected 2, got 1)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 106, in load_executor executor_cls, import_source = cls.import_executor_cls(executor_name)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 148, in import_executor_cls return _import_and_validate(executor_name), ConnectorSource.CUSTOM_PATH
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 129, in _import_and_validate executor = import_string(path)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/module_loading.py", line 34, in import_string raise ImportError(f"{dotted_path} doesn't look like a module path")
ImportError: CustomExecutor doesn't look like a module path

This can be fixed either by just passing in the actual class to executor_class or passing in #f"{self.job.executor.__class__.__module__}.{self.job.executor_class}" to ExecutorLoader.import_executor_cls or setting self.job.executor_class to be #f"{self.job.executor.__class__.__module__}.{self.job.executor.__class__.__name}"

I'm not sure which of these three is the best solution, though in my quick read through the code it seems like this isn't really called elsewhere besides in this specific file.

I ran the core tests and they all passed.


^ Add meaningful description above

Read the Pull Request Guidelines for more information.
In case of fundamental code changes, an Airflow Improvement Proposal (AIP) is needed.
In case of a new dependency, check compliance with the ASF 3rd Party License Policy.
In case of backwards incompatible changes please leave a note in a newsfragment file, named {pr_number}.significant.rst or {issue_number}.significant.rst, in newsfragments.

@boring-cyborgboring-cyborgBot added the area:Scheduler including HA (high availability) scheduler label Jun 23, 2023
@boring-cyborg

Copy link
Copy Markdown

Congratulations on your first Pull Request and welcome to the Apache Airflow community! If you have any issues or are unsure about any anything please check our Contribution Guide (https://github.com/apache/airflow/blob/main/CONTRIBUTING.rst)
Here are some useful points:

  • Pay attention to the quality of your code (ruff, mypy and type annotations). Our pre-commits will help you with that.
  • In case of a new feature add useful documentation (in docstrings or in docs/ directory). Adding a new operator? Check this short guide Consider adding an example DAG that shows how users should use it.
  • Consider using Breeze environment for testing locally, it's a heavy docker but it ships with a working Airflow and a lot of integrations.
  • Be patient and persistent. It might take some time to get a review or get the final approval from Committers.
  • Please follow ASF Code of Conduct for all communication including (but not limited to) comments on Pull Requests, Mailing list and Slack.
  • Be sure to read the Airflow Coding style.
    Apache Airflow is a community-driven project and together we are making it better 🚀.
    In case of doubts contact the developers at:
    Mailing List: dev@airflow.apache.org
    Slack: https://s.apache.org/airflow-slack

@potiuk
potiuk requested a review from o-nikolasJune 23, 2023 22:52
@o-nikolas

Copy link
Copy Markdown
Contributor

Hmm, this one is odd, because import_executor_class (see below) is written to be able to import default executors and executors from plugins. So the real fix is there if something is broken, not in the backfill job.

defimport_executor_cls(cls, executor_name: str) ->tuple[type[BaseExecutor], ConnectorSource]:
"""
Imports the executor class.
Supports the same formats as ExecutorLoader.load_executor.
:return: executor class via executor_name and executor import source
"""
def_import_and_validate(path: str) ->type[BaseExecutor]:
executor=import_string(path)
cls.validate_database_executor_compatibility(executor)
returnexecutor
ifexecutor_nameincls.executors:
return_import_and_validate(cls.executors[executor_name]), ConnectorSource.CORE
ifexecutor_name.count(".") ==1:
log.debug(
"The executor name looks like the plugin path (executor_name=%s). Trying to import a "
"executor from a plugin",
executor_name,
)
withsuppress(ImportError, AttributeError):
# Load plugins here for executors as at that time the plugins might not have been
# initialized yet
fromairflowimportplugins_manager
plugins_manager.integrate_executor_plugins()
return_import_and_validate(f"airflow.executors.{executor_name}"), ConnectorSource.PLUGIN
return_import_and_validate(executor_name), ConnectorSource.CUSTOM_PATH

@adh-wonolo can you include a full traceback (the one in the description has some strange formatting making it hard to read). And also an example of the executor path you're using? You can change the values if any of them are private, but try keep the formatting as similar as possible.

@o-nikolas

Copy link
Copy Markdown
Contributor

Also this backfill code has changed since I last touched it for AIP-51.
I'm confused why we're checking executor compatibility for the executor class on self.job (self.job.executor) instead of the executor that's passed into this method (executor) used on the line below (invalidating the above check for local anyway?):

executor_class, _=ExecutorLoader.import_executor_cls(
self.job.executor_class,
)
ifexecutor_class.is_local:
cfg_path=tmp_configuration_copy()
executor.queue_task_instance(

@potiuk It looks like you made that change in #30255, do you have any context on that?

@adh-wonolo

Copy link
Copy Markdown
ContributorAuthor

@o-nikolas the executor loads and functions normally for everything other than backfills which you can see in the logs too, its just that when executing

executor_class, _=ExecutorLoader.import_executor_cls(
self.job.executor_class,
)
ifexecutor_class.is_local:
cfg_path=tmp_configuration_copy()
executor.queue_task_instance(

All that's passed is the name of the Executor (in NomadExecutor) rather than the full path which the importer code can only resolve for core executors, hence why this could also be fixed by instead passing the path:
f"{self.job.executor.__class__.__module__}.{self.job.executor_class}" to that function instead or setting that as the value for self.job.executor_class more directly.

I'm storing the plugin in ./plugins/executor/nomad_executor.py

Here's the stacktrace:

$ airflow dags backfill clean_airflow_metadb --start-date 20230501 --end-date 20230612 --reset-dagruns
/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/cli/commands/dag_command.py:119 RemovedInAirflow3Warning: --ignore-first-depends-on-past is deprecated as the value is always set to True
[2023-06-27T09:54:21.489-0400] {dagbag.py:541} INFO - Filling up the DagBag from /Users/adh/repos/internal-tools-airflow/dags
You are about to delete these 2 tasks:
<TaskInstance: clean_airflow_metadb.clean_xcoms backfill__2023-05-01T00:00:00+00:00 [queued]>
<TaskInstance: clean_airflow_metadb.clean_xcoms backfill__2023-06-01T00:00:00+00:00 [scheduled]>
Are you sure? [y/n]
y
[2023-06-27T09:54:29.538-0400] {executor_loader.py:114} INFO - Loaded executor: plugins.executor.nomad_executor.NomadExecutor
/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/plugins_manager.py:258 RemovedInAirflow3Warning: This decorator is deprecated.
In previous versions, all subclasses of BaseOperator must use apply_default decorator for the `default_args` feature to work properly.
In current version, it is optional. The decorator is applied automatically using the metaclass.
2023-06-27 09:54:29,667 - [bugsnag] WARNING - No API key configured, couldn't notify
[2023-06-27T09:54:29.667-0400] {client.py:170} WARNING - No API key configured, couldn't notify
Traceback (most recent call last):
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/module_loading.py", line 32, in import_string
module_path, class_name = dotted_path.rsplit(".", 1)
ValueError: not enough values to unpack (expected 2, got 1)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/adh/.pyenv/versions/airflow310/bin/airflow", line 8, in <module>
sys.exit(main())
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/__main__.py", line 48, in main
args.func(args)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/cli/cli_config.py", line 52, in command
return func(*args, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/cli.py", line 112, in wrapper
return f(*args, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/cli/commands/dag_command.py", line 139, in dag_backfill
_run_dag_backfill(dags, args)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/cli/commands/dag_command.py", line 92, in _run_dag_backfill
dag.run(
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/models/dag.py", line 2490, in run
run_job(job=job, execute_callable=job_runner._execute)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/session.py", line 76, in wrapper
return func(*args, session=session, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/job.py", line 284, in run_job
return execute_job(job, execute_callable=execute_callable)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/job.py", line 313, in execute_job
ret = execute_callable()
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/session.py", line 76, in wrapper
return func(*args, session=session, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/backfill_job_runner.py", line 914, in _execute
self._execute_dagruns(
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/session.py", line 73, in wrapper
return func(*args, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/backfill_job_runner.py", line 801, in _execute_dagruns
processed_dag_run_dates = self._process_backfill_task_instances(
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/backfill_job_runner.py", line 643, in _process_backfill_task_instances
_per_task_process(key, ti, session)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/backfill_job_runner.py", line 539, in _per_task_process
executor_class, _ = ExecutorLoader.import_executor_cls(
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 148, in import_executor_cls
return _import_and_validate(executor_name), ConnectorSource.CUSTOM_PATH
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 129, in _import_and_validate
executor = import_string(path)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/module_loading.py", line 34, in import_string
raise ImportError(f"{dotted_path} doesn't look like a module path")
ImportError: NomadExecutor doesn't look like a module path

Happy to answer any other questions!

@potiuk

Copy link
Copy Markdown
Member

I looked at it and it loooks like a remnant from an old past that sneaked-in when I was refactoring BaseJob. Seems that the "executor_class" which was stored in the BaseJob was actually a different than "executor_class" that has been used in a number of places to check executor compatibility - and using it from job was simply a mistake.

But I also found out that the "job.executor_class" is something of a dead-relic. It has not been used anywhere else (only in one place in tests where it was not really needed any more). So I took the liberty to remove it altogether.

I also applied the same fix as you did here @adh-wonolo, but with a small twist (there is no need to get class to run the is_local property - the way python works, if you have an object of the class, you can run class method/property directly on the object and it will use the class one if there is no object property defined.

So I will close this one in favour of mine: #32219

@potiuk

Copy link
Copy Markdown
Member

I also made you co-author of that change @adh-wonolo . thanks for letting us know and providing the fix proposal!

@potiuk

Copy link
Copy Markdown
Member

And merged / marked for 2.6.3 -> once agin thans @adh-wonolo for raising it and providing proposed fix (and thanks @o-nikolas for raising my attantion :).

@adh-wonolo

Copy link
Copy Markdown
ContributorAuthor

Thanks so much @potiuk! Looking forward to collaborate again in the future :)

@adh-wonolo
adh-wonolo deleted the fix/backfill-custom-executor branch June 28, 2023 13:10
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:Schedulerincluding HA (high availability) scheduler

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@adh-wonolo@o-nikolas@potiuk
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix backfill_job_runner to work with custom executors - #32101

Closed
adh-wonolo wants to merge 1 commit into
apache:mainfrom
adh-wonolo:fix/backfill-custom-executor
Closed

Fix backfill_job_runner to work with custom executors#32101
adh-wonolo wants to merge 1 commit into
apache:mainfrom
adh-wonolo:fix/backfill-custom-executor

Conversation

@adh-wonolo

Copy link
Copy Markdown
Contributor

Backfill Job Runner pulls in the class name of your executor but doesn't pull in the full path so if you aren't using a default core executor you get an error like:

 File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/module_loading.py", line 32, in import_string module_path, class_name = dotted_path.rsplit(".", 1)
ValueError: not enough values to unpack (expected 2, got 1)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 106, in load_executor executor_cls, import_source = cls.import_executor_cls(executor_name)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 148, in import_executor_cls return _import_and_validate(executor_name), ConnectorSource.CUSTOM_PATH
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 129, in _import_and_validate executor = import_string(path)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/module_loading.py", line 34, in import_string raise ImportError(f"{dotted_path} doesn't look like a module path")
ImportError: CustomExecutor doesn't look like a module path

This can be fixed either by just passing in the actual class to executor_class or passing in #f"{self.job.executor.__class__.__module__}.{self.job.executor_class}" to ExecutorLoader.import_executor_cls or setting self.job.executor_class to be #f"{self.job.executor.__class__.__module__}.{self.job.executor.__class__.__name}"

I'm not sure which of these three is the best solution, though in my quick read through the code it seems like this isn't really called elsewhere besides in this specific file.

I ran the core tests and they all passed.


^ Add meaningful description above

Read the Pull Request Guidelines for more information.
In case of fundamental code changes, an Airflow Improvement Proposal (AIP) is needed.
In case of a new dependency, check compliance with the ASF 3rd Party License Policy.
In case of backwards incompatible changes please leave a note in a newsfragment file, named {pr_number}.significant.rst or {issue_number}.significant.rst, in newsfragments.

@boring-cyborgboring-cyborgBot added the area:Scheduler including HA (high availability) scheduler label Jun 23, 2023
@boring-cyborg

Copy link
Copy Markdown

Congratulations on your first Pull Request and welcome to the Apache Airflow community! If you have any issues or are unsure about any anything please check our Contribution Guide (https://github.com/apache/airflow/blob/main/CONTRIBUTING.rst)
Here are some useful points:

  • Pay attention to the quality of your code (ruff, mypy and type annotations). Our pre-commits will help you with that.
  • In case of a new feature add useful documentation (in docstrings or in docs/ directory). Adding a new operator? Check this short guide Consider adding an example DAG that shows how users should use it.
  • Consider using Breeze environment for testing locally, it's a heavy docker but it ships with a working Airflow and a lot of integrations.
  • Be patient and persistent. It might take some time to get a review or get the final approval from Committers.
  • Please follow ASF Code of Conduct for all communication including (but not limited to) comments on Pull Requests, Mailing list and Slack.
  • Be sure to read the Airflow Coding style.
    Apache Airflow is a community-driven project and together we are making it better 🚀.
    In case of doubts contact the developers at:
    Mailing List: dev@airflow.apache.org
    Slack: https://s.apache.org/airflow-slack

@potiuk
potiuk requested a review from o-nikolasJune 23, 2023 22:52
@o-nikolas

Copy link
Copy Markdown
Contributor

Hmm, this one is odd, because import_executor_class (see below) is written to be able to import default executors and executors from plugins. So the real fix is there if something is broken, not in the backfill job.

defimport_executor_cls(cls, executor_name: str) ->tuple[type[BaseExecutor], ConnectorSource]:
"""
Imports the executor class.
Supports the same formats as ExecutorLoader.load_executor.
:return: executor class via executor_name and executor import source
"""
def_import_and_validate(path: str) ->type[BaseExecutor]:
executor=import_string(path)
cls.validate_database_executor_compatibility(executor)
returnexecutor
ifexecutor_nameincls.executors:
return_import_and_validate(cls.executors[executor_name]), ConnectorSource.CORE
ifexecutor_name.count(".") ==1:
log.debug(
"The executor name looks like the plugin path (executor_name=%s). Trying to import a "
"executor from a plugin",
executor_name,
)
withsuppress(ImportError, AttributeError):
# Load plugins here for executors as at that time the plugins might not have been
# initialized yet
fromairflowimportplugins_manager
plugins_manager.integrate_executor_plugins()
return_import_and_validate(f"airflow.executors.{executor_name}"), ConnectorSource.PLUGIN
return_import_and_validate(executor_name), ConnectorSource.CUSTOM_PATH

@adh-wonolo can you include a full traceback (the one in the description has some strange formatting making it hard to read). And also an example of the executor path you're using? You can change the values if any of them are private, but try keep the formatting as similar as possible.

@o-nikolas

Copy link
Copy Markdown
Contributor

Also this backfill code has changed since I last touched it for AIP-51.
I'm confused why we're checking executor compatibility for the executor class on self.job (self.job.executor) instead of the executor that's passed into this method (executor) used on the line below (invalidating the above check for local anyway?):

executor_class, _=ExecutorLoader.import_executor_cls(
self.job.executor_class,
)
ifexecutor_class.is_local:
cfg_path=tmp_configuration_copy()
executor.queue_task_instance(

@potiuk It looks like you made that change in #30255, do you have any context on that?

@adh-wonolo

Copy link
Copy Markdown
ContributorAuthor

@o-nikolas the executor loads and functions normally for everything other than backfills which you can see in the logs too, its just that when executing

executor_class, _=ExecutorLoader.import_executor_cls(
self.job.executor_class,
)
ifexecutor_class.is_local:
cfg_path=tmp_configuration_copy()
executor.queue_task_instance(

All that's passed is the name of the Executor (in NomadExecutor) rather than the full path which the importer code can only resolve for core executors, hence why this could also be fixed by instead passing the path:
f"{self.job.executor.__class__.__module__}.{self.job.executor_class}" to that function instead or setting that as the value for self.job.executor_class more directly.

I'm storing the plugin in ./plugins/executor/nomad_executor.py

Here's the stacktrace:

$ airflow dags backfill clean_airflow_metadb --start-date 20230501 --end-date 20230612 --reset-dagruns
/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/cli/commands/dag_command.py:119 RemovedInAirflow3Warning: --ignore-first-depends-on-past is deprecated as the value is always set to True
[2023-06-27T09:54:21.489-0400] {dagbag.py:541} INFO - Filling up the DagBag from /Users/adh/repos/internal-tools-airflow/dags
You are about to delete these 2 tasks:
<TaskInstance: clean_airflow_metadb.clean_xcoms backfill__2023-05-01T00:00:00+00:00 [queued]>
<TaskInstance: clean_airflow_metadb.clean_xcoms backfill__2023-06-01T00:00:00+00:00 [scheduled]>
Are you sure? [y/n]
y
[2023-06-27T09:54:29.538-0400] {executor_loader.py:114} INFO - Loaded executor: plugins.executor.nomad_executor.NomadExecutor
/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/plugins_manager.py:258 RemovedInAirflow3Warning: This decorator is deprecated.
In previous versions, all subclasses of BaseOperator must use apply_default decorator for the `default_args` feature to work properly.
In current version, it is optional. The decorator is applied automatically using the metaclass.
2023-06-27 09:54:29,667 - [bugsnag] WARNING - No API key configured, couldn't notify
[2023-06-27T09:54:29.667-0400] {client.py:170} WARNING - No API key configured, couldn't notify
Traceback (most recent call last):
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/module_loading.py", line 32, in import_string
module_path, class_name = dotted_path.rsplit(".", 1)
ValueError: not enough values to unpack (expected 2, got 1)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/adh/.pyenv/versions/airflow310/bin/airflow", line 8, in <module>
sys.exit(main())
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/__main__.py", line 48, in main
args.func(args)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/cli/cli_config.py", line 52, in command
return func(*args, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/cli.py", line 112, in wrapper
return f(*args, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/cli/commands/dag_command.py", line 139, in dag_backfill
_run_dag_backfill(dags, args)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/cli/commands/dag_command.py", line 92, in _run_dag_backfill
dag.run(
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/models/dag.py", line 2490, in run
run_job(job=job, execute_callable=job_runner._execute)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/session.py", line 76, in wrapper
return func(*args, session=session, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/job.py", line 284, in run_job
return execute_job(job, execute_callable=execute_callable)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/job.py", line 313, in execute_job
ret = execute_callable()
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/session.py", line 76, in wrapper
return func(*args, session=session, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/backfill_job_runner.py", line 914, in _execute
self._execute_dagruns(
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/session.py", line 73, in wrapper
return func(*args, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/backfill_job_runner.py", line 801, in _execute_dagruns
processed_dag_run_dates = self._process_backfill_task_instances(
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/backfill_job_runner.py", line 643, in _process_backfill_task_instances
_per_task_process(key, ti, session)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/backfill_job_runner.py", line 539, in _per_task_process
executor_class, _ = ExecutorLoader.import_executor_cls(
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 148, in import_executor_cls
return _import_and_validate(executor_name), ConnectorSource.CUSTOM_PATH
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 129, in _import_and_validate
executor = import_string(path)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/module_loading.py", line 34, in import_string
raise ImportError(f"{dotted_path} doesn't look like a module path")
ImportError: NomadExecutor doesn't look like a module path

Happy to answer any other questions!

@potiuk

Copy link
Copy Markdown
Member

I looked at it and it loooks like a remnant from an old past that sneaked-in when I was refactoring BaseJob. Seems that the "executor_class" which was stored in the BaseJob was actually a different than "executor_class" that has been used in a number of places to check executor compatibility - and using it from job was simply a mistake.

But I also found out that the "job.executor_class" is something of a dead-relic. It has not been used anywhere else (only in one place in tests where it was not really needed any more). So I took the liberty to remove it altogether.

I also applied the same fix as you did here @adh-wonolo, but with a small twist (there is no need to get class to run the is_local property - the way python works, if you have an object of the class, you can run class method/property directly on the object and it will use the class one if there is no object property defined.

So I will close this one in favour of mine: #32219

@potiuk

Copy link
Copy Markdown
Member

I also made you co-author of that change @adh-wonolo . thanks for letting us know and providing the fix proposal!

@potiuk

Copy link
Copy Markdown
Member

And merged / marked for 2.6.3 -> once agin thans @adh-wonolo for raising it and providing proposed fix (and thanks @o-nikolas for raising my attantion :).

@adh-wonolo

Copy link
Copy Markdown
ContributorAuthor

Thanks so much @potiuk! Looking forward to collaborate again in the future :)

@adh-wonolo
adh-wonolo deleted the fix/backfill-custom-executor branch June 28, 2023 13:10
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:Schedulerincluding HA (high availability) scheduler

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@adh-wonolo@o-nikolas@potiuk
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix backfill_job_runner to work with custom executors - #32101

Closed
adh-wonolo wants to merge 1 commit into
apache:mainfrom
adh-wonolo:fix/backfill-custom-executor
Closed

Fix backfill_job_runner to work with custom executors#32101
adh-wonolo wants to merge 1 commit into
apache:mainfrom
adh-wonolo:fix/backfill-custom-executor

Conversation

@adh-wonolo

Copy link
Copy Markdown
Contributor

Backfill Job Runner pulls in the class name of your executor but doesn't pull in the full path so if you aren't using a default core executor you get an error like:

 File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/module_loading.py", line 32, in import_string module_path, class_name = dotted_path.rsplit(".", 1)
ValueError: not enough values to unpack (expected 2, got 1)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 106, in load_executor executor_cls, import_source = cls.import_executor_cls(executor_name)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 148, in import_executor_cls return _import_and_validate(executor_name), ConnectorSource.CUSTOM_PATH
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 129, in _import_and_validate executor = import_string(path)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/module_loading.py", line 34, in import_string raise ImportError(f"{dotted_path} doesn't look like a module path")
ImportError: CustomExecutor doesn't look like a module path

This can be fixed either by just passing in the actual class to executor_class or passing in #f"{self.job.executor.__class__.__module__}.{self.job.executor_class}" to ExecutorLoader.import_executor_cls or setting self.job.executor_class to be #f"{self.job.executor.__class__.__module__}.{self.job.executor.__class__.__name}"

I'm not sure which of these three is the best solution, though in my quick read through the code it seems like this isn't really called elsewhere besides in this specific file.

I ran the core tests and they all passed.


^ Add meaningful description above

Read the Pull Request Guidelines for more information.
In case of fundamental code changes, an Airflow Improvement Proposal (AIP) is needed.
In case of a new dependency, check compliance with the ASF 3rd Party License Policy.
In case of backwards incompatible changes please leave a note in a newsfragment file, named {pr_number}.significant.rst or {issue_number}.significant.rst, in newsfragments.

@boring-cyborgboring-cyborgBot added the area:Scheduler including HA (high availability) scheduler label Jun 23, 2023
@boring-cyborg

Copy link
Copy Markdown

Congratulations on your first Pull Request and welcome to the Apache Airflow community! If you have any issues or are unsure about any anything please check our Contribution Guide (https://github.com/apache/airflow/blob/main/CONTRIBUTING.rst)
Here are some useful points:

  • Pay attention to the quality of your code (ruff, mypy and type annotations). Our pre-commits will help you with that.
  • In case of a new feature add useful documentation (in docstrings or in docs/ directory). Adding a new operator? Check this short guide Consider adding an example DAG that shows how users should use it.
  • Consider using Breeze environment for testing locally, it's a heavy docker but it ships with a working Airflow and a lot of integrations.
  • Be patient and persistent. It might take some time to get a review or get the final approval from Committers.
  • Please follow ASF Code of Conduct for all communication including (but not limited to) comments on Pull Requests, Mailing list and Slack.
  • Be sure to read the Airflow Coding style.
    Apache Airflow is a community-driven project and together we are making it better 🚀.
    In case of doubts contact the developers at:
    Mailing List: dev@airflow.apache.org
    Slack: https://s.apache.org/airflow-slack

@potiuk
potiuk requested a review from o-nikolasJune 23, 2023 22:52
@o-nikolas

Copy link
Copy Markdown
Contributor

Hmm, this one is odd, because import_executor_class (see below) is written to be able to import default executors and executors from plugins. So the real fix is there if something is broken, not in the backfill job.

defimport_executor_cls(cls, executor_name: str) ->tuple[type[BaseExecutor], ConnectorSource]:
"""
Imports the executor class.
Supports the same formats as ExecutorLoader.load_executor.
:return: executor class via executor_name and executor import source
"""
def_import_and_validate(path: str) ->type[BaseExecutor]:
executor=import_string(path)
cls.validate_database_executor_compatibility(executor)
returnexecutor
ifexecutor_nameincls.executors:
return_import_and_validate(cls.executors[executor_name]), ConnectorSource.CORE
ifexecutor_name.count(".") ==1:
log.debug(
"The executor name looks like the plugin path (executor_name=%s). Trying to import a "
"executor from a plugin",
executor_name,
)
withsuppress(ImportError, AttributeError):
# Load plugins here for executors as at that time the plugins might not have been
# initialized yet
fromairflowimportplugins_manager
plugins_manager.integrate_executor_plugins()
return_import_and_validate(f"airflow.executors.{executor_name}"), ConnectorSource.PLUGIN
return_import_and_validate(executor_name), ConnectorSource.CUSTOM_PATH

@adh-wonolo can you include a full traceback (the one in the description has some strange formatting making it hard to read). And also an example of the executor path you're using? You can change the values if any of them are private, but try keep the formatting as similar as possible.

@o-nikolas

Copy link
Copy Markdown
Contributor

Also this backfill code has changed since I last touched it for AIP-51.
I'm confused why we're checking executor compatibility for the executor class on self.job (self.job.executor) instead of the executor that's passed into this method (executor) used on the line below (invalidating the above check for local anyway?):

executor_class, _=ExecutorLoader.import_executor_cls(
self.job.executor_class,
)
ifexecutor_class.is_local:
cfg_path=tmp_configuration_copy()
executor.queue_task_instance(

@potiuk It looks like you made that change in #30255, do you have any context on that?

@adh-wonolo

Copy link
Copy Markdown
ContributorAuthor

@o-nikolas the executor loads and functions normally for everything other than backfills which you can see in the logs too, its just that when executing

executor_class, _=ExecutorLoader.import_executor_cls(
self.job.executor_class,
)
ifexecutor_class.is_local:
cfg_path=tmp_configuration_copy()
executor.queue_task_instance(

All that's passed is the name of the Executor (in NomadExecutor) rather than the full path which the importer code can only resolve for core executors, hence why this could also be fixed by instead passing the path:
f"{self.job.executor.__class__.__module__}.{self.job.executor_class}" to that function instead or setting that as the value for self.job.executor_class more directly.

I'm storing the plugin in ./plugins/executor/nomad_executor.py

Here's the stacktrace:

$ airflow dags backfill clean_airflow_metadb --start-date 20230501 --end-date 20230612 --reset-dagruns
/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/cli/commands/dag_command.py:119 RemovedInAirflow3Warning: --ignore-first-depends-on-past is deprecated as the value is always set to True
[2023-06-27T09:54:21.489-0400] {dagbag.py:541} INFO - Filling up the DagBag from /Users/adh/repos/internal-tools-airflow/dags
You are about to delete these 2 tasks:
<TaskInstance: clean_airflow_metadb.clean_xcoms backfill__2023-05-01T00:00:00+00:00 [queued]>
<TaskInstance: clean_airflow_metadb.clean_xcoms backfill__2023-06-01T00:00:00+00:00 [scheduled]>
Are you sure? [y/n]
y
[2023-06-27T09:54:29.538-0400] {executor_loader.py:114} INFO - Loaded executor: plugins.executor.nomad_executor.NomadExecutor
/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/plugins_manager.py:258 RemovedInAirflow3Warning: This decorator is deprecated.
In previous versions, all subclasses of BaseOperator must use apply_default decorator for the `default_args` feature to work properly.
In current version, it is optional. The decorator is applied automatically using the metaclass.
2023-06-27 09:54:29,667 - [bugsnag] WARNING - No API key configured, couldn't notify
[2023-06-27T09:54:29.667-0400] {client.py:170} WARNING - No API key configured, couldn't notify
Traceback (most recent call last):
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/module_loading.py", line 32, in import_string
module_path, class_name = dotted_path.rsplit(".", 1)
ValueError: not enough values to unpack (expected 2, got 1)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/adh/.pyenv/versions/airflow310/bin/airflow", line 8, in <module>
sys.exit(main())
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/__main__.py", line 48, in main
args.func(args)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/cli/cli_config.py", line 52, in command
return func(*args, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/cli.py", line 112, in wrapper
return f(*args, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/cli/commands/dag_command.py", line 139, in dag_backfill
_run_dag_backfill(dags, args)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/cli/commands/dag_command.py", line 92, in _run_dag_backfill
dag.run(
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/models/dag.py", line 2490, in run
run_job(job=job, execute_callable=job_runner._execute)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/session.py", line 76, in wrapper
return func(*args, session=session, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/job.py", line 284, in run_job
return execute_job(job, execute_callable=execute_callable)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/job.py", line 313, in execute_job
ret = execute_callable()
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/session.py", line 76, in wrapper
return func(*args, session=session, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/backfill_job_runner.py", line 914, in _execute
self._execute_dagruns(
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/session.py", line 73, in wrapper
return func(*args, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/backfill_job_runner.py", line 801, in _execute_dagruns
processed_dag_run_dates = self._process_backfill_task_instances(
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/backfill_job_runner.py", line 643, in _process_backfill_task_instances
_per_task_process(key, ti, session)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/backfill_job_runner.py", line 539, in _per_task_process
executor_class, _ = ExecutorLoader.import_executor_cls(
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 148, in import_executor_cls
return _import_and_validate(executor_name), ConnectorSource.CUSTOM_PATH
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 129, in _import_and_validate
executor = import_string(path)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/module_loading.py", line 34, in import_string
raise ImportError(f"{dotted_path} doesn't look like a module path")
ImportError: NomadExecutor doesn't look like a module path

Happy to answer any other questions!

@potiuk

Copy link
Copy Markdown
Member

I looked at it and it loooks like a remnant from an old past that sneaked-in when I was refactoring BaseJob. Seems that the "executor_class" which was stored in the BaseJob was actually a different than "executor_class" that has been used in a number of places to check executor compatibility - and using it from job was simply a mistake.

But I also found out that the "job.executor_class" is something of a dead-relic. It has not been used anywhere else (only in one place in tests where it was not really needed any more). So I took the liberty to remove it altogether.

I also applied the same fix as you did here @adh-wonolo, but with a small twist (there is no need to get class to run the is_local property - the way python works, if you have an object of the class, you can run class method/property directly on the object and it will use the class one if there is no object property defined.

So I will close this one in favour of mine: #32219

@potiuk

Copy link
Copy Markdown
Member

I also made you co-author of that change @adh-wonolo . thanks for letting us know and providing the fix proposal!

@potiuk

Copy link
Copy Markdown
Member

And merged / marked for 2.6.3 -> once agin thans @adh-wonolo for raising it and providing proposed fix (and thanks @o-nikolas for raising my attantion :).

@adh-wonolo

Copy link
Copy Markdown
ContributorAuthor

Thanks so much @potiuk! Looking forward to collaborate again in the future :)

@adh-wonolo
adh-wonolo deleted the fix/backfill-custom-executor branch June 28, 2023 13:10
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:Schedulerincluding HA (high availability) scheduler

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@adh-wonolo@o-nikolas@potiuk
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Fix backfill_job_runner to work with custom executors - #32101

Closed
adh-wonolo wants to merge 1 commit into
apache:mainfrom
adh-wonolo:fix/backfill-custom-executor
Closed

Fix backfill_job_runner to work with custom executors#32101
adh-wonolo wants to merge 1 commit into
apache:mainfrom
adh-wonolo:fix/backfill-custom-executor

Conversation

@adh-wonolo

Copy link
Copy Markdown
Contributor

Backfill Job Runner pulls in the class name of your executor but doesn't pull in the full path so if you aren't using a default core executor you get an error like:

 File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/module_loading.py", line 32, in import_string module_path, class_name = dotted_path.rsplit(".", 1)
ValueError: not enough values to unpack (expected 2, got 1)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 106, in load_executor executor_cls, import_source = cls.import_executor_cls(executor_name)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 148, in import_executor_cls return _import_and_validate(executor_name), ConnectorSource.CUSTOM_PATH
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 129, in _import_and_validate executor = import_string(path)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/module_loading.py", line 34, in import_string raise ImportError(f"{dotted_path} doesn't look like a module path")
ImportError: CustomExecutor doesn't look like a module path

This can be fixed either by just passing in the actual class to executor_class or passing in #f"{self.job.executor.__class__.__module__}.{self.job.executor_class}" to ExecutorLoader.import_executor_cls or setting self.job.executor_class to be #f"{self.job.executor.__class__.__module__}.{self.job.executor.__class__.__name}"

I'm not sure which of these three is the best solution, though in my quick read through the code it seems like this isn't really called elsewhere besides in this specific file.

I ran the core tests and they all passed.


^ Add meaningful description above

Read the Pull Request Guidelines for more information.
In case of fundamental code changes, an Airflow Improvement Proposal (AIP) is needed.
In case of a new dependency, check compliance with the ASF 3rd Party License Policy.
In case of backwards incompatible changes please leave a note in a newsfragment file, named {pr_number}.significant.rst or {issue_number}.significant.rst, in newsfragments.

@boring-cyborgboring-cyborgBot added the area:Scheduler including HA (high availability) scheduler label Jun 23, 2023
@boring-cyborg

Copy link
Copy Markdown

Congratulations on your first Pull Request and welcome to the Apache Airflow community! If you have any issues or are unsure about any anything please check our Contribution Guide (https://github.com/apache/airflow/blob/main/CONTRIBUTING.rst)
Here are some useful points:

  • Pay attention to the quality of your code (ruff, mypy and type annotations). Our pre-commits will help you with that.
  • In case of a new feature add useful documentation (in docstrings or in docs/ directory). Adding a new operator? Check this short guide Consider adding an example DAG that shows how users should use it.
  • Consider using Breeze environment for testing locally, it's a heavy docker but it ships with a working Airflow and a lot of integrations.
  • Be patient and persistent. It might take some time to get a review or get the final approval from Committers.
  • Please follow ASF Code of Conduct for all communication including (but not limited to) comments on Pull Requests, Mailing list and Slack.
  • Be sure to read the Airflow Coding style.
    Apache Airflow is a community-driven project and together we are making it better 🚀.
    In case of doubts contact the developers at:
    Mailing List: dev@airflow.apache.org
    Slack: https://s.apache.org/airflow-slack

@potiuk
potiuk requested a review from o-nikolasJune 23, 2023 22:52
@o-nikolas

Copy link
Copy Markdown
Contributor

Hmm, this one is odd, because import_executor_class (see below) is written to be able to import default executors and executors from plugins. So the real fix is there if something is broken, not in the backfill job.

defimport_executor_cls(cls, executor_name: str) ->tuple[type[BaseExecutor], ConnectorSource]:
"""
Imports the executor class.
Supports the same formats as ExecutorLoader.load_executor.
:return: executor class via executor_name and executor import source
"""
def_import_and_validate(path: str) ->type[BaseExecutor]:
executor=import_string(path)
cls.validate_database_executor_compatibility(executor)
returnexecutor
ifexecutor_nameincls.executors:
return_import_and_validate(cls.executors[executor_name]), ConnectorSource.CORE
ifexecutor_name.count(".") ==1:
log.debug(
"The executor name looks like the plugin path (executor_name=%s). Trying to import a "
"executor from a plugin",
executor_name,
)
withsuppress(ImportError, AttributeError):
# Load plugins here for executors as at that time the plugins might not have been
# initialized yet
fromairflowimportplugins_manager
plugins_manager.integrate_executor_plugins()
return_import_and_validate(f"airflow.executors.{executor_name}"), ConnectorSource.PLUGIN
return_import_and_validate(executor_name), ConnectorSource.CUSTOM_PATH

@adh-wonolo can you include a full traceback (the one in the description has some strange formatting making it hard to read). And also an example of the executor path you're using? You can change the values if any of them are private, but try keep the formatting as similar as possible.

@o-nikolas

Copy link
Copy Markdown
Contributor

Also this backfill code has changed since I last touched it for AIP-51.
I'm confused why we're checking executor compatibility for the executor class on self.job (self.job.executor) instead of the executor that's passed into this method (executor) used on the line below (invalidating the above check for local anyway?):

executor_class, _=ExecutorLoader.import_executor_cls(
self.job.executor_class,
)
ifexecutor_class.is_local:
cfg_path=tmp_configuration_copy()
executor.queue_task_instance(

@potiuk It looks like you made that change in #30255, do you have any context on that?

@adh-wonolo

Copy link
Copy Markdown
ContributorAuthor

@o-nikolas the executor loads and functions normally for everything other than backfills which you can see in the logs too, its just that when executing

executor_class, _=ExecutorLoader.import_executor_cls(
self.job.executor_class,
)
ifexecutor_class.is_local:
cfg_path=tmp_configuration_copy()
executor.queue_task_instance(

All that's passed is the name of the Executor (in NomadExecutor) rather than the full path which the importer code can only resolve for core executors, hence why this could also be fixed by instead passing the path:
f"{self.job.executor.__class__.__module__}.{self.job.executor_class}" to that function instead or setting that as the value for self.job.executor_class more directly.

I'm storing the plugin in ./plugins/executor/nomad_executor.py

Here's the stacktrace:

$ airflow dags backfill clean_airflow_metadb --start-date 20230501 --end-date 20230612 --reset-dagruns
/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/cli/commands/dag_command.py:119 RemovedInAirflow3Warning: --ignore-first-depends-on-past is deprecated as the value is always set to True
[2023-06-27T09:54:21.489-0400] {dagbag.py:541} INFO - Filling up the DagBag from /Users/adh/repos/internal-tools-airflow/dags
You are about to delete these 2 tasks:
<TaskInstance: clean_airflow_metadb.clean_xcoms backfill__2023-05-01T00:00:00+00:00 [queued]>
<TaskInstance: clean_airflow_metadb.clean_xcoms backfill__2023-06-01T00:00:00+00:00 [scheduled]>
Are you sure? [y/n]
y
[2023-06-27T09:54:29.538-0400] {executor_loader.py:114} INFO - Loaded executor: plugins.executor.nomad_executor.NomadExecutor
/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/plugins_manager.py:258 RemovedInAirflow3Warning: This decorator is deprecated.
In previous versions, all subclasses of BaseOperator must use apply_default decorator for the `default_args` feature to work properly.
In current version, it is optional. The decorator is applied automatically using the metaclass.
2023-06-27 09:54:29,667 - [bugsnag] WARNING - No API key configured, couldn't notify
[2023-06-27T09:54:29.667-0400] {client.py:170} WARNING - No API key configured, couldn't notify
Traceback (most recent call last):
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/module_loading.py", line 32, in import_string
module_path, class_name = dotted_path.rsplit(".", 1)
ValueError: not enough values to unpack (expected 2, got 1)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/adh/.pyenv/versions/airflow310/bin/airflow", line 8, in <module>
sys.exit(main())
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/__main__.py", line 48, in main
args.func(args)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/cli/cli_config.py", line 52, in command
return func(*args, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/cli.py", line 112, in wrapper
return f(*args, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/cli/commands/dag_command.py", line 139, in dag_backfill
_run_dag_backfill(dags, args)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/cli/commands/dag_command.py", line 92, in _run_dag_backfill
dag.run(
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/models/dag.py", line 2490, in run
run_job(job=job, execute_callable=job_runner._execute)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/session.py", line 76, in wrapper
return func(*args, session=session, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/job.py", line 284, in run_job
return execute_job(job, execute_callable=execute_callable)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/job.py", line 313, in execute_job
ret = execute_callable()
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/session.py", line 76, in wrapper
return func(*args, session=session, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/backfill_job_runner.py", line 914, in _execute
self._execute_dagruns(
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/session.py", line 73, in wrapper
return func(*args, **kwargs)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/backfill_job_runner.py", line 801, in _execute_dagruns
processed_dag_run_dates = self._process_backfill_task_instances(
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/backfill_job_runner.py", line 643, in _process_backfill_task_instances
_per_task_process(key, ti, session)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/jobs/backfill_job_runner.py", line 539, in _per_task_process
executor_class, _ = ExecutorLoader.import_executor_cls(
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 148, in import_executor_cls
return _import_and_validate(executor_name), ConnectorSource.CUSTOM_PATH
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/executors/executor_loader.py", line 129, in _import_and_validate
executor = import_string(path)
File "/Users/adh/.pyenv/versions/3.10.7/envs/airflow310/lib/python3.10/site-packages/airflow/utils/module_loading.py", line 34, in import_string
raise ImportError(f"{dotted_path} doesn't look like a module path")
ImportError: NomadExecutor doesn't look like a module path

Happy to answer any other questions!

@potiuk

Copy link
Copy Markdown
Member

I looked at it and it loooks like a remnant from an old past that sneaked-in when I was refactoring BaseJob. Seems that the "executor_class" which was stored in the BaseJob was actually a different than "executor_class" that has been used in a number of places to check executor compatibility - and using it from job was simply a mistake.

But I also found out that the "job.executor_class" is something of a dead-relic. It has not been used anywhere else (only in one place in tests where it was not really needed any more). So I took the liberty to remove it altogether.

I also applied the same fix as you did here @adh-wonolo, but with a small twist (there is no need to get class to run the is_local property - the way python works, if you have an object of the class, you can run class method/property directly on the object and it will use the class one if there is no object property defined.

So I will close this one in favour of mine: #32219

@potiuk

Copy link
Copy Markdown
Member

I also made you co-author of that change @adh-wonolo . thanks for letting us know and providing the fix proposal!

@potiuk

Copy link
Copy Markdown
Member

And merged / marked for 2.6.3 -> once agin thans @adh-wonolo for raising it and providing proposed fix (and thanks @o-nikolas for raising my attantion :).

@adh-wonolo

Copy link
Copy Markdown
ContributorAuthor

Thanks so much @potiuk! Looking forward to collaborate again in the future :)

@adh-wonolo
adh-wonolo deleted the fix/backfill-custom-executor branch June 28, 2023 13:10
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:Schedulerincluding HA (high availability) scheduler

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@adh-wonolo@o-nikolas@potiuk