Manage Jobs
Introduction
Starting from 1.0.0, Apache Gravitino introduces a new submodule called the job system for users to register, run, and manage jobs. This job system integrates with the existing metadata management, enabling users to execute the jobs or actions based on the metadata, known as metadata-driven actions. For instance, this allows users to run jobs for tasks such as compacting Iceberg tables or cleaning old data based on TTL properties.
The aim of the job system is to provide a unified way to manage job templates and jobs, including registering job templates, running jobs based on the job templates, and other related tasks. The job system itself is a unified job submitter that allows users to run jobs through it, but it doesn't provide the actual job execution capabilities. Instead, it relies on the existing job executors (schedulers), such as Apache Airflow, Apache Livy, to execute the jobs. Gravitino's job system provides an extensible way to connect to different job executors.
- The job system is a new feature introduced in Gravitino 1.0.0, and it is still under development, so some features may not be fully implemented yet.
- The aim of the job system is not to replace the existing job executors. So, it can only support running a single job at a time, and it doesn't support job scheduling for now.
Job Operations
Register a New Job Template
Before running a job, the first step is to register a job template. Gravitino
supports two types of job templates: shell and spark (we will add more job templates in the
future).
Shell Job Template
The shell job template is used to run scripts, it can be a shell script, or any executable
script. The template is defined as follows:
{
"name": "my_shell_job_template",
"jobType": "shell",
"comment": "A shell job template to run a script",
"executable": "/path/to/my_script.sh",
"arguments": ["{{arg1}}", "{{arg2}}"],
"environments": {
"ENV_VAR1": "{{value1}}",
"ENV_VAR2": "{{value2}}"
},
"customFields": {
"field1": "{{value1}}",
"field2": "{{value2}}"
},
"scripts": ["/path/to/script1.sh", "/path/to/script2.sh"]
}
Here is a brief description of the fields in the job template:
name: The name of the job template, must be unique.jobType: The type of the job template, useshellfor a shell job template.comment: A comment for the job template, which can be used to describe the job template.executable: The path to the executable script, which can be a shell script or any executable script.arguments: The arguments to pass to the executable script.environments: The environment variables to set when running the jo.customFields: Custom fields for the job template, which can be used to store additional information.scripts: A list of scripts that the main executable script can use.
Please note that:
- The
executableandscriptsmust be accessible by the Gravitino server. Gravitino supports accessing files from the local file system, HTTP(S) URLs, and FTP(S) URLs (more distributed file system support will be added in the future). So theexecutableandscriptscan be a local file path, or a URL likehttp://example.com/my_script.sh. - The
executable,arguments,environments,customFieldsandscriptscan use placeholders like{{arg1}}and{{value1}}, the style is{{foo}}. They will be replaced with the actual values when running the job, so you can use them to pass dynamic values to the job template. - Gravitino will copy the
executableandscriptsfiles to the job working directory when running the job, so you can use the relative path in theexecutableandscriptsto refer to other scripts in the job working directory.
Spark Job Template
The spark job template is used to run Spark jobs, it is a Spark application JAR file for now.
The template is defined as follows:
{
"name": "my_spark_job_template",
"jobType": "spark",
"comment": "A Spark job template to run a Spark application",
"executable": "/path/to/my_spark_app.jar",
"arguments": ["{{arg1}}", "{{arg2}}"],
"environments": {
"ENV_VAR1": "{{value1}}",
"ENV_VAR2": "{{value2}}"
},
"customFields": {
"field1": "{{value1}}",
"field2": "{{value2}}"
},
"className": "com.example.MySparkApp",
"jars": ["/path/to/dependency1.jar", "/path/to/dependency2.jar"],
"files": ["/path/to/file1.txt", "/path/to/file2.txt"],
"archives": ["/path/to/archive1.zip", "/path/to/archive2.zip"],
"configs": {
"spark.executor.memory": "2g",
"spark.executor.cores": "2"
}
}
Here is a brief description of the fields in the Spark job template:
name: The name of the job template, which must be unique.jobType: The type of the job template, usesparkfor Spark job template.comment: A comment for the job template, which can be used to describe the job template.executable: The path to the Spark application JAR file, which can be a local file path or a URL with a supported scheme.arguments: The arguments to pass to the Spark application.environments: The environment variables to set when running the job.customFields: Custom fields for the job template, which can be used to store additional information.className: The main class of the Spark application. It is required for Java/Scala Spark application. For PySpark application, this field can benullinstead.jars: A list of JAR files to add to the Spark job classpath, which can be a local file path or a URL with a supported scheme.files: A list of files to be copied to the working directory of the Spark job, which can be a local file path or a URL with a supported scheme.archives: A list of archives to be extracted to the working directory of the Spark job, which can be a local file path or a URL with a supported scheme.configs: A map of Spark configurations to set when running the Spark job.
Note that:
- The
executable,jars,files, andarchivesmust be accessible by the Gravitino server. Gravitino supports accessing files from the local file system, HTTP(S) URLs, and FTP(S) URLs (more distributed file system supports will be added in the future). So theexecutable,jars,files, andarchivescan be a local file path, or a URL likehttp://example.com/my_spark_app.jar. - The
executable,arguments,environments,customFields,className,jars,files,archives, andconfigscan use placeholders like{{arg1}}and{{value1}}, the style is{{foo}}. They will be replaced with the actual values when running the job, so you can use them to pass dynamic values to the job template. - Gravitino will copy the
executable,jars,files, andarchivesfiles to the job working directory when running the job, so you can use the relative path in theexecutable,jars,files, andarchivesto refer to other files in the job working directory. - The
classNameis required for the Java and Scala Spark job template, it is the main class of the Spark application to be executed. For PySpark job template, this field can benullinstead.
To register a job template, you can use REST API or the Java and Python SDKs. Here is the example to register a shell job template:
- Shell
- Java
- Python
curl -X POST -H "Accept: application/vnd.gravitino.v1+json" \
-H "Content-Type: application/json" \
-d '{
"jobTemplate": {
"name": "my_shell_job_template",
"jobType": "shell",
"comment": "A shell job template to run a script",
"executable": "/path/to/my_script.sh",
"arguments": ["{{arg1}}", "{{arg2}}"],
"environments": {
"ENV_VAR1": "{{value1}}",
"ENV_VAR2": "{{value2}}"
},
"customFields": {
"field1": "{{value1}}",
"field2": "{{value2}}"
},
"scripts": ["/path/to/script1.sh", "/path/to/script2.sh"]
}
}' \
http://localhost:8090/api/metalakes/test/jobs/templates
ShellJobTemplate jobTemplate = ShellJobTemplate.builder()
.name("my_shell_job_template")
.comment("A shell job template to run a script")
.executable("/path/to/my_script.sh")
.arguments(List.of("{{arg1}}", "{{arg2}}"))
.environments(Map.of("ENV_VAR1", "{{value1}}", "ENV_VAR2", "{{value2}}"))
.customFields(Map.of("field1", "{{value1}}", "field2", "{{value2}}"))
.scripts(List.of("/path/to/script1.sh", "/path/to/script2.sh"))
.build();
GravitinoClient client = ...;
client.registerJobTemplate(jobTemplate);
shell_job_template = (
ShellJobTemplate.builder()
.with_name("my_shell_job_template")
.with_comment("A shell job template to run a script")
.with_executable("/path/to/my_script.sh")
.with_arguments(["{{arg1}}", "{{arg2}}"])
.with_environments({"ENV_VAR1": "{{value1}}", "ENV_VAR2": "{{value2}}"})
.with_custom_fields({"field1": "{{value1}}", "field2": "{{value2}}"})
.with_scripts(["/path/to/script1.sh", "/path/to/script2.sh"])
.build()
)
client = GravitinoClient(...)
client.register_job_template(shell_job_template)
List Registered Job Templates
List all the registered job templates under a metalake by using the REST API or the Java and Python SDKs.
- Shell
- Java
- Python
curl -X GET -H "Accept: application/vnd.gravitino.v1+json" \
http://localhost:8090/api/metalakes/test/jobs/templates
Or using query parameter "details=true" to get more details of the job templates:
curl -X GET -H "Accept: application/vnd.gravitino.v1+json" \
http://localhost:8090/api/metalakes/test/jobs/templates?details=true
GravitinoClient client = ...;
List<JobTemplate> detailedJobTemplates = client.listJobTemplates();
client = GravitinoClient(...)
detailed_job_templates = client.list_job_templates()
Get a Registered Job Template by Name
Get a registered job template by its name using the REST API or the Java and Python SDKs.
- Shell
- Java
- Python
curl -X GET -H "Accept: application/vnd.gravitino.v1+json" \
http://localhost:8090/api/metalakes/test/jobs/templates/my_shell_job_template
GravitinoClient client = ...;
JobTemplate jobTemplate = client.getJobTemplate("my_shell_job_template");
client = GravitinoClient(...)
job_template = client.get_job_template("my_shell_job_template")
Delete a Registered Job Template by Name
Delete a registered job template by its name using the REST API or the Java and Python SDKs.
Note that deleting a job template will also delete all the jobs that are using this job template.
If there are queued, started, or to be cancelled jobs that are using this job template, the deletion
will fail with an InUseException error.
- Shell
- Java
- Python
curl -X DELETE -H "Accept: application/vnd.gravitino.v1+json" \
http://localhost:8090/api/metalakes/test/jobs/templates/my_shell_job_template
GravitinoClient client = ...;
client.deleteJobTemplate("my_shell_job_template");
client = GravitinoClient(...)
client.delete_job_template("my_shell_job_template")
Alter a Registered Job Template
Alter a registered job template by its name using the REST API or the Java and Python
SDKs. Gravitino supports altering the name, comment and template fields of the job template.
- Shell
- Java
- Python
curl -X PUT -H "Accept: application/vnd.gravitino.v1+json" \
-H "Content-Type: application/json" \
-d '{
"updates": [
{
"@type": "rename",
"newName": "altered_shell_job_template"
},
{
"@type": "comment",
"newComment": "An altered shell job template"
},
{
"@type": "updateTemplate",
"newTemplate": {
"@type": "shell",
"newExecutable": "/new/path/to/my_script.sh",
"newArguments": ["{{new_arg1}}", "{{new_arg2}}"],
"newEnvironments": {
"NEW_ENV_VAR1": "{{new_value1}}",
"NEW_ENV_VAR2": "{{new_value2}}"
},
"newCustomFields": {
"new_field1": "{{new_value1}}",
"new_field2": "{{new_value2}}"
},
"newScripts": ["/new/path/to/script1.sh", "/new/path/to/script2.sh"]
}
}
]
}' http://localhost:8090/api/metalakes/test/jobs/templates/my_shell_job_template
ShellTemplateUpdate newTemplate = ShellTemplateUpdate.builder()
.withNewExecutable("/new/path/to/my_script.sh")
.withNewArguments(ImmutableList.of("{{new_arg1}}", "{{new_arg2}}"))
.withNewEnvironments(ImmutableMap.of("NEW_ENV_VAR1", "{{new_value1}}", "NEW_ENV_VAR2", "{{new_value2}}"))
.withNewCustomFields(ImmutableMap.of("new_field1", "{{new_value1}}", "new_field2", "{{new_value2}}"))
.withNewScripts(List.of("/new/path/to/script1.sh", "/new/path/to/script2.sh"))
.build();
List<JobTemplateChange> changes = List.of(
JobTemplateChange.rename("altered_shell_job_template"),
JobTemplateChange.updateComment("An altered shell job template"),
JobTemplateChange.updateTemplateUpdateJobTemplate(newTemplate));
GravitinoClient client = ...;
client.alterJobTemplate("my_shell_job_template", changes);
new_template = ShellTemplateUpdate(
new_executable="/new/path/to/my_script.sh",
new_arguments=["{{new_arg1}}", "{{new_arg2}}"],
new_environments={"NEW_ENV_VAR1": "{{new_value1}}", "NEW_ENV_VAR2": "{{new_value2}}"},
new_custom_fields={"new_field1": "{{new_value1}}", "new_field2": "{{new_value2}}"},
new_scripts=["/new/path/to/script1.sh", "/new/path/to/script2.sh"]
)
changes = [
JobTemplateChange.rename("altered_shell_job_template"),
JobTemplateChange.update_comment("An altered shell job template"),
JobTemplateChange.update_template(new_template)
]
client = GravitinoClient(...)
client.alter_job_template("my_shell_job_template", changes)
Run a Job Based on a Job Template
To run a job based on the registered job template, you can use the REST API or the Java and Python SDKs. When running a job, you need to provide the job template name and the parameters to replace the placeholders in the job template.
Gravitino leverages the job executor to run the job, so you need to specify the job executor
through configuration gravitino.job.executor. By default, it is set to "local", which means
the job will be launched as a process within the same machine that runs the Gravitino server. Note
that the local job executor is only for testing. If you want to run the job in a distributed environment,
you need to implement your own JobExecutor and set the configuration; see
Implement a custom job executor section below.
When running a Spark job template with the local executor, configure one of:
gravitino.jobExecutor.local.sparkHomeSPARK_HOME
If neither is set before the Gravitino server starts, Spark jobs may fail to start.
- Shell
- Java
- Python
curl -X POST -H "Accept: application/vnd.gravitino.v1+json" \
-H "Content-Type: application/json" \
-d '{
"jobTemplateName": "my_shell_job_template",
"jobConf": {
"arg1": "value1",
"arg2": "value2",
"value1": "env_value1",
"value2": "env_value2"
}
}' \
http://localhost:8090/api/metalakes/test/jobs/runs
GravitinoClient client = ...;
JobHandle jobHandle = client.runJob("my_shell_job_template", ImmutableMap.of(
"arg1", "value1",
"arg2", "value2",
"value1", "env_value1",
"value2", "env_value2"
));
client = GravitinoClient(...)
job_handle = client.run_job("my_shell_job_template", {
"arg1": "value1",
"arg2": "value2",
"value1": "env_value1",
"value2": "env_value2"
})
The returned JobHandle contains the job ID and other information about the job. Use the job ID to
check the job status and cancel the job.
The runJob API will return immediately after the job is submitted to the job executor, and the job will be executed asynchronously. Check the job status using the job ID returned by the runJob API.
List All Jobs
List all the jobs under a metalake by using the REST API or the Java and Python SDKs.
- Shell
- Java
- Python
curl -X GET -H "Accept: application/vnd.gravitino.v1+json" \
http://localhost:8090/api/metalakes/test/jobs/runs
Or using query parameter "jobTemplateName=my_shell_job_template" to filter jobs by job template name:
curl -X GET -H "Accept: application/vnd.gravitino.v1+json" \
http://localhost:8090/api/metalakes/test/jobs/runs?jobTemplateName=my_shell_job_template
GravitinoClient client = ...;
List<JobHandle> jobHandles = client.listJobs();
// To filter jobs by job template name
List<JobHandle> filteredJobHandles = client.listJobs("my_shell_job_template");
client = GravitinoClient(...)
job_handles = client.list_jobs()
# To filter jobs by job template name
filtered_job_handles = client.list_jobs(job_template_name="my_shell_job_template")