统一地图以定义作业接口

https://blueprints.launchpad.net/sahara/+spec/unified-job-interface-map

本规范建议在作业创建的 API 中添加一个“接口”地图,以便注册作业的操作员可以定义一种统一、人类可读的方式来传递执行该作业可能需要或接受的所有参数、配置和变量。这将允许在作业执行阶段进行平台无关的向导操作,并允许用户以持久、标准化的格式记录他们自己作业的使用方法。

问题描述

目前,我们的每个数据处理引擎都需要或可以选择接收任何参数、变量、配置值和数据源(这些可以是 0、1 或多个输入到 0、1 或多个输出)。这迫使用户承担记录他们自己的作业的负担,而这些作业可能由不具备专业技术知识的操作员进行操作。

在作业注册时(而不是作业执行时)定义作业接口的一种单一、人类可读的方式,将带来以下好处:

  • 更统一的插件跨 UI 流程

  • 作业创建者(可能是一位技术用户)和作业执行者之间的明确职责分离

  • 纠正我们当前关于数据源的假设的一种方式(对于几个插件,我们不适当地假设 1 个输入源和 1 个输出源)

提议的变更

在创建作业时,可以选择将一个“interface”列表添加到作业 json 中(虽然我们实际上是在创建一个地图,但呈现列表结构将允许更直观的排序和更少的无用错误情况)。此列表的每个成员都描述了作业的一个参数(无论它是作为配置值、命名参数还是位置参数传递的)。

接口由以下 jsonschema 对象字段描述

"interface": {
    "type": "array",
    "uniqueItems": True,
    "items": {
        "type": "object",
        "properties": {
            "name": {
                "type": "string",
                "minLength": 1
            },
            "description": {
                "type": "string"
            },
            "mapping": {
                "type": "object",
                "properties": {
                    "type": {
                        "type": "string",
                        "enum": ["args", "configs", "params"]
                    },
                    "location": {
                        "type": "string",
                        "minLength": 1
                    }
                },
                "additionalProperties": False,
                "required": [
                    "type",
                    "location"
                ]
            },
            "value_type": {
                "type": "string",
                "enum": ["string",
                         "number",
                         "data_source",
                         "input_data_source",
                         "output_data_source"],
                "default": "string"
            },
            "required": {
                "type": "boolean"
            },
            "default": {
                "type": "string"
            }
        },
        "additionalProperties": False,
        "required": [
            "name",
            "mapping",
            "required"
        ]
    }
}

模式验证包括:

  1. 名称必须唯一。

  2. 映射必须唯一。

  3. 所有位置参数的位置必须是一个连续的整数序列,包含最小值 0。

  4. 位置参数可能不是必需的,但如果未提供,则必须给出默认值。

作业执行也将具有一个更简单的接口字段定义,由以下内容描述:

"interface": {
    "type": "simple_config"
}

执行时出现的新错误情况包括:

  1. 一个配置值或参数有两个定义(一个通过接口地图,一个通过 configs、params 或数据源)。

  2. 接口值未通过所字段指定的类型验证。

  3. 执行接口地图中的一个键不等于作业定义接口地图中的任何键。

  4. 指定的映射类型不被正在创建的作业类型接受(例如,为 Spark 作业指定 params 类型)。

  5. 输入数据源不包含数据。

  6. 输出数据源包含数据。

在附加位置值的情况下,args 列表中给定的位置参数将附加到接口位置参数列表(无论提供的值还是默认值)。这将允许 *args 模式,如果插件允许的话。

通过当前机制传递的 params 和 configs,如果与执行接口地图中的任何键不重叠,将被合并并按正常方式传递给作业。这同样适用于通过输入源和输出源字段传递的 $INPUT 和 $OUTPUT 参数。

替代方案

事实上,经过讨论,似乎没有更好的替代方案来概括这个计划(除了什么都不做)。将所有作业配置留到执行阶段是一个真正的问题,因为我们支持的数据处理引擎根本缺乏统一的接口。如果我们想创建一个统一的流程,我们需要创建一个;如果我们想创建一个,作业定义阶段对用户造成的痛苦最小,并且一个简单、扁平的地图是能够完成这项工作最易读和最灵活的工具。

