ZH Mapping and Backend Limits - AlanDevise/TsGate GitHub Wiki
2.1.3 已通过 GitHub 源码/标签和 Maven Central 发布,同 SHA CI 与公开制品核验通过。 当前坐标为 2.1.3;详见 2.1.3 发布说明.
2.1.3 修订
2.1.3 收紧结构化过滤参数校验,在整批预检前归并 IoTDB 物理表名的大小写变体,在测试与 CI 中核对后端登记,拒绝 IoTDB/InfluxDB 3 的重复结果键,并在复制输入前检查直接 adapter 批写的记录数。公开 API、Template → SPI → adapter 主干、Java 17/客户端/服务端基线与配置默认值均保持。 2.1.3 发布说明.
应用批次记录数提前检查:2.1.3
四个内置适配器在复制非空集合前检查报告的输入记录数,复制后再复核快照大小;直接适配器调用补齐提前保护,TGTemplate 原有路径保持。报告大小明显超限时,在遍历记录或数据库 I/O 前以 ARGUMENT_ERROR、NOT_COMMITTED 拒绝,确认写入为零。既有状态检查与 null/空批次行为及优先级保留。
各后端已有 max-batch-records 仍约束应用记录数,默认均为 10000,配置分别为 tsdb.iotdb.max-batch-records、tsdb.influxdb.max-batch-records、tsdb.influxdb1.max-batch-records、tsdb.opengemini.max-batch-records。HTTP 物理请求及 Tablet 限额仍单独处理。本修订不新增字节/列数预算,也不能约束任意自定义 Collection 的全部分配;这是提前数量检查,不是 JVM 内存硬上限。范围与验证状态见 2.1.3 发布说明。
IoTDB 写入先校验 measurement 名,再按 Locale.ROOT 小写物理表身份归并:Metrics、metrics、METRICS 均使用 metrics。已发布的 2.1.2 按原始拼写分组,可能把同一物理表拆成不同分组,绕过跨记录的列角色/类型检查。2.1.3 让同一物理表的全部记录在借 session 前共同检查;任一冲突均以 METADATA_ERROR、NOT_COMMITTED 拒绝完整应用批次,确认记录/Tablet 数为零且无数据库 I/O,包括批次中其他原本有效的表。
兼容变体保留同一物理表内的原始行序,并按原有 tablet-max-row-size 共同拆分 Tablet;物理批次数以归并后实际 Tablet 为准,请求及确认记录数仍以输入记录计数。调用方 TSDBRecord 的 measurement 名、tag/field map 键、值与 null 不会被改写。写入失败已定位到尝试写入的表时,failedMeasurement 使用小写物理表名;写前预检或获取连接失败尚未定位 Tablet 时仍可为 null。这不增加跨 Tablet 事务或服务端 schema 预检,也不改变 InfluxDB 1.x、InfluxDB 3、openGemini 的大小写行为。
返回结果名:2.1.3
IoTDB 在构造行 map 前检查 SDK 输出列名列表,精确重复即返回 QUERY_ERROR,没有结果行时也检查。InfluxDB 3 的成功响应 JSON 解析器拒绝同一对象内的重复键,不再让后值覆盖前值。任一返回行存在重复,整次操作失败。统一查询、计数与适配器原生 executeQuery 结果使用相同保护路径。
例如,IoTDB 输出列 ["value", "value"] 与 InfluxDB 3 响应 [{"value":null,"value":2}] 会被拒绝。value 与 Value 等大小写不同的名称、不同 JSON 行对象中反复出现同名键、合法 null,以及既有数值精度语义仍保留。POJO 精确匹配优先级、大小写无关 fallback、缺列及默认值行为不变;本修订不增加严格 DTO 映射。
InfluxDB 1.x/openGemini 原有重复 columns 与 tag/field 冲突检查保持;这些检查不表示任意 JSON 元数据键重复都会被拒绝。借出的原生客户端保留 SDK 自身结果处理。范围与验证状态见 2.1.3 发布说明。
2.1.2 的结果完整性修改
聚合输出名按后端物理列身份预检:实际生成时的 window_start、分组 tag 与 alias 必须唯一。query/count 及模板执行均在 I/O 前以 ARGUMENT_ERROR 拒绝冲突。Influx 标识符保留不同大小写;IoTDB 统一为小写。见聚合输出名。
InfluxDB 3 JSON 读取将缺失或仅含空白的 HTTP body 判为 QUERY_ERROR。真实 JSON [] 仍表示成功的空结果,缺失响应不能伪装成空成功。原有非法 JSON、行数及解压后字节预算仍生效。
默认 POJO 读取按类生命周期复用构造器、继承字段、目标类型及候选列名的读计划。读计划与写入元数据分离,普通无注解 DTO 继续可用。精确/注解/宽松匹配的优先级、显式 null、数值/时间转换和缺失列语义保持。缓存保存类所属反射元数据,不保存结果行或业务实例,也不增加公开 mapper API;没有基准数据时不宣称具体吞吐提升。
InfluxDB 1.x 与 OpenGemini 的聚合响应还包含隐式物理 time 列,因此精确命名为 time 的聚合 alias 或分组 tag 在 I/O 前被拒绝;大小写不同的 Time 仍可使用。此协议专属保留名不扩大到 IoTDB 或 InfluxDB 3。
后端能力与边界
| 能力 | IoTDB 表模型 | InfluxDB 3 Core | InfluxDB OSS 1.x | openGemini 默认引擎 |
|---|---|---|---|---|
| POJO / 批量写入、普通读、tag/field 过滤 | 支持 | 支持 | 支持 | 支持 |
| 原生查询语言 | SQL | SQL | InfluxQL | InfluxQL |
| 时间游标 / offset 分页 | 支持 | 支持 | 支持;相同时间多行仍需注意游标的固有限制 | 支持;相同时间多行仍需注意游标的固有限制 |
| FIELD 排序 / 严格复合游标 | 支持 | 支持 | 明确报 UNSUPPORTED_OPERATION |
明确报 UNSUPPORTED_OPERATION |
| 聚合 / tag 分组 / UTC 或固定偏移窗口 | 支持 | 支持 | 支持;窗口聚合必须显式设置起止时间 | 支持;窗口聚合必须显式设置起止时间 |
| 地区时区自然日 / DST | 有界 CASE 自然日分桶 | 有界 CASE 自然日分桶 | 暂不支持地区日历窗口,明确报错 | 暂不支持地区日历窗口,明确报错 |
| 分页总数 | 服务端 COUNT / 子查询 | 服务端 COUNT / 子查询 | 流式扫描逻辑结果行计数,受响应字节上限保护 | 流式扫描逻辑结果行计数,受响应字节上限保护 |
| 原生 client Bean | ITableSessionPool |
com.influxdb.v3.client.InfluxDBClient |
org.influxdb.InfluxDB |
兼容的 org.influxdb.InfluxDB |
openGemini 一列描述 2.1.0 适配器,组合复用 InfluxDB1 兼容实现并保留统一查询边界,范围为已验证的 1.4.1/1.5.2 默认引擎部署,不覆盖 COLUMNSTORE、Arrow 或全部原生功能。准确验证范围见 OpenGemini 接入指南。
OpenGemini 适配器另有 measurement 名预检、精确空表错误归一与原生健康桥接。新 measurement/tag series 的索引可能在写入确认后才对查询可见,需要读到具体数据点的业务应设置截止时间并在读侧轮询。只有精确的 measurement not found 错误会在明确指定 measurement 的结构化统一 query 中转为空行、count 中转为零;原生 executeQuery 与借用原生 client 的 DTO 保留服务端错误,包括多个 measurement 或子查询的错误。measurement 名含 ,、;、/、\,等于 . / .. 或含不可打印字符时,整个批次在 I/O 前被拒绝。兼容原生 client 的稳定代理用真实 X-Geminidb-Version 响应头处理 ping() / version(),其他原生方法照常委托。
InfluxDB 3 严格游标兼容性取决于服务端与 SQL 策略组合:tsdb.influxdb.strict-cursor-sql 默认 or,union-all 是结构化续页的显式替代方案。应使用兼容性与验证列出的准确组合,不能认为所有 3.x 均支持两种查询形态。该开关不会改写原生 SQL,也不会为 InfluxDB 1.x 增加严格游标。union-all 模式对严格游标与聚合的组合报 UNSUPPORTED_OPERATION,包括第一页。
最终 m 个游标键在 UNION 策略下最多生成 m 个互斥分支,每个分支重复业务条件与时间范围,合并后再做全局排序分页。它保留混合 ASC/DESC 排序、模板补齐的 time/tag 键及响应行数/字节保护,但可能增加服务端扫描和排序开销;这些响应上限不能限制数据库的扫描量或 CPU。两种策略均不提供跨页快照,不接受缺键/null 游标,也不提高时间戳精度。详见分页。
InfluxDB 1.x 使用 MEAN 对应统一 API 的 AVG,IN/BETWEEN 翻译为等价条件;不会将 InfluxDB 3 SQL 原样发送给 1.x。
InfluxQL 的 LIMIT/OFFSET 是每个 series 的限制,因此 tag 分组查询会先读取受限的全部分组结果,再全局排序分页;
分组结果超过 max-query-rows 或响应超过字节上限时即失败,即使请求的单页较小。普通计数采用不保留全部行的流式扫描,避免 COUNT(*) 按 field 非空值计数造成总行数错误。
InfluxDB 1.x 显式列选择需要注解实体,且至少包含一个 @TGField;仅选 tag 会明确拒绝,不返回误导性的空结果。
原始 TSDBQuery 没有实体元数据时可保留空的 selectColumns 使用 SELECT *,或改用原生 InfluxQL;窗口查询不能把聚合别名 / group tag 命名为 window_start。
InfluxDB 1.x 的物理时间列固定为 time,不支持自定义时间列;原生查询不支持返回多个语句结果,服务端返回 partial 结果时明确失败。
共享契约检查与 IoTDB 列身份
2.1.1 在测试目录复用后端契约断言,并以机器可检查的测试能力矩阵记录支持、不支持和需要配置的能力。这是验证基础设施,不是新的公开能力 API。不支持的操作必须验证 UNSUPPORTED_OPERATION;依赖配置的操作必须使用指定设置。上表仍受兼容性与验证中准确服务端证据的限制。
复用接口为 SharedAdapterContract,后端注册及能力断言与 adapter-capabilities.csv 核对。能力状态为 SUPPORTED、UNSUPPORTED、CONFIG_REQUIRED;配套的 adapter-conditions.csv 将准确服务端版本/设置关联至对应回归方法:InfluxDB 3 Core 3.0.0/3.0.3 严格游标需要 union-all,IoTDB 2.0.2 需要关闭 Tablet RPC 压缩。mock 契约通过不能认证未测试服务端。
IoTDB 批次的列角色/类型检查和预备行值使用一致的小写物理身份。同一记录的大小写重复列在 I/O 前拒绝;不同记录的兼容变体合并并保留值和 null。跨行角色/类型冲突以 METADATA_ERROR/NOT_COMMITTED 拒绝完整批次;调用方 map 保留原键。详见写入行为。
查询结果映射
查询结果最终映射为业务承接对象,承接类需要可访问的无参构造方法;普通 Java record 不能直接作为查询 DTO:
- 带有 TSDB 注解的字段按注解物理列名匹配,列缺失时不会回退到 Java 字段名。
- 无注解字段优先按返回列原名匹配字段名,其次支持大小写无关匹配和下划线转驼峰匹配。
- 无注解 DTO 中名为
time/timestamp的字段,还可匹配返回的time、Time、_time、timestamp时间别名;带注解字段仍遵循物理列名规则。 - 返回列多于承接对象字段时会被忽略;承接对象字段找不到返回列时保留构造后的字段值。
long/int/short/byte/BigInteger转换要求整数精确可表示:非法文本、小数截断和越界均抛METADATA_ERROR,不会用 0 或 null 替代错误值。- boolean 文本仅接受忽略大小写及首尾空白的
true/false;其他文本(包括1、yes)报错,不会静默变为 false。 - float / double 拒绝 NaN、Infinity、溢出和非零值下溢为 0;普通 IEEE 754 舍入仍存在。两个 InfluxDB adapter 与 openGemini 兼容适配器均按 BigDecimal 解析 JSON 小数,避免解析阶段先转 Double;映射到 BigDecimal 可保留响应提供的十进制数字,无法恢复数据库存储前已损失的精度。已经由其他来源读取成 Float / Double 的值只保留该二进制值,不能恢复原始精度。
Instant文本转换保留纳秒;long / Date 的时间语义是毫秒。源 null 映射到引用类型仍为 null,映射到 primitive 保留构造后的字段值。- 映射异常包含字段、源类型、目标类型等上下文,底层转换异常保留为 cause。
Map 结果类型
Map.class 与 LinkedHashMap.class 返回 LinkedHashMap,保留结果列顺序;HashMap.class 返回 HashMap;TreeMap.class 返回按键排序的 TreeMap。其他 Map 接口或子类在数据库 I/O 前报 ARGUMENT_ERROR,即使查询结果为空也会校验。不反射调用任意 Map 构造器。规则适用于模板查询/结果接口,普通 POJO 映射保持原有行为。
常见限制
可移植测试源码与可复用 Docker 配置在各模块 src/test/ 中纳入版本控制。本地夹具、凭据和运行报告保留在 .local-test/ 并由 Git 忽略;共享运行器与 GitHub 工作流提供可复现检查,详见测试与发布。
- 当前只支持 IoTDB 表模型,不支持 IoTDB 树模型转换口子。
- 统一
TGTemplate面向当前唯一启用的 adapter;允许同时引入多个依赖,但不支持同时启用多个后端或双写。 - 链式查询按后端能力表提供支持;复杂 JOIN、子查询、数据库私有函数等应使用
executeQuery(...)或借用的原生 client,并遵循对应入口限制。 - 同一字段在同一库表中应保持稳定类型,避免 IoTDB 类型冲突或 InfluxDB field 类型混淆。
← 分页与原生查询 · 兼容性、验证与成熟度 →
2.1.0 中的 OpenGemini
实测 1.4.1/1.5.2 的 Influx 兼容 HTTP 写入路径会先把整数字段转换为 float64,再存储为整数。整数协议后缀也不能避免这一转换:9007199254740993i 读回为 9007199254740992。因此 OpenGemini 适配器在 I/O 前检查整个批次,只接受 signed-int64 范围内且可由 IEEE 754 float64 精确表示的整数字段。-2^53 至 2^53 之间的整数全部精确;2^53+2、Long.MIN_VALUE 等部分更大绝对值的整数也精确。2^53+1、Long.MAX_VALUE 和 Long.MIN_VALUE+1 在写前返回 ARGUMENT_ERROR、NOT_COMMITTED、零物理请求,不强制转换,也不编码成字符串。公共结构化 query/count 同样预检整数过滤字面量,包括 IN/BETWEEN 的每个值,避免服务端数值比较舍入后错误匹配。 极大整数型 BigDecimal 过滤值先与 signed-int64 边界比较再转换为整数,避免超大指数绕过检查或触发无界展开。Float/Double 及非整数查询字面量仍遵循普通浮点语义;已有 BigDecimal 字段校验继续生效。Epoch 毫秒时间戳走独立的时间路径。原生 SQL 与借用原生客户端写入绕过这些检查,保留服务端精度边界;该校验也不保证聚合累加无溢出或浮点运算精确。此限制针对本次 HTTP 适配,不代表 openGemini 的所有引擎或其他写入协议。
TsGate 2.1.0 包含独立的 OpenGemini adapter 与 starter。配置、默认引擎边界及准确版本测试见 OpenGemini 接入指南,发行说明见 2.1.0 发布说明。