Skip to content

ZH Writes and Errors

Alan Zhang edited this page Oct 3, 2026 · 2 revisions

Home · GitHub

English | 简体中文

TsGate 2.1.0: 本页使用 com.alandevise.tsgate.*。从 2.0.0 升级时须更新 import、反射类名和包扫描配置,并重新编译。Central 2.0.0 保留 com.alandevise.tsdb.*。详见迁移步骤与发布状态。

注解 POJO

import com.alandevise.tsgate.annotation.TGField;
import com.alandevise.tsgate.annotation.TGMeasurement;
import com.alandevise.tsgate.annotation.TGTag;
import com.alandevise.tsgate.annotation.TGTime;

@TGMeasurement("ACCRUE")
public class AccrueRecord {

    @TGTime
    private Long timestamp;

    @TGTag("device_code")
    private String deviceCode;

    @TGTag("point_type")
    private String pointType;

    @TGTag("source")
    private String source;

    @TGField("value")
    private Double value;

    @TGField("status")
    private Long status;
}

注解说明:

  • @TGMeasurement:声明目标 measurement/table。
  • @TGTime:声明时间字段;字段类型只能是 Long/long,值为 Unix Epoch 毫秒且不得为空,不做隐式时区转换。
  • @TGTag:声明 tag 字段;写入时转换为字符串。
  • @TGField:声明普通 field 字段;至少需要一个非空 field 值。

写入 POJO 必须声明一个 @TGTime 和至少一个 @TGField。查询承接对象可以不是写入 POJO,也可以不声明 TsGate 注解。

标准 API

注入 com.alandevise.tsgate.core.TGTemplate。启用后端时,对应 starter 注册的统一模板默认 Bean 名为 tgTemplate:

import com.alandevise.tsgate.core.TGTemplate;
import org.springframework.beans.factory.annotation.Autowired;

@Autowired
private TGTemplate tgTemplate;

写入

不传 database 时使用 adapter 配置中的默认 database。

boolean success = tgTemplate.write(record);

指定 database:

boolean success = tgTemplate.write("tsdb", record);

批量写入:

boolean success = tgTemplate.batchWrite("tsdb", records);

需要判断提交边界时使用详细结果;失败会抛出携带同一结果对象的 TSDBBatchWriteException:

BatchWriteResult result = tgTemplate.batchWriteDetailed("tsdb", records);

commitState 可能为 SUCCESS、NOT_COMMITTED、PARTIALLY_COMMITTED 或 UNKNOWN。网络异常、HTTP 408、5xx 等响应 不能证明当前物理批次没有落库,按 UNKNOWN 返回,错误码为 BATCH_COMMIT_UNKNOWN;此前已确认提交的条数和批次数仍保留。 InfluxDB 3 在 accept_partial=false 下将 HTTP 400/401/403/404/405/413/415/422/429 视为当前批次的明确拒绝,根据此前提交数量报告零提交或部分提交。 InfluxDB 1.x 仅将 HTTP 401/403/404/405/413/415/429 视为明确拒绝;HTTP 400 可能已经写入部分点,按 UNKNOWN 处理。 retryable 与提交确定性独立:429、408 和暂时性 5xx 可由调用方按幂等策略重试;501/505 不提示重试。适配层不会据此自动重放。 accept_partial=false 的校验拒绝语义参见官方写入说明。

单条写入内部会包装成单元素集合,直接作为一个批次下发;当前 starter 不做异步攒批、无界缓冲或背压队列。

OpenGemini 适配在 I/O 前检查整个批次:measurement 名不能含逗号、分号、正斜杠、反斜杠,不能等于 . 或 ..,也不能含不可打印字符。字母、组合标记、数字、标点、符号及 ASCII 空格遵循服务端可打印名称规则,= 保留原样。非法名称报 ARGUMENT_ERROR,结果为 NOT_COMMITTED、零物理批次,不会重命名;继承的 line protocol 校验仍生效。请求发出后,HTTP 400 的部分写入响应仍沿用 InfluxDB 1.x 的 UNKNOWN 语义。

异常错误码

适配层统一抛出 TSDBException 或其子类。错误码固定为 1003xx 六位整数;业务侧可以通过 exception.getErrorCode() 获取枚举,也可以通过 exception.getCode() 直接获取整数编码。错误码是业务错误分类,不等同于 HTTP 状态码。

错误码 枚举 含义
100300 CONFIGURATION_ERROR 必填配置缺失、批量上限或连接池配置非法
100301 ARGUMENT_ERROR SQL、分页、过滤条件、标识符或写入值非法
100302 METADATA_ERROR 注解模型、物理列、Java 类型或结果映射异常
100303 ADAPTER_STATE_ERROR adapter 或底层 client 尚未初始化或不可用
100304 CONNECTION_ERROR 网络连接、超时、服务不可用或限流
100305 PERMISSION_ERROR 登录认证失败或权限不足
100306 RESOURCE_NOT_FOUND database、table、column 等资源不存在
100307 WRITE_ERROR 服务端明确拒绝写入且提交边界可判定
100308 QUERY_ERROR 查询语法、执行或响应解析失败
100309 BATCH_COMMIT_UNKNOWN 写入后断连或响应不足以确认当前批次是否提交
100310 UNSUPPORTED_OPERATION 统一 API 不支持的后端能力或调用组合
100399 INTERNAL_ERROR 无法进一步分类的内部错误及旧构造器兼容兜底
try{
        tgTemplate.write(record);
}catch(
TSDBException e){
int code = e.getCode();
String detail = e.getMessage();
Throwable rootCause = e.getCause();
}

业务 Web 层负责把该业务错误码映射为统一响应和适当的 HTTP 状态;starter 不注册全局异常处理器。

