Back to Blog

DolphinScheduler Deep Dive: DAG Scheduling Engine and Data Pipeline Orchestration

1. [DolphinScheduler's Role in coomia-dip](#1-dolphinschedulers-role-in-coomia-dip)

CoomiaPublished on November 18, 202511 min read
Share this articleTwitter / X

Series: S8 Technology Deep Dives · Article 10 | Level: Advanced | Reading Time: 20 min

DolphinScheduler Deep Dive: DAG Scheduling Engine and Data Pipeline Orchestration

#TL;DR

  • DolphinScheduler is the batch processing scheduling core of coomia-dip's Pipeline & Orchestration (Pipeline & Orchestration Layer), responsible for orchestrating ETL pipelines, data quality checks, scheduled aggregations, and other DAG workflows
  • This article deeply analyzes DolphinScheduler's Master/Worker architecture, DAG parsing and task dispatch mechanisms, fault tolerance strategies, and integration with coomia-dip's Ontology layer
  • Covers multi-tenancy isolation, resource management, alert integration, and DolphinScheduler 3.x's API-driven workflow definitions

#Table of Contents

  1. DolphinScheduler's Role in coomia-dip
  2. Master-Worker Architecture
  3. DAG Parsing and Task Scheduling
  4. Task Types and Plugin System
  5. Fault Tolerance and Retry Mechanisms
  6. Multi-Tenancy and Resource Isolation
  7. Integration with coomia-dip Ontology
  8. API-Driven Workflow Definition
  9. Alerting and Monitoring Integration
  10. Performance Tuning and Production Practices
  11. Key Takeaways

#1. DolphinScheduler's Role in coomia-dip

#1.1 DolphinScheduler vs Temporal Division of Labor

coomia-dip uses both DolphinScheduler and Temporal, with clear responsibility boundaries:

DimensionDolphinSchedulerTemporal
Use caseBatch ETL, scheduled jobsReal-time event-driven, long-running workflows
TriggerCron / Manual / DependencyAPI / Event / Schedule
Task granularityCoarse (Shell/SQL/Flink Job)Fine (function-level Activity)
DAG definitionVisual drag-and-drop + JSONCode-defined
User audienceData engineersDevelopers
coomia-dip LayerF (Pipeline)E (Agent Runtime)

#1.2 Typical Use Cases

Code
DolphinScheduler-managed pipelines:

1. Bronze Layer Data Ingestion
   ┌──────────┐    ┌──────────┐    ┌──────────┐
   │ MySQL CDC│───→│ Kafka    │───→│ Iceberg  │
   │ Full Sync│    │ Topic    │    │ Bronze   │
   └──────────┘    └──────────┘    └──────────┘

2. Silver Layer Data Cleansing
   ┌──────────┐    ┌──────────┐    ┌──────────┐
   │ Iceberg  │───→│ Flink    │───→│ Iceberg  │
   │ Bronze   │    │ SQL Job  │    │ Silver   │
   └──────────┘    └──────────┘    └──────────┘

3. Gold Layer Aggregation
   ┌──────────┐    ┌──────────┐    ┌──────────┐
   │ Iceberg  │───→│ Trino    │───→│ Doris    │
   │ Silver   │    │ Query    │    │ Gold     │
   └──────────┘    └──────────┘    └──────────┘

#2. Master-Worker Architecture

#2.1 Core Components

Code
┌─────────────────────────────────────────────────────┐
│                 DolphinScheduler                     │
│                                                      │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐          │
│  │  Master  │  │  Master  │  │  API     │          │
│  │  Server  │  │  Server  │  │  Server  │          │
│  └────┬─────┘  └────┬─────┘  └────┬─────┘          │
│       │              │              │                │
│  ┌────┴──────────────┴──────────────┴────┐          │
│  │           ZooKeeper (Registry)         │          │
│  └────┬──────────────┬──────────────┬────┘          │
│       │              │              │                │
│  ┌────┴─────┐  ┌────┴─────┐  ┌────┴─────┐          │
│  │  Worker  │  │  Worker  │  │  Worker  │          │
│  │  Server  │  │  Server  │  │  Server  │          │
│  └──────────┘  └──────────┘  └──────────┘          │
│                                                      │
│  ┌──────────────────────────────────────┐           │
│  │     PostgreSQL (Metadata Store)       │           │
│  └──────────────────────────────────────┘           │
└─────────────────────────────────────────────────────┘

#2.2 Master Server Responsibilities

  • DAG parsing: Parses workflow definitions into DAG graphs
  • Task scheduling: Dispatches tasks based on dependencies and priorities
  • Fault management: Detects Worker failures, reassigns tasks
  • Slot management: Tracks available slots on each Worker
Java
// Master scheduling loop (simplified)
public class MasterSchedulerThread implements Runnable {
    @Override
    public void run() {
        while (isRunning) {
            // 1. Fetch commands to schedule from DB
            List<Command> commands = commandService.findCommandPage(masterSlots);

            for (Command command : commands) {
                // 2. Build DAG
                ProcessInstance processInstance = createProcessInstance(command);
                DAG<String, TaskNode, TaskNodeRelation> dag = buildDAG(processInstance);

                // 3. Get ready tasks (all predecessors completed)
                List<TaskNode> readyTasks = getReadyTasks(dag, processInstance);

                // 4. Dispatch to Workers
                for (TaskNode task : readyTasks) {
                    WorkerInfo selectedWorker = selectWorker(task);
                    dispatchTask(task, selectedWorker);
                }
            }

            Thread.sleep(100);  // Scheduling interval
        }
    }
}

#2.3 Worker Server Responsibilities

  • Task execution: Receives and executes tasks from Master
  • Log collection: Collects task logs and reports them
  • Heartbeat reporting: Periodically reports liveness and load to Registry
Java
// Worker task execution flow
public class WorkerTaskExecuteRunnable implements Runnable {
    @Override
    public void run() {
        try {
            // 1. Parse task parameters
            TaskExecutionContext context = buildContext(taskInstance);

            // 2. Create task plugin instance
            AbstractTask task = TaskPluginManager.createTask(
                taskInstance.getTaskType(), context
            );

            // 3. Initialize
            task.init();

            // 4. Execute
            task.handle();

            // 5. Report result
            reportTaskResult(task.getExitStatus());
        } catch (Exception e) {
            reportTaskResult(TaskExecutionStatus.FAILURE);
        }
    }
}

#3. DAG Parsing and Task Scheduling

#3.1 DAG Data Structure

JSON
{
  "tasks": [
    {
      "id": "task-extract",
      "name": "Bronze Layer Extract",
      "type": "FLINK",
      "dependence": [],
      "params": {
        "flinkVersion": "1.18",
        "jobManagerMemory": "2g",
        "taskManagerMemory": "4g",
        "mainJar": "coomia-dip-pipeline-1.0.jar",
        "mainClass": "com.onto.pipeline.BronzeExtract"
      }
    },
    {
      "id": "task-validate",
      "name": "Data Quality Check",
      "type": "SQL",
      "dependence": ["task-extract"],
      "params": {
        "datasource": "doris-analytics",
        "sql": "SELECT COUNT(*) FROM quality_check WHERE status='FAIL'"
      }
    },
    {
      "id": "task-transform",
      "name": "Silver Layer Transform",
      "type": "FLINK",
      "dependence": ["task-validate"],
      "conditionResult": {
        "successNode": ["task-load"],
        "failedNode": ["task-alert"]
      }
    },
    {
      "id": "task-load",
      "name": "Gold Layer Load",
      "type": "SQL",
      "dependence": ["task-transform"]
    },
    {
      "id": "task-alert",
      "name": "Quality Alert",
      "type": "HTTP",
      "dependence": ["task-transform"]
    }
  ]
}

#3.2 Topological Sort and Parallelism

Code
DAG topological sort result:

Level 0: [task-extract]              → Parallelism 1
Level 1: [task-validate]             → Parallelism 1
Level 2: [task-transform]            → Parallelism 1
Level 3: [task-load, task-alert]     → Parallelism 2 (can run in parallel)

#3.3 Scheduling Priority

Java
// Priority calculation formula
int priority = processInstancePriority * 10 + taskInstancePriority;

// Priority levels
public enum Priority {
    HIGHEST(0),
    HIGH(1),
    MEDIUM(2),
    LOW(3),
    LOWEST(4);
}

// Scheduling queue sorted by priority
PriorityQueue<TaskInstance> queue = new PriorityQueue<>(
    Comparator.comparingInt(TaskInstance::getProcessInstancePriority)
              .thenComparingInt(TaskInstance::getTaskInstancePriority)
              .thenComparing(TaskInstance::getSubmitTime)
);

#4. Task Types and Plugin System

#4.1 Task Types Used in coomia-dip

Task TypePurposecoomia-dip Scenario
SHELLScript executionData file processing, environment prep
SQLDatabase queriesData quality checks, aggregation
FLINKFlink JobsCDC pipelines, stream processing
HTTPREST API callsTrigger coomia-dip APIs
PYTHONPython scriptsML model training, data analysis
DEPENDENTCross-workflow dependencyPipeline dependency chains
SUB_PROCESSSub-workflowModular pipelines
CONDITIONSConditional branchingData quality branching

#4.2 Custom Task Plugin

Java
// coomia-dip custom task plugin: Ontology Sync
@AutoService(TaskChannelFactory.class)
public class OntologySyncTaskChannelFactory implements TaskChannelFactory {

    @Override
    public String getName() {
        return "ONTOLOGY_SYNC";
    }

    @Override
    public TaskChannel create() {
        return new OntologySyncTaskChannel();
    }
}

public class OntologySyncTask extends AbstractTask {

    private final OntologySyncParameters parameters;

    @Override
    public void handle() throws TaskException {
        // 1. Call coomia-dip gRPC API to get Schema
        OntologySchema schema = ontologyClient.getSchema(
            parameters.getWorldId(),
            parameters.getObjectType()
        );

        // 2. Sync to target storage
        syncToTarget(schema, parameters.getTargetDataSource());

        // 3. Record sync result
        setExitStatusCode(TaskConstants.EXIT_CODE_SUCCESS);
    }
}

#5. Fault Tolerance and Retry Mechanisms

#5.1 Failure Types and Handling Strategies

Failure TypeDetectionHandling Strategy
Worker crashZooKeeper heartbeat timeoutReassign task to another Worker
Master crashZooKeeper electionStandby Master takes over
Task execution failureNon-zero exit codeRetry per retry policy
Task timeoutTimeout detection threadKill process + mark failed
Network partitionZooKeeper sessionRe-register after recovery

#5.2 Retry Configuration

JSON
{
  "task": {
    "name": "Bronze Layer Extract",
    "retryTimes": 3,
    "retryInterval": "1 minute",
    "failRetryStrategy": "RETRY_FROM_CURRENT_TASK",
    "timeout": {
      "strategy": "WARN_AND_FAILED",
      "interval": 3600,
      "enable": true
    }
  }
}

#5.3 Workflow-Level Fault Tolerance

JSON
{
  "processDefinition": {
    "name": "CDC Bronze Pipeline",
    "failureStrategy": "CONTINUE",
    "processInstancePriority": "HIGH",
    "warningType": "FAILURE",
    "warningGroupId": 1
  }
}
StrategyBehavior
ENDAny task failure terminates entire workflow
CONTINUEFailed task marked, independent downstream tasks continue

#6. Multi-Tenancy and Resource Isolation

#6.1 coomia-dip Tenant Model

Code
DolphinScheduler Tenant Hierarchy:

Security Level
├── Admin (coomia-dip administrator)
│   └── Manages all projects and resources
├── Tenant: coomia-dip-world-001
│   ├── User: data-engineer-1
│   ├── Project: bronze-pipelines
│   ├── Project: silver-pipelines
│   └── Resource: /shared/jars/
├── Tenant: coomia-dip-world-002
│   ├── User: data-engineer-2
│   └── Project: etl-pipelines
└── Tenant: coomia-dip-system
    └── Project: system-maintenance

#6.2 Worker Group Resource Isolation

YAML
# Worker group configuration
worker-groups:
  - name: "high-memory"
    workers: ["worker-1", "worker-2"]
    description: "16GB+ memory for large Flink jobs"

  - name: "gpu"
    workers: ["worker-3"]
    description: "GPU nodes for ML training"

  - name: "default"
    workers: ["worker-4", "worker-5", "worker-6"]
    description: "General task execution"

#6.3 Queue Management

YAML
# YARN queue mapping
queues:
  - name: "production"
    capacity: 60%
    max-capacity: 80%
    tenant: "coomia-dip-production"

  - name: "development"
    capacity: 20%
    max-capacity: 40%
    tenant: "coomia-dip-dev"

  - name: "system"
    capacity: 20%
    max-capacity: 30%
    tenant: "coomia-dip-system"

#7. Integration with coomia-dip Ontology

#7.1 Ontology-Based Pipeline Templates

Python
# coomia-dip pipeline template generator
class OntologyPipelineGenerator:
    """Auto-generates DolphinScheduler pipelines from Ontology Schema"""

    def generate_cdc_pipeline(
        self,
        object_type: str,
        source_db: str,
        target_lakehouse: str,
    ) -> dict:
        return {
            "name": f"cdc-{object_type}-pipeline",
            "tasks": [
                {
                    "name": f"extract-{object_type}",
                    "type": "FLINK",
                    "params": self._build_flink_cdc_params(
                        object_type, source_db
                    ),
                },
                {
                    "name": f"validate-{object_type}",
                    "type": "SQL",
                    "dependence": [f"extract-{object_type}"],
                    "params": self._build_quality_check_params(object_type),
                },
                {
                    "name": f"transform-{object_type}",
                    "type": "FLINK",
                    "dependence": [f"validate-{object_type}"],
                    "params": self._build_transform_params(
                        object_type, target_lakehouse
                    ),
                },
            ],
        }

    def _build_flink_cdc_params(self, object_type: str, source_db: str) -> dict:
        schema = self.ontology_client.get_schema(object_type)
        return {
            "flinkVersion": "1.18",
            "mainClass": "com.onto.pipeline.CdcExtractor",
            "programArguments": (
                f"--source-table {schema.source_table} "
                f"--source-db {source_db} "
                f"--target-table iceberg.bronze.{object_type} "
                f"--columns {','.join(p.name for p in schema.properties)}"
            ),
        }

#7.2 Pipeline Execution Events Written Back to Ontology

Python
# DolphinScheduler Webhook → coomia-dip audit events
@app.post("/api/v1/dolphinscheduler/webhook")
async def handle_ds_webhook(event: DSWebhookEvent):
    if event.type == "PROCESS_INSTANCE_SUCCESS":
        await ontology_client.create_audit_event(
            event_type="PIPELINE_COMPLETED",
            source="DolphinScheduler",
            metadata={
                "process_id": event.process_instance_id,
                "process_name": event.process_name,
                "duration_seconds": event.duration,
                "task_count": event.task_count,
            },
        )
    elif event.type == "PROCESS_INSTANCE_FAILURE":
        await ontology_client.create_audit_event(
            event_type="PIPELINE_FAILED",
            source="DolphinScheduler",
            metadata={
                "process_id": event.process_instance_id,
                "error": event.error_message,
                "failed_task": event.failed_task_name,
            },
        )

#8. API-Driven Workflow Definition

#8.1 DolphinScheduler 3.x REST API

Python
import httpx

class DolphinSchedulerClient:
    """DolphinScheduler REST API client"""

    def __init__(self, base_url: str, token: str):
        self.base_url = base_url
        self.headers = {"token": token}

    async def create_process_definition(
        self,
        project_code: int,
        name: str,
        task_definition_json: str,
        task_relation_json: str,
    ) -> dict:
        async with httpx.AsyncClient() as client:
            response = await client.post(
                f"{self.base_url}/projects/{project_code}/process-definition",
                headers=self.headers,
                data={
                    "name": name,
                    "taskDefinitionJson": task_definition_json,
                    "taskRelationJson": task_relation_json,
                    "tenantCode": "coomia-dip",
                    "executionType": "PARALLEL",
                    "timeout": 0,
                },
            )
            return response.json()

    async def run_process(
        self,
        project_code: int,
        process_definition_code: int,
        schedule_time: str | None = None,
    ) -> dict:
        async with httpx.AsyncClient() as client:
            data = {
                "processDefinitionCode": process_definition_code,
                "failureStrategy": "CONTINUE",
                "warningType": "FAILURE",
                "scheduleTime": schedule_time,
                "startNodeList": "",
                "taskDependType": "TASK_POST",
                "runMode": "RUN_MODE_SERIAL",
                "processInstancePriority": "MEDIUM",
                "workerGroup": "default",
            }
            response = await client.post(
                f"{self.base_url}/projects/{project_code}/executors/start-process-instance",
                headers=self.headers,
                data=data,
            )
            return response.json()

#9. Alerting and Monitoring Integration

#9.1 Alert Plugin Configuration

YAML
# coomia-dip DolphinScheduler alert configuration
alert:
  plugins:
    - name: "webhook"
      config:
        url: "http://coomia-dip-api:8080/api/v1/alerts/dolphinscheduler"
        headerParams: '{"Authorization": "Bearer ${coomia-dip_TOKEN}"}'
        bodyParams: '{"source": "dolphinscheduler", "event": "${msg}"}'
        contentField: "msg"
        requestType: "POST"

#9.2 Prometheus Metrics

YAML
# DolphinScheduler Prometheus metrics
metrics:
  - name: dolphinscheduler_master_running_process_count
    type: gauge
    help: "Number of currently running process instances"

  - name: dolphinscheduler_master_task_dispatch_count
    type: counter
    help: "Total task dispatch count"

  - name: dolphinscheduler_worker_task_execution_count
    type: counter
    labels: [task_type, status]
    help: "Worker task execution count"

  - name: dolphinscheduler_worker_task_execution_duration
    type: histogram
    labels: [task_type]
    help: "Task execution duration distribution"

#10. Performance Tuning and Production Practices

#10.1 Master Tuning

YAML
master:
  max-cpu-load-avg: 0.7          # CPU load ceiling
  reserved-memory: 0.3            # Reserved memory ratio
  exec-threads: 100               # Scheduling threads
  dispatch-task-number: 3         # Tasks dispatched per cycle
  host-selector: lower-weight     # Worker selection strategy
  task-commit-interval: 1000      # Task commit interval (ms)

#10.2 Worker Tuning

YAML
worker:
  exec-threads: 100               # Task execution threads
  heartbeat-interval: 10          # Heartbeat interval (seconds)
  max-cpu-load-avg: 0.75         # CPU load ceiling
  reserved-memory: 0.25           # Reserved memory ratio
  tenant-auto-create: true        # Auto-create Linux users

#10.3 Database Optimization

SQL
-- Periodically clean historical data (retain 90 days)
DELETE FROM t_ds_process_instance
WHERE start_time < DATE_SUB(NOW(), INTERVAL 90 DAY)
  AND state IN (7, 8);  -- SUCCESS, FAILURE

-- Create indexes for common queries
CREATE INDEX idx_process_instance_state_start
ON t_ds_process_instance (state, start_time);

CREATE INDEX idx_task_instance_process_state
ON t_ds_task_instance (process_instance_id, state);

#10.4 Resource Planning

ComponentInstancesCPUMemoryDisk
Master2 (HA)4 cores8 GB50 GB
Worker3-104-8 cores8-16 GB100 GB
API Server22 cores4 GB20 GB
PostgreSQL1 (HA)4 cores16 GBSSD 200 GB
ZooKeeper32 cores4 GBSSD 50 GB

#11. Key Takeaways

TopicKey Conclusion
PositioningBatch DAG scheduling, complementary to Temporal
ArchitectureMaster scheduling + Worker execution + ZooKeeper registry
DAGTopological sort determines execution order; same-level tasks can parallelize
Fault toleranceAutomatic Worker failure reassignment, task-level retries
Multi-tenancyTenant + Project + Worker Group three-level isolation
Ontology integrationAuto-generate pipeline definitions from Schema
API-driven3.x REST API supports code-based pipeline management
MonitoringPrometheus + custom alert plugins

Next up: S8-11 (already published) deeply analyzes Redis's 5 roles in coomia-dip.