Presto 验证器¶
Presto 验证器是一个用于运行查询并验证正确性的工具。它可以用来测试新的 Presto 版本是否产生正确的查询结果,或者测试一对 Presto 查询是否具有相同的语义。
在每次 Presto 版本发布期间,都会运行验证器以确保没有正确性回归。
使用验证器¶
在 MySQL 数据库中,创建以下表并用您要运行的查询加载它
CREATE TABLE verifier_queries (
id int(11) unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
suite varchar(256) NOT NULL,
name varchar(256) DEFAULT NULL,
control_catalog varchar(256) NOT NULL,
control_schema varchar(256) NOT NULL,
control_query text NOT NULL,
control_username varchar(256) DEFAULT NULL,
control_password varchar(256) DEFAULT NULL,
control_session_properties text DEFAULT NULL,
test_catalog varchar(256) NOT NULL,
test_schema varchar(256) NOT NULL,
test_query text NOT NULL,
test_username varchar(256) DEFAULT NULL,
test_password varchar(256) DEFAULT NULL,
test_session_properties text DEFAULT NULL)
接下来,创建一个 config.properties
文件
source-query.suites=suite
source-query.database=jdbc:mysql://127.0.0.1:3306/mydb?user=my_username&password=my_password
control.hosts=127.0.0.1
control.http-port=8080
control.jdbc-port=8080
control.application-name=verifier-test
test.hosts=127.0.0.1
test.http-port=8081
test.jdbc-port=8081
test.application-name=verifier-test
test-id=1
验证器可以通过设置配置 running-mode
来在 query-bank
或 control-test
模式下运行。
control-test
: 这是默认模式。控制查询和测试查询都会执行,并比较它们的校验和结果。query-bank
: 在此模式下,控制查询将被跳过,比较将在保存的快照结果和测试结果之间进行。
创建一个 verifier_snapshots
表
CREATE TABLE verifier_snapshots (
id int(11) unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
suite varchar(256) NOT NULL,
name varchar(256) NOT NULL DEFAULT '.',
is_explain BOOLEAN NOT NULL DEFAULT false,
snapshot json NOT NULL,
updated_at datetime NOT NULL DEFAULT now(),
UNIQUE(suite, name, is_explain));
下载 presto-verifier-0.289-executable.jar 并将其重命名为 verifier.jar
。要运行验证器
java -Xmx1G -jar verifier.jar verify config.properties
在 query-bank
模式下运行之前,必须保存快照。添加配置
running-mode=query-bank
save-snapshot=true
运行验证器,快照将被保存到表 verifier_snapshots
中。
要在 query-bank
模式下运行,请设置 save-snapshot=false
或直接删除它
running-mode=query-bank
#save-snapshot=true
验证器流程¶
以下步骤总结了验证器的流程。
- 导入源查询
从 MySQL 表中读取源查询列表(带有配置的查询对)。
- 查询预处理和过滤
将覆盖应用于每个查询的目录、模式、用户名和密码。
根据白名单和黑名单过滤查询。白名单在黑名单之前应用。
过滤掉语法无效的查询。
过滤掉不支持验证的查询。支持
Select
、Insert
、CreateTableAsSelect
、create table
和create view
。
- 查询重写
在执行之前重写查询,以确保不会修改生产数据。
- 将
Select
查询重写为CreateTableAsSelect
列名是通过运行带有
LIMIT 0
的Select
查询来确定的。对于未命名的列使用人工名称。
- 将
- 将
Insert
和CreateTableAsSelect
查询重写以替换其表名。 构造一个设置查询以创建
Insert
查询所需的表。
- 将
如果设置了配置,则根据
nondeterministic-function-substitutes
重写函数调用。
- 查询执行
从概念上讲,验证器配置了控制集群和测试集群。但是,对于某些测试,它们可能指向同一个 Presto 集群。在
Query-bank
模式下,控制集群会被跳过,取而代之的是使用保存的快照。- 对于每个源查询,按以下顺序执行以下查询。
控制设置查询
控制查询
测试设置查询
测试查询
控制和测试拆卸查询
- 查询会受到超时和重试的影响。
集群连接失败和短暂的 Presto 故障会进行重试。
查询重试可能会隐藏可靠性问题,因此验证器会记录所有发生的 Presto 查询故障,包括重试。
某些查询故障会自动提交以进行重新验证,例如查询期间分区删除或表删除。
有关查询故障的自动解决,请参阅 故障解决。
- 输出结果
验证结果可以导出为
JSON
或人类可读的文本。
列校验和¶
对于控制/测试查询中的每一列,将在校验和查询中生成一个或多个列。
- 浮点列
- 对于
DOUBLE
和REAL
列,将生成 4 列用于验证 列的有限值的总和
NAN
列的计数列的正无穷大计数
列的负无穷大计数
- 对于
检查
NAN
计数、正无穷大和负无穷大计数是否匹配。检查控制总和和测试总和的空值。
如果控制均值或测试均值非常接近 0,则检查两者是否都接近 0。
检查控制总和和测试总和之间的相对误差。
- VARCHAR 列
对于
VARCHAR
列,将使用checksum()
为验证生成一个简单的校验和列。- 如果设置了
validate-string-as-double
,则将生成以下七列。如果在将所有值强制转换为DOUBLE
之前和之后,NULL
计数相等,则应用浮点验证。否则,检查简单的校验和是否匹配。 校验和
NULL
值的计数将所有值转换为
DOUBLE
类型后的NULL
值数量
- 将所有值转换为
DOUBLE
类型后 有限值的总和
NAN
值的计数正无穷值的计数
负无穷值的计数
- 如果设置了
- 数组列
- 对于类型为
array(E)
的数组列arr
,将生成三个列用于验证 基数的总和
基数的校验和
- 数组校验和
如果
E
不可排序,则数组校验和为checksum(arr)
。如果
E
可排序,则数组校验和为coalesce(checksum(try(array_sort(arr))), checksum(arr))
。
- 对于类型为
- 如果设置了
use-error-margin-for-floating-point-arrays
并且E
为DOUBLE
或REAL
,则将生成以下六个列。检查基数的总和是否匹配,以及基数的校验和是否匹配。将浮点验证应用于其余结果。 基数的总和
基数的校验和
所有数组值的有限元素的总和
所有数组值的
NAN
元素的计数所有数组值的正无穷元素的计数
所有数组值的负无穷元素的计数
- 如果设置了
- 如果设置了
validate-string-as-double
并且E
为VARCHAR
,则将生成以下九个列。检查基数的总和和校验和是否匹配。如果在将所有数组元素转换为DOUBLE
类型之前和之后,NULL
计数相等,则应用浮点验证。否则,检查数组校验和是否匹配。 基数的总和
基数的校验和
数组校验和
checksum(array_sort(arr))
所有数组值的
NULL
元素的计数将所有数组元素转换为
DOUBLE
类型后的NULL
元素的计数
- 将所有数组元素转换为
DOUBLE
类型后 所有数组值的有限元素的总和
所有数组值的
NAN
元素的计数所有数组值的正无穷元素的计数
所有数组值的负无穷元素的计数
- 如果设置了
- 映射列
- 对于类型为
map(K, V)
的映射列,将生成四个列用于验证 基数的总和
映射的校验和
键集的数组校验和
值集的数组校验和
- 对于类型为
- 如果设置了
validate-string-as-double
并且K
为VARCHAR
,则将生成六个附加列 所有键集的
NULL
元素的计数将所有映射键转换为
DOUBLE
类型后的键集的NULL
元素的计数
- 将所有映射键转换为
DOUBLE
类型后 所有键集的有限元素的总和
所有键集的
NAN
元素的计数所有键集的正无穷元素的计数
所有键集的负无穷元素的计数
- 如果设置了
- 如果设置了
validate-string-as-double
并且V
为VARCHAR
,则将生成六个附加列 所有值集的
NULL
元素的计数将所有映射值转换为
DOUBLE
类型后的值集的NULL
元素的计数
- 将所有映射值转换为
DOUBLE
类型后 所有值集的有限元素的总和
所有值集的
NAN
元素的计数所有值集的正无穷元素的计数
所有值集的负无穷元素的计数
- 如果设置了
- 行列
根据字段的类型递归地对行字段进行校验和计算。
对于所有其他列类型,使用checksum()
函数生成一个简单的校验和。
确定性¶
结果不匹配,无论是行计数不匹配还是列不匹配,都可能是由非确定性查询功能引起的。为了避免错误警报,我们对控制查询执行确定性分析。如果发现查询是非确定性的,我们将跳过验证,因为它不会提供任何见解。
确定性分析遵循以下步骤。如果在任何时候发现查询是非确定性的,则分析将得出结论。
可以使用
determinism.non-deterministic-catalog
指定非确定性目录。如果查询引用了这些目录中的任何表,则该查询将被视为非确定性。再次运行控制查询,并将结果与初始控制查询运行进行比较。
- 如果查询在顶层有一个
LIMIT n
子句,但没有ORDER BY
子句 运行一个查询来计算在没有
LIMIT
子句的情况下,控制查询产生的行数。如果结果行数大于
n
,则将控制查询视为非确定性。
- 如果查询在顶层有一个
故障解决¶
配置差异,包括集群大小,会导致查询在控制集群上成功,但在测试集群上失败。校验和查询也可能失败,这可能是由于Presto或Presto Verifier的限制造成的。因此,我们允许Verifier自动解决某些查询故障。
EXCEEDED_GLOBAL_MEMORY_LIMIT
:如果控制查询使用的内存比测试查询多,则解决。EXCEEDED_TIME_LIMIT
:无条件解决。TOO_MANY_HIVE_PARTITIONS
:如果测试集群没有足够的worker来确保分配给每个worker的分区数保持在限制范围内,则解决。COMPILER_ERROR
,GENERATED_BYTECODE_TOO_LARGE
:如果控制校验和查询以该错误失败,则解决。如果控制查询有太多列,则在某些情况下,生成的校验和查询可能会太大。
在结果不匹配的情况下,Verifier可能正在发出噪声信号,我们允许Verifier自动解决某些不匹配。
- 结构化类型列:如果数组元素或映射键/值包含浮点类型,则列校验和不太可能匹配。
对于数组列,如果元素类型包含浮点类型并且基数校验和匹配,则解决。
- 对于映射列,当以下两个条件都为真时,解决不匹配
基数校验和匹配。
不包含浮点类型的键或值的校验和匹配。
仅当所有列都被解决时,才解决测试用例。
- 已解决的函数:在结果不匹配的情况下,如果查询在
指定列表中使用了一个函数,则测试用例将被标记为已解决。
解释模式¶
在解释模式下,Verifier检查源查询是否可以被解释,而不是它们是否产生相同的结果。当控制查询和测试查询都可以被解释时,验证将被标记为成功。
输出事件中的字段matchType
可以用作控制运行和测试运行之间是否存在计划差异的指示器。
对于非DML查询,将跳过控制查询和计划比较。
扩展Verifier¶
除了配置属性之外,Verifier还可以扩展以进一步更改行为。
AbstractVerifyCommand显示了可以扩展的组件。实现抽象类并创建一个类似于PrestoVerifier的命令行包装器。
配置参考¶
常规配置¶
名称 |
描述 |
---|---|
|
一个逗号分隔的列表,指定要验证的套件中查询的名称。 |
|
一个逗号分隔的列表,指定要从套件中排除的查询的名称。 |
|
源查询提供程序的名称。支持 |
|
保存验证器查询的表的名称。仅当 |
|
一个逗号分隔的列表,指定应该将输出事件发射到哪里。支持 |
|
|
|
人类可读事件的输出文件。如果没有设置,则将人类可读事件发射到 |
|
要附加到控制目标表的表前缀。 |
|
要附加到测试目标表的表前缀。 |
|
如果 |
|
如果 |
|
要附加到输出事件的字符串。 |
|
并发验证的最大数量。 |
|
套件验证的次数。 |
|
源查询验证的次数。 |
|
控制总和与浮点列测试总和之间可容忍的最大相对误差。 |
|
低于此阈值的浮点平均值被视为 |
|
结果不匹配时是否运行拆卸查询。 |
|
源查询重新提交以进行验证的次数限制。 |
|
设置为 |
|
设置为 |
|
设置为 |
|
函数替换规范,格式为 用逗号连接函数替换。 选择一个返回值类型和参数类型与原始函数兼容的函数替换,以生成有效的源查询。例如, 如果需要将函数参数应用于函数替换,则将其声明为标识符。 |
查询覆盖配置¶
以下配置控制验证开始之前查询元数据修改的行为。测试查询的对应项也可用,前缀 control
被替换为 test
。
名称 |
描述 |
---|---|
|
如果指定,要应用于所有查询的目录。 |
|
如果指定,要应用于所有查询的模式。 |
|
如果指定,要应用于所有查询的用户名。 |
|
如果指定,要应用于所有查询的密码。 |
|
支持 3 个值。 |
|
要应用于所有查询的会话属性。 |
查询执行配置¶
以下配置控制控制集群上查询执行的行为。测试集群的对应项也可用,前缀 control
被替换为 test
。
名称 |
描述 |
---|---|
|
控制集群主机名或 IP 地址的逗号分隔列表。 |
|
控制集群的 JDBC 端口。 |
|
控制集群的 HTTP 端口。 |
|
表示控制 JDBC 的附加 URL 参数的 |
|
控制查询和测试查询的执行时间限制。 |
|
的执行时间限制 |
|
校验和查询的执行时间限制。 |
|
要传递到 ClientInfo 的 ApplicationName。可用于设置源。 |
确定性分析器配置¶
名称 |
描述 |
---|---|
|
是否为确定性分析中生成的表运行拆卸查询。 |
|
要检查控制查询确定性的附加控制运行的最大次数。 |
|
是否启用对具有顶级 |
|
非确定性目录的逗号分隔列表。引用来自这些目录的表的查询被视为非确定性的。 |
故障解决配置¶
名称 |
描述 |
---|---|
|
是否启用测试查询失败的故障解决程序 |
|
是否启用测试查询失败的故障解决程序 |
|
是否启用由于验证器限制而导致的故障的故障解决程序。 |
|
是否启用测试查询失败的故障解决程序 |
|
控制集群和测试集群上配置的每个编写器的最大桶数。 |
|
测试集群大小缓存的时间限制。 |
|
是否启用结构化类型列的列不匹配的故障解决程序。 |
|
是否启用 |
|
逗号分隔的函数列表。如果查询使用列表中的任何函数,则解决不匹配。 |