From ca67d8d2f05b116eea086fb715d6bc2aca0627c5 Mon Sep 17 00:00:00 2001 From: Wade Zhang Date: Fri, 25 Aug 2023 23:28:02 +0800 Subject: [PATCH 1/5] doc: remove taosx and explorer from website --- docs/zh/17-operation/08-web.md | 178 ----- docs/zh/18-data-transfer/01-taosX.md | 972 ------------------------ docs/zh/18-data-transfer/02-explorer.md | 128 ---- docs/zh/18-data-transfer/index.md | 3 - 4 files changed, 1281 deletions(-) delete mode 100644 docs/zh/17-operation/08-web.md delete mode 100644 docs/zh/18-data-transfer/01-taosX.md delete mode 100644 docs/zh/18-data-transfer/02-explorer.md delete mode 100644 docs/zh/18-data-transfer/index.md diff --git a/docs/zh/17-operation/08-web.md b/docs/zh/17-operation/08-web.md deleted file mode 100644 index 6560bb979f..0000000000 --- a/docs/zh/17-operation/08-web.md +++ /dev/null @@ -1,178 +0,0 @@ ---- -title: Web 管理工具 -description: 基于 Web 的系统管理工具 ---- - -## 简介 - -为了易于企业版用户更容易使用和管理数据库,TDengine 3.0 企业版提供了一个全新的可视化组件 taosExplorer。用户能够在其中方便地管理数据库管理系统中中各元素(数据库、超级表、子表)的生命周期,执行查询,监控系统状态,管理用户和授权,完成数据备份和恢复,与其它集群之间进行数据同步,导出数据,管理主题和流计算。 - -**欲体验基于 Web 的 TDengine 系统管理能力,请联系 TDengine 市场或销售团队** - -## 部署服务 - -### 准备工作 - -1. taosExplorer 没有独立的安装包,请使用 taosX 安装包进行安装。 -2. 在启动 taosExplorer 之前,请先确认 TDengine 集群已经正确设置并运行(即 taosd 服务),taosAdapter 也已经正确设置和运行并与 TDengine 集群保持连接状态。如果想要使用数据备份和恢复或者数据同步功能,请确保 taosX 服务和 Agent 服务也已经正确设置和运行。 - -### 配置 - -在启动 taosExplorer 之前,请确保配置文件中的内容正确。 - -```TOML -listen = "0.0.0.0:6060" -log_level = "info" -cluster = "http://localhost:6041" -x_api = "http://localhost:6050" -``` - -说明: - -- listen - taosExplorer 对外提供服务的地址 -- log_level - 日志级别,可选值为 "debug", "info", "warn", "error", "fatal" -- cluster - TDengine集群的 taosadapter 地址 -- x_api - taosX 的服务地址 - -### 启动 - -然后启动 taosExplorer,可以直接在命令行执行 taos-explorer 或者使用下面的 systemctl 脚本用 systemctl 来启动 taosExplorer 服务 - -```shell -[Unit] -Description=Explorer for TDengine -After=network-online.target -Wants=network-online.target - -[Service] -Type=simple -ExecStart=/usr/bin/taos-explorer -Restart=always - -[Install] -WantedBy=multi-user.target -``` - -### 问题排查 - -1. 当通过浏览器打开taosExplorer站点遇到“无法访问此网站”的错误信息时,请通过命令行登录taosExplorer所在机器,并使用命令systemctl status taos-explorer.service检查服务的状态,如果返回的状态是inactive,请使用命令systemctl start taos-explorer.service启动服务。 -2. 如果需要获取taosExplorer的详细日志,可通过命令journalctl -u taos-explorer - -## 登录 - -在 TDengine 管理系统的登录页面,输入正确的用户名和密码后,点击登录按钮,即可登录。 - -说明: -- 这里的用户,需要在所连接的 TDengine 中创建,TDengine 默认的用户名和密码为`root/taosdata`; -- 在 TDengine 中创建用户时,默认会设置用户的 SYSINFO 属性值为1, 表示该用户可以查看系统信息,只有 SYSINFO 属性为 1 的用户才能正常登录 TDengine 管理系统。 - -## 面板 - -taosExplorer 内置了一个简单的仪表盘展示以下集群信息,点击左侧功能列表中的 "面板" 可以启用此功能。 - -- 默认的仪表盘会返回对应 Grafana 的安装配置向导 -- 配置过 Grafana 的仪表盘在点击' 面板' 时会跳转到对应的配置地址(该地址来源于 /profile 接口的返回值) - - -## 数据浏览器 - -点击功能列表的“数据浏览器”入口,在“数据浏览器”中可以创建和删除数据库、创建和删除超级表和子表,执行SQL语句,查看SQL语句的执行结果。此外,超级管理员还有对数据库的管理权限,其他用户不提供该功能。 - -具体权限有: - -1.查看(提供数据库/超级表/普通表的基本信息) - -2.编辑 (编辑数据库/超级表/普通表的信息) - -3.数据库管理权限 (仅限超级管理员,该操作可以给指定用户配置数据库管理权限) - -4.删除 (删除数据库/超级表/普通表) - -5.追加 (选择对应的数据库/超级表/普通表名称直接追加到右侧sql输入区域,避免了手工输入) - - -## 系统管理 - -点击功能列表中的“系统管理”入口,可以创建用户、对用户进行访问授权、以及删除用户。还能够对当前所管理的集群中的数据进行备份和恢复。也可以配置一个远程 TDengine 的地址进行数据同步。同时也提供了集群信息和许可证的信息以及代理信息以供查看。系统管理 菜单只有 root 用户才有权限看到 - -### 用户管理 - -点击“系统管理”后,默认会进入“用户”标签页。 -在用户列表,可以查看系统中已存在的用户及其创建时间,并可以对用户进行启用、禁用,编辑(包括修改密码,数据库的读写权限等),删除等操作。 -点击用户列表右上方的“+新增”按钮,即可打开“新增用户”对话框: -1. 输入新增用户的用户名称,必填 -2. 输入新增用户的登录密码,必填,密码长度要求为8-16个字符,且至少要满足以下4个条件中的3个:大写字母,小写字母,数字,特殊字符 -3. 选择新增用户对系统中已存在的数据库的读写权限,非必填,默认情况下,新增用户对所有已存在的数据库无读写权限 -4. 提写完成后,点击确定按钮,即可新增用户。 - -### 系统信息 - -点击“集群”标签后,可以查看DNodes, MNodes和QNodes的状态、创建时间等信息,并可以对以上节点进行新增和删除操作。 - -### 许可证管理 - -点击“许可证”标签后,可以查看系统和系统和各连接器的许可证信息。 -点击位于“许可证”标签页右上角的“激活许可证”按钮,输入“激活码”和“连接器激活码”后,点击“确定”按钮,即可激活,激活码请联系 TDengine 客户成功团队获取。 - -## 数据订阅 - -本章节,将介绍如何在 TDengine 集群中,创建主题,并将其分享给其他用户,以及如何查看一个主题的消费者信息。 - -通过 Explorer, 您可以轻松地完成对数据订阅的管理,从而更好地利用 TDengine 提供的数据订阅能力。 -点击左侧导航栏中的“数据订阅”,即可跳转至数据订阅配置管理页面。 -您可以通过以下两种方式创建主题:使用向导和自定义 SQL 语句。通过自定义 SQL 创建主题时,您需要了解 TDengine 提供的数据订阅 SQL 语句的语法,并保证其正确性。 - -注: 对于数据订阅的详细说明,可参考官方文档中关于“数据订阅”章节,创建数据订阅之前需要先准备源数据库(或源数据库包含相应的超级表或者表),其中源数据库需配置wal_retention_period > 0 。 - -包括主题,消费者,共享主题和示例代码 - -### 创建主题 - -1. 在“主题”标签页,点击“新增新主题”按钮以后,选择向导窗格,然后输入“主题名称”; -2. 在“数据库”下拉列表中,选择相应的数据库; -3. 在“类型”标签下,选择“数据库” 或 “超级表” 或 “子查询”,这里以默认值“数据库”为例; -4. 然后点击“创建” 按钮,即可创建对应的主题。 - -### 分享主题 - -1. 在“共享主题”标签页,在“主题“下拉列表中,选择将要分享的主题; -2. 点击“添加可消费该主题的用户”按钮,然后在“用户名”下拉列表中选择相应的用户,然后点击“新增”,即可将该主题分享给此用户。 - - -### 查看消费者信息 - -1. 通过执行下一节“示例代码”所述的“完整实例”,即可消费共享主题 -2. 在“消费者”标签页,可查看到消费者的有关信息 - -### 示例代码 - -1. 在“示例代码”标签页,在“主题“下拉列表中,选择相应的主题; -2. 选择您熟悉的语言,然后您可以阅读以及使用这部分示例代码用来”创建消费“,”订阅主题“,通过执行 “完整实例”中的程序即可消费共享主题 - -## 流计算 - -通过 Explorer, 您可以轻松地完成对流的管理,从而更好地利用 TDengine 提供的流计算能力。 -点击左侧导航栏中的“流计算”,即可跳转至流计算配置管理页面。 -您可以通过以下两种方式创建流:流计算向导和自定义 SQL 语句。当前,通过流计算向导创建流时,暂不支持分组功能。通过自定义 SQL 创建流时,您需要了解 TDengine 提供的流计算 SQL 语句的语法,并保证其正确性。 - -注: 对于流计算的详细说明,可参考官方文档中关于“流式计算”章节,创建流计算之前需要先准备源数据库以及相应的超级表或表、输出的数据库。 - -### 流计算向导 - -1. 点击“创建流计算”按钮以后,选择流计算向导窗格,然后输入“流名称”; -2. 在“输出”部分,输入相应的“数据库”,“超级表”以及“子表前缀”; -3. 在“源”部分,选择相应的“数据库”,然后根据具体情况,选择使用“超级表”或“表”: - 1. 如果使用“超级表“,请从“超级表”下拉列表中选择相应的超级表, 并在“字段设置”区域,选择相应的字段 - 2. 如果使用“表“,请从“表”下拉列表中选择相应的表, 并在“字段设置”区域,选择相应的字段 -4. 对于窗口设置,根据需要选择”SESSION“, "STATE"或"INTERVAL", 并配置相应的值; -5. 对于”执行“部分,选择相应的”触发器“类型,并设置“Watermark”, "Ignore Expired", "DELETE_MARK", "FILL_HISTORY", "IGNORE UPDATE"; -6. 然后点击“创建” 按钮,即可创建对应的流计算。 - -### 使用 SQL 语句建流 - -1. 点击“创建流计算”按钮以后,选择流计算SQL窗格,然后输入类似如下的SQL语句(反引号内为源数据库以及相应的超级表或表、输出的数据库,请按您的环境更新反引号内的内容) - -```shell -CREATE STREAM `test_stream` TRIGGER WINDOW_CLOSE IGNORE EXPIRED 1 INTO `db_name`.`stable1` SUBTABLE(CONCAT('table1',tbname)) AS SELECT count(*) FROM `test_db`.`stable_name` PARTITION BY tbname INTERVAL(1m) -``` -2. 点击“创建”按钮,即可创建对应的流计算。 \ No newline at end of file diff --git a/docs/zh/18-data-transfer/01-taosX.md b/docs/zh/18-data-transfer/01-taosX.md deleted file mode 100644 index 72d2cb2211..0000000000 --- a/docs/zh/18-data-transfer/01-taosX.md +++ /dev/null @@ -1,972 +0,0 @@ ---- -title: 数据接入、同步和备份 ---- - -## 简介 - -为了能够方便地将各种数据源中的数据导入 TDengine 3.0,TDengine 3.0 企业版提供了一个全新的工具 taosX 用于帮助用户快速将其它数据源中的数据传输到 TDengine 中。 taosX 定义了自己的集成框架,方便扩展新的数据源。目前支持的数据源有 TDengine 自身(即从一个 TDengine 集群到另一个 TDengine 集群),Pi, OPC UA。除了数据接入外,taosX 还支持数据备份、数据同步、数据迁移以及数据导出功能。 - -**欲体验 taosX 的各种数据接入能力,请联系 TDengine 市场或销售团队。** - -## 使用前提 - -使用 taosX 需要已经部署好 TDengine 中的 taosd 和 taosAdapter,具体细节请参考 [系统部署](../../deployment/deploy) - -**使用限制**:taosX 只能用于企业版数据库服务端。 - -## 安装与配置 - -安装 taosX 需要使用独立的 taosX 安装包,其中除了 taosX 之外,还包含 Pi 连接器(限 Windows), OPC 连接器, InfluxDB 连接器, MQTT 连接器,以及必要的 Agent 组件,taosX + Agent + 某个连接器可以用于将相应数据源的数据同步到 TDengine。taosX 安装包中还包含了 taos-explorer 这个可视化管理组件 - -### Linux 安装 - -下载需要的 taosX 安装包,下文以安装包 `taosx-1.0.0-linux-x64.tar.gz` 为例展示如何安装: - -``` bash -# 在任意目录下解压文件 -tar -zxf taosx-1.0.0-linux-x64.tar.gz -cd taosx-1.0.0-linux-x64 - -# 安装 -sudo ./install.sh - -# 验证 -taosx -V -# taosx 1.0.0-494d280c (built linux-x86_64 2023-06-21 11:06:00 +08:00) -taosx-agent -V -# taosx-agent 1.0.0-494d280c (built linux-x86_64 2023-06-21 11:06:01 +08:00) - -# 卸载 -cd /usr/local/taosx -sudo ./uninstall.sh -``` - -**常见问题:** - -1. 安装后系统中增加了哪些文件? - * /usr/bin: taosx, taosx-agent, taos-explorer - * /usr/local/taosx/plugins: influxdb, mqtt, opc - * /etc/systemd/system:taosx.service, taosx-agent.service, taos-explorer.service - * /usr/local/taosx: uninstall.sh - * /etc/taox: agent.toml, explorer.toml - -2. taosx -V 提示 "Command not found" 应该如何解决? - * 检验问题1,保证所有的文件都被复制到对应的目录 - ``` bash - ls /usr/bin | grep taosx - ``` - -### Windows 安装 - -- 下载需要的 taosX 安装包,例如 taosx-1.0.0-Windows-x64-installer.exe,执行安装 -- 可使用 uninstall_taosx.exe 进行卸载 -- 命令行执行 ```sc start/stop taosx``` 启动/停止 taosx 服务 -- 命令行执行 ```sc start/stop taosx-agent``` 启动/停止 taosx-agent 服务 -- 命令行执行 ```sc start/stop taos-explorer``` 启动/停止 taosx-agent 服务 -- windows 默认安装在```C:\Program Files\taosX```,目录结构如下: -~~~ -├── bin -│   ├── taosx.exe -│   ├── taosx-srv.exe -│   ├── taosx-srv.xml -│   ├── taosx-agent.exe -│   ├── taosx-agent-srv.exe -│   ├── taosx-agent-srv.xml -│   ├── taos-explorer.exe -│   ├── taos-explorer-srv.exe -│   └── taos-explorer-srv.xml -├── plugins -│   ├── influxdb -│   │   └── taosx-inflxdb.jar -│   ├── mqtt -│   │   └── taosx-mqtt.exe -│   ├── opc -│   | └── taosx-opc.exe -│   ├── pi -│   | └── taosx-pi.exe -│   | └── taosx-pi-backfill.exe -│   | └── ... -└── config -│   ├── agent.toml -│   ├── explorer.toml -├── uninstall_taosx.exe -├── uninstall_taosx.dat -~~~ - -**运行模式** - -taosX 是进行数据同步与复制的核心组件,以下运行模式指 taosX 的运行模式,其它组件的运行模式在 taosX 的不同运行模式下与之适配。 - -## 命令行模式 - -可以直接在命令行上添加必要的参数直接启动 taosX 即为命令行模式运行。当命令行参数所指定的任务完成后 taosX 会自动停止。taosX 在运行中如果出现错误也会自动停止。也可以在任意时刻使用 ctrl+c 停止 taosX 的运行。本节介绍如何使用 taosX 的各种使用场景下的命令行。 - -### 命令行参数说明 - -**注意:部分参数暂无法通过 explorer设置【见:其他参数说明】,之后会逐步开放) ** - -命令行执行示例: - -```shell -taosx -f -t <其他参数> -``` - -以下参数说明及示例中若无特殊说明 `` 的格式均为占位符,使用时需要使用实际参数进行替换。 - -### DSN (Data Source Name) - -taosX 命令行模式使用 DSN 来表示一个数据源(来源或目的源),典型的 DSN 如下: - -```bash -# url-like -[+]://[[:@]:][/][?=[&=]] -|------|------------|---|-----------|-----------|------|------|----------|-----------------------| -|driver| protocol | | username | password | host | port | object | params | - -// url 示例 -tmq+ws://root:taosdata@localhost:6030/db1?timeout=never -``` -[] 中的数据都为可选参数。 - -1. 不同的驱动 (driver) 拥有不同的参数。driver 包含如下选项: - -- taos:使用查询接口从 TDengine 获取数据 -- tmq:启用数据订阅从 TDengine 获取数据 -- local:数据备份或恢复 -- pi: 启用 pi-connector从 pi 数据库中获取数据 -- opc:启用 opc-connector 从 opc-server 中获取数据 -- mqtt: 启用 mqtt-connector 获取 mqtt-broker 中的数据 -- kafka: 启用 Kafka 连接器从 Kafka Topics 中订阅消息写入 -- influxdb: 启用 influxdb 连接器从 InfluxDB 获取数据 -- csv:从 CSV 文件解析数据 - -2. +protocol 包含如下选项: -- +ws: 当 driver 取值为 taos 或 tmq 时使用,表示使用 rest 获取数据。不使用 +ws 则表示使用原生连接获取数据,此时需要 taosx 所在的服务器安装 taosc。 -- +ua: 当 driver 取值为 opc 时使用,表示采集的数据的 opc-server 为 opc-ua -- +da: 当 driver 取值为 opc 时使用,表示采集的数据的 opc-server 为 opc-da - -3. host:port 表示数据源的地址和端口。 -4. object 表示具体的数据源,可以是TDengine的数据库、超级表、表,也可以是本地备份文件的路径,也可以是对应数据源服务器中的数据库。 -5. username 和 password 表示该数据源的用户名和密码。 -6. params 代表了 dsn 的参数。 - -### 其它参数说明 - -1. parser 通过 --parser 或 -p 设置,设置 transform 的 parser 生效。可以通过 Explorer 在如 CSV,MQTT,KAFKA 数据源的任务配置进行设置。 - - 配置示例: - - ```shell - --parser "{\"parse\":{\"ts\":{\"as\":\"timestamp(ms)\"},\"topic\":{\"as\":\"varchar\",\"alias\":\"t\"},\"partition\":{\"as\":\"int\",\"alias\":\"p\"},\"offset\":{\"as\":\"bigint\",\"alias\":\"o\"},\"key\":{\"as\":\"binary\",\"alias\":\"k\"},\"value\":{\"as\":\"binary\",\"alias\":\"v\"}},\"model\":[{\"name\":\"t_{t}\",\"using\":\"kafka_data\",\"tags\":[\"t\",\"p\"],\"columns\":[\"ts\",\"o\",\"k\",\"v\"]}]}" - - ``` - -2. transform 通过 --transform 或 -T 设置,配置数据同步(仅支持 2.6 到 3.0 以及 3.0 之间同步)过程中对于表名及表字段的一些操作。暂无法通过 Explorer 进行设置。配置说明如下: - - ```shell - 1.AddTag,为表添加 TAG。设置示例:-T add-tag:=。 - 2.表重命名: - 2.1 重命名表限定 - 2.1.1 RenameTable:对所有符合条件的表进行重命名。 - 2.1.2 RenameChildTable:对所有符合条件的子表进行重命名。 - 2.1.3 RenameSuperTable:对所有符合条件的超级表进行重命名。 - 2.2 重命名方式 - 2.2.1 Prefix:添加前缀。 - 2.2.2 Suffix:添加后缀。 - 2.2.3 Template:模板方式。 - 2.2.4 ReplaceWithRegex:正则替换。taosx 1.1.0 新增。 - 重命名配置方式: - <表限定>:<重命名方式>:<重命名值> - 使用示例: - 1.为所有表添加前缀 - --transform rename-table:prefix: - 2.为符合条件的表替换前缀:prefix1 替换为 prefix2,以下示例中的 <> 为正则表达式的不再是占位符。 - -T rename-child-table:replace_with_regex:^prefix1(?)::prefix2_$old - - 示例说明:^prefix1(?) 为正则表达式,该表达式会匹配表名中包含以 prefix1 开始的表名并将后缀部分记录为 old,prefix2$old 则会使用 prefix2 与 old 进行替换。注意:两部分使用关键字符 :: 进行分隔,所以需要保证正则表达式中不能包含该字符。 - 若有更复杂的替换需求请参考:https://docs.rs/regex/latest/regex/#example-replacement-with-named-capture-groups 或咨询 taosx 开发人员。 - ``` - -3. jobs 指定任务并发数,仅支持 tmq 任务。暂无法通过 Explorer 进行设置。通过 --jobs `` 或 -j `` 进行设置。 -4. -v 用于指定 taosx 的日志级别,-v 表示启用 info 级别日志,-vv 对应 debug,-vvv 对应 trace。 - - -### 从 TDengine 到 TDengine 的数据同步 - -#### TDengine 3.0 -> TDengine 3.0 - -在两个相同版本 (都是 3.0.x.y)的 TDengine 集群之间将源集群中的存量及增量数据同步到目标集群中。 - -命令行模式下支持的参数如下: - -| 参数名称 | 说明 | 默认值 | -|-----------|------------------------------------------------------------------|----------------------------| -| group.id | 订阅使用的分组ID | 若为空则使用 hash 生成一个 | -| client.id | 订阅使用的客户端ID | taosx | -| timeout | 监听数据的超时时间,当设置为 never 表示 taosx 不会停止持续监听。 | 500ms | -| offset | 从指定的 offset 开始订阅,格式为 `:`,若有多个 vgroup 则用半角逗号隔开 | 若为空则从 0 开始订阅 | -| token | 目标源参数。 认证使用参数。 | 无 | - -示例: -```shell -taosx run \ - -f 'tmq://root:taosdata@localhost:6030/db1?group.id=taosx1&client.id=taosx&timeout=never&offset=2:10' \ - -t 'taos://root:taosdata@another.com:6030/db2' -``` - - - -#### TDengine 2.6 -> TDengine 3.0 - -将 2.6 版本 TDengine 集群中的数据迁移到 3.0 版本 TDengine 集群。 - -#### 命令行参数 - -| 参数名称 | 说明 | 默认值 | -|--------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------| -| libraryPath | 在 option 模式下指定 taos 库路径 | 无 | -| configDir | 指定 taos.cfg 配置文件路径 | 无 | -| mode | 数据源参数。 history 表示历史数据。 realtime 表示实时同步。 all 表示以上两种。 | history | -| restro | 数据源参数。 在同步实时数据前回溯指定时间长度的数据进行同步。 restro=10m 表示回溯最近 10 分钟的数据以后,启动实时同步。 | 无 | -| interval | 数据源参数。 轮询间隔 ,mode=realtime&interval=5s 指定轮询间隔为 5s | 无 | -| excursion | 数据源参数。 允许一段时间的乱序数据 | 500ms | -| stables | 数据源参数。 仅同步指定超级表的数据,多个超级表名用英文逗号 ,分隔 | 无 | -| tables | 数据源参数。 仅同步指定子表的数据,表名格式为 {stable}.{table} 或 {table},多个表名用英文逗号 , 分隔,支持 @filepath 的方式输入一个文件,每行视为一个表名,如 tables=@./tables.txt 表示从 ./tables.txt 中按行读取每个表名,空行将被忽略。 | 无 | -| select-from-stable | 数据源参数。 从超级表获取 select {columns} from stable where tbname in ({tbnames}) ,这种情况 tables 使用 {stable}.{table} 数据格式,如 meters.d0 表示 meters 超级表下面的 d0 子表。 | 默认使用 select \* from table 获取数据 | -| assert | 目标源参数。 taos:///db1?assert 将检测数据库是否存在,如不存在,将自动创建目标数据库。 | 默认不自动创建库。 | -| force-stmt | 目标源参数。 当 TDengine 版本大于 3.0 时,仍然使用 STMT 方式写入。 | 默认为 raw block 写入方式 | -| batch-size | 目标源参数。 设置 STMT 写入模式下的最大批次插入条数。 | | -| interval | 目标源参数。 每批次写入后的休眠时间。 | 无 | -| max-sql-length | 目标源参数。 用于建表的 SQL 最大长度,单位为 bytes。 | 默认 800_000 字节。 | -| failes-to | 目标源参数。 添加此参数,值为文件路径,将写入错误的表及其错误原因写入该文件,正常执行其他表的同步任务。 | 默认写入错误立即退出。 | -| timeout-per-table | 目标源参数。 为子表或普通表同步任务添加超时。 | 无 | -| update-tags | 目标源参数。 检查子表存在与否,不存在时正常建表,存在时检查标签值是否一致,不一致则更新。 | 无 | - -#### 示例 - -1.使用原生连接同步数据 - -```shell -taosx run \ - -f 'taos://td1:6030/db1?libraryPath=./libtaos.so.2.6.0.30&mode=all' \ - -t 'taos://td2:6030/db2?libraryPath=./libtaos.so.3.0.1.8&assert \ - -v -``` - -2.使用 WebSocket 同步数据超级表 stable1 和 stable2 的数据 - -```shell -taosx run \ - -f 'taos+ws://:@td1:6041/db1?stables=stable1,stable2' \ - -t 'taos+wss://td2:6041/db2?assert&token= \ - -v -``` - -### 从 TDengine 备份数据文件到本地 - -示例: -```shell -taosx run -f 'tmq://root:taosdata@td1:6030/db1' -t 'local:/path_directory/' - -``` -以上示例执行的结果及参数说明: - -将集群 td1 中的数据库 db1 的所有数据,备份到 taosx 所在设备的 /path_directory 路径下。 - -数据源(-f 参数的 DSN)的 object 支持配置为 数据库级(dbname)、超级表级(dbname.stablename)、子表/普通表级(dbname.tablename),对应备份数据的级别数据库级、超级表级、子表/普通表级 - - -### 从本地数据文件恢复到 TDengine - -#### 示例 -```shell -taosx run -f 'local:/path_directory/' -t 'taos://root:taosdata@td2:6030/db1?assert' -``` - -以上示例执行的结果: - -将 taosx 所在设备 /path_directory 路径下已备份的数据文件,恢复到集群 td2 的数据库 db1 中,如果 db1 不存在,则自动建库。 - -目标源(-t 参数的 DSN)中的 object 支持配置为数据库(dbname)、超级表(dbname.stablename)、子表/普通表(dbname.tablename),对应备份数据的级别数据库级、超级表级、子表/普通表级,前提是备份的数据文件也是对应的数据库级、超级表级、子表/普通表级数据。 - - -#### 常见错误排查 - -(1) 如果使用原生连接,任务启动失败并报以下错误: - -```text -Error: tmq to td task exec error - -Caused by: - [0x000B] Unable to establish connection -``` -产生原因是与数据源的端口链接异常,需检查数据源 FQDN 是否联通及端口 6030 是否可正常访问。 - -(2) 如果使用 WebSocket 连接,任务启动失败并报以下错误: - -```text -Error: tmq to td task exec error - -Caused by: - 0: WebSocket internal error: IO error: failed to lookup address information: Temporary failure in name resolution - 1: IO error: failed to lookup address information: Temporary failure in name resolution - 2: failed to lookup address information: Temporary failure in name resolution -``` - -使用 WebSocket 连接时可能遇到多种错误类型,错误信息可以在 ”Caused by“ 后查看,以下是几种可能的错误: - -- "Temporary failure in name resolution": DNS 解析错误,检查 IP 或 FQDN 是否能够正常访问。 -- "IO error: Connection refused (os error 111)": 端口访问失败,检查端口是否配置正确或是否已开启和可访问。 -- "IO error: received corrupt message": 消息解析失败,可能是使用了 wss 方式启用了 SSL,但源端口不支持。 -- "HTTP error: *": 可能连接到错误的 taosAdapter 端口或 LSB/Nginx/Proxy 配置错误。 -- "WebSocket protocol error: Handshake not finished": WebSocket 连接错误,通常是因为配置的端口不正确。 - -(3) 如果任务启动失败并报以下错误: - -```text -Error: tmq to td task exec error - -Caused by: - [0x038C] WAL retention period is zero -``` - -是由于源端数据库 WAL 配置错误,无法订阅。 - -解决方式: -修改数据 WAL 配置: - -```sql -alter database test wal_retention_period 3600; -``` - -### 从 OPC-UA 同步数据到 TDengine - -#### 配置参数 - -| 参数名称 | 类型 | 描述 | -|-----------------|--------|-----------------------------------------------------------------------------| -| interval | int | 采集间隔(单位:秒),默认为1秒 | -| concurrent | int | 采集器并发数,默认为1 | -| batch_size | int | 采集器上报的批次点位数,默认为100 | -| batch_timeout | int | 采集器上报的超时时间(单位:秒),默认为20秒 | -| connect_timeout | int | 连接的超时时间(单位:秒),默认为10秒 | -| request_timeout | int | 请求的超时时间(单位:秒),默认为10秒 | -| security_policy | string | OPC-UA连接安全策略(可配置为None/Basic128Rsa15/Basic256/Basic256Sha256) | -| security_mode | string | OPC-UA连接模式(可配置为None/Sign/SignAndEncrypt) | -| certificate | string | cert.pem的路径。当安全模式或策略不是”无”时生效 | -| private_key | string | key.pem的路径。 当安全模式或策略不是”无”时生效 | -| csv_config_file | string | 包含 OPC UA 的点位配置和表配置。与配置 csv_config_file 配置互斥,csv_config_file 优先生效| -| ua.nodes | string | OPC-UA 测点的 NodeID。和 opc_table_config 配置结合使用,两者需要同时配置。与配置 csv_config_file 配置互斥,csv_config_file 优先生效。配置格式为 ::,code 用于建子表。| -| opc_table_config | string | OPCUA 单列模式表配置。需要与 ua.nodes 配合使用。| -| debug | bool | 启用 OPC 连接器的 debug 日志。默认为 false。| -| enable | bool | 原始数据存储。默认为 false| -| path | string | 原始数据存储路径。enable 为 true 时必须配置。| -| keep | int | 原始数据保存天数。enable 为 true 时必须配置。| - -补充: -1. opc_table_config 说明: - -```json -{ - "stable_prefix": "meters", // 超级表前缀 - "column_configs": - [ - { - "column_name": "received_time", // 存储接收时间 - "column_type": "timestamp", - "column_alias": "ts", // 接收时间建表列用列名为 ts - "is_primary_key": true // 接收时间时间戳作为主键 - }, - { - "column_name": "original_time", - "column_type": "timestamp", - "column_alias": "ts_2", - "is_primary_key": false - }, - { - "column_name": "value", // 数据列 - "column_alias": "valueaa", // 数据列别名 - "is_primary_key": false - }, - { - "column_name": "quality", // 质量位列 - "column_type": "int", - "column_alias": "quality11", // 质量位列别名 - "is_primary_key": false - } - ] -} -``` - -#### 示例 - -1. 使用 ua.nodes 和 opc_table_config 的配置示例: -采集 nodeid 为 ns=2;i=2 和 ns=2;i=3 的点位,将其写入到集群 tdengine 的 opc 库中超级表前缀为 meters,如果 ns=2;i=2 的点位类型为 float 则会创建 meters_float 的超级表,超级表使用 opc 接收的数据作为时间戳索引列,并且保留原始时间戳列,原始时间戳列名为 ts_2,数据列存储为 valueaa,同时存储质量数据到 quality11 列。 - -```shell -taosx run \ - -f "opcua://uauser:uapass@localhost:4840?ua.nodes=ns=2;i=2::DSF1312,ns=2;i=3::DSF1313&opc_table_config={\"stable_prefix\": \"meters\", \"column_configs\": [{\"column_name\": \"received_time\", \"column_type\": \"timestamp\", \"column_alias\": \"ts\", \"is_primary_key\": true }, {\"column_name\": \"original_time\", \"column_type\": \"timestamp\", \"column_alias\": \"ts_2\", \"is_primary_key\": false }, {\"column_name\": \"value\", \"column_alias\": \"valueaa\", \"is_primary_key\": false }, {\"column_name\": \"quality\", \"column_type\": \"int\", \"column_alias\": \"quality11\", \"is_primary_key\": false } ] }" \ - -t "taos://tdengine:6030/opc" - - - -``` - -2. 使用 CSV 配置文件 - -```shell -taosx run -f "opcua://?csv_config_file=@" -t "taos+ws://tdengine:6041/opc" -``` - -#### CSV 配置文件模板 - - -### 从 OPC-DA 同步数据到 TDengine (Windows) - -#### 配置参数 - -| 参数名称 | 类型 | 描述 | -|-----------------|--------|-----------------------------------------------------------------------------| -| interval | int | 采集间隔(单位:秒),默认为1秒 | -| concurrent | int | 采集器并发数,默认为1 | -| batch_size | int | 采集器上报的批次点位数,默认为100 | -| batch_timeout | int | 采集器上报的超时时间(单位:秒),默认为20秒 | -| connect_timeout | int | 连接的超时时间(单位:秒),默认为10秒 | -| request_timeout | int | 请求的超时时间(单位:秒),默认为10秒 | -| csv_config_file | string | 包含 OPC UA 的点位配置和表配置。与 ua.nodes 两者之间需要配置一个。CSV 的配置模版参考:OPC 需求汇总及完成现状 | -| da.tags | string | OPC-UA 测点的 NodeID。和 opc_table_config 配置结合使用,两者需要同时配置。与配置 csv_config_file 配置互斥,csv_config_file 优先生效。| -| opc_table_config | string | OPCUA 单列模式表配置。需要与 da.tags 配合使用| -| debug | bool | 启用 OPC 连接器的 debug 日志。默认为 false。| -| enable | bool | 原始数据存储。默认为 false| -| path | string | 原始数据存储路径。enable 为 true 时必须配置。| -| keep | int | 原始数据保存天数。enable 为 true 时必须配置。| - -#### 应用示例 - -```shell -taosx run \ - -f "opc+da://Matrikon.OPC.Simulation.1?nodes=localhost&da.tags=Random.Real8::tb3::c1::int" - -t "taos://tdengine:6030/opc" -``` - -以上示例的执行结果: - -采集 Matrikon.OPC.Simulation.1 服务器上 OPC DA 中 da.tags 为 Random.Real8的数据,数据类型为int,对应在 TDengine 中以表名为 tb3 ,列名为c1,列类型为 int 型 schema 来创建表(如果对应表已存在,则直接采集数据并写入)。 - -#### 常见错误排查 - -(1) 如果使用原生连接,任务启动失败并打印如下错误: -```text -Error: tmq to td task exec error - -Caused by: - 0: Error occurred while creating a new object: [0x000B] Unable to establish connection -``` -解决方式: - -检查目标端 TDengine 的 FQDN 是否联通及端口 6030 是否可正常访问。 - -(2) 如果使用 WebSocket 连接任务启动失败并打印如下错误:: - -```text -Error: tmq to td task exec error - -Caused by: - 0: WebSocket internal error: IO error: failed to lookup address information: Temporary failure in name resolution - 1: IO error: failed to lookup address information: Temporary failure in name resolution - 2: failed to lookup address information: Temporary failure in name resolution -``` - -使用 WebSocket 连接时可能遇到多种错误类型,错误信息可以在 ”Caused by“ 后查看,以下是几种可能的错误: - -- "Temporary failure in name resolution": DNS 解析错误,检查目标端 TDengine的 IP 或 FQDN 是否能够正常访问。 -- "IO error: Connection refused (os error 111)": 端口访问失败,检查目标端口是否配置正确或是否已开启和可访问(通常为6041端口)。 -- "HTTP error: *": 可能连接到错误的 taosAdapter 端口或 LSB/Nginx/Proxy 配置错误。 -- "WebSocket protocol error: Handshake not finished": WebSocket 连接错误,通常是因为配置的端口不正确。 - -### 从 PI 同步数据到 TDengine (Windows) - -#### PI DSN 配置 - -PI DSN 的完整配置如下: - -```shell -pi://[:@]PIServerName/AFDatabaseName?[TemplateForPIPoint][&TemplateForAFElement][&PointList][&][&][&UpdateInterval] -``` - -在 taosX CLI 运行时支持的参数如下,其中 TemplateForPIPoint、TemplateForAFElement、PointList 三个参数至少配置一项: -- PISystemName:选填,连接配置 PI 系统服务名,默认值与 PIServerName 一致 -- MaxWaitLen:选填,数据最大缓冲条数,默认值为 1000 ,有效取值范围为 [1,10000] -- UpdateInterval:选填,PI System 取数据频率,默认值为 10000(毫秒:ms),有效取值范围为 [10,600000] -- TemplateForPIPoint:选填,使用 PI Point 模式将模板按照 element 的每个 Arrtribution 作为子表导入到 TDengine -- TemplateForAFElement:选填,使用 AF Point 模式将模板按照 element 的 Attribution 集合作为一个子表导入到 TDengine -- PointList:选填,使用 PointList 模式将指定csv文件中描述的点位信息在 PI 数据库中的数据导入到 TDengine - - -#### 应用示例 - -将位于服务器 WIN-2OA23UM12TN 中的 PI 数据库 Met1,模板 template1、template2配置为 TemplateForPIPoint模式,模板 template3、template4 配置为 TemplateForAFElement 模式,服务器 /home/ 路径下的点位文件 points.csv 配置为 PointList 模式,连接配置 PI 系统服务名为 PI,数据最大缓冲条数为1000,PI System 取数据频率为10000ms,将该库中的数据同步到 服务器 tdengine 的 pi 库中。完整的示例如下: - -```shell -taosx run \ - -f "pi://WIN-2OA23UM12TN/Met1?TemplateForPIPoint=template1,template2&TemplateForAFElement=template3,template4" \ - -t "taos://tdengine:6030/pi" -``` - - -#### 常见错误排查 - -(1) 如果使用原生连接,任务启动失败并打印如下错误: -```text -Error: tmq to td task exec error - -Caused by: - 0: Error occurred while creating a new object: [0x000B] Unable to establish connection -``` -解决方式: - -检查目标端 TDengine 的 FQDN 是否联通及端口 6030 是否可正常访问。 - -(2) 如果使用 WebSocket 连接任务启动失败并打印如下错误:: - -```text -Error: tmq to td task exec error - -Caused by: - 0: WebSocket internal error: IO error: failed to lookup address information: Temporary failure in name resolution - 1: IO error: failed to lookup address information: Temporary failure in name resolution - 2: failed to lookup address information: Temporary failure in name resolution -``` - -使用 WebSocket 连接时可能遇到多种错误类型,错误信息可以在 ”Caused by“ 后查看,以下是几种可能的错误: - -- "Temporary failure in name resolution": DNS 解析错误,检查目标端 TDengine的 IP 或 FQDN 是否能够正常访问。 -- "IO error: Connection refused (os error 111)": 端口访问失败,检查目标端口是否配置正确或是否已开启和可访问(通常为6041端口)。 -- "HTTP error: *": 可能连接到错误的 taosAdapter 端口或 LSB/Nginx/Proxy 配置错误。 -- "WebSocket protocol error: Handshake not finished": WebSocket 连接错误,通常是因为配置的端口不正确。 - - -### 从 InfluxDB 同步数据到 TDengine - -#### 命令行参数 - -将数据从 InfluxDB 同步至 TDengine 的命令,如下所示: - -```bash -taosx run --from "" --to "" -``` - -其中,InfluxDB DSN 符合 DSN 的通用规则,这里仅对其特有的参数进行说明: -- version: 必填,InfluxDB 的版本,主要用于区分 1.x 与 2.x 两个版本,二者使用不同的认证参数; -- version = 1.x - - username: 必填,InfluxDB 用户,该用户至少在该组织中拥有读取权限; - - password: 必填,InfluxDB 用户的登陆密码; -- version = 2.x - - orgId: 必填,InfluxDB 中的 Orgnization ID; - - token: 必填,InfluxDB 中生成的 API token, 这个 token 至少要拥有以上 Bucket 的 Read 权限; -- bucket: 必填,InfluxDB 中的 Bucket 名称,一次只能同步一个 Bucket; -- measurements: 非必填,可以指定需要同步的多个 Measurements(英文逗号分割),未指定则同步全部; -- beginTime: 必填,格式为:YYYY-MM-DD'T'HH:MM:SS'Z', 时区采用 UTC 时区,例如:2023-06-01T00:00:00+0800, 即北京时间2023-06-01 00:00:00(东八区时间); -- endTime: 非必填,可以不指定该字段或值为空,格式与beginTime相同;如果未指定,提交任务后,将持续进行数据同步; -- readWindow: 非必填,可以不指定该字段或值为空,可选项为D、H、M(天、时、分);如果未指定,则默认按 M 拆分读取窗口。 - -#### 示例 - -将位于 192.168.1.10 的 InfluxDB 中, Bucket 名称为 test_bucket, 从UTC时间2023年06月01日00时00分00秒开始的数据,通过运行在 192.168.1.20 上的 taoskeeper, 同步至 TDengine 的 test_db 数据库中,完整的命令如下所示: -```bash -# version = 1.x -taosx run \ - --from "influxdb+http://192.168.1.10:8086/?version=1.7&username=test&password=123456&bucket=test_bucket&measurements=&beginTime=2023-06-01T00:00:00+0800&readWindow=M" \ - --to "taos+http://192.168.1.20:6041/test_db" \ - -vv - -# version = 2.x -taosx run \ - --from "influxdb+http://192.168.1.10:8086/?version=2.7&orgId=3233855dc7e37d8d&token=OZ2sB6Ie6qcKcYAmcHnL-i3STfLVg_IRPQjPIzjsAQ4aUxCWzYhDesNape1tp8IsX9AH0ld41C-clTgo08CGYA==&bucket=test_bucket&measurements=&beginTime=2023-06-01T00:00:00+0800&readWindow=M" \ - --to "taos+http://192.168.1.20:6041/test_db" \ - -vv -``` - -在这个命令中,未指定endTime, 所以任务会长期运行,持续同步最新的数据。 - - -### 从 OpenTSDB 同步数据到 TDengine - -#### 命令行参数 - -将数据从 OpenTSDB 同步至 TDengine 的命令,如下所示: - -```bash -taosx run --from "" --to "" -``` - -其中,OpenTSDB DSN 符合 DSN 的通用规则,这里仅对其特有的参数进行说明: -- metrics: 非必填,可以指定需要同步的多个 Metrics(英文逗号分割),未指定则同步全部; -- beginTime: 必填,格式为:YYYY-MM-DD'T'HH:MM:SS'Z', 时区采用 UTC 时区,例如:2023-06-01T00:00:00+0800, 即北京时间2023-06-01 00:00:00(东八区时间); -- endTime: 非必填,可以不指定该字段或值为空,格式与beginTime相同;如果未指定,提交任务后,将持续进行数据同步; -- readWindow: 非必填,可以不指定该字段或值为空,可选项为D、H、M(天、时、分);如果未指定,则默认按分钟拆分读取窗口。 - -#### 示例 - -将位于 192.168.1.10 的 OpenTSDB 中, Metric 名称为 test_metric1 与 test_metric2 的两个数据源, 从UTC时间2023年06月01日00时00分00秒开始的数据,通过运行在 192.168.1.20 上的 taoskeeper, 同步至 TDengine 的 test_db 数据库中,完整的命令如下所示: - -```bash -taosx run \ - --from "opentsdb+http://192.168.1.10:4242/?metrics=test_metric1,test_metric2&beginTime=2023-06-01T00:00:00+0800&readWindow=M" \ - --to "taos+http://192.168.1.20:6041/test_db" \ - -vv -``` - -在这个命令中,未指定endTime, 所以任务会长期运行,持续同步最新的数据。 - - -### 从 MQTT 同步数据到 TDengine - -目前,MQTT 连接器仅支持从 MQTT 服务端消费 JSON 格式的消息,并将其同步至 TDengine. 命令如下所示: - -```bash -taosx run --from "" --to "" --parser "@" -``` - -其中: -- `--from` 用于指定 MQTT 数据源的 DSN -- `--to` 用于指定 TDengine 的 DSN -- `--parser` 用于指定一个 JSON 格式的配置文件,该文件决定了如何解析 JSON 格式的 MQTT 消息,以及写入 TDengine 时的超级表名、子表名、字段名称和类型,以及标签名称和类型等。 - -#### MQTT DSN 配置 - -MQTT DSN 符合 DSN 的通用规则,这里仅对其特有的参数进行说明: -- topics: 必填,用于配置监听的 MQTT 主题名称和连接器支持的最大 QoS, 采用 `::` 的形式;支持配置多个主题,使用逗号分隔;配置主题时,还可以使用 MQTT 协议的支持的通配符#和+; -- version: 非必填,用于配置 MQTT 协议的版本,支持的版本包括:3.1/3.1.1/5.0, 默认值为3.1; -- clean_session: 非必填,用于配置连接器作为 MQTT 客户端连接至 MQTT 服务端时,服务端是否保存该会话信息,其默认值为 true, 即不保存会话信息; -- client_id: 必填,用于配置连接器作为 MQTT 客户端连接至 MQTT 服务端时的客户端 id; -- keep_alive: 非必填,用于配置连接器作为 MQTT 客户端,向 MQTT 服务端发出 PINGREG 消息后的等待时间,如果连接器在该时间内,未收到来自 MQTT 服务端的 PINGREQ, 连接器则主动断开连接;该配置的单位为秒,默认值为 60; -- ca: 非必填,用于指定连接器与 MQTT 服务端建立 SSL/TLS 连接时,使用的 CA 证书,其值为在证书文件的绝对路径前添加@, 例如:@/home/admin/certs/ca.crt; -- cert: 非必填,用于指定连接器与 MQTT 服务端建立 SSL/TLS 连接时,使用的客户端证书,其值为在证书文件的绝对路径前添加@, 例如:@/home/admin/certs/client.crt; -- cert_key: 非必填,用于指定连接器与 MQTT 服务端建立 SSL/TLS 连接时,使用的客户端私钥,其值为在私钥文件的绝对路径前添加@, 例如:@/home/admin/certs/client.key; -- log_level: 非必填,用于配置连接器的日志级别,连接器支持 error/warn/info/debug/trace 5种日志级别,默认值为 info. - -一个完整的 MQTT DSN 示例如下: -```bash -mqtt://:@:8883?topics=testtopic/1::2&version=3.1&clean_session=true&log_level=info&client_id=taosdata_1234&keep_alive=60&ca=@/home/admin/certs/ca.crt&cert=@/home/admin/certs/client.crt&cert_key=@/home/admin/certs/client.key -``` - -#### MQTT 连接器的解释器配置 - -连接器的解释器配置文件,即`--parser`配置项的参数,它的值为一个 JSON 文件,其配置可分为`parse`和`model`两部分,模板如下所示: - -```json -{ - "parse": { - "payload": { - "json": [ - { - "name": "ts", - "alias": "ts", - "cast": "TIMESTAMP" - }, - ... - ] - } - }, - "model": { - "using": "", - "name": "{alias}", - "columns": [ ... ], - "tags": [ ... ] - } -} -``` - -各字段的说明如下: -- parse 部分目前仅支持 json 一种 payload, json 字段的值是一个由 JSON Object 构成的 JSON Array: - - 每个 JSON Ojbect 包括 name, alias, cast 三个字段; - - name 字段用于指定如何从 MQTT 消息中提取字段,如果 MQTT 消息是一个简单的 JSON Object, 这里可以直接设置其字段名;如果 MQTT 消息是一个复杂的 JSON Object, 这里可以使用 JSON Path 提取字段,例如:`$.data.city`; - - alias 字段用于命名 MQTT 消息中的字段同步至 TDengine 后使用的名称; - - cast 字段用于指定 MQTT 消息中的字段同步至 TDengine 后使用的类型。 -- model 部分用于设置 TDengine 超级表、子表、列和标签等信息: - - using 字段用于指定超级表名称; - - name 字段用于指定子表名称,它的值可以分为前缀和变量两部分,变量为 parse 部分设置的 alias 的值,需要使用{}, 例如:d{id}; - - columns 字段用于设置 MQTT 消息中的哪些字段作为 TDengine 超级表中的列,取值为 parse 部分设置的 alias 的值;需要注意的是,这里的顺序会决定 TDengine 超级表中列的顺序,因此第一列必须为 TIMESTAMP 类型; - - tags 字段用于设置 MQTT 消息中的哪些字段作为 TDengine 超级表中的标签,取值为 parse 部分设置的 alias 的值。 - -#### 举例说明 - -在 192.168.1.10 的 1883 端口运行着一个 MQTT broker, 用户名、口令分别为admin, 123456; 现欲将其中的消息,通过运行在 192.168.1.20 的 taosadapter 同步至 TDengine 的 test 数据库中。MQTT 消息格式为: - -```json -{ - "id": 1, - "current": 10.77, - "voltage": 222, - "phase": 0.77, - "groupid": 7, - "location": "California.SanDiego" -} -``` - -MQTT 消息同步至 TDengine 时, 如果采用 meters 作为超级表名,前缀“d”拼接id字段的值作为子表名,ts, id, current, voltage, phase作为超级表的列,groupid, location作为超级表的标签,其解释器的配置如下: -```json -{ - "parse": { - "payload": { - "json": [ - { - "name": "ts", - "alias": "ts", - "cast": "TIMESTAMP" - }, - { - "name": "id", - "alias": "id", - "cast": "INT" - }, - { - "name": "voltage", - "alias": "voltage", - "cast": "INT" - }, - { - "name": "phase", - "alias": "phase", - "cast": "FLOAT" - }, - { - "name": "current", - "alias": "current", - "cast": "FLOAT" - }, - { - "name": "groupid", - "alias": "groupid", - "cast": "INT" - }, - { - "name": "location", - "alias": "location", - "cast": "VARCHAR(20)" - } - ] - } - }, - "model": { - "name": "d{id}", - "using": "meters", - "columns": [ - "ts", - "id", - "current", - "voltage", - "phase" - ], - "tags": [ - "groupid", - "location" - ] - } -} -``` - -如果以上parser配置位于`/home/admin/parser.json`中,那么完整的命令如下所示: - -```bash -taosx run \ - -f "mqtt://admin:123456@192.168.1.10:1883?topics=testtopic/1::2&version=3.1&clean_session=true&log_level=info&client_id=1234&keep_alive=60" \ - -t "taos+ws://192.168.1.20:6041/test" - --parser "@/home/admin/parser.json" - --verbose -``` - -### 从 Kafka 同步数据到 TDengine - -#### 命令行参数 - -taosx 支持从 Kafka 消费数据,写入 TDengine。命令如下所示: -```sehll -taosx run -f "" -t "" -``` -或 -```shell -taosx run -f "" -t "" --parser "@" -``` -其中: -- -f或--from: Kafka 的 DSN -- -t或--to :TDengine 的 DSN -- --parser :一个 JSON 格式的配置文件,或JSON格式的字符串。 - -#### Kafka DSN 配置的配置 - -| 参数 | 说明 | 必填? | 缺省值 | 适用于 | 示例 | -|-----|---------------|----------|---------|---------|----------| -| group| 消费者的group。允许组为空字符串,在这种情况下,生成的消费者将是无组的 | 否 | "" | 源端 | | -| topics | 指定要使用的主题。指定主题的所有可用分区都将被使用,除非在指定 topic_partitions 时被覆盖。| 该参数或topic_partitions必须至少指定一个,以便将主题分配给消费者。| None | 源端 | topics=tp1,tp2 | -| topic_partitions | 显式指定要使用的主题分区。只使用已标识主题的指定分区。 | 该参数或topics必须至少指定一个,以便将主题分配给消费者。 | None | 源端 | topic_partitions=tp1:0..2,tp2:1 | -| fallback_offset | topic偏移量时可能的值:- Earliest:接收最早的可用偏移量; - Latest:接收最近的偏移量; - ByTime(i64):用于请求在某一特定时间(ms)之前的所有消息;Unix时间戳(毫秒) | 否 | Earliest | 源端 | fallback_offset=Earliest | -| offset_storage | 定义在获取或提交组偏移量时,要使用的可用存储:- Zookeeper:基于Zookeeper的存储(从kafka 0.8.1开始可用);- Kafka:基于Kafka的存储(从Kafka 0.8.2开始可用)。这是组存储其偏移量的首选方法。 | 否 | Kafka | 源端 | offset_storage=Kafka | -| timeout | 从kafka订阅数据时,如果超时后没有获取到有效数据,退出 | 否 | 500 | 源端 | timeout=never | -| use_ssl | 是否使用SSL认证 | 否 | | 源端 | | -| cert | SSL证书的文件路径 | 否 | | | 源端 | | -| cert_key | SSL证书key的文件路径 | 否 | | 源端 || - - -#### 示例一 - -从192.168.1.92服务器的Kafka实例中消费数据,同步到192.168.1.92上的TDengine,不使用parser。 - -1. kafka - -```shell -#!/bin/bash -KAFKA_HOME=/root/zyyang/kafka_2.13-3.1.0 -$KAFKA_HOME/bin/kafka-topics.sh --bootstrap-server 127.0.0.1:9092 --topic tp1 --delete -$KAFKA_HOME/bin/kafka-topics.sh --bootstrap-server 127.0.0.1:9092 --topic tp2 --delete -$KAFKA_HOME/bin/kafka-topics.sh --bootstrap-server 127.0.0.1:9092 --topic tp1 --partitions 5 --replication-factor 1 --create -$KAFKA_HOME/bin/kafka-topics.sh --bootstrap-server 127.0.0.1:9092 --topic tp2 --partitions 1 --replication-factor 1 --create -$KAFKA_HOME/bin/kafka-console-producer.sh --bootstrap-server 127.0.0.1:9092 --topic tp1 << EOF -{"id": 1, "message": "hello"} -{"id": 2, "message": "hello"} -{"id": 3, "message": "hello"} -{"id": 4, "message": "hello"} -{"id": 5, "message": "hello"} -EOF -$KAFKA_HOME/bin/kafka-console-producer.sh --bootstrap-server 127.0.0.1:9092 --topic tp2 << EOF -{"id": 1, "message": "aaa"} -{"id": 2, "message": "aaa"} -{"id": 3, "message": "aaa"} -{"id": 4, "message": "aaa"} -{"id": 5, "message": "aaa"} -EOF -$KAFKA_HOME/bin/kafka-topics.sh --bootstrap-server 127.0.0.1:9092 --topic tp1 --describe -$KAFKA_HOME/bin/kafka-topics.sh --bootstrap-server 127.0.0.1:9092 --topic tp2 --describe -``` - -2. TDengine - -```shell -drop database if exists kafka_to_taos; -create database if not exists kafka_to_taos precision 'ms'; -use kafka_to_taos; -``` - -3. taosx - -```shell -taosx run -f "kafka://192.168.1.92:9092/?topics=tp1,tp2&timeout=5000" -t "taos://192.168.1.92:6030/kafka_to_taos" --parser "{\"parse\":{\"ts\":{\"as\":\"timestamp(ms)\"},\"topic\":{\"as\":\"varchar\",\"alias\":\"t\"},\"partition\":{\"as\":\"int\",\"alias\":\"p\"},\"offset\":{\"as\":\"bigint\",\"alias\":\"o\"},\"key\":{\"as\":\"binary\",\"alias\":\"k\"},\"value\":{\"as\":\"binary\",\"alias\":\"v\"}},\"model\":[{\"name\":\"t_{t}\",\"using\":\"kafka_data\",\"tags\":[\"t\",\"p\"],\"columns\":[\"ts\",\"o\",\"k\",\"v\"]}]}" -``` - -#### 示例2 - -从192.168.1.92服务器的Kafka实例中消费数据,同步到192.168.1.92上的TDengine,使用parser解析value中的JSON数据。 - -1. kafka,同“示例1” -2. TDengine,同“示例1” -3. Taosx - -```shell -taosx run -f "kafka://192.168.1.92:9092/?topics=tp1,tp2&timeout=5000" -t "taos://192.168.0.201:6030/kafka_to_taos" --parser "{\"parse\":{\"ts\":{\"as\":\"timestamp(ms)\"},\"topic\":{\"as\":\"varchar\",\"alias\":\"t\"},\"partition\":{\"as\":\"int\",\"alias\":\"p\"},\"offset\":{\"as\":\"bigint\",\"alias\":\"o\"},\"value\":{\"json\":[\"id::int\",\"message::binary\"]}},\"model\":[{\"name\":\"t_{t}\",\"using\":\"kafka_data\",\"tags\":[\"t\",\"p\"],\"columns\":[\"ts\",\"o\",\"id\",\"message\"]}]}" -``` - -## 服务模式 - -在服务模式下, 一共需要三个组件协同完成数据迁移。 taosX,Agent 以及 taosExplorer 均已服务态运行,各种操作通过 taosExplorer 的图形界面进行。taos-Explorer 组件除了数据迁移之外,还提供了使用 TDengine 的图形化界面。 - -### 部署 taosX - -#### 配置 - -taosX 仅支持通过命令行参数进行配置。服务模式下,taosX 支持的命令行参数可以通过以下方式查看: - -``` -taosx serve --help -``` - -建议通过 Systemd 的方式,启动 taosX 的服务模式,其 Systemd 的配置文件位于:`/etc/systemd/system/taosx.service`. 如需修改 taosX 的启动参数,可以编辑该文件中的以下行: - -``` -ExecStart=/usr/bin/taosx serve -v -``` - -修改后,需执行以下命令重启 taosX 服务,使配置生效: - -``` -systemctl daemon-reload -systemctl restart taosx -``` - -#### 启动 - -Linux 系统上以 Systemd 的方式启动 taosX 的命令如下: - -```shell -systemctl start taosx -``` - -Windows 系统上,请在 "Services" 系统管理工具中找到 "taosX" 服务,然后点击 "启动这个服务"。 - -#### 问题排查 - -1. 如何修改 taosX 的日志级别? - -taosX 的日志级别是通过命令行参数指定的,默认的日志级别为 Info, 具体参数如下: -- INFO: `taosx serve -v` -- DEBUG: `taosx serve -vv` -- TRACE: `taosx serve -vvv` - -Systemd 方式启动时,如何修改命令行参数,请参考“配置”章节。 - -2. 如何查看 taosX 的日志? - -以 Systemd 方式启动时,可通过 journalctl 命令查看日志。以滚动方式,实时查看最新日志的命令如下: - -``` -journalctl -u taosx -f -``` - -### 部署 Agent - -#### 配置 - -Agent 默认的配置文件位于`/etc/taos/agent.toml`, 包含以下配置项: -- endpoint: 必填,taosX 的 GRPC endpoint -- token: 必填,在 taosExplorer 上创建 agent 时,产生的token -- debug_level: 非必填,默认为 info, 还支持 debug, trace 等级别 - -如下所示: - -```TOML -endpoint = "grpc://:6055" -token = "" -log_level = "debug" -``` - -日志保存时间设置 -日志保存的天数可以通过环境变量进行设置 TAOSX_LOGS_KEEP_DAYS, 默认为 30 天。 - -```shell -export TAOSX_LOGS_KEEP_DAYS=7 -``` - -#### 启动 - -Linux 系统上 Agent 可以通过 Systemd 命令启动: - -``` -systemctl start taosx-agent -``` - -Windows 系统上通过系统管理工具 "Services" 找到 taosx-agent 服务,然后启动它。 - -#### 问题排查 - -可以通过 journalctl 查看 Agent 的日志 - -``` -journalctl -u taosx-agent -f -``` - -### 部署 taosExplorer - - -### 数据同步功能 - -请参考 taosExplorer \ No newline at end of file diff --git a/docs/zh/18-data-transfer/02-explorer.md b/docs/zh/18-data-transfer/02-explorer.md deleted file mode 100644 index fd374cc734..0000000000 --- a/docs/zh/18-data-transfer/02-explorer.md +++ /dev/null @@ -1,128 +0,0 @@ ---- -title: 基于可视化界面的数据接入和数据迁移 ---- - -本节讲述使用 taos Explorer 的可视化界面进行数据迁移,使用此功能需要依赖 taosd, taosAdapter, taosX, taos-explorer 等几个服务组件。关于 taosd 和 taosAdapter 的部署请参考 [系统部署](../../deployment/deploy),[taosX](../taosX),以及 [部署 taos-explorer](../../operation/web) - -## 功能入口 - -点击 explorer 左侧功能列表中的 "数据写入",可以配置不同类型的数据源,包括 TDengine Subscription, PI, OPC-UA, OPC-DA, InfluxDB, MQTT,Kafka, CSV 等,将它们的数据写入到当前正在被管理的 TDengine 集群中。 - -## TDengine 订阅 - -进入TDengine订阅任务配置页面: -1. 在连接协议栏中,配置连接协议,默认为原生连接,可配置为WS、WSS; -2. 在服务器栏中配置服务器的 IP 或域名; -3. 在端口栏中配置连接的端口号,默认值为6030; -4. 在主题栏中,配置可以配置订阅一个或多个数据库,或超级表或普通表,也可以是一个已创建的 Topic; -5. 在认证栏,可以配置访问 TDengine 的用户名密码,用户名默认值为 root,密码默认值为 taosdata;如果数据源为云服务实例,则可以选择令牌认证方式并配置实例 token; -6. 在订阅初始位置栏,可配置从最早数据(earliest)或最晚(latest)数据开始订阅,默认为 earliest; -7. 在超时栏配置超时时间,可配置为 never: 表示无超时时间,持续进行订阅,也可指定超时时间:5s, 1m 等,支持单位 ms(毫秒),s(秒),m(分钟),h(小时),d(天),M(月),y(年)。 -8. 在目标数据库栏中,选择本地 TDengine 的库作为目标库,点击 submit,即可启动一个 TDengine 订阅任务。 - -## Pi - -1. 在 PI 数据接入页面,设置 PI 服务器的名称、AF 数据库名称。 -2. 在监测点集栏,可以配置选择 Point 模式监测点集合、Point 模式监测的 AF 模板、AF 模式监测的 AF 模板。 -3. 在 PI 系统设置栏,可以配置 PI 系统名,默认为 PI 服务器名。 -4. 在 Data Queue 栏,可以配置 PI 连接器运行参数:MaxWaitLen(数据最大缓冲条数),默认值为 1000 ,有效取值范围为 [1,10000];UpdateInterval(PI System 取数据频率),默认值为 10000(毫秒:ms),有效取值范围为 [10,600000];重启补偿时间(Max Backfill Range,单位:天),每次重启服务时向前补偿该天数的数据,默认为1天。 -5. 在目标数据库栏,选择需要写入的 TDengine 数据库,点击 submit ,即可启动一个 PI 数据接入任务。 - -## OPC-UA - -1. 在 OPC-UA页面,配置 OPC-server 的地址,输入格式为 127.0.0.1:6666/OPCUA/ServerPath。 -2. 在认证栏,选择访问方式。可以选择匿名访问、用户名密码访问、证书访问。使用证书访问时,需配置证书文件信息、私钥文件信息、OPC-UA 安全协议和 OPC-UA 安全策略 -3. 在 Data Sets 栏,配置点位信息。(可通过“选择”按钮选择正则表达式过滤点位,每次最多能过滤出10条点位);点位配置有两种方式:1.手动输入点位信息 2.上传csv文件配置点位信息 -4. 在连接配置栏,配置连接超时间隔和采集超时间隔(单位:秒),默认值为10秒。 -5. 在采集配置栏,配置采集间隔(单位:秒)、点位数量、采集模式。采集模式可选择observe(轮询模式)和subscribe(订阅模式),默认值为observe。 -6. 在库表配置栏,配置目标 TDengine 中存储数据的超级表、子表结构信息。 -7. 在其他配置栏,配置并行度、单次采集上报批次(默认值100)、上报超时时间(单位:秒,默认值10)、是否开启debug级别日志。 -8. 在目标数据库栏,选择需要写入的 TDengine 数据库,点击 submit,即可启动一个 OPC-UA 数据接入任务。 - -## OPC-DA - -1. 在 OPC-DA页面,配置 OPC-server 的地址,输入格式为 127.0.0.1<,localhost>/Matrikon.OPC.Simulation.1。 -2. 在数据点栏,配置 OPC-DA 采集点信息。(可通过“选择”按钮选择正则表达式过滤点位,每次最多能过滤出10条点位)。点位配置有两种方式:1.手动输入点位信息 2.上传csv文件配置点位信息 -3. 在连接栏,配置连接超时时间(单位:秒,默认值为10秒)、采集超时时间(单位:秒,默认值为10秒)。 -4. 在库表配置栏,配置目标 TDengine 中存储数据的超级表、子表结构信息。 -5. 在其他配置栏,配置并行度、单次采集上报批次(默认值100)、上报超时时间(单位:秒,默认值10)、是否开启debug级别日志。 -6. 在目标数据库栏,选择需要写入的 TDengine 数据库,点击 submit,即可启动一个 OPC-DA 数据接入任务。 - -## InfluxDB - -进入 InfluxDB 数据源同步任务的编辑页面后: -1. 在服务器地址输入框, 输入 InfluxDB 服务器的地址,可以输入 IP 地址或域名,此项为必填字段; -2. 在端口输入框, 输入 InfluxDB 服务器端口,默认情况下,InfluxDB 监听8086端口的 HTTP 请求和8088端口的 HTTPS 请求,此项为必填字段; -3. 在组织 ID 输入框,输入将要同步的组织 ID,此项为必填字段; -4. 在令牌 Token 输入框,输入一个至少拥有读取这个组织 ID 下的指定 Bucket 权限的 Token, 此项为必填字段; -5. 在同步设置的起始时间项下,通过点选选择一个同步数据的起始时间,起始时间使用 UTC 时间, 此项为必填字段; -6. 在同步设置的结束时间项下,当不指定结束时间时,将持续进行最新数据的同步;当指定结束时间时,将只同步到这个结束时间为止; 结束时间使用 UTC 时间,此项为可选字段; -7. 在桶 Bucket 输入框,输入一个需要同步的 Bucket,目前只支持同步一个 Bucket 至 TDengine 数据库,此项为必填字段; -8. 在目标数据库下拉列表,选择一个将要写入的 TDengine 目标数据库 (注意:目前只支持同步到精度为纳秒的 TDengine 目标数据库),此项为必填字段; -9. 填写完成以上信息后,点击提交按钮,即可直接启动从 InfluxDB 到 TDengine 的数据同步。 - -## MQTT - -进入 MQTT 数据源同步任务的编辑页面后: -1. 在 MQTT 地址卡片,输入 MQTT 地址,必填字段,包括 IP 和 端口号,例如:192.168.1.10:1883; -2. 在认证卡片,输入 MQTT 连接器访问 MQTT 服务器时的用户名和密码,这两个字段为选填字段,如果未输入,即采用匿名认证的方式; -3. 在 SSL 证书卡片,可以选择是否打开 SSL/TLS 开关,如果打开此开关,MQTT 连接器和 MQTT 服务器之间的通信将采用 SSL/TLS 的方式进行加密;打开这个开关后,会出现 CA, 客户端证书和客户端私钥三个必填配置项,可以在这里输入证书和私钥文件的内容; -4. 在连接卡片,可以配置以下信息: - - MQTT 协议:支持3.1/3.1.1/5.0三个版本; - - Client ID: MQTT 连接器连接 MQTT 服务器时所使用的客户端 ID, 用于标识客户端的身份; - - Keep Alive: 用于配置 MQTT 连接器与 MQTT 服务器之间的Keep Alive时间,默认值为60秒; - - Clean Session: 用于配置 MQTT 连接器是否以Clean Session的方式连接至 MQTT 服务器,默认值为True; - - 订阅主题及 QoS 配置:这里用来配置监听的 MQTT 主题,以及该主题支持的最大QoS, 主题和 QoS 的配置之间用::分隔,多个主题之间用,分隔,主题的配置可以支持 MQTT 协议的通配符#和+; -5. 在其他卡片,可以配置 MQTT 连接器的日志级别,支持 error, warn, info, debug, trace 5个级别,默认值为 info; -6. MQTT Payload 解析卡片,用于配置如何解析 MQTT 消息: - - 配置表的第一行为 ts 字段,该字段为 TIMESTAMP 类型,它的值为 MQTT 连接器收到 MQTT 消息的时间; - - 配置表的第二行为 topic 字段,为该消息的主题名称,可以选择将该字段作为列或者标签同步至 TDengine; - - 配置表的第三行为 qos 字段,为该消息的 QoS 属性,可以选择将该字段作为列或者标签同步至 TDengine; - - 剩余的配置项皆为自定义字段,每个字段都需要配置:字段(来源),列(目标),列类型(目标)。字段(来源)是指该 MQTT 消息中的字段名称,当前仅支持 JSON 类型的 MQTT 消息同步,可以使用 JSON Path 语法从 MQTT 消息中提取字段,例如:$.data.id; 列(目标)是指同步至 TDengine 后的字段名称;列类型(目标)是指同步至 TDengine 后的字段类型,可以从下拉列表中选择;当且仅当以上3个配置都填写后,才能新增下一个字段; - - 如果 MQTT 消息中包含时间戳,可以选择新增一个自定义字段,将其作为同步至 TDengine 时的主键;需要注意的是,MQTT 消息中时间戳的仅支持 Unix Timestamp格式,且该字段的列类型(目标)的选择,需要与创建 TDengine 数据库时的配置一致; - - 子表命名规则:用于配置子表名称,采用“前缀+{列类型(目标)}”的格式,例如:d{id}; - - 超级表名:用于配置同步至 TDengine 时,采用的超级表名; -7. 在目标数据库卡片,可以选择同步至 TDengine 的数据库名称,支持直接从下拉列表中选择。 -8. 填写完成以上信息后,点击提交按钮,即可直接启动从 MQTT 到 TDengine 的数据同步。 - -## Kafka - -1. 在Kafka页面,配置Kafka选项,必填字段,包括:bootstrap_server,例如192.168.1.92:9092; -2. 如果使用SSL认证,在SSL认证卡中,选择cert和cert_key的文件路径; -3. 配置其他参数,topics、topic_partitions这2个参数至少填写一个,其他参数有默认值; -4. 如果消费的Kafka数据是JSON格式,可以配置parser卡片,对数据进行解析转换; -5. 在目标数据库卡片中,选择同步到TDengine的数据库名称,支持从下拉列表中选择; -6. 填写完以上信息后,点击提交按钮,即可启动从Kafka到TDengine的数据同步。 - -## CSV - -1. 在CSV页面,配置CSV选项,可设置忽略前N行,可输入具体的数字 -2. CSV的写入配置,设置批次写入量,默认是1000 -3. CSV文件解析,用于获取CSV对应的列信息: - - 上传CSV文件或者输入CSV文件的地址 - - 选择是否包包含Header - - 包含Header情况下直接执行下一步,查询出对应CSV的列信息,获取CSV的配置信息 - - 不包含Header情况,需要输入自定列信息,并以逗号分隔,然后下一步,获取CSV的配置信息 - - CSV的配置项,每个字段都需要配置:CSV列,DB列,列类型(目标),主键(整个配置只能有一个主键,且主键必须是TIMESTAMP类型),作为列,作为Tag。CSV列是指该 CSV文件中的列或者自定义的列;DB列是对应的数据表的列 - - 子表命名规则:用于配置子表名称,采用“前缀+{列类型(目标)}”的格式,例如:d{id}; - - 超级表名:用于配置同步至 TDengine 时,采用的超级表名; -4. 在目标数据库卡片,可以选择同步至 TDengine 的数据库名称,支持直接从下拉列表中选择。 -5. 填写完成以上信息后,点击提交按钮,即可直接启动从 CSV到 TDengine 的数据同步。 - - -## 备份和恢复 - -您可以将当前连接的 TDengine 集群中的数据备份至一个或多个本地文件中,稍后可以通过这些文件进行数据恢复。本章节将介绍数据备份和恢复的具体步骤。 - -### 备份数据到本地文件 - -1. 进入系统管理页面,点击【备份】进入数据备份页面,点击右上角【新增备份】。 -2. 在数据备份配置页面中可以配置三个参数: - - 备份周期:必填项,配置每次执行数据备份的时间间隔,可通过下拉框选择每天、每 7 天、每 30 天执行一次数据备份,配置后,会在对应的备份周期的0:00时启动一次数据备份任务; - - 数据库:必填项,配置需要备份的数据库名(数据库的 wal_retention_period 参数需大于0); - - 目录:必填项,配置将数据备份到 taosX 所在运行环境中指定的路径下,如 /root/data_backup; -3. 点击【确定】,可创建数据备份任务。 - -### 从本地文件恢复 - -1. 完成数据备份任务创建后,在页面中对应的数据备份任务右侧点击【数据恢复】,可将已经备份到指定路径下的数据恢复到当前 TDengine 中。 \ No newline at end of file diff --git a/docs/zh/18-data-transfer/index.md b/docs/zh/18-data-transfer/index.md deleted file mode 100644 index 749ad16308..0000000000 --- a/docs/zh/18-data-transfer/index.md +++ /dev/null @@ -1,3 +0,0 @@ ---- -title: 数据集成 ---- \ No newline at end of file From c6607931939b3fb4994ed2b13c1ece74e5981bef Mon Sep 17 00:00:00 2001 From: Wade Zhang Date: Fri, 25 Aug 2023 23:34:39 +0800 Subject: [PATCH 2/5] doc: remove grant from website --- docs/zh/12-taos-sql/25-grant.md | 239 -------------------------------- 1 file changed, 239 deletions(-) delete mode 100644 docs/zh/12-taos-sql/25-grant.md diff --git a/docs/zh/12-taos-sql/25-grant.md b/docs/zh/12-taos-sql/25-grant.md deleted file mode 100644 index 42d740539f..0000000000 --- a/docs/zh/12-taos-sql/25-grant.md +++ /dev/null @@ -1,239 +0,0 @@ ---- -sidebar_label: 权限管理 -title: 权限管理 -description: 企业版中才具有的权限管理功能 ---- - -本节讲述如何在 TDengine 中进行权限管理的相关操作。权限管理是 TDengine 企业版的特有功能,欲试用 TDengine 企业版请联系 TDengine 销售或市场团队。 - -TDengine 中的权限管理分为用户管理、数据库授权管理以及消息订阅授权管理。 - -当 TDengine 安装并部署成功后,系统中内置有 "root" 用户。持有默认 "root" 用户密码的系统管理员应该第一时间修改 root 用户的密码,并根据业务需要创建普通用户并为这些用户授予适当的权限。在未授权的情况下,普通用户可以创建 DATABASE,并拥有自己创建的 DATABASE 的所有权限,包括删除数据库、修改数据库、查询时序数据和写入时序数据。超级用户可以给普通用户授予其他(即非该用户所创建的) DATABASE 的读写权限,使其可以在这些 DATABASE 上读写数据,但不能对其进行删除和修改数据库的操作。超级用户或者 topic 的创建者也可以给其它用户授予对某个 topic 的订阅权限。 - -## 用户管理 - -用户管理涉及用户的整个生命周期,从创建用户、对用户进行授权、撤销对用户的授权、查看用户信息、直到删除用户。 - -### 创建用户 - -创建用户的操作只能由 root 用户进行,语法如下 - -```sql -CREATE USER user_name PASS 'password' [SYSINFO {1\|0}]; -``` - -说明: - -- user_name 最长为 23 字节。 -- password 最长为 128 字节,合法字符包括"a-zA-Z0-9!?\$%\^&\*()_–+={[}]:;@\~\#\|\<,\>.?/",不可以出现单双引号、撇号、反斜杠和空格,且不可以为空。 -- SYSINFO 表示用户是否可以查看系统信息。1 表示可以查看,0 表示不可以查看。系统信息包括服务端配置信息、服务端各种节点信息(如 DNODE、QNODE等)、存储相关的信息等。默认为可以查看系统信息。 - -示例:创建密码为123456且可以查看系统信息的用户 test - -``` -SQL taos\> create user test pass '123456' sysinfo 1; Query OK, 0 of 0 rows affected (0.001254s) -``` - -### 查看用户 - -查看系统中的用户信息请使用 show users 命令,示例如下 - -```sql -show users; -``` - -也可以通过查询系统表 `INFORMATION_SCHEMA.INS_USERS` 获取系统中的用户信息,示例如下 - -```sql -select * from information_schema.ins_users; -``` - -### 删除用户 - -删除用户请使用 - -```sql -DROP USER user_name; -``` - -### 修改用户信息 - -修改用户信息的命令如下 - -```sql -ALTER USER user_name alter_user_clause alter_user_clause: { PASS 'literal' \| ENABLE value \| SYSINFO value } -``` - -说明: - -- PASS:修改用户密码。 -- ENABLE:修改用户是否启用。1 表示启用此用户,0 表示禁用此用户。 -- SYSINFO:修改用户是否可查看系统信息。1 表示可以查看系统信息,0 表示不可以查看系统信息。 - -示例:禁用 test 用户 - -```sql -alter user test enable 0; Query OK, 0 of 0 rows affected (0.001160s) -``` - -```sql -CREATE USER use_name PASS 'password' [SYSINFO {1|0}]; -``` - -## 访问控制 - -在 TDengine 企业版中,系统管理员可以根据业务和数据安全的需要控制任意一个用户对每一个数据库、订阅甚至表级别的访问。 - -```sql -GRANT privileges ON priv_level TO user_name - -privileges : { - ALL - | priv_type [, priv_type] ... -} - -priv_type : { - READ - | WRITE -} - -priv_level : { - dbname.* - | *.* -} -``` - -### 数据库权限 - - -TDengine 有超级用户和普通用户两类用户。超级用户缺省创建为root,拥有所有权限。使用超级用户创建出来的用户为普通用户。在未授权的情况下,普通用户可以创建 DATABASE,并拥有自己创建的 DATABASE 的所有权限,包括删除数据库、修改数据库、查询时序数据和写入时序数据。超级用户可以给普通用户授予其他 DATABASE 的读写权限,使其可以在此 DATABASE 上读写数据,但不能对其进行删除和修改数据库的操作。 - -对于非DATABASE的对象,如USER、DNODE、UDF、QNODE等,普通用户只有读权限(一般为SHOW命令),不能创建和修改。 - -对数据库的访问权限包含读和写两种权限,它们可以被分别授予,也可以被同时授予。 - -补充说明 - -- priv_level 格式中 "." 之前为数据库名称, "." 之后为表名称 -- "dbname.\*" 意思是名为 "dbname" 的数据库中的所有表 -- "\*.\*" 意思是所有数据库名中的所有表 - -**下表中总结了数据库权限的各种组合** - -对 root 用户和普通用户的权限的说明如下表 - -| 用户 | 描述 | 权限说明 | -|----------|------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| 超级用户 | 只有 root 是超级用户 | DB 外部 所有操作权限,例如user、dnode、udf、qnode等的CRUD DB 权限,包括 创建 删除 更新,例如修改 Option,移动 Vgruop等 读 写 Enable/Disable 用户 | -| 普通用户 | 除 root 以外的其它用户均为普通用户 | 在可读的 DB 中,普通用户可以进行读操作 select describe show subscribe 在可写 DB 的内部,用户可以进行写操作: 创建、删除、修改 超级表 创建、删除、修改 子表 创建、删除、修改 topic 写入数据 被限制系统信息时,不可进行如下操作 show dnode、mnode、vgroups、qnode、snode 修改用户包括自身密码 show db时只能看到自己的db,并且不能看到vgroups、副本、cache等信息 无论是否被限制系统信息,都可以 管理 udf 可以创建 DB 自己创建的 DB 具备所有权限 非自己创建的 DB ,参照读、写列表中的权限 | - -### 消息订阅授权 - -任意用户都可以在自己拥有读权限的数据库上创建 topic。超级用户 root 可以在任意数据库上创建 topic。每个 topic 的订阅权限都可以被独立授权给任何用户,不管该用户是否拥有该数据库的访问权限。删除 topic 只能由 root 用户或者该 topic 的创建者进行。topic 只能由超级用户、topic的创建者或者被显式授予 subscribe 权限的用户订阅。 - -授予订阅权限的语法如下: - -```sql -GRANT privileges ON priv_level TO user_name privileges : { ALL | priv_type [, priv_type] ... } priv_type : { SUBSCRIBE } priv_level : { topic_name } -``` - -### 基于标签的授权(表级授权) - -从 TDengine 3.0.5.0 开始,我们支持按标签授权某个超级表中部分特定的子表。具体的 SQL 语法如下。 - -```sql -GRANT privileges ON priv_level [WITH tag_condition] TO user_name - -privileges : { - ALL - | SUBSCRIBE - | priv_type [, priv_type] ... -} - -priv_type : { - READ - | WRITE -} - -priv_level : { - dbname.tbname - | dbname.* - | *.* - | topic_name -} - -REVOKE privileges ON priv_level [WITH tag_condition] FROM user_name - -privileges : { - ALL - | priv_type [, priv_type] ... -} - -priv_type : { - READ - | WRITE -} - -priv_level : { - dbname.tbname - | dbname.* - | *.* -} -``` - -上面 SQL 的语义为: - -- 用户可以通过 dbname.tbname 来为指定的表(包括超级表和普通表)授予或回收其读写权限,不支持直接对子表授予或回收权限。 -- 用户可以通过 dbname.tbname 和 WITH 子句来为符合条件的所有子表授予或回收其读写权限。使用 WITH 子句时,权限级别必须为超级表。 - -**表级权限和数据库权限的关系** - -下表列出了在不同的数据库授权和表级授权的组合下产生的实际权限。 - -| |**表无授权** | **表读授权** | **表读授权有标签条件** | **表写授权** | **表写授权有标签条件** | -| -------------- | ---------------- | -------- | ---------- | ------ | ----------- | -| **数据库无授权** | 无授权 | 对此表有读权限,对数据库下的其他表无权限 | 对此表符合标签权限的子表有读权限,对数据库下的其他表无权限 | 对此表有写权限,对数据库下的其他表无权限 | 对此表符合标签权限的子表有写权限,对数据库下的其他表无权限 | -| **数据库读授权** | 对所有表有读权限 | 对所有表有读权限 | 对此表符合标签权限的子表有读权限,对数据库下的其他表有读权限 | 对此表有写权限,对所有表有读权限 | 对此表符合标签权限的子表有写权限,所有表有读权限 | -| **数据库写授权** | 对所有表有写权限 | 对此表有读权限,对所有表有写权限 | 对此表符合标签权限的子表有读权限,对所有表有写权限 | 对所有表有写权限 | 对此表符合标签权限的子表有写权限,数据库下的其他表有写权限 | - -### 查看用户授权 - -使用下面的命令可以显示一个用户所拥有的授权: - -```sql -show user privileges -``` -## 撤销授权 - -```sql -REVOKE privileges ON priv_level FROM user_name - -privileges : { - ALL - | priv_type [, priv_type] ... -} - -priv_type : { - READ - | WRITE -} - -priv_level : { - dbname.* - | *.* -} - -``` - -### 撤销授权 - -1. 撤销数据库访问的授权 - -```sql -REVOKE privileges ON priv_level FROM user_name privileges : { ALL \| priv_type [, priv_type] ... } priv_type : { READ \| WRITE } priv_level : { dbname.\* \| \*.\* } -``` - -2. 撤销数据订阅的授权 - -```sql -REVOKE privileges ON priv_level FROM user_name privileges : { ALL \| priv_type [, priv_type] ... } priv_type : { SUBSCRIBE } priv_level : { topi_name } From 1372cede489fc173ab2eab01c12ebc12ba13186d Mon Sep 17 00:00:00 2001 From: dmchen Date: Mon, 28 Aug 2023 17:44:10 +0800 Subject: [PATCH 3/5] init --- include/common/tmsg.h | 44 ------------------------ source/dnode/mnode/impl/src/mndDb.c | 35 +++++++++++++++++-- source/dnode/mnode/impl/src/mndDnode.c | 23 ++++++++----- source/dnode/mnode/impl/src/mndMnode.c | 10 ++---- source/dnode/mnode/impl/src/mndProfile.c | 11 +++--- source/dnode/mnode/impl/src/mndQnode.c | 2 +- source/dnode/mnode/impl/src/mndStb.c | 24 +++++++++++-- source/dnode/mnode/impl/src/mndStream.c | 19 ++++++++-- source/dnode/mnode/impl/src/mndTopic.c | 11 ++++-- source/dnode/mnode/impl/src/mndUser.c | 20 +++++++---- source/dnode/mnode/impl/src/mndVgroup.c | 8 +++-- source/dnode/vnode/src/vnd/vnodeSvr.c | 6 +++- 12 files changed, 127 insertions(+), 86 deletions(-) diff --git a/include/common/tmsg.h b/include/common/tmsg.h index 42a0549024..8deec53470 100644 --- a/include/common/tmsg.h +++ b/include/common/tmsg.h @@ -767,8 +767,6 @@ typedef struct { char* pAst2; int64_t deleteMark1; int64_t deleteMark2; - int32_t sqlLen; - char* sql; } SMCreateStbReq; int32_t tSerializeSMCreateStbReq(void* buf, int32_t bufLen, SMCreateStbReq* pReq); @@ -789,8 +787,6 @@ typedef struct { int8_t source; // 1-taosX or 0-taosClient int8_t reserved[6]; tb_uid_t suid; - int32_t sqlLen; - char* sql; } SMDropStbReq; int32_t tSerializeSMDropStbReq(void* buf, int32_t bufLen, SMDropStbReq* pReq); @@ -804,8 +800,6 @@ typedef struct { int32_t ttl; int32_t commentLen; char* comment; - int32_t sqlLen; - char* sql; } SMAlterStbReq; int32_t tSerializeSMAlterStbReq(void* buf, int32_t bufLen, SMAlterStbReq* pReq); @@ -875,8 +869,6 @@ int32_t tDeserializeSCreateAcctReq(void* buf, int32_t bufLen, SCreateAcctReq* pR typedef struct { char user[TSDB_USER_LEN]; - int32_t sqlLen; - char *sql; } SDropUserReq, SDropAcctReq; int32_t tSerializeSDropUserReq(void* buf, int32_t bufLen, SDropUserReq* pReq); @@ -889,8 +881,6 @@ typedef struct { int8_t enable; char user[TSDB_USER_LEN]; char pass[TSDB_USET_PASSWORD_LEN]; - int32_t sqlLen; - char* sql; } SCreateUserReq; int32_t tSerializeSCreateUserReq(void* buf, int32_t bufLen, SCreateUserReq* pReq); @@ -907,8 +897,6 @@ typedef struct { char tabName[TSDB_TABLE_NAME_LEN]; char* tagCond; int32_t tagCondLen; - int32_t sqlLen; - char* sql; } SAlterUserReq; int32_t tSerializeSAlterUserReq(void* buf, int32_t bufLen, SAlterUserReq* pReq); @@ -1071,8 +1059,6 @@ typedef struct { int16_t hashPrefix; int16_t hashSuffix; int32_t tsdbPageSize; - int32_t sqlLen; - char* sql; } SCreateDbReq; int32_t tSerializeSCreateDbReq(void* buf, int32_t bufLen, SCreateDbReq* pReq); @@ -1098,8 +1084,6 @@ typedef struct { int32_t minRows; int32_t walRetentionPeriod; int32_t walRetentionSize; - int32_t sqlLen; - char* sql; } SAlterDbReq; int32_t tSerializeSAlterDbReq(void* buf, int32_t bufLen, SAlterDbReq* pReq); @@ -1108,8 +1092,6 @@ int32_t tDeserializeSAlterDbReq(void* buf, int32_t bufLen, SAlterDbReq* pReq); typedef struct { char db[TSDB_DB_FNAME_LEN]; int8_t ignoreNotExists; - int32_t sqlLen; - char* sql; } SDropDbReq; int32_t tSerializeSDropDbReq(void* buf, int32_t bufLen, SDropDbReq* pReq); @@ -1307,8 +1289,6 @@ void tFreeSUserAuthBatchRsp(SUserAuthBatchRsp* pRsp); typedef struct { char db[TSDB_DB_FNAME_LEN]; STimeWindow timeRange; - int32_t sqlLen; - char* sql; } SCompactDbReq; int32_t tSerializeSCompactDbReq(void* buf, int32_t bufLen, SCompactDbReq* pReq); @@ -1872,8 +1852,6 @@ void tFreeSExplainRsp(SExplainRsp* pRsp); typedef struct { char fqdn[TSDB_FQDN_LEN]; // end point, hostname:port int32_t port; - int32_t sqlLen; - char* sql; } SCreateDnodeReq; int32_t tSerializeSCreateDnodeReq(void* buf, int32_t bufLen, SCreateDnodeReq* pReq); @@ -1885,8 +1863,6 @@ typedef struct { int32_t port; int8_t force; int8_t unsafe; - int32_t sqlLen; - char* sql; } SDropDnodeReq; int32_t tSerializeSDropDnodeReq(void* buf, int32_t bufLen, SDropDnodeReq* pReq); @@ -1902,8 +1878,6 @@ enum { typedef struct { int32_t dnodeId; int8_t restoreType; - int32_t sqlLen; - char* sql; } SRestoreDnodeReq; int32_t tSerializeSRestoreDnodeReq(void* buf, int32_t bufLen, SRestoreDnodeReq* pReq); @@ -1913,8 +1887,6 @@ typedef struct { int32_t dnodeId; char config[TSDB_DNODE_CONFIG_LEN]; char value[TSDB_DNODE_VALUE_LEN]; - int32_t sqlLen; - char* sql; } SMCfgDnodeReq; int32_t tSerializeSMCfgDnodeReq(void* buf, int32_t bufLen, SMCfgDnodeReq* pReq); @@ -1930,8 +1902,6 @@ int32_t tDeserializeSDCfgDnodeReq(void* buf, int32_t bufLen, SDCfgDnodeReq* pReq typedef struct { int32_t dnodeId; - int32_t sqlLen; - char *sql; } SMCreateMnodeReq, SMDropMnodeReq, SDDropMnodeReq, SMCreateQnodeReq, SMDropQnodeReq, SDCreateQnodeReq, SDDropQnodeReq, SMCreateSnodeReq, SMDropSnodeReq, SDCreateSnodeReq, SDDropSnodeReq; @@ -1972,8 +1942,6 @@ int32_t tDeserializeSKillTransReq(void* buf, int32_t bufLen, SKillTransReq* pReq typedef struct { int32_t useless; // useless - int32_t sqlLen; - char* sql; } SBalanceVgroupReq; int32_t tSerializeSBalanceVgroupReq(void* buf, int32_t bufLen, SBalanceVgroupReq* pReq); @@ -1992,8 +1960,6 @@ typedef struct { int32_t dnodeId1; int32_t dnodeId2; int32_t dnodeId3; - int32_t sqlLen; - char* sql; } SRedistributeVgroupReq; int32_t tSerializeSRedistributeVgroupReq(void* buf, int32_t bufLen, SRedistributeVgroupReq* pReq); @@ -2001,8 +1967,6 @@ int32_t tDeserializeSRedistributeVgroupReq(void* buf, int32_t bufLen, SRedistrib typedef struct { int32_t useless; - int32_t sqlLen; - char* sql; } SBalanceVgroupLeaderReq; int32_t tSerializeSBalanceVgroupLeaderReq(void* buf, int32_t bufLen, SBalanceVgroupLeaderReq* pReq); @@ -2262,7 +2226,6 @@ typedef struct { int64_t deleteMark; int8_t igUpdate; int64_t lastTs; - int32_t sqlLen; } SCMCreateStreamReq; typedef struct { @@ -2299,7 +2262,6 @@ typedef struct { char subDbName[TSDB_DB_FNAME_LEN]; char* ast; char subStbName[TSDB_TABLE_FNAME_LEN]; - int32_t sqlLen; } SCMCreateTopicReq; int32_t tSerializeSCMCreateTopicReq(void* buf, int32_t bufLen, const SCMCreateTopicReq* pReq); @@ -2484,8 +2446,6 @@ typedef struct { typedef struct { char name[TSDB_TOPIC_FNAME_LEN]; int8_t igNotExists; - int32_t sqlLen; - char* sql; } SMDropTopicReq; int32_t tSerializeSMDropTopicReq(void* buf, int32_t bufLen, SMDropTopicReq* pReq); @@ -2585,8 +2545,6 @@ typedef struct SVCreateTbReq { SSchemaWrapper schemaRow; } ntb; }; - int32_t sqlLen; - char* sql; } SVCreateTbReq; int tEncodeSVCreateTbReq(SEncoder* pCoder, const SVCreateTbReq* pReq); @@ -3061,8 +3019,6 @@ typedef struct { typedef struct { char name[TSDB_STREAM_FNAME_LEN]; int8_t igNotExists; - int32_t sqlLen; - char* sql; } SMDropStreamReq; typedef struct { diff --git a/source/dnode/mnode/impl/src/mndDb.c b/source/dnode/mnode/impl/src/mndDb.c index c58df5c88c..972705f7a8 100644 --- a/source/dnode/mnode/impl/src/mndDb.c +++ b/source/dnode/mnode/impl/src/mndDb.c @@ -736,7 +736,23 @@ static int32_t mndProcessCreateDbReq(SRpcMsg *pReq) { code = mndCreateDb(pMnode, pReq, &createReq, pUser); if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; - auditRecord(pReq, pMnode->clusterId, "createDB", createReq.db, "", ""); + char detail[3000] = {0}; + sprintf(detail, "buffer:%d, cacheLast:%d, cacheLastSize:%d, compression:%d, daysPerFile:%d, " + "daysToKeep0:%d, daysToKeep:%d, daysToKeep2:%d, hashPrefix:%d, " + "hashSuffix:%d, ignoreExist:%d, maxRows:%d, minRows:%d, numOfRetensions:%d, " + "numOfStables:%d, numOfVgroups:%d, pages:%d, pageSize:%d, precision:%d, " + "replications:%d, schemaless:%d, sstTrigger:%d, strict:%d, " + "tsdbPageSize:%d, walFsyncPeriod:%d, walLevel:%d, walRetentionPeriod:%d, " + "walRetentionSize:%" PRId64 ", walRollPeriod:%d, walSegmentSize:%" PRId64, + createReq.buffer, createReq.cacheLast, createReq.cacheLastSize, createReq.compression, createReq.daysPerFile, + createReq.daysToKeep0, createReq.daysToKeep1, createReq.daysToKeep2, createReq.hashPrefix, + createReq.hashSuffix, createReq.ignoreExist, createReq.maxRows, createReq.minRows, createReq.numOfRetensions, + createReq.numOfStables, createReq.numOfVgroups, createReq.pages, createReq.pageSize, createReq.precision, + createReq.replications, createReq.schemaless, createReq.sstTrigger, createReq.strict, + createReq.tsdbPageSize, createReq.walFsyncPeriod, createReq.walLevel, createReq.walRetentionPeriod, + createReq.walRetentionSize, createReq.walRollPeriod, createReq.walSegmentSize); + + auditRecord(pReq, pMnode->clusterId, "createDB", createReq.db, "", detail); _OVER: if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { @@ -980,7 +996,17 @@ static int32_t mndProcessAlterDbReq(SRpcMsg *pReq) { if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; } - auditRecord(pReq, pMnode->clusterId, "alterDB", alterReq.db, "", ""); + char detail[3000] = {0}; + sprintf(detail, "buffer:%d, cacheLast:%d, cacheLastSize:%d, daysPerFile:%d, daysToKeep0:%d, " + "daysToKeep1:%d, daysToKeep2:%d, db:%s, minRows:%d, pages:%d, pageSize:%d, " + "replications:%d, sstTrigger:%d, strict:%d, walFsyncPeriod:%d, " + "walRetentionSize:%d", + alterReq.buffer, alterReq.cacheLast, alterReq.cacheLastSize, alterReq.daysPerFile, alterReq.daysToKeep0, + alterReq.daysToKeep1, alterReq.daysToKeep2, alterReq.db, alterReq.minRows, alterReq.pages, alterReq.pageSize, + alterReq.replications, alterReq.sstTrigger, alterReq.strict, alterReq.walFsyncPeriod, + alterReq.walRetentionSize); + + auditRecord(pReq, pMnode->clusterId, "alterDB", alterReq.db, "", detail); _OVER: if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { @@ -1271,7 +1297,10 @@ static int32_t mndProcessDropDbReq(SRpcMsg *pReq) { code = TSDB_CODE_ACTION_IN_PROGRESS; } - auditRecord(pReq, pMnode->clusterId, "dropDB", dropReq.db, "", ""); + char detail[1000] = {0}; + sprintf(detail, "ignoreNotExists:%d", dropReq.ignoreNotExists); + + auditRecord(pReq, pMnode->clusterId, "dropDB", dropReq.db, "", detail); _OVER: if (code != TSDB_CODE_SUCCESS && code != TSDB_CODE_ACTION_IN_PROGRESS) { diff --git a/source/dnode/mnode/impl/src/mndDnode.c b/source/dnode/mnode/impl/src/mndDnode.c index 1bcbc4982b..949d41ef07 100644 --- a/source/dnode/mnode/impl/src/mndDnode.c +++ b/source/dnode/mnode/impl/src/mndDnode.c @@ -910,11 +910,10 @@ static int32_t mndProcessCreateDnodeReq(SRpcMsg *pReq) { if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; tsGrantHBInterval = 5; - char detail[1000] = {0}; - sprintf(detail, "%s:%d", - createReq.fqdn, createReq.port); + char obj[200] = {0}; + sprintf(obj, "%s:%d", createReq.fqdn, createReq.port); - auditRecord(pReq, pMnode->clusterId, "createDnode", detail, "", ""); + auditRecord(pReq, pMnode->clusterId, "createDnode", obj, "", ""); _OVER: if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { @@ -1066,10 +1065,13 @@ static int32_t mndProcessDropDnodeReq(SRpcMsg *pReq) { char obj1[150] = {0}; sprintf(obj1, "%s:%d", dropReq.fqdn, dropReq.port); - char obj2[10] = {0}; + char obj2[30] = {0}; sprintf(obj2, "%d", dropReq.dnodeId); - auditRecord(pReq, pMnode->clusterId, "dropDnode", obj1, obj2, ""); + char detail[100] = {0}; + sprintf(detail, "force:%d, unsafe:%d", dropReq.force, dropReq.unsafe); + + auditRecord(pReq, pMnode->clusterId, "dropDnode", obj1, obj2, detail); _OVER: if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { @@ -1252,10 +1254,13 @@ static int32_t mndProcessConfigDnodeReq(SRpcMsg *pReq) { } } - char detail[50] = {0}; - sprintf(detail, "%d", cfgReq.dnodeId); + char obj[50] = {0}; + sprintf(obj, "%d", cfgReq.dnodeId); - auditRecord(pReq, pMnode->clusterId, "alterDnode", detail, "", ""); + char detail[500] = {0}; + sprintf(detail, "config:%s, value:%s", cfgReq.config, cfgReq.value); + + auditRecord(pReq, pMnode->clusterId, "alterDnode", obj, "", detail); int32_t code = -1; SSdb *pSdb = pMnode->pSdb; diff --git a/source/dnode/mnode/impl/src/mndMnode.c b/source/dnode/mnode/impl/src/mndMnode.c index 8b9deb3988..5827a30b43 100644 --- a/source/dnode/mnode/impl/src/mndMnode.c +++ b/source/dnode/mnode/impl/src/mndMnode.c @@ -653,14 +653,10 @@ static int32_t mndProcessCreateMnodeReq(SRpcMsg *pReq) { code = mndCreateMnode(pMnode, pReq, pDnode, &createReq); if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; - char detail[1000] = {0}; - - char obj[20] = {0}; + char obj[40] = {0}; sprintf(obj, "%d", createReq.dnodeId); - sprintf(detail, "dnodeId:%d", createReq.dnodeId); - - auditRecord(pReq, pMnode->clusterId, "createMnode", obj, detail, ""); + auditRecord(pReq, pMnode->clusterId, "createMnode", obj, "", ""); _OVER: if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { @@ -798,7 +794,7 @@ static int32_t mndProcessDropMnodeReq(SRpcMsg *pReq) { code = mndDropMnode(pMnode, pReq, pObj); if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; - char obj[20] = {0}; + char obj[40] = {0}; sprintf(obj, "%d", dropReq.dnodeId); auditRecord(pReq, pMnode->clusterId, "dropMnode", obj, "", ""); diff --git a/source/dnode/mnode/impl/src/mndProfile.c b/source/dnode/mnode/impl/src/mndProfile.c index db1546e33f..9847024bee 100644 --- a/source/dnode/mnode/impl/src/mndProfile.c +++ b/source/dnode/mnode/impl/src/mndProfile.c @@ -309,15 +309,14 @@ _CONNECT: code = 0; - char detail[1000] = {0}; - - char obj[30] = {0}; + char obj[100] = {0}; sprintf(obj, "%s:%d", ip, pConn->port); - sprintf(detail, "user:%s, from:%s, connType%d", - connReq.user, obj, connReq.connType); + char detail[1000] = {0}; + sprintf(detail, "connType:%d, db:%s, pid:%d, startTime:%" PRId64 ", sVer:%s, app:%s", + connReq.connType, connReq.db, connReq.pid, connReq.startTime, connReq.sVer, connReq.app); - auditRecord(pReq, pMnode->clusterId, "login", connReq.app, obj, detail); + auditRecord(pReq, pMnode->clusterId, "login", connReq.user, obj, detail); _OVER: diff --git a/source/dnode/mnode/impl/src/mndQnode.c b/source/dnode/mnode/impl/src/mndQnode.c index 45efabe97d..767e06a8d4 100644 --- a/source/dnode/mnode/impl/src/mndQnode.c +++ b/source/dnode/mnode/impl/src/mndQnode.c @@ -423,7 +423,7 @@ static int32_t mndProcessDropQnodeReq(SRpcMsg *pReq) { char obj[33] = {0}; sprintf(obj, "%d", dropReq.dnodeId); - auditRecord(pReq, pMnode->clusterId, "createQnode", obj, "", ""); + auditRecord(pReq, pMnode->clusterId, "dropQnode", obj, "", ""); _OVER: if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { diff --git a/source/dnode/mnode/impl/src/mndStb.c b/source/dnode/mnode/impl/src/mndStb.c index aa3ee89fd3..a5fe818133 100644 --- a/source/dnode/mnode/impl/src/mndStb.c +++ b/source/dnode/mnode/impl/src/mndStb.c @@ -1174,7 +1174,17 @@ static int32_t mndProcessCreateStbReq(SRpcMsg *pReq) { } if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; - auditRecord(pReq, pMnode->clusterId, "createStb", pDb->name, createReq.name, ""); + char detail[2000] = {0}; + sprintf(detail, "colVer:%d, delay1:%" PRId64 ", delay2:%" PRId64 ", deleteMark1:%" PRId64 ", " + "deleteMark2:%" PRId64 ", igExists:%d, numOfColumns:%d, numOfFuncs:%d, numOfTags:%d, " + "source:%d, suid:%" PRId64 ", tagVer:%d, ttl:%d, " + "watermark1:%" PRId64 ", watermark2:%" PRId64, + createReq.colVer, createReq.delay1, createReq.delay2, createReq.deleteMark1, + createReq.deleteMark2, createReq.igExists, createReq.numOfColumns, createReq.numOfFuncs, createReq.numOfTags, + createReq.source, createReq.suid, createReq.tagVer, createReq.ttl, + createReq.watermark1, createReq.watermark2); + + auditRecord(pReq, pMnode->clusterId, "createStb", pDb->name, createReq.name, detail); _OVER: if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { @@ -2244,7 +2254,11 @@ static int32_t mndProcessAlterStbReq(SRpcMsg *pReq) { code = mndAlterStb(pMnode, pReq, &alterReq, pDb, pStb); if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; - auditRecord(pReq, pMnode->clusterId, "alterStb", pDb->name, alterReq.name, ""); + char detail[2000] = {0}; + sprintf(detail, "alterType:%d, numOfFields:%d, ttl:%d" , + alterReq.alterType, alterReq.numOfFields, alterReq.ttl); + + auditRecord(pReq, pMnode->clusterId, "alterStb", pDb->name, alterReq.name, detail); _OVER: if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { @@ -2507,7 +2521,11 @@ static int32_t mndProcessDropStbReq(SRpcMsg *pReq) { code = mndDropStb(pMnode, pReq, pDb, pStb); if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; - auditRecord(pReq, pMnode->clusterId, "dropStb", pDb->name, dropReq.name, ""); + char detail[2000] = {0}; + sprintf(detail, "igNotExists:%d, source:%d" , + dropReq.igNotExists, dropReq.source); + + auditRecord(pReq, pMnode->clusterId, "dropStb", pDb->name, dropReq.name, detail); _OVER: if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { diff --git a/source/dnode/mnode/impl/src/mndStream.c b/source/dnode/mnode/impl/src/mndStream.c index c553257094..03bb84b04d 100644 --- a/source/dnode/mnode/impl/src/mndStream.c +++ b/source/dnode/mnode/impl/src/mndStream.c @@ -829,7 +829,19 @@ static int32_t mndProcessCreateStreamReq(SRpcMsg *pReq) { code = TSDB_CODE_ACTION_IN_PROGRESS; - auditRecord(pReq, pMnode->clusterId, "createStream", createStreamReq.name, "", ""); + char detail[2000] = {0}; + sprintf(detail, "checkpointFreq:%" PRId64 ", createStb:%d, deleteMark:%" PRId64 ", " + "fillHistory:%d, igExists:%d, " + "igExpired:%d, igUpdate:%d, lastTs:%" PRId64 ", " + "maxDelay:%" PRId64 ", numOfTags:%d, sourceDB:%s, " + "targetStbFullName:%s, triggerType:%d, watermark:%" PRId64, + createStreamReq.checkpointFreq, createStreamReq.createStb, createStreamReq.deleteMark, + createStreamReq.fillHistory, createStreamReq.igExists, + createStreamReq.igExpired, createStreamReq.igUpdate, createStreamReq.lastTs, + createStreamReq.maxDelay, createStreamReq.numOfTags, createStreamReq.sourceDB, + createStreamReq.targetStbFullName, createStreamReq.triggerType, createStreamReq.watermark); + + auditRecord(pReq, pMnode->clusterId, "createStream", createStreamReq.name, "", detail); _OVER: if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { @@ -1076,7 +1088,10 @@ static int32_t mndProcessDropStreamReq(SRpcMsg *pReq) { return -1; } - auditRecord(pReq, pMnode->clusterId, "dropStream", dropReq.name, "", ""); + char detail[100] = {0}; + sprintf(detail, "igNotExists:%d", dropReq.igNotExists); + + auditRecord(pReq, pMnode->clusterId, "dropStream", dropReq.name, "", detail); sdbRelease(pMnode->pSdb, pStream); mndTransDrop(pTrans); diff --git a/source/dnode/mnode/impl/src/mndTopic.c b/source/dnode/mnode/impl/src/mndTopic.c index 831e67bea3..e1d964a8a3 100644 --- a/source/dnode/mnode/impl/src/mndTopic.c +++ b/source/dnode/mnode/impl/src/mndTopic.c @@ -622,7 +622,11 @@ static int32_t mndProcessCreateTopicReq(SRpcMsg *pReq) { code = TSDB_CODE_ACTION_IN_PROGRESS; } - auditRecord(pReq, pMnode->clusterId, "crateTopic", createTopicReq.name, createTopicReq.subDbName, createTopicReq.sql); + char detail[1000] = {0}; + sprintf(detail, "igExists:%d, subStbName:%s, subType:%d, withMeta:%d", + createTopicReq.igExists, createTopicReq.subStbName, createTopicReq.subType, createTopicReq.withMeta); + + auditRecord(pReq, pMnode->clusterId, "crateTopic", createTopicReq.name, createTopicReq.subDbName, detail); _OVER: if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { @@ -815,7 +819,10 @@ static int32_t mndProcessDropTopicReq(SRpcMsg *pReq) { return -1; } - auditRecord(pReq, pMnode->clusterId, "dropTopic", dropReq.name, "", dropReq.sql); + char detail[100] = {0}; + sprintf(detail, "igNotExists:%d", dropReq.igNotExists); + + auditRecord(pReq, pMnode->clusterId, "dropTopic", dropReq.name, "", detail); return TSDB_CODE_ACTION_IN_PROGRESS; } diff --git a/source/dnode/mnode/impl/src/mndUser.c b/source/dnode/mnode/impl/src/mndUser.c index 8afc73bef6..098f260bb6 100644 --- a/source/dnode/mnode/impl/src/mndUser.c +++ b/source/dnode/mnode/impl/src/mndUser.c @@ -656,7 +656,11 @@ static int32_t mndProcessCreateUserReq(SRpcMsg *pReq) { code = mndCreateUser(pMnode, pOperUser->acct, &createReq, pReq); if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; - auditRecord(pReq, pMnode->clusterId, "createUser", createReq.user, "", ""); + char detail[1000] = {0}; + sprintf(detail, "createType:%d, enable:%d, superUser:%d, sysInfo:%d", + createReq.createType, createReq.enable, createReq.superUser, createReq.sysInfo); + + auditRecord(pReq, pMnode->clusterId, "createUser", createReq.user, "", detail); _OVER: if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { @@ -973,13 +977,17 @@ static int32_t mndProcessAlterUserReq(SRpcMsg *pReq) { code = mndAlterUser(pMnode, pUser, &newUser, pReq); if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; + char detail[1000] = {0}; + sprintf(detail, "alterType:%d, enable:%d, superUser:%d, sysInfo:%d, tabName:%s", + alterReq.alterType, alterReq.enable, alterReq.superUser, alterReq.sysInfo, alterReq.tabName); + if(alterReq.alterType == TSDB_ALTER_USER_PASSWD){ - auditRecord(pReq, pMnode->clusterId, "changePassword", alterReq.user, alterReq.objname, ""); + auditRecord(pReq, pMnode->clusterId, "changePassword", alterReq.user, alterReq.objname, detail); } else if(alterReq.alterType == TSDB_ALTER_USER_SUPERUSER || alterReq.alterType == TSDB_ALTER_USER_ENABLE || alterReq.alterType == TSDB_ALTER_USER_SYSINFO){ - auditRecord(pReq, pMnode->clusterId, "alterUser", alterReq.user, alterReq.objname, ""); + auditRecord(pReq, pMnode->clusterId, "alterUser", alterReq.user, alterReq.objname, detail); } else if(alterReq.alterType == TSDB_ALTER_USER_ADD_READ_DB|| alterReq.alterType == TSDB_ALTER_USER_ADD_WRITE_DB|| @@ -988,10 +996,10 @@ static int32_t mndProcessAlterUserReq(SRpcMsg *pReq) { alterReq.alterType == TSDB_ALTER_USER_ADD_READ_TABLE|| alterReq.alterType == TSDB_ALTER_USER_ADD_WRITE_TABLE|| alterReq.alterType == TSDB_ALTER_USER_ADD_ALL_TABLE){ - auditRecord(pReq, pMnode->clusterId, "GrantPrivileges", alterReq.user, alterReq.objname, ""); + auditRecord(pReq, pMnode->clusterId, "GrantPrivileges", alterReq.user, alterReq.objname, detail); } else{ - auditRecord(pReq, pMnode->clusterId, "RevokePrivileges", alterReq.user, alterReq.objname, ""); + auditRecord(pReq, pMnode->clusterId, "RevokePrivileges", alterReq.user, alterReq.objname, detail); } _OVER: @@ -1063,7 +1071,7 @@ static int32_t mndProcessDropUserReq(SRpcMsg *pReq) { code = mndDropUser(pMnode, pReq, pUser); if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; - auditRecord(pReq, pMnode->clusterId, "dropUser", dropReq.user, "", dropReq.sql); + auditRecord(pReq, pMnode->clusterId, "dropUser", dropReq.user, "", ""); _OVER: if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { diff --git a/source/dnode/mnode/impl/src/mndVgroup.c b/source/dnode/mnode/impl/src/mndVgroup.c index ff621198ff..b16ec8c2cb 100644 --- a/source/dnode/mnode/impl/src/mndVgroup.c +++ b/source/dnode/mnode/impl/src/mndVgroup.c @@ -2175,7 +2175,11 @@ static int32_t mndProcessRedistributeVgroupMsg(SRpcMsg *pReq) { char obj[33] = {0}; sprintf(obj, "%d", req.vgId); - auditRecord(pReq, pMnode->clusterId, "RedistributeVgroup", obj, "", req.sql); + char detail[1000] = {0}; + sprintf(detail, "dnodeId1:%d, dnodeId2:%d, dnodeId3:%d", + req.dnodeId1, req.dnodeId2, req.dnodeId3); + + auditRecord(pReq, pMnode->clusterId, "RedistributeVgroup", obj, "", detail); _OVER: if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { @@ -2987,7 +2991,7 @@ static int32_t mndProcessBalanceVgroupMsg(SRpcMsg *pReq) { code = mndBalanceVgroup(pMnode, pReq, pArray); } - auditRecord(pReq, pMnode->clusterId, "balanceVgroup", "", "", req.sql); + auditRecord(pReq, pMnode->clusterId, "balanceVgroup", "", "", ""); _OVER: if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { diff --git a/source/dnode/vnode/src/vnd/vnodeSvr.c b/source/dnode/vnode/src/vnd/vnodeSvr.c index 3687756ffc..ccdde8ade4 100644 --- a/source/dnode/vnode/src/vnd/vnodeSvr.c +++ b/source/dnode/vnode/src/vnd/vnodeSvr.c @@ -934,7 +934,11 @@ static int32_t vnodeProcessCreateTbReq(SVnode *pVnode, int64_t ver, void *pReq, int32_t clusterId = pVnode->config.syncCfg.nodeInfo[0].clusterId; - auditRecord(pReq, clusterId, "createTable", pVnode->config.dbname, pCreateReq->name, ""); + char detail[1000] = {0}; + sprintf(detail, "btime:%" PRId64 ", flags:%d, ttl:%d, type:%d", + pCreateReq->btime, pCreateReq->flags, pCreateReq->ttl, pCreateReq->type); + + auditRecord(pReq, clusterId, "createTable", pVnode->config.dbname, pCreateReq->name, detail); } vDebug("vgId:%d, add %d new created tables into query table list", TD_VID(pVnode), (int32_t)taosArrayGetSize(tbUids)); From 023cf096b3888aace8f81ff58e91f97b6f48cec3 Mon Sep 17 00:00:00 2001 From: yihaoDeng Date: Tue, 29 Aug 2023 10:25:44 +0800 Subject: [PATCH 4/5] handle taosd quit --- source/libs/transport/src/transSvr.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/source/libs/transport/src/transSvr.c b/source/libs/transport/src/transSvr.c index c6c412022a..40610d7651 100644 --- a/source/libs/transport/src/transSvr.c +++ b/source/libs/transport/src/transSvr.c @@ -289,8 +289,15 @@ static bool uvHandleReq(SSvrConn* pConn) { } void uvOnRecvCb(uv_stream_t* cli, ssize_t nread, const uv_buf_t* buf) { - SSvrConn* conn = cli->data; - STrans* pTransInst = conn->pTransInst; + SSvrConn* conn = cli->data; + SWorkThrd* pThrd = conn->hostThrd; + + if (true == pThrd->quit) { + tInfo("work thread received quit msg, destroy conn"); + destroyConn(conn, true); + return; + } + STrans* pTransInst = conn->pTransInst; SConnBuffer* pBuf = &conn->readBuf; if (nread > 0) { From df2b2484df89563abcb7a828a2421e59d73c76a1 Mon Sep 17 00:00:00 2001 From: Shungang Li Date: Tue, 29 Aug 2023 17:15:40 +0800 Subject: [PATCH 5/5] fix: ttl cache entry record the original information for deletion --- source/dnode/vnode/src/inc/metaTtl.h | 4 +- source/dnode/vnode/src/meta/metaTtl.c | 53 +++++++++++++++++---------- 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/source/dnode/vnode/src/inc/metaTtl.h b/source/dnode/vnode/src/inc/metaTtl.h index c2cd389dab..ead2d89b28 100644 --- a/source/dnode/vnode/src/inc/metaTtl.h +++ b/source/dnode/vnode/src/inc/metaTtl.h @@ -26,7 +26,7 @@ extern "C" { #endif typedef enum DirtyEntryType { - ENTRY_TYPE_DEL = 1, + ENTRY_TYPE_DELETE = 1, ENTRY_TYPE_UPSERT = 2, } DirtyEntryType; @@ -44,6 +44,8 @@ typedef struct STtlManger { typedef struct { int64_t ttlDays; int64_t changeTimeMs; + int64_t ttlDaysDirty; + int64_t changeTimeMsDirty; } STtlCacheEntry; typedef struct { diff --git a/source/dnode/vnode/src/meta/metaTtl.c b/source/dnode/vnode/src/meta/metaTtl.c index 3c45982311..f920296b4a 100644 --- a/source/dnode/vnode/src/meta/metaTtl.c +++ b/source/dnode/vnode/src/meta/metaTtl.c @@ -209,7 +209,8 @@ static int32_t ttlMgrFillCacheOneEntry(const void *pKey, int keyLen, const void int64_t ttlDays = *(int64_t *)pVal; int64_t changeTimeMs = ttlKey->deleteTimeMs - ttlDays * tsTtlUnit * 1000; - STtlCacheEntry data = {.ttlDays = ttlDays, .changeTimeMs = changeTimeMs}; + STtlCacheEntry data = { + .ttlDays = ttlDays, .changeTimeMs = changeTimeMs, .ttlDaysDirty = ttlDays, .changeTimeMsDirty = changeTimeMs}; return taosHashPut(pCache, &uid, sizeof(uid), &data, sizeof(data)); } @@ -257,34 +258,37 @@ static int32_t ttlMgrFindExpiredOneEntry(const void *pKey, int keyLen, const voi static int ttlMgrConvert(TTB *pOldTtlIdx, TTB *pNewTtlIdx, void *pMeta) { SMeta *meta = pMeta; - metaInfo("ttlMgr convert ttl start."); + metaInfo("ttlMgr convert start."); SConvertData cvData = {.pNewTtlIdx = pNewTtlIdx, .pMeta = meta}; int ret = tdbTbTraversal(pOldTtlIdx, &cvData, ttlMgrConvertOneEntry); if (ret < 0) { - metaError("failed to convert ttl since %s", tstrerror(terrno)); + metaError("failed to convert since %s", tstrerror(terrno)); } - metaInfo("ttlMgr convert ttl end."); + metaInfo("ttlMgr convert end."); return ret; } int ttlMgrInsertTtl(STtlManger *pTtlMgr, const STtlUpdTtlCtx *updCtx) { if (updCtx->ttlDays == 0) return 0; - STtlCacheEntry cacheEntry = {.ttlDays = updCtx->ttlDays, .changeTimeMs = updCtx->changeTimeMs}; + STtlCacheEntry cacheEntry = {.ttlDays = updCtx->ttlDays, + .changeTimeMs = updCtx->changeTimeMs, + .ttlDaysDirty = updCtx->ttlDays, + .changeTimeMsDirty = updCtx->changeTimeMs}; STtlDirtyEntry dirtryEntry = {.type = ENTRY_TYPE_UPSERT}; int ret = taosHashPut(pTtlMgr->pTtlCache, &updCtx->uid, sizeof(updCtx->uid), &cacheEntry, sizeof(cacheEntry)); if (ret < 0) { - metaError("%s, ttlMgr insert failed to update ttl cache since %s", pTtlMgr->logPrefix, tstrerror(terrno)); + metaError("%s, ttlMgr insert failed to update cache since %s", pTtlMgr->logPrefix, tstrerror(terrno)); goto _out; } ret = taosHashPut(pTtlMgr->pDirtyUids, &updCtx->uid, sizeof(updCtx->uid), &dirtryEntry, sizeof(dirtryEntry)); if (ret < 0) { - metaError("%s, ttlMgr insert failed to update ttl dirty uids since %s", pTtlMgr->logPrefix, tstrerror(terrno)); + metaError("%s, ttlMgr insert failed to update dirty uids since %s", pTtlMgr->logPrefix, tstrerror(terrno)); goto _out; } @@ -304,11 +308,11 @@ _out: int ttlMgrDeleteTtl(STtlManger *pTtlMgr, const STtlDelTtlCtx *delCtx) { if (delCtx->ttlDays == 0) return 0; - STtlDirtyEntry dirtryEntry = {.type = ENTRY_TYPE_DEL}; + STtlDirtyEntry dirtryEntry = {.type = ENTRY_TYPE_DELETE}; int ret = taosHashPut(pTtlMgr->pDirtyUids, &delCtx->uid, sizeof(delCtx->uid), &dirtryEntry, sizeof(dirtryEntry)); if (ret < 0) { - metaError("%s, ttlMgr del failed to update ttl dirty uids since %s", pTtlMgr->logPrefix, tstrerror(terrno)); + metaError("%s, ttlMgr del failed to update dirty uids since %s", pTtlMgr->logPrefix, tstrerror(terrno)); goto _out; } @@ -332,19 +336,22 @@ int ttlMgrUpdateChangeTime(STtlManger *pTtlMgr, const STtlUpdCtimeCtx *pUpdCtime goto _out; } - STtlCacheEntry cacheEntry = {.ttlDays = oldData->ttlDays, .changeTimeMs = pUpdCtimeCtx->changeTimeMs}; + STtlCacheEntry cacheEntry = {.ttlDays = oldData->ttlDays, + .changeTimeMs = oldData->changeTimeMs, + .ttlDaysDirty = oldData->ttlDays, + .changeTimeMsDirty = pUpdCtimeCtx->changeTimeMs}; STtlDirtyEntry dirtryEntry = {.type = ENTRY_TYPE_UPSERT}; ret = taosHashPut(pTtlMgr->pTtlCache, &pUpdCtimeCtx->uid, sizeof(pUpdCtimeCtx->uid), &cacheEntry, sizeof(cacheEntry)); if (ret < 0) { - metaError("%s, ttlMgr update ctime failed to update ttl cache since %s", pTtlMgr->logPrefix, tstrerror(terrno)); + metaError("%s, ttlMgr update ctime failed to update cache since %s", pTtlMgr->logPrefix, tstrerror(terrno)); goto _out; } ret = taosHashPut(pTtlMgr->pDirtyUids, &pUpdCtimeCtx->uid, sizeof(pUpdCtimeCtx->uid), &dirtryEntry, sizeof(dirtryEntry)); if (ret < 0) { - metaError("%s, ttlMgr update ctime failed to update ttl dirty uids since %s", pTtlMgr->logPrefix, + metaError("%s, ttlMgr update ctime failed to update dirty uids since %s", pTtlMgr->logPrefix, tstrerror(terrno)); goto _out; } @@ -396,27 +403,35 @@ int ttlMgrFlush(STtlManger *pTtlMgr, TXN *pTxn) { STtlIdxKeyV1 ttlKey; ttlMgrBuildKey(&ttlKey, cacheEntry->ttlDays, cacheEntry->changeTimeMs, *pUid); + STtlIdxKeyV1 ttlKeyDirty; + ttlMgrBuildKey(&ttlKeyDirty, cacheEntry->ttlDaysDirty, cacheEntry->changeTimeMsDirty, *pUid); + if (pEntry->type == ENTRY_TYPE_UPSERT) { - ret = tdbTbUpsert(pTtlMgr->pTtlIdx, &ttlKey, sizeof(ttlKey), &cacheEntry->ttlDays, sizeof(cacheEntry->ttlDays), - pTxn); + // delete old key & upsert new key + tdbTbDelete(pTtlMgr->pTtlIdx, &ttlKey, sizeof(ttlKey), pTxn); // maybe first insert, ignore error + ret = tdbTbUpsert(pTtlMgr->pTtlIdx, &ttlKeyDirty, sizeof(ttlKeyDirty), &cacheEntry->ttlDaysDirty, + sizeof(cacheEntry->ttlDaysDirty), pTxn); if (ret < 0) { - metaError("%s, ttlMgr flush failed to flush ttl cache upsert since %s", pTtlMgr->logPrefix, tstrerror(terrno)); + metaError("%s, ttlMgr flush failed to upsert since %s", pTtlMgr->logPrefix, tstrerror(terrno)); goto _out; } - } else if (pEntry->type == ENTRY_TYPE_DEL) { + + cacheEntry->ttlDays = cacheEntry->ttlDaysDirty; + cacheEntry->changeTimeMs = cacheEntry->changeTimeMsDirty; + } else if (pEntry->type == ENTRY_TYPE_DELETE) { ret = tdbTbDelete(pTtlMgr->pTtlIdx, &ttlKey, sizeof(ttlKey), pTxn); if (ret < 0) { - metaError("%s, ttlMgr flush failed to flush ttl cache del since %s", pTtlMgr->logPrefix, tstrerror(terrno)); + metaError("%s, ttlMgr flush failed to delete since %s", pTtlMgr->logPrefix, tstrerror(terrno)); goto _out; } ret = taosHashRemove(pTtlMgr->pTtlCache, pUid, sizeof(*pUid)); if (ret < 0) { - metaError("%s, ttlMgr flush failed to delete ttl cache since %s", pTtlMgr->logPrefix, tstrerror(terrno)); + metaError("%s, ttlMgr flush failed to remove cache since %s", pTtlMgr->logPrefix, tstrerror(terrno)); goto _out; } } else { - metaError("%s, ttlMgr flush failed to flush ttl cache, unknown type: %d", pTtlMgr->logPrefix, pEntry->type); + metaError("%s, ttlMgr flush failed, unknown type: %d", pTtlMgr->logPrefix, pEntry->type); goto _out; }