同步写入语义

  • write、batchWrite 和 batchWriteDetailed 都是同步 API:方法成功返回表示底层 TSDB 已确认本次写入结果,而不是仅进入适配层内存队列。
  • OpenGemini 适配的新 measurement 或 tag series 在异步索引合并后才对查询可见。业务需要时,应在读侧显式轮询期望 timestamp、tags 与值并设置截止时间;每次 adapter 查询只发一次请求,不做透明可见性重试。
  • 同步不等于全局串行。业务侧可以并发调用,底层通过 IoTDB SessionPool 或 InfluxDB HTTP 连接池复用连接;大批数据应优先使用批量 API 降低网络往返。
  • TSDB 或连接池变慢时,延迟、超时或异常会直接传递给调用方,形成明确的压力反馈。adapter 不在内部暂存数据,因此不存在应用重启时尚未 flush 的内存数据。
  • 需要异步削峰、持久化重试或可靠投递时,应由业务线程池、消息队列或专用数据通道实现;这些组件才能根据业务语义决定队列容量、拒绝策略、 持久化、重试和去重方式。

page() 和 strictCursorPage() 只用于明细查询;聚合查询必须使用 page(pageNum, pageSize) 的 offset 分页。聚合结果会按 window_start + groupByTags(无时间窗口时按 groupByTags)稳定排序。offset 分页会额外执行一次总数查询:明细查询直接统计满足 条件的原始行数,聚合/分组查询统计聚合结果行数。

连接和批量写入

  • IoTDB:强制使用官方 ITableSessionPool;tsdb.iotdb.pool.enabled=false 时应用启动失败。
  • IoTDB:必须配置并预先创建默认 database;该库设置到 TableSessionPoolBuilder,普通读写不再发送冗余 USE,跨库操作关闭 Session 时由官方客户端恢复默认库。
  • 此前验证的 IoTDB SDK 2.0.10 在关闭 Session 恢复默认库失败时不会可靠补充对应池槽位,因此默认库必须在应用运行期间保持存在,业务账号也必须保留 对默认库的访问权限。
  • IoTDB SDK 2.0.11 仍在借用的 Session 关闭时恢复默认数据库,升级客户端不免除保持该库可用的要求。
  • IoTDB:池满时等待归还,获取超时直接返回,不更换物理池;获取阶段其他连接异常也不自动换池。查询执行阶段仍保留已知 SDK 空响应缺陷和连接异常的按版本恢复,只读最多重试一次。此修复不提供获取阶段槽位泄漏的自动恢复,也未引入跨池信号量;故障换池时 旧池排空与新池使用可能短暂重叠,max-size 仍是单个物理池的上限。
  • IoTDB:批量写入会按 measurement 组装 Tablet,单个 Tablet 最大行数由 tsdb.iotdb.table.tablet-max-row-size 控制。
  • IoTDB:tsdb.iotdb.table.rpc-compression-enabled 默认 true,省略即保留默认;2.0.2 等不兼容旧服务端显式设为 false。它控制 Tablet RPC 紧凑编码/压缩,不是 Thrift 传输或磁盘压缩。验证时需要一个实际 Tablet 至少 10 行并在写入后回查,不能只看业务 batch 总量;初始池和故障替换池使用同一配置,不增加写入自动重试,也不改变提交状态报告。
  • IoTDB:字段值为 null 时从同批次非空值推断类型;始终为 null 的列省略,完全没有可推断 field 的表批次在 I/O 前拒绝。
  • IoTDB:字段精确映射为 INT32/INT64/FLOAT/DOUBLE/BOOLEAN/STRING/BLOB/DATE;未支持的 Java 类型在首个 Tablet 写入前拒绝。
  • IoTDB/InfluxDB:单次业务批量写入默认最多 10000 条,分别由各自的 max-batch-records 配置控制。
  • InfluxDB 3:全部记录会先编码校验,再按最多 5000 行或 1 MiB UTF-8 payload 拆分为一个或多个 HTTP 请求;写接口使用 accept_partial=false。
  • InfluxDB 1.x:同样先做全批校验和分块,但服务端 HTTP 400 可能已写入部分合法点。此时提交状态为 UNKNOWN,已确认数量只包含此前成功请求;不自动重试可能已经部分提交的块。
  • 两个 InfluxDB adapter 均拒绝以 # 开头的 measurement,防止被 line protocol 当注释后静默丢点;measurement 的 = 与后端反斜杠规则各自正确编码。
  • InfluxDB 整数字段支持 Byte/Short/Integer/Long 和 signed-int64 范围内的 BigInteger,后者使用整数协议后缀避免 float64 舍入;不接受含义不明的 Number 子类。
  • InfluxDB Float/Double 必须有限。1.x 的 BigDecimal 仅接受 float64 可精确表示值;3.x 的 BigDecimal 使用正常 double 舍入,但拒绝溢出以及非零值下溢为 0。
  • InfluxDB 1.x 额外拒绝 _field / _measurement / time 作为 tag 或 field 键,避免其协议保留名称造成丢点或拒绝。
  • 批量写入不提供跨多个 Tablet 或 HTTP 请求的数据库事务。前置转换或校验失败可确认整批零写入;开始 I/O 后应根据 BatchWriteResult.commitState 判断成功、零提交、部分提交或状态未知。
  • 查询和分页的业务 limit/pageSize 必须在 1~10000 范围内,offset 计算溢出时直接失败。
  • 当前 adapter 是同步写入模型,不维护异步缓冲队列;如果业务侧采集流量可能超过 TSDB 或网络写入能力,应在业务侧或专门的数据通道中 设计限流、削峰、重试和背压。

← 配置与数据库选择 · 链式查询与聚合 →

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 发布说明。

Clone this wiki locally