flowsense-engine 0.3.0__tar.gz → 0.4.0__tar.gz
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.
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/CHANGELOG.md +27 -0
- {flowsense_engine-0.3.0/src/flowsense_engine.egg-info → flowsense_engine-0.4.0}/PKG-INFO +52 -8
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/README.md +51 -7
- flowsense_engine-0.4.0/docs/alerting.md +57 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/docs/architecture.md +19 -3
- flowsense_engine-0.4.0/docs/authentication.md +77 -0
- flowsense_engine-0.4.0/docs/grafana.md +66 -0
- flowsense_engine-0.4.0/docs/prometheus.md +58 -0
- flowsense_engine-0.4.0/docs/python-api.md +142 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/docs/releasing.md +4 -4
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/pyproject.toml +1 -1
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/__init__.py +16 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/application/__init__.py +24 -2
- flowsense_engine-0.4.0/src/flowsense/application/batch.py +129 -0
- flowsense_engine-0.4.0/src/flowsense/application/client.py +61 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/application/output.py +17 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/application/ports.py +20 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/application/serialization.py +35 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/cli/main.py +35 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/infrastructure/airflow/__init__.py +17 -1
- flowsense_engine-0.4.0/src/flowsense/infrastructure/airflow/auth.py +63 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/infrastructure/airflow/client.py +54 -23
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/infrastructure/airflow/config.py +24 -8
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/infrastructure/airflow/dto.py +5 -0
- flowsense_engine-0.4.0/src/flowsense/infrastructure/airflow/factory.py +35 -0
- flowsense_engine-0.4.0/src/flowsense/observability/__init__.py +5 -0
- flowsense_engine-0.4.0/src/flowsense/observability/prometheus.py +248 -0
- flowsense_engine-0.4.0/src/flowsense/observability/service.py +90 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0/src/flowsense_engine.egg-info}/PKG-INFO +52 -8
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense_engine.egg-info/SOURCES.txt +16 -0
- flowsense_engine-0.4.0/tests/test_airflow_auth.py +114 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/tests/test_airflow_compatibility.py +61 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/tests/test_airflow_factory.py +32 -2
- flowsense_engine-0.4.0/tests/test_batch_analysis_output.py +71 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/tests/test_cli.py +33 -1
- flowsense_engine-0.4.0/tests/test_library_client.py +169 -0
- flowsense_engine-0.4.0/tests/test_observability_assets.py +58 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/tests/test_package_metadata.py +1 -1
- flowsense_engine-0.4.0/tests/test_prometheus.py +152 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/tests/test_public_api.py +8 -0
- flowsense_engine-0.3.0/src/flowsense/infrastructure/airflow/factory.py +0 -12
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/LICENSE +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/MANIFEST.in +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/docs/benchmarks.md +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/docs/migrating-to-0.3.md +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/setup.cfg +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/application/analysis_engine.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/application/analyzer.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/application/pipeline.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/application/request.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/application/use_cases.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/cli/__init__.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/cli/report.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/collector/__init__.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/collector/airflow_client.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/config.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/domain/__init__.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/domain/enums.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/domain/exceptions.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/domain/models.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/domain/policy.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/domain/results.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/engine/__init__.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/engine/analyzer.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/engine/change_point.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/engine/drift.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/engine/history.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/engine/impact.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/engine/propagation.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/engine/root_cause.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/engine/timing.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/engine/trend.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/engine/validation.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/infrastructure/__init__.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/infrastructure/airflow/exceptions.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/infrastructure/airflow/mapper.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/mcp/__init__.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/mcp/server.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/models/__init__.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/models/dag_analysis.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/models/task_run.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/py.typed +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense/version.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense_engine.egg-info/dependency_links.txt +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense_engine.egg-info/entry_points.txt +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense_engine.egg-info/requires.txt +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/src/flowsense_engine.egg-info/top_level.txt +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/tests/test_airflow_client.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/tests/test_airflow_client_integration.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/tests/test_airflow_contract.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/tests/test_airflow_mapper.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/tests/test_airflow_resilience.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/tests/test_analysis_golden.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/tests/test_analysis_output.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/tests/test_analysis_pipeline.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/tests/test_analysis_request.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/tests/test_analysis_use_case.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/tests/test_analyzer.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/tests/test_architecture.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/tests/test_benchmark_analysis.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/tests/test_change_point.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/tests/test_cli_report.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/tests/test_dag_summary.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/tests/test_deprecations.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/tests/test_domain.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/tests/test_drift.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/tests/test_history.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/tests/test_impact.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/tests/test_mcp_integration.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/tests/test_mcp_server.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/tests/test_observation_validation.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/tests/test_policy.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/tests/test_propagation.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/tests/test_root_cause.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/tests/test_timing.py +0 -0
- {flowsense_engine-0.3.0 → flowsense_engine-0.4.0}/tests/test_trend.py +0 -0
|
@@ -5,6 +5,33 @@ All notable changes to FlowSense are documented in this file. The project uses
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [0.4.0] - 2026-09-20
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Added paginated Airflow 2 and 3 DAG discovery with optional paused-DAG
|
|
13
|
+
inclusion.
|
|
14
|
+
- Added an immutable multi-DAG analysis result and bounded, opt-in concurrency
|
|
15
|
+
through `FlowSenseClient.analyze_many()`.
|
|
16
|
+
- Added a versioned, typed batch output document with serialization and JSON
|
|
17
|
+
Schema helpers.
|
|
18
|
+
- Added the `FlowSenseClient` facade for concise embedded Python usage and an
|
|
19
|
+
explicit Airflow data-source factory for application-owned configuration.
|
|
20
|
+
- Added pluggable Airflow authentication providers for Basic Auth, login-token
|
|
21
|
+
exchange, static or rotating bearer tokens, and deployment-specific headers.
|
|
22
|
+
- Added a bounded Prometheus metrics exporter and the optional
|
|
23
|
+
`flowsense serve-metrics` command for continuous DAG analysis.
|
|
24
|
+
- Added a provisioned Grafana dashboard and local Docker Compose example for
|
|
25
|
+
inspecting analysis health, coverage, severity, task drift, and handoffs.
|
|
26
|
+
- Added Prometheus alert rules, Alertmanager configuration, synthetic rule
|
|
27
|
+
tests, and CI validation for FlowSense analysis failures and anomalies.
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
- Kept observability integrations as optional delivery adapters so the core
|
|
32
|
+
analysis package remains independent from Prometheus, Grafana, and hosted
|
|
33
|
+
service infrastructure.
|
|
34
|
+
|
|
8
35
|
## [0.3.0] - 2026-09-20
|
|
9
36
|
|
|
10
37
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flowsense-engine
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Temporal drift and anomaly detection for Apache Airflow
|
|
5
5
|
Author: Omer Cengiz
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -37,7 +37,7 @@ Dynamic: license-file
|
|
|
37
37
|
|
|
38
38
|
Temporal drift, anomaly detection, and dependency-aware propagation analysis for Apache Airflow.
|
|
39
39
|
|
|
40
|
-
Current package release: `0.
|
|
40
|
+
Current package release: `0.4.0`. See [CHANGELOG.md](CHANGELOG.md) for release
|
|
41
41
|
notes, [docs/architecture.md](docs/architecture.md) for the system architecture,
|
|
42
42
|
[docs/benchmarks.md](docs/benchmarks.md) for performance measurement, and
|
|
43
43
|
[docs/migrating-to-0.3.md](docs/migrating-to-0.3.md) for migration guidance.
|
|
@@ -180,6 +180,7 @@ AIRFLOW_USERNAME=your_username
|
|
|
180
180
|
AIRFLOW_PASSWORD=your_password
|
|
181
181
|
AIRFLOW_API_VERSION=v2
|
|
182
182
|
AIRFLOW_AUTH_MODE=token
|
|
183
|
+
# AIRFLOW_BEARER_TOKEN=your_static_token
|
|
183
184
|
AIRFLOW_CONNECT_TIMEOUT=10
|
|
184
185
|
AIRFLOW_READ_TIMEOUT=10
|
|
185
186
|
AIRFLOW_MAX_RETRIES=2
|
|
@@ -192,6 +193,12 @@ Stable REST API deployments. Airflow 3.x uses the `v2` API and typically uses
|
|
|
192
193
|
token authentication. Authentication still depends on the API auth backend
|
|
193
194
|
configured in the Airflow deployment.
|
|
194
195
|
|
|
196
|
+
Static bearer tokens can use `AIRFLOW_AUTH_MODE=bearer` with
|
|
197
|
+
`AIRFLOW_BEARER_TOKEN`; username and password are not required in that mode.
|
|
198
|
+
Library integrations may inject a custom `AirflowAuthProvider` for rotating
|
|
199
|
+
tokens, identity-aware proxies, or deployment-specific headers. See
|
|
200
|
+
[docs/authentication.md](docs/authentication.md) for the complete contract.
|
|
201
|
+
|
|
195
202
|
CI verifies both integrations through versioned Airflow 2 and Airflow 3 REST
|
|
196
203
|
contract fixtures. These boundary tests cover authentication, endpoint routing,
|
|
197
204
|
response validation, domain mapping, and the complete analysis use case.
|
|
@@ -279,21 +286,41 @@ tooling, or client code generation.
|
|
|
279
286
|
|
|
280
287
|
## Library API
|
|
281
288
|
|
|
282
|
-
|
|
283
|
-
|
|
289
|
+
`FlowSenseClient` is the recommended in-process Python API. The environment
|
|
290
|
+
configured Airflow factory keeps the basic setup concise:
|
|
284
291
|
|
|
285
292
|
```python
|
|
286
|
-
from flowsense import
|
|
293
|
+
from flowsense import FlowSenseClient
|
|
287
294
|
from flowsense.infrastructure.airflow import create_airflow_data_source
|
|
288
295
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
)
|
|
296
|
+
client = FlowSenseClient(create_airflow_data_source)
|
|
297
|
+
analysis = client.analyze("flowsense_demo")
|
|
292
298
|
|
|
293
299
|
print(analysis.overall_severity)
|
|
294
300
|
print(analysis.primary_origin)
|
|
295
301
|
```
|
|
296
302
|
|
|
303
|
+
Applications that own configuration can combine `AirflowConfig` with
|
|
304
|
+
`create_airflow_data_source_factory`. Typed integrations may continue to call
|
|
305
|
+
`client.execute(AnalysisRequest(...))`. See the complete
|
|
306
|
+
[Python API guide](docs/python-api.md).
|
|
307
|
+
|
|
308
|
+
Multiple DAGs can be analyzed with bounded, opt-in concurrency. Expected
|
|
309
|
+
FlowSense failures are isolated per DAG:
|
|
310
|
+
|
|
311
|
+
```python
|
|
312
|
+
result = client.analyze_many(
|
|
313
|
+
["orders", "payments", "inventory"],
|
|
314
|
+
max_concurrency=3,
|
|
315
|
+
)
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
`AirflowClient.list_dag_ids()` provides paginated DAG discovery and excludes
|
|
319
|
+
paused DAGs by default.
|
|
320
|
+
|
|
321
|
+
`serialize_batch_analysis(result)` converts the result into the stable batch
|
|
322
|
+
output contract without exposing raw exception objects.
|
|
323
|
+
|
|
297
324
|
Analysis behavior can be customized with an immutable policy:
|
|
298
325
|
|
|
299
326
|
```python
|
|
@@ -351,6 +378,23 @@ The server exposes the `analyze_airflow_dag` tool, which returns task drift,
|
|
|
351
378
|
handoff drift, impact classification, propagation paths, and primary root-cause
|
|
352
379
|
information for a DAG.
|
|
353
380
|
|
|
381
|
+
## Prometheus
|
|
382
|
+
|
|
383
|
+
Run continuous analysis and expose the latest DAG and bounded task-level metrics:
|
|
384
|
+
|
|
385
|
+
```bash
|
|
386
|
+
flowsense serve-metrics example_dag --host 0.0.0.0 --port 9108
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
See [docs/prometheus.md](docs/prometheus.md) for the metric contract,
|
|
390
|
+
cardinality policy, and Prometheus scrape configuration.
|
|
391
|
+
|
|
392
|
+
A provisioned Prometheus and Grafana development stack, including the
|
|
393
|
+
`FlowSense Overview` dashboard, is documented in
|
|
394
|
+
[docs/grafana.md](docs/grafana.md).
|
|
395
|
+
Default operational alerts and Alertmanager routing are documented in
|
|
396
|
+
[docs/alerting.md](docs/alerting.md).
|
|
397
|
+
|
|
354
398
|
## Development
|
|
355
399
|
|
|
356
400
|
Run unit tests:
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Temporal drift, anomaly detection, and dependency-aware propagation analysis for Apache Airflow.
|
|
4
4
|
|
|
5
|
-
Current package release: `0.
|
|
5
|
+
Current package release: `0.4.0`. See [CHANGELOG.md](CHANGELOG.md) for release
|
|
6
6
|
notes, [docs/architecture.md](docs/architecture.md) for the system architecture,
|
|
7
7
|
[docs/benchmarks.md](docs/benchmarks.md) for performance measurement, and
|
|
8
8
|
[docs/migrating-to-0.3.md](docs/migrating-to-0.3.md) for migration guidance.
|
|
@@ -145,6 +145,7 @@ AIRFLOW_USERNAME=your_username
|
|
|
145
145
|
AIRFLOW_PASSWORD=your_password
|
|
146
146
|
AIRFLOW_API_VERSION=v2
|
|
147
147
|
AIRFLOW_AUTH_MODE=token
|
|
148
|
+
# AIRFLOW_BEARER_TOKEN=your_static_token
|
|
148
149
|
AIRFLOW_CONNECT_TIMEOUT=10
|
|
149
150
|
AIRFLOW_READ_TIMEOUT=10
|
|
150
151
|
AIRFLOW_MAX_RETRIES=2
|
|
@@ -157,6 +158,12 @@ Stable REST API deployments. Airflow 3.x uses the `v2` API and typically uses
|
|
|
157
158
|
token authentication. Authentication still depends on the API auth backend
|
|
158
159
|
configured in the Airflow deployment.
|
|
159
160
|
|
|
161
|
+
Static bearer tokens can use `AIRFLOW_AUTH_MODE=bearer` with
|
|
162
|
+
`AIRFLOW_BEARER_TOKEN`; username and password are not required in that mode.
|
|
163
|
+
Library integrations may inject a custom `AirflowAuthProvider` for rotating
|
|
164
|
+
tokens, identity-aware proxies, or deployment-specific headers. See
|
|
165
|
+
[docs/authentication.md](docs/authentication.md) for the complete contract.
|
|
166
|
+
|
|
160
167
|
CI verifies both integrations through versioned Airflow 2 and Airflow 3 REST
|
|
161
168
|
contract fixtures. These boundary tests cover authentication, endpoint routing,
|
|
162
169
|
response validation, domain mapping, and the complete analysis use case.
|
|
@@ -244,21 +251,41 @@ tooling, or client code generation.
|
|
|
244
251
|
|
|
245
252
|
## Library API
|
|
246
253
|
|
|
247
|
-
|
|
248
|
-
|
|
254
|
+
`FlowSenseClient` is the recommended in-process Python API. The environment
|
|
255
|
+
configured Airflow factory keeps the basic setup concise:
|
|
249
256
|
|
|
250
257
|
```python
|
|
251
|
-
from flowsense import
|
|
258
|
+
from flowsense import FlowSenseClient
|
|
252
259
|
from flowsense.infrastructure.airflow import create_airflow_data_source
|
|
253
260
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
)
|
|
261
|
+
client = FlowSenseClient(create_airflow_data_source)
|
|
262
|
+
analysis = client.analyze("flowsense_demo")
|
|
257
263
|
|
|
258
264
|
print(analysis.overall_severity)
|
|
259
265
|
print(analysis.primary_origin)
|
|
260
266
|
```
|
|
261
267
|
|
|
268
|
+
Applications that own configuration can combine `AirflowConfig` with
|
|
269
|
+
`create_airflow_data_source_factory`. Typed integrations may continue to call
|
|
270
|
+
`client.execute(AnalysisRequest(...))`. See the complete
|
|
271
|
+
[Python API guide](docs/python-api.md).
|
|
272
|
+
|
|
273
|
+
Multiple DAGs can be analyzed with bounded, opt-in concurrency. Expected
|
|
274
|
+
FlowSense failures are isolated per DAG:
|
|
275
|
+
|
|
276
|
+
```python
|
|
277
|
+
result = client.analyze_many(
|
|
278
|
+
["orders", "payments", "inventory"],
|
|
279
|
+
max_concurrency=3,
|
|
280
|
+
)
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
`AirflowClient.list_dag_ids()` provides paginated DAG discovery and excludes
|
|
284
|
+
paused DAGs by default.
|
|
285
|
+
|
|
286
|
+
`serialize_batch_analysis(result)` converts the result into the stable batch
|
|
287
|
+
output contract without exposing raw exception objects.
|
|
288
|
+
|
|
262
289
|
Analysis behavior can be customized with an immutable policy:
|
|
263
290
|
|
|
264
291
|
```python
|
|
@@ -316,6 +343,23 @@ The server exposes the `analyze_airflow_dag` tool, which returns task drift,
|
|
|
316
343
|
handoff drift, impact classification, propagation paths, and primary root-cause
|
|
317
344
|
information for a DAG.
|
|
318
345
|
|
|
346
|
+
## Prometheus
|
|
347
|
+
|
|
348
|
+
Run continuous analysis and expose the latest DAG and bounded task-level metrics:
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
flowsense serve-metrics example_dag --host 0.0.0.0 --port 9108
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
See [docs/prometheus.md](docs/prometheus.md) for the metric contract,
|
|
355
|
+
cardinality policy, and Prometheus scrape configuration.
|
|
356
|
+
|
|
357
|
+
A provisioned Prometheus and Grafana development stack, including the
|
|
358
|
+
`FlowSense Overview` dashboard, is documented in
|
|
359
|
+
[docs/grafana.md](docs/grafana.md).
|
|
360
|
+
Default operational alerts and Alertmanager routing are documented in
|
|
361
|
+
[docs/alerting.md](docs/alerting.md).
|
|
362
|
+
|
|
319
363
|
## Development
|
|
320
364
|
|
|
321
365
|
Run unit tests:
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Prometheus alerting
|
|
2
|
+
|
|
3
|
+
The local observability stack loads a tested set of FlowSense alert rules and
|
|
4
|
+
routes firing alerts to Alertmanager.
|
|
5
|
+
|
|
6
|
+
## Included alerts
|
|
7
|
+
|
|
8
|
+
| Alert | Default condition | Severity |
|
|
9
|
+
| --- | --- | --- |
|
|
10
|
+
| `FlowSenseExporterDown` | Metrics endpoint unavailable for 2 minutes | Critical |
|
|
11
|
+
| `FlowSenseAnalysisFailed` | Latest analysis failed for 2 minutes | Warning |
|
|
12
|
+
| `FlowSenseAnalysisStale` | No successful analysis for 10 minutes in total | Warning |
|
|
13
|
+
| `FlowSenseCriticalDAG` | DAG severity remains critical for 2 minutes | Critical |
|
|
14
|
+
| `FlowSenseCriticalTask` | Task severity remains critical for 2 minutes | Critical |
|
|
15
|
+
| `FlowSenseTaskMetricsDropped` | Cardinality limit omits tasks for 5 minutes | Warning |
|
|
16
|
+
| `FlowSenseAnalysisSlow` | Analysis remains above 30 seconds for 5 minutes | Warning |
|
|
17
|
+
|
|
18
|
+
The stale rule expression becomes true after five minutes without a successful
|
|
19
|
+
analysis and must remain true for another five minutes before firing. Adjust
|
|
20
|
+
thresholds in `deploy/observability/alerts.yml` to match the configured analysis
|
|
21
|
+
interval and operational expectations.
|
|
22
|
+
|
|
23
|
+
## Validate rules
|
|
24
|
+
|
|
25
|
+
Run syntax and unit checks with the Prometheus image pinned by the Compose file:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
docker run --rm \
|
|
29
|
+
--entrypoint promtool \
|
|
30
|
+
-v "$PWD/deploy/observability:/workspace:ro" \
|
|
31
|
+
-w /workspace \
|
|
32
|
+
prom/prometheus:v3.14.0 \
|
|
33
|
+
check rules alerts.yml
|
|
34
|
+
|
|
35
|
+
docker run --rm \
|
|
36
|
+
--entrypoint promtool \
|
|
37
|
+
-v "$PWD/deploy/observability:/workspace:ro" \
|
|
38
|
+
-w /workspace \
|
|
39
|
+
prom/prometheus:v3.14.0 \
|
|
40
|
+
test rules alerts.test.yml
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Configure notifications
|
|
44
|
+
|
|
45
|
+
The committed Alertmanager configuration intentionally uses an empty local
|
|
46
|
+
receiver. Firing alerts are visible in the Alertmanager UI and the Grafana
|
|
47
|
+
dashboard, but no external notification is sent by default.
|
|
48
|
+
|
|
49
|
+
For production, replace the receiver in
|
|
50
|
+
`deploy/observability/alertmanager.yml` with the organization's managed email,
|
|
51
|
+
Slack, PagerDuty, webhook, or other supported integration. Keep credentials out
|
|
52
|
+
of Git and inject them through the deployment platform's secret management
|
|
53
|
+
mechanism.
|
|
54
|
+
|
|
55
|
+
Alertmanager is available locally at `http://localhost:9093`. Verify routing and
|
|
56
|
+
notification delivery in a non-production environment before enabling it for
|
|
57
|
+
operational incidents.
|
|
@@ -48,6 +48,17 @@ implemented by infrastructure adapters. The existing functional
|
|
|
48
48
|
`analyze_dag(dag_id, source, policy)` API remains available for callers that
|
|
49
49
|
already own a data source.
|
|
50
50
|
|
|
51
|
+
`FlowSenseClient` is the supported convenience facade for embedded Python
|
|
52
|
+
consumers. It translates the concise `analyze(...)` call into an
|
|
53
|
+
`AnalysisRequest` and delegates to `AnalyzeDAG`; it contains no collection or
|
|
54
|
+
analysis logic of its own. Its optional multi-DAG operation creates one
|
|
55
|
+
use-case execution and context-managed data source per DAG, preserving the
|
|
56
|
+
single-DAG application boundary.
|
|
57
|
+
|
|
58
|
+
`BatchAnalysisDocument` is a separate versioned envelope around the existing
|
|
59
|
+
single-DAG `AnalysisDocument`. Expected failures cross the output boundary as
|
|
60
|
+
typed failure records rather than runtime exception objects.
|
|
61
|
+
|
|
51
62
|
`DAGAnalysisEngine` is the coarse-grained algorithm extension port. The default
|
|
52
63
|
implementation owns the complete statistical workflow after data collection.
|
|
53
64
|
Alternative engines can be injected into `AnalyzeDAG` without changing CLI,
|
|
@@ -66,12 +77,17 @@ are required.
|
|
|
66
77
|
variables. `load_airflow_config()` and `create_airflow_data_source()` belong to
|
|
67
78
|
the infrastructure composition boundary.
|
|
68
79
|
|
|
80
|
+
Authentication is supplied through `AirflowAuthProvider`. Built-in providers
|
|
81
|
+
cover Basic Auth, Airflow login-token exchange, static bearer tokens, and custom
|
|
82
|
+
headers without coupling request collection to a deployment's identity system.
|
|
83
|
+
|
|
69
84
|
### Delivery adapters
|
|
70
85
|
|
|
71
|
-
`flowsense.cli` and `flowsense.
|
|
86
|
+
`flowsense.cli`, `flowsense.mcp`, and `flowsense.observability` translate user input into an
|
|
72
87
|
`AnalysisRequest`, invoke `AnalyzeDAG`, and translate the result into their own
|
|
73
|
-
output mechanism.
|
|
74
|
-
|
|
88
|
+
output mechanism. Prometheus export implements the `AnalysisMetricsSink` port
|
|
89
|
+
and keeps a bounded latest-value snapshot. Business analysis and data-collection
|
|
90
|
+
orchestration must not be duplicated in these adapters.
|
|
75
91
|
|
|
76
92
|
## Main analysis flow
|
|
77
93
|
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Airflow authentication
|
|
2
|
+
|
|
3
|
+
FlowSense keeps Airflow authentication behind the `AirflowAuthProvider` port.
|
|
4
|
+
The collector does not need to know whether credentials come from Airflow,
|
|
5
|
+
a reverse proxy, a secret manager, or an external identity system.
|
|
6
|
+
|
|
7
|
+
## Built-in modes
|
|
8
|
+
|
|
9
|
+
### Airflow login token
|
|
10
|
+
|
|
11
|
+
This remains the default for Airflow 3 deployments that expose `/auth/token`:
|
|
12
|
+
|
|
13
|
+
```env
|
|
14
|
+
AIRFLOW_AUTH_MODE=token
|
|
15
|
+
AIRFLOW_USERNAME=airflow
|
|
16
|
+
AIRFLOW_PASSWORD=secret
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
FlowSense exchanges the credentials once and caches the returned bearer token
|
|
20
|
+
for the lifetime of the client.
|
|
21
|
+
|
|
22
|
+
### Basic authentication
|
|
23
|
+
|
|
24
|
+
Common Airflow 2 Stable REST API deployments can use:
|
|
25
|
+
|
|
26
|
+
```env
|
|
27
|
+
AIRFLOW_API_VERSION=v1
|
|
28
|
+
AIRFLOW_AUTH_MODE=basic
|
|
29
|
+
AIRFLOW_USERNAME=airflow
|
|
30
|
+
AIRFLOW_PASSWORD=secret
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Static bearer token
|
|
34
|
+
|
|
35
|
+
For deployments that issue a token outside Airflow:
|
|
36
|
+
|
|
37
|
+
```env
|
|
38
|
+
AIRFLOW_AUTH_MODE=bearer
|
|
39
|
+
AIRFLOW_BEARER_TOKEN=secret-token
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Username and password are not required in bearer mode. Secrets are excluded
|
|
43
|
+
from the `AirflowConfig` representation, but applications must still keep them
|
|
44
|
+
out of logs, shell history, source control, and command-line arguments.
|
|
45
|
+
|
|
46
|
+
## Custom provider
|
|
47
|
+
|
|
48
|
+
Enterprise integrations can inject an `AirflowAuthProvider` directly. This is
|
|
49
|
+
appropriate for rotating tokens, identity-aware proxies, workload identity, or
|
|
50
|
+
deployment-specific headers:
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from flowsense.infrastructure.airflow import (
|
|
54
|
+
AirflowClient,
|
|
55
|
+
AirflowConfig,
|
|
56
|
+
HeaderAuthProvider,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
provider = HeaderAuthProvider({"X-Forwarded-User": "flowsense"})
|
|
60
|
+
client = AirflowClient(
|
|
61
|
+
AirflowConfig(base_url="https://airflow.example.com"),
|
|
62
|
+
auth_provider=provider,
|
|
63
|
+
)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
For rotating bearer credentials, pass a callable. It is evaluated for every
|
|
67
|
+
Airflow request:
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from flowsense.infrastructure.airflow import BearerTokenAuthProvider
|
|
71
|
+
|
|
72
|
+
provider = BearerTokenAuthProvider(load_current_token)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Custom providers should return only authentication material and leave request
|
|
76
|
+
execution, retry, timeout, payload validation, and error translation to
|
|
77
|
+
`AirflowClient`.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Grafana dashboard
|
|
2
|
+
|
|
3
|
+
FlowSense ships a provisioned local observability stack containing Prometheus
|
|
4
|
+
and Grafana. Airflow credentials stay in the host process running FlowSense;
|
|
5
|
+
the containers only scrape the exported metrics.
|
|
6
|
+
|
|
7
|
+
## Start FlowSense metrics
|
|
8
|
+
|
|
9
|
+
Configure the required `AIRFLOW_*` environment variables, then run:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
flowsense serve-metrics example_dag another_dag \
|
|
13
|
+
--host 0.0.0.0 \
|
|
14
|
+
--port 9108 \
|
|
15
|
+
--interval-seconds 60
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Binding to `0.0.0.0` makes the endpoint reachable from the local Prometheus
|
|
19
|
+
container. Do not expose this port to untrusted networks without an appropriate
|
|
20
|
+
network policy or reverse proxy.
|
|
21
|
+
|
|
22
|
+
## Start Prometheus and Grafana
|
|
23
|
+
|
|
24
|
+
From the repository root:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
docker compose -f deploy/observability/docker-compose.yml up -d
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Open:
|
|
31
|
+
|
|
32
|
+
- Grafana: `http://localhost:3000` (`admin` / `admin` by default)
|
|
33
|
+
- Prometheus: `http://localhost:9090`
|
|
34
|
+
- Alertmanager: `http://localhost:9093`
|
|
35
|
+
|
|
36
|
+
Set `GRAFANA_ADMIN_USER` and `GRAFANA_ADMIN_PASSWORD` before starting Compose
|
|
37
|
+
to override the local defaults. The Prometheus data source and `FlowSense
|
|
38
|
+
Overview` dashboard are provisioned automatically.
|
|
39
|
+
|
|
40
|
+
## Dashboard panels
|
|
41
|
+
|
|
42
|
+
The dashboard provides:
|
|
43
|
+
|
|
44
|
+
- current DAG severity and analysis health;
|
|
45
|
+
- analysis coverage and runtime;
|
|
46
|
+
- anomalous task and handoff counts;
|
|
47
|
+
- DAG severity history;
|
|
48
|
+
- task duration deviation and robust z-score history;
|
|
49
|
+
- current task severity;
|
|
50
|
+
- task cardinality omissions;
|
|
51
|
+
- last successful analysis time.
|
|
52
|
+
- currently firing FlowSense alerts.
|
|
53
|
+
|
|
54
|
+
Use the DAG and task variables at the top of the dashboard to narrow the view.
|
|
55
|
+
The default time range is six hours and the dashboard refreshes every 30 seconds.
|
|
56
|
+
See [alerting.md](alerting.md) for rule behavior, validation, and production
|
|
57
|
+
notification configuration.
|
|
58
|
+
|
|
59
|
+
## Stop the stack
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
docker compose -f deploy/observability/docker-compose.yml down
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Prometheus and Grafana data remain in named volumes. To explicitly remove those
|
|
66
|
+
local volumes, add `--volumes` to the command.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Prometheus metrics
|
|
2
|
+
|
|
3
|
+
FlowSense can continuously analyze one or more Airflow DAGs and expose the
|
|
4
|
+
latest results using the Prometheus text exposition format. The endpoint uses
|
|
5
|
+
only the Python standard library, so no additional runtime dependency is
|
|
6
|
+
required.
|
|
7
|
+
|
|
8
|
+
Start the service:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
flowsense serve-metrics example_dag another_dag \
|
|
12
|
+
--host 0.0.0.0 \
|
|
13
|
+
--port 9108 \
|
|
14
|
+
--interval-seconds 60
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The command uses the same `AIRFLOW_*` configuration as `flowsense analyze`.
|
|
18
|
+
Configure Prometheus to scrape `http://<flowsense-host>:9108/metrics`.
|
|
19
|
+
|
|
20
|
+
## Metric contract
|
|
21
|
+
|
|
22
|
+
All metrics are gauges. DAG-level metrics carry only the `dag_id` label:
|
|
23
|
+
|
|
24
|
+
- `flowsense_analysis_success`
|
|
25
|
+
- `flowsense_analysis_duration_seconds`
|
|
26
|
+
- `flowsense_analysis_last_success_timestamp_seconds`
|
|
27
|
+
- `flowsense_analysis_last_failure_timestamp_seconds`
|
|
28
|
+
- `flowsense_dag_overall_severity`
|
|
29
|
+
- `flowsense_dag_runs_analyzed`
|
|
30
|
+
- `flowsense_dag_analysis_coverage_ratio`
|
|
31
|
+
- `flowsense_dag_anomalous_tasks`
|
|
32
|
+
- `flowsense_dag_anomalous_handoffs`
|
|
33
|
+
- `flowsense_dag_affected_tasks`
|
|
34
|
+
- `flowsense_dag_change_points`
|
|
35
|
+
- `flowsense_dag_trends`
|
|
36
|
+
- `flowsense_dag_diagnostics`
|
|
37
|
+
- `flowsense_dag_dropped_tasks`
|
|
38
|
+
|
|
39
|
+
Task-level metrics additionally carry `task_id`:
|
|
40
|
+
|
|
41
|
+
- `flowsense_task_severity`
|
|
42
|
+
- `flowsense_task_deviation_percent`
|
|
43
|
+
- `flowsense_task_robust_z_score`
|
|
44
|
+
|
|
45
|
+
Severity values are `0` for normal, `1` for medium, `2` for high, and `3` for
|
|
46
|
+
critical.
|
|
47
|
+
|
|
48
|
+
## Cardinality policy
|
|
49
|
+
|
|
50
|
+
Task metrics are limited to 200 task IDs per DAG by default. Task IDs are sorted
|
|
51
|
+
to make selection deterministic. Use `--max-tasks-per-dag` to change the
|
|
52
|
+
limit, or set it to `0` to publish DAG-level metrics only. The exporter reports
|
|
53
|
+
the number of omitted tasks through `flowsense_dag_dropped_tasks`.
|
|
54
|
+
|
|
55
|
+
The exporter retains the latest successful analysis snapshot when a later
|
|
56
|
+
collection fails. In that case `flowsense_analysis_success` becomes `0` and the
|
|
57
|
+
failure timestamp advances, while the last known DAG and task measurements
|
|
58
|
+
remain available.
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# Python library API
|
|
2
|
+
|
|
3
|
+
`FlowSenseClient` is the recommended entry point for applications embedding
|
|
4
|
+
FlowSense. It is a lightweight in-process facade: it does not start a server,
|
|
5
|
+
own a scheduler, or store analysis results.
|
|
6
|
+
|
|
7
|
+
## Environment-configured Airflow
|
|
8
|
+
|
|
9
|
+
Use the standard Airflow environment variables for the shortest setup:
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
from flowsense import FlowSenseClient
|
|
13
|
+
from flowsense.infrastructure.airflow import create_airflow_data_source
|
|
14
|
+
|
|
15
|
+
client = FlowSenseClient(create_airflow_data_source)
|
|
16
|
+
analysis = client.analyze("example_dag")
|
|
17
|
+
|
|
18
|
+
print(analysis.overall_severity)
|
|
19
|
+
print(analysis.primary_origin)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Explicit Airflow configuration
|
|
23
|
+
|
|
24
|
+
Applications that own their configuration should avoid process-global
|
|
25
|
+
environment state:
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
from flowsense import FlowSenseClient
|
|
29
|
+
from flowsense.infrastructure.airflow import (
|
|
30
|
+
AirflowConfig,
|
|
31
|
+
BasicAuthProvider,
|
|
32
|
+
create_airflow_data_source_factory,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
config = AirflowConfig(
|
|
36
|
+
base_url="https://airflow.example.com",
|
|
37
|
+
api_version="v2",
|
|
38
|
+
history_run_limit=100,
|
|
39
|
+
)
|
|
40
|
+
source_factory = create_airflow_data_source_factory(
|
|
41
|
+
config,
|
|
42
|
+
auth_provider=BasicAuthProvider("flowsense", "secret"),
|
|
43
|
+
)
|
|
44
|
+
client = FlowSenseClient(source_factory)
|
|
45
|
+
|
|
46
|
+
analysis = client.analyze(
|
|
47
|
+
"example_dag",
|
|
48
|
+
history_run_limit=50,
|
|
49
|
+
dag_run_id="scheduled__2026-09-20T00:00:00+00:00",
|
|
50
|
+
)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Secrets are deliberately supplied to an authentication provider rather than
|
|
54
|
+
stored in application code. See [authentication.md](authentication.md) for
|
|
55
|
+
built-in and custom providers.
|
|
56
|
+
|
|
57
|
+
## Typed requests and policies
|
|
58
|
+
|
|
59
|
+
Use `execute()` when a request is created at another application boundary:
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from flowsense import AnalysisPolicy, AnalysisRequest
|
|
63
|
+
|
|
64
|
+
request = AnalysisRequest(
|
|
65
|
+
dag_id="example_dag",
|
|
66
|
+
history_run_limit=50,
|
|
67
|
+
policy=AnalysisPolicy(
|
|
68
|
+
minimum_history=10,
|
|
69
|
+
baseline_window=30,
|
|
70
|
+
),
|
|
71
|
+
)
|
|
72
|
+
analysis = client.execute(request)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Both `analyze()` and `execute()` return the domain-level `DAGAnalysis` model.
|
|
76
|
+
Use `serialize_analysis()` or `build_analysis_document()` when a versioned
|
|
77
|
+
external output contract is required.
|
|
78
|
+
|
|
79
|
+
## Analyze multiple DAGs
|
|
80
|
+
|
|
81
|
+
Airflow-backed applications can discover active DAG ids before starting a
|
|
82
|
+
batch. Discovery uses the configured, paginated Airflow 2 or 3 REST API:
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
from flowsense.infrastructure.airflow import AirflowClient
|
|
86
|
+
|
|
87
|
+
with AirflowClient(config, auth_provider=auth_provider) as airflow:
|
|
88
|
+
dag_ids = airflow.list_dag_ids()
|
|
89
|
+
|
|
90
|
+
result = client.analyze_many(dag_ids, max_concurrency=3)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Paused DAGs are excluded by default. Pass `include_paused=True` when they are
|
|
94
|
+
also required.
|
|
95
|
+
|
|
96
|
+
`analyze_many()` is intended for applications that monitor several DAGs in one
|
|
97
|
+
process. It preserves the requested DAG order and separates successful analyses
|
|
98
|
+
from expected FlowSense failures:
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
result = client.analyze_many(
|
|
102
|
+
["orders", "payments", "inventory"],
|
|
103
|
+
history_run_limit=50,
|
|
104
|
+
max_concurrency=3,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
for dag_id, analysis in result.analyses.items():
|
|
108
|
+
print(dag_id, analysis.overall_severity)
|
|
109
|
+
|
|
110
|
+
for dag_id, failure in result.failures.items():
|
|
111
|
+
print(dag_id, failure)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Batch results have their own versioned external contract. Raw exception objects
|
|
115
|
+
are converted to stable failure records containing `error_type` and `message`:
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
from flowsense import serialize_batch_analysis
|
|
119
|
+
|
|
120
|
+
payload = serialize_batch_analysis(result)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Use `build_batch_analysis_document()` for a typed Pydantic document or
|
|
124
|
+
`batch_analysis_json_schema()` for the corresponding JSON Schema. The current
|
|
125
|
+
batch envelope schema version is `1.0`; each successful analysis retains its
|
|
126
|
+
own analysis schema version.
|
|
127
|
+
|
|
128
|
+
Concurrency is opt-in and bounded; `max_concurrency` defaults to `1`. Each DAG
|
|
129
|
+
receives its own context-managed data source. Expected `FlowSenseError`
|
|
130
|
+
instances are isolated in `failures`, while unexpected programming or adapter
|
|
131
|
+
errors still propagate to the caller. Use individual `AnalysisRequest` objects
|
|
132
|
+
when each DAG needs a different policy or historical run id. When concurrency
|
|
133
|
+
is greater than one, custom data-source factories and injected analysis engines
|
|
134
|
+
must be safe to call from multiple threads.
|
|
135
|
+
|
|
136
|
+
## Custom data sources and engines
|
|
137
|
+
|
|
138
|
+
`FlowSenseClient` depends on the `DAGDataSourceFactory` and
|
|
139
|
+
`DAGAnalysisEngine` application ports. A new platform adapter can implement the
|
|
140
|
+
data-source protocol and supply a context-managed factory without depending on
|
|
141
|
+
Airflow. A complete alternative analysis workflow can be injected through the
|
|
142
|
+
optional `analysis_engine` constructor argument.
|