apache-airflow-providers-edge3 2.0.0rc1__py3-none-any.whl → 3.0.1rc1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. airflow/providers/edge3/__init__.py +1 -1
  2. airflow/providers/edge3/cli/api_client.py +30 -28
  3. airflow/providers/edge3/cli/dataclasses.py +3 -10
  4. airflow/providers/edge3/cli/definition.py +261 -0
  5. airflow/providers/edge3/cli/edge_command.py +8 -206
  6. airflow/providers/edge3/cli/worker.py +226 -198
  7. airflow/providers/edge3/example_dags/win_notepad.py +1 -1
  8. airflow/providers/edge3/executors/edge_executor.py +24 -49
  9. airflow/providers/edge3/get_provider_info.py +1 -0
  10. airflow/providers/edge3/models/edge_job.py +1 -2
  11. airflow/providers/edge3/models/edge_worker.py +61 -16
  12. airflow/providers/edge3/plugins/edge_executor_plugin.py +1 -1
  13. airflow/providers/edge3/plugins/www/dist/main.umd.cjs +8 -8
  14. airflow/providers/edge3/plugins/www/package.json +32 -27
  15. airflow/providers/edge3/plugins/www/pnpm-lock.yaml +1625 -1716
  16. airflow/providers/edge3/plugins/www/src/global.d.ts +24 -0
  17. airflow/providers/edge3/plugins/www/src/layouts/NavTabs.tsx +25 -3
  18. airflow/providers/edge3/plugins/www/src/main.tsx +6 -1
  19. airflow/providers/edge3/plugins/www/src/theme.ts +1 -1
  20. airflow/providers/edge3/worker_api/datamodels.py +12 -1
  21. airflow/providers/edge3/worker_api/routes/jobs.py +21 -8
  22. airflow/providers/edge3/worker_api/routes/logs.py +1 -1
  23. airflow/providers/edge3/worker_api/routes/worker.py +16 -3
  24. {apache_airflow_providers_edge3-2.0.0rc1.dist-info → apache_airflow_providers_edge3-3.0.1rc1.dist-info}/METADATA +14 -10
  25. {apache_airflow_providers_edge3-2.0.0rc1.dist-info → apache_airflow_providers_edge3-3.0.1rc1.dist-info}/RECORD +29 -29
  26. {apache_airflow_providers_edge3-2.0.0rc1.dist-info → apache_airflow_providers_edge3-3.0.1rc1.dist-info}/licenses/NOTICE +1 -1
  27. airflow/providers/edge3/plugins/templates/edge_worker_hosts.html +0 -175
  28. airflow/providers/edge3/plugins/templates/edge_worker_jobs.html +0 -69
  29. {apache_airflow_providers_edge3-2.0.0rc1.dist-info → apache_airflow_providers_edge3-3.0.1rc1.dist-info}/WHEEL +0 -0
  30. {apache_airflow_providers_edge3-2.0.0rc1.dist-info → apache_airflow_providers_edge3-3.0.1rc1.dist-info}/entry_points.txt +0 -0
  31. {apache_airflow_providers_edge3-2.0.0rc1.dist-info → apache_airflow_providers_edge3-3.0.1rc1.dist-info}/licenses/LICENSE +0 -0
@@ -23,17 +23,15 @@ from copy import deepcopy
23
23
  from datetime import datetime, timedelta
24
24
  from typing import TYPE_CHECKING, Any
25
25
 
26
- from sqlalchemy import delete, inspect, text
26
+ from sqlalchemy import delete, inspect, select, text
27
27
  from sqlalchemy.exc import NoSuchTableError
28
28
  from sqlalchemy.orm import Session
29
29
 
30
- from airflow.cli.cli_config import GroupCommand
31
30
  from airflow.configuration import conf
32
31
  from airflow.executors import workloads
33
32
  from airflow.executors.base_executor import BaseExecutor
34
33
  from airflow.models.taskinstance import TaskInstance
35
34
  from airflow.providers.common.compat.sdk import Stats, timezone
36
- from airflow.providers.edge3.cli.edge_command import EDGE_COMMANDS
37
35
  from airflow.providers.edge3.models.edge_job import EdgeJobModel
