bluspark/airflow-dag-run-bundle
Install the Bundle
composer require bluspark/airflow-dag-run-bundle
Ensure BlusparkAirflowDagRunBundle is enabled in config/bundles.php (Symfony Flex handles this automatically).
Configure Airflow Connection
Create config/packages/bluspark_airflow_dag_run.yaml:
bluspark_airflow_dag_run:
airflow_host: "https://your-airflow-instance.com"
airflow_dag_ids: "export-dag:export_files" # Format: "dag-name:dag-id"
airflow_username: "your-username"
airflow_password: "your-password" # Use env vars or Symfony secrets in production
Set Up Messenger Transport
Configure messenger.yaml to route DagRunMessageExecuted events:
framework:
messenger:
transports:
my_project_transport: "%env(MESSENGER_TRANSPORT_DSN)%"
routing:
'Bluspark\AirflowDagRunBundle\Scheduler\Message\DagRunMessageExecuted': my_project_transport
First Use Case: Trigger a DAG Run
Inject the AirflowDagRunClient service and trigger a DAG:
use Bluspark\AirflowDagRunBundle\Client\AirflowDagRunClientInterface;
public function __construct(
private AirflowDagRunClientInterface $airflowClient
) {}
public function triggerExport(): void
{
$this->airflowClient->triggerDagRun(
'export-dag',
['export_filename' => 'report_2023.csv'] // Optional DAG-specific config
);
}
Triggering DAGs
Use AirflowDagRunClientInterface to initiate DAG runs with optional parameters:
$this->airflowClient->triggerDagRun(
'data-processing',
['input_file' => '/path/to/file.csv', 'timeout' => 3600]
);
airflow_dag_ids config to avoid runtime errors.Handling Results via Messenger
The bundle dispatches DagRunMessageExecuted events when Airflow completes the DAG. Configure a message handler:
use Bluspark\AirflowDagRunBundle\Scheduler\Message\DagRunMessageExecuted;
public function __invoke(DagRunMessageExecuted $message)
{
if ($message->isSuccess()) {
$this->processExportFile($message->getExportFilename());
}
}
Retry Logic Implement a retry strategy for failed DAG runs using Symfony Messenger’s retry middleware:
# config/packages/messenger.yaml
framework:
messenger:
transports:
async: "%env(MESSENGER_TRANSPORT_DSN)%"
failure_transport: failed
retry_strategy:
max_retries: 3
delay: 1000
Integration with Symfony Commands Create a custom command to manually trigger DAGs:
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class TriggerAirflowDagCommand extends Command
{
protected static $defaultName = 'app:trigger-airflow-dag';
public function __construct(
private AirflowDagRunClientInterface $airflowClient
) {}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->airflowClient->triggerDagRun('reporting-dag');
$output->writeln('DAG triggered successfully!');
return Command::SUCCESS;
}
}
Dynamic Configuration Override DAG configurations per environment or context:
# config/packages/dev/bluspark_airflow_dag_run.yaml
bluspark_airflow_dag_run:
airflow_dag_ids:
- "dev-export:dev-123"
- "test-processing:test-456"
Authentication Issues
bluspark_airflow_dag_run.yaml are insecure.%env% or parameter_bag to inject credentials:
airflow_password: "%env(AIRFLOW_PASSWORD)%"
DAG ID Mismatches
airflow_dag_ids.if (!$this->airflowClient->isDagIdValid('unknown-dag')) {
throw new \InvalidArgumentException('DAG ID not configured.');
}
Messenger Transport Delays
DagRunMessageExecuted events may arrive late or not at all.sync://) for testing or critical paths.Airflow API Rate Limits
$this->airflowClient->triggerDagRun('dag-id', [], 5); // 5-second delay
Symfony < 6.4 Compatibility
DagRunMessageExecuted handler in older versions:
// src/EventListener/DagRunListener.php
public function onKernelTerminate(KernelEvent $event)
{
if ($event->getRequest()->isXmlHttpRequest()) {
$this->airflowClient->pollForResults();
}
}
Logging Enable debug logging for the bundle to trace DAG runs:
# config/packages/monolog.yaml
monolog:
handlers:
main:
level: debug
channels: ["!event"]
airflow:
type: stream
path: "%kernel.logs_dir%/airflow.log"
level: debug
channels: ["airflow"]
Testing
Mock the AirflowDagRunClientInterface in unit tests:
$mockClient = $this->createMock(AirflowDagRunClientInterface::class);
$mockClient->method('triggerDagRun')
->willReturn(['run_id' => 'test-123']);
$this->container->set(AirflowDagRunClientInterface::class, $mockClient);
Extending the Bundle
triggerDagRun method by creating a decorator:
class CustomAirflowClient implements AirflowDagRunClientInterface
{
public function __construct(private AirflowDagRunClientInterface $decorated) {}
public function triggerDagRun(string $dagId, array $params = [], int $delay = 0): array
{
$params['custom_key'] = 'custom_value';
return $this->decorated->triggerDagRun($dagId, $params, $delay);
}
}
services.yaml:
services:
Bluspark\AirflowDagRunBundle\Client\AirflowDagRunClientInterface: '@custom_airflow_client'
custom_airflow_client:
class: App\Service\CustomAirflowClient
decorates: 'Bluspark\AirflowDagRunBundle\Client\AirflowDagRunClient'
Monitoring
Track DAG run statuses by extending the DagRunMessageExecuted event:
public function __invoke(DagRunMessageExecuted $message)
{
$this->statisticsService->record(
'airflow_dag_runs',
['dag_id' => $message->getDagId(), 'status' => $message->isSuccess() ? 'success' : 'failed']
);
}
Environment-Specific DAGs Use Symfony’s parameter bags to switch DAG configurations:
// src/DependencyInjection/Configuration.php
$rootNode
->children()
->arrayNode('airflow_dag_ids')
->prototype('scalar')->end()
->defaultValue('%kernel.environment%_dag_ids')
->end();
How can I help you explore Laravel packages today?