数据模型影响

需要创建一个新的表来存储接口字段,由以下 DDL 描述(以 MySQL 语法呈现,以便于理解)

CREATE TABLE job_interface_arguments (
    id VARCHAR(36) NOT NULL,
    job_id VARCHAR(36) NOT NULL,
    name VARCHAR(80) NOT NULL, # ex: 'Main Class'
    description TEXT, # ex: 'The main Java class for this job.'
    mapping_type VARCHAR(80) NOT NULL, # ex: 'configs'
    location TEXT NOT NULL, # ex: 'edp.java.main_class'
    value_type VARCHAR(80) NOT NULL, # ex: 'string'
    required BOOL NOT NULL, # ex: 0
    order TINYINT NOT NULL, # ex: 1
    default_value TEXT, # ex: 'org.openstack.sahara.examples.WordCount'
    created_at DATETIME,
    PRIMARY KEY (id),
    FOREIGN KEY (job_id)
        REFERENCES jobs(id)
        ON DELETE CASCADE
);

此表将对 (job_id, name) 和 (job_id, mapping_type, location) 具有唯一性约束。

注意:虽然上面的 TEXT 类型字段(保存 Description)可以有效给出上限并存储为 VARCHAR,但 TEXT 在作业实际上需要过长的参数或配置了相当大的键的情况下更安全。此实现细节当然值得辩论,关于效率与可用性。

幸运的是,此更改不需要为现有数据进行迁移;接口字段表与作业表具有 (0, 1 或多个)-to-one 的关系,并且现有 configs/params/args 方法传播作业执行数据可以继续正常工作。

REST API 影响

创建作业模式将具有一个新的“interface”字段,如上所述。上述每个异常情况都将生成 400:Bad Request 错误。

此字段也将表示在 Job 资源的 GET 方法中。

创建作业执行模式将具有一个新的“interface”字段,如上所述。上述每个异常情况都将生成 400:Bad Request 错误。此字段不会在获取作业执行对象时返回;而是返回最终合并的配置。

预计没有其他影响。

注意:我对本文档中术语的更好选择持开放态度。由于“args”、“params”和“configs”已经被占用,因此命名一个新的选项变得困难。“Interface”和“Interface arguments”在我看来是所有情况下最好的选择。如果您能提出更好的方案,请提出。

注意:由于接口字段将在数据层表示为单独的记录,因此可以为该对象创建一组全新的 CRUD 方法。然而,我认为采取这种行动是不必要的:如果作业二进制文件发生更改,则无论如何都必须重新创建作业,并且一个合理的接口不需要在任何具体的二进制文件的生命周期内进行更改。

其他最终用户影响

python-saharaclient 需要与上述接口更改相对应的更改。

部署者影响

无。

开发者影响

无。

Sahara-image-elements impact

无。

Sahara-dashboard / Horizon 影响

立即更改不需要 Horizon 更改。任何使用此功能的 UI 都应表示为单独的蓝图和规范,并且无疑会涉及与此功能完全无关的向导决策。

实现

负责人

主要负责人

egafford

其他贡献者

工作项

  1. API 更新如上所述。

  2. DB 层更新如上所述。

  3. 作业执行参数验证和传播到集群。

  4. 测试。

  5. Python-saharaclient 更新和测试。

依赖项

目前没有。

测试

一个 tempest 测试将涵盖将每种映射类型注入到作业中(args、configs、params)。这将通过 Pig 作业进行测试,因为该类型可以接受所有上述类型。此测试将包括映射到 Swift 数据源和 HDFS 数据源的参数,以确保两种 URL 类型都通过流程保留。

假定彻底的单元测试。

文档影响

没有尚未提及的。

参考资料

聊天 (2014/12/05;从 2014-12-05T16:07:55 开始)