38
36
  from airflow.providers.edge3.models.edge_logs import EdgeLogsModel
39
37
  from airflow.providers.edge3.models.edge_worker import EdgeWorkerModel, EdgeWorkerState, reset_metrics
@@ -42,10 +40,9 @@ from airflow.utils.session import NEW_SESSION, provide_session
42
40
  from airflow.utils.state import TaskInstanceState
43
41
 
44
42
  if TYPE_CHECKING:
45
- import argparse
46
-
47
43
  from sqlalchemy.engine.base import Engine
48
44
 
45
+ from airflow.cli.cli_config import GroupCommand
49
46
  from airflow.models.taskinstancekey import TaskInstanceKey
50
47
 
51
48
  # TODO: Airflow 2 type hints; remove when Airflow 2 support is removed
@@ -140,17 +137,15 @@ class EdgeExecutor(BaseExecutor):
140
137
  key = task_instance.key
141
138
 
142
139
  # Check if job already exists with same dag_id, task_id, run_id, map_index, try_number
143
- existing_job = (
144
- session.query(EdgeJobModel)
145
- .filter_by(
146
- dag_id=key.dag_id,
147
- task_id=key.task_id,
148
- run_id=key.run_id,
149
- map_index=key.map_index,
150
- try_number=key.try_number,
140
+ existing_job = session.scalars(
141
+ select(EdgeJobModel).where(
142
+ EdgeJobModel.dag_id == key.dag_id,
143
+ EdgeJobModel.task_id == key.task_id,
144
+ EdgeJobModel.run_id == key.run_id,
145
+ EdgeJobModel.map_index == key.map_index,
146
+ EdgeJobModel.try_number == key.try_number,
151
147
  )
152
- .first()
153
- )
148
+ ).first()
154
149
 
155
150
  if existing_job:
156
151
  existing_job.state = TaskInstanceState.QUEUED
@@ -176,10 +171,10 @@ class EdgeExecutor(BaseExecutor):
176
171
  """Reset worker state if heartbeat timed out."""
177
172
  changed = False
178
173
  heartbeat_interval: int = conf.getint("edge", "heartbeat_interval")
179
- lifeless_workers: list[EdgeWorkerModel] = (
180
- session.query(EdgeWorkerModel)
174
+ lifeless_workers: Sequence[EdgeWorkerModel] = session.scalars(
175
+ select(EdgeWorkerModel)
181
176
  .with_for_update(skip_locked=True)
182
- .filter(
177
+ .where(
183
178
  EdgeWorkerModel.state.not_in(
184
179
  [
185
180
  EdgeWorkerState.UNKNOWN,
@@ -189,8 +184,7 @@ class EdgeExecutor(BaseExecutor):
189
184
  ),
190
185
  EdgeWorkerModel.last_update < (timezone.utcnow() - timedelta(seconds=heartbeat_interval * 5)),
191
186
  )
192
- .all()
193
- )
187
+ ).all()
194
188
 
195
189
  for worker in lifeless_workers:
196
190
  changed = True
@@ -212,15 +206,14 @@ class EdgeExecutor(BaseExecutor):
212
206
  def _update_orphaned_jobs(self, session: Session) -> bool:
213
207
  """Update status ob jobs when workers die and don't update anymore."""
214
208
  heartbeat_interval: int = conf.getint("scheduler", "task_instance_heartbeat_timeout")
215
- lifeless_jobs: list[EdgeJobModel] = (
216
- session.query(EdgeJobModel)
209
+ lifeless_jobs: Sequence[EdgeJobModel] = session.scalars(
210
+ select(EdgeJobModel)
217
211
  .with_for_update(skip_locked=True)
218
- .filter(
212
+ .where(
219
213
  EdgeJobModel.state == TaskInstanceState.RUNNING,
220
214
  EdgeJobModel.last_update < (timezone.utcnow() - timedelta(seconds=heartbeat_interval)),
221
215
  )
222
- .all()
223
- )
216
+ ).all()
224
217
 
225
218
  for job in lifeless_jobs:
226
219
  ti = TaskInstance.get_task_instance(
@@ -254,10 +247,10 @@ class EdgeExecutor(BaseExecutor):
254
247
  purged_marker = False
255
248
  job_success_purge = conf.getint("edge", "job_success_purge")
256
249
  job_fail_purge = conf.getint("edge", "job_fail_purge")
257
- jobs: list[EdgeJobModel] = (
258
- session.query(EdgeJobModel)
250
+ jobs: Sequence[EdgeJobModel] = session.scalars(
251
+ select(EdgeJobModel)
259
252
  .with_for_update(skip_locked=True)
260
- .filter(
253
+ .where(
261
254
  EdgeJobModel.state.in_(
262
255
  [
263
256
  TaskInstanceState.RUNNING,
@@ -269,8 +262,7 @@ class EdgeExecutor(BaseExecutor):
269
262
  ]
270
263
  )
271
264
  )
272
- .all()
273
- )
265
+ ).all()
274
266
 
275
267
  # Sync DB with executor otherwise runs out of sync in multi scheduler deployment
276
268
  already_removed = self.running - set(job.key for job in jobs)
@@ -388,23 +380,6 @@ class EdgeExecutor(BaseExecutor):
388
380
 
389
381
  @staticmethod
390
382
  def get_cli_commands() -> list[GroupCommand]:
391
- return [
392
- GroupCommand(
393
- name="edge",
394
- help="Edge Worker components",
395
- description=(
396
- "Start and manage Edge Worker. Works only when using EdgeExecutor. For more information, "
397
- "see https://airflow.apache.org/docs/apache-airflow-providers-edge3/stable/edge_executor.html"
398
- ),
399
- subcommands=EDGE_COMMANDS,
400
- ),
401
- ]
402
-
403
-
404
- def _get_parser() -> argparse.ArgumentParser:
405
- """
406
- Generate documentation; used by Sphinx.
383
+ from airflow.providers.edge3.cli.definition import get_edge_cli_commands
407
384
 
408
- :meta private:
409
- """
410
- return EdgeExecutor._get_parser()
385
+ return get_edge_cli_commands()
@@ -32,6 +32,7 @@ def get_provider_info():
32
32
  "plugin-class": "airflow.providers.edge3.plugins.edge_executor_plugin.EdgeExecutorPlugin",
33
33
  }
34
34
  ],
35
+ "cli": ["airflow.providers.edge3.cli.definition.get_edge_cli_commands"],
35
36
  "executors": ["airflow.providers.edge3.executors.EdgeExecutor"],
36
37
  "config": {
37
38
  "edge": {
@@ -27,8 +27,7 @@ from sqlalchemy import (
27
27
  from sqlalchemy.orm import Mapped
28
28
 
29
29
  from airflow.models.base import Base, StringID
30
- from airflow.models.taskinstancekey import TaskInstanceKey
31
- from airflow.providers.common.compat.sdk import timezone
30
+ from airflow.providers.common.compat.sdk import TaskInstanceKey, timezone
32
31
  from airflow.providers.common.compat.sqlalchemy.orm import mapped_column
33
32
  from airflow.utils.log.logging_mixin import LoggingMixin
34
33
  from airflow.utils.sqlalchemy import UtcDateTime
@@ -173,27 +173,72 @@ def set_metrics(
173
173
  EdgeWorkerState.OFFLINE_MAINTENANCE,
174
174
  )
175
175
 
176
- Stats.gauge(f"edge_worker.connected.{worker_name}", int(connected))
177
- Stats.gauge("edge_worker.connected", int(connected), tags={"worker_name": worker_name})
176
+ try:
177
+ from airflow.sdk._shared.observability.metrics.dual_stats_manager import DualStatsManager
178
+
179
+ DualStatsManager.gauge(
180
+ "edge_worker.connected",
181
+ int(connected),
182
+ tags={},
183
+ extra_tags={"worker_name": worker_name},
184
+ )
178
185
 
179
- Stats.gauge(f"edge_worker.maintenance.{worker_name}", int(maintenance))
180
- Stats.gauge("edge_worker.maintenance", int(maintenance), tags={"worker_name": worker_name})
186
+ DualStatsManager.gauge(
187
+ "edge_worker.maintenance",
188
+ int(maintenance),
189
+ tags={},
190
+ extra_tags={"worker_name": worker_name},
191
+ )
181
192
 
182
- Stats.gauge(f"edge_worker.jobs_active.{worker_name}", jobs_active)
183
- Stats.gauge("edge_worker.jobs_active", jobs_active, tags={"worker_name": worker_name})
193
+ DualStatsManager.gauge(
194
+ "edge_worker.jobs_active",
195
+ jobs_active,
196
+ tags={},
197
+ extra_tags={"worker_name": worker_name},
198
+ )
184
199
 
185
- Stats.gauge(f"edge_worker.concurrency.{worker_name}", concurrency)
186
- Stats.gauge("edge_worker.concurrency", concurrency, tags={"worker_name": worker_name})
200
+ DualStatsManager.gauge(
201
+ "edge_worker.concurrency",
202
+ concurrency,
203
+ tags={},
204
+ extra_tags={"worker_name": worker_name},
205
+ )
187
206
 
188
- Stats.gauge(f"edge_worker.free_concurrency.{worker_name}", free_concurrency)
189
- Stats.gauge("edge_worker.free_concurrency", free_concurrency, tags={"worker_name": worker_name})
207
+ DualStatsManager.gauge(
208
+ "edge_worker.free_concurrency",
209
+ free_concurrency,
210
+ tags={},
211
+ extra_tags={"worker_name": worker_name},
212
+ )
190
213
 
191
- Stats.gauge(f"edge_worker.num_queues.{worker_name}", len(queues))
192
- Stats.gauge(
193
- "edge_worker.num_queues",
194
- len(queues),
195
- tags={"worker_name": worker_name, "queues": ",".join(queues)},
196
- )
214
+ DualStatsManager.gauge(
215
+ "edge_worker.num_queues",
216
+ len(queues),
217
+ tags={},
218
+ extra_tags={"worker_name": worker_name, "queues": ",".join(queues)},
219
+ )
220
+ except ImportError:
221
+ Stats.gauge(f"edge_worker.connected.{worker_name}", int(connected))
222
+ Stats.gauge("edge_worker.connected", int(connected), tags={"worker_name": worker_name})
223
+
224
+ Stats.gauge(f"edge_worker.maintenance.{worker_name}", int(maintenance))
225
+ Stats.gauge("edge_worker.maintenance", int(maintenance), tags={"worker_name": worker_name})
226
+
227
+ Stats.gauge(f"edge_worker.jobs_active.{worker_name}", jobs_active)
228
+ Stats.gauge("edge_worker.jobs_active", jobs_active, tags={"worker_name": worker_name})
229
+
230
+ Stats.gauge(f"edge_worker.concurrency.{worker_name}", concurrency)
231
+ Stats.gauge("edge_worker.concurrency", concurrency, tags={"worker_name": worker_name})
232
+
233
+ Stats.gauge(f"edge_worker.free_concurrency.{worker_name}", free_concurrency)
234
+ Stats.gauge("edge_worker.free_concurrency", free_concurrency, tags={"worker_name": worker_name})
235
+
236
+ Stats.gauge(f"edge_worker.num_queues.{worker_name}", len(queues))
237
+ Stats.gauge(
238
+ "edge_worker.num_queues",
239
+ len(queues),
240
+ tags={"worker_name": worker_name, "queues": ",".join(queues)},
241
+ )
197
242
 
198
243
 
199
244
  def reset_metrics(worker_name: str) -> None:
@@ -22,7 +22,7 @@ from typing import TYPE_CHECKING, Any
22
22
 
23
23
  from airflow.configuration import conf
24
24
  from airflow.exceptions import AirflowConfigException
25
- from airflow.plugins_manager import AirflowPlugin
25
+ from airflow.providers.common.compat.sdk import AirflowPlugin
26
26
  from airflow.providers.edge3.version_compat import AIRFLOW_V_3_1_PLUS
27
27
  from airflow.utils.session import NEW_SESSION, provide_session
28
28