diff --git a/Jenkinsfile2 b/Jenkinsfile2 index 7a6f79339901c8a9e61aa3961016378dc38c721f..14c03068d7a32745bb269d07d7903da12253694b 100644 --- a/Jenkinsfile2 +++ b/Jenkinsfile2 @@ -121,7 +121,7 @@ def pre_test_win(){ set date /t time /t - rd /s /Q C:\\workspace\\%EXECUTOR_NUMBER%\\TDinternal\\debug + rd /s /Q C:\\workspace\\%EXECUTOR_NUMBER%\\TDinternal\\debug || exit 0 ''' bat ''' cd C:\\workspace\\%EXECUTOR_NUMBER%\\TDinternal diff --git a/cmake/cmake.define b/cmake/cmake.define index 4e27ff5f479ab89cd683d4c3e581c238487ef926..0ae4f56f71db237ca08a7a0a10bcbfe99e58b2d6 100644 --- a/cmake/cmake.define +++ b/cmake/cmake.define @@ -46,11 +46,17 @@ ENDIF () IF (TD_WINDOWS) MESSAGE("${Yellow} set compiler flag for Windows! ${ColourReset}") - SET(COMMON_FLAGS "/W3 /D_WIN32") + SET(COMMON_FLAGS "/w /D_WIN32 /Zi") SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /MANIFEST:NO") # IF (MSVC AND (MSVC_VERSION GREATER_EQUAL 1900)) # SET(COMMON_FLAGS "${COMMON_FLAGS} /Wv:18") # ENDIF () + IF (CMAKE_DEPFILE_FLAGS_C) + SET(CMAKE_DEPFILE_FLAGS_C "") + ENDIF () + IF (CMAKE_DEPFILE_FLAGS_CXX) + SET(CMAKE_DEPFILE_FLAGS_CXX "") + ENDIF () SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COMMON_FLAGS}") SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMMON_FLAGS}") @@ -65,8 +71,8 @@ ELSE () ENDIF () IF (${SANITIZER} MATCHES "true") - SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror -Werror=return-type -fPIC -gdwarf-2 -fsanitize=address -fsanitize=undefined -fno-sanitize-recover=all -fsanitize=float-divide-by-zero -fsanitize=float-cast-overflow -fno-sanitize=null -fno-sanitize=alignment -static-libasan -g3") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wno-literal-suffix -Werror=return-type -fPIC -gdwarf-2 -fsanitize=address -fsanitize=undefined -fno-sanitize-recover=all -fsanitize=float-divide-by-zero -fsanitize=float-cast-overflow -fno-sanitize=null -fno-sanitize=alignment -static-libasan -g3") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror -Werror=return-type -fPIC -gdwarf-2 -fsanitize=address -fsanitize=undefined -fsanitize-recover=all -fsanitize=float-divide-by-zero -fsanitize=float-cast-overflow -fno-sanitize=null -fno-sanitize=alignment -g3") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wno-literal-suffix -Werror=return-type -fPIC -gdwarf-2 -fsanitize=address -fsanitize=undefined -fsanitize-recover=all -fsanitize=float-divide-by-zero -fsanitize=float-cast-overflow -fno-sanitize=null -fno-sanitize=alignment -g3") MESSAGE(STATUS "Will compile with Address Sanitizer!") ELSE () SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror -Werror=return-type -fPIC -gdwarf-2 -g3") diff --git a/cmake/cmake.options b/cmake/cmake.options index d83ab49fd5fa6e987fb8a3a7e82c770c2387fd78..cb6fd1400d43b6073d81ab43e46140343b277512 100644 --- a/cmake/cmake.options +++ b/cmake/cmake.options @@ -46,6 +46,18 @@ IF(${TD_WINDOWS}) ON ) + option( + BUILD_TEST + "If build unit tests using googletest" + OFF + ) +ELSE () + + option( + BUILD_TEST + "If build unit tests using googletest" + ON + ) ENDIF () option( @@ -54,12 +66,6 @@ option( OFF ) -option( - BUILD_TEST - "If build unit tests using googletest" - ON -) - option( BUILD_WITH_LEVELDB "If build with leveldb" @@ -140,5 +146,6 @@ option( option( BUILD_WITH_INVERTEDINDEX "If use invertedIndex" - ON + OFF ) + diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt index aba955ff3ba68fe5bef617b295330417509b9c9f..31b9936f3e19fa6c3b943f92b9b0664ac7f3897b 100644 --- a/contrib/CMakeLists.txt +++ b/contrib/CMakeLists.txt @@ -100,8 +100,10 @@ endif(${BUILD_WITH_NURAFT}) # addr2line if(${BUILD_ADDR2LINE}) - cat("${TD_SUPPORT_DIR}/libdwarf_CMakeLists.txt.in" ${CONTRIB_TMP_FILE}) - cat("${TD_SUPPORT_DIR}/addr2line_CMakeLists.txt.in" ${CONTRIB_TMP_FILE}) + if(NOT ${TD_WINDOWS}) + cat("${TD_SUPPORT_DIR}/libdwarf_CMakeLists.txt.in" ${CONTRIB_TMP_FILE}) + cat("${TD_SUPPORT_DIR}/addr2line_CMakeLists.txt.in" ${CONTRIB_TMP_FILE}) + endif(NOT ${TD_WINDOWS}) endif(${BUILD_ADDR2LINE}) # download dependencies @@ -335,45 +337,47 @@ endif(${BUILD_WITH_SQLITE}) # addr2line if(${BUILD_ADDR2LINE}) - check_include_file( "sys/types.h" HAVE_SYS_TYPES_H) - check_include_file( "sys/stat.h" HAVE_SYS_STAT_H ) - check_include_file( "inttypes.h" HAVE_INTTYPES_H ) - check_include_file( "stddef.h" HAVE_STDDEF_H ) - check_include_file( "stdlib.h" HAVE_STDLIB_H ) - check_include_file( "string.h" HAVE_STRING_H ) - check_include_file( "memory.h" HAVE_MEMORY_H ) - check_include_file( "strings.h" HAVE_STRINGS_H ) - check_include_file( "stdint.h" HAVE_STDINT_H ) - check_include_file( "unistd.h" HAVE_UNISTD_H ) - check_include_file( "sgidefs.h" HAVE_SGIDEFS_H ) - check_include_file( "stdafx.h" HAVE_STDAFX_H ) - check_include_file( "elf.h" HAVE_ELF_H ) - check_include_file( "libelf.h" HAVE_LIBELF_H ) - check_include_file( "libelf/libelf.h" HAVE_LIBELF_LIBELF_H) - check_include_file( "alloca.h" HAVE_ALLOCA_H ) - check_include_file( "elfaccess.h" HAVE_ELFACCESS_H) - check_include_file( "sys/elf_386.h" HAVE_SYS_ELF_386_H ) - check_include_file( "sys/elf_amd64.h" HAVE_SYS_ELF_AMD64_H) - check_include_file( "sys/elf_sparc.h" HAVE_SYS_ELF_SPARC_H) - check_include_file( "sys/ia64/elf.h" HAVE_SYS_IA64_ELF_H ) - set(VERSION 0.3.1) - set(PACKAGE_VERSION "\"${VERSION}\"") - configure_file(libdwarf/cmake/config.h.cmake config.h) - file(GLOB_RECURSE LIBDWARF_SOURCES "libdwarf/src/lib/libdwarf/*.c") - add_library(libdwarf STATIC ${LIBDWARF_SOURCES}) - set_target_properties(libdwarf PROPERTIES OUTPUT_NAME "libdwarf") - if(HAVE_LIBELF_H OR HAVE_LIBELF_LIBELF_H) - target_link_libraries(libdwarf PUBLIC libelf) - endif() - target_include_directories(libdwarf SYSTEM PUBLIC "libdwarf/src/lib/libdwarf" ${CMAKE_CURRENT_BINARY_DIR}) - file(READ "addr2line/addr2line.c" ADDR2LINE_CONTENT) - string(REPLACE "static int" "int" ADDR2LINE_CONTENT "${ADDR2LINE_CONTENT}") - string(REPLACE "static void" "void" ADDR2LINE_CONTENT "${ADDR2LINE_CONTENT}") - string(REPLACE "main(" "main_addr2line(" ADDR2LINE_CONTENT "${ADDR2LINE_CONTENT}") - file(WRITE "addr2line/addr2line.c" "${ADDR2LINE_CONTENT}") - add_library(addr2line STATIC "addr2line/addr2line.c") - target_link_libraries(addr2line PUBLIC libdwarf dl z) - target_include_directories(addr2line PUBLIC "libdwarf/src/lib/libdwarf" ) + if(NOT ${TD_WINDOWS}) + check_include_file( "sys/types.h" HAVE_SYS_TYPES_H) + check_include_file( "sys/stat.h" HAVE_SYS_STAT_H ) + check_include_file( "inttypes.h" HAVE_INTTYPES_H ) + check_include_file( "stddef.h" HAVE_STDDEF_H ) + check_include_file( "stdlib.h" HAVE_STDLIB_H ) + check_include_file( "string.h" HAVE_STRING_H ) + check_include_file( "memory.h" HAVE_MEMORY_H ) + check_include_file( "strings.h" HAVE_STRINGS_H ) + check_include_file( "stdint.h" HAVE_STDINT_H ) + check_include_file( "unistd.h" HAVE_UNISTD_H ) + check_include_file( "sgidefs.h" HAVE_SGIDEFS_H ) + check_include_file( "stdafx.h" HAVE_STDAFX_H ) + check_include_file( "elf.h" HAVE_ELF_H ) + check_include_file( "libelf.h" HAVE_LIBELF_H ) + check_include_file( "libelf/libelf.h" HAVE_LIBELF_LIBELF_H) + check_include_file( "alloca.h" HAVE_ALLOCA_H ) + check_include_file( "elfaccess.h" HAVE_ELFACCESS_H) + check_include_file( "sys/elf_386.h" HAVE_SYS_ELF_386_H ) + check_include_file( "sys/elf_amd64.h" HAVE_SYS_ELF_AMD64_H) + check_include_file( "sys/elf_sparc.h" HAVE_SYS_ELF_SPARC_H) + check_include_file( "sys/ia64/elf.h" HAVE_SYS_IA64_ELF_H ) + set(VERSION 0.3.1) + set(PACKAGE_VERSION "\"${VERSION}\"") + configure_file(libdwarf/cmake/config.h.cmake config.h) + file(GLOB_RECURSE LIBDWARF_SOURCES "libdwarf/src/lib/libdwarf/*.c") + add_library(libdwarf STATIC ${LIBDWARF_SOURCES}) + set_target_properties(libdwarf PROPERTIES OUTPUT_NAME "libdwarf") + if(HAVE_LIBELF_H OR HAVE_LIBELF_LIBELF_H) + target_link_libraries(libdwarf PUBLIC libelf) + endif() + target_include_directories(libdwarf SYSTEM PUBLIC "libdwarf/src/lib/libdwarf" ${CMAKE_CURRENT_BINARY_DIR}) + file(READ "addr2line/addr2line.c" ADDR2LINE_CONTENT) + string(REPLACE "static int" "int" ADDR2LINE_CONTENT "${ADDR2LINE_CONTENT}") + string(REPLACE "static void" "void" ADDR2LINE_CONTENT "${ADDR2LINE_CONTENT}") + string(REPLACE "main(" "main_addr2line(" ADDR2LINE_CONTENT "${ADDR2LINE_CONTENT}") + file(WRITE "addr2line/addr2line.c" "${ADDR2LINE_CONTENT}") + add_library(addr2line STATIC "addr2line/addr2line.c") + target_link_libraries(addr2line PUBLIC libdwarf dl z) + target_include_directories(addr2line PUBLIC "libdwarf/src/lib/libdwarf" ) + endif(NOT ${TD_WINDOWS}) endif(${BUILD_ADDR2LINE}) diff --git a/docs-cn/01-index.md b/docs-cn/01-index.md new file mode 100644 index 0000000000000000000000000000000000000000..d2e6706892f3997af115e71d1da455ebce2ecbec --- /dev/null +++ b/docs-cn/01-index.md @@ -0,0 +1,25 @@ +--- +title: TDengine 文档 +sidebar_label: 文档首页 +slug: / +--- + +TDengine 是一款[高性能](https://www.taosdata.com/fast)、[分布式](https://www.taosdata.com/scalable)、[支持 SQL](https://www.taosdata.com/sql-support) 的时序数据库 (Database)。本文档是 TDengine 用户手册,主要是介绍 TDengine 的基本概念、安装、使用、功能、开发接口、运营维护、TDengine 内核设计等等,它主要是面向架构师、开发者与系统管理员的。 + +TDengine 充分利用了时序数据的特点,提出了“一个数据采集点一张表”与“超级表”的概念,设计了创新的存储引擎,让数据的写入、查询和存储效率都得到极大的提升。为正确理解并使用TDengine, 无论如何,请您仔细阅读[基本概念](./concept)一章。 + +如果你是开发者,请一定仔细阅读[开发指南](./develop)一章,该部分对数据库连接、建模、插入数据、查询、连续查询、缓存、数据订阅、用户自定义函数等功能都做了详细介绍,并配有各种编程语言的示例代码。大部分情况下,你只要把示例代码拷贝粘贴,针对自己的应用稍作改动,就能跑起来。 + +我们已经生活在大数据的时代,纵向扩展已经无法满足日益增长的业务需求,任何系统都必须具有水平扩展的能力,集群成为大数据以及 database 系统的不可缺失功能。TDengine 团队不仅实现了集群功能,而且将这一重要核心功能开源。怎么部署、管理和维护 TDengine 集群,请参考[集群管理](./cluster)一章。 + +TDengine 采用 SQL 作为其查询语言,大大降低学习成本、降低迁移成本,但同时针对时序数据场景,又做了一些扩展,以支持插值、降采样、时间加权平均等操作。[SQL 手册](./taos-sql)一章详细描述了 SQL 语法、详细列出了各种支持的命令和函数。 + +如果你是系统管理员,关心安装、升级、容错灾备、关心数据导入、导出,配置参数,怎么监测 TDengine 是否健康运行,怎么提升系统运行的性能,那么请仔细参考[运维指南](./operation)一章。 + +如果你对 TDengine 外围工具,REST API, 各种编程语言的连接器想做更多详细了解,请看[参考指南](./reference)一章。 + +如果你对 TDengine 内部的架构设计很有兴趣,欢迎仔细阅读[技术内幕](./tdinternal)一章,里面对集群的设计、数据分区、分片、写入、读出、查询、聚合查询的流程都做了详细的介绍。如果你想研读 TDengine 代码甚至贡献代码,请一定仔细读完这一章。 + +最后,作为一个开源软件,欢迎大家的参与。如果发现文档的任何错误,描述不清晰的地方,都请在每个页面的最下方,点击“编辑本文档“直接进行修改。 + +Together, we make a difference! diff --git a/docs-cn/02-intro.md b/docs-cn/02-intro.md new file mode 100644 index 0000000000000000000000000000000000000000..2a56c5e9e667b511003b1ee08801ddcb54ff2ec4 --- /dev/null +++ b/docs-cn/02-intro.md @@ -0,0 +1,125 @@ +--- +title: 产品简介 +toc_max_heading_level: 2 +--- + +TDengine 是一款高性能、分布式、支持 SQL 的时序数据库 (Database),其核心代码,包括集群功能全部开源(开源协议,AGPL v3.0)。TDengine 能被广泛运用于物联网、工业互联网、车联网、IT 运维、金融等领域。除核心的时序数据库 (Database) 功能外,TDengine 还提供[缓存](/develop/cache/)、[数据订阅](/develop/subscribe)、[流式计算](/develop/continuous-query)等大数据平台所需要的系列功能,最大程度减少研发和运维的复杂度。 + +本章节介绍TDengine的主要功能、竞争优势、适用场景、与其他数据库的对比测试等等,让大家对TDengine有个整体的了解。 + +## 主要功能 + +TDengine的主要功能如下: + +1. 高速数据写入,除 [SQL 写入](/develop/insert-data/sql-writing)外,还支持 [Schemaless 写入](/reference/schemaless/),支持 [InfluxDB LINE 协议](/develop/insert-data/influxdb-line),[OpenTSDB Telnet](/develop/insert-data/opentsdb-telnet), [OpenTSDB JSON ](/develop/insert-data/opentsdb-json)等协议写入; +2. 第三方数据采集工具 [Telegraf](/third-party/telegraf),[Prometheus](/third-party/prometheus),[StatsD](/third-party/statsd),[collectd](/third-party/collectd),[icinga2](/third-party/icinga2), [TCollector](/third-party/tcollector), [EMQ](/third-party/emq-broker), [HiveMQ](/third-party/hive-mq-broker) 等都可以进行配置后,不用任何代码,即可将数据写入; +3. 支持[各种查询](/develop/query-data),包括聚合查询、嵌套查询、降采样查询、插值等 +4. 支持[用户自定义函数](/develop/udf) +5. 支持[缓存](/develop/cache),将每张表的最后一条记录缓存起来,这样无需 Redis +6. 支持[连续查询](/develop/continuous-query)(Continuous Query) +7. 支持[数据订阅](/develop/subscribe),而且可以指定过滤条件 +8. 支持[集群](/cluster/),可以通过多节点进行水平扩展,并通过多副本实现高可靠 +9. 提供[命令行程序](/reference/taos-shell),便于管理集群,检查系统状态,做即席查询 +10. 提供多种数据的[导入](/operation/import)、[导出](/operation/export) +11. 支持对[TDengine 集群本身的监控](/operation/monitor) +12. 提供 [C/C++](/reference/connector/cpp), [Java](/reference/connector/java), [Python](/reference/connector/python), [Go](/reference/connector/go), [Rust](/reference/connector/rust), [Node.js](/reference/connector/node) 等多种编程语言的[连接器](/reference/connector/) +13. 支持 [REST 接口](/reference/rest-api/) +14. 支持与[ Grafana 无缝集成](/third-party/grafana) +15. 支持与 Google Data Studio 无缝集成 + +更多细小的功能,请阅读整个文档。 + +## 竞争优势 + +由于 TDengine 充分利用了[时序数据特点](https://www.taosdata.com/blog/2019/07/09/105.html),比如结构化、无需事务、很少删除或更新、写多读少等等,设计了全新的针对时序数据的存储引擎和计算引擎,因此与其他时序数据库相比,TDengine 有以下特点: + +- **[高性能](https://www.taosdata.com/fast)**:通过创新的存储引擎设计,无论是数据写入还是查询,TDengine 的性能比通用数据库快 10 倍以上,也远超其他时序数据库,而且存储空间也大为节省。 + +- **[分布式](https://www.taosdata.com/scalable)**:通过原生分布式的设计,TDengine 提供了水平扩展的能力,只需要增加节点就能获得更强的数据处理能力,同时通过多副本机制保证了系统的高可用。 + +- **[支持 SQL](https://www.taosdata.com/sql-support)**:TDengine 采用 SQL 作为数据查询语言,减少学习和迁移成本,同时提供 SQL 扩展来处理时序数据特有的分析,而且支持方便灵活的 schemaless 数据写入。 + +- **All in One**:将数据库、消息队列、缓存、流式计算等功能融合一起,应用无需再集成 Kafka/Redis/HBase/Spark 等软件,大幅降低应用开发和维护成本。 + +- **零管理**:安装、集群几秒搞定,无任何依赖,不用分库分表,系统运行状态监测能与 Grafana 或其他运维工具无缝集成。 + +- **零学习成本**:采用 SQL 查询语言,支持 C/C++、Python、Java、Go、Rust、Node.js、C#、Lua(社区贡献)、PHP(社区贡献) 等多种编程语言,与 MySQL 相似,零学习成本。 + +- **无缝集成**:不用一行代码,即可与 Telegraf、Grafana、Prometheus、EMQX、HiveMQ、StatsD、collectd、icinga、TCollector、Matlab、R 等第三方工具无缝集成。 + +- **互动 Console**: 通过命令行 console,不用编程,执行 SQL 语句就能做即席查询、各种数据库的操作、管理以及集群的维护. + +采用 TDengine,可将典型的物联网、车联网、工业互联网大数据平台的总拥有成本大幅降低。表现在几个方面: + +1. 由于其超强性能,它能将系统需要的计算资源和存储资源大幅降低 +2. 因为采用 SQL 接口,能与众多第三放软件无缝集成,学习迁移成本大幅下降 +3. 因为其 All In One 的特性,系统复杂度降低,能降研发成本 +4. 因为运维维护简单,运营维护成本能大幅降低 + +## 技术生态 + +在整个时序大数据平台中,TDengine 在其中扮演的角色如下: + +
+ +![TDengine技术生态图](eco_system.png) + +
+
图 1. TDengine技术生态图
+ +上图中,左侧是各种数据采集或消息队列,包括 OPC-UA、MQTT、Telegraf、也包括 Kafka, 他们的数据将被源源不断的写入到 TDengine。右侧则是可视化、BI 工具、组态软件、应用程序。下侧则是 TDengine 自身提供的命令行程序 (CLI) 以及可视化管理管理。 + +## 总体适用场景 + +作为一个高性能、分布式、支持 SQL 的时序数据库 (Database),TDengine 的典型适用场景包括但不限于 IoT、工业互联网、车联网、IT 运维、能源、金融证券等领域。需要指出的是,TDengine 是针对时序数据场景设计的专用数据库和专用大数据处理工具,因充分利用了时序大数据的特点,它无法用来处理网络爬虫、微博、微信、电商、ERP、CRM 等通用型数据。本文对适用场景做更多详细的分析。 + +### 数据源特点和需求 + +从数据源角度,设计人员可以从下面几个角度分析 TDengine 在目标应用系统里面的适用性。 + +| 数据源特点和需求 | 不适用 | 可能适用 | 非常适用 | 简单说明 | +| ---------------------------- | ------ | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------------- | +| 总体数据量巨大 | | | √ | TDengine 在容量方面提供出色的水平扩展功能,并且具备匹配高压缩的存储结构,达到业界最优的存储效率。 | +| 数据输入速度偶尔或者持续巨大 | | | √ | TDengine 的性能大大超过同类产品,可以在同样的硬件环境下持续处理大量的输入数据,并且提供很容易在用户环境里面运行的性能评估工具。 | +| 数据源数目巨大 | | | √ | TDengine 设计中包含专门针对大量数据源的优化,包括数据的写入和查询,尤其适合高效处理海量(千万或者更多量级)的数据源。 | + +### 系统架构要求 + +| 系统架构要求 | 不适用 | 可能适用 | 非常适用 | 简单说明 | +| ---------------------- | ------ | -------- | -------- | ----------------------------------------------------------------------------------------------------- | +| 要求简单可靠的系统架构 | | | √ | TDengine 的系统架构非常简单可靠,自带消息队列,缓存,流式计算,监控等功能,无需集成额外的第三方产品。 | +| 要求容错和高可靠 | | | √ | TDengine 的集群功能,自动提供容错灾备等高可靠功能。 | +| 标准化规范 | | | √ | TDengine 使用标准的 SQL 语言提供主要功能,遵守标准化规范。 | + +### 系统功能需求 + +| 系统功能需求 | 不适用 | 可能适用 | 非常适用 | 简单说明 | +| -------------------------- | ------ | -------- | -------- | --------------------------------------------------------------------------------------------------------------------- | +| 要求完整的内置数据处理算法 | | √ | | TDengine 的实现了通用的数据处理算法,但是还没有做到妥善处理各行各业的所有要求,因此特殊类型的处理还需要应用层面处理。 | +| 需要大量的交叉查询处理 | | √ | | 这种类型的处理更多应该用关系型数据系统处理,或者应该考虑 TDengine 和关系型数据系统配合实现系统功能。 | + +### 系统性能需求 + +| 系统性能需求 | 不适用 | 可能适用 | 非常适用 | 简单说明 | +| ---------------------- | ------ | -------- | -------- | ------------------------------------------------------------------------------------------------------ | +| 要求较大的总体处理能力 | | | √ | TDengine 的集群功能可以轻松地让多服务器配合达成处理能力的提升。 | +| 要求高速处理数据 | | | √ | TDengine 的专门为 IoT 优化的存储和数据处理的设计,一般可以让系统得到超出同类产品多倍数的处理速度提升。 | +| 要求快速处理小粒度数据 | | | √ | 这方面 TDengine 性能可以完全对标关系型和 NoSQL 型数据处理系统。 | + +### 系统维护需求 + +| 系统维护需求 | 不适用 | 可能适用 | 非常适用 | 简单说明 | +| ---------------------- | ------ | -------- | -------- | --------------------------------------------------------------------------------------------------------------------- | +| 要求系统可靠运行 | | | √ | TDengine 的系统架构非常稳定可靠,日常维护也简单便捷,对维护人员的要求简洁明了,最大程度上杜绝人为错误和事故。 | +| 要求运维学习成本可控 | | | √ | 同上。 | +| 要求市场有大量人才储备 | √ | | | TDengine 作为新一代产品,目前人才市场里面有经验的人员还有限。但是学习成本低,我们作为厂家也提供运维的培训和辅助服务。 | + +## 与其他数据库的对比测试 + +- [用 InfluxDB 开源的性能测试工具对比 InfluxDB 和 TDengine](https://www.taosdata.com/blog/2020/01/13/1105.html) +- [TDengine 与 OpenTSDB 对比测试](https://www.taosdata.com/blog/2019/08/21/621.html) +- [TDengine 与 Cassandra 对比测试](https://www.taosdata.com/blog/2019/08/14/573.html) +- [TDengine 与 InfluxDB 对比测试](https://www.taosdata.com/blog/2019/07/19/419.html) +- [TDengine VS InfluxDB ,写入性能大 PK !](https://www.taosdata.com/2021/11/05/3248.html) +- [TDengine 和 InfluxDB 查询性能对比测试报告](https://www.taosdata.com/2022/02/22/5969.html) +- [TDengine 与 InfluxDB、OpenTSDB、Cassandra、MySQL、ClickHouse 等数据库的对比测试报告](https://www.taosdata.com/downloads/TDengine_Testing_Report_cn.pdf) diff --git a/docs-cn/04-concept/_category_.yml b/docs-cn/04-concept/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..aad75dce21f63a6510bc0b8da4c93952767adfdf --- /dev/null +++ b/docs-cn/04-concept/_category_.yml @@ -0,0 +1 @@ +label: 基本概念 \ No newline at end of file diff --git a/docs-cn/04-concept/index.md b/docs-cn/04-concept/index.md new file mode 100644 index 0000000000000000000000000000000000000000..ca25595260953f8d941ccaf367bdc45a8325488f --- /dev/null +++ b/docs-cn/04-concept/index.md @@ -0,0 +1,173 @@ +--- +title: 数据模型和基本概念 +--- + +为了便于解释基本概念,便于撰写示例程序,整个 TDengine 文档以智能电表作为典型时序数据场景。假设每个智能电表采集电流、电压、相位三个量,有多个智能电表,每个电表有位置 location 和分组 group ID 的静态属性. 其采集的数据类似如下的表格: + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Device IDTime StampCollected MetricsTags
Device IDTime StampcurrentvoltagephaselocationgroupId
d1001153854868500010.32190.31Beijing.Chaoyang2
d1002153854868400010.22200.23Beijing.Chaoyang3
d1003153854868650011.52210.35Beijing.Haidian3
d1004153854868550013.42230.29Beijing.Haidian2
d1001153854869500012.62180.33Beijing.Chaoyang2
d1004153854869660011.82210.28Beijing.Haidian2
d1002153854869665010.32180.25Beijing.Chaoyang3
d1001153854869680012.32210.31Beijing.Chaoyang2
+表 1:智能电表数据示例 +
+ +每一条记录都有设备 ID,时间戳,采集的物理量以及每个设备相关的静态标签。每个设备是受外界的触发,或按照设定的周期采集数据。采集的数据点是时序的,是一个数据流。 + +## 采集量 (Metric) + +采集量是指传感器、设备或其他类型采集点采集的物理量,比如电流、电压、温度、压力、GPS 位置等,是随时间变化的,数据类型可以是整型、浮点型、布尔型,也可是字符串。随着时间的推移,存储的采集量的数据量越来越大。 + +## 标签 (Label/Tag) + +标签是指传感器、设备或其他类型采集点的静态属性,不是随时间变化的,比如设备型号、颜色、设备的所在地等,数据类型可以是任何类型。虽然是静态的,但 TDengine 容许用户修改、删除或增加标签值。与采集量不一样的是,随时间的推移,存储的标签的数据量不会有什么变化。 + +## 数据采集点 (Data Collection Point) + +数据采集点是指按照预设时间周期或受事件触发采集物理量的硬件或软件。一个数据采集点可以采集一个或多个采集量,**但这些采集量都是同一时刻采集的,具有相同的时间戳**。对于复杂的设备,往往有多个数据采集点,每个数据采集点采集的周期都可能不一样,而且完全独立,不同步。比如对于一台汽车,有数据采集点专门采集 GPS 位置,有数据采集点专门采集发动机状态,有数据采集点专门采集车内的环境,这样一台汽车就有三个数据采集点。 + +## 表 (Table) + +因为采集量一般是结构化数据,同时为降低学习门槛,TDengine 采用传统的关系型数据库模型管理数据。用户需要先创建库,然后创建表,之后才能插入或查询数据。 + +为充分利用其数据的时序性和其他数据特点,TDengine 采取**一个数据采集点一张表**的策略,要求对每个数据采集点单独建表(比如有一千万个智能电表,就需创建一千万张表,上述表格中的 d1001,d1002,d1003,d1004 都需单独建表),用来存储这个数据采集点所采集的时序数据。这种设计有几大优点: + +1. 由于不同数据采集点产生数据的过程完全独立,每个数据采集点的数据源是唯一的,一张表也就只有一个写入者,这样就可采用无锁方式来写,写入速度就能大幅提升。 +2. 对于一个数据采集点而言,其产生的数据是按照时间排序的,因此写的操作可用追加的方式实现,进一步大幅提高数据写入速度。 +3. 一个数据采集点的数据是以块为单位连续存储的。如果读取一个时间段的数据,它能大幅减少随机读取操作,成数量级的提升读取和查询速度。 +4. 一个数据块内部,采用列式存储,对于不同数据类型,采用不同压缩算法,而且由于一个数据采集点的采集量的变化是缓慢的,压缩率更高。 + +如果采用传统的方式,将多个数据采集点的数据写入一张表,由于网络延时不可控,不同数据采集点的数据到达服务器的时序是无法保证的,写入操作是要有锁保护的,而且一个数据采集点的数据是难以保证连续存储在一起的。**采用一个数据采集点一张表的方式,能最大程度的保证单个数据采集点的插入和查询的性能是最优的。** + +TDengine 建议用数据采集点的名字(如上表中的 D1001)来做表名。每个数据采集点可能同时采集多个采集量(如上表中的 current,voltage,phase),每个采集量对应一张表中的一列,数据类型可以是整型、浮点型、字符串等。除此之外,表的第一列必须是时间戳,即数据类型为 timestamp。对采集量,TDengine 将自动按照时间戳建立索引,但对采集量本身不建任何索引。数据用列式存储方式保存。 + +对于复杂的设备,比如汽车,它有多个数据采集点,那么就需要为一台汽车建立多张表。 + +## 超级表 (STable) + +由于一个数据采集点一张表,导致表的数量巨增,难以管理,而且应用经常需要做采集点之间的聚合操作,聚合的操作也变得复杂起来。为解决这个问题,TDengine 引入超级表(Super Table,简称为 STable)的概念。 + +超级表是指某一特定类型的数据采集点的集合。同一类型的数据采集点,其表的结构是完全一样的,但每个表(数据采集点)的静态属性(标签)是不一样的。描述一个超级表(某一特定类型的数据采集点的集合),除需要定义采集量的表结构之外,还需要定义其标签的 schema,标签的数据类型可以是整数、浮点数、字符串,标签可以有多个,可以事后增加、删除或修改。如果整个系统有 N 个不同类型的数据采集点,就需要建立 N 个超级表。 + +在 TDengine 的设计里,**表用来代表一个具体的数据采集点,超级表用来代表一组相同类型的数据采集点集合**。 + +## 子表 (Subtable) + +当为某个具体数据采集点创建表时,用户可以使用超级表的定义做模板,同时指定该具体采集点(表)的具体标签值来创建该表。**通过超级表创建的表称之为子表**。正常的表与子表的差异在于: + +1. 子表就是表,因此所有正常表的SQL操作都可以在子表上执行。 +2. 子表在正常表的基础上有扩展,它是带有静态标签的,而且这些标签可以事后增加、删除、修改,而正常的表没有。 +3. 子表一定属于一张超级表,但普通表不属于任何超级表 +4. 普通表无法转为子表,子表也无法转为普通表。 + +超级表与与基于超级表建立的子表之间的关系表现在: + +1. 一张超级表包含有多张子表,这些子表具有相同的采集量 schema,但带有不同的标签值。 +2. 不能通过子表调整数据或标签的模式,对于超级表的数据模式修改立即对所有的子表生效。 +3. 超级表只定义一个模板,自身不存储任何数据或标签信息。因此,不能向一个超级表写入数据,只能将数据写入子表中。 + +查询既可以在表上进行,也可以在超级表上进行。针对超级表的查询,TDengine 将把所有子表中的数据视为一个整体数据集进行处理,会先把满足标签过滤条件的表从超级表中找出来,然后再扫描这些表的时序数据,进行聚合操作,这样需要扫描的数据集会大幅减少,从而显著提高查询的性能。本质上,TDengine 通过对超级表查询的支持,实现了多个同类数据采集点的高效聚合。 + +TDengine系统建议给一个数据采集点建表,需要通过超级表建表,而不是建普通表。 + +## 库 (database) + +库是指一组表的集合。TDengine 容许一个运行实例有多个库,而且每个库可以配置不同的存储策略。不同类型的数据采集点往往具有不同的数据特征,包括数据采集频率的高低,数据保留时间的长短,副本的数目,数据块的大小,是否允许更新数据等等。为了在各种场景下 TDengine 都能最大效率的工作,TDengine 建议将不同数据特征的超级表创建在不同的库里。 + +一个库里,可以有一到多个超级表,但一个超级表只属于一个库。一个超级表所拥有的子表全部存在一个库里。 + +## FQDN & End Point + +FQDN (fully qualified domain name, 完全限定域名)是 Internet 上特定计算机或主机的完整域名。FQDN 由两部分组成:主机名和域名。例如,假设邮件服务器的 FQDN 可能是 mail.tdengine.com。主机名是 mail,主机位于域名 tdengine.com 中。DNS(Domain Name System),负责将 FQDN 翻译成 IP,是互联网应用的寻址方式。对于没有 DNS 的系统,可以通过配置 hosts 文件来解决。 + +TDengine 集群的每个节点是由 End Point 来唯一标识的,End Point 是由 FQDN 外加 Port 组成,比如 h1.tdengine.com:6030。这样当 IP 发生变化的时候,我们依然可以使用 FQDN 来动态找到节点,不需要更改集群的任何配置。而且采用 FQDN,便于内网和外网对同一个集群的统一访问。 + +TDengine 不建议采用直接的 IP 地址访问集群,不利于管理。不了解 FQDN 概念,请看博文[《一篇文章说清楚 TDengine 的 FQDN》](https://www.taosdata.com/blog/2020/09/11/1824.html)。 diff --git a/docs-cn/05-get-started/_apt_get_install.mdx b/docs-cn/05-get-started/_apt_get_install.mdx new file mode 100644 index 0000000000000000000000000000000000000000..b1bc4a13517bbfdc9eda86a58b89aee8e41fa470 --- /dev/null +++ b/docs-cn/05-get-started/_apt_get_install.mdx @@ -0,0 +1,26 @@ +可以使用 apt-get 工具从官方仓库安装。 + +**安装包仓库** + +``` +wget -qO - http://repos.taosdata.com/tdengine.key | sudo apt-key add - +echo "deb [arch=amd64] http://repos.taosdata.com/tdengine-stable stable main" | sudo tee /etc/apt/sources.list.d/tdengine-stable.list +``` + +如果安装 Beta 版需要安装包仓库 + +``` +echo "deb [arch=amd64] http://repos.taosdata.com/tdengine-beta beta main" | sudo tee /etc/apt/sources.list.d/tdengine-beta.list +``` + +**使用 apt-get 命令安装** + +``` +sudo apt-get update +apt-cache policy tdengine +sudo apt-get install tdengine +``` + +:::tip +apt-get 方式只适用于 Debian 或 Ubuntu 系统 +:::: diff --git a/docs-cn/05-get-started/_category_.yml b/docs-cn/05-get-started/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..b2348fade63c7bb717eac3e6e6b8dfda3c73b17a --- /dev/null +++ b/docs-cn/05-get-started/_category_.yml @@ -0,0 +1 @@ +label: 立即开始 diff --git a/docs-cn/05-get-started/_pkg_install.mdx b/docs-cn/05-get-started/_pkg_install.mdx new file mode 100644 index 0000000000000000000000000000000000000000..83c987af8bcf24a9593105b680d32a0421344d5f --- /dev/null +++ b/docs-cn/05-get-started/_pkg_install.mdx @@ -0,0 +1,17 @@ +import PkgList from "/components/PkgList"; + +TDengine 的安装非常简单,从下载到安装成功仅仅只要几秒钟。 + +为方便使用,从 2.4.0.10 开始,标准的服务端安装包包含了 taos、taosd、taosAdapter、taosdump、taosBenchmark、TDinsight 安装脚本和示例代码;如果您只需要用到服务端程序和客户端连接的 C/C++ 语言支持,也可以仅下载 lite 版本的安装包。 + +在安装包格式上,我们提供 tar.gz, rpm 和 deb 格式,为企业客户提供 tar.gz 格式安装包,以方便在特定操作系统上使用。需要注意的是,rpm 和 deb 包不含 taosdump、taosBenchmark 和 TDinsight 安装脚本,这些工具需要通过安装 taosTool 包获得。 + +发布版本包括稳定版和 Beta 版,Beta 版含有更多新功能。正式上线或测试建议安装稳定版。您可以根据需要选择下载: + + + +具体的安装方法,请参见[安装包的安装和卸载](/operation/pkg-install)。 + +下载其他组件、最新 Beta 版及之前版本的安装包,请点击[这里](https://www.taosdata.com/all-downloads) + +查看 Release Notes, 请点击[这里](https://github.com/taosdata/TDengine/releases) diff --git a/docs-cn/05-get-started/index.md b/docs-cn/05-get-started/index.md new file mode 100644 index 0000000000000000000000000000000000000000..458df909166b9769af2052ba654699e869d2081c --- /dev/null +++ b/docs-cn/05-get-started/index.md @@ -0,0 +1,173 @@ +--- +title: 立即开始 +description: '从 Docker,安装包或使用 apt-get 快速安装 TDengine, 通过命令行程序TAOS CLI和工具 taosdemo 快速体验 TDengine 功能' +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import PkgInstall from "./\_pkg_install.mdx"; +import AptGetInstall from "./\_apt_get_install.mdx"; + +## 安装 + +TDengine 完整的软件包包括服务端(taosd)、用于与第三方系统对接并提供 RESTful 接口的 taosAdapter、应用驱动(taosc)、命令行程序 (CLI,taos) 和一些工具软件,目前 2.X 版服务端 taosd 和 taosAdapter 仅在 Linux 系统上安装和运行,后续将支持 Windows、macOS 等系统。应用驱动 taosc 与 TDengine CLI 可以在 Windows 或 Linux 上安装和运行。TDengine 除了提供多种语言的连接器之外,还通过 [taosAdapter](/reference/taosadapter) 提供 [RESTful 接口](/reference/rest-api)。但在 2.4 之前的版本中没有 taosAdapter,RESTful 接口是由 taosd 内置的 HTTP 服务提供的。 + +TDengine 支持 X64/ARM64/MIPS64/Alpha64 硬件平台,后续将支持 ARM32、RISC-V 等 CPU 架构。 + + + +如果已经安装了 docker, 只需执行下面的命令。 + +```shell +docker run -d -p 6030-6049:6030-6049 -p 6030-6049:6030-6049/udp tdengine/tdengine +``` + +确定该容器已经启动并且在正常运行 + +```shell +docker ps +``` + +进入该容器并执行 bash + +```shell +docker exec -it bash +``` + +然后就可以执行相关的 Linux 命令操作和访问 TDengine + +详细操作方法请参照 [通过 Docker 快速体验 TDengine](/train-faq/docker)。 + +:::info +从 2.4.0.10 开始,除 taosd 以外,Docker 镜像还包含:taos、taosAdapter、taosdump、taosBenchmark、TDinsight 安装脚本和示例代码。启动 Docker 容器时,将同时启动 taosAdapter 和 taosd,实现对 RESTful 的支持。 + +::: + + + + + + + + + + +如果您希望对 TDengine 贡献代码或对内部实现感兴趣,请参考我们的 [TDengine GitHub 主页](https://github.com/taosdata/TDengine) 下载源码构建和安装. + +下载其他组件、最新 Beta 版及之前版本的安装包,请点击[这里](https://www.taosdata.com/cn/all-downloads/)。 + + + + +## 启动 + +安装后,请使用 `systemctl` 命令来启动 TDengine 的服务进程。 + +```bash +systemctl start taosd +``` + +检查服务是否正常工作: + +```bash +systemctl status taosd +``` + +如果 TDengine 服务正常工作,那么您可以通过 TDengine 的命令行程序 `taos` 来访问并体验 TDengine。 + +:::info + +- systemctl 命令需要 _root_ 权限来运行,如果您非 _root_ 用户,请在命令前添加 sudo 。 +- 为更好的获得产品反馈,改善产品,TDengine 会采集基本的使用信息,但您可以修改系统配置文件 taos.cfg 里的配置参数 telemetryReporting,将其设为 0,就可将其关闭。 +- TDengine 采用 FQDN(一般就是 hostname)作为节点的 ID,为保证正常运行,需要给运行 taosd 的服务器配置好 FQDN,在 TDengine CLI 或应用运行的机器配置好 DNS 服务或 hosts 文件,保证 FQDN 能够解析。 +- `systemctl stop taosd` 指令在执行后并不会马上停止 TDengine 服务,而是会等待系统中必要的落盘工作正常完成。在数据量很大的情况下,这可能会消耗较长时间。 + +TDengine 支持在使用 [`systemd`](https://en.wikipedia.org/wiki/Systemd) 做进程服务管理的 Linux 系统上安装,用 `which systemctl` 命令来检测系统中是否存在 `systemd` 包: + +```bash +which systemctl +``` + +如果系统中不支持 `systemd`,也可以用手动运行 `/usr/local/taos/bin/taosd` 方式启动 TDengine 服务。 + +:::note + +## TDengine 命令行 (CLI) + +为便于检查 TDengine 的状态,执行数据库 (Database) 的各种即席(Ad Hoc)查询,TDengine 提供一命令行应用程序(以下简称为 TDengine CLI) taos。要进入 TDengine 命令行,您只要在安装有 TDengine 的 Linux 终端执行 `taos` 即可。 + +```bash +taos +``` + +如果连接服务成功,将会打印出欢迎消息和版本信息。如果失败,则会打印错误消息出来(请参考 [FAQ](/train-faq/faq) 来解决终端连接服务端失败的问题)。 TDengine CLI 的提示符号如下: + +```cmd +taos> +``` + +在 TDengine CLI 中,用户可以通过 SQL 命令来创建/删除数据库、表等,并进行数据库(database)插入查询操作。在终端中运行的 SQL 语句需要以分号结束来运行。示例: + +```sql +create database demo; +use demo; +create table t (ts timestamp, speed int); +insert into t values ('2019-07-15 00:00:00', 10); +insert into t values ('2019-07-15 01:00:00', 20); +select * from t; + ts | speed | +======================================== + 2019-07-15 00:00:00.000 | 10 | + 2019-07-15 01:00:00.000 | 20 | +Query OK, 2 row(s) in set (0.003128s) +``` + +除执行 SQL 语句外,系统管理员还可以从 TDengine CLI 进行检查系统运行状态、添加删除用户账号等操作。TAOS CLI 连同应用驱动也可以独立安装在 Linux 或 Windows 机器上运行,更多细节请参考 [这里](../reference/taos-shell/) + +## 使用 taosBenchmark 体验写入速度 + +启动 TDengine 的服务,在 Linux 终端执行 `taosBenchmark` (曾命名为 `taosdemo`): + +```bash +taosBenchmark +``` + +该命令将在数据库 test 下面自动创建一张超级表 meters,该超级表下有 1 万张表,表名为 "d0" 到 "d9999",每张表有 1 万条记录,每条记录有 (ts, current, voltage, phase) 四个字段,时间戳从 "2017-07-14 10:40:00 000" 到 "2017-07-14 10:40:09 999",每张表带有标签 location 和 groupId,groupId 被设置为 1 到 10, location 被设置为 "beijing" 或者 "shanghai"。 + +这条命令很快完成 1 亿条记录的插入。具体时间取决于硬件性能,即使在一台普通的 PC 服务器往往也仅需十几秒。 + +taosBenchmark 命令本身带有很多选项,配置表的数目、记录条数等等,您可以设置不同参数进行体验,请执行 `taosBenchmark --help` 详细列出。taosBenchmark 详细使用方法请参照 [如何使用 taosBenchmark 对 TDengine 进行性能测试](https://www.taosdata.com/2021/10/09/3111.html)。 + +## 使用 TDengine CLI 体验查询速度 + +使用上述 taosBenchmark 插入数据后,可以在 TDengine CLI 输入查询命令,体验查询速度。 + +查询超级表下记录总条数: + +```sql +taos> select count(*) from test.meters; +``` + +查询 1 亿条记录的平均值、最大值、最小值等: + +```sql +taos> select avg(current), max(voltage), min(phase) from test.meters; +``` + +查询 location="beijing" 的记录总条数: + +```sql +taos> select count(*) from test.meters where location="beijing"; +``` + +查询 groupId=10 的所有记录的平均值、最大值、最小值等: + +```sql +taos> select avg(current), max(voltage), min(phase) from test.meters where groupId=10; +``` + +对表 d10 按 10s 进行平均值、最大值和最小值聚合统计: + +```sql +taos> select avg(current), max(voltage), min(phase) from test.d10 interval(10s); +``` diff --git a/docs-cn/07-develop/01-connect/_category_.yml b/docs-cn/07-develop/01-connect/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..f75d563ac9e061f8b8d66392031413f4051e351e --- /dev/null +++ b/docs-cn/07-develop/01-connect/_category_.yml @@ -0,0 +1 @@ +label: 建立连接 diff --git a/docs-cn/07-develop/01-connect/_connect_c.mdx b/docs-cn/07-develop/01-connect/_connect_c.mdx new file mode 100644 index 0000000000000000000000000000000000000000..9cd8669561195b49e8428ed490ad97bb5653ae6a --- /dev/null +++ b/docs-cn/07-develop/01-connect/_connect_c.mdx @@ -0,0 +1,3 @@ +```c title="原生连接" +{{#include docs-examples/c/connect_example.c}} +``` diff --git a/docs-cn/07-develop/01-connect/_connect_cs.mdx b/docs-cn/07-develop/01-connect/_connect_cs.mdx new file mode 100644 index 0000000000000000000000000000000000000000..821820e8fe1d87a35e01943530179eeb6e0f48be --- /dev/null +++ b/docs-cn/07-develop/01-connect/_connect_cs.mdx @@ -0,0 +1,8 @@ +```csharp title="原生连接" +{{#include docs-examples/csharp/ConnectExample.cs}} +``` + +:::info +C# 连接器目前只支持原生连接。 + +::: diff --git a/docs-cn/07-develop/01-connect/_connect_go.mdx b/docs-cn/07-develop/01-connect/_connect_go.mdx new file mode 100644 index 0000000000000000000000000000000000000000..478768caaacc8aceb9a3f5a85f008dde00125eb7 --- /dev/null +++ b/docs-cn/07-develop/01-connect/_connect_go.mdx @@ -0,0 +1,17 @@ +#### 使用数据库访问统一接口 + +```go title="原生连接" +{{#include docs-examples/go/connect/cgoexample/main.go}} +``` + +```go title="REST 连接" +{{#include docs-examples/go/connect/restexample/main.go}} +``` + +#### 使用高级封装 + +也可以使用 driver-go 的 af 包建立连接。这个模块封装了 TDengine 的高级功能, 如:参数绑定、订阅等。 + +```go title="使用 af 包建立原生连接" +{{#include docs-examples/go/connect/afconn/main.go}} +``` diff --git a/docs-cn/07-develop/01-connect/_connect_java.mdx b/docs-cn/07-develop/01-connect/_connect_java.mdx new file mode 100644 index 0000000000000000000000000000000000000000..635f39ceb28ffc3fd0b0d8edb057d9aa01c593de --- /dev/null +++ b/docs-cn/07-develop/01-connect/_connect_java.mdx @@ -0,0 +1,15 @@ +```java title="原生连接" +{{#include docs-examples/java/src/main/java/com/taos/example/JNIConnectExample.java}} +``` + +```java title="REST 连接" +{{#include docs-examples/java/src/main/java/com/taos/example/RESTConnectExample.java:main}} +``` + +使用 REST 连接时,如果查询数据量比较大,还可开启批量拉取功能。 + +```java title="开启批量拉取功能" {4} +{{#include docs-examples/java/src/main/java/com/taos/example/WSConnectExample.java:main}} +``` + +更多连接参数配置,参考[Java 连接器](/reference/connector/java) diff --git a/docs-cn/07-develop/01-connect/_connect_node.mdx b/docs-cn/07-develop/01-connect/_connect_node.mdx new file mode 100644 index 0000000000000000000000000000000000000000..199a6e3faa88fcb295379309a250990bf97fa973 --- /dev/null +++ b/docs-cn/07-develop/01-connect/_connect_node.mdx @@ -0,0 +1,7 @@ +```js title="原生连接" +{{#include docs-examples/node/nativeexample/connect.js}} +``` + +```js title="REST 连接" +{{#include docs-examples/node/restexample/connect.js}} +``` diff --git a/docs-cn/07-develop/01-connect/_connect_php.mdx b/docs-cn/07-develop/01-connect/_connect_php.mdx new file mode 100644 index 0000000000000000000000000000000000000000..2431df2a722659ae6e5962a955fba139be3e5f67 --- /dev/null +++ b/docs-cn/07-develop/01-connect/_connect_php.mdx @@ -0,0 +1,3 @@ +```php title="原生连接" +{{#include docs-examples/php/connect.php}} +``` diff --git a/docs-cn/07-develop/01-connect/_connect_python.mdx b/docs-cn/07-develop/01-connect/_connect_python.mdx new file mode 100644 index 0000000000000000000000000000000000000000..c0043c752e14bcc38f97c1046f2852a3f7fa2b7b --- /dev/null +++ b/docs-cn/07-develop/01-connect/_connect_python.mdx @@ -0,0 +1,3 @@ +```python title="原生连接" +{{#include docs-examples/python/connect_example.py}} +``` diff --git a/docs-cn/07-develop/01-connect/_connect_r.mdx b/docs-cn/07-develop/01-connect/_connect_r.mdx new file mode 100644 index 0000000000000000000000000000000000000000..8aab6121a66b38540bf1b8ebf5b48a513282ac7a --- /dev/null +++ b/docs-cn/07-develop/01-connect/_connect_r.mdx @@ -0,0 +1,3 @@ +```r title="原生连接" +{{#include docs-examples/R/connect_native.r:demo}} +``` diff --git a/docs-cn/07-develop/01-connect/_connect_rust.mdx b/docs-cn/07-develop/01-connect/_connect_rust.mdx new file mode 100644 index 0000000000000000000000000000000000000000..9e64724c178ba2c72e14fc9878bf9c3237bb50e7 --- /dev/null +++ b/docs-cn/07-develop/01-connect/_connect_rust.mdx @@ -0,0 +1,8 @@ +```rust title="原生连接/REST 连接" +{{#include docs-examples/rust/nativeexample/examples/connect.rs}} +``` + +:::note +对于 Rust 连接器, 连接方式的不同只体现在使用的特性不同。如果启用了 "rest" 特性,那么只有 RESTful 的实现会被编译进来。 + +::: diff --git a/docs-cn/07-develop/01-connect/index.md b/docs-cn/07-develop/01-connect/index.md new file mode 100644 index 0000000000000000000000000000000000000000..3a15d03f93cee7dd064f29b4911019cae3632b9a --- /dev/null +++ b/docs-cn/07-develop/01-connect/index.md @@ -0,0 +1,284 @@ +--- +title: 建立连接 +description: "本节介绍如何使用连接器建立与 TDengine 的连接,给出连接器安装、连接的简单说明。" +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import ConnJava from "./_connect_java.mdx"; +import ConnGo from "./_connect_go.mdx"; +import ConnRust from "./_connect_rust.mdx"; +import ConnNode from "./_connect_node.mdx"; +import ConnPythonNative from "./_connect_python.mdx"; +import ConnCSNative from "./_connect_cs.mdx"; +import ConnC from "./_connect_c.mdx"; +import ConnR from "./_connect_r.mdx"; +import ConnPHP from "./_connect_php.mdx"; +import InstallOnWindows from "../../14-reference/03-connector/_linux_install.mdx"; +import InstallOnLinux from "../../14-reference/03-connector/_windows_install.mdx"; +import VerifyLinux from "../../14-reference/03-connector/_verify_linux.mdx"; +import VerifyWindows from "../../14-reference/03-connector/_verify_windows.mdx"; + +TDengine 提供了丰富的应用程序开发接口,为了便于用户快速开发自己的应用,TDengine 支持了多种编程语言的连接器,其中官方连接器包括支持 C/C++、Java、Python、Go、Node.js、C#、Rust、Lua(社区贡献)和 PHP (社区贡献)的连接器。这些连接器支持使用原生接口(taosc)和 REST 接口(部分语言暂不支持)连接 TDengine 集群。社区开发者也贡献了多个非官方连接器,例如 ADO.NET 连接器、Lua 连接器和 PHP 连接器。 + +## 连接器建立连接的方式 + +连接器建立连接的方式,TDengine 提供两种: + +1. 通过 taosAdapter 组件提供的 REST API 建立与 taosd 的连接,这种连接方式下文中简称“REST 连接” +2. 通过客户端驱动程序 taosc 直接与服务端程序 taosd 建立连接,这种连接方式下文中简称“原生连接”。 + +无论使用何种方式建立连接,连接器都提供了相同或相似的 API 操作数据库,都可以执行 SQL 语句,只是初始化连接的方式稍有不同,用户在使用上不会感到什么差别。 + +关键不同点在于: + +1. 使用 REST 连接,用户无需安装客户端驱动程序 taosc,具有跨平台易用的优势,但性能要下降 30%左右。 +2. 使用原生连接可以体验 TDengine 的全部功能,如[参数绑定接口](/reference/connector/cpp#参数绑定-api)、[订阅](/reference/connector/cpp#订阅和消费-api)等等。 + +## 安装客户端驱动 taosc + +如果选择原生连接,而且应用程序不在 TDengine 同一台服务器上运行,你需要先安装客户端驱动,否则可以跳过此一步。为避免客户端驱动和服务端不兼容,请使用一致的版本。 + +### 安装步骤 + + + + + + + + + + +### 安装验证 + +以上安装和配置完成后,并确认 TDengine 服务已经正常启动运行,此时可以执行安装包里带有的 TDengine 命令行程序 taos 进行登录。 + + + + + + + + + + +## 安装连接器 + + + + +如果使用 maven 管理项目,只需在 pom.xml 中加入以下依赖。 + +```xml + + com.taosdata.jdbc + taos-jdbcdriver + 2.0.38 + +``` + + + + +使用 `pip` 从 PyPI 安装: + +``` +pip install taospy +``` + +从 Git URL 安装: + +``` +pip install git+https://github.com/taosdata/taos-connector-python.git +``` + + + + +编辑 `go.mod` 添加 `driver-go` 依赖即可。 + +```go-mod title=go.mod +module goexample + +go 1.17 + +require github.com/taosdata/driver-go/v2 develop +``` + +:::note +driver-go 使用 cgo 封装了 taosc 的 API。cgo 需要使用 gcc 编译 C 的源码。因此需要确保你的系统上有 gcc。 + +::: + + + + +编辑 `Cargo.toml` 添加 `libtaos` 依赖即可。 + +```toml title=Cargo.toml +[dependencies] +libtaos = { version = "0.4.2"} +``` + +:::info +Rust 连接器通过不同的特性区分不同的连接方式。如果要建立 REST 连接,需要开启 `rest` 特性: + +```toml +libtaos = { version = "*", features = ["rest"] } +``` + +::: + + + + +Node.js 连接器通过不同的包提供不同的连接方式。 + +1. 安装 Node.js 原生连接器 + + ``` + npm i td2.0-connector + ``` + +:::note +推荐 Node 版本大于等于 `node-v12.8.0` 小于 `node-v13.0.0` +::: + +2. 安装 Node.js REST 连接器 + + ``` + npm i td2.0-rest-connector + ``` + + + + +编辑项目配置文件中添加 [TDengine.Connector](https://www.nuget.org/packages/TDengine.Connector/) 的引用即可: + +```xml title=csharp.csproj {12} + + + + Exe + net6.0 + enable + enable + TDengineExample.AsyncQueryExample + + + + + + + +``` + +也可通过 dotnet 命令添加: + +``` +dotnet add package TDengine.Connector +``` + +:::note +以下示例代码,均基于 dotnet6.0,如果使用其它版本,可能需要做适当调整。 + +::: + + + + +1. 下载 [taos-jdbcdriver-version-dist.jar](https://repo1.maven.org/maven2/com/taosdata/jdbc/taos-jdbcdriver/2.0.38/)。 +2. 安装 R 的依赖包`RJDBC`: + +```R +install.packages("RJDBC") +``` + + + + +如果已经安装了 TDengine 服务端软件或 TDengine 客户端驱动 taosc, 那么已经安装了 C 连接器,无需额外操作。 +
+ +
+ + +**下载代码并解压:** + +```shell +curl -L -o php-tdengine.tar.gz https://github.com/Yurunsoft/php-tdengine/archive/refs/tags/v1.0.2.tar.gz \ +&& mkdir php-tdengine \ +&& tar -xzf php-tdengine.tar.gz -C php-tdengine --strip-components=1 +``` + +> 版本 `v1.0.0` 可替换为任意更新的版本,可在 Release 中查看最新版本。 + +**非 Swoole 环境:** + +```shell +phpize && ./configure && make -j && make install +``` + +**手动指定 TDengine 目录:** + +```shell +phpize && ./configure --with-tdengine-dir=/usr/local/Cellar/tdengine/2.4.0.0 && make -j && make install +``` + +> `--with-tdengine-dir=` 后跟上 TDengine 目录。 +> 适用于默认找不到的情况,或者 macOS 系统用户。 + +**Swoole 环境:** + +```shell +phpize && ./configure --enable-swoole && make -j && make install +``` + +**启用扩展:** + +方法一:在 `php.ini` 中加入 `extension=tdengine` + +方法二:运行带参数 `php -d extension=tdengine test.php` + + +
+ +## 建立连接 + +在执行这一步之前,请确保有一个正在运行的,且可以访问到的 TDengine,而且服务端的 FQDN 配置正确。以下示例代码,都假设 TDengine 安装在本机,且 FQDN(默认 localhost) 和 serverPort(默认 6030) 都使用默认配置。 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +:::tip +如果建立连接失败,大部分情况下是 FQDN 或防火墙的配置不正确,详细的排查方法请看[《常见问题及反馈》](https://docs.taosdata.com/train-faq/faq)中的“遇到错误 Unable to establish connection, 我怎么办?” + +::: diff --git a/docs-cn/07-develop/02-model/_category_.yml b/docs-cn/07-develop/02-model/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..e5dae7c27cf17ff737aa8301bc79aad468c28791 --- /dev/null +++ b/docs-cn/07-develop/02-model/_category_.yml @@ -0,0 +1,2 @@ +label: 数据建模 + diff --git a/docs-cn/07-develop/02-model/index.mdx b/docs-cn/07-develop/02-model/index.mdx new file mode 100644 index 0000000000000000000000000000000000000000..a060e3c84b8c5b8e25714ce15fb2bc7afc7d49d2 --- /dev/null +++ b/docs-cn/07-develop/02-model/index.mdx @@ -0,0 +1,86 @@ +--- +title: TDengine 数据建模 +--- + +TDengine 采用类关系型数据模型,需要建库、建表。因此对于一个具体的应用场景,需要考虑库、超级表和普通表的设计。本节不讨论细致的语法规则,只介绍概念。 + +关于数据建模请参考[视频教程](https://www.taosdata.com/blog/2020/11/11/1945.html)。 + +## 创建库 + +不同类型的数据采集点往往具有不同的数据特征,包括数据采集频率的高低,数据保留时间的长短,副本的数目,数据块的大小,是否允许更新数据等等。为了在各种场景下 TDengine 都能最大效率的工作,TDengine 建议将不同数据特征的表创建在不同的库里,因为每个库可以配置不同的存储策略。创建一个库时,除 SQL 标准的选项外,还可以指定保留时长、副本数、内存块个数、时间精度、文件块里最大最小记录条数、是否压缩、一个数据文件覆盖的天数等多种参数。比如: + +```sql +CREATE DATABASE power KEEP 365 DAYS 10 BLOCKS 6 UPDATE 1; +``` + +上述语句将创建一个名为 power 的库,这个库的数据将保留 365 天(超过 365 天将被自动删除),每 10 天一个数据文件,内存块数为 6,允许更新数据。详细的语法及参数请见 [数据库管理](/taos-sql/database) 章节。 + +创建库之后,需要使用 SQL 命令 `USE` 将当前库切换过来,例如: + +```sql +USE power; +``` + +将当前连接里操作的库换为 power,否则对具体表操作前,需要使用“库名.表名”来指定库的名字。 + +:::note + +- 任何一张表或超级表必须属于某个库,在创建表之前,必须先创建库。 +- 处于两个不同库的表是不能进行 JOIN 操作的。 +- 创建并插入记录、查询历史记录的时候,均需要指定时间戳。 + +::: + +## 创建超级表 + +一个物联网系统,往往存在多种类型的设备,比如对于电网,存在智能电表、变压器、母线、开关等等。为便于多表之间的聚合,使用 TDengine, 需要对每个类型的数据采集点创建一个超级表。以[表 1](/tdinternal/arch#model_table1) 中的智能电表为例,可以使用如下的 SQL 命令创建超级表: + +```sql +CREATE STABLE meters (ts timestamp, current float, voltage int, phase float) TAGS (location binary(64), groupId int); +``` + +:::note +这一指令中的 STABLE 关键字,在 2.0.15 之前的版本中需写作 TABLE 。 +::: + +与创建普通表一样,创建超级表时,需要提供表名(示例中为 meters),表结构 Schema,即数据列的定义。第一列必须为时间戳(示例中为 ts),其他列为采集的物理量(示例中为 current, voltage, phase),数据类型可以为整型、浮点型、字符串等。除此之外,还需要提供标签的 schema (示例中为 location, groupId),标签的数据类型可以为整型、浮点型、字符串等。采集点的静态属性往往可以作为标签,比如采集点的地理位置、设备型号、设备组 ID、管理员 ID 等等。标签的 schema 可以事后增加、删除、修改。具体定义以及细节请见 [TAOS SQL 的超级表管理](/taos-sql/stable) 章节。 + +每一种类型的数据采集点需要建立一个超级表,因此一个物联网系统,往往会有多个超级表。对于电网,我们就需要对智能电表、变压器、母线、开关等都建立一个超级表。在物联网中,一个设备就可能有多个数据采集点(比如一台风力发电的风机,有的采集点采集电流、电压等电参数,有的采集点采集温度、湿度、风向等环境参数),这个时候,对这一类型的设备,需要建立多张超级表。 + +一张超级表最多容许 4096 列 (在 2.1.7.0 版本之前,列数限制为 1024 列),如果一个采集点采集的物理量个数超过 4096,需要建多张超级表来处理。一个系统可以有多个 DB,一个 DB 里可以有一到多个超级表。 + +## 创建表 + +TDengine 对每个数据采集点需要独立建表。与标准的关系型数据库一样,一张表有表名,Schema,但除此之外,还可以带有一到多个标签。创建时,需要使用超级表做模板,同时指定标签的具体值。以[表 1](/tdinternal/arch#model_table1)中的智能电表为例,可以使用如下的 SQL 命令建表: + +```sql +CREATE TABLE d1001 USING meters TAGS ("Beijing.Chaoyang", 2); +``` + +其中 d1001 是表名,meters 是超级表的表名,后面紧跟标签 Location 的具体标签值 ”Beijing.Chaoyang",标签 groupId 的具体标签值 2。虽然在创建表时,需要指定标签值,但可以事后修改。详细细则请见 [TAOS SQL 的表管理](/taos-sql/table) 章节。 + +:::warning +目前 TDengine 没有从技术层面限制使用一个 database (db1) 的超级表作为模板建立另一个 database (db2) 的子表,后续会禁止这种用法,不建议使用这种方法建表。 + +::: + +TDengine 建议将数据采集点的全局唯一 ID 作为表名(比如设备序列号)。但对于有的场景,并没有唯一的 ID,可以将多个 ID 组合成一个唯一的 ID。不建议将具有唯一性的 ID 作为标签值。 + +### 自动建表 + +在某些特殊场景中,用户在写数据时并不确定某个数据采集点的表是否存在,此时可在写入数据时使用自动建表语法来创建不存在的表,若该表已存在则不会建立新表且后面的 USING 语句被忽略。比如: + +```sql +INSERT INTO d1001 USING meters TAGS ("Beijng.Chaoyang", 2) VALUES (now, 10.2, 219, 0.32); +``` + +上述 SQL 语句将记录`(now, 10.2, 219, 0.32)`插入表 d1001。如果表 d1001 还未创建,则使用超级表 meters 做模板自动创建,同时打上标签值 `"Beijing.Chaoyang", 2`。 + +关于自动建表的详细语法请参见 [插入记录时自动建表](/taos-sql/insert#插入记录时自动建表) 章节。 + +## 多列模型 vs 单列模型 + +TDengine 支持多列模型,只要物理量是一个数据采集点同时采集的(时间戳一致),这些量就可以作为不同列放在一张超级表里。但还有一种极限的设计,单列模型,每个采集的物理量都单独建表,因此每种类型的物理量都单独建立一超级表。比如电流、电压、相位,就建三张超级表。 + +TDengine 建议尽可能采用多列模型,因为插入效率以及存储效率更高。但对于有些场景,一个采集点的采集量的种类经常变化,这个时候,如果采用多列模型,就需要频繁修改超级表的结构定义,让应用变的复杂,这个时候,采用单列模型会显得更简单。 diff --git a/docs-cn/07-develop/03-insert-data/01-sql-writing.mdx b/docs-cn/07-develop/03-insert-data/01-sql-writing.mdx new file mode 100644 index 0000000000000000000000000000000000000000..e63ffce6dd07366da99fe1f41d0a2a8d7a623f31 --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/01-sql-writing.mdx @@ -0,0 +1,137 @@ +--- +title: SQL 写入 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import JavaSQL from "./_java_sql.mdx"; +import JavaStmt from "./_java_stmt.mdx"; +import PySQL from "./_py_sql.mdx"; +import PyStmt from "./_py_stmt.mdx"; +import GoSQL from "./_go_sql.mdx"; +import GoStmt from "./_go_stmt.mdx"; +import RustSQL from "./_rust_sql.mdx"; +import RustStmt from "./_rust_stmt.mdx"; +import NodeSQL from "./_js_sql.mdx"; +import NodeStmt from "./_js_stmt.mdx"; +import CsSQL from "./_cs_sql.mdx"; +import CsStmt from "./_cs_stmt.mdx"; +import CSQL from "./_c_sql.mdx"; +import CStmt from "./_c_stmt.mdx"; +import PhpSQL from "./_php_sql.mdx"; +import PhpStmt from "./_php_stmt.mdx"; + +## SQL 写入简介 + +应用通过连接器执行 INSERT 语句来插入数据,用户还可以通过 TAOS Shell,手动输入 INSERT 语句插入数据。 + +### 一次写入一条 +下面这条 INSERT 就将一条记录写入到表 d1001 中: + +```sql +INSERT INTO d1001 VALUES (1538548685000, 10.3, 219, 0.31); +``` + +### 一次写入多条 + +TDengine 支持一次写入多条记录,比如下面这条命令就将两条记录写入到表 d1001 中: + +```sql +INSERT INTO d1001 VALUES (1538548684000, 10.2, 220, 0.23) (1538548696650, 10.3, 218, 0.25); +``` + +### 一次写入多表 + +TDengine 也支持一次向多个表写入数据,比如下面这条命令就向 d1001 写入两条记录,向 d1002 写入一条记录: + +```sql +INSERT INTO d1001 VALUES (1538548685000, 10.3, 219, 0.31) (1538548695000, 12.6, 218, 0.33) d1002 VALUES (1538548696800, 12.3, 221, 0.31); +``` + +详细的 SQL INSERT 语法规则参考 [TAOS SQL 的数据写入](/taos-sql/insert)。 + +:::info + +- 要提高写入效率,需要批量写入。一批写入的记录条数越多,插入效率就越高。但一条记录不能超过 16K,一条 SQL 语句总长度不能超过 1M 。 +- TDengine 支持多线程同时写入,要进一步提高写入速度,一个客户端需要打开 20 个以上的线程同时写。但线程数达到一定数量后,无法再提高,甚至还会下降,因为线程频繁切换,带来额外开销。 + +::: + +:::warning + +- 对同一张表,如果新插入记录的时间戳已经存在,默认情形下(UPDATE=0)新记录将被直接抛弃,也就是说,在一张表里,时间戳必须是唯一的。如果应用自动生成记录,很有可能生成的时间戳是一样的,这样,成功插入的记录条数会小于应用插入的记录条数。如果在创建数据库时使用了 UPDATE 1 选项,插入相同时间戳的新记录将覆盖原有记录。 +- 写入的数据的时间戳必须大于当前时间减去配置参数 keep 的时间。如果 keep 配置为 3650 天,那么无法写入比 3650 天还早的数据。写入数据的时间戳也不能大于当前时间加配置参数 days。如果 days 为 2,那么无法写入比当前时间还晚 2 天的数据。 + +::: + +## 示例程序 + +### 普通 SQL 写入 + + + + + + + + + + + + + + + + + + + + + + + + + + + + +:::note + +1. 无论 RESTful 方式建立连接还是本地驱动方式建立连接,以上示例代码都能正常工作。 +2. 唯一需要注意的是:由于 RESTful 接口无状态, 不能使用 `use db` 语句来切换数据库, 所以在上面示例中使用了`dbName.tbName`指定表名。 + +::: + +### 参数绑定写入 + +TDengine 也提供了支持参数绑定的 Prepare API,与 MySQL 类似,这些 API 目前也仅支持用问号 `?` 来代表待绑定的参数。从 2.1.1.0 和 2.1.2.0 版本开始,TDengine 大幅改进了参数绑定接口对数据写入(INSERT)场景的支持。这样在通过参数绑定接口写入数据时,就避免了 SQL 语法解析的资源消耗,从而在绝大多数情况下显著提升写入性能。 + +需要注意的是,只有使用原生连接的连接器,才能使用参数绑定功能。 + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs-cn/07-develop/03-insert-data/02-influxdb-line.mdx b/docs-cn/07-develop/03-insert-data/02-influxdb-line.mdx new file mode 100644 index 0000000000000000000000000000000000000000..dedd7f0e70834e21257bda78dd184f5ddc520160 --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/02-influxdb-line.mdx @@ -0,0 +1,70 @@ +--- +sidebar_label: InfluxDB 行协议 +title: InfluxDB 行协议 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import JavaLine from "./_java_line.mdx"; +import PyLine from "./_py_line.mdx"; +import GoLine from "./_go_line.mdx"; +import RustLine from "./_rust_line.mdx"; +import NodeLine from "./_js_line.mdx"; +import CsLine from "./_cs_line.mdx"; +import CLine from "./_c_line.mdx"; + +## 协议介绍 + +InfluxDB Line 协议采用一行字符串来表示一行数据。分为四部分: + +``` +measurement,tag_set field_set timestamp +``` + +- measurement 将作为超级表名。它与 tag_set 之间使用一个英文逗号来分隔。 +- tag_set 将作为标签数据,其格式形如 `=,=`,也即可以使用英文逗号来分隔多个标签数据。它与 field_set 之间使用一个半角空格来分隔。 +- field_set 将作为普通列数据,其格式形如 `=,=`,同样是使用英文逗号来分隔多个普通列的数据。它与 timestamp 之间使用一个半角空格来分隔。 +- timestamp 即本行数据对应的主键时间戳。 + +例如: + +``` +meters,location=Beijing.Haidian,groupid=2 current=13.4,voltage=223,phase=0.29 1648432611249500 +``` + +:::note + +- tag_set 中的所有的数据自动转化为 nchar 数据类型; +- field_set 中的每个数据项都需要对自身的数据类型进行描述, 比如 1.2f32 代表 float 类型的数值 1.2, 如果不带类型后缀会被当作 double 处理; +- timestamp 支持多种时间精度。写入数据的时候需要用参数指定时间精度,支持从小时到纳秒的 6 种时间精度。 + +::: + +要了解更多可参考:[InfluxDB Line 协议官方文档](https://docs.influxdata.com/influxdb/v2.0/reference/syntax/line-protocol/) 和 [TDengine 无模式写入参考指南](/reference/schemaless/#无模式写入行协议) + + +## 示例代码 + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs-cn/07-develop/03-insert-data/03-opentsdb-telnet.mdx b/docs-cn/07-develop/03-insert-data/03-opentsdb-telnet.mdx new file mode 100644 index 0000000000000000000000000000000000000000..dfbe6efda67b6928999287900637e0a251b86562 --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/03-opentsdb-telnet.mdx @@ -0,0 +1,84 @@ +--- +sidebar_label: OpenTSDB 行协议 +title: OpenTSDB 行协议 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import JavaTelnet from "./_java_opts_telnet.mdx"; +import PyTelnet from "./_py_opts_telnet.mdx"; +import GoTelnet from "./_go_opts_telnet.mdx"; +import RustTelnet from "./_rust_opts_telnet.mdx"; +import NodeTelnet from "./_js_opts_telnet.mdx"; +import CsTelnet from "./_cs_opts_telnet.mdx"; +import CTelnet from "./_c_opts_telnet.mdx"; + +## 协议介绍 + +OpenTSDB 行协议同样采用一行字符串来表示一行数据。OpenTSDB 采用的是单列模型,因此一行只能包含一个普通数据列。标签列依然可以有多个。分为四部分,具体格式约定如下: + +```txt + =[ =] +``` + +- metric 将作为超级表名。 +- timestamp 本行数据对应的时间戳。根据时间戳的长度自动识别时间精度。支持秒和毫秒两种时间精度 +- value 度量值,必须为一个数值。对应的列名也是 “value”。 +- 最后一部分是标签集, 用空格分隔不同标签, 所有标签自动转化为 nchar 数据类型; + +例如: + +```txt +meters.current 1648432611250 11.3 location=Beijing.Haidian groupid=3 +``` + +参考[OpenTSDB Telnet API文档](http://opentsdb.net/docs/build/html/api_telnet/put.html)。 + +## 示例代码 + + + + + + + + + + + + + + + + + + + + + + + + + +以上示例代码会自动创建 2 个超级表, 每个超级表有 4 条数据。 + +```cmd +taos> use test; +Database changed. + +taos> show stables; + name | created_time | columns | tags | tables | +============================================================================================ + meters.current | 2022-03-30 17:04:10.877 | 2 | 2 | 2 | + meters.voltage | 2022-03-30 17:04:10.882 | 2 | 2 | 2 | +Query OK, 2 row(s) in set (0.002544s) + +taos> select tbname, * from `meters.current`; + tbname | ts | value | groupid | location | +================================================================================================================================== + t_0e7bcfa21a02331c06764f275... | 2022-03-28 09:56:51.249 | 10.800000000 | 3 | Beijing.Haidian | + t_0e7bcfa21a02331c06764f275... | 2022-03-28 09:56:51.250 | 11.300000000 | 3 | Beijing.Haidian | + t_7e7b26dd860280242c6492a16... | 2022-03-28 09:56:51.249 | 10.300000000 | 2 | Beijing.Chaoyang | + t_7e7b26dd860280242c6492a16... | 2022-03-28 09:56:51.250 | 12.600000000 | 2 | Beijing.Chaoyang | +Query OK, 4 row(s) in set (0.005399s) +``` diff --git a/docs-cn/07-develop/03-insert-data/04-opentsdb-json.mdx b/docs-cn/07-develop/03-insert-data/04-opentsdb-json.mdx new file mode 100644 index 0000000000000000000000000000000000000000..5d445997d061ca052e4f3673b8e881ea4acf0ade --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/04-opentsdb-json.mdx @@ -0,0 +1,99 @@ +--- +sidebar_label: OpenTSDB JSON 格式协议 +title: OpenTSDB JSON 格式协议 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import JavaJson from "./_java_opts_json.mdx"; +import PyJson from "./_py_opts_json.mdx"; +import GoJson from "./_go_opts_json.mdx"; +import RustJson from "./_rust_opts_json.mdx"; +import NodeJson from "./_js_opts_json.mdx"; +import CsJson from "./_cs_opts_json.mdx"; +import CJson from "./_c_opts_json.mdx"; + +## 协议介绍 + +OpenTSDB JSON 格式协议采用一个 JSON 字符串表示一行或多行数据。例如: + +```json +[ + { + "metric": "sys.cpu.nice", + "timestamp": 1346846400, + "value": 18, + "tags": { + "host": "web01", + "dc": "lga" + } + }, + { + "metric": "sys.cpu.nice", + "timestamp": 1346846400, + "value": 9, + "tags": { + "host": "web02", + "dc": "lga" + } + } +] +``` + +与 OpenTSDB 行协议类似, metric 将作为超级表名, timestamp 表示时间戳,value 表示度量值, tags 表示标签集。 + + +参考[OpenTSDB HTTP API文档](http://opentsdb.net/docs/build/html/api_http/put.html)。 + +:::note +- 对于 JSON 格式协议,TDengine 并不会自动把所有标签转成 nchar 类型, 字符串将将转为 nchar 类型, 数值将同样转换为 double 类型。 +- TDengine 只接收 JSON **数组格式**的字符串,即使一行数据也需要转换成数组形式。 + +::: + +## 示例代码 + + + + + + + + + + + + + + + + + + + + + + + + + +以上示例代码会自动创建 2 个超级表, 每个超级表有 2 条数据。 + +```cmd +taos> use test; +Database changed. + +taos> show stables; + name | created_time | columns | tags | tables | +============================================================================================ + meters.current | 2022-03-29 16:05:25.193 | 2 | 2 | 1 | + meters.voltage | 2022-03-29 16:05:25.200 | 2 | 2 | 1 | +Query OK, 2 row(s) in set (0.001954s) + +taos> select * from `meters.current`; + ts | value | groupid | location | +=================================================================================================================== + 2022-03-28 09:56:51.249 | 10.300000000 | 2.000000000 | Beijing.Chaoyang | + 2022-03-28 09:56:51.250 | 12.600000000 | 2.000000000 | Beijing.Chaoyang | +Query OK, 2 row(s) in set (0.004076s) +``` diff --git a/docs-cn/07-develop/03-insert-data/_c_line.mdx b/docs-cn/07-develop/03-insert-data/_c_line.mdx new file mode 100644 index 0000000000000000000000000000000000000000..5ef2e9af774c54e9f090357286f83d2280c2ab11 --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_c_line.mdx @@ -0,0 +1,3 @@ +```c +{{#include docs-examples/c/line_example.c:main}} +``` \ No newline at end of file diff --git a/docs-cn/07-develop/03-insert-data/_c_opts_json.mdx b/docs-cn/07-develop/03-insert-data/_c_opts_json.mdx new file mode 100644 index 0000000000000000000000000000000000000000..22ad2e0122797248a372734aac0f3a16a1356530 --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_c_opts_json.mdx @@ -0,0 +1,3 @@ +```c +{{#include docs-examples/c/json_protocol_example.c:main}} +``` \ No newline at end of file diff --git a/docs-cn/07-develop/03-insert-data/_c_opts_telnet.mdx b/docs-cn/07-develop/03-insert-data/_c_opts_telnet.mdx new file mode 100644 index 0000000000000000000000000000000000000000..508d7bc98a149f49766bcd0a474ffe226cbe30bb --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_c_opts_telnet.mdx @@ -0,0 +1,3 @@ +```c +{{#include docs-examples/c/telnet_line_example.c:main}} +``` \ No newline at end of file diff --git a/docs-cn/07-develop/03-insert-data/_c_sql.mdx b/docs-cn/07-develop/03-insert-data/_c_sql.mdx new file mode 100644 index 0000000000000000000000000000000000000000..f4153fd2c427677a338d0c377663d0335f2672f0 --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_c_sql.mdx @@ -0,0 +1,3 @@ +```c +{{#include docs-examples/c/insert_example.c}} +``` \ No newline at end of file diff --git a/docs-cn/07-develop/03-insert-data/_c_stmt.mdx b/docs-cn/07-develop/03-insert-data/_c_stmt.mdx new file mode 100644 index 0000000000000000000000000000000000000000..01ac067519a2bd224e313fd70169722ba5f20413 --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_c_stmt.mdx @@ -0,0 +1,6 @@ +```c title=一次绑定一行 +{{#include docs-examples/c/stmt_example.c}} +``` +```c title=一次绑定多行 72:117 +{{#include docs-examples/c/multi_bind_example.c}} +``` \ No newline at end of file diff --git a/docs-cn/07-develop/03-insert-data/_category_.yml b/docs-cn/07-develop/03-insert-data/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..430b3e4209ec12c6abdbfa825c9a95582376d058 --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_category_.yml @@ -0,0 +1 @@ +label: 写入数据 \ No newline at end of file diff --git a/docs-cn/07-develop/03-insert-data/_cs_line.mdx b/docs-cn/07-develop/03-insert-data/_cs_line.mdx new file mode 100644 index 0000000000000000000000000000000000000000..9c275ee3d7c7a1e52fbb34dbae922004543ee3ce --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_cs_line.mdx @@ -0,0 +1,3 @@ +```csharp +{{#include docs-examples/csharp/InfluxDBLineExample.cs}} +``` diff --git a/docs-cn/07-develop/03-insert-data/_cs_opts_json.mdx b/docs-cn/07-develop/03-insert-data/_cs_opts_json.mdx new file mode 100644 index 0000000000000000000000000000000000000000..3d538b8506b298241faecd8098f89571359135c9 --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_cs_opts_json.mdx @@ -0,0 +1,3 @@ +```csharp +{{#include docs-examples/csharp/OptsJsonExample.cs}} +``` diff --git a/docs-cn/07-develop/03-insert-data/_cs_opts_telnet.mdx b/docs-cn/07-develop/03-insert-data/_cs_opts_telnet.mdx new file mode 100644 index 0000000000000000000000000000000000000000..c53bf3d7233115351e5af03b7d9e6318aa4a0da6 --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_cs_opts_telnet.mdx @@ -0,0 +1,3 @@ +```csharp +{{#include docs-examples/csharp/OptsTelnetExample.cs}} +``` diff --git a/docs-cn/07-develop/03-insert-data/_cs_sql.mdx b/docs-cn/07-develop/03-insert-data/_cs_sql.mdx new file mode 100644 index 0000000000000000000000000000000000000000..c7688bfbe77a1135424d829fe9b29fbb1bc93ae2 --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_cs_sql.mdx @@ -0,0 +1,3 @@ +```csharp +{{#include docs-examples/csharp/SQLInsertExample.cs}} +``` diff --git a/docs-cn/07-develop/03-insert-data/_cs_stmt.mdx b/docs-cn/07-develop/03-insert-data/_cs_stmt.mdx new file mode 100644 index 0000000000000000000000000000000000000000..97c3b910ffeb9e0c88fc143a02014115e819c147 --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_cs_stmt.mdx @@ -0,0 +1,3 @@ +```csharp +{{#include docs-examples/csharp/StmtInsertExample.cs}} +``` diff --git a/docs-cn/07-develop/03-insert-data/_go_line.mdx b/docs-cn/07-develop/03-insert-data/_go_line.mdx new file mode 100644 index 0000000000000000000000000000000000000000..cd225945b70e28bef2ca7fdaf0d9be0ad7ffc18c --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_go_line.mdx @@ -0,0 +1,3 @@ +```go +{{#include docs-examples/go/insert/line/main.go}} +``` diff --git a/docs-cn/07-develop/03-insert-data/_go_opts_json.mdx b/docs-cn/07-develop/03-insert-data/_go_opts_json.mdx new file mode 100644 index 0000000000000000000000000000000000000000..0c0d3e5b6330e046988cdd02234285ec67e92f01 --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_go_opts_json.mdx @@ -0,0 +1,3 @@ +```go +{{#include docs-examples/go/insert/json/main.go}} +``` diff --git a/docs-cn/07-develop/03-insert-data/_go_opts_telnet.mdx b/docs-cn/07-develop/03-insert-data/_go_opts_telnet.mdx new file mode 100644 index 0000000000000000000000000000000000000000..d5ca40cc146e62412476289853e8e2739e0e9e4b --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_go_opts_telnet.mdx @@ -0,0 +1,3 @@ +```go +{{#include docs-examples/go/insert/telnet/main.go}} +``` diff --git a/docs-cn/07-develop/03-insert-data/_go_sql.mdx b/docs-cn/07-develop/03-insert-data/_go_sql.mdx new file mode 100644 index 0000000000000000000000000000000000000000..613a65add1741eb763a4b24e65d180d05f7d670f --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_go_sql.mdx @@ -0,0 +1,3 @@ +```go +{{#include docs-examples/go/insert/sql/main.go}} +``` diff --git a/docs-cn/07-develop/03-insert-data/_go_stmt.mdx b/docs-cn/07-develop/03-insert-data/_go_stmt.mdx new file mode 100644 index 0000000000000000000000000000000000000000..7bb6792d6df5b250850bd0a0021ecceba994aa09 --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_go_stmt.mdx @@ -0,0 +1,8 @@ +```go +{{#include docs-examples/go/insert/stmt/main.go}} +``` + +:::tip +driver-go 的模块 `github.com/taosdata/driver-go/v2/wrapper` 是 C 接口的底层封装。使用这个模块也可以实现参数绑定写入。 + +::: diff --git a/docs-cn/07-develop/03-insert-data/_java_line.mdx b/docs-cn/07-develop/03-insert-data/_java_line.mdx new file mode 100644 index 0000000000000000000000000000000000000000..2e59a5d4701b2a2ab04ec5711845dc5c80067a1e --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_java_line.mdx @@ -0,0 +1,3 @@ +```java +{{#include docs-examples/java/src/main/java/com/taos/example/LineProtocolExample.java}} +``` diff --git a/docs-cn/07-develop/03-insert-data/_java_opts_json.mdx b/docs-cn/07-develop/03-insert-data/_java_opts_json.mdx new file mode 100644 index 0000000000000000000000000000000000000000..826a1a07d9405cb193849f9d21e5444f68517914 --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_java_opts_json.mdx @@ -0,0 +1,3 @@ +```java +{{#include docs-examples/java/src/main/java/com/taos/example/JSONProtocolExample.java}} +``` diff --git a/docs-cn/07-develop/03-insert-data/_java_opts_telnet.mdx b/docs-cn/07-develop/03-insert-data/_java_opts_telnet.mdx new file mode 100644 index 0000000000000000000000000000000000000000..954dcc1a482a150dea0b190e1e0593adbfbde796 --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_java_opts_telnet.mdx @@ -0,0 +1,3 @@ +```java +{{#include docs-examples/java/src/main/java/com/taos/example/TelnetLineProtocolExample.java}} +``` diff --git a/docs-cn/07-develop/03-insert-data/_java_sql.mdx b/docs-cn/07-develop/03-insert-data/_java_sql.mdx new file mode 100644 index 0000000000000000000000000000000000000000..a863378defe43b1f22c1f98087a34f053a7d6619 --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_java_sql.mdx @@ -0,0 +1,3 @@ +```java +{{#include docs-examples/java/src/main/java/com/taos/example/RestInsertExample.java:insert}} +``` \ No newline at end of file diff --git a/docs-cn/07-develop/03-insert-data/_java_stmt.mdx b/docs-cn/07-develop/03-insert-data/_java_stmt.mdx new file mode 100644 index 0000000000000000000000000000000000000000..54443e535fa84bdf8dc9161ed4ad00f50b26266c --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_java_stmt.mdx @@ -0,0 +1,3 @@ +```java +{{#include docs-examples/java/src/main/java/com/taos/example/StmtInsertExample.java}} +``` diff --git a/docs-cn/07-develop/03-insert-data/_js_line.mdx b/docs-cn/07-develop/03-insert-data/_js_line.mdx new file mode 100644 index 0000000000000000000000000000000000000000..172c9bc17b8cff8b2620720b235a9c8e69bd4197 --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_js_line.mdx @@ -0,0 +1,3 @@ +```js +{{#include docs-examples/node/nativeexample/influxdb_line_example.js}} +``` diff --git a/docs-cn/07-develop/03-insert-data/_js_opts_json.mdx b/docs-cn/07-develop/03-insert-data/_js_opts_json.mdx new file mode 100644 index 0000000000000000000000000000000000000000..20ac9ec91e8dc6675828b16d7da0acb09afd3b5f --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_js_opts_json.mdx @@ -0,0 +1,3 @@ +```js +{{#include docs-examples/node/nativeexample/opentsdb_json_example.js}} +``` diff --git a/docs-cn/07-develop/03-insert-data/_js_opts_telnet.mdx b/docs-cn/07-develop/03-insert-data/_js_opts_telnet.mdx new file mode 100644 index 0000000000000000000000000000000000000000..c3c8c40bd642f4f443de88e3db006ad50724d514 --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_js_opts_telnet.mdx @@ -0,0 +1,3 @@ +```js +{{#include docs-examples/node/nativeexample/opentsdb_telnet_example.js}} +``` diff --git a/docs-cn/07-develop/03-insert-data/_js_sql.mdx b/docs-cn/07-develop/03-insert-data/_js_sql.mdx new file mode 100644 index 0000000000000000000000000000000000000000..f5e17c76892a57a94192a95451b508b1c176c984 --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_js_sql.mdx @@ -0,0 +1,3 @@ +```js +{{#include docs-examples/node/nativeexample/insert_example.js}} +``` diff --git a/docs-cn/07-develop/03-insert-data/_js_stmt.mdx b/docs-cn/07-develop/03-insert-data/_js_stmt.mdx new file mode 100644 index 0000000000000000000000000000000000000000..17a6c9785c7dc1e3c3fa6a59982913f1f139f9c2 --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_js_stmt.mdx @@ -0,0 +1,12 @@ +```js title=一次绑定一行 +{{#include docs-examples/node/nativeexample/param_bind_example.js}} +``` + +```js title=一次绑定多行 +{{#include docs-examples/node/nativeexample/multi_bind_example.js:insertData}} +``` + +:::info +一次绑定一行效率不如一次绑定多行,但支持非 INSERT 语句。一次绑定多行效率更高,但仅支持 INSERT 语句。 + +::: diff --git a/docs-cn/07-develop/03-insert-data/_php_sql.mdx b/docs-cn/07-develop/03-insert-data/_php_sql.mdx new file mode 100644 index 0000000000000000000000000000000000000000..42d6a548479d526e7ecdba12807cf9cafb911ee5 --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_php_sql.mdx @@ -0,0 +1,3 @@ +```php +{{#include docs-examples/php/insert.php}} +``` diff --git a/docs-cn/07-develop/03-insert-data/_php_stmt.mdx b/docs-cn/07-develop/03-insert-data/_php_stmt.mdx new file mode 100644 index 0000000000000000000000000000000000000000..c1ba4ed3b160514fafb50886d799fc27e60927ed --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_php_stmt.mdx @@ -0,0 +1,3 @@ +```php +{{#include docs-examples/php/insert_stmt.php}} +``` diff --git a/docs-cn/07-develop/03-insert-data/_py_line.mdx b/docs-cn/07-develop/03-insert-data/_py_line.mdx new file mode 100644 index 0000000000000000000000000000000000000000..d3bb1ebb3403b53fa43bfc9d5d1a0de9764d7583 --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_py_line.mdx @@ -0,0 +1,3 @@ +```py +{{#include docs-examples/python/line_protocol_example.py}} +``` diff --git a/docs-cn/07-develop/03-insert-data/_py_opts_json.mdx b/docs-cn/07-develop/03-insert-data/_py_opts_json.mdx new file mode 100644 index 0000000000000000000000000000000000000000..cfbfe13ccfdb4f3f34b77300812863fdf70d0f59 --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_py_opts_json.mdx @@ -0,0 +1,3 @@ +```py +{{#include docs-examples/python/json_protocol_example.py}} +``` diff --git a/docs-cn/07-develop/03-insert-data/_py_opts_telnet.mdx b/docs-cn/07-develop/03-insert-data/_py_opts_telnet.mdx new file mode 100644 index 0000000000000000000000000000000000000000..14bc65a7a3da815abadf7f25c8deffeac666c8d7 --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_py_opts_telnet.mdx @@ -0,0 +1,3 @@ +```py +{{#include docs-examples/python/telnet_line_protocol_example.py}} +``` diff --git a/docs-cn/07-develop/03-insert-data/_py_sql.mdx b/docs-cn/07-develop/03-insert-data/_py_sql.mdx new file mode 100644 index 0000000000000000000000000000000000000000..c0e15b8ec115b9244d50a47c9eafec04bcfdd70c --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_py_sql.mdx @@ -0,0 +1,3 @@ +```py +{{#include docs-examples/python/native_insert_example.py}} +``` diff --git a/docs-cn/07-develop/03-insert-data/_py_stmt.mdx b/docs-cn/07-develop/03-insert-data/_py_stmt.mdx new file mode 100644 index 0000000000000000000000000000000000000000..8241ea86bc64ac64d842dc0a6cddc0eae0399503 --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_py_stmt.mdx @@ -0,0 +1,12 @@ +```py title=一次绑定一行 +{{#include docs-examples/python/bind_param_example.py}} +``` + +```py title=一次绑定多行 +{{#include docs-examples/python/multi_bind_example.py:bind_batch}} +``` + +:::info +一次绑定一行效率不如一次绑定多行,但支持非 INSERT 语句。一次绑定多行效率更高,但仅支持 INSERT 语句。 + +::: \ No newline at end of file diff --git a/docs-cn/07-develop/03-insert-data/_rust_line.mdx b/docs-cn/07-develop/03-insert-data/_rust_line.mdx new file mode 100644 index 0000000000000000000000000000000000000000..696ddb7b854751b8dee01047066f97f74212933f --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_rust_line.mdx @@ -0,0 +1,3 @@ +```rust +{{#include docs-examples/rust/schemalessexample/examples/influxdb_line_example.rs}} +``` diff --git a/docs-cn/07-develop/03-insert-data/_rust_opts_json.mdx b/docs-cn/07-develop/03-insert-data/_rust_opts_json.mdx new file mode 100644 index 0000000000000000000000000000000000000000..97d9052dacd1894cc7548a59951ecfaad9caee87 --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_rust_opts_json.mdx @@ -0,0 +1,3 @@ +```rust +{{#include docs-examples/rust/schemalessexample/examples/opentsdb_json_example.rs}} +``` diff --git a/docs-cn/07-develop/03-insert-data/_rust_opts_telnet.mdx b/docs-cn/07-develop/03-insert-data/_rust_opts_telnet.mdx new file mode 100644 index 0000000000000000000000000000000000000000..14021f43d8aff30c35dc30c5d278d4e51f375024 --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_rust_opts_telnet.mdx @@ -0,0 +1,3 @@ +```rust +{{#include docs-examples/rust/schemalessexample/examples/opentsdb_telnet_example.rs}} +``` diff --git a/docs-cn/07-develop/03-insert-data/_rust_sql.mdx b/docs-cn/07-develop/03-insert-data/_rust_sql.mdx new file mode 100644 index 0000000000000000000000000000000000000000..8e8013e4ad734efcc262ea2f750b82210a538e49 --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_rust_sql.mdx @@ -0,0 +1,3 @@ +```rust +{{#include docs-examples/rust/restexample/examples/insert_example.rs}} +``` diff --git a/docs-cn/07-develop/03-insert-data/_rust_stmt.mdx b/docs-cn/07-develop/03-insert-data/_rust_stmt.mdx new file mode 100644 index 0000000000000000000000000000000000000000..590a7a0e717426ed0235331c49dfc578bc55b2f7 --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/_rust_stmt.mdx @@ -0,0 +1,3 @@ +```rust +{{#include docs-examples/rust/nativeexample/examples/stmt_example.rs}} +``` diff --git a/docs-cn/07-develop/03-insert-data/index.md b/docs-cn/07-develop/03-insert-data/index.md new file mode 100644 index 0000000000000000000000000000000000000000..55a28e4a8ba13501e2f481c9aba67b7300da98d0 --- /dev/null +++ b/docs-cn/07-develop/03-insert-data/index.md @@ -0,0 +1,12 @@ +--- +title: 写入数据 +--- + +TDengine 支持多种写入协议,包括 SQL,InfluxDB Line 协议, OpenTSDB Telnet 协议,OpenTSDB JSON 格式协议。数据可以单条插入,也可以批量插入,可以插入一个数据采集点的数据,也可以同时插入多个数据采集点的数据。同时,TDengine 支持多线程插入,支持时间乱序数据插入,也支持历史数据插入。InfluxDB Line 协议、OpenTSDB Telnet 协议和 OpenTSDB JSON 格式协议是 TDengine 支持的三种无模式写入协议。使用无模式方式写入无需提前创建超级表和子表,并且引擎能自适用数据对表结构做调整。 + +```mdx-code-block +import DocCardList from '@theme/DocCardList'; +import {useCurrentSidebarCategory} from '@docusaurus/theme-common'; + + +``` \ No newline at end of file diff --git a/docs-cn/07-develop/04-query-data/_c.mdx b/docs-cn/07-develop/04-query-data/_c.mdx new file mode 100644 index 0000000000000000000000000000000000000000..76c9067e2f6af19465cf7c52c3e9b48bb868547d --- /dev/null +++ b/docs-cn/07-develop/04-query-data/_c.mdx @@ -0,0 +1,3 @@ +```c +{{#include docs-examples/c/query_example.c}} +``` \ No newline at end of file diff --git a/docs-cn/07-develop/04-query-data/_c_async.mdx b/docs-cn/07-develop/04-query-data/_c_async.mdx new file mode 100644 index 0000000000000000000000000000000000000000..09f3d3b3ff6d6644f837642ef41db459ba7c5753 --- /dev/null +++ b/docs-cn/07-develop/04-query-data/_c_async.mdx @@ -0,0 +1,3 @@ +```c +{{#include docs-examples/c/async_query_example.c:demo}} +``` \ No newline at end of file diff --git a/docs-cn/07-develop/04-query-data/_category_.yml b/docs-cn/07-develop/04-query-data/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..69273cb31cc54fd438bf82b408e529f8d3d9ccb4 --- /dev/null +++ b/docs-cn/07-develop/04-query-data/_category_.yml @@ -0,0 +1 @@ +label: 查询数据 diff --git a/docs-cn/07-develop/04-query-data/_cs.mdx b/docs-cn/07-develop/04-query-data/_cs.mdx new file mode 100644 index 0000000000000000000000000000000000000000..2ab52feb564eff0fe251bc9900ea2539171e5dba --- /dev/null +++ b/docs-cn/07-develop/04-query-data/_cs.mdx @@ -0,0 +1,3 @@ +```csharp +{{#include docs-examples/csharp/QueryExample.cs}} +``` diff --git a/docs-cn/07-develop/04-query-data/_cs_async.mdx b/docs-cn/07-develop/04-query-data/_cs_async.mdx new file mode 100644 index 0000000000000000000000000000000000000000..f868994b303e62016b5e2f9304275135855c6ae5 --- /dev/null +++ b/docs-cn/07-develop/04-query-data/_cs_async.mdx @@ -0,0 +1,3 @@ +```csharp +{{#include docs-examples/csharp/AsyncQueryExample.cs}} +``` diff --git a/docs-cn/07-develop/04-query-data/_go.mdx b/docs-cn/07-develop/04-query-data/_go.mdx new file mode 100644 index 0000000000000000000000000000000000000000..417c12315c06517e2f3de850ac9a379b7714b519 --- /dev/null +++ b/docs-cn/07-develop/04-query-data/_go.mdx @@ -0,0 +1,3 @@ +```go +{{#include docs-examples/go/query/sync/main.go}} +``` diff --git a/docs-cn/07-develop/04-query-data/_go_async.mdx b/docs-cn/07-develop/04-query-data/_go_async.mdx new file mode 100644 index 0000000000000000000000000000000000000000..72fff411b980a0dcbdcaf4274722c63e0351db6f --- /dev/null +++ b/docs-cn/07-develop/04-query-data/_go_async.mdx @@ -0,0 +1,3 @@ +```go +{{#include docs-examples/go/query/async/main.go}} +``` diff --git a/docs-cn/07-develop/04-query-data/_java.mdx b/docs-cn/07-develop/04-query-data/_java.mdx new file mode 100644 index 0000000000000000000000000000000000000000..519b9266144486231caf3ee593e973d438941ee4 --- /dev/null +++ b/docs-cn/07-develop/04-query-data/_java.mdx @@ -0,0 +1,3 @@ +```java +{{#include docs-examples/java/src/main/java/com/taos/example/RestQueryExample.java}} +``` diff --git a/docs-cn/07-develop/04-query-data/_js.mdx b/docs-cn/07-develop/04-query-data/_js.mdx new file mode 100644 index 0000000000000000000000000000000000000000..c5e4c4f3fc20d3940a2bc6e13e6a5dea8a15ff13 --- /dev/null +++ b/docs-cn/07-develop/04-query-data/_js.mdx @@ -0,0 +1,3 @@ +```js +{{#include docs-examples/node/nativeexample/query_example.js}} +``` diff --git a/docs-cn/07-develop/04-query-data/_js_async.mdx b/docs-cn/07-develop/04-query-data/_js_async.mdx new file mode 100644 index 0000000000000000000000000000000000000000..c65d54ed12f6c4bbeb333e0de0ba9ca4638bff84 --- /dev/null +++ b/docs-cn/07-develop/04-query-data/_js_async.mdx @@ -0,0 +1,3 @@ +```js +{{#include docs-examples/node/nativeexample/async_query_example.js}} +``` diff --git a/docs-cn/07-develop/04-query-data/_php.mdx b/docs-cn/07-develop/04-query-data/_php.mdx new file mode 100644 index 0000000000000000000000000000000000000000..6264bd99f534fbd800f1f349d93ac69b31c77397 --- /dev/null +++ b/docs-cn/07-develop/04-query-data/_php.mdx @@ -0,0 +1,3 @@ +```go +{{#include docs-examples/php/query.php}} +``` diff --git a/docs-cn/07-develop/04-query-data/_py.mdx b/docs-cn/07-develop/04-query-data/_py.mdx new file mode 100644 index 0000000000000000000000000000000000000000..6a1bacdd3ef91e9484c1d87d6a22de8b128e2144 --- /dev/null +++ b/docs-cn/07-develop/04-query-data/_py.mdx @@ -0,0 +1,11 @@ +通过迭代逐行获取查询结果。 + +```py +{{#include docs-examples/python/query_example.py:iter}} +``` + +一次获取所有查询结果,并把每一行转化为一个字典返回。 + +```py +{{#include docs-examples/python/query_example.py:fetch_all}} +``` diff --git a/docs-cn/07-develop/04-query-data/_py_async.mdx b/docs-cn/07-develop/04-query-data/_py_async.mdx new file mode 100644 index 0000000000000000000000000000000000000000..2399a50df645804788036e17bf223c53482d4eaf --- /dev/null +++ b/docs-cn/07-develop/04-query-data/_py_async.mdx @@ -0,0 +1,8 @@ +```py +{{#include docs-examples/python/async_query_example.py}} +``` + +:::note +这个示例程序,目前在 Windows 系统上还无法运行 + +::: diff --git a/docs-cn/07-develop/04-query-data/_rust.mdx b/docs-cn/07-develop/04-query-data/_rust.mdx new file mode 100644 index 0000000000000000000000000000000000000000..742d70fd025ff44b573eedf78441c9d73defad45 --- /dev/null +++ b/docs-cn/07-develop/04-query-data/_rust.mdx @@ -0,0 +1,3 @@ +```rust +{{#include docs-examples/rust/restexample/examples/query_example.rs}} +``` diff --git a/docs-cn/07-develop/04-query-data/index.mdx b/docs-cn/07-develop/04-query-data/index.mdx new file mode 100644 index 0000000000000000000000000000000000000000..b0a6bad3eaad174a97d8dce4e1ba0125cbf5dc03 --- /dev/null +++ b/docs-cn/07-develop/04-query-data/index.mdx @@ -0,0 +1,181 @@ +--- +title: 查询数据 +description: "主要查询功能,通过连接器执行同步查询和异步查询" +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import JavaQuery from "./_java.mdx"; +import PyQuery from "./_py.mdx"; +import GoQuery from "./_go.mdx"; +import RustQuery from "./_rust.mdx"; +import NodeQuery from "./_js.mdx"; +import CsQuery from "./_cs.mdx"; +import CQuery from "./_c.mdx"; +import PhpQuery from "./_php.mdx"; +import PyAsync from "./_py_async.mdx"; +import NodeAsync from "./_js_async.mdx"; +import CsAsync from "./_cs_async.mdx"; +import CAsync from "./_c_async.mdx"; + +## 主要查询功能 + +TDengine 采用 SQL 作为查询语言。应用程序可以通过 REST API 或连接器发送 SQL 语句,用户还可以通过 TDengine 命令行工具 taos 手动执行 SQL 即席查询(Ad-Hoc Query)。TDengine 支持如下查询功能: + +- 单列、多列数据查询 +- 标签和数值的多种过滤条件:>, <, =, <\>, like 等 +- 聚合结果的分组(Group by)、排序(Order by)、约束输出(Limit/Offset) +- 数值列及聚合结果的四则运算 +- 时间戳对齐的连接查询(Join Query: 隐式连接)操作 +- 多种聚合/计算函数: count, max, min, avg, sum, twa, stddev, leastsquares, top, bottom, first, last, percentile, apercentile, last_row, spread, diff 等 + +例如:在命令行工具 taos 中,从表 d1001 中查询出 voltage > 215 的记录,按时间降序排列,仅仅输出 2 条。 + +```sql +taos> select * from d1001 where voltage > 215 order by ts desc limit 2; + ts | current | voltage | phase | +====================================================================================== + 2018-10-03 14:38:16.800 | 12.30000 | 221 | 0.31000 | + 2018-10-03 14:38:15.000 | 12.60000 | 218 | 0.33000 | +Query OK, 2 row(s) in set (0.001100s) +``` + +为满足物联网场景的需求,TDengine 支持几个特殊的函数,比如 twa(时间加权平均),spread (最大值与最小值的差),last_row(最后一条记录)等,更多与物联网场景相关的函数将添加进来。TDengine 还支持连续查询。 + +具体的查询语法请看 [TAOS SQL 的数据查询](/taos-sql/select) 章节。 + +## 多表聚合查询 + +物联网场景中,往往同一个类型的数据采集点有多个。TDengine 采用超级表(STable)的概念来描述某一个类型的数据采集点,一张普通的表来描述一个具体的数据采集点。同时 TDengine 使用标签来描述数据采集点的静态属性,一个具体的数据采集点有具体的标签值。通过指定标签的过滤条件,TDengine 提供了一高效的方法将超级表(某一类型的数据采集点)所属的子表进行聚合查询。对普通表的聚合函数以及绝大部分操作都适用于超级表,语法完全一样。 + +### 示例一 + +在 TAOS Shell,查找北京所有智能电表采集的电压平均值,并按照 location 分组。 + +``` +taos> SELECT AVG(voltage) FROM meters GROUP BY location; + avg(voltage) | location | +============================================================= + 222.000000000 | Beijing.Haidian | + 219.200000000 | Beijing.Chaoyang | +Query OK, 2 row(s) in set (0.002136s) +``` + +### 示例二 + +在 TAOS shell, 查找 groupId 为 2 的所有智能电表过去 24 小时的记录条数,电流的最大值。 + +``` +taos> SELECT count(*), max(current) FROM meters where groupId = 2 and ts > now - 24h; + cunt(*) | max(current) | +================================== + 5 | 13.4 | +Query OK, 1 row(s) in set (0.002136s) +``` + +TDengine 仅容许对属于同一个超级表的表之间进行聚合查询,不同超级表之间的聚合查询不支持。在 [TAOS SQL 的数据查询](/taos-sql/select) 一章,查询类操作都会注明是否支持超级表。 + +## 降采样查询、插值 + +物联网场景里,经常需要通过降采样(down sampling)将采集的数据按时间段进行聚合。TDengine 提供了一个简便的关键词 interval 让按照时间窗口的查询操作变得极为简单。比如,将智能电表 d1001 采集的电流值每 10 秒钟求和 + +``` +taos> SELECT sum(current) FROM d1001 INTERVAL(10s); + ts | sum(current) | +====================================================== + 2018-10-03 14:38:00.000 | 10.300000191 | + 2018-10-03 14:38:10.000 | 24.900000572 | +Query OK, 2 row(s) in set (0.000883s) +``` + +降采样操作也适用于超级表,比如:将北京所有智能电表采集的电流值每秒钟求和 + +``` +taos> SELECT SUM(current) FROM meters where location like "Beijing%" INTERVAL(1s); + ts | sum(current) | +====================================================== + 2018-10-03 14:38:04.000 | 10.199999809 | + 2018-10-03 14:38:05.000 | 32.900000572 | + 2018-10-03 14:38:06.000 | 11.500000000 | + 2018-10-03 14:38:15.000 | 12.600000381 | + 2018-10-03 14:38:16.000 | 36.000000000 | +Query OK, 5 row(s) in set (0.001538s) +``` + +降采样操作也支持时间偏移,比如:将所有智能电表采集的电流值每秒钟求和,但要求每个时间窗口从 500 毫秒开始 + +``` +taos> SELECT SUM(current) FROM meters INTERVAL(1s, 500a); + ts | sum(current) | +====================================================== + 2018-10-03 14:38:04.500 | 11.189999809 | + 2018-10-03 14:38:05.500 | 31.900000572 | + 2018-10-03 14:38:06.500 | 11.600000000 | + 2018-10-03 14:38:15.500 | 12.300000381 | + 2018-10-03 14:38:16.500 | 35.000000000 | +Query OK, 5 row(s) in set (0.001521s) +``` + +物联网场景里,每个数据采集点采集数据的时间是难同步的,但很多分析算法(比如 FFT)需要把采集的数据严格按照时间等间隔的对齐,在很多系统里,需要应用自己写程序来处理,但使用 TDengine 的降采样操作就轻松解决。 + +如果一个时间间隔里,没有采集的数据,TDengine 还提供插值计算的功能。 + +语法规则细节请见 [TAOS SQL 的按时间窗口切分聚合](/taos-sql/interval) 章节。 + +## 示例代码 + +### 查询数据 + +在 [SQL 写入](/develop/insert-data/sql-writing) 一章,我们创建了 power 数据库,并向 meters 表写入了一些数据,以下示例代码展示如何查询这个表的数据。 + + + + + + + + + + + + + + + + + + + + + + + + + + + + +:::note + +1. 无论是使用 REST 连接还是原生连接的连接器,以上示例代码都能正常工作。 +2. 唯一需要注意的是:由于 REST 接口无状态, 不能使用 `use db` 语句来切换数据库。 + +::: + +### 异步查询 + +除同步查询 API 之外,TDengine 还提供性能更高的异步调用 API 处理数据插入、查询操作。在软硬件环境相同的情况下,异步 API 处理数据插入的速度比同步 API 快 2-4 倍。异步 API 采用非阻塞式的调用方式,在系统真正完成某个具体数据库操作前,立即返回。调用的线程可以去处理其他工作,从而可以提升整个应用的性能。异步 API 在网络延迟严重的情况下,优点尤为突出。 + +需要注意的是,只有使用原生连接的连接器,才能使用异步查询功能。 + + + + + + + + + + + + diff --git a/docs-cn/07-develop/05-continuous-query.mdx b/docs-cn/07-develop/05-continuous-query.mdx new file mode 100644 index 0000000000000000000000000000000000000000..2fd1b3cc755188f513fe511541a84efa3558d3ea --- /dev/null +++ b/docs-cn/07-develop/05-continuous-query.mdx @@ -0,0 +1,84 @@ +--- +sidebar_label: 连续查询 +description: "连续查询是一个按照预设频率自动执行的查询功能,提供按照时间窗口的聚合查询能力,是一种简化的时间驱动流式计算。" +title: "连续查询(Continuous Query)" +--- + +连续查询是 TDengine 定期自动执行的查询,采用滑动窗口的方式进行计算,是一种简化的时间驱动的流式计算。针对库中的表或超级表,TDengine 可提供定期自动执行的连续查询,用户可让 TDengine 推送查询的结果,也可以将结果再写回到 TDengine 中。每次执行的查询是一个时间窗口,时间窗口随着时间流动向前滑动。在定义连续查询的时候需要指定时间窗口(time window, 参数 interval)大小和每次前向增量时间(forward sliding times, 参数 sliding)。 + +TDengine 的连续查询采用时间驱动模式,可以直接使用 TAOS SQL 进行定义,不需要额外的操作。使用连续查询,可以方便快捷地按照时间窗口生成结果,从而对原始采集数据进行降采样(down sampling)。用户通过 TAOS SQL 定义连续查询以后,TDengine 自动在最后的一个完整的时间周期末端拉起查询,并将计算获得的结果推送给用户或者写回 TDengine。 + +TDengine 提供的连续查询与普通流计算中的时间窗口计算具有以下区别: + +- 不同于流计算的实时反馈计算结果,连续查询只在时间窗口关闭以后才开始计算。例如时间周期是 1 天,那么当天的结果只会在 23:59:59 以后才会生成。 +- 如果有历史记录写入到已经计算完成的时间区间,连续查询并不会重新进行计算,也不会重新将结果推送给用户。对于写回 TDengine 的模式,也不会更新已经存在的计算结果。 +- 使用连续查询推送结果的模式,服务端并不缓存客户端计算状态,也不提供 Exactly-Once 的语义保证。如果用户的应用端崩溃,再次拉起的连续查询将只会从再次拉起的时间开始重新计算最近的一个完整的时间窗口。如果使用写回模式,TDengine 可确保数据写回的有效性和连续性。 + +## 连续查询语法 + +```sql +[CREATE TABLE AS] SELECT select_expr [, select_expr ...] + FROM {tb_name_list} + [WHERE where_condition] + [INTERVAL(interval_val [, interval_offset]) [SLIDING sliding_val]] + +``` + +INTERVAL: 连续查询作用的时间窗口 + +SLIDING: 连续查询的时间窗口向前滑动的时间间隔 + +## 使用连续查询 + +下面以智能电表场景为例介绍连续查询的具体使用方法。假设我们通过下列 SQL 语句创建了超级表和子表: + +```sql +create table meters (ts timestamp, current float, voltage int, phase float) tags (location binary(64), groupId int); +create table D1001 using meters tags ("Beijing.Chaoyang", 2); +create table D1002 using meters tags ("Beijing.Haidian", 2); +... +``` + +可以通过下面这条 SQL 语句以一分钟为时间窗口、30 秒为前向增量统计这些电表的平均电压。 + +```sql +select avg(voltage) from meters interval(1m) sliding(30s); +``` + +每次执行这条语句,都会重新计算所有数据。 如果需要每隔 30 秒执行一次来增量计算最近一分钟的数据,可以把上面的语句改进成下面的样子,每次使用不同的 `startTime` 并定期执行: + +```sql +select avg(voltage) from meters where ts > {startTime} interval(1m) sliding(30s); +``` + +这样做没有问题,但 TDengine 提供了更简单的方法,只要在最初的查询语句前面加上 `create table {tableName} as` 就可以了,例如: + +```sql +create table avg_vol as select avg(voltage) from meters interval(1m) sliding(30s); +``` + +会自动创建一个名为 `avg_vol` 的新表,然后每隔 30 秒,TDengine 会增量执行 `as` 后面的 SQL 语句,并将查询结果写入这个表中,用户程序后续只要从 `avg_vol` 中查询数据即可。例如: + +```sql +taos> select * from avg_vol; + ts | avg_voltage_ | +=================================================== + 2020-07-29 13:37:30.000 | 222.0000000 | + 2020-07-29 13:38:00.000 | 221.3500000 | + 2020-07-29 13:38:30.000 | 220.1700000 | + 2020-07-29 13:39:00.000 | 223.0800000 | +``` + +需要注意,查询时间窗口的最小值是 10 毫秒,没有时间窗口范围的上限。 + +此外,TDengine 还支持用户指定连续查询的起止时间。如果不输入开始时间,连续查询将从第一条原始数据所在的时间窗口开始;如果没有输入结束时间,连续查询将永久运行;如果用户指定了结束时间,连续查询在系统时间达到指定的时间以后停止运行。比如使用下面的 SQL 创建的连续查询将运行一小时,之后会自动停止。 + +```sql +create table avg_vol as select avg(voltage) from meters where ts > now and ts <= now + 1h interval(1m) sliding(30s); +``` + +需要说明的是,上面例子中的 `now` 是指创建连续查询的时间,而不是查询执行的时间,否则,查询就无法自动停止了。另外,为了尽量避免原始数据延迟写入导致的问题,TDengine 中连续查询的计算有一定的延迟。也就是说,一个时间窗口过去后,TDengine 并不会立即计算这个窗口的数据,所以要稍等一会(一般不会超过 1 分钟)才能查到计算结果。 + +## 管理连续查询 + +用户可在控制台中通过 `show streams` 命令来查看系统中全部运行的连续查询,并可以通过 `kill stream` 命令杀掉对应的连续查询。后续版本会提供更细粒度和便捷的连续查询管理命令。 diff --git a/docs-cn/07-develop/06-subscribe.mdx b/docs-cn/07-develop/06-subscribe.mdx new file mode 100644 index 0000000000000000000000000000000000000000..d471c114e827d7c4b40195c2c1b3c8f6a9d26ed4 --- /dev/null +++ b/docs-cn/07-develop/06-subscribe.mdx @@ -0,0 +1,253 @@ +--- +sidebar_label: 数据订阅 +description: "轻量级的数据订阅与推送服务。连续写入到 TDengine 中的时序数据能够被自动推送到订阅客户端。" +title: 数据订阅 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import Java from "./_sub_java.mdx"; +import Python from "./_sub_python.mdx"; +import Go from "./_sub_go.mdx"; +import Rust from "./_sub_rust.mdx"; +import Node from "./_sub_node.mdx"; +import CSharp from "./_sub_cs.mdx"; +import CDemo from "./_sub_c.mdx"; + +基于数据天然的时间序列特性,TDengine 的数据写入(insert)与消息系统的数据发布(pub)逻辑上一致,均可视为系统中插入一条带时间戳的新记录。同时,TDengine 在内部严格按照数据时间序列单调递增的方式保存数据。本质上来说,TDengine 中每一张表均可视为一个标准的消息队列。 + +TDengine 内嵌支持轻量级的消息订阅与推送服务。使用系统提供的 API,用户可使用普通查询语句订阅数据库中的一张或多张表。订阅的逻辑和操作状态的维护均是由客户端完成,客户端定时轮询服务器是否有新的记录到达,有新的记录到达就会将结果反馈到客户。 + +TDengine 的订阅与推送服务的状态是由客户端维持,TDengine 服务端并不维持。因此如果应用重启,从哪个时间点开始获取最新数据,由应用决定。 + +TDengine 的 API 中,与订阅相关的主要有以下三个: + +```c +taos_subscribe +taos_consume +taos_unsubscribe +``` + +这些 API 的文档请见 [C/C++ Connector](/reference/connector/cpp),下面仍以智能电表场景为例介绍一下它们的具体用法(超级表和子表结构请参考上一节“连续查询”),完整的示例代码可以在 [这里](https://github.com/taosdata/TDengine/blob/master/examples/c/subscribe.c) 找到。 + +如果我们希望当某个电表的电流超过一定限制(比如 10A)后能得到通知并进行一些处理, 有两种方法:一是分别对每张子表进行查询,每次查询后记录最后一条数据的时间戳,后续只查询这个时间戳之后的数据: + +```sql +select * from D1001 where ts > {last_timestamp1} and current > 10; +select * from D1002 where ts > {last_timestamp2} and current > 10; +... +``` + +这确实可行,但随着电表数量的增加,查询数量也会增加,客户端和服务端的性能都会受到影响,当电表数增长到一定的程度,系统就无法承受了。 + +另一种方法是对超级表进行查询。这样,无论有多少电表,都只需一次查询: + +```sql +select * from meters where ts > {last_timestamp} and current > 10; +``` + +但是,如何选择 `last_timestamp` 就成了一个新的问题。因为,一方面数据的产生时间(也就是数据时间戳)和数据入库的时间一般并不相同,有时偏差还很大;另一方面,不同电表的数据到达 TDengine 的时间也会有差异。所以,如果我们在查询中使用最慢的那台电表的数据的时间戳作为 `last_timestamp`,就可能重复读入其它电表的数据;如果使用最快的电表的时间戳,其它电表的数据就可能被漏掉。 + +TDengine 的订阅功能为上面这个问题提供了一个彻底的解决方案。 + +首先是使用 `taos_subscribe` 创建订阅: + +```c +TAOS_SUB* tsub = NULL; +if (async) { +  // create an asynchronized subscription, the callback function will be called every 1s +  tsub = taos_subscribe(taos, restart, topic, sql, subscribe_callback, &blockFetch, 1000); +} else { +  // create an synchronized subscription, need to call 'taos_consume' manually +  tsub = taos_subscribe(taos, restart, topic, sql, NULL, NULL, 0); +} +``` + +TDengine 中的订阅既可以是同步的,也可以是异步的,上面的代码会根据从命令行获取的参数 `async` 的值来决定使用哪种方式。这里,同步的意思是用户程序要直接调用 `taos_consume` 来拉取数据,而异步则由 API 在内部的另一个线程中调用 `taos_consume`,然后把拉取到的数据交给回调函数 `subscribe_callback`去处理。(注意,`subscribe_callback` 中不宜做较为耗时的操作,否则有可能导致客户端阻塞等不可控的问题。) + +参数 `taos` 是一个已经建立好的数据库连接,在同步模式下无特殊要求。但在异步模式下,需要注意它不会被其它线程使用,否则可能导致不可预计的错误,因为回调函数在 API 的内部线程中被调用,而 TDengine 的部分 API 不是线程安全的。 + +参数 `sql` 是查询语句,可以在其中使用 where 子句指定过滤条件。在我们的例子中,如果只想订阅电流超过 10A 时的数据,可以这样写: + +```sql +select * from meters where current > 10; +``` + +注意,这里没有指定起始时间,所以会读到所有时间的数据。如果只想从一天前的数据开始订阅,而不需要更早的历史数据,可以再加上一个时间条件: + +```sql +select * from meters where ts > now - 1d and current > 10; +``` + +订阅的 `topic` 实际上是它的名字,因为订阅功能是在客户端 API 中实现的,所以没必要保证它全局唯一,但需要它在一台客户端机器上唯一。 + +如果名为 `topic` 的订阅不存在,参数 `restart` 没有意义;但如果用户程序创建这个订阅后退出,当它再次启动并重新使用这个 `topic` 时,`restart` 就会被用于决定是从头开始读取数据,还是接续上次的位置进行读取。本例中,如果 `restart` 是 **true**(非零值),用户程序肯定会读到所有数据。但如果这个订阅之前就存在了,并且已经读取了一部分数据,且 `restart` 是 **false**(**0**),用户程序就不会读到之前已经读取的数据了。 + +`taos_subscribe`的最后一个参数是以毫秒为单位的轮询周期。在同步模式下,如果前后两次调用 `taos_consume` 的时间间隔小于此时间,`taos_consume` 会阻塞,直到间隔超过此时间。异步模式下,这个时间是两次调用回调函数的最小时间间隔。 + +`taos_subscribe` 的倒数第二个参数用于用户程序向回调函数传递附加参数,订阅 API 不对其做任何处理,只原样传递给回调函数。此参数在同步模式下无意义。 + +订阅创建以后,就可以消费其数据了,同步模式下,示例代码是下面的 else 部分: + +```c +if (async) { +  getchar(); +} else while(1) { +  TAOS_RES* res = taos_consume(tsub); +  if (res == NULL) { +    printf("failed to consume data."); +    break; +  } else { +    print_result(res, blockFetch); +    getchar(); +  } +} +``` + +这里是一个 **while** 循环,用户每按一次回车键就调用一次 `taos_consume`,而 `taos_consume` 的返回值是查询到的结果集,与 `taos_use_result` 完全相同,例子中使用这个结果集的代码是函数 `print_result`: + +```c +void print_result(TAOS_RES* res, int blockFetch) { +  TAOS_ROW row = NULL; +  int num_fields = taos_num_fields(res); +  TAOS_FIELD* fields = taos_fetch_fields(res); +  int nRows = 0; +  if (blockFetch) { +    nRows = taos_fetch_block(res, &row); +    for (int i = 0; i < nRows; i++) { +      char temp[256]; +      taos_print_row(temp, row + i, fields, num_fields); +      puts(temp); +    } +  } else { +    while ((row = taos_fetch_row(res))) { +      char temp[256]; +      taos_print_row(temp, row, fields, num_fields); +      puts(temp); +      nRows++; +    } +  } +  printf("%d rows consumed.\n", nRows); +} +``` + +其中的 `taos_print_row` 用于处理订阅到数据,在我们的例子中,它会打印出所有符合条件的记录。而异步模式下,消费订阅到的数据则显得更为简单: + +```c +void subscribe_callback(TAOS_SUB* tsub, TAOS_RES *res, void* param, int code) { +  print_result(res, *(int*)param); +} +``` + +当要结束一次数据订阅时,需要调用 `taos_unsubscribe`: + +```c +taos_unsubscribe(tsub, keep); +``` + +其第二个参数,用于决定是否在客户端保留订阅的进度信息。如果这个参数是**false**(**0**),那无论下次调用 `taos_subscribe` 时的 `restart` 参数是什么,订阅都只能重新开始。另外,进度信息的保存位置是 _{DataDir}/subscribe/_ 这个目录下,每个订阅有一个与其 `topic` 同名的文件,删掉某个文件,同样会导致下次创建其对应的订阅时只能重新开始。 + +代码介绍完毕,我们来看一下实际的运行效果。假设: + +- 示例代码已经下载到本地 +- TDengine 也已经在同一台机器上安装好 +- 示例所需的数据库、超级表、子表已经全部创建好 + +则可以在示例代码所在目录执行以下命令来编译并启动示例程序: + +```bash +make +./subscribe -sql='select * from meters where current > 10;' +``` + +示例程序启动后,打开另一个终端窗口,启动 TDengine CLI 向 **D1001** 插入一条电流为 12A 的数据: + +```sql +$ taos +> use test; +> insert into D1001 values(now, 12, 220, 1); +``` + +这时,因为电流超过了 10A,您应该可以看到示例程序将它输出到了屏幕上。您可以继续插入一些数据观察示例程序的输出。 + +## 示例程序 + +下面的示例程序展示是如何使用连接器订阅所有电流超过 10A 的记录。 + +### 准备数据 + +``` +# create database "power" +taos> create database power; +# use "power" as the database in following operations +taos> use power; +# create super table "meters" +taos> create table meters(ts timestamp, current float, voltage int, phase int) tags(location binary(64), groupId int); +# create tabes using the schema defined by super table "meters" +taos> create table d1001 using meters tags ("Beijing.Chaoyang", 2); +taos> create table d1002 using meters tags ("Beijing.Haidian", 2); +# insert some rows +taos> insert into d1001 values("2020-08-15 12:00:00.000", 12, 220, 1),("2020-08-15 12:10:00.000", 12.3, 220, 2),("2020-08-15 12:20:00.000", 12.2, 220, 1); +taos> insert into d1002 values("2020-08-15 12:00:00.000", 9.9, 220, 1),("2020-08-15 12:10:00.000", 10.3, 220, 1),("2020-08-15 12:20:00.000", 11.2, 220, 1); +# filter out the rows in which current is bigger than 10A +taos> select * from meters where current > 10; + ts | current | voltage | phase | location | groupid | +=========================================================================================================== + 2020-08-15 12:10:00.000 | 10.30000 | 220 | 1 | Beijing.Haidian | 2 | + 2020-08-15 12:20:00.000 | 11.20000 | 220 | 1 | Beijing.Haidian | 2 | + 2020-08-15 12:00:00.000 | 12.00000 | 220 | 1 | Beijing.Chaoyang | 2 | + 2020-08-15 12:10:00.000 | 12.30000 | 220 | 2 | Beijing.Chaoyang | 2 | + 2020-08-15 12:20:00.000 | 12.20000 | 220 | 1 | Beijing.Chaoyang | 2 | +Query OK, 5 row(s) in set (0.004896s) +``` +### 示例代码 + + + + + + + + + {/* + + */} + + + + {/* + + + + + */} + + + + + +### 运行示例程序 + +示例程序会先消费符合查询条件的所有历史数据: + +```bash +ts: 1597464000000 current: 12.0 voltage: 220 phase: 1 location: Beijing.Chaoyang groupid : 2 +ts: 1597464600000 current: 12.3 voltage: 220 phase: 2 location: Beijing.Chaoyang groupid : 2 +ts: 1597465200000 current: 12.2 voltage: 220 phase: 1 location: Beijing.Chaoyang groupid : 2 +ts: 1597464600000 current: 10.3 voltage: 220 phase: 1 location: Beijing.Haidian groupid : 2 +ts: 1597465200000 current: 11.2 voltage: 220 phase: 1 location: Beijing.Haidian groupid : 2 +``` + +接着,使用 TDengine CLI 向表中新增一条数据: + +``` +# taos +taos> use power; +taos> insert into d1001 values(now, 12.4, 220, 1); +``` + +因为这条数据的电流大于 10A,示例程序会将其消费: + +``` +ts: 1651146662805 current: 12.4 voltage: 220 phase: 1 location: Beijing.Chaoyang groupid: 2 +``` diff --git a/docs-cn/07-develop/07-cache.md b/docs-cn/07-develop/07-cache.md new file mode 100644 index 0000000000000000000000000000000000000000..fd31335310d62d792e5173e38a9aa778ee6c6c60 --- /dev/null +++ b/docs-cn/07-develop/07-cache.md @@ -0,0 +1,21 @@ +--- +sidebar_label: 缓存 +title: 缓存 +description: "提供写驱动的缓存管理机制,将每个表最近写入的一条记录持续保存在缓存中,可以提供高性能的最近状态查询。" +--- + +TDengine 采用时间驱动缓存管理策略(First-In-First-Out,FIFO),又称为写驱动的缓存管理机制。这种策略有别于读驱动的数据缓存模式(Least-Recent-Used,LRU),直接将最近写入的数据保存在系统的缓存中。当缓存达到临界值的时候,将最早的数据批量写入磁盘。一般意义上来说,对于物联网数据的使用,用户最为关心最近产生的数据,即当前状态。TDengine 充分利用了这一特性,将最近到达的(当前状态)数据保存在缓存中。 + +TDengine 通过查询函数向用户提供毫秒级的数据获取能力。直接将最近到达的数据保存在缓存中,可以更加快速地响应用户针对最近一条或一批数据的查询分析,整体上提供更快的数据库查询响应能力。从这个意义上来说,可通过设置合适的配置参数将 TDengine 作为数据缓存来使用,而不需要再部署额外的缓存系统,可有效地简化系统架构,降低运维的成本。需要注意的是,TDengine 重启以后系统的缓存将被清空,之前缓存的数据均会被批量写入磁盘,缓存的数据将不会像专门的 key-value 缓存系统再将之前缓存的数据重新加载到缓存中。 + +TDengine 分配固定大小的内存空间作为缓存空间,缓存空间可根据应用的需求和硬件资源配置。通过适当的设置缓存空间,TDengine 可以提供极高性能的写入和查询的支持。TDengine 中每个虚拟节点(virtual node)创建时分配独立的缓存池。每个虚拟节点管理自己的缓存池,不同虚拟节点间不共享缓存池。每个虚拟节点内部所属的全部表共享该虚拟节点的缓存池。 + +TDengine 将内存池按块划分进行管理,数据在内存块里是以行(row)的形式存储。一个 vnode 的内存池是在 vnode 创建时按块分配好,而且每个内存块按照先进先出的原则进行管理。在创建内存池时,块的大小由系统配置参数 cache 决定;每个 vnode 中内存块的数目则由配置参数 blocks 决定。因此对于一个 vnode,总的内存大小为:`cache * blocks`。一个 cache block 需要保证每张表能存储至少几十条以上记录,才会有效率。 + +你可以通过函数 last_row() 快速获取一张表或一张超级表的最后一条记录,这样很便于在大屏显示各设备的实时状态或采集值。例如: + +```sql +select last_row(voltage) from meters where location='Beijing.Chaoyang'; +``` + +该 SQL 语句将获取所有位于北京朝阳区的电表最后记录的电压值。 diff --git a/docs-cn/07-develop/08-udf.md b/docs-cn/07-develop/08-udf.md new file mode 100644 index 0000000000000000000000000000000000000000..09681650db32200e60c0fdb787d3e455dd339d85 --- /dev/null +++ b/docs-cn/07-develop/08-udf.md @@ -0,0 +1,208 @@ +--- +sidebar_label: 用户定义函数 +title: UDF(用户定义函数) +description: "支持用户编码的聚合函数和标量函数,在查询中嵌入并使用用户定义函数,拓展查询的能力和功能。" +--- + +在有些应用场景中,应用逻辑需要的查询无法直接使用系统内置的函数来表示。利用 UDF 功能,TDengine 可以插入用户编写的处理代码并在查询中使用它们,就能够很方便地解决特殊应用场景中的使用需求。 UDF 通常以数据表中的一列数据做为输入,同时支持以嵌套子查询的结果作为输入。 + +从 2.2.0.0 版本开始,TDengine 支持通过 C/C++ 语言进行 UDF 定义。接下来结合示例讲解 UDF 的使用方法。 + +用户可以通过 UDF 实现两类函数: 标量函数 和 聚合函数。 + +## 用 C/C++ 语言来定义 UDF + +### 标量函数 + +用户可以按照下列函数模板定义自己的标量计算函数 + + `void udfNormalFunc(char* data, short itype, short ibytes, int numOfRows, long long* ts, char* dataOutput, char* interBuf, char* tsOutput, int* numOfOutput, short otype, short obytes, SUdfInit* buf)` + + 其中 udfNormalFunc 是函数名的占位符,以上述模板实现的函数对行数据块进行标量计算,其参数项是固定的,用于按照约束完成与引擎之间的数据交换。 + +- udfNormalFunc 中各参数的具体含义是: + - data:输入数据。 + - itype:输入数据的类型。这里采用的是短整型表示法,与各种数据类型对应的值可以参见 [column_meta 中的列类型说明](/reference/rest-api/)。例如 4 用于表示 INT 型。 + - iBytes:输入数据中每个值会占用的字节数。 + - numOfRows:输入数据的总行数。 + - ts:主键时间戳在输入中的列数据(只读)。 + - dataOutput:输出数据的缓冲区,缓冲区大小为用户指定的输出类型大小 \* numOfRows。 + - interBuf:中间计算结果的缓冲区,大小为用户在创建 UDF 时指定的 BUFSIZE 大小。通常用于计算中间结果与最终结果不一致时使用,由引擎负责分配与释放。 + - tsOutput:主键时间戳在输出时的列数据,如果非空可用于输出结果对应的时间戳。 + - numOfOutput:输出结果的个数(行数)。 + - oType:输出数据的类型。取值含义与 itype 参数一致。 + - oBytes:输出数据中每个值占用的字节数。 + - buf:用于在 UDF 与引擎间的状态控制信息传递块。 + + [add_one.c](https://github.com/taosdata/TDengine/blob/develop/tests/script/sh/add_one.c) 是结构最简单的 UDF 实现,也即上面定义的 udfNormalFunc 函数的一个具体实现。其功能为:对传入的一个数据列(可能因 WHERE 子句进行了筛选)中的每一项,都输出 +1 之后的值,并且要求输入的列数据类型为 INT。 + +### 聚合函数 + +用户可以按照如下函数模板定义自己的聚合函数。 + +`void abs_max_merge(char* data, int32_t numOfRows, char* dataOutput, int32_t* numOfOutput, SUdfInit* buf)` + +其中 udfMergeFunc 是函数名的占位符,以上述模板实现的函数用于对计算中间结果进行聚合,只有针对超级表的聚合查询才需要调用该函数。其中各参数的具体含义是: + + - data:udfNormalFunc 的输出数据数组,如果使用了 interBuf 那么 data 就是 interBuf 的数组。 + - numOfRows:data 中数据的行数。 + - dataOutput:输出数据的缓冲区,大小等于一条最终结果的大小。如果此时输出还不是最终结果,可以选择输出到 interBuf 中即 data 中。 + - numOfOutput:输出结果的个数(行数)。 + - buf:用于在 UDF 与引擎间的状态控制信息传递块。 + +[abs_max.c](https://github.com/taosdata/TDengine/blob/develop/tests/script/sh/abs_max.c) 实现的是一个聚合函数,功能是对一组数据按绝对值取最大值。 + +其计算过程为:与所在查询语句相关的数据会被分为多个行数据块,对每个行数据块调用 udfNormalFunc(在本例的实现代码中,实际函数名是 `abs_max`)来生成每个子表的中间结果,再将子表的中间结果调用 udfMergeFunc(本例中,其实际的函数名是 `abs_max_merge`)进行聚合,生成超级表的最终聚合结果或中间结果。聚合查询最后还会通过 udfFinalizeFunc(本例中,其实际的函数名是 `abs_max_finalize`)再把超级表的中间结果处理为最终结果,最终结果只能含 0 或 1 条结果数据。 + +其他典型场景,如协方差的计算,也可通过定义聚合 UDF 的方式实现。 + +### 最终计算 + +用户可以按下面的函数模板实现自己的函数对计算结果进行最终计算,通常用于有 interBuf 使用的场景。 + +`void abs_max_finalize(char* dataOutput, char* interBuf, int* numOfOutput, SUdfInit* buf)` + +其中 udfFinalizeFunc 是函数名的占位符 ,其中各参数的具体含义是: + - dataOutput:输出数据的缓冲区。 + - interBuf:中间结算结果缓冲区,可作为输入。 + - numOfOutput:输出数据的个数,对聚合函数来说只能是 0 或者 1。 + - buf:用于在 UDF 与引擎间的状态控制信息传递块。 + +## UDF 实现方式的规则总结 + +三类 UDF 函数: udfNormalFunc、udfMergeFunc、udfFinalizeFunc ,其函数名约定使用相同的前缀,此前缀即 udfNormalFunc 的实际函数名,也即 udfNormalFunc 函数不需要在实际函数名后添加后缀;而udfMergeFunc 的函数名要加上后缀 `_merge`、udfFinalizeFunc 的函数名要加上后缀 `_finalize`,这是 UDF 实现规则的一部分,系统会按照这些函数名后缀来调用相应功能。 + +根据 UDF 函数类型的不同,用户所要实现的功能函数也不同: + +- 标量函数:UDF 中需实现 udfNormalFunc。 +- 聚合函数:UDF 中需实现 udfNormalFunc、udfMergeFunc(对超级表查询)、udfFinalizeFunc。 + +:::note +如果对应的函数不需要具体的功能,也需要实现一个空函数。 + +::: + +## 编译 UDF + +用户定义函数的 C 语言源代码无法直接被 TDengine 系统使用,而是需要先编译为 动态链接库,之后才能载入 TDengine 系统。 + +例如,按照上一章节描述的规则准备好了用户定义函数的源代码 add_one.c,以 Linux 为例可以执行如下指令编译得到动态链接库文件: + +```bash +gcc -g -O0 -fPIC -shared add_one.c -o add_one.so +``` + +这样就准备好了动态链接库 add_one.so 文件,可以供后文创建 UDF 时使用了。为了保证可靠的系统运行,编译器 GCC 推荐使用 7.5 及以上版本。 + +## 在系统中管理和使用 UDF + +### 创建 UDF + +用户可以通过 SQL 指令在系统中加载客户端所在主机上的 UDF 函数库(不能通过 RESTful 接口或 HTTP 管理界面来进行这一过程)。一旦创建成功,则当前 TDengine 集群的所有用户都可以在 SQL 指令中使用这些函数。UDF 存储在系统的 MNode 节点上,因此即使重启 TDengine 系统,已经创建的 UDF 也仍然可用。 + +在创建 UDF 时,需要区分标量函数和聚合函数。如果创建时声明了错误的函数类别,则可能导致通过 SQL 指令调用函数时出错。此外, UDF 支持输入与输出类型不一致,用户需要保证输入数据类型与 UDF 程序匹配,UDF 输出数据类型与 OUTPUTTYPE 匹配。 + +- 创建标量函数 +```sql +CREATE FUNCTION ids(X) AS ids(Y) OUTPUTTYPE typename(Z) [ BUFSIZE B ]; +``` + + - ids(X):标量函数未来在 SQL 指令中被调用时的函数名,必须与函数实现中 udfNormalFunc 的实际名称一致; + - ids(Y):包含 UDF 函数实现的动态链接库的库文件绝对路径(指的是库文件在当前客户端所在主机上的保存路径,通常是指向一个 .so 文件),这个路径需要用英文单引号或英文双引号括起来; + - typename(Z):此函数计算结果的数据类型,与上文中 udfNormalFunc 的 itype 参数不同,这里不是使用数字表示法,而是直接写类型名称即可; + - B:中间计算结果的缓冲区大小,单位是字节,最小 0,最大 512,如果不使用可以不设置。 + + 例如,如下语句可以把 add_one.so 创建为系统中可用的 UDF: + + ```sql + CREATE FUNCTION add_one AS "/home/taos/udf_example/add_one.so" OUTPUTTYPE INT; + ``` + +- 创建聚合函数: +```sql +CREATE AGGREGATE FUNCTION ids(X) AS ids(Y) OUTPUTTYPE typename(Z) [ BUFSIZE B ]; +``` + + - ids(X):聚合函数未来在 SQL 指令中被调用时的函数名,必须与函数实现中 udfNormalFunc 的实际名称一致; + - ids(Y):包含 UDF 函数实现的动态链接库的库文件绝对路径(指的是库文件在当前客户端所在主机上的保存路径,通常是指向一个 .so 文件),这个路径需要用英文单引号或英文双引号括起来; + - typename(Z):此函数计算结果的数据类型,与上文中 udfNormalFunc 的 itype 参数不同,这里不是使用数字表示法,而是直接写类型名称即可; + - B:中间计算结果的缓冲区大小,单位是字节,最小 0,最大 512,如果不使用可以不设置。 + + 关于中间计算结果的使用,可以参考示例程序[demo.c](https://github.com/taosdata/TDengine/blob/develop/tests/script/sh/demo.c) + + 例如,如下语句可以把 demo.so 创建为系统中可用的 UDF: + + ```sql + CREATE AGGREGATE FUNCTION demo AS "/home/taos/udf_example/demo.so" OUTPUTTYPE DOUBLE bufsize 14; + ``` + +### 管理 UDF + +- 删除指定名称的用户定义函数: +``` +DROP FUNCTION ids(X); +``` + +- ids(X):此参数的含义与 CREATE 指令中的 ids(X) 参数一致,也即要删除的函数的名字,例如 +```sql +DROP FUNCTION add_one; +``` +- 显示系统中当前可用的所有 UDF: +```sql +SHOW FUNCTIONS; +``` + +### 调用 UDF + +在 SQL 指令中,可以直接以在系统中创建 UDF 时赋予的函数名来调用用户定义函数。例如: +```sql +SELECT X(c) FROM table/stable; +``` + +表示对名为 c 的数据列调用名为 X 的用户定义函数。SQL 指令中用户定义函数可以配合 WHERE 等查询特性来使用。 + +## UDF 的一些使用限制 + +在当前版本下,使用 UDF 存在如下这些限制: + +1. 在创建和调用 UDF 时,服务端和客户端都只支持 Linux 操作系统; +2. UDF 不能与系统内建的 SQL 函数混合使用,暂不支持在一条 SQL 语句中使用多个不同名的 UDF ; +3. UDF 只支持以单个数据列作为输入; +4. UDF 只要创建成功,就会被持久化存储到 MNode 节点中; +5. 无法通过 RESTful 接口来创建 UDF; +6. UDF 在 SQL 中定义的函数名,必须与 .so 库文件实现中的接口函数名前缀保持一致,也即必须是 udfNormalFunc 的名称,而且不可与 TDengine 中已有的内建 SQL 函数重名。 + +## 示例代码 + +### 标量函数示例 [add_one](https://github.com/taosdata/TDengine/blob/develop/tests/script/sh/add_one.c) + +
+add_one.c + +```c +{{#include tests/script/sh/add_one.c}} +``` + +
+ +### 向量函数示例 [abs_max](https://github.com/taosdata/TDengine/blob/develop/tests/script/sh/abs_max.c) + +
+abs_max.c + +```c +{{#include tests/script/sh/abs_max.c}} +``` + +
+ +### 使用中间计算结果示例 [demo](https://github.com/taosdata/TDengine/blob/develop/tests/script/sh/demo.c) + +
+demo.c + +```c +{{#include tests/script/sh/demo.c}} +``` + +
diff --git a/docs-cn/07-develop/_category_.yml b/docs-cn/07-develop/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..509a9405c42939a4819b87669a4c5b244bd29a8b --- /dev/null +++ b/docs-cn/07-develop/_category_.yml @@ -0,0 +1 @@ +label: 开发指南 \ No newline at end of file diff --git a/docs-cn/07-develop/_sub_c.mdx b/docs-cn/07-develop/_sub_c.mdx new file mode 100644 index 0000000000000000000000000000000000000000..95fef0042d0a277f9136e6e6f8c15558487232f9 --- /dev/null +++ b/docs-cn/07-develop/_sub_c.mdx @@ -0,0 +1,3 @@ +```c +{{#include docs-examples/c/subscribe_demo.c}} +``` \ No newline at end of file diff --git a/docs-cn/07-develop/_sub_cs.mdx b/docs-cn/07-develop/_sub_cs.mdx new file mode 100644 index 0000000000000000000000000000000000000000..80934aa4d014a076896dce7f41e520f06ffd735d --- /dev/null +++ b/docs-cn/07-develop/_sub_cs.mdx @@ -0,0 +1,3 @@ +```csharp +{{#include docs-examples/csharp/SubscribeDemo.cs}} +``` \ No newline at end of file diff --git a/docs-cn/07-develop/_sub_go.mdx b/docs-cn/07-develop/_sub_go.mdx new file mode 100644 index 0000000000000000000000000000000000000000..cd908fc12c3a35f49ca108ee56c3951c5388a95f --- /dev/null +++ b/docs-cn/07-develop/_sub_go.mdx @@ -0,0 +1,3 @@ +```go +{{#include docs-examples/go/sub/main.go}} +``` \ No newline at end of file diff --git a/docs-cn/07-develop/_sub_java.mdx b/docs-cn/07-develop/_sub_java.mdx new file mode 100644 index 0000000000000000000000000000000000000000..1ee0cb1a21e35f6760f8680e2ba6dedee92201cd --- /dev/null +++ b/docs-cn/07-develop/_sub_java.mdx @@ -0,0 +1,7 @@ +```java +{{#include docs-examples/java/src/main/java/com/taos/example/SubscribeDemo.java}} +``` +:::note +目前 Java 接口没有提供异步订阅模式,但用户程序可以通过创建 `TimerTask` 等方式达到同样的效果。 + +::: \ No newline at end of file diff --git a/docs-cn/07-develop/_sub_node.mdx b/docs-cn/07-develop/_sub_node.mdx new file mode 100644 index 0000000000000000000000000000000000000000..c93ad627ce9a77ca71a014b41d571089e6c1727b --- /dev/null +++ b/docs-cn/07-develop/_sub_node.mdx @@ -0,0 +1,3 @@ +```js +{{#include docs-examples/node/nativeexample/subscribe_demo.js}} +``` \ No newline at end of file diff --git a/docs-cn/07-develop/_sub_python.mdx b/docs-cn/07-develop/_sub_python.mdx new file mode 100644 index 0000000000000000000000000000000000000000..b817deeba6e283a3ba16fee0d580d3823c999536 --- /dev/null +++ b/docs-cn/07-develop/_sub_python.mdx @@ -0,0 +1,3 @@ +```py +{{#include docs-examples/python/subscribe_demo.py}} +``` \ No newline at end of file diff --git a/docs-cn/07-develop/_sub_rust.mdx b/docs-cn/07-develop/_sub_rust.mdx new file mode 100644 index 0000000000000000000000000000000000000000..4750cf7a3b871db48c9e5a26b22ab4b8a03f11be --- /dev/null +++ b/docs-cn/07-develop/_sub_rust.mdx @@ -0,0 +1,3 @@ +```rs +{{#include docs-examples/rust/nativeexample/examples/subscribe_demo.rs}} +``` \ No newline at end of file diff --git a/docs-cn/07-develop/index.md b/docs-cn/07-develop/index.md new file mode 100644 index 0000000000000000000000000000000000000000..0393a87ab2ae7a5b08eea75d7a0bea95614b8131 --- /dev/null +++ b/docs-cn/07-develop/index.md @@ -0,0 +1,24 @@ +--- +title: 开发指南 +--- + +开发一个应用,如果你准备采用TDengine作为时序数据处理的工具,那么有如下几个事情要做: +1. 确定应用到TDengine的链接方式。无论你使用何种编程语言,你总可以使用REST接口, 但也可以使用每种编程语言独有的连接器方便的进行链接。 +2. 根据自己的应用场景,确定数据模型。根据数据特征,决定建立一个还是多个库;分清静态标签、采集量,建立正确的超级表,建立子表。 +3. 决定插入数据的方式。TDengine支持使用标准的SQL写入,但同时也支持schemaless模式写入,这样不用手工建表,可以将数据直接写入。 +4. 根据业务要求,看需要撰写哪些SQL查询语句。 +5. 如果你要基于时序数据做实时的统计分析,包括各种监测看板,那么建议你采用TDengine的连续查询功能,而不用上线Spark, Flink等复杂的流式计算系统。 +6. 如果你的应用有模块需要消费插入的数据,希望有新的数据插入时,就能获取通知,那么建议你采用TDengine提供的数据订阅功能,而无需专门部署Kafka或其他消息队列软件。 +7. 在很多场景下(如车辆管理),应用需要获取每个数据采集点的最新状态,那么建议你采用TDengine的cache功能,而不用单独部署Redis等缓存软件。 +8. 如果你发现TDengine的函数无法满足你的要求,那么你可以使用用户自定义函数来解决问题。 + +本部分内容就是按照上述的顺序组织的。为便于理解,TDengine为每个功能为每个支持的编程语言都提供了示例代码。如果你希望深入了解SQL的使用,需要查看[SQL手册](/taos-sql/)。如果想更深入地了解各连接器的使用,请阅读[连接器参考指南](/reference/connector/)。如果还希望想将TDengine与第三方系统集成起来,比如Grafana, 请参考[第三方工具](/third-party/)。 + +如果在开发过程中遇到任何问题,请点击每个页面下方的["反馈问题"](https://github.com/taosdata/TDengine/issues/new/choose), 在GitHub上直接递交issue。 + +```mdx-code-block +import DocCardList from '@theme/DocCardList'; +import {useCurrentSidebarCategory} from '@docusaurus/theme-common'; + + +``` diff --git a/docs-cn/10-cluster/01-deploy.md b/docs-cn/10-cluster/01-deploy.md new file mode 100644 index 0000000000000000000000000000000000000000..cee140c0ec13bc9c8052a599a2147acc1aa15a8d --- /dev/null +++ b/docs-cn/10-cluster/01-deploy.md @@ -0,0 +1,138 @@ +--- +title: 集群部署 +--- + +## 准备工作 + +### 第零步 + +规划集群所有物理节点的 FQDN,将规划好的 FQDN 分别添加到每个物理节点的 /etc/hosts;修改每个物理节点的 /etc/hosts,将所有集群物理节点的 IP 与 FQDN 的对应添加好。【如部署了 DNS,请联系网络管理员在 DNS 上做好相关配置】 + +### 第一步 + +如果搭建集群的物理节点中,存有之前的测试数据、装过 1.X 的版本,或者装过其他版本的 TDengine,请先将其删除,并清空所有数据(如果需要保留原有数据,请联系涛思交付团队进行旧版本升级、数据迁移),具体步骤请参考博客[《TDengine 多种安装包的安装和卸载》](https://www.taosdata.com/blog/2019/08/09/566.html)。 + +:::note +因为 FQDN 的信息会写进文件,如果之前没有配置或者更改 FQDN,且启动了 TDengine。请一定在确保数据无用或者备份的前提下,清理一下之前的数据(rm -rf /var/lib/taos/\*); +::: + +:::note +客户端所在服务器也需要配置,确保它可以正确解析每个节点的 FQDN 配置,不管是通过 DNS 服务,还是修改 hosts 文件。 +::: + +### 第二步 + +建议关闭所有物理节点的防火墙,至少保证端口:6030 - 6042 的 TCP 和 UDP 端口都是开放的。强烈建议先关闭防火墙,集群搭建完毕之后,再来配置端口; + +### 第三步 + +在所有物理节点安装 TDengine,且版本必须是一致的,但不要启动 taosd。安装时,提示输入是否要加入一个已经存在的 TDengine 集群时,第一个物理节点直接回车创建新集群,后续物理节点则输入该集群任何一个在线的物理节点的 FQDN:端口号(默认 6030); + +### 第四步 + +检查所有数据节点,以及应用程序所在物理节点的网络设置: + +每个物理节点上执行命令 `hostname -f`,查看和确认所有节点的 hostname 是不相同的(应用驱动所在节点无需做此项检查); + +每个物理节点上执行 ping host,其中 host 是其他物理节点的 hostname,看能否 ping 通其它物理节点;如果不能 ping 通,需要检查网络设置,或 /etc/hosts 文件(Windows 系统默认路径为 C:\Windows\system32\drivers\etc\hosts),或 DNS 的配置。如果无法 ping 通,是无法组成集群的; + +从应用运行的物理节点,ping taosd 运行的数据节点,如果无法 ping 通,应用是无法连接 taosd 的,请检查应用所在物理节点的 DNS 设置或 hosts 文件; + +每个数据节点的 End Point 就是输出的 hostname 外加端口号,比如 h1.taosdata.com:6030。 + +### 第五步 + +修改 TDengine 的配置文件(所有节点的文件 /etc/taos/taos.cfg 都需要修改)。假设准备启动的第一个数据节点 End Point 为 h1.taosdata.com:6030,其与集群配置相关参数如下: + +```c +// firstEp 是每个数据节点首次启动后连接的第一个数据节点 +firstEp h1.taosdata.com:6030 + +// 必须配置为本数据节点的 FQDN,如果本机只有一个 hostname,可注释掉本项 +fqdn h1.taosdata.com + +// 配置本数据节点的端口号,缺省是 6030 +serverPort 6030 + +// 副本数为偶数的时候,需要配置,请参考《Arbitrator 的使用》的部分 +arbitrator ha.taosdata.com:6042 +``` + +一定要修改的参数是 firstEp 和 fqdn。在每个数据节点,firstEp 需全部配置成一样,但 fqdn 一定要配置成其所在数据节点的值。其他参数可不做任何修改,除非你很清楚为什么要修改。 + +加入到集群中的数据节点 dnode,涉及集群相关的下表 9 项参数必须完全相同,否则不能成功加入到集群中。 + +| **#** | **配置参数名称** | **含义** | +| ----- | ------------------ | ------------------------------------------- | +| 1 | numOfMnodes | 系统中管理节点个数 | +| 2 | mnodeEqualVnodeNum | 一个 mnode 等同于 vnode 消耗的个数 | +| 3 | offlineThreshold | dnode 离线阈值,超过该时间将导致 Dnode 离线 | +| 4 | statusInterval | dnode 向 mnode 报告状态时长 | +| 5 | arbitrator | 系统中裁决器的 End Point | +| 6 | timezone | 时区 | +| 7 | balance | 是否启动负载均衡 | +| 8 | maxTablesPerVnode | 每个 vnode 中能够创建的最大表个数 | +| 9 | maxVgroupsPerDb | 每个 DB 中能够使用的最大 vgroup 个数 | + +:::note +在 2.0.19.0 及更早的版本中,除以上 9 项参数外,dnode 加入集群时,还会要求 locale 和 charset 参数的取值也一致。 + +::: + +## 启动集群 + +### 启动第一个数据节点 + +按照《立即开始》里的步骤,启动第一个数据节点,例如 h1.taosdata.com,然后执行 taos,启动 taos shell,从 shell 里执行命令“SHOW DNODES”,如下所示: + +``` +Welcome to the TDengine shell from Linux, Client Version:2.0.0.0 + + +Copyright (c) 2017 by TAOS Data, Inc. All rights reserved. + +taos> show dnodes; + id | end_point | vnodes | cores | status | role | create_time | +===================================================================================== + 1 | h1.taos.com:6030 | 0 | 2 | ready | any | 2020-07-31 03:49:29.202 | +Query OK, 1 row(s) in set (0.006385s) + +taos> +``` + +上述命令里,可以看到刚启动的数据节点的 End Point 是:h1.taos.com:6030,就是这个新集群的 firstEp。 + +### 启动后续数据节点 + +将后续的数据节点添加到现有集群,具体有以下几步: + +按照《立即开始》一章的方法在每个物理节点启动 taosd;(注意:每个物理节点都需要在 taos.cfg 文件中将 firstEp 参数配置为新集群首个节点的 End Point——在本例中是 h1.taos.com:6030) + +在第一个数据节点,使用 CLI 程序 taos,登录进 TDengine 系统,执行命令: + +```sql +CREATE DNODE "h2.taos.com:6030"; +``` + +将新数据节点的 End Point(准备工作中第四步获知的)添加进集群的 EP 列表。“fqdn:port”需要用双引号引起来,否则出错。请注意将示例的“h2.taos.com:6030” 替换为这个新数据节点的 End Point。 + +然后执行命令 + +```sql +SHOW DNODES; +``` + +查看新节点是否被成功加入。如果该被加入的数据节点处于离线状态,请做两个检查: + +查看该数据节点的 taosd 是否正常工作,如果没有正常运行,需要先检查为什么? +查看该数据节点 taosd 日志文件 taosdlog.0 里前面几行日志(一般在 /var/log/taos 目录),看日志里输出的该数据节点 fqdn 以及端口号是否为刚添加的 End Point。如果不一致,需要将正确的 End Point 添加进去。 +按照上述步骤可以源源不断的将新的数据节点加入到集群。 + +:::tip + +任何已经加入集群在线的数据节点,都可以作为后续待加入节点的 firstEp。 +firstEp 这个参数仅仅在该数据节点首次加入集群时有作用,加入集群后,该数据节点会保存最新的 mnode 的 End Point 列表,不再依赖这个参数。 +接下来,配置文件中的 firstEp 参数就主要在客户端连接的时候使用了,例如 taos shell 如果不加参数,会默认连接由 firstEp 指定的节点。 +两个没有配置 firstEp 参数的数据节点 dnode 启动后,会独立运行起来。这个时候,无法将其中一个数据节点加入到另外一个数据节点,形成集群。无法将两个独立的集群合并成为新的集群。 + +::: diff --git a/docs-cn/10-cluster/02-cluster-mgmt.md b/docs-cn/10-cluster/02-cluster-mgmt.md new file mode 100644 index 0000000000000000000000000000000000000000..6ab8ec091b898e8f441d75ef898dc0ff06fce6be --- /dev/null +++ b/docs-cn/10-cluster/02-cluster-mgmt.md @@ -0,0 +1,216 @@ +--- +title: 数据节点管理 +--- + +上面已经介绍如何从零开始搭建集群。集群组建完成后,可以随时查看集群中当前的数据节点的状态,还可以添加新的数据节点进行扩容,删除数据节点,甚至手动进行数据节点之间的负载均衡操作。 + +:::note + +以下所有执行命令的操作需要先登陆进 TDengine 系统,必要时请使用 root 权限。 + +::: + +## 查看数据节点 + +启动 TDengine CLI 程序 taos,然后执行: + +```sql +SHOW DNODES; +``` + +它将列出集群中所有的 dnode,每个 dnode 的 ID,end_point(fqdn:port),状态(ready,offline 等),vnode 数目,还未使用的 vnode 数目等信息。在添加或删除一个数据节点后,可以使用该命令查看。 + +输出如下(具体内容仅供参考,取决于实际的集群配置) + +``` +taos> show dnodes; + id | end_point | vnodes | cores | status | role | create_time | offline reason | +====================================================================================================================================== + 1 | localhost:6030 | 9 | 8 | ready | any | 2022-04-15 08:27:09.359 | | +Query OK, 1 row(s) in set (0.008298s) +``` + +## 查看虚拟节点组 + +为充分利用多核技术,并提供 scalability,数据需要分片处理。因此 TDengine 会将一个 DB 的数据切分成多份,存放在多个 vnode 里。这些 vnode 可能分布在多个数据节点 dnode 里,这样就实现了水平扩展。一个 vnode 仅仅属于一个 DB,但一个 DB 可以有多个 vnode。vnode 所在的数据节点是 mnode 根据当前系统资源的情况,自动进行分配的,无需任何人工干预。 + +启动 CLI 程序 taos,然后执行: + +```sql +USE SOME_DATABASE; +SHOW VGROUPS; +``` + +输出如下(具体内容仅供参考,取决于实际的集群配置) + +``` +taos> show dnodes; + id | end_point | vnodes | cores | status | role | create_time | offline reason | +====================================================================================================================================== + 1 | localhost:6030 | 9 | 8 | ready | any | 2022-04-15 08:27:09.359 | | +Query OK, 1 row(s) in set (0.008298s) + +taos> use db; +Database changed. + +taos> show vgroups; + vgId | tables | status | onlines | v1_dnode | v1_status | compacting | +========================================================================================== + 14 | 38000 | ready | 1 | 1 | master | 0 | + 15 | 38000 | ready | 1 | 1 | master | 0 | + 16 | 38000 | ready | 1 | 1 | master | 0 | + 17 | 38000 | ready | 1 | 1 | master | 0 | + 18 | 37001 | ready | 1 | 1 | master | 0 | + 19 | 37000 | ready | 1 | 1 | master | 0 | + 20 | 37000 | ready | 1 | 1 | master | 0 | + 21 | 37000 | ready | 1 | 1 | master | 0 | +Query OK, 8 row(s) in set (0.001154s) +``` + +## 添加数据节点 + +启动 CLI 程序 taos,然后执行: + +```sql +CREATE DNODE "fqdn:port"; +``` + +将新数据节点的 End Point 添加进集群的 EP 列表。“fqdn:port“需要用双引号引起来,否则出错。一个数据节点对外服务的 fqdn 和 port 可以通过配置文件 taos.cfg 进行配置,缺省是自动获取。【强烈不建议用自动获取方式来配置 FQDN,可能导致生成的数据节点的 End Point 不是所期望的】 + +示例如下: +``` +taos> create dnode "localhost:7030"; +Query OK, 0 of 0 row(s) in database (0.008203s) + +taos> show dnodes; + id | end_point | vnodes | cores | status | role | create_time | offline reason | +====================================================================================================================================== + 1 | localhost:6030 | 9 | 8 | ready | any | 2022-04-15 08:27:09.359 | | + 2 | localhost:7030 | 0 | 0 | offline | any | 2022-04-19 08:11:42.158 | status not received | +Query OK, 2 row(s) in set (0.001017s) +``` + +在上面的示例中可以看到新创建的 dnode 的状态为 offline,待该 dnode 被启动并连接上配置文件中指定的 firstEp后再次查看,得到如下结果(示例) + +``` +taos> show dnodes; + id | end_point | vnodes | cores | status | role | create_time | offline reason | +====================================================================================================================================== + 1 | localhost:6030 | 3 | 8 | ready | any | 2022-04-15 08:27:09.359 | | + 2 | localhost:7030 | 6 | 8 | ready | any | 2022-04-19 08:14:59.165 | | +Query OK, 2 row(s) in set (0.001316s) +``` +从中可以看到两个 dnode 状态都为 ready + + +## 删除数据节点 + +启动 CLI 程序 taos,然后执行: + +```sql +DROP DNODE "fqdn:port"; +``` +或者 +```sql +DROP DNODE dnodeId; +``` + +通过 “fqdn:port” 或 dnodeID 来指定一个具体的节点都是可以的。其中 fqdn 是被删除的节点的 FQDN,port 是其对外服务器的端口号;dnodeID 可以通过 SHOW DNODES 获得。 + +示例如下: +``` +taos> show dnodes; + id | end_point | vnodes | cores | status | role | create_time | offline reason | +====================================================================================================================================== + 1 | localhost:6030 | 9 | 8 | ready | any | 2022-04-15 08:27:09.359 | | + 2 | localhost:7030 | 0 | 0 | offline | any | 2022-04-19 08:11:42.158 | status not received | +Query OK, 2 row(s) in set (0.001017s) + +taos> drop dnode 2; +Query OK, 0 of 0 row(s) in database (0.000518s) + +taos> show dnodes; + id | end_point | vnodes | cores | status | role | create_time | offline reason | +====================================================================================================================================== + 1 | localhost:6030 | 9 | 8 | ready | any | 2022-04-15 08:27:09.359 | | +Query OK, 1 row(s) in set (0.001137s) +``` + +上面的示例中,初次执行 `show dnodes` 列出了两个 dnode, 执行 `drop dnode 2` 删除其中 ID 为 2 的 dnode 之后再次执行 `show dnodes`,可以看到只剩下 ID 为 1 的 dnode 。 + +:::warning + +数据节点一旦被 drop 之后,不能重新加入集群。需要将此节点重新部署(清空数据文件夹)。集群在完成 `drop dnode` 操作之前,会将该 dnode 的数据迁移走。 +请注意 `drop dnode` 和 停止 taosd 进程是两个不同的概念,不要混淆:因为删除 dnode 之前要执行迁移数据的操作,因此被删除的 dnode 必须保持在线状态。待删除操作结束之后,才能停止 taosd 进程。 +一个数据节点被 drop 之后,其他节点都会感知到这个 dnodeID 的删除操作,任何集群中的节点都不会再接收此 dnodeID 的请求。 +dnodeID 是集群自动分配的,不得人工指定。它在生成时是递增的,不会重复。 + +::: + +## 手动迁移数据节点 + +手动将某个 vnode 迁移到指定的 dnode。 + +启动 CLI 程序 taos,然后执行: + +```sql +ALTER DNODE BALANCE "VNODE:-DNODE:"; +``` + +其中:source-dnodeId 是源 dnodeId,也就是待迁移的 vnode 所在的 dnodeID;vgId 可以通过 SHOW VGROUPS 获得,列表的第一列;dest-dnodeId 是目标 dnodeId。 + +首先执行 `show vgroups` 查看 vgroup 的分布情况 +``` +taos> show vgroups; + vgId | tables | status | onlines | v1_dnode | v1_status | compacting | +========================================================================================== + 14 | 38000 | ready | 1 | 3 | master | 0 | + 15 | 38000 | ready | 1 | 3 | master | 0 | + 16 | 38000 | ready | 1 | 3 | master | 0 | + 17 | 38000 | ready | 1 | 3 | master | 0 | + 18 | 37001 | ready | 1 | 3 | master | 0 | + 19 | 37000 | ready | 1 | 1 | master | 0 | + 20 | 37000 | ready | 1 | 1 | master | 0 | + 21 | 37000 | ready | 1 | 1 | master | 0 | +Query OK, 8 row(s) in set (0.001314s) +``` + +从中可以看到在 dnode 3 中有5个 vgroup,而 dnode 1 有 3 个 vgroup,假定我们想将其中 vgId 为18 的 vgroup 从 dnode 3 迁移到 dnode 1 + +``` +taos> alter dnode 3 balance "vnode:18-dnode:1"; + +DB error: Balance already enabled (0.00755 +``` + +上面的结果表明目前所在数据库已经启动了 balance 选项,所以无法进行手动迁移。 + +停止整个集群,将两个 dnode 的配置文件中的 balance 都设置为 0 (默认为1)之后,重新启动集群,再次执行 ` alter dnode` 和 `show vgroups` 命令如下 +``` +taos> alter dnode 3 balance "vnode:18-dnode:1"; +Query OK, 0 row(s) in set (0.000575s) + +taos> show vgroups; + vgId | tables | status | onlines | v1_dnode | v1_status | v2_dnode | v2_status | compacting | +================================================================================================================= + 14 | 38000 | ready | 1 | 3 | master | 0 | NULL | 0 | + 15 | 38000 | ready | 1 | 3 | master | 0 | NULL | 0 | + 16 | 38000 | ready | 1 | 3 | master | 0 | NULL | 0 | + 17 | 38000 | ready | 1 | 3 | master | 0 | NULL | 0 | + 18 | 37001 | ready | 2 | 1 | slave | 3 | master | 0 | + 19 | 37000 | ready | 1 | 1 | master | 0 | NULL | 0 | + 20 | 37000 | ready | 1 | 1 | master | 0 | NULL | 0 | + 21 | 37000 | ready | 1 | 1 | master | 0 | NULL | 0 | +Query OK, 8 row(s) in set (0.001242s) +``` + +从上面的输出可以看到 vgId 为 18 的 vnode 被从 dnode 3 迁移到了 dnode 1。 + +:::warning + +只有在集群的自动负载均衡选项关闭时(balance 设置为 0),才允许手动迁移。 +只有处于正常工作状态的 vnode 才能被迁移:master/slave;当处于 offline/unsynced/syncing 状态时,是不能迁移的。 +迁移前,务必核实目标 dnode 的资源足够:CPU、内存、硬盘。 + +::: + diff --git a/docs-cn/10-cluster/03-ha-and-lb.md b/docs-cn/10-cluster/03-ha-and-lb.md new file mode 100644 index 0000000000000000000000000000000000000000..3d15feb11c47fe821fa4689de2665a65ea17cbe9 --- /dev/null +++ b/docs-cn/10-cluster/03-ha-and-lb.md @@ -0,0 +1,87 @@ +--- +title: 高可用与负载均衡 +--- + +## Vnode 的高可用性 + +TDengine 通过多副本的机制来提供系统的高可用性,包括 vnode 和 mnode 的高可用性。 + +vnode 的副本数是与 DB 关联的,一个集群里可以有多个 DB,根据运营的需求,每个 DB 可以配置不同的副本数。创建数据库时,通过参数 replica 指定副本数(缺省为 1)。如果副本数为 1,系统的可靠性无法保证,只要数据所在的节点宕机,就将无法提供服务。集群的节点数必须大于等于副本数,否则创建表时将返回错误“more dnodes are needed”。比如下面的命令将创建副本数为 3 的数据库 demo: + +```sql +CREATE DATABASE demo replica 3; +``` + +一个 DB 里的数据会被切片分到多个 vnode group,vnode group 里的 vnode 数目就是 DB 的副本数,同一个 vnode group 里各 vnode 的数据是完全一致的。为保证高可用性,vnode group 里的 vnode 一定要分布在不同的数据节点 dnode 里(实际部署时,需要在不同的物理机上),只要一个 vnode group 里超过半数的 vnode 处于工作状态,这个 vnode group 就能正常的对外服务。 + +一个数据节点 dnode 里可能有多个 DB 的数据,因此一个 dnode 离线时,可能会影响到多个 DB。如果一个 vnode group 里的一半或一半以上的 vnode 不工作,那么该 vnode group 就无法对外服务,无法插入或读取数据,这样会影响到它所属的 DB 的一部分表的读写操作。 + +因为 vnode 的引入,无法简单地给出结论:“集群中过半数据节点 dnode 工作,集群就应该工作”。但是对于简单的情形,很好下结论。比如副本数为 3,只有三个 dnode,那如果仅有一个节点不工作,整个集群还是可以正常工作的,但如果有两个数据节点不工作,那整个集群就无法正常工作了。 + +## Mnode 的高可用性 + +TDengine 集群是由 mnode(taosd 的一个模块,管理节点)负责管理的,为保证 mnode 的高可用,可以配置多个 mnode 副本,副本数由系统配置参数 numOfMnodes 决定,有效范围为 1-3。为保证元数据的强一致性,mnode 副本之间是通过同步的方式进行数据复制的。 + +一个集群有多个数据节点 dnode,但一个 dnode 至多运行一个 mnode 实例。多个 dnode 情况下,哪个 dnode 可以作为 mnode 呢?这是完全由系统根据整个系统资源情况,自动指定的。用户可通过 CLI 程序 taos,在 TDengine 的 console 里,执行如下命令: + +```sql +SHOW MNODES; +``` + +来查看 mnode 列表,该列表将列出 mnode 所处的 dnode 的 End Point 和角色(master,slave,unsynced 或 offline)。当集群中第一个数据节点启动时,该数据节点一定会运行一个 mnode 实例,否则该数据节点 dnode 无法正常工作,因为一个系统是必须有至少一个 mnode 的。如果 numOfMnodes 配置为 2,启动第二个 dnode 时,该 dnode 也将运行一个 mnode 实例。 + +为保证 mnode 服务的高可用性,numOfMnodes 必须设置为 2 或更大。因为 mnode 保存的元数据必须是强一致的,如果 numOfMnodes 大于 2,复制参数 quorum 自动设为 2,也就是说,至少要保证有两个副本写入数据成功,才通知客户端应用写入成功。 + +:::note +一个 TDengine 高可用系统,无论是 vnode 还是 mnode,都必须配置多个副本。 + +::: + +## 负载均衡 + +有三种情况,将触发负载均衡,而且都无需人工干预。 + +当一个新数据节点添加进集群时,系统将自动触发负载均衡,一些节点上的数据将被自动转移到新数据节点上,无需任何人工干预。 +当一个数据节点从集群中移除时,系统将自动把该数据节点上的数据转移到其他数据节点,无需任何人工干预。 +如果一个数据节点过热(数据量过大),系统将自动进行负载均衡,将该数据节点的一些 vnode 自动挪到其他节点。 +当上述三种情况发生时,系统将启动各个数据节点的负载计算,从而决定如何挪动。 + +:::tip +负载均衡由参数 balance 控制,它决定是否启动自动负载均衡,0 表示禁用,1 表示启用自动负载均衡。 + +::: + +## 数据节点离线处理 + +如果一个数据节点离线,TDengine 集群将自动检测到。有如下两种情况: + +该数据节点离线超过一定时间(taos.cfg 里配置参数 offlineThreshold 控制时长),系统将自动把该数据节点删除,产生系统报警信息,触发负载均衡流程。如果该被删除的数据节点重新上线时,它将无法加入集群,需要系统管理员重新将其添加进集群才会开始工作。 + +离线后,在 offlineThreshold 的时长内重新上线,系统将自动启动数据恢复流程,等数据完全恢复后,该节点将开始正常工作。 + +:::note +如果一个虚拟节点组(包括 mnode 组)里所归属的每个数据节点都处于离线或 unsynced 状态,必须等该虚拟节点组里的所有数据节点都上线、都能交换状态信息后,才能选出 Master,该虚拟节点组才能对外提供服务。比如整个集群有 3 个数据节点,副本数为 3,如果 3 个数据节点都宕机,然后 2 个数据节点重启,是无法工作的,只有等 3 个数据节点都重启成功,才能对外服务。 + +::: + +## Arbitrator 的使用 + +如果副本数为偶数,当一个 vnode group 里一半或超过一半的 vnode 不工作时,是无法从中选出 master 的。同理,一半或超过一半的 mnode 不工作时,是无法选出 mnode 的 master 的,因为存在“split brain”问题。 + +为解决这个问题,TDengine 引入了 Arbitrator 的概念。Arbitrator 模拟一个 vnode 或 mnode 在工作,但只简单的负责网络连接,不处理任何数据插入或访问。只要包含 Arbitrator 在内,超过半数的 vnode 或 mnode 工作,那么该 vnode group 或 mnode 组就可以正常的提供数据插入或查询服务。比如对于副本数为 2 的情形,如果一个节点 A 离线,但另外一个节点 B 正常,而且能连接到 Arbitrator,那么节点 B 就能正常工作。 + +总之,在目前版本下,TDengine 建议在双副本环境要配置 Arbitrator,以提升系统的可用性。 + +Arbitrator 的执行程序名为 tarbitrator。该程序对系统资源几乎没有要求,只需要保证有网络连接,找任何一台 Linux 服务器运行它即可。以下简要描述安装配置的步骤: + +请点击 安装包下载,在 TDengine Arbitrator Linux 一节中,选择合适的版本下载并安装。 +该应用的命令行参数 -p 可以指定其对外服务的端口号,缺省是 6042。 + +修改每个 taosd 实例的配置文件,在 taos.cfg 里将参数 arbitrator 设置为 tarbitrator 程序所对应的 End Point。(如果该参数配置了,当副本数为偶数时,系统将自动连接配置的 Arbitrator。如果副本数为奇数,即使配置了 Arbitrator,系统也不会去建立连接。) + +在配置文件中配置了的 Arbitrator,会出现在 SHOW DNODES 指令的返回结果中,对应的 role 列的值会是“arb”。 +查看集群 Arbitrator 的状态【2.0.14.0 以后支持】 + +```sql +SHOW DNODES; +``` diff --git a/docs-cn/10-cluster/_category_.yml b/docs-cn/10-cluster/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..3cee5ce4cdc29e7febf456e567ba08cf2753d305 --- /dev/null +++ b/docs-cn/10-cluster/_category_.yml @@ -0,0 +1 @@ +label: 集群管理 diff --git a/docs-cn/10-cluster/index.md b/docs-cn/10-cluster/index.md new file mode 100644 index 0000000000000000000000000000000000000000..ef2a7253c977cbdbd101ba6af5d7e1584aaf34bd --- /dev/null +++ b/docs-cn/10-cluster/index.md @@ -0,0 +1,14 @@ +--- +title: 集群管理 +--- + +TDengine 支持集群,提供水平扩展的能力。如果需要获得更高的处理能力,只需要多增加节点即可。TDengine 采用虚拟节点技术,将一个节点虚拟化为多个虚拟节点,以实现负载均衡。同时,TDengine可以将多个节点上的虚拟节点组成虚拟节点组,通过多副本机制,以保证供系统的高可用。TDengine的集群功能完全开源。 + +本章节主要介绍集群的部署、维护,以及如何实现高可用和负载均衡。 + +```mdx-code-block +import DocCardList from '@theme/DocCardList'; +import {useCurrentSidebarCategory} from '@docusaurus/theme-common'; + + +``` diff --git a/docs-cn/12-taos-sql/01-data-type.md b/docs-cn/12-taos-sql/01-data-type.md new file mode 100644 index 0000000000000000000000000000000000000000..be5c9a8cb4ed7f4ed9f9c7e11faf1b0f8f6e51b8 --- /dev/null +++ b/docs-cn/12-taos-sql/01-data-type.md @@ -0,0 +1,50 @@ +--- +sidebar_label: 支持的数据类型 +title: 支持的数据类型 +description: "TDengine 支持的数据类型: 时间戳、浮点型、JSON 类型等" +--- + +使用 TDengine,最重要的是时间戳。创建并插入记录、查询历史记录的时候,均需要指定时间戳。时间戳有如下规则: + +- 时间格式为 `YYYY-MM-DD HH:mm:ss.MS`,默认时间分辨率为毫秒。比如:`2017-08-12 18:25:58.128` +- 内部函数 now 是客户端的当前时间 +- 插入记录时,如果时间戳为 now,插入数据时使用提交这条记录的客户端的当前时间 +- Epoch Time:时间戳也可以是一个长整数,表示从格林威治时间 1970-01-01 00:00:00.000 (UTC/GMT) 开始的毫秒数(相应地,如果所在 Database 的时间精度设置为“微秒”,则长整型格式的时间戳含义也就对应于从格林威治时间 1970-01-01 00:00:00.000 (UTC/GMT) 开始的微秒数;纳秒精度逻辑类似。) +- 时间可以加减,比如 now-2h,表明查询时刻向前推 2 个小时(最近 2 小时)。数字后面的时间单位可以是 b(纳秒)、u(微秒)、a(毫秒)、s(秒)、m(分)、h(小时)、d(天)、w(周)。 比如 `select * from t1 where ts > now-2w and ts <= now-1w`,表示查询两周前整整一周的数据。在指定降采样操作(down sampling)的时间窗口(interval)时,时间单位还可以使用 n (自然月) 和 y (自然年)。 + +TDengine 缺省的时间戳精度是毫秒,但通过在 `CREATE DATABASE` 时传递的 PRECISION 参数也可以支持微秒和纳秒。(从 2.1.5.0 版本开始支持纳秒精度) + +```sql +CREATE DATABASE db_name PRECISION 'ns'; +``` + +在 TDengine 中,普通表的数据模型中可使用以下 10 种数据类型。 + +| # | **类型** | **Bytes** | **说明** | +| --- | :-------: | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 1 | TIMESTAMP | 8 | 时间戳。缺省精度毫秒,可支持微秒和纳秒。从格林威治时间 1970-01-01 00:00:00.000 (UTC/GMT) 开始,计时不能早于该时间。(从 2.0.18.0 版本开始,已经去除了这一时间范围限制)(从 2.1.5.0 版本开始支持纳秒精度) | +| 2 | INT | 4 | 整型,范围 [-2^31+1, 2^31-1], -2^31 用作 NULL | +| 3 | BIGINT | 8 | 长整型,范围 [-2^63+1, 2^63-1], -2^63 用作 NULL | +| 4 | FLOAT | 4 | 浮点型,有效位数 6-7,范围 [-3.4E38, 3.4E38] | +| 5 | DOUBLE | 8 | 双精度浮点型,有效位数 15-16,范围 [-1.7E308, 1.7E308] | +| 6 | BINARY | 自定义 | 记录单字节字符串,建议只用于处理 ASCII 可见字符,中文等多字节字符需使用 nchar。理论上,最长可以有 16374 字节。binary 仅支持字符串输入,字符串两端需使用单引号引用。使用时须指定大小,如 binary(20) 定义了最长为 20 个单字节字符的字符串,每个字符占 1 byte 的存储空间,总共固定占用 20 bytes 的空间,此时如果用户字符串超出 20 字节将会报错。对于字符串内的单引号,可以用转义字符反斜线加单引号来表示,即 `\’`。 | +| 7 | SMALLINT | 2 | 短整型, 范围 [-32767, 32767], -32768 用作 NULL | +| 8 | TINYINT | 1 | 单字节整型,范围 [-127, 127], -128 用作 NULL | +| 9 | BOOL | 1 | 布尔型,{true, false} | +| 10 | NCHAR | 自定义 | 记录包含多字节字符在内的字符串,如中文字符。每个 nchar 字符占用 4 bytes 的存储空间。字符串两端使用单引号引用,字符串内的单引号需用转义字符 `\’`。nchar 使用时须指定字符串大小,类型为 nchar(10) 的列表示此列的字符串最多存储 10 个 nchar 字符,会固定占用 40 bytes 的空间。如果用户字符串长度超出声明长度,将会报错。 | +| 11 | JSON | | json 数据类型, 只有 tag 可以是 json 格式 | + +:::tip +TDengine 对 SQL 语句中的英文字符不区分大小写,自动转化为小写执行。因此用户大小写敏感的字符串及密码,需要使用单引号将字符串引起来。 + +::: + +:::note +虽然 BINARY 类型在底层存储上支持字节型的二进制字符,但不同编程语言对二进制数据的处理方式并不保证一致,因此建议在 BINARY 类型中只存储 ASCII 可见字符,而避免存储不可见字符。多字节的数据,例如中文字符,则需要使用 NCHAR 类型进行保存。如果强行使用 BINARY 类型保存中文字符,虽然有时也能正常读写,但并不带有字符集信息,很容易出现数据乱码甚至数据损坏等情况。 + +::: + +:::note +SQL 语句中的数值类型将依据是否存在小数点,或使用科学计数法表示,来判断数值类型是否为整型或者浮点型,因此在使用时要注意相应类型越界的情况。例如,9999999999999999999 会认为超过长整型的上边界而溢出,而 9999999999999999999.0 会被认为是有效的浮点数。 + +::: diff --git a/docs-cn/12-taos-sql/02-database.md b/docs-cn/12-taos-sql/02-database.md new file mode 100644 index 0000000000000000000000000000000000000000..566fec324148fede8d897869656b83e657569f59 --- /dev/null +++ b/docs-cn/12-taos-sql/02-database.md @@ -0,0 +1,128 @@ +--- +sidebar_label: 数据库管理 +title: 数据库管理 +description: "创建、删除数据库,查看、修改数据库参数" +--- + +## 创建数据库 + +``` +CREATE DATABASE [IF NOT EXISTS] db_name [KEEP keep] [DAYS days] [UPDATE 1]; +``` + +:::info +1. KEEP 是该数据库的数据保留多长天数,缺省是 3650 天(10 年),数据库会自动删除超过时限的数据; +2. UPDATE 标志数据库支持更新相同时间戳数据;(从 2.1.7.0 版本开始此参数支持设为 2,表示允许部分列更新,也即更新数据行时未被设置的列会保留原值。)(从 2.0.8.0 版本开始支持此参数。注意此参数不能通过 `ALTER DATABASE` 指令进行修改。) + 1. UPDATE 设为 0 时,表示不允许更新数据,后发送的相同时间戳的数据会被直接丢弃; + 2. UPDATE 设为 1 时,表示更新全部列数据,即如果更新一个数据行,其中某些列没有提供取值,那么这些列会被设为 NULL; + 3. UPDATE 设为 2 时,表示支持更新部分列数据,即如果更新一个数据行,其中某些列没有提供取值,那么这些列会保持原有数据行中的对应值; + 4. 更多关于 UPDATE 参数的用法,请参考[FAQ](/train-faq/faq)。 +3. 数据库名最大长度为 33; +4. 一条 SQL 语句的最大长度为 65480 个字符; +5. 创建数据库时可用的参数有: + - cache: [详细说明](/reference/config/#cache) + - blocks: [详细说明](/reference/config/#blocks) + - days: [详细说明](/reference/config/#days) + - keep: [详细说明](/reference/config/#keep) + - minRows: [详细说明](/reference/config/#minrows) + - maxRows: [详细说明](/reference/config/#maxrows) + - wal: [详细说明](/reference/config/#wallevel) + - fsync: [详细说明](/reference/config/#fsync) + - update: [详细说明](/reference/config/#update) + - cacheLast: [详细说明](/reference/config/#cachelast) + - replica: [详细说明](/reference/config/#replica) + - quorum: [详细说明](/reference/config/#quorum) + - maxVgroupsPerDb: [详细说明](/reference/config/#maxvgroupsperdb) + - comp: [详细说明](/reference/config/#comp) + - precision: [详细说明](/reference/config/#precision) +6. 请注意上面列出的所有参数都可以配置在配置文件 `taosd.cfg` 中作为创建数据库时使用的默认配置, `create database` 的参数中明确指定的会覆盖配置文件中的设置。 + +::: + +### 创建数据库示例 + +创建时间精度为纳秒的数据库, 保留 1 年数据: + +```sql +CREATE DATABASE test PRECISION 'ns' KEEP 365; +``` + +## 显示系统当前参数 + +``` +SHOW VARIABLES; +``` + +## 使用数据库 + +``` +USE db_name; +``` + +使用/切换数据库(在 REST 连接方式下无效)。 + +## 删除数据库 + +``` +DROP DATABASE [IF EXISTS] db_name; +``` + +删除数据库。指定 Database 所包含的全部数据表将被删除,谨慎使用! + +## 修改数据库参数 + +``` +ALTER DATABASE db_name COMP 2; +``` + +COMP 参数是指修改数据库文件压缩标志位,缺省值为 2,取值范围为 [0, 2]。0 表示不压缩,1 表示一阶段压缩,2 表示两阶段压缩。 + +``` +ALTER DATABASE db_name REPLICA 2; +``` + +REPLICA 参数是指修改数据库副本数,取值范围 [1, 3]。在集群中使用,副本数必须小于或等于 DNODE 的数目。 + +``` +ALTER DATABASE db_name KEEP 365; +``` + +KEEP 参数是指修改数据文件保存的天数,缺省值为 3650,取值范围 [days, 365000],必须大于或等于 days 参数值。 + +``` +ALTER DATABASE db_name QUORUM 2; +``` + +QUORUM 参数是指数据写入成功所需要的确认数,取值范围 [1, 2]。对于异步复制,quorum 设为 1,具有 master 角色的虚拟节点自己确认即可。对于同步复制,quorum 设为 2。原则上,Quorum >= 1 并且 Quorum <= replica(副本数),这个参数在启动一个同步模块实例时需要提供。 + +``` +ALTER DATABASE db_name BLOCKS 100; +``` + +BLOCKS 参数是每个 VNODE (TSDB) 中有多少 cache 大小的内存块,因此一个 VNODE 的用的内存大小粗略为(cache \* blocks)。取值范围 [3, 1000]。 + +``` +ALTER DATABASE db_name CACHELAST 0; +``` + +CACHELAST 参数控制是否在内存中缓存子表的最近数据。缺省值为 0,取值范围 [0, 1, 2, 3]。其中 0 表示不缓存,1 表示缓存子表最近一行数据,2 表示缓存子表每一列的最近的非 NULL 值,3 表示同时打开缓存最近行和列功能。(从 2.0.11.0 版本开始支持参数值 [0, 1],从 2.1.2.0 版本开始支持参数值 [0, 1, 2, 3]。) +说明:缓存最近行,将显著改善 LAST_ROW 函数的性能表现;缓存每列的最近非 NULL 值,将显著改善无特殊影响(WHERE、ORDER BY、GROUP BY、INTERVAL)下的 LAST 函数的性能表现。 + +:::tip +以上所有参数修改后都可以用 show databases 来确认是否修改成功。另外,从 2.1.3.0 版本开始,修改这些参数后无需重启服务器即可生效。 +::: + +## 显示系统所有数据库 + +``` +SHOW DATABASES; +``` + +## 显示一个数据库的创建语句 + +``` +SHOW CREATE DATABASE db_name; +``` + +常用于数据库迁移。对一个已经存在的数据库,返回其创建语句;在另一个集群中执行该语句,就能得到一个设置完全相同的 Database。 + diff --git a/docs-cn/12-taos-sql/03-table.md b/docs-cn/12-taos-sql/03-table.md new file mode 100644 index 0000000000000000000000000000000000000000..675c157b3def0d670f771f55b767f3ca4f2a28af --- /dev/null +++ b/docs-cn/12-taos-sql/03-table.md @@ -0,0 +1,123 @@ +--- +title: 表管理 +--- + +## 创建数据表 + +``` +CREATE TABLE [IF NOT EXISTS] tb_name (timestamp_field_name TIMESTAMP, field1_name data_type1 [, field2_name data_type2 ...]); +``` + +:::info 说明 + +1. 表的第一个字段必须是 TIMESTAMP,并且系统自动将其设为主键; +2. 表名最大长度为 192; +3. 表的每行长度不能超过 16k 个字符;(注意:每个 BINARY/NCHAR 类型的列还会额外占用 2 个字节的存储位置) +4. 子表名只能由字母、数字和下划线组成,且不能以数字开头,不区分大小写 +5. 使用数据类型 binary 或 nchar,需指定其最长的字节数,如 binary(20),表示 20 字节; +6. 为了兼容支持更多形式的表名,TDengine 引入新的转义符 "\`",可以让表名与关键词不冲突,同时不受限于上述表名称合法性约束检查。但是同样具有长度限制要求。使用转义字符以后,不再对转义字符中的内容进行大小写统一。 + 例如:\`aBc\` 和 \`abc\` 是不同的表名,但是 abc 和 aBc 是相同的表名。 + 需要注意的是转义字符中的内容必须是可打印字符。 + 上述的操作逻辑和约束要求与 MySQL 数据的操作一致。 + 从 2.3.0.0 版本开始支持这种方式。 + +::: + +### 以超级表为模板创建数据表 + +``` +CREATE TABLE [IF NOT EXISTS] tb_name USING stb_name TAGS (tag_value1, ...); +``` + +以指定的超级表为模板,指定 TAGS 的值来创建数据表。 + +### 以超级表为模板创建数据表,并指定具体的 TAGS 列 + +``` +CREATE TABLE [IF NOT EXISTS] tb_name USING stb_name (tag_name1, ...) TAGS (tag_value1, ...); +``` + +以指定的超级表为模板,指定一部分 TAGS 列的值来创建数据表(没被指定的 TAGS 列会设为空值)。 +说明:从 2.0.17.0 版本开始支持这种方式。在之前的版本中,不允许指定 TAGS 列,而必须显式给出所有 TAGS 列的取值。 + +### 批量创建数据表 + +``` +CREATE TABLE [IF NOT EXISTS] tb_name1 USING stb_name TAGS (tag_value1, ...) [IF NOT EXISTS] tb_name2 USING stb_name TAGS (tag_value2, ...) ...; +``` + +以更快的速度批量创建大量数据表(服务器端 2.0.14 及以上版本)。 + +:::info + +1.批量建表方式要求数据表必须以超级表为模板。 2.在不超出 SQL 语句长度限制的前提下,单条语句中的建表数量建议控制在 1000 ~ 3000 之间,将会获得比较理想的建表速度。 + +::: + +## 删除数据表 + +``` +DROP TABLE [IF EXISTS] tb_name; +``` + +## 显示当前数据库下的所有数据表信息 + +``` +SHOW TABLES [LIKE tb_name_wildchar]; +``` + +显示当前数据库下的所有数据表信息。 + +## 显示一个数据表的创建语句 + +``` +SHOW CREATE TABLE tb_name; +``` + +常用于数据库迁移。对一个已经存在的数据表,返回其创建语句;在另一个集群中执行该语句,就能得到一个结构完全相同的数据表。 + +## 获取表的结构信息 + +``` +DESCRIBE tb_name; +``` + +## 修改表定义 + +### 表增加列 + +``` +ALTER TABLE tb_name ADD COLUMN field_name data_type; +``` + +:::info + +1. 列的最大个数为 1024,最小个数为 2;(从 2.1.7.0 版本开始,改为最多允许 4096 列) +2. 列名最大长度为 64。 + +::: + +### 表删除列 + +``` +ALTER TABLE tb_name DROP COLUMN field_name; +``` + +如果表是通过超级表创建,更改表结构的操作只能对超级表进行。同时针对超级表的结构更改对所有通过该结构创建的表生效。对于不是通过超级表创建的表,可以直接修改表结构。 + +### 表修改列宽 + +``` +ALTER TABLE tb_name MODIFY COLUMN field_name data_type(length); +``` + +如果数据列的类型是可变长格式(BINARY 或 NCHAR),那么可以使用此指令修改其宽度(只能改大,不能改小)。(2.1.3.0 版本新增) +如果表是通过超级表创建,更改表结构的操作只能对超级表进行。同时针对超级表的结构更改对所有通过该结构创建的表生效。对于不是通过超级表创建的表,可以直接修改表结构。 + +### 修改子表标签值 + +``` +ALTER TABLE tb_name SET TAG tag_name=new_tag_value; +``` + +如果表是通过超级表创建,可以使用此指令修改其标签值 diff --git a/docs-cn/12-taos-sql/04-stable.md b/docs-cn/12-taos-sql/04-stable.md new file mode 100644 index 0000000000000000000000000000000000000000..a3c227317c85917b64b2477994d335710610ec70 --- /dev/null +++ b/docs-cn/12-taos-sql/04-stable.md @@ -0,0 +1,125 @@ +--- +sidebar_label: 超级表管理 +title: 超级表 STable 管理 +--- + +:::note + +在 2.0.15.0 及以后的版本中开始支持 STABLE 保留字。也即,在本节后文的指令说明中,CREATE、DROP、ALTER 三个指令在 2.0.15.0 之前的版本中 STABLE 保留字需写作 TABLE。 + +::: + +## 创建超级表 + +``` +CREATE STABLE [IF NOT EXISTS] stb_name (timestamp_field_name TIMESTAMP, field1_name data_type1 [, field2_name data_type2 ...]) TAGS (tag1_name tag_type1, tag2_name tag_type2 [, tag3_name tag_type3]); +``` + +创建 STable,与创建表的 SQL 语法相似,但需要指定 TAGS 字段的名称和类型。 + +:::info + +1. TAGS 列的数据类型不能是 timestamp 类型;(从 2.1.3.0 版本开始,TAGS 列中支持使用 timestamp 类型,但需注意在 TAGS 中的 timestamp 列写入数据时需要提供给定值,而暂不支持四则运算,例如 `NOW + 10s` 这类表达式) +2. TAGS 列名不能与其他列名相同; +3. TAGS 列名不能为预留关键字(参见:[参数限制与保留关键字](/taos-sql/keywords/) 章节); +4. TAGS 最多允许 128 个,至少 1 个,总长度不超过 16 KB。 + +::: + +## 删除超级表 + +``` +DROP STABLE [IF EXISTS] stb_name; +``` + +删除 STable 会自动删除通过 STable 创建的子表。 + +## 显示当前数据库下的所有超级表信息 + +``` +SHOW STABLES [LIKE tb_name_wildcard]; +``` + +查看数据库内全部 STable,及其相关信息,包括 STable 的名称、创建时间、列数量、标签(TAG)数量、通过该 STable 建表的数量。 + +## 显示一个超级表的创建语句 + +``` +SHOW CREATE STABLE stb_name; +``` + +常用于数据库迁移。对一个已经存在的超级表,返回其创建语句;在另一个集群中执行该语句,就能得到一个结构完全相同的超级表。 + +## 获取超级表的结构信息 + +``` +DESCRIBE stb_name; +``` + +## 修改超级表普通列 + +### 超级表增加列 + +``` +ALTER STABLE stb_name ADD COLUMN field_name data_type; +``` + +### 超级表删除列 + +``` +ALTER STABLE stb_name DROP COLUMN field_name; +``` + +### 超级表修改列宽 + +``` +ALTER STABLE stb_name MODIFY COLUMN field_name data_type(length); +``` + +如果数据列的类型是可变长格式(BINARY 或 NCHAR),那么可以使用此指令修改其宽度(只能改大,不能改小)。(2.1.3.0 版本新增) + +## 修改超级表标签列 + +### 添加标签 + +``` +ALTER STABLE stb_name ADD TAG new_tag_name tag_type; +``` + +为 STable 增加一个新的标签,并指定新标签的类型。标签总数不能超过 128 个,总长度不超过 16k 个字符。 + +### 删除标签 + +``` +ALTER STABLE stb_name DROP TAG tag_name; +``` + +删除超级表的一个标签,从超级表删除某个标签后,该超级表下的所有子表也会自动删除该标签。 + +### 修改标签名 + +``` +ALTER STABLE stb_name CHANGE TAG old_tag_name new_tag_name; +``` + +修改超级表的标签名,从超级表修改某个标签名后,该超级表下的所有子表也会自动更新该标签名。 + +### 修改标签列宽度 + +``` +ALTER STABLE stb_name MODIFY TAG tag_name data_type(length); +``` + +如果标签的类型是可变长格式(BINARY 或 NCHAR),那么可以使用此指令修改其宽度(只能改大,不能改小)。(2.1.3.0 版本新增) + +### 超级表查询 +使用 SELECT 语句可以完成在超级表上的投影及聚合两类查询,在 WHERE 语句中可以对标签及列进行筛选及过滤。 + +如果在超级表查询语句中不加 ORDER BY, 返回顺序是先返回一个子表的所有数据,然后再返回下个子表的所有数据,所以返回的数据是无序的。如果增加了 ORDER BY 语句,会严格按 ORDER BY 语句指定的顺序返回的。 + + + +:::note +除了更新标签的值的操作是针对子表进行,其他所有的标签操作(添加标签、删除标签等)均只能作用于 STable,不能对单个子表操作。对 STable 添加标签以后,依托于该 STable 建立的所有表将自动增加了一个标签,所有新增标签的默认值都是 NULL。 + +::: diff --git a/docs-cn/12-taos-sql/05-insert.md b/docs-cn/12-taos-sql/05-insert.md new file mode 100644 index 0000000000000000000000000000000000000000..e542e442b78c9033ae37196f4913a7c67fb19d8b --- /dev/null +++ b/docs-cn/12-taos-sql/05-insert.md @@ -0,0 +1,149 @@ +--- +sidebar_label: 数据写入 +title: 数据写入 +--- + +## 写入语法 + +``` +INSERT INTO + tb_name + [USING stb_name [(tag1_name, ...)] TAGS (tag1_value, ...)] + [(field1_name, ...)] + VALUES (field1_value, ...) [(field1_value2, ...) ...] | FILE csv_file_path + [tb2_name + [USING stb_name [(tag1_name, ...)] TAGS (tag1_value, ...)] + [(field1_name, ...)] + VALUES (field1_value, ...) [(field1_value2, ...) ...] | FILE csv_file_path + ...]; +``` + +## 插入一条或多条记录 + +指定已经创建好的数据子表的表名,并通过 VALUES 关键字提供一行或多行数据,即可向数据库写入这些数据。例如,执行如下语句可以写入一行记录: + +``` +INSERT INTO d1001 VALUES (NOW, 10.2, 219, 0.32); +``` + +或者,可以通过如下语句写入两行记录: + +``` +INSERT INTO d1001 VALUES ('2021-07-13 14:06:32.272', 10.2, 219, 0.32) (1626164208000, 10.15, 217, 0.33); +``` + +:::note + +1. 在第二个例子中,两行记录的首列时间戳使用了不同格式的写法。其中字符串格式的时间戳写法不受所在 DATABASE 的时间精度设置影响;而长整形格式的时间戳写法会受到所在 DATABASE 的时间精度设置影响——例子中的时间戳在毫秒精度下可以写作 1626164208000,而如果是在微秒精度设置下就需要写为 1626164208000000,纳秒精度设置下需要写为 1626164208000000000。 +2. 在使用“插入多条记录”方式写入数据时,不能把第一列的时间戳取值都设为 NOW,否则会导致语句中的多条记录使用相同的时间戳,于是就可能出现相互覆盖以致这些数据行无法全部被正确保存。其原因在于,NOW 函数在执行中会被解析为所在 SQL 语句的实际执行时间,出现在同一语句中的多个 NOW 标记也就会被替换为完全相同的时间戳取值。 +3. 允许插入的最老记录的时间戳,是相对于当前服务器时间,减去配置的 keep 值(数据保留的天数);允许插入的最新记录的时间戳,是相对于当前服务器时间,加上配置的 days 值(数据文件存储数据的时间跨度,单位为天)。keep 和 days 都是可以在创建数据库时指定的,缺省值分别是 3650 天和 10 天。 + +::: + +## 插入记录,数据对应到指定的列 + +向数据子表中插入记录时,无论插入一行还是多行,都可以让数据对应到指定的列。对于 SQL 语句中没有出现的列,数据库将自动填充为 NULL。主键(时间戳)不能为 NULL。例如: + +``` +INSERT INTO d1001 (ts, current, phase) VALUES ('2021-07-13 14:06:33.196', 10.27, 0.31); +``` + +:::info +如果不指定列,也即使用全列模式——那么在 VALUES 部分提供的数据,必须为数据表的每个列都显式地提供数据。全列模式写入速度会远快于指定列,因此建议尽可能采用全列写入方式,此时空列可以填入 NULL。 + +::: + +## 向多个表插入记录 + +可以在一条语句中,分别向多个表插入一条或多条记录,并且也可以在插入过程中指定列。例如: + +``` +INSERT INTO d1001 VALUES ('2021-07-13 14:06:34.630', 10.2, 219, 0.32) ('2021-07-13 14:06:35.779', 10.15, 217, 0.33) + d1002 (ts, current, phase) VALUES ('2021-07-13 14:06:34.255', 10.27, 0.31); +``` + +## 插入记录时自动建表 + +如果用户在写数据时并不确定某个表是否存在,此时可以在写入数据时使用自动建表语法来创建不存在的表,若该表已存在则不会建立新表。自动建表时,要求必须以超级表为模板,并写明数据表的 TAGS 取值。例如: + +``` +INSERT INTO d21001 USING meters TAGS ('Beijing.Chaoyang', 2) VALUES ('2021-07-13 14:06:32.272', 10.2, 219, 0.32); +``` + +也可以在自动建表时,只是指定部分 TAGS 列的取值,未被指定的 TAGS 列将置为 NULL。例如: + +``` +INSERT INTO d21001 USING meters (groupId) TAGS (2) VALUES ('2021-07-13 14:06:33.196', 10.15, 217, 0.33); +``` + +自动建表语法也支持在一条语句中向多个表插入记录。例如: + +``` +INSERT INTO d21001 USING meters TAGS ('Beijing.Chaoyang', 2) VALUES ('2021-07-13 14:06:34.630', 10.2, 219, 0.32) ('2021-07-13 14:06:35.779', 10.15, 217, 0.33) + d21002 USING meters (groupId) TAGS (2) VALUES ('2021-07-13 14:06:34.255', 10.15, 217, 0.33) + d21003 USING meters (groupId) TAGS (2) (ts, current, phase) VALUES ('2021-07-13 14:06:34.255', 10.27, 0.31); +``` + +:::info +在 2.0.20.5 版本之前,在使用自动建表语法并指定列时,子表的列名必须紧跟在子表名称后面,而不能如例子里那样放在 TAGS 和 VALUES 之间。从 2.0.20.5 版本开始,两种写法都可以,但不能在一条 SQL 语句中混用,否则会报语法错误。 +::: + +## 插入来自文件的数据记录 + +除了使用 VALUES 关键字插入一行或多行数据外,也可以把要写入的数据放在 CSV 文件中(英文逗号分隔、英文单引号括住每个值)供 SQL 指令读取。其中 CSV 文件无需表头。例如,如果 /tmp/csvfile.csv 文件的内容为: + +``` +'2021-07-13 14:07:34.630', '10.2', '219', '0.32' +'2021-07-13 14:07:35.779', '10.15', '217', '0.33' +``` + +那么通过如下指令可以把这个文件中的数据写入子表中: + +``` +INSERT INTO d1001 FILE '/tmp/csvfile.csv'; +``` + +## 插入来自文件的数据记录,并自动建表 + +从 2.1.5.0 版本开始,支持在插入来自 CSV 文件的数据时,以超级表为模板来自动创建不存在的数据表。例如: + +``` +INSERT INTO d21001 USING meters TAGS ('Beijing.Chaoyang', 2) FILE '/tmp/csvfile.csv'; +``` + +也可以在一条语句中向多个表以自动建表的方式插入记录。例如: + +``` +INSERT INTO d21001 USING meters TAGS ('Beijing.Chaoyang', 2) FILE '/tmp/csvfile_21001.csv' + d21002 USING meters (groupId) TAGS (2) FILE '/tmp/csvfile_21002.csv'; +``` + +## 历史记录写入 + +可使用 IMPORT 或者 INSERT 命令,IMPORT 的语法,功能与 INSERT 完全一样。 + +针对 insert 类型的 SQL 语句,我们采用的流式解析策略,在发现后面的错误之前,前面正确的部分 SQL 仍会执行。下面的 SQL 中,INSERT 语句是无效的,但是 d1001 仍会被创建。 + +``` +taos> CREATE TABLE meters(ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS(location BINARY(30), groupId INT); +Query OK, 0 row(s) affected (0.008245s) + +taos> SHOW STABLES; + name | created_time | columns | tags | tables | +============================================================================================ + meters | 2020-08-06 17:50:27.831 | 4 | 2 | 0 | +Query OK, 1 row(s) in set (0.001029s) + +taos> SHOW TABLES; +Query OK, 0 row(s) in set (0.000946s) + +taos> INSERT INTO d1001 USING meters TAGS('Beijing.Chaoyang', 2) VALUES('a'); + +DB error: invalid SQL: 'a' (invalid timestamp) (0.039494s) + +taos> SHOW TABLES; + table_name | created_time | columns | stable_name | +====================================================================================================== + d1001 | 2020-08-06 17:52:02.097 | 4 | meters | +Query OK, 1 row(s) in set (0.001091s) +``` diff --git a/docs-cn/12-taos-sql/06-select.md b/docs-cn/12-taos-sql/06-select.md new file mode 100644 index 0000000000000000000000000000000000000000..3a860119cfe664f9ac3b0ebd046b5f4f0a612118 --- /dev/null +++ b/docs-cn/12-taos-sql/06-select.md @@ -0,0 +1,462 @@ +--- +sidebar_label: 数据查询 +title: 数据查询 +--- + +## 查询语法 + +``` +SELECT select_expr [, select_expr ...] + FROM {tb_name_list} + [WHERE where_condition] + [SESSION(ts_col, tol_val)] + [STATE_WINDOW(col)] + [INTERVAL(interval_val [, interval_offset]) [SLIDING sliding_val]] + [FILL(fill_mod_and_val)] + [GROUP BY col_list] + [ORDER BY col_list { DESC | ASC }] + [SLIMIT limit_val [SOFFSET offset_val]] + [LIMIT limit_val [OFFSET offset_val]] + [>> export_file]; +``` + +## 通配符 + +通配符 \* 可以用于代指全部列。对于普通表,结果中只有普通列。 + +``` +taos> SELECT * FROM d1001; + ts | current | voltage | phase | +====================================================================================== + 2018-10-03 14:38:05.000 | 10.30000 | 219 | 0.31000 | + 2018-10-03 14:38:15.000 | 12.60000 | 218 | 0.33000 | + 2018-10-03 14:38:16.800 | 12.30000 | 221 | 0.31000 | +Query OK, 3 row(s) in set (0.001165s) +``` + +在针对超级表,通配符包含 _标签列_ 。 + +``` +taos> SELECT * FROM meters; + ts | current | voltage | phase | location | groupid | +===================================================================================================================================== + 2018-10-03 14:38:05.500 | 11.80000 | 221 | 0.28000 | Beijing.Haidian | 2 | + 2018-10-03 14:38:16.600 | 13.40000 | 223 | 0.29000 | Beijing.Haidian | 2 | + 2018-10-03 14:38:05.000 | 10.80000 | 223 | 0.29000 | Beijing.Haidian | 3 | + 2018-10-03 14:38:06.500 | 11.50000 | 221 | 0.35000 | Beijing.Haidian | 3 | + 2018-10-03 14:38:04.000 | 10.20000 | 220 | 0.23000 | Beijing.Chaoyang | 3 | + 2018-10-03 14:38:16.650 | 10.30000 | 218 | 0.25000 | Beijing.Chaoyang | 3 | + 2018-10-03 14:38:05.000 | 10.30000 | 219 | 0.31000 | Beijing.Chaoyang | 2 | + 2018-10-03 14:38:15.000 | 12.60000 | 218 | 0.33000 | Beijing.Chaoyang | 2 | + 2018-10-03 14:38:16.800 | 12.30000 | 221 | 0.31000 | Beijing.Chaoyang | 2 | +Query OK, 9 row(s) in set (0.002022s) +``` + +通配符支持表名前缀,以下两个 SQL 语句均为返回全部的列: + +``` +SELECT * FROM d1001; +SELECT d1001.* FROM d1001; +``` + +在 JOIN 查询中,带前缀的\*和不带前缀\*返回的结果有差别, \*返回全部表的所有列数据(不包含标签),带前缀的通配符,则只返回该表的列数据。 + +``` +taos> SELECT * FROM d1001, d1003 WHERE d1001.ts=d1003.ts; + ts | current | voltage | phase | ts | current | voltage | phase | +================================================================================================================================== + 2018-10-03 14:38:05.000 | 10.30000| 219 | 0.31000 | 2018-10-03 14:38:05.000 | 10.80000| 223 | 0.29000 | +Query OK, 1 row(s) in set (0.017385s) +``` + +``` +taos> SELECT d1001.* FROM d1001,d1003 WHERE d1001.ts = d1003.ts; + ts | current | voltage | phase | +====================================================================================== + 2018-10-03 14:38:05.000 | 10.30000 | 219 | 0.31000 | +Query OK, 1 row(s) in set (0.020443s) +``` + +在使用 SQL 函数来进行查询的过程中,部分 SQL 函数支持通配符操作。其中的区别在于: +`count(*)`函数只返回一列。`first`、`last`、`last_row`函数则是返回全部列。 + +``` +taos> SELECT COUNT(*) FROM d1001; + count(*) | +======================== + 3 | +Query OK, 1 row(s) in set (0.001035s) +``` + +``` +taos> SELECT FIRST(*) FROM d1001; + first(ts) | first(current) | first(voltage) | first(phase) | +========================================================================================= + 2018-10-03 14:38:05.000 | 10.30000 | 219 | 0.31000 | +Query OK, 1 row(s) in set (0.000849s) +``` + +## 标签列 + +从 2.0.14 版本开始,支持在普通表的查询中指定 _标签列_,且标签列的值会与普通列的数据一起返回。 + +``` +taos> SELECT location, groupid, current FROM d1001 LIMIT 2; + location | groupid | current | +====================================================================== + Beijing.Chaoyang | 2 | 10.30000 | + Beijing.Chaoyang | 2 | 12.60000 | +Query OK, 2 row(s) in set (0.003112s) +``` + +注意:普通表的通配符 \* 中并不包含 _标签列_。 + +## 获取标签列或普通列的去重取值 + +从 2.0.15.0 版本开始,支持在超级表查询标签列时,指定 DISTINCT 关键字,这样将返回指定标签列的所有不重复取值。注意,在 2.1.6.0 版本之前,DISTINCT 只支持处理单个标签列,而从 2.1.6.0 版本开始,DISTINCT 可以对多个标签列进行处理,输出这些标签列取值不重复的组合。 + +```sql +SELECT DISTINCT tag_name [, tag_name ...] FROM stb_name; +``` + +从 2.1.7.0 版本开始,DISTINCT 也支持对数据子表或普通表进行处理,也即支持获取单个普通列的不重复取值,或多个普通列取值的不重复组合。 + +```sql +SELECT DISTINCT col_name [, col_name ...] FROM tb_name; +``` + +:::info + +1. cfg 文件中的配置参数 maxNumOfDistinctRes 将对 DISTINCT 能够输出的数据行数进行限制。其最小值是 100000,最大值是 100000000,默认值是 10000000。如果实际计算结果超出了这个限制,那么会仅输出这个数量范围内的部分。 +2. 由于浮点数天然的精度机制原因,在特定情况下,对 FLOAT 和 DOUBLE 列使用 DISTINCT 并不能保证输出值的完全唯一性。 +3. 在当前版本下,DISTINCT 不能在嵌套查询的子查询中使用,也不能与聚合函数、GROUP BY、或 JOIN 在同一条语句中混用。 + +::: + +## 结果集列名 + +`SELECT`子句中,如果不指定返回结果集合的列名,结果集列名称默认使用`SELECT`子句中的表达式名称作为列名称。此外,用户可使用`AS`来重命名返回结果集合中列的名称。例如: + +``` +taos> SELECT ts, ts AS primary_key_ts FROM d1001; + ts | primary_key_ts | +==================================================== + 2018-10-03 14:38:05.000 | 2018-10-03 14:38:05.000 | + 2018-10-03 14:38:15.000 | 2018-10-03 14:38:15.000 | + 2018-10-03 14:38:16.800 | 2018-10-03 14:38:16.800 | +Query OK, 3 row(s) in set (0.001191s) +``` + +但是针对`first(*)`、`last(*)`、`last_row(*)`不支持针对单列的重命名。 + +## 隐式结果列 + +`Select_exprs`可以是表所属列的列名,也可以是基于列的函数表达式或计算式,数量的上限 256 个。当用户使用了`interval`或`group by tags`的子句以后,在最后返回结果中会强制返回时间戳列(第一列)和 group by 子句中的标签列。后续的版本中可以支持关闭 group by 子句中隐式列的输出,列输出完全由 select 子句控制。 + +## 表(超级表)列表 + +FROM 关键字后面可以是若干个表(超级表)列表,也可以是子查询的结果。 +如果没有指定用户的当前数据库,可以在表名称之前使用数据库的名称来指定表所属的数据库。例如:`power.d1001` 方式来跨库使用表。 + +``` +SELECT * FROM power.d1001; +------------------------------ +USE power; +SELECT * FROM d1001; +``` + +## 特殊功能 + +部分特殊的查询功能可以不使用 FROM 子句执行。获取当前所在的数据库 database(): + +``` +taos> SELECT DATABASE(); + database() | +================================= + power | +Query OK, 1 row(s) in set (0.000079s) +``` + +如果登录的时候没有指定默认数据库,且没有使用`USE`命令切换数据,则返回 NULL。 + +``` +taos> SELECT DATABASE(); + database() | +================================= + NULL | +Query OK, 1 row(s) in set (0.000184s) +``` + +获取服务器和客户端版本号: + +``` +taos> SELECT CLIENT_VERSION(); + client_version() | +=================== + 2.0.0.0 | +Query OK, 1 row(s) in set (0.000070s) + +taos> SELECT SERVER_VERSION(); + server_version() | +=================== + 2.0.0.0 | +Query OK, 1 row(s) in set (0.000077s) +``` + +服务器状态检测语句。如果服务器正常,返回一个数字(例如 1)。如果服务器异常,返回 error code。该 SQL 语法能兼容连接池对于 TDengine 状态的检查及第三方工具对于数据库服务器状态的检查。并可以避免出现使用了错误的心跳检测 SQL 语句导致的连接池连接丢失的问题。 + +``` +taos> SELECT SERVER_STATUS(); + server_status() | +================== + 1 | +Query OK, 1 row(s) in set (0.000074s) + +taos> SELECT SERVER_STATUS() AS status; + status | +============== + 1 | +Query OK, 1 row(s) in set (0.000081s) +``` + +## \_block_dist 函数 + +**功能说明**: 用于获得指定的(超级)表的数据块分布信息 + +```txt title="语法" +SELECT _block_dist() FROM { tb_name | stb_name } +``` + +**返回结果类型**:字符串。 + +**适用数据类型**:不能输入任何参数。 + +**嵌套子查询支持**:不支持子查询或嵌套查询。 + +**返回结果**: + +- 返回 FROM 子句中输入的表或超级表的数据块分布情况。不支持查询条件。 +- 返回的结果是该表或超级表的数据块所包含的行数的数据分布直方图。 + +```txt title="返回结果" +summary: +5th=[392], 10th=[392], 20th=[392], 30th=[392], 40th=[792], 50th=[792] 60th=[792], 70th=[792], 80th=[792], 90th=[792], 95th=[792], 99th=[792] Min=[392(Rows)] Max=[800(Rows)] Avg=[666(Rows)] Stddev=[2.17] Rows=[2000], Blocks=[3], Size=[5.440(Kb)] Comp=[0.23] RowsInMem=[0] SeekHeaderTime=[1(us)] +``` + +**上述信息的说明如下**: + +- 查询的(超级)表所包含的存储在文件中的数据块(data block)中所包含的数据行的数量分布直方图信息:5%, 10%, 20%, 30%, 40%, 50%, 60%, 70%, 80%, 90%, 95%, 99% 的数值; +- 所有数据块中,包含行数最少的数据块所包含的行数量, 其中的 Min 指标 392 行。 +- 所有数据块中,包含行数最多的数据块所包含的行数量, 其中的 Max 指标 800 行。 +- 所有数据块行数的算数平均值 666 行(其中的 Avg 项)。 +- 所有数据块中行数分布的均方差为 2.17 ( stddev )。 +- 数据块包含的行的总数为 2000 行(Rows)。 +- 数据块总数是 3 个数据块 (Blocks)。 +- 数据块占用磁盘空间大小 5.44 Kb (size)。 +- 压缩后的数据块的大小除以原始数据的所获得的压缩比例: 23%(Comp),及压缩后的数据规模是原始数据规模的 23%。 +- 内存中存在的数据行数是 0,表示内存中没有数据缓存。 +- 获取数据块信息的过程中读取头文件的时间开销 1 微秒(SeekHeaderTime)。 + +**支持版本**:指定计算算法的功能从 2.1.0.x 版本开始,2.1.0.0 之前的版本不支持指定使用算法的功能。 + +## TAOS SQL 中特殊关键词 + +- `TBNAME`: 在超级表查询中可视为一个特殊的标签,代表查询涉及的子表名 +- `_c0`: 表示表(超级表)的第一列 + +## 小技巧 + +获取一个超级表所有的子表名及相关的标签信息: + +``` +SELECT TBNAME, location FROM meters; +``` + +统计超级表下辖子表数量: + +``` +SELECT COUNT(TBNAME) FROM meters; +``` + +以上两个查询均只支持在 WHERE 条件子句中添加针对标签(TAGS)的过滤条件。例如: + +``` +taos> SELECT TBNAME, location FROM meters; + tbname | location | +================================================================== + d1004 | Beijing.Haidian | + d1003 | Beijing.Haidian | + d1002 | Beijing.Chaoyang | + d1001 | Beijing.Chaoyang | +Query OK, 4 row(s) in set (0.000881s) + +taos> SELECT COUNT(tbname) FROM meters WHERE groupId > 2; + count(tbname) | +======================== + 2 | +Query OK, 1 row(s) in set (0.001091s) +``` + +- 可以使用 \* 返回所有列,或指定列名。可以对数字列进行四则运算,可以给输出的列取列名。 + - 暂不支持含列名的四则运算表达式用于条件过滤算子(例如,不支持 `where a*2>6;`,但可以写 `where a>6/2;`)。 + - 暂不支持含列名的四则运算表达式作为 SQL 函数的应用对象(例如,不支持 `select min(2*a) from t;`,但可以写 `select 2*min(a) from t;`)。 +- WHERE 语句可以使用各种逻辑判断来过滤数字值,或使用通配符来过滤字符串。 +- 输出结果缺省按首列时间戳升序排序,但可以指定按降序排序( \_c0 指首列时间戳)。使用 ORDER BY 对其他字段进行排序,排序结果顺序不确定。 +- 参数 LIMIT 控制输出条数,OFFSET 指定从第几条开始输出。LIMIT/OFFSET 对结果集的执行顺序在 ORDER BY 之后。且 `LIMIT 5 OFFSET 2` 可以简写为 `LIMIT 2, 5`。 + - 在有 GROUP BY 子句的情况下,LIMIT 参数控制的是每个分组中至多允许输出的条数。 +- 参数 SLIMIT 控制由 GROUP BY 指令划分的分组中,至多允许输出几个分组的数据。且 `SLIMIT 5 SOFFSET 2` 可以简写为 `SLIMIT 2, 5`。 +- 通过 “>>” 输出结果可以导出到指定文件。 + +## 条件过滤操作 + +| **Operation** | **Note** | **Applicable Data Types** | +| ------------- | ------------------------ | ----------------------------------------- | +| > | larger than | all types except bool | +| < | smaller than | all types except bool | +| >= | larger than or equal to | all types except bool | +| <= | smaller than or equal to | all types except bool | +| = | equal to | all types | +| <\> | not equal to | all types | +| is [not] null | is null or is not null | all types | +| between and | within a certain range | all types except bool | +| in | match any value in a set | all types except first column `timestamp` | +| like | match a wildcard string | **`binary`** **`nchar`** | +| match/nmatch | filter regex | **`binary`** **`nchar`** | + +**使用说明**: + +- <\> 算子也可以写为 != ,请注意,这个算子不能用于数据表第一列的 timestamp 字段。 +- like 算子使用通配符字符串进行匹配检查。 + - 在通配符字符串中:'%'(百分号)匹配 0 到任意个字符;'\_'(下划线)匹配单个任意 ASCII 字符。 + - 如果希望匹配字符串中原本就带有的 \_(下划线)字符,那么可以在通配符字符串中写作 `\_`,也即加一个反斜线来进行转义。(从 2.2.0.0 版本开始支持) + - 通配符字符串最长不能超过 20 字节。(从 2.1.6.1 版本开始,通配符字符串的长度放宽到了 100 字节,并可以通过 taos.cfg 中的 maxWildCardsLength 参数来配置这一长度限制。但不建议使用太长的通配符字符串,将有可能严重影响 LIKE 操作的执行性能。) +- 同时进行多个字段的范围过滤,需要使用关键词 AND 来连接不同的查询条件,暂不支持 OR 连接的不同列之间的查询过滤条件。 + - 从 2.3.0.0 版本开始,已支持完整的同一列和/或不同列间的 AND/OR 运算。 +- 针对单一字段的过滤,如果是时间过滤条件,则一条语句中只支持设定一个;但针对其他的(普通)列或标签列,则可以使用 `OR` 关键字进行组合条件的查询过滤。例如: `((value > 20 AND value < 30) OR (value < 12))`。 + - 从 2.3.0.0 版本开始,允许使用多个时间过滤条件,但首列时间戳的过滤运算结果只能包含一个区间。 +- 从 2.0.17.0 版本开始,条件过滤开始支持 BETWEEN AND 语法,例如 `WHERE col2 BETWEEN 1.5 AND 3.25` 表示查询条件为“1.5 ≤ col2 ≤ 3.25”。 +- 从 2.1.4.0 版本开始,条件过滤开始支持 IN 算子,例如 `WHERE city IN ('Beijing', 'Shanghai')`。说明:BOOL 类型写作 `{true, false}` 或 `{0, 1}` 均可,但不能写作 0、1 之外的整数;FLOAT 和 DOUBLE 类型会受到浮点数精度影响,集合内的值在精度范围内认为和数据行的值完全相等才能匹配成功;TIMESTAMP 类型支持非主键的列。 +- 从 2.3.0.0 版本开始,条件过滤开始支持正则表达式,关键字 match/nmatch,不区分大小写。 + +## 正则表达式过滤 + +### 语法 + +```txt +WHERE (column|tbname) **match/MATCH/nmatch/NMATCH** _regex_ +``` + +### 正则表达式规范 + +确保使用的正则表达式符合 POSIX 的规范,具体规范内容可参见[Regular Expressions](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html) + +### 使用限制 + +只能针对表名(即 tbname 筛选)、binary/nchar 类型标签值进行正则表达式过滤,不支持普通列的过滤。 + +正则匹配字符串长度不能超过 128 字节。可以通过参数 _maxRegexStringLen_ 设置和调整最大允许的正则匹配字符串,该参数是客户端配置参数,需要重启才能生效。 + +## JOIN 子句 + +从 2.2.0.0 版本开始,TDengine 对内连接(INNER JOIN)中的自然连接(Natural join)操作实现了完整的支持。也即支持“普通表与普通表之间”、“超级表与超级表之间”、“子查询与子查询之间”进行自然连接。自然连接与内连接的主要区别是,自然连接要求参与连接的字段在不同的表/超级表中必须是同名字段。也即,TDengine 在连接关系的表达中,要求必须使用同名数据列/标签列的相等关系。 + +在普通表与普通表之间的 JOIN 操作中,只能使用主键时间戳之间的相等关系。例如: + +```sql +SELECT * +FROM temp_tb_1 t1, pressure_tb_1 t2 +WHERE t1.ts = t2.ts +``` + +在超级表与超级表之间的 JOIN 操作中,除了主键时间戳一致的条件外,还要求引入能实现一一对应的标签列的相等关系。例如: + +```sql +SELECT * +FROM temp_stable t1, temp_stable t2 +WHERE t1.ts = t2.ts AND t1.deviceid = t2.deviceid AND t1.status=0; +``` + +类似地,也可以对多个子查询的查询结果进行 JOIN 操作。 + +:::note + +JOIN语句存在如下限制要求: + +- 参与一条语句中 JOIN 操作的表/超级表最多可以有 10 个。 +- 在包含 JOIN 操作的查询语句中不支持 FILL。 +- 暂不支持参与 JOIN 操作的表之间聚合后的四则运算。 +- 不支持只对其中一部分表做 GROUP BY。 +- JOIN 查询的不同表的过滤条件之间不能为 OR。 +- JOIN 查询要求连接条件不能是普通列,只能针对标签和主时间字段列(第一列)。 + +::: + +## 嵌套查询 + +“嵌套查询”又称为“子查询”,也即在一条 SQL 语句中,“内层查询”的计算结果可以作为“外层查询”的计算对象来使用。 + +从 2.2.0.0 版本开始,TDengine 的查询引擎开始支持在 FROM 子句中使用非关联子查询(“非关联”的意思是,子查询不会用到父查询中的参数)。也即在普通 SELECT 语句的 tb_name_list 位置,用一个独立的 SELECT 语句来代替(这一 SELECT 语句被包含在英文圆括号内),于是完整的嵌套查询 SQL 语句形如: + +``` +SELECT ... FROM (SELECT ... FROM ...) ...; +``` + +:::info + +- 目前仅支持一层嵌套,也即不能在子查询中再嵌入子查询。 +- 内层查询的返回结果将作为“虚拟表”供外层查询使用,此虚拟表可以使用 AS 语法做重命名,以便于外层查询中方便引用。 +- 目前不能在“连续查询”功能中使用子查询。 +- 在内层和外层查询中,都支持普通的表间/超级表间 JOIN。内层查询的计算结果也可以再参与数据子表的 JOIN 操作。 +- 目前内层查询、外层查询均不支持 UNION 操作。 +- 内层查询支持的功能特性与非嵌套的查询语句能力是一致的。 + - 内层查询的 ORDER BY 子句一般没有意义,建议避免这样的写法以免无谓的资源消耗。 +- 与非嵌套的查询语句相比,外层查询所能支持的功能特性存在如下限制: + - 计算函数部分: + - 如果内层查询的结果数据未提供时间戳,那么计算过程依赖时间戳的函数在外层会无法正常工作。例如:TOP, BOTTOM, FIRST, LAST, DIFF。 + - 计算过程需要两遍扫描的函数,在外层查询中无法正常工作。例如:此类函数包括:STDDEV, PERCENTILE。 + - 外层查询中不支持 IN 算子,但在内层中可以使用。 + - 外层查询不支持 GROUP BY。 + +::: + +## UNION ALL 子句 + +```txt title=语法 +SELECT ... +UNION ALL SELECT ... +[UNION ALL SELECT ...] +``` + +TDengine 支持 UNION ALL 操作符。也就是说,如果多个 SELECT 子句返回结果集的结构完全相同(列名、列类型、列数、顺序),那么可以通过 UNION ALL 把这些结果集合并到一起。目前只支持 UNION ALL 模式,也即在结果集的合并过程中是不去重的。在同一个 sql 语句中,UNION ALL 最多支持 100 个。 + +### SQL 示例 + +对于下面的例子,表 tb1 用以下语句创建: + +``` +CREATE TABLE tb1 (ts TIMESTAMP, col1 INT, col2 FLOAT, col3 BINARY(50)); +``` + +查询 tb1 刚过去的一个小时的所有记录: + +``` +SELECT * FROM tb1 WHERE ts >= NOW - 1h; +``` + +查询表 tb1 从 2018-06-01 08:00:00.000 到 2018-06-02 08:00:00.000 时间范围,并且 col3 的字符串是'nny'结尾的记录,结果按照时间戳降序: + +``` +SELECT * FROM tb1 WHERE ts > '2018-06-01 08:00:00.000' AND ts <= '2018-06-02 08:00:00.000' AND col3 LIKE '%nny' ORDER BY ts DESC; +``` + +查询 col1 与 col2 的和,并取名 complex, 时间大于 2018-06-01 08:00:00.000, col2 大于 1.2,结果输出仅仅 10 条记录,从第 5 条开始: + +``` +SELECT (col1 + col2) AS 'complex' FROM tb1 WHERE ts > '2018-06-01 08:00:00.000' AND col2 > 1.2 LIMIT 10 OFFSET 5; +``` + +查询过去 10 分钟的记录,col2 的值大于 3.14,并且将结果输出到文件 `/home/testoutput.csv`: + +``` +SELECT COUNT(*) FROM tb1 WHERE ts >= NOW - 10m AND col2 > 3.14 >> /home/testoutput.csv; +``` diff --git a/docs-cn/12-taos-sql/07-function.md b/docs-cn/12-taos-sql/07-function.md new file mode 100644 index 0000000000000000000000000000000000000000..f6e564419ddaa18931b0f0e0e4e7b5b3219a92f6 --- /dev/null +++ b/docs-cn/12-taos-sql/07-function.md @@ -0,0 +1,1794 @@ +--- +sidebar_label: SQL 函数 +title: SQL 函数 +--- + +## 聚合函数 + +TDengine 支持针对数据的聚合查询。提供支持的聚合和选择函数如下: + +### COUNT + +``` +SELECT COUNT([*|field_name]) FROM tb_name [WHERE clause]; +``` + +**功能说明**:统计表/超级表中记录行数或某列的非空值个数。 + +**返回数据类型**:长整型 INT64。 + +**应用字段**:应用全部字段。 + +**适用于**:表、超级表。 + +**使用说明**: + +- 可以使用星号(\*)来替代具体的字段,使用星号(\*)返回全部记录数量。 +- 针对同一表的(不包含 NULL 值)字段查询结果均相同。 +- 如果统计对象是具体的列,则返回该列中非 NULL 值的记录数量。 + +**示例**: + +``` +taos> SELECT COUNT(*), COUNT(voltage) FROM meters; + count(*) | count(voltage) | +================================================ + 9 | 9 | +Query OK, 1 row(s) in set (0.004475s) + +taos> SELECT COUNT(*), COUNT(voltage) FROM d1001; + count(*) | count(voltage) | +================================================ + 3 | 3 | +Query OK, 1 row(s) in set (0.001075s) +``` + +### AVG + +``` +SELECT AVG(field_name) FROM tb_name [WHERE clause]; +``` + +**功能说明**:统计表/超级表中某列的平均值。 + +**返回数据类型**:双精度浮点数 Double。 + +**应用字段**:不能应用在 timestamp、binary、nchar、bool 字段。 + +**适用于**:表、超级表。 + +**示例**: + +``` +taos> SELECT AVG(current), AVG(voltage), AVG(phase) FROM meters; + avg(current) | avg(voltage) | avg(phase) | +==================================================================================== + 11.466666751 | 220.444444444 | 0.293333333 | +Query OK, 1 row(s) in set (0.004135s) + +taos> SELECT AVG(current), AVG(voltage), AVG(phase) FROM d1001; + avg(current) | avg(voltage) | avg(phase) | +==================================================================================== + 11.733333588 | 219.333333333 | 0.316666673 | +Query OK, 1 row(s) in set (0.000943s) +``` + +### TWA + +``` +SELECT TWA(field_name) FROM tb_name WHERE clause; +``` + +**功能说明**:时间加权平均函数。统计表中某列在一段时间内的时间加权平均。 + +**返回数据类型**:双精度浮点数 Double。 + +**应用字段**:不能应用在 timestamp、binary、nchar、bool 类型字段。 + +**适用于**:表、超级表。 + +**使用说明**: + +- 从 2.1.3.0 版本开始,TWA 函数可以在由 GROUP BY 划分出单独时间线的情况下用于超级表(也即 GROUP BY tbname)。 + +### IRATE + +``` +SELECT IRATE(field_name) FROM tb_name WHERE clause; +``` + +**功能说明**:计算瞬时增长率。使用时间区间中最后两个样本数据来计算瞬时增长速率;如果这两个值呈递减关系,那么只取最后一个数用于计算,而不是使用二者差值。 + +**返回数据类型**:双精度浮点数 Double。 + +**应用字段**:不能应用在 timestamp、binary、nchar、bool 类型字段。 + +**适用于**:表、超级表。 + +**使用说明**: + +- 从 2.1.3.0 版本开始此函数可用,IRATE 可以在由 GROUP BY 划分出单独时间线的情况下用于超级表(也即 GROUP BY tbname)。 + +### SUM + +``` +SELECT SUM(field_name) FROM tb_name [WHERE clause]; +``` + +**功能说明**:统计表/超级表中某列的和。 + +**返回数据类型**:双精度浮点数 Double 和长整型 INT64。 + +**应用字段**:不能应用在 timestamp、binary、nchar、bool 类型字段。 + +**适用于**:表、超级表。 + +**示例**: + +``` +taos> SELECT SUM(current), SUM(voltage), SUM(phase) FROM meters; + sum(current) | sum(voltage) | sum(phase) | +================================================================================ + 103.200000763 | 1984 | 2.640000001 | +Query OK, 1 row(s) in set (0.001702s) + +taos> SELECT SUM(current), SUM(voltage), SUM(phase) FROM d1001; + sum(current) | sum(voltage) | sum(phase) | +================================================================================ + 35.200000763 | 658 | 0.950000018 | +Query OK, 1 row(s) in set (0.000980s) +``` + +### STDDEV + +``` +SELECT STDDEV(field_name) FROM tb_name [WHERE clause]; +``` + +**功能说明**:统计表中某列的均方差。 + +**返回数据类型**:双精度浮点数 Double。 + +**应用字段**:不能应用在 timestamp、binary、nchar、bool 类型字段。 + +**适用于**:表、超级表(从 2.0.15.1 版本开始) + +**示例**: + +``` +taos> SELECT STDDEV(current) FROM d1001; + stddev(current) | +============================ + 1.020892909 | +Query OK, 1 row(s) in set (0.000915s) +``` + +### LEASTSQUARES + +``` +SELECT LEASTSQUARES(field_name, start_val, step_val) FROM tb_name [WHERE clause]; +``` + +**功能说明**:统计表中某列的值是主键(时间戳)的拟合直线方程。start_val 是自变量初始值,step_val 是自变量的步长值。 + +**返回数据类型**:字符串表达式(斜率, 截距)。 + +**应用字段**:不能应用在 timestamp、binary、nchar、bool 类型字段。 + +**适用于**:表。 + +**示例**: + +``` +taos> SELECT LEASTSQUARES(current, 1, 1) FROM d1001; + leastsquares(current, 1, 1) | +===================================================== +{slop:1.000000, intercept:9.733334} | +Query OK, 1 row(s) in set (0.000921s) +``` + +### MODE + +``` +SELECT MODE(field_name) FROM tb_name [WHERE clause]; +``` + +**功能说明**:返回出现频率最高的值,若存在多个频率相同的最高值,输出空。不能匹配标签、时间戳输出。 + +**返回数据类型**:同应用的字段。 + +**应用字段**:适合于除时间主列外的任何类型字段。 + +**使用说明**:由于返回数据量未知,考虑到内存因素,为了函数可以正常返回结果,建议不重复的数据量在 10 万级别,否则会报错。 + +**支持的版本**:2.6.0.0 及以后的版本。 + +**示例**: + +``` +taos> select voltage from d002; + voltage | +======================== + 1 | + 1 | + 2 | + 19 | +Query OK, 4 row(s) in set (0.003545s) + +taos> select mode(voltage) from d002; + mode(voltage) | +======================== + 1 | +Query OK, 1 row(s) in set (0.019393s) +``` + +### HYPERLOGLOG + +``` +SELECT HYPERLOGLOG(field_name) FROM { tb_name | stb_name } [WHERE clause]; +``` + +**功能说明**: + - 采用 hyperloglog 算法,返回某列的基数。该算法在数据量很大的情况下,可以明显降低内存的占用,但是求出来的基数是个估算值,标准误差(标准误差是多次实验,每次的平均数的标准差,不是与真实结果的误差)为 0.81%。 + - 在数据量较少的时候该算法不是很准确,可以使用 select count(data) from (select unique(col) as data from table) 的方法。 + +**返回结果类型**:整形。 + +**应用字段**:适合于任何类型字段。 + +**支持的版本**:2.6.0.0 及以后的版本。 + +**示例**: + +``` +taos> select dbig from shll; + dbig | +======================== + 1 | + 1 | + 1 | + NULL | + 2 | + 19 | + NULL | + 9 | +Query OK, 8 row(s) in set (0.003755s) + +taos> select hyperloglog(dbig) from shll; + hyperloglog(dbig)| +======================== + 4 | +Query OK, 1 row(s) in set (0.008388s) +``` + +## 选择函数 + +在使用所有的选择函数的时候,可以同时指定输出 ts 列或标签列(包括 tbname),这样就可以方便地知道被选出的值是源于哪个数据行的。 + +### MIN + +``` +SELECT MIN(field_name) FROM {tb_name | stb_name} [WHERE clause]; +``` + +**功能说明**:统计表/超级表中某列的值最小值。 + +**返回数据类型**:同应用的字段。 + +**应用字段**:不能应用在 timestamp、binary、nchar、bool 类型字段。 + +**适用于**:表、超级表。 + +**示例**: + +``` +taos> SELECT MIN(current), MIN(voltage) FROM meters; + min(current) | min(voltage) | +====================================== + 10.20000 | 218 | +Query OK, 1 row(s) in set (0.001765s) + +taos> SELECT MIN(current), MIN(voltage) FROM d1001; + min(current) | min(voltage) | +====================================== + 10.30000 | 218 | +Query OK, 1 row(s) in set (0.000950s) +``` + +### MAX + +``` +SELECT MAX(field_name) FROM { tb_name | stb_name } [WHERE clause]; +``` + +**功能说明**:统计表/超级表中某列的值最大值。 + +**返回数据类型**:同应用的字段。 + +**应用字段**:不能应用在 timestamp、binary、nchar、bool 类型字段。 + +**适用于**:表、超级表。 + +**示例**: + +``` +taos> SELECT MAX(current), MAX(voltage) FROM meters; + max(current) | max(voltage) | +====================================== + 13.40000 | 223 | +Query OK, 1 row(s) in set (0.001123s) + +taos> SELECT MAX(current), MAX(voltage) FROM d1001; + max(current) | max(voltage) | +====================================== + 12.60000 | 221 | +Query OK, 1 row(s) in set (0.000987s) +``` + +### FIRST + +``` +SELECT FIRST(field_name) FROM { tb_name | stb_name } [WHERE clause]; +``` + +**功能说明**:统计表/超级表中某列的值最先写入的非 NULL 值。 + +**返回数据类型**:同应用的字段。 + +**应用字段**:所有字段。 + +**适用于**:表、超级表。 + +**使用说明**: + +- 如果要返回各个列的首个(时间戳最小)非 NULL 值,可以使用 FIRST(\*); +- 如果结果集中的某列全部为 NULL 值,则该列的返回结果也是 NULL; +- 如果结果集中所有列全部为 NULL 值,则不返回结果。 + +**示例**: + +``` +taos> SELECT FIRST(*) FROM meters; + first(ts) | first(current) | first(voltage) | first(phase) | +========================================================================================= +2018-10-03 14:38:04.000 | 10.20000 | 220 | 0.23000 | +Query OK, 1 row(s) in set (0.004767s) + +taos> SELECT FIRST(current) FROM d1002; + first(current) | +======================= + 10.20000 | +Query OK, 1 row(s) in set (0.001023s) +``` + +### LAST + +``` +SELECT LAST(field_name) FROM { tb_name | stb_name } [WHERE clause]; +``` + +**功能说明**:统计表/超级表中某列的值最后写入的非 NULL 值。 + +**返回数据类型**:同应用的字段。 + +**应用字段**:所有字段。 + +**适用于**:表、超级表。 + +**使用说明**: + +- 如果要返回各个列的最后(时间戳最大)一个非 NULL 值,可以使用 LAST(\*); +- 如果结果集中的某列全部为 NULL 值,则该列的返回结果也是 NULL;如果结果集中所有列全部为 NULL 值,则不返回结果。 +- 在用于超级表时,时间戳完全一样且同为最大的数据行可能有多个,那么会从中随机返回一条,而并不保证多次运行所挑选的数据行必然一致。 + + +**示例**: + +``` +taos> SELECT LAST(*) FROM meters; + last(ts) | last(current) | last(voltage) | last(phase) | +======================================================================================== +2018-10-03 14:38:16.800 | 12.30000 | 221 | 0.31000 | +Query OK, 1 row(s) in set (0.001452s) + +taos> SELECT LAST(current) FROM d1002; + last(current) | +======================= + 10.30000 | +Query OK, 1 row(s) in set (0.000843s) +``` + +### TOP + +``` +SELECT TOP(field_name, K) FROM { tb_name | stb_name } [WHERE clause]; +``` + +**功能说明**: 统计表/超级表中某列的值最大 _k_ 个非 NULL 值。如果多条数据取值一样,全部取用又会超出 k 条限制时,系统会从相同值中随机选取符合要求的数量返回。 + +**返回数据类型**:同应用的字段。 + +**应用字段**:不能应用在 timestamp、binary、nchar、bool 类型字段。 + +**适用于**:表、超级表。 + +**使用说明**: + +- *k*值取值范围 1≤*k*≤100; +- 系统同时返回该记录关联的时间戳列; +- 限制:TOP 函数不支持 FILL 子句。 + +**示例**: + +``` +taos> SELECT TOP(current, 3) FROM meters; + ts | top(current, 3) | +================================================= +2018-10-03 14:38:15.000 | 12.60000 | +2018-10-03 14:38:16.600 | 13.40000 | +2018-10-03 14:38:16.800 | 12.30000 | +Query OK, 3 row(s) in set (0.001548s) + +taos> SELECT TOP(current, 2) FROM d1001; + ts | top(current, 2) | +================================================= +2018-10-03 14:38:15.000 | 12.60000 | +2018-10-03 14:38:16.800 | 12.30000 | +Query OK, 2 row(s) in set (0.000810s) +``` + +### BOTTOM + +``` +SELECT BOTTOM(field_name, K) FROM { tb_name | stb_name } [WHERE clause]; +``` + +**功能说明**:统计表/超级表中某列的值最小 _k_ 个非 NULL 值。如果多条数据取值一样,全部取用又会超出 k 条限制时,系统会从相同值中随机选取符合要求的数量返回。 + +**返回数据类型**:同应用的字段。 + +**应用字段**:不能应用在 timestamp、binary、nchar、bool 类型字段。 + +**适用于**:表、超级表。 + +**使用说明**: + +- *k*值取值范围 1≤*k*≤100; +- 系统同时返回该记录关联的时间戳列; +- 限制:BOTTOM 函数不支持 FILL 子句。 + +**示例**: + +``` +taos> SELECT BOTTOM(voltage, 2) FROM meters; + ts | bottom(voltage, 2) | +=============================================== +2018-10-03 14:38:15.000 | 218 | +2018-10-03 14:38:16.650 | 218 | +Query OK, 2 row(s) in set (0.001332s) + +taos> SELECT BOTTOM(current, 2) FROM d1001; + ts | bottom(current, 2) | +================================================= +2018-10-03 14:38:05.000 | 10.30000 | +2018-10-03 14:38:16.800 | 12.30000 | +Query OK, 2 row(s) in set (0.000793s) +``` + +### PERCENTILE + +``` +SELECT PERCENTILE(field_name, P) FROM { tb_name } [WHERE clause]; +``` + +**功能说明**:统计表中某列的值百分比分位数。 + +**返回数据类型**: 双精度浮点数 Double。 + +**应用字段**:不能应用在 timestamp、binary、nchar、bool 类型字段。 + +**适用于**:表。 + +**使用说明**:*P*值取值范围 0≤*P*≤100,为 0 的时候等同于 MIN,为 100 的时候等同于 MAX。 + +**示例**: + +``` +taos> SELECT PERCENTILE(current, 20) FROM d1001; +percentile(current, 20) | +============================ + 11.100000191 | +Query OK, 1 row(s) in set (0.000787s) +``` + +### APERCENTILE + +``` +SELECT APERCENTILE(field_name, P[, algo_type]) +FROM { tb_name | stb_name } [WHERE clause] +``` + +**功能说明**:统计表/超级表中指定列的值百分比分位数,与 PERCENTILE 函数相似,但是返回近似结果。 + +**返回数据类型**: 双精度浮点数 Double。 + +**应用字段**:不能应用在 timestamp、binary、nchar、bool 类型字段。 + +**适用于**:表、超级表。 + +**使用说明** + +- **P**值有效取值范围 0≤P≤100,为 0 的时候等同于 MIN,为 100 的时候等同于 MAX; +- **algo_type**的有效输入:**default** 和 **t-digest** +- 用于指定计算近似分位数的算法。可不提供第三个参数的输入,此时将使用 default 的算法进行计算,即 apercentile(column_name, 50, "default") 与 apercentile(column_name, 50) 等价。 +- 当使用“t-digest”参数的时候,将使用 t-digest 方式采样计算近似分位数。但该参数指定计算算法的功能从 2.2.0.x 版本开始支持,2.2.0.0 之前的版本不支持指定使用算法的功能。 + +**嵌套子查询支持**:适用于内层查询和外层查询。 + +``` +taos> SELECT APERCENTILE(current, 20) FROM d1001; +apercentile(current, 20) | +============================ + 10.300000191 | +Query OK, 1 row(s) in set (0.000645s) + +taos> select apercentile (count, 80, 'default') from stb1; + apercentile (c0, 80, 'default') | +================================== + 601920857.210056424 | +Query OK, 1 row(s) in set (0.012363s) + +taos> select apercentile (count, 80, 't-digest') from stb1; + apercentile (c0, 80, 't-digest') | +=================================== + 605869120.966666579 | +Query OK, 1 row(s) in set (0.011639s) +``` + +### LAST_ROW + +``` +SELECT LAST_ROW(field_name) FROM { tb_name | stb_name }; +``` + +**功能说明**:返回表/超级表的最后一条记录。 + +**返回数据类型**:同应用的字段。 + +**应用字段**:所有字段。 + +**适用于**:表、超级表。 + +**使用说明**: + +- 在用于超级表时,时间戳完全一样且同为最大的数据行可能有多个,那么会从中随机返回一条,而并不保证多次运行所挑选的数据行必然一致。 +- 不能与 INTERVAL 一起使用。 + +**示例**: + +``` + taos> SELECT LAST_ROW(current) FROM meters; + last_row(current) | + ======================= + 12.30000 | + Query OK, 1 row(s) in set (0.001238s) + + taos> SELECT LAST_ROW(current) FROM d1002; + last_row(current) | + ======================= + 10.30000 | + Query OK, 1 row(s) in set (0.001042s) +``` + +### INTERP [2.3.1 及之后的版本] + +``` +SELECT INTERP(field_name) FROM { tb_name | stb_name } [WHERE where_condition] [ RANGE(timestamp1,timestamp2) ] [EVERY(interval)] [FILL ({ VALUE | PREV | NULL | LINEAR | NEXT})]; +``` + +**功能说明**:返回表/超级表的指定时间截面指定列的记录值(插值)。 + +**返回数据类型**:同字段类型。 + +**应用字段**:数值型字段。 + +**适用于**:表、超级表、嵌套查询。 + + +**使用说明** + +- INTERP 用于在指定时间断面获取指定列的记录值,如果该时间断面不存在符合条件的行数据,那么会根据 FILL 参数的设定进行插值。 +- INTERP 的输入数据为指定列的数据,可以通过条件语句(where 子句)来对原始列数据进行过滤,如果没有指定过滤条件则输入为全部数据。 +- INTERP 的输出时间范围根据 RANGE(timestamp1,timestamp2)字段来指定,需满足 timestamp1<=timestamp2。其中 timestamp1(必选值)为输出时间范围的起始值,即如果 timestamp1 时刻符合插值条件则 timestamp1 为输出的第一条记录,timestamp2(必选值)为输出时间范围的结束值,即输出的最后一条记录的 timestamp 不能大于 timestamp2。如果没有指定 RANGE,那么满足过滤条件的输入数据中第一条记录的 timestamp 即为 timestamp1,最后一条记录的 timestamp 即为 timestamp2,同样也满足 timestamp1 <= timestamp2。 +- INTERP 根据 EVERY 字段来确定输出时间范围内的结果条数,即从 timestamp1 开始每隔固定长度的时间(EVERY 值)进行插值。如果没有指定 EVERY,则默认窗口大小为无穷大,即从 timestamp1 开始只有一个窗口。 +- INTERP 根据 FILL 字段来决定在每个符合输出条件的时刻如何进行插值,如果没有 FILL 字段则默认不插值,即输出为原始记录值或不输出(原始记录不存在)。 +- INTERP 只能在一个时间序列内进行插值,因此当作用于超级表时必须跟 group by tbname 一起使用,当作用嵌套查询外层时内层子查询不能含 GROUP BY 信息。 +- INTERP 的插值结果不受 ORDER BY timestamp 的影响,ORDER BY timestamp 只影响输出结果的排序。 + +**SQL示例(基于文档中广泛使用的电表 schema )**: + +- 单点线性插值 + +``` + taos> SELECT INTERP(current) FROM t1 RANGE('2017-7-14 18:40:00','2017-7-14 18:40:00') FILL(LINEAR); +``` + +- 在2017-07-14 18:00:00到2017-07-14 19:00:00间每隔5秒钟进行取值(不插值) + +``` + taos> SELECT INTERP(current) FROM t1 RANGE('2017-7-14 18:00:00','2017-7-14 19:00:00') EVERY(5s); +``` + +- 在2017-07-14 18:00:00到2017-07-14 19:00:00间每隔5秒钟进行线性插值 + +``` + taos> SELECT INTERP(current) FROM t1 RANGE('2017-7-14 18:00:00','2017-7-14 19:00:00') EVERY(5s) FILL(LINEAR); +``` + +- 在所有时间范围内每隔 5 秒钟进行向后插值 + +``` + taos> SELECT INTERP(current) FROM t1 EVERY(5s) FILL(NEXT); +``` + +- 根据 2017-07-14 17:00:00 到 2017-07-14 20:00:00 间的数据进行从 2017-07-14 18:00:00 到 2017-07-14 19:00:00 间每隔 5 秒钟进行线性插值 + +``` + taos> SELECT INTERP(current) FROM t1 where ts >= '2017-07-14 17:00:00' and ts <= '2017-07-14 20:00:00' RANGE('2017-7-14 18:00:00','2017-7-14 19:00:00') EVERY(5s) FILL(LINEAR); +``` + +### INTERP [2.3.1 之前的版本] + +``` +SELECT INTERP(field_name) FROM { tb_name | stb_name } WHERE ts='timestamp' [FILL ({ VALUE | PREV | NULL | LINEAR | NEXT})]; +``` + +**功能说明**:返回表/超级表的指定时间截面、指定字段的记录。 + +**返回数据类型**:同字段类型。 + +**应用字段**:数值型字段。 + +**适用于**:表、超级表。 + +**使用说明**: + +- 从 2.0.15.0 及以后版本可用 +- INTERP 必须指定时间断面,如果该时间断面不存在直接对应的数据,那么会根据 FILL 参数的设定进行插值。此外,条件语句里面可附带筛选条件,例如标签、tbname。 +- INTERP 查询要求查询的时间区间必须位于数据集合(表)的所有记录的时间范围之内。如果给定的时间戳位于时间范围之外,即使有插值指令,仍然不返回结果。 +- 单个 INTERP 函数查询只能够针对一个时间点进行查询,如果需要返回等时间间隔的断面数据,可以通过 INTERP 配合 EVERY 的方式来进行查询处理(而不是使用 INTERVAL),其含义是每隔固定长度的时间进行插值 + +**示例**: + +``` + taos> SELECT INTERP(*) FROM meters WHERE ts='2017-7-14 18:40:00.004'; + interp(ts) | interp(current) | interp(voltage) | interp(phase) | + ========================================================================================== + 2017-07-14 18:40:00.004 | 9.84020 | 216 | 0.32222 | + Query OK, 1 row(s) in set (0.002652s) +``` + +如果给定的时间戳无对应的数据,在不指定插值生成策略的情况下,不会返回结果,如果指定了插值策略,会根据插值策略返回结果。 + +``` + taos> SELECT INTERP(*) FROM meters WHERE tbname IN ('d636') AND ts='2017-7-14 18:40:00.005'; + Query OK, 0 row(s) in set (0.004022s) + + taos> SELECT INTERP(*) FROM meters WHERE tbname IN ('d636') AND ts='2017-7-14 18:40:00.005' FILL(PREV); + interp(ts) | interp(current) | interp(voltage) | interp(phase) | + ========================================================================================== + 2017-07-14 18:40:00.005 | 9.88150 | 217 | 0.32500 | + Query OK, 1 row(s) in set (0.003056s) +``` + +如下所示代码表示在时间区间 `['2017-7-14 18:40:00', '2017-7-14 18:40:00.014']` 中每隔 5 毫秒 进行一次断面计算。 + +``` + taos> SELECT INTERP(current) FROM d636 WHERE ts>='2017-7-14 18:40:00' AND ts<='2017-7-14 18:40:00.014' EVERY(5a); + ts | interp(current) | + ================================================= + 2017-07-14 18:40:00.000 | 10.04179 | + 2017-07-14 18:40:00.010 | 10.16123 | + Query OK, 2 row(s) in set (0.003487s) +``` + +### TAIL + +``` +SELECT TAIL(field_name, k, offset_val) FROM {tb_name | stb_name} [WHERE clause]; +``` + +**功能说明**:返回跳过最后 offset_value 个,然后取连续 k 个记录,不忽略 NULL 值。offset_val 可以不输入。此时返回最后的 k 个记录。当有 offset_val 输入的情况下,该函数功能等效于 `order by ts desc LIMIT k OFFSET offset_val`。 + +**参数范围**:k: [1,100] offset_val: [0,100]。 + +**返回结果数据类型**:同应用的字段。 + +**应用字段**:适合于除时间主列外的任何类型字段。 + +**支持版本**:2.6.0.0 及之后的版本。 + +**示例**: + +``` +taos> select ts,dbig from tail2; + ts | dbig | +================================================== +2021-10-15 00:31:33.000 | 1 | +2021-10-17 00:31:31.000 | NULL | +2021-12-24 00:31:34.000 | 2 | +2022-01-01 08:00:05.000 | 19 | +2022-01-01 08:00:06.000 | NULL | +2022-01-01 08:00:07.000 | 9 | +Query OK, 6 row(s) in set (0.001952s) + +taos> select tail(dbig,2,2) from tail2; +ts | tail(dbig,2,2) | +================================================== +2021-12-24 00:31:34.000 | 2 | +2022-01-01 08:00:05.000 | 19 | +Query OK, 2 row(s) in set (0.002307s) +``` + +### UNIQUE + +``` +SELECT UNIQUE(field_name) FROM {tb_name | stb_name} [WHERE clause]; +``` + +**功能说明**:返回该列的数值首次出现的值。该函数功能与 distinct 相似,但是可以匹配标签和时间戳信息。可以针对除时间列以外的字段进行查询,可以匹配标签和时间戳,其中的标签和时间戳是第一次出现时刻的标签和时间戳。 + +**返回结果数据类型**:同应用的字段。 + +**应用字段**:适合于除时间类型以外的字段。 + +**支持版本**:2.6.0.0 及之后的版本。 + +**使用说明**: + +- 该函数可以应用在普通表和超级表上。不能和窗口操作一起使用,例如 interval/state_window/session_window 。 +- 由于返回数据量未知,考虑到内存因素,为了函数可以正常返回结果,建议不重复的数据量在 10 万级别,否则会报错。 + +**示例**: + +``` +taos> select ts,voltage from unique1; + ts | voltage | +================================================== +2021-10-17 00:31:31.000 | 1 | +2022-01-24 00:31:31.000 | 1 | +2021-10-17 00:31:31.000 | 1 | +2021-12-24 00:31:31.000 | 2 | +2022-01-01 08:00:01.000 | 19 | +2021-10-17 00:31:31.000 | NULL | +2022-01-01 08:00:02.000 | NULL | +2022-01-01 08:00:03.000 | 9 | +Query OK, 8 row(s) in set (0.003018s) + +taos> select unique(voltage) from unique1; +ts | unique(voltage) | +================================================== +2021-10-17 00:31:31.000 | 1 | +2021-10-17 00:31:31.000 | NULL | +2021-12-24 00:31:31.000 | 2 | +2022-01-01 08:00:01.000 | 19 | +2022-01-01 08:00:03.000 | 9 | +Query OK, 5 row(s) in set (0.108458s) +``` + +## 计算函数 + +### DIFF + + ```sql + SELECT {DIFF(field_name, ignore_negative) | DIFF(field_name)} FROM tb_name [WHERE clause]; + ``` + +**功能说明**:统计表中某列的值与前一行对应值的差。 ignore_negative 取值为 0|1 , 可以不填,默认值为 0. 不忽略负值。ignore_negative 为 1 时表示忽略负数。 + +**返回结果数据类型**:同应用字段。 + +**应用字段**:不能应用在 timestamp、binary、nchar、bool 类型字段。 + +**适用于**:表、超级表。 + +**使用说明**: + +- 输出结果行数是范围内总行数减一,第一行没有结果输出。 +- 从 2.1.3.0 版本开始,DIFF 函数可以在由 GROUP BY 划分出单独时间线的情况下用于超级表(也即 GROUP BY tbname)。 +- 从 2.6.0 开始,DIFF 函数支持 ignore_negative 参数 + +**示例**: + + ```sql + taos> SELECT DIFF(current) FROM d1001; + ts | diff(current) | + ================================================= + 2018-10-03 14:38:15.000 | 2.30000 | + 2018-10-03 14:38:16.800 | -0.30000 | + Query OK, 2 row(s) in set (0.001162s) + ``` + +### DERIVATIVE + +``` +SELECT DERIVATIVE(field_name, time_interval, ignore_negative) FROM tb_name [WHERE clause]; +``` + +**功能说明**:统计表中某列数值的单位变化率。其中单位时间区间的长度可以通过 time_interval 参数指定,最小可以是 1 秒(1s);ignore_negative 参数的值可以是 0 或 1,为 1 时表示忽略负值。 + +**返回数据类型**:双精度浮点数。 + +**应用字段**:不能应用在 timestamp、binary、nchar、bool 类型字段。 + +**适用于**:表、超级表 + +**使用说明**: + +- 从 2.1.3.0 及以后版本可用;输出结果行数是范围内总行数减一,第一行没有结果输出。 +- DERIVATIVE 函数可以在由 GROUP BY 划分出单独时间线的情况下用于超级表(也即 GROUP BY tbname)。 + +**示例**: + +``` +taos> select derivative(current, 10m, 0) from t1; + ts | derivative(current, 10m, 0) | +======================================================== + 2021-08-20 10:11:22.790 | 0.500000000 | + 2021-08-20 11:11:22.791 | 0.166666620 | + 2021-08-20 12:11:22.791 | 0.000000000 | + 2021-08-20 13:11:22.792 | 0.166666620 | + 2021-08-20 14:11:22.792 | -0.666666667 | +Query OK, 5 row(s) in set (0.004883s) +``` + +### SPREAD + +``` +SELECT SPREAD(field_name) FROM { tb_name | stb_name } [WHERE clause]; +``` + +**功能说明**:统计表/超级表中某列的最大值和最小值之差。 + +**返回数据类型**:双精度浮点数。 + +**应用字段**:不能应用在 binary、nchar、bool 类型字段。 + +**适用于**:表、超级表。 + +**使用说明**:可用于 TIMESTAMP 字段,此时表示记录的时间覆盖范围。 + +**示例**: + +``` +taos> SELECT SPREAD(voltage) FROM meters; + spread(voltage) | +============================ + 5.000000000 | +Query OK, 1 row(s) in set (0.001792s) + +taos> SELECT SPREAD(voltage) FROM d1001; + spread(voltage) | +============================ + 3.000000000 | +Query OK, 1 row(s) in set (0.000836s) +``` + +### CEIL + +``` +SELECT CEIL(field_name) FROM { tb_name | stb_name } [WHERE clause]; +``` + +**功能说明**:获得指定列的向上取整数的结果。 + +**返回结果类型**:与指定列的原始数据类型一致。例如,如果指定列的原始数据类型为 Float,那么返回的数据类型也为 Float;如果指定列的原始数据类型为 Double,那么返回的数据类型也为 Double。 + +**适用数据类型**:不能应用在 timestamp、binary、nchar、bool 类型字段上;在超级表查询中使用时,不能应用在 tag 列,无论 tag 列的类型是什么类型。 + +**适用于**: 普通表、超级表。 + +**嵌套子查询支持**:适用于内层查询和外层查询。 + +**使用说明**: + +- 支持 +、-、\*、/ 运算,如 ceil(col1) + ceil(col2)。 +- 只能与普通列,选择(Selection)、投影(Projection)函数一起使用,不能与聚合(Aggregation)函数一起使用。 + +### FLOOR + +``` +SELECT FLOOR(field_name) FROM { tb_name | stb_name } [WHERE clause]; +``` + +**功能说明**:获得指定列的向下取整数的结果。 + 其他使用说明参见 CEIL 函数描述。 + +### ROUND + +``` +SELECT ROUND(field_name) FROM { tb_name | stb_name } [WHERE clause]; +``` + +**功能说明**:获得指定列的四舍五入的结果。 + 其他使用说明参见 CEIL 函数描述。 + +### CSUM + +```sql + SELECT CSUM(field_name) FROM { tb_name | stb_name } [WHERE clause] +``` + + **功能说明**:累加和(Cumulative sum),输出行与输入行数相同。 + + **返回结果类型**: 输入列如果是整数类型返回值为长整型 (int64_t),浮点数返回值为双精度浮点数(Double)。无符号整数类型返回值为无符号长整型(uint64_t)。 返回结果中同时带有每行记录对应的时间戳。 + + **适用数据类型**:不能应用在 timestamp、binary、nchar、bool 类型字段上;在超级表查询中使用时,不能应用在标签之上。 + + **嵌套子查询支持**: 适用于内层查询和外层查询。 + + **使用说明**: + + - 不支持 +、-、*、/ 运算,如 csum(col1) + csum(col2)。 + - 只能与聚合(Aggregation)函数一起使用。 该函数可以应用在普通表和超级表上。 + - 使用在超级表上的时候,需要搭配 Group by tbname使用,将结果强制规约到单个时间线。 + +**支持版本**: 从2.3.0.x开始支持 + +### MAVG + +```sql + SELECT MAVG(field_name, K) FROM { tb_name | stb_name } [WHERE clause] +``` + + **功能说明**: 计算连续 k 个值的移动平均数(moving average)。如果输入行数小于 k,则无结果输出。参数 k 的合法输入范围是 1≤ k ≤ 1000。 + + **返回结果类型**: 返回双精度浮点数类型。 + + **适用数据类型**: 不能应用在 timestamp、binary、nchar、bool 类型上;在超级表查询中使用时,不能应用在标签之上。 + + **嵌套子查询支持**: 适用于内层查询和外层查询。 + + **使用说明**: + + - 不支持 +、-、*、/ 运算,如 mavg(col1, k1) + mavg(col2, k1); + - 只能与普通列,选择(Selection)、投影(Projection)函数一起使用,不能与聚合(Aggregation)函数一起使用; + - 该函数可以应用在普通表和超级表上;使用在超级表上的时候,需要搭配 Group by tbname使用,将结果强制规约到单个时间线。 + +**支持版本**: 从2.3.0.x开始支持 + +### SAMPLE + +```sql + SELECT SAMPLE(field_name, K) FROM { tb_name | stb_name } [WHERE clause] +``` + + **功能说明**: 获取数据的 k 个采样值。参数 k 的合法输入范围是 1≤ k ≤ 1000。 + + **返回结果类型**: 同原始数据类型, 返回结果中带有该行记录的时间戳。 + + **适用数据类型**: 在超级表查询中使用时,不能应用在标签之上。 + + **嵌套子查询支持**: 适用于内层查询和外层查询。 + + **使用说明**: + + - 不能参与表达式计算;该函数可以应用在普通表和超级表上; + - 使用在超级表上的时候,需要搭配 Group by tbname 使用,将结果强制规约到单个时间线。 + +**支持版本**: 从2.3.0.x开始支持 + +### ASIN + +```sql + SELECT ASIN(field_name) FROM { tb_name | stb_name } [WHERE clause] +``` + +**功能说明**:获得指定列的反正弦结果 + +**返回结果类型**:DOUBLE。如果输入值为 NULL,输出值也为 NULL + +**适用数据类型**:不能应用在 timestamp、binary、nchar、bool 类型字段上;在超级表查询中使用时,不能应用在 tag 列 + +**嵌套子查询支持**:适用于内层查询和外层查询。 + +**使用说明**: + +- 只能与普通列,选择(Selection)、投影(Projection)函数一起使用,不能与聚合(Aggregation)函数一起使用。 +- 该函数可以应用在普通表和超级表上。 +- 版本2.6.0.x后支持 + +### ACOS + +```sql + SELECT ACOS(field_name) FROM { tb_name | stb_name } [WHERE clause] +``` + +**功能说明**:获得指定列的反余弦结果 + +**返回结果类型**:DOUBLE。如果输入值为 NULL,输出值也为 NULL + +**适用数据类型**:不能应用在 timestamp、binary、nchar、bool 类型字段上;在超级表查询中使用时,不能应用在 tag 列 + +**嵌套子查询支持**:适用于内层查询和外层查询。 + +**使用说明**: + +- 只能与普通列,选择(Selection)、投影(Projection)函数一起使用,不能与聚合(Aggregation)函数一起使用。 +- 该函数可以应用在普通表和超级表上。 +- 版本2.6.0.x后支持 + +### ATAN + +```sql + SELECT ATAN(field_name) FROM { tb_name | stb_name } [WHERE clause] +``` + +**功能说明**:获得指定列的反正切结果 + +**返回结果类型**:DOUBLE。如果输入值为 NULL,输出值也为 NULL + +**适用数据类型**:不能应用在 timestamp、binary、nchar、bool 类型字段上;在超级表查询中使用时,不能应用在 tag 列 + +**嵌套子查询支持**:适用于内层查询和外层查询。 + +**使用说明**: + +- 只能与普通列,选择(Selection)、投影(Projection)函数一起使用,不能与聚合(Aggregation)函数一起使用。 +- 该函数可以应用在普通表和超级表上。 +- 版本2.6.0.x后支持 + +### SIN + +```sql + SELECT SIN(field_name) FROM { tb_name | stb_name } [WHERE clause] +``` + +**功能说明**:获得指定列的正弦结果 + +**返回结果类型**:DOUBLE。如果输入值为 NULL,输出值也为 NULL + +**适用数据类型**:不能应用在 timestamp、binary、nchar、bool 类型字段上;在超级表查询中使用时,不能应用在 tag 列 + +**嵌套子查询支持**:适用于内层查询和外层查询。 + +**使用说明**: + +- 只能与普通列,选择(Selection)、投影(Projection)函数一起使用,不能与聚合(Aggregation)函数一起使用。 +- 该函数可以应用在普通表和超级表上。 +- 版本2.6.0.x后支持 + +### COS + +```sql + SELECT COS(field_name) FROM { tb_name | stb_name } [WHERE clause] +``` + +**功能说明**:获得指定列的余弦结果 + +**返回结果类型**:DOUBLE。如果输入值为 NULL,输出值也为 NULL + +**适用数据类型**:不能应用在 timestamp、binary、nchar、bool 类型字段上;在超级表查询中使用时,不能应用在 tag 列 + +**嵌套子查询支持**:适用于内层查询和外层查询。 + +**使用说明**: + +- 只能与普通列,选择(Selection)、投影(Projection)函数一起使用,不能与聚合(Aggregation)函数一起使用。 +- 该函数可以应用在普通表和超级表上。 +- 版本2.6.0.x后支持 + +### TAN + +```sql + SELECT TAN(field_name) FROM { tb_name | stb_name } [WHERE clause] +``` + +**功能说明**:获得指定列的正切结果 + +**返回结果类型**:DOUBLE。如果输入值为 NULL,输出值也为 NULL + +**适用数据类型**:不能应用在 timestamp、binary、nchar、bool 类型字段上;在超级表查询中使用时,不能应用在 tag 列 + +**嵌套子查询支持**:适用于内层查询和外层查询。 + +**使用说明**: + +- 只能与普通列,选择(Selection)、投影(Projection)函数一起使用,不能与聚合(Aggregation)函数一起使用。 +- 该函数可以应用在普通表和超级表上。 +- 版本2.6.0.x后支持 + +### POW + +```sql + SELECT POW(field_name, power) FROM { tb_name | stb_name } [WHERE clause] +``` + +**功能说明**:获得指定列的指数为 power 的幂 + +**返回结果类型**:DOUBLE。如果输入值为 NULL,输出值也为 NULL + +**适用数据类型**:不能应用在 timestamp、binary、nchar、bool 类型字段上;在超级表查询中使用时,不能应用在 tag 列 + +**嵌套子查询支持**:适用于内层查询和外层查询。 + +**使用说明**: + +- 只能与普通列,选择(Selection)、投影(Projection)函数一起使用,不能与聚合(Aggregation)函数一起使用。 +- 该函数可以应用在普通表和超级表上。 +- 版本2.6.0.x后支持 + +### LOG + +```sql + SELECT LOG(field_name, base) FROM { tb_name | stb_name } [WHERE clause] +``` + +**功能说明**:获得指定列对于底数 base 的对数 + +**返回结果类型**:DOUBLE。如果输入值为 NULL,输出值也为 NULL + +**适用数据类型**:不能应用在 timestamp、binary、nchar、bool 类型字段上;在超级表查询中使用时,不能应用在 tag 列 + +**嵌套子查询支持**:适用于内层查询和外层查询。 + +**使用说明**: + +- 只能与普通列,选择(Selection)、投影(Projection)函数一起使用,不能与聚合(Aggregation)函数一起使用。 +- 该函数可以应用在普通表和超级表上。 +- 版本2.6.0.x后支持 + +### ABS + +```sql + SELECT ABS(field_name) FROM { tb_name | stb_name } [WHERE clause] +``` + +**功能说明**:获得指定列的绝对值 + +**返回结果类型**:如果输入值为整数,输出值是 UBIGINT 类型。如果输入值是 FLOAT/DOUBLE 数据类型,输出值是 DOUBLE 数据类型。 + +**适用数据类型**:不能应用在 timestamp、binary、nchar、bool 类型字段上;在超级表查询中使用时,不能应用在 tag 列 + +**嵌套子查询支持**:适用于内层查询和外层查询。 + +**使用说明**: + +- 只能与普通列,选择(Selection)、投影(Projection)函数一起使用,不能与聚合(Aggregation)函数一起使用。 +- 该函数可以应用在普通表和超级表上。 +- 版本2.6.0.x后支持 + +### SQRT + +```sql + SELECT SQRT(field_name) FROM { tb_name | stb_name } [WHERE clause] +``` + +**功能说明**:获得指定列的平方根 + +**返回结果类型**:DOUBLE。如果输入值为 NULL,输出值也为 NULL + +**适用数据类型**:不能应用在 timestamp、binary、nchar、bool 类型字段上;在超级表查询中使用时,不能应用在 tag 列 + +**嵌套子查询支持**:适用于内层查询和外层查询。 + +**使用说明**: + +- 只能与普通列,选择(Selection)、投影(Projection)函数一起使用,不能与聚合(Aggregation)函数一起使用。 +- 该函数可以应用在普通表和超级表上。 +- 版本2.6.0.x后支持 + +### CAST + +```sql + SELECT CAST(expression AS type_name) FROM { tb_name | stb_name } [WHERE clause] +``` + +**功能说明**:数据类型转换函数,输入参数 expression 支持普通列、常量、标量函数及它们之间的四则运算,不支持 tag 列,只适用于 select 子句中。 + +**返回结果类型**:CAST 中指定的类型(type_name)。 + +**适用数据类型**: + +- 输入参数 expression 的类型可以是除 JSON 外目前所有类型字段(BOOL/TINYINT/SMALLINT/INT/BIGINT/FLOAT/DOUBLE/BINARY(M)/TIMESTAMP/NCHAR(M)/TINYINT UNSIGNED/SMALLINT UNSIGNED/INT UNSIGNED/BIGINT UNSIGNED); +- 输出目标类型只支持 BIGINT/BINARY(N)/TIMESTAMP/NCHAR(N)/BIGINT UNSIGNED。 + +**使用说明**: + +- 对于不能支持的类型转换会直接报错。 +- 如果输入值为NULL则输出值也为NULL。 +- 对于类型支持但某些值无法正确转换的情况对应的转换后的值以转换函数输出为准。目前可能遇到的几种情况: + 1)BINARY/NCHAR转BIGINT/BIGINT UNSIGNED时可能出现的无效字符情况,例如"a"可能转为0。 + 2)有符号数或TIMESTAMP转BIGINT UNSIGNED可能遇到的溢出问题。 + 3)BIGINT UNSIGNED转BIGINT可能遇到的溢出问题。 + 4)FLOAT/DOUBLE转BIGINT/BIGINT UNSIGNED可能遇到的溢出问题。 +- 版本2.6.0.x后支持 + +### CONCAT + +```sql + SELECT CONCAT(str1|column1, str2|column2, ...) FROM { tb_name | stb_name } [WHERE clause] +``` + +**功能说明**:字符串连接函数。 + +**返回结果类型**:同输入参数类型,BINARY 或者 NCHAR。 + +**适用数据类型**:输入参数或者全部是 BINARY 格式的字符串或者列,或者全部是 NCHAR 格式的字符串或者列。不能应用在 TAG 列。 + +**使用说明**: + +- 如果输入值为NULL,输出值为NULL。 +- 该函数最小参数个数为2个,最大参数个数为8个。 +- 该函数可以应用在普通表和超级表上。 +- 该函数适用于内层查询和外层查询。 +- 版本2.6.0.x后支持 + +### CONCAT_WS + +``` + SELECT CONCAT_WS(separator, str1|column1, str2|column2, ...) FROM { tb_name | stb_name } [WHERE clause] +``` + +**功能说明**:带分隔符的字符串连接函数。 + +**返回结果类型**:同输入参数类型,BINARY 或者 NCHAR。 + +**适用数据类型**:输入参数或者全部是 BINARY 格式的字符串或者列,或者全部是 NCHAR 格式的字符串或者列。不能应用在 TAG 列。 + +**使用说明**: + +- 如果separator值为NULL,输出值为NULL。如果separator值不为NULL,其他输入为NULL,输出为空串 +- 该函数最小参数个数为3个,最大参数个数为9个。 +- 该函数可以应用在普通表和超级表上。 +- 该函数适用于内层查询和外层查询。 +- 版本2.6.0.x后支持 + +### LENGTH + +``` + SELECT LENGTH(str|column) FROM { tb_name | stb_name } [WHERE clause] +``` + +**功能说明**:以字节计数的字符串长度。 + +**返回结果类型**:INT。 + +**适用数据类型**:输入参数是 BINARY 类型或者 NCHAR 类型的字符串或者列。不能应用在 TAG 列。 + +**使用说明** + +- 如果输入值为NULL,输出值为NULL。 +- 该函数可以应用在普通表和超级表上。 +- 函数适用于内层查询和外层查询。 +- 版本2.6.0.x后支持 + +### CHAR_LENGTH + +``` + SELECT CHAR_LENGTH(str|column) FROM { tb_name | stb_name } [WHERE clause] +``` + +**功能说明**:以字符计数的字符串长度。 + +**返回结果类型**:INT。 + +**适用数据类型**:输入参数是 BINARY 类型或者 NCHAR 类型的字符串或者列。不能应用在 TAG 列。 + +**使用说明** + +- 如果输入值为NULL,输出值为NULL。 +- 该函数可以应用在普通表和超级表上。 +- 该函数适用于内层查询和外层查询。 +- 版本2.6.0.x后支持 + +### LOWER + +``` + SELECT LOWER(str|column) FROM { tb_name | stb_name } [WHERE clause] +``` + +**功能说明**:将字符串参数值转换为全小写字母。 + +**返回结果类型**:同输入类型。 + +**适用数据类型**:输入参数是 BINARY 类型或者 NCHAR 类型的字符串或者列。不能应用在 TAG 列。 + +**使用说明**: + +- 如果输入值为NULL,输出值为NULL。 +- 该函数可以应用在普通表和超级表上。 +- 该函数适用于内层查询和外层查询。 +- 版本2.6.0.x后支持 + +### UPPER + +``` + SELECT UPPER(str|column) FROM { tb_name | stb_name } [WHERE clause] +``` + +**功能说明**:将字符串参数值转换为全大写字母。 + +**返回结果类型**:同输入类型。 + +**适用数据类型**:输入参数是 BINARY 类型或者 NCHAR 类型的字符串或者列。不能应用在 TAG 列。 + +**使用说明**: + +- 如果输入值为NULL,输出值为NULL。 +- 该函数可以应用在普通表和超级表上。 +- 该函数适用于内层查询和外层查询。 +- 版本2.6.0.x后支持 + +### LTRIM + +``` + SELECT LTRIM(str|column) FROM { tb_name | stb_name } [WHERE clause] +``` + +**功能说明**:返回清除左边空格后的字符串。 + +**返回结果类型**:同输入类型。 + +**适用数据类型**:输入参数是 BINARY 类型或者 NCHAR 类型的字符串或者列。不能应用在 TAG 列。 + +**使用说明**: + +- 如果输入值为NULL,输出值为NULL。 +- 该函数可以应用在普通表和超级表上。 +- 该函数适用于内层查询和外层查询。 +- 版本2.6.0.x后支持 + +### RTRIM + +``` + SELECT RTRIM(str|column) FROM { tb_name | stb_name } [WHERE clause] +``` + +**功能说明**:返回清除右边空格后的字符串。 + +**返回结果类型**:同输入类型。 + +**适用数据类型**:输入参数是 BINARY 类型或者 NCHAR 类型的字符串或者列。不能应用在 TAG 列。 + +**使用说明**: + +- 如果输入值为NULL,输出值为NULL。 +- 该函数可以应用在普通表和超级表上。 +- 该函数适用于内层查询和外层查询。 +- 版本2.6.0.x后支持 + +### SUBSTR + +``` + SELECT SUBSTR(str,pos[,len]) FROM { tb_name | stb_name } [WHERE clause] +``` + +**功能说明**:从源字符串 str 中的指定位置 pos 开始取一个长度为 len 的子串并返回。 + +**返回结果类型**:同输入类型。 + +**适用数据类型**:输入参数是 BINARY 类型或者 NCHAR 类型的字符串或者列。不能应用在 TAG 列。 + +**使用说明**: + +- 如果输入值为NULL,输出值为NULL。 +- 输入参数pos可以为正数,也可以为负数。如果pos是正数,表示开始位置从字符串开头正数计算。如果pos为负数,表示开始位置从字符串结尾倒数计算。如果输入参数len被忽略,返回的子串包含从pos开始的整个字串。 +- 该函数可以应用在普通表和超级表上。 +- 该函数适用于内层查询和外层查询。 +- 版本2.6.0.x后支持 + +### 四则运算 + +``` +SELECT field_name [+|-|*|/|%][Value|field_name] FROM { tb_name | stb_name } [WHERE clause]; +``` + +**功能说明**:统计表/超级表中某列或多列间的值加、减、乘、除、取余计算结果。 + +**返回数据类型**:双精度浮点数。 + +**应用字段**:不能应用在 timestamp、binary、nchar、bool 类型字段。 + +**适用于**:表、超级表。 + +**使用说明**: + +- 支持两列或多列之间进行计算,可使用括号控制计算优先级; +- NULL 字段不参与计算,如果参与计算的某行中包含 NULL,该行的计算结果为 NULL。 + +``` +taos> SELECT current + voltage * phase FROM d1001; +(current+(voltage*phase)) | +============================ + 78.190000713 | + 84.540003240 | + 80.810000718 | +Query OK, 3 row(s) in set (0.001046s) +``` + +### STATECOUNT + +``` +SELECT STATECOUNT(field_name, oper, val) FROM { tb_name | stb_name } [WHERE clause]; +``` + +**功能说明**:返回满足某个条件的连续记录的个数,结果作为新的一列追加在每行后面。条件根据参数计算,如果条件为 true 则加 1,条件为 false 则重置为-1,如果数据为 NULL,跳过该条数据。 + +**参数范围**: + +- oper : LT (小于)、GT(大于)、LE(小于等于)、GE(大于等于)、NE(不等于)、EQ(等于),不区分大小写。 +- val : 数值型 + +**返回结果类型**:整形。 + +**适用数据类型**:不能应用在 timestamp、binary、nchar、bool 类型字段上。 + +**嵌套子查询支持**:不支持应用在子查询上。 + +**支持的版本**:2.6 开始的版本。 + +**使用说明**: + +- 该函数可以应用在普通表上,在由 GROUP BY 划分出单独时间线的情况下用于超级表(也即 GROUP BY tbname) +- 不能和窗口操作一起使用,例如 interval/state_window/session_window。 + +**示例**: + +``` +taos> select ts,dbig from statef2; + ts | dbig | +======================================================== +2021-10-15 00:31:33.000000000 | 1 | +2021-10-17 00:31:31.000000000 | NULL | +2021-12-24 00:31:34.000000000 | 2 | +2022-01-01 08:00:05.000000000 | 19 | +2022-01-01 08:00:06.000000000 | NULL | +2022-01-01 08:00:07.000000000 | 9 | +Query OK, 6 row(s) in set (0.002977s) + +taos> select stateCount(dbig,GT,2) from statef2; +ts | dbig | statecount(dbig,gt,2) | +================================================================================ +2021-10-15 00:31:33.000000000 | 1 | -1 | +2021-10-17 00:31:31.000000000 | NULL | NULL | +2021-12-24 00:31:34.000000000 | 2 | -1 | +2022-01-01 08:00:05.000000000 | 19 | 1 | +2022-01-01 08:00:06.000000000 | NULL | NULL | +2022-01-01 08:00:07.000000000 | 9 | 2 | +Query OK, 6 row(s) in set (0.002791s) +``` + +### STATEDURATION + +```sql +SELECT stateDuration(field_name, oper, val, unit) FROM { tb_name | stb_name } [WHERE clause]; +``` + +**功能说明**:返回满足某个条件的连续记录的时间长度,结果作为新的一列追加在每行后面。条件根据参数计算,如果条件为 true 则加上两个记录之间的时间长度(第一个满足条件的记录时间长度记为 0),条件为 false 则重置为-1,如果数据为 NULL,跳过该条数据。 + +**参数范围**: + +- oper : LT (小于)、GT(大于)、LE(小于等于)、GE(大于等于)、NE(不等于)、EQ(等于),不区分大小写。 +- val : 数值型 +- unit : 时间长度的单位,范围[1s、1m、1h ],不足一个单位舍去。默认为 1s。 + +**返回结果类型**:整形。 + +**适用数据类型**:不能应用在 timestamp、binary、nchar、bool 类型字段上。 + +**嵌套子查询支持**:不支持应用在子查询上。 + +**支持的版本**:2.6 开始的版本。 + +**使用说明**: + +- 该函数可以应用在普通表上,在由 GROUP BY 划分出单独时间线的情况下用于超级表(也即 GROUP BY tbname) +- 不能和窗口操作一起使用,例如 interval/state_window/session_window。 + +**示例**: + +``` +taos> select ts,dbig from statef2; + ts | dbig | +======================================================== +2021-10-15 00:31:33.000000000 | 1 | +2021-10-17 00:31:31.000000000 | NULL | +2021-12-24 00:31:34.000000000 | 2 | +2022-01-01 08:00:05.000000000 | 19 | +2022-01-01 08:00:06.000000000 | NULL | +2022-01-01 08:00:07.000000000 | 9 | +Query OK, 6 row(s) in set (0.002407s) + +taos> select stateDuration(dbig,GT,2) from statef2; +ts | dbig | stateduration(dbig,gt,2) | +=================================================================================== +2021-10-15 00:31:33.000000000 | 1 | -1 | +2021-10-17 00:31:31.000000000 | NULL | NULL | +2021-12-24 00:31:34.000000000 | 2 | -1 | +2022-01-01 08:00:05.000000000 | 19 | 0 | +2022-01-01 08:00:06.000000000 | NULL | NULL | +2022-01-01 08:00:07.000000000 | 9 | 2 | +Query OK, 6 row(s) in set (0.002613s) +``` + +## 时间函数 + +从 2.6.0.0 版本开始,TDengine 查询引擎支持以下时间相关函数: + +### NOW + +```sql +SELECT NOW() FROM { tb_name | stb_name } [WHERE clause]; +SELECT select_expr FROM { tb_name | stb_name } WHERE ts_col cond_operatior NOW(); +INSERT INTO tb_name VALUES (NOW(), ...); +``` + +**功能说明**:返回客户端当前系统时间。 + +**返回结果数据类型**:TIMESTAMP 时间戳类型。 + +**应用字段**:在 WHERE 或 INSERT 语句中使用时只能作用于 TIMESTAMP 类型的字段。 + +**适用于**:表、超级表。 + +**使用说明**: + +- 支持时间加减操作,如 NOW() + 1s, 支持的时间单位如下: + b(纳秒)、u(微秒)、a(毫秒)、s(秒)、m(分)、h(小时)、d(天)、w(周)。 +- 返回的时间戳精度与当前 DATABASE 设置的时间精度一致。 + +**示例**: + +```sql +taos> SELECT NOW() FROM meters; + now() | +========================== + 2022-02-02 02:02:02.456 | +Query OK, 1 row(s) in set (0.002093s) + +taos> SELECT NOW() + 1h FROM meters; + now() + 1h | +========================== + 2022-02-02 03:02:02.456 | +Query OK, 1 row(s) in set (0.002093s) + +taos> SELECT COUNT(voltage) FROM d1001 WHERE ts < NOW(); + count(voltage) | +============================= + 5 | +Query OK, 5 row(s) in set (0.004475s) + +taos> INSERT INTO d1001 VALUES (NOW(), 10.2, 219, 0.32); +Query OK, 1 of 1 row(s) in database (0.002210s) +``` + +### TODAY + +```sql +SELECT TODAY() FROM { tb_name | stb_name } [WHERE clause]; +SELECT select_expr FROM { tb_name | stb_name } WHERE ts_col cond_operatior TODAY()]; +INSERT INTO tb_name VALUES (TODAY(), ...); +``` + +**功能说明**:返回客户端当日零时的系统时间。 + +**返回结果数据类型**:TIMESTAMP 时间戳类型。 + +**应用字段**:在 WHERE 或 INSERT 语句中使用时只能作用于 TIMESTAMP 类型的字段。 + +**适用于**:表、超级表。 + +**使用说明**: + +- 支持时间加减操作,如 TODAY() + 1s, 支持的时间单位如下: + b(纳秒),u(微秒),a(毫秒),s(秒),m(分),h(小时),d(天),w(周)。 +- 返回的时间戳精度与当前 DATABASE 设置的时间精度一致。 + +**示例**: + +```sql +taos> SELECT TODAY() FROM meters; + today() | +========================== + 2022-02-02 00:00:00.000 | +Query OK, 1 row(s) in set (0.002093s) + +taos> SELECT TODAY() + 1h FROM meters; + today() + 1h | +========================== + 2022-02-02 01:00:00.000 | +Query OK, 1 row(s) in set (0.002093s) + +taos> SELECT COUNT(voltage) FROM d1001 WHERE ts < TODAY(); + count(voltage) | +============================= + 5 | +Query OK, 5 row(s) in set (0.004475s) + +taos> INSERT INTO d1001 VALUES (TODAY(), 10.2, 219, 0.32); +Query OK, 1 of 1 row(s) in database (0.002210s) +``` + +### TIMEZONE + +```sql +SELECT TIMEZONE() FROM { tb_name | stb_name } [WHERE clause]; +``` + +**功能说明**:返回客户端当前时区信息。 + +**返回结果数据类型**:BINARY 类型。 + +**应用字段**:无 + +**适用于**:表、超级表。 + +**示例**: + +```sql +taos> SELECT TIMEZONE() FROM meters; + timezone() | +================================= + UTC (UTC, +0000) | +Query OK, 1 row(s) in set (0.002093s) +``` + +### TO_ISO8601 + +```sql +SELECT TO_ISO8601(ts_val | ts_col) FROM { tb_name | stb_name } [WHERE clause]; +``` + +**功能说明**:将 UNIX 时间戳转换成为 ISO8601 标准的日期时间格式,并附加客户端时区信息。 + +**返回结果数据类型**:BINARY 类型。 + +**应用字段**:UNIX 时间戳常量或是 TIMESTAMP 类型的列 + +**适用于**:表、超级表。 + +**使用说明**: + +- 如果输入是 UNIX 时间戳常量,返回格式精度由时间戳的位数决定; +- 如果输入是 TIMSTAMP 类型的列,返回格式的时间戳精度与当前 DATABASE 设置的时间精度一致。 + +**示例**: + +```sql +taos> SELECT TO_ISO8601(1643738400) FROM meters; + to_iso8601(1643738400) | +============================== + 2022-02-02T02:00:00+0800 | + +taos> SELECT TO_ISO8601(ts) FROM meters; + to_iso8601(ts) | +============================== + 2022-02-02T02:00:00+0800 | + 2022-02-02T02:00:00+0800 | + 2022-02-02T02:00:00+0800 | +``` + +### TO_UNIXTIMESTAMP + +```sql +SELECT TO_UNIXTIMESTAMP(datetime_string | ts_col) FROM { tb_name | stb_name } [WHERE clause]; +``` + +**功能说明**:将日期时间格式的字符串转换成为 UNIX 时间戳。 + +**返回结果数据类型**:长整型 INT64。 + +**应用字段**:字符串常量或是 BINARY/NCHAR 类型的列。 + +**适用于**:表、超级表。 + +**使用说明**: + +- 输入的日期时间字符串须符合 ISO8601/RFC3339 标准,无法转换的字符串格式将返回 0。 +- 返回的时间戳精度与当前 DATABASE 设置的时间精度一致。 + +**示例**: + +```sql +taos> SELECT TO_UNIXTIMESTAMP("2022-02-02T02:00:00.000Z") FROM meters; +to_unixtimestamp("2022-02-02T02:00:00.000Z") | +============================================== + 1643767200000 | + +taos> SELECT TO_UNIXTIMESTAMP(col_binary) FROM meters; + to_unixtimestamp(col_binary) | +======================================== + 1643767200000 | + 1643767200000 | + 1643767200000 | +``` + +### TIMETRUNCATE + +```sql +SELECT TIMETRUNCATE(ts_val | datetime_string | ts_col, time_unit) FROM { tb_name | stb_name } [WHERE clause]; +``` + +**功能说明**:将时间戳按照指定时间单位 time_unit 进行截断。 + +**返回结果数据类型**:TIMESTAMP 时间戳类型。 + +**应用字段**:UNIX 时间戳,日期时间格式的字符串,或者 TIMESTAMP 类型的列。 + +**适用于**:表、超级表。 + +**使用说明**: +- 支持的时间单位 time_unit 如下: + 1u(微秒),1a(毫秒),1s(秒),1m(分),1h(小时),1d(天)。 +- 返回的时间戳精度与当前 DATABASE 设置的时间精度一致。 + +**示例**: + +```sql +taos> SELECT TIMETRUNCATE(1643738522000, 1h) FROM meters; + timetruncate(1643738522000, 1h) | +=================================== + 2022-02-02 02:00:00.000 | +Query OK, 1 row(s) in set (0.001499s) + +taos> SELECT TIMETRUNCATE("2022-02-02 02:02:02", 1h) FROM meters; + timetruncate("2022-02-02 02:02:02", 1h) | +=========================================== + 2022-02-02 02:00:00.000 | +Query OK, 1 row(s) in set (0.003903s) + +taos> SELECT TIMETRUNCATE(ts, 1h) FROM meters; + timetruncate(ts, 1h) | +========================== + 2022-02-02 02:00:00.000 | + 2022-02-02 02:00:00.000 | + 2022-02-02 02:00:00.000 | +Query OK, 3 row(s) in set (0.003903s) +``` + +### TIMEDIFF + +```sql +SELECT TIMEDIFF(ts_val1 | datetime_string1 | ts_col1, ts_val2 | datetime_string2 | ts_col2 [, time_unit]) FROM { tb_name | stb_name } [WHERE clause]; +``` + +**功能说明**:计算两个时间戳之间的差值,并近似到时间单位 time_unit 指定的精度。 + +**返回结果数据类型**:长整型 INT64。 + +**应用字段**:UNIX 时间戳,日期时间格式的字符串,或者 TIMESTAMP 类型的列。 + +**适用于**:表、超级表。 + +**使用说明**: +- 支持的时间单位 time_unit 如下: + 1u(微秒),1a(毫秒),1s(秒),1m(分),1h(小时),1d(天)。 +- 如果时间单位 time_unit 未指定, 返回的时间差值精度与当前 DATABASE 设置的时间精度一致。 + +**示例**: + +```sql +taos> SELECT TIMEDIFF(1643738400000, 1643742000000) FROM meters; + timediff(1643738400000, 1643742000000) | +========================================= + 3600000 | +Query OK, 1 row(s) in set (0.002553s) +taos> SELECT TIMEDIFF(1643738400000, 1643742000000, 1h) FROM meters; + timediff(1643738400000, 1643742000000, 1h) | +============================================= + 1 | +Query OK, 1 row(s) in set (0.003726s) + +taos> SELECT TIMEDIFF("2022-02-02 03:00:00", "2022-02-02 02:00:00", 1h) FROM meters; + timediff("2022-02-02 03:00:00", "2022-02-02 02:00:00", 1h) | +============================================================= + 1 | +Query OK, 1 row(s) in set (0.001937s) + +taos> SELECT TIMEDIFF(ts_col1, ts_col2, 1h) FROM meters; + timediff(ts_col1, ts_col2, 1h) | +=================================== + 1 | +Query OK, 1 row(s) in set (0.001937s) +``` diff --git a/docs-cn/12-taos-sql/08-interval.md b/docs-cn/12-taos-sql/08-interval.md new file mode 100644 index 0000000000000000000000000000000000000000..d62e11b0dbd0ba49ceedb3807e05361f060969b3 --- /dev/null +++ b/docs-cn/12-taos-sql/08-interval.md @@ -0,0 +1,113 @@ +--- +sidebar_label: 按窗口切分聚合 +title: 按窗口切分聚合 +--- + + +TDengine 支持按时间段窗口切分方式进行聚合结果查询,比如温度传感器每秒采集一次数据,但需查询每隔 10 分钟的温度平均值。这种场景下可以使用窗口子句来获得需要的查询结果。 +窗口子句用于针对查询的数据集合进行按照窗口切分成为查询子集并进行聚合,窗口包含时间窗口(time window)、状态窗口(status window)、会话窗口(session window)三种窗口。其中时间窗口又可划分为滑动时间窗口和翻转时间窗口。 + +## 时间窗口 + +INTERVAL 子句用于产生相等时间周期的窗口,SLIDING 用以指定窗口向前滑动的时间。每次执行的查询是一个时间窗口,时间窗口随着时间流动向前滑动。在定义连续查询的时候需要指定时间窗口(time window )大小和每次前向增量时间(forward sliding times)。如图,[t0s, t0e] ,[t1s , t1e], [t2s, t2e] 是分别是执行三次连续查询的时间窗口范围,窗口的前向滑动的时间范围 sliding time 标识 。查询过滤、聚合等操作按照每个时间窗口为独立的单位执行。当 SLIDING 与 INTERVAL 相等的时候,滑动窗口即为翻转窗口。 + +![时间窗口示意图](/img/sql/timewindow-1.png) + +INTERVAL 和 SLIDING 子句需要配合聚合和选择函数来使用。以下 SQL 语句非法: + +``` +SELECT * FROM temp_tb_1 INTERVAL(1m); +``` + +SLIDING 的向前滑动的时间不能超过一个窗口的时间范围。以下语句非法: + +``` +SELECT COUNT(*) FROM temp_tb_1 INTERVAL(1m) SLIDING(2m); +``` + +当 SLIDING 与 INTERVAL 取值相等的时候,滑动窗口即为翻转窗口。 +_ 聚合时间段的窗口宽度由关键词 INTERVAL 指定,最短时间间隔 10 毫秒(10a);并且支持偏移 offset(偏移必须小于间隔),也即时间窗口划分与“UTC 时刻 0”相比的偏移量。SLIDING 语句用于指定聚合时间段的前向增量,也即每次窗口向前滑动的时长。 +_ 从 2.1.5.0 版本开始,INTERVAL 语句允许的最短时间间隔调整为 1 微秒(1u),当然如果所查询的 DATABASE 的时间精度设置为毫秒级,那么允许的最短时间间隔为 1 毫秒(1a)。 \* **注意**:用到 INTERVAL 语句时,除非极特殊的情况,都要求把客户端和服务端的 taos.cfg 配置文件中的 timezone 参数配置为相同的取值,以避免时间处理函数频繁进行跨时区转换而导致的严重性能影响。 + +## 状态窗口 + +使用整数(布尔值)或字符串来标识产生记录时候设备的状态量。产生的记录如果具有相同的状态量数值则归属于同一个状态窗口,数值改变后该窗口关闭。如下图所示,根据状态量确定的状态窗口分别是[2019-04-28 14:22:07,2019-04-28 14:22:10]和[2019-04-28 14:22:11,2019-04-28 14:22:12]两个。(状态窗口暂不支持对超级表使用) + +![时间窗口示意图](/img/sql/timewindow-3.png) + +使用 STATE_WINDOW 来确定状态窗口划分的列。例如: + +``` +SELECT COUNT(*), FIRST(ts), status FROM temp_tb_1 STATE_WINDOW(status); +``` + +## 会话窗口 + +会话窗口根据记录的时间戳主键的值来确定是否属于同一个会话。如下图所示,如果设置时间戳的连续的间隔小于等于 12 秒,则以下 6 条记录构成 2 个会话窗口,分别是:[2019-04-28 14:22:10,2019-04-28 14:22:30]和[2019-04-28 14:23:10,2019-04-28 14:23:30]。因为 2019-04-28 14:22:30 与 2019-04-28 14:23:10 之间的时间间隔是 40 秒,超过了连续时间间隔(12 秒)。 + +![时间窗口示意图](/img/sql/timewindow-2.png) + +在 tol_value 时间间隔范围内的结果都认为归属于同一个窗口,如果连续的两条记录的时间超过 tol_val,则自动开启下一个窗口。(会话窗口暂不支持对超级表使用) + +``` + +SELECT COUNT(*), FIRST(ts) FROM temp_tb_1 SESSION(ts, tol_val); +``` + +这种类型的查询语法如下: + +``` +SELECT function_list FROM tb_name + [WHERE where_condition] + [SESSION(ts_col, tol_val)] + [STATE_WINDOW(col)] + [INTERVAL(interval [, offset]) [SLIDING sliding]] + [FILL({NONE | VALUE | PREV | NULL | LINEAR | NEXT})] + +SELECT function_list FROM stb_name + [WHERE where_condition] + [INTERVAL(interval [, offset]) [SLIDING sliding]] + [FILL({NONE | VALUE | PREV | NULL | LINEAR | NEXT})] + [GROUP BY tags] +``` + +- 在聚合查询中,function_list 位置允许使用聚合和选择函数,并要求每个函数仅输出单个结果(例如:COUNT、AVG、SUM、STDDEV、LEASTSQUARES、PERCENTILE、MIN、MAX、FIRST、LAST),而不能使用具有多行输出结果的函数(例如:DIFF 以及四则运算)。 +- 此外 LAST_ROW 查询也不能与窗口聚合同时出现。 +- 标量函数(如:CEIL/FLOOR 等)也不能使用在窗口聚合查询中。 +- + +- WHERE 语句可以指定查询的起止时间和其他过滤条件。 +- FILL 语句指定某一窗口区间数据缺失的情况下的填充模式。填充模式包括以下几种: + 1. 不进行填充:NONE(默认填充模式)。 + 2. VALUE 填充:固定值填充,此时需要指定填充的数值。例如:FILL(VALUE, 1.23)。 + 3. PREV 填充:使用前一个非 NULL 值填充数据。例如:FILL(PREV)。 + 4. NULL 填充:使用 NULL 填充数据。例如:FILL(NULL)。 + 5. LINEAR 填充:根据前后距离最近的非 NULL 值做线性插值填充。例如:FILL(LINEAR)。 + 6. NEXT 填充:使用下一个非 NULL 值填充数据。例如:FILL(NEXT)。 + +:::info + +1. 使用 FILL 语句的时候可能生成大量的填充输出,务必指定查询的时间区间。针对每次查询,系统可返回不超过 1 千万条具有插值的结果。 +2. 在时间维度聚合中,返回的结果中时间序列严格单调递增。 +3. 如果查询对象是超级表,则聚合函数会作用于该超级表下满足值过滤条件的所有表的数据。如果查询中没有使用 GROUP BY 语句,则返回的结果按照时间序列严格单调递增;如果查询中使用了 GROUP BY 语句分组,则返回结果中每个 GROUP 内不按照时间序列严格单调递增。 + +::: + +时间聚合也常被用于连续查询场景,可以参考文档 [连续查询(Continuous Query)](/develop/continuous-query)。 + +## 示例 + +智能电表的建表语句如下: + +``` +CREATE TABLE meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT); +``` + +针对智能电表采集的数据,以 10 分钟为一个阶段,计算过去 24 小时的电流数据的平均值、最大值、电流的中位数。如果没有计算值,用前一个非 NULL 值填充。使用的查询语句如下: + +``` +SELECT AVG(current), MAX(current), APERCENTILE(current, 50) FROM meters + WHERE ts>=NOW-1d and ts<=now + INTERVAL(10m) + FILL(PREV); +``` diff --git a/docs-cn/12-taos-sql/09-limit.md b/docs-cn/12-taos-sql/09-limit.md new file mode 100644 index 0000000000000000000000000000000000000000..3c86a3862174377e6a00d046fb69627c773fe76e --- /dev/null +++ b/docs-cn/12-taos-sql/09-limit.md @@ -0,0 +1,54 @@ +--- +sidebar_label: 边界限制 +title: 边界限制 +--- + +## 一般限制 + +- 数据库名最大长度为 32。 +- 表名最大长度为 192,不包括数据库名前缀和分隔符 +- 每行数据最大长度 16k 个字符, 从 2.1.7.0 版本开始,每行数据最大长度 48k 个字符(注意:数据行内每个 BINARY/NCHAR 类型的列还会额外占用 2 个字节的存储位置)。 +- 列名最大长度为 64,最多允许 4096 列,最少需要 2 列,第一列必须是时间戳。注:从 2.1.7.0 版本(不含)以前最多允许 4096 列 +- 标签名最大长度为 64,最多允许 128 个,至少要有 1 个标签,一个表中标签值的总长度不超过 16k 个字符。 +- SQL 语句最大长度 1048576 个字符,也可通过客户端配置参数 maxSQLLength 修改,取值范围 65480 ~ 1048576。 +- SELECT 语句的查询结果,最多允许返回 4096 列(语句中的函数调用可能也会占用一些列空间),超限时需要显式指定较少的返回数据列,以避免语句执行报错。注: 2.1.7.0 版本(不含)之前为最多允许 1024 列 +- 库的数目,超级表的数目、表的数目,系统不做限制,仅受系统资源限制。 + +## GROUP BY 的限制 + +TAOS SQL 支持对标签、TBNAME 进行 GROUP BY 操作,也支持普通列进行 GROUP BY,前提是:仅限一列且该列的唯一值小于 10 万个。注意:group by 不支持 float,double 类型。 + +## IS NOT NULL 的限制 + +IS NOT NULL 与不为空的表达式适用范围。 + +IS NOT NULL 支持所有类型的列。不为空的表达式为 <\>"",仅对非数值类型的列适用。 + +## ORDER BY 的限制 + +- 非超级表只能有一个 order by. +- 超级表最多两个 order by, 并且第二个必须为 ts. +- order by tag,必须和 group by tag 一起,并且是同一个 tag。 tbname 和 tag 一样逻辑。 只适用于超级表 +- order by 普通列,必须和 group by 一起或者和 top/bottom 一起,并且是同一个普通列。 适用于超级表和普通表。如果同时存在 group by 和 top/bottom 一起,order by 优先必须和 group by 同一列。 +- order by ts. 适用于超级表和普通表。 +- order by ts 同时含有 group by 时 针对 group 内部用 ts 排序 + +## 表(列)名合法性说明 + +### TDengine 中的表(列)名命名规则如下: +只能由字母、数字、下划线构成,数字不能在首位,长度不能超过 192 字节,不区分大小写。这里表名称不包括数据库名的前缀和分隔符。 + +### 转义后表(列)名规则: +为了兼容支持更多形式的表(列)名,TDengine 引入新的转义符 "`",可以避免表名与关键词的冲突,同时不受限于上述表名合法性约束检查,转义符不计入表名的长度。 +转义后的表(列)名同样受到长度限制要求,且长度计算的时候不计算转义符。使用转义字符以后,不再对转义字符中的内容进行大小写统一。 + +例如: +\`aBc\` 和 \`abc\` 是不同的表(列)名,但是 abc 和 aBc 是相同的表(列)名。 + +:::note +转义字符中的内容必须是可打印字符。 + +::: + +### 支持版本 +支持转义符的功能从 2.3.0.1 版本开始。 \ No newline at end of file diff --git a/docs-cn/12-taos-sql/10-json.md b/docs-cn/12-taos-sql/10-json.md new file mode 100644 index 0000000000000000000000000000000000000000..4a4a8cca732ac433ba5ada1ec3805ebfa663edb3 --- /dev/null +++ b/docs-cn/12-taos-sql/10-json.md @@ -0,0 +1,91 @@ +--- +sidebar_label: JSON 类型使用说明 +title: JSON 类型使用说明 +--- + + +## 语法说明 + +1. 创建 json 类型 tag + + ``` + create stable s1 (ts timestamp, v1 int) tags (info json) + + create table s1_1 using s1 tags ('{"k1": "v1"}') + ``` + +2. json 取值操作符 -> + + ``` + select * from s1 where info->'k1' = 'v1' + + select info->'k1' from s1 + ``` + +3. json key 是否存在操作符 contains + + ``` + select * from s1 where info contains 'k2' + + select * from s1 where info contains 'k1' + ``` + +## 支持的操作 + +1. 在 where 条件中时,支持函数 match/nmatch/between and/like/and/or/is null/is no null,不支持 in + + ``` + select * from s1 where info->'k1' match 'v*'; + + select * from s1 where info->'k1' like 'v%' and info contains 'k2'; + + select * from s1 where info is null; + + select * from s1 where info->'k1' is not null + ``` + +2. 支持 json tag 放在 group by、order by、join 子句、union all 以及子查询中,比如 group by json->'key' + +3. 支持 distinct 操作. + + ``` + select distinct info->'k1' from s1 + ``` + +4. 标签操作 + + 支持修改 json 标签值(全量覆盖) + + 支持修改 json 标签名 + + 不支持添加 json 标签、删除 json 标签、修改 json 标签列宽 + +## 其他约束条件 + +1. 只有标签列可以使用 json 类型,如果用 json 标签,标签列只能有一个。 + +2. 长度限制:json 中 key 的长度不能超过 256,并且 key 必须为可打印 ascii 字符;json 字符串总长度不超过 4096 个字节。 + +3. json 格式限制: + + 1. json 输入字符串可以为空("","\t"," "或 null)或 object,不能为非空的字符串,布尔型和数组。 + 2. object 可为{},如果 object 为{},则整个 json 串记为空。key 可为"",若 key 为"",则 json 串中忽略该 k-v 对。 + 3. value 可以为数字(int/double)或字符串或 bool 或 null,暂不可以为数组。不允许嵌套。 + 4. 若 json 字符串中出现两个相同的 key,则第一个生效。 + 5. json 字符串里暂不支持转义。 + +4. 当查询 json 中不存在的 key 时,返回 NULL + +5. 当 json tag 作为子查询结果时,不再支持上层查询继续对子查询中的 json 串做解析查询。 + + 比如暂不支持 + + ``` + select jtag->'key' from (select jtag from stable) + ``` + + 不支持 + + ``` + select jtag->'key' from (select jtag from stable) where jtag->'key'>0 + ``` diff --git a/docs-cn/12-taos-sql/11-escape.md b/docs-cn/12-taos-sql/11-escape.md new file mode 100644 index 0000000000000000000000000000000000000000..756e5c81591e7414827fdc65e228cfafc96214ad --- /dev/null +++ b/docs-cn/12-taos-sql/11-escape.md @@ -0,0 +1,30 @@ +--- +title: 转义字符说明 +--- + +## 转义字符表 + +| 字符序列 | **代表的字符** | +| :------: | -------------- | +| `\'` | 单引号' | +| `\"` | 双引号" | +| \n | 换行符 | +| \r | 回车符 | +| \t | tab 符 | +| `\\` | 斜杠\ | +| `\%` | % 规则见下 | +| `\_` | \_ 规则见下 | + +:::note +转义符的功能从 2.4.0.4 版本开始 + +::: + +## 转义字符使用规则 + +1. 标识符里有转义字符(数据库名、表名、列名) + 1. 普通标识符: 直接提示错误的标识符,因为标识符规定必须是数字、字母和下划线,并且不能以数字开头。 + 2. 反引号``标识符: 保持原样,不转义 +2. 数据里有转义字符 + 1. 遇到上面定义的转义字符会转义(%和\_见下面说明),如果没有匹配的转义字符会忽略掉转义符\。 + 2. 对于%和\_,因为在 like 里这两个字符是通配符,所以在模式匹配 like 里用`\%`%和`\_`表示字符里本身的%和\_,如果在 like 模式匹配上下文之外使用`\%`或`\_`,则它们的计算结果为字符串`\%`和`\_`,而不是%和\_。 diff --git a/docs-cn/12-taos-sql/12-keywords/_category_.yml b/docs-cn/12-taos-sql/12-keywords/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..67738650a4564477f017542aea81767b3de72922 --- /dev/null +++ b/docs-cn/12-taos-sql/12-keywords/_category_.yml @@ -0,0 +1 @@ +label: 参数限制与保留关键字 \ No newline at end of file diff --git a/docs-cn/12-taos-sql/12-keywords/index.md b/docs-cn/12-taos-sql/12-keywords/index.md new file mode 100644 index 0000000000000000000000000000000000000000..608d4e080967cfd97072706cf0963ae669960be6 --- /dev/null +++ b/docs-cn/12-taos-sql/12-keywords/index.md @@ -0,0 +1,87 @@ +--- +sidebar_label: 参数限制与保留关键字 +title: TDengine 参数限制与保留关键字 +--- + +## 名称命名规则 + +1. 合法字符:英文字符、数字和下划线 +2. 允许英文字符或下划线开头,不允许以数字开头 +3. 不区分大小写 +4. 转义后表(列)名规则: + 为了兼容支持更多形式的表(列)名,TDengine 引入新的转义符 "`"。可用让表名与关键词不冲突,同时不受限于上述表名称合法性约束检查。 + 转义后的表(列)名同样受到长度限制要求,且长度计算的时候不计算转义符。使用转义字符以后,不再对转义字符中的内容进行大小写统一。 + + 例如:\`aBc\` 和 \`abc\` 是不同的表(列)名,但是 abc 和 aBc 是相同的表(列)名。 + 需要注意的是转义字符中的内容必须是可打印字符。 + 支持转义符的功能从 2.3.0.1 版本开始。 + +## 密码合法字符集 + +`[a-zA-Z0-9!?$%^&*()_–+={[}]:;@~#|<,>.?/]` + +去掉了 `` ‘“`\ `` (单双引号、撇号、反斜杠、空格) + +- 数据库名:不能包含“.”以及特殊字符,不能超过 32 个字符 +- 表名:不能包含“.”以及特殊字符,与所属数据库名一起,不能超过 192 个字符,每行数据最大长度 16k 个字符 +- 表的列名:不能包含特殊字符,不能超过 64 个字符 +- 数据库名、表名、列名,都不能以数字开头,合法的可用字符集是“英文字符、数字和下划线” +- 表的列数:不能超过 1024 列,最少需要 2 列,第一列必须是时间戳(从 2.1.7.0 版本开始,改为最多支持 4096 列) +- 记录的最大长度:包括时间戳 8 byte,不能超过 16KB(每个 BINARY/NCHAR 类型的列还会额外占用 2 个 byte 的存储位置) +- 单条 SQL 语句默认最大字符串长度:1048576 byte,但可通过系统配置参数 maxSQLLength 修改,取值范围 65480 ~ 1048576 byte +- 数据库副本数:不能超过 3 +- 用户名:不能超过 23 个 byte +- 用户密码:不能超过 15 个 byte +- 标签(Tags)数量:不能超过 128 个,可以 0 个 +- 标签的总长度:不能超过 16K byte +- 记录条数:仅受存储空间限制 +- 表的个数:仅受节点个数限制 +- 库的个数:仅受节点个数限制 +- 单个库上虚拟节点个数:不能超过 64 个 +- 库的数目,超级表的数目、表的数目,系统不做限制,仅受系统资源限制 +- SELECT 语句的查询结果,最多允许返回 1024 列(语句中的函数调用可能也会占用一些列空间),超限时需要显式指定较少的返回数据列,以避免语句执行报错。(从 2.1.7.0 版本开始,改为最多允许 4096 列) + +## 保留关键字 + +目前 TDengine 有将近 200 个内部保留关键字,这些关键字无论大小写均不可以用作库名、表名、STable 名、数据列名及标签列名等。这些关键字列表如下: + +| 关键字列表 | | | | | +| ----------- | ---------- | --------- | ---------- | ------------ | +| ABORT | CREATE | IGNORE | NULL | STAR | +| ACCOUNT | CTIME | IMMEDIATE | OF | STATE | +| ACCOUNTS | DATABASE | IMPORT | OFFSET | STATEMENT | +| ADD | DATABASES | IN | OR | STATE_WINDOW | +| AFTER | DAYS | INITIALLY | ORDER | STORAGE | +| ALL | DBS | INSERT | PARTITIONS | STREAM | +| ALTER | DEFERRED | INSTEAD | PASS | STREAMS | +| AND | DELIMITERS | INT | PLUS | STRING | +| AS | DESC | INTEGER | PPS | SYNCDB | +| ASC | DESCRIBE | INTERVAL | PRECISION | TABLE | +| ATTACH | DETACH | INTO | PREV | TABLES | +| BEFORE | DISTINCT | IS | PRIVILEGE | TAG | +| BEGIN | DIVIDE | ISNULL | QTIME | TAGS | +| BETWEEN | DNODE | JOIN | QUERIES | TBNAME | +| BIGINT | DNODES | KEEP | QUERY | TIMES | +| BINARY | DOT | KEY | QUORUM | TIMESTAMP | +| BITAND | DOUBLE | KILL | RAISE | TINYINT | +| BITNOT | DROP | LE | REM | TOPIC | +| BITOR | EACH | LIKE | REPLACE | TOPICS | +| BLOCKS | END | LIMIT | REPLICA | TRIGGER | +| BOOL | EQ | LINEAR | RESET | TSERIES | +| BY | EXISTS | LOCAL | RESTRICT | UMINUS | +| CACHE | EXPLAIN | LP | ROW | UNION | +| CACHELAST | FAIL | LSHIFT | RP | UNSIGNED | +| CASCADE | FILE | LT | RSHIFT | UPDATE | +| CHANGE | FILL | MATCH | SCORES | UPLUS | +| CLUSTER | FLOAT | MAXROWS | SELECT | USE | +| COLON | FOR | MINROWS | SEMI | USER | +| COLUMN | FROM | MINUS | SESSION | USERS | +| COMMA | FSYNC | MNODES | SET | USING | +| COMP | GE | MODIFY | SHOW | VALUES | +| COMPACT | GLOB | MODULES | SLASH | VARIABLE | +| CONCAT | GRANTS | NCHAR | SLIDING | VARIABLES | +| CONFLICT | GROUP | NE | SLIMIT | VGROUPS | +| CONNECTION | GT | NONE | SMALLINT | VIEW | +| CONNECTIONS | HAVING | NOT | SOFFSET | VNODES | +| CONNS | ID | NOTNULL | STABLE | WAL | +| COPY | IF | NOW | STABLES | WHERE | diff --git a/docs-cn/12-taos-sql/_category_.yml b/docs-cn/12-taos-sql/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..62290997ece68ce1a61d391c3976e338033c0dd1 --- /dev/null +++ b/docs-cn/12-taos-sql/_category_.yml @@ -0,0 +1 @@ +label: SQL 手册 diff --git a/docs-cn/12-taos-sql/index.md b/docs-cn/12-taos-sql/index.md new file mode 100644 index 0000000000000000000000000000000000000000..269bc1d2b5ddfa25c42652d8f639bfe2fb1d42e5 --- /dev/null +++ b/docs-cn/12-taos-sql/index.md @@ -0,0 +1,40 @@ +--- +title: TAOS SQL +description: "TAOS SQL 支持的语法规则、主要查询功能、支持的 SQL 查询函数,以及常用技巧等内容" +--- + +本文档说明 TAOS SQL 支持的语法规则、主要查询功能、支持的 SQL 查询函数,以及常用技巧等内容。阅读本文档需要读者具有基本的 SQL 语言的基础。 + +TAOS SQL 是用户对 TDengine 进行数据写入和查询的主要工具。TAOS SQL 为了便于用户快速上手,在一定程度上提供与标准 SQL 类似的风格和模式。严格意义上,TAOS SQL 并不是也不试图提供标准的 SQL 语法。此外,由于 TDengine 针对的时序性结构化数据不提供删除功能,因此在 TAO SQL 中不提供数据删除的相关功能。 + +TAOS SQL 不支持关键字的缩写,例如 DESCRIBE 不能缩写为 DESC。 + +本章节 SQL 语法遵循如下约定: + +- <\> 里的内容是用户需要输入的,但不要输入 <\> 本身 +- \[ \] 表示内容为可选项,但不能输入 [] 本身 +- | 表示多选一,选择其中一个即可,但不能输入 | 本身 +- … 表示前面的项可重复多个 + +为更好地说明 SQL 语法的规则及其特点,本文假设存在一个数据集。以智能电表(meters)为例,假设每个智能电表采集电流、电压、相位三个量。其建模如下: + +``` +taos> DESCRIBE meters; + Field | Type | Length | Note | +================================================================================= + ts | TIMESTAMP | 8 | | + current | FLOAT | 4 | | + voltage | INT | 4 | | + phase | FLOAT | 4 | | + location | BINARY | 64 | TAG | + groupid | INT | 4 | TAG | +``` + +数据集包含 4 个智能电表的数据,按照 TDengine 的建模规则,对应 4 个子表,其名称分别是 d1001, d1002, d1003, d1004。 + +```mdx-code-block +import DocCardList from '@theme/DocCardList'; +import {useCurrentSidebarCategory} from '@docusaurus/theme-common'; + + +``` \ No newline at end of file diff --git a/docs-cn/13-operation/01-pkg-install.md b/docs-cn/13-operation/01-pkg-install.md new file mode 100644 index 0000000000000000000000000000000000000000..92b04a42ec9b3a80187d4482f465190288038f8d --- /dev/null +++ b/docs-cn/13-operation/01-pkg-install.md @@ -0,0 +1,283 @@ +--- +title: 安装和卸载 +description: 安装、卸载、启动、停止和升级 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +TDengine 开源版本提供 deb 和 rpm 格式安装包,用户可以根据自己的运行环境选择合适的安装包。其中 deb 支持 Debian/Ubuntu 及衍生系统,rpm 支持 CentOS/RHEL/SUSE 及衍生系统。同时我们也为企业用户提供 tar.gz 格式安装包。 + +## 安装 + + + + +1、从官网下载获得 deb 安装包,例如 TDengine-server-2.4.0.7-Linux-x64.deb; +2、进入到 TDengine-server-2.4.0.7-Linux-x64.deb 安装包所在目录,执行如下的安装命令: + +``` +$ sudo dpkg -i TDengine-server-2.4.0.7-Linux-x64.deb +(Reading database ... 137504 files and directories currently installed.) +Preparing to unpack TDengine-server-2.4.0.7-Linux-x64.deb ... +TDengine is removed successfully! +Unpacking tdengine (2.4.0.7) over (2.4.0.7) ... +Setting up tdengine (2.4.0.7) ... +Start to install TDengine... + +System hostname is: ubuntu-1804 + +Enter FQDN:port (like h1.taosdata.com:6030) of an existing TDengine cluster node to join +OR leave it blank to build one: + +Enter your email address for priority support or enter empty to skip: +Created symlink /etc/systemd/system/multi-user.target.wants/taosd.service → /etc/systemd/system/taosd.service. + +To configure TDengine : edit /etc/taos/taos.cfg +To start TDengine : sudo systemctl start taosd +To access TDengine : taos -h ubuntu-1804 to login into TDengine server + + +TDengine is installed successfully! +``` + + + + + +1、从官网下载获得 rpm 安装包,例如 TDengine-server-2.4.0.7-Linux-x64.rpm; +2、进入到 TDengine-server-2.4.0.7-Linux-x64.rpm 安装包所在目录,执行如下的安装命令: + +``` +$ sudo rpm -ivh TDengine-server-2.4.0.7-Linux-x64.rpm +Preparing... ################################# [100%] +Updating / installing... + 1:tdengine-2.4.0.7-3 ################################# [100%] +Start to install TDengine... + +System hostname is: centos7 + +Enter FQDN:port (like h1.taosdata.com:6030) of an existing TDengine cluster node to join +OR leave it blank to build one: + +Enter your email address for priority support or enter empty to skip: + +Created symlink from /etc/systemd/system/multi-user.target.wants/taosd.service to /etc/systemd/system/taosd.service. + +To configure TDengine : edit /etc/taos/taos.cfg +To start TDengine : sudo systemctl start taosd +To access TDengine : taos -h centos7 to login into TDengine server + + +TDengine is installed successfully! +``` + + + + + +1、从官网下载获得 tar.gz 安装包,例如 TDengine-server-2.4.0.7-Linux-x64.tar.gz; +2、进入到 TDengine-server-2.4.0.7-Linux-x64.tar.gz 安装包所在目录,先解压文件后,进入子目录,执行其中的 install.sh 安装脚本: + +``` +$ tar xvzf TDengine-enterprise-server-2.4.0.7-Linux-x64.tar.gz +TDengine-enterprise-server-2.4.0.7/ +TDengine-enterprise-server-2.4.0.7/driver/ +TDengine-enterprise-server-2.4.0.7/driver/vercomp.txt +TDengine-enterprise-server-2.4.0.7/driver/libtaos.so.2.4.0.7 +TDengine-enterprise-server-2.4.0.7/install.sh +TDengine-enterprise-server-2.4.0.7/examples/ +... + +$ ll +total 43816 +drwxrwxr-x 3 ubuntu ubuntu 4096 Feb 22 09:31 ./ +drwxr-xr-x 20 ubuntu ubuntu 4096 Feb 22 09:30 ../ +drwxrwxr-x 4 ubuntu ubuntu 4096 Feb 22 09:30 TDengine-enterprise-server-2.4.0.7/ +-rw-rw-r-- 1 ubuntu ubuntu 44852544 Feb 22 09:31 TDengine-enterprise-server-2.4.0.7-Linux-x64.tar.gz + +$ cd TDengine-enterprise-server-2.4.0.7/ + + $ ll +total 40784 +drwxrwxr-x 4 ubuntu ubuntu 4096 Feb 22 09:30 ./ +drwxrwxr-x 3 ubuntu ubuntu 4096 Feb 22 09:31 ../ +drwxrwxr-x 2 ubuntu ubuntu 4096 Feb 22 09:30 driver/ +drwxrwxr-x 10 ubuntu ubuntu 4096 Feb 22 09:30 examples/ +-rwxrwxr-x 1 ubuntu ubuntu 33294 Feb 22 09:30 install.sh* +-rw-rw-r-- 1 ubuntu ubuntu 41704288 Feb 22 09:30 taos.tar.gz + +$ sudo ./install.sh + +Start to update TDengine... +Created symlink /etc/systemd/system/multi-user.target.wants/taosd.service → /etc/systemd/system/taosd.service. +Nginx for TDengine is updated successfully! + +To configure TDengine : edit /etc/taos/taos.cfg +To configure Taos Adapter (if has) : edit /etc/taos/taosadapter.toml +To start TDengine : sudo systemctl start taosd +To access TDengine : use taos -h ubuntu-1804 in shell OR from http://127.0.0.1:6060 + +TDengine is updated successfully! +Install taoskeeper as a standalone service +taoskeeper is installed, enable it by `systemctl enable taoskeeper` +``` + +:::info +install.sh 安装脚本在执行过程中,会通过命令行交互界面询问一些配置信息。如果希望采取无交互安装方式,那么可以用 -e no 参数来执行 install.sh 脚本。运行 `./install.sh -h` 指令可以查看所有参数的详细说明信息。 + +::: + + + + +:::note +当安装第一个节点时,出现 Enter FQDN:提示的时候,不需要输入任何内容。只有当安装第二个或以后更多的节点时,才需要输入已有集群中任何一个可用节点的 FQDN,支持该新节点加入集群。当然也可以不输入,而是在新节点启动前,配置到新节点的配置文件中。 + +::: + +## 卸载 + + + + +卸载命令如下: + +``` +$ sudo dpkg -r tdengine +(Reading database ... 137504 files and directories currently installed.) +Removing tdengine (2.4.0.7) ... +TDengine is removed successfully! + +``` + + + + + +卸载命令如下: + +``` +$ sudo rpm -e tdengine +TDengine is removed successfully! +``` + + + + + +卸载命令如下: + +``` +$ rmtaos +Nginx for TDengine is running, stopping it... +TDengine is removed successfully! + +taosKeeper is removed successfully! +``` + + + + +:::info +- TDengine 提供了多种安装包,但最好不要在一个系统上同时使用 tar.gz 安装包和 deb 或 rpm 安装包。否则会相互影响,导致在使用时出现问题。 + +- 对于 deb 包安装后,如果安装目录被手工误删了部分,出现卸载、或重新安装不能成功。此时,需要清除 TDengine 包的安装信息,执行如下命令: + + ``` + $ sudo rm -f /var/lib/dpkg/info/tdengine* + ``` + +然后再重新进行安装就可以了。 + +- 对于 rpm 包安装后,如果安装目录被手工误删了部分,出现卸载、或重新安装不能成功。此时,需要清除 TDengine 包的安装信息,执行如下命令: + + ``` + $ sudo rpm -e --noscripts tdengine + ``` + +然后再重新进行安装就可以了。 + +::: + +## 安装目录说明 + +TDengine 成功安装后,主安装目录是 /usr/local/taos,目录内容如下: + +``` +$ cd /usr/local/taos +$ ll +$ ll +total 28 +drwxr-xr-x 7 root root 4096 Feb 22 09:34 ./ +drwxr-xr-x 12 root root 4096 Feb 22 09:34 ../ +drwxr-xr-x 2 root root 4096 Feb 22 09:34 bin/ +drwxr-xr-x 2 root root 4096 Feb 22 09:34 cfg/ +lrwxrwxrwx 1 root root 13 Feb 22 09:34 data -> /var/lib/taos/ +drwxr-xr-x 2 root root 4096 Feb 22 09:34 driver/ +drwxr-xr-x 10 root root 4096 Feb 22 09:34 examples/ +drwxr-xr-x 2 root root 4096 Feb 22 09:34 include/ +lrwxrwxrwx 1 root root 13 Feb 22 09:34 log -> /var/log/taos/ +``` + +- 自动生成配置文件目录、数据库目录、日志目录。 +- 配置文件缺省目录:/etc/taos/taos.cfg, 软链接到 /usr/local/taos/cfg/taos.cfg; +- 数据库缺省目录:/var/lib/taos, 软链接到 /usr/local/taos/data; +- 日志缺省目录:/var/log/taos, 软链接到 /usr/local/taos/log; +- /usr/local/taos/bin 目录下的可执行文件,会软链接到 /usr/bin 目录下; +- /usr/local/taos/driver 目录下的动态库文件,会软链接到 /usr/lib 目录下; +- /usr/local/taos/include 目录下的头文件,会软链接到到 /usr/include 目录下; + +## 卸载和更新文件说明 + +卸载安装包的时候,将保留配置文件、数据库文件和日志文件,即 /etc/taos/taos.cfg 、 /var/lib/taos 、 /var/log/taos 。如果用户确认后不需保留,可以手工删除,但一定要慎重,因为删除后,数据将永久丢失,不可以恢复! + +如果是更新安装,当缺省配置文件( /etc/taos/taos.cfg )存在时,仍然使用已有的配置文件,安装包中携带的配置文件修改为 taos.cfg.orig 保存在 /usr/local/taos/cfg/ 目录,可以作为设置配置参数的参考样例;如果不存在配置文件,就使用安装包中自带的配置文件。 + +## 启动和停止 + +TDengine 使用 Linux 系统的 systemd/systemctl/service 来管理系统的启动和、停止、重启操作。TDengine 的服务进程是 taosd,默认情况下 TDengine 在系统启动后将自动启动。DBA 可以通过 systemd/systemctl/service 手动操作停止、启动、重新启动服务。 + +以 systemctl 为例,命令如下: + +- 启动服务进程:`systemctl start taosd` + +- 停止服务进程:`systemctl stop taosd` + +- 重启服务进程:`systemctl restart taosd` + +- 查看服务状态:`systemctl status taosd` + +注意:TDengine 在 2.4 版本之后包含一个独立组件 taosAdapter 需要使用 systemctl 命令管理 taosAdapter 服务的启动和停止。 + +如果服务进程处于活动状态,则 status 指令会显示如下的相关信息: + + ``` + Active: active (running) + ``` + +如果后台服务进程处于停止状态,则 status 指令会显示如下的相关信息: + + ``` + Active: inactive (dead) + ``` + +## 升级 +升级分为两个层面:升级安装包 和 升级运行中的实例。 + +升级安装包请遵循前述安装和卸载的步骤先卸载旧版本再安装新版本。 + +升级运行中的实例则要复杂得多,首先请注意版本号,TDengine 的版本号目前分为四段,如 2.4.0.14 和 2.4.0.16,只有前三段版本号一致(即只有第四段版本号不同)才能把一个运行中的实例进行升级。升级步骤如下: +- 停止数据写入 +- 确保所有数据落盘,即写入时序数据库 +- 停止 TDengine 集群 +- 卸载旧版本并安装新版本 +- 重新启动 TDengine 集群 +- 进行简单的查询操作确认旧数据没有丢失 +- 进行简单的写入操作确认 TDengine 集群可用 +- 重新恢复业务数据的写入 + +:::warning +TDengine 不保证低版本能够兼容高版本的数据,所以任何时候都不推荐降级 + +::: \ No newline at end of file diff --git a/docs-cn/13-operation/02-planning.mdx b/docs-cn/13-operation/02-planning.mdx new file mode 100644 index 0000000000000000000000000000000000000000..954ba7ca00ebdcb10cfcad515292d96127106ff3 --- /dev/null +++ b/docs-cn/13-operation/02-planning.mdx @@ -0,0 +1,81 @@ +--- +sidebar_label: 容量规划 +title: 容量规划 +--- + +使用 TDengine 来搭建一个物联网大数据平台,计算资源、存储资源需要根据业务场景进行规划。下面分别讨论系统运行所需要的内存、CPU 以及硬盘空间。 + +## 内存需求 + +每个 Database 可以创建固定数目的 vgroup,默认与 CPU 核数相同,可通过 maxVgroupsPerDb 配置;vgroup 中的每个副本会是一个 vnode;每个 vnode 会占用固定大小的内存(大小与数据库的配置参数 blocks 和 cache 有关);每个 Table 会占用与标签总长度有关的内存;此外,系统会有一些固定的内存开销。因此,每个 DB 需要的系统内存可通过如下公式计算: + +``` +Database Memory Size = maxVgroupsPerDb * replica * (blocks * cache + 10MB) + numOfTables * (tagSizePerTable + 0.5KB) +``` + +示例:假设 maxVgroupPerDB 是缺省值 64,cache 是缺省大小 16M, blocks 是缺省值 6,并且一个 DB 中有 10 万张表,单副本,标签总长度是 256 字节,则这个 DB 总的内存需求为:64 \* 1 \* (16 \* 6 + 10) + 100000 \* (0.25 + 0.5) / 1000 = 6792M。 + +在实际的系统运维中,我们通常会更关心 TDengine 服务进程(taosd)会占用的内存量。 + +``` +taosd 内存总量 = vnode 内存 + mnode 内存 + 查询内存 +``` + +其中: + +1. “vnode 内存”指的是集群中所有的 Database 存储分摊到当前 taosd 节点上所占用的内存资源。可以按上文“Database Memory Size”计算公式估算每个 DB 的内存占用量进行加总,再按集群中总共的 TDengine 节点数做平均(如果设置为多副本,则还需要乘以对应的副本倍数)。 +2. “mnode 内存”指的是集群中管理节点所占用的资源。如果一个 taosd 节点上分布有 mnode 管理节点,则内存消耗还需要增加“0.2KB \* 集群中数据表总数”。 +3. “查询内存”指的是服务端处理查询请求时所需要占用的内存。单条查询语句至少会占用“0.2KB \* 查询涉及的数据表总数”的内存量。 + +注意:以上内存估算方法,主要讲解了系统的“必须内存需求”,而不是“内存总数上限”。在实际运行的生产环境中,由于操作系统缓存、资源管理调度等方面的原因,内存规划应当在估算结果的基础上保留一定冗余,以维持系统状态和系统性能的稳定性。并且,生产环境通常会配置系统资源的监控工具,以便及时发现硬件资源的紧缺情况。 + +最后,如果内存充裕,可以考虑加大 Blocks 的配置,这样更多数据将保存在内存里,提高写入和查询速度。 + +### 客户端内存需求 + +客户端应用采用 taosc 客户端驱动连接服务端,会有内存需求的开销。 + +客户端的内存开销主要由写入过程中的 SQL 语句、表的元数据信息缓存、以及结构性开销构成。系统最大容纳的表数量为 N(每个通过超级表创建的表的 meta data 开销约 256 字节),最大并行写入线程数量 T,最大 SQL 语句长度 S(通常是 1 Mbytes)。由此可以进行客户端内存开销的估算(单位 MBytes): + +``` +M = (T * S * 3 + (N / 4096) + 100) +``` + +举例如下:用户最大并发写入线程数 100,子表数总数 10,000,000,那么客户端的内存最低要求是: + +``` +100 * 3 + (10000000 / 4096) + 100 = 2741 (MBytes) +``` + +即配置 3 GBytes 内存是最低要求。 + +## CPU 需求 + +CPU 的需求取决于如下两方面: + +- **数据插入** TDengine 单核每秒能至少处理一万个插入请求。每个插入请求可以带多条记录,一次插入一条记录与插入 10 条记录,消耗的计算资源差别很小。因此每次插入,条数越大,插入效率越高。如果一个插入请求带 200 条以上记录,单核就能达到每秒插入 100 万条记录的速度。但对前端数据采集的要求越高,因为需要缓存记录,然后一批插入。 +- **查询需求** TDengine 提供高效的查询,但是每个场景的查询差异很大,查询频次变化也很大,难以给出客观数字。需要用户针对自己的场景,写一些查询语句,才能确定。 + +因此仅对数据插入而言,CPU 是可以估算出来的,但查询所耗的计算资源无法估算。在实际运营过程中,不建议 CPU 使用率超过 50%,超过后,需要增加新的节点,以获得更多计算资源。 + +## 存储需求 + +TDengine 相对于通用数据库,有超高的压缩比,在绝大多数场景下,TDengine 的压缩比不会低于 5 倍,有的场合,压缩比可达到 10 倍以上,取决于实际场景的数据特征。压缩前的原始数据大小可通过如下方式计算: + +``` +Raw DataSize = numOfTables * rowSizePerTable * rowsPerTable +``` + +示例:1000 万台智能电表,每台电表每 15 分钟采集一次数据,每次采集的数据 128 字节,那么一年的原始数据量是:10000000 \* 128 \* 24 \* 60 / 15 \* 365 = 44.8512T。TDengine 大概需要消耗 44.851 / 5 = 8.97024T 空间。 + +用户可以通过参数 keep,设置数据在磁盘中的最大保存时长。为进一步减少存储成本,TDengine 还提供多级存储,最冷的数据可以存放在最廉价的存储介质上,应用的访问不用做任何调整,只是读取速度降低了。 + +为提高速度,可以配置多块硬盘,这样可以并发写入或读取数据。需要提醒的是,TDengine 采取多副本的方式提供数据的高可靠,因此不再需要采用昂贵的磁盘阵列。 + +## 物理机或虚拟机台数 + +根据上面的内存、CPU、存储的预估,就可以知道整个系统需要多少核、多少内存、多少存储空间。如果数据副本数不为 1,总需求量需要再乘以副本数。 + +因为 TDengine 具有很好的水平扩展能力,根据总量,再根据单个物理机或虚拟机的资源,就可以轻松决定需要购置多少台物理机或虚拟机了。 + +**立即计算 CPU、内存、存储,请参见:[资源估算方法](https://www.taosdata.com/config/config.html)。** diff --git a/docs-cn/13-operation/03-tolerance.md b/docs-cn/13-operation/03-tolerance.md new file mode 100644 index 0000000000000000000000000000000000000000..2c466819621adc10423c452328714c81e6f6f966 --- /dev/null +++ b/docs-cn/13-operation/03-tolerance.md @@ -0,0 +1,28 @@ +--- +title: 容错和灾备 +--- + +## 容错 + +TDengine 支持**WAL**(Write Ahead Log)机制,实现数据的容错能力,保证数据的高可用。 + +TDengine 接收到应用的请求数据包时,先将请求的原始数据包写入数据库日志文件,等数据成功写入数据库数据文件后,再删除相应的 WAL。这样保证了 TDengine 能够在断电等因素导致的服务重启时从数据库日志文件中恢复数据,避免数据的丢失。 + +涉及的系统配置参数有两个: + +- walLevel:WAL 级别,0:不写 WAL; 1:写 WAL, 但不执行 fsync; 2:写 WAL, 而且执行 fsync。 +- fsync:当 walLevel 设置为 2 时,执行 fsync 的周期。设置为 0,表示每次写入,立即执行 fsync。 + +如果要 100%的保证数据不丢失,需要将 walLevel 设置为 2,fsync 设置为 0。这时写入速度将会下降。但如果应用侧启动的写数据的线程数达到一定的数量(超过 50),那么写入数据的性能也会很不错,只会比 fsync 设置为 3000 毫秒下降 30%左右。 + +## 灾备 + +TDengine 的集群通过多个副本的机制,来提供系统的高可用性,实现灾备能力。 + +TDengine 集群是由 mnode 负责管理的,为保证 mnode 的高可靠,可以配置多个 mnode 副本,副本数由系统配置参数 numOfMnodes 决定,为了支持高可靠,需要设置大于 1。为保证元数据的强一致性,mnode 副本之间通过同步方式进行数据复制,保证了元数据的强一致性。 + +TDengine 集群中的时序数据的副本数是与数据库关联的,一个集群里可以有多个数据库,每个数据库可以配置不同的副本数。创建数据库时,通过参数 replica 指定副本数。为了支持高可靠,需要设置副本数大于 1。 + +TDengine 集群的节点数必须大于等于副本数,否则创建表时将报错。 + +当 TDengine 集群中的节点部署在不同的物理机上,并设置多个副本数时,就实现了系统的高可靠性,无需再使用其他软件或工具。TDengine 企业版还可以将副本部署在不同机房,从而实现异地容灾。 diff --git a/docs-cn/13-operation/06-admin.md b/docs-cn/13-operation/06-admin.md new file mode 100644 index 0000000000000000000000000000000000000000..7934d31eafb774fb45e1902bee29e8b518d152d6 --- /dev/null +++ b/docs-cn/13-operation/06-admin.md @@ -0,0 +1,42 @@ +--- +title: 用户管理 +--- + +系统管理员可以在 CLI 界面里添加、删除用户,也可以修改密码。CLI 里 SQL 语法如下: + +```sql +CREATE USER PASS <'password'>; +``` + +创建用户,并指定用户名和密码,密码需要用单引号引起来,单引号为英文半角 + +```sql +DROP USER ; +``` + +删除用户,限 root 用户使用 + +```sql +ALTER USER PASS <'password'>; +``` + +修改用户密码,为避免被转换为小写,密码需要用单引号引用,单引号为英文半角 + +```sql +ALTER USER PRIVILEGE ; +``` + +修改用户权限为:write 或 read,不需要添加单引号 + +说明:系统内共有 super/write/read 三种权限级别,但目前不允许通过 alter 指令把 super 权限赋予用户。 + +```sql +SHOW USERS; +``` + +显示所有用户 + +:::note +SQL 语法中,< >表示需要用户输入的部分,但请不要输入< >本身。 + +::: diff --git a/docs-cn/13-operation/07-import.md b/docs-cn/13-operation/07-import.md new file mode 100644 index 0000000000000000000000000000000000000000..7dee05720d4c3446181e8e0d81a5c27e35300ba8 --- /dev/null +++ b/docs-cn/13-operation/07-import.md @@ -0,0 +1,61 @@ +--- +title: 数据导入 +--- + +TDengine 提供多种方便的数据导入功能,一种按脚本文件导入,一种按数据文件导入,一种是 taosdump 工具导入本身导出的文件。 + +## 按脚本文件导入 + +TDengine 的 shell 支持 source filename 命令,用于批量运行文件中的 SQL 语句。用户可将建库、建表、写数据等 SQL 命令写在同一个文件中,每条命令单独一行,在 shell 中运行 source 命令,即可按顺序批量运行文件中的 SQL 语句。以‘#’开头的 SQL 语句被认为是注释,shell 将自动忽略。 + +## 按数据文件导入 + +TDengine 也支持在 shell 对已存在的表从 CSV 文件中进行数据导入。CSV 文件只属于一张表且 CSV 文件中的数据格式需与要导入表的结构相同,在导入的时候,其语法如下: + +```sql +insert into tb1 file 'path/data.csv'; +``` + +:::note +注意:如果 CSV 文件首行存在描述信息,请手动删除后再导入。如某列为空,填 NULL,无引号。\*\* + +::: + +例如,现在存在一个子表 d1001, 其表结构如下: + +```sql +taos> DESCRIBE d1001 + Field | Type | Length | Note | +================================================================================= + ts | TIMESTAMP | 8 | | + current | FLOAT | 4 | | + voltage | INT | 4 | | + phase | FLOAT | 4 | | + location | BINARY | 64 | TAG | + groupid | INT | 4 | TAG | +``` + +要导入的 data.csv 的格式如下: + +```csv +'2018-10-04 06:38:05.000',10.30000,219,0.31000 +'2018-10-05 06:38:15.000',12.60000,218,0.33000 +'2018-10-06 06:38:16.800',13.30000,221,0.32000 +'2018-10-07 06:38:05.000',13.30000,219,0.33000 +'2018-10-08 06:38:05.000',14.30000,219,0.34000 +'2018-10-09 06:38:05.000',15.30000,219,0.35000 +'2018-10-10 06:38:05.000',16.30000,219,0.31000 +'2018-10-11 06:38:05.000',17.30000,219,0.32000 +'2018-10-12 06:38:05.000',18.30000,219,0.31000 +``` + +那么可以用如下命令导入数据: + +```sql +taos> insert into d1001 file '~/data.csv'; +Query OK, 9 row(s) affected (0.004763s) +``` + +## taosdump 工具导入 + +TDengine 提供了方便的数据库导入导出工具 taosdump。用户可以将 taosdump 从一个系统导出的数据,导入到其他系统中。具体使用方法,请参见:[TDengine 数据备份工具: taosdump](/reference/taosdump)。 diff --git a/docs-cn/13-operation/08-export.md b/docs-cn/13-operation/08-export.md new file mode 100644 index 0000000000000000000000000000000000000000..042ecc7ba29f976d50bbca1e3155bd03b2ae7ccc --- /dev/null +++ b/docs-cn/13-operation/08-export.md @@ -0,0 +1,20 @@ +--- +title: 数据导出 +--- + +为方便数据导出,TDengine 提供了两种导出方式,分别是按表导出和用 taosdump 导出。 + +## 按表导出 CSV 文件 + +如果用户需要导出一个表或一个 STable 中的数据,可在 taos shell 中运行: + +```sql +select * from >> data.csv; +``` + +这样,表 tb_name 中的数据就会按照 CSV 格式导出到文件 data.csv 中。 + +## 用 taosdump 导出数据 + +利用 taosdump,用户可以根据需要选择导出所有数据库、一个数据库或者数据库中的一张表,所有数据或一时间段的数据,甚至仅仅表的定义。具体使用方法,请参见: +[TDengine 数据备份工具: taosdump](/reference/taosdump)。 diff --git a/docs-cn/13-operation/09-status.md b/docs-cn/13-operation/09-status.md new file mode 100644 index 0000000000000000000000000000000000000000..e7ae78bace91f6dab06591340965ba04efdd5edb --- /dev/null +++ b/docs-cn/13-operation/09-status.md @@ -0,0 +1,53 @@ +--- +title: 系统连接、任务查询管理 +--- + +系统管理员可以从 CLI 查询系统的连接、正在进行的查询、流式计算,并且可以关闭连接、停止正在进行的查询和流式计算。 + +## 显示数据库的连接 + +```sql +SHOW CONNECTIONS; +``` + +其结果中的一列显示 ip:port, 为连接的 IP 地址和端口号。 + +## 强制关闭数据库连接 + +```sql +KILL CONNECTION ; +``` + +其中的 connection-id 是 SHOW CONNECTIONS 中显示的第一列的数字。 + +## 显示数据查询 + +```sql +SHOW QUERIES; +``` + +其中第一列显示的以冒号隔开的两个数字为 query-id,为发起该 query 应用连接的 connection-id 和查询次数。 + +## 强制关闭数据查询 + +```sql +KILL QUERY ; +``` + +其中 query-id 是 SHOW QUERIES 中显示的 connection-id:query-no 字串,如“105:2”,拷贝粘贴即可。 + +## 显示连续查询 + +```sql +SHOW STREAMS; +``` + +其中第一列显示的以冒号隔开的两个数字为 stream-id, 为启动该 stream 应用连接的 connection-id 和发起 stream 的次数。 + +## 强制关闭连续查询 + +```sql +KILL STREAM ; +``` + +其中的 stream-id 是 SHOW STREAMS 中显示的 connection-id:stream-no 字串,如 103:2,拷贝粘贴即可。 diff --git a/docs-cn/13-operation/10-monitor.md b/docs-cn/13-operation/10-monitor.md new file mode 100644 index 0000000000000000000000000000000000000000..e30be775fb5c337b2a621bea92d3af31a2cb5cc0 --- /dev/null +++ b/docs-cn/13-operation/10-monitor.md @@ -0,0 +1,54 @@ +--- +title: 系统监控 +--- + +TDengine 启动后,会自动创建一个监测数据库 log,并自动将服务器的 CPU、内存、硬盘空间、带宽、请求数、磁盘读写速度、慢查询等信息定时写入该数据库。TDengine 还将重要的系统操作(比如登录、创建、删除数据库等)日志以及各种错误报警信息记录下来存放在 log 库里。系统管理员可以从 CLI 直接查看这个数据库,也可以在 WEB 通过图形化界面查看这些监测信息。 + +这些监测信息的采集缺省是打开的,但可以修改配置文件里的选项 monitor 将其关闭或打开。 + +## TDinsight - 使用监控数据库 + Grafana 对 TDengine 进行监控的解决方案 + +从 2.3.3.0 开始,监控数据库将提供更多的监控项,您可以从 [TDinsight Grafana Dashboard](https://grafana.com/grafana/dashboards/15167) 了解如何使用 TDinsight 方案对 TDengine 进行监控。 + +我们提供了一个自动化脚本 `TDinsight.sh` 对 TDinsight 进行部署。 + +下载 `TDinsight.sh`: + +```bash +wget https://github.com/taosdata/grafanaplugin/raw/master/dashboards/TDinsight.sh +chmod +x TDinsight.sh +``` + +准备: + +1. TDengine Server 信息: + + - TDengine RESTful 服务:对本地而言,可以是 `http://localhost:6041`,使用参数 `-a`。 + - TDengine 用户名和密码,使用 `-u` `-p` 参数设置。 + +2. Grafana 告警通知 + + - 使用已经存在的 Grafana Notification Channel `uid`,参数 `-E`。该参数可以使用 `curl -u admin:admin localhost:3000/api/alert-notifications |jq` 来获取。 + + ```bash + sudo ./TDinsight.sh -a http://localhost:6041 -u root -p taosdata -E + ``` + + - 使用 TDengine 数据源插件内置的阿里云短信告警通知,使用 `-s` 启用之,并设置如下参数: + + 1. 阿里云短信服务 Key ID,参数 `-I` + 2. 阿里云短信服务 Key Secret,参数 `K` + 3. 阿里云短信服务签名,参数 `-S` + 4. 短信通知模板号,参数 `-C` + 5. 短信通知模板输入参数,JSON 格式,参数 `-T`,如 `{"alarm_level":"%s","time":"%s","name":"%s","content":"%s"}` + 6. 逗号分隔的通知手机列表,参数 `-B` + + ```bash + sudo ./TDinsight.sh -a http://localhost:6041 -u root -p taosdata -s \ + -I XXXXXXX -K XXXXXXXX -S taosdata -C SMS_1111111 -B 18900000000 \ + -T '{"alarm_level":"%s","time":"%s","name":"%s","content":"%s"}' + ``` + +运行程序并重启 Grafana 服务,打开面板:`http://localhost:3000/d/tdinsight`。 + +更多使用场景和限制请参考[TDinsight](/reference/tdinsight/) 文档。 diff --git a/docs-cn/13-operation/11-optimize.md b/docs-cn/13-operation/11-optimize.md new file mode 100644 index 0000000000000000000000000000000000000000..1ca9e8c44492a5882613a0b55d959d7abca8b5f6 --- /dev/null +++ b/docs-cn/13-operation/11-optimize.md @@ -0,0 +1,100 @@ +--- +title: 性能优化 +--- + +因数据行 [update](/train-faq/faq/#update)、表删除、数据过期等原因,TDengine 的磁盘存储文件有可能出现数据碎片,影响查询操作的性能表现。从 2.1.3.0 版本开始,新增 SQL 指令 COMPACT 来启动碎片重整过程: + +```sql +COMPACT VNODES IN (vg_id1, vg_id2, ...) +``` + +COMPACT 命令对指定的一个或多个 VGroup 启动碎片重整,系统会通过任务队列尽快安排重整操作的具体执行。COMPACT 指令所需的 VGroup id,可以通过 `SHOW VGROUPS;` 指令的输出结果获取;而且在 `SHOW VGROUPS;` 中会有一个 compacting 列,值为 2 时表示对应的 VGroup 处于排队等待进行重整的状态,值为 1 时表示正在进行碎片重整,为 0 时则表示并没有处于重整状态(未要求进行重整或已经完成重整)。 + +需要注意的是,碎片重整操作会大幅消耗磁盘 I/O。因此在重整进行期间,有可能会影响节点的写入和查询性能,甚至在极端情况下导致短时间的阻写。 + +## 存储参数优化 + +不同应用场景的数据往往具有不同的数据特征,比如保留天数、副本数、采集频次、记录大小、采集点的数量、压缩等都可完全不同。为获得在存储上的最高效率,TDengine 提供如下存储相关的系统配置参数(既可以作为 create database 指令的参数,也可以写在 taos.cfg 配置文件中用来设定创建新数据库时所采用的默认值): + +| # | 配置参数名称 | 单位 | 含义 | **取值范围** | **缺省值** | +| --- | ------------ | ---- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | ---------- | +| 1 | days | 天 | 一个数据文件存储数据的时间跨度 | 1-3650 | 10 | +| 2 | keep | 天 | (可通过 alter database 修改)数据库中数据保留的天数。 | 1-36500 | 3650 | +| 3 | cache | MB | 内存块的大小 | 1-128 | 16 | +| 4 | blocks | | (可通过 alter database 修改)每个 VNODE(TSDB)中有多少个 cache 大小的内存块。因此一个 VNODE 使用的内存大小粗略为(cache \* blocks)。 | 3-10000 | 6 | +| 5 | quorum | | (可通过 alter database 修改)多副本环境下指令执行的确认数要求 | 1-2 | 1 | +| 6 | minRows | | 文件块中记录的最小条数 | 10-1000 | 100 | +| 7 | maxRows | | 文件块中记录的最大条数 | 200-10000 | 4096 | +| 8 | comp | | (可通过 alter database 修改)文件压缩标志位 | 0:关闭,1:一阶段压缩,2:两阶段压缩 | 2 | +| 9 | walLevel | | (作为 database 的参数时名为 wal;在 taos.cfg 中作为参数时需要写作 walLevel)WAL 级别 | 1:写 WAL,但不执行 fsync;2:写 WAL, 而且执行 fsync | 1 | +| 10 | fsync | 毫秒 | 当 wal 设置为 2 时,执行 fsync 的周期。设置为 0,表示每次写入,立即执行 fsync。 | | 3000 | +| 11 | replica | | (可通过 alter database 修改)副本个数 | 1-3 | 1 | +| 12 | precision | | 时间戳精度标识(2.1.2.0 版本之前、2.0.20.7 版本之前在 taos.cfg 文件中不支持此参数。)(从 2.1.5.0 版本开始,新增对纳秒时间精度的支持) | ms 表示毫秒,us 表示微秒,ns 表示纳秒 | ms | +| 13 | update | | 是否允许数据更新(从 2.1.7.0 版本开始此参数支持 0 ~ 2 的取值范围,在此之前取值只能是 [0, 1];而 2.0.8.0 之前的版本在 SQL 指令中不支持此参数。) | 0:不允许;1:允许更新整行;2:允许部分列更新。 | 0 | +| 14 | cacheLast | | (可通过 alter database 修改)是否在内存中缓存子表的最近数据(从 2.1.2.0 版本开始此参数支持 0 ~ 3 的取值范围,在此之前取值只能是 [0, 1];而 2.0.11.0 之前的版本在 SQL 指令中不支持此参数。)(2.1.2.0 版本之前、2.0.20.7 版本之前在 taos.cfg 文件中不支持此参数。) | 0:关闭;1:缓存子表最近一行数据;2:缓存子表每一列的最近的非 NULL 值;3:同时打开缓存最近行和列功能 | 0 | + +对于一个应用场景,可能有多种数据特征的数据并存,最佳的设计是将具有相同数据特征的表放在一个库里,这样一个应用有多个库,而每个库可以配置不同的存储参数,从而保证系统有最优的性能。TDengine 允许应用在创建库时指定上述存储参数,如果指定,该参数就将覆盖对应的系统配置参数。举例,有下述 SQL: + +```sql + CREATE DATABASE demo DAYS 10 CACHE 32 BLOCKS 8 REPLICA 3 UPDATE 1; +``` + +该 SQL 创建了一个库 demo, 每个数据文件存储 10 天数据,内存块为 32 兆字节,每个 VNODE 占用 8 个内存块,副本数为 3,允许更新,而其他参数与系统配置完全一致。 + +一个数据库创建成功后,仅部分参数可以修改并实时生效,其余参数不能修改: + +| **参数名** | **能否修改** | **范围** | **修改语法示例** | +| ----------- | ------------ | ---------------------------------------------------------- | -------------------------------------- | +| name | | | | +| create time | | | | +| ntables | | | | +| vgroups | | | | +| replica | **YES** | 在线 dnode 数目为:
1:1-1;
2:1-2;
\>=3:1-3 | ALTER DATABASE REPLICA _n_ | +| quorum | **YES** | 1-2 | ALTER DATABASE QUORUM _n_ | +| days | | | | +| keep | **YES** | days-365000 | ALTER DATABASE KEEP _n_ | +| cache | | | | +| blocks | **YES** | 3-1000 | ALTER DATABASE BLOCKS _n_ | +| minrows | | | | +| maxrows | | | | +| wal | | | | +| fsync | | | | +| comp | **YES** | 0-2 | ALTER DATABASE COMP _n_ | +| precision | | | | +| status | | | | +| update | | | | +| cachelast | **YES** | 0 \| 1 \| 2 \| 3 | ALTER DATABASE CACHELAST _n_ | + +**说明:**在 2.1.3.0 版本之前,通过 ALTER DATABASE 语句修改这些参数后,需要重启服务器才能生效。 + +TDengine 集群中加入一个新的 dnode 时,涉及集群相关的一些参数必须与已有集群的配置相同,否则不能成功加入到集群中。会进行校验的参数如下: + +- numOfMnodes:系统中管理节点个数。默认值:3。(2.0 版本从 2.0.20.11 开始、2.1 及以上版本从 2.1.6.0 开始,numOfMnodes 默认值改为 1。) +- mnodeEqualVnodeNum: 一个 mnode 等同于 vnode 消耗的个数。默认值:4。 +- offlineThreshold: dnode 离线阈值,超过该时间将导致该 dnode 从集群中删除。单位为秒,默认值:86400\*10(即 10 天)。 +- statusInterval: dnode 向 mnode 报告状态时长。单位为秒,默认值:1。 +- maxTablesPerVnode: 每个 vnode 中能够创建的最大表个数。默认值:1000000。 +- maxVgroupsPerDb: 每个数据库中能够使用的最大 vgroup 个数。 +- arbitrator: 系统中裁决器的 endpoint,缺省为空。 +- timezone、locale、charset 的配置见客户端配置。(2.0.20.0 及以上的版本里,集群中加入新节点已不要求 locale 和 charset 参数取值一致) +- balance:是否启用负载均衡。0:否,1:是。默认值:1。 +- flowctrl:是否启用非阻塞流控。0:否,1:是。默认值:1。 +- slaveQuery:是否启用 slave vnode 参与查询。0:否,1:是。默认值:1。 +- adjustMaster:是否启用 vnode master 负载均衡。0:否,1:是。默认值:1。 + +为方便调试,可通过 SQL 语句临时调整每个 dnode 的日志配置,系统重启后会失效: + +```sql +ALTER DNODE +``` + +- dnode_id: 可以通过 SQL 语句"SHOW DNODES"命令获取 +- config: 要调整的日志参数,在如下列表中取值 + > resetlog 截断旧日志文件,创建一个新日志文件 + > debugFlag < 131 | 135 | 143 > 设置 debugFlag 为 131、135 或者 143 + +例如: + +``` +alter dnode 1 debugFlag 135; +``` diff --git a/docs-cn/13-operation/17-diagnose.md b/docs-cn/13-operation/17-diagnose.md new file mode 100644 index 0000000000000000000000000000000000000000..e2a2ef035a33a295b206c77ec08edf8f7842671f --- /dev/null +++ b/docs-cn/13-operation/17-diagnose.md @@ -0,0 +1,131 @@ +--- +title: 诊断及其他 +--- + +## 网络连接诊断 + +当出现客户端应用无法访问服务端时,需要确认客户端与服务端之间网络的各端口连通情况,以便有针对性地排除故障。 + +目前网络连接诊断支持在:Linux 与 Linux,Linux 与 Windows 之间进行诊断测试。 + +诊断步骤: + +1. 如拟诊断的端口范围与服务器 taosd 实例的端口范围相同,须先停掉 taosd 实例 +2. 服务端命令行输入:`taos -n server -P -l ` 以服务端身份启动对端口 port 为基准端口的监听 +3. 客户端命令行输入:`taos -n client -h -P -l ` 以客户端身份启动对指定的服务器、指定的端口发送测试包 + +-l : 测试网络包的大小(单位:字节)。最小值是 11、最大值是 64000,默认值为 1000。 +注:两端命令行中指定的测试包长度必须一致,否则测试显示失败。 + +服务端运行正常的话会输出以下信息: + +```bash +# taos -n server -P 6000 +12/21 14:50:13.522509 0x7f536f455200 UTL work as server, host:172.27.0.7 startPort:6000 endPort:6011 pkgLen:1000 + +12/21 14:50:13.522659 0x7f5352242700 UTL TCP server at port:6000 is listening +12/21 14:50:13.522727 0x7f5351240700 UTL TCP server at port:6001 is listening +... +... +... +12/21 14:50:13.523954 0x7f5342fed700 UTL TCP server at port:6011 is listening +12/21 14:50:13.523989 0x7f53437ee700 UTL UDP server at port:6010 is listening +12/21 14:50:13.524019 0x7f53427ec700 UTL UDP server at port:6011 is listening +12/21 14:50:22.192849 0x7f5352242700 UTL TCP: read:1000 bytes from 172.27.0.8 at 6000 +12/21 14:50:22.192993 0x7f5352242700 UTL TCP: write:1000 bytes to 172.27.0.8 at 6000 +12/21 14:50:22.237082 0x7f5351a41700 UTL UDP: recv:1000 bytes from 172.27.0.8 at 6000 +12/21 14:50:22.237203 0x7f5351a41700 UTL UDP: send:1000 bytes to 172.27.0.8 at 6000 +12/21 14:50:22.237450 0x7f5351240700 UTL TCP: read:1000 bytes from 172.27.0.8 at 6001 +12/21 14:50:22.237576 0x7f5351240700 UTL TCP: write:1000 bytes to 172.27.0.8 at 6001 +12/21 14:50:22.281038 0x7f5350a3f700 UTL UDP: recv:1000 bytes from 172.27.0.8 at 6001 +12/21 14:50:22.281141 0x7f5350a3f700 UTL UDP: send:1000 bytes to 172.27.0.8 at 6001 +... +... +... +12/21 14:50:22.677443 0x7f5342fed700 UTL TCP: read:1000 bytes from 172.27.0.8 at 6011 +12/21 14:50:22.677576 0x7f5342fed700 UTL TCP: write:1000 bytes to 172.27.0.8 at 6011 +12/21 14:50:22.721144 0x7f53427ec700 UTL UDP: recv:1000 bytes from 172.27.0.8 at 6011 +12/21 14:50:22.721261 0x7f53427ec700 UTL UDP: send:1000 bytes to 172.27.0.8 at 6011 +``` + +客户端运行正常会输出以下信息: + +```bash +# taos -n client -h 172.27.0.7 -P 6000 +12/21 14:50:22.192434 0x7fc95d859200 UTL work as client, host:172.27.0.7 startPort:6000 endPort:6011 pkgLen:1000 + +12/21 14:50:22.192472 0x7fc95d859200 UTL server ip:172.27.0.7 is resolved from host:172.27.0.7 +12/21 14:50:22.236869 0x7fc95d859200 UTL successed to test TCP port:6000 +12/21 14:50:22.237215 0x7fc95d859200 UTL successed to test UDP port:6000 +... +... +... +12/21 14:50:22.676891 0x7fc95d859200 UTL successed to test TCP port:6010 +12/21 14:50:22.677240 0x7fc95d859200 UTL successed to test UDP port:6010 +12/21 14:50:22.720893 0x7fc95d859200 UTL successed to test TCP port:6011 +12/21 14:50:22.721274 0x7fc95d859200 UTL successed to test UDP port:6011 +``` + +仔细阅读打印出来的错误信息,可以帮助管理员找到原因,以解决问题。 + +## 启动状态及 RPC 诊断 + +`taos -n startup -h ` + +判断 taosd 服务端是否成功启动,是数据库管理员经常遇到的一种情形。特别当若干台服务器组成集群时,判断每个服务端实例是否成功启动就会是一个重要问题。除检索 taosd 服务端日志文件进行问题定位、分析外,还可以通过 `taos -n startup -h ` 来诊断一个 taosd 进程的启动状态。 + +针对多台服务器组成的集群,当服务启动过程耗时较长时,可通过该命令行来诊断每台服务器的 taosd 实例的启动状态,以准确定位问题。 + +`taos -n rpc -h ` + +该命令用来诊断已经启动的 taosd 实例的端口是否可正常访问。如果 taosd 程序异常或者失去响应,可以通过 `taos -n rpc -h ` 来发起一个与指定 fqdn 的 rpc 通信,看看 taosd 是否能收到,以此来判定是网络问题还是 taosd 程序异常问题。 + +## sync 及 arbitrator 诊断 + +``` +taos -n sync -P 6040 -h +taos -n sync -P 6042 -h +``` + +用来诊断 sync 端口是否工作正常,判断服务端 sync 模块是否成功工作。另外,-P 6042 用来诊断 arbitrator 是否配置正常,判断指定服务器的 arbitrator 是否能正常工作。 + +## 网络速度诊断 + +`taos -n speed -h -P 6030 -N 10 -l 10000000 -S TCP` + +从 2.2.0.0 版本开始,taos 工具新提供了一个网络速度诊断的模式,可以对一个正在运行中的 taosd 实例或者 `taos -n server` 方式模拟的一个服务端实例,以非压缩传输的方式进行网络测速。这个模式下可供调整的参数如下: + +-n:设为“speed”时,表示对网络速度进行诊断。 +-h:所要连接的服务端的 FQDN 或 ip 地址。如果不设置这一项,会使用本机 taos.cfg 文件中 FQDN 参数的设置作为默认值。 +-P:所连接服务端的网络端口。默认值为 6030。 +-N:诊断过程中使用的网络包总数。最小值是 1、最大值是 10000,默认值为 100。 +-l:单个网络包的大小(单位:字节)。最小值是 1024、最大值是 1024 `*` 1024 `*` 1024,默认值为 1024。 +-S:网络封包的类型。可以是 TCP 或 UDP,默认值为 TCP。 + +## FQDN 解析速度诊断 + +`taos -n fqdn -h ` + +从 2.2.0.0 版本开始,taos 工具新提供了一个 FQDN 解析速度的诊断模式,可以对一个目标 FQDN 地址尝试解析,并记录解析过程中所消耗的时间。这个模式下可供调整的参数如下: + +-n:设为“fqdn”时,表示对 FQDN 解析进行诊断。 +-h:所要解析的目标 FQDN 地址。如果不设置这一项,会使用本机 taos.cfg 文件中 FQDN 参数的设置作为默认值。 + +## 服务端日志 + +taosd 服务端日志文件标志位 debugflag 默认为 131,在 debug 时往往需要将其提升到 135 或 143 。 + +一旦设定为 135 或 143,日志文件增长很快,特别是写入、查询请求量较大时,增长速度惊人。如合并保存日志,很容易把日志内的关键信息(如配置信息、错误信息等)冲掉。为此,服务端将重要信息日志与其他日志分开存放: + +- taosinfo 存放重要信息日志, 包括:INFO/ERROR/WARNING 级别的日志信息。不记录 DEBUG、TRACE 级别的日志。 +- taosdlog 服务器端生成的日志,记录 taosinfo 中全部信息外,还根据设置的日志输出级别,记录 DEBUG(日志级别 135)、TRACE(日志级别是 143)。 + +## 客户端日志 + +每个独立运行的客户端(一个进程)生成一个独立的客户端日志,其命名方式采用 taoslog+<序号> 的方式命名。文件标志位 debugflag 默认为 131,在 debug 时往往需要将其提升到 135 或 143 。 + +- taoslog 客户端(driver)生成的日志,默认记录客户端 INFO/ERROR/WARNING 级别日志,还根据设置的日志输出级别,记录 DEBUG(日志级别 135)、TRACE(日志级别是 143)。 + +其中,日志文件最大长度由 numOfLogLines 来进行配置,一个 taosd 实例最多保留两个文件。 + +taosd 服务端日志采用异步落盘写入机制,优点是可以避免硬盘写入压力太大,对性能造成很大影响。缺点是,在极端情况下,存在少量日志行数丢失的可能。 diff --git a/docs-cn/13-operation/_category_.yml b/docs-cn/13-operation/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..315839970c47e4ac76f6f19f65331a44fd93229e --- /dev/null +++ b/docs-cn/13-operation/_category_.yml @@ -0,0 +1 @@ +label: 运维指南 diff --git a/docs-cn/13-operation/index.md b/docs-cn/13-operation/index.md new file mode 100644 index 0000000000000000000000000000000000000000..bc06fbdc138ee593c1206475095ef48d32493b37 --- /dev/null +++ b/docs-cn/13-operation/index.md @@ -0,0 +1,12 @@ +--- +title: 运维指南 +--- + +本章节主要为系统管理员写的,覆盖安装、下载、数据导入、导出、运行系统的监测、用户管理、连接管理等内容,同时介绍根据业务量,如何做容量规划,系统运行一段时间后,如何做系统优化。 + +```mdx-code-block +import DocCardList from '@theme/DocCardList'; +import {useCurrentSidebarCategory} from '@docusaurus/theme-common'; + + +``` diff --git a/docs-cn/14-reference/02-rest-api/02-rest-api.mdx b/docs-cn/14-reference/02-rest-api/02-rest-api.mdx new file mode 100644 index 0000000000000000000000000000000000000000..c7680ab3e9e109dbb328711f62881283241444fb --- /dev/null +++ b/docs-cn/14-reference/02-rest-api/02-rest-api.mdx @@ -0,0 +1,307 @@ +--- +title: REST API +--- + +为支持各种不同类型平台的开发,TDengine 提供符合 REST 设计标准的 API,即 REST API。为最大程度降低学习成本,不同于其他数据库 REST API 的设计方法,TDengine 直接通过 HTTP POST 请求 BODY 中包含的 SQL 语句来操作数据库,仅需要一个 URL。REST 连接器的使用参见[视频教程](https://www.taosdata.com/blog/2020/11/11/1965.html)。 + +:::note +与原生连接器的一个区别是,RESTful 接口是无状态的,因此 `USE db_name` 指令没有效果,所有对表名、超级表名的引用都需要指定数据库名前缀。从 2.2.0.0 版本开始,支持在 RESTful URL 中指定 db_name,这时如果 SQL 语句中没有指定数据库名前缀的话,会使用 URL 中指定的这个 db_name。从 2.4.0.0 版本开始,RESTful 默认由 taosAdapter 提供,要求必须在 URL 中指定 db_name。 +::: + +## 安装 + +RESTful 接口不依赖于任何 TDengine 的库,因此客户端不需要安装任何 TDengine 的库,只要客户端的开发语言支持 HTTP 协议即可。 + +## 验证 + +在已经安装 TDengine 服务器端的情况下,可以按照如下方式进行验证。 + +下面以 Ubuntu 环境中使用 curl 工具(确认已经安装)来验证 RESTful 接口的正常。 + +下面示例是列出所有的数据库,请把 h1.taosdata.com 和 6041(缺省值)替换为实际运行的 TDengine 服务 FQDN 和端口号: + +```html +curl -H 'Authorization: Basic cm9vdDp0YW9zZGF0YQ==' -d 'show databases;' h1.taosdata.com:6041/rest/sql +``` + +返回值结果如下表示验证通过: + +```json +{ + "status": "succ", + "head": [ + "name", + "created_time", + "ntables", + "vgroups", + "replica", + "quorum", + "days", + "keep1,keep2,keep(D)", + "cache(MB)", + "blocks", + "minrows", + "maxrows", + "wallevel", + "fsync", + "comp", + "precision", + "status" + ], + "data": [ + [ + "log", + "2020-09-02 17:23:00.039", + 4, + 1, + 1, + 1, + 10, + "30,30,30", + 1, + 3, + 100, + 4096, + 1, + 3000, + 2, + "us", + "ready" + ] + ], + "rows": 1 +} +``` + +## HTTP 请求格式 + +``` +http://:/rest/sql/[db_name] +``` + +参数说明: + +- fqnd: 集群中的任一台主机 FQDN 或 IP 地址 +- port: 配置文件中 httpPort 配置项,缺省为 6041 +- db_name: 可选参数,指定本次所执行的 SQL 语句的默认数据库库名。(从 2.2.0.0 版本开始支持) + +例如:`http://h1.taos.com:6041/rest/sql/test` 是指向地址为 `h1.taos.com:6041` 的 URL,并将默认使用的数据库库名设置为 `test`。 + +HTTP 请求的 Header 里需带有身份认证信息,TDengine 支持 Basic 认证与自定义认证两种机制,后续版本将提供标准安全的数字签名机制来做身份验证。 + +- 自定义身份认证信息如下所示(token 稍后介绍) + + ``` + Authorization: Taosd + ``` + +- Basic 身份认证信息如下所示 + + ``` + Authorization: Basic + ``` + +HTTP 请求的 BODY 里就是一个完整的 SQL 语句,SQL 语句中的数据表应提供数据库前缀,例如 db_name.tb_name。如果表名不带数据库前缀,又没有在 URL 中指定数据库名的话,系统会返回错误。因为 HTTP 模块只是一个简单的转发,没有当前 DB 的概念。 + +使用 `curl` 通过自定义身份认证方式来发起一个 HTTP Request,语法如下: + +```bash +curl -H 'Authorization: Basic ' -d '' :/rest/sql/[db_name] +``` + +或者 + +```bash +curl -u username:password -d '' :/rest/sql/[db_name] +``` + +其中,`TOKEN` 为 `{username}:{password}` 经过 Base64 编码之后的字符串,例如 `root:taosdata` 编码后为 `cm9vdDp0YW9zZGF0YQ==` + +## HTTP 返回格式 + +返回值为 JSON 格式,如下: + +```json +{ + "status": "succ", + "head": ["ts","current", …], + "column_meta": [["ts",9,8],["current",6,4], …], + "data": [ + ["2018-10-03 14:38:05.000", 10.3, …], + ["2018-10-03 14:38:15.000", 12.6, …] + ], + "rows": 2 +} +``` + +说明: + +- status: 告知操作结果是成功还是失败。 +- head: 表的定义,如果不返回结果集,则仅有一列 “affected_rows”。(从 2.0.17.0 版本开始,建议不要依赖 head 返回值来判断数据列类型,而推荐使用 column_meta。在后续版本中,有可能会从返回值中去掉 head 这一项。) +- column_meta: 从 2.0.17.0 版本开始,返回值中增加这一项来说明 data 里每一列的数据类型。具体每个列会用三个值来说明,分别为:列名、列类型、类型长度。例如`["current",6,4]`表示列名为“current”;列类型为 6,也即 float 类型;类型长度为 4,也即对应 4 个字节表示的 float。如果列类型为 binary 或 nchar,则类型长度表示该列最多可以保存的内容长度,而不是本次返回值中的具体数据长度。当列类型是 nchar 的时候,其类型长度表示可以保存的 unicode 字符数量,而不是 bytes。 +- data: 具体返回的数据,一行一行的呈现,如果不返回结果集,那么就仅有 [[affected_rows]]。data 中每一行的数据列顺序,与 column_meta 中描述数据列的顺序完全一致。 +- rows: 表明总共多少行数据。 + +column_meta 中的列类型说明: + +- 1:BOOL +- 2:TINYINT +- 3:SMALLINT +- 4:INT +- 5:BIGINT +- 6:FLOAT +- 7:DOUBLE +- 8:BINARY +- 9:TIMESTAMP +- 10:NCHAR + +## 自定义授权码 + +HTTP 请求中需要带有授权码 ``,用于身份识别。授权码通常由管理员提供,可简单的通过发送 `HTTP GET` 请求来获取授权码,操作如下: + +```bash +curl http://:/rest/login// +``` + +其中,`fqdn` 是 TDengine 数据库的 FQDN 或 IP 地址,`port` 是 TDengine 服务的端口号,`username` 为数据库用户名,`password` 为数据库密码,返回值为 JSON 格式,各字段含义如下: + +- status:请求结果的标志位 + +- code:返回值代码 + +- desc:授权码 + +获取授权码示例: + +```bash +curl http://192.168.0.1:6041/rest/login/root/taosdata +``` + +返回值: + +```json +{ + "status": "succ", + "code": 0, + "desc": "/KfeAzX/f9na8qdtNZmtONryp201ma04bEl8LcvLUd7a8qdtNZmtONryp201ma04" +} +``` + +## 使用示例 + +- 在 demo 库里查询表 d1001 的所有记录: + + ```bash + curl -H 'Authorization: Basic cm9vdDp0YW9zZGF0YQ==' -d 'select * from demo.d1001' 192.168.0.1:6041/rest/sql + ``` + + 返回值: + + ```json + { + "status": "succ", + "head": ["ts", "current", "voltage", "phase"], + "column_meta": [ + ["ts", 9, 8], + ["current", 6, 4], + ["voltage", 4, 4], + ["phase", 6, 4] + ], + "data": [ + ["2018-10-03 14:38:05.000", 10.3, 219, 0.31], + ["2018-10-03 14:38:15.000", 12.6, 218, 0.33] + ], + "rows": 2 + } + ``` + +- 创建库 demo: + + ```bash + curl -H 'Authorization: Basic cm9vdDp0YW9zZGF0YQ==' -d 'create database demo' 192.168.0.1:6041/rest/sql + ``` + + 返回值: + + ```json + { + "status": "succ", + "head": ["affected_rows"], + "column_meta": [["affected_rows", 4, 4]], + "data": [[1]], + "rows": 1 + } + ``` + +## 其他用法 + +### 结果集采用 Unix 时间戳 + +HTTP 请求 URL 采用 `/rest/sqlt` 时,返回结果集的时间戳将采用 Unix 时间戳格式表示,例如 + +```bash +curl -H 'Authorization: Basic cm9vdDp0YW9zZGF0YQ==' -d 'select * from demo.d1001' 192.168.0.1:6041/rest/sqlt +``` + +返回结果: + +```json +{ + "status": "succ", + "head": ["ts", "current", "voltage", "phase"], + "column_meta": [ + ["ts", 9, 8], + ["current", 6, 4], + ["voltage", 4, 4], + ["phase", 6, 4] + ], + "data": [ + [1538548685000, 10.3, 219, 0.31], + [1538548695000, 12.6, 218, 0.33] + ], + "rows": 2 +} +``` + +### 结果集采用 UTC 时间字符串 + +HTTP 请求 URL 采用 `/rest/sqlutc` 时,返回结果集的时间戳将采用 UTC 时间字符串表示,例如 + +```bash + curl -H 'Authorization: Basic cm9vdDp0YW9zZGF0YQ==' -d 'select * from demo.t1' 192.168.0.1:6041/rest/sqlutc +``` + +返回值: + +```json +{ + "status": "succ", + "head": ["ts", "current", "voltage", "phase"], + "column_meta": [ + ["ts", 9, 8], + ["current", 6, 4], + ["voltage", 4, 4], + ["phase", 6, 4] + ], + "data": [ + ["2018-10-03T14:38:05.000+0800", 10.3, 219, 0.31], + ["2018-10-03T14:38:15.000+0800", 12.6, 218, 0.33] + ], + "rows": 2 +} +``` + +## 重要配置项 + +下面仅列出一些与 RESTful 接口有关的配置参数,其他系统参数请看配置文件里的说明。 + +- 对外提供 RESTful 服务的端口号,默认绑定到 6041(实际取值是 serverPort + 11,因此可以通过修改 serverPort 参数的设置来修改)。 +- httpMaxThreads: 启动的线程数量,默认为 2(2.0.17.0 版本开始,默认值改为 CPU 核数的一半向下取整)。 +- restfulRowLimit: 返回结果集(JSON 格式)的最大条数,默认值为 10240。 +- httpEnableCompress: 是否支持压缩,默认不支持,目前 TDengine 仅支持 gzip 压缩格式。 +- httpDebugFlag: 日志开关,默认 131。131:仅错误和报警信息,135:调试信息,143:非常详细的调试信息。 +- httpDbNameMandatory: 是否必须在 RESTful URL 中指定默认的数据库名。默认为 0,即关闭此检查。如果设置为 1,那么每个 RESTful URL 中都必须设置一个默认数据库名,否则无论此时执行的 SQL 语句是否需要指定数据库,都会返回一个执行错误,拒绝执行此 SQL 语句。 + +:::note +如果使用 taosd 提供的 REST API, 那么以上配置需要写在 taosd 的配置文件 taos.cfg 中。如果使用 taosAdapter 提供的 REST API, 那么需要参考 taosAdapter [对应的配置方法](/reference/taosadapter/)。 +::: diff --git a/docs-cn/14-reference/02-rest-api/_category_.yml b/docs-cn/14-reference/02-rest-api/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..57a20d8458e937f60c41806be4392ebb2d13e0f7 --- /dev/null +++ b/docs-cn/14-reference/02-rest-api/_category_.yml @@ -0,0 +1 @@ +label: REST API diff --git a/docs-cn/14-reference/03-connector/03-connector.mdx b/docs-cn/14-reference/03-connector/03-connector.mdx new file mode 100644 index 0000000000000000000000000000000000000000..c0e714f148a7821e070be38a5484484fdd747e9a --- /dev/null +++ b/docs-cn/14-reference/03-connector/03-connector.mdx @@ -0,0 +1,117 @@ +--- +title: 连接器 +--- + +TDengine 提供了丰富的应用程序开发接口,为了便于用户快速开发自己的应用,TDengine 支持了多种编程语言的连接器,其中官方连接器包括支持 C/C++、Java、Python、Go、Node.js、C# 和 Rust 的连接器。这些连接器支持使用原生接口(taosc)和 REST 接口(部分语言暂不支持)连接 TDengine 集群。社区开发者也贡献了多个非官方连接器,例如 ADO.NET 连接器、Lua 连接器和 PHP 连接器。 + +![image-connector](/img/connector.png) + +## 支持的平台 + +目前 TDengine 的原生接口连接器可支持的平台包括:X64/X86/ARM64/ARM32/MIPS/Alpha 等硬件平台,以及 Linux/Win64/Win32 等开发环境。对照矩阵如下: + +| **CPU** | **OS** | **JDBC** | **Python** | **Go** | **Node.js** | **C#** | **Rust** | C/C++ | +| -------------- | --------- | -------- | ---------- | ------ | ----------- | ------ | -------- | ----- | +| **X86 64bit** | **Linux** | ● | ● | ● | ● | ● | ● | ● | +| **X86 64bit** | **Win64** | ● | ● | ● | ● | ● | ● | ● | +| **X86 64bit** | **Win32** | ● | ● | ● | ● | ○ | ○ | ● | +| **X86 32bit** | **Win32** | ○ | ○ | ○ | ○ | ○ | ○ | ● | +| **ARM64** | **Linux** | ● | ● | ● | ● | ○ | ○ | ● | +| **ARM32** | **Linux** | ● | ● | ● | ● | ○ | ○ | ● | +| **MIPS 龙芯** | **Linux** | ○ | ○ | ○ | ○ | ○ | ○ | ○ | +| **Alpha 申威** | **Linux** | ○ | ○ | -- | -- | -- | -- | ○ | +| **X86 海光** | **Linux** | ○ | ○ | ○ | -- | -- | -- | ○ | + +其中 ● 表示官方测试验证通过,○ 表示非官方测试验证通过,-- 表示未经验证。 + +使用 REST 连接由于不依赖客户端驱动可以支持更广泛的操作系统。 + +## 版本支持 + +TDengine 版本更新往往会增加新的功能特性,列表中的连接器版本为连接器最佳适配版本。 + +| **TDengine 版本** | **Java** | **Python** | **Go** | **C#** | **Node.js** | **Rust** | +| --------------------- | -------- | ---------- | ------------ | ------------- | --------------- | -------- | +| **2.4.0.14 及以上** | 2.0.38 | 当前版本 | develop 分支 | 1.0.2 - 1.0.6 | 2.0.10 - 2.0.12 | 当前版本 | +| **2.4.0.6 及以上** | 2.0.37 | 当前版本 | develop 分支 | 1.0.2 - 1.0.6 | 2.0.10 - 2.0.12 | 当前版本 | +| **2.4.0.4 - 2.4.0.5** | 2.0.37 | 当前版本 | develop 分支 | 1.0.2 - 1.0.6 | 2.0.10 - 2.0.12 | 当前版本 | +| **2.2.x.x ** | 2.0.36 | 当前版本 | master 分支 | n/a | 2.0.7 - 2.0.9 | 当前版本 | +| **2.0.x.x ** | 2.0.34 | 当前版本 | master 分支 | n/a | 2.0.1 - 2.0.6 | 当前版本 | + +## 功能特性 + +连接器对 TDengine 功能特性的支持对照如下: + +### 使用原生接口(taosc) + +| **功能特性** | **Java** | **Python** | **Go** | **C#** | **Node.js** | **Rust** | +| -------------- | -------- | ---------- | ------ | ------ | ----------- | -------- | +| **连接管理** | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | +| **普通查询** | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | +| **连续查询** | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | +| **参数绑定** | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | +| **订阅功能** | 支持 | 支持 | 支持 | 支持 | 支持 | 暂不支持 | +| **Schemaless** | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | +| **DataFrame** | 不支持 | 支持 | 不支持 | 不支持 | 不支持 | 不支持 | + +:::info +由于不同编程语言数据库框架规范不同,并不意味着所有 C/C++ 接口都需要对应封装支持。 +::: + +### 使用 REST 接口 + +| **功能特性** | **Java** | **Python** | **Go** | **C#(暂不支持)** | **Node.js** | **Rust** | +| ------------------------------ | -------- | ---------- | -------- | ------------------ | ----------- | -------- | +| **连接管理** | 支持 | 支持 | 支持 | N/A | 支持 | 支持 | +| **普通查询** | 支持 | 支持 | 支持 | N/A | 支持 | 支持 | +| **连续查询** | 支持 | 支持 | 支持 | N/A | 支持 | 支持 | +| **参数绑定** | 不支持 | 不支持 | 不支持 | N/A | 不支持 | 不支持 | +| **订阅功能** | 不支持 | 不支持 | 不支持 | N/A | 不支持 | 不支持 | +| **Schemaless** | 暂不支持 | 暂不支持 | 暂不支持 | N/A | 暂不支持 | 暂不支持 | +| **批量拉取(基于 WebSocket)** | 支持 | 暂不支持 | 暂不支持 | N/A | 暂不支持 | 暂不支持 | +| **DataFrame** | 不支持 | 支持 | 不支持 | N/A | 不支持 | 不支持 | + +:::warning + +- 无论选用何种编程语言的连接器,2.0 及以上版本的 TDengine 推荐数据库应用的每个线程都建立一个独立的连接,或基于线程建立连接池,以避免连接内的“USE statement”状态量在线程之间相互干扰(但连接的查询和写入操作都是线程安全的)。 + +::: + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import InstallOnWindows from "./_linux_install.mdx"; +import InstallOnLinux from "./_windows_install.mdx"; +import VerifyWindows from "./_verify_windows.mdx"; +import VerifyLinux from "./_verify_linux.mdx"; + +## 安装客户端驱动 + +:::info +只有在没有安装 TDengine 服务端软件的系统上使用原生接口连接器才需要安装客户端驱动。 + +::: + +### 安装步骤 + + + + + + + + + + +### 安装验证 + +以上安装和配置完成后,并确认 TDengine 服务已经正常启动运行,此时可以执行 TDengine CLI 工具进行登录。 + + + + + + + + + + diff --git a/docs-cn/14-reference/03-connector/_category_.yml b/docs-cn/14-reference/03-connector/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..abd3f666f3b93697fde59931ffd7b10a0308b6b7 --- /dev/null +++ b/docs-cn/14-reference/03-connector/_category_.yml @@ -0,0 +1 @@ +label: "连接器" \ No newline at end of file diff --git a/docs-cn/14-reference/03-connector/_linux_install.mdx b/docs-cn/14-reference/03-connector/_linux_install.mdx new file mode 100644 index 0000000000000000000000000000000000000000..eb7f68328809fdf75ce11a3bddb324b59bfe8dcb --- /dev/null +++ b/docs-cn/14-reference/03-connector/_linux_install.mdx @@ -0,0 +1,30 @@ +import PkgList from "/components/PkgList"; + +1. 下载客户端安装包 + + + + [所有下载](https://www.taosdata.com/cn/all-downloads/) + +2. 解压缩软件包 + + 将软件包放置在当前用户可读写的任意目录下,然后执行下面的命令:`tar -xzvf TDengine-client-VERSION.tar.gz` + 其中 VERSION 需要替换为实际版本的字符串。 +3. 执行安装脚本 + + 解压软件包之后,会在解压目录下看到以下文件(目录): + - _ install_client.sh_:安装脚本,用于应用驱动程序 + - _ taos.tar.gz_:应用驱动安装包 + - _ driver_:TDengine 应用驱动 driver + - _examples_: 各种编程语言的示例程序(c/C#/go/JDBC/MATLAB/python/R) + 运行 install_client.sh 进行安装。 +4. 配置 taos.cfg + + 编辑 `taos.cfg` 文件(默认路径/etc/taos/taos.cfg),将 `firstEP` 修改为 TDengine 服务器的 End Point,例如:`h1.tdengine.com:6030` + +:::tip + +1. 如本机没有部署 TDengine 服务,仅安装了应用驱动,则 `taos.cfg` 中仅需配置 `firstEP`,无需在本机配置 `FQDN`。 +2. 为防止与服务器端连接时出现“Unable to resolve FQDN”错误,建议确认本机的 `/etc/hosts` 文件已经配置了服务器正确的 FQDN 值,或配置好了 DNS 服务。 + +::: diff --git a/docs-cn/14-reference/03-connector/_preparition.mdx b/docs-cn/14-reference/03-connector/_preparition.mdx new file mode 100644 index 0000000000000000000000000000000000000000..87538ebfd8c60507aec90ee86e427d85979dbc4a --- /dev/null +++ b/docs-cn/14-reference/03-connector/_preparition.mdx @@ -0,0 +1,10 @@ +- 已安装客户端驱动(使用原生连接必须安装,使用 REST 连接无需安装) + +:::info + +由于 TDengine 的客户端驱动使用 C 语言编写,使用原生连接时需要加载系统对应安装在本地的客户端驱动共享库文件,通常包含在 TDengine 安装包。TDengine Linux 服务端安装包附带了 TDengine 客户端,也可以单独安装 [Linux 客户端](/get-started/) 。在 Windows 环境开发时需要安装 TDengine 对应的 [Windows 客户端](https://www.taosdata.com/cn/all-downloads/#TDengine-Windows-Client) 。 + +- libtaos.so: 在 Linux 系统中成功安装 TDengine 后,依赖的 Linux 版客户端驱动 libtaos.so 文件会被自动拷贝至 /usr/lib/libtaos.so,该目录包含在 Linux 自动扫描路径上,无需单独指定。 +- taos.dll: 在 Windows 系统中安装完客户端之后,依赖的 Windows 版客户端驱动 taos.dll 文件会自动拷贝到系统默认搜索路径 C:/Windows/System32 下,同样无需要单独指定。 + +::: diff --git a/docs-cn/14-reference/03-connector/_verify_linux.mdx b/docs-cn/14-reference/03-connector/_verify_linux.mdx new file mode 100644 index 0000000000000000000000000000000000000000..fcb8aae6ae27cdcec58e000c4ab2e8a7ec6d9a5e --- /dev/null +++ b/docs-cn/14-reference/03-connector/_verify_linux.mdx @@ -0,0 +1,14 @@ +在 Linux shell 下直接执行 `taos` 连接到 TDengine 服务,进入到 TDengine CLI 界面,示例如下: + +```text +$ taos +Welcome to the TDengine shell from Linux, Client Version:2.0.5.0 +Copyright (c) 2017 by TAOS Data, Inc. All rights reserved. +taos> show databases; +name | created_time | ntables | vgroups | replica | quorum | days | keep1,keep2,keep(D) | cache(MB)| blocks | minrows | maxrows | wallevel | fsync | comp | precision | status | +========================================================================================================================================================================================================================= +test | 2020-10-14 10:35:48.617 | 10 | 1 | 1 | 1 | 2 | 3650,3650,3650 | 16| 6 | 100 | 4096 | 1 | 3000 | 2 | ms | ready | +log | 2020-10-12 09:08:21.651 | 4 | 1 | 1 | 1 | 10 | 30,30,30 | 1| 3 | 100 | 4096 | 1 | 3000 | 2 | us | ready | +Query OK, 2 row(s) in set (0.001198s) +taos> +``` diff --git a/docs-cn/14-reference/03-connector/_verify_windows.mdx b/docs-cn/14-reference/03-connector/_verify_windows.mdx new file mode 100644 index 0000000000000000000000000000000000000000..87c9fbd024f2c80b88434d47f35d919ed1ea77b2 --- /dev/null +++ b/docs-cn/14-reference/03-connector/_verify_windows.mdx @@ -0,0 +1,14 @@ +在 cmd 下进入到 C:\TDengine 目录下直接执行 `taos.exe`,连接到 TDengine 服务,进入到 TDengine CLI 界面,示例如下: + +```text + C:\TDengine>taos + Welcome to the TDengine shell from Linux, Client Version:2.0.5.0 + Copyright (c) 2017 by TAOS Data, Inc. All rights reserved. + taos> show databases; + name | created_time | ntables | vgroups | replica | quorum | days | keep1,keep2,keep(D) | cache(MB) | blocks | minrows | maxrows | wallevel | fsync | comp | precision | status | + =================================================================================================================================================================================================================================================================== + test | 2020-10-14 10:35:48.617 | 10 | 1 | 1 | 1 | 2 | 3650,3650,3650 | 16 | 6 | 100 | 4096 | 1 | 3000 | 2 | ms | ready | + log | 2020-10-12 09:08:21.651 | 4 | 1 | 1 | 1 | 10 | 30,30,30 | 1 | 3 | 100 | 4096 | 1 | 3000 | 2 | us | ready | + Query OK, 2 row(s) in set (0.045000s) + taos> +``` diff --git a/docs-cn/14-reference/03-connector/_windows_install.mdx b/docs-cn/14-reference/03-connector/_windows_install.mdx new file mode 100644 index 0000000000000000000000000000000000000000..755f96b2d728621de5752ce752e5d249cda0f6d9 --- /dev/null +++ b/docs-cn/14-reference/03-connector/_windows_install.mdx @@ -0,0 +1,31 @@ +import PkgList from "/components/PkgList"; + +1. 下载客户端安装包 + + + + [所有下载](https://www.taosdata.com/cn/all-downloads/) + +2. 执行安装程序,按提示选择默认值,完成安装 +3. 安装路径 + + 默认安装路径为:C:\TDengine,其中包括以下文件(目录): + + - _taos.exe_:TDengine CLI 命令行程序 + - _cfg_ : 配置文件目录 + - _driver_: 应用驱动动态链接库 + - _examples_: 示例程序 bash/C/C#/go/JDBC/Python/Node.js + - _include_: 头文件 + - _log_ : 日志文件 + - _unins000.exe_: 卸载程序 + +4. 配置 taos.cfg + + 编辑 taos.cfg 文件(默认路径 C:\TDengine\cfg\taos.cfg),将 firstEP 修改为 TDengine 服务器的 End Point,例如:`h1.tdengine.com:6030`。 + +:::tip + +1. 如利用 FQDN 连接服务器,必须确认本机网络环境 DNS 已配置好,或在 hosts 文件中添加 FQDN 寻址记录, 如编辑 C:\Windows\system32\drivers\etc\hosts,添加类似如下的记录:`192.168.1.99 h1.taos.com` +2. 卸载:运行 unins000.exe 可卸载 TDengine 应用驱动。 + +::: diff --git a/docs-cn/14-reference/03-connector/cpp.mdx b/docs-cn/14-reference/03-connector/cpp.mdx new file mode 100644 index 0000000000000000000000000000000000000000..aba1d6c717dfec9228f38e89f90cbf1be0021045 --- /dev/null +++ b/docs-cn/14-reference/03-connector/cpp.mdx @@ -0,0 +1,451 @@ +--- +sidebar_position: 1 +sidebar_label: C/C++ +title: C/C++ Connector +--- + +C/C++ 开发人员可以使用 TDengine 的客户端驱动,即 C/C++连接器 (以下都用 TDengine 客户端驱动表示),开发自己的应用来连接 TDengine 集群完成数据存储、查询以及其他功能。TDengine 客户端驱动的 API 类似于 MySQL 的 C API。应用程序使用时,需要包含 TDengine 头文件 _taos.h_,里面列出了提供的 API 的函数原型;应用程序还要链接到所在平台上对应的动态库。 + +```c +#include +``` + +TDengine 服务端或客户端安装后,`taos.h` 位于: + +- Linux:`/usr/local/taos/include` +- Windows:`C:\TDengine\include` + +TDengine 客户端驱动的动态库位于: + +- Linux: `/usr/local/taos/driver/libtaos.so` +- Windows: `C:\TDengine\taos.dll` + +## 支持的平台 + +请参考[支持的平台列表](/reference/connector#支持的平台) + +## 支持的版本 + +TDengine 客户端驱动的版本号与 TDengine 服务端的版本号是一一对应的强对应关系,建议使用与 TDengine 服务端完全相同的客户端驱动。虽然低版本的客户端驱动在前三段版本号一致(即仅第四段版本号不同)的情况下也能够与高版本的服务端相兼容,但这并非推荐用法。强烈不建议使用高版本的客户端驱动访问低版本的服务端。 + +## 安装步骤 + +TDengine 客户端驱动的安装请参考 [安装指南](/reference/connector#安装步骤) + +## 建立连接 + +使用客户端驱动访问 TDengine 集群的基本过程为:建立连接、查询和写入、关闭连接、清除资源。 + +下面为建立连接的示例代码,其中省略了查询和写入部分,展示了如何建立连接、关闭连接以及清除资源。 + +```c + TAOS *taos = taos_connect("localhost:6030", "root", "taosdata", NULL, 0); + if (taos == NULL) { + printf("failed to connect to server, reason:%s\n", "null taos" /*taos_errstr(taos)*/); + exit(1); + } + + /* put your code here for read and write */ + + taos_close(taos); + taos_cleanup(); +``` + +在上面的示例代码中, `taos_connect()` 建立到客户端程序所在主机的 6030 端口的连接,`taos_close()`关闭当前连接,`taos_cleanup()`清除客户端驱动所申请和使用的资源。 + +:::note + +- 如未特别说明,当 API 的返回值是整数时,_0_ 代表成功,其它是代表失败原因的错误码,当返回值是指针时, _NULL_ 表示失败。 +- 所有的错误码以及对应的原因描述在 `taoserror.h` 文件中。 + +::: + +## 示例程序 + +本节展示了使用客户端驱动访问 TDengine 集群的常见访问方式的示例代码。 + +### 同步查询示例 + +
+同步查询 + +```c +{{#include examples/c/demo.c}} +``` + +
+ +### 异步查询示例 + +
+异步查询 + +```c +{{#include examples/c/asyncdemo.c}} +``` + +
+ +### 参数绑定示例 + +
+参数绑定 + +```c +{{#include examples/c/prepare.c}} +``` + +
+ +### 无模式写入示例 + +
+无模式写入 + +```c +{{#include examples/c/schemaless.c}} +``` + +
+ +### 订阅和消费示例 + +
+订阅和消费 + +```c +{{#include examples/c/subscribe.c}} +``` + +
+ +:::info +更多示例代码及下载请见 [GitHub](https://github.com/taosdata/TDengine/tree/develop/examples/c)。 +也可以在安装目录下的 `examples/c` 路径下找到。 该目录下有 makefile,在 Linux 环境下,直接执行 make 就可以编译得到执行文件。 +**提示:**在 ARM 环境下编译时,请将 makefile 中的 `-msse4.2` 去掉,这个选项只有在 x64/x86 硬件平台上才能支持。 + +::: + +## API 参考 + +以下分别介绍 TDengine 客户端驱动的基础 API、同步 API、异步 API、订阅 API 和无模式写入 API。 + +### 基础 API + +基础 API 用于完成创建数据库连接等工作,为其它 API 的执行提供运行时环境。 + +- `void taos_init()` + + 初始化运行环境。如果没有主动调用该 API,那么调用 `taos_connect()` 时驱动将自动调用该 API,故程序一般无需手动调用。 + +- `void taos_cleanup()` + + 清理运行环境,应用退出前应调用。 + +- `int taos_options(TSDB_OPTION option, const void * arg, ...)` + + 设置客户端选项,目前支持区域设置(`TSDB_OPTION_LOCALE`)、字符集设置(`TSDB_OPTION_CHARSET`)、时区设置(`TSDB_OPTION_TIMEZONE`)、配置文件路径设置(`TSDB_OPTION_CONFIGDIR`)。区域设置、字符集、时区默认为操作系统当前设置。 + +- `char *taos_get_client_info()` + + 获取客户端版本信息。 + +- `TAOS *taos_connect(const char *host, const char *user, const char *pass, const char *db, int port)` + + 创建数据库连接,初始化连接上下文。其中需要用户提供的参数包含: + + - host:TDengine 集群中任一节点的 FQDN + - user:用户名 + - pass:密码 + - db: 数据库名字,如果用户没有提供,也可以正常连接,用户可以通过该连接创建新的数据库,如果用户提供了数据库名字,则说明该数据库用户已经创建好,缺省使用该数据库 + - port:taosd 程序监听的端口 + + 返回值为空表示失败。应用程序需要保存返回的参数,以便后续使用。 + + :::info + 同一进程可以根据不同的 host/port 连接多个 TDengine 集群 + + ::: + +- `char *taos_get_server_info(TAOS *taos)` + + 获取服务端版本信息。 + +- `int taos_select_db(TAOS *taos, const char *db)` + + 将当前的缺省数据库设置为 `db`。 + +- `void taos_close(TAOS *taos)` + + 关闭连接,其中`taos`是 `taos_connect()` 返回的句柄。 + +### 同步查询 API + +本小节介绍 API 均属于同步接口。应用调用后,会阻塞等待响应,直到获得返回结果或错误信息。 + +- `TAOS_RES* taos_query(TAOS *taos, const char *sql)` + + 执行 SQL 语句,可以是 DQL、DML 或 DDL 语句。 其中的 `taos` 参数是通过 `taos_connect()` 获得的句柄。不能通过返回值是否是 `NULL` 来判断执行结果是否失败,而是需要用 `taos_errno()` 函数解析结果集中的错误代码来进行判断。 + +- `int taos_result_precision(TAOS_RES *res)` + + 返回结果集时间戳字段的精度,`0` 代表毫秒,`1` 代表微秒,`2` 代表纳秒。 + +- `TAOS_ROW taos_fetch_row(TAOS_RES *res)` + + 按行获取查询结果集中的数据。 + +- `int taos_fetch_block(TAOS_RES *res, TAOS_ROW *rows)` + + 批量获取查询结果集中的数据,返回值为获取到的数据的行数。 + +- `int taos_num_fields(TAOS_RES *res)` 和 `int taos_field_count(TAOS_RES *res)` + + 这两个 API 等价,用于获取查询结果集中的列数。 + +- `int* taos_fetch_lengths(TAOS_RES *res)` + + 获取结果集中每个字段的长度。返回值是一个数组,其长度为结果集的列数。 + +- `int taos_affected_rows(TAOS_RES *res)` + + 获取被所执行的 SQL 语句影响的行数。 + +- `TAOS_FIELD *taos_fetch_fields(TAOS_RES *res)` + + 获取查询结果集每列数据的属性(列的名称、列的数据类型、列的长度),与 `taos_num_fileds()` 配合使用,可用来解析 `taos_fetch_row()` 返回的一个元组(一行)的数据。 `TAOS_FIELD` 的结构如下: + +```c +typedef struct taosField { + char name[65]; // column name + uint8_t type; // data type + int16_t bytes; // length, in bytes +} TAOS_FIELD; +``` + +- `void taos_stop_query(TAOS_RES *res)` + + 停止当前查询的执行。 + +- `void taos_free_result(TAOS_RES *res)` + + 释放查询结果集以及相关的资源。查询完成后,务必调用该 API 释放资源,否则可能导致应用内存泄露。但也需注意,释放资源后,如果再调用 `taos_consume()` 等获取查询结果的函数,将导致应用崩溃。 + +- `char *taos_errstr(TAOS_RES *res)` + + 获取最近一次 API 调用失败的原因,返回值为字符串标识的错误提示信息。 + +- `int taos_errno(TAOS_RES *res)` + + 获取最近一次 API 调用失败的原因,返回值为错误代码。 + +:::note +2.0 及以上版本 TDengine 推荐数据库应用的每个线程都建立一个独立的连接,或基于线程建立连接池。而不推荐在应用中将该连接 (TAOS\*) 结构体传递到不同的线程共享使用。基于 TAOS 结构体发出的查询、写入等操作具有多线程安全性,但 “USE statement” 等状态量有可能在线程之间相互干扰。此外,C 语言的连接器可以按照需求动态建立面向数据库的新连接(该过程对用户不可见),同时建议只有在程序最后退出的时候才调用 `taos_close()` 关闭连接。 + +::: + +### 异步查询 API + +TDengine 还提供性能更高的异步 API 处理数据插入、查询操作。在软硬件环境相同的情况下,异步 API 处理数据插入的速度比同步 API 快 2 ~ 4 倍。异步 API 采用非阻塞式的调用方式,在系统真正完成某个具体数据库操作前,立即返回。调用的线程可以去处理其他工作,从而可以提升整个应用的性能。异步 API 在网络延迟严重的情况下,优势尤为突出。 + +异步 API 都需要应用提供相应的回调函数,回调函数参数设置如下:前两个参数都是一致的,第三个参数依不同的 API 而定。第一个参数 param 是应用调用异步 API 时提供给系统的,用于回调时,应用能够找回具体操作的上下文,依具体实现而定。第二个参数是 SQL 操作的结果集,如果为空,比如 insert 操作,表示没有记录返回,如果不为空,比如 select 操作,表示有记录返回。 + +异步 API 对于使用者的要求相对较高,用户可根据具体应用场景选择性使用。下面是两个重要的异步 API: + +- `void taos_query_a(TAOS *taos, const char *sql, void (*fp)(void *param, TAOS_RES *, int code), void *param);` + + 异步执行 SQL 语句。 + + - taos:调用 `taos_connect()` 返回的数据库连接 + - sql:需要执行的 SQL 语句 + - fp:用户定义的回调函数,其第三个参数 `code` 用于指示操作是否成功,`0` 表示成功,负数表示失败(调用 `taos_errstr()` 可获取失败原因)。应用在定义回调函数的时候,主要处理第二个参数 `TAOS_RES *`,该参数是查询返回的结果集 + - param:应用提供一个用于回调的参数 + +- `void taos_fetch_rows_a(TAOS_RES *res, void (*fp)(void *param, TAOS_RES *, int numOfRows), void *param);` + + 批量获取异步查询的结果集,只能与 `taos_query_a()` 配合使用。其中: + + - res:`taos_query_a()` 回调时返回的结果集 + - fp:回调函数。其参数 `param` 是用户可定义的传递给回调函数的参数结构体;`numOfRows` 是获取到的数据的行数(不是整个查询结果集的函数)。 在回调函数中,应用可以通过调用 `taos_fetch_row()` 前向迭代获取批量记录中每一行记录。读完一块内的所有记录后,应用需要在回调函数中继续调用 `taos_fetch_rows_a()` 获取下一批记录进行处理,直到返回的记录数 `numOfRows` 为零(结果返回完成)或记录数为负值(查询出错)。 + +TDengine 的异步 API 均采用非阻塞调用模式。应用程序可以用多线程同时打开多张表,并可以同时对每张打开的表进行查询或者插入操作。需要指出的是,**客户端应用必须确保对同一张表的操作完全串行化**,即对同一个表的插入或查询操作未完成时(未返回时),不能够执行第二个插入或查询操作。 + +### 参数绑定 API + +除了直接调用 `taos_query()` 进行查询,TDengine 也提供了支持参数绑定的 Prepare API,风格与 MySQL 类似,目前也仅支持用问号 `?` 来代表待绑定的参数。 + +从 2.1.1.0 和 2.1.2.0 版本开始,TDengine 大幅改进了参数绑定接口对数据写入(INSERT)场景的支持。这样在通过参数绑定接口写入数据时,就避免了 SQL 语法解析的资源消耗,从而在绝大多数情况下显著提升写入性能。此时的典型操作步骤如下: + +1. 调用 `taos_stmt_init()` 创建参数绑定对象; +2. 调用 `taos_stmt_prepare()` 解析 INSERT 语句; +3. 如果 INSERT 语句中预留了表名但没有预留 TAGS,那么调用 `taos_stmt_set_tbname()` 来设置表名; +4. 如果 INSERT 语句中既预留了表名又预留了 TAGS(例如 INSERT 语句采取的是自动建表的方式),那么调用 `taos_stmt_set_tbname_tags()` 来设置表名和 TAGS 的值; +5. 调用 `taos_stmt_bind_param_batch()` 以多列的方式设置 VALUES 的值,或者调用 `taos_stmt_bind_param()` 以单行的方式设置 VALUES 的值; +6. 调用 `taos_stmt_add_batch()` 把当前绑定的参数加入批处理; +7. 可以重复第 3 ~ 6 步,为批处理加入更多的数据行; +8. 调用 `taos_stmt_execute()` 执行已经准备好的批处理指令; +9. 执行完毕,调用 `taos_stmt_close()` 释放所有资源。 + +说明:如果 `taos_stmt_execute()` 执行成功,假如不需要改变 SQL 语句的话,那么是可以复用 `taos_stmt_prepare()` 的解析结果,直接进行第 3 ~ 6 步绑定新数据的。但如果执行出错,那么并不建议继续在当前的环境上下文下继续工作,而是建议释放资源,然后从 `taos_stmt_init()` 步骤重新开始。 + +接口相关的具体函数如下(也可以参考 [prepare.c](https://github.com/taosdata/TDengine/blob/develop/examples/c/prepare.c) 文件中使用对应函数的方式): + +- `TAOS_STMT* taos_stmt_init(TAOS *taos)` + + 创建一个 TAOS_STMT 对象用于后续调用。 + +- `int taos_stmt_prepare(TAOS_STMT *stmt, const char *sql, unsigned long length)` + + 解析一条 SQL 语句,将解析结果和参数信息绑定到 stmt 上,如果参数 length 大于 0,将使用此参数作为 SQL 语句的长度,如等于 0,将自动判断 SQL 语句的长度。 + +- `int taos_stmt_bind_param(TAOS_STMT *stmt, TAOS_BIND *bind)` + + 不如 `taos_stmt_bind_param_batch()` 效率高,但可以支持非 INSERT 类型的 SQL 语句。 + 进行参数绑定,bind 指向一个数组(代表所要绑定的一行数据),需保证此数组中的元素数量和顺序与 SQL 语句中的参数完全一致。TAOS_BIND 的使用方法与 MySQL 中的 MYSQL_BIND 类似,具体定义如下: + + ```c + typedef struct TAOS_BIND { + int buffer_type; + void * buffer; + uintptr_t buffer_length; // not in use + uintptr_t * length; + int * is_null; + int is_unsigned; // not in use + int * error; // not in use + } TAOS_BIND; + ``` + +- `int taos_stmt_set_tbname(TAOS_STMT* stmt, const char* name)` + + (2.1.1.0 版本新增,仅支持用于替换 INSERT 语句中的参数值) + 当 SQL 语句中的表名使用了 `?` 占位时,可以使用此函数绑定一个具体的表名。 + +- `int taos_stmt_set_tbname_tags(TAOS_STMT* stmt, const char* name, TAOS_BIND* tags)` + + (2.1.2.0 版本新增,仅支持用于替换 INSERT 语句中的参数值) + 当 SQL 语句中的表名和 TAGS 都使用了 `?` 占位时,可以使用此函数绑定具体的表名和具体的 TAGS 取值。最典型的使用场景是使用了自动建表功能的 INSERT 语句(目前版本不支持指定具体的 TAGS 列)。TAGS 参数中的列数量需要与 SQL 语句中要求的 TAGS 数量完全一致。 + +- `int taos_stmt_bind_param_batch(TAOS_STMT* stmt, TAOS_MULTI_BIND* bind)` + + (2.1.1.0 版本新增,仅支持用于替换 INSERT 语句中的参数值) + 以多列的方式传递待绑定的数据,需要保证这里传递的数据列的顺序、列的数量与 SQL 语句中的 VALUES 参数完全一致。TAOS_MULTI_BIND 的具体定义如下: + + ```c + typedef struct TAOS_MULTI_BIND { + int buffer_type; + void * buffer; + uintptr_t buffer_length; + uintptr_t * length; + char * is_null; + int num; // the number of columns + } TAOS_MULTI_BIND; + ``` + +- `int taos_stmt_add_batch(TAOS_STMT *stmt)` + + 将当前绑定的参数加入批处理中,调用此函数后,可以再次调用 `taos_stmt_bind_param()` 或 `taos_stmt_bind_param_batch()` 绑定新的参数。需要注意,此函数仅支持 INSERT/IMPORT 语句,如果是 SELECT 等其他 SQL 语句,将返回错误。 + +- `int taos_stmt_execute(TAOS_STMT *stmt)` + + 执行准备好的语句。目前,一条语句只能执行一次。 + +- `TAOS_RES* taos_stmt_use_result(TAOS_STMT *stmt)` + + 获取语句的结果集。结果集的使用方式与非参数化调用时一致,使用完成后,应对此结果集调用 `taos_free_result()` 以释放资源。 + +- `int taos_stmt_close(TAOS_STMT *stmt)` + + 执行完毕,释放所有资源。 + +- `char * taos_stmt_errstr(TAOS_STMT *stmt)` + + (2.1.3.0 版本新增) + 用于在其他 STMT API 返回错误(返回错误码或空指针)时获取错误信息。 + +### 无模式(schemaless)写入 API + +除了使用 SQL 方式或者使用参数绑定 API 写入数据外,还可以使用 Schemaless 的方式完成写入。Schemaless 可以免于预先创建超级表/数据子表的数据结构,而是可以直接写入数据,TDengine 系统会根据写入的数据内容自动创建和维护所需要的表结构。Schemaless 的使用方式详见 [Schemaless 写入](/reference/schemaless/) 章节,这里介绍与之配套使用的 C/C++ API。 + +- `TAOS_RES* taos_schemaless_insert(TAOS* taos, const char* lines[], int numLines, int protocol, int precision)` + + **功能说明** + 该接口将行协议的文本数据写入到 TDengine 中。 + + **参数说明** + taos: 数据库连接,通过 `taos_connect()` 函数建立的数据库连接。 + lines:文本数据。满足解析格式要求的无模式文本字符串。 + numLines:文本数据的行数,不能为 0 。 + protocol: 行协议类型,用于标识文本数据格式。 + precision:文本数据中的时间戳精度字符串。 + + **返回值** + TAOS_RES 结构体,应用可以通过使用 `taos_errstr()` 获得错误信息,也可以使用 `taos_errno()` 获得错误码。 + 在某些情况下,返回的 TAOS_RES 为 `NULL`,此时仍然可以调用 `taos_errno()` 来安全地获得错误码信息。 + 返回的 TAOS_RES 需要调用方来负责释放,否则会出现内存泄漏。 + + **说明** + 协议类型是枚举类型,包含以下三种格式: + + - TSDB_SML_LINE_PROTOCOL:InfluxDB 行协议(Line Protocol) + - TSDB_SML_TELNET_PROTOCOL: OpenTSDB Telnet 文本行协议 + - TSDB_SML_JSON_PROTOCOL: OpenTSDB Json 协议格式 + + 时间戳分辨率的定义,定义在 `taos.h` 文件中,具体内容如下: + + - TSDB_SML_TIMESTAMP_NOT_CONFIGURED = 0, + - TSDB_SML_TIMESTAMP_HOURS, + - TSDB_SML_TIMESTAMP_MINUTES, + - TSDB_SML_TIMESTAMP_SECONDS, + - TSDB_SML_TIMESTAMP_MILLI_SECONDS, + - TSDB_SML_TIMESTAMP_MICRO_SECONDS, + - TSDB_SML_TIMESTAMP_NANO_SECONDS + + 需要注意的是,时间戳分辨率参数只在协议类型为 `SML_LINE_PROTOCOL` 的时候生效。 + 对于 OpenTSDB 的文本协议,时间戳的解析遵循其官方解析规则 — 按照时间戳包含的字符的数量来确认时间精度。 + + **支持版本** + 该功能接口从 2.3.0.0 版本开始支持。 + +### 订阅和消费 API + +订阅 API 目前支持订阅一张或多张表,并通过定期轮询的方式不断获取写入表中的最新数据。 + +- `TAOS_SUB *taos_subscribe(TAOS* taos, int restart, const char* topic, const char *sql, TAOS_SUBSCRIBE_CALLBACK fp, void *param, int interval)` + + 该函数负责启动订阅服务,成功时返回订阅对象,失败时返回 `NULL`,其参数为: + + - taos:已经建立好的数据库连接 + - restart:如果订阅已经存在,是重新开始,还是继续之前的订阅 + - topic:订阅的主题(即名称),此参数是订阅的唯一标识 + - sql:订阅的查询语句,此语句只能是 `select` 语句,只应查询原始数据,只能按时间正序查询数据 + - fp:收到查询结果时的回调函数(稍后介绍函数原型),只在异步调用时使用,同步调用时此参数应该传 `NULL` + - param:调用回调函数时的附加参数,系统 API 将其原样传递到回调函数,不进行任何处理 + - interval:轮询周期,单位为毫秒。异步调用时,将根据此参数周期性的调用回调函数,为避免对系统性能造成影响,不建议将此参数设置的过小;同步调用时,如两次调用 `taos_consume()` 的间隔小于此周期,API 将会阻塞,直到时间间隔超过此周期。 + +- `typedef void (*TAOS_SUBSCRIBE_CALLBACK)(TAOS_SUB* tsub, TAOS_RES *res, void* param, int code)` + + 异步模式下,回调函数的原型,其参数为: + + - tsub:订阅对象 + - res:查询结果集,注意结果集中可能没有记录 + - param:调用 `taos_subscribe()` 时客户程序提供的附加参数 + - code:错误码 + + :::note + 在这个回调函数里不可以做耗时过长的处理,尤其是对于返回的结果集中数据较多的情况,否则有可能导致客户端阻塞等异常状态。如果必须进行复杂计算,则建议在另外的线程中进行处理。 + + ::: + +- `TAOS_RES *taos_consume(TAOS_SUB *tsub)` + + 同步模式下,该函数用来获取订阅的结果。 用户应用程序将其置于一个循环之中。 如两次调用 `taos_consume()` 的间隔小于订阅的轮询周期,API 将会阻塞,直到时间间隔超过此周期。如果数据库有新记录到达,该 API 将返回该最新的记录,否则返回一个没有记录的空结果集。 如果返回值为 `NULL`,说明系统出错。 异步模式下,用户程序不应调用此 API。 + + :::note + 在调用 `taos_consume()` 之后,用户应用应确保尽快调用 `taos_fetch_row()` 或 `taos_fetch_block()` 来处理订阅结果,否则服务端会持续缓存查询结果数据等待客户端读取,极端情况下会导致服务端内存消耗殆尽,影响服务稳定性。 + + ::: + +- `void taos_unsubscribe(TAOS_SUB *tsub, int keepProgress)` + + 取消订阅。 如参数 `keepProgress` 不为 0,API 会保留订阅的进度信息,后续调用 `taos_subscribe()` 时可以基于此进度继续;否则将删除进度信息,后续只能重新开始读取数据。 + diff --git a/docs-cn/14-reference/03-connector/csharp.mdx b/docs-cn/14-reference/03-connector/csharp.mdx new file mode 100644 index 0000000000000000000000000000000000000000..1e23df9286bf0cb3bf1db95e334301c04d01ad04 --- /dev/null +++ b/docs-cn/14-reference/03-connector/csharp.mdx @@ -0,0 +1,189 @@ +--- +toc_max_heading_level: 4 +sidebar_position: 7 +sidebar_label: C# +title: C# Connector +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +import Preparition from "./_preparition.mdx" +import CSInsert from "../../07-develop/03-insert-data/_cs_sql.mdx" +import CSInfluxLine from "../../07-develop/03-insert-data/_cs_line.mdx" +import CSOpenTSDBTelnet from "../../07-develop/03-insert-data/_cs_opts_telnet.mdx" +import CSOpenTSDBJson from "../../07-develop/03-insert-data/_cs_opts_json.mdx" +import CSQuery from "../../07-develop/04-query-data/_cs.mdx" +import CSAsyncQuery from "../../07-develop/04-query-data/_cs_async.mdx" + +`TDengine.Connector` 是 TDengine 提供的 C# 语言连接器。C# 开发人员可以通过它开发存取 TDengine 集群数据的 C# 应用软件。 + +`TDengine.Connector` 连接器支持通过 TDengine 客户端驱动(taosc)建立与 TDengine 运行实例的连接,提供数据写入、查询、订阅、schemaless 数据写入、参数绑定接口数据写入等功能 `TDengine.Connector` 目前暂未提供 REST 连接方式,用户可以参考 [REST API](/reference/rest-api/) 文档自行编写。 + +本文介绍如何在 Linux 或 Windows 环境中安装 `TDengine.Connector`,并通过 `TDengine.Connector` 连接 TDengine 集群,进行数据写入、查询等基本操作。 + +`TDengine.Connector` 的源码托管在 [GitHub](https://github.com/taosdata/taos-connector-dotnet)。 + +## 支持的平台 + +支持的平台和 TDengine 客户端驱动支持的平台一致。 + +## 版本支持 + +请参考[版本支持列表](/reference/connector#版本支持) + +## 支持的功能特性 + +1. 连接管理 +2. 普通查询 +3. 连续查询 +4. 参数绑定 +5. 订阅功能 +6. Schemaless + +## 安装步骤 + +### 安装前准备 + +* 安装 [.NET SDK](https://dotnet.microsoft.com/download) +* [Nuget 客户端](https://docs.microsoft.com/en-us/nuget/install-nuget-client-tools) (可选安装) +* 安装 TDengine 客户端驱动,具体步骤请参考[安装客户端驱动](/reference/connector#安装客户端驱动) + +### 使用 dotnet CLI 安装 + + + + +可以在当前 .NET 项目的路径下,通过 dotnet 命令引用 Nuget 中发布的 `TDengine.Connector` 到当前项目。 + +``` bash +dotnet add package TDengine.Connector +``` + + + + +可以下载 TDengine 的源码,直接引用最新版本的 TDengine.Connector 库 + +```bash +git clone https://github.com/taosdata/TDengine.git +cd TDengine/src/connector/C#/src/ +cp -r TDengineDriver/ myProject + +cd myProject +dotnet add TDengineDriver/TDengineDriver.csproj +``` + + + +## 建立连接 + +``` C# +using TDengineDriver; + +namespace TDengineExample +{ + + internal class EstablishConnection + { + static void Main(String[] args) + { + string host = "localhost"; + short port = 6030; + string username = "root"; + string password = "taosdata"; + string dbname = ""; + + var conn = TDengine.Connect(host, username, password, dbname, port); + if (conn == IntPtr.Zero) + { + Console.WriteLine("Connect to TDengine failed"); + } + else + { + Console.WriteLine("Connect to TDengine success"); + } + TDengine.Close(conn); + TDengine.Cleanup(); + } + } +} + +``` + +## 使用示例 + +### 写入数据 + +#### SQL 写入 + + + +#### InfluxDB 行协议写入 + + + +#### OpenTSDB Telnet 行协议写入 + + + +#### OpenTSDB JSON 行协议写入 + + + +### 查询数据 + +#### 同步查询 + + + +#### 异步查询 + + + +### 更多示例程序 + +|示例程序 | 示例程序描述 | +|--------------------------------------------------------------------------------------------------------------------|--------------------------------------------| +| [C#checker](https://github.com/taosdata/TDengine/tree/develop/examples/C%23/C%23checker) | 使用 TDengine.Connector 可以通过 help 命令中提供的参数,测试C# Driver的同步写入和查询 | +| [TDengineTest](https://github.com/taosdata/TDengine/tree/develop/examples/C%23/TDengineTest) | 使用 TDengine.Connector 实现的简单写入和查询的示例 | +| [insertCn](https://github.com/taosdata/TDengine/tree/develop/examples/C%23/insertCn) | 使用 TDengine.Connector 实现的写入和查询中文字符的示例 | +| [jsonTag](https://github.com/taosdata/TDengine/tree/develop/examples/C%23/jsonTag) | 使用 TDengine.Connector 实现的写入和查询 json tag 类型数据的示例 | +| [stmt](https://github.com/taosdata/TDengine/tree/develop/examples/C%23/stmt) | 使用 TDengine.Connector 实现的参数绑定的示例 | +| [schemaless](https://github.com/taosdata/TDengine/tree/develop/examples/C%23/schemaless) | 使用 TDengine.Connector 实现的使用 schemaless 写入的示例 | +| [benchmark](https://github.com/taosdata/TDengine/tree/develop/examples/C%23/taosdemo) | 使用 TDengine.Connector 实现的简易 Benchmark | +| [async query](https://github.com/taosdata/taos-connector-dotnet/blob/develop/examples/QueryAsyncSample.cs) | 使用 TDengine.Connector 实现的异步查询的示例 | +| [subscribe](https://github.com/taosdata/taos-connector-dotnet/blob/develop/examples/SubscribeSample.cs) | 使用 TDengine.Connector 实现的订阅数据的示例 | + +## 重要更新记录 + +| TDengine.Connector | 说明 | +|--------------------|--------------------------------| +| 1.0.6 | 修复 schemaless 在 1.0.4 和 1.0.5 中失效 bug。 | +| 1.0.5 | 修复 Windows 同步查询中文报错 bug。 | +| 1.0.4 | 新增异步查询,订阅等功能。修复绑定参数 bug。 | +| 1.0.3 | 新增参数绑定、schemaless、 json tag等功能。 | +| 1.0.2 | 新增连接管理、同步查询、错误信息等功能。 | + +## 其他说明 + +### 第三方驱动 + +`Maikebing.Data.Taos` 是一个 TDengine 的 ADO.NET 连接器,支持 Linux,Windows 平台。该连接器由社区贡献者`麦壳饼@@maikebing` 提供,具体请参考: + +* 接口下载: +* 用法说明: + +## 常见问题 + +1. "Unable to establish connection","Unable to resolve FQDN" + + 一般是因为 FQDN 配置不正确。可以参考[如何彻底搞懂 TDengine 的 FQDN](https://www.taosdata.com/blog/2021/07/29/2741.html)解决。 + +2. Unhandled exception. System.DllNotFoundException: Unable to load DLL 'taos' or one of its dependencies: 找不到指定的模块。 + + 一般是因为程序没有找到依赖的客户端驱动。解决方法为:Windows 下可以将 `C:\TDengine\driver\taos.dll` 拷贝到 `C:\Windows\System32\ ` 目录下,Linux 下建立如下软链接 `ln -s /usr/local/taos/driver/libtaos.so.x.x.x.x /usr/lib/libtaos.so` 即可。 + +## API 参考 + +[API 参考](https://docs.taosdata.com/api/connector-csharp/html/860d2ac1-dd52-39c9-e460-0829c4e5a40b.htm) diff --git a/docs-cn/14-reference/03-connector/go.mdx b/docs-cn/14-reference/03-connector/go.mdx new file mode 100644 index 0000000000000000000000000000000000000000..88b09aa5d0b0161973e3e7eabb4cf04357c134f3 --- /dev/null +++ b/docs-cn/14-reference/03-connector/go.mdx @@ -0,0 +1,411 @@ +--- +toc_max_heading_level: 4 +sidebar_position: 4 +sidebar_label: Go +title: TDengine Go Connector +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +import Preparition from "./_preparition.mdx" +import GoInsert from "../../07-develop/03-insert-data/_go_sql.mdx" +import GoInfluxLine from "../../07-develop/03-insert-data/_go_line.mdx" +import GoOpenTSDBTelnet from "../../07-develop/03-insert-data/_go_opts_telnet.mdx" +import GoOpenTSDBJson from "../../07-develop/03-insert-data/_go_opts_json.mdx" +import GoQuery from "../../07-develop/04-query-data/_go.mdx" + +`driver-go` 是 TDengine 的官方 Go 语言连接器,实现了 Go 语言[ database/sql ](https://golang.org/pkg/database/sql/) 包的接口。Go 开发人员可以通过它开发存取 TDengine 集群数据的应用软件。 + +`driver-go` 提供两种建立连接的方式。一种是**原生连接**,它通过 TDengine 客户端驱动程序(taosc)原生连接 TDengine 运行实例,支持数据写入、查询、订阅、schemaless 接口和参数绑定接口等功能。另外一种是 **REST 连接**,它通过 taosAdapter 提供的 REST 接口连接 TDengine 运行实例。REST 连接实现的功能特性集合和原生连接有少量不同。 + +本文介绍如何安装 `driver-go`,并通过 `driver-go` 连接 TDengine 集群、进行数据查询、数据写入等基本操作。 + +`driver-go` 的源码托管在 [GitHub](https://github.com/taosdata/driver-go)。 + +## 支持的平台 + +原生连接支持的平台和 TDengine 客户端驱动支持的平台一致。 +REST 连接支持所有能运行 Go 的平台。 + +## 版本支持 + +请参考[版本支持列表](/reference/connector#版本支持) + +## 支持的功能特性 + +### 原生连接 + +“原生连接”指连接器通过 TDengine 客户端驱动(taosc)直接与 TDengine 运行实例建立的连接。支持的功能特性有: + +* 普通查询 +* 连续查询 +* 订阅 +* schemaless 接口 +* 参数绑定接口 + +### REST 连接 + +"REST 连接"指连接器通过 taosAdapter 组件提供的 REST API 与 TDengine 运行实例建立的连接。支持的功能特性有: + +* 普通查询 +* 连续查询 + +## 安装步骤 + +### 安装前准备 + +* 安装 Go 开发环境(Go 1.14 及以上,GCC 4.8.5 及以上) +* 如果使用原生连接器,请安装 TDengine 客户端驱动,具体步骤请参考[安装客户端驱动](/reference/connector#安装客户端驱动) + +配置好环境变量,检查命令: + +* ```go env``` +* ```gcc -v``` + +### 使用 go get 安装 + +`go get -u github.com/taosdata/driver-go/v2@develop` + +### 使用 go mod 管理 + +1. 使用 `go mod` 命令初始化项目: + + ```text + go mod init taos-demo + ``` + +2. 引入 taosSql : + + ```go + import ( + "database/sql" + _ "github.com/taosdata/driver-go/v2/taosSql" + ) + ``` + +3. 使用 `go mod tidy` 更新依赖包: + + ```text + go mod tidy + ``` + +4. 使用 `go run taos-demo` 运行程序或使用 `go build` 命令编译出二进制文件。 + + ```text + go run taos-demo + go build + ``` + +## 建立连接 + +### 数据源名称(DSN) + +数据源名称具有通用格式,例如 [PEAR DB](http://pear.php.net/manual/en/package.database.db.intro-dsn.php),但没有类型前缀(方括号表示可选): + +``` text +[username[:password]@][protocol[(address)]]/[dbname][?param1=value1&...¶mN=valueN] +``` + +完整形式的 DSN: + +```text +username:password@protocol(address)/dbname?param=value +``` +### 使用连接器进行连接 + + + + +_taosSql_ 通过 cgo 实现了 Go 的 `database/sql/driver` 接口。只需要引入驱动就可以使用 [`database/sql`](https://golang.org/pkg/database/sql/) 的接口。 + +使用 `taosSql` 作为 `driverName` 并且使用一个正确的 [DSN](#DSN) 作为 `dataSourceName`,DSN 支持的参数: + +* configPath 指定 taos.cfg 目录 + +示例: + +```go +package main + +import ( + "database/sql" + "fmt" + + _ "github.com/taosdata/driver-go/v2/taosSql" +) + +func main() { + var taosUri = "root:taosdata@tcp(localhost:6030)/" + taos, err := sql.Open("taosSql", taosUri) + if err != nil { + fmt.Println("failed to connect TDengine, err:", err) + return + } +} +``` + + + + +_taosRestful_ 通过 `http client` 实现了 Go 的 `database/sql/driver` 接口。只需要引入驱动就可以使用[`database/sql`](https://golang.org/pkg/database/sql/)的接口。 + +使用 `taosRestful` 作为 `driverName` 并且使用一个正确的 [DSN](#DSN) 作为 `dataSourceName`,DSN 支持的参数: + +* `disableCompression` 是否接受压缩数据,默认为 true 不接受压缩数据,如果传输数据使用 gzip 压缩设置为 false。 +* `readBufferSize` 读取数据的缓存区大小默认为 4K(4096),当查询结果数据量多时可以适当调大该值。 + +示例: + +```go +package main + +import ( + "database/sql" + "fmt" + + _ "github.com/taosdata/driver-go/v2/taosRestful" +) + +func main() { + var taosUri = "root:taosdata@http(localhost:6041)/" + taos, err := sql.Open("taosRestful", taosUri) + if err != nil { + fmt.Println("failed to connect TDengine, err:", err) + return + } +} +``` + + + +## 使用示例 + +### 写入数据 + +#### SQL 写入 + + + +#### InfluxDB 行协议写入 + + + +#### OpenTSDB Telnet 行协议写入 + + + +#### OpenTSDB JSON 行协议写入 + + + +### 查询数据 + + + +### 更多示例程序 + +* [示例程序](https://github.com/taosdata/TDengine/tree/develop/examples/go) +* [视频教程](https://www.taosdata.com/blog/2020/11/11/1951.html)。 + +## 使用限制 + +由于 REST 接口无状态所以 `use db` 语法不会生效,需要将 db 名称放到 SQL 语句中,如:`create table if not exists tb1 (ts timestamp, a int)`改为`create table if not exists test.tb1 (ts timestamp, a int)`否则将报错`[0x217] Database not specified or available`。 + +也可以将 db 名称放到 DSN 中,将 `root:taosdata@http(localhost:6041)/` 改为 `root:taosdata@http(localhost:6041)/test`,此方法在 TDengine 2.4.0.5 版本的 taosAdapter 开始支持。当指定的 db 不存在时执行 `create database` 语句不会报错,而执行针对该 db 的其他查询或写入操作会报错。 + +完整示例如下: + +```go +package main + +import ( + "database/sql" + "fmt" + "time" + + _ "github.com/taosdata/driver-go/v2/taosRestful" +) + +func main() { + var taosDSN = "root:taosdata@http(localhost:6041)/test" + taos, err := sql.Open("taosRestful", taosDSN) + if err != nil { + fmt.Println("failed to connect TDengine, err:", err) + return + } + defer taos.Close() + taos.Exec("create database if not exists test") + taos.Exec("create table if not exists tb1 (ts timestamp, a int)") + _, err = taos.Exec("insert into tb1 values(now, 0)(now+1s,1)(now+2s,2)(now+3s,3)") + if err != nil { + fmt.Println("failed to insert, err:", err) + return + } + rows, err := taos.Query("select * from tb1") + if err != nil { + fmt.Println("failed to select from table, err:", err) + return + } + + defer rows.Close() + for rows.Next() { + var r struct { + ts time.Time + a int + } + err := rows.Scan(&r.ts, &r.a) + if err != nil { + fmt.Println("scan error:\n", err) + return + } + fmt.Println(r.ts, r.a) + } +} +``` + +## 常见问题 + +1. 无法找到包 `github.com/taosdata/driver-go/v2/taosRestful` + + 将 `go.mod` 中 require 块对`github.com/taosdata/driver-go/v2`的引用改为`github.com/taosdata/driver-go/v2 develop`,之后执行 `go mod tidy`。 + +2. database/sql 中 stmt(参数绑定)相关接口崩溃 + + REST 不支持参数绑定相关接口,建议使用`db.Exec`和`db.Query`。 + +3. 使用 `use db` 语句后执行其他语句报错 `[0x217] Database not specified or available` + + 在 REST 接口中 SQL 语句的执行无上下文关联,使用 `use db` 语句不会生效,解决办法见上方使用限制章节。 + +4. 使用 taosSql 不报错使用 taosRestful 报错 `[0x217] Database not specified or available` + + 因为 REST 接口无状态,使用 `use db` 语句不会生效,解决办法见上方使用限制章节。 + +5. 升级 `github.com/taosdata/driver-go/v2/taosRestful` + + 将 `go.mod` 文件中对 `github.com/taosdata/driver-go/v2` 的引用改为 `github.com/taosdata/driver-go/v2 develop`,之后执行 `go mod tidy`。 + +6. `readBufferSize` 参数调大后无明显效果 + + `readBufferSize` 调大后会减少获取结果时 `syscall` 的调用。如果查询结果的数据量不大,修改该参数不会带来明显提升,如果该参数修改过大,瓶颈会在解析 JSON 数据。如果需要优化查询速度,需要根据实际情况调整该值来达到查询效果最优。 + +7. `disableCompression` 参数设置为 `false` 时查询效率降低 + + 当 `disableCompression` 参数设置为 `false` 时查询结果会使用 `gzip` 压缩后传输,拿到数据后要先进行 `gzip` 解压。 + +8. `go get` 命令无法获取包,或者获取包超时 + + 设置 Go 代理 `go env -w GOPROXY=https://goproxy.cn,direct`。 + +## 常用 API + +### database/sql API + +* `sql.Open(DRIVER_NAME string, dataSourceName string) *DB` + + 该 API 用来打开 DB,返回一个类型为 \*DB 的对象。 + +:::info +该 API 成功创建的时候,并没有做权限等检查,只有在真正执行 Query 或者 Exec 的时候才能真正的去创建连接,并同时检查 user/password/host/port 是不是合法。 +::: + +* `func (db *DB) Exec(query string, args ...interface{}) (Result, error)` + + `sql.Open` 内置的方法,用来执行非查询相关 SQL。 + +* `func (db *DB) Query(query string, args ...interface{}) (*Rows, error)` + + `sql.Open` 内置的方法,用来执行查询语句。 + +### 高级功能(af)API + +`af` 包封装了连接管理、订阅、schemaless、参数绑定等 TDengine 高级功能。 + +#### 连接管理 + +* `af.Open(host, user, pass, db string, port int) (*Connector, error)` + + 该 API 通过 cgo 创建与 taosd 的连接。 + +* `func (conn *Connector) Close() error` + + 关闭与 taosd 的连接。 + +#### 订阅 + +* `func (conn *Connector) Subscribe(restart bool, topic string, sql string, interval time.Duration) (Subscriber, error)` + + 订阅数据。 + +* `func (s *taosSubscriber) Consume() (driver.Rows, error)` + + 消费订阅数据,返回 `database/sql/driver` 包的 `Rows` 结构。 + +* `func (s *taosSubscriber) Unsubscribe(keepProgress bool)` + + 取消订阅数据。 + +#### schemaless + +* `func (conn *Connector) InfluxDBInsertLines(lines []string, precision string) error` + + 写入 influxDB 行协议。 + +* `func (conn *Connector) OpenTSDBInsertTelnetLines(lines []string) error` + + 写入 OpenTDSB telnet 协议数据。 + +* `func (conn *Connector) OpenTSDBInsertJsonPayload(payload string) error` + + 写入 OpenTSDB JSON 协议数据。 + +#### 参数绑定 + +* `func (conn *Connector) StmtExecute(sql string, params *param.Param) (res driver.Result, err error)` + + 参数绑定单行插入。 + +* `func (conn *Connector) StmtQuery(sql string, params *param.Param) (rows driver.Rows, err error)` + + 参数绑定查询,返回 `database/sql/driver` 包的 `Rows` 结构。 + +* `func (conn *Connector) InsertStmt() *insertstmt.InsertStmt` + + 初始化参数。 + +* `func (stmt *InsertStmt) Prepare(sql string) error` + + 参数绑定预处理 SQL 语句。 + +* `func (stmt *InsertStmt) SetTableName(name string) error` + + 参数绑定设置表名。 + +* `func (stmt *InsertStmt) SetSubTableName(name string) error` + + 参数绑定设置子表名。 + +* `func (stmt *InsertStmt) BindParam(params []*param.Param, bindType *param.ColumnType) error` + + 参数绑定多行数据。 + +* `func (stmt *InsertStmt) AddBatch() error` + + 添加到参数绑定批处理。 + +* `func (stmt *InsertStmt) Execute() error` + + 执行参数绑定。 + +* `func (stmt *InsertStmt) GetAffectedRows() int` + + 获取参数绑定插入受影响行数。 + +* `func (stmt *InsertStmt) Close() error` + + 结束参数绑定。 + +## API 参考 + +全部 API 见 [driver-go 文档](https://pkg.go.dev/github.com/taosdata/driver-go/v2) diff --git a/docs-cn/14-reference/03-connector/java.mdx b/docs-cn/14-reference/03-connector/java.mdx new file mode 100644 index 0000000000000000000000000000000000000000..55abf84fd50fe1c4b5b6a07b28731a00d4534a05 --- /dev/null +++ b/docs-cn/14-reference/03-connector/java.mdx @@ -0,0 +1,839 @@ +--- +toc_max_heading_level: 4 +sidebar_position: 2 +sidebar_label: Java +title: TDengine Java Connector +description: TDengine Java 连接器基于标准 JDBC API 实现, 并提供原生连接与 REST连接两种连接器。 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +`taos-jdbcdriver` 是 TDengine 的官方 Java 语言连接器,Java 开发人员可以通过它开发存取 TDengine 数据库的应用软件。`taos-jdbcdriver` 实现了 JDBC driver 标准的接口,并提供两种形式的连接器。一种是通过 TDengine 客户端驱动程序(taosc)原生连接 TDengine 实例,支持数据写入、查询、订阅、schemaless 接口和参数绑定接口等功能,一种是通过 taosAdapter 提供的 REST 接口连接 TDengine 实例(2.4.0.0 及更高版本)。REST 连接实现的功能集合和原生连接有少量不同。 + +![tdengine-connector](tdengine-jdbc-connector.png) + +上图显示了两种 Java 应用使用连接器访问 TDengine 的两种方式: + +- JDBC 原生连接:Java 应用在物理节点 1(pnode1)上使用 TSDBDriver 直接调用客户端驱动(libtaos.so 或 taos.dll)的 API 将写入和查询请求发送到位于物理节点 2(pnode2)上的 taosd 实例。 +- JDBC REST 连接:Java 应用通过 RestfulDriver 将 SQL 封装成一个 REST 请求,发送给物理节点 2 的 REST 服务器(taosAdapter),通过 REST 服务器请求 taosd 并返回结果。 + +使用 REST 连接,不依赖 TDengine 客户端驱动,可以跨平台,更加方便灵活,但性能比原生连接器低约 30%。 + +:::info +TDengine 的 JDBC 驱动实现尽可能与关系型数据库驱动保持一致,但 TDengine 与关系对象型数据库的使用场景和技术特征存在差异,所以`taos-jdbcdriver` 与传统的 JDBC driver 也存在一定差异。在使用时需要注意以下几点: + +- TDengine 目前不支持针对单条数据记录的删除操作。 +- 目前不支持事务操作。 + +::: + +## 支持的平台 + +原生连接支持的平台和 TDengine 客户端驱动支持的平台一致。 +REST 连接支持所有能运行 Java 的平台。 + +## 版本支持 + +请参考[版本支持列表](/reference/connector#版本支持) + +## TDengine DataType 和 Java DataType + +TDengine 目前支持时间戳、数字、字符、布尔类型,与 Java 对应类型转换如下: + +| TDengine DataType | JDBCType (driver 版本 < 2.0.24) | JDBCType (driver 版本 >= 2.0.24) | +| ----------------- | --------------------------------- | ---------------------------------- | +| TIMESTAMP | java.lang.Long | java.sql.Timestamp | +| INT | java.lang.Integer | java.lang.Integer | +| BIGINT | java.lang.Long | java.lang.Long | +| FLOAT | java.lang.Float | java.lang.Float | +| DOUBLE | java.lang.Double | java.lang.Double | +| SMALLINT | java.lang.Short | java.lang.Short | +| TINYINT | java.lang.Byte | java.lang.Byte | +| BOOL | java.lang.Boolean | java.lang.Boolean | +| BINARY | java.lang.String | byte array | +| NCHAR | java.lang.String | java.lang.String | +| JSON | - | java.lang.String | + +**注意**:JSON 类型仅在 tag 中支持。 + +## 安装步骤 + +### 安装前准备 + +使用 Java Connector 连接数据库前,需要具备以下条件: + +- 已安装 Java 1.8 或以上版本运行时环境和 Maven 3.6 或以上版本 +- 已安装 TDengine 客户端驱动(使用原生连接必须安装,使用 REST 连接无需安装),具体步骤请参考[安装客户端驱动](/reference/connector#安装客户端驱动) + +### 安装连接器 + + + + +目前 taos-jdbcdriver 已经发布到 [Sonatype Repository](https://search.maven.org/artifact/com.taosdata.jdbc/taos-jdbcdriver) 仓库,且各大仓库都已同步。 + +- [sonatype](https://search.maven.org/artifact/com.taosdata.jdbc/taos-jdbcdriver) +- [mvnrepository](https://mvnrepository.com/artifact/com.taosdata.jdbc/taos-jdbcdriver) +- [maven.aliyun](https://maven.aliyun.com/mvn/search) + +Maven 项目中,在 pom.xml 中添加以下依赖: + +```xml-dtd + + com.taosdata.jdbc + taos-jdbcdriver + 2.0.** + +``` + + + + +可以通过下载 TDengine 的源码,自己编译最新版本的 Java connector + +```shell +git clone https://github.com/taosdata/TDengine.git +cd TDengine/src/connector/jdbc +mvn clean install -Dmaven.test.skip=true +``` + +编译后,在 target 目录下会产生 taos-jdbcdriver-2.0.XX-dist.jar 的 jar 包,并自动将编译的 jar 文件放在本地的 Maven 仓库中。 + + + + +## 建立连接 + +TDengine 的 JDBC URL 规范格式为: +`jdbc:[TAOS|TAOS-RS]://[host_name]:[port]/[database_name]?[user={user}|&password={password}|&charset={charset}|&cfgdir={config_dir}|&locale={locale}|&timezone={timezone}]` + +对于建立连接,原生连接与 REST 连接有细微不同。 + + + + +```java +Class.forName("com.taosdata.jdbc.TSDBDriver"); +String jdbcUrl = "jdbc:TAOS://taosdemo.com:6030/test?user=root&password=taosdata"; +Connection conn = DriverManager.getConnection(jdbcUrl); +``` + +以上示例,使用了 JDBC 原生连接的 TSDBDriver,建立了到 hostname 为 taosdemo.com,端口为 6030(TDengine 的默认端口),数据库名为 test 的连接。这个 URL 中指定用户名(user)为 root,密码(password)为 taosdata。 + +**注意**:使用 JDBC 原生连接,taos-jdbcdriver 需要依赖客户端驱动(Linux 下是 libtaos.so;Windows 下是 taos.dll)。 + +url 中的配置参数如下: + +- user:登录 TDengine 用户名,默认值 'root'。 +- password:用户登录密码,默认值 'taosdata'。 +- cfgdir:客户端配置文件目录路径,Linux OS 上默认值 `/etc/taos`,Windows OS 上默认值 `C:/TDengine/cfg`。 +- charset:客户端使用的字符集,默认值为系统字符集。 +- locale:客户端语言环境,默认值系统当前 locale。 +- timezone:客户端使用的时区,默认值为系统当前时区。 +- batchfetch: true:在执行查询时批量拉取结果集;false:逐行拉取结果集。默认值为:false。开启批量拉取同时获取一批数据在查询数据量较大时批量拉取可以有效的提升查询性能。 +- batchErrorIgnore:true:在执行 Statement 的 executeBatch 时,如果中间有一条 SQL 执行失败将继续执行下面的 SQL。false:不再执行失败 SQL 后的任何语句。默认值为:false。 + +JDBC 原生连接的使用请参见[视频教程](https://www.taosdata.com/blog/2020/11/11/1955.html)。 + +**使用 TDengine 客户端驱动配置文件建立连接 ** + +当使用 JDBC 原生连接连接 TDengine 集群时,可以使用 TDengine 客户端驱动配置文件,在配置文件中指定集群的 firstEp、secondEp 等参数。如下所示: + +1. 在 Java 应用中不指定 hostname 和 port + +```java +public Connection getConn() throws Exception{ + Class.forName("com.taosdata.jdbc.TSDBDriver"); + String jdbcUrl = "jdbc:TAOS://:/test?user=root&password=taosdata"; + Properties connProps = new Properties(); + connProps.setProperty(TSDBDriver.PROPERTY_KEY_CHARSET, "UTF-8"); + connProps.setProperty(TSDBDriver.PROPERTY_KEY_LOCALE, "en_US.UTF-8"); + connProps.setProperty(TSDBDriver.PROPERTY_KEY_TIME_ZONE, "UTC-8"); + Connection conn = DriverManager.getConnection(jdbcUrl, connProps); + return conn; +} +``` + +2. 在配置文件中指定 firstEp 和 secondEp + +```shell +# first fully qualified domain name (FQDN) for TDengine system +firstEp cluster_node1:6030 + +# second fully qualified domain name (FQDN) for TDengine system, for cluster only +secondEp cluster_node2:6030 + +# default system charset +# charset UTF-8 + +# system locale +# locale en_US.UTF-8 +``` + +以上示例,jdbc 会使用客户端的配置文件,建立到 hostname 为 cluster_node1、端口为 6030、数据库名为 test 的连接。当集群中 firstEp 节点失效时,JDBC 会尝试使用 secondEp 连接集群。 + +TDengine 中,只要保证 firstEp 和 secondEp 中一个节点有效,就可以正常建立到集群的连接。 + +> **注意**:这里的配置文件指的是调用 JDBC Connector 的应用程序所在机器上的配置文件,Linux OS 上默认值 /etc/taos/taos.cfg ,Windows OS 上默认值 C://TDengine/cfg/taos.cfg。 + + + + +```java +Class.forName("com.taosdata.jdbc.rs.RestfulDriver"); +String jdbcUrl = "jdbc:TAOS-RS://taosdemo.com:6041/test?user=root&password=taosdata"; +Connection conn = DriverManager.getConnection(jdbcUrl); +``` + +以上示例,使用了 JDBC REST 连接的 RestfulDriver,建立了到 hostname 为 taosdemo.com,端口为 6041,数据库名为 test 的连接。这个 URL 中指定用户名(user)为 root,密码(password)为 taosdata。 + +使用 JDBC REST 连接,不需要依赖客户端驱动。与 JDBC 原生连接相比,仅需要: + +1. driverClass 指定为“com.taosdata.jdbc.rs.RestfulDriver”; +2. jdbcUrl 以“jdbc:TAOS-RS://”开头; +3. 使用 6041 作为连接端口。 + +url 中的配置参数如下: + +- user:登录 TDengine 用户名,默认值 'root'。 +- password:用户登录密码,默认值 'taosdata'。 +- batchfetch: true:在执行查询时批量拉取结果集;false:逐行拉取结果集。默认值为:false。逐行拉取结果集使用 HTTP 方式进行数据传输。从 taos-jdbcdriver-2.0.38 和 TDengine 2.4.0.12 版本开始,JDBC REST 连接增加批量拉取数据功能。taos-jdbcdriver 与 TDengine 之间通过 WebSocket 连接进行数据传输。相较于 HTTP,WebSocket 可以使 JDBC REST 连接支持大数据量查询,并提升查询性能。 +- batchErrorIgnore:true:在执行 Statement 的 executeBatch 时,如果中间有一条 SQL 执行失败,继续执行下面的 SQL 了。false:不再执行失败 SQL 后的任何语句。默认值为:false。 + +**注意**:部分配置项(比如:locale、timezone)在 REST 连接中不生效。 + +:::note + +- 与原生连接方式不同,REST 接口是无状态的。在使用 JDBC REST 连接时,需要在 SQL 中指定表、超级表的数据库名称。例如: + +```sql +INSERT INTO test.t1 USING test.weather (ts, temperature) TAGS('beijing') VALUES(now, 24.6); +``` + +- 从 taos-jdbcdriver-2.0.36 和 TDengine 2.2.0.0 版本开始,如果在 url 中指定了 dbname,那么,JDBC REST 连接会默认使用/rest/sql/dbname 作为 restful 请求的 url,在 SQL 中不需要指定 dbname。例如:url 为 jdbc:TAOS-RS://127.0.0.1:6041/test,那么,可以执行 sql:insert into t1 using weather(ts, temperature) tags('beijing') values(now, 24.6); + +::: + + + + +### 指定 URL 和 Properties 获取连接 + +除了通过指定的 URL 获取连接,还可以使用 Properties 指定建立连接时的参数。 + +**注意**: + +- 应用中设置的 client parameter 为进程级别的,即如果要更新 client 的参数,需要重启应用。这是因为 client parameter 是全局参数,仅在应用程序的第一次设置生效。 +- 以下示例代码基于 taos-jdbcdriver-2.0.36。 + +```java +public Connection getConn() throws Exception{ + Class.forName("com.taosdata.jdbc.TSDBDriver"); + String jdbcUrl = "jdbc:TAOS://taosdemo.com:6030/test?user=root&password=taosdata"; + Properties connProps = new Properties(); + connProps.setProperty(TSDBDriver.PROPERTY_KEY_CHARSET, "UTF-8"); + connProps.setProperty(TSDBDriver.PROPERTY_KEY_LOCALE, "en_US.UTF-8"); + connProps.setProperty(TSDBDriver.PROPERTY_KEY_TIME_ZONE, "UTC-8"); + connProps.setProperty("debugFlag", "135"); + connProps.setProperty("maxSQLLength", "1048576"); + Connection conn = DriverManager.getConnection(jdbcUrl, connProps); + return conn; +} + +public Connection getRestConn() throws Exception{ + Class.forName("com.taosdata.jdbc.rs.RestfulDriver"); + String jdbcUrl = "jdbc:TAOS-RS://taosdemo.com:6041/test?user=root&password=taosdata"; + Properties connProps = new Properties(); + connProps.setProperty(TSDBDriver.PROPERTY_KEY_BATCH_LOAD, "true"); + Connection conn = DriverManager.getConnection(jdbcUrl, connProps); + return conn; +} +``` + +以上示例,建立一个到 hostname 为 taosdemo.com,端口为 6030/6041,数据库名为 test 的连接。这个连接在 url 中指定了用户名(user)为 root,密码(password)为 taosdata,并在 connProps 中指定了使用的字符集、语言环境、时区、是否开启批量拉取等信息。 + +properties 中的配置参数如下: + +- TSDBDriver.PROPERTY_KEY_USER:登录 TDengine 用户名,默认值 'root'。 +- TSDBDriver.PROPERTY_KEY_PASSWORD:用户登录密码,默认值 'taosdata'。 +- TSDBDriver.PROPERTY_KEY_BATCH_LOAD: true:在执行查询时批量拉取结果集;false:逐行拉取结果集。默认值为:false。 +- TSDBDriver.PROPERTY_KEY_BATCH_ERROR_IGNORE:true:在执行 Statement 的 executeBatch 时,如果中间有一条 SQL 执行失败,继续执行下面的 sq 了。false:不再执行失败 SQL 后的任何语句。默认值为:false。 +- TSDBDriver.PROPERTY_KEY_CONFIG_DIR:仅在使用 JDBC 原生连接时生效。客户端配置文件目录路径,Linux OS 上默认值 `/etc/taos`,Windows OS 上默认值 `C:/TDengine/cfg`。 +- TSDBDriver.PROPERTY_KEY_CHARSET:仅在使用 JDBC 原生连接时生效。 客户端使用的字符集,默认值为系统字符集。 +- TSDBDriver.PROPERTY_KEY_LOCALE:仅在使用 JDBC 原生连接时生效。 客户端语言环境,默认值系统当前 locale。 +- TSDBDriver.PROPERTY_KEY_TIME_ZONE:仅在使用 JDBC 原生连接时生效。 客户端使用的时区,默认值为系统当前时区。 +- 此外对 JDBC 原生连接,通过指定 URL 和 Properties 还可以指定其他参数,比如日志级别、SQL 长度等。更多详细配置请参考[客户端配置](/reference/config/#仅客户端适用)。 + +### 配置参数的优先级 + +通过前面三种方式获取连接,如果配置参数在 url、Properties、客户端配置文件中有重复,则参数的`优先级由高到低`分别如下: + +1. JDBC URL 参数,如上所述,可以在 JDBC URL 的参数中指定。 +2. Properties connProps +3. 使用原生连接时,TDengine 客户端驱动的配置文件 taos.cfg + +例如:在 url 中指定了 password 为 taosdata,在 Properties 中指定了 password 为 taosdemo,那么,JDBC 会使用 url 中的 password 建立连接。 + +## 使用示例 + +### 创建数据库和表 + +```java +Statement stmt = conn.createStatement(); + +// create database +stmt.executeUpdate("create database if not exists db"); + +// use database +stmt.executeUpdate("use db"); + +// create table +stmt.executeUpdate("create table if not exists tb (ts timestamp, temperature int, humidity float)"); +``` + +> **注意**:如果不使用 `use db` 指定数据库,则后续对表的操作都需要增加数据库名称作为前缀,如 db.tb。 + +### 插入数据 + +```java +// insert data +int affectedRows = stmt.executeUpdate("insert into tb values(now, 23, 10.3) (now + 1s, 20, 9.3)"); + +System.out.println("insert " + affectedRows + " rows."); +``` + +> now 为系统内部函数,默认为客户端所在计算机当前时间。 +> `now + 1s` 代表客户端当前时间往后加 1 秒,数字后面代表时间单位:a(毫秒),s(秒),m(分),h(小时),d(天),w(周),n(月),y(年)。 + +### 查询数据 + +```java +// query data +ResultSet resultSet = stmt.executeQuery("select * from tb"); + +Timestamp ts = null; +int temperature = 0; +float humidity = 0; +while(resultSet.next()){ + + ts = resultSet.getTimestamp(1); + temperature = resultSet.getInt(2); + humidity = resultSet.getFloat("humidity"); + + System.out.printf("%s, %d, %s\n", ts, temperature, humidity); +} +``` + +> 查询和操作关系型数据库一致,使用下标获取返回字段内容时从 1 开始,建议使用字段名称获取。 + +### 处理异常 + +在报错后,通过 SQLException 可以获取到错误的信息和错误码: + +```java +try (Statement statement = connection.createStatement()) { + // executeQuery + ResultSet resultSet = statement.executeQuery(sql); + // print result + printResult(resultSet); +} catch (SQLException e) { + System.out.println("ERROR Message: " + e.getMessage()); + System.out.println("ERROR Code: " + e.getErrorCode()); + e.printStackTrace(); +} +``` + +JDBC 连接器可能报错的错误码包括 3 种:JDBC driver 本身的报错(错误码在 0x2301 到 0x2350 之间),原生连接方法的报错(错误码在 0x2351 到 0x2400 之间),TDengine 其他功能模块的报错。 + +具体的错误码请参考: + +- [TDengine Java Connector](https://github.com/taosdata/TDengine/blob/develop/src/connector/jdbc/src/main/java/com/taosdata/jdbc/TSDBErrorNumbers.java) +- [TDengine_ERROR_CODE](https://github.com/taosdata/TDengine/blob/develop/src/inc/taoserror.h) + +### 通过参数绑定写入数据 + +从 2.1.2.0 版本开始,TDengine 的 JDBC 原生连接实现大幅改进了参数绑定方式对数据写入(INSERT)场景的支持。采用这种方式写入数据时,能避免 SQL 语法解析的资源消耗,从而在很多情况下显著提升写入性能。 + +**注意**: + +- JDBC REST 连接目前不支持参数绑定 +- 以下示例代码基于 taos-jdbcdriver-2.0.36 +- binary 类型数据需要调用 setString 方法,nchar 类型数据需要调用 setNString 方法 +- setString 和 setNString 都要求用户在 size 参数里声明表定义中对应列的列宽 + +```java +public class ParameterBindingDemo { + + private static final String host = "127.0.0.1"; + private static final Random random = new Random(System.currentTimeMillis()); + private static final int BINARY_COLUMN_SIZE = 20; + private static final String[] schemaList = { + "create table stable1(ts timestamp, f1 tinyint, f2 smallint, f3 int, f4 bigint) tags(t1 tinyint, t2 smallint, t3 int, t4 bigint)", + "create table stable2(ts timestamp, f1 float, f2 double) tags(t1 float, t2 double)", + "create table stable3(ts timestamp, f1 bool) tags(t1 bool)", + "create table stable4(ts timestamp, f1 binary(" + BINARY_COLUMN_SIZE + ")) tags(t1 binary(" + BINARY_COLUMN_SIZE + "))", + "create table stable5(ts timestamp, f1 nchar(" + BINARY_COLUMN_SIZE + ")) tags(t1 nchar(" + BINARY_COLUMN_SIZE + "))" + }; + private static final int numOfSubTable = 10, numOfRow = 10; + + public static void main(String[] args) throws SQLException { + + String jdbcUrl = "jdbc:TAOS://" + host + ":6030/"; + Connection conn = DriverManager.getConnection(jdbcUrl, "root", "taosdata"); + + init(conn); + + bindInteger(conn); + + bindFloat(conn); + + bindBoolean(conn); + + bindBytes(conn); + + bindString(conn); + + conn.close(); + } + + private static void init(Connection conn) throws SQLException { + try (Statement stmt = conn.createStatement()) { + stmt.execute("drop database if exists test_parabind"); + stmt.execute("create database if not exists test_parabind"); + stmt.execute("use test_parabind"); + for (int i = 0; i < schemaList.length; i++) { + stmt.execute(schemaList[i]); + } + } + } + + private static void bindInteger(Connection conn) throws SQLException { + String sql = "insert into ? using stable1 tags(?,?,?,?) values(?,?,?,?,?)"; + + try (TSDBPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSDBPreparedStatement.class)) { + + for (int i = 1; i <= numOfSubTable; i++) { + // set table name + pstmt.setTableName("t1_" + i); + // set tags + pstmt.setTagByte(0, Byte.parseByte(Integer.toString(random.nextInt(Byte.MAX_VALUE)))); + pstmt.setTagShort(1, Short.parseShort(Integer.toString(random.nextInt(Short.MAX_VALUE)))); + pstmt.setTagInt(2, random.nextInt(Integer.MAX_VALUE)); + pstmt.setTagLong(3, random.nextLong()); + // set columns + ArrayList tsList = new ArrayList<>(); + long current = System.currentTimeMillis(); + for (int j = 0; j < numOfRow; j++) + tsList.add(current + j); + pstmt.setTimestamp(0, tsList); + + ArrayList f1List = new ArrayList<>(); + for (int j = 0; j < numOfRow; j++) + f1List.add(Byte.parseByte(Integer.toString(random.nextInt(Byte.MAX_VALUE)))); + pstmt.setByte(1, f1List); + + ArrayList f2List = new ArrayList<>(); + for (int j = 0; j < numOfRow; j++) + f2List.add(Short.parseShort(Integer.toString(random.nextInt(Short.MAX_VALUE)))); + pstmt.setShort(2, f2List); + + ArrayList f3List = new ArrayList<>(); + for (int j = 0; j < numOfRow; j++) + f3List.add(random.nextInt(Integer.MAX_VALUE)); + pstmt.setInt(3, f3List); + + ArrayList f4List = new ArrayList<>(); + for (int j = 0; j < numOfRow; j++) + f4List.add(random.nextLong()); + pstmt.setLong(4, f4List); + + // add column + pstmt.columnDataAddBatch(); + } + // execute column + pstmt.columnDataExecuteBatch(); + } + } + + private static void bindFloat(Connection conn) throws SQLException { + String sql = "insert into ? using stable2 tags(?,?) values(?,?,?)"; + + TSDBPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSDBPreparedStatement.class); + + for (int i = 1; i <= numOfSubTable; i++) { + // set table name + pstmt.setTableName("t2_" + i); + // set tags + pstmt.setTagFloat(0, random.nextFloat()); + pstmt.setTagDouble(1, random.nextDouble()); + // set columns + ArrayList tsList = new ArrayList<>(); + long current = System.currentTimeMillis(); + for (int j = 0; j < numOfRow; j++) + tsList.add(current + j); + pstmt.setTimestamp(0, tsList); + + ArrayList f1List = new ArrayList<>(); + for (int j = 0; j < numOfRow; j++) + f1List.add(random.nextFloat()); + pstmt.setFloat(1, f1List); + + ArrayList f2List = new ArrayList<>(); + for (int j = 0; j < numOfRow; j++) + f2List.add(random.nextDouble()); + pstmt.setDouble(2, f2List); + + // add column + pstmt.columnDataAddBatch(); + } + // execute + pstmt.columnDataExecuteBatch(); + // close if no try-with-catch statement is used + pstmt.close(); + } + + private static void bindBoolean(Connection conn) throws SQLException { + String sql = "insert into ? using stable3 tags(?) values(?,?)"; + + try (TSDBPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSDBPreparedStatement.class)) { + for (int i = 1; i <= numOfSubTable; i++) { + // set table name + pstmt.setTableName("t3_" + i); + // set tags + pstmt.setTagBoolean(0, random.nextBoolean()); + // set columns + ArrayList tsList = new ArrayList<>(); + long current = System.currentTimeMillis(); + for (int j = 0; j < numOfRow; j++) + tsList.add(current + j); + pstmt.setTimestamp(0, tsList); + + ArrayList f1List = new ArrayList<>(); + for (int j = 0; j < numOfRow; j++) + f1List.add(random.nextBoolean()); + pstmt.setBoolean(1, f1List); + + // add column + pstmt.columnDataAddBatch(); + } + // execute + pstmt.columnDataExecuteBatch(); + } + } + + private static void bindBytes(Connection conn) throws SQLException { + String sql = "insert into ? using stable4 tags(?) values(?,?)"; + + try (TSDBPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSDBPreparedStatement.class)) { + + for (int i = 1; i <= numOfSubTable; i++) { + // set table name + pstmt.setTableName("t4_" + i); + // set tags + pstmt.setTagString(0, new String("abc")); + + // set columns + ArrayList tsList = new ArrayList<>(); + long current = System.currentTimeMillis(); + for (int j = 0; j < numOfRow; j++) + tsList.add(current + j); + pstmt.setTimestamp(0, tsList); + + ArrayList f1List = new ArrayList<>(); + for (int j = 0; j < numOfRow; j++) { + f1List.add(new String("abc")); + } + pstmt.setString(1, f1List, BINARY_COLUMN_SIZE); + + // add column + pstmt.columnDataAddBatch(); + } + // execute + pstmt.columnDataExecuteBatch(); + } + } + + private static void bindString(Connection conn) throws SQLException { + String sql = "insert into ? using stable5 tags(?) values(?,?)"; + + try (TSDBPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSDBPreparedStatement.class)) { + + for (int i = 1; i <= numOfSubTable; i++) { + // set table name + pstmt.setTableName("t5_" + i); + // set tags + pstmt.setTagNString(0, "北京-abc"); + + // set columns + ArrayList tsList = new ArrayList<>(); + long current = System.currentTimeMillis(); + for (int j = 0; j < numOfRow; j++) + tsList.add(current + j); + pstmt.setTimestamp(0, tsList); + + ArrayList f1List = new ArrayList<>(); + for (int j = 0; j < numOfRow; j++) { + f1List.add("北京-abc"); + } + pstmt.setNString(1, f1List, BINARY_COLUMN_SIZE); + + // add column + pstmt.columnDataAddBatch(); + } + // execute + pstmt.columnDataExecuteBatch(); + } + } +} +``` + +用于设定 TAGS 取值的方法总共有: + +```java +public void setTagNull(int index, int type) +public void setTagBoolean(int index, boolean value) +public void setTagInt(int index, int value) +public void setTagByte(int index, byte value) +public void setTagShort(int index, short value) +public void setTagLong(int index, long value) +public void setTagTimestamp(int index, long value) +public void setTagFloat(int index, float value) +public void setTagDouble(int index, double value) +public void setTagString(int index, String value) +public void setTagNString(int index, String value) +``` + +用于设定 VALUES 数据列的取值的方法总共有: + +```java +public void setInt(int columnIndex, ArrayList list) throws SQLException +public void setFloat(int columnIndex, ArrayList list) throws SQLException +public void setTimestamp(int columnIndex, ArrayList list) throws SQLException +public void setLong(int columnIndex, ArrayList list) throws SQLException +public void setDouble(int columnIndex, ArrayList list) throws SQLException +public void setBoolean(int columnIndex, ArrayList list) throws SQLException +public void setByte(int columnIndex, ArrayList list) throws SQLException +public void setShort(int columnIndex, ArrayList list) throws SQLException +public void setString(int columnIndex, ArrayList list, int size) throws SQLException +public void setNString(int columnIndex, ArrayList list, int size) throws SQLException +``` + +### 无模式写入 + +从 2.2.0.0 版本开始,TDengine 增加了对无模式写入功能。无模式写入兼容 InfluxDB 的 行协议(Line Protocol)、OpenTSDB 的 telnet 行协议和 OpenTSDB 的 JSON 格式协议。详情请参见[无模式写入](/reference/schemaless/)。 + +**注意**: + +- JDBC REST 连接目前不支持无模式写入 +- 以下示例代码基于 taos-jdbcdriver-2.0.36 + +```java +public class SchemalessInsertTest { + private static final String host = "127.0.0.1"; + private static final String lineDemo = "st,t1=3i64,t2=4f64,t3=\"t3\" c1=3i64,c3=L\"passit\",c2=false,c4=4f64 1626006833639000000"; + private static final String telnetDemo = "stb0_0 1626006833 4 host=host0 interface=eth0"; + private static final String jsonDemo = "{\"metric\": \"meter_current\",\"timestamp\": 1346846400,\"value\": 10.3, \"tags\": {\"groupid\": 2, \"location\": \"Beijing\", \"id\": \"d1001\"}}"; + + public static void main(String[] args) throws SQLException { + final String url = "jdbc:TAOS://" + host + ":6030/?user=root&password=taosdata"; + try (Connection connection = DriverManager.getConnection(url)) { + init(connection); + + SchemalessWriter writer = new SchemalessWriter(connection); + writer.write(lineDemo, SchemalessProtocolType.LINE, SchemalessTimestampType.NANO_SECONDS); + writer.write(telnetDemo, SchemalessProtocolType.TELNET, SchemalessTimestampType.MILLI_SECONDS); + writer.write(jsonDemo, SchemalessProtocolType.JSON, SchemalessTimestampType.NOT_CONFIGURED); + } + } + + private static void init(Connection connection) throws SQLException { + try (Statement stmt = connection.createStatement()) { + stmt.executeUpdate("drop database if exists test_schemaless"); + stmt.executeUpdate("create database if not exists test_schemaless"); + stmt.executeUpdate("use test_schemaless"); + } + } +} +``` + +### 订阅 + +TDengine Java 连接器支持订阅功能,应用 API 如下: + +#### 创建订阅 + +```java +TSDBSubscribe sub = ((TSDBConnection)conn).subscribe("topic", "select * from meters", false); +``` + +`subscribe` 方法的三个参数含义如下: + +- topic:订阅的主题(即名称),此参数是订阅的唯一标识 +- sql:订阅的查询语句,此语句只能是 `select` 语句,只应查询原始数据,只能按时间正序查询数据 +- restart:如果订阅已经存在,是重新开始,还是继续之前的订阅 + +如上面的例子将使用 SQL 语句 `select * from meters` 创建一个名为 `topic` 的订阅,如果这个订阅已经存在,将继续之前的查询进度,而不是从头开始消费所有的数据。 + +#### 订阅消费数据 + +```java +int total = 0; +while(true) { + TSDBResultSet rs = sub.consume(); + int count = 0; + while(rs.next()) { + count++; + } + total += count; + System.out.printf("%d rows consumed, total %d\n", count, total); + Thread.sleep(1000); +} +``` + +`consume` 方法返回一个结果集,其中包含从上次 `consume` 到目前为止的所有新数据。请务必按需选择合理的调用 `consume` 的频率(如例子中的 `Thread.sleep(1000)`),否则会给服务端造成不必要的压力。 + +#### 关闭订阅 + +```java +sub.close(true); +``` + +`close` 方法关闭一个订阅。如果其参数为 `true` 表示保留订阅进度信息,后续可以创建同名订阅继续消费数据;如为 `false` 则不保留订阅进度。 + +### 关闭资源 + +```java +resultSet.close(); +stmt.close(); +conn.close(); +``` + +> `注意务必要将 connection 进行关闭`,否则会出现连接泄露。 + +### 与连接池使用 + +#### HikariCP + +使用示例如下: + +```java + public static void main(String[] args) throws SQLException { + HikariConfig config = new HikariConfig(); + // jdbc properties + config.setJdbcUrl("jdbc:TAOS://127.0.0.1:6030/log"); + config.setUsername("root"); + config.setPassword("taosdata"); + // connection pool configurations + config.setMinimumIdle(10); //minimum number of idle connection + config.setMaximumPoolSize(10); //maximum number of connection in the pool + config.setConnectionTimeout(30000); //maximum wait milliseconds for get connection from pool + config.setMaxLifetime(0); // maximum life time for each connection + config.setIdleTimeout(0); // max idle time for recycle idle connection + config.setConnectionTestQuery("select server_status()"); //validation query + + HikariDataSource ds = new HikariDataSource(config); //create datasource + + Connection connection = ds.getConnection(); // get connection + Statement statement = connection.createStatement(); // get statement + + //query or insert + // ... + + connection.close(); // put back to conneciton pool +} +``` + +> 通过 HikariDataSource.getConnection() 获取连接后,使用完成后需要调用 close() 方法,实际上它并不会关闭连接,只是放回连接池中。 +> 更多 HikariCP 使用问题请查看[官方说明](https://github.com/brettwooldridge/HikariCP)。 + +#### Druid + +使用示例如下: + +```java +public static void main(String[] args) throws Exception { + + DruidDataSource dataSource = new DruidDataSource(); + // jdbc properties + dataSource.setDriverClassName("com.taosdata.jdbc.TSDBDriver"); + dataSource.setUrl(url); + dataSource.setUsername("root"); + dataSource.setPassword("taosdata"); + // pool configurations + dataSource.setInitialSize(10); + dataSource.setMinIdle(10); + dataSource.setMaxActive(10); + dataSource.setMaxWait(30000); + dataSource.setValidationQuery("select server_status()"); + + Connection connection = dataSource.getConnection(); // get connection + Statement statement = connection.createStatement(); // get statement + //query or insert + // ... + + connection.close(); // put back to conneciton pool +} +``` + +> 更多 druid 使用问题请查看[官方说明](https://github.com/alibaba/druid)。 + +**注意事项:** + +- TDengine `v1.6.4.1` 版本开始提供了一个专门用于心跳检测的函数 `select server_status()`,所以在使用连接池时推荐使用 `select server_status()` 进行 Validation Query。 + +如下所示,`select server_status()` 执行成功会返回 `1`。 + +```sql +taos> select server_status(); +server_status()| +================ +1 | +Query OK, 1 row(s) in set (0.000141s) +``` + +### 更多示例程序 + +示例程序源码位于 `TDengine/examples/JDBC` 下: + +- JDBCDemo:JDBC 示例源程序。 +- JDBCConnectorChecker:JDBC 安装校验源程序及 jar 包。 +- connectionPools:HikariCP, Druid, dbcp, c3p0 等连接池中使用 taos-jdbcdriver。 +- SpringJdbcTemplate:Spring JdbcTemplate 中使用 taos-jdbcdriver。 +- mybatisplus-demo:Springboot + Mybatis 中使用 taos-jdbcdriver。 + +请参考:[JDBC example](https://github.com/taosdata/TDengine/tree/develop/examples/JDBC) + +## 最近更新记录 + +| taos-jdbcdriver 版本 | 主要变化 | +| :------------------: | :----------------------------: | +| 2.0.38 | JDBC REST 连接增加批量拉取功能 | +| 2.0.37 | 增加对 json tag 支持 | +| 2.0.36 | 增加对 schemaless 写入支持 | + +## 常见问题 + +1. 使用 Statement 的 `addBatch()` 和 `executeBatch()` 来执行“批量写入/更新”,为什么没有带来性能上的提升? + + **原因**:TDengine 的 JDBC 实现中,通过 `addBatch` 方法提交的 SQL 语句,会按照添加的顺序,依次执行,这种方式没有减少与服务端的交互次数,不会带来性能上的提升。 + + **解决方法**:1. 在一条 insert 语句中拼接多个 values 值;2. 使用多线程的方式并发插入;3. 使用参数绑定的写入方式 + +2. java.lang.UnsatisfiedLinkError: no taos in java.library.path + + **原因**:程序没有找到依赖的本地函数库 taos。 + + **解决方法**:Windows 下可以将 C:\TDengine\driver\taos.dll 拷贝到 C:\Windows\System32\ 目录下,Linux 下将建立如下软链 `ln -s /usr/local/taos/driver/libtaos.so.x.x.x.x /usr/lib/libtaos.so` 即可。 + +3. java.lang.UnsatisfiedLinkError: taos.dll Can't load AMD 64 bit on a IA 32-bit platform + + **原因**:目前 TDengine 只支持 64 位 JDK。 + + **解决方法**:重新安装 64 位 JDK。 + +4. 其它问题请参考 [FAQ](/train-faq/faq) + +## API 参考 + +[taos-jdbcdriver doc](https://docs.taosdata.com/api/taos-jdbcdriver) diff --git a/docs-cn/14-reference/03-connector/node.mdx b/docs-cn/14-reference/03-connector/node.mdx new file mode 100644 index 0000000000000000000000000000000000000000..12345fa9fe995c41828df07703f0efb61a2e029d --- /dev/null +++ b/docs-cn/14-reference/03-connector/node.mdx @@ -0,0 +1,259 @@ +--- +toc_max_heading_level: 4 +sidebar_position: 6 +sidebar_label: Node.js +title: TDengine Node.js Connector +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +import Preparition from "./_preparition.mdx"; +import NodeInsert from "../../07-develop/03-insert-data/_js_sql.mdx"; +import NodeInfluxLine from "../../07-develop/03-insert-data/_js_line.mdx"; +import NodeOpenTSDBTelnet from "../../07-develop/03-insert-data/_js_opts_telnet.mdx"; +import NodeOpenTSDBJson from "../../07-develop/03-insert-data/_js_opts_json.mdx"; +import NodeQuery from "../../07-develop/04-query-data/_js.mdx"; +import NodeAsyncQuery from "../../07-develop/04-query-data/_js_async.mdx"; + +`td2.0-connector` 和 `td2.0-rest-connector` 是 TDengine 的官方 Node.js 语言连接器。Node.js 开发人员可以通过它开发可以存取 TDengine 集群数据的应用软件。 + +`td2.0-connector` 是**原生连接器**,它通过 TDengine 客户端驱动程序(taosc)连接 TDengine 运行实例,支持数据写入、查询、订阅、schemaless 接口和参数绑定接口等功能。`td2.0-rest-connector` 是 **REST 连接器**,它通过 taosAdapter 提供的 REST 接口连接 TDengine 的运行实例。REST 连接器可以在任何平台运行,但性能略为下降,接口实现的功能特性集合和原生接口有少量不同。 + +Node.js 连接器源码托管在 [GitHub](https://github.com/taosdata/taos-connector-node)。 + +## 支持的平台 + +原生连接器支持的平台和 TDengine 客户端驱动支持的平台一致。 +REST 连接器支持所有能运行 Node.js 的平台。 + +## 版本支持 + +请参考[版本支持列表](/reference/connector#版本支持) + +## 支持的功能特性 + +### 原生连接器 + +1. 连接管理 +2. 普通查询 +3. 连续查询 +4. 参数绑定 +5. 订阅功能 +6. Schemaless + +### REST 连接器 + +1. 连接管理 +2. 普通查询 +3. 连续查询 + +## 安装步骤 + +### 安装前准备 + +- 安装 Node.js 开发环境 +- 如果使用 REST 连接器,跳过此步。但如果使用原生连接器,请安装 TDengine 客户端驱动,具体步骤请参考[安装客户端驱动](/reference/connector#安装客户端驱动)。我们使用 [node-gyp](https://github.com/nodejs/node-gyp) 和 TDengine 实例进行交互,还需要根据具体操作系统来安装下文提到的一些依赖工具。 + + + + +- `python` (建议`v2.7` , `v3.x.x` 目前还不支持) +- `td2.0-connector` 2.0.6 支持 Node.js LTS v10.9.0 或更高版本, Node.js LTS v12.8.0 或更高版本;2.0.5 及更早版本支持 Node.js LTS v10.x 版本。其他版本可能存在包兼容性的问题 +- `make` +- C 语言编译器,[GCC](https://gcc.gnu.org) v4.8.5 或更高版本 + + + + +- 安装方法 1 + +使用微软的[ windows-build-tools ](https://github.com/felixrieseberg/windows-build-tools)在`cmd` 命令行界面执行`npm install --global --production windows-build-tools` 即可安装所有的必备工具。 + +- 安装方法 2 + +手动安装以下工具: + +- 安装 Visual Studio 相关:[Visual Studio Build 工具](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools) 或者 [Visual Studio 2017 Community](https://visualstudio.microsoft.com/pl/thank-you-downloading-visual-studio/?sku=Community) +- 安装 [Python](https://www.python.org/downloads/) 2.7(`v3.x.x` 暂不支持) 并执行 `npm config set python python2.7` +- 进入`cmd`命令行界面,`npm config set msvs_version 2017` + +参考微软的 Node.js 用户手册[ Microsoft's Node.js Guidelines for Windows](https://github.com/Microsoft/nodejs-guidelines/blob/master/windows-environment.md#compiling-native-addon-modules)。 + +如果在 Windows 10 ARM 上使用 ARM64 Node.js,还需添加 "Visual C++ compilers and libraries for ARM64" 和 "Visual C++ ATL for ARM64"。 + + + + +### 使用 npm 安装 + + + + +```bash +npm install td2.0-connector +``` + + + + +```bash +npm i td2.0-rest-connector +``` + + + + +### 安装验证 + +在安装好 TDengine 客户端后,使用 nodejsChecker.js 程序能够验证当前环境是否支持 Node.js 方式访问 TDengine。 + +验证方法: + +- 新建安装验证目录,例如:`~/tdengine-test`,下载 GitHub 上 [nodejsChecker.js 源代码](https://github.com/taosdata/TDengine/tree/develop/examples/nodejs/nodejsChecker.js)到本地。 + +- 在命令行中执行以下命令。 + +```bash +npm init -y +npm install td2.0-connector +node nodejsChecker.js host=localhost +``` + +- 执行以上步骤后,在命令行会输出 nodejsChecker.js 连接 TDengine 实例,并执行简单插入和查询的结果。 + +## 建立连接 + +请选择使用一种连接器。 + + + + +安装并引用 `td2.0-connector` 包。 + +```javascript +//A cursor also needs to be initialized in order to interact with TDengine from Node.js. +const taos = require("td2.0-connector"); +var conn = taos.connect({ + host: "127.0.0.1", + user: "root", + password: "taosdata", + config: "/etc/taos", + port: 0, +}); +var cursor = conn.cursor(); // Initializing a new cursor + +//Close a connection +conn.close(); +``` + + + + +安装并引用 `td2.0-rest-connector` 包。 + +```javascript +//A cursor also needs to be initialized in order to interact with TDengine from Node.js. +import { options, connect } from "td2.0-rest-connector"; +options.path = "/rest/sqlt"; +// set host +options.host = "localhost"; +// set other options like user/passwd + +let conn = connect(options); +let cursor = conn.cursor(); +``` + + + + +## 使用示例 + +### 写入数据 + +#### SQL 写入 + + + +#### InfluxDB 行协议写入 + + + +#### OpenTSDB Telnet 行协议写入 + + + +#### OpenTSDB JSON 行协议写入 + + + +### 查询数据 + +#### 同步查询 + + + +#### 异步查询 + + + +## 更多示例程序 + +| 示例程序 | 示例程序描述 | +| ------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------- | +| [connection](https://github.com/taosdata/taos-connector-node/tree/develop/nodejs/examples/cursorClose.js) | 建立连接的示例。 | +| [stmtBindBatch](https://github.com/taosdata/taos-connector-node/tree/develop/nodejs/examples/stmtBindParamBatchSample.js) | 绑定多行参数插入的示例。 | +| [stmtBind](https://github.com/taosdata/taos-connector-node/tree/develop/nodejs/examples/stmtBindParamSample.js) | 一行一行绑定参数插入的示例。 | +| [stmtBindSingleParamBatch](https://github.com/taosdata/taos-connector-node/tree/develop/nodejs/examples/stmtBindSingleParamBatchSample.js) | 按列绑定参数插入的示例。 | +| [stmtUseResult](https://github.com/taosdata/taos-connector-node/tree/develop/nodejs/examples/stmtUseResultSample.js) | 绑定参数查询的示例。 | +| [json tag](https://github.com/taosdata/taos-connector-node/tree/develop/nodejs/examples/testJsonTag.js) | Json tag 的使用示例。 | +| [Nanosecond](https://github.com/taosdata/taos-connector-node/tree/develop/nodejs/examples/testNanoseconds.js) | 时间戳为纳秒精度的使用的示例。 | +| [Microsecond](https://github.com/taosdata/taos-connector-node/tree/develop/nodejs/examples/testMicroseconds.js) | 时间戳为微秒精度的使用的示例。 | +| [schemless insert](https://github.com/taosdata/taos-connector-node/tree/develop/nodejs/examples/testSchemalessInsert.js) | schemless 插入的示例。 | +| [subscribe](https://github.com/taosdata/taos-connector-node/tree/develop/nodejs/examples/testSubscribe.js) | 订阅的使用示例。 | +| [asyncQuery](https://github.com/taosdata/taos-connector-node/tree/develop/nodejs/examples/tset.js) | 异步查询的使用示例。 | +| [REST](https://github.com/taosdata/taos-connector-node/blob/develop/typescript-rest/example/example.ts) | 使用 REST 连接的 TypeScript 使用示例。 | + +## 使用限制 + +Node.js 连接器 >= v2.0.6 目前支持 node 的版本为:支持 >=v12.8.0 <= v12.9.1 || >=v10.20.0 <= v10.9.0 ;2.0.5 及更早版本支持 v10.x 版本,其他版本可能存在包兼容性的问题。 + +## 其他说明 + +Node.js 连接器的使用参见[视频教程](https://www.taosdata.com/blog/2020/11/11/1957.html)。 + +## 常见问题 + +1. 使用 REST 连接需要启动 taosadapter。 + + ```bash + sudo systemctl start taosadapter + ``` + +2. Node.js 版本 + + 连接器 >v2.0.6 目前兼容的 Node.js 版本为:>=v10.20.0 <= v10.9.0 || >=v12.8.0 <= v12.9.1 + +3. "Unable to establish connection","Unable to resolve FQDN" + + 一般都是因为配置 FQDN 不正确。 可以参考[如何彻底搞懂 TDengine 的 FQDN](https://www.taosdata.com/blog/2021/07/29/2741.html) 。 + +## 重要更新记录 + +### 原生连接器 + +| td2.0-connector 版本 | 说明 | +| -------------------- | ---------------------------------------------------------------- | +| 2.0.12 | 修复 cursor.close() 报错的 bug。 | +| 2.0.11 | 支持绑定参数、json tag、schemaless 接口等功能。 | +| 2.0.10 | 支持连接管理,普通查询、连续查询、获取系统信息、订阅功能等功能。 | + +### REST 连接器 + +| td2.0-rest-connector 版本 | 说明 | +| ------------------------- | ---------------------------------------------------------------- | +| 1.0.3 | 支持连接管理、普通查询、获取系统信息、错误信息、连续查询等功能。 | + +## API 参考 + +[API 参考](https://docs.taosdata.com/api/td2.0-connector/) diff --git a/docs-cn/14-reference/03-connector/python.mdx b/docs-cn/14-reference/03-connector/python.mdx new file mode 100644 index 0000000000000000000000000000000000000000..6608fb7bd21ab586216d82c1b137830576a1e432 --- /dev/null +++ b/docs-cn/14-reference/03-connector/python.mdx @@ -0,0 +1,349 @@ +--- +sidebar_position: 3 +sidebar_label: Python +title: TDengine Python Connector +description: "taospy 是 TDengine 的官方 Python 连接器。taospy 提供了丰富的 API, 使得 Python 应用可以很方便地使用 TDengine。tasopy 对 TDengine 的原生接口和 REST 接口都进行了封装, 分别对应 tasopy 的两个子模块:tasos 和 taosrest。除了对原生接口和 REST 接口的封装,taospy 还提供了符合 Python 数据访问规范(PEP 249)的编程接口。这使得 taospy 和很多第三方工具集成变得简单,比如 SQLAlchemy 和 pandas" +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +`taospy` 是 TDengine 的官方 Python 连接器。`taospy` 提供了丰富的 API, 使得 Python 应用可以很方便地使用 TDengine。`taospy` 对 TDengine 的[原生接口](/reference/connector/cpp)和 [REST 接口](/reference/rest-api)都进行了封装, 分别对应 `taospy` 包的 `taos` 模块 和 `taosrest` 模块。 +除了对原生接口和 REST 接口的封装,`taospy` 还提供了符合 [Python 数据访问规范(PEP 249)](https://peps.python.org/pep-0249/) 的编程接口。这使得 `taospy` 和很多第三方工具集成变得简单,比如 [SQLAlchemy](https://www.sqlalchemy.org/) 和 [pandas](https://pandas.pydata.org/)。 + +使用客户端驱动提供的原生接口直接与服务端建立的连接的方式下文中称为“原生连接”;使用 taosAdapter 提供的 REST 接口与服务端建立的连接的方式下文中称为“REST 连接”。 + +Python 连接器的源码托管在 [GitHub](https://github.com/taosdata/taos-connector-python)。 + +## 支持的平台 + +- 原生连接[支持的平台](/reference/connector/#支持的平台)和 TDengine 客户端支持的平台一致。 +- REST 连接支持所有能运行 Python 的平台。 + +## 版本选择 + +无论使用什么版本的 TDengine 都建议使用最新版本的 `taospy`。 + +## 支持的功能 + +- 原生连接支持 TDeingine 的所有核心功能, 包括: 连接管理、执行 SQL、参数绑定、订阅、无模式写入(schemaless)。 +- REST 连接支持的功能包括:连接管理、执行 SQL。 (通过执行 SQL 可以: 管理数据库、管理表和超级表、写入数据、查询数据、创建连续查询等)。 + +## 安装 + +### 准备 + +1. 安装 Python。建议使用 Python >= 3.6。如果系统上还没有 Python 可参考 [Python BeginnersGuide](https://wiki.python.org/moin/BeginnersGuide/Download) 安装。 +2. 安装 [pip](https://pypi.org/project/pip/)。大部分情况下 Python 的安装包都自带了 pip 工具, 如果没有请参考 [pip docuemntation](https://pip.pypa.io/en/stable/installation/) 安装。 +3. 如果使用原生连接,还需[安装客户端驱动](../#安装客户端驱动)。客户端软件包含了 TDengine 客户端动态链接库(libtaos.so 或 taos.dll) 和 TDengine CLI。 + +### 使用 pip 安装 + +#### 卸载旧版本 + +如果以前安装过旧版本的 Python 连接器, 请提前卸载。 + +``` +pip3 uninstall taos taospy +``` + +:::note +较早的 TDengine 客户端软件包含了 Python 连接器。如果从客户端软件的安装目录安装了 Python 连接器,那么对应的 Python 包名是 `taos`。 所以上述卸载命令包含了 `taos`, 不存在也没关系。 + +::: + +#### 安装 `taospy` + + + + +安装最新版本 + +``` +pip3 install taospy +``` + +也可以指定某个特定版本安装。 + +``` +pip3 install taospy==2.3.0 +``` + + + + +``` +pip3 install git+https://github.com/taosdata/taos-connector-python.git +``` + + + + +### 安装验证 + + + + +对于原生连接,需要验证客户端驱动和 Python 连接器本身是否都正确安装。如果能成功导入 `taos` 模块,则说明已经正确安装了客户端驱动和 Python 连接器。可在 Python 交互式 Shell 中输入: + +```python +import taos +``` + + + + +对于 REST 连接,只需验证是否能成功导入 `taosrest` 模块。可在 Python 交互式 Shell 中输入: + +```python +import taosrest +``` + + + + +:::tip +如果系统上有多个版本的 Python,则可能有多个 `pip` 命令。要确保使用的 `pip` 命令路径是正确的。上面我们用 `pip3` 命令安装,排除了使用 Python 2.x 版本对应的 `pip` 的可能性。但是如果系统上有多个 Python 3.x 版本,仍需检查安装路径是否正确。最简单的验证方式是,在命令再次输入 `pip3 install taospy`, 就会打印出 `taospy` 的具体安装位置,比如在 Windows 上: + +``` +C:\> pip3 install taospy +Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple +Requirement already satisfied: taospy in c:\users\username\appdata\local\programs\python\python310\lib\site-packages (2.3.0) +``` + +::: + +## 建立连接 + +### 连通性测试 + +在用连接器建立连接之前,建议先测试本地 TDengine CLI 到 TDengine 集群的连通性。 + + + + +请确保 TDengine 集群已经启动, 且集群中机器的 FQDN (如果启动的是单机版,FQDN 默认为 hostname)在本机能够解析, 可用 `ping` 命令进行测试: + +``` +ping +``` + +然后测试用 TDengine CLI 能否正常连接集群: + +``` +taos -h -p +``` + +上面的 FQDN 可以为集群中任意一个 dnode 的 FQDN, PORT 为这个 dnode 对应的 serverPort。 + + + + +对于 REST 连接, 除了确保集群已经启动,还要确保 taosAdapter 组件已经启动。可以使用如下 curl 命令测试: + +``` +curl -u root:taosdata http://:/rest/sql -d "select server_version()" +``` + +上面的 FQDN 为运行 taosAdapter 的机器的 FQDN, PORT 为 taosAdapter 配置的监听端口, 默认为 6041。 +如果测试成功,会输出服务器版本信息,比如: + +```json +{ + "status": "succ", + "head": ["server_version()"], + "column_meta": [["server_version()", 8, 8]], + "data": [["2.4.0.16"]], + "rows": 1 +} +``` + + + + +### 使用连接器建立连接 + +以下示例代码假设 TDengine 安装在本机, 且 FQDN 和 serverPort 都使用了默认配置。 + + + + +```python +{{#include docs-examples/python/connect_native_reference.py}} +``` + +`connect` 函数的所有参数都是可选的关键字参数。下面是连接参数的具体说明: + +- `host` : 要连接的节点的 FQDN。 没有默认值。如果不同提供此参数,则会连接客户端配置文件中的 firstEP。 +- `user` :TDengine 用户名。 默认值是 root。 +- `password` : TDengine 用户密码。 默认值是 taosdata。 +- `port` : 要连接的数据节点的起始端口,即 serverPort 配置。默认值是 6030。只有在提供了 host 参数的时候,这个参数才生效。 +- `config` : 客户端配置文件路径。 在 Windows 系统上默认是 `C:\TDengine\cfg`。 在 Linux 系统上默认是 `/etc/taos/`。 +- `timezone` : 查询结果中 TIMESTAMP 类型的数据,转换为 python 的 datetime 对象时使用的时区。默认为本地时区。 + +:::warning +`config` 和 `timezone` 都是进程级别的配置。建议一个进程建立的所有连接都使用相同的参数值。否则可能产生无法预知的错误。 +::: + +:::tip +`connect` 函数返回 `taos.TaosConnection` 实例。 在客户端多线程的场景下,推荐每个线程申请一个独立的连接实例,而不建议多线程共享一个连接。 + +::: + + + + +```python +{{#include docs-examples/python/connect_rest_examples.py:connect}} +``` + +`connect()` 函数的所有参数都是可选的关键字参数。下面是连接参数的具体说明: + +- `host`: 要连接的主机。默认是 localhost。 +- `user`: TDenigne 用户名。默认是 root。 +- `password`: TDeingine 用户密码。默认是 taosdata。 +- `port`: taosAdapter REST 服务监听端口。默认是 6041. +- `timeout`: HTTP 请求超时时间。单位为秒。默认为 `socket._GLOBAL_DEFAULT_TIMEOUT`。 一般无需配置。 + + + + +## 示例程序 + +### 基本使用 + + + + +##### TaosConnection 类的使用 + +`TaosConnection` 类既包含对 PEP249 Connection 接口的实现(如:`cursor`方法和 `close` 方法),也包含很多扩展功能(如: `execute`、 `query`、`schemaless_insert` 和 `subscribe` 方法。 + +```python title="execute 方法" +{{#include docs-examples/python/connection_usage_native_reference.py:insert}} +``` + +```python title="query 方法" +{{#include docs-examples/python/connection_usage_native_reference.py:query}} +``` + +:::tip +查询结果只能获取一次。比如上面的示例中 `fetch_all()` 和 `fetch_all_into_dict()` 只能用一个。重复获取得到的结果为空列表。 +::: + +##### TaosResult 类的使用 + +上面 `TaosConnection` 类的使用示例中,我们已经展示了两种获取查询结果的方法: `fetch_all()` 和 `fetch_all_into_dict()`。除此之外 `TaosResult` 还提供了按行迭代(`rows_iter`)或按数据块迭代(`blocks_iter`)结果集的方法。在查询数据量较大的场景,使用这两个方法会更高效。 + +```python title="blocks_iter 方法" +{{#include docs-examples/python/result_set_examples.py}} +``` +##### TaosCursor 类的使用 + +`TaosConnection` 类和 `TaosResult` 类已经实现了原生接口的所有功能。如果你对 PEP249 规范中的接口比较熟悉也可以使用 `TaosCursor` 类提供的方法。 + +```python title="TaosCursor 的使用" +{{#include docs-examples/python/cursor_usage_native_reference.py}} +``` + +:::note +TaosCursor 类使用原生连接进行写入、查询操作。在客户端多线程的场景下,这个游标实例必须保持线程独享,不能跨线程共享使用,否则会导致返回结果出现错误。 + +::: + + + + +##### TaosRestCursor 类的使用 + +`TaosRestCursor` 类是对 PEP249 Cursor 接口的实现。 + +```python title="TaosRestCursor 的使用" +{{#include docs-examples/python/connect_rest_examples.py:basic}} +``` +- `cursor.execute` : 用来执行任意 SQL 语句。 +- `cursor.rowcount`: 对于写入操作返回写入成功记录数。对于查询操作,返回结果集行数。 +- `cursor.description` : 返回字段的描述信息。关于描述信息的具体格式请参考[TaosRestCursor](https://docs.taosdata.com/api/taospy/taosrest/cursor.html)。 + +##### RestClient 类的使用 + +`RestClient` 类是对于 [REST API](/reference/rest-api) 的直接封装。它只包含一个 `sql()` 方法用于执行任意 SQL 语句, 并返回执行结果。 + +```python title="RestClient 的使用" +{{#include docs-examples/python/rest_client_example.py}} +``` + +对于 `sql()` 方法更详细的介绍, 请参考 [RestClient](https://docs.taosdata.com/api/taospy/taosrest/restclient.html)。 + + + + + + +### 与 pandas 一起使用 + + + + +```python +{{#include docs-examples/python/conn_native_pandas.py}} +``` + + + + +```python +{{#include docs-examples/python/conn_rest_pandas.py}} +``` + + + + +### 其它示例程序 + +| 示例程序链接 | 示例程序内容 | +| ------------------------------------------------------------------------------------------------------------- | ----------------------- | +| [bind_multi.py](https://github.com/taosdata/taos-connector-python/blob/main/examples/bind-multi.py) | 参数绑定, 一次绑定多行 | +| [bind_row.py](https://github.com/taosdata/taos-connector-python/blob/main/examples/bind-row.py) | 参数绑定,一次绑定一行 | +| [insert_lines.py](https://github.com/taosdata/taos-connector-python/blob/main/examples/insert-lines.py) | InfluxDB 行协议写入 | +| [json_tag.py](https://github.com/taosdata/taos-connector-python/blob/main/examples/json-tag.py) | 使用 JSON 类型的标签 | +| [subscribe-async.py](https://github.com/taosdata/taos-connector-python/blob/main/examples/subscribe-async.py) | 异步订阅 | +| [subscribe-sync.py](https://github.com/taosdata/taos-connector-python/blob/main/examples/subscribe-sync.py) | 同步订阅 | + +## 其它说明 + +### 异常处理 + +所有数据库操作如果出现异常,都会直接抛出来。由应用程序负责异常处理。比如: + +```python +{{#include docs-examples/python/handle_exception.py}} +``` + +### 关于纳秒 (nanosecond) + +由于目前 Python 对 nanosecond 支持的不完善(见下面的链接),目前的实现方式是在 nanosecond 精度时返回整数,而不是 ms 和 us 返回的 datetime 类型,应用开发者需要自行处理,建议使用 pandas 的 to_datetime()。未来如果 Python 正式完整支持了纳秒,Python 连接器可能会修改相关接口。 + +1. https://stackoverflow.com/questions/10611328/parsing-datetime-strings-containing-nanoseconds +2. https://www.python.org/dev/peps/pep-0564/ + + +## 常见问题 + +欢迎[提问或报告问题](https://github.com/taosdata/taos-connector-python/issues)。 + +## 重要更新 + +| 连接器版本 | 重要更新 | 发布日期 | +| ---------- | --------------------------------------------------------------------------------- | ---------- | +| 2.3.1 | 1. support TDengine REST API
2. remove support for Python version below 3.6 | 2022-04-28 | +| 2.2.5 | support timezone option when connect | 2022-04-13 | +| 2.2.2 | support sqlalchemy dialect plugin | 2022-03-28 | + + +[**Release Notes**](https://github.com/taosdata/taos-connector-python/releases) + +## API 参考 + +- [taos](https://docs.taosdata.com/api/taospy/taos/) +- [taosrest](https://docs.taosdata.com/api/taospy/taosrest) diff --git a/docs-cn/14-reference/03-connector/rust.mdx b/docs-cn/14-reference/03-connector/rust.mdx new file mode 100644 index 0000000000000000000000000000000000000000..25a8409b6e6faca651d1eaf3e02fbd4a0199c557 --- /dev/null +++ b/docs-cn/14-reference/03-connector/rust.mdx @@ -0,0 +1,388 @@ +--- +toc_max_heading_level: 4 +sidebar_position: 5 +sidebar_label: Rust +title: TDengine Rust Connector +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +import Preparition from "./_preparition.mdx" +import RustInsert from "../../07-develop/03-insert-data/_rust_sql.mdx" +import RustInfluxLine from "../../07-develop/03-insert-data/_rust_line.mdx" +import RustOpenTSDBTelnet from "../../07-develop/03-insert-data/_rust_opts_telnet.mdx" +import RustOpenTSDBJson from "../../07-develop/03-insert-data/_rust_opts_json.mdx" +import RustQuery from "../../07-develop/04-query-data/_rust.mdx" + +[![Crates.io](https://img.shields.io/crates/v/libtaos)](https://crates.io/crates/libtaos) ![Crates.io](https://img.shields.io/crates/d/libtaos) [![docs.rs](https://img.shields.io/docsrs/libtaos)](https://docs.rs/libtaos) + +`libtaos` 是 TDengine 的官方 Rust 语言连接器。Rust 开发人员可以通过它开发存取 TDengine 数据库的应用软件。 + +`libtaos` 提供两种建立连接的方式。一种是**原生连接**,它通过 TDengine 客户端驱动程序(taosc)连接 TDengine 运行实例。另外一种是 **REST 连接**,它通过 taosAdapter 的 REST 接口连接 TDengine 运行实例。你可以通过不同的 “特性(即 Cargo 关键字 features)” 来指定使用哪种连接器。REST 连接支持任何平台,但原生连接支持所有 TDengine 客户端能运行的平台。 + +`libtaos` 的源码托管在 [GitHub](https://github.com/taosdata/libtaos-rs)。 + +## 支持的平台 + +原生连接支持的平台和 TDengine 客户端驱动支持的平台一致。 +REST 连接支持所有能运行 Rust 的平台。 + +## 版本支持 + +请参考[版本支持列表](/reference/connector#版本支持) + +Rust 连接器仍然在快速开发中,1.0 之前无法保证其向后兼容。建议使用 2.4 版本以上的 TDengine,以避免已知问题。 + +## 安装 + +### 安装前准备 +* 安装 Rust 开发工具链 +* 如果使用原生连接,请安装 TDengine 客户端驱动,具体步骤请参考[安装客户端驱动](/reference/connector#安装客户端驱动) + +### 添加 libtaos 依赖 + +根据选择的连接方式,按照如下说明在 [Rust](https://rust-lang.org) 项目中添加 [libtaos][libtaos] 依赖: + + + + +在 `Cargo.toml` 文件中添加 [libtaos][libtaos]: + +```toml +[dependencies] +# use default feature +libtaos = "*" +``` + + + + +在 `Cargo.toml` 文件中添加 [libtaos][libtaos],并启用 `rest` 特性。 + +```toml +[dependencies] +# use rest feature +libtaos = { version = "*", features = ["rest"]} +``` + + + + + +### 使用连接池 + +请在 `Cargo.toml` 中启用 `r2d2` 特性。 + +```toml +[dependencies] +# with taosc +libtaos = { version = "*", features = ["r2d2"] } +# or rest +libtaos = { version = "*", features = ["rest", "r2d2"] } +``` + +## 建立连接 + +[TaosCfgBuilder] 为使用者提供构造器形式的 API,以便于后续创建连接或使用连接池。 + +```rust +let cfg: TaosCfg = TaosCfgBuilder::default() + .ip("127.0.0.1") + .user("root") + .pass("taosdata") + .db("log") // do not set if not require a default database. + .port(6030u16) + .build() + .expect("TaosCfg builder error"); +} +``` + +现在您可以使用该对象创建连接: + +```rust +let conn = cfg.connect()?; +``` + +连接对象可以创建多个: + +```rust +let conn = cfg.connect()?; +let conn2 = cfg.connect()?; +``` + +可以在应用中使用连接池: + +```rust +let pool = r2d2::Pool::builder() + .max_size(10000) // max connections + .build(cfg)?; + +// ... +// Use pool to get connection +let conn = pool.get()?; +``` + +之后您可以对数据库进行相关操作: + +```rust +async fn demo() -> Result<(), Error> { + // get connection ... + + // create database + conn.exec("create database if not exists demo").await?; + // change database context + conn.exec("use demo").await?; + // create table + conn.exec("create table if not exists tb1 (ts timestamp, v int)").await?; + // insert + conn.exec("insert into tb1 values(now, 1)").await?; + // query + let rows = conn.query("select * from tb1").await?; + for row in rows.rows { + println!("{}", row.into_iter().join(",")); + } +} +``` + +## 使用示例 + +### 写入数据 + +#### SQL 写入 + + + +#### InfluxDB 行协议写入 + + + +#### OpenTSDB Telnet 行协议写入 + + + +#### OpenTSDB JSON 行协议写入 + + + +### 查询数据 + + + +### 更多示例程序 + +| 程序路径 | 程序说明 | +| -------------- | ----------------------------------------------------------------------------- | +| [demo.rs] | 基本API 使用示例 | +| [bailongma-rs] | 使用 TDengine 作为存储后端的 Prometheus 远程存储 API 适配器,使用 r2d2 连接池 | + +## API 参考 + +### 连接构造器 API + +[Builder Pattern](https://doc.rust-lang.org/1.0.0/style/ownership/builders.html) 构造器模式是 Rust 处理复杂数据类型或可选配置类型的解决方案。[libtaos] 实现中,使用连接构造器 [TaosCfgBuilder] 作为 TDengine Rust 连接器的入口。[TaosCfgBuilder] 提供对服务器、端口、数据库、用户名和密码等的可选配置。 + +使用 `default()` 方法可以构建一个默认参数的 [TaosCfg],用于后续连接数据库或建立连接池。 + +```rust +let cfg = TaosCfgBuilder::default().build()?; +``` + +使用构造器模式,用户可按需设置: + +```rust +let cfg = TaosCfgBuilder::default() + .ip("127.0.0.1") + .user("root") + .pass("taosdata") + .db("log") + .port(6030u16) + .build()?; +``` + +使用 [TaosCfg] 对象创建 TDengine 连接: + +```rust +let conn: Taos = cfg.connect(); +``` + +### 连接池 + +在复杂应用中,建议启用连接池。[libtaos] 的连接池使用 [r2d2] 实现。 + +如下,可以生成一个默认参数的连接池。 + +```rust +let pool = r2d2::Pool::new(cfg)?; +``` + +同样可以使用连接池的构造器,对连接池参数进行设置: + +```rust + use std::time::Duration; + let pool = r2d2::Pool::builder() + .max_size(5000) // max connections + .max_lifetime(Some(Duration::from_minutes(100))) // lifetime of each connection + .min_idle(Some(1000)) // minimal idle connections + .connection_timeout(Duration::from_minutes(2)) + .build(cfg); +``` + +在应用代码中,使用 `pool.get()?` 来获取一个连接对象 [Taos]。 + +```rust +let taos = pool.get()?; +``` + +### 连接 + +[Taos] 结构体是 [libtaos] 中的连接管理者,主要提供了两个 API: + +1. `exec`: 执行某个非查询类 SQL 语句,例如 `CREATE`,`ALTER`,`INSERT` 等。 + + ```rust + taos.exec().await?; + ``` + +2. `query`:执行查询语句,返回 [TaosQueryData] 对象。 + + ```rust + let q = taos.query("select * from log.logs").await?; + ``` + + [TaosQueryData] 对象存储了查询结果数据和返回的列的基本信息(列名,类型,长度): + + 列信息使用 [ColumnMeta] 存储: + + ```rust + let cols = &q.column_meta; + for col in cols { + println!("name: {}, type: {:?}, bytes: {}", col.name, col.type_, col.bytes); + } + ``` + + 逐行获取数据: + + ```rust + for (i, row) in q.rows.iter().enumerate() { + for (j, cell) in row.iter().enumerate() { + println!("cell({}, {}) data: {}", i, j, cell); + } + } + ``` + +需要注意的是,需要使用 Rust 异步函数和异步运行时。 + +[Taos] 提供部分 SQL 的 Rust 方法化以减少 `format!` 代码块的频率: + +- `.describe(table: &str)`: 执行 `DESCRIBE` 并返回一个 Rust 数据结构。 +- `.create_database(database: &str)`: 执行 `CREATE DATABASE` 语句。 +- `.use_database(database: &str)`: 执行 `USE` 语句。 + +除此之外,该结构也是 [参数绑定](#参数绑定接口) 和 [行协议接口](#行协议接口) 的入口,使用方法请参考具体的 API 说明。 + +### 参数绑定接口 + +与 C 接口类似,Rust 提供参数绑定接口。首先,通过 [Taos] 对象创建一个 SQL 语句的参数绑定对象 [Stmt]: + +```rust +let mut stmt: Stmt = taos.stmt("insert into ? values(?,?)")?; +``` + +参数绑定对象提供了一组接口用于实现参数绑定: + +##### `.set_tbname(tbname: impl ToCString)` + +用于绑定表名。 + +##### `.set_tbname_tags(tbname: impl ToCString, tags: impl IntoParams)` + +当 SQL 语句使用超级表时,用于绑定子表表名和标签值: + +```rust +let mut stmt = taos.stmt("insert into ? using stb0 tags(?) values(?,?)")?; +// tags can be created with any supported type, here is an example using JSON +let v = Field::Json(serde_json::from_str("{\"tag1\":\"一二三四五六七八九十\"}").unwrap()); +stmt.set_tbname_tags("tb0", [&tag])?; +``` + +##### `.bind(params: impl IntoParams)` + +用于绑定值类型。使用 [Field] 结构体构建需要的类型并绑定: + +```rust +let ts = Field::Timestamp(Timestamp::now()); +let value = Field::Float(0.0); +stmt.bind(vec![ts, value].iter())?; +``` + +##### `.execute()` + +执行 SQL。[Stmt] 对象可以复用,在执行后可以重新绑定并执行。 + +```rust +stmt.execute()?; + +// next bind cycle. +//stmt.set_tbname()?; +//stmt.bind()?; +//stmt.execute()?; +``` + +### 行协议接口 + +行协议接口支持多种模式和不同精度,需要引入 schemaless 模块中的常量以进行设置: + +```rust +use libtaos::*; +use libtaos::schemaless::*; +``` + +- InfluxDB 行协议 + + ```rust + let lines = [ + "st,t1=abc,t2=def,t3=anything c1=3i64,c3=L\"pass\",c2=false 1626006833639000000" + "st,t1=abc,t2=def,t3=anything c1=3i64,c3=L\"abc\",c4=4f64 1626006833639000000" + ]; + taos.schemaless_insert(&lines, TSDB_SML_LINE_PROTOCOL, TSDB_SML_TIMESTAMP_NANOSECONDS)?; + ``` + +- OpenTSDB Telnet 协议 + + ```rust + let lines = ["sys.if.bytes.out 1479496100 1.3E3 host=web01 interface=eth0"]; + taos.schemaless_insert(&lines, TSDB_SML_LINE_PROTOCOL, TSDB_SML_TIMESTAMP_SECONDS)?; + ``` + +- OpenTSDB JSON 协议 + + ```rust + let lines = [r#" + { + "metric": "st", + "timestamp": 1626006833, + "value": 10, + "tags": { + "t1": true, + "t2": false, + "t3": 10, + "t4": "123_abc_.!@#$%^&*:;,./?|+-=()[]{}<>" + } + }"#]; + taos.schemaless_insert(&lines, TSDB_SML_LINE_PROTOCOL, TSDB_SML_TIMESTAMP_SECONDS)?; + ``` + +其他相关结构体 API 使用说明请移步 Rust 文档托管网页:。 + +[libtaos]: https://github.com/taosdata/libtaos-rs +[tdengine]: https://github.com/taosdata/TDengine +[bailongma-rs]: https://github.com/taosdata/bailongma-rs +[r2d2]: https://crates.io/crates/r2d2 +[demo.rs]: https://github.com/taosdata/libtaos-rs/blob/main/examples/demo.rs +[TaosCfgBuilder]: https://docs.rs/libtaos/latest/libtaos/struct.TaosCfgBuilder.html +[TaosCfg]: https://docs.rs/libtaos/latest/libtaos/struct.TaosCfg.html +[Taos]: https://docs.rs/libtaos/latest/libtaos/struct.Taos.html +[TaosQueryData]: https://docs.rs/libtaos/latest/libtaos/field/struct.TaosQueryData.html +[Field]: https://docs.rs/libtaos/latest/libtaos/field/enum.Field.html +[Stmt]: https://docs.rs/libtaos/latest/libtaos/stmt/struct.Stmt.html diff --git a/docs-cn/14-reference/03-connector/tdengine-jdbc-connector.png b/docs-cn/14-reference/03-connector/tdengine-jdbc-connector.png new file mode 100644 index 0000000000000000000000000000000000000000..1cb8401ea30b01d8db652ed4ea70ecc511de7461 Binary files /dev/null and b/docs-cn/14-reference/03-connector/tdengine-jdbc-connector.png differ diff --git a/docs-cn/14-reference/04-taosadapter.md b/docs-cn/14-reference/04-taosadapter.md new file mode 100644 index 0000000000000000000000000000000000000000..90a31ec94c94559311e2c91cd34f75af7e87e9a0 --- /dev/null +++ b/docs-cn/14-reference/04-taosadapter.md @@ -0,0 +1,338 @@ +--- +title: "taosAdapter" +description: "taosAdapter 是一个 TDengine 的配套工具,是 TDengine 集群和应用程序之间的桥梁和适配器。它提供了一种易于使用和高效的方式来直接从数据收集代理软件(如 Telegraf、StatsD、collectd 等)摄取数据。它还提供了 InfluxDB/OpenTSDB 兼容的数据摄取接口,允许 InfluxDB/OpenTSDB 应用程序无缝移植到 TDengine" +sidebar_label: "taosAdapter" +--- + +import Prometheus from "./_prometheus.mdx" +import CollectD from "./_collectd.mdx" +import StatsD from "./_statsd.mdx" +import Icinga2 from "./_icinga2.mdx" +import TCollector from "./_tcollector.mdx" + +taosAdapter 是一个 TDengine 的配套工具,是 TDengine 集群和应用程序之间的桥梁和适配器。它提供了一种易于使用和高效的方式来直接从数据收集代理软件(如 Telegraf、StatsD、collectd 等)摄取数据。它还提供了 InfluxDB/OpenTSDB 兼容的数据摄取接口,允许 InfluxDB/OpenTSDB 应用程序无缝移植到 TDengine。 + +taosAdapter 提供以下功能: + +- RESTful 接口 +- 兼容 InfluxDB v1 写接口 +- 兼容 OpenTSDB JSON 和 telnet 格式写入 +- 无缝连接到 Telegraf +- 无缝连接到 collectd +- 无缝连接到 StatsD +- 支持 Prometheus remote_read 和 remote_write + +## taosAdapter 架构图 + +![taosAdapter Architecture](taosAdapter-architecture.png) + +## taosAdapter 部署方法 + +### 安装 taosAdapter + +taosAdapter 从 TDengine v2.4.0.0 版本开始成为 TDengine 服务端软件 的一部分,如果您使用 TDengine server 您不需要任何额外的步骤来安装 taosAdapter。您可以从[涛思数据官方网站](https://taosdata.com/cn/all-downloads/)下载 TDengine server(taosAdapter 包含在 v2.4.0.0 及以上版本)安装包。如果需要将 taosAdapter 分离部署在 TDengine server 之外的服务器上,则应该在该服务器上安装完整的 TDengine 来安装 taosAdapter。如果您需要使用源代码编译生成 taosAdapter,您可以参考[构建 taosAdapter](https://github.com/taosdata/taosadapter/blob/develop/BUILD-CN.md)文档。 + +### start/stop taosAdapter + +在 Linux 系统上 taosAdapter 服务默认由 systemd 管理。使用命令 `systemctl start taosadapter` 可以启动 taosAdapter 服务。使用命令 `systemctl stop taosadapter` 可以停止 taosAdapter 服务。 + +### 移除 taosAdapter + +使用命令 rmtaos 可以移除包括 taosAdapter 在内的 TDengine server 软件。 + +### 升级 taosAdapter + +taosAdapter 和 TDengine server 需要使用相同版本。请通过升级 TDengine server 来升级 taosAdapter。 +与 taosd 分离部署的 taosAdapter 必须通过升级其所在服务器的 TDengine server 才能得到升级。 + +## taosAdapter 参数列表 + +taosAdapter 支持通过命令行参数、环境变量和配置文件来进行配置。默认配置文件是 /etc/taos/taosadapter.toml。 + +命令行参数优先于环境变量优先于配置文件,命令行用法是 arg=val,如 taosadapter -p=30000 --debug=true,详细列表如下: + +```shell +Usage of taosAdapter: + --collectd.db string collectd db name. Env "TAOS_ADAPTER_COLLECTD_DB" (default "collectd") + --collectd.enable enable collectd. Env "TAOS_ADAPTER_COLLECTD_ENABLE" (default true) + --collectd.password string collectd password. Env "TAOS_ADAPTER_COLLECTD_PASSWORD" (default "taosdata") + --collectd.port int collectd server port. Env "TAOS_ADAPTER_COLLECTD_PORT" (default 6045) + --collectd.user string collectd user. Env "TAOS_ADAPTER_COLLECTD_USER" (default "root") + --collectd.worker int collectd write worker. Env "TAOS_ADAPTER_COLLECTD_WORKER" (default 10) + -c, --config string config path default /etc/taos/taosadapter.toml + --cors.allowAllOrigins cors allow all origins. Env "TAOS_ADAPTER_CORS_ALLOW_ALL_ORIGINS" (default true) + --cors.allowCredentials cors allow credentials. Env "TAOS_ADAPTER_CORS_ALLOW_Credentials" + --cors.allowHeaders stringArray cors allow HEADERS. Env "TAOS_ADAPTER_ALLOW_HEADERS" + --cors.allowOrigins stringArray cors allow origins. Env "TAOS_ADAPTER_ALLOW_ORIGINS" + --cors.allowWebSockets cors allow WebSockets. Env "TAOS_ADAPTER_CORS_ALLOW_WebSockets" + --cors.exposeHeaders stringArray cors expose headers. Env "TAOS_ADAPTER_Expose_Headers" + --debug enable debug mode. Env "TAOS_ADAPTER_DEBUG" + --help Print this help message and exit + --influxdb.enable enable influxdb. Env "TAOS_ADAPTER_INFLUXDB_ENABLE" (default true) + --log.path string log path. Env "TAOS_ADAPTER_LOG_PATH" (default "/var/log/taos") + --log.rotationCount uint log rotation count. Env "TAOS_ADAPTER_LOG_ROTATION_COUNT" (default 30) + --log.rotationSize string log rotation size(KB MB GB), must be a positive integer. Env "TAOS_ADAPTER_LOG_ROTATION_SIZE" (default "1GB") + --log.rotationTime duration log rotation time. Env "TAOS_ADAPTER_LOG_ROTATION_TIME" (default 24h0m0s) + --logLevel string log level (panic fatal error warn warning info debug trace). Env "TAOS_ADAPTER_LOG_LEVEL" (default "info") + --monitor.collectDuration duration Set monitor duration. Env "TAOS_MONITOR_COLLECT_DURATION" (default 3s) + --monitor.identity string The identity of the current instance, or 'hostname:port' if it is empty. Env "TAOS_MONITOR_IDENTITY" + --monitor.incgroup Whether running in cgroup. Env "TAOS_MONITOR_INCGROUP" + --monitor.password string TDengine password. Env "TAOS_MONITOR_PASSWORD" (default "taosdata") + --monitor.pauseAllMemoryThreshold float Memory percentage threshold for pause all. Env "TAOS_MONITOR_PAUSE_ALL_MEMORY_THRESHOLD" (default 80) + --monitor.pauseQueryMemoryThreshold float Memory percentage threshold for pause query. Env "TAOS_MONITOR_PAUSE_QUERY_MEMORY_THRESHOLD" (default 70) + --monitor.user string TDengine user. Env "TAOS_MONITOR_USER" (default "root") + --monitor.writeInterval duration Set write to TDengine interval. Env "TAOS_MONITOR_WRITE_INTERVAL" (default 30s) + --monitor.writeToTD Whether write metrics to TDengine. Env "TAOS_MONITOR_WRITE_TO_TD" (default true) + --node_exporter.caCertFile string node_exporter ca cert file path. Env "TAOS_ADAPTER_NODE_EXPORTER_CA_CERT_FILE" + --node_exporter.certFile string node_exporter cert file path. Env "TAOS_ADAPTER_NODE_EXPORTER_CERT_FILE" + --node_exporter.db string node_exporter db name. Env "TAOS_ADAPTER_NODE_EXPORTER_DB" (default "node_exporter") + --node_exporter.enable enable node_exporter. Env "TAOS_ADAPTER_NODE_EXPORTER_ENABLE" + --node_exporter.gatherDuration duration node_exporter gather duration. Env "TAOS_ADAPTER_NODE_EXPORTER_GATHER_DURATION" (default 5s) + --node_exporter.httpBearerTokenString string node_exporter http bearer token. Env "TAOS_ADAPTER_NODE_EXPORTER_HTTP_BEARER_TOKEN_STRING" + --node_exporter.httpPassword string node_exporter http password. Env "TAOS_ADAPTER_NODE_EXPORTER_HTTP_PASSWORD" + --node_exporter.httpUsername string node_exporter http username. Env "TAOS_ADAPTER_NODE_EXPORTER_HTTP_USERNAME" + --node_exporter.insecureSkipVerify node_exporter skip ssl check. Env "TAOS_ADAPTER_NODE_EXPORTER_INSECURE_SKIP_VERIFY" (default true) + --node_exporter.keyFile string node_exporter cert key file path. Env "TAOS_ADAPTER_NODE_EXPORTER_KEY_FILE" + --node_exporter.password string node_exporter password. Env "TAOS_ADAPTER_NODE_EXPORTER_PASSWORD" (default "taosdata") + --node_exporter.responseTimeout duration node_exporter response timeout. Env "TAOS_ADAPTER_NODE_EXPORTER_RESPONSE_TIMEOUT" (default 5s) + --node_exporter.urls strings node_exporter urls. Env "TAOS_ADAPTER_NODE_EXPORTER_URLS" (default [http://localhost:9100]) + --node_exporter.user string node_exporter user. Env "TAOS_ADAPTER_NODE_EXPORTER_USER" (default "root") + --opentsdb.enable enable opentsdb. Env "TAOS_ADAPTER_OPENTSDB_ENABLE" (default true) + --opentsdb_telnet.dbs strings opentsdb_telnet db names. Env "TAOS_ADAPTER_OPENTSDB_TELNET_DBS" (default [opentsdb_telnet,collectd_tsdb,icinga2_tsdb,tcollector_tsdb]) + --opentsdb_telnet.enable enable opentsdb telnet,warning: without auth info(default false). Env "TAOS_ADAPTER_OPENTSDB_TELNET_ENABLE" + --opentsdb_telnet.maxTCPConnections int max tcp connections. Env "TAOS_ADAPTER_OPENTSDB_TELNET_MAX_TCP_CONNECTIONS" (default 250) + --opentsdb_telnet.password string opentsdb_telnet password. Env "TAOS_ADAPTER_OPENTSDB_TELNET_PASSWORD" (default "taosdata") + --opentsdb_telnet.ports ints opentsdb telnet tcp port. Env "TAOS_ADAPTER_OPENTSDB_TELNET_PORTS" (default [6046,6047,6048,6049]) + --opentsdb_telnet.tcpKeepAlive enable tcp keep alive. Env "TAOS_ADAPTER_OPENTSDB_TELNET_TCP_KEEP_ALIVE" + --opentsdb_telnet.user string opentsdb_telnet user. Env "TAOS_ADAPTER_OPENTSDB_TELNET_USER" (default "root") + --pool.idleTimeout duration Set idle connection timeout. Env "TAOS_ADAPTER_POOL_IDLE_TIMEOUT" (default 1h0m0s) + --pool.maxConnect int max connections to taosd. Env "TAOS_ADAPTER_POOL_MAX_CONNECT" (default 4000) + --pool.maxIdle int max idle connections to taosd. Env "TAOS_ADAPTER_POOL_MAX_IDLE" (default 4000) + -P, --port int http port. Env "TAOS_ADAPTER_PORT" (default 6041) + --prometheus.enable enable prometheus. Env "TAOS_ADAPTER_PROMETHEUS_ENABLE" (default true) + --restfulRowLimit int restful returns the maximum number of rows (-1 means no limit). Env "TAOS_ADAPTER_RESTFUL_ROW_LIMIT" (default -1) + --ssl.certFile string ssl cert file path. Env "TAOS_ADAPTER_SSL_CERT_FILE" + --ssl.enable enable ssl. Env "TAOS_ADAPTER_SSL_ENABLE" + --ssl.keyFile string ssl key file path. Env "TAOS_ADAPTER_SSL_KEY_FILE" + --statsd.allowPendingMessages int statsd allow pending messages. Env "TAOS_ADAPTER_STATSD_ALLOW_PENDING_MESSAGES" (default 50000) + --statsd.db string statsd db name. Env "TAOS_ADAPTER_STATSD_DB" (default "statsd") + --statsd.deleteCounters statsd delete counter cache after gather. Env "TAOS_ADAPTER_STATSD_DELETE_COUNTERS" (default true) + --statsd.deleteGauges statsd delete gauge cache after gather. Env "TAOS_ADAPTER_STATSD_DELETE_GAUGES" (default true) + --statsd.deleteSets statsd delete set cache after gather. Env "TAOS_ADAPTER_STATSD_DELETE_SETS" (default true) + --statsd.deleteTimings statsd delete timing cache after gather. Env "TAOS_ADAPTER_STATSD_DELETE_TIMINGS" (default true) + --statsd.enable enable statsd. Env "TAOS_ADAPTER_STATSD_ENABLE" (default true) + --statsd.gatherInterval duration statsd gather interval. Env "TAOS_ADAPTER_STATSD_GATHER_INTERVAL" (default 5s) + --statsd.maxTCPConnections int statsd max tcp connections. Env "TAOS_ADAPTER_STATSD_MAX_TCP_CONNECTIONS" (default 250) + --statsd.password string statsd password. Env "TAOS_ADAPTER_STATSD_PASSWORD" (default "taosdata") + --statsd.port int statsd server port. Env "TAOS_ADAPTER_STATSD_PORT" (default 6044) + --statsd.protocol string statsd protocol [tcp or udp]. Env "TAOS_ADAPTER_STATSD_PROTOCOL" (default "udp") + --statsd.tcpKeepAlive enable tcp keep alive. Env "TAOS_ADAPTER_STATSD_TCP_KEEP_ALIVE" + --statsd.user string statsd user. Env "TAOS_ADAPTER_STATSD_USER" (default "root") + --statsd.worker int statsd write worker. Env "TAOS_ADAPTER_STATSD_WORKER" (default 10) + --taosConfigDir string load taos client config path. Env "TAOS_ADAPTER_TAOS_CONFIG_FILE" + --version Print the version and exit +``` + +备注: +使用浏览器进行接口调用请根据实际情况设置如下跨源资源共享(CORS)参数: + +```text +AllowAllOrigins +AllowOrigins +AllowHeaders +ExposeHeaders +AllowCredentials +AllowWebSockets +``` + +如果不通过浏览器进行接口调用无需关心这几项配置。 + +关于 CORS 协议细节请参考:[https://www.w3.org/wiki/CORS_Enabled](https://www.w3.org/wiki/CORS_Enabled) 或 [https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS)。 + +示例配置文件参见 [example/config/taosadapter.toml](https://github.com/taosdata/taosadapter/blob/develop/example/config/taosadapter.toml)。 + +## 功能列表 + +- 与 RESTful 接口兼容 + [https://www.taosdata.com/cn/documentation/connector#restful](https://www.taosdata.com/cn/documentation/connector#restful) +- 兼容 InfluxDB v1 写接口 + [https://docs.influxdata.com/influxdb/v2.0/reference/api/influxdb-1x/write/](https://docs.influxdata.com/influxdb/v2.0/reference/api/influxdb-1x/write/) +- 兼容 OpenTSDB JSON 和 telnet 格式写入 + - + - +- 与 collectd 无缝连接 + collectd 是一个系统统计收集守护程序,请访问 [https://collectd.org/](https://collectd.org/) 了解更多信息。 +- Seamless connection with StatsD + StatsD 是一个简单而强大的统计信息汇总的守护程序。请访问 [https://github.com/statsd/statsd](https://github.com/statsd/statsd) 了解更多信息。 +- 与 icinga2 的无缝连接 + icinga2 是一个收集检查结果指标和性能数据的软件。请访问 [https://icinga.com/docs/icinga-2/latest/doc/14-features/#opentsdb-writer](https://icinga.com/docs/icinga-2/latest/doc/14-features/#opentsdb-writer) 了解更多信息。 +- 与 tcollector 无缝连接 + TCollector 是一个客户端进程,从本地收集器收集数据,并将数据推送到 OpenTSDB。请访问 [http://opentsdb.net/docs/build/html/user_guide/utilities/tcollector.html](http://opentsdb.net/docs/build/html/user_guide/utilities/tcollector.html) 了解更多信息。 +- 无缝连接 node_exporter + node_export 是一个机器指标的导出器。请访问 [https://github.com/prometheus/node_exporter](https://github.com/prometheus/node_exporter) 了解更多信息。 +- 支持 Prometheus remote_read 和 remote_write + remote_read 和 remote_write 是 Prometheus 数据读写分离的集群方案。请访问[https://prometheus.io/blog/2019/10/10/remote-read-meets-streaming/#remote-apis](https://prometheus.io/blog/2019/10/10/remote-read-meets-streaming/#remote-apis) 了解更多信息。 + +## 接口 + +### TDengine RESTful 接口 + +您可以使用任何支持 http 协议的客户端通过访问 RESTful 接口地址 `http://:6041/` 来写入数据到 TDengine 或从 TDengine 中查询数据。细节请参考[官方文档](/reference/connector#restful)。支持如下 EndPoint : + +```text +/rest/sql +/rest/sqlt +/rest/sqlutc +``` + +### InfluxDB + +您可以使用任何支持 http 协议的客户端访问 Restful 接口地址 `http://:6041/` 来写入 InfluxDB 兼容格式的数据到 TDengine。EndPoint 如下: + +```text +/influxdb/v1/write +``` + +支持 InfluxDB 查询参数如下: + +- `db` 指定 TDengine 使用的数据库名 +- `precision` TDengine 使用的时间精度 +- `u` TDengine 用户名 +- `p` TDengine 密码 + +注意: 目前不支持 InfluxDB 的 token 验证方式只支持 Basic 验证和查询参数验证。 + +### OpenTSDB + +您可以使用任何支持 http 协议的客户端访问 Restful 接口地址 `http://:6041/` 来写入 OpenTSDB 兼容格式的数据到 TDengine。EndPoint 如下: + +```text +/opentsdb/v1/put/json/:db +/opentsdb/v1/put/telnet/:db +``` + +### collectd + + + +### StatsD + + + +### icinga2 OpenTSDB writer + + + +### TCollector + + + +### node_exporter + +Prometheus 使用的由\*NIX 内核暴露的硬件和操作系统指标的输出器 + +- 启用 taosAdapter 的配置 node_exporter.enable +- 设置 node_exporter 的相关配置 +- 重新启动 taosAdapter + +### prometheus + + + +## 内存使用优化方法 + +taosAdapter 将监测自身运行过程中内存使用率并通过两个阈值进行调节。有效值范围为 -1 到 100 的整数,单位为系统物理内存的百分比。 + +- pauseQueryMemoryThreshold +- pauseAllMemoryThreshold + +当超过 pauseQueryMemoryThreshold 阈值时时停止处理查询请求。 + +http 返回内容: + +- code 503 +- body "query memory exceeds threshold" + +当超过 pauseAllMemoryThreshold 阈值时停止处理所有写入和查询请求。 + +http 返回内容: + +- code 503 +- body "memory exceeds threshold" + +当内存回落到阈值之下时恢复对应功能。 + +状态检查接口 `http://:6041/-/ping` + +- 正常返回 `code 200` +- 无参数 如果内存超过 pauseAllMemoryThreshold 将返回 `code 503` +- 请求参数 `action=query` 如果内存超过 pauseQueryMemoryThreshold 或 pauseAllMemoryThreshold 将返回 `code 503` + +对应配置参数 + +```text + monitor.collectDuration 监测间隔 环境变量 "TAOS_MONITOR_COLLECT_DURATION" (默认值 3s) + monitor.incgroup 是否是cgroup中运行(容器中运行设置为 true) 环境变量 "TAOS_MONITOR_INCGROUP" + monitor.pauseAllMemoryThreshold 不再进行插入和查询的内存阈值 环境变量 "TAOS_MONITOR_PAUSE_ALL_MEMORY_THRESHOLD" (默认值 80) + monitor.pauseQueryMemoryThreshold 不再进行查询的内存阈值 环境变量 "TAOS_MONITOR_PAUSE_QUERY_MEMORY_THRESHOLD" (默认值 70) +``` + +您可以根据具体项目应用场景和运营策略进行相应调整,并建议使用运营监控软件及时进行系统内存状态监控。负载均衡器也可以通过这个接口检查 taosAdapter 运行状态。 + +## taosAdapter 监控指标 + +taosAdapter 采集 http 相关指标、cpu 百分比和内存百分比。 + +### http 接口 + +提供符合 [OpenMetrics](https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md) 接口: + +```text +http://:6041/metrics +``` + +### 写入 TDengine + +taosAdapter 支持将 http 监控、cpu 百分比和内存百分比写入 TDengine。 + +有关配置参数 + +| **配置项** | **描述** | **默认值** | +| ----------------------- | --------------------------------------------------------- | ---------- | +| monitor.collectDuration | cpu 和内存采集间隔 | 3s | +| monitor.identity | 当前 taosadapter 的标识符如果不设置将使用 'hostname:port' | | +| monitor.incgroup | 是否是 cgroup 中运行(容器中运行设置为 true) | false | +| monitor.writeToTD | 是否写入到 TDengine | true | +| monitor.user | TDengine 连接用户名 | root | +| monitor.password | TDengine 连接密码 | taosdata | +| monitor.writeInterval | 写入 TDengine 间隔 | 30s | + +## 结果返回条数限制 + +taosAdapter 通过参数 `restfulRowLimit` 来控制结果的返回条数,-1 代表无限制,默认无限制。 + +该参数控制以下接口返回 + +- `http://:6041/rest/sql` +- `http://:6041/rest/sqlt` +- `http://:6041/rest/sqlutc` +- `http://:6041/prometheus/v1/remote_read/:db` + +## 故障解决 + +您可以通过命令 `systemctl status taosadapter` 来检查 taosAdapter 运行状态。 + +您也可以通过设置 --logLevel 参数或者环境变量 TAOS_ADAPTER_LOG_LEVEL 来调节 taosAdapter 日志输出详细程度。有效值包括: panic、fatal、error、warn、warning、info、debug 以及 trace。 + +## 如何从旧版本 TDengine 迁移到 taosAdapter + +在 TDengine server 2.2.x.x 或更早期版本中,taosd 进程包含一个内嵌的 http 服务。如前面所述,taosAdapter 是一个使用 systemd 管理的独立软件,拥有自己的进程。并且两者有一些配置参数和行为是不同的,请见下表: + +| **#** | **embedded httpd** | **taosAdapter** | **comment** | +| ----- | ------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ | +| 1 | httpEnableRecordSql | --logLevel=debug | | +| 2 | httpMaxThreads | n/a | taosAdapter 自动管理线程池,无需此参数 | +| 3 | telegrafUseFieldNum | 请参考 taosAdapter telegraf 配置方法 | | +| 4 | restfulRowLimit | restfulRowLimit | 内嵌 httpd 默认输出 10240 行数据,最大允许值为 102400。taosAdapter 也提供 restfulRowLimit 但是默认不做限制。您可以根据实际场景需求进行配置 | +| 5 | httpDebugFlag | 不适用 | httpdDebugFlag 对 taosAdapter 不起作用 | +| 6 | httpDBNameMandatory | 不适用 | taosAdapter 要求 URL 中必须指定数据库名 | diff --git a/docs-cn/14-reference/05-taosbenchmark.md b/docs-cn/14-reference/05-taosbenchmark.md new file mode 100644 index 0000000000000000000000000000000000000000..f34d12a5462a7c078b9c237ee8be19a86ed250a7 --- /dev/null +++ b/docs-cn/14-reference/05-taosbenchmark.md @@ -0,0 +1,434 @@ +--- +title: taosBenchmark +sidebar_label: taosBenchmark +toc_max_heading_level: 4 +description: 'taosBenchmark (曾用名 taosdemo ) 是一个用于测试 TDengine 产品性能的工具' +--- + +## 简介 + +taosBenchmark (曾用名 taosdemo ) 是一个用于测试 TDengine 产品性能的工具。taosBenchmark 可以测试 TDengine 的插入、查询和订阅等功能的性能,它可以模拟由大量设备产生的大量数据,还可以灵活地控制数据库、超级表、标签列的数量和类型、数据列的数量和类型、子表的数量、每张子表的数据量、插入数据的时间间隔、taosBenchmark 的工作线程数量、是否以及如何插入乱序数据等。为了兼容过往用户的使用习惯,安装包提供 了 taosdemo 作为 taosBenchmark 的软链接。 + +## 安装 + +taosBenchmark 有两种安装方式: + +- 安装 TDengine 官方安装包的同时会自动安装 taosBenchmark, 详情请参考[ TDengine 安装](/operation/pkg-install)。 + +- 单独编译 taos-tools 并安装, 详情请参考 [taos-tools](https://github.com/taosdata/taos-tools) 仓库。 + +## 运行 + +### 配置和运行方式 + +taosBenchmark 支持两种配置方式:[命令行参数](#命令行参数详解) 和 [JSON 配置文件](#配置文件参数详解)。这两种方式是互斥的,在使用配置文件时只能使用一个命令行参数 `-f ` 指定配置文件。在使用命令行参数运行 taosBenchmark 并控制其行为时则不能使用 `-f` 参数而要用其它参数来进行配置。除此之外,taosBenchmark 还提供了一种特殊的运行方式,即无参数运行。 + +taosBenchmark 支持对 TDengine 做完备的性能测试,其所支持的 TDengine 功能分为三大类:写入、查询和订阅。这三种功能之间是互斥的,每次运行 taosBenchmark 只能选择其中之一。值得注意的是,所要测试的功能类型在使用命令行配置方式时是不可配置的,命令行配置方式只能测试写入性能。若要测试 TDengine 的查询和订阅性能,必须使用配置文件的方式,通过配置文件中的参数 `filetype` 指定所要测试的功能类型。 + +**在运行 taosBenchmark 之前要确保 TDengine 集群已经在正确运行。** + +### 无命令行参数运行 + +执行下列命令即可快速体验 taosBenchmark 对 TDengine 进行基于默认配置的写入性能测试。 + +```bash +taosBenchmark +``` + +在无参数运行时,taosBenchmark 默认连接 `/etc/taos` 下指定的 TDengine 集群,并在 TDengine 中创建一个名为 test 的数据库,test 数据库下创建名为 meters 的一张超级表,超级表下创建 10000 张表,每张表中写入 10000 条记录。注意,如果已有 test 数据库,这个命令会先删除该数据库后建立一个全新的 test 数据库。 + +### 使用命令行配置参数运行 + +在使用命令行参数运行 taosBenchmark 并控制其行为时,`-f ` 参数不能使用。所有配置参数都必须通过命令行指定。以下是使用命令行方式测试 taosBenchmark 写入性能的一个示例。 + +```bash +taosBenchmark -I stmt -n 200 -t 100 +``` + +上面的命令 `taosBenchmark` 将创建一个名为`test`的数据库,在其中建立一张超级表`meters`,在该超级表中建立 100 张子表并使用参数绑定的方式为每张子表插入 200 条记录。 + +### 使用配置文件运行 + +taosBenchmark 安装包中提供了配置文件的示例,位于 `/examples/taosbenchmark-json` 下 + +使用如下命令行即可运行 taosBenchmark 并通过配置文件控制其行为。 + +```bash +taosBenchmark -f +``` + +**下面是几个配置文件的示例:** + +#### 插入场景 JSON 配置文件示例 + +
+insert.json + +```json +{{#include /taos-tools/example/insert.json}} +``` + +
+ +#### 查询场景 JSON 配置文件示例 + +
+query.json + +```json +{{#include /taos-tools/example/query.json}} +``` + +
+ +#### 订阅场景 JSON 配置文件示例 + +
+subscribe.json + +```json +{{#include /taos-tools/example/subscribe.json}} +``` + +
+ +## 命令行参数详解 + +- **-f/--file ** : + 要使用的 JSON 配置文件,由该文件指定所有参数,本参数与命令行其他参数不能同时使用。没有默认值。 + +- **-c/--config-dir ** : + TDengine 集群配置文件所在的目录,默认路径是 /etc/taos 。 + +- **-h/--host ** : + 指定要连接的 TDengine 服务端的 FQDN,默认值为 localhost 。 + +- **-P/--port ** : + 要连接的 TDengine 服务器的端口号,默认值为 6030 。 + +- **-I/--interface ** : + 插入模式,可选项有 taosc, rest, stmt, sml, sml-rest, 分别对应普通写入、restful 接口写入、参数绑定接口写入、schemaless 接口写入、restful schemaless 接口写入 (由 taosAdapter 提供)。默认值为 taosc。 + +- **-u/--user ** : + 用于连接 TDengine 服务端的用户名,默认为 root 。 + +- **-p/--password ** : + 用于连接 TDengine 服务端的密码,默认值为 taosdata。 + +- **-o/--output ** : + 结果输出文件的路径,默认值为 ./output.txt。 + +- **-T/--thread ** : + 插入数据的线程数量,默认为 8 。 + +- **-B/--interlace-rows ** : + 启用交错插入模式并同时指定向每个子表每次插入的数据行数。交错插入模式是指依次向每张子表插入由本参数所指定的行数并重复这个过程,直到所有子表的数据都插入完成。默认值为 0, 即向一张子表完成数据插入后才会向下一张子表进行数据插入。 + +- **-i/--insert-interval ** : + 指定交错插入模式的插入间隔,单位为 ms,默认值为 0。 只有当 `-B/--interlace-rows` 大于 0 时才起作用。意味着数据插入线程在为每个子表插入隔行扫描记录后,会等待该值指定的时间间隔后再进行下一轮写入。 + +- **-r/--rec-per-req ** : + 每次向 TDengine 请求写入的数据行数,默认值为 30000 。 + +- **-t/--tables ** : + 指定子表的数量,默认为 10000 。 + +- **-S/--timestampstep ** : + 每个子表中插入数据的时间戳步长,单位是 ms,默认值是 1。 + +- **-n/--records ** : + 每个子表插入的记录数,默认值为 10000 。 + +- **-d/--database ** : + 所使用的数据库的名称,默认值为 test 。 + +- **-b/--data-type ** : + 超级表的数据列的类型。如果不使用则默认为有三个数据列,其类型分别为 FLOAT, INT, FLOAT 。 + +- **-l/--columns ** : + 超级表的数据列的总数量。如果同时设置了该参数和 `-b/--data-type`,则最后的结果列数为两者取大。如果本参数指定的数量大于 `-b/--data-type` 指定的列数,则未指定的列类型默认为 INT, 例如: `-l 5 -b float,double`, 那么最后的列为 `FLOAT,DOUBLE,INT,INT,INT`。如果 columns 指定的数量小于或等于 `-b/--data-type` 指定的列数,则结果为 `-b/--data-type` 指定的列和类型,例如: `-l 3 -b float,double,float,bigint`,那么最后的列为 `FLOAT,DOUBLE,FLOAT,BIGINT` 。 + +- **-A/--tag-type ** : + 超级表的标签列类型。nchar 和 binary 类型可以同时设置长度,例如: + +``` +taosBenchmark -A INT,DOUBLE,NCHAR,BINARY(16) +``` + +如果没有设置标签类型,默认是两个标签,其类型分别为 INT 和 BINARY(16)。 +注意:在有的 shell 比如 bash 命令里面 “()” 需要转义,则上述指令应为: + +``` +taosBenchmark -A INT,DOUBLE,NCHAR,BINARY\(16\) +``` + +- **-w/--binwidth **: + nchar 和 binary 类型的默认长度,默认值为 64。 + +- **-m/--table-prefix ** : + 子表名称的前缀,默认值为 "d"。 + +- **-E/--escape-character** : + 开关参数,指定在超级表和子表名称中是否使用转义字符。默认值为不使用。 + +- **-C/--chinese** : + 开关参数,指定 nchar 和 binary 是否使用 Unicode 中文字符。默认值为不使用。 + +- **-N/--normal-table** : + 开关参数,指定只创建普通表,不创建超级表。默认值为 false。仅当插入模式为 taosc, stmt, rest 模式下可以使用。 + +- **-M/--random** : + 开关参数,插入数据为生成的随机值。默认值为 false。若配置此参数,则随机生成要插入的数据。对于数值类型的 标签列/数据列,其值为该类型取值范围内的随机值。对于 NCHAR 和 BINARY 类型的 标签列/数据列,其值为指定长度范围内的随机字符串。 + +- **-x/--aggr-func** : + 开关参数,指示插入后查询聚合函数。默认值为 false。 + +- **-y/--answer-yes** : + 开关参数,要求用户在提示后确认才能继续。默认值为 false 。 + +- **-O/--disorder ** : + 指定乱序数据的百分比概率,其值域为 [0,50]。默认为 0,即没有乱序数据。 + +- **-R/--disorder-range ** : + 指定乱序数据的时间戳回退范围。所生成的乱序时间戳为非乱序情况下应该使用的时间戳减去这个范围内的一个随机值。仅在 `-O/--disorder` 指定的乱序数据百分比大于 0 时有效。 + +- **-F/--prepare_rand ** : + 生成的随机数据中唯一值的数量。若为 1 则表示所有数据都相同。默认值为 10000 。 + +- **-a/--replica ** : + 创建数据库时指定其副本数,默认值为 1 。 + +- **-V/--version** : + 显示版本信息并退出。不能与其它参数混用。 + +- **-?/--help** : + 显示帮助信息并退出。不能与其它参数混用。 + +## 配置文件参数详解 + +### 通用配置参数 + +本节所列参数适用于所有功能模式。 + +- **filetype** : 要测试的功能,可选值为 `insert`, `query` 和 `subscribe`。分别对应插入、查询和订阅功能。每个配置文件中只能指定其中之一。 +- **cfgdir** : TDengine 集群配置文件所在的目录,默认路径是 /etc/taos 。 + +- **host** : 指定要连接的 TDengine 服务端的 FQDN,默认值为 localhost。 + +- **port** : 要连接的 TDengine 服务器的端口号,默认值为 6030。 + +- **user** : 用于连接 TDengine 服务端的用户名,默认为 root。 + +- **password** : 用于连接 TDengine 服务端的密码,默认值为 taosdata。 + +### 插入场景配置参数 + +插入场景下 `filetype` 必须设置为 `insert`,该参数及其它通用参数详见[通用配置参数](#通用配置参数) + +#### 数据库相关配置参数 + +创建数据库时的相关参数在 json 配置文件中的 `dbinfo` 中配置,具体参数如下。这些参数与 TDengine 中 `create database` 时所指定的数据库参数相对应。 + +- **name** : 数据库名。 + +- **drop** : 插入前是否删除数据库,默认为 true。 + +- **replica** : 创建数据库时指定的副本数。 + +- **days** : 单个数据文件中存储数据的时间跨度,默认值为 10。 + +- **cache** : 缓存块的大小,单位是 MB,默认值是 16。 + +- **blocks** : 每个 vnode 中缓存块的数量,默认为 6。 + +- **precision** : 数据库时间精度,默认值为 "ms"。 + +- **keep** : 保留数据的天数,默认值为 3650。 + +- **minRows** : 文件块中的最小记录数,默认值为 100。 + +- **maxRows** : 文件块中的最大记录数,默认值为 4096。 + +- **comp** : 文件压缩标志,默认值为 2。 + +- **walLevel** : WAL 级别,默认为 1。 + +- **cacheLast** : 是否允许将每个表的最后一条记录保留在内存中,默认值为 0,可选值为 0,1,2,3。 + +- **quorum** : 多副本模式下的写确认数量,默认值为 1。 + +- **fsync** : 当 wal 设置为 2 时,fsync 的间隔时间,单位为 ms,默认值为 3000。 + +- **update** : 是否支持数据更新,默认值为 0, 可选值为 0, 1, 2。 + +#### 超级表相关配置参数 + +创建超级表时的相关参数在 json 配置文件中的 `super_tables` 中配置,具体参数如下表。 + +- **name**: 超级表名,必须配置,没有默认值。 +- **child_table_exists** : 子表是否已经存在,默认值为 "no",可选值为 "yes" 或 "no"。 + +- **child_table_count** : 子表的数量,默认值为 10。 + +- **child_table_prefix** : 子表名称的前缀,必选配置项,没有默认值。 + +- **escape_character** : 超级表和子表名称中是否包含转义字符,默认值为 "no",可选值为 "yes" 或 "no"。 + +- **auto_create_table** : 仅当 insert_mode 为 taosc, rest, stmt 并且 childtable_exists 为 "no" 时生效,该参数为 "yes" 表示 taosBenchmark 在插入数据时会自动创建不存在的表;为 "no" 则表示先提前建好所有表再进行插入。 + +- **batch_create_tbl_num** : 创建子表时每批次的建表数量,默认为 10。注:实际的批数不一定与该值相同,当执行的 SQL 语句大于支持的最大长度时,会自动截断再执行,继续创建。 + +- **data_source** : 数据的来源,默认为 taosBenchmark 随机产生,可以配置为 "rand" 和 "sample"。为 "sample" 时使用 sample_file 参数指定的文件内的数据。 + +- **insert_mode** : 插入模式,可选项有 taosc, rest, stmt, sml, sml-rest, 分别对应普通写入、restful 接口写入、参数绑定接口写入、schemaless 接口写入、restful schemaless 接口写入 (由 taosAdapter 提供)。默认值为 taosc 。 + +- **non_stop_mode** : 指定是否持续写入,若为 "yes" 则 insert_rows 失效,直到 Ctrl + C 停止程序,写入才会停止。默认值为 "no",即写入指定数量的记录后停止。注:即使在持续写入模式下 insert_rows 失效,但其也必须被配置为一个非零正整数。 + +- **line_protocol** : 使用行协议插入数据,仅当 insert_mode 为 sml 或 sml-rest 时生效,可选项为 line, telnet, json。 + +- **tcp_transfer** : telnet 模式下的通信协议,仅当 insert_mode 为 sml-rest 并且 line_protocol 为 telnet 时生效。如果不配置,则默认为 http 协议。 + +- **insert_rows** : 每个子表插入的记录数,默认为 0 。 + +- **childtable_offset** : 仅当 childtable_exists 为 yes 时生效,指定从超级表获取子表列表时的偏移量,即从第几个子表开始。 + +- **childtable_limit** : 仅当 childtable_exists 为 yes 时生效,指定从超级表获取子表列表的上限。 + +- **interlace_rows** : 启用交错插入模式并同时指定向每个子表每次插入的数据行数。交错插入模式是指依次向每张子表插入由本参数所指定的行数并重复这个过程,直到所有子表的数据都插入完成。默认值为 0, 即向一张子表完成数据插入后才会向下一张子表进行数据插入。 + +- **insert_interval** : 指定交错插入模式的插入间隔,单位为 ms,默认值为 0。 只有当 `-B/--interlace-rows` 大于 0 时才起作用。意味着数据插入线程在为每个子表插入隔行扫描记录后,会等待该值指定的时间间隔后再进行下一轮写入。 + +- **partial_col_num** : 若该值为正数 n 时, 则仅向前 n 列写入,仅当 insert_mode 为 taosc 和 rest 时生效,如果 n 为 0 则是向全部列写入。 + +- **disorder_ratio** : 指定乱序数据的百分比概率,其值域为 [0,50]。默认为 0,即没有乱序数据。 + +- **disorder_range** : 指定乱序数据的时间戳回退范围。所生成的乱序时间戳为非乱序情况下应该使用的时间戳减去这个范围内的一个随机值。仅在 `-O/--disorder` 指定的乱序数据百分比大于 0 时有效。 + +- **timestamp_step** : 每个子表中插入数据的时间戳步长,单位与数据库的 `precision` 一致,默认值是 1。 + +- **start_timestamp** : 每个子表的时间戳起始值,默认值是 now。 + +- **sample_format** : 样本数据文件的类型,现在只支持 "csv" 。 + +- **sample_file** : 指定 csv 格式的文件作为数据源,仅当 data_source 为 sample 时生效。若 csv 文件内的数据行数小于等于 prepared_rand,那么会循环读取 csv 文件数据直到与 prepared_rand 相同;否则则会只读取 prepared_rand 个数的行的数据。也即最终生成的数据行数为二者取小。 + +- **use_sample_ts** : 仅当 data_source 为 sample 时生效,表示 sample_file 指定的 csv 文件内是否包含第一列时间戳,默认为 no。 若设置为 yes, 则使用 csv 文件第一列作为时间戳,由于同一子表时间戳不能重复,生成的数据量取决于 csv 文件内的数据行数相同,此时 insert_rows 失效。 + +- **tags_file** : 仅当 insert_mode 为 taosc, rest 的模式下生效。 最终的 tag 的数值与 childtable_count 有关,如果 csv 文件内的 tag 数据行小于给定的子表数量,那么会循环读取 csv 文件数据直到生成 childtable_count 指定的子表数量;否则则只会读取 childtable_count 行 tag 数据。也即最终生成的子表数量为二者取小。 + +#### 标签列与数据列配置参数 + +指定超级表标签列与数据列的配置参数分别在 `super_tables` 中的 `columns` 和 `tag` 中。 + +- **type** : 指定列类型,可选值请参考 TDengine 支持的数据类型。 + 注:JSON 数据类型比较特殊,只能用于标签,当使用 JSON 类型作为 tag 时有且只能有这一个标签,此时 count 和 len 代表的意义分别是 JSON tag 内的 key-value pair 的个数和每个 KV pair 的 value 的值的长度,value 默认为 string。 + +- **len** : 指定该数据类型的长度,对 NCHAR,BINARY 和 JSON 数据类型有效。如果对其他数据类型配置了该参数,若为 0 , 则代表该列始终都是以 null 值写入;如果不为 0 则被忽略。 + +- **count** : 指定该类型列连续出现的数量,例如 "count": 4096 即可生成 4096 个指定类型的列。 + +- **name** : 列的名字,若与 count 同时使用,比如 "name":"current", "count":3, 则 3 个列的名字分别为 current, current_2. current_3。 + +- **min** : 数据类型的 列/标签 的最小值。 + +- **max** : 数据类型的 列/标签 的最大值。 + +- **values** : nchar/binary 列/标签的值域,将从值中随机选择。 + +#### 插入行为配置参数 + +- **thread_count** : 插入数据的线程数量,默认为 8。 + +- **create_table_thread_count** : 建表的线程数量,默认为 8。 + +- **connection_pool_size** : 预先建立的与 TDengine 服务端之间的连接的数量。若不配置,则与所指定的线程数相同。 + +- **result_file** : 结果输出文件的路径,默认值为 ./output.txt。 + +- **confirm_parameter_prompt** : 开关参数,要求用户在提示后确认才能继续。默认值为 false 。 + +- **interlace_rows** : 启用交错插入模式并同时指定向每个子表每次插入的数据行数。交错插入模式是指依次向每张子表插入由本参数所指定的行数并重复这个过程,直到所有子表的数据都插入完成。默认值为 0, 即向一张子表完成数据插入后才会向下一张子表进行数据插入。 + 在 `super_tables` 中也可以配置该参数,若配置则以 `super_tables` 中的配置为高优先级,覆盖全局设置。 + +- **insert_interval** : + 指定交错插入模式的插入间隔,单位为 ms,默认值为 0。 只有当 `-B/--interlace-rows` 大于 0 时才起作用。意味着数据插入线程在为每个子表插入隔行扫描记录后,会等待该值指定的时间间隔后再进行下一轮写入。 + 在 `super_tables` 中也可以配置该参数,若配置则以 `super_tables` 中的配置为高优先级,覆盖全局设置。 + +- **num_of_records_per_req** : + 每次向 TDengine 请求写入的数据行数,默认值为 30000 。当其设置过大时,TDengine 客户端驱动会返回相应的错误信息,此时需要调低这个参数的设置以满足写入要求。 + +- **prepare_rand** : 生成的随机数据中唯一值的数量。若为 1 则表示所有数据都相同。默认值为 10000 。 + +### 查询场景配置参数 + +查询场景下 `filetype` 必须设置为 `query`,该参数及其它通用参数详见[通用配置参数](#通用配置参数) + +#### 执行指定查询语句的配置参数 + +查询子表或者普通表的配置参数在 `specified_table_query` 中设置。 + +- **query_interval** : 查询时间间隔,单位是秒,默认值为 0。 + +- **threads** : 执行查询 SQL 的线程数,默认值为 1。 + +- **sqls**: + - **sql**: 执行的 SQL 命令,必填。 + - **result**: 保存查询结果的文件,未指定则不保存。 + +#### 查询超级表的配置参数 + +查询超级表的配置参数在 `super_table_query` 中设置。 + +- **stblname** : 指定要查询的超级表的名称,必填。 + +- **query_interval** : 查询时间间隔,单位是秒,默认值为 0。 + +- **threads** : 执行查询 SQL 的线程数,默认值为 1。 + +- **sqls** : + - **sql** : 执行的 SQL 命令,必填;对于超级表的查询 SQL,在 SQL 命令中保留 "xxxx",程序会自动将其替换为超级表的所有子表名。 + 替换为超级表中所有的子表名。 + - **result** : 保存查询结果的文件,未指定则不保存。 + +### 订阅场景配置参数 + +订阅场景下 `filetype` 必须设置为 `subscribe`,该参数及其它通用参数详见[通用配置参数](#通用配置参数) + +#### 执行指定订阅语句的配置参数 + +订阅子表或者普通表的配置参数在 `specified_table_query` 中设置。 + +- **threads** : 执行 SQL 的线程数,默认为 1。 + +- **interval** : 执行订阅的时间间隔,单位为秒,默认为 0。 + +- **restart** : "yes" 表示开始新的订阅,"no" 表示继续之前的订阅,默认值为 "no"。 + +- **keepProgress** : "yes" 表示保留订阅进度,"no" 表示不保留,默认值为 "no"。 + +- **resubAfterConsume** : "yes" 表示取消之前的订阅然后再次订阅, "no" 表示继续之前的订阅,默认值为 "no"。 + +- **sqls** : + - **sql** : 执行的 SQL 命令,必填。 + - **result** : 保存查询结果的文件,未指定则不保存。 + +#### 订阅超级表的配置参数 + +订阅超级表的配置参数在 `super_table_query` 中设置。 + +- **stblname** : 要订阅的超级表名称,必填。 + +- **threads** : 执行 SQL 的线程数,默认为 1。 + +- **interval** : 执行订阅的时间间隔,单位为秒,默认为 0。 + +- **restart** : "yes" 表示开始新的订阅,"no" 表示继续之前的订阅,默认值为 "no"。 + +- **keepProgress** : "yes" 表示保留订阅进度,"no" 表示不保留,默认值为 "no"。 + +- **resubAfterConsume** : "yes" 表示取消之前的订阅然后再次订阅, "no" 表示继续之前的订阅,默认值为 "no"。 + +- **sqls** : + - **sql** : 执行的 SQL 命令,必填;对于超级表的查询 SQL,在 SQL 命令中保留 "xxxx",程序会自动将其替换为超级表的所有子表名。 + 替换为超级表中所有的子表名。 + - **result** : 保存查询结果的文件,未指定则不保存。 diff --git a/docs-cn/14-reference/06-taosdump.md b/docs-cn/14-reference/06-taosdump.md new file mode 100644 index 0000000000000000000000000000000000000000..7131493ec9439225d8047288ed86026c887f0aac --- /dev/null +++ b/docs-cn/14-reference/06-taosdump.md @@ -0,0 +1,118 @@ +--- +title: taosdump +description: "taosdump 是一个支持从运行中的 TDengine 集群备份数据并将备份的数据恢复到相同或另一个运行中的 TDengine 集群中的工具应用程序" +--- + +## 简介 + +taosdump 是一个支持从运行中的 TDengine 集群备份数据并将备份的数据恢复到相同或另一个运行中的 TDengine 集群中的工具应用程序。 + +taosdump 可以用数据库、超级表或普通表作为逻辑数据单元进行备份,也可以对数据库、超级 +表和普通表中指定时间段内的数据记录进行备份。使用时可以指定数据备份的目录路径,如果 +不指定位置,taosdump 默认会将数据备份到当前目录。 + +如果指定的位置已经有数据文件,taosdump 会提示用户并立即退出,避免数据被覆盖。这意味着同一路径只能被用于一次备份。 +如果看到相关提示,请小心操作。 + +taosdump 是一个逻辑备份工具,它不应被用于备份任何原始数据、环境设置、 +硬件信息、服务端配置或集群的拓扑结构。taosdump 使用 +[ Apache AVRO ](https://avro.apache.org/)作为数据文件格式来存储备份数据。 + +## 安装 + +taosdump 有两种安装方式: + +- 安装 taosTools 官方安装包, 请从[所有下载链接](https://www.taosdata.com/all-downloads)页面找到 taosTools 并下载安装。 + +- 单独编译 taos-tools 并安装, 详情请参考 [taos-tools](https://github.com/taosdata/taos-tools) 仓库。 + +## 常用使用场景 + +### taosdump 备份数据 + +1. 备份所有数据库:指定 `-A` 或 `--all-databases` 参数; +2. 备份多个指定数据库:使用 `-D db1,db2,...` 参数; +3. 备份指定数据库中的某些超级表或普通表:使用 `dbname stbname1 stbname2 tbname1 tbname2 ...` 参数,注意这种输入序列第一个参数为数据库名称,且只支持一个数据库,第二个和之后的参数为该数据库中的超级表或普通表名称,中间以空格分隔; +4. 备份系统 log 库:TDengine 集群通常会包含一个系统数据库,名为 `log`,这个数据库内的数据为 TDengine 自我运行的数据,taosdump 默认不会对 log 库进行备份。如果有特定需求对 log 库进行备份,可以使用 `-a` 或 `--allow-sys` 命令行参数。 +5. “宽容”模式备份:taosdump 1.4.1 之后的版本提供 `-n` 参数和 `-L` 参数,用于备份数据时不使用转义字符和“宽容”模式,可以在表名、列名、标签名没使用转义字符的情况下减少备份数据时间和备份数据占用空间。如果不确定符合使用 `-n` 和 `-L` 条件时请使用默认参数进行“严格”模式进行备份。转义字符的说明请参考[官方文档](/taos-sql/escape)。 + +:::tip +- taosdump 1.4.1 之后的版本提供 `-I` 参数,用于解析 avro 文件 schema 和数据,如果指定 `-s` 参数将只解析 schema。 +- taosdump 1.4.2 之后的备份使用 `-B` 参数指定的批次数,默认值为 16384,如果在某些环境下由于网络速度或磁盘性能不足导致 "Error actual dump .. batch .." 可以通过 `-B` 参数挑战为更小的值进行尝试。 + +::: + +### taosdump 恢复数据 + +恢复指定路径下的数据文件:使用 `-i` 参数加上数据文件所在路径。如前面提及,不应该使用同一个目录备份不同数据集合,也不应该在同一路径多次备份同一数据集,否则备份数据会造成覆盖或多次备份。 + +:::tip +taosdump 内部使用 TDengine stmt binding API 进行恢复数据的写入,为提高数据恢复性能,目前使用 16384 为一次写入批次。如果备份数据中有比较多列数据,可能会导致产生 "WAL size exceeds limit" 错误,此时可以通过使用 `-B` 参数调整为一个更小的值进行尝试。 + +::: + +## 详细命令行参数列表 + +以下为 taosdump 详细命令行参数列表: + +``` +Usage: taosdump [OPTION...] dbname [tbname ...] + or: taosdump [OPTION...] --databases db1,db2,... + or: taosdump [OPTION...] --all-databases + or: taosdump [OPTION...] -i inpath + or: taosdump [OPTION...] -o outpath + + -h, --host=HOST Server host dumping data from. Default is + localhost. + -p, --password User password to connect to server. Default is + taosdata. + -P, --port=PORT Port to connect + -u, --user=USER User name used to connect to server. Default is + root. + -c, --config-dir=CONFIG_DIR Configure directory. Default is /etc/taos + -i, --inpath=INPATH Input file path. + -o, --outpath=OUTPATH Output file path. + -r, --resultFile=RESULTFILE DumpOut/In Result file path and name. + -a, --allow-sys Allow to dump system database + -A, --all-databases Dump all databases. + -D, --databases=DATABASES Dump inputted databases. Use comma to separate + databases' name. + -N, --without-property Dump database without its properties. + -s, --schemaonly Only dump tables' schema. + -y, --answer-yes Input yes for prompt. It will skip data file + checking! + -d, --avro-codec=snappy Choose an avro codec among null, deflate, snappy, + and lzma. + -S, --start-time=START_TIME Start time to dump. Either epoch or + ISO8601/RFC3339 format is acceptable. ISO8601 + format example: 2017-10-01T00:00:00.000+0800 or + 2017-10-0100:00:00:000+0800 or '2017-10-01 + 00:00:00.000+0800' + -E, --end-time=END_TIME End time to dump. Either epoch or ISO8601/RFC3339 + format is acceptable. ISO8601 format example: + 2017-10-01T00:00:00.000+0800 or + 2017-10-0100:00:00.000+0800 or '2017-10-01 + 00:00:00.000+0800' + -B, --data-batch=DATA_BATCH Number of data per query/insert statement when + backup/restore. Default value is 16384. If you see + 'error actual dump .. batch ..' when backup or if + you see 'WAL size exceeds limit' error when + restore, please adjust the value to a smaller one + and try. The workable value is related to the + length of the row and type of table schema. + -I, --inspect inspect avro file content and print on screen + -L, --loose-mode Using loose mode if the table name and column name + use letter and number only. Default is NOT. + -n, --no-escape No escape char '`'. Default is using it. + -T, --thread-num=THREAD_NUM Number of thread for dump in file. Default is + 5. + -g, --debug Print debug info. + -?, --help Give this help list + --usage Give a short usage message + -V, --version Print program version + +Mandatory or optional arguments to long options are also mandatory or optional +for any corresponding short options. + +Report bugs to . +``` diff --git a/docs-cn/14-reference/07-tdinsight/assets/15146-tdengine-monitor-dashboard.json b/docs-cn/14-reference/07-tdinsight/assets/15146-tdengine-monitor-dashboard.json new file mode 100644 index 0000000000000000000000000000000000000000..f651983528ca824b4e6b14586aac5a5bfb4ecab8 --- /dev/null +++ b/docs-cn/14-reference/07-tdinsight/assets/15146-tdengine-monitor-dashboard.json @@ -0,0 +1,3191 @@ +{ + "__inputs": [ + { + "name": "DS_TDENGINE", + "label": "TDengine", + "description": "", + "type": "datasource", + "pluginId": "tdengine-datasource", + "pluginName": "TDengine" + } + ], + "__requires": [ + { + "type": "panel", + "id": "gauge", + "name": "Gauge", + "version": "" + }, + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "7.5.10" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "panel", + "id": "piechart", + "name": "Pie chart v2", + "version": "" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + }, + { + "type": "datasource", + "id": "tdengine-datasource", + "name": "TDengine", + "version": "3.1.0" + }, + { + "type": "panel", + "id": "text", + "name": "Text", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "TDengine nodes metrics.", + "editable": true, + "gnetId": 15146, + "graphTooltip": 0, + "id": null, + "iteration": 1635263227798, + "links": [], + "panels": [ + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 57, + "panels": [], + "title": "Cluster Status", + "type": "row" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 24, + "x": 0, + "y": 1 + }, + "id": 32, + "options": { + "content": "

TDengine Cluster Dashboard

>\n", + "mode": "markdown" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "mnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show mnodes", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "-- OVERVIEW --", + "transformations": [ + { + "id": "calculateField", + "options": { + "binary": { + "left": "Time", + "operator": "+", + "reducer": "sum", + "right": "" + }, + "mode": "binary", + "reduce": { + "reducer": "sum" + } + } + } + ], + "type": "text" + }, + { + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 8, + "x": 0, + "y": 4 + }, + "id": 28, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": true + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show mnodes", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Master MNode", + "transformations": [ + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "regex", + "options": { + "value": "master" + } + }, + "fieldName": "role" + } + ], + "match": "all", + "type": "include" + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": ["dnodes"] + } + } + } + ], + "type": "stat" + }, + { + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 7, + "x": 8, + "y": 4 + }, + "id": 70, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "/^Time$/", + "values": true + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show mnodes", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Master MNode Create Time", + "transformations": [ + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "regex", + "options": { + "value": "master" + } + }, + "fieldName": "role" + } + ], + "match": "all", + "type": "include" + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": ["Time"] + } + } + }, + { + "id": "calculateField", + "options": { + "mode": "reduceRow", + "reduce": { + "reducer": "min" + } + } + } + ], + "type": "stat" + }, + { + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": null, + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 9, + "x": 15, + "y": 4 + }, + "id": 29, + "options": { + "showHeader": true + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show variables", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Variables", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": ["value", "name"] + } + } + }, + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "regex", + "options": { + "value": ".*" + } + }, + "fieldName": "name" + } + ], + "match": "all", + "type": "include" + } + } + ], + "type": "table" + }, + { + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 0, + "y": 7 + }, + "id": 33, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "/.*/", + "values": true + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Table", + "queryType": "SQL", + "refId": "A", + "sql": "select server_version()", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Server Version", + "transformations": [], + "type": "stat" + }, + { + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 2, + "y": 7 + }, + "id": 27, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": true + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show mnodes", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Number of MNodes", + "transformations": [ + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "greater", + "options": { + "value": 0 + } + }, + "fieldName": "id" + } + ], + "match": "any", + "type": "include" + } + }, + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": ["count"] + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": ["id"] + } + } + } + ], + "type": "stat" + }, + { + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 5, + "y": 7 + }, + "id": 41, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["last"], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show dnodes", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Total Dnodes", + "transformations": [ + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": ["count"] + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": ["id"] + } + } + } + ], + "type": "stat" + }, + { + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 7, + "y": 7 + }, + "id": 31, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["last"], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show dnodes", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Offline Dnodes", + "transformations": [ + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "regex", + "options": { + "value": "ready" + } + }, + "fieldName": "status" + } + ], + "match": "all", + "type": "exclude" + } + }, + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": ["count"] + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": ["id"] + } + } + } + ], + "type": "stat" + }, + { + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 9, + "y": 7 + }, + "id": 65, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": true + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show databases;", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Number of Databases", + "transformations": [ + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": ["count"] + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": ["name"] + } + } + } + ], + "type": "stat" + }, + { + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 12, + "y": 7 + }, + "id": 69, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": true + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show databases;", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Total Number of Vgroups", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": ["vgroups"] + } + } + }, + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": ["sum"] + } + } + ], + "type": "stat" + }, + { + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "displayMode": "auto", + "filterable": true + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "role" + }, + "properties": [ + { + "id": "mappings", + "value": [ + { + "from": "", + "id": 1, + "text": "", + "to": "", + "type": 2, + "value": "" + } + ] + } + ] + } + ] + }, + "gridPos": { + "h": 3, + "w": 9, + "x": 0, + "y": 10 + }, + "id": 67, + "options": { + "showHeader": true + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "formatType": "Table", + "queryType": "SQL", + "refId": "A", + "sql": "show dnodes", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Number of DNodes for each Role", + "transformations": [ + { + "id": "groupBy", + "options": { + "fields": { + "end_point": { + "aggregations": ["count"], + "operation": "aggregate" + }, + "role": { + "aggregations": [], + "operation": "groupby" + } + } + } + }, + { + "id": "filterFieldsByName", + "options": {} + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": {}, + "renameByName": { + "end_point (count)": "Number of DNodes", + "role": "Dnode Role" + } + } + } + ], + "type": "table" + }, + { + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 9, + "y": 10 + }, + "id": 55, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": true + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show connections", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Number of Connections", + "transformations": [ + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": ["count"] + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": ["connId"] + } + } + } + ], + "type": "stat" + }, + { + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 12, + "y": 10 + }, + "id": 68, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": true + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show databases;", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Total Number of Tables", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": ["ntables"] + } + } + }, + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": ["sum"] + } + } + ], + "type": "stat" + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 13 + }, + "id": 24, + "panels": [], + "title": "Dnodes Status", + "type": "row" + }, + { + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "displayMode": "auto", + "filterable": true + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "status" + }, + "properties": [ + { + "id": "custom.width", + "value": 86 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "vnodes" + }, + "properties": [ + { + "id": "custom.width", + "value": 77 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "role" + }, + "properties": [ + { + "id": "custom.width", + "value": 84 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "cores" + }, + "properties": [ + { + "id": "custom.width", + "value": 75 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "end_point" + }, + "properties": [ + { + "id": "custom.width", + "value": 205 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "id" + }, + "properties": [ + { + "id": "custom.width", + "value": 78 + } + ] + } + ] + }, + "gridPos": { + "h": 5, + "w": 16, + "x": 0, + "y": 14 + }, + "id": 36, + "options": { + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "formatType": "Table", + "queryType": "SQL", + "refId": "A", + "sql": "show dnodes", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "DNodes Status", + "type": "table" + }, + { + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 16, + "y": 14 + }, + "id": 40, + "options": { + "displayLabels": [], + "legend": { + "displayMode": "table", + "placement": "right", + "values": ["value"] + }, + "pieType": "pie", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "/.*/", + "values": false + }, + "text": { + "titleSize": 6 + } + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "formatType": "Table", + "queryType": "SQL", + "refId": "A", + "sql": "show dnodes", + "target": "select metric", + "type": "timeserie" + } + ], + "title": "Offline Reasons", + "transformations": [ + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "regex", + "options": { + "value": "ready" + } + }, + "fieldName": "status" + } + ], + "match": "all", + "type": "exclude" + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": ["offline reason", "end_point"] + } + } + }, + { + "id": "groupBy", + "options": { + "fields": { + "Time": { + "aggregations": ["count"], + "operation": "aggregate" + }, + "end_point": { + "aggregations": ["count"], + "operation": "aggregate" + }, + "offline reason": { + "aggregations": [], + "operation": "groupby" + } + } + } + } + ], + "type": "piechart" + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 19 + }, + "id": 22, + "panels": [], + "title": "Mnodes Status", + "type": "row" + }, + { + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 24, + "x": 0, + "y": 20 + }, + "id": 38, + "options": { + "showHeader": true + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "formatType": "Table", + "queryType": "SQL", + "refId": "A", + "sql": "show mnodes;", + "target": "select metric", + "type": "timeserie" + } + ], + "title": "Mnodes Status", + "type": "table" + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 25 + }, + "id": 20, + "panels": [], + "repeat": "fqdn", + "title": "节点资源占用 [ $fqdn ]", + "type": "row" + }, + { + "datasource": "${DS_TDENGINE}", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "decmbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 0, + "y": 26 + }, + "id": 66, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": ["mean"], + "fields": "/^taosd$/", + "values": false + }, + "showThresholdLabels": true, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "alias": "memory", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select last(mem_taosd) as taosd, last(mem_total) as total from log.dn where fqdn = '$fqdn' and ts >= now -5m and ts < now", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Current Memory Usage of taosd", + "type": "gauge" + }, + { + "datasource": "${DS_TDENGINE}", + "description": "taosd max memery last 10 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 0.5 + }, + { + "color": "red", + "value": 0.8 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "last(cpu_taosd)" + }, + "properties": [ + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + } + ] + } + ] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 5, + "y": 26 + }, + "id": 45, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": ["mean"], + "fields": "/^last\\(cpu_taosd\\)$/", + "values": false + }, + "showThresholdLabels": true, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "alias": "mem_taosd", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select last(cpu_taosd) from log.dn where fqdn = '$fqdn'", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Current CPU Usage of taosd", + "type": "gauge" + }, + { + "datasource": "${DS_TDENGINE}", + "description": "avg band speed last one minute", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 8192, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 4916 + }, + { + "color": "red", + "value": 6554 + } + ] + }, + "unit": "Kbits" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 4, + "x": 10, + "y": 26 + }, + "id": 14, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": ["last"], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "alias": "band_speed", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select avg(band_speed) from log.dn where fqdn='$fqdn' and ts >= now-5m and ts < now interval(1m)", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "band speed", + "type": "gauge" + }, + { + "datasource": "${DS_TDENGINE}", + "description": "io read/write rate", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 8192, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 4916 + }, + { + "color": "red", + "value": 6554 + } + ] + }, + "unit": "Kbits" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 14, + "y": 26 + }, + "id": 48, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": ["last"], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "alias": "", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select last(io_read) as io_read, last(io_write) as io_write from log.dn where fqdn='$fqdn' and ts >= now-1h and ts < now interval(1m)", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "IO Rate", + "type": "gauge" + }, + { + "datasource": "${DS_TDENGINE}", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 1, + "min": 0, + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 75 + }, + { + "color": "red", + "value": 80 + }, + { + "color": "dark-red", + "value": 95 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 19, + "y": 26 + }, + "id": 51, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": ["last"], + "fields": "/^disk_used_percent$/", + "values": false + }, + "showThresholdLabels": true, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "alias": "disk_used", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select last(disk_used) as used from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(1m)", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + }, + { + "alias": "disk_total", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "B", + "sql": "select last(disk_total) as total from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(1m)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "disk_used_percent", + "expression": "A/B", + "formatType": "Time series", + "hide": false, + "queryType": "Arithmetic", + "refId": "C", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Disk Used", + "transformations": [ + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": ["lastNotNull"] + } + } + ], + "type": "gauge" + }, + { + "datasource": "${DS_TDENGINE}", + "description": "taosd max memery last 10 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "decmbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 0, + "y": 32 + }, + "id": 12, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": ["mean"], + "fields": "/^taosd$/", + "values": false + }, + "showThresholdLabels": true, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "alias": "memory", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select max(mem_taosd) as taosd, max(mem_total) as total from log.dn where fqdn = '$fqdn' and ts >= now -5m and ts < now", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Max Memory Usage of taosd in Last 5 minute", + "type": "gauge" + }, + { + "datasource": "${DS_TDENGINE}", + "description": "taosd max memery last 10 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "max": 1, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 0.5 + }, + { + "color": "red", + "value": 0.8 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 5, + "y": 32 + }, + "id": 43, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": ["mean"], + "fields": "", + "values": false + }, + "showThresholdLabels": true, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "alias": "mem_taosd", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select max(cpu_taosd) from log.dn where fqdn = '$fqdn' and ts >= now -5m and ts < now", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Max CPU Usage of taosd in Last 5 minute", + "type": "gauge" + }, + { + "datasource": "${DS_TDENGINE}", + "description": "avg band speed last one minute", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 8192, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 4916 + }, + { + "color": "red", + "value": 6554 + } + ] + }, + "unit": "Kbits" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 4, + "x": 10, + "y": 32 + }, + "id": 50, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": ["last"], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "alias": "band_speed", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select max(band_speed) from log.dn where fqdn = '$fqdn' and ts >= now-1h", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Max band speed in last hour", + "type": "gauge" + }, + { + "datasource": "${DS_TDENGINE}", + "description": "io read/write rate", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 8192, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 4916 + }, + { + "color": "red", + "value": 6554 + } + ] + }, + "unit": "Kbits" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 14, + "y": 32 + }, + "id": 49, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": ["last"], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "alias": "", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select max(io_read) as io_read, max(io_write) as io_write from log.dn where fqdn = '$fqdn'", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Max IO Rate in last hour", + "type": "gauge" + }, + { + "datasource": "${DS_TDENGINE}", + "description": "io read/write rate", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 8192, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 4916 + }, + { + "color": "red", + "value": 6554 + } + ] + }, + "unit": "cpm" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 19, + "y": 32 + }, + "id": 52, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": ["last"], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "alias": "req-http", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select sum(req_http) as req_http from log.dn where fqdn = '$fqdn' and ts >= now - 1h interval(1m)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "req-inserts", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "B", + "sql": "select sum(req_insert) as req_insert from log.dn where fqdn = '$fqdn' and ts >= now - 1h interval(1m)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "req-selects", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "C", + "sql": "select sum(req_select) as req_select from log.dn where fqdn = '$fqdn' and ts >= now - 1h interval(1m)", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Requests in last Minute", + "type": "gauge" + }, + { + "aliasColors": {}, + "bars": false, + "cacheTimeout": null, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TDENGINE}", + "description": "monitor system cpu", + "fieldConfig": { + "defaults": { + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 11, + "w": 12, + "x": 0, + "y": 38 + }, + "hiddenSeries": false, + "hideTimeOverride": true, + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.10", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "cpu_system", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "A", + "sql": "select avg(cpu_system) from log.dn where fqdn='$fqdn' and ts >= now-1h and ts < now interval(30s)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "cpu_taosd", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "B", + "sql": "select avg(cpu_taosd) from log.dn where fqdn='$fqdn' and ts >= now-1h and ts < now interval(30s)", + "target": "select metric", + "type": "timeserie" + } + ], + "thresholds": [], + "timeFrom": "1h", + "timeRegions": [], + "timeShift": "30s", + "title": "CPU 资源占用情况", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": null, + "format": "percent", + "label": "使用占比", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "cacheTimeout": null, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TDENGINE}", + "description": "monitor system cpu", + "fieldConfig": { + "defaults": { + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 11, + "w": 12, + "x": 12, + "y": 38 + }, + "hiddenSeries": false, + "hideTimeOverride": true, + "id": 42, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.10", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "system", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "A", + "sql": "select avg(mem_system) from log.dn where fqdn = '$fqdn' and ts >= now-1h and ts < now interval(30s)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "taosd", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "B", + "sql": "select avg(mem_taosd) from log.dn where fqdn = '$fqdn' and ts >= now-1h and ts < now interval(30s)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "total", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "C", + "sql": "select avg(mem_total) from log.dn where fqdn = '$fqdn' and ts >= now-1h and ts < now interval(30s)", + "target": "select metric", + "type": "timeserie" + } + ], + "thresholds": [], + "timeFrom": "1h", + "timeRegions": [], + "timeShift": "30s", + "title": "内存资源占用情况", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": null, + "format": "decmbytes", + "label": "使用占比", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 49 + }, + "hiddenSeries": false, + "id": 54, + "legend": { + "alignAsTable": false, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.10", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "percent", + "yaxis": 2 + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "disk_used", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "A", + "sql": "select avg(disk_used) from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(30s)", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + }, + { + "alias": "disk_total", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "B", + "sql": "select avg(disk_total) from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(30s)", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + }, + { + "alias": "percent", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "D", + "sql": "select avg(disk_used)/avg(disk_total) * 100 from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(30s)", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Disk Used Percent", + "tooltip": { + "shared": false, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "gbytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "percent", + "label": "Disk Used", + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 49 + }, + "hiddenSeries": false, + "id": 64, + "legend": { + "alignAsTable": false, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.10", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "disk_used", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "A", + "sql": "select derivative(value, 1m, 0) from (select avg(disk_used) as value from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(1m))", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Disk Used Increasing Rate per Minute", + "tooltip": { + "shared": false, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "percentunit", + "label": "Disk Used", + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "cacheTimeout": null, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TDENGINE}", + "description": "total select request per minute last hour", + "fieldConfig": { + "defaults": { + "unit": "cpm" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 58 + }, + "hiddenSeries": false, + "id": 8, + "interval": null, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "maxDataPoints": 100, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.10", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "req_select", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select sum(req_select) from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(1m)", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + }, + { + "alias": "req_insert", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "B", + "sql": "select sum(req_insert) from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(1m)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "req_http", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "C", + "sql": "select sum(req_http) from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(1m)", + "target": "select metric", + "type": "timeserie" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Requets Count per Minutes $fqdn", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "cpm", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "cacheTimeout": null, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TDENGINE}", + "description": "io", + "fieldConfig": { + "defaults": { + "links": [], + "unit": "Kbits" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 58 + }, + "hiddenSeries": false, + "hideTimeOverride": true, + "id": 47, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.10", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "io-read", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "A", + "sql": "select avg(io_read) from log.dn where fqdn = '$fqdn' and ts >= now-1h and ts < now interval(1m)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "io-write", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "B", + "sql": "select avg(io_write) from log.dn where fqdn = '$fqdn' and ts >= now-1h and ts < now interval(1m)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "io-read-last-hour", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "C", + "sql": "select avg(io_read) from log.dn where fqdn = '$fqdn' and ts >= now-2h and ts < now - 1h interval(1m)", + "target": "select metric", + "timeshift": { + "period": 1, + "unit": "hours" + }, + "type": "timeserie" + }, + { + "alias": "io-write-last-hour", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "D", + "sql": "select avg(io_write) from log.dn where fqdn = '$fqdn' and ts >= now-1h and ts < now interval(1m)", + "target": "select metric", + "timeshift": { + "period": 1, + "unit": "hours" + }, + "type": "timeserie" + } + ], + "thresholds": [], + "timeFrom": "1h", + "timeRegions": [], + "timeShift": "30s", + "title": "IO", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": null, + "format": "Kbits", + "label": "IO Rate", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 67 + }, + "id": 63, + "panels": [], + "title": "Login History", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": { + "displayName": "Logins Per Minute", + "unit": "cpm" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 68 + }, + "hiddenSeries": false, + "id": 61, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.10", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "logins", + "nullPointMode": "null as zero" + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "logins", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select count(*) from log.log where ts >= $from and ts < $to interval (1m)", + "target": "select metric", + "type": "timeserie" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Login Counts per Minute", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "cpm", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "1m", + "schemaVersion": 27, + "style": "dark", + "tags": ["TDengine", "multiple"], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "TDengine", + "value": "TDengine" + }, + "description": "TDengine Data Source Selector", + "error": null, + "hide": 0, + "includeAll": false, + "label": "Datasource", + "multi": false, + "name": "ds", + "options": [], + "query": "tdengine-datasource", + "queryValue": "", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "allValue": null, + "current": {}, + "datasource": "${DS_TDENGINE}", + "definition": "select fqdn from log.dn", + "description": "TDengine Nodes FQDN (Hostname)", + "error": null, + "hide": 0, + "includeAll": false, + "label": null, + "multi": false, + "name": "fqdn", + "options": [], + "query": "select fqdn from log.dn", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] + }, + "timezone": "", + "title": "Multiple TDengines Monitoring", + "uid": "tdengine-multiple", + "version": 4 +} diff --git a/docs-cn/14-reference/07-tdinsight/assets/15155-tdengine-alert-demo.json b/docs-cn/14-reference/07-tdinsight/assets/15155-tdengine-alert-demo.json new file mode 100644 index 0000000000000000000000000000000000000000..ab98354aaeae8c4dc945aae085d12fec50b530d1 --- /dev/null +++ b/docs-cn/14-reference/07-tdinsight/assets/15155-tdengine-alert-demo.json @@ -0,0 +1,212 @@ +{ + "__inputs": [ + { + "name": "DS_TDENGINE", + "label": "TDengine", + "description": "", + "type": "datasource", + "pluginId": "tdengine-datasource", + "pluginName": "TDengine" + } + ], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "7.5.10" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "datasource", + "id": "tdengine-datasource", + "name": "TDengine", + "version": "3.1.0" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "", + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": null, + "links": [], + "panels": [ + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [5], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": ["A", "10s", "now"] + }, + "reducer": { + "params": [], + "type": "avg" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "10s", + "handler": 1, + "message": "This is a alert demo!", + "name": "cpu load average alert", + "noDataState": "no_data", + "notifications": [ + { + "uid": "4sKm9jd7k" + }, + { + "uid": "QyczKkFnk" + } + ] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TDENGINE}", + "decimals": null, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 0 + }, + "hiddenSeries": false, + "id": 2, + "legend": { + "avg": true, + "current": true, + "hideEmpty": false, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.10", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "system", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select avg(cpu_system) as system from log.dn where ts >= now-1h and ts < now interval(30s)", + "target": "select metric", + "type": "timeserie" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 5, + "visible": true + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "CPU Load Average", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percent", + "label": "Percent", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "schemaVersion": 27, + "style": "dark", + "tags": ["TDengine"], + "templating": { + "list": [] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "TDengine Alert Demo", + "uid": "aLN9ujOnk", + "version": 15 +} diff --git a/docs-cn/14-reference/07-tdinsight/assets/TDinsight-1-cluster-status.png b/docs-cn/14-reference/07-tdinsight/assets/TDinsight-1-cluster-status.png new file mode 100644 index 0000000000000000000000000000000000000000..4708f836feb21980f2db7fed4a55f799b23a6ec1 Binary files /dev/null and b/docs-cn/14-reference/07-tdinsight/assets/TDinsight-1-cluster-status.png differ diff --git a/docs-cn/14-reference/07-tdinsight/assets/TDinsight-2-dnodes.png b/docs-cn/14-reference/07-tdinsight/assets/TDinsight-2-dnodes.png new file mode 100644 index 0000000000000000000000000000000000000000..f2684e6eed70e8f56697eae42b495d6bd62815e8 Binary files /dev/null and b/docs-cn/14-reference/07-tdinsight/assets/TDinsight-2-dnodes.png differ diff --git a/docs-cn/14-reference/07-tdinsight/assets/TDinsight-3-mnodes.png b/docs-cn/14-reference/07-tdinsight/assets/TDinsight-3-mnodes.png new file mode 100644 index 0000000000000000000000000000000000000000..74686691e4106b8646c3deee1e0ce73b2f53f1ea Binary files /dev/null and b/docs-cn/14-reference/07-tdinsight/assets/TDinsight-3-mnodes.png differ diff --git a/docs-cn/14-reference/07-tdinsight/assets/TDinsight-4-requests.png b/docs-cn/14-reference/07-tdinsight/assets/TDinsight-4-requests.png new file mode 100644 index 0000000000000000000000000000000000000000..27964215567f9f961c0aeaf1b863188437008fb7 Binary files /dev/null and b/docs-cn/14-reference/07-tdinsight/assets/TDinsight-4-requests.png differ diff --git a/docs-cn/14-reference/07-tdinsight/assets/TDinsight-5-database.png b/docs-cn/14-reference/07-tdinsight/assets/TDinsight-5-database.png new file mode 100644 index 0000000000000000000000000000000000000000..b0d3abbf21ec4d4bd7bfb95fcc03a5f936b22665 Binary files /dev/null and b/docs-cn/14-reference/07-tdinsight/assets/TDinsight-5-database.png differ diff --git a/docs-cn/14-reference/07-tdinsight/assets/TDinsight-6-dnode-usage.png b/docs-cn/14-reference/07-tdinsight/assets/TDinsight-6-dnode-usage.png new file mode 100644 index 0000000000000000000000000000000000000000..2b54cbeb83bcff12f20461a4f57f882e2073f231 Binary files /dev/null and b/docs-cn/14-reference/07-tdinsight/assets/TDinsight-6-dnode-usage.png differ diff --git a/docs-cn/14-reference/07-tdinsight/assets/TDinsight-7-login-history.png b/docs-cn/14-reference/07-tdinsight/assets/TDinsight-7-login-history.png new file mode 100644 index 0000000000000000000000000000000000000000..eb3848657f13900c856ac595c20766465157e9c4 Binary files /dev/null and b/docs-cn/14-reference/07-tdinsight/assets/TDinsight-7-login-history.png differ diff --git a/docs-cn/14-reference/07-tdinsight/assets/TDinsight-8-taosadapter.png b/docs-cn/14-reference/07-tdinsight/assets/TDinsight-8-taosadapter.png new file mode 100644 index 0000000000000000000000000000000000000000..d94b2e02ac9855bb3d2f77d8902e068839db364f Binary files /dev/null and b/docs-cn/14-reference/07-tdinsight/assets/TDinsight-8-taosadapter.png differ diff --git a/docs-cn/14-reference/07-tdinsight/assets/TDinsight-full.png b/docs-cn/14-reference/07-tdinsight/assets/TDinsight-full.png new file mode 100644 index 0000000000000000000000000000000000000000..654df2934597ce600a1dc2dcd0cab7e29de7076d Binary files /dev/null and b/docs-cn/14-reference/07-tdinsight/assets/TDinsight-full.png differ diff --git a/docs-cn/14-reference/07-tdinsight/assets/alert-manager-status.png b/docs-cn/14-reference/07-tdinsight/assets/alert-manager-status.png new file mode 100644 index 0000000000000000000000000000000000000000..e3afa22c0326d70567ec4529c83101c746daac87 Binary files /dev/null and b/docs-cn/14-reference/07-tdinsight/assets/alert-manager-status.png differ diff --git a/docs-cn/14-reference/07-tdinsight/assets/alert-notification-channel.png b/docs-cn/14-reference/07-tdinsight/assets/alert-notification-channel.png new file mode 100644 index 0000000000000000000000000000000000000000..198bf37141c86a66cdd91b47a331bcdeb83daaf8 Binary files /dev/null and b/docs-cn/14-reference/07-tdinsight/assets/alert-notification-channel.png differ diff --git a/docs-cn/14-reference/07-tdinsight/assets/alert-query-demo.png b/docs-cn/14-reference/07-tdinsight/assets/alert-query-demo.png new file mode 100644 index 0000000000000000000000000000000000000000..ace3aa3c2f8f14fabdac54bc25ae2d9449445b69 Binary files /dev/null and b/docs-cn/14-reference/07-tdinsight/assets/alert-query-demo.png differ diff --git a/docs-cn/14-reference/07-tdinsight/assets/alert-rule-condition-notifications.png b/docs-cn/14-reference/07-tdinsight/assets/alert-rule-condition-notifications.png new file mode 100644 index 0000000000000000000000000000000000000000..7082e49f6beb8690c36f98a3f4ff2befdb8fd014 Binary files /dev/null and b/docs-cn/14-reference/07-tdinsight/assets/alert-rule-condition-notifications.png differ diff --git a/docs-cn/14-reference/07-tdinsight/assets/alert-rule-test.png b/docs-cn/14-reference/07-tdinsight/assets/alert-rule-test.png new file mode 100644 index 0000000000000000000000000000000000000000..ffd4911b53854c42dbf0ff11838cb604fa694138 Binary files /dev/null and b/docs-cn/14-reference/07-tdinsight/assets/alert-rule-test.png differ diff --git a/docs-cn/14-reference/07-tdinsight/assets/howto-add-datasource-button.png b/docs-cn/14-reference/07-tdinsight/assets/howto-add-datasource-button.png new file mode 100644 index 0000000000000000000000000000000000000000..802c7366f921301bd7fbc62458e56b2d1eaf195c Binary files /dev/null and b/docs-cn/14-reference/07-tdinsight/assets/howto-add-datasource-button.png differ diff --git a/docs-cn/14-reference/07-tdinsight/assets/howto-add-datasource-tdengine.png b/docs-cn/14-reference/07-tdinsight/assets/howto-add-datasource-tdengine.png new file mode 100644 index 0000000000000000000000000000000000000000..019ec921b6f808671f4f864ddf3380159d4a0dcc Binary files /dev/null and b/docs-cn/14-reference/07-tdinsight/assets/howto-add-datasource-tdengine.png differ diff --git a/docs-cn/14-reference/07-tdinsight/assets/howto-add-datasource-test.png b/docs-cn/14-reference/07-tdinsight/assets/howto-add-datasource-test.png new file mode 100644 index 0000000000000000000000000000000000000000..3963abb4ea8ae0e6f5557466f7a5b746c2d2ea3c Binary files /dev/null and b/docs-cn/14-reference/07-tdinsight/assets/howto-add-datasource-test.png differ diff --git a/docs-cn/14-reference/07-tdinsight/assets/howto-add-datasource.png b/docs-cn/14-reference/07-tdinsight/assets/howto-add-datasource.png new file mode 100644 index 0000000000000000000000000000000000000000..837100464b35a5cafac474723aef603f91945ebc Binary files /dev/null and b/docs-cn/14-reference/07-tdinsight/assets/howto-add-datasource.png differ diff --git a/docs-cn/14-reference/07-tdinsight/assets/howto-dashboard-display.png b/docs-cn/14-reference/07-tdinsight/assets/howto-dashboard-display.png new file mode 100644 index 0000000000000000000000000000000000000000..98223df25499effac343ff5723544a3c289f18fa Binary files /dev/null and b/docs-cn/14-reference/07-tdinsight/assets/howto-dashboard-display.png differ diff --git a/docs-cn/14-reference/07-tdinsight/assets/howto-dashboard-import-options.png b/docs-cn/14-reference/07-tdinsight/assets/howto-dashboard-import-options.png new file mode 100644 index 0000000000000000000000000000000000000000..07aba348f02b4fb8ef68e79664920c119b842d4c Binary files /dev/null and b/docs-cn/14-reference/07-tdinsight/assets/howto-dashboard-import-options.png differ diff --git a/docs-cn/14-reference/07-tdinsight/assets/howto-import-dashboard.png b/docs-cn/14-reference/07-tdinsight/assets/howto-import-dashboard.png new file mode 100644 index 0000000000000000000000000000000000000000..7e28939ead8bf3b6e2b4330e4f9b59c2e39b5c1c Binary files /dev/null and b/docs-cn/14-reference/07-tdinsight/assets/howto-import-dashboard.png differ diff --git a/docs-cn/14-reference/07-tdinsight/assets/import-dashboard-15167.png b/docs-cn/14-reference/07-tdinsight/assets/import-dashboard-15167.png new file mode 100644 index 0000000000000000000000000000000000000000..981f640b14d18aa6f0682768d8405a232df500f6 Binary files /dev/null and b/docs-cn/14-reference/07-tdinsight/assets/import-dashboard-15167.png differ diff --git a/docs-cn/14-reference/07-tdinsight/assets/import-dashboard-for-tdengine.png b/docs-cn/14-reference/07-tdinsight/assets/import-dashboard-for-tdengine.png new file mode 100644 index 0000000000000000000000000000000000000000..94ef4fa5fe63e535118a81707b413c028ce01f70 Binary files /dev/null and b/docs-cn/14-reference/07-tdinsight/assets/import-dashboard-for-tdengine.png differ diff --git a/docs-cn/14-reference/07-tdinsight/assets/import-via-grafana-dot-com.png b/docs-cn/14-reference/07-tdinsight/assets/import-via-grafana-dot-com.png new file mode 100644 index 0000000000000000000000000000000000000000..670cacc377c2801fa9437c3c132c5c7fbc361b0f Binary files /dev/null and b/docs-cn/14-reference/07-tdinsight/assets/import-via-grafana-dot-com.png differ diff --git a/docs-cn/14-reference/07-tdinsight/assets/import_dashboard.png b/docs-cn/14-reference/07-tdinsight/assets/import_dashboard.png new file mode 100644 index 0000000000000000000000000000000000000000..d74cd36c96ee0fd24ddc6feae2da07824816f745 Binary files /dev/null and b/docs-cn/14-reference/07-tdinsight/assets/import_dashboard.png differ diff --git a/docs-cn/14-reference/07-tdinsight/assets/tdengine-grafana-7.x.json b/docs-cn/14-reference/07-tdinsight/assets/tdengine-grafana-7.x.json new file mode 100644 index 0000000000000000000000000000000000000000..b4254c428b28a0084e54b5e3c509dd2e0ec651b9 --- /dev/null +++ b/docs-cn/14-reference/07-tdinsight/assets/tdengine-grafana-7.x.json @@ -0,0 +1,3358 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "TDengine nodes metrics.", + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": 3, + "iteration": 1634275785625, + "links": [], + "panels": [ + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 57, + "panels": [], + "title": "Cluster Status", + "type": "row" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 24, + "x": 0, + "y": 1 + }, + "id": 32, + "options": { + "content": "

TDengine Cluster Dashboard

>\n", + "mode": "markdown" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "mnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show mnodes", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "-- OVERVIEW --", + "transformations": [ + { + "id": "calculateField", + "options": { + "binary": { + "left": "Time", + "operator": "+", + "reducer": "sum", + "right": "" + }, + "mode": "binary", + "reduce": { + "reducer": "sum" + } + } + } + ], + "type": "text" + }, + { + "datasource": "${ds}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 8, + "x": 0, + "y": 4 + }, + "id": 28, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": true + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show mnodes", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Master MNode", + "transformations": [ + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "regex", + "options": { + "value": "master" + } + }, + "fieldName": "role" + } + ], + "match": "all", + "type": "include" + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "dnodes" + ] + } + } + } + ], + "type": "stat" + }, + { + "datasource": "${ds}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 7, + "x": 8, + "y": 4 + }, + "id": 70, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/^Time$/", + "values": true + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show mnodes", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Master MNode Create Time", + "transformations": [ + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "regex", + "options": { + "value": "master" + } + }, + "fieldName": "role" + } + ], + "match": "all", + "type": "include" + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "Time" + ] + } + } + }, + { + "id": "calculateField", + "options": { + "mode": "reduceRow", + "reduce": { + "reducer": "min" + } + } + } + ], + "type": "stat" + }, + { + "datasource": "${ds}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": null, + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 9, + "x": 15, + "y": 4 + }, + "id": 29, + "options": { + "showHeader": true + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show variables", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Variables", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "value", + "name" + ] + } + } + }, + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "regex", + "options": { + "value": ".*" + } + }, + "fieldName": "name" + } + ], + "match": "all", + "type": "include" + } + } + ], + "type": "table" + }, + { + "datasource": "${ds}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 0, + "y": 7 + }, + "id": 33, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/.*/", + "values": true + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Table", + "queryType": "SQL", + "refId": "A", + "sql": "select server_version()", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Server Version", + "transformations": [], + "type": "stat" + }, + { + "datasource": "${ds}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 2, + "y": 7 + }, + "id": 27, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": true + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show mnodes", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Number of MNodes", + "transformations": [ + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "greater", + "options": { + "value": 0 + } + }, + "fieldName": "id" + } + ], + "match": "any", + "type": "include" + } + }, + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": [ + "count" + ] + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "id" + ] + } + } + } + ], + "type": "stat" + }, + { + "datasource": "${ds}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 5, + "y": 7 + }, + "id": 41, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show dnodes", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Total Dnodes", + "transformations": [ + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": [ + "count" + ] + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "id" + ] + } + } + } + ], + "type": "stat" + }, + { + "datasource": "${ds}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 7, + "y": 7 + }, + "id": 31, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show dnodes", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Offline Dnodes", + "transformations": [ + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "regex", + "options": { + "value": "ready" + } + }, + "fieldName": "status" + } + ], + "match": "all", + "type": "exclude" + } + }, + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": [ + "count" + ] + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "id" + ] + } + } + } + ], + "type": "stat" + }, + { + "datasource": "${ds}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 9, + "y": 7 + }, + "id": 65, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": true + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show databases;", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Number of Databases", + "transformations": [ + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": [ + "count" + ] + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "name" + ] + } + } + } + ], + "type": "stat" + }, + { + "datasource": "${ds}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 12, + "y": 7 + }, + "id": 69, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": true + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show databases;", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Total Number of Vgroups", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "vgroups" + ] + } + } + }, + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": [ + "sum" + ] + } + } + ], + "type": "stat" + }, + { + "datasource": "${ds}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "displayMode": "auto", + "filterable": true + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "role" + }, + "properties": [ + { + "id": "mappings", + "value": [ + { + "from": "", + "id": 1, + "text": "", + "to": "", + "type": 2, + "value": "" + } + ] + } + ] + } + ] + }, + "gridPos": { + "h": 3, + "w": 9, + "x": 0, + "y": 10 + }, + "id": 67, + "options": { + "showHeader": true + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "formatType": "Table", + "queryType": "SQL", + "refId": "A", + "sql": "show dnodes", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Number of DNodes for each Role", + "transformations": [ + { + "id": "groupBy", + "options": { + "fields": { + "end_point": { + "aggregations": [ + "count" + ], + "operation": "aggregate" + }, + "role": { + "aggregations": [], + "operation": "groupby" + } + } + } + }, + { + "id": "filterFieldsByName", + "options": {} + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": {}, + "renameByName": { + "end_point (count)": "Number of DNodes", + "role": "Dnode Role" + } + } + } + ], + "type": "table" + }, + { + "datasource": "${ds}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 9, + "y": 10 + }, + "id": 55, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": true + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show connections", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Number of Connections", + "transformations": [ + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": [ + "count" + ] + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "connId" + ] + } + } + } + ], + "type": "stat" + }, + { + "datasource": "${ds}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 12, + "y": 10 + }, + "id": 68, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": true + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show databases;", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Total Number of Tables", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "ntables" + ] + } + } + }, + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": [ + "sum" + ] + } + } + ], + "type": "stat" + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 13 + }, + "id": 24, + "panels": [], + "title": "Dnodes Status", + "type": "row" + }, + { + "datasource": "${ds}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "displayMode": "auto", + "filterable": true + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "status" + }, + "properties": [ + { + "id": "custom.width", + "value": null + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "vnodes" + }, + "properties": [ + { + "id": "custom.width", + "value": null + } + ] + } + ] + }, + "gridPos": { + "h": 5, + "w": 16, + "x": 0, + "y": 14 + }, + "id": 36, + "options": { + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "formatType": "Table", + "queryType": "SQL", + "refId": "A", + "sql": "show dnodes", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "DNodes Status", + "type": "table" + }, + { + "datasource": "${ds}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 16, + "y": 14 + }, + "id": 40, + "options": { + "displayLabels": [], + "legend": { + "displayMode": "table", + "placement": "right", + "values": [ + "value" + ] + }, + "pieType": "pie", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/.*/", + "values": false + }, + "text": { + "titleSize": 6 + } + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "formatType": "Table", + "queryType": "SQL", + "refId": "A", + "sql": "show dnodes", + "target": "select metric", + "type": "timeserie" + } + ], + "title": "Offline Reasons", + "transformations": [ + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "regex", + "options": { + "value": "ready" + } + }, + "fieldName": "status" + } + ], + "match": "all", + "type": "exclude" + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "offline reason", + "end_point" + ] + } + } + }, + { + "id": "groupBy", + "options": { + "fields": { + "Time": { + "aggregations": [ + "count" + ], + "operation": "aggregate" + }, + "end_point": { + "aggregations": [ + "count" + ], + "operation": "aggregate" + }, + "offline reason": { + "aggregations": [], + "operation": "groupby" + } + } + } + } + ], + "type": "piechart" + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 19 + }, + "id": 22, + "panels": [], + "title": "Mnodes Status", + "type": "row" + }, + { + "datasource": "${ds}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 24, + "x": 0, + "y": 20 + }, + "id": 38, + "options": { + "showHeader": true + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "formatType": "Table", + "queryType": "SQL", + "refId": "A", + "sql": "show mnodes;", + "target": "select metric", + "type": "timeserie" + } + ], + "title": "Mnodes Status", + "type": "table" + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 25 + }, + "id": 20, + "panels": [], + "repeat": "fqdn", + "scopedVars": { + "fqdn": { + "selected": true, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + } + }, + "title": "节点资源占用 [ $fqdn ]", + "type": "row" + }, + { + "datasource": "${ds}", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "decmbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 0, + "y": 26 + }, + "id": 66, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "/^taosd$/", + "values": false + }, + "showThresholdLabels": true, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "scopedVars": { + "fqdn": { + "selected": true, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + } + }, + "targets": [ + { + "alias": "memory", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select last(mem_taosd) as taosd, last(mem_total) as total from log.dn where fqdn = '$fqdn' and ts >= now -5m and ts < now", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Current Memory Usage of taosd", + "type": "gauge" + }, + { + "datasource": "${ds}", + "description": "taosd max memery last 10 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 0.5 + }, + { + "color": "red", + "value": 0.8 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "last(cpu_taosd)" + }, + "properties": [ + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + } + ] + } + ] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 5, + "y": 26 + }, + "id": 45, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "/^last\\(cpu_taosd\\)$/", + "values": false + }, + "showThresholdLabels": true, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "scopedVars": { + "fqdn": { + "selected": true, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + } + }, + "targets": [ + { + "alias": "mem_taosd", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select last(cpu_taosd) from log.dn where fqdn = '$fqdn'", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Current CPU Usage of taosd", + "type": "gauge" + }, + { + "datasource": "${ds}", + "description": "avg band speed last one minute", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 8192, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 4916 + }, + { + "color": "red", + "value": 6554 + } + ] + }, + "unit": "Kbits" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 4, + "x": 10, + "y": 26 + }, + "id": 14, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "scopedVars": { + "fqdn": { + "selected": true, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + } + }, + "targets": [ + { + "alias": "band_speed", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select avg(band_speed) from log.dn where fqdn='$fqdn' and ts >= now-5m and ts < now interval(1m)", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "band speed", + "type": "gauge" + }, + { + "datasource": "${ds}", + "description": "io read/write rate", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 8192, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 4916 + }, + { + "color": "red", + "value": 6554 + } + ] + }, + "unit": "Kbits" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 14, + "y": 26 + }, + "id": 48, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "scopedVars": { + "fqdn": { + "selected": true, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + } + }, + "targets": [ + { + "alias": "", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select last(io_read) as io_read, last(io_write) as io_write from log.dn where fqdn='$fqdn' and ts >= now-1h and ts < now interval(1m)", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "IO Rate", + "type": "gauge" + }, + { + "datasource": "${ds}", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 1, + "min": 0, + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 75 + }, + { + "color": "red", + "value": 80 + }, + { + "color": "dark-red", + "value": 95 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 19, + "y": 26 + }, + "id": 51, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "/^disk_used_percent$/", + "values": false + }, + "showThresholdLabels": true, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "scopedVars": { + "fqdn": { + "selected": true, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + } + }, + "targets": [ + { + "alias": "disk_used", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select last(disk_used) as used from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(1m)", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + }, + { + "alias": "disk_total", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "B", + "sql": "select last(disk_total) as total from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(1m)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "disk_used_percent", + "expression": "A/B", + "formatType": "Time series", + "hide": false, + "queryType": "Arithmetic", + "refId": "C", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Disk Used", + "transformations": [ + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": [ + "lastNotNull" + ] + } + } + ], + "type": "gauge" + }, + { + "datasource": "${ds}", + "description": "taosd max memery last 10 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "decmbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 0, + "y": 32 + }, + "id": 12, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "/^taosd$/", + "values": false + }, + "showThresholdLabels": true, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "scopedVars": { + "fqdn": { + "selected": true, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + } + }, + "targets": [ + { + "alias": "memory", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select max(mem_taosd) as taosd, max(mem_total) as total from log.dn where fqdn = '$fqdn' and ts >= now -5m and ts < now", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Max Memory Usage of taosd in Last 5 minute", + "type": "gauge" + }, + { + "datasource": "${ds}", + "description": "taosd max memery last 10 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "max": 1, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 0.5 + }, + { + "color": "red", + "value": 0.8 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 5, + "y": 32 + }, + "id": 43, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": true, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "scopedVars": { + "fqdn": { + "selected": true, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + } + }, + "targets": [ + { + "alias": "mem_taosd", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select max(cpu_taosd) from log.dn where fqdn = '$fqdn' and ts >= now -5m and ts < now", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Max CPU Usage of taosd in Last 5 minute", + "type": "gauge" + }, + { + "datasource": "${ds}", + "description": "avg band speed last one minute", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 8192, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 4916 + }, + { + "color": "red", + "value": 6554 + } + ] + }, + "unit": "Kbits" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 4, + "x": 10, + "y": 32 + }, + "id": 50, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "scopedVars": { + "fqdn": { + "selected": true, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + } + }, + "targets": [ + { + "alias": "band_speed", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select max(band_speed) from log.dn where fqdn = '$fqdn' and ts >= now-1h", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Max band speed in last hour", + "type": "gauge" + }, + { + "datasource": "${ds}", + "description": "io read/write rate", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 8192, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 4916 + }, + { + "color": "red", + "value": 6554 + } + ] + }, + "unit": "Kbits" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 14, + "y": 32 + }, + "id": 49, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "scopedVars": { + "fqdn": { + "selected": true, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + } + }, + "targets": [ + { + "alias": "", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select max(io_read) as io_read, max(io_write) as io_write from log.dn where fqdn = '$fqdn'", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Max IO Rate in last hour", + "type": "gauge" + }, + { + "datasource": "${ds}", + "description": "io read/write rate", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 8192, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 4916 + }, + { + "color": "red", + "value": 6554 + } + ] + }, + "unit": "cpm" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 19, + "y": 32 + }, + "id": 52, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "scopedVars": { + "fqdn": { + "selected": true, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + } + }, + "targets": [ + { + "alias": "req-http", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select sum(req_http) as req_http from log.dn where fqdn = '$fqdn' and ts >= now - 1h interval(1m)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "req-inserts", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "B", + "sql": "select sum(req_insert) as req_insert from log.dn where fqdn = '$fqdn' and ts >= now - 1h interval(1m)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "req-selects", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "C", + "sql": "select sum(req_select) as req_select from log.dn where fqdn = '$fqdn' and ts >= now - 1h interval(1m)", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Requests in last Minute", + "type": "gauge" + }, + { + "aliasColors": {}, + "bars": false, + "cacheTimeout": null, + "dashLength": 10, + "dashes": false, + "datasource": "${ds}", + "description": "monitor system cpu", + "fieldConfig": { + "defaults": { + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 11, + "w": 12, + "x": 0, + "y": 38 + }, + "hiddenSeries": false, + "hideTimeOverride": true, + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.10", + "pointradius": 2, + "points": false, + "renderer": "flot", + "scopedVars": { + "fqdn": { + "selected": true, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + } + }, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "cpu_system", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "A", + "sql": "select avg(cpu_system) from log.dn where fqdn='$fqdn' and ts >= now-1h and ts < now interval(30s)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "cpu_taosd", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "B", + "sql": "select avg(cpu_taosd) from log.dn where fqdn='$fqdn' and ts >= now-1h and ts < now interval(30s)", + "target": "select metric", + "type": "timeserie" + } + ], + "thresholds": [], + "timeFrom": "1h", + "timeRegions": [], + "timeShift": "30s", + "title": "CPU 资源占用情况", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:58", + "decimals": null, + "format": "percent", + "label": "使用占比", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:59", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "cacheTimeout": null, + "dashLength": 10, + "dashes": false, + "datasource": "${ds}", + "description": "monitor system cpu", + "fieldConfig": { + "defaults": { + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 11, + "w": 12, + "x": 12, + "y": 38 + }, + "hiddenSeries": false, + "hideTimeOverride": true, + "id": 42, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.10", + "pointradius": 2, + "points": false, + "renderer": "flot", + "scopedVars": { + "fqdn": { + "selected": true, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + } + }, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "system", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "A", + "sql": "select avg(mem_system) from log.dn where fqdn = '$fqdn' and ts >= now-1h and ts < now interval(30s)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "taosd", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "B", + "sql": "select avg(mem_taosd) from log.dn where fqdn = '$fqdn' and ts >= now-1h and ts < now interval(30s)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "total", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "C", + "sql": "select avg(mem_total) from log.dn where fqdn = '$fqdn' and ts >= now-1h and ts < now interval(30s)", + "target": "select metric", + "type": "timeserie" + } + ], + "thresholds": [], + "timeFrom": "1h", + "timeRegions": [], + "timeShift": "30s", + "title": "内存资源占用情况", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:58", + "decimals": null, + "format": "decmbytes", + "label": "使用占比", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:59", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${ds}", + "fieldConfig": { + "defaults": { + "unit": "percent" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 49 + }, + "hiddenSeries": false, + "id": 54, + "legend": { + "alignAsTable": false, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.10", + "pointradius": 2, + "points": false, + "renderer": "flot", + "scopedVars": { + "fqdn": { + "selected": true, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + } + }, + "seriesOverrides": [ + { + "$$hashKey": "object:249", + "alias": "disk_used", + "hiddenSeries": true + }, + { + "$$hashKey": "object:256", + "alias": "disk_total", + "hiddenSeries": true + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "disk_used", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "A", + "sql": "select avg(disk_used) from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(30s)", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + }, + { + "alias": "disk_total", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "B", + "sql": "select avg(disk_total) from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(30s)", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + }, + { + "alias": "percent", + "expression": "A/B * 100", + "formatType": "Time series", + "hide": false, + "queryType": "Arithmetic", + "refId": "C", + "target": "select metric", + "type": "timeserie" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Disk Used Percent", + "tooltip": { + "shared": false, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:456", + "format": "percent", + "label": null, + "logBase": 1, + "max": "100", + "min": "0", + "show": true + }, + { + "$$hashKey": "object:457", + "format": "percentunit", + "label": "Disk Used", + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${ds}", + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 49 + }, + "hiddenSeries": false, + "id": 64, + "legend": { + "alignAsTable": false, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.10", + "pointradius": 2, + "points": false, + "renderer": "flot", + "scopedVars": { + "fqdn": { + "selected": true, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + } + }, + "seriesOverrides": [ + { + "$$hashKey": "object:834", + "alias": "percent", + "yaxis": 2 + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "disk_used", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "A", + "sql": "select avg(disk_used) from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(30s)", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + }, + { + "alias": "disk_total", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "B", + "sql": "select avg(disk_total) from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(30s)", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + }, + { + "alias": "percent", + "expression": "A/B", + "formatType": "Time series", + "hide": false, + "queryType": "Arithmetic", + "refId": "C", + "target": "select metric", + "type": "timeserie" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Disk Used", + "tooltip": { + "shared": false, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:456", + "format": "decgbytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:457", + "format": "percentunit", + "label": "Disk Used", + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "cacheTimeout": null, + "dashLength": 10, + "dashes": false, + "datasource": "${ds}", + "description": "total select request per minute last hour", + "fieldConfig": { + "defaults": { + "unit": "cpm" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 58 + }, + "hiddenSeries": false, + "id": 8, + "interval": null, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "maxDataPoints": 100, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.10", + "pointradius": 2, + "points": false, + "renderer": "flot", + "scopedVars": { + "fqdn": { + "selected": true, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + } + }, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "req_select", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select sum(req_select) from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(1m)", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + }, + { + "alias": "req_insert", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "B", + "sql": "select sum(req_insert) from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(1m)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "req_http", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "C", + "sql": "select sum(req_http) from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(1m)", + "target": "select metric", + "type": "timeserie" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Requets Count per Minutes $fqdn", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:127", + "format": "cpm", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "$$hashKey": "object:128", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "cacheTimeout": null, + "dashLength": 10, + "dashes": false, + "datasource": "${ds}", + "description": "io", + "fieldConfig": { + "defaults": { + "links": [], + "unit": "Kbits" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 58 + }, + "hiddenSeries": false, + "hideTimeOverride": true, + "id": 47, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.10", + "pointradius": 2, + "points": false, + "renderer": "flot", + "scopedVars": { + "fqdn": { + "selected": true, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + } + }, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "io-read", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "A", + "sql": "select avg(io_read) from log.dn where fqdn = '$fqdn' and ts >= now-1h and ts < now interval(1m)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "io-write", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "B", + "sql": "select avg(io_write) from log.dn where fqdn = '$fqdn' and ts >= now-1h and ts < now interval(1m)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "io-read-last-hour", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "C", + "sql": "select avg(io_read) from log.dn where fqdn = '$fqdn' and ts >= now-2h and ts < now - 1h interval(1m)", + "target": "select metric", + "timeshift": { + "period": 1, + "unit": "hours" + }, + "type": "timeserie" + }, + { + "alias": "io-write-last-hour", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "D", + "sql": "select avg(io_write) from log.dn where fqdn = '$fqdn' and ts >= now-1h and ts < now interval(1m)", + "target": "select metric", + "timeshift": { + "period": 1, + "unit": "hours" + }, + "type": "timeserie" + } + ], + "thresholds": [], + "timeFrom": "1h", + "timeRegions": [], + "timeShift": "30s", + "title": "IO", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:58", + "decimals": null, + "format": "Kbits", + "label": "使用占比", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:59", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 67 + }, + "id": 63, + "panels": [], + "title": "Login History", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${ds}", + "fieldConfig": { + "defaults": { + "displayName": "Logins Per Minute", + "unit": "cpm" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 68 + }, + "hiddenSeries": false, + "id": 61, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.10", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "$$hashKey": "object:756", + "alias": "logins", + "nullPointMode": "null as zero" + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "logins", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select count(*) from log.log where ts >= $from and ts < $to interval (1m)", + "target": "select metric", + "type": "timeserie" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Login Counts per Minute", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:585", + "format": "cpm", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:586", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "1m", + "schemaVersion": 27, + "style": "dark", + "tags": [ + "TDengine" + ], + "templating": { + "list": [ + { + "current": { + "selected": true, + "text": "TDengine", + "value": "TDengine" + }, + "description": "TDengine Data Source Selector", + "error": null, + "hide": 0, + "includeAll": false, + "label": "Datasource", + "multi": false, + "name": "ds", + "options": [], + "query": "tdengine-datasource", + "queryValue": "", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "allValue": null, + "current": { + "selected": false, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + }, + "datasource": "${ds}", + "definition": "select fqdn from log.dn", + "description": "TDengine Nodes FQDN (Hostname)", + "error": null, + "hide": 0, + "includeAll": false, + "label": null, + "multi": false, + "name": "fqdn", + "options": [], + "query": "select fqdn from log.dn", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "TDengine", + "uid": "tdengine", + "version": 8 +} \ No newline at end of file diff --git a/docs-cn/14-reference/07-tdinsight/assets/tdengine-grafana.json b/docs-cn/14-reference/07-tdinsight/assets/tdengine-grafana.json new file mode 100644 index 0000000000000000000000000000000000000000..7696cfce3fcca3dd5f9278c91d3df2c1b32b9c97 --- /dev/null +++ b/docs-cn/14-reference/07-tdinsight/assets/tdengine-grafana.json @@ -0,0 +1,627 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": true, + "colors": ["#299c46", "rgba(237, 129, 40, 0.89)", "#d44a3a"], + "datasource": "TDengine", + "description": "total select request per minute last hour", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 6, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 8, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "options": {}, + "postfix": "次数/min", + "postfixFontSize": "20%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true, + "ymax": null, + "ymin": null + }, + "tableColumn": "", + "targets": [ + { + "alias": "req_select", + "refId": "A", + "sql": "select sum(req_select) from log.dn where ts >= now-1h and ts < now interval(1m)", + "target": "select metric", + "type": "timeserie" + } + ], + "thresholds": "120,240", + "timeFrom": null, + "timeShift": null, + "title": "req select", + "type": "singlestat", + "valueFontSize": "150%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "total" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": true, + "colors": ["#299c46", "rgba(237, 129, 40, 0.89)", "#d44a3a"], + "datasource": "TDengine", + "description": "total insert request per minute for last hour", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 6, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 6, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "options": {}, + "postfix": "次数/min", + "postfixFontSize": "20%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": true, + "ymax": null, + "ymin": null + }, + "tableColumn": "", + "targets": [ + { + "alias": "req_insert", + "refId": "A", + "sql": "select sum(req_insert) from log.dn where ts >= now-1h and ts < now interval(1m)", + "target": "select metric", + "type": "timeserie" + } + ], + "thresholds": "110,240", + "timeFrom": null, + "timeShift": null, + "title": "req insert", + "type": "singlestat", + "valueFontSize": "150%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "total" + }, + { + "datasource": "TDengine", + "description": "taosd max memery last 10 minutes", + "gridPos": { + "h": 6, + "w": 8, + "x": 0, + "y": 6 + }, + "id": 12, + "options": { + "fieldOptions": { + "calcs": ["mean"], + "defaults": { + "mappings": [], + "max": 4096, + "min": 0, + "thresholds": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + }, + { + "color": "#EAB839", + "value": 2048 + } + ], + "unit": "decmbytes" + }, + "thresholds": [ + { + "index": 0, + "color": "green", + "value": null + }, + { + "index": 1, + "color": "red", + "value": 80 + }, + { + "index": 2, + "color": "#EAB839", + "value": 2048 + } + ], + "override": {}, + "values": false + }, + "orientation": "auto", + "showThresholdLabels": true, + "showThresholdMarkers": true + }, + "pluginVersion": "6.4.3", + "targets": [ + { + "alias": "mem_taosd", + "refId": "A", + "sql": "select max(mem_taosd) from log.dn where ts >= now -10m and ts < now", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "taosd memery", + "type": "gauge" + }, + { + "datasource": "TDengine", + "description": "max System Memory last 1 hour", + "gridPos": { + "h": 6, + "w": 8, + "x": 8, + "y": 6 + }, + "id": 10, + "options": { + "fieldOptions": { + "calcs": ["last"], + "mappings": [], + "defaults": { + "mappings": [], + "max": 4, + "min": 0, + "thresholds": [ + { + "color": "green", + "value": null + }, + { + "color": "semi-dark-orange", + "value": 60 + }, + { + "color": "dark-red", + "value": 80 + } + ], + "title": "", + "unit": "decmbytes" + }, + "thresholds": [ + { + "index": 0, + "color": "green", + "value": null + }, + { + "index": 1, + "color": "semi-dark-orange", + "value": 60 + }, + { + "index": 2, + "color": "dark-red", + "value": 80 + } + ], + "override": {}, + "values": false + }, + "orientation": "auto", + "showThresholdLabels": true, + "showThresholdMarkers": true + }, + "pluginVersion": "6.4.3", + "targets": [ + { + "alias": "mem_system", + "refId": "A", + "sql": "select max(mem_system) from log.dn where ts >= now -10h and ts < now", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "system memory", + "type": "gauge" + }, + { + "datasource": "TDengine", + "description": "avg band speed last one minute", + "gridPos": { + "h": 6, + "w": 8, + "x": 16, + "y": 6 + }, + "id": 14, + "options": { + "fieldOptions": { + "calcs": ["last"], + "defaults": { + "mappings": [], + "max": 8192, + "min": 0, + "thresholds": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 4916 + }, + { + "color": "red", + "value": 6554 + } + ], + "unit": "Kbits" + }, + "mappings": [], + "thresholds": [ + { + "index": 0, + "color": "green", + "value": null + }, + { + "index": 1, + "color": "#EAB839", + "value": 4916 + }, + { + "index": 2, + "color": "red", + "value": 6554 + } + ], + "override": {}, + "values": false + }, + "orientation": "auto", + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "6.4.3", + "targets": [ + { + "alias": "band_speed", + "refId": "A", + "sql": "select avg(band_speed) from log.dn where ts >= now-1h and ts < now interval(1m)", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "band speed", + "type": "gauge" + }, + { + "aliasColors": {}, + "bars": false, + "cacheTimeout": null, + "dashLength": 10, + "dashes": false, + "datasource": "TDengine", + "description": "monitor system cpu", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 11, + "w": 12, + "x": 0, + "y": 12 + }, + "hideTimeOverride": true, + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pluginVersion": "6.4.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "cpu_system11", + "hide": false, + "refId": "A", + "sql": "select avg(cpu_system) from log.dn where ts >= now-1h and ts < now interval(1s)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "cpu_taosd", + "hide": false, + "refId": "B", + "sql": "select avg(cpu_taosd) from log.dn where ts >= now-1h and ts < now interval(1s)", + "target": "select metric", + "type": "timeserie" + } + ], + "thresholds": [], + "timeFrom": "1h", + "timeRegions": [], + "timeShift": "30s", + "title": "cpu_system", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": null, + "format": "percent", + "label": "使用占比", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "TDengine", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 11, + "w": 12, + "x": 12, + "y": 12 + }, + "id": 18, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "", + "refId": "A", + "sql": "select avg(disk_used) disk_used from log.dn where ts >= $from and ts < $to interval(1s) group by fqdn", + "target": "select metric", + "type": "timeserie" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "avg_disk_used", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decgbytes", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "5s", + "schemaVersion": 20, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "TDengine", + "uid": "FE-vpe0Wk", + "version": 1 +} diff --git a/docs-cn/14-reference/07-tdinsight/assets/tdengine_dashboard.png b/docs-cn/14-reference/07-tdinsight/assets/tdengine_dashboard.png new file mode 100644 index 0000000000000000000000000000000000000000..0101e7430cb2ef673818de8bd3af53d0d082ad3f Binary files /dev/null and b/docs-cn/14-reference/07-tdinsight/assets/tdengine_dashboard.png differ diff --git a/docs-cn/14-reference/07-tdinsight/index.md b/docs-cn/14-reference/07-tdinsight/index.md new file mode 100644 index 0000000000000000000000000000000000000000..a554d7ee6b36797940282fa8401df2f22c4cf579 --- /dev/null +++ b/docs-cn/14-reference/07-tdinsight/index.md @@ -0,0 +1,428 @@ +--- +title: TDinsight - 基于Grafana的TDengine零依赖监控解决方案 +sidebar_label: TDinsight +--- + +TDinsight 是使用内置监控数据库和 [Grafana] 对 TDengine 进行监控的解决方案。 + +TDengine 启动后,会自动创建一个监测数据库 `log`,并自动将服务器的 CPU、内存、硬盘空间、带宽、请求数、磁盘读写速度、慢查询等信息定时写入该数据库,并对重要的系统操作(比如登录、创建、删除数据库等)以及各种错误报警信息进行记录。通过 [Grafana] 和 [TDengine 数据源插件](https://github.com/taosdata/grafanaplugin/releases),TDinsight 将集群状态、节点信息、插入及查询请求、资源使用情况等进行可视化展示,同时还支持 vnode、dnode、mnode 节点状态异常告警,为开发者实时监控 TDengine 集群运行状态提供了便利。本文将指导用户安装 Grafana 服务器并通过 `TDinsight.sh` 安装脚本自动安装 TDengine 数据源插件及部署 TDinsight 可视化面板。 + +## 系统要求 + +要部署 TDinsight,需要一个单节点的 TDengine 服务器或一个多节点的 [TDengine] 集群,以及一个[Grafana]服务器。此仪表盘需要 TDengine 2.3.3.0 及以上,并启用 `log` 数据库(`monitor = 1`)。 + +## 安装 Grafana + +我们建议在此处使用最新的[Grafana] 7 或 8 版本。您可以在任何[支持的操作系统](https://grafana.com/docs/grafana/latest/installation/requirements/#supported-operating-systems)中,按照 [Grafana 官方文档安装说明](https://grafana.com/docs/grafana/latest/installation/) 安装 [Grafana]。 + +### 在 Debian 或 Ubuntu 上安装 Grafana + +对于 Debian 或 Ubuntu 操作系统,建议使用 Grafana 镜像仓库。使用如下命令从零开始安装: + +```bash +sudo apt-get install -y apt-transport-https +sudo apt-get install -y software-properties-common wget +wget -q -O - https://packages.grafana.com/gpg.key |\ + sudo apt-key add - +echo "deb https://packages.grafana.com/oss/deb stable main" |\ + sudo tee -a /etc/apt/sources.list.d/grafana.list +sudo apt-get update +sudo apt-get install grafana +``` + +### 在 CentOS / RHEL 上安装 Grafana + +您可以从官方 YUM 镜像仓库安装。 + +```bash +sudo tee /etc/yum.repos.d/grafana.repo << EOF +[grafana] +name=grafana +baseurl=https://packages.grafana.com/oss/rpm +repo_gpgcheck=1 +enabled=1 +gpgcheck=1 +gpgkey=https://packages.grafana.com/gpg.key +sslverify=1 +sslcacert=/etc/pki/tls/certs/ca-bundle.crt +EOF +sudo yum install grafana +``` + +或者用 RPM 安装: + +```bash +wget https://dl.grafana.com/oss/release/grafana-7.5.11-1.x86_64.rpm +sudo yum install grafana-7.5.11-1.x86_64.rpm +# or +sudo yum install \ + https://dl.grafana.com/oss/release/grafana-7.5.11-1.x86_64.rpm +``` + +## 自动部署 TDinsight + +我们提供了一个自动化安装脚本 [`TDinsight.sh`](https://github.com/taosdata/grafanaplugin/releases/latest/download/TDinsight.sh) 脚本以便用户快速进行安装配置。 + +您可以通过 `wget` 或其他工具下载该脚本: + +```bash +wget https://github.com/taosdata/grafanaplugin/releases/latest/download/TDinsight.sh +chmod +x TDinsight.sh +./TDinsight.sh +``` + +这个脚本会自动下载最新的[Grafana TDengine 数据源插件](https://github.com/taosdata/grafanaplugin/releases/latest) 和 [TDinsight 仪表盘](https://grafana.com/grafana/dashboards/15167) ,将命令行选项中的可配置参数转为 [Grafana Provisioning](https://grafana.com/docs/grafana/latest/administration/provisioning/) 配置文件,以进行自动化部署及更新等操作。利用该脚本提供的告警设置选项,你还可以获得内置的阿里云短信告警通知支持。 + +假设您在同一台主机上使用 TDengine 和 Grafana 的默认服务。运行 `./TDinsight.sh` 并打开 Grafana 浏览器窗口就可以看到 TDinsight 仪表盘了。 + +下面是 TDinsight.sh 的用法说明: + +```text +Usage: + ./TDinsight.sh + ./TDinsight.sh -h|--help + ./TDinsight.sh -n -a -u -p + +Install and configure TDinsight dashboard in Grafana on Ubuntu 18.04/20.04 system. + +-h, -help, --help Display help + +-V, -verbose, --verbose Run script in verbose mode. Will print out each step of execution. + +-v, --plugin-version TDengine datasource plugin version, [default: latest] + +-P, --grafana-provisioning-dir Grafana provisioning directory, [default: /etc/grafana/provisioning/] +-G, --grafana-plugins-dir Grafana plugins directory, [default: /var/lib/grafana/plugins] +-O, --grafana-org-id Grafana organization id. [default: 1] + +-n, --tdengine-ds-name TDengine datasource name, no space. [default: TDengine] +-a, --tdengine-api TDengine REST API endpoint. [default: http://127.0.0.1:6041] +-u, --tdengine-user TDengine user name. [default: root] +-p, --tdengine-password TDengine password. [default: taosdata] + +-i, --tdinsight-uid Replace with a non-space ASCII code as the dashboard id. [default: tdinsight] +-t, --tdinsight-title Dashboard title. [default: TDinsight] +-e, --tdinsight-editable If the provisioning dashboard could be editable. [default: false] + +-E, --external-notifier Apply external notifier uid to TDinsight dashboard. + +Aliyun SMS as Notifier: +-s, --sms-enabled To enable tdengine-datasource plugin builtin Aliyun SMS webhook. +-N, --sms-notifier-name Provisioning notifier name.[default: TDinsight Builtin SMS] +-U, --sms-notifier-uid Provisioning notifier uid, use lowercase notifier name by default. +-D, --sms-notifier-is-default Set notifier as default. +-I, --sms-access-key-id Aliyun SMS access key id +-K, --sms-access-key-secret Aliyun SMS access key secret +-S, --sms-sign-name Sign name +-C, --sms-template-code Template code +-T, --sms-template-param Template param, a escaped JSON string like '{"alarm_level":"%s","time":"%s","name":"%s","content":"%s"}' +-B, --sms-phone-numbers Comma-separated numbers list, eg "189xxxxxxxx,132xxxxxxxx" +-L, --sms-listen-addr [default: 127.0.0.1:9100] +``` + +大多数命令行选项都可以通过环境变量获得同样的效果。 + +| 短选项 | 长选项 | 环境变量 | 说明 | +| ------ | -------------------------- | ---------------------------- | --------------------------------------------------------------------------- | +| -v | --plugin-version | TDENGINE_PLUGIN_VERSION | TDengine 数据源插件版本,默认使用最新版。 | +| -P | --grafana-provisioning-dir | GF_PROVISIONING_DIR | Grafana 配置目录,默认为`/etc/grafana/provisioning/` | +| -G | --grafana-plugins-dir | GF_PLUGINS_DIR | Grafana 插件目录,默认为`/var/lib/grafana/plugins`。 | +| -O | --grafana-org-id | GF_ORG_ID | Grafana 组织 ID,默认为 1。 | +| -n | --tdengine-ds-name | TDENGINE_DS_NAME | TDengine 数据源名称,默认为 TDengine。 | +| -a | --tdengine-api | TDENGINE_API | TDengine REST API 端点。默认为`http://127.0.0.1:6041`。 | +| -u | --tdengine-user | TDENGINE_USER | TDengine 用户名。 [默认值:root] | +| -p | --tdengine-密码 | TDENGINE_PASSWORD | TDengine 密码。 [默认:taosdata] | +| -i | --tdinsight-uid | TDINSIGHT_DASHBOARD_UID | TDinsight 仪表盘`uid`。 [默认值:tdinsight] | +| -t | --tdinsight-title | TDINSIGHT_DASHBOARD_TITLE | TDinsight 仪表盘标题。 [默认:TDinsight] | +| -e | --tdinsight-可编辑 | TDINSIGHT_DASHBOARD_EDITABLE | 如果配置仪表盘可以编辑。 [默认值:false] | +| -E | --external-notifier | EXTERNAL_NOTIFIER | 将外部通知程序 uid 应用于 TDinsight 仪表盘。 | +| -s | --sms-enabled | SMS_ENABLED | 启用阿里云短信 webhook 内置的 tdengine-datasource 插件。 | +| -N | --sms-notifier-name | SMS_NOTIFIER_NAME | 供应通知程序名称。[默认:`TDinsight Builtin SMS`] | +| -U | --sms-notifier-uid | SMS_NOTIFIER_UID | "Notification Channel" `uid`,默认使用程序名称的小写,其他字符用 “-” 代替。 | +| -D | --sms-notifier-is-default | SMS_NOTIFIER_IS_DEFAULT | 将内置短信通知设置为默认值。 | +| -I | --sms-access-key-id | SMS_ACCESS_KEY_ID | 阿里云短信访问密钥 id | +| -K | --sms-access-key-secret | SMS_ACCESS_KEY_SECRET | 阿里云短信访问秘钥 | +| -S | --sms-sign-name | SMS_SIGN_NAME | 签名 | +| -C | --sms-template-code | SMS_TEMPLATE_CODE | 模板代码 | +| -T | --sms-template-param | SMS_TEMPLATE_PARAM | 模板参数的 JSON 模板 | +| -B | --sms-phone-numbers | SMS_PHONE_NUMBERS | 逗号分隔的手机号列表,例如`"189xxxxxxxx,132xxxxxxxx"` | +| -L | --sms-listen-addr | SMS_LISTEN_ADDR | 内置 SMS webhook 监听地址,默认为`127.0.0.1:9100` | + +假设您在主机 `tdengine` 上启动 TDengine 数据库,HTTP API 端口为 `6041`,用户为 `root1`,密码为 `pass5ord`。执行脚本: + +```bash +sudo ./TDinsight.sh -a http://tdengine:6041 -u root1 -p pass5ord +``` + +我们提供了一个“-E”选项,用于从命令行配置 TDinsight 使用现有的通知通道(Notification Channel)。假设你的 Grafana 用户和密码是 `admin:admin`,使用以下命令获取已有的通知通道的`uid`: + +```bash +curl --no-progress-meter -u admin:admin http://localhost:3000/api/alert-notifications | jq +``` + +使用上面获取的 `uid` 值作为 `-E` 输入。 + +```bash +sudo ./TDinsight.sh -a http://tdengine:6041 -u root1 -p pass5ord -E existing-notifier +``` + +如果你想使用[阿里云短信](https://www.aliyun.com/product/sms)服务作为通知渠道,你应该使用`-s`标志启用并添加以下参数: + +- `-N`:Notification Channel 名,默认为`TDinsight Builtin SMS`。 +- `-U`:Channel uid,默认是 `name` 的小写,任何其他字符都替换为 - ,对于默认的 `-N`,其 uid 为 `tdinsight-builtin-sms`。 +- `-I`:阿里云短信访问密钥 id。 +- `-K`:阿里云短信访问秘钥。 +- `-S`:阿里云短信签名。 +- `-C`:阿里云短信模板 ID。 +- `-T`:阿里云短信模板参数,为 JSON 格式模板,示例如下 `'{"alarm_level":"%s","time":"%s","name":"%s","content":"%s "}'`。有四个参数:告警级别、时间、名称和告警内容。 +- `-B`:电话号码列表,以逗号`,`分隔。 + +如果要监控多个 TDengine 集群,则需要设置多个 TDinsight 仪表盘。设置非默认 TDinsight 需要进行一些更改: `-n` `-i` `-t` 选项需要更改为非默认名称,如果使用 内置短信告警功能,`-N` 和 `-L` 也应该改变。 + +```bash +sudo ./TDengine.sh -n TDengine-Env1 -a http://another:6041 -u root -p taosdata -i tdinsight-env1 -t 'TDinsight Env1' +# 如果使用内置短信通知 +sudo ./TDengine.sh -n TDengine-Env1 -a http://another:6041 -u root -p taosdata -i tdinsight-env1 -t 'TDinsight Env1' \ + -s -N 'Env1 SMS' -I xx -K xx -S xx -C SMS_XX -T '' -B 00000000000 -L 127.0.0.01:10611 +``` + +请注意,配置数据源、通知 Channel 和仪表盘在前端是不可更改的。您应该再次通过此脚本更新配置或手动更改 `/etc/grafana/provisioning` 目录(这是 Grafana 的默认目录,根据需要使用`-P`选项更改)中的配置文件。 + +特别地,当您使用 Grafana Cloud 或其他组织时,`-O` 可用于设置组织 ID。 `-G` 可指定 Grafana 插件安装目录。 `-e` 参数将仪表盘设置为可编辑。 + +## 手动设置 TDinsight + +### 安装 TDengine 数据源插件 + +从 GitHub 安装 TDengine 最新版数据源插件。 + +```bash +get_latest_release() { + curl --silent "https://api.github.com/repos/taosdata/grafanaplugin/releases/latest" | + grep '"tag_name":' | + sed -E 's/.*"v([^"]+)".*/\1/' +} +TDENGINE_PLUGIN_VERSION=$(get_latest_release) +sudo grafana-cli \ + --pluginUrl https://github.com/taosdata/grafanaplugin/releases/download/v$TDENGINE_PLUGIN_VERSION/tdengine-datasource-$TDENGINE_PLUGIN_VERSION.zip \ + plugins install tdengine-datasource +``` + +:::note +3.1.6 和更早版本插件需要在配置文件 `/etc/grafana/grafana.ini` 中添加如下设置,以启用未签名插件。 + +```ini +[plugins] +allow_loading_unsigned_plugins = tdengine-datasource +``` +::: + +### 启动 Grafana 服务 + +```bash +sudo systemctl start grafana-server +sudo systemctl enable grafana-server +``` + +### 登录到 Grafana + +在 Web 浏览器中打开默认的 Grafana 网址:`http://localhost:3000`。 +默认用户名/密码都是 `admin`。Grafana 会要求在首次登录后更改密码。 + +### 添加 TDengine 数据源 + +指向 **Configurations** -> **Data Sources** 菜单,然后点击 **Add data source** 按钮。 + +![添加数据源按钮](./assets/howto-add-datasource-button.png) + +搜索并选择**TDengine**。 + +![添加数据源](./assets/howto-add-datasource-tdengine.png) + +配置 TDengine 数据源。 + +![数据源配置](./assets/howto-add-datasource.png) + +保存并测试,正常情况下会报告 'TDengine Data source is working'。 + +![数据源测试](./assets/howto-add-datasource-test.png) + +### 导入仪表盘 + +指向 **+** / **Create** - **import**(或 `/dashboard/import` url)。 + +![导入仪表盘和配置](./assets/import_dashboard.png) + +在 **Import via grafana.com** 位置键入仪表盘 ID `15167` 并 **Load**。 + +![通过 grafana.com 导入](./assets/import-dashboard-15167.png) + +导入完成后,TDinsight 的完整页面视图如下所示。 + +![显示](./assets/TDinsight-full.png) + +## TDinsight 仪表盘详细信息 + +TDinsight 仪表盘旨在提供 TDengine 相关资源使用情况[dnodes, mnodes, vnodes](https://www.taosdata.com/cn/documentation/architecture#cluster)或数据库的使用情况和状态。 + +指标详情如下: + +### 集群状态 + +![tdinsight-mnodes-overview](./assets/TDinsight-1-cluster-status.png) + +这部分包括集群当前信息和状态,告警信息也在此处(从左到右,从上到下)。 + +- **First EP**:当前 TDengine 集群中的`firstEp`设置。 +- **Version**:TDengine 服务器版本(master mnode)。 +- **Master Uptime**: 当前 Master MNode 被选举为 Master 后经过的时间。 +- **Expire Time** - 企业版过期时间。 +- **Used Measuring Points** - 企业版已使用的测点数。 +- **Databases** - 数据库个数。 +- **Connections** - 当前连接个数。 +- **DNodes/MNodes/VGroups/VNodes**:每种资源的总数和存活数。 +- **DNodes/MNodes/VGroups/VNodes Alive Percent**:每种资源的存活数/总数的比例,启用告警规则,并在资源存活率(1 分钟内平均健康资源比例)不足 100%时触发。 +- **Measuring Points Used**:启用告警规则的测点数用量(社区版无数据,默认情况下是健康的)。 +- **Grants Expire Time**:启用告警规则的企业版过期时间(社区版无数据,默认情况是健康的)。 +- **Error Rate**:启用警报的集群总合错误率(每秒平均错误数)。 +- **Variables**:`show variables` 表格展示。 + +### DNodes 状态 + +![tdinsight-mnodes-overview](./assets/TDinsight-2-dnodes.png) + +- **DNodes Status**:`show dnodes` 的简单表格视图。 +- **DNodes Lifetime**:从创建 dnode 开始经过的时间。 +- **DNodes Number**:DNodes 数量变化。 +- **Offline Reason**:如果有任何 dnode 状态为离线,则以饼图形式展示离线原因。 + +### MNode 概述 + +![tdinsight-mnodes-overview](./assets/TDinsight-3-mnodes.png) + +1. **MNodes Status**:`show mnodes` 的简单表格视图。 +2. **MNodes Number**:类似于`DNodes Number`,MNodes 数量变化。 + +### 请求 + +![tdinsight-requests](./assets/TDinsight-4-requests.png) + +1. **Requests Rate(Inserts per Second)**:平均每秒插入次数。 +2. **Requests (Selects)**:查询请求数及变化率(count of second)。 +3. **Requests (HTTP)**:HTTP 请求数和请求速率(count of second)。 + +### 数据库 + +![tdinsight-database](./assets/TDinsight-5-database.png) + +数据库使用情况,对变量 `$database` 的每个值即每个数据库进行重复多行展示。 + +1. **STables**:超级表数量。 +2. **Total Tables**:所有表数量。 +3. **Sub Tables**:所有超级表子表的数量。 +4. **Tables**:所有普通表数量随时间变化图。 +5. **Tables Number Foreach VGroups**:每个 VGroups 包含的表数量。 + +### DNode 资源使用情况 + +![dnode-usage](./assets/TDinsight-6-dnode-usage.png) + +数据节点资源使用情况展示,对变量 `$fqdn` 即每个数据节点进行重复多行展示。包括: + +1. **Uptime**:从创建 dnode 开始经过的时间。 +2. **Has MNodes?**:当前 dnode 是否为 mnode。 +3. **CPU Cores**:CPU 核数。 +4. **VNodes Number**:当前 dnode 的 VNodes 数量。 +5. **VNodes Masters**:处于 master 角色的 vnode 数量。 +6. **Current CPU Usage of taosd**:taosd 进程的 CPU 使用率。 +7. **Current Memory Usage of taosd**:taosd 进程的内存使用情况。 +8. **Disk Used**:taosd 数据目录的总磁盘使用百分比。 +9. **CPU Usage**:进程和系统 CPU 使用率。 +10. **RAM Usage**:RAM 使用指标时间序列视图。 +11. **Disk Used**:多级存储下每个级别使用的磁盘(默认为 level0 级)。 +12. **Disk Increasing Rate per Minute**:每分钟磁盘用量增加或减少的百分比。 +13. **Disk IO**:磁盘 IO 速率。 +14. **Net IO**:网络 IO,除本机网络之外的总合网络 IO 速率。 + +### 登录历史 + +![登录历史](./assets/TDinsight-7-login-history.png) + +目前只报告每分钟登录次数。 + +### 监控 taosAdapter + +![taosadapter](./assets/TDinsight-8-taosadapter.png) + +支持监控 taosAdapter 请求统计和状态详情。包括: + +1. **http_request**: 包含总请求数,请求失败数以及正在处理的请求数 +2. **top 3 request endpoint**: 按终端分组,请求排名前三的数据 +3. **Memory Used**: taosAdapter 内存使用情况 +4. **latency_quantile(ms)**: (1, 2, 5, 9, 99)阶段的分位数 +5. **top 3 failed request endpoint**: 按终端分组,请求失败排名前三的数据 +6. **CPU Used**: taosAdapter CPU 使用情况 + +## 升级 + +通过 `TDinsight.sh` 脚本安装的 TDinsight,可以通过重新运行该脚本就可以升级到最新的 Grafana 插件和 TDinsight Dashboard。 + +手动安装的情况下,可按照上述步骤自行安装新的 Grafana 插件和 Dashboard。 + +## 卸载 + +通过 `TDinsight.sh` 脚本安装的 TDinsight,可以使用命令行 `TDinsight.sh -R` 清理相关资源。 + +手动安装时,要完全卸载 TDinsight,需要清理以下内容: + +1. Grafana 中的 TDinsight Dashboard。 +2. Grafana 中的 Data Source 数据源。 +3. 从插件安装目录删除 `tdengine-datasource` 插件。 + +## 整合的 Docker 示例 + +```bash +git clone --depth 1 https://github.com/taosdata/grafanaplugin.git +cd grafanaplugin +``` + +根据需要在 `docker-compose.yml` 文件中修改: + +```yaml +version: '3.7' + +services: + grafana: + image: grafana/grafana:7.5.10 + volumes: + - ./dist:/var/lib/grafana/plugins/tdengine-datasource + - ./grafana/grafana.ini:/etc/grafana/grafana.ini + - ./grafana/provisioning/:/etc/grafana/provisioning/ + - grafana-data:/var/lib/grafana + environment: + TDENGINE_API: ${TDENGINE_API} + TDENGINE_USER: ${TDENGINE_USER} + TDENGINE_PASS: ${TDENGINE_PASS} + SMS_ACCESS_KEY_ID: ${SMS_ACCESS_KEY_ID} + SMS_ACCESS_KEY_SECRET: ${SMS_ACCESS_KEY_SECRET} + SMS_SIGN_NAME: ${SMS_SIGN_NAME} + SMS_TEMPLATE_CODE: ${SMS_TEMPLATE_CODE} + SMS_TEMPLATE_PARAM: '${SMS_TEMPLATE_PARAM}' + SMS_PHONE_NUMBERS: $SMS_PHONE_NUMBERS + SMS_LISTEN_ADDR: ${SMS_LISTEN_ADDR} + ports: + - 3000:3000 +volumes: + grafana-data: +``` + +替换`docker-compose.yml`中的环境变量或保存环境变量到`.env`文件,然后用`docker-compose up`启动 Grafana。`docker-compose` 工具的具体用法参见 [Docker Compose Reference](https://docs.docker.com/compose/) + +```bash +docker-compose up -d +``` + +TDinsight 已经通过 Provisioning 部署完毕,请到 http://localhost:3000/d/tdinsight/ 查看仪表盘。 + +[grafana]: https://grafana.com +[tdengine]: https://www.taosdata.com diff --git a/docs-cn/14-reference/08-taos-shell.md b/docs-cn/14-reference/08-taos-shell.md new file mode 100644 index 0000000000000000000000000000000000000000..d4a516d2d2c30c7ae7122a04006b646aa1103635 --- /dev/null +++ b/docs-cn/14-reference/08-taos-shell.md @@ -0,0 +1,88 @@ +--- +title: TDengine 命令行(CLI) +sidebar_label: TDengine CLI +description: TDengine CLI 的使用说明和技巧 +--- + +TDengine 命令行程序(以下简称 TDengine CLI)是用户操作 TDengine 实例并与之交互的最简洁最常用的方式。 + +## 安装 + +如果在 TDengine 服务器端执行,无需任何安装,已经自动安装好 TDengine CLI。如果要在非 TDengine 服务器端运行,需要安装 TDengine 客户端驱动安装包,具体安装,请参考 [连接器](/reference/connector/)。 + +## 执行 + +要进入 TDengine CLI,您只要在 Linux 终端或 Windows 终端执行 `taos` 即可。 + +```bash +taos +``` + +如果连接服务成功,将会打印出欢迎消息和版本信息。如果失败,则会打印错误消息。(请参考 [FAQ](/train-faq/faq) 来解决终端连接服务端失败的问题)。TDengine CLI 的提示符号如下: + +```cmd +taos> +``` + +进入 TDengine CLI 后,你可执行各种 SQL 语句,包括插入、查询以及各种管理命令。 + +## 执行 SQL 脚本 + +在 TDengine CLI 里可以通过 `source` 命令来运行脚本文件中的多条 SQL 命令。 + +```sql +taos> source ; +``` + +## 在线修改显示字符宽度 + +可以在 TDengine CLI 里使用如下命令调整字符显示宽度 + +```sql +taos> SET MAX_BINARY_DISPLAY_WIDTH ; +``` + +如显示的内容后面以 ... 结尾时,表示该内容已被截断,可通过本命令修改显示字符宽度以显示完整的内容。 + +## 命令行参数 + +您可通过配置命令行参数来改变 TDengine CLI 的行为。以下为常用的几个命令行参数: + +- -h, --host=HOST: 要连接的 TDengine 服务端所在服务器的 FQDN, 默认为连接本地服务 +- -P, --port=PORT: 指定服务端所用端口号 +- -u, --user=USER: 连接时使用的用户名 +- -p, --password=PASSWORD: 连接服务端时使用的密码 +- -?, --help: 打印出所有命令行参数 + +还有更多其他参数: + +- -c, --config-dir: 指定配置文件目录,Linux 环境下默认为 `/etc/taos`,该目录下的配置文件默认名称为 `taos.cfg` +- -C, --dump-config: 打印 -c 指定的目录中 `taos.cfg` 的配置参数 +- -d, --database=DATABASE: 指定连接到服务端时使用的数据库 +- -D, --directory=DIRECTORY: 导入指定路径中的 SQL 脚本文件 +- -f, --file=FILE: 以非交互模式执行 SQL 脚本文件。文件中一个 SQL 语句只能占一行 +- -k, --check=CHECK: 指定要检查的表 +- -l, --pktlen=PKTLEN: 网络测试时使用的测试包大小 +- -n, --netrole=NETROLE: 网络连接测试时的测试范围,默认为 `startup`, 可选值为 `client`、`server`、`rpc`、`startup`、`sync`、`speed` 和 `fqdn` 之一 +- -r, --raw-time: 将时间输出出无符号 64 位整数类型(即 C 语音中 uint64_t) +- -s, --commands=COMMAND: 以非交互模式执行的 SQL 命令 +- -S, --pkttype=PKTTYPE: 指定网络测试所用的包类型,默认为 TCP。只有 netrole 为 `speed` 时既可以指定为 TCP 也可以指定为 UDP +- -T, --thread=THREADNUM: 以多线程模式导入数据时的线程数 +- -s, --commands: 在不进入终端的情况下运行 TDengine 命令 +- -z, --timezone=TIMEZONE: 指定时区,默认为本地时区 +- -V, --version: 打印出当前版本号 + +示例: + +```bash +taos -h h1.taos.com -s "use db; show tables;" +``` + +## TDengine CLI 小技巧 + +- 可以使用上下光标键查看历史输入的指令 +- 在 TDengine CLI 中使用 `alter user` 命令可以修改用户密码,缺省密码为 `taosdata` +- Ctrl+C 中止正在进行中的查询 +- 执行 `RESET QUERY CACHE` 可清除本地表 Schema 的缓存 +- 批量执行 SQL 语句。可以将一系列的 TDengine CLI 命令(以英文 ; 结尾,每个 SQL 语句为一行)按行存放在文件里,在 TDengine CLI 里执行命令 `source ` 自动执行该文件里所有的 SQL 语句 +- 输入 `q` 或 `quit` 或 `exit` 回车,可以退出 TDengine CLI diff --git a/docs-cn/14-reference/09-support-platform/_category_.yml b/docs-cn/14-reference/09-support-platform/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..ce2aa5213b1e85bbd07b7242d8bb1c1525e5c687 --- /dev/null +++ b/docs-cn/14-reference/09-support-platform/_category_.yml @@ -0,0 +1 @@ +label: 支持平台列表 diff --git a/docs-cn/14-reference/09-support-platform/index.md b/docs-cn/14-reference/09-support-platform/index.md new file mode 100644 index 0000000000000000000000000000000000000000..d396e4be1fed2e3ecdf202c6e57d509bc1941ecd --- /dev/null +++ b/docs-cn/14-reference/09-support-platform/index.md @@ -0,0 +1,40 @@ +--- +title: 支持平台列表 +description: "TDengine 服务端、客户端和连接器支持的平台列表" +--- + +## TDengine 服务端支持的平台列表 + +| | **CentOS 7/8** | **Ubuntu 16/18/20** | **Other Linux** | **统信 UOS** | **银河/中标麒麟** | **凝思 V60/V80** | **华为 EulerOS** | +| ------------ | -------------- | ------------------- | --------------- | ------------ | ----------------- | ---------------- | ---------------- | +| X64 | ● | ● | | ○ | ● | ● | ● | +| 龙芯 MIPS64 | | | ● | | | | | +| 鲲鹏 ARM64 | | ○ | ○ | | ● | | | +| 申威 Alpha64 | | | ○ | ● | | | | +| 飞腾 ARM64 | | ○ 优麒麟 | | | | | | +| 海光 X64 | ● | ● | ● | ○ | ● | ● | | +| 瑞芯微 ARM64 | | | ○ | | | | | +| 全志 ARM64 | | | ○ | | | | | +| 炬力 ARM64 | | | ○ | | | | | +| 华为云 ARM64 | | | | | | | ● | + +注: ● 表示经过官方测试验证, ○ 表示非官方测试验证。 + +## TDengine 客户端和连接器支持的平台列表 + +目前 TDengine 的连接器可支持的平台广泛,目前包括:X64/X86/ARM64/ARM32/MIPS/Alpha 等硬件平台,以及 Linux/Win64/Win32 等开发环境。 + +对照矩阵如下: + +| **CPU** | **X64 64bit** | | | **X86 32bit** | **ARM64** | **ARM32** | **MIPS 龙芯** | **Alpha 申威** | **X64 海光** | +| ----------- | ------------- | --------- | --------- | ------------- | --------- | --------- | ------------- | -------------- | ------------ | +| **OS** | **Linux** | **Win64** | **Win32** | **Win32** | **Linux** | **Linux** | **Linux** | **Linux** | **Linux** | +| **C/C++** | ● | ● | ● | ○ | ● | ● | ● | ● | ● | +| **JDBC** | ● | ● | ● | ○ | ● | ● | ● | ● | ● | +| **Python** | ● | ● | ● | ○ | ● | ● | ● | -- | ● | +| **Go** | ● | ● | ● | ○ | ● | ● | ○ | -- | -- | +| **NodeJs** | ● | ● | ○ | ○ | ● | ● | ○ | -- | -- | +| **C#** | ● | ● | ○ | ○ | ○ | ○ | ○ | -- | -- | +| **RESTful** | ● | ● | ● | ● | ● | ● | ● | ● | ● | + +注:● 表示官方测试验证通过,○ 表示非官方测试验证通过,-- 表示未经验证。 diff --git a/docs-cn/14-reference/11-docker/_category_.yml b/docs-cn/14-reference/11-docker/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..68c16927f4a9098311dec116aab41e8faa07fe81 --- /dev/null +++ b/docs-cn/14-reference/11-docker/_category_.yml @@ -0,0 +1 @@ +label: TDengine Docker 镜像 \ No newline at end of file diff --git a/docs-cn/14-reference/11-docker/index.md b/docs-cn/14-reference/11-docker/index.md new file mode 100644 index 0000000000000000000000000000000000000000..d0b7f1420420655909f6ceec5b249d2dad16dd3c --- /dev/null +++ b/docs-cn/14-reference/11-docker/index.md @@ -0,0 +1,515 @@ +--- +title: 用 Docker 部署 TDengine +description: '本章主要介绍如何在容器中启动 TDengine 服务并访问它' +--- + +本章主要介绍如何在容器中启动 TDengine 服务并访问它。可以在 docker run 命令行中或者 docker-compose 文件中使用环境变量来控制容器中服务的行为。 + +## 启动 TDengine + +TDengine 镜像启动时默认激活 HTTP 服务,使用下列命令 + +```shell +docker run -d --name tdengine -p 6041:6041 tdengine/tdengine +``` + +以上命令启动了一个名为“tdengine”的容器,并把其中的 HTTP 服务的端 6041 映射到了主机端口 6041。使用如下命令可以验证该容器中提供的 HTTP 服务是否可用: + +```shell +curl -u root:taosdata -d "show databases" localhost:6041/rest/sql +``` + +使用如下命令可以在该容器中执行 TDengine 的客户端 taos 对 TDengine 进行访问: + +```shell +$ docker exec -it tdengine taos + +Welcome to the TDengine shell from Linux, Client Version:2.4.0.0 +Copyright (c) 2020 by TAOS Data, Inc. All rights reserved. + +taos> show databases; + name | created_time | ntables | vgroups | replica | quorum | days | keep | cache(MB) | blocks | minrows | maxrows | wallevel | fsync | comp | cachelast | precision | update | status | +==================================================================================================================================================================================================================================================================================== + log | 2022-01-17 13:57:22.270 | 10 | 1 | 1 | 1 | 10 | 30 | 1 | 3 | 100 | 4096 | 1 | 3000 | 2 | 0 | us | 0 | ready | +Query OK, 1 row(s) in set (0.002843s) +``` + +因为运行在容器中的 TDengine 服务端使用容器的 hostname 建立连接,使用 taos shell 或者各种连接器(例如 JDBC-JNI)从容器外访问容器内的 TDengine 比较复杂,所以上述方式是访问容器中 TDengine 服务的最简单的方法,适用于一些简单场景。如果在一些复杂场景下想要从容器化使用 taos shell 或者各种连接器访问容器中的 TDengine 服务,请参考下一节。 + +## 在 host 网络上启动 TDengine + +```shell +docker run -d --name tdengine --network host tdengine/tdengine +``` + +上面的命令在 host 网络上启动 TDengine,并使用主机的 FQDN 建立连接而不是使用容器的 hostname 。这种方式和在主机上使用 `systemctl` 启动 TDengine 效果相同。在主机已安装 TDengine 客户端情况下,可以直接使用下面的命令访问它。 + +```shell +$ taos + +Welcome to the TDengine shell from Linux, Client Version:2.4.0.0 +Copyright (c) 2020 by TAOS Data, Inc. All rights reserved. + +taos> show dnodes; + id | end_point | vnodes | cores | status | role | create_time | offline reason | +====================================================================================================================================== + 1 | myhost:6030 | 1 | 8 | ready | any | 2022-01-17 22:10:32.619 | | +Query OK, 1 row(s) in set (0.003233s) +``` + +## 以指定的 hostname 和 port 启动 TDengine + +利用 `TAOS_FQDN` 环境变量或者 `taos.cfg` 中的 `fqdn` 配置项可以使 TDengine 在指定的 hostname 上建立连接。这种方式可以为部署提供更大的灵活性。 + +```shell +docker run -d \ + --name tdengine \ + -e TAOS_FQDN=tdengine \ + -p 6030-6049:6030-6049 \ + -p 6030-6049:6030-6049/udp \ + tdengine/tdengine +``` + +上面的命令在容器中启动一个 TDengine 服务,其所监听的 hostname 为 tdengine ,并将容器的 6030 到 6049 端口段映射到主机的 6030 到 6049 端口段 (tcp 和 udp 都需要映射)。如果主机上该端口段已经被占用,可以修改上述命令指定一个主机上空闲的端口段。如果 `rpcForceTcp` 被设置为 `1` ,可以只映射 tcp 协议。 + +接下来,要确保 "tdengine" 这个 hostname 在 `/etc/hosts` 中可解析。 + +```shell +echo 127.0.0.1 tdengine |sudo tee -a /etc/hosts +``` + +最后,可以从 taos shell 或者任意连接器以 "tdengine" 为服务端地址访问 TDengine 服务。 + +```shell +taos -h tdengine -P 6030 +``` + +如果 `TAOS_FQDN` 被设置为与所在主机名相同,则效果与 “在 host 网络上启动 TDengine” 相同。 + +## 在指定网络上启动 TDengine + +也可以在指定的特定网络上启动 TDengine。下面是详细步骤: + +1. 首先,创建一个 docker 网络,命名为 td-net + + ```shell + docker network create td-net + ``` + +2. 启动 TDengine + + 以下命令在 td-net 网络上启动 TDengine 服务 + + ```shell + docker run -d --name tdengine --network td-net \ + -e TAOS_FQDN=tdengine \ + tdengine/tdengine + ``` + +3. 在同一网络上的另一容器中启动 TDengine 客户端 + + ```shell + docker run --rm -it --network td-net -e TAOS_FIRST_EP=tdengine tdengine/tdengine taos + # or + #docker run --rm -it --network td-net -e tdengine/tdengine taos -h tdengine + ``` + +## 在容器中启动客户端应用 + +如果想在容器中启动自己的应用的话,需要将相应的对 TDengine 的依赖也要加入到镜像中,例如: + +```docker +FROM ubuntu:20.04 +RUN apt-get update && apt-get install -y wget +ENV TDENGINE_VERSION=2.4.0.0 +RUN wget -c https://www.taosdata.com/assets-download/TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz \ + && tar xvf TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz \ + && cd TDengine-client-${TDENGINE_VERSION} \ + && ./install_client.sh \ + && cd ../ \ + && rm -rf TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz TDengine-client-${TDENGINE_VERSION} +## add your application next, eg. go, build it in builder stage, copy the binary to the runtime +#COPY --from=builder /path/to/build/app /usr/bin/ +#CMD ["app"] +``` + +以下是一个 go 应用程序的示例: + +```go +/* + * In this test program, we'll create a database and insert 4 records then select out. + */ +package main + +import ( + "database/sql" + "flag" + "fmt" + "time" + + _ "github.com/taosdata/driver-go/v2/taosSql" +) + +type config struct { + hostName string + serverPort string + user string + password string +} + +var configPara config +var taosDriverName = "taosSql" +var url string + +func init() { + flag.StringVar(&configPara.hostName, "h", "", "The host to connect to TDengine server.") + flag.StringVar(&configPara.serverPort, "p", "", "The TCP/IP port number to use for the connection to TDengine server.") + flag.StringVar(&configPara.user, "u", "root", "The TDengine user name to use when connecting to the server.") + flag.StringVar(&configPara.password, "P", "taosdata", "The password to use when connecting to the server.") + flag.Parse() +} + +func printAllArgs() { + fmt.Printf("============= args parse result: =============\n") + fmt.Printf("hostName: %v\n", configPara.hostName) + fmt.Printf("serverPort: %v\n", configPara.serverPort) + fmt.Printf("usr: %v\n", configPara.user) + fmt.Printf("password: %v\n", configPara.password) + fmt.Printf("================================================\n") +} + +func main() { + printAllArgs() + + url = "root:taosdata@/tcp(" + configPara.hostName + ":" + configPara.serverPort + ")/" + + taos, err := sql.Open(taosDriverName, url) + checkErr(err, "open database error") + defer taos.Close() + + taos.Exec("create database if not exists test") + taos.Exec("use test") + taos.Exec("create table if not exists tb1 (ts timestamp, a int)") + _, err = taos.Exec("insert into tb1 values(now, 0)(now+1s,1)(now+2s,2)(now+3s,3)") + checkErr(err, "failed to insert") + rows, err := taos.Query("select * from tb1") + checkErr(err, "failed to select") + + defer rows.Close() + for rows.Next() { + var r struct { + ts time.Time + a int + } + err := rows.Scan(&r.ts, &r.a) + if err != nil { + fmt.Println("scan error:\n", err) + return + } + fmt.Println(r.ts, r.a) + } +} + +func checkErr(err error, prompt string) { + if err != nil { + fmt.Println("ERROR: %s\n", prompt) + panic(err) + } +} +``` + +如下是完整版本的 dockerfile + +```docker +FROM golang:1.17.6-buster as builder +ENV TDENGINE_VERSION=2.4.0.0 +RUN wget -c https://www.taosdata.com/assets-download/TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz \ + && tar xvf TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz \ + && cd TDengine-client-${TDENGINE_VERSION} \ + && ./install_client.sh \ + && cd ../ \ + && rm -rf TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz TDengine-client-${TDENGINE_VERSION} +WORKDIR /usr/src/app/ +ENV GOPROXY="https://goproxy.io,direct" +COPY ./main.go ./go.mod ./go.sum /usr/src/app/ +RUN go env +RUN go mod tidy +RUN go build + +FROM ubuntu:20.04 +RUN apt-get update && apt-get install -y wget +ENV TDENGINE_VERSION=2.4.0.0 +RUN wget -c https://www.taosdata.com/assets-download/TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz \ + && tar xvf TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz \ + && cd TDengine-client-${TDENGINE_VERSION} \ + && ./install_client.sh \ + && cd ../ \ + && rm -rf TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz TDengine-client-${TDENGINE_VERSION} + +## add your application next, eg. go, build it in builder stage, copy the binary to the runtime +COPY --from=builder /usr/src/app/app /usr/bin/ +CMD ["app"] +``` + +目前我们已经有了 `main.go`, `go.mod`, `go.sum`, `app.dockerfile`, 现在可以构建出这个应用程序并在 `td-net` 网络上启动它 + +```shell +$ docker build -t app -f app.dockerfile +$ docker run --rm --network td-net app -h tdengine -p 6030 +============= args parse result: ============= +hostName: tdengine +serverPort: 6030 +usr: root +password: taosdata +================================================ +2022-01-17 15:56:55.48 +0000 UTC 0 +2022-01-17 15:56:56.48 +0000 UTC 1 +2022-01-17 15:56:57.48 +0000 UTC 2 +2022-01-17 15:56:58.48 +0000 UTC 3 +2022-01-17 15:58:01.842 +0000 UTC 0 +2022-01-17 15:58:02.842 +0000 UTC 1 +2022-01-17 15:58:03.842 +0000 UTC 2 +2022-01-17 15:58:04.842 +0000 UTC 3 +2022-01-18 01:43:48.029 +0000 UTC 0 +2022-01-18 01:43:49.029 +0000 UTC 1 +2022-01-18 01:43:50.029 +0000 UTC 2 +2022-01-18 01:43:51.029 +0000 UTC 3 +``` + +## 用 docker-compose 启动 TDengine 集群 + +1. 如下 docker-compose 文件启动一个 2 副本、2 管理节点、2 数据节点以及 1 个 arbitrator 的 TDengine 集群。 + + ```docker + version: "3" + services: + arbitrator: + image: tdengine/tdengine:$VERSION + command: tarbitrator + td-1: + image: tdengine/tdengine:$VERSION + environment: + TAOS_FQDN: "td-1" + TAOS_FIRST_EP: "td-1" + TAOS_NUM_OF_MNODES: "2" + TAOS_REPLICA: "2" + TAOS_ARBITRATOR: arbitrator:6042 + volumes: + - taosdata-td1:/var/lib/taos/ + - taoslog-td1:/var/log/taos/ + td-2: + image: tdengine/tdengine:$VERSION + environment: + TAOS_FQDN: "td-2" + TAOS_FIRST_EP: "td-1" + TAOS_NUM_OF_MNODES: "2" + TAOS_REPLICA: "2" + TAOS_ARBITRATOR: arbitrator:6042 + volumes: + - taosdata-td2:/var/lib/taos/ + - taoslog-td2:/var/log/taos/ + volumes: + taosdata-td1: + taoslog-td1: + taosdata-td2: + taoslog-td2: + ``` + +:::note + +- `VERSION` 环境变量被用来设置 tdengine image tag +- 在新创建的实例上必须设置 `TAOS_FIRST_EP` 以使其能够加入 TDengine 集群;如果有高可用需求,则需要同时使用 `TAOS_SECOND_EP` +- `TAOS_REPLICA` 用来设置缺省的数据库副本数量,其取值范围为[1,3] + 在双副本环境下,推荐使用 arbitrator, 用 TAOS_ARBITRATOR 来设置 + ::: + +2. 启动集群 + + ```shell + $ VERSION=2.4.0.0 docker-compose up -d + Creating network "test_default" with the default driver + Creating volume "test_taosdata-td1" with default driver + Creating volume "test_taoslog-td1" with default driver + Creating volume "test_taosdata-td2" with default driver + Creating volume "test_taoslog-td2" with default driver + Creating test_td-1_1 ... done + Creating test_arbitrator_1 ... done + Creating test_td-2_1 ... done + ``` + +3. 查看节点状态 + + ```shell + $ docker-compose ps + Name Command State Ports + --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + test_arbitrator_1 /usr/bin/entrypoint.sh tar ... Up 6030/tcp, 6031/tcp, 6032/tcp, 6033/tcp, 6034/tcp, 6035/tcp, 6036/tcp, 6037/tcp, 6038/tcp, 6039/tcp, 6040/tcp, 6041/tcp, 6042/tcp + test_td-1_1 /usr/bin/entrypoint.sh taosd Up 6030/tcp, 6031/tcp, 6032/tcp, 6033/tcp, 6034/tcp, 6035/tcp, 6036/tcp, 6037/tcp, 6038/tcp, 6039/tcp, 6040/tcp, 6041/tcp, 6042/tcp + test_td-2_1 /usr/bin/entrypoint.sh taosd Up 6030/tcp, 6031/tcp, 6032/tcp, 6033/tcp, 6034/tcp, 6035/tcp, 6036/tcp, 6037/tcp, 6038/tcp, 6039/tcp, 6040/tcp, 6041/tcp, 6042/tcp + ``` + +4. 用 taos shell 查看 dnodes + + ```shell + $ docker-compose exec td-1 taos -s "show dnodes" + + Welcome to the TDengine shell from Linux, Client Version:2.4.0.0 + Copyright (c) 2020 by TAOS Data, Inc. All rights reserved. + + taos> show dnodes + id | end_point | vnodes | cores | status | role | create_time | offline reason | + ====================================================================================================================================== + 1 | td-1:6030 | 1 | 8 | ready | any | 2022-01-18 02:47:42.871 | | + 2 | td-2:6030 | 0 | 8 | ready | any | 2022-01-18 02:47:43.518 | | + 0 | arbitrator:6042 | 0 | 0 | ready | arb | 2022-01-18 02:47:43.633 | - | + Query OK, 3 row(s) in set (0.000811s) + ``` + +## taosAdapter + +1. taosAdapter 在 TDengine 容器中默认是启动的。如果想要禁用它,在启动时指定环境变量 `TAOS_DISABLE_ADAPTER=true` + +2. 同时为了部署灵活起见,可以在独立的容器中启动 taosAdapter + + ```docker + services: + # ... + adapter: + image: tdengine/tdengine:$VERSION + command: taosadapter + ``` + + 如果要部署多个 taosAdapter 来提高吞吐量并提供高可用性,推荐配置方式为使用 nginx 等反向代理来提供统一的访问入口。具体配置方法请参考 nginx 的官方文档。如下是示例: + + ```docker + version: "3" + + networks: + inter: + api: + + services: + arbitrator: + image: tdengine/tdengine:$VERSION + command: tarbitrator + networks: + - inter + td-1: + image: tdengine/tdengine:$VERSION + networks: + - inter + environment: + TAOS_FQDN: "td-1" + TAOS_FIRST_EP: "td-1" + TAOS_NUM_OF_MNODES: "2" + TAOS_REPLICA: "2" + TAOS_ARBITRATOR: arbitrator:6042 + volumes: + - taosdata-td1:/var/lib/taos/ + - taoslog-td1:/var/log/taos/ + td-2: + image: tdengine/tdengine:$VERSION + networks: + - inter + environment: + TAOS_FQDN: "td-2" + TAOS_FIRST_EP: "td-1" + TAOS_NUM_OF_MNODES: "2" + TAOS_REPLICA: "2" + TAOS_ARBITRATOR: arbitrator:6042 + volumes: + - taosdata-td2:/var/lib/taos/ + - taoslog-td2:/var/log/taos/ + adapter: + image: tdengine/tdengine:$VERSION + command: taosadapter + networks: + - inter + environment: + TAOS_FIRST_EP: "td-1" + TAOS_SECOND_EP: "td-2" + deploy: + replicas: 4 + nginx: + image: nginx + depends_on: + - adapter + networks: + - inter + - api + ports: + - 6041:6041 + - 6044:6044/udp + command: [ + "sh", + "-c", + "while true; + do curl -s http://adapter:6041/-/ping >/dev/null && break; + done; + printf 'server{listen 6041;location /{proxy_pass http://adapter:6041;}}' + > /etc/nginx/conf.d/rest.conf; + printf 'stream{server{listen 6044 udp;proxy_pass adapter:6044;}}' + >> /etc/nginx/nginx.conf;cat /etc/nginx/nginx.conf; + nginx -g 'daemon off;'", + ] + volumes: + taosdata-td1: + taoslog-td1: + taosdata-td2: + taoslog-td2: + ``` + +## 使用 docker swarm 部署 + +如果要想将基于容器的 TDengine 集群部署在多台主机上,可以使用 docker swarm。首先要在这些主机上建立 docke swarm 集群,请参考 docker 官方文档。 + +docker-compose 文件可以参考上节。下面是使用 docker swarm 启动 TDengine 的命令: + +```shell +$ VERSION=2.4.0 docker stack deploy -c docker-compose.yml taos +Creating network taos_inter +Creating network taos_api +Creating service taos_arbitrator +Creating service taos_td-1 +Creating service taos_td-2 +Creating service taos_adapter +Creating service taos_nginx +``` + +查看和管理 + +```shell +$ docker stack ps taos +ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS +79ni8temw59n taos_nginx.1 nginx:latest TM1701 Running Running about a minute ago +3e94u72msiyg taos_adapter.1 tdengine/tdengine:2.4.0 TM1702 Running Running 56 seconds ago +100amjkwzsc6 taos_td-2.1 tdengine/tdengine:2.4.0 TM1703 Running Running about a minute ago +pkjehr2vvaaa taos_td-1.1 tdengine/tdengine:2.4.0 TM1704 Running Running 2 minutes ago +tpzvgpsr1qkt taos_arbitrator.1 tdengine/tdengine:2.4.0 TM1705 Running Running 2 minutes ago +rvss3g5yg6fa taos_adapter.2 tdengine/tdengine:2.4.0 TM1706 Running Running 56 seconds ago +i2augxamfllf taos_adapter.3 tdengine/tdengine:2.4.0 TM1707 Running Running 56 seconds ago +lmjyhzccpvpg taos_adapter.4 tdengine/tdengine:2.4.0 TM1708 Running Running 56 seconds ago +$ docker service ls +ID NAME MODE REPLICAS IMAGE PORTS +561t4lu6nfw6 taos_adapter replicated 4/4 tdengine/tdengine:2.4.0 +3hk5ct3q90sm taos_arbitrator replicated 1/1 tdengine/tdengine:2.4.0 +d8qr52envqzu taos_nginx replicated 1/1 nginx:latest *:6041->6041/tcp, *:6044->6044/udp +2isssfvjk747 taos_td-1 replicated 1/1 tdengine/tdengine:2.4.0 +9pzw7u02ichv taos_td-2 replicated 1/1 tdengine/tdengine:2.4.0 +``` + +从上面的输出可以看到有两个 dnode, 和两个 taosAdapter,以及一个 nginx 反向代理服务。 + +接下来,我们可以减少 taosAdapter 服务的数量 + +```shell +$ docker service scale taos_adapter=1 +taos_adapter scaled to 1 +overall progress: 1 out of 1 tasks +1/1: running [==================================================>] +verify: Service converged + +$ docker service ls -f name=taos_adapter +ID NAME MODE REPLICAS IMAGE PORTS +561t4lu6nfw6 taos_adapter replicated 1/1 tdengine/tdengine:2.4.0 +``` diff --git a/docs-cn/14-reference/12-config/_category_.yml b/docs-cn/14-reference/12-config/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..8d3cfcc8d0d8966bf1354f9ece83faea105f9c7a --- /dev/null +++ b/docs-cn/14-reference/12-config/_category_.yml @@ -0,0 +1 @@ +label: 配置参数 \ No newline at end of file diff --git a/docs-cn/14-reference/12-config/index.md b/docs-cn/14-reference/12-config/index.md new file mode 100644 index 0000000000000000000000000000000000000000..cbb3833b5bb170720c2aa7bea6687a50feeae7d5 --- /dev/null +++ b/docs-cn/14-reference/12-config/index.md @@ -0,0 +1,1131 @@ +--- +title: 配置参数 +description: 'TDengine 客户端和服务配置列表' +--- + +## 为服务端指定配置文件 + +TDengine 系统后台服务由 taosd 提供,可以在配置文件 taos.cfg 里修改配置参数,以满足不同场景的需求。配置文件的缺省位置在/etc/taos 目录,可以通过 taosd 命令行执行参数 -c 指定配置文件目录。比如,指定配置文件位于`/home/user` 这个目录: + +``` +taosd -c /home/user +``` + +另外可以使用 `-C` 显示当前服务器配置参数: + +``` +taosd -C +``` + +## 为客户端指定配置文件 + +TDengine 系统的前台交互客户端应用程序为 taos,以及应用驱动,它可以与 taosd 共享同一个配置文件 taos.cfg,也可以使用单独指定配置文件。运行 taos 时,使用参数-c 指定配置文件目录,如 taos -c /home/cfg,表示使用/home/cfg/目录下的 taos.cfg 配置文件中的参数,缺省目录是/etc/taos。更多 taos 的使用方法请见帮助信息 `taos --help`。 + +**2.0.10.0 之后版本支持命令行以下参数显示当前客户端参数的配置** + +```bash +taos -C +``` + +```bash +taos --dump-config +``` + +# 配置参数详细列表 + +:::note +本节内容覆盖产品的配置参数,适用于服务端的参数按其对产品行为的影响进行分类,这其中有部分参数也同时适用于客户端;但有少量参数仅适用于客户端,这部分参数进行了单独归类。 + +::: + +:::note +配置文件参数修改后,需要重启*taosd*服务,或客户端应用才能生效。 + +::: + +## 连接相关 + +### firstEp + +| 属性 | 说明 | +| -------- | --------------------------------------------------------------- | +| 适用范围 | 服务端和客户端均适用 | +| 含义 | taosd 或者 taos 启动时,主动连接的集群中首个 dnode 的 endpoint | +| 缺省值 | localhost:6030 | + +### secondEp + +| 属性 | 说明 | +| -------- | -------------------------------------------------------------------------------------- | +| 适用范围 | 服务端和客户端均适用 | +| 含义 | taosd 或者 taos 启动时,如果 firstEp 连接不上,尝试连接集群中第二个 dnode 的 endpoint | +| 缺省值 | 无 | + +### fqdn + +| 属性 | 说明 | +| -------- | ----------------------------------------------------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 数据节点的 FQDN。如果习惯 IP 地址访问,可设置为该节点的 IP 地址。 | +| 缺省值 | 缺省为操作系统配置的第一个 hostname。 | +| 补充说明 | 这个参数值的长度需要控制在 96 个字符以内。 | + +### serverPort + +| 属性 | 说明 | +| -------- | ----------------------------------------------------------------------------------------------------------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | taosd 启动后,对外服务的端口号 | +| 缺省值 | 6030 | +| 补充说明 | RESTful 服务在 2.4.0.0 之前(不含)由 taosd 提供,默认端口为 6041; 在 2.4.0.0 及后续版本由 taosAdapter,默认端口为 6041 | + +:::note +对于端口,TDengine 会使用从 serverPort 起 13 个连续的 TCP 和 UDP 端口号,请务必在防火墙打开。因此如果是缺省配置,需要打开从 6030 到 6042 共 13 个端口,而且必须 TCP 和 UDP 都打开。(详细的端口情况请参见下表) +::: +| 协议 | 默认端口 | 用途说明 | 修改方法 | +| :--- | :-------- | :---------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------- | +| TCP | 6030 | 客户端与服务端之间通讯。 | 由配置文件设置 serverPort 决定。 | +| TCP | 6035 | 多节点集群的节点间通讯。 | 随 serverPort 端口变化。 | +| TCP | 6040 | 多节点集群的节点间数据同步。 | 随 serverPort 端口变化。 | +| TCP | 6041 | 客户端与服务端之间的 RESTful 通讯。 | 随 serverPort 端口变化。注意 taosAdapter 配置或有不同,请参考相应[文档](/reference/taosadapter/)。 | +| TCP | 6042 | Arbitrator 的服务端口。 | 随 Arbitrator 启动参数设置变化。 | +| TCP | 6043 | TaosKeeper 监控服务端口。 | 随 TaosKeeper 启动参数设置变化。 | +| TCP | 6044 | 支持 StatsD 的数据接入端口。 | 随 taosAdapter 启动参数设置变化(2.3.0.1+以上版本)。 | +| UDP | 6045 | 支持 collectd 数据接入端口。 | 随 taosAdapter 启动参数设置变化(2.3.0.1+以上版本)。 | +| TCP | 6060 | 企业版内 Monitor 服务的网络端口。 | | +| UDP | 6030-6034 | 客户端与服务端之间通讯。 | 随 serverPort 端口变化。 | +| UDP | 6035-6039 | 多节点集群的节点间通讯。 | 随 serverPort 端口变化。 + +### maxShellConns + +| 属性 | 说明 | +| -------- | ----------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 一个 dnode 容许的连接数 | +| 取值范围 | 10-50000000 | +| 缺省值 | 5000 | + +### maxConnections + +| 属性 | 说明 | +| -------- | ------------------------------------------------------------------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 一个数据库连接所容许的 dnode 连接数 | +| 取值范围 | 1-100000 | +| 缺省值 | 5000 | +| 补充说明 | 实际测试下来,如果默认没有配,选 50 个 worker thread 会产生 Network unavailable | + +### rpcForceTcp + +| 属性 | 说明 | +| -------- | --------------------------------------------------- | +| 适用范围 | 服务端和客户端均适用 | +| 含义 | 强制使用 TCP 传输 | +| 取值范围 | 0: 不开启 1: 开启 | +| 缺省值 | 0 | +| 补充说明 | 在网络比较差的环境中,建议开启。
2.0 版本新增。 | + +## 监控相关 + +### monitor + +| 属性 | 说明 | +| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 服务器内部的系统监控开关。监控主要负责收集物理节点的负载状况,包括 CPU、内存、硬盘、网络带宽、HTTP 请求量的监控记录,记录信息存储在`LOG`库中。 | +| 取值范围 | 0:关闭监控服务, 1:激活监控服务。 | +| 缺省值 | 0 | + +### monitorInterval + +| 属性 | 说明 | +| -------- | -------------------------------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 监控数据库记录系统参数(CPU/内存)的时间间隔 | +| 单位 | 秒 | +| 取值范围 | 1-600 | +| 缺省值 | 30 | + +### telemetryReporting + +| 属性 | 说明 | +| -------- | ---------------------------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 是否允许 TDengine 采集和上报基本使用信息 | +| 取值范围 | 0:不允许 1:允许 | +| 缺省值 | 1 | + +## 查询相关 + +### queryBufferSize + +| 属性 | 说明 | +| -------- | ------------------------------------------------------------------------------------------------------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 为所有并发查询占用保留的内存大小。 | +| 单位 | MB | +| 缺省值 | 无 | +| 补充说明 | 计算规则可以根据实际应用可能的最大并发数和表的数字相乘,再乘 170 。
(2.0.15 以前的版本中,此参数的单位是字节) | + +### ratioOfQueryCores + +| 属性 | 说明 | +| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 设置查询线程的最大数量。 | +| 缺省值 | 1 | +| 补充说明 | 最小值 0 表示只有 1 个查询线程
最大值 2 表示最大建立 2 倍 CPU 核数的查询线程。
默认为 1,表示最大和 CPU 核数相等的查询线程。
该值可以为小数,即 0.5 表示最大建立 CPU 核数一半的查询线程。 | + +### maxNumOfDistinctRes + +| 属性 | 说明 | +| -------- | -------------------------------- | --- | +| 适用范围 | 仅服务端适用 | +| 含义 | 允许返回的 distinct 结果最大行数 | +| 取值范围 | 默认值为 10 万,最大值 1 亿 | +| 缺省值 | 10 万 | +| 补充说明 | 2.3 版本新增。 | | + +## 区域相关 + +### timezone + +| 属性 | 说明 | +| -------- | ------------------------------ | +| 适用范围 | 服务端和客户端均适用 | +| 含义 | 时区 | +| 缺省值 | 从系统中动态获取当前的时区设置 | + +:::info +为应对多时区的数据写入和查询问题,TDengine 采用 Unix 时间戳(Unix Timestamp)来记录和存储时间戳。Unix 时间戳的特点决定了任一时刻不论在任何时区,产生的时间戳均一致。需要注意的是,Unix 时间戳是在客户端完成转换和记录。为了确保客户端其他形式的时间转换为正确的 Unix 时间戳,需要设置正确的时区。 + +在 Linux 系统中,客户端会自动读取系统设置的时区信息。用户也可以采用多种方式在配置文件设置时区。例如: + +``` +timezone UTC-8 +timezone GMT-8 +timezone Asia/Shanghai +``` + +均是合法的设置东八区时区的格式。但需注意,Windows 下并不支持 `timezone Asia/Shanghai` 这样的写法,而必须写成 `timezone UTC-8`。 + +时区的设置对于查询和写入 SQL 语句中非 Unix 时间戳的内容(时间戳字符串、关键词 now 的解析)产生影响。例如: + +```sql +SELECT count(*) FROM table_name WHERE TS<'2019-04-11 12:01:08'; +``` + +在东八区,SQL 语句等效于 + +```sql +SELECT count(*) FROM table_name WHERE TS<1554955268000; +``` + +在 UTC 时区,SQL 语句等效于 + +```sql +SELECT count(*) FROM table_name WHERE TS<1554984068000; +``` + +为了避免使用字符串时间格式带来的不确定性,也可以直接使用 Unix 时间戳。此外,还可以在 SQL 语句中使用带有时区的时间戳字符串,例如:RFC3339 格式的时间戳字符串,2013-04-12T15:52:01.123+08:00 或者 ISO-8601 格式时间戳字符串 2013-04-12T15:52:01.123+0800。上述两个字符串转化为 Unix 时间戳不受系统所在时区的影响。 + +::: + +### locale + +| 属性 | 说明 | +| -------- | ----------------------------------------------------------------------- | +| 适用范围 | 服务端和客户端均适用 | +| 含义 | 系统区位信息及编码格式 | +| 缺省值 | 系统中动态获取,如果自动获取失败,需要用户在配置文件设置或通过 API 设置 | + +:::info +TDengine 为存储中文、日文、韩文等非 ASCII 编码的宽字符,提供一种专门的字段类型 nchar。写入 nchar 字段的数据将统一采用 UCS4-LE 格式进行编码并发送到服务器。需要注意的是,编码正确性是客户端来保证。因此,如果用户想要正常使用 nchar 字段来存储诸如中文、日文、韩文等非 ASCII 字符,需要正确设置客户端的编码格式。 + +客户端的输入的字符均采用操作系统当前默认的编码格式,在 Linux 系统上多为 UTF-8,部分中文系统编码则可能是 GB18030 或 GBK 等。在 docker 环境中默认的编码是 POSIX。在中文版 Windows 系统中,编码则是 CP936。客户端需要确保正确设置自己所使用的字符集,即客户端运行的操作系统当前编码字符集,才能保证 nchar 中的数据正确转换为 UCS4-LE 编码格式。 + +在 Linux 中 locale 的命名规则为: <语言>\_<地区>.<字符集编码> 如:zh_CN.UTF-8,zh 代表中文,CN 代表大陆地区,UTF-8 表示字符集。字符集编码为客户端正确解析本地字符串提供编码转换的说明。Linux 系统与 Mac OSX 系统可以通过设置 locale 来确定系统的字符编码,由于 Windows 使用的 locale 中不是 POSIX 标准的 locale 格式,因此在 Windows 下需要采用另一个配置参数 charset 来指定字符编码。在 Linux 系统中也可以使用 charset 来指定字符编码。 + +::: + +### charset + +| 属性 | 说明 | +| -------- | ----------------------------------------------------------------------- | +| 适用范围 | 服务端和客户端均适用 | +| 含义 | 字符集编码 | +| 缺省值 | 系统中动态获取,如果自动获取失败,需要用户在配置文件设置或通过 API 设置 | + +:::info +如果配置文件中不设置 charset,在 Linux 系统中,taos 在启动时候,自动读取系统当前的 locale 信息,并从 locale 信息中解析提取 charset 编码格式。如果自动读取 locale 信息失败,则尝试读取 charset 配置,如果读取 charset 配置也失败,则中断启动过程。 + +在 Linux 系统中,locale 信息包含了字符编码信息,因此正确设置了 Linux 系统 locale 以后可以不用再单独设置 charset。例如: + +``` +locale zh_CN.UTF-8 +``` + +在 Windows 系统中,无法从 locale 获取系统当前编码。如果无法从配置文件中读取字符串编码信息,taos 默认设置为字符编码为 CP936。其等效在配置文件中添加如下配置: + +``` +charset CP936 +``` + +如果需要调整字符编码,请查阅当前操作系统使用的编码,并在配置文件中正确设置。 + +在 Linux 系统中,如果用户同时设置了 locale 和字符集编码 charset,并且 locale 和 charset 的不一致,后设置的值将覆盖前面设置的值。 + +``` +locale zh_CN.UTF-8 +charset GBK +``` + +则 charset 的有效值是 GBK。 + +``` +charset GBK +locale zh_CN.UTF-8 +``` + +charset 的有效值是 UTF-8。 + +::: + +## 存储相关 + +### dataDir + +| 属性 | 说明 | +| -------- | ------------------------------------------ | +| 适用范围 | 仅服务端适用 | +| 含义 | 数据文件目录,所有的数据文件都将写入该目录 | +| 缺省值 | /var/lib/taos | + +### cache + +| 属性 | 说明 | +| -------- | ------------ | +| 适用范围 | 仅服务端适用 | +| 含义 | 内存块的大小 | +| 单位 | MB | +| 缺省值 | 16 | + +### blocks + +| 属性 | 说明 | +| -------- | ----------------------------------------------------------------------------------------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 每个 vnode(tsdb)中有多少 cache 大小的内存块。因此一个 vnode 的用的内存大小粗略为(cache \* blocks) | +| 缺省值 | 6 | + +### days + +| 属性 | 说明 | +| -------- | -------------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 数据文件存储数据的时间跨度 | +| 单位 | 天 | +| 缺省值 | 10 | + +### keep + +| 属性 | 说明 | +| -------- | -------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 数据保留的天数 | +| 单位 | 天 | +| 缺省值 | 3650 | + +### minRows + +| 属性 | 说明 | +| -------- | ---------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 文件块中记录的最小条数 | +| 缺省值 | 100 | + +### maxRows + +| 属性 | 说明 | +| -------- | ---------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 文件块中记录的最大条数 | +| 缺省值 | 4096 | + +### walLevel + +| 属性 | 说明 | +| -------- | --------------------------------------------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | WAL 级别 | +| 取值范围 | 0: 不写WAL;
1:写 WAL, 但不执行 fsync
2:写 WAL, 而且执行 fsync | +| 缺省值 | 1 | + +### fsync + +| 属性 | 说明 | +| -------- | -------------------------------------------------------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 当 WAL 设置为 2 时,执行 fsync 的周期 | +| 单位 | 毫秒 | +| 取值范围 | 最小为 0,表示每次写入,立即执行 fsync
最大为 180000(三分钟) | +| 缺省值 | 3000 | + +### update + +| 属性 | 说明 | +| -------- | ---------------------------------------------------------------------------------------------------------------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 允许更新已存在的数据行 | +| 取值范围 | 0:不允许更新
1:允许整行更新
2:允许部分列更新。(2.1.7.0 版本开始此参数支持设为 2,在此之前取值只能是 [0, 1]) | +| 缺省值 | 0 | +| 补充说明 | 2.0.8.0 版本之前,不支持此参数。 | + +### cacheLast + +| 属性 | 说明 | +| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| 适用范围 | 仅服务端适用 | +| 含义 | 是否在内存中缓存子表的最近数据 | +| 取值范围 | 0:关闭
1:缓存子表最近一行数据
2:缓存子表每一列的最近的非 NULL 值
3:同时打开缓存最近行和列功能。(2.1.2.0 版本开始此参数支持 0 ~ 3 的取值范围,在此之前取值只能是 [0, 1]) | +| 缺省值 | 0 | +| 补充说明 | 2.1.2.0 版本之前、2.0.20.7 版本之前在 taos.cfg 文件中不支持此参数。 | + +### minimalTmpDirGB + +| 属性 | 说明 | +| -------- | ------------------------------------------------ | +| 适用范围 | 服务端和客户端均适用 | +| 含义 | 当日志文件夹的磁盘大小小于该值时,停止写临时文件 | +| 单位 | GB | +| 缺省值 | 1.0 | + +### minimalDataDirGB + +| 属性 | 说明 | +| -------- | ------------------------------------------------ | +| 适用范围 | 仅服务端适用 | +| 含义 | 当日志文件夹的磁盘大小小于该值时,停止写时序数据 | +| 单位 | GB | +| 缺省值 | 2.0 | + +### vnodeBak + +| 属性 | 说明 | +| -------- | -------------------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 删除 vnode 时是否备份 vnode 目录 | +| 取值范围 | 0:否,1:是 | +| 缺省值 | 1 | + +## 集群相关 + +### numOfMnodes + +| 属性 | 说明 | +| -------- | ------------------ | +| 适用范围 | 仅服务端适用 | +| 含义 | 系统中管理节点个数 | +| 缺省值 | 3 | + +### replica + +| 属性 | 说明 | +| -------- | ------------ | +| 适用范围 | 仅服务端适用 | +| 含义 | 副本个数 | +| 取值范围 | 1-3 | +| 缺省值 | 1 | + +### quorum + +| 属性 | 说明 | +| -------- | -------------------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 多副本环境下指令执行的确认数要求 | +| 取值范围 | 1,2 | +| 缺省值 | 1 | + +### role + +| 属性 | 说明 | +| -------- | ----------------------------------------------------------------------------------------------------------------------------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | dnode 的可选角色 | +| 取值范围 | 0:any(既可作为 mnode,也可分配 vnode)
1:mgmt(只能作为 mnode,不能分配 vnode)
2:dnode(不能作为 mnode,只能分配 vnode) | +| 缺省值 | 0 | + +### balance + +| 属性 | 说明 | +| -------- | ---------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 是否启动负载均衡 | +| 取值范围 | 0,1 | +| 缺省值 | 1 | + +### balanceInterval + +| 属性 | 说明 | +| -------- | ------------------------------------------------ | +| 适用范围 | 仅服务端适用 | +| 含义 | 管理节点在正常运行状态下,检查负载均衡的时间间隔 | +| 单位 | 秒 | +| 取值范围 | 1-30000 | +| 缺省值 | 300 | + +### arbitrator + +| 属性 | 说明 | +| -------- | ------------------------------------------ | +| 适用范围 | 仅服务端适用 | +| 含义 | 系统中裁决器的 endpoint,其格式如 firstEp | +| 缺省值 | 空 | + +## 时间相关 + +### precision + +| 属性 | 说明 | +| -------- | ------------------------------------------------- | +| 适用范围 | 仅服务端 | +| 含义 | 创建数据库时使用的时间精度 | +| 取值范围 | ms: millisecond; us: microsecond ; ns: nanosecond | +| 缺省值 | ms | + +### rpcTimer + +| 属性 | 说明 | +| -------- | -------------------- | +| 适用范围 | 服务端和客户端均适用 | +| 含义 | rpc 重试时长 | +| 单位 | 毫秒 | +| 取值范围 | 100-3000 | +| 缺省值 | 300 | + +### rpcMaxTime + +| 属性 | 说明 | +| -------- | -------------------- | +| 适用范围 | 服务端和客户端均适用 | +| 含义 | rpc 等待应答最大时长 | +| 单位 | 秒 | +| 取值范围 | 100-7200 | +| 缺省值 | 600 | + +### statusInterval + +| 属性 | 说明 | +| -------- | --------------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | dnode 向 mnode 报告状态间隔 | +| 单位 | 秒 | +| 取值范围 | 1-10 | +| 缺省值 | 1 | + +### shellActivityTimer + +| 属性 | 说明 | +| -------- | --------------------------------- | +| 适用范围 | 服务端和客户端均适用 | +| 含义 | shell 客户端向 mnode 发送心跳间隔 | +| 单位 | 秒 | +| 取值范围 | 1-120 | +| 缺省值 | 3 | + +### tableMetaKeepTimer + +| 属性 | 说明 | +| -------- | --------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 表的元数据 cache 时长 | +| 单位 | 秒 | +| 取值范围 | 1-8640000 | +| 缺省值 | 7200 | + +### maxTmrCtrl + +| 属性 | 说明 | +| -------- | -------------------- | +| 适用范围 | 服务端和客户端均适用 | +| 含义 | 定时器个数 | +| 单位 | 个 | +| 取值范围 | 8-2048 | +| 缺省值 | 512 | + +### offlineThreshold + +| 属性 | 说明 | +| -------- | ------------------------------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | dnode 离线阈值,超过该时间将导致 dnode 离线 | +| 单位 | 秒 | +| 取值范围 | 5-7200000 | +| 缺省值 | 86400\*10(10 天) | + +## 性能调优 + +### numOfThreadsPerCore + +| 属性 | 说明 | +| -------- | ----------------------------------- | +| 适用范围 | 服务端和客户端均适用 | +| 含义 | 每个 CPU 核生成的队列消费者线程数量 | +| 缺省值 | 1.0 | + +### ratioOfQueryThreads + +| 属性 | 说明 | +| -------- | ------------------------------------------------------------------------------------------------------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 设置查询线程的最大数量 | +| 取值范围 | 0:表示只有 1 个查询线程
1:表示最大和 CPU 核数相等的查询线程
2:表示最大建立 2 倍 CPU 核数的查询线程。 | +| 缺省值 | 1 | +| 补充说明 | 该值可以为小数,即 0.5 表示最大建立 CPU 核数一半的查询线程。 | + +### maxVgroupsPerDb + +| 属性 | 说明 | +| -------- | ------------------------------------ | +| 适用范围 | 仅服务端适用 | +| 含义 | 每个 DB 中 能够使用的最大 vnode 个数 | +| 取值范围 | 0-8192 | +| 缺省值 | | + +### maxTablesPerVnode + +| 属性 | 说明 | +| -------- | --------------------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 每个 vnode 中能够创建的最大表个数 | +| 缺省值 | 1000000 | + +### minTablesPerVnode + +| 属性 | 说明 | +| -------- | --------------------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 每个 vnode 中必须创建表的最小数量 | +| 缺省值 | 1000 | + +### tableIncStepPerVnode + +| 属性 | 说明 | +| -------- | ------------------------------------------------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 每个 vnode 中超过最小表数,i.e. minTablesPerVnode, 后递增步长 | +| 缺省值 | 1000 | + +### maxNumOfOrderedRes + +| 属性 | 说明 | +| -------- | -------------------------------------- | +| 适用范围 | 服务端和客户端均适用 | +| 含义 | 支持超级表时间排序允许的最多记录数限制 | +| 缺省值 | 10 万 | + +### mnodeEqualVnodeNum + +| 属性 | 说明 | +| -------- | ------------------------------------ | +| 适用范围 | 仅服务端适用 | +| 含义 | 将一个 mnode 等同于 vnode 消耗的个数 | +| 缺省值 | 4 | + +### numOfCommitThreads + +| 属性 | 说明 | +| -------- | ---------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 设置写入线程的最大数量 | +| 缺省值 | | + +## 压缩相关 + +### comp + +| 属性 | 说明 | +| -------- | ----------------------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 文件压缩标志位 | +| 取值范围 | 0:关闭,1:一阶段压缩,2:两阶段压缩 | +| 缺省值 | 2 | + +### tsdbMetaCompactRatio + +| 属性 | 说明 | +| -------- | -------------------------------------------------------------- | +| 含义 | tsdb meta 文件中冗余数据超过多少阈值,开启 meta 文件的压缩功能 | +| 取值范围 | 0:不开启,[1-100]:冗余数据比例 | +| 缺省值 | 0 | + +### compressMsgSize + +| 属性 | 说明 | +| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 客户端与服务器之间进行消息通讯过程中,对通讯的消息进行压缩的阈值。如果要压缩消息,建议设置为 64330 字节,即大于 64330 字节的消息体才进行压缩。 | +| 单位 | bytes | +| 取值范围 | `0 `表示对所有的消息均进行压缩 >0: 超过该值的消息才进行压缩 -1: 不压缩 | +| 缺省值 | -1 | + +### compressColData + +| 属性 | 说明 | +| -------- | --------------------------------------------------------------------------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 客户端与服务器之间进行消息通讯过程中,对服务器端查询结果进行列压缩的阈值。 | +| 单位 | bytes | +| 取值范围 | 0: 对所有查询结果均进行压缩 >0: 查询结果中任意列大小超过该值的消息才进行压缩 -1: 不压缩 | +| 缺省值 | -1 | +| 补充说明 | 2.3.0.0 版本新增。 | + +### lossyColumns + +| 属性 | 说明 | +| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| 适用范围 | 服务器端 | +| 含义 | 配置要进行有损压缩的浮点数据类型 | +| 取值范围 | 空字符串:关闭有损压缩
float:只对 float 类型进行有损压缩
double:只对 double 类型进行有损压缩
float \| double:float double 都进行有损压缩 | +| 缺省值 | 空字符串 | +| 补充说明 | 有损压缩默认为关闭状态,只有配置后才生效 | + +### fPrecision + +| 属性 | 说明 | +| -------- | -------------------------------- | +| 适用范围 | 服务器端 | +| 含义 | 设置 float 类型浮点数压缩精度 | +| 取值范围 | 0.1 ~ 0.00000001 | +| 缺省值 | 0.00000001 | +| 补充说明 | 小于此值的浮点数尾数部分将被截取 | + +### dPrecision + +| 属性 | 说明 | +| -------- | -------------------------------- | +| 适用范围 | 服务器端 | +| 含义 | 设置 double 类型浮点数压缩精度 | +| 取值范围 | 0.1 ~ 0.0000000000000001 | +| 缺省值 | 0.0000000000000001 | +| 补充说明 | 小于此值的浮点数尾数部分将被截取 | + +## 连续查询相关 + +### stream + +| 属性 | 说明 | +| -------- | ------------------------------ | +| 适用范围 | 仅服务端适用 | +| 含义 | 是否启用连续查询(流计算功能) | +| 取值范围 | 0:不允许
1:允许 | +| 缺省值 | 1 | + +### minSlidingTime + +| 属性 | 说明 | +| -------- | ----------------------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 最小滑动窗口时长 | +| 单位 | 毫秒 | +| 取值范围 | 10-1000000 | +| 缺省值 | 10 | +| 补充说明 | 支持 us 补值后,这个值就是 1us 了。 | + +### minIntervalTime + +| 属性 | 说明 | +| -------- | -------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 时间窗口最小值 | +| 单位 | 毫秒 | +| 取值范围 | 1-1000000 | +| 缺省值 | 10 | + +### maxStreamCompDelay + +| 属性 | 说明 | +| -------- | -------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 连续查询启动最大延迟 | +| 单位 | 毫秒 | +| 取值范围 | 10-1000000000 | +| 缺省值 | 20000 | + +### maxFirstStreamCompDelay + +| 属性 | 说明 | +| -------- | -------------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 第一次连续查询启动最大延迟 | +| 单位 | 毫秒 | +| 取值范围 | 10-1000000000 | +| 缺省值 | 10000 | + +### retryStreamCompDelay + +| 属性 | 说明 | +| -------- | -------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 连续查询重试等待间隔 | +| 单位 | 毫秒 | +| 取值范围 | 10-1000000000 | +| 缺省值 | 10 | + +### streamCompDelayRatio + +| 属性 | 说明 | +| -------- | ---------------------------------------------------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 连续查询的延迟时间计算系数,实际延迟时间为本参数乘以计算时间窗口 | +| 取值范围 | 0.1-0.9 | +| 缺省值 | 0.1 | + +:::info +为避免多个 stream 同时执行占用太多系统资源,程序中对 stream 的执行时间人为增加了一些随机的延时。
maxFirstStreamCompDelay 是 stream 第一次执行前最少要等待的时间。
streamCompDelayRatio 是延迟时间的计算系数,它乘以查询的 interval 后为延迟时间基准。
maxStreamCompDelay 是延迟时间基准的上限。
实际延迟时间为一个不超过延迟时间基准的随机值。
stream 某次计算失败后需要重试,retryStreamCompDelay 是重试的等待时间基准。
实际重试等待时间为不超过等待时间基准的随机值。 + +::: + +## HTTP 相关 + +:::note +HTTP 服务在 2.4.0.0(不含)以前的版本中由 taosd 提供,在 2.4.0.0 以后(含)由 taosAdapter 提供。 +本节的配置参数仅在 2.4.0.0(不含)以前的版本中生效。如果您使用的是 2.4.0.0(含)及以后的版本请参考[文档](/reference/taosadapter/)。 + +::: + +### http + +| 属性 | 说明 | +| -------- | --------------------------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 服务器内部的 http 服务开关。 | +| 取值范围 | 0:关闭 http 服务, 1:激活 http 服务。 | +| 缺省值 | 1 | + +### httpEnableRecordSql + +| 属性 | 说明 | +| -------- | --------------------------------------------------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 记录通过 RESTFul 接口,产生的 SQL 调用。 | +| 缺省值 | 0 | +| 补充说明 | 生成的文件(httpnote.0/httpnote.1),与服务端日志所在目录相同。 | + +### httpMaxThreads + +| 属性 | 说明 | +| -------- | ------------------------------------------------------------------------------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | RESTFul 接口的线程数。taosAdapter 配置或有不同,请参考相应[文档](/reference/taosadapter/)。 | +| 缺省值 | 2 | + +### restfulRowLimit + +| 属性 | 说明 | +| -------- | ----------------------------------------------------------------------------------------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | RESTFul 接口单次返回的记录条数。taosAdapter 配置或有不同,请参考相应[文档](/reference/taosadapter/)。 | +| 缺省值 | 10240 | +| 补充说明 | 最大 10,000,000 | + +### httpDBNameMandatory + +| 属性 | 说明 | +| -------- | ---------------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | 是否在 URL 中输入 数据库名称 | +| 取值范围 | 0:不开启,1:开启 | +| 缺省值 | 0 | +| 补充说明 | 2.3 版本新增。 | + +## 日志相关 + +### logDir + +| 属性 | 说明 | +| -------- | -------------------------------------------------- | +| 适用范围 | 服务端和客户端均适用 | +| 含义 | 日志文件目录,客户端和服务器的运行日志将写入该目录 | +| 缺省值 | /var/log/taos | + +### minimalLogDirGB + +| 属性 | 说明 | +| -------- | -------------------------------------------- | +| 适用范围 | 服务端和客户端均适用 | +| 含义 | 当日志文件夹的磁盘大小小于该值时,停止写日志 | +| 单位 | GB | +| 缺省值 | 1.0 | + +### numOfLogLines + +| 属性 | 说明 | +| -------- | ---------------------------- | +| 适用范围 | 服务端和客户端均适用 | +| 含义 | 单个日志文件允许的最大行数。 | +| 缺省值 | 10,000,000 | + +### asyncLog + +| 属性 | 说明 | +| -------- | -------------------- | +| 适用范围 | 服务端和客户端均适用 | +| 含义 | 日志写入模式 | +| 取值范围 | 0:同步、1:异步 | +| 缺省值 | 1 | + +### logKeepDays + +| 属性 | 说明 | +| -------- | ----------------------------------------------------------------------------------- | +| 适用范围 | 服务端和客户端均适用 | +| 含义 | 日志文件的最长保存时间 | +| 单位 | 天 | +| 缺省值 | 0 | +| 补充说明 | 大于 0 时,日志文件会被重命名为 taosdlog.xxx,其中 xxx 为日志文件最后修改的时间戳。 | + +### debugFlag + +| 属性 | 说明 | +| -------- | ------------------------------------------------------------------------------------------------- | +| 适用范围 | 服务端和客户端均适用 | +| 含义 | 运行日志开关 | +| 取值范围 | 131(输出错误和警告日志),135(输出错误、警告和调试日志),143(输出错误、警告、调试和跟踪日志) | +| 缺省值 | 131 或 135(不同模块有不同的默认值) | + +### mDebugFlag + +| 属性 | 说明 | +| -------- | ------------------ | +| 适用范围 | 仅服务端适用 | +| 含义 | 管理模块的日志开关 | +| 取值范围 | 同上 | +| 缺省值 | 135 | + +### dDebugFlag + +| 属性 | 说明 | +| -------- | -------------------- | +| 适用范围 | 服务端和客户端均适用 | +| 含义 | dnode 模块的日志开关 | +| 取值范围 | 同上 | +| 缺省值 | 135 | + +### sDebugFlag + +| 属性 | 说明 | +| -------- | -------------------- | +| 适用范围 | 服务端和客户端均适用 | +| 含义 | sync 模块的日志开关 | +| 取值范围 | 同上 | +| 缺省值 | 135 | + +### wDebugFlag + +| 属性 | 说明 | +| -------- | -------------------- | +| 适用范围 | 服务端和客户端均适用 | +| 含义 | WAL 模块的日志开关 | +| 取值范围 | 同上 | +| 缺省值 | 135 | + +### sdbDebugFlag + +| 属性 | 说明 | +| -------- | -------------------- | +| 适用范围 | 服务端和客户端均适用 | +| 含义 | sdb 模块的日志开关 | +| 取值范围 | 同上 | +| 缺省值 | 135 | + +### rpcDebugFlag + +| 属性 | 说明 | +| -------- | -------------------- | +| 适用范围 | 服务端和客户端均适用 | +| 含义 | rpc 模块的日志开关 | +| 取值范围 | 同上 | +| 缺省值 | | + +### tmrDebugFlag + +| 属性 | 说明 | +| -------- | -------------------- | +| 适用范围 | 服务端和客户端均适用 | +| 含义 | 定时器模块的日志开关 | +| 取值范围 | 同上 | +| 缺省值 | | + +### cDebugFlag + +| 属性 | 说明 | +| -------- | --------------------- | +| 适用范围 | 仅客户端适用 | +| 含义 | client 模块的日志开关 | +| 取值范围 | 同上 | +| 缺省值 | | + +### jniDebugFlag + +| 属性 | 说明 | +| -------- | ------------------ | +| 适用范围 | 仅客户端适用 | +| 含义 | jni 模块的日志开关 | +| 取值范围 | 同上 | +| 缺省值 | | + +### odbcDebugFlag + +| 属性 | 说明 | +| -------- | ------------------- | +| 适用范围 | 仅客户端适用 | +| 含义 | odbc 模块的日志开关 | +| 取值范围 | 同上 | +| 缺省值 | | + +### uDebugFlag + +| 属性 | 说明 | +| -------- | ---------------------- | +| 适用范围 | 服务端和客户端均适用 | +| 含义 | 共用功能模块的日志开关 | +| 取值范围 | 同上 | +| 缺省值 | | + +### httpDebugFlag + +| 属性 | 说明 | +| -------- | ------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | http 模块的日志开关 | +| 取值范围 | 同上 | +| 缺省值 | | + +### mqttDebugFlag + +| 属性 | 说明 | +| -------- | ------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | mqtt 模块的日志开关 | +| 取值范围 | 同上 | +| 缺省值 | | + +### monitorDebugFlag + +| 属性 | 说明 | +| -------- | ------------------ | +| 适用范围 | 仅服务端适用 | +| 含义 | 监控模块的日志开关 | +| 取值范围 | 同上 | +| 缺省值 | | + +### qDebugFlag + +| 属性 | 说明 | +| -------- | -------------------- | +| 适用范围 | 服务端和客户端均适用 | +| 含义 | 查询模块的日志开关 | +| 取值范围 | 同上 | +| 缺省值 | | + +### vDebugFlag + +| 属性 | 说明 | +| -------- | -------------------- | +| 适用范围 | 服务端和客户端均适用 | +| 含义 | vnode 模块的日志开关 | +| 取值范围 | 同上 | +| 缺省值 | | + +### tsdbDebugFlag + +| 属性 | 说明 | +| -------- | ------------------- | +| 适用范围 | 仅服务端适用 | +| 含义 | TSDB 模块的日志开关 | +| 取值范围 | 同上 | +| 缺省值 | | + +### cqDebugFlag + +| 属性 | 说明 | +| -------- | ---------------------- | +| 适用范围 | 服务端和客户端均适用 | +| 含义 | 连续查询模块的日志开关 | +| 取值范围 | 同上 | +| 缺省值 | | + +## 仅客户端适用 + +### maxSQLLength + +| 属性 | 说明 | +| -------- | --------------------------- | +| 适用范围 | 仅客户端适用 | +| 含义 | 单条 SQL 语句允许的最长限制 | +| 单位 | bytes | +| 取值范围 | 65480-1048576 | +| 缺省值 | 1048576 | + +### tscEnableRecordSql + +| 属性 | 说明 | +| -------- | ----------------------------------------------------------------------------------- | +| 含义 | 是否记录客户端 sql 语句到文件 | +| 取值范围 | 0:否,1:是 | +| 缺省值 | 0 | +| 补充说明 | 生成的文件(tscnote-xxxx.0/tscnote-xxx.1,xxxx 是 pid),与客户端日志所在目录相同。 | + +### maxBinaryDisplayWidth + +| 属性 | 说明 | +| -------- | -------------------------------------------------------------------------- | +| 含义 | Taos shell 中 binary 和 nchar 字段的显示宽度上限,超过此限制的部分将被隐藏 | +| 取值范围 | 5 - | +| 缺省值 | 30 | + +:::info +实际上限按以下规则计算:如果字段值的长度大于 maxBinaryDisplayWidth,则显示上限为 **字段名长度** 和 **maxBinaryDisplayWidth** 的较大者。
否则,上限为 **字段名长度** 和 **字段值长度** 的较大者。
可在 shell 中通过命令 set max_binary_display_width nn 动态修改此选项 + +::: + +### maxWildCardsLength + +| 属性 | 说明 | +| -------- | ------------------------------------------ | +| 含义 | 设定 LIKE 算子的通配符字符串允许的最大长度 | +| 单位 | bytes | +| 取值范围 | 0-16384 | +| 缺省值 | 100 | +| 补充说明 | 2.1.6.1 版本新增。 | + +### clientMerge + +| 属性 | 说明 | +| -------- | ---------------------------- | +| 含义 | 是否允许客户端对写入数据去重 | +| 取值范围 | 0:不开启,1:开启 | +| 缺省值 | 0 | +| 补充说明 | 2.3 版本新增。 | + +### maxRegexStringLen + +| 属性 | 说明 | +| -------- | -------------------------- | +| 含义 | 正则表达式最大允许长度 | +| 取值范围 | 默认值 128,最大长度 16384 | +| 缺省值 | 128 | +| 补充说明 | 2.3 版本新增。 | + +## 其他 + +### enableCoreFile + +| 属性 | 说明 | +| -------- | ------------------------------------------------------------------------------------------------------------------------------------------ | +| 适用范围 | 服务端和客户端均适用 | +| 含义 | 是否开启服务 crash 时生成 core 文件 | +| 取值范围 | 0:否,1:是 | +| 缺省值 | 1 | +| 补充说明 | 不同的启动方式,生成 core 文件的目录如下:1、systemctl start taosd 启动:生成的 core 在根目录下
2、手动启动,就在 taosd 执行目录下。 | diff --git a/docs-cn/14-reference/12-directory.md b/docs-cn/14-reference/12-directory.md new file mode 100644 index 0000000000000000000000000000000000000000..f8c8cb4a082f691cf75db9bed3b42d0d6e1bc8a3 --- /dev/null +++ b/docs-cn/14-reference/12-directory.md @@ -0,0 +1,42 @@ +--- +title: 文件目录结构 +description: "TDengine 安装目录说明" +--- + +安装 TDengine 后,默认会在操作系统中生成下列目录或文件: + +| 目录/文件 | 说明 | +| ------------------------- | -------------------------------------------------------------------- | +| /usr/local/taos/bin | TDengine 可执行文件目录。其中的执行文件都会软链接到/usr/bin 目录下。 | +| /usr/local/taos/driver | TDengine 动态链接库目录。会软链接到/usr/lib 目录下。 | +| /usr/local/taos/examples | TDengine 各种语言应用示例目录。 | +| /usr/local/taos/include | TDengine 对外提供的 C 语言接口的头文件。 | +| /etc/taos/taos.cfg | TDengine 默认[配置文件] | +| /var/lib/taos | TDengine 默认数据文件目录。可通过[配置文件]修改位置。 | +| /var/log/taos | TDengine 默认日志文件目录。可通过[配置文件]修改位置。 | + +## 可执行文件 + +TDengine 的所有可执行文件默认存放在 _/usr/local/taos/bin_ 目录下。其中包括: + +- _taosd_:TDengine 服务端可执行文件 +- _taos_:TDengine Shell 可执行文件 +- _taosdump_:数据导入导出工具 +- _taosBenchmark_:TDengine 测试工具 +- _remove.sh_:卸载 TDengine 的脚本,请谨慎执行,链接到/usr/bin 目录下的**rmtaos**命令。会删除 TDengine 的安装目录/usr/local/taos,但会保留/etc/taos、/var/lib/taos、/var/log/taos +- _taosadapter_: 提供 RESTful 服务和接受其他多种软件写入请求的服务端可执行文件 +- _tarbitrator_: 提供双节点集群部署的仲裁功能 +- _run_taosd_and_taosadapter.sh_:同时启动 taosd 和 taosAdapter 的脚本 +- _TDinsight.sh_:用于下载 TDinsight 并安装的脚本 +- _set_core.sh_:用于方便调试设置系统生成 core dump 文件的脚本 +- _taosd-dump-cfg.gdb_:用于方便调试 taosd 的 gdb 执行脚本。 + +:::note +2.4.0.0 版本之后的 taosBenchmark 和 taosdump 需要安装独立安装包 taosTools。 + +::: + +:::tip +您可以通过修改系统配置文件 taos.cfg 来配置不同的数据目录和日志目录。 + +::: diff --git a/docs-cn/14-reference/13-schemaless/13-schemaless.md b/docs-cn/14-reference/13-schemaless/13-schemaless.md new file mode 100644 index 0000000000000000000000000000000000000000..4de310c248d7763690acef80cdca1c50f609d63b --- /dev/null +++ b/docs-cn/14-reference/13-schemaless/13-schemaless.md @@ -0,0 +1,165 @@ +--- +title: Schemaless 写入 +description: 'Schemaless 写入方式,可以免于预先创建超级表/子表的步骤,随着数据写入接口能够自动创建与数据对应的存储结构' +--- + +在物联网应用中,常会采集比较多的数据项,用于实现智能控制、业务分析、设备监控等。由于应用逻辑的版本升级,或者设备自身的硬件调整等原因,数据采集项就有可能比较频繁地出现变动。为了在这种情况下方便地完成数据记录工作,TDengine +从 2.2.0.0 版本开始,提供调用 Schemaless 写入方式,可以免于预先创建超级表/子表的步骤,随着数据写入接口能够自动创建与数据对应的存储结构。并且在必要时,Schemaless +将自动增加必要的数据列,保证用户写入的数据可以被正确存储。 + +无模式写入方式建立的超级表及其对应的子表与通过 SQL 直接建立的超级表和子表完全没有区别,你也可以通过,SQL 语句直接向其中写入数据。需要注意的是,通过无模式写入方式建立的表,其表名是基于标签值按照固定的映射规则生成,所以无法明确地进行表意,缺乏可读性。 + +## 无模式写入行协议 + +TDengine 的无模式写入的行协议兼容 InfluxDB 的 行协议(Line Protocol)、OpenTSDB 的 telnet 行协议、OpenTSDB 的 JSON 格式协议。但是使用这三种协议的时候,需要在 API 中指定输入内容使用解析协议的标准。 + +对于 InfluxDB、OpenTSDB 的标准写入协议请参考各自的文档。下面首先以 InfluxDB 的行协议为基础,介绍 TDengine 扩展的协议内容,允许用户采用更加精细的方式控制(超级表)模式。 + +Schemaless 采用一个字符串来表达一个数据行(可以向写入 API 中一次传入多行字符串来实现多个数据行的批量写入),其格式约定如下: + +```json +measurement,tag_set field_set timestamp +``` + +其中: + +- measurement 将作为数据表名。它与 tag_set 之间使用一个英文逗号来分隔。 +- tag_set 将作为标签数据,其格式形如 `=,=`,也即可以使用英文逗号来分隔多个标签数据。它与 field_set 之间使用一个半角空格来分隔。 +- field_set 将作为普通列数据,其格式形如 `=,=`,同样是使用英文逗号来分隔多个普通列的数据。它与 timestamp 之间使用一个半角空格来分隔。 +- timestamp 即本行数据对应的主键时间戳。 + +tag_set 中的所有的数据自动转化为 nchar 数据类型,并不需要使用双引号(")。 + +在无模式写入数据行协议中,field_set 中的每个数据项都需要对自身的数据类型进行描述。具体来说: + +- 如果两边有英文双引号,表示 BINARY(32) 类型。例如 `"abc"`。 +- 如果两边有英文双引号而且带有 L 前缀,表示 NCHAR(32) 类型。例如 `L"报错信息"`。 +- 对空格、等号(=)、逗号(,)、双引号("),前面需要使用反斜杠(\)进行转义。(都指的是英文半角符号) +- 数值类型将通过后缀来区分数据类型: + +| **序号** | **后缀** | **映射类型** | **大小(字节)** | +| -------- | -------- | ------------ | -------------- | +| 1 | 无或 f64 | double | 8 | +| 2 | f32 | float | 4 | +| 3 | i8 | TinyInt | 1 | +| 4 | i16 | SmallInt | 2 | +| 5 | i32 | Int | 4 | +| 6 | i64 或 i | Bigint | 8 | + +- t, T, true, True, TRUE, f, F, false, False 将直接作为 BOOL 型来处理。 + +例如如下数据行表示:向名为 st 的超级表下的 t1 标签为 "3"(NCHAR)、t2 标签为 "4"(NCHAR)、t3 +标签为 "t3"(NCHAR)的数据子表,写入 c1 列为 3(BIGINT)、c2 列为 false(BOOL)、c3 +列为 "passit"(BINARY)、c4 列为 4(DOUBLE)、主键时间戳为 1626006833639000000 的一行数据。 + +```json +st,t1=3,t2=4,t3=t3 c1=3i64,c3="passit",c2=false,c4=4f64 1626006833639000000 +``` + +需要注意的是,如果描述数据类型后缀时使用了错误的大小写,或者为数据指定的数据类型有误,均可能引发报错提示而导致数据写入失败。 + +## 无模式写入的主要处理逻辑 + +无模式写入按照如下原则来处理行数据: + +1. 将使用如下规则来生成子表名:首先将 measurement 的名称和标签的 key 和 value 组合成为如下的字符串 + +```json +"measurement,tag_key1=tag_value1,tag_key2=tag_value2" +``` + +需要注意的是,这里的 tag_key1, tag_key2 并不是用户输入的标签的原始顺序,而是使用了标签名称按照字符串升序排列后的结果。所以,tag_key1 并不是在行协议中输入的第一个标签。 +排列完成以后计算该字符串的 MD5 散列值 "md5_val"。然后将计算的结果与字符串组合生成表名:“t_md5_val”。其中的 “t\*” 是固定的前缀,每个通过该映射关系自动生成的表都具有该前缀。 + +2. 如果解析行协议获得的超级表不存在,则会创建这个超级表。 +3. 如果解析行协议获得子表不存在,则 Schemaless 会按照步骤 1 或 2 确定的子表名来创建子表。 +4. 如果数据行中指定的标签列或普通列不存在,则在超级表中增加对应的标签列或普通列(只增不减)。 +5. 如果超级表中存在一些标签列或普通列未在一个数据行中被指定取值,那么这些列的值在这一行中会被置为 + NULL。 +6. 对 BINARY 或 NCHAR 列,如果数据行中所提供值的长度超出了列类型的限制,自动增加该列允许存储的字符长度上限(只增不减),以保证数据的完整保存。 +7. 如果指定的数据子表已经存在,而且本次指定的标签列取值跟已保存的值不一样,那么最新的数据行中的值会覆盖旧的标签列取值。 +8. 整个处理过程中遇到的错误会中断写入过程,并返回错误代码。 + +:::tip +无模式所有的处理逻辑,仍会遵循 TDengine 对数据结构的底层限制,例如每行数据的总长度不能超过 +16k 字节。这方面的具体限制约束请参见 [TAOS SQL 边界限制](/taos-sql/limit) + +::: + +## 时间分辨率识别 + +无模式写入过程中支持三个指定的模式,具体如下 + +| **序号** | **值** | **说明** | +| -------- | ------------------- | ------------------------------- | +| 1 | SML_LINE_PROTOCOL | InfluxDB 行协议(Line Protocol) | +| 2 | SML_TELNET_PROTOCOL | OpenTSDB 文本行协议 | +| 3 | SML_JSON_PROTOCOL | JSON 协议格式 | + +在 SML_LINE_PROTOCOL 解析模式下,需要用户指定输入的时间戳的时间分辨率。可用的时间分辨率如下表所示: + +| **序号** | **时间分辨率定义** | **含义** | +| -------- | --------------------------------- | -------------- | +| 1 | TSDB_SML_TIMESTAMP_NOT_CONFIGURED | 未定义(无效) | +| 2 | TSDB_SML_TIMESTAMP_HOURS | 小时 | +| 3 | TSDB_SML_TIMESTAMP_MINUTES | 分钟 | +| 4 | TSDB_SML_TIMESTAMP_SECONDS | 秒 | +| 5 | TSDB_SML_TIMESTAMP_MILLI_SECONDS | 毫秒 | +| 6 | TSDB_SML_TIMESTAMP_MICRO_SECONDS | 微秒 | +| 7 | TSDB_SML_TIMESTAMP_NANO_SECONDS | 纳秒 | + +在 SML_TELNET_PROTOCOL 和 SML_JSON_PROTOCOL 模式下,根据时间戳的长度来确定时间精度(与 OpenTSDB 标准操作方式相同),此时会忽略用户指定的时间分辨率。 + +## 数据模式映射规则 + +本节将说明行协议的数据如何映射成为具有模式的数据。每个行协议中数据 measurement 映射为 +超级表名称。tag_set 中的 标签名称为 数据模式中的标签名,field_set 中的名称为列名称。以如下数据为例,说明映射规则: + +```json +st,t1=3,t2=4,t3=t3 c1=3i64,c3="passit",c2=false,c4=4f64 1626006833639000000 +``` + +该行数据映射生成一个超级表: st, 其包含了 3 个类型为 nchar 的标签,分别是:t1, t2, t3。五个数据列,分别是 ts(timestamp),c1 (bigint),c3(binary),c2 (bool), c4 (bigint)。映射成为如下 SQL 语句: + +```json +create stable st (_ts timestamp, c1 bigint, c2 bool, c3 binary(6), c4 bigint) tags(t1 nchar(1), t2 nchar(1), t3 nchar(2)) +``` + +## 数据模式变更处理 + +本节将说明不同行数据写入情况下,对于数据模式的影响。 + +在使用行协议写入一个明确的标识的字段类型的时候,后续更改该字段的类型定义,会出现明确的数据模式错误,即会触发写入 API 报告错误。如下所示, + +```json +st,t1=3,t2=4,t3=t3 c1=3i64,c3="passit",c2=false,c4=4 1626006833639000000 +st,t1=3,t2=4,t3=t3 c1=3i64,c3="passit",c2=false,c4=4i 1626006833640000000 +``` + +第一行的数据类型映射将 c4 列定义为 Double, 但是第二行的数据又通过数值后缀方式声明该列为 BigInt, 由此会触发无模式写入的解析错误。 + +如果列前面的行协议将数据列声明为了 binary, 后续的要求长度更长的 binary 长度,此时会触发超级表模式的变更。 + +```json +st,t1=3,t2=4,t3=t3 c1=3i64,c5="pass" 1626006833639000000 +st,t1=3,t2=4,t3=t3 c1=3i64,c5="passit" 1626006833640000000 +``` + +第一行中行协议解析会声明 c5 列是一个 binary(4)的字段,第二次行数据写入会提取列 c5 仍然是 binary 列,但是其宽度为 6,此时需要将 binary 的宽度增加到能够容纳 新字符串的宽度。 + +```json +st,t1=3,t2=4,t3=t3 c1=3i64 1626006833639000000 +st,t1=3,t2=4,t3=t3 c1=3i64,c6="passit" 1626006833640000000 +``` + +第二行数据相对于第一行来说增加了一个列 c6,类型为 binary(6)。那么此时会自动增加一个列 c6, 类型为 binary(6)。 + +## 写入完整性 + +TDengine 提供数据写入的幂等性保证,即您可以反复调用 API 进行出错数据的写入操作。但是不提供多行数据写入的原子性保证。即在多行数据一批次写入过程中,会出现部分数据写入成功,部分数据写入失败的情况。 + +## 错误码 + +如果是无模式写入过程中的数据本身错误,应用会得到 TSDB_CODE_TSC_LINE_SYNTAX_ERROR +错误信息,该错误信息表明错误发生在写入文本中。其他的错误码与原系统一致,可以通过 +taos_errstr 获取具体的错误原因。 diff --git a/docs-cn/14-reference/13-schemaless/_category_.yml b/docs-cn/14-reference/13-schemaless/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..2e93df8b366a0d60d4af35ff35076b8345a1ad6a --- /dev/null +++ b/docs-cn/14-reference/13-schemaless/_category_.yml @@ -0,0 +1 @@ +label: Schemaless 写入 diff --git a/docs-cn/14-reference/_category_.yml b/docs-cn/14-reference/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..ae861a15ff626b1e0424a28838830702262aa377 --- /dev/null +++ b/docs-cn/14-reference/_category_.yml @@ -0,0 +1 @@ +label: 参考指南 \ No newline at end of file diff --git a/docs-cn/14-reference/_collectd.mdx b/docs-cn/14-reference/_collectd.mdx new file mode 100644 index 0000000000000000000000000000000000000000..af3388f680228cacee3ea1081e3c01faec54e670 --- /dev/null +++ b/docs-cn/14-reference/_collectd.mdx @@ -0,0 +1,85 @@ +### 配置 taosAdapter + +配置 taosAdapter 接收 collectd 数据的方法: + +- 在 taosAdapter 配置文件(默认位置为 /etc/taos/taosadapter.toml)中使能配置项 + +``` +... +[opentsdb_telnet] +enable = true +maxTCPConnections = 250 +tcpKeepAlive = false +dbs = ["opentsdb_telnet", "collectd", "icinga2", "tcollector"] +ports = [6046, 6047, 6048, 6049] +user = "root" +password = "taosdata" +... +``` + +其中 taosAdapter 默认写入的数据库名称为 `collectd`,也可以修改 taosAdapter 配置文件 dbs 项来指定不同的名称。user 和 password 填写实际 TDengine 配置的值。修改过配置文件 taosAdapter 需重新启动。 + +- 也可以使用 taosAdapter 命令行参数或设置环境变量启动的方式,使能 taosAdapter 接收 collectd 数据功能,具体细节请参考 [taosAdapter 的使用手册](/reference/taosadapter) + +### 配置 collectd +# +collectd 使用插件机制可以以多种形式将采集到的监控数据写入到不同的数据存储软件。TDengine 支持直接采集插件和 write_tsdb 插件。 + +#### 配置接收直接采集插件数据 + +修改 collectd 配置文件(默认位置 /etc/collectd/collectd.conf)相关配置项。 + +```text +LoadPlugin network + + Server "" "" + +``` + +其中 填写运行 taosAdapter 的服务器域名或 IP 地址。 填写 taosAdapter 用于接收 collectd 数据的端口(默认为 6045)。 + +示例如下: + +```text +LoadPlugin network + + Server "127.0.0.1" "6045" + +``` + +#### 配置 write_tsdb 插件数据 + +修改 collectd 配置文件(默认位置 /etc/collectd/collectd.conf)相关配置项。 + +```text +LoadPlugin write_tsdb + + + Host "" + Port "" + ... + + +``` + +其中 填写运行 taosAdapter 的服务器域名或 IP 地址。 填写 taosAdapter 用于接收 collectd write_tsdb 插件的数据(默认为 6047)。 + +```text +LoadPlugin write_tsdb + + + Host "127.0.0.1" + Port "6047" + HostTags "status=production" + StoreRates false + AlwaysAppendDS false + + +``` + +然后重启 collectd: + +``` +systemctl restart collectd +``` + diff --git a/docs-cn/14-reference/_icinga2.mdx b/docs-cn/14-reference/_icinga2.mdx new file mode 100644 index 0000000000000000000000000000000000000000..fdb341e264df8f9315ccd51abbc638adef65c06a --- /dev/null +++ b/docs-cn/14-reference/_icinga2.mdx @@ -0,0 +1,44 @@ +### 配置 taosAdapter + +配置 taosAdapter 接收 icinga2 数据的方法: + +- 在 taosAdapter 配置文件(默认位置 /etc/taos/taosadapter.toml)中使能配置项 + +``` +... +[opentsdb_telnet] +enable = true +maxTCPConnections = 250 +tcpKeepAlive = false +dbs = ["opentsdb_telnet", "collectd", "icinga2", "tcollector"] +ports = [6046, 6047, 6048, 6049] +user = "root" +password = "taosdata" +... +``` + +其中 taosAdapter 默认写入的数据库名称为 `icinga2`,也可以修改 taosAdapter 配置文件 dbs 项来指定不同的名称。user 和 password 填写实际 TDengine 配置的值。修改过 taosAdapter 需重新启动。 + +- 也可以使用 taosAdapter 命令行参数或设置环境变量启动的方式,使能 taosAdapter 接收 icinga2 数据功能,具体细节请参考 [taosAdapter 的使用手册](/reference/taosadapter) + +### 配置 icinga2 + +- 使能 icinga2 的 opentsdb-writer(参考链接 https://icinga.com/docs/icinga-2/latest/doc/14-features/#opentsdb-writer) +- 修改配置文件 `/etc/icinga2/features-enabled/opentsdb.conf` 填写 为运行 taosAdapter 的服务器的域名或 IP 地址, 填写 taosAdapter 支持接收 icinga2 数据的相应端口(默认为 6048) + +``` +object OpenTsdbWriter "opentsdb" { + host = "" + port = +} +``` + +示例文件: + +``` +object OpenTsdbWriter "opentsdb" { + host = "127.0.0.1" + port = 6048 +} +``` + diff --git a/docs-cn/14-reference/_prometheus.mdx b/docs-cn/14-reference/_prometheus.mdx new file mode 100644 index 0000000000000000000000000000000000000000..be73d95cbc001727ee1ea60759f37e0a836643c2 --- /dev/null +++ b/docs-cn/14-reference/_prometheus.mdx @@ -0,0 +1,31 @@ +配置 Prometheus 是通过编辑 Prometheus 配置文件 prometheus.yml (默认位置 /etc/prometheus/prometheus.yml)完成的。 + +### 配置第三方数据库地址 + +将其中的 remote_read url 和 remote_write url 指向运行 taosAdapter 服务的服务器域名或 IP 地址,REST 服务端口(taosAdapter 默认使用 6041),以及希望写入 TDengine 的数据库名称,并确保相应的 URL 形式如下: + +- remote_read url : `http://:/prometheus/v1/remote_read/` +- remote_write url : `http://:/prometheus/v1/remote_write/` + +### 配置 Basic 验证 + +- username: +- password: + +### prometheus.yml 文件中 remote_write 和 remote_read 相关部分配置示例 + +```yaml +remote_write: + - url: "http://localhost:6041/prometheus/v1/remote_write/prometheus_data" + basic_auth: + username: root + password: taosdata + +remote_read: + - url: "http://localhost:6041/prometheus/v1/remote_read/prometheus_data" + basic_auth: + username: root + password: taosdata + remote_timeout: 10s + read_recent: true +``` diff --git a/docs-cn/14-reference/_statsd.mdx b/docs-cn/14-reference/_statsd.mdx new file mode 100644 index 0000000000000000000000000000000000000000..b225c44267d7ae32e3845972f683f664e11e9cae --- /dev/null +++ b/docs-cn/14-reference/_statsd.mdx @@ -0,0 +1,55 @@ +### 配置 taosAdapter + +配置 taosAdapter 接收 StatsD 数据的方法: + +- 在 taosAdapter 配置文件(默认位置 /etc/taos/taosadapter.toml)中使能配置项 + +``` +... +[statsd] +enable = true +port = 6044 +db = "statsd" +user = "root" +password = "taosdata" +worker = 10 +gatherInterval = "5s" +protocol = "udp" +maxTCPConnections = 250 +tcpKeepAlive = false +allowPendingMessages = 50000 +deleteCounters = true +deleteGauges = true +deleteSets = true +deleteTimings = true +... +``` + +其中 taosAdapter 默认写入的数据库名称为 `statsd`,也可以修改 taosAdapter 配置文件 db 项来指定不同的名称。user 和 password 填写实际 TDengine 配置的值。修改过配置文件 taosAdapter 需重新启动。 + +- 也可以使用 taosAdapter 命令行参数或设置环境变量启动的方式,使能 taosAdapter 接收 StatsD 数据功能,具体细节请参考 [taosAdapter 的使用手册](/reference/taosadapter) + +### 配置 StatsD + +使用 StatsD 需要下载其[源代码](https://github.com/statsd/statsd)。其配置文件请参考其源代码下载到本地的根目录下的示例文件 `exampleConfig.js` 进行修改。其中 填写运行 taosAdapter 的服务器域名或 IP 地址,请填写 taosAdapter 接收 StatsD 数据的端口(默认为 6044)。 + +``` +backends 部分添加 "./backends/repeater" +repeater 部分添加 { host:'', port: } +``` + +示例配置文件: + +``` +{ +port: 8125 +, backends: ["./backends/repeater"] +, repeater: [{ host: '127.0.0.1', port: 6044}] +} +``` + +增加如下内容后启动 StatsD(假设配置文件修改为 config.js)。 + +``` +node stats.js config.js & +``` diff --git a/docs-cn/14-reference/_tcollector.mdx b/docs-cn/14-reference/_tcollector.mdx new file mode 100644 index 0000000000000000000000000000000000000000..8e51975b512d658e27a53b469d981e1c85a871dc --- /dev/null +++ b/docs-cn/14-reference/_tcollector.mdx @@ -0,0 +1,82 @@ + +### 配置 taosAdapter + +配置 taosAdapter 接收 TCollector 数据的方法: + +- 在 taosAdapter 配置文件(默认位置 /etc/taos/taosadapter.toml)中使能配置项 + +``` +... +[opentsdb_telnet] +enable = true +maxTCPConnections = 250 +tcpKeepAlive = false +dbs = ["opentsdb_telnet", "collectd", "icinga2", "tcollector"] +ports = [6046, 6047, 6048, 6049] +user = "root" +password = "taosdata" +... +``` + +其中 taosAdapter 默认写入的数据库名称为 `tcollector`,也可以修改 taosAdapter 配置文件 dbs 项来指定不同的名称。user 和 password 填写实际 TDengine 配置的值。修改过配置文件 taosAdapter 需重新启动。 + +- 也可以使用 taosAdapter 命令行参数或设置环境变量启动的方式,使能 taosAdapter 接收 tcollector 数据功能,具体细节请参考 [taosAdapter 的使用手册](/reference/taosadapter) + +### 配置 TCollector + +使用 TCollector 需下载其[源代码](https://github.com/OpenTSDB/tcollector)。其配置项在其源代码中。注意:TCollector 各个版本区别较大,这里仅以当前 master 分支最新代码 (git commit: 37ae920) 为例。 + +修改 `collectors/etc/config.py` 和 `tcollector.py` 两个文件中相应内容。将原指向 OpenTSDB 宿主机的地址修改为 taosAdapter 被部署的服务器域名或 IP 地址,修改端口为 taosAdapter 支持 TCollector 使用的相应端口(默认为 6049)。 + +示例为源代码修改内容的 git diff 输出: + +``` +index e7e7a1c..ec3e23c 100644 +--- a/collectors/etc/config.py ++++ b/collectors/etc/config.py +@@ -59,13 +59,13 @@ def get_defaults(): + 'http_password': False, + 'reconnectinterval': 0, + 'http_username': False, +- 'port': 4242, ++ 'port': 6049, + 'pidfile': '/var/run/tcollector.pid', + 'http': False, + 'http_api_path': "api/put", + 'tags': [], + 'remove_inactive_collectors': False, +- 'host': '', ++ 'host': '127.0.0.1', + 'logfile': '/var/log/tcollector.log', + 'cdir': default_cdir, + 'ssl': False, +diff --git a/tcollector.py b/tcollector.py +index 21f9b23..4c71ba2 100755 +--- a/tcollector.py ++++ b/tcollector.py +@@ -64,7 +64,7 @@ ALIVE = True + # exceptions, something is not right and tcollector will shutdown. + # Hopefully some kind of supervising daemon will then restart it. + MAX_UNCAUGHT_EXCEPTIONS = 100 +-DEFAULT_PORT = 4242 ++DEFAULT_PORT = 6049 + MAX_REASONABLE_TIMESTAMP = 2209212000 # Good until Tue 3 Jan 14:00:00 GMT 2040 + # How long to wait for datapoints before assuming + # a collector is dead and restarting it +@@ -943,13 +943,13 @@ def parse_cmdline(argv): + 'http_password': False, + 'reconnectinterval': 0, + 'http_username': False, +- 'port': 4242, ++ 'port': 6049, + 'pidfile': '/var/run/tcollector.pid', + 'http': False, + 'http_api_path': "api/put", + 'tags': [], + 'remove_inactive_collectors': False, +- 'host': '', ++ 'host': '127.0.0.1', + 'logfile': '/var/log/tcollector.log', + 'cdir': default_cdir, + 'ssl': False, +``` diff --git a/docs-cn/14-reference/_telegraf.mdx b/docs-cn/14-reference/_telegraf.mdx new file mode 100644 index 0000000000000000000000000000000000000000..bae46d66062ad7d5bfc2487900c02bd7a19ce4f6 --- /dev/null +++ b/docs-cn/14-reference/_telegraf.mdx @@ -0,0 +1,27 @@ + +在 Telegraf 配置文件(默认位置 /etc/telegraf/telegraf.conf) 增加 outputs.http 输出模块配置: + +``` +[[outputs.http]] + url = "http://:/influxdb/v1/write?db=" + ... + username = "" + password = "" + ... +``` + +其中 请填写运行 taosAdapter 服务的服务器域名或 IP 地址, 请填写 REST 服务的端口(默认为 6041), 请填写当前运行的 TDengine 实际配置, 请填写希望在 TDengine 保存 Telegraf 数据的数据库名。 + +示例如下: + +``` +[[outputs.http]] + url = "http://127.0.0.1:6041/influxdb/v1/write?db=telegraf" + method = "POST" + timeout = "5s" + username = "root" + password = "taosdata" + data_format = "influx" + influx_max_line_bytes = 250 +``` + diff --git a/docs-cn/14-reference/index.md b/docs-cn/14-reference/index.md new file mode 100644 index 0000000000000000000000000000000000000000..f48ce31fcefd2f0d875aee64e7d92490f4e92fcb --- /dev/null +++ b/docs-cn/14-reference/index.md @@ -0,0 +1,12 @@ +--- +title: 参考指南 +--- + +参考指南是对 TDengine 本身、 TDengine 各语言连接器及自带的工具最详细的介绍。 + +```mdx-code-block +import DocCardList from '@theme/DocCardList'; +import {useCurrentSidebarCategory} from '@docusaurus/theme-common'; + + +``` \ No newline at end of file diff --git a/docs-cn/14-reference/taosAdapter-architecture.png b/docs-cn/14-reference/taosAdapter-architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..08a9018553aae6f86b42d127b372d0cecfa9bdf8 Binary files /dev/null and b/docs-cn/14-reference/taosAdapter-architecture.png differ diff --git a/docs-cn/20-third-party/01-grafana.mdx b/docs-cn/20-third-party/01-grafana.mdx new file mode 100644 index 0000000000000000000000000000000000000000..9a4c33d8aceb086ff8ba8dca0f38b1bcbf762005 --- /dev/null +++ b/docs-cn/20-third-party/01-grafana.mdx @@ -0,0 +1,105 @@ +--- +sidebar_label: Grafana +title: Grafana +--- + +TDengine 能够与开源数据可视化系统 [Grafana](https://www.grafana.com/) 快速集成搭建数据监测报警系统,整个过程无需任何代码开发,TDengine 中数据表的内容可以在仪表盘(DashBoard)上进行可视化展现。关于 TDengine 插件的使用您可以在[GitHub](https://github.com/taosdata/grafanaplugin/blob/master/README.md)中了解更多。 + +## 前置条件 + +要让 Grafana 能正常添加 TDengine 数据源,需要以下几方面的准备工作。 + +- TDengine 集群已经部署并正常运行 +- taosAdapter 已经安装并正常运行。具体细节请参考 [taosAdapter 的使用手册](/reference/taosadapter) + +## 安装 Grafana + +目前 TDengine 支持 Grafana 7.0 以上的版本。用户可以根据当前的操作系统,到 Grafana 官网下载安装包,并执行安装。下载地址如下:。 + +## 配置 Grafana + +TDengine 的 Grafana 插件托管在 GitHub,可从 下载,当前最新版本为 3.1.4。 + +推荐使用 [`grafana-cli` 命令行工具](https://grafana.com/docs/grafana/latest/administration/cli/) 进行插件安装。 + +```bash +sudo -u grafana grafana-cli \ + --pluginUrl https://github.com/taosdata/grafanaplugin/releases/download/v3.1.7/tdengine-datasource-3.1.7.zip \ + plugins install tdengine-datasource +``` + +或者下载到本地并解压到 Grafana 插件目录。 + +```bash +GF_VERSION=3.1.7 +wget https://github.com/taosdata/grafanaplugin/releases/download/v$GF_VERSION/tdengine-datasource-$GF_VERSION.zip +``` + +以 CentOS 7.2 操作系统为例,将插件包解压到 /var/lib/grafana/plugins 目录下,重新启动 grafana 即可。 + +```bash +sudo unzip tdengine-datasource-$GF_VERSION.zip -d /var/lib/grafana/plugins/ +``` + +:::note +3.1.6 和更早版本未签名,会在 Grafana 7.3+ / 8.x 版本签名检查时失败导致无法加载插件,需要在 grafana.ini 文件中修改配置如下: + +```ini +[plugins] +allow_loading_unsigned_plugins = tdengine-datasource +``` + +::: + +在 Docker 环境下,可以使用如下的环境变量设置自动安装并设置 TDengine 插件: + +```bash +GF_INSTALL_PLUGINS=https://github.com/taosdata/grafanaplugin/releases/download/v3.1.4/tdengine-datasource-3.1.4.zip;tdengine-datasource +GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS=tdengine-datasource +``` + +## 使用 Grafana + +### 配置数据源 + +用户可以直接通过 http://localhost:3000 的网址,登录 Grafana 服务器(用户名/密码:admin/admin),通过左侧 `Configuration -> Data Sources` 可以添加数据源,如下图所示: + +![img](/img/connections/add_datasource1.jpg) + +点击 `Add data source` 可进入新增数据源页面,在查询框中输入 TDengine 可选择添加,如下图所示: + +![img](/img/connections/add_datasource2.jpg) + +进入数据源配置页面,按照默认提示修改相应配置即可: + +![img](/img/connections/add_datasource3.jpg) + +- Host: TDengine 集群中提供 REST 服务 (在 2.4 之前由 taosd 提供, 从 2.4 开始由 taosAdapter 提供)的组件所在服务器的 IP 地址与 TDengine REST 服务的端口号(6041),默认 http://localhost:6041。 +- User:TDengine 用户名。 +- Password:TDengine 用户密码。 + +点击 `Save & Test` 进行测试,成功会有如下提示: + +![img](/img/connections/add_datasource4.jpg) + +### 创建 Dashboard + +回到主界面创建 Dashboard,点击 Add Query 进入面板查询页面: + +![img](/img/connections/create_dashboard1.jpg) + +如上图所示,在 Query 中选中 `TDengine` 数据源,在下方查询框可输入相应 SQL 进行查询,具体说明如下: + +- INPUT SQL:输入要查询的语句(该 SQL 语句的结果集应为两列多行),例如:`select avg(mem_system) from log.dn where ts >= $from and ts < $to interval($interval)` ,其中,from、to 和 interval 为 TDengine 插件的内置变量,表示从 Grafana 插件面板获取的查询范围和时间间隔。除了内置变量外,`也支持可以使用自定义模板变量`。 +- ALIAS BY:可设置当前查询别名。 +- GENERATE SQL: 点击该按钮会自动替换相应变量,并生成最终执行的语句。 + +按照默认提示查询当前 TDengine 部署所在服务器指定间隔系统内存平均使用量如下: + +![img](/img/connections/create_dashboard2.jpg) + +> 关于如何使用 Grafana 创建相应的监测界面以及更多有关使用 Grafana 的信息,请参考 Grafana 官方的[文档](https://grafana.com/docs/)。 + +### 导入 Dashboard + +在 2.3.3.0 及以上版本,您可以导入 TDinsight Dashboard (Grafana Dashboard ID: [15167](https://grafana.com/grafana/dashboards/15167)) 作为 TDengine 集群的监控可视化工具。安装和使用说明请见 [TDinsight 用户手册](/reference/tdinsight/)。 diff --git a/docs-cn/20-third-party/02-prometheus.md b/docs-cn/20-third-party/02-prometheus.md new file mode 100644 index 0000000000000000000000000000000000000000..0fe534b8df263064e5269e1732b69893efd7a79a --- /dev/null +++ b/docs-cn/20-third-party/02-prometheus.md @@ -0,0 +1,89 @@ +--- +sidebar_label: Prometheus +title: Prometheus +--- + +import Prometheus from "../14-reference/_prometheus.mdx" + +Prometheus 是一款流行的开源监控告警系统。Prometheus 于2016年加入了 Cloud Native Computing Foundation (云原生云计算基金会,简称 CNCF),成为继 Kubernetes 之后的第二个托管项目,该项目拥有非常活跃的开发人员和用户社区。 + +Prometheus 提供了 `remote_write` 和 `remote_read` 接口来利用其它数据库产品作为它的存储引擎。为了让 Prometheus 生态圈的用户能够利用 TDengine 的高效写入和查询,TDengine 也提供了对这两个接口的支持。 + +通过适当的配置, Prometheus 的数据可以通过 `remote_write` 接口存储到 TDengine 中,也可以通过 `remote_read` 接口来查询存储在 TDengine 中的数据,充分利用 TDengine 对时序数据的高效存储查询性能和集群处理能力。 + +## 前置条件 + +要将 Prometheus 数据写入 TDengine 需要以下几方面的准备工作。 +- TDengine 集群已经部署并正常运行 +- taosAdapter 已经安装并正常运行。具体细节请参考 [taosAdapter 的使用手册](/reference/taosadapter) +- Prometheus 已经安装。安装 Prometheus 请参考[官方文档](https://prometheus.io/docs/prometheus/latest/installation/) + +## 配置步骤 + + +## 验证方法 + +重启 Prometheus 后可参考以下示例验证从 Prometheus 向 TDengine 写入数据并能够正确读出。 + +### 使用 TDengine CLI 查询写入数据 +``` +taos> show databases; + name | created_time | ntables | vgroups | replica | quorum | days | keep | cache(MB) | blocks | minrows | maxrows | wallevel | fsync | comp | cachelast | precision | update | status | +==================================================================================================================================================================================================================================================================================== + test | 2022-04-12 08:07:58.756 | 1 | 1 | 1 | 1 | 10 | 3650 | 16 | 6 | 100 | 4096 | 1 | 3000 | 2 | 0 | ms | 0 | ready | + log | 2022-04-20 07:19:50.260 | 2 | 1 | 1 | 1 | 10 | 3650 | 16 | 6 | 100 | 4096 | 1 | 3000 | 2 | 0 | ms | 0 | ready | + prometheus_data | 2022-04-20 07:21:09.202 | 158 | 1 | 1 | 1 | 10 | 3650 | 16 | 6 | 100 | 4096 | 1 | 3000 | 2 | 0 | ns | 2 | ready | + db | 2022-04-15 06:37:08.512 | 1 | 1 | 1 | 1 | 10 | 3650 | 16 | 6 | 100 | 4096 | 1 | 3000 | 2 | 0 | ms | 0 | ready | +Query OK, 4 row(s) in set (0.000585s) + +taos> use prometheus_data; +Database changed. + +taos> show stables; + name | created_time | columns | tags | tables | +============================================================================================ + metrics | 2022-04-20 07:21:09.209 | 2 | 1 | 1389 | +Query OK, 1 row(s) in set (0.000487s) + +taos> select * from metrics limit 10; + ts | value | labels | +============================================================================================= + 2022-04-20 07:21:09.193000000 | 0.000024996 | {"__name__":"go_gc_duration... | + 2022-04-20 07:21:14.193000000 | 0.000024996 | {"__name__":"go_gc_duration... | + 2022-04-20 07:21:19.193000000 | 0.000024996 | {"__name__":"go_gc_duration... | + 2022-04-20 07:21:24.193000000 | 0.000024996 | {"__name__":"go_gc_duration... | + 2022-04-20 07:21:29.193000000 | 0.000024996 | {"__name__":"go_gc_duration... | + 2022-04-20 07:21:09.193000000 | 0.000054249 | {"__name__":"go_gc_duration... | + 2022-04-20 07:21:14.193000000 | 0.000054249 | {"__name__":"go_gc_duration... | + 2022-04-20 07:21:19.193000000 | 0.000054249 | {"__name__":"go_gc_duration... | + 2022-04-20 07:21:24.193000000 | 0.000054249 | {"__name__":"go_gc_duration... | + 2022-04-20 07:21:29.193000000 | 0.000054249 | {"__name__":"go_gc_duration... | +Query OK, 10 row(s) in set (0.011146s) +``` + +### 使用 promql-cli 通过 remote_read 从 TDengine 读取数据 + +安装 promql-cli + +``` + go install github.com/nalbury/promql-cli@latest +``` + +在 TDengine 和 taosAdapter 服务运行状态对 Prometheus 数据进行查询 + +``` +ubuntu@shuduo-1804 ~ $ promql-cli --host "http://127.0.0.1:9090" "sum(up) by (job)" +JOB VALUE TIMESTAMP +prometheus 1 2022-04-20T08:05:26Z +node 1 2022-04-20T08:05:26Z +``` + +暂停 taosAdapter 服务后对 Prometheus 数据进行查询 + +``` +ubuntu@shuduo-1804 ~ $ sudo systemctl stop taosadapter.service +ubuntu@shuduo-1804 ~ $ promql-cli --host "http://127.0.0.1:9090" "sum(up) by (job)" +VALUE TIMESTAMP + +``` + diff --git a/docs-cn/20-third-party/03-telegraf.md b/docs-cn/20-third-party/03-telegraf.md new file mode 100644 index 0000000000000000000000000000000000000000..88a69211c0592940d7f75d34c03bcc0593cd74d6 --- /dev/null +++ b/docs-cn/20-third-party/03-telegraf.md @@ -0,0 +1,67 @@ +--- +sidebar_label: Telegraf +title: Telegraf 写入 +--- + +import Telegraf from "../14-reference/_telegraf.mdx" + +Telegraf 是一款十分流行的指标采集开源软件。在数据采集和平台监控系统中,Telegraf 可以采集多种组件的运行信息,而不需要自己手写脚本定时采集,降低数据获取的难度。 + +只需要将 Telegraf 的输出配置增加指向 taosAdapter 对应的 url 并修改若干配置项即可将 Telegraf 的数据写入到 TDengine 中。将 Telegraf 的数据存在到 TDengine 中可以充分利用 TDengine 对时序数据的高效存储查询性能和集群处理能力。 + +## 前置条件 + +要将 Telegraf 数据写入 TDengine 需要以下几方面的准备工作。 +- TDengine 集群已经部署并正常运行 +- taosAdapter 已经安装并正常运行。具体细节请参考 [taosAdapter 的使用手册](/reference/taosadapter) +- Telegraf 已经安装。安装 Telegraf 请参考[官方文档](https://docs.influxdata.com/telegraf/v1.22/install/) + +## 配置步骤 + + +## 验证方法 + +重启 Telegraf 服务: + +``` +sudo systemctl restart telegraf +``` + +使用 TDengine CLI 验证从 Telegraf 向 TDengine 写入数据并能够正确读出: + +``` +taos> show databases; + name | created_time | ntables | vgroups | replica | quorum | days | keep | cache(MB) | blocks | minrows | maxrows | wallevel | fsync | comp | cachelast | precision | update | status | +==================================================================================================================================================================================================================================================================================== + telegraf | 2022-04-20 08:47:53.488 | 22 | 1 | 1 | 1 | 10 | 3650 | 16 | 6 | 100 | 4096 | 1 | 3000 | 2 | 0 | ns | 2 | ready | + log | 2022-04-20 07:19:50.260 | 9 | 1 | 1 | 1 | 10 | 3650 | 16 | 6 | 100 | 4096 | 1 | 3000 | 2 | 0 | ms | 0 | ready | +Query OK, 2 row(s) in set (0.002401s) + +taos> use telegraf; +Database changed. + +taos> show stables; + name | created_time | columns | tags | tables | +============================================================================================ + swap | 2022-04-20 08:47:53.532 | 7 | 1 | 1 | + cpu | 2022-04-20 08:48:03.488 | 11 | 2 | 5 | + system | 2022-04-20 08:47:53.512 | 8 | 1 | 1 | + diskio | 2022-04-20 08:47:53.550 | 12 | 2 | 15 | + kernel | 2022-04-20 08:47:53.503 | 6 | 1 | 1 | + mem | 2022-04-20 08:47:53.521 | 35 | 1 | 1 | + processes | 2022-04-20 08:47:53.555 | 12 | 1 | 1 | + disk | 2022-04-20 08:47:53.541 | 8 | 5 | 2 | +Query OK, 8 row(s) in set (0.000521s) + +taos> select * from telegraf.system limit 10; + ts | load1 | load5 | load15 | n_cpus | n_users | uptime | uptime_format | host +| +============================================================================================================================================================================================================================================= + 2022-04-20 08:47:50.000000000 | 0.000000000 | 0.050000000 | 0.070000000 | 4 | 1 | 5533 | 1:32 | shuduo-1804 +| + 2022-04-20 08:48:00.000000000 | 0.000000000 | 0.050000000 | 0.070000000 | 4 | 1 | 5543 | 1:32 | shuduo-1804 +| + 2022-04-20 08:48:10.000000000 | 0.000000000 | 0.040000000 | 0.070000000 | 4 | 1 | 5553 | 1:32 | shuduo-1804 +| +Query OK, 3 row(s) in set (0.013269s) +``` diff --git a/docs-cn/20-third-party/05-collectd.md b/docs-cn/20-third-party/05-collectd.md new file mode 100644 index 0000000000000000000000000000000000000000..04892fd42e92e962fcccadf626f67c432e78d286 --- /dev/null +++ b/docs-cn/20-third-party/05-collectd.md @@ -0,0 +1,73 @@ +--- +sidebar_label: collectd +title: collectd 写入 +--- + +import CollectD from "../14-reference/_collectd.mdx" + +collectd 是一个用来收集系统性能的守护进程。collectd 提供各种存储方式来存储不同值的机制。它会在系统运行和存储信息时周期性的统计系统的相关统计信息。利用这些信息有助于查找当前系统性能瓶颈和预测系统未来的负载等。 + +只需要将 collectd 的配置指向运行 taosAdapter 的服务器域名(或 IP 地址)和相应端口即可将 collectd 采集的数据写入到 TDengine,可以充分利用 TDengine 对时序数据的高效存储查询性能和集群处理能力。 + +## 前置条件 + +要将 collectd 数据写入 TDengine,需要几方面的准备工作。 +- TDengine 集群已经部署并正常运行 +- taosAdapter 已经安装并正常运行,具体细节请参考[ taosAdapter 的使用手册](/reference/taosadapter) +- collectd 已经安装。安装 collectd 请参考[官方文档](https://collectd.org/download.shtml) + +## 配置步骤 + + +## 验证方法 + +重启 collectd + +``` +sudo systemctl restart collectd +``` + +使用 TDengine CLI 验证从 collectd 向 TDengine 写入数据并能够正确读出: + +``` +taos> show databases; + name | created_time | ntables | vgroups | replica | quorum | days | keep | cache(MB) | blocks | minrows | maxrows | wallevel | fsync | comp | cachelast | precision | update | status | +==================================================================================================================================================================================================================================================================================== + collectd | 2022-04-20 09:27:45.460 | 95 | 1 | 1 | 1 | 10 | 3650 | 16 | 6 | 100 | 4096 | 1 | 3000 | 2 | 0 | ns | 2 | ready | + log | 2022-04-20 07:19:50.260 | 11 | 1 | 1 | 1 | 10 | 3650 | 16 | 6 | 100 | 4096 | 1 | 3000 | 2 | 0 | ms | 0 | ready | +Query OK, 2 row(s) in set (0.003266s) + +taos> use collectd; +Database changed. + +taos> show stables; + name | created_time | columns | tags | tables | +============================================================================================ + load_1 | 2022-04-20 09:27:45.492 | 2 | 2 | 1 | + memory_value | 2022-04-20 09:27:45.463 | 2 | 3 | 6 | + df_value | 2022-04-20 09:27:45.463 | 2 | 4 | 25 | + load_2 | 2022-04-20 09:27:45.501 | 2 | 2 | 1 | + load_0 | 2022-04-20 09:27:45.485 | 2 | 2 | 1 | + interface_1 | 2022-04-20 09:27:45.488 | 2 | 3 | 12 | + irq_value | 2022-04-20 09:27:45.476 | 2 | 3 | 31 | + interface_0 | 2022-04-20 09:27:45.480 | 2 | 3 | 12 | + entropy_value | 2022-04-20 09:27:45.473 | 2 | 2 | 1 | + swap_value | 2022-04-20 09:27:45.477 | 2 | 3 | 5 | +Query OK, 10 row(s) in set (0.002236s) + +taos> select * from collectd.memory_value limit 10; + ts | value | host | type_instance | type | +========================================================================================================================================================= + 2022-04-20 09:27:45.459653462 | 54689792.000000000 | shuduo-1804 | buffered | memory | + 2022-04-20 09:27:55.453168283 | 57212928.000000000 | shuduo-1804 | buffered | memory | + 2022-04-20 09:28:05.453004291 | 57942016.000000000 | shuduo-1804 | buffered | memory | + 2022-04-20 09:27:45.459653462 | 6381330432.000000000 | shuduo-1804 | free | memory | + 2022-04-20 09:27:55.453168283 | 6357643264.000000000 | shuduo-1804 | free | memory | + 2022-04-20 09:28:05.453004291 | 6349987840.000000000 | shuduo-1804 | free | memory | + 2022-04-20 09:27:45.459653462 | 107040768.000000000 | shuduo-1804 | slab_recl | memory | + 2022-04-20 09:27:55.453168283 | 107536384.000000000 | shuduo-1804 | slab_recl | memory | + 2022-04-20 09:28:05.453004291 | 107634688.000000000 | shuduo-1804 | slab_recl | memory | + 2022-04-20 09:27:45.459653462 | 309137408.000000000 | shuduo-1804 | used | memory | +Query OK, 10 row(s) in set (0.010348s) +``` + diff --git a/docs-cn/20-third-party/06-statsd.md b/docs-cn/20-third-party/06-statsd.md new file mode 100644 index 0000000000000000000000000000000000000000..bcbd6c42ae5dd1c83be428797544d254e11a0238 --- /dev/null +++ b/docs-cn/20-third-party/06-statsd.md @@ -0,0 +1,68 @@ +--- +sidebar_label: StatsD +title: StatsD 直接写入 +--- + +import StatsD from "../14-reference/_statsd.mdx" + +StatsD 是汇总和总结应用指标的一个简单的守护进程,近些年来发展迅速,已经变成了一个用于收集应用性能指标的统一的协议。 + +只需要在 StatsD 的配置文件中填写运行 taosAdapter 的服务器域名(或 IP 地址)和相应端口即可将 StatsD 的数据写入到 TDengine 中,可以充分利用 TDengine 对时序数据的高效存储查询性能和集群处理能力。 + +## 前置条件 + +要将 StatsD 数据写入 TDengine 需要以下几方面的准备工作。 +- TDengine 集群已经部署并正常运行 +- taosAdapter 已经安装并正常运行。具体细节请参考 [taosAdapter 的使用手册](/reference/taosadapter) +- StatsD 已经安装。安装 StatsD 请参考[官方文档](https://github.com/statsd/statsd) + +## 配置步骤 + + +## 验证方法 + +运行 StatsD: + +``` +$ node stats.js config.js & +[1] 8546 +$ 20 Apr 09:54:41 - [8546] reading config file: exampleConfig.js +20 Apr 09:54:41 - server is up INFO +``` + +使用 nc 写入测试数据: + +``` +$ echo "foo:1|c" | nc -u -w0 127.0.0.1 8125 +``` + +使用 TDengine CLI 验证从 StatsD 向 TDengine 写入数据并能够正确读出: + +``` +Welcome to the TDengine shell from Linux, Client Version:2.4.0.0 +Copyright (c) 2020 by TAOS Data, Inc. All rights reserved. + +taos> show databases; + name | created_time | ntables | vgroups | replica | quorum | days | keep | cache(MB) | blocks | minrows | maxrows | wallevel | fsync | comp | cachelast | precision | update | status | +==================================================================================================================================================================================================================================================================================== + log | 2022-04-20 07:19:50.260 | 11 | 1 | 1 | 1 | 10 | 3650 | 16 | 6 | 100 | 4096 | 1 | 3000 | 2 | 0 | ms | 0 | ready | + statsd | 2022-04-20 09:54:51.220 | 1 | 1 | 1 | 1 | 10 | 3650 | 16 | 6 | 100 | 4096 | 1 | 3000 | 2 | 0 | ns | 2 | ready | +Query OK, 2 row(s) in set (0.003142s) + +taos> use statsd; +Database changed. + +taos> show stables; + name | created_time | columns | tags | tables | +============================================================================================ + foo | 2022-04-20 09:54:51.234 | 2 | 1 | 1 | +Query OK, 1 row(s) in set (0.002161s) + +taos> select * from foo; + ts | value | metric_type | +======================================================================================= + 2022-04-20 09:54:51.219614235 | 1 | counter | +Query OK, 1 row(s) in set (0.004179s) + +taos> +``` diff --git a/docs-cn/20-third-party/07-icinga2.md b/docs-cn/20-third-party/07-icinga2.md new file mode 100644 index 0000000000000000000000000000000000000000..ed1f1404a730eca5f51e2ff9bbcd54949018f8ea --- /dev/null +++ b/docs-cn/20-third-party/07-icinga2.md @@ -0,0 +1,74 @@ +--- +sidebar_label: icinga2 +title: icinga2 写入 +--- + +import Icinga2 from "../14-reference/_icinga2.mdx" + +icinga2 是一款开源主机、网络监控软件,最初由 Nagios 网络监控应用发展而来。目前,icinga2 遵从 GNU GPL v2 许可协议发行。 + +只需要将 icinga2 的配置修改指向 taosAdapter 对应的服务器和相应端口即可将 icinga2 采集的数据存在到 TDengine 中,可以充分利用 TDengine 对时序数据的高效存储查询性能和集群处理能力。 + +## 前置条件 + +要将 icinga2 数据写入 TDengine 需要以下几方面的准备工作。 +- TDengine 集群已经部署并正常运行 +- taosAdapter 已经安装并正常运行。具体细节请参考[ taosAdapter 的使用手册](/reference/taosadapter) +- icinga2 已经安装。安装 icinga2 请参考[官方文档](https://icinga.com/docs/icinga-2/latest/doc/02-installation/) + +## 配置步骤 + + +## 验证方法 + +重启 taosAdapter: +``` +sudo systemctl restart taosadapter +``` + +重启 icinga2: + +``` +sudo systemctl restart icinga2 +``` + +等待 10 秒左右后,使用 TDengine CLI 查询 TDengine 验证是否创建相应数据库并写入数据: + +``` +taos> show databases; + name | created_time | ntables | vgroups | replica | quorum | days | keep | cache(MB) | blocks | minrows | maxrows | wallevel | fsync | comp | cachelast | precision | update | status | +==================================================================================================================================================================================================================================================================================== + log | 2022-04-20 07:19:50.260 | 11 | 1 | 1 | 1 | 10 | 3650 | 16 | 6 | 100 | 4096 | 1 | 3000 | 2 | 0 | ms | 0 | ready | + icinga2 | 2022-04-20 12:11:39.697 | 13 | 1 | 1 | 1 | 10 | 3650 | 16 | 6 | 100 | 4096 | 1 | 3000 | 2 | 0 | ns | 2 | ready | +Query OK, 2 row(s) in set (0.001867s) + +taos> use icinga2; +Database changed. + +taos> show stables; + name | created_time | columns | tags | tables | +============================================================================================ + icinga.service.users.state_... | 2022-04-20 12:11:39.726 | 2 | 1 | 1 | + icinga.service.users.acknow... | 2022-04-20 12:11:39.756 | 2 | 1 | 1 | + icinga.service.procs.downti... | 2022-04-20 12:11:44.541 | 2 | 1 | 1 | + icinga.service.users.users | 2022-04-20 12:11:39.770 | 2 | 1 | 1 | + icinga.service.procs.procs_min | 2022-04-20 12:11:44.599 | 2 | 1 | 1 | + icinga.service.users.users_min | 2022-04-20 12:11:39.809 | 2 | 1 | 1 | + icinga.check.max_check_atte... | 2022-04-20 12:11:39.847 | 2 | 3 | 2 | + icinga.service.procs.state_... | 2022-04-20 12:11:44.522 | 2 | 1 | 1 | + icinga.service.procs.procs_... | 2022-04-20 12:11:44.576 | 2 | 1 | 1 | + icinga.service.users.users_... | 2022-04-20 12:11:39.796 | 2 | 1 | 1 | + icinga.check.latency | 2022-04-20 12:11:39.869 | 2 | 3 | 2 | + icinga.service.procs.procs_... | 2022-04-20 12:11:44.588 | 2 | 1 | 1 | + icinga.service.users.downti... | 2022-04-20 12:11:39.746 | 2 | 1 | 1 | + icinga.service.users.users_... | 2022-04-20 12:11:39.783 | 2 | 1 | 1 | + icinga.service.users.reachable | 2022-04-20 12:11:39.736 | 2 | 1 | 1 | + icinga.service.procs.procs | 2022-04-20 12:11:44.565 | 2 | 1 | 1 | + icinga.service.procs.acknow... | 2022-04-20 12:11:44.554 | 2 | 1 | 1 | + icinga.service.procs.state | 2022-04-20 12:11:44.509 | 2 | 1 | 1 | + icinga.service.procs.reachable | 2022-04-20 12:11:44.532 | 2 | 1 | 1 | + icinga.check.current_attempt | 2022-04-20 12:11:39.825 | 2 | 3 | 2 | + icinga.check.execution_time | 2022-04-20 12:11:39.898 | 2 | 3 | 2 | + icinga.service.users.state | 2022-04-20 12:11:39.704 | 2 | 1 | 1 | +Query OK, 22 row(s) in set (0.002317s) +``` diff --git a/docs-cn/20-third-party/08-tcollector.md b/docs-cn/20-third-party/08-tcollector.md new file mode 100644 index 0000000000000000000000000000000000000000..a1245e8c27f302d56f88fa382b5f38f9bd49a0aa --- /dev/null +++ b/docs-cn/20-third-party/08-tcollector.md @@ -0,0 +1,67 @@ +--- +sidebar_label: TCollector +title: TCollector 写入 +--- + +import TCollector from "../14-reference/_tcollector.mdx" + +TCollector 是 openTSDB 的一部分,它用来采集客户端日志发送给数据库。 + +只需要将 TCollector 的配置修改指向运行 taosAdapter 的服务器域名(或 IP 地址)和相应端口即可将 TCollector 采集的数据存在到 TDengine 中,可以充分利用 TDengine 对时序数据的高效存储查询性能和集群处理能力。 + +## 前置条件 + +要将 TCollector 数据写入 TDengine 需要以下几方面的准备工作。 +- TDengine 集群已经部署并正常运行 +- taosAdapter 已经安装并正常运行。具体细节请参考 [taosAdapter 的使用手册](/reference/taosadapter) +- TCollector 已经安装。安装 TCollector 请参考[官方文档](http://opentsdb.net/docs/build/html/user_guide/utilities/tcollector.html#installation-of-tcollector) + +## 配置步骤 + + +## 验证方法 + +重启 taosAdapter: + +``` +sudo systemctl restart taosadapter +``` + +手动执行 `sudo ./tcollector.py` + +等待数秒后使用 TDengine CLI 查询 TDengine 是否创建相应数据库并写入数据。 + +``` +taos> show databases; + name | created_time | ntables | vgroups | replica | quorum | days | keep | cache(MB) | blocks | minrows | maxrows | wallevel | fsync | comp | cachelast | precision | update | status | +==================================================================================================================================================================================================================================================================================== + tcollector | 2022-04-20 12:44:49.604 | 88 | 1 | 1 | 1 | 10 | 3650 | 16 | 6 | 100 | 4096 | 1 | 3000 | 2 | 0 | ns | 2 | ready | + log | 2022-04-20 07:19:50.260 | 11 | 1 | 1 | 1 | 10 | 3650 | 16 | 6 | 100 | 4096 | 1 | 3000 | 2 | 0 | ms | 0 | ready | +Query OK, 2 row(s) in set (0.002679s) + +taos> use tcollector; +Database changed. + +taos> show stables; + name | created_time | columns | tags | tables | +============================================================================================ + proc.meminfo.hugepages_rsvd | 2022-04-20 12:44:53.945 | 2 | 1 | 1 | + proc.meminfo.directmap1g | 2022-04-20 12:44:54.110 | 2 | 1 | 1 | + proc.meminfo.vmallocchunk | 2022-04-20 12:44:53.724 | 2 | 1 | 1 | + proc.meminfo.hugepagesize | 2022-04-20 12:44:54.004 | 2 | 1 | 1 | + tcollector.reader.lines_dro... | 2022-04-20 12:44:49.675 | 2 | 1 | 1 | + proc.meminfo.sunreclaim | 2022-04-20 12:44:53.437 | 2 | 1 | 1 | + proc.stat.ctxt | 2022-04-20 12:44:55.363 | 2 | 1 | 1 | + proc.meminfo.swaptotal | 2022-04-20 12:44:53.158 | 2 | 1 | 1 | + proc.uptime.total | 2022-04-20 12:44:52.813 | 2 | 1 | 1 | + tcollector.collector.lines_... | 2022-04-20 12:44:49.895 | 2 | 2 | 51 | + proc.meminfo.vmallocused | 2022-04-20 12:44:53.704 | 2 | 1 | 1 | + proc.meminfo.memavailable | 2022-04-20 12:44:52.939 | 2 | 1 | 1 | + sys.numa.foreign_allocs | 2022-04-20 12:44:57.929 | 2 | 2 | 1 | + proc.meminfo.committed_as | 2022-04-20 12:44:53.639 | 2 | 1 | 1 | + proc.vmstat.pswpin | 2022-04-20 12:44:54.177 | 2 | 1 | 1 | + proc.meminfo.cmafree | 2022-04-20 12:44:53.865 | 2 | 1 | 1 | + proc.meminfo.mapped | 2022-04-20 12:44:53.349 | 2 | 1 | 1 | + proc.vmstat.pgmajfault | 2022-04-20 12:44:54.251 | 2 | 1 | 1 | +... +``` diff --git a/docs-cn/20-third-party/09-emq-broker.md b/docs-cn/20-third-party/09-emq-broker.md new file mode 100644 index 0000000000000000000000000000000000000000..f57ccb20e6517c51b55093d11fa767bef7d0c9a8 --- /dev/null +++ b/docs-cn/20-third-party/09-emq-broker.md @@ -0,0 +1,192 @@ +--- +sidebar_label: EMQX Broker +title: EMQX Broker 写入 +--- + +MQTT 是流行的物联网数据传输协议,[EMQX](https://github.com/emqx/emqx)是一开源的 MQTT Broker 软件,无需任何代码,只需要在 EMQX Dashboard 里使用“规则”做简单配置,即可将 MQTT 的数据直接写入 TDengine。EMQX 支持通过 发送到 Web 服务的方式保存数据到 TDengine,也在企业版上提供原生的 TDengine 驱动实现直接保存。 + +## 前置条件 + +要让 EMQX 能正常添加 TDengine 数据源,需要以下几方面的准备工作。 +- TDengine 集群已经部署并正常运行 +- taosAdapter 已经安装并正常运行。具体细节请参考 [taosAdapter 的使用手册](/reference/taosadapter) +- 如果使用后文介绍的模拟写入程序,需要安装合适版本的 Node.js,推荐安装 v12。 + +## 安装并启动 EMQX + +用户可以根据当前的操作系统,到 EMQX 官网下载安装包,并执行安装。下载地址如下:。安装后使用 `sudo emqx start` 或 `sudo systemctl start emqx` 启动 EMQX 服务。 + +## 在 TDengine 中为接收 MQTT 数据创建相应数据库和表结构 + +### 以 Docker 安装 TDengine 为例 + +```bash + docker exec -it tdengine bash + taos +``` + +### 创建数据库和表 + +```sql + create database test; + use test; + create table: + + CREATE TABLE sensor_data (ts timestamp, temperature float, humidity float, volume float, PM10 float, pm25 float, SO2 float, NO2 float, CO float, sensor_id NCHAR(255), area TINYINT, coll_time timestamp); +``` + +注:表结构以博客[数据传输、存储、展现,EMQX + TDengine 搭建 MQTT 物联网数据可视化平台](https://www.taosdata.com/blog/2020/08/04/1722.html)为例。后续操作均以此博客场景为例进行,请你根据实际应用场景进行修改。 + +## 配置 EMQX 规则 + +由于 EMQX 不同版本配置界面所有不同,这里仅以 v4.4.3 为例,其他版本请参考相应官网文档。 + +### 登录 EMQX Dashboard + +使用浏览器打开网址 http://IP:18083 并登录 EMQX Dashboard。初次安装用户名为 `admin` 密码为:`public` + +![img](./emqx/login-dashboard.png) + +### 创建规则(Rule) + +选择左侧“规则引擎(Rule Engine)”中的“规则(Rule)”并点击“创建(Create)”按钮: + +![img](./emqx/rule-engine.png) + +### 编辑 SQL 字段 + +![img](./emqx/create-rule.png) + +### 新增“动作(action handler)” + +![img](./emqx/add-action-handler.png) + +### 新增“资源(Resource)” + +![img](./emqx/create-resource.png) + +选择“发送数据到 Web 服务“并点击“新建资源”按钮: + +### 编辑“资源(Resource)” + +选择“发送数据到 Web 服务“并填写 请求 URL 为 运行 taosAdapter 的服务器地址和端口(默认为 6041)。其他属性请保持默认值。 + +![img](./emqx/edit-resource.png) + +### 编辑“动作(action)” + +编辑资源配置,增加 Authorization 认证的键/值配对项,相关文档请参考[ TDengine REST API 文档](https://docs.taosdata.com/reference/rest-api/)。在消息体中输入规则引擎替换模板。 + +![img](./emqx/edit-action.png) + +## 编写模拟测试程序 + +```javascript + // mock.js + const mqtt = require('mqtt') + const Mock = require('mockjs') + const EMQX_SERVER = 'mqtt://localhost:1883' + const CLIENT_NUM = 10 + const STEP = 5000 // 模拟采集时间间隔 ms + const AWAIT = 5000 // 每次发送完后休眠时间,防止消息速率过快 ms + const CLIENT_POOL = [] + startMock() + function sleep(timer = 100) { + return new Promise(resolve => { + setTimeout(resolve, timer) + }) + } + async function startMock() { + const now = Date.now() + for (let i = 0; i < CLIENT_NUM; i++) { + const client = await createClient(`mock_client_${i}`) + CLIENT_POOL.push(client) + } + // last 24h every 5s + const last = 24 * 3600 * 1000 + for (let ts = now - last; ts <= now; ts += STEP) { + for (const client of CLIENT_POOL) { + const mockData = generateMockData() + const data = { + ...mockData, + id: client.clientId, + area: 0, + ts, + } + client.publish('sensor/data', JSON.stringify(data)) + } + const dateStr = new Date(ts).toLocaleTimeString() + console.log(`${dateStr} send success.`) + await sleep(AWAIT) + } + console.log(`Done, use ${(Date.now() - now) / 1000}s`) + } + /** + * Init a virtual mqtt client + * @param {string} clientId ClientID + */ + function createClient(clientId) { + return new Promise((resolve, reject) => { + const client = mqtt.connect(EMQX_SERVER, { + clientId, + }) + client.on('connect', () => { + console.log(`client ${clientId} connected`) + resolve(client) + }) + client.on('reconnect', () => { + console.log('reconnect') + }) + client.on('error', (e) => { + console.error(e) + reject(e) + }) + }) + } + /** + * Generate mock data + */ + function generateMockData() { + return { + "temperature": parseFloat(Mock.Random.float(22, 100).toFixed(2)), + "humidity": parseFloat(Mock.Random.float(12, 86).toFixed(2)), + "volume": parseFloat(Mock.Random.float(20, 200).toFixed(2)), + "PM10": parseFloat(Mock.Random.float(0, 300).toFixed(2)), + "pm25": parseFloat(Mock.Random.float(0, 300).toFixed(2)), + "SO2": parseFloat(Mock.Random.float(0, 50).toFixed(2)), + "NO2": parseFloat(Mock.Random.float(0, 50).toFixed(2)), + "CO": parseFloat(Mock.Random.float(0, 50).toFixed(2)), + "area": Mock.Random.integer(0, 20), + "ts": 1596157444170, + } + } +``` + +注意:代码中 CLIENT_NUM 在开始测试中可以先设置一个较小的值,避免硬件性能不能完全处理较大并发客户端数量。 + +![img](./emqx/client-num.png) + +## 执行测试模拟发送 MQTT 数据 + +``` +npm install mqtt mockjs --save --registry=https://registry.npm.taobao.org +node mock.js +``` + +![img](./emqx/run-mock.png) + +## 验证 EMQX 接收到数据 + +在 EMQX Dashboard 规则引擎界面进行刷新,可以看到有多少条记录被正确接收到: + +![img](./emqx/check-rule-matched.png) + +## 验证数据写入到 TDengine + +使用 TDengine CLI 程序登录并查询相应数据库和表,验证数据是否被正确写入到 TDengine 中: + +![img](./emqx/check-result-in-taos.png) + +TDengine 详细使用方法请参考 [TDengine 官方文档](https://docs.taosdata.com/)。 +EMQX 详细使用方法请参考 [EMQX 官方文档](https://www.emqx.io/docs/zh/v4.4/rule/rule-engine.html)。 + diff --git a/docs-cn/20-third-party/10-hive-mq-broker.md b/docs-cn/20-third-party/10-hive-mq-broker.md new file mode 100644 index 0000000000000000000000000000000000000000..f75ed793d6272ae27f92676e2096ef455f638aa6 --- /dev/null +++ b/docs-cn/20-third-party/10-hive-mq-broker.md @@ -0,0 +1,6 @@ +--- +sidebar_label: HiveMQ Broker +title: HiveMQ Broker 写入 +--- + +[HiveMQ](https://www.hivemq.com/) 是一个提供免费个人版和企业版的 MQTT 代理,主要用于企业和新兴的机器到机器 M2M 通讯和内部传输,满足可伸缩性、易管理和安全特性。HiveMQ 提供了开源的插件开发包。可以通过 HiveMQ extension - TDengine 保存数据到 TDengine。详细使用方法请参考 [HiveMQ extension - TDengine 说明文档](https://github.com/huskar-t/hivemq-tdengine-extension/blob/b62a26ecc164a310104df57691691b237e091c89/README.md)。 diff --git a/docs-cn/20-third-party/11-kafka.md b/docs-cn/20-third-party/11-kafka.md new file mode 100644 index 0000000000000000000000000000000000000000..d12d5fab75671d8a1e7356e766d0e8979c6519c2 --- /dev/null +++ b/docs-cn/20-third-party/11-kafka.md @@ -0,0 +1,379 @@ +--- +sidebar_label: Kafka +title: TDengine Kafka Connector 使用教程 +--- + +TDengine Kafka Connector 包含两个插件: TDengine Source Connector 和 TDengine Sink Connector。用户只需提供简单的配置文件,就可以将 Kafka 中指定 topic 的数据(批量或实时)同步到 TDengine, 或将 TDengine 中指定数据库的数据(批量或实时)同步到 Kafka。 + +## 什么是 Kafka Connect? + +Kafka Connect 是 Apache Kafka 的一个组件,用于使其它系统,比如数据库、云服务、文件系统等能方便地连接到 Kafka。数据既可以通过 Kafka Connect 从其它系统流向 Kafka, 也可以通过 Kafka Connect 从 Kafka 流向其它系统。从其它系统读数据的插件称为 Source Connector, 写数据到其它系统的插件称为 Sink Connector。Source Connector 和 Sink Connector 都不会直接连接 Kafka Broker,Source Connector 把数据转交给 Kafka Connect。Sink Connector 从 Kafka Connect 接收数据。 + +![](kafka/Kafka_Connect.png) + +TDengine Source Connector 用于把数据实时地从 TDengine 读出来发送给 Kafka Connect。TDengine Sink Connector 用于 从 Kafka Connect 接收数据并写入 TDengine。 + +![](kafka/streaming-integration-with-kafka-connect.png) + +## 什么是 Confluent? + +Confluent 在 Kafka 的基础上增加很多扩展功能。包括: + +1. Schema Registry +2. REST 代理 +3. 非 Java 客户端 +4. 很多打包好的 Kafka Connect 插件 +5. 管理和监控 Kafka 的 GUI —— Confluent 控制中心 + +这些扩展功能有的包含在社区版本的 Confluent 中,有的只有企业版能用。 +![](kafka/confluentPlatform.png) + +Confluent 企业版提供了 `confluent` 命令行工具管理各个组件。 + +## 前置条件 + +运行本教程中示例的前提条件。 + +1. Linux 操作系统 +2. 已安装 Java 8 和 Maven +3. 已安装 Git +4. 已安装并启动 TDengine。如果还没有可参考[安装和卸载](/operation/pkg-install) + +## 安装 Confluent + +Confluent 提供了 Docker 和二进制包两种安装方式。本文仅介绍二进制包方式安装。 + +在任意目录下执行: + +``` +curl -O http://packages.confluent.io/archive/7.1/confluent-7.1.1.tar.gz +tar xzf confluent-7.1.1.tar.gz -C /opt/test +``` + +然后需要把 `$CONFLUENT_HOME/bin` 目录加入 PATH。 + +```title=".profile" +export CONFLUENT_HOME=/opt/confluent-7.1.1 +PATH=$CONFLUENT_HOME/bin +export PATH +``` + +以上脚本可以追加到当前用户的 profile 文件(~/.profile 或 ~/.bash_profile) + +安装完成之后,可以输入`confluent version`做简单验证: + +``` +# confluent version +confluent - Confluent CLI + +Version: v2.6.1 +Git Ref: 6d920590 +Build Date: 2022-02-18T06:14:21Z +Go Version: go1.17.6 (linux/amd64) +Development: false +``` + +## 安装 TDengine Connector 插件 + +### 从源码安装 + +``` +git clone https://github.com:taosdata/kafka-connect-tdengine.git +cd kafka-connect-tdengine +mvn clean package +unzip -d $CONFLUENT_HOME/share/confluent-hub-components/ target/components/packages/taosdata-kafka-connect-tdengine-0.1.0.zip +``` + +以上脚本先 clone 项目源码,然后用 Maven 编译打包。打包完成后在 `target/components/packages/` 目录生成了插件的 zip 包。把这个 zip 包解压到安装插件的路径即可。安装插件的路径在配置文件 `$CONFLUENT_HOME/etc/kafka/connect-standalone.properties` 中。默认的路径为 `$CONFLUENT_HOME/share/confluent-hub-components/`。 + +### 用 confluent-hub 安装 + +[Confluent Hub](https://www.confluent.io/hub) 提供下载 Kafka Connect 插件的服务。在 TDengine Kafka Connector 发布到 Confluent Hub 后可以使用命令工具 `confluent-hub` 安装。 +**TDengine Kafka Connector 目前没有正式发布,不能用这种方式安装**。 + +## 启动 Confluent + +``` +confluent local services start +``` + +:::note +一定要先安装插件再启动 Confluent, 否则会出现找不到类的错误。Kafka Connect 的日志(默认路径: /tmp/confluent.xxxx/connect/logs/connect.log)中会输出成功安装的插件,据此可判断插件是否安装成功。 +::: + +:::tip +若某组件启动失败,可尝试清空数据,重新启动。数据目录在启动时将被打印到控制台,比如 : + +```title="控制台输出日志" {1} +Using CONFLUENT_CURRENT: /tmp/confluent.106668 +Starting ZooKeeper +ZooKeeper is [UP] +Starting Kafka +Kafka is [UP] +Starting Schema Registry +Schema Registry is [UP] +Starting Kafka REST +Kafka REST is [UP] +Starting Connect +Connect is [UP] +Starting ksqlDB Server +ksqlDB Server is [UP] +Starting Control Center +Control Center is [UP] +``` + +清空数据可执行 `rm -rf /tmp/confluent.106668`。 +::: + +## TDengine Sink Connector 的使用 + +TDengine Sink Connector 的作用是同步指定 topic 的数据到 TDengine。用户无需提前创建数据库和超级表。可手动指定目标数据库的名字(见配置参数 connection.database), 也可按一定规则生成(见配置参数 connection.database.prefix)。 + +TDengine Sink Connector 内部使用 TDengine [无模式写入接口](/reference/connector/cpp#无模式写入-api)写数据到 TDengine,目前支持三种格式的数据:[InfluxDB 行协议格式](/develop/insert-data/influxdb-line)、 [OpenTSDB Telnet 协议格式](/develop/insert-data/opentsdb-telnet) 和 [OpenTSDB JSON 协议格式](/develop/insert-data/opentsdb-json)。 + +下面的示例将主题 meters 的数据,同步到目标数据库 power。数据格式为 InfluxDB Line 协议格式。 + +### 添加配置文件 + +``` +mkdir ~/test +cd ~/test +vi sink-demo.properties +``` + +sink-demo.properties 内容如下: + +```ini title="sink-demo.properties" +name=tdengine-sink-demo +connector.class=com.taosdata.kafka.connect.sink.TDengineSinkConnector +tasks.max=1 +topics=meters +connection.url=jdbc:TAOS://127.0.0.1:6030 +connection.user=root +connection.password=taosdata +connection.database=power +db.schemaless=line +key.converter=org.apache.kafka.connect.storage.StringConverter +value.converter=org.apache.kafka.connect.storage.StringConverter +``` + +关键配置说明: + +1. `topics=meters` 和 `connection.database=power`, 表示订阅主题 meters 的数据,并写入数据库 power。 +2. `db.schemaless=line`, 表示使用 InfluxDB Line 协议格式的数据。 + +### 创建 Connector 实例 + +``` +confluent local services connect connector load TDengineSinkConnector --config ./sink-demo.properties +``` + +若以上命令执行成功,则有如下输出: + +```json +{ + "name": "TDengineSinkConnector", + "config": { + "connection.database": "power", + "connection.password": "taosdata", + "connection.url": "jdbc:TAOS://127.0.0.1:6030", + "connection.user": "root", + "connector.class": "com.taosdata.kafka.connect.sink.TDengineSinkConnector", + "db.schemaless": "line", + "key.converter": "org.apache.kafka.connect.storage.StringConverter", + "tasks.max": "1", + "topics": "meters", + "value.converter": "org.apache.kafka.connect.storage.StringConverter", + "name": "TDengineSinkConnector" + }, + "tasks": [], + "type": "sink" +} +``` + +### 写入测试数据 + +准备测试数据的文本文件,内容如下: + +```txt title="test-data.txt" +meters,location=Beijing.Haidian,groupid=2 current=11.8,voltage=221,phase=0.28 1648432611249000000 +meters,location=Beijing.Haidian,groupid=2 current=13.4,voltage=223,phase=0.29 1648432611250000000 +meters,location=Beijing.Haidian,groupid=3 current=10.8,voltage=223,phase=0.29 1648432611249000000 +meters,location=Beijing.Haidian,groupid=3 current=11.3,voltage=221,phase=0.35 1648432611250000000 +``` + +使用 kafka-console-producer 向主题 meters 添加测试数据。 + +``` +cat test-data.txt | kafka-console-producer --broker-list localhost:9092 --topic meters +``` + +:::note +如果目标数据库 power 不存在,那么 TDengine Sink Connector 会自动创建数据库。自动创建数据库使用的时间精度为纳秒,这就要求写入数据的时间戳精度也是纳秒。如果写入数据的时间戳精度不是纳秒,将会抛异常。 +::: + +### 验证同步是否成功 + +使用 TDengine CLI 验证同步是否成功。 + +``` +taos> use power; +Database changed. + +taos> select * from meters; + ts | current | voltage | phase | groupid | location | +=============================================================================================================================================================== + 2022-03-28 09:56:51.249000000 | 11.800000000 | 221.000000000 | 0.280000000 | 2 | Beijing.Haidian | + 2022-03-28 09:56:51.250000000 | 13.400000000 | 223.000000000 | 0.290000000 | 2 | Beijing.Haidian | + 2022-03-28 09:56:51.249000000 | 10.800000000 | 223.000000000 | 0.290000000 | 3 | Beijing.Haidian | + 2022-03-28 09:56:51.250000000 | 11.300000000 | 221.000000000 | 0.350000000 | 3 | Beijing.Haidian | +Query OK, 4 row(s) in set (0.004208s) +``` + +若看到了以上数据,则说明同步成功。若没有,请检查 Kafka Connect 的日志。配置参数的详细说明见[配置参考](#配置参考)。 + +## TDengine Source Connector 的使用 + +TDengine Source Connector 的作用是将 TDengine 某个数据库某一时刻之后的数据全部推送到 Kafka。TDengine Source Connector 的实现原理是,先分批拉取历史数据,再用定时查询的策略同步增量数据。同时会监控表的变化,可以自动同步新增的表。如果重启 Kafka Connect, 会从上次中断的位置继续同步。 + +TDengine Source Connector 会将 TDengine 数据表中的数据转换成 [InfluxDB Line 协议格式](/develop/insert-data/influxdb-line/) 或 [OpenTSDB JSON 协议格式](/develop/insert-data/opentsdb-json), 然后写入 Kafka。 + +下面的示例程序同步数据库 test 中的数据到主题 tdengine-source-test。 + +### 添加配置文件 + +``` +vi source-demo.properties +``` + +输入以下内容: + +```ini title="source-demo.properties" +name=TDengineSourceConnector +connector.class=com.taosdata.kafka.connect.source.TDengineSourceConnector +tasks.max=1 +connection.url=jdbc:TAOS://127.0.0.1:6030 +connection.username=root +connection.password=taosdata +connection.database=test +connection.attempts=3 +connection.backoff.ms=5000 +topic.prefix=tdengine-source- +poll.interval.ms=1000 +fetch.max.rows=100 +out.format=line +key.converter=org.apache.kafka.connect.storage.StringConverter +value.converter=org.apache.kafka.connect.storage.StringConverter +``` + +### 准备测试数据 + +准备生成测试数据的 SQL 文件。 + +```sql title="prepare-source-data.sql" +DROP DATABASE IF EXISTS test; +CREATE DATABASE test; +USE test; +CREATE STABLE meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT); +INSERT INTO d1001 USING meters TAGS(Beijing.Chaoyang, 2) VALUES('2018-10-03 14:38:05.000',10.30000,219,0.31000) d1001 USING meters TAGS(Beijing.Chaoyang, 2) VALUES('2018-10-03 14:38:15.000',12.60000,218,0.33000) d1001 USING meters TAGS(Beijing.Chaoyang, 2) VALUES('2018-10-03 14:38:16.800',12.30000,221,0.31000) d1002 USING meters TAGS(Beijing.Chaoyang, 3) VALUES('2018-10-03 14:38:16.650',10.30000,218,0.25000) d1003 USING meters TAGS(Beijing.Haidian, 2) VALUES('2018-10-03 14:38:05.500',11.80000,221,0.28000) d1003 USING meters TAGS(Beijing.Haidian, 2) VALUES('2018-10-03 14:38:16.600',13.40000,223,0.29000) d1004 USING meters TAGS(Beijing.Haidian, 3) VALUES('2018-10-03 14:38:05.000',10.80000,223,0.29000) d1004 USING meters TAGS(Beijing.Haidian, 3) VALUES('2018-10-03 14:38:06.500',11.50000,221,0.35000); +``` + +使用 TDengine CLI, 执行 SQL 文件。 + +``` +taos -f prepare-source-data.sql +``` + +### 创建 Connector 实例 + +``` +confluent local services connect connector load TDengineSourceConnector --config source-demo.properties +``` + +### 查看 topic 数据 + +使用 kafka-console-consumer 命令行工具监控主题 tdengine-source-test 中的数据。一开始会输出所有历史数据, 往 TDengine 插入两条新的数据之后,kafka-console-consumer 也立即输出了新增的两条数据。 + +``` +kafka-console-consumer --bootstrap-server localhost:9092 --from-beginning --topic tdengine-source-test +``` + +输出: + +``` +...... +meters,location="beijing.chaoyang",groupid=2i32 current=10.3f32,voltage=219i32,phase=0.31f32 1538548685000000000 +meters,location="beijing.chaoyang",groupid=2i32 current=12.6f32,voltage=218i32,phase=0.33f32 1538548695000000000 +...... +``` + +此时会显示所有历史数据。切换到 TDengine CLI, 插入两条新的数据: + +``` +USE test; +INSERT INTO d1001 VALUES (now, 13.3, 229, 0.38); +INSERT INTO d1002 VALUES (now, 16.3, 233, 0.22); +``` + +再切换回 kafka-console-consumer, 此时命令行窗口已经打印出刚插入的 2 条数据。 + +### unload 插件 + +测试完毕之后,用 unload 命令停止已加载的 connector。 + +查看当前活跃的 connector: + +``` +confluent local services connect connector status +``` + +如果按照前述操作,此时应有两个活跃的 connector。使用下面的命令 unload: + +``` +confluent local services connect connector unload TDengineSourceConnector +confluent local services connect connector unload TDengineSourceConnector +``` + +## 配置参考 + +### 通用配置 + +以下配置项对 TDengine Sink Connector 和 TDengine Source Connector 均适用。 + +1. `name`: connector 名称。 +2. `connector.class`: connector 的完整类名, 如: com.taosdata.kafka.connect.sink.TDengineSinkConnector。 +3. `tasks.max`: 最大任务数, 默认 1。 +4. `topics`: 需要同步的 topic 列表, 多个用逗号分隔, 如 `topic1,topic2`。 +5. `connection.url`: TDengine JDBC 连接字符串, 如 `jdbc:TAOS://127.0.0.1:6030`。 +6. `connection.user`: TDengine 用户名, 默认 root。 +7. `connection.password` :TDengine 用户密码, 默认 taosdata。 +8. `connection.attempts` :最大尝试连接次数。默认 3。 +9. `connection.backoff.ms` : 创建连接失败重试时间隔时间,单位为 ms。 默认 5000。 + +### TDengine Sink Connector 特有的配置 + +1. `connection.database`: 目标数据库名。如果指定的数据库不存在会则自动创建。自动建库使用的时间精度为纳秒。默认值为 null。为 null 时目标数据库命名规则参考 `connection.database.prefix` 参数的说明 +2. `connection.database.prefix`: 当 connection.database 为 null 时, 目标数据库的前缀。可以包含占位符 '${topic}'。 比如 kafka_${topic}, 对于主题 'orders' 将写入数据库 'kafka_orders'。 默认 null。当为 null 时,目标数据库的名字和主题的名字是一致的。 +3. `batch.size`: 分批写入每批记录数。当 Sink Connector 一次接收到的数据大于这个值时将分批写入。 +4. `max.retries`: 发生错误时的最大重试次数。默认为 1。 +5. `retry.backoff.ms`: 发送错误时重试的时间间隔。单位毫秒,默认 3000。 +6. `db.schemaless`: 数据格式,必须指定为: line、json、telnet 中的一个。分别代表 InfluxDB 行协议格式、 OpenTSDB JSON 格式、 OpenTSDB Telnet 行协议格式。 + +### TDengine Source Connector 特有的配置 + +1. `connection.database`: 源数据库名称,无缺省值。 +2. `topic.prefix`: 数据导入 kafka 后 topic 名称前缀。 使用 `topic.prefix` + `connection.database` 名称作为完整 topic 名。默认为空字符串 ""。 +3. `timestamp.initial`: 数据同步起始时间。格式为'yyyy-MM-dd HH:mm:ss'。默认 "1970-01-01 00:00:00"。 +4. `poll.interval.ms`: 拉取数据间隔,单位为 ms。默认 1000。 +5. `fetch.max.rows` : 检索数据库时最大检索条数。 默认为 100。 +6. `out.format`: 数据格式。取值 line 或 json。line 表示 InfluxDB Line 协议格式, json 表示 OpenTSDB JSON 格式。默认 line。 + +## 问题反馈 + +https://github.com/taosdata/kafka-connect-tdengine/issues + +## 参考 + +1. https://www.confluent.io/what-is-apache-kafka +2. https://developer.confluent.io/learn-kafka/kafka-connect/intro +3. https://docs.confluent.io/platform/current/platform.html diff --git a/docs-cn/20-third-party/_category_.yml b/docs-cn/20-third-party/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..514d8c19a878d9700bf11cd7f25208278dfa902e --- /dev/null +++ b/docs-cn/20-third-party/_category_.yml @@ -0,0 +1 @@ +label: 第三方工具 \ No newline at end of file diff --git a/docs-cn/20-third-party/_deploytaosadapter.mdx b/docs-cn/20-third-party/_deploytaosadapter.mdx new file mode 100644 index 0000000000000000000000000000000000000000..ec40744c9a4620801e59aadd49996a0b6381f1bc --- /dev/null +++ b/docs-cn/20-third-party/_deploytaosadapter.mdx @@ -0,0 +1,18 @@ +### 部署taosAdapter + +taosAdapter 可以和 TDengine 部署在同一个系统中,也可以分离部署。 + +启动 taosAdapter: + +``` +systemctl start taosadapter +``` + +检查 taosAdapter 的运行状态: + +``` +systemctl status taosadapter +``` + +taosAdapter 详细的配置参数和使用请参考 `taosadapter --help` 命令输出以及 [参考文档](/reference/taosadapter) 。 + diff --git a/docs-cn/20-third-party/emqx/add-action-handler.png b/docs-cn/20-third-party/emqx/add-action-handler.png new file mode 100644 index 0000000000000000000000000000000000000000..97a1f933ecfadfcab399938806d73c5a5ecc6427 Binary files /dev/null and b/docs-cn/20-third-party/emqx/add-action-handler.png differ diff --git a/docs-cn/20-third-party/emqx/check-result-in-taos.png b/docs-cn/20-third-party/emqx/check-result-in-taos.png new file mode 100644 index 0000000000000000000000000000000000000000..c17a5c1ea2b9bbd49263056c8bf09c9aabab07d5 Binary files /dev/null and b/docs-cn/20-third-party/emqx/check-result-in-taos.png differ diff --git a/docs-cn/20-third-party/emqx/check-rule-matched.png b/docs-cn/20-third-party/emqx/check-rule-matched.png new file mode 100644 index 0000000000000000000000000000000000000000..9e9a466946a1afa857e2bbc07b14956dd0f984b6 Binary files /dev/null and b/docs-cn/20-third-party/emqx/check-rule-matched.png differ diff --git a/docs-cn/20-third-party/emqx/client-num.png b/docs-cn/20-third-party/emqx/client-num.png new file mode 100644 index 0000000000000000000000000000000000000000..fff48cbf3b271c367079ddde425b3f9b014062f7 Binary files /dev/null and b/docs-cn/20-third-party/emqx/client-num.png differ diff --git a/docs-cn/20-third-party/emqx/create-resource.png b/docs-cn/20-third-party/emqx/create-resource.png new file mode 100644 index 0000000000000000000000000000000000000000..58da4c391a3692b9f5fa348d952701eab8bcb746 Binary files /dev/null and b/docs-cn/20-third-party/emqx/create-resource.png differ diff --git a/docs-cn/20-third-party/emqx/create-rule.png b/docs-cn/20-third-party/emqx/create-rule.png new file mode 100644 index 0000000000000000000000000000000000000000..73b0b6ee3e6065a142df98abe8c0dbb32b34f89d Binary files /dev/null and b/docs-cn/20-third-party/emqx/create-rule.png differ diff --git a/docs-cn/20-third-party/emqx/edit-action.png b/docs-cn/20-third-party/emqx/edit-action.png new file mode 100644 index 0000000000000000000000000000000000000000..2a43ee369a439cf11cee23c11f25d6a84b26d7dc Binary files /dev/null and b/docs-cn/20-third-party/emqx/edit-action.png differ diff --git a/docs-cn/20-third-party/emqx/edit-resource.png b/docs-cn/20-third-party/emqx/edit-resource.png new file mode 100644 index 0000000000000000000000000000000000000000..0a0b3560044f4ed6e0a8f040b74085a7e8948b1f Binary files /dev/null and b/docs-cn/20-third-party/emqx/edit-resource.png differ diff --git a/docs-cn/20-third-party/emqx/login-dashboard.png b/docs-cn/20-third-party/emqx/login-dashboard.png new file mode 100644 index 0000000000000000000000000000000000000000..d6c5035c98d860faf639ef6611c6719adf80c47b Binary files /dev/null and b/docs-cn/20-third-party/emqx/login-dashboard.png differ diff --git a/docs-cn/20-third-party/emqx/rule-engine.png b/docs-cn/20-third-party/emqx/rule-engine.png new file mode 100644 index 0000000000000000000000000000000000000000..db110a837b024c82ee9d22f02dcd3a9d06abdd55 Binary files /dev/null and b/docs-cn/20-third-party/emqx/rule-engine.png differ diff --git a/docs-cn/20-third-party/emqx/rule-header-key-value.png b/docs-cn/20-third-party/emqx/rule-header-key-value.png new file mode 100644 index 0000000000000000000000000000000000000000..b81b9a9684aa2f98d00b7ec21e5de411fb450312 Binary files /dev/null and b/docs-cn/20-third-party/emqx/rule-header-key-value.png differ diff --git a/docs-cn/20-third-party/emqx/run-mock.png b/docs-cn/20-third-party/emqx/run-mock.png new file mode 100644 index 0000000000000000000000000000000000000000..0da25818575247732d5d3a783aa52cf7ce24662c Binary files /dev/null and b/docs-cn/20-third-party/emqx/run-mock.png differ diff --git a/docs-cn/20-third-party/index.md b/docs-cn/20-third-party/index.md new file mode 100644 index 0000000000000000000000000000000000000000..b493203225c89944759a216f2b75e0afa6ad03ba --- /dev/null +++ b/docs-cn/20-third-party/index.md @@ -0,0 +1,14 @@ +--- +title: 第三方工具 +--- + +TDengine 通过对标准 SQL 命令、常用数据库连接器标准(例如 JDBC)、ORM 以及其他流行时序数据库写入协议(例如 InfluxDB Line Protocol、OpenTSDB JSON、OpenTSDB Telnet 等)的支持可以使 TDengine 非常容易和第三方工具共同使用。 + +对于支持的第三方工具,无需任何代码,你只需要做简单的配置,就可以将 TDengine 与第三方工具无缝集成起来。 + +```mdx-code-block +import DocCardList from '@theme/DocCardList'; +import {useCurrentSidebarCategory} from '@docusaurus/theme-common'; + + +``` diff --git a/docs-cn/20-third-party/kafka/Kafka_Connect.png b/docs-cn/20-third-party/kafka/Kafka_Connect.png new file mode 100644 index 0000000000000000000000000000000000000000..f3dc02ea2a743c6e1ae5531e14f820e3adeca29a Binary files /dev/null and b/docs-cn/20-third-party/kafka/Kafka_Connect.png differ diff --git a/docs-cn/20-third-party/kafka/confluentPlatform.png b/docs-cn/20-third-party/kafka/confluentPlatform.png new file mode 100644 index 0000000000000000000000000000000000000000..f8e69f2c7f64d809996b2d1bf1370b67b8030850 Binary files /dev/null and b/docs-cn/20-third-party/kafka/confluentPlatform.png differ diff --git a/docs-cn/20-third-party/kafka/streaming-integration-with-kafka-connect.png b/docs-cn/20-third-party/kafka/streaming-integration-with-kafka-connect.png new file mode 100644 index 0000000000000000000000000000000000000000..26d8a866d706180c900d69bb6f57ca2dff0047dd Binary files /dev/null and b/docs-cn/20-third-party/kafka/streaming-integration-with-kafka-connect.png differ diff --git a/docs-cn/21-tdinternal/01-arch.md b/docs-cn/21-tdinternal/01-arch.md new file mode 100644 index 0000000000000000000000000000000000000000..6f479efc1ad13e27899e7819d194a2df59ed3ad1 --- /dev/null +++ b/docs-cn/21-tdinternal/01-arch.md @@ -0,0 +1,302 @@ +--- +sidebar_label: 整体架构 +title: 整体架构 +--- + +## 集群与基本逻辑单元 + +TDengine 的设计是基于单个硬件、软件系统不可靠,基于任何单台计算机都无法提供足够计算能力和存储能力处理海量数据的假设进行设计的。因此 TDengine 从研发的第一天起,就按照分布式高可靠架构进行设计,是支持水平扩展的,这样任何单台或多台服务器发生硬件故障或软件错误都不影响系统的可用性和可靠性。同时,通过节点虚拟化并辅以自动化负载均衡技术,TDengine 能最高效率地利用异构集群中的计算和存储资源降低硬件投资。 + +### 主要逻辑单元 + +TDengine 分布式架构的逻辑结构图如下: + +![TDengine架构示意图](/img/architecture/structure.png) + +
图 1 TDengine架构示意图
+ +一个完整的 TDengine 系统是运行在一到多个物理节点上的,逻辑上,它包含数据节点(dnode)、TDengine 应用驱动(taosc)以及应用(app)。系统中存在一到多个数据节点,这些数据节点组成一个集群(cluster)。应用通过 taosc 的 API 与 TDengine 集群进行互动。下面对每个逻辑单元进行简要介绍。 + +**物理节点(pnode):** pnode 是一独立运行、拥有自己的计算、存储和网络能力的计算机,可以是安装有 OS 的物理机、虚拟机或 Docker 容器。物理节点由其配置的 FQDN(Fully Qualified Domain Name)来标识。TDengine 完全依赖 FQDN 来进行网络通讯,如果不了解 FQDN,请看博文[《一篇文章说清楚 TDengine 的 FQDN》](https://www.taosdata.com/blog/2020/09/11/1824.html)。 + +**数据节点(dnode):** dnode 是 TDengine 服务器侧执行代码 taosd 在物理节点上的一个运行实例,一个工作的系统必须有至少一个数据节点。dnode 包含零到多个逻辑的虚拟节点(vnode),零或者至多一个逻辑的管理节点(mnode)。dnode 在系统中的唯一标识由实例的 End Point(EP)决定。EP 是 dnode 所在物理节点的 FQDN(Fully Qualified Domain Name)和系统所配置的网络端口号(Port)的组合。通过配置不同的端口,一个物理节点(一台物理机、虚拟机或容器)可以运行多个实例,或有多个数据节点。 + +**虚拟节点(vnode):** 为更好的支持数据分片、负载均衡,防止数据过热或倾斜,数据节点被虚拟化成多个虚拟节点(vnode,图中 V2,V3,V4 等)。每个 vnode 都是一个相对独立的工作单元,是时序数据存储的基本单元,具有独立的运行线程、内存空间与持久化存储的路径。一个 vnode 包含一定数量的表(数据采集点)。当创建一张新表时,系统会检查是否需要创建新的 vnode。一个数据节点上能创建的 vnode 的数量取决于该数据节点所在物理节点的硬件资源。一个 vnode 只属于一个 DB,但一个 DB 可以有多个 vnode。一个 vnode 除存储的时序数据外,也保存有所包含的表的 schema、标签值等。一个虚拟节点由所属的数据节点的 EP,以及所属的 VGroup ID 在系统内唯一标识,由管理节点创建并管理。 + +**管理节点(mnode):** 一个虚拟的逻辑单元,负责所有数据节点运行状态的监控和维护,以及节点之间的负载均衡(图中 M)。同时,管理节点也负责元数据(包括用户、数据库、表、静态标签等)的存储和管理,因此也称为 Meta Node。TDengine 集群中可配置多个(开源版最多不超过 3 个)mnode,它们自动构建成为一个虚拟管理节点组(图中 M0,M1,M2)。mnode 间采用 master/slave 的机制进行管理,而且采取强一致方式进行数据同步,任何数据更新操作只能在 Master 上进行。mnode 集群的创建由系统自动完成,无需人工干预。每个 dnode 上至多有一个 mnode,由所属的数据节点的 EP 来唯一标识。每个 dnode 通过内部消息交互自动获取整个集群中所有 mnode 所在的 dnode 的 EP。 + +**虚拟节点组(VGroup):** 不同数据节点上的 vnode 可以组成一个虚拟节点组(vgroup)来保证系统的高可靠。虚拟节点组内采取 master/slave 的方式进行管理。写操作只能在 master vnode 上进行,系统采用异步复制的方式将数据同步到 slave vnode,这样确保了一份数据在多个物理节点上有拷贝。一个 vgroup 里虚拟节点个数就是数据的副本数。如果一个 DB 的副本数为 N,系统必须有至少 N 数据节点。副本数在创建 DB 时通过参数 replica 可以指定,缺省为 1。使用 TDengine 的多副本特性,可以不再需要昂贵的磁盘阵列等存储设备,就可以获得同样的数据高可靠性。虚拟节点组由管理节点创建、管理,并且由管理节点分配一个系统唯一的 ID,VGroup ID。如果两个虚拟节点的 VGroup ID 相同,说明他们属于同一个组,数据互为备份。虚拟节点组里虚拟节点的个数是可以动态改变的,容许只有一个,也就是没有数据复制。VGroup ID 是永远不变的,即使一个虚拟节点组被删除,它的 ID 也不会被收回重复利用。 + +**Taosc** taosc 是 TDengine 给应用提供的驱动程序(driver),负责处理应用与集群的接口交互,提供 C/C++ 语言原生接口,内嵌于 JDBC、C#、Python、Go、Node.js 语言连接库里。应用都是通过 taosc 而不是直接连接集群中的数据节点与整个集群进行交互的。这个模块负责获取并缓存元数据;将插入、查询等请求转发到正确的数据节点;在把结果返回给应用时,还需要负责最后一级的聚合、排序、过滤等操作。对于 JDBC、C/C++、C#、Python、Go、Node.js 接口而言,这个模块是在应用所处的物理节点上运行。同时,为支持全分布式的 RESTful 接口,taosc 在 TDengine 集群的每个 dnode 上都有一运行实例。 + +### 节点之间的通讯 + +**通讯方式:**TDengine 系统的各个数据节点之间,以及应用驱动与各数据节点之间的通讯是通过 TCP/UDP 进行的。因为考虑到物联网场景,数据写入的包一般不大,因此 TDengine 除采用 TCP 做传输之外,还采用 UDP 方式,因为 UDP 更加高效,而且不受连接数的限制。TDengine 实现了自己的超时、重传、确认等机制,以确保 UDP 的可靠传输。对于数据量不到 15K 的数据包,采取 UDP 的方式进行传输,超过 15K 的,或者是查询类的操作,自动采取 TCP 的方式进行传输。同时,TDengine 根据配置和数据包,会自动对数据进行压缩/解压缩,数字签名/认证等处理。对于数据节点之间的数据复制,只采用 TCP 方式进行数据传输。 + +**FQDN 配置:**一个数据节点有一个或多个 FQDN,可以在系统配置文件 taos.cfg 通过参数“fqdn”进行指定,如果没有指定,系统将自动获取计算机的 hostname 作为其 FQDN。如果节点没有配置 FQDN,可以直接将该节点的配置参数 fqdn 设置为它的 IP 地址。但不建议使用 IP,因为 IP 地址可变,一旦变化,将让集群无法正常工作。一个数据节点的 EP(End Point)由 FQDN + Port 组成。采用 FQDN,需要保证 DNS 服务正常工作,或者在节点以及应用所在的节点配置好 hosts 文件。另外,这个参数值的长度需要控制在 96 个字符以内。 + +**端口配置:**一个数据节点对外的端口由 TDengine 的系统配置参数 serverPort 决定,对集群内部通讯的端口是 serverPort+5。为支持多线程高效的处理 UDP 数据,每个对内和对外的 UDP 连接,都需要占用 5 个连续的端口。 + +- 集群内数据节点之间的数据复制操作占用一个 TCP 端口,是 serverPort+10。 +- 集群数据节点对外提供 RESTful 服务占用一个 TCP 端口,是 serverPort+11。 +- 集群内数据节点与 Arbitrator 节点之间通讯占用一个 TCP 端口,是 serverPort+12。 + +因此一个数据节点总的端口范围为 serverPort 到 serverPort+12,总共 13 个 TCP/UDP 端口。使用时,需要确保防火墙将这些端口打开。每个数据节点可以配置不同的 serverPort。详细的端口情况请参见 [TDengine 2.0 端口说明](/train-faq/faq#port) + +**集群对外连接:**TDengine 集群可以容纳单个、多个甚至几千个数据节点。应用只需要向集群中任何一个数据节点发起连接即可,连接需要提供的网络参数是一数据节点的 End Point(FQDN 加配置的端口号)。通过命令行 CLI 启动应用 taos 时,可以通过选项-h 来指定数据节点的 FQDN,-P 来指定其配置的端口号,如果端口不配置,将采用 TDengine 的系统配置参数 serverPort。 + +**集群内部通讯:**各个数据节点之间通过 TCP/UDP 进行连接。一个数据节点启动时,将获取 mnode 所在的 dnode 的 EP 信息,然后与系统中的 mnode 建立起连接,交换信息。获取 mnode 的 EP 信息有三步: + +1. 检查 mnodeEpSet.json 文件是否存在,如果不存在或不能正常打开获得 mnode EP 信息,进入第二步; +2. 检查系统配置文件 taos.cfg,获取节点配置参数 firstEp、secondEp(这两个参数指定的节点可以是不带 mnode 的普通节点,这样的话,节点被连接时会尝试重定向到 mnode 节点),如果不存在或者 taos.cfg 里没有这两个配置参数,或无效,进入第三步; +3. 将自己的 EP 设为 mnode EP,并独立运行起来。 + +获取 mnode EP 列表后,数据节点发起连接,如果连接成功,则成功加入进工作的集群,如果不成功,则尝试 mnode EP 列表中的下一个。如果都尝试了,但连接都仍然失败,则休眠几秒后,再进行尝试。 + +**Mnode 的选择:**TDengine 逻辑上有管理节点,但没有单独的执行代码,服务器侧只有一套执行代码 taosd。那么哪个数据节点会是管理节点呢?这是系统自动决定的,无需任何人工干预。原则如下:一个数据节点启动时,会检查自己的 End Point,并与获取的 mnode EP List 进行比对,如果在其中,该数据节点认为自己应该启动 mnode 模块,成为 mnode。如果自己的 EP 不在 mnode EP List 里,则不启动 mnode 模块。在系统的运行过程中,由于负载均衡、宕机等原因,mnode 有可能迁移至新的 dnode,但一切都是透明的,无需人工干预,配置参数的修改,是 mnode 自己根据资源做出的决定。 + +**新数据节点的加入:**系统有了一个数据节点后,就已经成为一个工作的系统。添加新的节点进集群时,有两个步骤,第一步:使用 TDengine CLI 连接到现有工作的数据节点,然后用命令“CREATE DNODE”将新的数据节点的 End Point 添加进去;第二步:在新的数据节点的系统配置参数文件 taos.cfg 里,将 firstEp,secondEp 参数设置为现有集群中任意两个数据节点的 EP 即可。具体添加的详细步骤请见详细的用户手册。这样就把集群一步一步的建立起来。 + +**重定向:**无论是 dnode 还是 taosc,最先都是要发起与 mnode 的连接,但 mnode 是系统自动创建并维护的,因此对于用户来说,并不知道哪个 dnode 在运行 mnode。TDengine 只要求向系统中任何一个工作的 dnode 发起连接即可。因为任何一个正在运行的 dnode,都维护有目前运行的 mnode EP List。当收到一个来自新启动的 dnode 或 taosc 的连接请求,如果自己不是 mnode,则将 mnode EP List 回复给对方,taosc 或新启动的 dnode 收到这个 list,就重新尝试建立连接。当 mnode EP List 发生改变,通过节点之间的消息交互,各个数据节点就很快获取最新列表,并通知 taosc。 + +### 一个典型的消息流程 + +为解释 vnode、mnode、taosc 和应用之间的关系以及各自扮演的角色,下面对写入数据这个典型操作的流程进行剖析。 + +![TDengine典型的操作流程](/img/architecture/message.png) + +
图 2 TDengine 典型的操作流程
+ +1. 应用通过 JDBC 或其他 API 接口发起插入数据的请求。 +2. taosc 会检查缓存,看是否保存有该表的 meta data。如果有,直接到第 4 步。如果没有,taosc 将向 mnode 发出 get meta-data 请求。 +3. mnode 将该表的 meta-data 返回给 taosc。Meta-data 包含有该表的 schema,而且还有该表所属的 vgroup 信息(vnode ID 以及所在的 dnode 的 End Point,如果副本数为 N,就有 N 组 End Point)。如果 taosc 迟迟得不到 mnode 回应,而且存在多个 mnode,taosc 将向下一个 mnode 发出请求。 +4. taosc 向 master vnode 发起插入请求。 +5. vnode 插入数据后,给 taosc 一个应答,表示插入成功。如果 taosc 迟迟得不到 vnode 的回应,taosc 会认为该节点已经离线。这种情况下,如果被插入的数据库有多个副本,taosc 将向 vgroup 里下一个 vnode 发出插入请求。 +6. taosc 通知 APP,写入成功。 + +对于第二和第三步,taosc 启动时,并不知道 mnode 的 End Point,因此会直接向配置的集群对外服务的 End Point 发起请求。如果接收到该请求的 dnode 并没有配置 mnode,该 dnode 会在回复的消息中告知 mnode EP 列表,这样 taosc 会重新向新的 mnode 的 EP 发出获取 meta-data 的请求。 + +对于第四和第五步,没有缓存的情况下,taosc 无法知道虚拟节点组里谁是 master,就假设第一个 vnodeID 就是 master,向它发出请求。如果接收到请求的 vnode 并不是 master,它会在回复中告知谁是 master,这样 taosc 就向建议的 master vnode 发出请求。一旦得到插入成功的回复,taosc 会缓存 master 节点的信息。 + +上述是插入数据的流程,查询、计算的流程也完全一致。taosc 把这些复杂的流程全部封装屏蔽了,对于应用来说无感知也无需任何特别处理。 + +通过 taosc 缓存机制,只有在第一次对一张表操作时,才需要访问 mnode,因此 mnode 不会成为系统瓶颈。但因为 schema 有可能变化,而且 vgroup 有可能发生改变(比如负载均衡发生),因此 taosc 会定时和 mnode 交互,自动更新缓存。 + +## 存储模型与数据分区、分片 + +### 存储模型 + +TDengine 存储的数据包括采集的时序数据以及库、表相关的元数据、标签数据等,这些数据具体分为三部分: + +- 时序数据:存放于 vnode 里,由 data、head 和 last 三个文件组成,数据量大,查询量取决于应用场景。容许乱序写入,但暂时不支持删除操作,并且仅在 update 参数设置为 1 时允许更新操作。通过采用一个采集点一张表的模型,一个时间段的数据是连续存储,对单张表的写入是简单的追加操作,一次读,可以读到多条记录,这样保证对单个采集点的插入和查询操作,性能达到最优。 +- 标签数据:存放于 vnode 里的 meta 文件,支持增删改查四个标准操作。数据量不大,有 N 张表,就有 N 条记录,因此可以全内存存储。如果标签过滤操作很多,查询将十分频繁,因此 TDengine 支持多核多线程并发查询。只要计算资源足够,即使有数千万张表,过滤结果能毫秒级返回。 +- 元数据:存放于 mnode 里,包含系统节点、用户、DB、Table Schema 等信息,支持增删改查四个标准操作。这部分数据的量不大,可以全内存保存,而且由于客户端有缓存,查询量也不大。因此目前的设计虽是集中式存储管理,但不会构成性能瓶颈。 + +与典型的 NoSQL 存储模型相比,TDengine 将标签数据与时序数据完全分离存储,它具有两大优势: + +- 能够极大地降低标签数据存储的冗余度:一般的 NoSQL 数据库或时序数据库,采用的 K-V 存储,其中的 Key 包含时间戳、设备 ID、各种标签。每条记录都带有这些重复的内容,浪费存储空间。而且如果应用要在历史数据上增加、修改或删除标签,需要遍历数据,重写一遍,操作成本极其昂贵。 +- 能够实现极为高效的多表之间的聚合查询:做多表之间聚合查询时,先把符合标签过滤条件的表查找出来,然后再查找这些表相应的数据块,这样大幅减少要扫描的数据集,从而大幅提高查询效率。而且标签数据采用全内存的结构进行管理和维护,千万级别规模的标签数据查询可以在毫秒级别返回。 + +### 数据分片 + +对于海量的数据管理,为实现水平扩展,一般都需要采取分片(Sharding)分区(Partitioning)策略。TDengine 是通过 vnode 来实现数据分片的,通过一个时间段一个数据文件来实现时序数据分区的。 + +vnode(虚拟数据节点)负责为采集的时序数据提供写入、查询和计算功能。为便于负载均衡、数据恢复、支持异构环境,TDengine 将一个数据节点根据其计算和存储资源切分为多个 vnode。这些 vnode 的管理是 TDengine 自动完成的,对应用完全透明。 + +对于单独一个数据采集点,无论其数据量多大,一个 vnode(或 vgroup,如果副本数大于 1)有足够的计算资源和存储资源来处理(如果每秒生成一条 16 字节的记录,一年产生的原始数据不到 0.5G),因此 TDengine 将一张表(一个数据采集点)的所有数据都存放在一个 vnode 里,而不会让同一个采集点的数据分布到两个或多个 dnode 上。而且一个 vnode 可存储多个数据采集点(表)的数据,一个 vnode 可容纳的表的数目的上限为一百万。设计上,一个 vnode 里所有的表都属于同一个 DB。一个数据节点上,除非特殊配置,一个 DB 拥有的 vnode 数目不会超过系统核的数目。 + +创建 DB 时,系统并不会马上分配资源。但当创建一张表时,系统将看是否有已经分配的 vnode,且该 vnode 是否有空余的表空间,如果有,立即在该有空位的 vnode 创建表。如果没有,系统将从集群中,根据当前的负载情况,在一个 dnode 上创建一新的 vnode,然后创建表。如果 DB 有多个副本,系统不是只创建一个 vnode,而是一个 vgroup(虚拟数据节点组)。系统对 vnode 的数目没有任何限制,仅仅受限于物理节点本身的计算和存储资源。 + +每张表的 meta data(包含 schema,标签等)也存放于 vnode 里,而不是集中存放于 mnode,实际上这是对 Meta 数据的分片,这样便于高效并行的进行标签过滤操作。 + +### 数据分区 + +TDengine 除 vnode 分片之外,还对时序数据按照时间段进行分区。每个数据文件只包含一个时间段的时序数据,时间段的长度由 DB 的配置参数 days 决定。这种按时间段分区的方法还便于高效实现数据的保留策略,只要数据文件超过规定的天数(系统配置参数 keep),将被自动删除。而且不同的时间段可以存放于不同的路径和存储介质,以便于大数据的冷热管理,实现多级存储。 + +总的来说,**TDengine 是通过 vnode 以及时间两个维度,对大数据进行切分**,便于并行高效的管理,实现水平扩展。 + +### 负载均衡 + +每个 dnode 都定时向 mnode(虚拟管理节点)报告其状态(包括硬盘空间、内存大小、CPU、网络、虚拟节点个数等),因此 mnode 了解整个集群的状态。基于整体状态,当 mnode 发现某个 dnode 负载过重,它会将 dnode 上的一个或多个 vnode 挪到其他 dnode。在挪动过程中,对外服务继续进行,数据插入、查询和计算操作都不受影响。 + +如果 mnode 一段时间没有收到 dnode 的状态报告,mnode 会认为这个 dnode 已经离线。如果离线时间超过一定时长(时长由配置参数 offlineThreshold 决定),该 dnode 将被 mnode 强制剔除出集群。该 dnode 上的 vnodes 如果副本数大于 1,系统将自动在其他 dnode 上创建新的副本,以保证数据的副本数。如果该 dnode 上还有 mnode,而且 mnode 的副本数大于 1,系统也将自动在其他 dnode 上创建新的 mnode,以保证 mnode 的副本数。 + +当新的数据节点被添加进集群,因为新的计算和存储被添加进来,系统也将自动启动负载均衡流程。 + +负载均衡过程无需任何人工干预,应用也无需重启,将自动连接新的节点,完全透明。 + +**提示:负载均衡由参数 balance 控制,决定开启/关闭自动负载均衡。** + +## 数据写入与复制流程 + +如果一个数据库有 N 个副本,那一个虚拟节点组就有 N 个虚拟节点,但是只有一个是 master,其他都是 slave。当应用将新的记录写入系统时,只有 master vnode 能接受写的请求。如果 slave vnode 收到写的请求,系统将通知 taosc 需要重新定向。 + +### Master Vnode 写入流程 + +Master Vnode 遵循下面的写入流程: + +![TDengine Master写入流程](/img/architecture/write_master.png) + +
图 3 TDengine Master 写入流程
+ +1. master vnode 收到应用的数据插入请求,验证 OK,进入下一步; +2. 如果系统配置参数 walLevel 大于 0,vnode 将把该请求的原始数据包写入数据库日志文件 WAL。如果 walLevel 设置为 2,而且 fsync 设置为 0,TDengine 还将 WAL 数据立即落盘,以保证即使宕机,也能从数据库日志文件中恢复数据,避免数据的丢失; +3. 如果有多个副本,vnode 将把数据包转发给同一虚拟节点组内的 slave vnodes,该转发包带有数据的版本号(version); +4. 写入内存,并将记录加入到 skip list; +5. master vnode 返回确认信息给应用,表示写入成功; +6. 如果第 2、3、4 步中任何一步失败,将直接返回错误给应用。 + +### Slave Vnode 写入流程 + +对于 slave vnode,写入流程是: + +![TDengine Slave 写入流程](/img/architecture/write_slave.png) + +
图 4 TDengine Slave 写入流程
+ +1. slave vnode 收到 Master vnode 转发了的数据插入请求。检查 last version 是否与 master 一致,如果一致,进入下一步。如果不一致,需要进入同步状态。 +2. 如果系统配置参数 walLevel 大于 0,vnode 将把该请求的原始数据包写入数据库日志文件 WAL。如果 walLevel 设置为 2,而且 fsync 设置为 0,TDengine 还将 WAL 数据立即落盘,以保证即使宕机,也能从数据库日志文件中恢复数据,避免数据的丢失。 +3. 写入内存,更新内存中的 skip list。 + +与 master vnode 相比,slave vnode 不存在转发环节,也不存在回复确认环节,少了两步。但写内存与 WAL 是完全一样的。 + +### 主从选择 + +Vnode 会保持一个数据版本号(version),对内存数据进行持久化存储时,对该版本号也进行持久化存储。每个数据更新操作,无论是采集的时序数据还是元数据,这个版本号将增加 1。 + +一个 vnode 启动时,角色(master、slave)是不定的,数据是处于未同步状态,它需要与虚拟节点组内其他节点建立 TCP 连接,并互相交换 status,其中包括 version 和自己的角色。通过 status 的交换,系统进入选主流程,规则如下: + +1. 如果只有一个副本,该副本永远就是 master +2. 所有副本都在线时,版本最高的被选为 master +3. 在线的虚拟节点数过半,而且有虚拟节点是 slave 的话,该虚拟节点自动成为 master +4. 对于 2 和 3,如果多个虚拟节点满足成为 master 的要求,那么虚拟节点组的节点列表里,最前面的选为 master + +更多的关于数据复制的流程,请见[《TDengine 2.0 数据复制模块设计》](/tdinternal/replica/)。 + +### 同步复制 + +对于数据一致性要求更高的场景,异步数据复制无法满足要求,因为有极小的概率丢失数据,因此 TDengine 提供同步复制的机制供用户选择。在创建数据库时,除指定副本数 replica 之外,用户还需要指定新的参数 quorum。如果 quorum 大于 1,它表示每次 master 转发给副本时,需要等待 quorum-1 个回复确认,才能通知应用,数据在 slave 已经写入成功。如果在一定的时间内,得不到 quorum-1 个回复确认,master vnode 将返回错误给应用。 + +采用同步复制,系统的性能会有所下降,而且 latency 会增加。因为元数据要强一致,mnode 之间的数据同步缺省就是采用的同步复制。 + +## 缓存与持久化 + +### 缓存 + +TDengine 采用时间驱动缓存管理策略(First-In-First-Out,FIFO),又称为写驱动的缓存管理机制。这种策略有别于读驱动的数据缓存模式(Least-Recent-Used,LRU),直接将最近写入的数据保存在系统的缓存中。当缓存达到临界值的时候,将最早的数据批量写入磁盘。一般意义上来说,对于物联网数据的使用,用户最为关心的是刚产生的数据,即当前状态。TDengine 充分利用这一特性,将最近到达的(当前状态)数据保存在缓存中。 + +TDengine 通过查询函数向用户提供毫秒级的数据获取能力。直接将最近到达的数据保存在缓存中,可以更加快速地响应用户针对最近一条或一批数据的查询分析,整体上提供更快的数据库查询响应能力。从这个意义上来说,**可通过设置合适的配置参数将 TDengine 作为数据缓存来使用,而不需要再部署 Redis 或其他额外的缓存系统**,可有效地简化系统架构,降低运维的成本。需要注意的是,TDengine 重启以后系统的缓存将被清空,之前缓存的数据均会被批量写入磁盘,缓存的数据将不会像专门的 key-value 缓存系统再将之前缓存的数据重新加载到缓存中。 + +每个 vnode 有自己独立的内存,而且由多个固定大小的内存块组成,不同 vnode 之间完全隔离。数据写入时,类似于日志的写法,数据被顺序追加写入内存,但每个 vnode 维护有自己的 skip list,便于迅速查找。当三分之一以上的内存块写满时,启动落盘操作,而且后续写的操作在新的内存块进行。这样,一个 vnode 里有三分之一内存块是保留有最近的数据的,以达到缓存、快速查找的目的。一个 vnode 的内存块的个数由配置参数 blocks 决定,内存块的大小由配置参数 cache 决定。 + +### 持久化存储 + +TDengine 采用数据驱动的方式让缓存中的数据写入硬盘进行持久化存储。当 vnode 中缓存的数据达到一定规模时,为了不阻塞后续数据的写入,TDengine 也会拉起落盘线程将缓存的数据写入持久化存储。TDengine 在数据落盘时会打开新的数据库日志文件,在落盘成功后则会删除老的数据库日志文件,避免日志文件无限制地增长。 + +为充分利用时序数据特点,TDengine 将一个 vnode 保存在持久化存储的数据切分成多个文件,每个文件只保存固定天数的数据,这个天数由系统配置参数 days 决定。切分成多个文件后,给定查询的起止日期,无需任何索引,就可以立即定位需要打开哪些数据文件,大大加快读取速度。 + +对于采集的数据,一般有保留时长,这个时长由系统配置参数 keep 决定。超过这个设置天数的数据文件,将被系统自动删除,释放存储空间。 + +给定 days 与 keep 两个参数,一个典型工作状态的 vnode 中总的数据文件数为:向上取整 `(keep/days)+1` 个。总的数据文件个数不宜过大,也不宜过小。10 到 100 以内合适。基于这个原则,可以设置合理的 days。目前的版本,参数 keep 可以修改,但对于参数 days,一旦设置后,不可修改。 + +在每个数据文件里,一张表的数据是一块一块存储的。一张表可以有一到多个数据文件块。在一个文件块里,数据是列式存储的,占用的是一片连续的存储空间,这样大大提高读取速度。文件块的大小由系统参数 maxRows (每块最大记录条数)决定,缺省值为 4096。这个值不宜过大,也不宜过小。过大,定位具体时间段的数据的搜索时间会变长,影响读取速度;过小,数据块的索引太大,压缩效率偏低,也影响读取速度。 + +每个数据文件(.data 结尾)都有一个对应的索引文件(.head 结尾),该索引文件对每张表都有一数据块的摘要信息,记录了每个数据块在数据文件中的偏移量,数据的起止时间等信息,以帮助系统迅速定位需要查找的数据。每个数据文件还有一对应的 last 文件(.last 结尾),该文件是为防止落盘时数据块碎片化而设计的。如果一张表落盘的记录条数没有达到系统配置参数 minRows(每块最小记录条数),将被先存储到 last 文件,等下次落盘时,新落盘的记录将与 last 文件的记录进行合并,再写入数据文件。 + +数据写入磁盘时,根据系统配置参数 comp 决定是否压缩数据。TDengine 提供了三种压缩选项:无压缩、一阶段压缩和两阶段压缩,分别对应 comp 值为 0、1 和 2 的情况。一阶段压缩根据数据的类型进行了相应的压缩,压缩算法包括 delta-delta 编码、simple 8B 方法、zig-zag 编码、LZ4 等算法。二阶段压缩在一阶段压缩的基础上又用通用压缩算法进行了压缩,压缩率更高。 + +### 多级存储 + +说明:多级存储功能仅企业版支持,从 2.0.16.0 版本开始提供。 + +在默认配置下,TDengine 会将所有数据保存在 /var/lib/taos 目录下,而且每个 vnode 的数据文件保存在该目录下的不同目录。为扩大存储空间,尽量减少文件读取的瓶颈,提高数据吞吐率 TDengine 可通过配置系统参数 dataDir 让多个挂载的硬盘被系统同时使用。 + +除此之外,TDengine 也提供了数据分级存储的功能,将不同时间段的数据存储在挂载的不同介质上的目录里,从而实现不同“热度”的数据存储在不同的存储介质上,充分利用存储,节约成本。比如,最新采集的数据需要经常访问,对硬盘的读取性能要求高,那么用户可以配置将这些数据存储在 SSD 盘上。超过一定期限的数据,查询需求量没有那么高,那么可以存储在相对便宜的 HDD 盘上。 + +多级存储支持 3 级,每级最多可配置 16 个挂载点。 + +TDengine 多级存储配置方式如下(在配置文件/etc/taos/taos.cfg 中): + +``` +dataDir [path] +``` + +- path: 挂载点的文件夹路径 +- level: 介质存储等级,取值为 0,1,2。 + 0 级存储最新的数据,1 级存储次新的数据,2 级存储最老的数据,省略默认为 0。 + 各级存储之间的数据流向:0 级存储 -> 1 级存储 -> 2 级存储。 + 同一存储等级可挂载多个硬盘,同一存储等级上的数据文件分布在该存储等级的所有硬盘上。 + 需要说明的是,数据在不同级别的存储介质上的移动,是由系统自动完成的,用户无需干预。 +- primary: 是否为主挂载点,0(否)或 1(是),省略默认为 1。 + +在配置中,只允许一个主挂载点的存在(level=0,primary=1),例如采用如下的配置方式: + +``` +dataDir /mnt/data1 0 1 +dataDir /mnt/data2 0 0 +dataDir /mnt/data3 1 0 +dataDir /mnt/data4 1 0 +dataDir /mnt/data5 2 0 +dataDir /mnt/data6 2 0 +``` + +:::note + +1. 多级存储不允许跨级配置,合法的配置方案有:仅 0 级,仅 0 级+ 1 级,以及 0 级+ 1 级+ 2 级。而不允许只配置 level=0 和 level=2,而不配置 level=1。 +2. 禁止手动移除使用中的挂载盘,挂载盘目前不支持非本地的网络盘。 +3. 多级存储目前不支持删除已经挂载的硬盘的功能。 + +::: + +## 数据查询 + +TDengine 提供了多种多样针对表和超级表的查询处理功能,除了常规的聚合查询之外,还提供针对时序数据的窗口查询、统计聚合等功能。TDengine 的查询处理需要客户端、vnode、mnode 节点协同完成。 + +### 单表查询 + +SQL 语句的解析和校验工作在客户端完成。解析 SQL 语句并生成抽象语法树(Abstract Syntax Tree,AST),然后对其进行校验和检查。以及向管理节点(mnode)请求查询中指定表的元数据信息(table metadata)。 + +根据元数据信息中的 End Point 信息,将查询请求序列化后发送到该表所在的数据节点(dnode)。dnode 接收到查询请求后,识别出该查询请求指向的虚拟节点(vnode),将消息转发到 vnode 的查询执行队列。vnode 的查询执行线程建立基础的查询执行环境,并立即返回该查询请求,同时开始执行该查询。 + +客户端在获取查询结果的时候,dnode 的查询执行队列中的工作线程会等待 vnode 执行线程执行完成,才能将查询结果返回到请求的客户端。 + +### 按时间轴聚合、降采样、插值 + +时序数据有别于普通数据的显著特征是每条记录均具有时间戳,因此针对具有时间戳的数据在时间轴上进行聚合是不同于普通数据库的重要功能。从这点上来看,与流计算引擎的窗口查询有相似的地方。 + +在 TDengine 中引入关键词 interval 来进行时间轴上固定长度时间窗口的切分,并按照时间窗口对数据进行聚合,对窗口范围内的数据按需进行聚合。例如: + +```sql +SELECT COUNT(*) FROM d1001 INTERVAL(1h); +``` + +针对 d1001 设备采集的数据,按照 1 小时的时间窗口返回每小时存储的记录数量。 + +在需要连续获得查询结果的应用场景下,如果给定的时间区间存在数据缺失,会导致该区间数据结果也丢失。TDengine 提供策略针对时间轴聚合计算的结果进行插值,通过使用关键词 fill 就能够对时间轴聚合结果进行插值。例如: + +```sql +SELECT COUNT(*) FROM d1001 WHERE ts >= '2017-7-14 00:00:00' AND ts < '2017-7-14 23:59:59' INTERVAL(1h) FILL(PREV); +``` + +针对 d1001 设备采集数据统计每小时记录数,如果某一个小时不存在数据,则返回之前一个小时的统计数据。TDengine 提供前向插值(prev)、线性插值(linear)、空值填充(NULL)、特定值填充(value)。 + +### 多表聚合查询 + +TDengine 对每个数据采集点单独建表,但在实际应用中经常需要对不同的采集点数据进行聚合。为高效的进行聚合操作,TDengine 引入超级表(STable)的概念。超级表用来代表一特定类型的数据采集点,它是包含多张表的表集合,集合里每张表的模式(schema)完全一致,但每张表都带有自己的静态标签,标签可以有多个,可以随时增加、删除和修改。应用可通过指定标签的过滤条件,对一个 STable 下的全部或部分表进行聚合或统计操作,这样大大简化应用的开发。其具体流程如下图所示: + +![多表聚合查询原理图](/img/architecture/multi_tables.png) + +
图 5 多表聚合查询原理图
+ +1. 应用将一个查询条件发往系统; +2. taosc 将超级表的名字发往 meta node(管理节点); +3. 管理节点将超级表所拥有的 vnode 列表发回 taosc; +4. taosc 将计算的请求连同标签过滤条件发往这些 vnode 对应的多个数据节点; +5. 每个 vnode 先在内存里查找出自己节点里符合标签过滤条件的表的集合,然后扫描存储的时序数据,完成相应的聚合计算,将结果返回给 taosc; +6. taosc 将多个数据节点返回的结果做最后的聚合,将其返回给应用。 + +由于 TDengine 在 vnode 内将标签数据与时序数据分离存储,通过在内存里过滤标签数据,先找到需要参与聚合操作的表的集合,将需要扫描的数据集大幅减少,大幅提升聚合计算速度。同时,由于数据分布在多个 vnode/dnode,聚合计算操作在多个 vnode 里并发进行,又进一步提升了聚合的速度。 对普通表的聚合函数以及绝大部分操作都适用于超级表,语法完全一样,细节请看 TAOS SQL。 + +### 预计算 + +为有效提升查询处理的性能,针对物联网数据的不可更改的特点,在数据块头部记录该数据块中存储数据的统计信息:包括最大值、最小值、和。我们称之为预计算单元。如果查询处理涉及整个数据块的全部数据,直接使用预计算结果,完全不需要读取数据块的内容。由于预计算数据量远小于磁盘上存储的数据块数据的大小,对于磁盘 I/O 为瓶颈的查询处理,使用预计算结果可以极大地减小读取 I/O 压力,加速查询处理的流程。预计算机制与 PostgreSQL 的索引 BRIN(block range index)有异曲同工之妙。 diff --git a/docs-cn/21-tdinternal/02-replica.md b/docs-cn/21-tdinternal/02-replica.md new file mode 100644 index 0000000000000000000000000000000000000000..6a384b982d22956dd514d8df05dc827ca6f8b729 --- /dev/null +++ b/docs-cn/21-tdinternal/02-replica.md @@ -0,0 +1,256 @@ +--- +sidebar_label: 数据复制模块设计 +title: 数据复制模块设计 +--- + +## 数据复制概述 + +数据复制(Replication)是指同一份数据在多个物理地点保存。它的目的是防止数据丢失,提高系统的高可用性(High Availability),而且通过应用访问多个副本,提升数据查询性能。 + +在高可靠的大数据系统里,数据复制是必不可少的一大功能。数据复制又分为实时复制与非实时复制。实时复制是指任何数据的更新(包括数据的增加、删除、修改)操作,会被实时的复制到所有副本,这样任何一台机器宕机或网络出现故障,整个系统还能提供最新的数据,保证系统的正常工作。而非实时复制,是指传统的数据备份操作,按照固定的时间周期,将一份数据全量或增量复制到其他地方。如果主节点宕机,副本是很大可能没有最新数据,因此在有些场景是无法满足要求的。 + +TDengine面向的是物联网场景,需要支持数据的实时复制,来最大程度保证系统的可靠性。实时复制有两种方式,一种是异步复制,一种是同步复制。异步复制(Asynchronous Replication)是指数据由Master转发给Slave后,Master并不需要等待Slave回复确认,这种方式效率高,但有极小的概率会丢失数据。同步复制是指Master将数据转发给Slave后,需要等待Slave的回复确认,才会通知应用写入成功,这种方式效率偏低,但能保证数据绝不丢失。 + +数据复制是与数据存储(写入、读取)密切相关的,但两者又是相对独立,可以完全脱耦的。在TDengine系统中,有两种不同类型的数据,一种是时序数据,由TSDB模块负责;一种是元数据(Meta Data), 由MNODE负责。这两种性质不同的数据都需要同步功能。数据复制模块通过不同的实例启动配置参数,为这两种类型数据都提供同步功能。 + +在阅读本文之前,请先阅读《[TDengine 2.0 整体架构](/tdinternal/arch/)》,了解TDengine的集群设计和基本概念 + +特别注明:本文中提到数据更新操作包括数据的增加、删除与修改。 + +## 基本概念和定义 + +TDengine里存在vnode, mnode, vnode用来存储时序数据,mnode用来存储元数据。但从同步数据复制的模块来看,两者没有本质的区别,因此本文里的虚拟节点不仅包括vnode, 也包括mnode, vgroup也指mnode group, 除非特别注明。 + +**版本(version)**: + +一个虚拟节点组里多个虚拟节点互为备份,来保证数据的有效与可靠,是依靠虚拟节点组的数据版本号来维持的。TDengine2.0设计里,对于版本的定义如下:客户端发起增加、删除、修改的流程,无论是一条记录还是多条,只要是在一个请求里,这个数据更新请求被TDengine的一个虚拟节点收到后,经过合法性检查后,可以被写入系统时,就会被分配一个版本号。这个版本号在一个虚拟节点里从1开始,是单调连续递增的。无论这条记录是采集的时序数据还是meta data, 一样处理。当Master转发一个写入请求到slave时,必须带上版本号。一个虚拟节点将一数据更新请求写入WAL时,需要带上版本号。 + +不同虚拟节点组的数据版本号是完全独立的,互不相干的。版本号本质上是数据更新记录的transaction ID,但用来标识数据集的版本。 + +**角色(role):** + +一个虚拟节点可以是master, slave, unsynced或offline状态。 + +- master: 具有最新的数据,容许客户端往里写入数据,一个虚拟节点组,至多一个master. +- slave:与master是同步的,但不容许客户端往里写入数据,根据配置,可以容许客户端对其进行查询。 +- unsynced: 节点处于非同步状态,比如虚拟节点刚启动、或与其他虚拟节点的连接出现故障等。处于该状态时,该虚拟节点既不能提供写入,也不能提供查询服务。 +- offline: 由于宕机或网络原因,无法访问到某虚拟节点时,其他虚拟节点将该虚拟节点标为离线。但请注意,该虚拟节点本身的状态可能是unsynced或其他,但不会是离线。 + +**Quorum:** + +指数据写入成功所需要的确认数。对于异步复制,quorum设为1,具有master角色的虚拟节点自己确认即可。对于同步复制,需要至少大于等于2。原则上,Quorum >=1 并且 Quorum <= replication(副本数)。这个参数在启动一个同步模块实例时需要提供。 + +**WAL:** + +TDengine的WAL(Write Ahead Log)与cassandra的commit log, mySQL的bin log, Postgres的WAL没本质区别。没有写入数据库文件,还保存在内存的数据都会先存在WAL。当数据已经成功写入数据库数据文件,相应的WAL会被删除。但需要特别指明的是,在TDengine系统里,有几点: + +- 每个虚拟节点有自己独立的wal +- WAL里包含而且仅仅包含来自客户端的数据更新操作,每个更新操作都会被打上一个版本号 + +**复制实例:** + +复制模块只是一可执行的代码,复制实例是指正在运行的复制模块的一个实例,一个节点里,可以存在多个实例。原则上,一个节点有多少虚拟节点,就可以启动多少实例。对于副本数为1的场景,应用可以决定是否需要启动同步实例。应用启动一个同步模块的实例时,需要提供的就是虚拟节点组的配置信息,包括: + +- 虚拟节点个数,即replication number +- 各虚拟节点所在节点的信息,包括node的end point +- quorum, 需要的数据写入成功的确认数 +- 虚拟节点的初始版本号 + +## 数据复制模块的基本工作原理 + +TDengine采取的是Master-Slave模式进行同步,与流行的RAFT一致性算法比较一致。总结下来,有几点: + +1. 一个vgroup里有一到多个虚拟节点,每个虚拟节点都有自己的角色 +2. 客户端只能向角色是master的虚拟节点发起数据更新操作,因为master具有最新版本的数据,如果向非Master发起数据更新操作,会直接收到错误 +3. 客户端可以向master, 也可以向角色是Slave的虚拟节点发起查询操作,但不能对unsynced的虚拟节点发起任何操作 +4. 如果master不存在,这个vgroup是不能对外提供数据更新和查询服务的 +5. master收到客户端的数据更新操作时,会将其转发给slave节点 +6. 一个虚拟节点的版本号比master低的时候,会发起数据恢复流程,成功后,才会成为slave + +数据实时复制有三个主要流程:选主、数据转发、数据恢复。后续做详细讨论。 + +## 虚拟节点之间的网络连接 + +虚拟节点之间通过TCP进行连接,节点之间的状态交换、数据包的转发都是通过这个TCP连接(peerFd)进行。为避免竞争,两个虚拟节点之间的TCP连接,总是由IP地址(UINT32)小的节点作为TCP客户端发起。一旦TCP连接被中断,虚拟节点能通过TCP socket自动检测到,将对方标为offline。如果监测到任何错误(比如数据恢复流程),虚拟节点将主动重置该连接。 + +一旦作为客户端的节点连接不成或中断,它将周期性的每隔一秒钟去试图去连接一次。因为TCP本身有心跳机制,虚拟节点之间不再另行提供心跳。 + +如果一个unsynced节点要发起数据恢复流程,它与Master将建立起专有的TCP连接(syncFd)。数据恢复完成后,该连接会被关闭。而且为限制资源的使用,系统只容许一定数量(配置参数tsMaxSyncNum)的数据恢复的socket存在。如果超过这个数字,系统会将新的数据恢复请求延后处理。 + +任意一个节点,无论有多少虚拟节点,都会启动而且只会启动一个TCP server, 来接受来自其他虚拟节点的上述两类TCP的连接请求。当TCP socket建立起来,客户端侧发送的消息体里会带有vgId(全局唯一的vgroup ID), TCP 服务器侧会检查该vgId是否已经在该节点启动运行。如果已经启动运行,就接受其请求。如果不存在,就直接将连接请求关闭。在TDengine代码里,mnode group的vgId设置为1。 + +## 选主流程 + +当同一组的两个虚拟节点之间(vnode A, vnode B)建立连接后,他们互换status消息。status消息里包含本地存储的同一虚拟节点组内所有虚拟节点的role和version。 + +如果一个虚拟节点(vnode A)检测到与同一虚拟节点组内另外一虚拟节点(vnode B)的连接中断,vnode A将立即把vnode B的role设置为offline。无论是接收到另外一虚拟节点发来的status消息,还是检测与另外一虚拟节点的连接中断,该虚拟节点都将进入状态处理流程。状态处理流程的规则如下: + +1. 如果检测到在线的节点数没有超过一半,则将自己的状态设置为unsynced. +2. 如果在线的虚拟节点数超过一半,会检查master节点是否存在,如果存在,则会决定是否将自己状态改为slave或启动数据恢复流程。 +3. 如果master不存在,则会检查自己保存的各虚拟节点的状态信息与从另一节点接收到的是否一致,如果一致,说明节点组里状态已经稳定一致,则会触发选举流程。如果不一致,说明状态还没趋于一致,即使master不存在,也不进行选主。由于要求状态信息一致才进行选举,每个虚拟节点根据同样的信息,会选出同一个虚拟节点做master,无需投票表决。 +4. 自己的状态是根据规则自己决定并修改的,并不需要其他节点同意,包括成为master。一个节点无权修改其他节点的状态。 +5. 如果一个虚拟节点检测到自己或其他虚拟节点的role发生改变,该节点会广播它自己保存的各个虚拟节点的状态信息(role和version)。 + +具体的流程图如下: + +![replica-master.png](/img/architecture/replica-master.png) + +选择Master的具体规则如下: + +1. 如果只有一个副本,该副本永远就是master +2. 所有副本都在线时,版本最高的被选为master +3. 在线的虚拟节点数过半,而且有虚拟节点是slave的话,该虚拟节点自动成为master +4. 对于2和3,如果多个虚拟节点满足成为master的要求,那么虚拟节点组的节点列表里,最前面的选为master + +按照上面的规则,如果所有虚拟节点都是unsynced(比如全部重启),只有所有虚拟节点上线,才能选出master,该虚拟节点组才能开始对外提供服务。当一个虚拟节点的role发生改变时,sync模块回通过回调函数notifyRole通知应用。 + +## 数据转发流程 + +如果vnode A是master, vnode B是slave, vnode A能接受客户端的写请求,而vnode B不能。当vnode A收到写的请求后,遵循下面的流程: + +![replica-forward.png](/img/architecture/replica-forward.png) + +1. 应用对写请求做基本的合法性检查,通过,则给该请求包打上一个版本号(version, 单调递增) +2. 应用将打上版本号的写请求封装一个WAL Head, 写入WAL(Write Ahead Log) +3. 应用调用API syncForwardToPeer,如果vnode B是slave状态,sync模块将包含WAL Head的数据包通过Forward消息发送给vnode B,否则就不转发。 +4. vnode B收到Forward消息后,调用回调函数writeToCache, 交给应用处理 +5. vnode B应用在写入成功后,都需要调用syncConfirmForward通知sync模块已经写入成功。 +6. 如果quorum大于1,vnode B需要等待应用的回复确认,收到确认后,vnode B发送Forward Response消息给node A。 +7. 如果quorum大于1,vnode A需要等待vnode B或其他副本对Forward消息的确认。 +8. 如果quorum大于1,vnode A收到quorum-1条确认消息后,调用回调函数confirmForward,通知应用写入成功。 +9. 如果quorum为1,上述6,7,8步不会发生。 +10. 如果要等待slave的确认,master会启动2秒的定时器(可配置),如果超时,则认为失败。 + +对于回复确认,sync模块提供的是异步回调函数,因此APP在调用syncForwardToPeer之后,无需等待,可以处理下一个操作。在Master与Slave的TCP连接管道里,可能有多个Forward消息,这些消息是严格按照应用提供的顺序排好的。对于Forward Response也是一样,TCP管道里存在多个,但都是排序好的。这个顺序,SYNC模块并没有做特别的事情,是由APP单线程顺序写来保证的(TDengine里每个vnode的写数据,都是单线程)。 + +## 数据恢复流程 + +如果一虚拟节点(vnode B) 处于unsynced状态,master存在(vnode A),而且其版本号比master的低,它将立即启动数据恢复流程。在理解恢复流程时,需要澄清几个关于文件的概念和处理规则。 + +1. 每个文件(无论是archived data的file还是wal)都有一个index, 这需要应用来维护(vnode里,该index就是fileId*3 + 0/1/2, 对应data, head与last三个文件)。如果index为0,表示系统里最老的数据文件。对于mode里的文件,数量是固定的,对应于acct, user, db, table等文件。 +2. 任何一个数据文件(file)有名字、大小,还有一个magic number。只有文件名、大小与magic number一致时,两个文件才判断是一样的,无需同步。Magic number可以是checksum, 也可以是简单的文件大小。怎么计算magic,换句话说,如何检测数据文件是否有效,完全由应用决定。 +3. 文件名的处理有点复杂,因为每台服务器的路径可能不一致。比如node A的TDengine的数据文件存放在 /etc/taos目录下,而node B的数据存放在 /home/jhtao目录下。因此同步模块需要应用在启动一个同步实例时提供一个path,这样两台服务器的绝对路径可以不一样,但仍然可以做对比,做同步。 +4. 当sync模块调用回调函数getFileInfo获得数据文件信息时,有如下的规则 + * index 为0,表示获取最老的文件,同时修改index返回给sync模块。如果index不为0,表示获取指定位置的文件。 + * 如果name为空,表示sync想获取位于index位置的文件信息,包括magic, size。Master节点会这么调用 + * 如果name不为空,表示sync想获取指定文件名和index的信息,slave节点会这么调用 + * 如果某个index的文件不存在,magic返回0,表示文件已经是最后一个。因此整个系统里,文件的index必须是连续的一段整数。 +5. 当sync模块调用回调函数getWalInfo获得wal信息时,有如下规则 + * index为0,表示获得最老的WAL文件, 返回时,index更新为具体的数字 + * 如果返回0,表示这是最新的一个WAL文件,如果返回值是1,表示后面还有更新的WAL文件 + * 返回的文件名为空,那表示没有WAL文件 +6. 无论是getFileInfo, 还是getWalInfo, 只要获取出错(不是文件不存在),返回-1即可,系统会报错,停止同步 + +整个数据恢复流程分为两大步骤,第一步,先恢复archived data(file), 然后恢复wal。具体流程如下: + +![replica-restore.png](/img/architecture/replica-restore.png) + +1. 通过已经建立的TCP连接,发送sync req给master节点 +2. master收到sync req后,以client的身份,向vnode B主动建立一新的专用于同步的TCP连接(syncFd) +3. 新的TCP连接建立成功后,master将开始retrieve流程,对应的,vnode B将同步启动restore流程 +4. Retrieve/Restore流程里,先处理所有archived data (vnode里的data, head, last文件),后处理WAL data。 +5. 对于archived data,master将通过回调函数getFileInfo获取数据文件的基本信息,包括文件名、magic以及文件大小。 +6. master 将获得的文件名、magic以及文件大小发给vnode B +7. vnode B将回调函数getFile获得magic和文件大小,如果两者一致,就认为无需同步,如果两者不一致 ,就认为需要同步。vnode B将结果通过消息FileAck发回master +8. 如果文件需要同步,master就调用sendfile把整个文件发往vnode B +9. 如果文件不需要同步,master(vnode A)就重复5,6,7,8,直到所有文件被处理完 + +对于WAL同步,流程如下: + +1. master节点调用回调函数getWalInfo,获取WAL的文件名。 +2. 如果getWalInfo返回值大于0,表示该文件还不是最后一个WAL,因此master调用sendfile一下把该文件发送给vnode B +3. 如果getWalInfo返回时为0,表示该文件是最后一个WAL,因为文件可能还处于写的状态中,sync模块要根据WAL Head的定义逐条读出记录,然后发往vnode B。 +4. vnode A读取TCP连接传来的数据,按照WAL Head,逐条读取,如果版本号比现有的大,调用回调函数writeToCache,交给应用处理。如果小,直接扔掉。 +5. 上述流程循环,直到所有WAL文件都被处理完。处理完后,master就会将新来的数据包通过Forward消息转发给slave。 + +从同步文件启动起,sync模块会通过inotify监控所有处理过的file以及wal。一旦发现被处理过的文件有更新变化,同步流程将中止,会重新启动。因为有可能落盘操作正在进行(比如历史数据导入,内存数据落盘),把已经处理过的文件进行了修改,需要重新同步才行。 + +对于最后一个WAL (LastWal)的处理逻辑有点复杂,因为这个文件往往是打开写的状态,有很多场景需要考虑,比如: + +- LastWal文件size在增长,需要重新读; +- LastWal文件虽然已经打开写,但内容为空; +- LastWal文件已经被关闭,应用生成了新的Last WAL文件; +- LastWal文件没有被关闭,但数据落盘的原因,没有读到完整的一条记录; +- LastWal文件没有被关闭,但数据落盘的原因,还有部分记录暂时读取不到; + +sync模块通过inotify监控LastWal文件的更新和关闭操作。而且在确认已经尽可能读完LastWal的数据后,会将对方同步状态设置为SYNC_CACHE。该状态下,master节点会将新的记录转发给vnode B,而此时vnode B并没有完成同步,需要把这些转发包先存在recv buffer里,等WAL处理完后,vnode A再把recv buffer里的数据包通过回调writeToCache交给应用处理。 + +等vnode B把这些buffered forwards处理完,同步流程才算结束,vnode B正式变为slave。 + +## Master分布均匀性问题 + +因为Master负责写、转发,消耗的资源会更多,因此Master在整个集群里分布均匀比较理想。 + +但在TDengine的设计里,如果多个虚拟节点都符合master条件,TDengine选在列表中最前面的做Master, 这样是否导致在集群里,Master数量的分布不均匀问题呢?这取决于应用的设计。 + +给一个具体例子,系统里仅仅有三个节点,IP地址分别为IP1, IP2, IP3. 在各个节点上,TDengine创建了多个虚拟节点组,每个虚拟节点组都有三个副本。如果三个副本的顺序在所有虚拟节点组里都是IP1, IP2, IP3, 那毫无疑问,master将集中在IP1这个节点,这是我们不想看到的。 + +但是,如果在创建虚拟节点组时,增加随机性,这个问题就不存在了。比如在vgroup 1, 顺序是IP1, IP2, IP3, 在vgroup 2里,顺序是IP2, IP3, IP1, 在vgroup 3里,顺序是IP3, IP1, IP2。最后master的分布会是均匀的。 + +因此在创建一个虚拟节点组时,应用需要保证节点的顺序是round robin或完全随机。 + +## 少数虚拟节点写入成功的问题 + +在某种情况下,写入成功的确认数大于0,但小于配置的Quorum, 虽然有虚拟节点数据更新成功,master仍然会认为数据更新失败,并通知客户端写入失败。 + +这个时候,系统存在数据不一致的问题,因为有的虚拟节点已经写入成功,而有的写入失败。一个处理方式是,Master重置(reset)与其他虚拟节点的连接,该虚拟节点组将自动进入选举流程。按照规则,已经成功写入数据的虚拟节点将成为新的master,组内的其他虚拟节点将从master那里恢复数据。 + +因为写入失败,客户端会重新写入数据。但对于TDengine而言,是OK的。因为时序数据都是有时间戳的,时间戳相同的数据更新操作,第一次会执行,但第二次会自动扔掉。对于Meta Data(增加、删除库、表等等)的操作,也是OK的。一张表、库已经被创建或删除,再创建或删除,不会被执行的。 + +在TDengine的设计里,虚拟节点与虚拟节点之间,是一个TCP连接,是一个pipeline,数据块一个接一个按顺序在这个pipeline里等待处理。一旦某个数据块的处理失败,这个连接会被重置,后续的数据块的处理都会失败。因此不会存在Pipeline里一个数据块更新失败,但下一个数据块成功的可能。 + +## Split Brain的问题 + +选举流程中,有个强制要求,那就是一定有超过半数的虚拟节点在线。但是如果replication正好是偶数,这个时候,完全可能存在splt brain问题。 + +为解决这个问题,TDengine提供Arbitrator的解决方法。Arbitrator是一个节点,它的任务就是接受任何虚拟节点的连接请求,并保持它。 + +在启动复制模块实例时,在配置参数中,应用可以提供Arbitrator的IP地址。如果是奇数个副本,复制模块不会与这个arbitrator去建立连接,但如果是偶数个副本,就会主动去建立连接。 + +Arbitrator的程序tarbitrator.c在复制模块的同一目录, 编译整个系统时,会在bin目录生成。命令行参数“-?”查看可以配置的参数,比如绑定的IP地址,监听的端口号。 + +## 与RAFT相比的异同 + +数据一致性协议流行的有两种,Paxos与Raft. 本设计的实现与Raft有很多类同之处,下面做一些比较 + +相同之处: + +- 三大流程一致:Raft里有Leader election, replication, safety,完全对应TDengine的选举、数据转发、数据恢复三个流程。 +- 节点状态定义一致:Raft里每个节点有Leader, Follower, Candidate三个状态,TDengine里是Master, Slave, Unsynced, Offline。多了一个offlince, 但本质上是一样的,因为offline是外界看一个节点的状态,但该节点本身是处于master, slave 或unsynced的。 +- 数据转发流程完全一样,Master(leader)需要等待回复确认。 +- 数据恢复流程几乎一样,Raft没有涉及历史数据同步问题,只考虑了WAL数据同步。 + +不同之处: + +- 选举流程不一样:Raft里任何一个节点是candidate时,主动向其他节点发出vote request,如果超过半数回答Yes,这个candidate就成为Leader,开始一个新的term。而TDengine的实现里,节点上线、离线或角色改变都会触发状态消息在节点组内传播,等节点组里状态稳定一致之后才触发选举流程,因为状态稳定一致,基于同样的状态信息,每个节点做出的决定会是一致的,一旦某个节点符合成为master的条件,无需其他节点认可,它会自动将自己设为master。TDengine里,任何一个节点检测到其他节点或自己的角色发生改变,就会向节点组内其他节点进行广播。Raft里不存在这样的机制,因此需要投票来解决。 +- 对WAL的一条记录,Raft用term + index来做唯一标识。但TDengine只用version(类似index),在TDengine实现里,仅仅用version是完全可行的, 因为TDengine的选举机制,没有term的概念。 + +如果整个虚拟节点组全部宕机,重启,但不是所有虚拟节点都上线,这个时候TDengine是不会选出master的,因为未上线的节点有可能有最高version的数据。而RAFT协议,只要超过半数上线,就会选出Leader。 + +## Meta Data的数据复制 + +TDengine里存在时序数据,也存在Meta Data。Meta Data对数据的可靠性要求更高,那么TDengine设计能否满足要求呢?下面做个仔细分析。 + +TDengine里Meta Data包括以下: + +- account 信息 +- 一个account下面,可以有多个user, 多个DB +- 一个DB下面有多个vgroup +- 一个DB下面有多个stable +- 一个vgroup下面有多个table +- 整个系统有多个mnode, dnode +- 一个dnode可以有多个vnode + +上述的account, user, DB, vgroup, table, stable, mnode, dnode都有自己的属性,这些属性是TDengine自己定义的,不会开放给用户进行修改。这些Meta Data的查询都比较简单,都可以采用key-value模型进行存储。这些Meta Data还具有几个特点: + +1. 上述的Meta Data之间有一定的层级关系,比如必须先创建DB,才能创建table, stable。只有先创建dnode,才可能创建vnode, 才可能创建vgroup。因此他们创建的顺序是绝对不能错的。 +2. 在客户端应用的数据更新操作得到TDengine服务器侧确认后,所执行的数据更新操作绝对不能丢失。否则会造成客户端应用与服务器的数据不一致。 +3. 上述的Meta Data是容许重复操作的。比如插入新记录后,再插入一次,删除一次后,再删除一次,更新一次后,再更新一次,不会对系统产生任何影响,不会改变系统任何状态。 + +对于特点1,本设计里,数据的写入是单线程的,按照到达的先后顺序,给每个数据更新操作打上版本号,版本号大的记录一定是晚于版本号小的写入系统,数据写入顺序是100%保证的,绝对不会让版本号大的记录先写入。复制过程中,数据块的转发也是严格按照顺序进行的,因此TDengine的数据复制设计是能保证Meta Data的创建顺序的。 + +对于特点2,只要Quorum数设置等于replica,那么一定能保证回复确认过的数据更新操作不会在服务器侧丢失。即使某节点永不起来,只要超过一半的节点还是online, 查询服务不会受到任何影响。这时,如果某个节点离线超过一定时长,系统可以自动补充新的节点,以保证在线的节点数在绝大部分时间是100%的。 + +对于特点3,完全可能发生,服务器确实持久化存储了某一数据更新操作,但客户端应用出了问题,认为操作不成功,它会重新发起操作。但对于Meta Data而言,没有关系,客户端可以再次发起同样的操作,不会有任何影响。 + +总结来看,只要quorum设置大于一,本数据复制的设计是能满足Meta Data的需求的。目前,还没有发现漏洞。 diff --git a/docs-cn/21-tdinternal/03-taosd.md b/docs-cn/21-tdinternal/03-taosd.md new file mode 100644 index 0000000000000000000000000000000000000000..6a5734102c85db291339ce93a2231cb8196053f6 --- /dev/null +++ b/docs-cn/21-tdinternal/03-taosd.md @@ -0,0 +1,119 @@ +--- +sidebar_label: taosd 的设计 +title: taosd的设计 +--- + +逻辑上,TDengine 系统包含 dnode,taosc 和 App,dnode 是服务器侧执行代码 taosd 的一个运行实例,因此 taosd 是 TDengine 的核心,本文对 taosd 的设计做一简单的介绍,模块内的实现细节请见其他文档。 + +## 系统模块图 + +taosd 包含 rpc,dnode,vnode,tsdb,query,cq,sync,wal,mnode,http,monitor 等模块,具体如下图: + +![modules.png](/img/architecture/modules.png) + +taosd 的启动入口是 dnode 模块,dnode 然后启动其他模块,包括可选配置的 http,monitor 模块。taosc 或 dnode 之间交互的消息都是通过 rpc 模块进行,dnode 模块根据接收到的消息类型,将消息分发到 vnode 或 mnode 的消息队列,或由 dnode 模块自己消费。dnode 的工作线程(worker)消费消息队列里的消息,交给 mnode 或 vnode 进行处理。下面对各个模块做简要说明。 + +## RPC 模块 + +该模块负责 taosd 与 taosc,以及其他数据节点之间的通讯。TDengine 没有采取标准的 HTTP 或 gRPC 等第三方工具,而是实现了自己的通讯模块 RPC。 + +考虑到物联网场景下,数据写入的包一般不大,因此除支持 TCP 连接之外,RPC 还支持 UDP 连接。当数据包小于 15K 时,RPC 将采用 UDP 方式进行连接,否则将采用 TCP 连接。对于查询类的消息,RPC 不管包的大小,总是采取 TCP 连接。对于 UDP 连接,RPC 实现了自己的超时、重传、顺序检查等机制,以保证数据可靠传输。 + +RPC 模块还提供数据压缩功能,如果数据包的字节数超过系统配置参数 compressMsgSize,RPC 在传输中将自动压缩数据,以节省带宽。 + +为保证数据的安全和数据的 integrity,RPC 模块采用 MD5 做数字签名,对数据的真实性和完整性进行认证。 + +## DNODE 模块 + +该模块是整个 taosd 的入口,它具体负责如下任务: + +- 系统的初始化,包括 + - 从文件 taos.cfg 读取系统配置参数,从文件 dnodeCfg.json 读取数据节点的配置参数; + - 启动 RPC 模块,并建立起与 taosc 通讯的 server 连接,与其他数据节点通讯的 server 连接; + - 启动并初始化 dnode 的内部管理,该模块将扫描该数据节点已有的 vnode ,并打开它们; + - 初始化可配置的模块,如 mnode,http,monitor 等。 +- 数据节点的管理,包括 + - 定时的向 mnode 发送 status 消息,报告自己的状态; + - 根据 mnode 的指示,创建、改变、删除 vnode; + - 根据 mnode 的指示,修改自己的配置参数; +- 消息的分发、消费,包括 + - 为每一个 vnode 和 mnode 的创建并维护一个读队列、一个写队列; + - 将从 taosc 或其他数据节点来的消息,根据消息类型,将其直接分发到不同的消息队列,或由自己的管理模块直接消费; + - 维护一个读的线程池,消费读队列的消息,交给 vnode 或 mnode 处理。为支持高并发,一个读线程(worker)可以消费多个队列的消息,一个读队列可以由多个 worker 消费; + - 维护一个写的线程池,消费写队列的消息,交给 vnode 或 mnode 处理。为保证写操作的序列化,一个写队列只能由一个写线程负责,但一个写线程可以负责多个写队列。 + +taosd 的消息消费由 dnode 通过读写线程池进行控制,是系统的中枢。该模块内的结构体图如下: + +![dnode.png](/img/architecture/dnode.png) + +## VNODE 模块 + +vnode 是一独立的数据存储查询逻辑单元,但因为一个 vnode 只能容许一个 DB ,因此 vnode 内部没有 account,DB,user 等概念。为实现更好的模块化、封装以及未来的扩展,它有很多子模块,包括负责存储的 TSDB,负责查询的 query,负责数据复制的 sync,负责数据库日志的的 WAL,负责连续查询的 cq(continuous query),负责事件触发的流计算的 event 等模块,这些子模块只与 vnode 模块发生关系,与其他模块没有任何调用关系。模块图如下: + +![vnode.png](/img/architecture/vnode.png) + +vnode 模块向下,与 dnodeVRead,dnodeVWrite 发生互动,向上,与子模块发生互动。它主要的功能有: + +- 协调各个子模块的互动。各个子模块之间都不直接调用,都需要通过 vnode 模块进行; +- 对于来自 taosc 或 mnode 的写操作,vnode 模块将其分解为写日志(WAL),转发(sync),本地存储(TSDB)子模块的操作; +- 对于查询操作,分发到 query 模块进行。 + +一个数据节点里有多个 vnode,因此 vnode 模块是有多个运行实例的。每个运行实例是完全独立的。 + +vnode 与其子模块是通过 API 直接调用,而不是通过消息队列传递。而且各个子模块只与 vnode 模块有交互,不与 dnode,rpc 等模块发生任何直接关联。 + +## MNODE 模块 + +mnode 是整个系统的大脑,负责整个系统的资源调度,负责 meta data 的管理与存储。 + +一个运行的系统里,只有一个 mnode,但它有多个副本(由系统配置参数 numOfMnodes 控制)。这些副本分布在不同的 dnode 里,目的是保证系统的高可靠运行。副本之间的数据复制是采用同步而非异步的方式,以确保数据的一致性,确保数据不会丢失。这些副本会自动选举一个 Master,其他副本是 slave。所有数据更新类的操作,都只能在 master 上进行,而查询类的可以在 slave 节点上进行。代码实现上,同步模块与 vnode 共享,但 mnode 被分配一个特殊的 vgroup ID: 1,而且 quorum 大于 1。整个集群系统是由多个 dnode 组成的,运行的 mnode 的副本数不可能超过 dnode 的个数,但不会超过配置的副本数。如果某个 mnode 副本宕机一段时间,只要超过半数的 mnode 副本仍在运行,运行的 mnode 会自动根据整个系统的资源情况,在其他 dnode 里再启动一个 mnode,以保证运行的副本数。 + +各个 dnode 通过信息交换,保存有 mnode 各个副本的 End Point 列表,并向其中的 master 节点定时(间隔由系统配置参数 statusInterval 控制)发送 status 消息,消息体里包含该 dnode 的 CPU、内存、剩余存储空间、vnode 个数,以及各个 vnode 的状态(存储空间、原始数据大小、记录条数、角色等)。这样 mnode 就了解整个系统的资源情况,如果用户创建新的表,就可以决定需要在哪个 dnode 创建;如果增加或删除 dnode,或者监测到某 dnode 数据过热、或离线太长,就可以决定需要挪动那些 vnode,以实现负载均衡。 + +mnode 里还负责 account,user,DB,stable,table,vgroup,dnode 的创建、删除与更新。mnode 不仅把这些 entity 的 meta data 保存在内存,还做持久化存储。但为节省内存,各个表的标签值不保存在 mnode(保存在 vnode),而且子表不维护自己的 schema,而是与 stable 共享。为减小 mnode 的查询压力,taosc 会缓存 table、stable 的 schema。对于查询类的操作,各个 slave mnode 也可以提供,以减轻 master 压力。 + +## TSDB 模块 + +TSDB 模块是 vnode 中的负责快速高并发地存储和读取属于该 vnode 的表的元数据及采集的时序数据的引擎。除此之外,TSDB 还提供了表结构的修改、表标签值的修改等功能。TSDB 提供 API 供 vnode 和 query 等模块调用。TSDB 中存储了两类数据,1:元数据信息;2:时序数据 + +### 元数据信息 + +TSDB 中存储的元数据包含属于其所在的 vnode 中表的类型,schema 的定义等。对于超级表和超级表下的子表而言,又包含了 tag 的 schema 定义以及子表的 tag 值等。对于元数据信息而言,TSDB 就相当于一个全内存的 KV 型数据库,属于该 vnode 的表对象全部在内存中,方便快速查询表的信息。除此之外,TSDB 还对其中的子表,按照 tag 的第一列取值做了全内存的索引,大大加快了对于标签的过滤查询。TSDB 中的元数据的最新状态在落盘时,会以追加(append-only)的形式,写入到 meta 文件中。meta 文件只进行追加操作,即便是元数据的删除,也会以一条记录的形式写入到文件末尾。TSDB 也提供了对于元数据的修改操作,如表 schema 的修改,tag schema 的修改以及 tag 值的修改等。 + +### 时序数据 + +每个 TSDB 在创建时,都会事先分配一定量的内存缓冲区,且内存缓冲区的大小可配可修改。表采集的时序数据,在写入 TSDB 时,首先以追加的方式写入到分配的内存缓冲区中,同时建立基于时间戳的内存索引,方便快速查询。当内存缓冲区的数据积累到一定的程度时(达到内存缓冲区总大小的 1/3),则会触发落盘操作,将缓冲区中的数据持久化到硬盘文件上。时序数据在内存缓冲区中是以行(row)的形式存储的。 + +而时序数据在写入到 TSDB 的数据文件时,是以列(column)的形式存储的。TSDB 中的数据文件包含多个数据文件组,每个数据文件组中又包含 .head、.data 和 .last 三个文件,如(v2f1801.head、v2f1801.data、v2f1801.last)数据文件组。TSDB 中的数据文件组是按照时间跨度进行分片的,默认是 10 天一个文件组,且可通过配置文件及建库选项进行配置。分片的数据文件组又按照编号递增排列,方便快速定位某一时间段的时序数据,高效定位数据文件组。时序数据在 TSDB 的数据文件中是以块的形式进行列式存储的,每个块中只包含一张表的数据,且数据在一个块中是按照时间顺序递增排列的。在一个数据文件组中,.head 文件负责存储数据块的索引及统计信息,如每个块的位置,压缩算法,时间戳范围等。存储在 .head 文件中一张表的索引信息是按照数据块中存储的数据的时间递增排列的,方便进行折半查找等工作。.head 和 .last 文件是存储真实数据块的文件,若数据块中的数据累计到一定程度,则会写入 .data 文件中,否则,会写入 .last 文件中,等待下次落盘时合并数据写入 .data 文件中,从而大大减少文件中块的个数,避免数据的过度碎片化。 + +## Query 模块 + +该模块负责整体系统的查询处理。客户端调用该该模块进行 SQL 语法解析,并将查询或写入请求发送到 vnode ,同时负责针对超级表的查询进行二阶段的聚合操作。在 vnode 端,该模块调用 TSDB 模块读取系统中存储的数据进行查询处理。query 模块还定义了系统能够支持的全部查询函数,查询函数的实现机制与查询框架无耦合,可以在不修改查询流程的情况下动态增加查询函数。详细的设计请参见《TDengine 2.0 查询模块设计》。 + +## SYNC 模块 + +该模块实现数据的多副本复制,包括 vnode 与 mnode 的数据复制,支持异步和同步两种复制方式,以满足 meta data 与时序数据不同复制的需求。因为它为 mnode 与 vnode 共享,系统为 mnode 副本预留了一个特殊的 vgroup ID:1。因此 vnode group 的 ID 是从 2 开始的。 + +每个 vnode/mnode 模块实例会有一对应的 sync 模块实例,他们是一一对应的。详细设计请见[TDengine 2.0 数据复制模块设计](/tdinternal/replica/) + +## WAL 模块 + +该模块负责将新插入的数据写入 write ahead log(WAL),为 vnode,mnode 共享。以保证服务器 crash 或其他故障,能从 WAL 中恢复数据。 + +每个 vnode/mnode 模块实例会有一对应的 WAL 模块实例,是完全一一对应的。WAL 的落盘操作由两个参数 walLevel,fsync 控制。看具体场景,如果要 100% 保证数据不会丢失,需要将 walLevel 配置为 2,fsync 设置为 0,每条数据插入请求,都会实时落盘后,才会给应用确认 + +## HTTP 模块 + +该模块负责处理系统对外的 RESTful 接口,可以通过配置,由 dnode 启动或停止 。(仅 2.2 及之前的版本中存在) + +该模块将接收到的 RESTful 请求,做了各种合法性检查后,将其变成标准的 SQL 语句,通过 taosc 的异步接口,将请求发往整个系统中的任一 dnode 。收到处理后的结果后,再翻译成 HTTP 协议,返回给应用。 + +如果 HTTP 模块启动,就意味着启动了一个 taosc 的实例。任一一个 dnode 都可以启动该模块,以实现对 RESTful 请求的分布式处理。 + +## Monitor 模块 + +该模块负责检测一个 dnode 的运行状态,可以通过配置,由 dnode 启动或停止。原则上,每个 dnode 都应该启动一个 monitor 实例。 + +Monitor 采集 TDengine 里的关键操作,比如创建、删除、更新账号、表、库等,而且周期性的收集 CPU、内存、网络等资源的使用情况(采集周期由系统配置参数 monitorInterval 控制)。获得这些数据后,monitor 模块将采集的数据写入系统的日志库(DB 名字由系统配置参数 monitorDbName 控制)。 + +Monitor 模块使用 taosc 来将采集的数据写入系统,因此每个 monitor 实例,都有一个 taosc 运行实例。 diff --git a/docs-cn/21-tdinternal/12-tsz-compress.md b/docs-cn/21-tdinternal/12-tsz-compress.md new file mode 100644 index 0000000000000000000000000000000000000000..baf5df15db3b44edc9e0bd6909e46fa84b676a0b --- /dev/null +++ b/docs-cn/21-tdinternal/12-tsz-compress.md @@ -0,0 +1,44 @@ +--- +title: TSZ 压缩算法 +--- + +TSZ 压缩算法是 TDengine 为浮点数据类型提供更加丰富的压缩功能,可以实现浮点数的有损至无损全状态压缩,相比原来在 TDengine 中原有压缩算法,TSZ 压缩算法压缩选项更丰富,压缩率更高,即使切到无损状态下对浮点数压缩,压缩率也会比原来的压缩算法高一倍。 + +## 适合场景 + +TSZ 压缩算法压缩率比原来的要高,但压缩时间会更长,即开启 TSZ 压缩算法写入速度会有一些下降,通常情况下会有 20% 左右的下降。影响写入速度是因为需要更多的 CPU 计算,所以从原始数据到压缩好数据的交付时间变长,导致写入速度变慢。如果您的服务器 CPU 配置很高的话,这个影响会变小甚至没有。 + +另外如果设备产生了大量的高精度浮点数,存储占用的空间非常庞大,但实际使用并不需要那么高的精度时,可以通过 TSZ 压缩的有损压缩功能,把精度压缩至指定的长度,节约存储空间。 + +总结:采集到了大量浮点数,存储时占用空间过大或出有存储空间不足,需要超高压缩率的场景。 + +## 使用步骤 + +- 检查版本支持,2.4.0.10 及之后 TDengine 的版本都支持此功能 + +- 配置选项开启功能,在 TDengine 的配置文件 taos.cfg 增加一行以下内容,打开 TSZ 功能 + +```TSZ +lossyColumns float|double +``` + +- 根据自己需要配置其它选项,如果不配置都会按默认值处理。 + +- 重启服务,配置生效。 +- 确认功能已开启,在服务启动过程中输出的信息如果有前面配置的内容,表明功能已生效: + +```TSZ Test +02/22 10:49:27.607990 00002933 UTL lossyColumns float|double +``` + +## 注意事项 + +- 确认版本是否支持 + +- 除了服务器启动时的输出的配置成功信息外,不再会有其它的信息输出是使用的哪种压缩算法,可以通过配置前后数据库文件大小来比较效果 + +- 如果浮点数类型列较少,看整体数据文件大小效果会不太明显 + +- 此压缩产生的数据文件中浮点数据部分将不能被 2.4.0.10 以下的版本解析,即不向下兼容,使用时避免更换回旧版本,以免数据不能被读取出来。 + +- 在使用过程中允许反复开启和关闭 TSZ 压缩选项的操作,前后两种压缩算法产生的数据都能正常读取。 diff --git a/docs-cn/21-tdinternal/30-iot-big-data.md b/docs-cn/21-tdinternal/30-iot-big-data.md new file mode 100644 index 0000000000000000000000000000000000000000..a234713f883056e3d1a0dcbfe8e2e47a82865f81 --- /dev/null +++ b/docs-cn/21-tdinternal/30-iot-big-data.md @@ -0,0 +1,9 @@ +--- +title: 物联网大数据 +description: "物联网、工业互联网大数据的特点;物联网大数据平台应具备的功能和特点;通用大数据架构为什么不适合处理物联网数据;物联网、车联网、工业互联网大数据平台,为什么推荐使用 TDengine" +--- + +- [物联网、工业互联网大数据的特点](https://www.taosdata.com/blog/2019/07/09/105.html) +- [物联网大数据平台应具备的功能和特点](https://www.taosdata.com/blog/2019/07/29/542.html) +- [通用大数据架构为什么不适合处理物联网数据?](https://www.taosdata.com/blog/2019/07/09/107.html) +- [物联网、车联网、工业互联网大数据平台,为什么推荐使用 TDengine?](https://www.taosdata.com/blog/2019/07/09/109.html) diff --git a/docs-cn/21-tdinternal/_category_.yml b/docs-cn/21-tdinternal/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..c7509bf66224fa94759de9a2ae82955e2a7eb82f --- /dev/null +++ b/docs-cn/21-tdinternal/_category_.yml @@ -0,0 +1 @@ +label: 技术内幕 \ No newline at end of file diff --git a/docs-cn/21-tdinternal/index.md b/docs-cn/21-tdinternal/index.md new file mode 100644 index 0000000000000000000000000000000000000000..63a746623e0dd955f61ba887a76f8ecf7eb16972 --- /dev/null +++ b/docs-cn/21-tdinternal/index.md @@ -0,0 +1,10 @@ +--- +title: 技术内幕 +--- + +```mdx-code-block +import DocCardList from '@theme/DocCardList'; +import {useCurrentSidebarCategory} from '@docusaurus/theme-common'; + + +``` \ No newline at end of file diff --git a/docs-cn/25-application/01-telegraf.md b/docs-cn/25-application/01-telegraf.md new file mode 100644 index 0000000000000000000000000000000000000000..f63a6701eed2b4c5b98f577d5b2867ae6dada387 --- /dev/null +++ b/docs-cn/25-application/01-telegraf.md @@ -0,0 +1,82 @@ +--- +sidebar_label: TDengine + Telegraf + Grafana +title: 使用 TDengine + Telegraf + Grafana 快速搭建 IT 运维展示系统 +--- + +## 背景介绍 + +TDengine 是涛思数据专为物联网、车联网、工业互联网、IT 运维等设计和优化的大数据平台。自从 2019 年 7 月开源以来,凭借创新的数据建模设计、快捷的安装方式、易用的编程接口和强大的数据写入查询性能博得了大量时序数据开发者的青睐。 + +IT 运维监测数据通常都是对时间特性比较敏感的数据,例如: + +- 系统资源指标:CPU、内存、IO、带宽等。 +- 软件系统指标:存活状态、连接数目、请求数目、超时数目、错误数目、响应时间、服务类型及其他与业务有关的指标。 + +当前主流的 IT 运维系统通常包含一个数据采集模块,一个数据存储模块,和一个可视化显示模块。Telegraf 和 Grafana 分别是当前最流行的数据采集模块和可视化显示模块之一。而数据存储模块可供选择的软件比较多,其中 OpenTSDB 或 InfluxDB 比较流行。而 TDengine 作为新兴的时序大数据平台,具备极强的高性能、高可靠、易管理、易维护的优势。 + +本文介绍不需要写一行代码,通过简单修改几行配置文件,就可以快速搭建一个基于 TDengine + Telegraf + Grafana 的 IT 运维系统。架构如下图: + +![IT-DevOps-Solutions-Telegraf.png](/img/IT-DevOps-Solutions-Telegraf.png) + +## 安装步骤 + +### 安装 Telegraf,Grafana 和 TDengine + +安装 Telegraf、Grafana 和 TDengine 请参考相关官方文档。 + +### Telegraf + +请参考[官方文档](https://portal.influxdata.com/downloads/)。 + +### Grafana + +请参考[官方文档](https://grafana.com/grafana/download)。 + +### TDengine + +从涛思数据官网[下载](http://taosdata.com/cn/all-downloads/)页面下载最新 TDengine-server 2.4.0.x 或以上版本安装。 + +## 数据链路设置 + +### 下载 TDengine 插件到 Grafana 插件目录 + +```bash +1. wget -c https://github.com/taosdata/grafanaplugin/releases/download/v3.1.3/tdengine-datasource-3.1.3.zip +2. sudo unzip tdengine-datasource-3.1.3.zip -d /var/lib/grafana/plugins/ +3. sudo chown grafana:grafana -R /var/lib/grafana/plugins/tdengine +4. echo -e "[plugins]\nallow_loading_unsigned_plugins = tdengine-datasource\n" | sudo tee -a /etc/grafana/grafana.ini +5. sudo systemctl restart grafana-server.service +``` + +### 修改 /etc/telegraf/telegraf.conf + +配置方法,在 `/etc/telegraf/telegraf.conf` 增加如下文字,其中 `database name` 请填写希望在 TDengine 保存 Telegraf 数据的数据库名,`TDengine server/cluster host`、`username` 和 `password` 填写 TDengine 实际值: + +``` +[[outputs.http]] + url = "http://:6041/influxdb/v1/write?db=" + method = "POST" + timeout = "5s" + username = "" + password = "" + data_format = "influx" + influx_max_line_bytes = 250 +``` + +然后重启 Telegraf: + +```bash +sudo systemctl start telegraf +``` + +### 导入 Dashboard + +使用 Web 浏览器访问 `IP:3000` 登录 Grafana 界面,系统初始用户名密码为 admin/admin。 +点击左侧齿轮图标并选择 `Plugins`,应该可以找到 TDengine data source 插件图标。 +点击左侧加号图标并选择 `Import`,从 `https://github.com/taosdata/grafanaplugin/blob/master/examples/telegraf/grafana/dashboards/telegraf-dashboard-v0.1.0.json` 下载 dashboard JSON 文件后导入。之后可以看到如下界面的仪表盘: + +![IT-DevOps-Solutions-telegraf-dashboard.png](/img/IT-DevOps-Solutions-telegraf-dashboard.png) + +## 总结 + +以上演示如何快速搭建一个完整的 IT 运维展示系统。得力于 TDengine 2.4.0.0 版本中新增的 schemaless 协议解析功能,以及强大的生态软件适配能力,用户可以短短数分钟就可以搭建一个高效易用的 IT 运维系统。TDengine 强大的数据写入查询性能和其他丰富功能请参考官方文档和产品落地案例。 diff --git a/docs-cn/25-application/02-collectd.md b/docs-cn/25-application/02-collectd.md new file mode 100644 index 0000000000000000000000000000000000000000..5e6bc6577b2f4c8564e4533ced745d0b214ec748 --- /dev/null +++ b/docs-cn/25-application/02-collectd.md @@ -0,0 +1,95 @@ +--- +sidebar_label: TDengine + collectd/StatsD + Grafana +title: 使用 TDengine + collectd/StatsD + Grafana 快速搭建 IT 运维监控系统 +--- + +## 背景介绍 + +TDengine 是涛思数据专为物联网、车联网、工业互联网、IT 运维等设计和优化的大数据平台。自从 2019 年 7 月开源以来,凭借创新的数据建模设计、快捷的安装方式、易用的编程接口和强大的数据写入查询性能博得了大量时序数据开发者的青睐。 + +IT 运维监测数据通常都是对时间特性比较敏感的数据,例如: + +- 系统资源指标:CPU、内存、IO、带宽等。 +- 软件系统指标:存活状态、连接数目、请求数目、超时数目、错误数目、响应时间、服务类型及其他与业务有关的指标。 + +当前主流的 IT 运维系统通常包含一个数据采集模块,一个数据存储模块,和一个可视化显示模块。collectd / statsD 作为老牌开源数据采集工具,具有广泛的用户群。但是 collectd / StatsD 自身功能有限,往往需要配合 Telegraf、Grafana 以及时序数据库组合搭建成为完整的监控系统。而 TDengine 新版本支持多种数据协议接入,可以直接接受 collectd 和 statsD 的数据写入,并提供 Grafana dashboard 进行图形化展示。 + +本文介绍不需要写一行代码,通过简单修改几行配置文件,就可以快速搭建一个基于 TDengine + collectd / statsD + Grafana 的 IT 运维系统。架构如下图: + +![IT-DevOps-Solutions-Collectd-StatsD.png](/img/IT-DevOps-Solutions-Collectd-StatsD.png) + +## 安装步骤 + +安装 collectd, StatsD, Grafana 和 TDengine 请参考相关官方文档。 + +### 安装 collectd + +请参考[官方文档](https://collectd.org/documentation.shtml)。 + +### 安装 StatsD + +请参考[官方文档](https://github.com/statsd/statsd)。 + +### 安装 Grafana + +请参考[官方文档](https://grafana.com/grafana/download)。 + +### 安装 TDengine + +从涛思数据官网[下载](http://taosdata.com/cn/all-downloads/)页面下载最新 TDengine-server 2.4.0.x 或以上版本安装。 + +## 数据链路设置 + +### 复制 TDengine 插件到 grafana 插件目录 + +```bash +1. wget -c https://github.com/taosdata/grafanaplugin/releases/download/v3.1.3/tdengine-datasource-3.1.3.zip +2. sudo unzip tdengine-datasource-3.1.3.zip -d /var/lib/grafana/plugins/ +3. sudo chown grafana:grafana -R /var/lib/grafana/plugins/tdengine +4. echo -e "[plugins]\nallow_loading_unsigned_plugins = tdengine-datasource\n" | sudo tee -a /etc/grafana/grafana.ini +5. sudo systemctl restart grafana-server.service +``` + +### 配置 collectd + +在 `/etc/collectd/collectd.conf` 文件中增加如下内容,其中 `host` 和 `port` 请填写 TDengine 和 taosAdapter 配置的实际值: + +``` +LoadPlugin network + + Server "" "" + + +sudo systemctl start collectd +``` + +### 配置 StatsD + +在 `config.js` 文件中增加如下内容后启动 StatsD,其中 `host` 和 `port` 请填写 TDengine 和 taosAdapter 配置的实际值: + +``` +backends 部分添加 "./backends/repeater" +repeater 部分添加 { host:'', port: } +``` + +### 导入 Dashboard + +使用 Web 浏览器访问运行 Grafana 的服务器的 3000 端口 `host:3000` 登录 Grafana 界面,系统初始用户名密码为 `admin/admin`。 +点击左侧齿轮图标并选择 `Plugins`,应该可以找到 TDengine data source 插件图标。 + +#### 导入 collectd 仪表盘 + +从 https://github.com/taosdata/grafanaplugin/blob/master/examples/collectd/grafana/dashboards/collect-metrics-with-tdengine-v0.1.0.json 下载 dashboard json 文件,点击左侧加号图标并选择 `Import`,按照界面提示选择 JSON 文件导入。之后可以看到如下界面的仪表盘: + +![IT-DevOps-Solutions-collectd-dashboard.png](/img/IT-DevOps-Solutions-collectd-dashboard.png) + +#### 导入 StatsD 仪表盘 + +从 `https://github.com/taosdata/grafanaplugin/blob/master/examples/statsd/dashboards/statsd-with-tdengine-v0.1.0.json` 下载 dashboard json 文件,点击左侧加号图标并选择 `Import`,按照界面提示导入 JSON 文件。之后可以看到如下界面的仪表盘: +![IT-DevOps-Solutions-statsd-dashboard.png](/img/IT-DevOps-Solutions-statsd-dashboard.png) + +## 总结 + +TDengine 作为新兴的时序大数据平台,具备极强的高性能、高可靠、易管理、易维护的优势。得力于 TDengine 2.4.0.0 版本中新增的 schemaless 协议解析功能,以及强大的生态软件适配能力,用户可以短短数分钟就可以搭建一个高效易用的 IT 运维系统或者适配一个已存在的系统。 + +TDengine 强大的数据写入查询性能和其他丰富功能请参考官方文档和产品成功落地案例。 diff --git a/docs-cn/25-application/03-immigrate.md b/docs-cn/25-application/03-immigrate.md new file mode 100644 index 0000000000000000000000000000000000000000..012fb2a73792f764c3a18e758ed18b4f956f7af8 --- /dev/null +++ b/docs-cn/25-application/03-immigrate.md @@ -0,0 +1,423 @@ +--- +sidebar_label: OpenTSDB 迁移到 TDengine +title: OpenTSDB 应用迁移到 TDengine 的最佳实践 +--- + +作为一个分布式、可伸缩、基于 HBase 的分布式时序数据库系统,得益于其先发优势,OpenTSDB 被 DevOps 领域的人员引入并广泛地应用在了运维监控领域。但最近几年,随着云计算、微服务、容器化等新技术快速落地发展,企业级服务种类变得越来越多,架构也越来越复杂,应用运行基础环境日益多样化,给系统和运行监控带来的压力也越来越大。从这一现状出发,使用 OpenTSDB 作为 DevOps 的监控后端存储,越来越受困于其性能问题以及迟缓的功能升级,以及由此而衍生出来的应用部署成本上升和运行效率降低等问题,这些问题随着系统规模的扩大日益严重。 + +在这一背景下,为满足高速增长的物联网大数据市场和技术需求,在吸取众多传统关系型数据库、NoSQL 数据库、流计算引擎、消息队列等软件的优点之后,涛思数据自主开发出创新型大数据处理产品 TDengine。在时序大数据处理上,TDengine 有着自己独特的优势。就 OpenTSDB 当前遇到的问题来说,TDengine 能够有效解决。 + +相对于 OpenTSDB,TDengine 具有如下显著特点: + +- 数据写入和查询的性能远超 OpenTSDB; +- 针对时序数据的高效压缩机制,压缩后在磁盘上的存储空间不到 1/5; +- 安装部署非常简单,单一安装包完成安装部署,不依赖其他的第三方软件,整个安装部署过程秒级搞定; +- 提供的内建函数覆盖 OpenTSDB 支持的全部查询函数,还支持更多的时序数据查询函数、标量函数及聚合函数,支持多种时间窗口聚合、连接查询、表达式运算、多种分组聚合、用户定义排序、以及用户定义函数等高级查询功能。采用类 SQL 的语法规则,更加简单易学,基本上没有学习成本。 +- 支持多达 128 个标签,标签总长度可达到 16 KB; +- 除 REST 接口之外,还提供 C/C++、Java、Python、Go、Rust、Node.js、C#、Lua(社区贡献)、PHP(社区贡献)等多种语言的接口,支持 JDBC 等多种企业级标准连接器协议。 + +如果我们将原本运行在 OpenTSDB 上的应用迁移到 TDengine 上,不仅可以有效地降低计算和存储资源的占用、减少部署服务器的规模,还能够极大减少运行维护的成本的输出,让运维管理工作更简单、更轻松,大幅降低总拥有成本。与 OpenTSDB 一样,TDengine 也已经进行了开源,不同的是,除了单机版,后者还实现了集群版开源,被厂商绑定的顾虑一扫而空。 + +在下文中我们将就“使用最典型并广泛应用的运维监控(DevOps)场景”来说明,如何在不编码的情况下将 OpenTSDB 的应用快速、安全、可靠地迁移到 TDengine 之上。后续的章节会做更深度的介绍,以便于进行非 DevOps 场景的迁移。 + +## DevOps 应用快速迁移 + +### 1、典型应用场景 + +一个典型的 DevOps 应用场景的系统整体的架构如下图(图 1) 所示。 + +**图 1. DevOps 场景中典型架构** +![IT-DevOps-Solutions-Immigrate-OpenTSDB-Arch](/img/IT-DevOps-Solutions-Immigrate-OpenTSDB-Arch.jpg "图1. DevOps 场景中典型架构") + +在该应用场景中,包含了部署在应用环境中负责收集机器度量(Metrics)、网络度量(Metrics)以及应用度量(Metrics)的 Agent 工具、汇聚 Agent 收集信息的数据收集器,数据持久化存储和管理的系统以及监控数据可视化工具(例如:Grafana 等)。 + +其中,部署在应用节点的 Agents 负责向 collectd/Statsd 提供不同来源的运行指标,collectd/StatsD 则负责将汇聚的数据推送到 OpenTSDB 集群系统,然后使用可视化看板 Grafana 将数据可视化呈现出来。 + +### 2、迁移服务 + +- **TDengine 安装部署** + +首先是 TDengine 的安装,从官网上下载 TDengine 最新稳定版进行安装。各种安装包的使用帮助请参见博客[《TDengine 多种安装包的安装和卸载》](https://www.taosdata.com/blog/2019/08/09/566.html)。 + +注意,安装完成以后,不要立即启动 `taosd` 服务,在正确配置完成参数以后再启动。 + +- **调整数据收集器配置** + +在 TDengine 2.4 版本中,包含一个组件 taosAdapter。taosAdapter 是一个无状态、可快速弹性伸缩的组件,它可以兼容 Influxdb 的 Line Protocol 和 OpenTSDB 的 telnet/JSON 写入协议规范,提供了丰富的数据接入能力,有效的节省用户迁移成本,降低用户应用迁移的难度。 + +用户可以根据需求弹性部署 taosAdapter 实例,结合场景的需要,快速提升数据写入的吞吐量,为不同应用场景下的数据写入提供保障。 + +通过 taosAdapter,用户可以将 collectd 或 StatsD 收集的数据直接推送到 TDengine ,实现应用场景的无缝迁移,非常的轻松便捷。taosAdapter 还支持 Telegraf、Icinga、TCollector 、node_exporter 的数据接入,使用详情参考[taosAdapter](/reference/taosadapter/)。 + +如果使用 collectd,修改其默认位置 `/etc/collectd/collectd.conf` 的配置文件为指向 taosAdapter 部署的节点 IP 地址和端口。假设 taosAdapter 的 IP 地址为 192.168.1.130,端口为 6046,配置如下: + +```html +LoadPlugin write_tsdb + + + Host "192.168.1.130" Port "6046" HostTags "status=production" StoreRates + false AlwaysAppendDS false + + +``` + +即可让 collectd 将数据使用推送到 OpenTSDB 的插件方式推送到 taosAdapter, taosAdapter 将调用 API 将数据写入到 TDengine 中,从而完成数据的写入工作。如果你使用的是 StatsD 相应地调整配置文件信息。 + +- **调整看板(Dashboard)系统** + +在数据能够正常写入 TDengine 后,可以调整适配 Grafana 将写入 TDengine 的数据可视化呈现出来。获取和使用 TDengine 提供的 Grafana 插件请参考[与其他工具的连接](/third-party/grafana)。 + +TDengine 提供了默认的两套 Dashboard 模板,用户只需要将 Grafana 目录下的模板导入到 Grafana 中即可激活使用。 + +**图 2. 导入 Grafana 模板** +![](/img/IT-DevOps-Solutions-Immigrate-OpenTSDB-Dashboard.jpg "图2. 导入 Grafana 模板") + +操作完以上步骤后,就完成了将 OpenTSDB 替换成为 TDengine 的迁移工作。可以看到整个流程非常简单,不需要写代码,只需要对某些配置文件进行调整即可完成全部的迁移工作。 + +### 3、迁移后架构 + +完成迁移以后,此时的系统整体的架构如下图(图 3)所示,而整个过程中采集端、数据写入端、以及监控呈现端均保持了稳定,除了极少的配置调整外,不涉及任何重要的更改和变动。OpenTSDB 大量的应用场景均为 DevOps ,这种场景下,简单的参数设置即可完成 OpenTSDB 到 TDengine 迁移动作,使用上 TDengine 更加强大的处理能力和查询性能。 + +在绝大多数的 DevOps 场景中,如果你拥有一个小规模的 OpenTSDB 集群(3 台及以下的节点)作为 DevOps 的存储端,依赖于 OpenTSDB 为系统持久化层提供数据存储和查询功能,那么你可以安全地将其替换为 TDengine,并节约更多的计算和存储资源。在同等计算资源配置情况下,单台 TDengine 即可满足 3 ~ 5 台 OpenTSDB 节点提供的服务能力。如果规模比较大,那便需要采用 TDengine 集群。 + +如果你的应用特别复杂,或者应用领域并不是 DevOps 场景,你可以继续阅读后续的章节,更加全面深入地了解将 OpenTSDB 的应用迁移到 TDengine 的高级话题。 + +**图 3. 迁移完成后的系统架构** +![IT-DevOps-Solutions-Immigrate-TDengine-Arch](/img/IT-DevOps-Solutions-Immigrate-TDengine-Arch.jpg "图 3. 迁移完成后的系统架构") + +## 其他场景的迁移评估与策略 + +### 1、TDengine 与 OpenTSDB 的差异 + +本章将详细介绍 OpenTSDB 与 TDengine 在系统功能层面上存在的差异。阅读完本章的内容,你可以全面地评估是否能够将某些基于 OpenTSDB 的复杂应用迁移到 TDengine 上,以及迁移之后应该注意的问题。 + +TDengine 当前只支持 Grafana 的可视化看板呈现,所以如果你的应用中使用了 Grafana 以外的前端看板(例如[TSDash](https://github.com/facebook/tsdash)、[Status Wolf](https://github.com/box/StatusWolf)等),那么前端看板将无法直接迁移到 TDengine,需要将前端看板重新适配到 Grafana 才可以正常运行。 + +在 2.3.0.x 版本中,TDengine 只能够支持 collectd 和 StatsD 作为数据收集汇聚软件,当然后面会陆续提供更多的数据收集聚合软件的接入支持。如果您的收集端使用了其他类型的数据汇聚器,您的应用需要适配到这两个数据汇聚端系统,才能够将数据正常写入。除了上述两个数据汇聚端软件协议以外,TDengine 还支持通过 InfluxDB 的行协议和 OpenTSDB 的数据写入协议、JSON 格式将数据直接写入,您可以重写数据推送端的逻辑,使用 TDengine 支持的行协议来写入数据。 + +此外,如果你的应用中使用了 OpenTSDB 以下特性,在将应用迁移到 TDengine 之前你还需要了解以下注意事项: + +1. `/api/stats`:如果你的应用中使用了该项特性来监控 OpenTSDB 的服务状态,并在应用中建立了相关的逻辑来联动处理,那么这部分状态读取和获取的逻辑需要重新适配到 TDengine。TDengine 提供了全新的处理集群状态监控机制,来满足你的应用对其进行的监控和维护的需求。 +2. `/api/tree`:如果你依赖于 OpenTSDB 的该项特性来进行时间线的层级化组织和维护,那么便无法将其直接迁移至 TDengine。TDengine 采用了数据库->超级表->子表这样的层级来组织和维护时间线,归属于同一个超级表的所有的时间线在系统中同一个层级,但是可以通过不同标签值的特殊构造来模拟应用逻辑上的多级结构。 +3. `Rollup And PreAggregates`:采用了 Rollup 和 PreAggregates 需要应用来决定在合适的地方访问 Rollup 的结果,在某些场景下又要访问原始的结果,这种结构的不透明性让应用处理逻辑变得极为复杂而且完全不具有移植性。我们认为这种策略是时序数据库无法提供高性能聚合情况下的妥协与折中。TDengine 暂不支持多个时间线的自动降采样和(时间段范围的)预聚合,由于 其拥有的高性能查询处理逻辑,即使不依赖于 Rollup 和 (时间段)预聚合计算结果,也能够提供很高性能的查询响应,而且让你的应用查询处理逻辑更加简单。 +4. `Rate`: TDengine 提供了两个计算数值变化率的函数,分别是 Derivative(其计算结果与 InfluxDB 的 Derivative 行为一致)和 IRate(其计算结果与 Prometheus 中的 IRate 函数计算结果一致)。但是这两个函数的计算结果与 Rate 有细微的差别,但整体上功能更强大。此外,**OpenTSDB 提供的所有计算函数,TDengine 均有对应的查询函数支持,并且 TDengine 的查询函数功能远超过 OpenTSDB 支持的查询函数,**可以极大地简化你的应用处理逻辑。 + +通过上面的介绍,相信你应该能够了解 OpenTSDB 迁移到 TDengine 带来的变化,这些信息也有助于你正确地判断是否可以接受将应用 迁移到 TDengine 之上,体验 TDengine 提供的强大的时序数据处理能力和便捷的使用体验。 + +### 2、迁移策略 + +首先将基于 OpenTSDB 的系统进行迁移涉及到的数据模式设计、系统规模估算、数据写入端改造,进行数据分流、应用适配工作;之后将两个系统并行运行一段时间,再将历史数据迁移到 TDengine 中。当然如果你的应用中有部分功能强依赖于上述 OpenTSDB 特性,同时又不希望停止使用,可以考虑保持原有的 OpenTSDB 系统运行,同时启动 TDengine 来提供主要的服务。 + +## 数据模型设计 + +一方面,TDengine 要求其入库的数据具有严格的模式定义。另一方面,TDengine 的数据模型相对于 OpenTSDB 来说又更加丰富,多值模型能够兼容全部的单值模型的建立需求。 + +现在让我们假设一个 DevOps 的场景,我们使用了 collectd 收集设备的基础度量(metrics),包含了 memory 、swap、disk 等几个度量,其在 OpenTSDB 中的模式如下: + +| 序号 | 测量(metric) | 值名称 | 类型 | tag1 | tag2 | tag3 | tag4 | tag5 | +| ---- | -------------- | ------ | ------ | ---- | ----------- | -------------------- | --------- | ------ | +| 1 | memory | value | double | host | memory_type | memory_type_instance | source | n/a | +| 2 | swap | value | double | host | swap_type | swap_type_instance | source | n/a | +| 3 | disk | value | double | host | disk_point | disk_instance | disk_type | source | + +TDengine 要求存储的数据具有数据模式,即写入数据之前需创建超级表并指定超级表的模式。对于数据模式的建立,你有两种方式来完成此项工作:1)充分利用 TDengine 对 OpenTSDB 的数据原生写入的支持,调用 TDengine 提供的 API 将(文本行或 JSON 格式)数据写入,并自动化地建立单值模型。采用这种方式不需要对数据写入应用进行较大的调整,也不需要对写入的数据格式进行转换。 + +在 C 语言层面,TDengine 提供了 `taos_schemaless_insert()` 函数来直接写入 OpenTSDB 格式的数据(在更早版本中该函数名称是 `taos_insert_lines()`)。其代码参考示例请参见安装包目录下示例代码 schemaless.c。 + +2)在充分理解 TDengine 的数据模型基础上,结合生成数据的特点,手动方式建立 OpenTSDB 到 TDengine 的数据模型调整的映射关系。TDengine 能够支持多值模型和单值模型,考虑到 OpenTSDB 均为单值映射模型,这里推荐使用单值模型在 TDengine 中进行建模。 + +- **单值模型**。 + +具体步骤如下:将度量(metrics)的名称作为 TDengine 超级表的名称,该超级表建成后具有两个基础的数据列—时间戳(timestamp)和值(value),超级表的标签等效于 度量 的标签信息,标签数量等同于度量 的标签的数量。子表的表名采用具有固定规则的方式进行命名:`metric + '_' + tags1_value + '_' + tag2_value + '_' + tag3_value ...`作为子表名称。 + +在 TDengine 中建立 3 个超级表: + +```sql +create stable memory(ts timestamp, val float) tags(host binary(12),memory_type binary(20), memory_type_instance binary(20), source binary(20)); +create stable swap(ts timestamp, val double) tags(host binary(12), swap_type binary(20), swap_type_binary binary(20), source binary(20)); +create stable disk(ts timestamp, val double) tags(host binary(12), disk_point binary(20), disk_instance binary(20), disk_type binary(20), source binary(20)); +``` + +对于子表使用动态建表的方式创建如下所示: + +```sql +insert into memory_vm130_memory_buffered_collectd using memory tags(‘vm130’, ‘memory’, 'buffer', 'collectd') values(1632979445, 3.0656); +``` + +最终系统中会建立 340 个左右的子表,3 个超级表。需要注意的是,如果采用串联标签值的方式导致子表名称超过系统限制(191 字节),那么需要采用一定的编码方式(例如 MD5)将其转化为可接受长度。 + +- **多值模型** + +如果你想要利用 TDengine 的多值模型能力,需要首先满足以下要求:不同的采集量具有相同的采集频率,且能够通过消息队列**同时到达**数据写入端,从而确保使用 SQL 语句将多个指标一次性写入。将度量的名称作为超级表的名称,建立具有相同采集频率且能够同时到达的数据多列模型。子表的表名采用具有固定规则的方式进行命名。上述每个度量均只包含一个测量值,因此无法将其转化为多值模型。 + +## 数据分流与应用适配 + +从消息队列中订阅数据,并启动调整后的写入程序写入数据。 + +数据开始写入持续一段时间后,可以采用 SQL 语句检查写入的数据量是否符合预计的写入要求。统计数据量使用如下 SQL 语句: + +```sql +select count(*) from memory +``` + +完成查询后,如果写入的数据与预期的相比没有差别,同时写入程序本身没有异常的报错信息,那么可用确认数据写入是完整有效的。 + +TDengine 不支持采用 OpenTSDB 的查询语法进行查询或数据获取处理,但是针对 OpenTSDB 的每种查询都提供对应的支持。可以用检查附录 1 获取对应的查询处理的调整和应用使用的方式,如果需要全面了解 TDengine 支持的查询类型,请参阅 TDengine 的用户手册。 + +TDengine 支持标准的 JDBC 3.0 接口操纵数据库,你也可以使用其他类型的高级语言的连接器来查询读取数据,以适配你的应用。具体的操作和使用帮助也请参阅用户手册。 + +## 历史数据迁移 + +### 1、使用工具自动迁移数据 + +为了方便历史数据的迁移工作,我们为数据同步工具 DataX 提供了插件,能够将数据自动写入到 TDengine 中,需要注意的是 DataX 的自动化数据迁移只能够支持单值模型的数据迁移过程。 + +DataX 具体的使用方式及如何使用 DataX 将数据写入 TDengine 请参见[基于 DataX 的 TDengine 数据迁移工具](https://www.taosdata.com/blog/2021/10/26/3156.html)。 + +在对 DataX 进行迁移实践后,我们发现通过启动多个进程,同时迁移多个 metric 的方式,可以大幅度的提高迁移历史数据的效率,下面是迁移过程中的部分记录,希望这些能为应用迁移工作带来参考。 + +| DataX 实例个数 (并发进程个数) | 迁移记录速度 (条/秒) | +| ----------------------------- | --------------------- | +| 1 | 约 13.9 万 | +| 2 | 约 21.8 万 | +| 3 | 约 24.9 万 | +| 5 | 约 29.5 万 | +| 10 | 约 33 万 | + +
(注:测试数据源自 单节点 Intel(R) Core(TM) i7-10700 CPU@2.90GHz 16 核 64G 硬件设备,channel 和 batchSize 分别为 8 和 1000,每条记录包含 10 个 tag) + +### 2、手动迁移数据 + +如果你需要使用多值模型进行数据写入,就需要自行开发一个将数据从 OpenTSDB 导出的工具,然后确认哪些时间线能够合并导入到同一个时间线,再将可以同时导入的时间通过 SQL 语句的写入到数据库中。 + +手动迁移数据需要注意以下两个问题: + +1)在磁盘中存储导出数据时,磁盘需要有足够的存储空间以便能够充分容纳导出的数据文件。为了避免全量数据导出后导致磁盘文件存储紧张,可以采用部分导入的模式,对于归属于同一个超级表的时间线优先导出,然后将导出部分的数据文件导入到 TDengine 系统中。 + +2)在系统全负载运行下,如果有足够的剩余计算和 IO 资源,可以建立多线程的导入机制,最大限度地提升数据迁移的效率。考虑到数据解析对于 CPU 带来的巨大负载,需要控制最大的并行任务数量,以避免因导入历史数据而触发的系统整体过载。 + +由于 TDengine 本身操作简易性,所以不需要在整个过程中进行索引维护、数据格式的变化处理等工作,整个过程只需要顺序执行即可。 + +当历史数据完全导入到 TDengine 以后,此时两个系统处于同时运行的状态,之后便可以将查询请求切换到 TDengine 上,从而实现无缝的应用切换。 + +## 附录 1: OpenTSDB 查询函数对应表 + +### Avg + +等效函数:avg + +示例: + +```sql +SELECT avg(val) FROM (SELECT first(val) FROM super_table WHERE ts >= startTime and ts <= endTime INTERVAL(20s) Fill(linear)) INTERVAL(20s) +``` + +备注: + +1. Interval 内的数值与外层查询的 interval 数值需要相同。 +2. 在 TDengine 中插值处理需要使用子查询来协助完成,如上所示,在内层查询中指明插值类型即可,由于 OpenTSDB 中数值的插值使用了线性插值,因此在插值子句中使用 fill(linear) 来声明插值类型。以下有相同插值计算需求的函数,均采用该方法处理。 +3. Interval 中参数 20s 表示将内层查询按照 20 秒一个时间窗口生成结果。在真实的查询中,需要调整为不同的记录之间的时间间隔。这样可确保等效于原始数据生成了插值结果。 +4. 由于 OpenTSDB 特殊的插值策略和机制,聚合查询(Aggregate)中先插值再计算的方式导致其计算结果与 TDengine 不可能完全一致。但是在降采样(Downsample)的情况下,TDengine 和 OpenTSDB 能够获得一致的结果(由于 OpenTSDB 在聚合查询和降采样查询中采用了完全不同的插值策略)。 + +### Count + +等效函数:count + +示例: + +```sql +select count(\*) from super_table_name; +``` + +### Dev + +等效函数:stddev + +示例: + +```sql +Select stddev(val) from table_name +``` + +### Estimated percentiles + +等效函数:apercentile + +示例: + +```sql +Select apercentile(col1, 50, “t-digest”) from table_name +``` + +备注: + +1. 近似查询处理过程中,OpenTSDB 默认采用 t-digest 算法,所以为了获得相同的计算结果,需要在 apercentile 函数中指明使用的算法。TDengine 能够支持两种不同的近似处理算法,分别通过“default”和“t-digest”来声明。 +### First + +等效函数:first + +示例: + +```sql +Select first(col1) from table_name +``` + +### Last + +等效函数:last + +示例: + +```sql +Select last(col1) from table_name +``` + +### Max + +等效函数:max + +示例: + +```sql +Select max(value) from (select first(val) value from table_name interval(10s) fill(linear)) interval(10s) +``` + +备注:Max 函数需要插值,原因见上。 + +### Min + +等效函数:min + +示例: + +```sql +Select min(value) from (select first(val) value from table_name interval(10s) fill(linear)) interval(10s); +``` + +### MinMax + +等效函数:max + +```sql +Select max(val) from table_name +``` + +备注:该函数无插值需求,因此可用直接计算。 + +### MimMin + +等效函数:min + +```sql +Select min(val) from table_name +``` + +备注:该函数无插值需求,因此可用直接计算。 + +### Percentile + +等效函数:percentile + +备注: + +### Sum + +等效函数:sum + +```sql +Select max(value) from (select first(val) value from table_name interval(10s) fill(linear)) interval(10s) +``` + +备注:该函数无插值需求,因此可用直接计算。 + +### Zimsum + +等效函数:sum + +```sql +Select sum(val) from table_name +``` + +备注:该函数无插值需求,因此可用直接计算。 + +完整示例: + +```json +// OpenTSDB 查询 JSON +query = { +“start”:1510560000, +“end”: 1515000009, +“queries”:[{ +“aggregator”: “count”, +“metric”:”cpu.usage_user”, +}] +} + +//等效查询 SQL: +SELECT count(*) +FROM `cpu.usage_user` +WHERE ts>=1510560000 AND ts<=1515000009 +``` + +## 附录 2: 资源估算方法 + +### 数据生成环境 + +我们仍然使用第 4 章中的假设环境,3 个测量值。分别是:温度和湿度的数据写入的速率是每 5 秒一条记录,时间线 10 万个。空气质量的写入速率是 10 秒一条记录,时间线 1 万个,查询的请求频率 500 QPS。 + +### 存储资源估算 + +假设产生数据并需要存储的传感器设备数量为 `n`,数据生成的频率为`t`条/秒,每条记录的长度为 `L` bytes,则每天产生的数据规模为 `n×t×L` bytes。假设压缩比为 C,则每日产生数据规模为 `(n×t×L)/C` bytes。存储资源预估为能够容纳 1.5 年的数据规模,生产环境下 TDengine 的压缩比 C 一般在 5 ~ 7 之间,同时为最后结果增加 20% 的冗余,可计算得到需要存储资源: + +```matlab +(n×t×L)×(365×1.5)×(1+20%)/C +``` + +结合以上的计算公式,将参数带入计算公式,在不考虑标签信息的情况下,每年产生的原始数据规模是 11.8TB。需要注意的是,由于标签信息在 TDengine 中关联到每个时间线,并不是每条记录。所以需要记录的数据量规模相对于产生的数据有一定的降低,而这部分标签数据整体上可以忽略不记。假设压缩比为 5,则保留的数据规模最终为 2.56 TB。 + +### 存储设备选型考虑 + +硬盘应该选用具有较好随机读性能的硬盘设备,如果能够有 SSD,尽可能考虑使用 SSD。较好的随机读性能的磁盘对于提升系统查询性能具有极大的帮助,能够整体上提升系统的查询响应性能。为了获得较好的查询性能,硬盘设备的单线程随机读 IOPS 的性能指标不应该低于 1000,能够达到 5000 IOPS 以上为佳。为了获得当前的设备随机读取的 IO 性能的评估,建议使用 `fio` 软件对其进行运行性能评估(具体的使用方式请参阅附录 1),确认其是否能够满足大文件随机读性能要求。 + +硬盘写性能对于 TDengine 的影响不大。TDengine 写入过程采用了追加写的模式,所以只要有较好的顺序写性能即可,一般意义上的 SAS 硬盘和 SSD 均能够很好地满足 TDengine 对于磁盘写入性能的要求。 + +### 计算资源估算 + +由于物联网数据的特殊性,数据产生的频率固定以后,TDengine 写入的过程对于(计算和存储)资源消耗都保持一个相对固定的量。《[TDengine 运维指南](/operation/)》上的描述,该系统中每秒 22000 个写入,消耗 CPU 不到 1 个核。 + +在针对查询所需要消耗的 CPU 资源的估算上,假设应用要求数据库提供的 QPS 为 10000,每次查询消耗的 CPU 时间约 1 ms,那么每个核每秒提供的查询为 1000 QPS,满足 10000 QPS 的查询请求,至少需要 10 个核。为了让系统整体上 CPU 负载小于 50%,整个集群需要 10 个核的两倍,即 20 个核。 + +### 内存资源估算 + +数据库默认为每个 Vnode 分配内存 16MB\*3 缓冲区,集群系统包括 22 个 CPU 核,则默认会建立 22 个虚拟节点 Vnode,每个 Vnode 包含 1000 张表,则可以容纳所有的表。则约 1 个半小时写满一个 block,从而触发落盘,可以不做调整。22 个 Vnode 共计需要内存缓存约 1GB。考虑到查询所需要的内存,假设每次查询的内存开销约 50MB,则 500 个查询并发需要的内存约 25GB。 + +综上所述,可使用单台 16 核 32GB 的机器,或者使用 2 台 8 核 16GB 机器构成的集群。 + +## 附录 3: 集群部署及启动 + +TDengine 提供了丰富的帮助文档说明集群安装、部署的诸多方面的内容,这里提供相应的文档列表,供你参考。 + +### 集群部署 + +首先是安装 TDengine,从官网上下载 TDengine 最新稳定版,解压缩后运行 install.sh 进行安装。各种安装包的使用帮助请参见博客[《TDengine 多种安装包的安装和卸载》](https://www.taosdata.com/blog/2019/08/09/566.html)。 + +注意安装完成以后,不要立即启动 `taosd` 服务,在正确配置完成参数以后才启动 `taosd` 服务。 + +### 设置运行参数并启动服务 + +为确保系统能够正常获取运行的必要信息。请在服务端正确设置以下关键参数: + +FQDN、firstEp、secondEP、dataDir、logDir、tmpDir、serverPort。各参数的具体含义及设置的要求,可参见文档《[TDengine 集群安装、管理](/cluster/)》 + +按照相同的步骤,在需要运行的节点上设置参数,并启动 `taosd` 服务,然后添加 Dnode 到集群中。 + +最后启动 `taos` 命令行程序,执行命令 `show dnodes`,如果能看到所有的加入集群的节点,那么集群顺利搭建完成。具体的操作流程及注意事项,请参阅文档《[TDengine 集群安装、管理](/cluster/)》 + +## 附录 4: 超级表名称 + +由于 OpenTSDB 的 metric 名称中带有点号(“.”),例如“cpu.usage_user”这种名称的 metric。但是点号在 TDengine 中具有特殊含义,是用来分隔数据库和表名称的分隔符。TDengine 也提供转义符,以允许用户在(超级)表名称中使用关键词或特殊分隔符(如:点号)。为了使用特殊字符,需要采用转义字符将表的名称括起来,例如:`cpu.usage_user`这样就是合法的(超级)表名称。 + +## 附录 5:参考文章 + +1. [使用 TDengine + collectd/StatsD + Grafana 快速搭建 IT 运维监控系统](/application/collectd/) +2. [通过 collectd 将采集数据直接写入 TDengine](/third-party/collectd/) diff --git a/docs-cn/25-application/_category_.yml b/docs-cn/25-application/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..f43a4601b6c269822cbc0de1b7ed99dfdc70cfe5 --- /dev/null +++ b/docs-cn/25-application/_category_.yml @@ -0,0 +1 @@ +label: 应用实践 diff --git a/docs-cn/25-application/index.md b/docs-cn/25-application/index.md new file mode 100644 index 0000000000000000000000000000000000000000..1305cf230f78b68f988918921540a1df05f0931f --- /dev/null +++ b/docs-cn/25-application/index.md @@ -0,0 +1,10 @@ +--- +title: 应用实践 +--- + +```mdx-code-block +import DocCardList from '@theme/DocCardList'; +import {useCurrentSidebarCategory} from '@docusaurus/theme-common'; + + +``` \ No newline at end of file diff --git a/docs-cn/27-train-faq/01-faq.md b/docs-cn/27-train-faq/01-faq.md new file mode 100644 index 0000000000000000000000000000000000000000..0ceae1fa490d99a1d9e55454d0c0c49ec1b384e6 --- /dev/null +++ b/docs-cn/27-train-faq/01-faq.md @@ -0,0 +1,195 @@ +--- +title: 常见问题及反馈 +--- + +## 问题反馈 + +如果 FAQ 中的信息不能够帮到您,需要 TDengine 技术团队的技术支持与协助,请将以下两个目录中内容打包: + +1. /var/log/taos (如果没有修改过默认路径) +2. /etc/taos + +附上必要的问题描述,包括使用的 TDengine 版本信息、平台环境信息、发生该问题的执行操作、出现问题的表征及大概的时间,在 [GitHub](https://github.com/taosdata/TDengine) 提交 issue。 + +为了保证有足够的 debug 信息,如果问题能够重复,请修改/etc/taos/taos.cfg 文件,最后面添加一行“debugFlag 135"(不带引号本身),然后重启 taosd, 重复问题,然后再递交。也可以通过如下 SQL 语句,临时设置 taosd 的日志级别。 + +``` + alter dnode debugFlag 135; +``` + +但系统正常运行时,请一定将 debugFlag 设置为 131,否则会产生大量的日志信息,降低系统效率。 + +## 常见问题列表 + +### 1. TDengine2.0 之前的版本升级到 2.0 及以上的版本应该注意什么?☆☆☆ + +2.0 版在之前版本的基础上,进行了完全的重构,配置文件和数据文件是不兼容的。在升级之前务必进行如下操作: + +1. 删除配置文件,执行 `sudo rm -rf /etc/taos/taos.cfg` +2. 删除日志文件,执行 `sudo rm -rf /var/log/taos/` +3. 确保数据已经不再需要的前提下,删除数据文件,执行 `sudo rm -rf /var/lib/taos/` +4. 安装最新稳定版本的 TDengine +5. 如果需要迁移数据或者数据文件损坏,请联系涛思数据官方技术支持团队,进行协助解决 + +### 2. Windows 平台下 JDBCDriver 找不到动态链接库,怎么办? + +请看为此问题撰写的[技术博客](https://www.taosdata.com/blog/2019/12/03/950.html)。 + +### 3. 创建数据表时提示 more dnodes are needed + +请看为此问题撰写的[技术博客](https://www.taosdata.com/blog/2019/12/03/965.html)。 + +### 4. 如何让 TDengine crash 时生成 core 文件? + +请看为此问题撰写的[技术博客](https://www.taosdata.com/blog/2019/12/06/974.html)。 + +### 5. 遇到错误“Unable to establish connection” 怎么办? + +客户端遇到连接故障,请按照下面的步骤进行检查: + +1. 检查网络环境 + + - 云服务器:检查云服务器的安全组是否打开 TCP/UDP 端口 6030-6042 的访问权限 + - 本地虚拟机:检查网络能否 ping 通,尽量避免使用`localhost` 作为 hostname + - 公司服务器:如果为 NAT 网络环境,请务必检查服务器能否将消息返回值客户端 + +2. 确保客户端与服务端版本号是完全一致的,开源社区版和企业版也不能混用 + +3. 在服务器,执行 `systemctl status taosd` 检查*taosd*运行状态。如果没有运行,启动*taosd* + +4. 确认客户端连接时指定了正确的服务器 FQDN (Fully Qualified Domain Name —— 可在服务器上执行 Linux 命令 hostname -f 获得),FQDN 配置参考:[一篇文章说清楚 TDengine 的 FQDN](https://www.taosdata.com/blog/2020/09/11/1824.html)。 + +5. ping 服务器 FQDN,如果没有反应,请检查你的网络,DNS 设置,或客户端所在计算机的系统 hosts 文件。如果部署的是 TDengine 集群,客户端需要能 ping 通所有集群节点的 FQDN。 + +6. 检查防火墙设置(Ubuntu 使用 ufw status,CentOS 使用 firewall-cmd --list-port),确认 TCP/UDP 端口 6030-6042 是打开的 + +7. 对于 Linux 上的 JDBC(ODBC, Python, Go 等接口类似)连接, 确保*libtaos.so*在目录*/usr/local/taos/driver*里, 并且*/usr/local/taos/driver*在系统库函数搜索路径*LD_LIBRARY_PATH*里 + +8. 对于 Windows 上的 JDBC, ODBC, Python, Go 等连接,确保*C:\TDengine\driver\taos.dll*在你的系统库函数搜索目录里 (建议*taos.dll*放在目录 _C:\Windows\System32_) + +9. 如果仍不能排除连接故障 + + - Linux 系统请使用命令行工具 nc 来分别判断指定端口的 TCP 和 UDP 连接是否通畅 + 检查 UDP 端口连接是否工作:`nc -vuz {hostIP} {port} ` + 检查服务器侧 TCP 端口连接是否工作:`nc -l {port}` + 检查客户端侧 TCP 端口连接是否工作:`nc {hostIP} {port}` + + - Windows 系统请使用 PowerShell 命令 Net-TestConnection -ComputerName {fqdn} -Port {port} 检测服务段端口是否访问 + +10. 也可以使用 taos 程序内嵌的网络连通检测功能,来验证服务器和客户端之间指定的端口连接是否通畅(包括 TCP 和 UDP):[TDengine 内嵌网络检测工具使用指南](https://www.taosdata.com/blog/2020/09/08/1816.html)。 + +### 6. 遇到错误 “Unexpected generic error in RPC”或者“Unable to resolve FQDN” 怎么办? + +产生这个错误,是由于客户端或数据节点无法解析 FQDN(Fully Qualified Domain Name)导致。对于 TAOS Shell 或客户端应用,请做如下检查: + +1. 请检查连接的服务器的 FQDN 是否正确,FQDN 配置参考:[一篇文章说清楚 TDengine 的 FQDN](https://www.taosdata.com/blog/2020/09/11/1824.html) +2. 如果网络配置有 DNS server,请检查是否正常工作 +3. 如果网络没有配置 DNS server,请检查客户端所在机器的 hosts 文件,查看该 FQDN 是否配置,并是否有正确的 IP 地址 +4. 如果网络配置 OK,从客户端所在机器,你需要能 Ping 该连接的 FQDN,否则客户端是无法连接服务器的 +5. 如果服务器曾经使用过 TDengine,且更改过 hostname,建议检查 data 目录的 dnodeEps.json 是否符合当前配置的 EP,路径默认为/var/lib/taos/dnode。正常情况下,建议更换新的数据目录或者备份后删除以前的数据目录,这样可以避免该问题。 +6. 检查/etc/hosts 和/etc/hostname 是否是预配置的 FQDN + +### 7. 虽然语法正确,为什么我还是得到 "Invalid SQL" 错误? + +如果你确认语法正确,2.0 之前版本,请检查 SQL 语句长度是否超过 64K。如果超过,也会返回这个错误。 + +### 8. 是否支持 validation queries? + +TDengine 还没有一组专用的 validation queries。然而建议你使用系统监测的数据库”log"来做。 + + + +### 9. 我可以删除或更新一条记录吗? + +TDengine 目前尚不支持删除功能,未来根据用户需求可能会支持。 + +从 2.0.8.0 开始,TDengine 支持更新已经写入数据的功能。使用更新功能需要在创建数据库时使用 UPDATE 1 参数,之后可以使用 INSERT INTO 命令更新已经写入的相同时间戳数据。UPDATE 参数不支持 ALTER DATABASE 命令修改。没有使用 UPDATE 1 参数创建的数据库,写入相同时间戳的数据不会修改之前的数据,也不会报错。 + +另需注意,在 UPDATE 设置为 0 时,后发送的相同时间戳的数据会被直接丢弃,但并不会报错,而且仍然会被计入 affected rows (所以不能利用 INSERT 指令的返回信息进行时间戳查重)。这样设计的主要原因是,TDengine 把写入的数据看做一个数据流,无论时间戳是否出现冲突,TDengine 都认为产生数据的原始设备真实地产生了这样的数据。UPDATE 参数只是控制这样的流数据在进行持久化时要怎样处理——UPDATE 为 0 时,表示先写入的数据覆盖后写入的数据;而 UPDATE 为 1 时,表示后写入的数据覆盖先写入的数据。这种覆盖关系如何选择,取决于对数据的后续使用和统计中,希望以先还是后生成的数据为准。 + +此外,从 2.1.7.0 版本开始,支持将 UPDATE 参数设为 2,表示“支持部分列更新”。也即,当 UPDATE 设为 1 时,如果更新一个数据行,其中某些列没有提供取值,那么这些列会被设为 NULL;而当 UPDATE 设为 2 时,如果更新一个数据行,其中某些列没有提供取值,那么这些列会保持原有数据行中的对应值。 + +### 10. 我怎么创建超过 1024 列的表? + +使用 2.0 及其以上版本,默认支持 1024 列;2.0 之前的版本,TDengine 最大允许创建 250 列的表。但是如果确实超过限值,建议按照数据特性,逻辑地将这个宽表分解成几个小表。(从 2.1.7.0 版本开始,表的最大列数增加到了 4096 列。) + +### 11. 最有效的写入数据的方法是什么? + +批量插入。每条写入语句可以一张表同时插入多条记录,也可以同时插入多张表的多条记录。 + +### 12. Windows 系统下插入的 nchar 类数据中的汉字被解析成了乱码如何解决? + +Windows 下插入 nchar 类的数据中如果有中文,请先确认系统的地区设置成了中国(在 Control Panel 里可以设置),这时 cmd 中的`taos`客户端应该已经可以正常工作了;如果是在 IDE 里开发 Java 应用,比如 Eclipse, IntelliJ,请确认 IDE 里的文件编码为 GBK(这是 Java 默认的编码类型),然后在生成 Connection 时,初始化客户端的配置,具体语句如下: + +```JAVA +Class.forName("com.taosdata.jdbc.TSDBDriver"); +Properties properties = new Properties(); +properties.setProperty(TSDBDriver.LOCALE_KEY, "UTF-8"); +Connection = DriverManager.getConnection(url, properties); +``` + +### 13.JDBC 报错: the executed SQL is not a DML or a DDL? + +请更新至最新的 JDBC 驱动,参考 [Java 连接器](/reference/connector/java) + +### 14. taos connect failed, reason: invalid timestamp + +常见原因是服务器和客户端时间没有校准,可以通过和时间服务器同步的方式(Linux 下使用 ntpdate 命令,Windows 在系统时间设置中选择自动同步)校准。 + +### 15. 表名显示不全 + +由于 taos shell 在终端中显示宽度有限,有可能比较长的表名显示不全,如果按照显示的不全的表名进行相关操作会发生 Table does not exist 错误。解决方法可以是通过修改 taos.cfg 文件中的设置项 maxBinaryDisplayWidth, 或者直接输入命令 set max_binary_display_width 100。或者在命令结尾使用 \G 参数来调整结果的显示方式。 + +### 16. 如何进行数据迁移? + +TDengine 是根据 hostname 唯一标志一台机器的,在数据文件从机器 A 移动机器 B 时,注意如下两件事: + + - 2.0.0.0 至 2.0.6.x 的版本,重新配置机器 B 的 hostname 为机器 A 的 hostname。 + - 2.0.7.0 及以后的版本,到/var/lib/taos/dnode 下,修复 dnodeEps.json 的 dnodeId 对应的 FQDN,重启。确保机器内所有机器的此文件是完全相同的。 + - 1.x 和 2.x 版本的存储结构不兼容,需要使用迁移工具或者自己开发应用导出导入数据。 + +### 17. 如何在命令行程序 taos 中临时调整日志级别 + +为了调试方便,从 2.0.16 版本开始,命令行程序 taos 新增了与日志记录相关的两条指令: + +```sql +ALTER LOCAL flag_name flag_value; +``` + +其含义是,在当前的命令行程序下,修改一个特定模块的日志记录级别(只对当前命令行程序有效,如果 taos 命令行程序重启,则需要重新设置): + + - flag_name 的取值可以是:debugFlag,cDebugFlag,tmrDebugFlag,uDebugFlag,rpcDebugFlag + - flag_value 的取值可以是:131(输出错误和警告日志),135( 输出错误、警告和调试日志),143( 输出错误、警告、调试和跟踪日志) + +```sql +ALTER LOCAL RESETLOG; +``` + +其含义是,清空本机所有由客户端生成的日志文件。 + + + +### 18. go 语言编写组件编译失败怎样解决? + +TDengine 2.3.0.0 及之后的版本包含一个使用 go 语言开发的 taosAdapter 独立组件,需要单独运行,取代之前 taosd 内置的 httpd ,提供包含原 httpd 功能以及支持多种其他软件(Prometheus、Telegraf、collectd、StatsD 等)的数据接入功能。 +使用最新 develop 分支代码编译需要先 `git submodule update --init --recursive` 下载 taosAdapter 仓库代码后再编译。 + +目前编译方式默认自动编译 taosAdapter。go 语言版本要求 1.14 以上,如果发生 go 编译错误,往往是国内访问 go mod 问题,可以通过设置 go 环境变量来解决: + +```sh +go env -w GO111MODULE=on +go env -w GOPROXY=https://goproxy.cn,direct +``` + +如果希望继续使用之前的内置 httpd,可以关闭 taosAdapter 编译,使用 +`cmake .. -DBUILD_HTTP=true` 使用原来内置的 httpd。 + +### 19. 如何查询数据占用的存储空间大小? + +默认情况下,TDengine 的数据文件存储在 /var/lib/taos ,日志文件存储在 /var/log/taos 。 + +若想查看所有数据文件占用的具体大小,可以执行 Shell 指令:`du -sh /var/lib/taos/vnode --exclude='wal'` 来查看。此处排除了 WAL 目录,因为在持续写入的情况下,这里大小几乎是固定的,并且每当正常关闭 TDengine 让数据落盘后,WAL 目录都会清空。 + +若想查看单个数据库占用的大小,可在命令行程序 taos 内指定要查看的数据库后执行 `show vgroups;` ,通过得到的 VGroup id 去 /var/lib/taos/vnode 下查看包含的文件夹大小。 + +若仅仅想查看指定(超级)表的数据块分布及大小,可查看[_block_dist 函数](https://docs.taosdata.com/taos-sql/select/#_block_dist-%E5%87%BD%E6%95%B0) diff --git a/docs-cn/27-train-faq/02-video.mdx b/docs-cn/27-train-faq/02-video.mdx new file mode 100644 index 0000000000000000000000000000000000000000..b644412332fe817ea7fdc2c9ddc176ecc9858c56 --- /dev/null +++ b/docs-cn/27-train-faq/02-video.mdx @@ -0,0 +1,25 @@ +--- +title: 视频教程 +--- + +## 技术公开课 + +- [技术公开课:开源、高效的物联网大数据平台,TDengine 内核技术剖析](https://www.taosdata.com/blog/2020/12/25/2126.html) + +## 视频教程 + +- [TDengine 视频教程 - 快速上手](https://www.taosdata.com/blog/2020/11/11/1941.html) +- [TDengine 视频教程 - 数据建模](https://www.taosdata.com/blog/2020/11/11/1945.html) +- [TDengine 视频教程 - 集群搭建](https://www.taosdata.com/blog/2020/11/11/1961.html) +- [TDengine 视频教程 - Go Connector](https://www.taosdata.com/blog/2020/11/11/1951.html) +- [TDengine 视频教程 - JDBC Connector](https://www.taosdata.com/blog/2020/11/11/1955.html) +- [TDengine 视频教程 - Node.js Connector](https://www.taosdata.com/blog/2020/11/11/1957.html) +- [TDengine 视频教程 - Python Connector](https://www.taosdata.com/blog/2020/11/11/1963.html) +- [TDengine 视频教程 - RESTful Connector](https://www.taosdata.com/blog/2020/11/11/1965.html) +- [TDengine 视频教程 - “零”代码运维监控](https://www.taosdata.com/blog/2020/11/11/1959.html) + +## 微课堂 + +关注 TDengine 视频号, 有精心制作的微课堂。 + + diff --git a/docs-cn/27-train-faq/03-docker.md b/docs-cn/27-train-faq/03-docker.md new file mode 100644 index 0000000000000000000000000000000000000000..845a8751846c0995a43fb1c01e6ace3080176838 --- /dev/null +++ b/docs-cn/27-train-faq/03-docker.md @@ -0,0 +1,330 @@ +--- +title: 通过 Docker 快速体验 TDengine +--- + +虽然并不推荐在生产环境中通过 Docker 来部署 TDengine 服务,但 Docker 工具能够很好地屏蔽底层操作系统的环境差异,很适合在开发测试或初次体验时用于安装运行 TDengine 的工具集。特别是,借助 Docker,能够比较方便地在 macOS 和 Windows 系统上尝试 TDengine,而无需安装虚拟机或额外租用 Linux 服务器。另外,从 2.0.14.0 版本开始,TDengine 提供的镜像已经可以同时支持 X86-64、X86、arm64、arm32 平台,像 NAS、树莓派、嵌入式开发板之类可以运行 docker 的非主流计算机也可以基于本文档轻松体验 TDengine。 + +下文通过 Step by Step 风格的介绍,讲解如何通过 Docker 快速建立 TDengine 的单节点运行环境,以支持开发和测试。 + +## 下载 Docker + +Docker 工具自身的下载请参考 [Docker 官网文档](https://docs.docker.com/get-docker/)。 + +安装完毕后可以在命令行终端查看 Docker 版本。如果版本号正常输出,则说明 Docker 环境已经安装成功。 + +```bash +$ docker -v +Docker version 20.10.3, build 48d30b5 +``` + +## 使用 Docker 在容器中运行 TDengine + +### 在 Docker 容器中运行 TDengine server + +```bash +$ docker run -d -p 6030-6049:6030-6049 -p 6030-6049:6030-6049/udp tdengine/tdengine +526aa188da767ae94b244226a2b2eec2b5f17dd8eff592893d9ec0cd0f3a1ccd +``` + +这条命令,启动一个运行了 TDengine server 的 docker 容器,并且将容器的 6030 到 6049 端口映射到宿主机的 6030 到 6049 端口上。如果宿主机已经运行了 TDengine server 并占用了相同端口,需要映射容器的端口到不同的未使用端口段。(详情参见 [TDengine 2.0 端口说明](/train-faq/faq#port)。为了支持 TDengine 客户端操作 TDengine server 服务, TCP 和 UDP 端口都需要打开。 + +- **docker run**:通过 Docker 运行一个容器 +- **-d**:让容器在后台运行 +- **-p**:指定映射端口。注意:如果不是用端口映射,依然可以进入 Docker 容器内部使用 TDengine 服务或进行应用开发,只是不能对容器外部提供服务 +- **tdengine/tdengine**:拉取的 TDengine 官方发布的应用镜像 +- **526aa188da767ae94b244226a2b2eec2b5f17dd8eff592893d9ec0cd0f3a1ccd**:这个返回的长字符是容器 ID,我们也可以通过容器 ID 来查看对应的容器 + +进一步,还可以使用 docker run 命令启动运行 TDengine server 的 docker 容器,并使用 `--name` 命令行参数将容器命名为 `tdengine`,使用 `--hostname` 指定 hostname 为 `tdengine-server`,通过 `-v` 挂载本地目录到容器,实现宿主机与容器内部的数据同步,防止容器删除后,数据丢失。 + +```bash +docker run -d --name tdengine --hostname="tdengine-server" -v ~/work/taos/log:/var/log/taos -v ~/work/taos/data:/var/lib/taos -p 6030-6049:6030-6049 -p 6030-6049:6030-6049/udp tdengine/tdengine +``` + +- **--name tdengine**:设置容器名称,我们可以通过容器名称来访问对应的容器 +- **--hostname=tdengine-server**:设置容器内 Linux 系统的 hostname,我们可以通过映射 hostname 和 IP 来解决容器 IP 可能变化的问题。 +- **-v**:设置宿主机文件目录映射到容器内目录,避免容器删除后数据丢失。 + +### 使用 docker ps 命令确认容器是否已经正确运行 + +```bash +docker ps +``` + +输出示例如下: + +``` +CONTAINER ID IMAGE COMMAND CREATED STATUS ··· +c452519b0f9b tdengine/tdengine "taosd" 14 minutes ago Up 14 minutes ··· +``` + +- **docker ps**:列出所有正在运行状态的容器信息。 +- **CONTAINER ID**:容器 ID。 +- **IMAGE**:使用的镜像。 +- **COMMAND**:启动容器时运行的命令。 +- **CREATED**:容器创建时间。 +- **STATUS**:容器状态。UP 表示运行中。 + +### 通过 docker exec 命令,进入到 docker 容器中去做开发 + +```bash +$ docker exec -it tdengine /bin/bash +root@tdengine-server:~/TDengine-server-2.4.0.4# +``` + +- **docker exec**:通过 docker exec 命令进入容器,如果退出,容器不会停止。 +- **-i**:进入交互模式。 +- **-t**:指定一个终端。 +- **tdengine**:容器名称,需要根据 docker ps 指令返回的值进行修改。 +- **/bin/bash**:载入容器后运行 bash 来进行交互。 + +进入容器后,执行 taos shell 客户端程序。 + +```bash +root@tdengine-server:~/TDengine-server-2.4.0.4# taos + +Welcome to the TDengine shell from Linux, Client Version:2.4.0.4 +Copyright (c) 2020 by TAOS Data, Inc. All rights reserved. + +taos> +``` + +TDengine 终端成功连接服务端,打印出了欢迎消息和版本信息。如果失败,会有错误信息打印出来。 + +在 TDengine 终端中,可以通过 SQL 命令来创建/删除数据库、表、超级表等,并可以进行插入和查询操作。具体可以参考 [TAOS SQL 说明文档](/taos-sql/)。 + +### 在宿主机访问 Docker 容器中的 TDengine server + +在使用了 -p 命令行参数映射了正确的端口启动了 TDengine Docker 容器后,就在宿主机使用 taos shell 命令即可访问运行在 Docker 容器中的 TDengine。 + +``` +$ taos + +Welcome to the TDengine shell from Linux, Client Version:2.4.0.4 +Copyright (c) 2020 by TAOS Data, Inc. All rights reserved. + +taos> +``` + +也可以在宿主机使用 curl 通过 RESTful 端口访问 Docker 容器内的 TDengine server。 + +``` +curl -u root:taosdata -d 'show databases' 127.0.0.1:6041/rest/sql +``` + +输出示例如下: + +``` +{"status":"succ","head":["name","created_time","ntables","vgroups","replica","quorum","days","keep0,keep1,keep(D)","cache(MB)","blocks","minrows","maxrows","wallevel","fsync","comp","cachelast","precision","update","status"],"column_meta":[["name",8,32],["created_time",9,8],["ntables",4,4],["vgroups",4,4],["replica",3,2],["quorum",3,2],["days",3,2],["keep0,keep1,keep(D)",8,24],["cache(MB)",4,4],["blocks",4,4],["minrows",4,4],["maxrows",4,4],["wallevel",2,1],["fsync",4,4],["comp",2,1],["cachelast",2,1],["precision",8,3],["update",2,1],["status",8,10]],"data":[["test","2021-08-18 06:01:11.021",10000,4,1,1,10,"3650,3650,3650",16,6,100,4096,1,3000,2,0,"ms",0,"ready"],["log","2021-08-18 05:51:51.065",4,1,1,1,10,"30,30,30",1,3,100,4096,1,3000,2,0,"us",0,"ready"]],"rows":2} +``` + +这条命令,通过 REST API 访问 TDengine server,这时连接的是本机的 6041 端口,可见连接成功。 + +TDengine REST API 详情请参考[官方文档](/reference/rest-api/)。 + +### 使用 Docker 容器运行 TDengine server 和 taosAdapter + +在 TDengine 2.4.0.0 之后版本的 Docker 容器,开始提供一个独立运行的组件 taosAdapter,代替之前版本 TDengine 中 taosd 进程中内置的 http server。taosAdapter 支持通过 RESTful 接口对 TDengine server 的数据写入和查询能力,并提供和 InfluxDB/OpenTSDB 兼容的数据摄取接口,允许 InfluxDB/OpenTSDB 应用程序无缝移植到 TDengine。在新版本 Docker 镜像中,默认启用了 taosAdapter,也可以使用 docker run 命令中设置 TAOS_DISABLE_ADAPTER=true 来禁用 taosAdapter;也可以在 docker run 命令中单独使用 taosAdapter,而不运行 taosd 。 + +注意:如果容器中运行 taosAdapter,需要根据需要映射其他端口,具体端口默认配置和修改方法请参考[taosAdapter 文档](/reference/taosadapter/)。 + +使用 docker 运行 TDengine 2.4.0.4 版本镜像(taosd + taosAdapter): + +```bash +docker run -d --name tdengine-all -p 6030-6049:6030-6049 -p 6030-6049:6030-6049/udp tdengine/tdengine:2.4.0.4 +``` + +使用 docker 运行 TDengine 2.4.0.4 版本镜像(仅 taosAdapter,需要设置 firstEp 配置项 或 TAOS_FIRST_EP 环境变量): + +```bash +docker run -d --name tdengine-taosa -p 6041-6049:6041-6049 -p 6041-6049:6041-6049/udp -e TAOS_FIRST_EP=tdengine-all tdengine/tdengine:2.4.0.4 taosadapter +``` + +使用 docker 运行 TDengine 2.4.0.4 版本镜像(仅 taosd): + +```bash +docker run -d --name tdengine-taosd -p 6030-6042:6030-6042 -p 6030-6042:6030-6042/udp -e TAOS_DISABLE_ADAPTER=true tdengine/tdengine:2.4.0.4 +``` + +使用 curl 命令验证 RESTful 接口可以正常工作: + +```bash +curl -H 'Authorization: Basic cm9vdDp0YW9zZGF0YQ==' -d 'show databases;' 127.0.0.1:6041/rest/sql +``` + +输出示例如下: + +``` +{"status":"succ","head":["name","created_time","ntables","vgroups","replica","quorum","days","keep","cache(MB)","blocks","minrows","maxrows","wallevel","fsync","comp","cachelast","precision","update","status"],"column_meta":[["name",8,32],["created_time",9,8],["ntables",4,4],["vgroups",4,4],["replica",3,2],["quorum",3,2],["days",3,2],["keep",8,24],["cache(MB)",4,4],["blocks",4,4],["minrows",4,4],["maxrows",4,4],["wallevel",2,1],["fsync",4,4],["comp",2,1],["cachelast",2,1],["precision",8,3],["update",2,1],["status",8,10]],"data":[["log","2021-12-28 09:18:55.765",10,1,1,1,10,"30",1,3,100,4096,1,3000,2,0,"us",0,"ready"]],"rows":1} +``` + +### 应用示例:在宿主机使用 taosBenchmark 写入数据到 Docker 容器中的 TDengine server + +1. 在宿主机命令行界面执行 taosBenchmark (曾命名为 taosdemo)写入数据到 Docker 容器中的 TDengine server + + ```bash + $ taosBenchmark + + taosBenchmark is simulating data generated by power equipments monitoring... + + host: 127.0.0.1:6030 + user: root + password: taosdata + configDir: + resultFile: ./output.txt + thread num of insert data: 10 + thread num of create table: 10 + top insert interval: 0 + number of records per req: 30000 + max sql length: 1048576 + database count: 1 + database[0]: + database[0] name: test + drop: yes + replica: 1 + precision: ms + super table count: 1 + super table[0]: + stbName: meters + autoCreateTable: no + childTblExists: no + childTblCount: 10000 + childTblPrefix: d + dataSource: rand + iface: taosc + insertRows: 10000 + interlaceRows: 0 + disorderRange: 1000 + disorderRatio: 0 + maxSqlLen: 1048576 + timeStampStep: 1 + startTimestamp: 2017-07-14 10:40:00.000 + sampleFormat: + sampleFile: + tagsFile: + columnCount: 3 + column[0]:FLOAT column[1]:INT column[2]:FLOAT + tagCount: 2 + tag[0]:INT tag[1]:BINARY(16) + + Press enter key to continue or Ctrl-C to stop + ``` + + 回车后,该命令将在数据库 test 下面自动创建一张超级表 meters,该超级表下有 1 万张表,表名为 "d0" 到 "d9999",每张表有 1 万条记录,每条记录有 (ts, current, voltage, phase) 四个字段,时间戳从 "2017-07-14 10:40:00 000" 到 "2017-07-14 10:40:09 999",每张表带有标签 location 和 groupId,groupId 被设置为 1 到 10, location 被设置为 "beijing" 或者 "shanghai"。 + + 最后共插入 1 亿条记录。 + +2. 进入 TDengine 终端,查看 taosBenchmark 生成的数据。 + + - **进入命令行。** + + ```bash + $ root@c452519b0f9b:~/TDengine-server-2.4.0.4# taos + + Welcome to the TDengine shell from Linux, Client Version:2.4.0.4 + Copyright (c) 2020 by TAOS Data, Inc. All rights reserved. + + taos> + ``` + + - **查看数据库。** + + ```bash + $ taos> show databases; + name | created_time | ntables | vgroups | ··· + test | 2021-08-18 06:01:11.021 | 10000 | 6 | ··· + log | 2021-08-18 05:51:51.065 | 4 | 1 | ··· + + ``` + + - **查看超级表。** + + ```bash + $ taos> use test; + Database changed. + + $ taos> show stables; + name | created_time | columns | tags | tables | + ============================================================================================ + meters | 2021-08-18 06:01:11.116 | 4 | 2 | 10000 | + Query OK, 1 row(s) in set (0.003259s) + + ``` + + - **查看表,限制输出十条。** + + ```bash + $ taos> select * from test.t0 limit 10; + + DB error: Table does not exist (0.002857s) + taos> select * from test.d0 limit 10; + ts | current | voltage | phase | + ====================================================================================== + 2017-07-14 10:40:00.000 | 10.12072 | 223 | 0.34167 | + 2017-07-14 10:40:00.001 | 10.16103 | 224 | 0.34445 | + 2017-07-14 10:40:00.002 | 10.00204 | 220 | 0.33334 | + 2017-07-14 10:40:00.003 | 10.00030 | 220 | 0.33333 | + 2017-07-14 10:40:00.004 | 9.84029 | 216 | 0.32222 | + 2017-07-14 10:40:00.005 | 9.88028 | 217 | 0.32500 | + 2017-07-14 10:40:00.006 | 9.88110 | 217 | 0.32500 | + 2017-07-14 10:40:00.007 | 10.08137 | 222 | 0.33889 | + 2017-07-14 10:40:00.008 | 10.12063 | 223 | 0.34167 | + 2017-07-14 10:40:00.009 | 10.16086 | 224 | 0.34445 | + Query OK, 10 row(s) in set (0.016791s) + + ``` + + - **查看 d0 表的标签值。** + + ```bash + $ taos> select groupid, location from test.d0; + groupid | location | + ================================= + 0 | shanghai | + Query OK, 1 row(s) in set (0.003490s) + ``` + +### 应用示例:使用数据收集代理软件写入 TDengine + +taosAdapter 支持多个数据收集代理软件(如 Telegraf、StatsD、collectd 等),这里仅模拟 StasD 写入数据,在宿主机执行命令如下: + +``` +echo "foo:1|c" | nc -u -w0 127.0.0.1 6044 +``` + +然后可以使用 taos shell 查询 taosAdapter 自动创建的数据库 statsd 和 超级表 foo 中的内容: + +``` +taos> show databases; + name | created_time | ntables | vgroups | replica | quorum | days | keep | cache(MB) | blocks | minrows | maxrows | wallevel | fsync | comp | cachelast | precision | update | status | +==================================================================================================================================================================================================================================================================================== + log | 2021-12-28 09:18:55.765 | 12 | 1 | 1 | 1 | 10 | 30 | 1 | 3 | 100 | 4096 | 1 | 3000 | 2 | 0 | us | 0 | ready | + statsd | 2021-12-28 09:21:48.841 | 1 | 1 | 1 | 1 | 10 | 3650 | 16 | 6 | 100 | 4096 | 1 | 3000 | 2 | 0 | ns | 2 | ready | +Query OK, 2 row(s) in set (0.002112s) + +taos> use statsd; +Database changed. + +taos> show stables; + name | created_time | columns | tags | tables | +============================================================================================ + foo | 2021-12-28 09:21:48.894 | 2 | 1 | 1 | +Query OK, 1 row(s) in set (0.001160s) + +taos> select * from foo; + ts | value | metric_type | +======================================================================================= + 2021-12-28 09:21:48.840820836 | 1 | counter | +Query OK, 1 row(s) in set (0.001639s) + +taos> +``` + +可以看到模拟数据已经被写入到 TDengine 中。 + +## 停止正在 Docker 中运行的 TDengine 服务 + +```bash +docker stop tdengine +``` + +- **docker stop**:通过 docker stop 停止指定的正在运行中的 docker 镜像。 diff --git a/docs-cn/27-train-faq/_category_.yml b/docs-cn/27-train-faq/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..16b32bc38fd3ef88313150cf89e32b15696fe7ff --- /dev/null +++ b/docs-cn/27-train-faq/_category_.yml @@ -0,0 +1 @@ +label: FAQ 及其他 diff --git a/docs-cn/27-train-faq/index.md b/docs-cn/27-train-faq/index.md new file mode 100644 index 0000000000000000000000000000000000000000..b42bff0288fc8ab59810a7d7121be28ddf781551 --- /dev/null +++ b/docs-cn/27-train-faq/index.md @@ -0,0 +1,10 @@ +--- +title: FAQ 及其他 +--- + +```mdx-code-block +import DocCardList from '@theme/DocCardList'; +import {useCurrentSidebarCategory} from '@docusaurus/theme-common'; + + +``` \ No newline at end of file diff --git a/docs-cn/eco_system.png b/docs-cn/eco_system.png new file mode 100644 index 0000000000000000000000000000000000000000..bf8bf8f1e0a2311fc12202d712a8a2f9b8ce419b Binary files /dev/null and b/docs-cn/eco_system.png differ diff --git a/docs-en/01-index.md b/docs-en/01-index.md new file mode 100644 index 0000000000000000000000000000000000000000..f5b7f3e0f61507efbb09506b48548c12317e700b --- /dev/null +++ b/docs-en/01-index.md @@ -0,0 +1,27 @@ +--- +title: TDengine Documentation +sidebar_label: Documentation Home +slug: / +--- + +TDengine is a [high-performance](https://tdengine.com/fast), [scalable](https://tdengine.com/scalable) time series database with [SQL support](https://tdengine.com/sql-support). This document is the TDengine user manual. It introduces the basic, as well as novel concepts, in TDengine, and also talks in detail about installation, features, SQL, APIs, operation, maintenance, kernel design and other topics. It’s written mainly for architects, developers and system administrators. + +To get an overview of TDengine, such as a feature list, benchmarks, and competitive advantages, please browse through the [Introduction](./intro) section. + +TDengine greatly improves the efficiency of data ingestion, querying and storage by exploiting the characteristics of time series data, introducing the novel concepts of "one table for one data collection point" and "super table", and designing an innovative storage engine. To understand the new concepts in TDengine and make full use of the features and capabilities of TDengine, please read [“Concepts”](./concept) thoroughly. + +If you are a developer, please read the [“Developer Guide”](./develop) carefully. This section introduces the database connection, data modeling, data ingestion, query, continuous query, cache, data subscription, user-defined functions, and other functionality in detail. Sample code is provided for a variety of programming languages. In most cases, you can just copy and paste the sample code, make a few changes to accommodate your application, and it will work. + +We live in the era of big data, and scale-up is unable to meet the growing needs of business. Any modern data system must have the ability to scale out, and clustering has become an indispensable feature of big data systems. Not only did the TDengine team develop the cluster feature, but also decided to open source this important feature. To learn how to deploy, manage and maintain a TDengine cluster please refer to ["cluster"](./cluster). + +TDengine uses ubiquitious SQL as its query language, which greatly reduces learning costs and migration costs. In addition to the standard SQL, TDengine has extensions to better support time series data analysis. These extensions include functions such as roll up, interpolation and time weighted average, among many others. The ["SQL Reference"](./taos-sql) chapter describes the SQL syntax in detail, and lists the various supported commands and functions. + +If you are a system administrator who cares about installation, upgrade, fault tolerance, disaster recovery, data import, data export, system configuration, how to monitor whether TDengine is running healthily, and how to improve system performance, please refer to, and thoroughly read the ["Administration"](./operation) section. + +If you want to know more about TDengine tools, the REST API, and connectors for various programming languages, please see the ["Reference"](./reference) chapter. + +If you are very interested in the internal design of TDengine, please read the chapter ["Inside TDengine”](./tdinternal), which introduces the cluster design, data partitioning, sharding, writing, and reading processes in detail. If you want to study TDengine code or even contribute code, please read this chapter carefully. + +TDengine is an open source database, and we would love for you to be a part of TDengine. If you find any errors in the documentation, or see parts where more clarity or elaboration is needed, please click "Edit this page" at the bottom of each page to edit it directly. + +Together, we make a difference. diff --git a/docs-en/02-intro/_category_.yml b/docs-en/02-intro/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..a3d691e87b15eaf6a62030a130179ffe2e8e5fa6 --- /dev/null +++ b/docs-en/02-intro/_category_.yml @@ -0,0 +1 @@ +label: Introduction diff --git a/docs-en/02-intro/eco_system.png b/docs-en/02-intro/eco_system.png new file mode 100644 index 0000000000000000000000000000000000000000..bf8bf8f1e0a2311fc12202d712a8a2f9b8ce419b Binary files /dev/null and b/docs-en/02-intro/eco_system.png differ diff --git a/docs-en/02-intro/index.md b/docs-en/02-intro/index.md new file mode 100644 index 0000000000000000000000000000000000000000..e2309943f3983dcbf7957ef6d478aefa64d7a902 --- /dev/null +++ b/docs-en/02-intro/index.md @@ -0,0 +1,113 @@ +--- +title: Introduction +toc_max_heading_level: 2 +--- + +TDengine is a high-performance, scalable time-series database with SQL support. Its code, including its cluster feature is open source under GNU AGPL v3.0. Besides the database engine, it provides [caching](/develop/cache), [stream processing](/develop/continuous-query), [data subscription](/develop/subscribe) and other functionalities to reduce the complexity and cost of development and operation. + +This section introduces the major features, competitive advantages, suited scenarios and benchmarks to help you get a high level picture for TDengine. + +## Major Features + +The major features are listed below: + +1. Besides [using SQL to insert](/develop/insert-data/sql-writing),it supports [Schemaless writing](/reference/schemaless/),and it supports [InfluxDB LINE](/develop/insert-data/influxdb-line),[OpenTSDB Telnet](/develop/insert-data/opentsdb-telnet), [OpenTSDB JSON ](/develop/insert-data/opentsdb-json) and other protocols. +2. Support for seamless integration with third-party data collection agents like [Telegraf](/third-party/telegraf),[Prometheus](/third-party/prometheus),[StatsD](/third-party/statsd),[collectd](/third-party/collectd),[icinga2](/third-party/icinga2), [TCollector](/third-party/tcollector), [EMQX](/third-party/emq-broker), [HiveMQ](/third-party/hive-mq-broker). Without a line of code, those agents can write data points into TDengine just by configuration. +3. Support for [all kinds of queries](/develop/query-data), including aggregation, nested query, downsampling, interpolation, etc. +4. Support for [user defined functions](/develop/udf) +5. Support for [caching](/develop/cache). TDengine always saves the last data point in cache, so Redis is not needed in some scenarios. +6. Support for [continuous query](/develop/continuous-query). +7. Support for [data subscription](/develop/subscribe) with the capability to specify filter conditions. +8. Support for [cluster](/cluster/), with the capability of increasing processing power by adding more nodes. High availability is supported by replication. +9. Provides interactive [command-line interface](/reference/taos-shell) for management, maintenance and ad-hoc query. +10. Provides many ways to [import](/operation/import) and [export](/operation/export) data. +11. Provides [monitoring](/operation/monitor) on TDengine running instances. +12. Provides [connectors](/reference/connector/) for [C/C++](/reference/connector/cpp), [Java](/reference/connector/java), [Python](/reference/connector/python), [Go](/reference/connector/go), [Rust](/reference/connector/rust), [Node.js](/reference/connector/node) and other programming languages. +13. Provides a [REST API](/reference/rest-api/). +14. Supports the seamless integration with [Grafana](/third-party/grafana) for visualization. +15. Supports seamless integration with Google Data Studio. + +For more detail on features, please read through the whole documentation. + +## Competitive Advantages + +TDengine makes full use of [the characteristics of time series data](https://tdengine.com/2019/07/09/86.html), such as structured, no transaction, rarely delete or update, etc., and builds its own innovative storage engine and computing engine to differentiate itself from other time series databases with the following advantages. + +- **[High Performance](https://tdengine.com/fast)**: TDengine outperforms other time series databases in data ingestion and querying while significantly reducing storage cost and compute costs, with an innovatively designed and purpose-built storage engine. + +- **[Scalable](https://tdengine.com/scalable)**: TDengine provides out-of-box scalability and high-availability through its native distributed design. Nodes can be added through simple configuration to achieve greater data processing power. In addition, this feature is open source. + +- **[SQL Support](https://tdengine.com/sql-support)**: TDengine uses SQL as the query language, thereby reducing learning and migration costs, while adding SQL extensions to handle time-series data better, and supporting convenient and flexible schemaless data ingestion. + +- **All in One**: TDengine has built-in caching, stream processing and data subscription functions. It is no longer necessary to integrate Kafka/Redis/HBase/Spark or other software in some scenarios. It makes the system architecture much simpler, cost-effective and easier to maintain. + +- **Seamless Integration**: Without a single line of code, TDengine provide seamless, configurable integration with third-party tools such as Telegraf, Grafana, EMQX, Prometheus, StatsD, collectd, etc. More third-party tools are being integrated. + +- **Zero Management**: Installation and cluster setup can be done in seconds. Data partitioning and sharding are executed automatically. TDengine’s running status can be monitored via Grafana or other DevOps tools. + +- **Zero Learning Costs**: With SQL as the query language and support for ubiquitous tools like Python, Java, C/C++, Go, Rust, and Node.js connectors, there are zero learning costs. + +- **Interactive Console**: TDengine provides convenient console access to the database to run ad hoc queries, maintain the database, or manage the cluster without any programming. + +With TDengine, the total cost of ownership of time-series data platform can be greatly reduced. Because 1: with its superior performance, the computing and storage resources are reduced significantly; 2:with SQL support, it can be seamlessly integrated with many third party tools, and learning costs/migration costs are reduced significantly; 3: with its simple architecture and zero management, the operation and maintenance costs are reduced. + +## Technical Ecosystem +In the time-series data processing platform, TDengine stands in a role like this diagram below: + +![TDengine Technical Ecosystem ](eco_system.png) + +
Figure 1. TDengine Technical Ecosystem
+ +On the left side, there are data collection agents like OPC-UA, MQTT, Telegraf and Kafka. On the right side, visualization/BI tools, HMI, Python/R, and IoT Apps can be connected. TDengine itself provides interactive command-line interface and web interface for management and maintenance. + +## Suited Scenarios + +As a high-performance, scalable and SQL supported time-series database, TDengine's typical application scenarios include but are not limited to IoT, Industrial Internet, Connected Vehicles, IT operation and maintenance, energy, financial markets and other fields. TDengine is a purpose-built database optimized for the characteristics of time series data, it cannot be used to process data from web crawlers, social media, e-commerce, ERP, CRM, etc. This section makes a more detailed analysis of the applicable scenarios. + +### Characteristics and Requirements of Data Sources + +| **Data Source Characteristics and Requirements** | **Not Applicable** | **Might Be Applicable** | **Very Applicable** | **Description** | +| -------------------------------------------------------- | ------------------ | ----------------------- | ------------------- | :----------------------------------------------------------- | +| A massive amount of total data | | | √ | TDengine provides excellent scale-out functions in terms of capacity, and has a storage structure with matching high compression ratio to achieve the best storage efficiency in the industry.| +| Data input velocity is extremely high | | | √ | TDengine's performance is much higher than that of other similar products. It can continuously process larger amounts of input data in the same hardware environment, and provides a performance evaluation tool that can easily run in the user environment. | +| A huge number of data sources | | | √ | TDengine is optimized specifically for a huge number of data sources. It is especially suitable for efficiently ingesting, writing and querying data from billions of data sources. | + +### System Architecture Requirements + +| **System Architecture Requirements** | **Not Applicable** | **Might Be Applicable** | **Very Applicable** | **Description** | +| ------------------------------------------------- | ------------------ | ----------------------- | ------------------- | ------------------------------------------------------------ | +| A simple and reliable system architecture | | | √ | TDengine's system architecture is very simple and reliable, with its own message queue, cache, stream computing, monitoring and other functions. There is no need to integrate any additional third-party products. | +| Fault-tolerance and high-reliability | | | √ | TDengine has cluster functions to automatically provide high-reliability and high-availability functions such as fault tolerance and disaster recovery. | +| Standardization support | | | √ | TDengine supports standard SQL and provides SQL extensions for time-series data analysis. | + +### System Function Requirements + +| **System Function Requirements** | **Not Applicable** | **Might Be Applicable** | **Very Applicable** | **Description** | +| ------------------------------------------------- | ------------------ | ----------------------- | ------------------- | ------------------------------------------------------------ | +| Complete data processing algorithms built-in | | √ | | While TDengine implements various general data processing algorithms, industry specific algorithms and special types of processing will need to be implemented at the application level.| +| A large number of crosstab queries | | √ | | This type of processing is better handled by general purpose relational database systems but TDengine can work in concert with relational database systems to provide more complete solutions. | + +### System Performance Requirements + +| **System Performance Requirements** | **Not Applicable** | **Might Be Applicable** | **Very Applicable** | **Description** | +| ------------------------------------------------- | ------------------ | ----------------------- | ------------------- | ------------------------------------------------------------ | +| Very large total processing capacity | | | √ | TDengine’s cluster functions can easily improve processing capacity via multi-server coordination. | +| Extremely high-speed data processing | | | √ | TDengine’s storage and data processing are optimized for IoT, and can process data many times faster than similar products.| +| Extremely fast processing of high resolution data | | | √ | TDengine has achieved the same or better performance than other relational and NoSQL data processing systems. | + +### System Maintenance Requirements + +| **System Maintenance Requirements** | **Not Applicable** | **Might Be Applicable** | **Very Applicable** | **Description** | +| ------------------------------------------------- | ------------------ | ----------------------- | ------------------- | ------------------------------------------------------------ | +| Native high-reliability | | | √ | TDengine has a very robust, reliable and easily configurable system architecture to simplify routine operation. Human errors and accidents are eliminated to the greatest extent, with a streamlined experience for operators. | +| Minimize learning and maintenance costs | | | √ | In addition to being easily configurable, standard SQL support and the Taos shell for ad hoc queries makes maintenance simpler, allows reuse and reduces learning costs.| +| Abundant talent supply | √ | | | Given the above, and given the extensive training and professional services provided by TDengine, it is easy to migrate from existing solutions or create a new and lasting solution based on TDengine.| + +## Comparison with other databases + +- [Writing Performance Comparison of TDengine and InfluxDB ](https://tdengine.com/2022/02/23/4975.html) +- [Query Performance Comparison of TDengine and InfluxDB](https://tdengine.com/2022/02/24/5120.html) +- [TDengine vs InfluxDB、OpenTSDB、Cassandra、MySQL、ClickHouse](https://www.tdengine.com/downloads/TDengine_Testing_Report_en.pdf) +- [TDengine vs OpenTSDB](https://tdengine.com/2019/09/12/710.html) +- [TDengine vs Cassandra](https://tdengine.com/2019/09/12/708.html) +- [TDengine vs InfluxDB](https://tdengine.com/2019/09/12/706.html) diff --git a/docs-en/04-concept/_category_.yml b/docs-en/04-concept/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..12c659a9265e86d0e74d88a751c19d5d715e9fe0 --- /dev/null +++ b/docs-en/04-concept/_category_.yml @@ -0,0 +1 @@ +label: Concepts \ No newline at end of file diff --git a/docs-en/04-concept/index.md b/docs-en/04-concept/index.md new file mode 100644 index 0000000000000000000000000000000000000000..abc553ab6d90042cb2389ba0b71d3b5395dcebfd --- /dev/null +++ b/docs-en/04-concept/index.md @@ -0,0 +1,170 @@ +--- +title: Concepts +--- + +In order to explain the basic concepts and provide some sample code, the TDengine documentation takes smart meters as a typical time series data scenario. Assuming that each smart meter collects three metrics of current, voltage, and phase, there are multiple smart meters, and each meter has static attributes like location and group ID, the collected data will be similar to the following table: + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Device IDTime StampCollected MetricsTags
Device IDTime StampcurrentvoltagephaselocationgroupId
d1001153854868500010.32190.31Beijing.Chaoyang2
d1002153854868400010.22200.23Beijing.Chaoyang3
d1003153854868650011.52210.35Beijing.Haidian3
d1004153854868550013.42230.29Beijing.Haidian2
d1001153854869500012.62180.33Beijing.Chaoyang2
d1004153854869660011.82210.28Beijing.Haidian2
d1002153854869665010.32180.25Beijing.Chaoyang3
d1001153854869680012.32210.31Beijing.Chaoyang2
+Table 1: Smart meter example data +
+ +Each row contains the device ID, time stamp, collected metrics (current, voltage, phase as above), and static tags (location and groupId in Table 1) associated with the devices. Each smart meter generates a row (measurement) in a pre-defined time interval or triggered by an external event. The device produces a sequence of measurements with associated time stamps. + +## Metric + +Metric refers to the physical quantity collected by sensors, equipment or other types of data collection devices, such as current, voltage, temperature, pressure, GPS position, etc., which change with time, and the data type can be integer, float, Boolean, or strings. As time goes by, the amount of collected metric data stored increases. + +## Label/Tag + +Label/Tag refers to the static properties of sensors, equipment or other types of data collection devices, which do not change with time, such as device model, color, fixed location of the device, etc. The data type can be any type. Although static, TDengine allows users to add, delete or update tag values at any time. Unlike the collected metric data, the amount of tag data stored does not change over time. + +## Data Collection Point + +Data Collection Point (DCP) refers to hardware or software that collects metrics based on preset time periods or triggered by events. A data collection point can collect one or multiple metrics, but these metrics are collected at the same time and have the same time stamp. For some complex equipments, there are often multiple data collection points, and the sampling rate of each collection point may be different, and fully independent. For example, for a car, there could be a data collection point to collect GPS position metrics, a data collection point to collect engine status metrics, and a data collection point to collect the environment metrics inside the car, so in this example the car would have three data collection points. + +## Table + +Since time-series data is most likely to be structured data, TDengine adopts the traditional relational database model to process them with a short learning curve. You need to create a database, create tables, then insert data points and execute queries to explore the data. + +To make full use of time-series data characteristics, TDengine adopts a strategy of "**One Table for One Data Collection Point**". TDengine requires the user to create a table for each data collection point (DCP) to store collected time-series data. For example, if there are over 10 million smart meters, it means 10 million tables should be created. For the table above, 4 tables should be created for devices D1001, D1002, D1003, and D1004 to store the data collected. This design has several benefits: + +1. Since the metric data from different DCP are fully independent, the data source of each DCP is unique, and a table has only one writer. In this way, data points can be written in a lock-free manner, and the writing speed can be greatly improved. +2. For a DCP, the metric data generated by DCP is ordered by timestamp, so the write operation can be implemented by simple appending, which further greatly improves the data writing speed. +3. The metric data from a DCP is continuously stored in block by block. If you read data for a period of time, it can greatly reduce random read operations and improve read and query performance by orders of magnitude. +4. Inside a data block for a DCP, columnar storage is used, and different compression algorithms are used for different data types. Metrics generally don't vary as significantly between themselves over a time range as compared to other metrics, this allows for a higher compression rate. + +If the metric data of multiple DCPs are traditionally written into a single table, due to the uncontrollable network delay, the timing of the data from different DCPs arriving at the server cannot be guaranteed, the writing operation must be protected by locks, and the metric data from one DCP cannot be guaranteed to be continuously stored together. **One table for one data collection point can ensure the best performance of insert and query of a single data collection point to the greatest extent.** + +TDengine suggests using DCP ID as the table name (like D1001 in the above table). Each DCP may collect one or multiple metrics (like the current, voltage, phase as above). Each metric has a corresponding column in the table. The data type for a column can be int, float, string and others. In addition, the first column in the table must be a timestamp. TDengine uses the time stamp as the index, and won’t build the index on any metrics stored. Column wise storage is used. + +## Super Table (STable) + +The design of one table for one data collection point will require a huge number of tables, which is difficult to manage. Furthermore, applications often need to take aggregation operations among DCPs, thus aggregation operations will become complicated. To support aggregation over multiple tables efficiently, the STable(Super Table) concept is introduced by TDengine. + +STable is a template for a type of data collection point. A STable contains a set of data collection points (tables) that have the same schema or data structure, but with different static attributes (tags). To describe a STable, in addition to defining the table structure of the metrics, it is also necessary to define the schema of its tags. The data type of tags can be int, float, string, and there can be multiple tags, which can be added, deleted, or modified afterward. If the whole system has N different types of data collection points, N STables need to be established. + +In the design of TDengine, **a table is used to represent a specific data collection point, and STable is used to represent a set of data collection points of the same type**. + +## Subtable + +When creating a table for a specific data collection point, the user can use a STable as a template and specifies the tag values of this specific DCP to create it. **The table created by using a STable as the template is called subtable** in TDengine. The difference between regular table and subtable is: +1. Subtable is a table, all SQL commands applied on a regular table can be applied on subtable. +2. Subtable is a table with extensions, it has static tags (labels), and these tags can be added, deleted, and updated after it is created. But a regular table does not have tags. +3. A subtable belongs to only one STable, but a STable may have many subtables. Regular tables do not belong to a STable. +4. A regular table can not be converted into a subtable, and vice versa. + +The relationship between a STable and the subtables created based on this STable is as follows: + +1. A STable contains multiple subtables with the same metric schema but with different tag values. +2. The schema of metrics or labels cannot be adjusted through subtables, and it can only be changed via STable. Changes to the schema of a STable takes effect immediately for all associated subtables. +3. STable defines only one template and does not store any data or label information by itself. Therefore, data cannot be written to a STable, only to subtables. + +Queries can be executed on both a table (subtable) and a STable. For a query on a STable, TDengine will treat the data in all its subtables as a whole data set for processing. TDengine will first find the subtables that meet the tag filter conditions, then scan the time-series data of these subtables to perform aggregation operation, which can greatly reduce the data sets to be scanned, thus greatly improving the performance of data aggregation across multiple DCPs. + +In TDengine, it is recommended to use a subtable instead of a regular table for a DCP. + +## Database + +A database is a collection of tables. TDengine allows a running instance to have multiple databases, and each database can be configured with different storage policies. Different types of DCPs often have different data characteristics, including the frequency of data collection, data retention time, the number of replications, the size of data blocks, whether data is allowed to be updated, and so on. In order for TDengine to work with maximum efficiency in various scenarios, TDengine recommends that STables with different data characteristics be created in different databases. + +In a database, there can be one or more STables, but a STable belongs to only one database. All tables owned by a STable are stored in only one database. + +## FQDN & End Point + +FQDN (Fully Qualified Domain Name) is the full domain name of a specific computer or host on the Internet. FQDN consists of two parts: hostname and domain name. For example, the FQDN of a mail server might be mail.tdengine.com. The hostname is mail, and the host is located in the domain name tdengine.com. DNS (Domain Name System) is responsible for translating FQDN into IP. For systems without DNS, it can be solved by configuring the hosts file. + +Each node of a TDengine cluster is uniquely identified by an End Point, which consists of an FQDN and a Port, such as h1.tdengine.com:6030. In this way, when the IP changes, we can still use the FQDN to dynamically find the node without changing any configuration of the cluster. In addition, FQDN is used to facilitate unified access to the same cluster from the Intranet and the Internet. + +TDengine does not recommend using an IP address to access the cluster, FQDN is recommended for cluster management. diff --git a/docs-en/05-get-started/_apt_get_install.mdx b/docs-en/05-get-started/_apt_get_install.mdx new file mode 100644 index 0000000000000000000000000000000000000000..40f6cad1f672a97fd28e6d4b5795d32b2ff0d26c --- /dev/null +++ b/docs-en/05-get-started/_apt_get_install.mdx @@ -0,0 +1,26 @@ +`apt-get` can be used to install TDengine from official package repository. + +**Package Repository** + +``` +wget -qO - http://repos.taosdata.com/tdengine.key | sudo apt-key add - +echo "deb [arch=amd64] http://repos.taosdata.com/tdengine-stable stable main" | sudo tee /etc/apt/sources.list.d/tdengine-stable.list +``` + +The repository required for installing beta versions can be configured as below: + +``` +echo "deb [arch=amd64] http://repos.taosdata.com/tdengine-beta beta main" | sudo tee /etc/apt/sources.list.d/tdengine-beta.list +``` + +**Install With apt-get** + +``` +sudo apt-get update +apt-cache policy tdengine +sudo apt-get install tdengine +``` + +:::tip +`apt-get` can only be used on Debian or Ubuntu Linux. +:::: diff --git a/docs-en/05-get-started/_category_.yml b/docs-en/05-get-started/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..043ae21554ffd8f274c6afe41c5ae5e7da742b26 --- /dev/null +++ b/docs-en/05-get-started/_category_.yml @@ -0,0 +1 @@ +label: Get Started diff --git a/docs-en/05-get-started/_pkg_install.mdx b/docs-en/05-get-started/_pkg_install.mdx new file mode 100644 index 0000000000000000000000000000000000000000..af04d2b70bda7575e57cc49a5aa60f19689113e6 --- /dev/null +++ b/docs-en/05-get-started/_pkg_install.mdx @@ -0,0 +1,17 @@ +import PkgList from "/components/PkgList"; + +It's very easy to install TDengine and would take you only a few minutes from downloading to finishing installation. + +For the convenience of users, from version 2.4.0.10, the standard server side installation package includes `taos`, `taosd`, `taosAdapter`, `taosBenchmark` and sample code. If only the `taosd` server and C/C++ connector are required, you can also choose to download the lite package. + +Three kinds of packages are provided, tar.gz, rpm and deb. Especially the tar.gz package is provided for the convenience of enterprise customers on different kinds of operating systems, it includes `taosdump` and TDinsight installation script which are normally only provided in taos-tools rpm and deb packages. + +Between two major release versions, some beta versions may be delivered for users to try some new features. + + + +For the details please refer to [Install and Uninstall](/operation/pkg-install)。 + +To see the details of versions, please refer to [Download List](https://www.taosdata.com/all-downloads) and [Release Notes](https://github.com/taosdata/TDengine/releases). + + diff --git a/docs-en/05-get-started/index.md b/docs-en/05-get-started/index.md new file mode 100644 index 0000000000000000000000000000000000000000..39b2d02eca3c15aebd5715ee64e455781c8236e5 --- /dev/null +++ b/docs-en/05-get-started/index.md @@ -0,0 +1,171 @@ +--- +title: Get Started +description: 'Install TDengine from Docker image, apt-get or package, and run TAOS CLI and taosBenchmark to experience the features' +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import PkgInstall from "./\_pkg_install.mdx"; +import AptGetInstall from "./\_apt_get_install.mdx"; + +## Quick Install + +The full package of TDengine includes the server(taosd), taosAdapter for connecting with third-party systems and providing a RESTful interface, client driver(taosc), command-line program(CLI, taos) and some tools. For the current version, the server taosd and taosAdapter can only be installed and run on Linux systems. In the future taosd and taosAdapter will also be supported on Windows, macOS and other systems. The client driver taosc and TDengine CLI can be installed and run on Windows or Linux. In addition to the connectors of multiple languages, [RESTful interface](/reference/rest-api) is also provided by [taosAdapter](/reference/taosadapter) in TDengine. Prior to version 2.4.0.0, however, there is no taosAdapter, the RESTful interface is provided by the built-in HTTP service of taosd. + +TDengine supports X64/ARM64/MIPS64/Alpha64 hardware platforms, and will support ARM32, RISC-V and other CPU architectures in the future. + + + +If docker is already installed on your computer, execute the following command: + +```shell +docker run -d -p 6030-6049:6030-6049 -p 6030-6049:6030-6049/udp tdengine/tdengine +``` + +Make sure the container is running + +```shell +docker ps +``` + +Enter into container and execute bash + +```shell +docker exec -it bash +``` + +Then you can execute the Linux commands and access TDengine. + +For detailed steps, please visit [Experience TDengine via Docker](/train-faq/docker)。 + +:::info +Starting from 2.4.0.10,besides taosd,TDengine docker image includes: taos,taosAdapter,taosdump,taosBenchmark,TDinsight, scripts and sample code. Once the TDengine container is started,it will start both taosAdapter and taosd automatically to support RESTful interface. + +::: + + + + + + + + + + +If you like to check the source code, build the package by yourself or contribute to the project, please check [TDengine GitHub Repository](https://github.com/taosdata/TDengine) + + + + +## Quick Launch + +After installation, you can launch the TDengine service by the 'systemctl' command to start 'taosd'. + +```bash +systemctl start taosd +``` + +Check if taosd is running: + +```bash +systemctl status taosd +``` + +If everything is fine, you can run TDengine command-line interface `taos` to access TDengine and test it out yourself. + +:::info + +- systemctl requires _root_ privileges,if you are not _root_ ,please add sudo before the command. +- To get feedback and keep improving the product, TDengine is collecting some basic usage information, but you can turn it off by setting telemetryReporting to 0 in configuration file taos.cfg. +- TDengine uses FQDN (usually hostname)as the ID for a node. To make the system work, you need to configure the FQDN for the server running taosd, and configure the DNS service or hosts file on the the machine where the application or TDengine CLI runs to ensure that the FQDN can be resolved. +- `systemctl stop taosd` won't stop the server right away, it will wait until all the data in memory are flushed to disk. It may takes time depending on the cache size. + +TDengine supports the installation on system which runs [`systemd`](https://en.wikipedia.org/wiki/Systemd) for process management,use `which systemctl` to check if the system has `systemd` installed: + +```bash +which systemctl +``` + +If the system does not have `systemd`,you can start TDengine manually by executing `/usr/local/taos/bin/taosd` + +:::note + +## Command Line Interface + +To manage the TDengine running instance,or execute ad-hoc queries, TDengine provides a Command Line Interface (hereinafter referred to as TDengine CLI) taos. To enter into the interactive CLI,execute `taos` on a Linux terminal where TDengine is installed. + +```bash +taos +``` + +If it connects to the TDengine server successfully, it will print out the version and welcome message. If it fails, it will print out the error message, please check [FAQ](/train-faq/faq) for trouble shooting connection issue. TDengine CLI's prompt is: + +```cmd +taos> +``` + +Inside TDengine CLI,you can execute SQL commands to create/drop database/table, and run queries. The SQL command must be ended with a semicolon. For example: + +```sql +create database demo; +use demo; +create table t (ts timestamp, speed int); +insert into t values ('2019-07-15 00:00:00', 10); +insert into t values ('2019-07-15 01:00:00', 20); +select * from t; + ts | speed | +======================================== + 2019-07-15 00:00:00.000 | 10 | + 2019-07-15 01:00:00.000 | 20 | +Query OK, 2 row(s) in set (0.003128s) +``` + +Besides executing SQL commands, system administrators can check running status, add/drop user accounts and manage the running instances. TAOS CLI with client driver can be installed and run on either Linux or Windows machines. For more details on CLI, please [check here](../reference/taos-shell/). + +## Experience the blazing fast speed + +After TDengine server is running,execute `taosBenchmark` (previously named taosdemo) from a Linux terminal: + +```bash +taosBenchmark +``` + +This command will create a super table "meters" under database "test". Under "meters", 10000 tables are created with names from "d0" to "d9999". Each table has 10000 rows and each row has four columns (ts, current, voltage, phase). Time stamp is starting from "2017-07-14 10:40:00 000" to "2017-07-14 10:40:09 999". Each table has tags "location" and "groupId". groupId is set 1 to 10 randomly, and location is set to "beijing" or "shanghai". + +This command will insert 100 million rows into the database quickly. Time to insert depends on the hardware configuration, it only takes a dozen seconds for a regular PC server. + +taosBenchmark provides command-line options and a configuration file to customize the scenarios, like number of tables, number of rows per table, number of columns and more. Please execute `taosBenchmark --help` to list them. For details on running taosBenchmark, please check [reference for taosBenchmark](/reference/taosbenchmark) + +## Experience query speed + +After using taosBenchmark to insert a number of rows data, you can execute queries from TDengine CLI to experience the lightning fast query speed. + +query the total number of rows under super table "meters": + +```sql +taos> select count(*) from test.meters; +``` + +query the average, maximum, minimum of 100 million rows: + +```sql +taos> select avg(current), max(voltage), min(phase) from test.meters; +``` + +query the total number of rows with location="beijing": + +```sql +taos> select count(*) from test.meters where location="beijing"; +``` + +query the average, maximum, minimum of all rows with groupId=10: + +```sql +taos> select avg(current), max(voltage), min(phase) from test.meters where groupId=10; +``` + +query the average, maximum, minimum for table d10 in 10 seconds time interval: + +```sql +taos> select avg(current), max(voltage), min(phase) from test.d10 interval(10s); +``` diff --git a/docs-en/07-develop/01-connect/_category_.yml b/docs-en/07-develop/01-connect/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..83f9754f582f541ca62c7ff8701698dd949c3f99 --- /dev/null +++ b/docs-en/07-develop/01-connect/_category_.yml @@ -0,0 +1 @@ +label: Connect diff --git a/docs-en/07-develop/01-connect/_connect_c.mdx b/docs-en/07-develop/01-connect/_connect_c.mdx new file mode 100644 index 0000000000000000000000000000000000000000..174bf45c4e2f26bab8f57c098f9f8f00d2f5064d --- /dev/null +++ b/docs-en/07-develop/01-connect/_connect_c.mdx @@ -0,0 +1,3 @@ +```c title="Native Connection" +{{#include docs-examples/c/connect_example.c}} +``` diff --git a/docs-en/07-develop/01-connect/_connect_cs.mdx b/docs-en/07-develop/01-connect/_connect_cs.mdx new file mode 100644 index 0000000000000000000000000000000000000000..52ea2d437123a26bd87e6f3fdc05a17141f9f835 --- /dev/null +++ b/docs-en/07-develop/01-connect/_connect_cs.mdx @@ -0,0 +1,8 @@ +```csharp title="Native Connection" +{{#include docs-examples/csharp/ConnectExample.cs}} +``` + +:::info +C# connector supports only native connection for now. + +::: diff --git a/docs-en/07-develop/01-connect/_connect_go.mdx b/docs-en/07-develop/01-connect/_connect_go.mdx new file mode 100644 index 0000000000000000000000000000000000000000..1dd5d67e3533bba21960269e49e3d843b026efc8 --- /dev/null +++ b/docs-en/07-develop/01-connect/_connect_go.mdx @@ -0,0 +1,17 @@ +#### Unified Database Access Interface + +```go title="Native Connection" +{{#include docs-examples/go/connect/cgoexample/main.go}} +``` + +```go title="REST Connection" +{{#include docs-examples/go/connect/restexample/main.go}} +``` + +#### Advanced Features + +The af package of driver-go can also be used to establish connection, with this way some advanced features of TDengine, like parameter binding and subscription, can be used. + +```go title="Establish native connection using af package" +{{#include docs-examples/go/connect/afconn/main.go}} +``` diff --git a/docs-en/07-develop/01-connect/_connect_java.mdx b/docs-en/07-develop/01-connect/_connect_java.mdx new file mode 100644 index 0000000000000000000000000000000000000000..1c3e9326bf2ae597ffba683250dd43986e670469 --- /dev/null +++ b/docs-en/07-develop/01-connect/_connect_java.mdx @@ -0,0 +1,15 @@ +```java title="Native Connection" +{{#include docs-examples/java/src/main/java/com/taos/example/JNIConnectExample.java}} +``` + +```java title="REST Connection" +{{#include docs-examples/java/src/main/java/com/taos/example/RESTConnectExample.java:main}} +``` + +When using REST connection, the feature of bulk pulling can be enabled if the size of resulting data set is huge. + +```java title="Enable Bulk Pulling" {4} +{{#include docs-examples/java/src/main/java/com/taos/example/WSConnectExample.java:main}} +``` + +More configuration about connection,please refer to [Java Connector](/reference/connector/java) diff --git a/docs-en/07-develop/01-connect/_connect_node.mdx b/docs-en/07-develop/01-connect/_connect_node.mdx new file mode 100644 index 0000000000000000000000000000000000000000..489b0386e991ee1e8ddd173205637b75ae5a0c95 --- /dev/null +++ b/docs-en/07-develop/01-connect/_connect_node.mdx @@ -0,0 +1,7 @@ +```js title="Native Connection" +{{#include docs-examples/node/nativeexample/connect.js}} +``` + +```js title="REST Connection" +{{#include docs-examples/node/restexample/connect.js}} +``` diff --git a/docs-en/07-develop/01-connect/_connect_python.mdx b/docs-en/07-develop/01-connect/_connect_python.mdx new file mode 100644 index 0000000000000000000000000000000000000000..44b7586fadbf618231fce7753d3b4b68853a7f57 --- /dev/null +++ b/docs-en/07-develop/01-connect/_connect_python.mdx @@ -0,0 +1,3 @@ +```python title="Native Connection" +{{#include docs-examples/python/connect_example.py}} +``` diff --git a/docs-en/07-develop/01-connect/_connect_r.mdx b/docs-en/07-develop/01-connect/_connect_r.mdx new file mode 100644 index 0000000000000000000000000000000000000000..09c3d71ac35b1134d3089247daea9a13db4129e2 --- /dev/null +++ b/docs-en/07-develop/01-connect/_connect_r.mdx @@ -0,0 +1,3 @@ +```r title="Native Connection" +{{#include docs-examples/R/connect_native.r:demo}} +``` diff --git a/docs-en/07-develop/01-connect/_connect_rust.mdx b/docs-en/07-develop/01-connect/_connect_rust.mdx new file mode 100644 index 0000000000000000000000000000000000000000..aa19f58de6c9bab69df0663e5369402ab1a8f899 --- /dev/null +++ b/docs-en/07-develop/01-connect/_connect_rust.mdx @@ -0,0 +1,8 @@ +```rust title="Native Connection/REST Connection" +{{#include docs-examples/rust/nativeexample/examples/connect.rs}} +``` + +:::note +For Rust connector, the connection depends on the feature being used. If "rest" feature is enabled, then only the implementation for "rest" is compiled and packaged. + +::: diff --git a/docs-en/07-develop/01-connect/index.md b/docs-en/07-develop/01-connect/index.md new file mode 100644 index 0000000000000000000000000000000000000000..ee11c8f5445233abe44e3bc006e1f15846b54ada --- /dev/null +++ b/docs-en/07-develop/01-connect/index.md @@ -0,0 +1,241 @@ +--- +sidebar_label: Connection +title: Connect to TDengine +description: "This document explains how to establish connection to TDengine, and briefly introduce how to install and use TDengine connectors." +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import ConnJava from "./\_connect_java.mdx"; +import ConnGo from "./\_connect_go.mdx"; +import ConnRust from "./\_connect_rust.mdx"; +import ConnNode from "./\_connect_node.mdx"; +import ConnPythonNative from "./\_connect_python.mdx"; +import ConnCSNative from "./\_connect_cs.mdx"; +import ConnC from "./\_connect_c.mdx"; +import ConnR from "./\_connect_r.mdx"; +import InstallOnWindows from "../../14-reference/03-connector/\_linux_install.mdx"; +import InstallOnLinux from "../../14-reference/03-connector/\_windows_install.mdx"; +import VerifyLinux from "../../14-reference/03-connector/\_verify_linux.mdx"; +import VerifyWindows from "../../14-reference/03-connector/\_verify_windows.mdx"; + +Any application programs running on any kind of platforms can access TDengine through the REST API provided by TDengine. For the details, please refer to [REST API](/reference/rest-api/). Besides, application programs can use the connectors of multiple programming languages to access TDengine, including C/C++, Java, Python, Go, Node.js, C#, and Rust. This chapter describes how to establish connection to TDengine and briefly introduce how to install and use connectors. For details about the connectors, please refer to [Connectors](/reference/connector/) + +## Establish Connection + +There are two ways for a connector to establish connections to TDengine: + +1. Connection through the REST API provided by taosAdapter component, this way is called "REST connection" hereinafter. +2. Connection through the TDengine client driver (taosc), this way is called "Native connection" hereinafter. + +Either way, same or similar APIs are provided by connectors to access database or execute SQL statements, no obvious difference can be observed. + +Key differences: + +1. With REST connection, it's not necessary to install TDengine client driver (taosc), it's more friendly for cross-platform with the cost of 30% performance downgrade. When taosc has an upgrade, application does not need to make changes. +2. With native connection, full compatibility of TDengine can be utilized, like [Parameter Binding](/reference/connector/cpp#parameter-binding-api), [Subscription](/reference/connector/cpp#subscription-and-consumption-api), etc. But taosc has to be installed, some platforms may not be supported. + +## Install Client Driver taosc + +If choosing to use native connection and the application is not on the same host as TDengine server, TDengine client driver taosc needs to be installed on the host where the application is. If choosing to use REST connection or the application is on the same host as server side, this step can be skipped. It's better to use same version of taosc as the server. + +### Install + + + + + + + + + + +### Verify + +After the above installation and configuration are done and making sure TDengine service is already started and in service, the TDengine command-line interface `taos` can be launched to access TDengine. + + + + + + + + + + +## Install Connectors + + + + +If `maven` is used to manage the projects, what needs to be done is only adding below dependency in `pom.xml`. + +```xml + + com.taosdata.jdbc + taos-jdbcdriver + 2.0.38 + +``` + + + + +Install from PyPI using `pip`: + +``` +pip install taospy +``` + +Install from Git URL: + +``` +pip install git+https://github.com/taosdata/taos-connector-python.git +``` + + + + +Just need to add `driver-go` dependency in `go.mod` . + +```go-mod title=go.mod +module goexample + +go 1.17 + +require github.com/taosdata/driver-go/v2 develop +``` + +:::note +`driver-go` uses `cgo` to wrap the APIs provided by taosc, while `cgo` needs `gcc` to compile source code in C language, so please make sure you have proper `gcc` on your system. + +::: + + + + +Just need to add `libtaos` dependency in `Cargo.toml`. + +```toml title=Cargo.toml +[dependencies] +libtaos = { version = "0.4.2"} +``` + +:::info +Rust connector uses different features to distinguish the way to establish connection. To establish REST connection, please enable `rest` feature. + +```toml +libtaos = { version = "*", features = ["rest"] } +``` + +::: + + + + +Node.js connector provides different ways of establishing connections by providing different packages. + +1. Install Node.js Native Connector + +``` +npm i td2.0-connector +``` + +:::note +It's recommend to use Node whose version is between `node-v12.8.0` and `node-v13.0.0`. +::: + +2. Install Node.js REST Connector + +``` +npm i td2.0-rest-connector +``` + + + + +Just need to add the reference to [TDengine.Connector](https://www.nuget.org/packages/TDengine.Connector/) in the project configuration file. + +```xml title=csharp.csproj {12} + + + + Exe + net6.0 + enable + enable + TDengineExample.AsyncQueryExample + + + + + + + +``` + +Or add by `dotnet` command. + +``` +dotnet add package TDengine.Connector +``` + +:::note +The sample code below are based on dotnet6.0, they may need to be adjusted if your dotnet version is not exactly same. + +::: + + + + +1. Download [taos-jdbcdriver-version-dist.jar](https://repo1.maven.org/maven2/com/taosdata/jdbc/taos-jdbcdriver/2.0.38/). +2. Install the dependency package `RJDBC`: + +```R +install.packages("RJDBC") +``` + + + + +If the client driver (taosc) is already installed, then the C connector is already available. +
+ +
+
+ +## Establish Connection + +Prior to establishing connection, please make sure TDengine is already running and accessible. The following sample code assumes TDengine is running on the same host as the client program, with FQDN configured to "localhost" and serverPort configured to "6030". + + + + + + + + + + + + + + + + + + + + + + + + + + + + +:::tip +If the connection fails, in most cases it's caused by improper configuration for FQDN or firewall. Please refer to the section "Unable to establish connection" in [FAQ](https://docs.taosdata.com/train-faq/faq). + +::: diff --git a/docs-en/07-develop/02-model/_category_.yml b/docs-en/07-develop/02-model/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..a2b49eb879c593b29cba1b1bfab3f5b2b615c1e6 --- /dev/null +++ b/docs-en/07-develop/02-model/_category_.yml @@ -0,0 +1,2 @@ +label: Data Model + diff --git a/docs-en/07-develop/02-model/index.mdx b/docs-en/07-develop/02-model/index.mdx new file mode 100644 index 0000000000000000000000000000000000000000..962a75338f0384ee8facb4682342e25e536e4ecb --- /dev/null +++ b/docs-en/07-develop/02-model/index.mdx @@ -0,0 +1,84 @@ +--- +title: Data Model +--- + +The data model employed by TDengine is similar to relational database, you need to create databases and tables. For a specific application, the design of databases, STables (abbreviated for super table), and tables need to be considered. This chapter will explain the big picture without syntax details. + +## Create Database + +The characteristics of data from different data collection points may be different, such as collection frequency, days to keep, number of replicas, data block size, whether it's allowed to update data, etc. For TDengine to operate with the best performance, it's strongly suggested to put the data with different characteristics into different databases because different storage policy can be set for each database. When creating a database, there are a lot of parameters that can be configured, such as the days to keep data, the number of replicas, the number of memory blocks, time precision, the minimum and maximum number of rows in each data block, compress or not, the time range of the data in single data file, etc. Below is an example of the SQL statement for creating a database. + +```sql +CREATE DATABASE power KEEP 365 DAYS 10 BLOCKS 6 UPDATE 1; +``` + +In the above SQL statement, a database named "power" will be created, the data in it will be kept for 365 days, which means the data older than 365 days will be deleted automatically, a new data file will be created every 10 days, the number of memory blocks is 6, data is allowed to be updated. For more details please refer to [Database](/taos-sql/database). + +After creating a database, the current database in use can be switched using SQL command `USE`, for example below SQL statement switches the current database to `power`. Without current database specified, table name must be preceded with the corresponding database name. + +```sql +USE power; +``` + +:::note + +- Any table or STable must belong to a database. To create a table or STable, the database it belongs to must be ready. +- JOIN operation can't be performed tables from two different databases. +- Timestamp needs to be specified when inserting rows or querying historical rows. + +::: + +## Create STable + +In a time-series application, there may be multiple kinds of data collection points. For example, in the electrical power system there are meters, transformers, bus bars, switches, etc. For easy and efficient aggregation of multiple tables, one STable needs to be created for each kind of data collection point. For example, for the meters in [table 1](/tdinternal/arch#model_table1), below SQL statement can be used to create the super table. + +```sql +CREATE STable meters (ts timestamp, current float, voltage int, phase float) TAGS (location binary(64), groupId int); +``` + +:::note +If you are using versions prior to 2.0.15, the `STable` keyword needs to be replaced with `TABLE`. + +::: + +Similar to creating a regular table, when creating a STable, name and schema need to be provided too. In the STable schema, the first column must be timestamp (like ts in the example), and other columns (like current, voltage and phase in the example) are the data collected. The type of a column can be integer, float, double, string ,etc. Besides, the schema for tags need to be provided, like location and groupId in the example. The type of a tag can be integer, float, string, etc. The static properties of a data collection point can be defined as tags, like the location, device type, device group ID, manager ID, etc. Tags in the schema can be added, removed or updated. Please refer to [STable](/taos-sql/stable) for more details. + +For each kind of data collection points, a corresponding STable must be created. There may be many STables in an application. For electrical power system, we need to create a STable respectively for meters, transformers, busbars, switches. There may be multiple kinds of data collection points on a single device, for example there may be one data collection point for electrical data like current and voltage and another point for environmental data like temperature, humidity and wind direction, multiple STables are required for such kind of device. + +At most 4096 (or 1024 prior to version 2.1.7.0) columns are allowed in a STable. If there are more than 4096 of metrics to bo collected for a data collection point, multiple STables are required for such kind of data collection point. There can be multiple databases in system, while one or more STables can exist in a database. + +## Create Table + +A specific table needs to be created for each data collection point. Similar to RDBMS, table name and schema are required to create a table. Beside, one or more tags can be created for each table. To create a table, a STable needs to be used as template and the values need to be specified for the tags. For example, for the meters in [Table 1](/tdinternal/arch#model_table1), the table can be created using below SQL statement. + +```sql +CREATE TABLE d1001 USING meters TAGS ("Beijing.Chaoyang", 2); +``` + +In the above SQL statement, "d1001" is the table name, "meters" is the STable name, followed by the value of tag "Location" and the value of tag "groupId", which are "Beijing.Chaoyang" and "2" respectively in the example. The tag values can be updated after the table is created. Please refer to [Tables](/taos-sql/table) for details. + +In TDengine system, it's recommended to create a table for a data collection point via STable. Table created via STable is called subtable in some parts of TDengine document. All SQL commands applied on regular table can be applied on subtable. + +:::warning +It's not recommended to create a table in a database while using a STable from another database as template. + +:::tip +It's suggested to use the global unique ID of a data collection point as the table name, for example the device serial number. If there isn't such a unique ID, multiple IDs that are not global unique can be combined to form a global unique ID. It's not recommended to use a global unique ID as tag value. + +## Create Table Automatically + +In some circumstances, it's not sure whether the table already exists when inserting rows. The table can be created automatically using the SQL statement below, and nothing will happen if the table already exist. + +```sql +INSERT INTO d1001 USING meters TAGS ("Beijng.Chaoyang", 2) VALUES (now, 10.2, 219, 0.32); +``` + +In the above SQL statement, a row with value `(now, 10.2, 219, 0.32)` will be inserted into table "d1001". If table "d1001" doesn't exist, it will be created automatically using STable "meters" as template with tag value `"Beijing.Chaoyang", 2`. + +For more details please refer to [Create Table Automatically](/taos-sql/insert#automatically-create-table-when-inserting). + +## Single Column vs Multiple Column + +Multiple columns data model is supported in TDengine. As long as multiple metrics are collected by same data collection point at same time, i.e. the timestamp are identical, these metrics can be put in single stable as columns. However, there is another kind of design, i.e. single column data model, a table is created for each metric, which means a STable is required for each kind of metric. For example, 3 STables are required for current, voltage and phase. + +It's recommended to use multiple column data model as much as possible because it's better in the performance of inserting or querying rows. In some cases, however, the metrics to be collected vary frequently and correspondingly the STable schema needs to be changed frequently too. In such case, it's more convenient to use single column data model. diff --git a/docs-en/07-develop/03-insert-data/01-sql-writing.mdx b/docs-en/07-develop/03-insert-data/01-sql-writing.mdx new file mode 100644 index 0000000000000000000000000000000000000000..9f66992d3de755389c3a0722ebb09097177742f1 --- /dev/null +++ b/docs-en/07-develop/03-insert-data/01-sql-writing.mdx @@ -0,0 +1,130 @@ +--- +sidebar_label: SQL +title: Insert Using SQL +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import JavaSQL from "./_java_sql.mdx"; +import JavaStmt from "./_java_stmt.mdx"; +import PySQL from "./_py_sql.mdx"; +import PyStmt from "./_py_stmt.mdx"; +import GoSQL from "./_go_sql.mdx"; +import GoStmt from "./_go_stmt.mdx"; +import RustSQL from "./_rust_sql.mdx"; +import RustStmt from "./_rust_stmt.mdx"; +import NodeSQL from "./_js_sql.mdx"; +import NodeStmt from "./_js_stmt.mdx"; +import CsSQL from "./_cs_sql.mdx"; +import CsStmt from "./_cs_stmt.mdx"; +import CSQL from "./_c_sql.mdx"; +import CStmt from "./_c_stmt.mdx"; + +## Introduction + +Application program can execute `INSERT` statement through connectors to insert rows. TAOS CLI can be launched manually to insert data too. + +### Insert Single Row + +Below SQL statement is used to insert one row into table "d1001". + +```sql +INSERT INTO d1001 VALUES (1538548685000, 10.3, 219, 0.31); +``` + +### Insert Multiple Rows + +Multiple rows can be inserted in single SQL statement. Below example inserts 2 rows into table "d1001". + +```sql +INSERT INTO d1001 VALUES (1538548684000, 10.2, 220, 0.23) (1538548696650, 10.3, 218, 0.25); +``` + +### Insert into Multiple Tables + +Data can be inserted into multiple tables in same SQL statement. Below example inserts 2 rows into table "d1001" and 1 row into table "d1002". + +```sql +INSERT INTO d1001 VALUES (1538548685000, 10.3, 219, 0.31) (1538548695000, 12.6, 218, 0.33) d1002 VALUES (1538548696800, 12.3, 221, 0.31); +``` + +For more details about `INSERT` please refer to [INSERT](/taos-sql/insert). + +:::info + +- Inserting in batch can gain better performance. Normally, the higher the batch size, the better the performance. Please be noted each single row can't exceed 16K bytes and each single SQL statement can't exceed 1M bytes. +- Inserting with multiple threads can gain better performance too. However, depending on the system resources on the application side and the server side, with the number of inserting threads grows to a specific point, the performance may drop instead of growing. The proper number of threads need to be tested in a specific environment to find the best number. + +::: + +:::warning + +- If the timestamp for the row to be inserted already exists in the table, the behavior depends on the value of parameter `UPDATE`. If it's set to 0 (also the default value), the row will be discarded. If it's set to 1, the new values will override the old values for the same row. +- The timestamp to be inserted must be newer than the timestamp of subtracting current time by the parameter `KEEP`. If `KEEP` is set to 3650 days, then the data older than 3650 days ago can't be inserted. The timestamp to be inserted can't be newer than the timestamp of current time plus parameter `DAYS`. If `DAYS` is set to 2, the data newer than 2 days later can't be inserted. + +::: + +## Examples + +### Insert Using SQL + + + + + + + + + + + + + + + + + + + + + + + + + +:::note + +1. With either native connection or REST connection, the above samples can work well. +2. Please be noted that `use db` can't be used with REST connection because REST connection is stateless, so in the samples `dbName.tbName` is used to specify the table name. + +::: + +### Insert with Parameter Binding + +TDengine also provides Prepare API that support parameter binding. Similar to MySQL, only `?` can be used in these APIs to represent the parameters to bind. From version 2.1.1.0 and 2.1.2.0, parameter binding support for inserting data has been improved significantly to improve the insert performance by avoiding the cost of parsing SQL statements. + +Parameter binding is available only with native connection. + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs-en/07-develop/03-insert-data/02-influxdb-line.mdx b/docs-en/07-develop/03-insert-data/02-influxdb-line.mdx new file mode 100644 index 0000000000000000000000000000000000000000..172003d203fa309ce51b3ecae9a7490a59f513d7 --- /dev/null +++ b/docs-en/07-develop/03-insert-data/02-influxdb-line.mdx @@ -0,0 +1,70 @@ +--- +sidebar_label: InfluxDB Line Protocol +title: InfluxDB Line Protocol +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import JavaLine from "./_java_line.mdx"; +import PyLine from "./_py_line.mdx"; +import GoLine from "./_go_line.mdx"; +import RustLine from "./_rust_line.mdx"; +import NodeLine from "./_js_line.mdx"; +import CsLine from "./_cs_line.mdx"; +import CLine from "./_c_line.mdx"; + +## Introduction + +A single line of text is used in InfluxDB Line protocol format represents one row of data, each line contains 4 parts as shown below. + +``` +measurement,tag_set field_set timestamp +``` + +- `measurement` will be used as the STable name +- `tag_set` will be used as tags, with format like `=,=` +- `field_set`will be used as data columns, with format like `=,=` +- `timestamp` is the primary key timestamp corresponding to this row of data + +For example: + +``` +meters,location=Beijing.Haidian,groupid=2 current=13.4,voltage=223,phase=0.29 1648432611249500 +``` + +:::note + +- All the data in `tag_set` will be converted to ncahr type automatically . +- Each data in `field_set` must be self-description for its data type. For example 1.2f32 means a value 1.2 of float type, it will be treated as double without the "f" type suffix. +- Multiple kinds of precision can be used for the `timestamp` field. Time precision can be from nanosecond (ns) to hour (h). + +::: + +For more details please refer to [InfluxDB Line Protocol](https://docs.influxdata.com/influxdb/v2.0/reference/syntax/line-protocol/) and [TDengine Schemaless](/reference/schemaless/#Schemaless-Line-Protocol) + + +## Examples + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs-en/07-develop/03-insert-data/03-opentsdb-telnet.mdx b/docs-en/07-develop/03-insert-data/03-opentsdb-telnet.mdx new file mode 100644 index 0000000000000000000000000000000000000000..66bb67c25669b906183526377f60b969ea3d1e85 --- /dev/null +++ b/docs-en/07-develop/03-insert-data/03-opentsdb-telnet.mdx @@ -0,0 +1,84 @@ +--- +sidebar_label: OpenTSDB Line Protocol +title: OpenTSDB Line Protocol +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import JavaTelnet from "./_java_opts_telnet.mdx"; +import PyTelnet from "./_py_opts_telnet.mdx"; +import GoTelnet from "./_go_opts_telnet.mdx"; +import RustTelnet from "./_rust_opts_telnet.mdx"; +import NodeTelnet from "./_js_opts_telnet.mdx"; +import CsTelnet from "./_cs_opts_telnet.mdx"; +import CTelnet from "./_c_opts_telnet.mdx"; + +## Introduction + +A single line of text is used in OpenTSDB line protocol to represent one row of data. OpenTSDB employs single column data model, so one line can only contains single data column. There can be multiple tags. Each line contains 4 parts as below: + +``` + =[ =] +``` + +- `metric` will be used as STable name. +- `timestamp` is the timestamp of current row of data. The time precision will be determined automatically based on the length of the timestamp. second and millisecond time precision are supported.\ +- `value` is a metric which must be a numeric value, the corresponding column name is "value". +- The last part is tag sets separated by space, all tags will be converted to nchar type automatically. + +For example: + +```txt +meters.current 1648432611250 11.3 location=Beijing.Haidian groupid=3 +``` + +Please refer to [OpenTSDB Telnet API](http://opentsdb.net/docs/build/html/api_telnet/put.html) for more details. + +## Examples + + + + + + + + + + + + + + + + + + + + + + + + + +2 STables will be crated automatically while each STable has 4 rows of data in the above sample code. + +```cmd +taos> use test; +Database changed. + +taos> show STables; + name | created_time | columns | tags | tables | +============================================================================================ + meters.current | 2022-03-30 17:04:10.877 | 2 | 2 | 2 | + meters.voltage | 2022-03-30 17:04:10.882 | 2 | 2 | 2 | +Query OK, 2 row(s) in set (0.002544s) + +taos> select tbname, * from `meters.current`; + tbname | ts | value | groupid | location | +================================================================================================================================== + t_0e7bcfa21a02331c06764f275... | 2022-03-28 09:56:51.249 | 10.800000000 | 3 | Beijing.Haidian | + t_0e7bcfa21a02331c06764f275... | 2022-03-28 09:56:51.250 | 11.300000000 | 3 | Beijing.Haidian | + t_7e7b26dd860280242c6492a16... | 2022-03-28 09:56:51.249 | 10.300000000 | 2 | Beijing.Chaoyang | + t_7e7b26dd860280242c6492a16... | 2022-03-28 09:56:51.250 | 12.600000000 | 2 | Beijing.Chaoyang | +Query OK, 4 row(s) in set (0.005399s) +``` diff --git a/docs-en/07-develop/03-insert-data/04-opentsdb-json.mdx b/docs-en/07-develop/03-insert-data/04-opentsdb-json.mdx new file mode 100644 index 0000000000000000000000000000000000000000..d4f723dcdeb78c54ba31fd4f6aa2528a90376c5f --- /dev/null +++ b/docs-en/07-develop/03-insert-data/04-opentsdb-json.mdx @@ -0,0 +1,99 @@ +--- +sidebar_label: OpenTSDB JSON Protocol +title: OpenTSDB JSON Protocol +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import JavaJson from "./_java_opts_json.mdx"; +import PyJson from "./_py_opts_json.mdx"; +import GoJson from "./_go_opts_json.mdx"; +import RustJson from "./_rust_opts_json.mdx"; +import NodeJson from "./_js_opts_json.mdx"; +import CsJson from "./_cs_opts_json.mdx"; +import CJson from "./_c_opts_json.mdx"; + +## Introduction + +A JSON string is used in OpenTSDB JSON to represent one or more rows of data, for example: + +```json +[ + { + "metric": "sys.cpu.nice", + "timestamp": 1346846400, + "value": 18, + "tags": { + "host": "web01", + "dc": "lga" + } + }, + { + "metric": "sys.cpu.nice", + "timestamp": 1346846400, + "value": 9, + "tags": { + "host": "web02", + "dc": "lga" + } + } +] +``` + +Similar to OpenTSDB line protocol, `metric` will be used as the STable name, `timestamp` is the timestamp to be used, `value` represents the metric collected, `tags` are the tag sets. + + +Please refer to [OpenTSDB HTTP API](http://opentsdb.net/docs/build/html/api_http/put.html) for more details. + +:::note +- In JSON protocol, strings will be converted to nchar type and numeric values will be converted to double type. +- Only data in array format is accepted, array must be used even there is only one row. + +::: + +## Examples + + + + + + + + + + + + + + + + + + + + + + + + + +The above sample code will created 2 STables automatically while each STable has 2 rows of data. + +```cmd +taos> use test; +Database changed. + +taos> show STables; + name | created_time | columns | tags | tables | +============================================================================================ + meters.current | 2022-03-29 16:05:25.193 | 2 | 2 | 1 | + meters.voltage | 2022-03-29 16:05:25.200 | 2 | 2 | 1 | +Query OK, 2 row(s) in set (0.001954s) + +taos> select * from `meters.current`; + ts | value | groupid | location | +=================================================================================================================== + 2022-03-28 09:56:51.249 | 10.300000000 | 2.000000000 | Beijing.Chaoyang | + 2022-03-28 09:56:51.250 | 12.600000000 | 2.000000000 | Beijing.Chaoyang | +Query OK, 2 row(s) in set (0.004076s) +``` diff --git a/docs-en/07-develop/03-insert-data/_c_line.mdx b/docs-en/07-develop/03-insert-data/_c_line.mdx new file mode 100644 index 0000000000000000000000000000000000000000..5ef2e9af774c54e9f090357286f83d2280c2ab11 --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_c_line.mdx @@ -0,0 +1,3 @@ +```c +{{#include docs-examples/c/line_example.c:main}} +``` \ No newline at end of file diff --git a/docs-en/07-develop/03-insert-data/_c_opts_json.mdx b/docs-en/07-develop/03-insert-data/_c_opts_json.mdx new file mode 100644 index 0000000000000000000000000000000000000000..22ad2e0122797248a372734aac0f3a16a1356530 --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_c_opts_json.mdx @@ -0,0 +1,3 @@ +```c +{{#include docs-examples/c/json_protocol_example.c:main}} +``` \ No newline at end of file diff --git a/docs-en/07-develop/03-insert-data/_c_opts_telnet.mdx b/docs-en/07-develop/03-insert-data/_c_opts_telnet.mdx new file mode 100644 index 0000000000000000000000000000000000000000..508d7bc98a149f49766bcd0a474ffe226cbe30bb --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_c_opts_telnet.mdx @@ -0,0 +1,3 @@ +```c +{{#include docs-examples/c/telnet_line_example.c:main}} +``` \ No newline at end of file diff --git a/docs-en/07-develop/03-insert-data/_c_sql.mdx b/docs-en/07-develop/03-insert-data/_c_sql.mdx new file mode 100644 index 0000000000000000000000000000000000000000..f4153fd2c427677a338d0c377663d0335f2672f0 --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_c_sql.mdx @@ -0,0 +1,3 @@ +```c +{{#include docs-examples/c/insert_example.c}} +``` \ No newline at end of file diff --git a/docs-en/07-develop/03-insert-data/_c_stmt.mdx b/docs-en/07-develop/03-insert-data/_c_stmt.mdx new file mode 100644 index 0000000000000000000000000000000000000000..7f5ef23a849689c36e732b6fd374a131695c9090 --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_c_stmt.mdx @@ -0,0 +1,6 @@ +```c title=Single Row Binding +{{#include docs-examples/c/stmt_example.c}} +``` +```c title=Multiple Row Binding 72:117 +{{#include docs-examples/c/multi_bind_example.c}} +``` \ No newline at end of file diff --git a/docs-en/07-develop/03-insert-data/_category_.yml b/docs-en/07-develop/03-insert-data/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..e515d60e09ec44894e2c42f38fee74fe4286e17f --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_category_.yml @@ -0,0 +1 @@ +label: Insert Data diff --git a/docs-en/07-develop/03-insert-data/_cs_line.mdx b/docs-en/07-develop/03-insert-data/_cs_line.mdx new file mode 100644 index 0000000000000000000000000000000000000000..9c275ee3d7c7a1e52fbb34dbae922004543ee3ce --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_cs_line.mdx @@ -0,0 +1,3 @@ +```csharp +{{#include docs-examples/csharp/InfluxDBLineExample.cs}} +``` diff --git a/docs-en/07-develop/03-insert-data/_cs_opts_json.mdx b/docs-en/07-develop/03-insert-data/_cs_opts_json.mdx new file mode 100644 index 0000000000000000000000000000000000000000..3d538b8506b298241faecd8098f89571359135c9 --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_cs_opts_json.mdx @@ -0,0 +1,3 @@ +```csharp +{{#include docs-examples/csharp/OptsJsonExample.cs}} +``` diff --git a/docs-en/07-develop/03-insert-data/_cs_opts_telnet.mdx b/docs-en/07-develop/03-insert-data/_cs_opts_telnet.mdx new file mode 100644 index 0000000000000000000000000000000000000000..c53bf3d7233115351e5af03b7d9e6318aa4a0da6 --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_cs_opts_telnet.mdx @@ -0,0 +1,3 @@ +```csharp +{{#include docs-examples/csharp/OptsTelnetExample.cs}} +``` diff --git a/docs-en/07-develop/03-insert-data/_cs_sql.mdx b/docs-en/07-develop/03-insert-data/_cs_sql.mdx new file mode 100644 index 0000000000000000000000000000000000000000..c7688bfbe77a1135424d829fe9b29fbb1bc93ae2 --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_cs_sql.mdx @@ -0,0 +1,3 @@ +```csharp +{{#include docs-examples/csharp/SQLInsertExample.cs}} +``` diff --git a/docs-en/07-develop/03-insert-data/_cs_stmt.mdx b/docs-en/07-develop/03-insert-data/_cs_stmt.mdx new file mode 100644 index 0000000000000000000000000000000000000000..97c3b910ffeb9e0c88fc143a02014115e819c147 --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_cs_stmt.mdx @@ -0,0 +1,3 @@ +```csharp +{{#include docs-examples/csharp/StmtInsertExample.cs}} +``` diff --git a/docs-en/07-develop/03-insert-data/_go_line.mdx b/docs-en/07-develop/03-insert-data/_go_line.mdx new file mode 100644 index 0000000000000000000000000000000000000000..cd225945b70e28bef2ca7fdaf0d9be0ad7ffc18c --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_go_line.mdx @@ -0,0 +1,3 @@ +```go +{{#include docs-examples/go/insert/line/main.go}} +``` diff --git a/docs-en/07-develop/03-insert-data/_go_opts_json.mdx b/docs-en/07-develop/03-insert-data/_go_opts_json.mdx new file mode 100644 index 0000000000000000000000000000000000000000..0c0d3e5b6330e046988cdd02234285ec67e92f01 --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_go_opts_json.mdx @@ -0,0 +1,3 @@ +```go +{{#include docs-examples/go/insert/json/main.go}} +``` diff --git a/docs-en/07-develop/03-insert-data/_go_opts_telnet.mdx b/docs-en/07-develop/03-insert-data/_go_opts_telnet.mdx new file mode 100644 index 0000000000000000000000000000000000000000..d5ca40cc146e62412476289853e8e2739e0e9e4b --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_go_opts_telnet.mdx @@ -0,0 +1,3 @@ +```go +{{#include docs-examples/go/insert/telnet/main.go}} +``` diff --git a/docs-en/07-develop/03-insert-data/_go_sql.mdx b/docs-en/07-develop/03-insert-data/_go_sql.mdx new file mode 100644 index 0000000000000000000000000000000000000000..613a65add1741eb763a4b24e65d180d05f7d670f --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_go_sql.mdx @@ -0,0 +1,3 @@ +```go +{{#include docs-examples/go/insert/sql/main.go}} +``` diff --git a/docs-en/07-develop/03-insert-data/_go_stmt.mdx b/docs-en/07-develop/03-insert-data/_go_stmt.mdx new file mode 100644 index 0000000000000000000000000000000000000000..c32bc21fb9bcaf45059e4f47df73fb57f047ed1c --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_go_stmt.mdx @@ -0,0 +1,8 @@ +```go +{{#include docs-examples/go/insert/stmt/main.go}} +``` + +:::tip +`github.com/taosdata/driver-go/v2/wrapper` module in driver-go is the wrapper for C API, it can be used to insert data with parameter binding. + +::: diff --git a/docs-en/07-develop/03-insert-data/_java_line.mdx b/docs-en/07-develop/03-insert-data/_java_line.mdx new file mode 100644 index 0000000000000000000000000000000000000000..2e59a5d4701b2a2ab04ec5711845dc5c80067a1e --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_java_line.mdx @@ -0,0 +1,3 @@ +```java +{{#include docs-examples/java/src/main/java/com/taos/example/LineProtocolExample.java}} +``` diff --git a/docs-en/07-develop/03-insert-data/_java_opts_json.mdx b/docs-en/07-develop/03-insert-data/_java_opts_json.mdx new file mode 100644 index 0000000000000000000000000000000000000000..826a1a07d9405cb193849f9d21e5444f68517914 --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_java_opts_json.mdx @@ -0,0 +1,3 @@ +```java +{{#include docs-examples/java/src/main/java/com/taos/example/JSONProtocolExample.java}} +``` diff --git a/docs-en/07-develop/03-insert-data/_java_opts_telnet.mdx b/docs-en/07-develop/03-insert-data/_java_opts_telnet.mdx new file mode 100644 index 0000000000000000000000000000000000000000..954dcc1a482a150dea0b190e1e0593adbfbde796 --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_java_opts_telnet.mdx @@ -0,0 +1,3 @@ +```java +{{#include docs-examples/java/src/main/java/com/taos/example/TelnetLineProtocolExample.java}} +``` diff --git a/docs-en/07-develop/03-insert-data/_java_sql.mdx b/docs-en/07-develop/03-insert-data/_java_sql.mdx new file mode 100644 index 0000000000000000000000000000000000000000..a863378defe43b1f22c1f98087a34f053a7d6619 --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_java_sql.mdx @@ -0,0 +1,3 @@ +```java +{{#include docs-examples/java/src/main/java/com/taos/example/RestInsertExample.java:insert}} +``` \ No newline at end of file diff --git a/docs-en/07-develop/03-insert-data/_java_stmt.mdx b/docs-en/07-develop/03-insert-data/_java_stmt.mdx new file mode 100644 index 0000000000000000000000000000000000000000..54443e535fa84bdf8dc9161ed4ad00f50b26266c --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_java_stmt.mdx @@ -0,0 +1,3 @@ +```java +{{#include docs-examples/java/src/main/java/com/taos/example/StmtInsertExample.java}} +``` diff --git a/docs-en/07-develop/03-insert-data/_js_line.mdx b/docs-en/07-develop/03-insert-data/_js_line.mdx new file mode 100644 index 0000000000000000000000000000000000000000..172c9bc17b8cff8b2620720b235a9c8e69bd4197 --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_js_line.mdx @@ -0,0 +1,3 @@ +```js +{{#include docs-examples/node/nativeexample/influxdb_line_example.js}} +``` diff --git a/docs-en/07-develop/03-insert-data/_js_opts_json.mdx b/docs-en/07-develop/03-insert-data/_js_opts_json.mdx new file mode 100644 index 0000000000000000000000000000000000000000..20ac9ec91e8dc6675828b16d7da0acb09afd3b5f --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_js_opts_json.mdx @@ -0,0 +1,3 @@ +```js +{{#include docs-examples/node/nativeexample/opentsdb_json_example.js}} +``` diff --git a/docs-en/07-develop/03-insert-data/_js_opts_telnet.mdx b/docs-en/07-develop/03-insert-data/_js_opts_telnet.mdx new file mode 100644 index 0000000000000000000000000000000000000000..c3c8c40bd642f4f443de88e3db006ad50724d514 --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_js_opts_telnet.mdx @@ -0,0 +1,3 @@ +```js +{{#include docs-examples/node/nativeexample/opentsdb_telnet_example.js}} +``` diff --git a/docs-en/07-develop/03-insert-data/_js_sql.mdx b/docs-en/07-develop/03-insert-data/_js_sql.mdx new file mode 100644 index 0000000000000000000000000000000000000000..f5e17c76892a57a94192a95451b508b1c176c984 --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_js_sql.mdx @@ -0,0 +1,3 @@ +```js +{{#include docs-examples/node/nativeexample/insert_example.js}} +``` diff --git a/docs-en/07-develop/03-insert-data/_js_stmt.mdx b/docs-en/07-develop/03-insert-data/_js_stmt.mdx new file mode 100644 index 0000000000000000000000000000000000000000..964d7ddc11b90031b70936efb85fbaabe873ddbb --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_js_stmt.mdx @@ -0,0 +1,12 @@ +```js title=Single Row Binding +{{#include docs-examples/node/nativeexample/param_bind_example.js}} +``` + +```js title=Multiple Row Binding +{{#include docs-examples/node/nativeexample/multi_bind_example.js:insertData}} +``` + +:::info +Multiple row binding is better in performance than single row binding, but it can only be used with `INSERT` statement while single row binding can be used for other SQL statements besides `INSERT`. + +::: diff --git a/docs-en/07-develop/03-insert-data/_py_line.mdx b/docs-en/07-develop/03-insert-data/_py_line.mdx new file mode 100644 index 0000000000000000000000000000000000000000..d3bb1ebb3403b53fa43bfc9d5d1a0de9764d7583 --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_py_line.mdx @@ -0,0 +1,3 @@ +```py +{{#include docs-examples/python/line_protocol_example.py}} +``` diff --git a/docs-en/07-develop/03-insert-data/_py_opts_json.mdx b/docs-en/07-develop/03-insert-data/_py_opts_json.mdx new file mode 100644 index 0000000000000000000000000000000000000000..cfbfe13ccfdb4f3f34b77300812863fdf70d0f59 --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_py_opts_json.mdx @@ -0,0 +1,3 @@ +```py +{{#include docs-examples/python/json_protocol_example.py}} +``` diff --git a/docs-en/07-develop/03-insert-data/_py_opts_telnet.mdx b/docs-en/07-develop/03-insert-data/_py_opts_telnet.mdx new file mode 100644 index 0000000000000000000000000000000000000000..14bc65a7a3da815abadf7f25c8deffeac666c8d7 --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_py_opts_telnet.mdx @@ -0,0 +1,3 @@ +```py +{{#include docs-examples/python/telnet_line_protocol_example.py}} +``` diff --git a/docs-en/07-develop/03-insert-data/_py_sql.mdx b/docs-en/07-develop/03-insert-data/_py_sql.mdx new file mode 100644 index 0000000000000000000000000000000000000000..c0e15b8ec115b9244d50a47c9eafec04bcfdd70c --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_py_sql.mdx @@ -0,0 +1,3 @@ +```py +{{#include docs-examples/python/native_insert_example.py}} +``` diff --git a/docs-en/07-develop/03-insert-data/_py_stmt.mdx b/docs-en/07-develop/03-insert-data/_py_stmt.mdx new file mode 100644 index 0000000000000000000000000000000000000000..16d98f54329ad0d3dfb463392f5c1d41c9aab25b --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_py_stmt.mdx @@ -0,0 +1,12 @@ +```py title=Single Row Binding +{{#include docs-examples/python/bind_param_example.py}} +``` + +```py title=Multiple Row Binding +{{#include docs-examples/python/multi_bind_example.py:bind_batch}} +``` + +:::info +Multiple row binding is better in performance than single row binding, but it can only be used with `INSERT` statement while single row binding can be used for other SQL statements besides `INSERT`. + +::: \ No newline at end of file diff --git a/docs-en/07-develop/03-insert-data/_rust_line.mdx b/docs-en/07-develop/03-insert-data/_rust_line.mdx new file mode 100644 index 0000000000000000000000000000000000000000..696ddb7b854751b8dee01047066f97f74212933f --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_rust_line.mdx @@ -0,0 +1,3 @@ +```rust +{{#include docs-examples/rust/schemalessexample/examples/influxdb_line_example.rs}} +``` diff --git a/docs-en/07-develop/03-insert-data/_rust_opts_json.mdx b/docs-en/07-develop/03-insert-data/_rust_opts_json.mdx new file mode 100644 index 0000000000000000000000000000000000000000..97d9052dacd1894cc7548a59951ecfaad9caee87 --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_rust_opts_json.mdx @@ -0,0 +1,3 @@ +```rust +{{#include docs-examples/rust/schemalessexample/examples/opentsdb_json_example.rs}} +``` diff --git a/docs-en/07-develop/03-insert-data/_rust_opts_telnet.mdx b/docs-en/07-develop/03-insert-data/_rust_opts_telnet.mdx new file mode 100644 index 0000000000000000000000000000000000000000..14021f43d8aff30c35dc30c5d278d4e51f375024 --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_rust_opts_telnet.mdx @@ -0,0 +1,3 @@ +```rust +{{#include docs-examples/rust/schemalessexample/examples/opentsdb_telnet_example.rs}} +``` diff --git a/docs-en/07-develop/03-insert-data/_rust_sql.mdx b/docs-en/07-develop/03-insert-data/_rust_sql.mdx new file mode 100644 index 0000000000000000000000000000000000000000..8e8013e4ad734efcc262ea2f750b82210a538e49 --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_rust_sql.mdx @@ -0,0 +1,3 @@ +```rust +{{#include docs-examples/rust/restexample/examples/insert_example.rs}} +``` diff --git a/docs-en/07-develop/03-insert-data/_rust_stmt.mdx b/docs-en/07-develop/03-insert-data/_rust_stmt.mdx new file mode 100644 index 0000000000000000000000000000000000000000..590a7a0e717426ed0235331c49dfc578bc55b2f7 --- /dev/null +++ b/docs-en/07-develop/03-insert-data/_rust_stmt.mdx @@ -0,0 +1,3 @@ +```rust +{{#include docs-examples/rust/nativeexample/examples/stmt_example.rs}} +``` diff --git a/docs-en/07-develop/03-insert-data/index.md b/docs-en/07-develop/03-insert-data/index.md new file mode 100644 index 0000000000000000000000000000000000000000..ee80d436f11f19b422df261845f1c209620251f2 --- /dev/null +++ b/docs-en/07-develop/03-insert-data/index.md @@ -0,0 +1,12 @@ +--- +title: Insert +--- + +TDengine supports multiple protocols of inserting data, including SQL, InfluxDB Line protocol, OpenTSDB Telnet protocol, OpenTSDB JSON protocol. Data can be inserted row by row, or in batch. Data from one or more collecting points can be inserted simultaneously. In the meantime, data can be inserted with multiple threads, out of order data and historical data can be inserted too. InfluxDB Line protocol, OpenTSDB Telnet protocol and OpenTSDB JSON protocol are the 3 kinds of schemaless insert protocols supported by TDengine. It's not necessary to create stable and table in advance if using schemaless protocols, and the schemas can be adjusted automatically according to the data to be inserted. + +```mdx-code-block +import DocCardList from '@theme/DocCardList'; +import {useCurrentSidebarCategory} from '@docusaurus/theme-common'; + + +``` \ No newline at end of file diff --git a/docs-en/07-develop/04-query-data/_c.mdx b/docs-en/07-develop/04-query-data/_c.mdx new file mode 100644 index 0000000000000000000000000000000000000000..76c9067e2f6af19465cf7c52c3e9b48bb868547d --- /dev/null +++ b/docs-en/07-develop/04-query-data/_c.mdx @@ -0,0 +1,3 @@ +```c +{{#include docs-examples/c/query_example.c}} +``` \ No newline at end of file diff --git a/docs-en/07-develop/04-query-data/_c_async.mdx b/docs-en/07-develop/04-query-data/_c_async.mdx new file mode 100644 index 0000000000000000000000000000000000000000..09f3d3b3ff6d6644f837642ef41db459ba7c5753 --- /dev/null +++ b/docs-en/07-develop/04-query-data/_c_async.mdx @@ -0,0 +1,3 @@ +```c +{{#include docs-examples/c/async_query_example.c:demo}} +``` \ No newline at end of file diff --git a/docs-en/07-develop/04-query-data/_category_.yml b/docs-en/07-develop/04-query-data/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..5912a48fc31ed36235c0d34d8b0909bf3b518aaa --- /dev/null +++ b/docs-en/07-develop/04-query-data/_category_.yml @@ -0,0 +1 @@ +label: Select Data diff --git a/docs-en/07-develop/04-query-data/_cs.mdx b/docs-en/07-develop/04-query-data/_cs.mdx new file mode 100644 index 0000000000000000000000000000000000000000..2ab52feb564eff0fe251bc9900ea2539171e5dba --- /dev/null +++ b/docs-en/07-develop/04-query-data/_cs.mdx @@ -0,0 +1,3 @@ +```csharp +{{#include docs-examples/csharp/QueryExample.cs}} +``` diff --git a/docs-en/07-develop/04-query-data/_cs_async.mdx b/docs-en/07-develop/04-query-data/_cs_async.mdx new file mode 100644 index 0000000000000000000000000000000000000000..f868994b303e62016b5e2f9304275135855c6ae5 --- /dev/null +++ b/docs-en/07-develop/04-query-data/_cs_async.mdx @@ -0,0 +1,3 @@ +```csharp +{{#include docs-examples/csharp/AsyncQueryExample.cs}} +``` diff --git a/docs-en/07-develop/04-query-data/_go.mdx b/docs-en/07-develop/04-query-data/_go.mdx new file mode 100644 index 0000000000000000000000000000000000000000..417c12315c06517e2f3de850ac9a379b7714b519 --- /dev/null +++ b/docs-en/07-develop/04-query-data/_go.mdx @@ -0,0 +1,3 @@ +```go +{{#include docs-examples/go/query/sync/main.go}} +``` diff --git a/docs-en/07-develop/04-query-data/_go_async.mdx b/docs-en/07-develop/04-query-data/_go_async.mdx new file mode 100644 index 0000000000000000000000000000000000000000..72fff411b980a0dcbdcaf4274722c63e0351db6f --- /dev/null +++ b/docs-en/07-develop/04-query-data/_go_async.mdx @@ -0,0 +1,3 @@ +```go +{{#include docs-examples/go/query/async/main.go}} +``` diff --git a/docs-en/07-develop/04-query-data/_java.mdx b/docs-en/07-develop/04-query-data/_java.mdx new file mode 100644 index 0000000000000000000000000000000000000000..519b9266144486231caf3ee593e973d438941ee4 --- /dev/null +++ b/docs-en/07-develop/04-query-data/_java.mdx @@ -0,0 +1,3 @@ +```java +{{#include docs-examples/java/src/main/java/com/taos/example/RestQueryExample.java}} +``` diff --git a/docs-en/07-develop/04-query-data/_js.mdx b/docs-en/07-develop/04-query-data/_js.mdx new file mode 100644 index 0000000000000000000000000000000000000000..c5e4c4f3fc20d3940a2bc6e13e6a5dea8a15ff13 --- /dev/null +++ b/docs-en/07-develop/04-query-data/_js.mdx @@ -0,0 +1,3 @@ +```js +{{#include docs-examples/node/nativeexample/query_example.js}} +``` diff --git a/docs-en/07-develop/04-query-data/_js_async.mdx b/docs-en/07-develop/04-query-data/_js_async.mdx new file mode 100644 index 0000000000000000000000000000000000000000..c65d54ed12f6c4bbeb333e0de0ba9ca4638bff84 --- /dev/null +++ b/docs-en/07-develop/04-query-data/_js_async.mdx @@ -0,0 +1,3 @@ +```js +{{#include docs-examples/node/nativeexample/async_query_example.js}} +``` diff --git a/docs-en/07-develop/04-query-data/_py.mdx b/docs-en/07-develop/04-query-data/_py.mdx new file mode 100644 index 0000000000000000000000000000000000000000..aeae42a15e5c39b7e9d227afc424e77658109705 --- /dev/null +++ b/docs-en/07-develop/04-query-data/_py.mdx @@ -0,0 +1,11 @@ +Result set is iterated row by row. + +```py +{{#include docs-examples/python/query_example.py:iter}} +``` + +Result set is retrieved as a whole, each row is converted to a dict and returned. + +```py +{{#include docs-examples/python/query_example.py:fetch_all}} +``` \ No newline at end of file diff --git a/docs-en/07-develop/04-query-data/_py_async.mdx b/docs-en/07-develop/04-query-data/_py_async.mdx new file mode 100644 index 0000000000000000000000000000000000000000..ed6880ae64e59a860e7dc75a5d3c1ad5d2614d01 --- /dev/null +++ b/docs-en/07-develop/04-query-data/_py_async.mdx @@ -0,0 +1,8 @@ +```py +{{#include docs-examples/python/async_query_example.py}} +``` + +:::note +This sample code can't be run on Windows system for now. + +::: diff --git a/docs-en/07-develop/04-query-data/_rust.mdx b/docs-en/07-develop/04-query-data/_rust.mdx new file mode 100644 index 0000000000000000000000000000000000000000..742d70fd025ff44b573eedf78441c9d73defad45 --- /dev/null +++ b/docs-en/07-develop/04-query-data/_rust.mdx @@ -0,0 +1,3 @@ +```rust +{{#include docs-examples/rust/restexample/examples/query_example.rs}} +``` diff --git a/docs-en/07-develop/04-query-data/index.mdx b/docs-en/07-develop/04-query-data/index.mdx new file mode 100644 index 0000000000000000000000000000000000000000..4016f8453ba9e0679a2798b92cd40efcb926343b --- /dev/null +++ b/docs-en/07-develop/04-query-data/index.mdx @@ -0,0 +1,186 @@ +--- +Sidebar_label: Select +title: Select +description: "This chapter introduces major query functionalities and how to perform sync and async query using connectors." +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import JavaQuery from "./_java.mdx"; +import PyQuery from "./_py.mdx"; +import GoQuery from "./_go.mdx"; +import RustQuery from "./_rust.mdx"; +import NodeQuery from "./_js.mdx"; +import CsQuery from "./_cs.mdx"; +import CQuery from "./_c.mdx"; +import PyAsync from "./_py_async.mdx"; +import NodeAsync from "./_js_async.mdx"; +import CsAsync from "./_cs_async.mdx"; +import CAsync from "./_c_async.mdx"; + +## Introduction + +SQL is used by TDengine as the query language. Application programs can send SQL statements to TDengine through REST API or connectors. TDengine CLI `taos` can also be used to execute SQL Ad-Hoc query. Here is the list of major query functionalities supported by TDengine: + +- Query on single column or multiple columns +- Filter on tags or data columns:>, <, =, <\>, like +- Grouping of results: `Group By` +- Sorting of results: `Order By` +- Limit the number of results: `Limit/Offset` +- Arithmetic on columns of numeric types or aggregate results +- Join query with timestamp alignment +- Aggregate functions: count, max, min, avg, sum, twa, stddev, leastsquares, top, bottom, first, last, percentile, apercentile, last_row, spread, diff + +For example, below SQL statement can be executed in TDengine CLI `taos` to select the rows whose voltage column is bigger than 215 and limit the output to only 2 rows. + +```sql +select * from d1001 where voltage > 215 order by ts desc limit 2; +``` + +```title=Output +taos> select * from d1001 where voltage > 215 order by ts desc limit 2; + ts | current | voltage | phase | +====================================================================================== + 2018-10-03 14:38:16.800 | 12.30000 | 221 | 0.31000 | + 2018-10-03 14:38:15.000 | 12.60000 | 218 | 0.33000 | +Query OK, 2 row(s) in set (0.001100s) +``` + +To meet the requirements in many use cases, some special functions have been added in TDengine, for example `twa` (Time Weighted Average), `spared` (The difference between the maximum and the minimum), `last_row` (the last row), more and more functions will be added to better perform in many use cases. Furthermore, continuous query is also supported in TDengine. + +For detailed query syntax please refer to [Select](/taos-sql/select). + +## Aggregation among Tables + +In many use cases, there are always multiple kinds of data collection points. A new concept, called STable (abbreviated for super table), is used in TDengine to represent a kind of data collection points, and a table is used to represent a specific data collection point. Tags are used by TDengine to represent the static properties of data collection points. A specific data collection point has its own values for static properties. By specifying filter conditions on tags, aggregation can be performed efficiently among all the subtables created via the same STable, i.e. same kind of data collection points, can be. Aggregate functions applicable for tables can be used directly on STables, syntax is exactly same. + +In summary, for a STable, its subtables can be aggregated by a simple query on STable, it's kind of join operation. But tables belong to different STables could not be aggregated. + +### Example 1 + +In TDengine CLI `taos`, use below SQL to get the average voltage of all the meters in BeiJing grouped by location. + +``` +taos> SELECT AVG(voltage) FROM meters GROUP BY location; + avg(voltage) | location | +============================================================= + 222.000000000 | Beijing.Haidian | + 219.200000000 | Beijing.Chaoyang | +Query OK, 2 row(s) in set (0.002136s) +``` + +### Example 2 + +In TDengine CLI `taos`, use below SQL to get the number of rows and the maximum current in the past 24 hours from meters whose groupId is 2. + +``` +taos> SELECT count(*), max(current) FROM meters where groupId = 2 and ts > now - 24h; + cunt(*) | max(current) | +================================== + 5 | 13.4 | +Query OK, 1 row(s) in set (0.002136s) +``` + +Join query is allowed between only the tables of same STable. In [Select](/taos-sql/select), all query operations are marked as whether it supports STable or not. + +## Down Sampling and Interpolation + +In IoT use cases, down sampling is widely used to aggregate the data by time range. `INTERVAL` keyword in TDengine can be used to simplify the query by time window. For example, below SQL statement can be used to get the sum of current every 10 seconds from meters table d1001. + +``` +taos> SELECT sum(current) FROM d1001 INTERVAL(10s); + ts | sum(current) | +====================================================== + 2018-10-03 14:38:00.000 | 10.300000191 | + 2018-10-03 14:38:10.000 | 24.900000572 | +Query OK, 2 row(s) in set (0.000883s) +``` + +Down sampling can also be used for STable. For example, below SQL statement can be used to get the sum of current from all meters in BeiJing. + +``` +taos> SELECT SUM(current) FROM meters where location like "Beijing%" INTERVAL(1s); + ts | sum(current) | +====================================================== + 2018-10-03 14:38:04.000 | 10.199999809 | + 2018-10-03 14:38:05.000 | 32.900000572 | + 2018-10-03 14:38:06.000 | 11.500000000 | + 2018-10-03 14:38:15.000 | 12.600000381 | + 2018-10-03 14:38:16.000 | 36.000000000 | +Query OK, 5 row(s) in set (0.001538s) +``` + +Down sampling also supports time offset. For example, below SQL statement can be used to get the sum of current from all meters but each time window must start at the boundary of 500 milliseconds. + +``` +taos> SELECT SUM(current) FROM meters INTERVAL(1s, 500a); + ts | sum(current) | +====================================================== + 2018-10-03 14:38:04.500 | 11.189999809 | + 2018-10-03 14:38:05.500 | 31.900000572 | + 2018-10-03 14:38:06.500 | 11.600000000 | + 2018-10-03 14:38:15.500 | 12.300000381 | + 2018-10-03 14:38:16.500 | 35.000000000 | +Query OK, 5 row(s) in set (0.001521s) +``` + +In many use cases, it's hard to align the timestamp of the data collected by each collection point. However, a lot of algorithms like FFT require the data to be aligned with same time interval and application programs have to handle by themselves in many systems. In TDengine, it's easy to achieve the alignment using down sampling. + +Interpolation can be performed in TDengine if there is no data in a time range. + +For more details please refer to [Aggregate by Window](/taos-sql/interval). + +## Examples + +### Query + +In the section describing [Insert](/develop/insert-data/sql-writing), a database named `power` is created and some data are inserted into STable `meters`. Below sample code demonstrates how to query the data in this STable. + + + + + + + + + + + + + + + + + + + + + + + + + +:::note + +1. With either REST connection or native connection, the above sample code work well. +2. Please be noted that `use db` can't be used in case of REST connection because it's stateless. + +::: + +### Asynchronous Query + +Besides synchronous query, asynchronous query API is also provided by TDengine to insert or query data more efficiently. With similar hardware and software environment, async API is 2~4 times faster than sync APIs. Async API works in non-blocking mode, which means an operation can be returned without finishing so that the calling thread can switch to other works to improve the performance of the whole application system. Async APIs perform especially better in case of poor network. + +Please be noted that async query can only be used with native connection. + + + + + + + + + + + + diff --git a/docs-en/07-develop/05-continuous-query.mdx b/docs-en/07-develop/05-continuous-query.mdx new file mode 100644 index 0000000000000000000000000000000000000000..97e32a17ff325a9f67ac0a732be3dd72ccca8888 --- /dev/null +++ b/docs-en/07-develop/05-continuous-query.mdx @@ -0,0 +1,83 @@ +--- +sidebar_label: Continuous Query +description: "Continuous query is a query that's executed automatically according to predefined frequency to provide aggregate query capability by time window, it's actually a simplified time driven stream computing." +title: "Continuous Query" +--- + +Continuous query is a query that's executed automatically according to predefined frequency to provide aggregate query capability by time window, it's actually a simplified time driven stream computing. Continuous query can be performed on a table or STable in TDengine. The result of continuous query can be pushed to client or written back to TDengine. Each query is executed on a time window, which moves forward with time. The size of time window and the forward sliding time need to be specified with parameter `INTERVAL` and `SLIDING` respectively. + +Continuous query in TDengine is time driven, and can be defined using TAOS SQL directly without any extra operations. With continuous query, the result can be generated according to time window to achieve down sampling of original data. Once a continuous query is defined using TAOS SQL, the query is automatically executed at the end of each time window and the result is pushed back to client or written to TDengine. + +There are some differences between continuous query in TDengine and time window computation in stream computing: + +- The computation is performed and the result is returned in real time in stream computing, but the computation in continuous query is only started when a time window closes. For example, if the time window is 1 day, then the result will only be generated at 23:59:59. +- If a historical data row is written in to a time widow for which the computation has been finished, the computation will not be performed again and the result will not be pushed to client again either. If the result has been written into TDengine, there will be no update for the result. +- In continuous query, if the result is pushed to client, the client status is not cached on the server side and Exactly-once is not guaranteed by the server either. If the client program crashes, a new time window will be generated from the time where the continuous query is restarted. If the result is written into TDengine, the data written into TDengine can be guaranteed as valid and continuous. + +## Syntax + +```sql +[CREATE TABLE AS] SELECT select_expr [, select_expr ...] + FROM {tb_name_list} + [WHERE where_condition] + [INTERVAL(interval_val [, interval_offset]) [SLIDING sliding_val]] + +``` + +INTERVAL: The time window for which continuous query is performed + +SLIDING: The time step for which the time window moves forward each time + +## How to Use + +In this section the use case of meters will be used to introduce how to use continuous query. Assume the STable and sub tables have been created using below SQL statement. + +```sql +create table meters (ts timestamp, current float, voltage int, phase float) tags (location binary(64), groupId int); +create table D1001 using meters tags ("Beijing.Chaoyang", 2); +create table D1002 using meters tags ("Beijing.Haidian", 2); +``` + +The average voltage for each time window of one minute with 30 seconds as the length of moving forward can be retrieved using below SQL statement. + +```sql +select avg(voltage) from meters interval(1m) sliding(30s); +``` + +Whenever the above SQL statement is executed, all the existing data will be computed again. If the computation needs to be performed every 30 seconds automatically to compute on the data in the past one minute, the above SQL statement needs to be revised as below, in which `{startTime}` stands for the beginning timestamp in the latest time window. + +```sql +select avg(voltage) from meters where ts > {startTime} interval(1m) sliding(30s); +``` + +Another easier way for same purpose is prepend `create table {tableName} as` before the `select`. + +```sql +create table avg_vol as select avg(voltage) from meters interval(1m) sliding(30s); +``` + +A table named as `avg_vol` will be created automatically, then every 30 seconds the `select` statement will be executed automatically on the data in the past 1 minutes, i.e. the latest time window, and the result is written into table `avg_vol`. The client program just needs to query from table `avg_vol`. For example: + +```sql +taos> select * from avg_vol; + ts | avg_voltage_ | +=================================================== + 2020-07-29 13:37:30.000 | 222.0000000 | + 2020-07-29 13:38:00.000 | 221.3500000 | + 2020-07-29 13:38:30.000 | 220.1700000 | + 2020-07-29 13:39:00.000 | 223.0800000 | +``` + +Please be noted that the minimum allowed time window is 10 milliseconds, and no upper limit. + +Besides, it's allowed to specify the start and end time of continuous query. If the start time is not specified, the timestamp of the first original row will be considered as the start time; if the end time is not specified, the continuous will be performed infinitely, otherwise it will be terminated once the end time is reached. For example, the continuous query in below SQL statement will be started from now and terminated one hour later. + +```sql +create table avg_vol as select avg(voltage) from meters where ts > now and ts <= now + 1h interval(1m) sliding(30s); +``` + +`now` in above SQL statement stands for the time when the continuous query is created, not the time when the computation is actually performed. Besides, to avoid the trouble caused by the delay of original data as much as possible, the actual computation in continuous query is also started with a little delay. That means, once a time window closes, the computation is not started immediately. Normally, the result can only be available a little time later, normally within one minute, after the time window closes. + +## How to Manage + +`show streams` command can be used in TDengine CLI `taos` to show all the continuous queries in the system, and `kill stream` can be used to terminate a continuous query. diff --git a/docs-en/07-develop/06-subscribe.mdx b/docs-en/07-develop/06-subscribe.mdx new file mode 100644 index 0000000000000000000000000000000000000000..56f4ed83d8ebc6f21afbdd2eca2e01f11b313883 --- /dev/null +++ b/docs-en/07-develop/06-subscribe.mdx @@ -0,0 +1,257 @@ +--- +sidebar_label: Subscription +description: "Lightweight service for data subscription and pushing, the time series data inserted into TDengine continuously can be pushed automatically to the subscribing clients." +title: Data Subscription +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import Java from "./_sub_java.mdx"; +import Python from "./_sub_python.mdx"; +import Go from "./_sub_go.mdx"; +import Rust from "./_sub_rust.mdx"; +import Node from "./_sub_node.mdx"; +import CSharp from "./_sub_cs.mdx"; +import CDemo from "./_sub_c.mdx"; + +## Introduction + +According to the time series nature of the data, data inserting in TDengine is similar to data publishing in message queues, they both can be considered as a new data record with timestamp is inserted into the system. Data is stored in ascending order of timestamp inside TDengine, so essentially each table in TDengine can be considered as a message queue. + +Lightweight service for data subscription and pushing is built in TDengine. With the API provided by TDengine, client programs can used `select` statement to subscribe the data from one or more tables. The subscription and and state maintenance is performed on the client side, the client programs polls the server to check whether there is new data, and if so the new data will be pushed back to the client side. If the client program is restarted, where to start for retrieving new data is up to the client side. + +There are 3 major APIs related to subscription provided in the TDengine client driver. + +```c +taos_subscribe +taos_consume +taos_unsubscribe +``` + +For more details about these API please refer to [C/C++ Connector](/reference/connector/cpp). Their usage will be introduced below using the use case of meters, in which the schema of STable and sub tables please refer to the previous section "continuous query". Full sample code can be found [here](https://github.com/taosdata/TDengine/blob/master/examples/c/subscribe.c). + +If we want to get notification and take some actions if the current exceeds a threshold, like 10A, from some meters, there are two ways: + +The first way is to query on each sub table and record the last timestamp matching the criteria, then after some time query on the data later than recorded timestamp and repeat this process. The SQL statements for this way are as below. + +```sql +select * from D1001 where ts > {last_timestamp1} and current > 10; +select * from D1002 where ts > {last_timestamp2} and current > 10; +... +``` + +The above way works, but the problem is that the number of `select` statements increases with the number of meters grows. Finally the performance of both client side and server side will be unacceptable once the number of meters grows to a big enough number. + +A better way is to query on the STable, only one `select` is enough regardless of the number of meters, like below: + +```sql +select * from meters where ts > {last_timestamp} and current > 10; +``` + +However, how to choose `last_timestamp` becomes a new problem if using this way. Firstly, the timestamp when the data is generated is different from the timestamp when the data is inserted into the database, sometimes the difference between them may be very big. Secondly, the time when the data from different meters may arrives at the database may be different too. If the timestamp of the "slowest" meter is used as `last_timestamp` in the query, the data from other meters may be selected repeatedly; but if the timestamp of the "fasted" meters is used as `last_timestamp`, some data from other meters may be missed. + +All the problems mentioned above can be resolved thoroughly using subscription provided by TDengine. + +The first step is to create subscription using `taos_subscribe`. + +```c +TAOS_SUB* tsub = NULL; +if (async) { +  // create an asynchronous subscription, the callback function will be called every 1s +  tsub = taos_subscribe(taos, restart, topic, sql, subscribe_callback, &blockFetch, 1000); +} else { +  // create an synchronous subscription, need to call 'taos_consume' manually +  tsub = taos_subscribe(taos, restart, topic, sql, NULL, NULL, 0); +} +``` + +The subscription in TDengine can be either synchronous or asynchronous. In the above sample code, the value of variable `async` is determined from the CLI input, then it's used to create either an async or sync subscription. Sync subscription means the client program needs to invoke `taos_consume` to retrieve data, and async subscription means another thread created by `taos_subscribe` internally invokes `taos_consume` to retrieve data and pass the data to `subscribe_callback` for processing, `subscribe_callback` is a call back function provided by the client program and it's suggested not to do time consuming operation in the call back function. + +The parameter `taos` is an established connection. There is nothing special in sync subscription mode. In async subscription, it should be exclusively by current thread, otherwise unpredictable error may occur. + +The parameter `sql` is a `select` statement in which `where` clause can be used to specify filter conditions. In our example, the data whose current exceeds 10A needs to be subscribed like below SQL statement: + +```sql +select * from meters where current > 10; +``` + +Please be noted that, all the data will be processed because no start time is specified. If only the data from one day ago needs to be processed, a time related condition can be added: + +```sql +select * from meters where ts > now - 1d and current > 10; +``` + +The parameter `topic` is the name of the subscription, it needs to be guaranteed unique in the client program, but it's not necessary to be globally unique because subscription is implemented in the APIs on client side. + +If the subscription named as `topic` doesn't exist, parameter `restart` would be ignored. If the subscription named as `topic` has been created before by the client program which then exited, when the client program is restarted to use this `topic`, parameter `restart` is used to determine retrieving data from beginning or from the last point where the subscription was broken. If the value of `restart` is **true** (i.e. a non-zero value), the data will be retrieved from beginning, or if it is **false** (i.e. zero), the data already consumed before will not be processed again. + +The last parameter of `taos_subscribe` is the polling interval in unit of millisecond. In sync mode, if the time difference between two continuous invocations to `taos_consume` is smaller than the interval specified by `taos_subscribe`, `taos_consume` would be blocked until the interval is reached. In async mode, this interval is the minimum interval between two invocations to the call back function. + +The last second parameter of `taos_subscribe` is used to pass arguments to the call back function. `taos_subscribe` doesn't process this parameter and simply passes it to the call back function. This parameter is simply ignored in sync mode. + +After a subscription is created, its data can be consumed and processed, below is the sample code of how to consume data in sync mode, in the else part if `if (async)`. + +```c +if (async) { +  getchar(); +} else while(1) { +  TAOS_RES* res = taos_consume(tsub); +  if (res == NULL) { +    printf("failed to consume data."); +    break; +  } else { +    print_result(res, blockFetch); +    getchar(); +  } +} +``` + +In the above sample code, there is an infinite loop, each time carriage return is entered `taos_consume` is invoked, the return value of `taos_consume` is the selected result set, exactly as the input of `taos_use_result`, in the above sample `print_result` is used instead to simplify the sample. Below is the implementation of `print_result`. + +```c +void print_result(TAOS_RES* res, int blockFetch) { +  TAOS_ROW row = NULL; +  int num_fields = taos_num_fields(res); +  TAOS_FIELD* fields = taos_fetch_fields(res); +  int nRows = 0; +  if (blockFetch) { +    nRows = taos_fetch_block(res, &row); +    for (int i = 0; i < nRows; i++) { +      char temp[256]; +      taos_print_row(temp, row + i, fields, num_fields); +      puts(temp); +    } +  } else { +    while ((row = taos_fetch_row(res))) { +      char temp[256]; +      taos_print_row(temp, row, fields, num_fields); +      puts(temp); +      nRows++; +    } +  } +  printf("%d rows consumed.\n", nRows); +} +``` + +In the above code `taos_print_row` is used to process the data consumed. All the matching rows will be printed. + +In async mode, the data consuming is simpler as below. + +```c +void subscribe_callback(TAOS_SUB* tsub, TAOS_RES *res, void* param, int code) { +  print_result(res, *(int*)param); +} +``` + +`taos_unsubscribe` can be invoked to terminate a subscription. + +```c +taos_unsubscribe(tsub, keep); +``` + +The second parameter `keep` is used to specify whether to keep the subscription progress on the client sde. If it is **false**, i.e. **0**, then subscription will be restarted from beginning regardless of the `restart` parameter's value in when `taos_subscribe` is invoked again. The subscription progress information is stored in _{DataDir}/subscribe/_ , under which there is a file with same name as `topic` for each subscription, the subscription will be restarted from beginning if the corresponding progress file is removed. + +Now let's see the effect of the above sample code, assuming below prerequisites have been done. + +- The sample code has been downloaded to local system +- TDengine has been installed and launched properly on same system +- The database, STable, sub tables required in the sample code have been ready + +It's ready to launch below command in the directory where the sample code resides to compile and start the program. + +```bash +make +./subscribe -sql='select * from meters where current > 10;' +``` + +After the program is started, open another terminal and launch TDengine CLI `taos`, then use below SQL commands to insert a row whose current is 12A into table **D1001**. + +```sql +use test; +insert into D1001 values(now, 12, 220, 1); +``` + +Then, this row of data will be shown by the example program on the first terminal because its current exceeds 10A. More data can be inserted for you to observe the output of the example program. + +## Examples + +Below example program demonstrates how to subscribe the data rows whose current exceeds 10A using connectors. + +### Prepare Data + +```bash +# create database "power" +taos> create database power; +# use "power" as the database in following operations +taos> use power; +# create super table "meters" +taos> create table meters(ts timestamp, current float, voltage int, phase int) tags(location binary(64), groupId int); +# create tabes using the schema defined by super table "meters" +taos> create table d1001 using meters tags ("Beijing.Chaoyang", 2); +taos> create table d1002 using meters tags ("Beijing.Haidian", 2); +# insert some rows +taos> insert into d1001 values("2020-08-15 12:00:00.000", 12, 220, 1),("2020-08-15 12:10:00.000", 12.3, 220, 2),("2020-08-15 12:20:00.000", 12.2, 220, 1); +taos> insert into d1002 values("2020-08-15 12:00:00.000", 9.9, 220, 1),("2020-08-15 12:10:00.000", 10.3, 220, 1),("2020-08-15 12:20:00.000", 11.2, 220, 1); +# filter out the rows in which current is bigger than 10A +taos> select * from meters where current > 10; + ts | current | voltage | phase | location | groupid | +=========================================================================================================== + 2020-08-15 12:10:00.000 | 10.30000 | 220 | 1 | Beijing.Haidian | 2 | + 2020-08-15 12:20:00.000 | 11.20000 | 220 | 1 | Beijing.Haidian | 2 | + 2020-08-15 12:00:00.000 | 12.00000 | 220 | 1 | Beijing.Chaoyang | 2 | + 2020-08-15 12:10:00.000 | 12.30000 | 220 | 2 | Beijing.Chaoyang | 2 | + 2020-08-15 12:20:00.000 | 12.20000 | 220 | 1 | Beijing.Chaoyang | 2 | +Query OK, 5 row(s) in set (0.004896s) +``` + +### Example Programs + + + + + + + + + {/* + + */} + + + + {/* + + + + + */} + + + + + +### Run the Examples + +The example programs firstly consume all historical data matching the criteria. + +```bash +ts: 1597464000000 current: 12.0 voltage: 220 phase: 1 location: Beijing.Chaoyang groupid : 2 +ts: 1597464600000 current: 12.3 voltage: 220 phase: 2 location: Beijing.Chaoyang groupid : 2 +ts: 1597465200000 current: 12.2 voltage: 220 phase: 1 location: Beijing.Chaoyang groupid : 2 +ts: 1597464600000 current: 10.3 voltage: 220 phase: 1 location: Beijing.Haidian groupid : 2 +ts: 1597465200000 current: 11.2 voltage: 220 phase: 1 location: Beijing.Haidian groupid : 2 +``` + +Next, use TDengine CLI to insert a new row. + +``` +# taos +taos> use power; +taos> insert into d1001 values(now, 12.4, 220, 1); +``` + +Because the current in inserted row exceeds 10A, it will be consumed by the example program. + +``` +ts: 1651146662805 current: 12.4 voltage: 220 phase: 1 location: Beijing.Chaoyang groupid: 2 +``` diff --git a/docs-en/07-develop/07-cache.md b/docs-en/07-develop/07-cache.md new file mode 100644 index 0000000000000000000000000000000000000000..13db6c363802abed290cfc4d4466d40e48852f3d --- /dev/null +++ b/docs-en/07-develop/07-cache.md @@ -0,0 +1,19 @@ +--- +sidebar_label: Cache +title: Cache +description: "The latest row of each table is kept in cache to provide high performance query of latest state." +--- + +The cache management policy in TDengine is First-In-First-Out (FIFO), which is also known as insert driven cache management policy and different from read driven cache management, i.e. Least-Recent-Used (LRU). It simply stores the latest data in cache and flushes the oldest data in cache to disk when the cache usage reaches a threshold. In IoT use cases, the most cared about data is the latest data, i.e. current state. The cache policy in TDengine is based the nature of IoT data. + +Caching the latest data provides the capability of retrieving data in milliseconds. With this capability, TDengine can be configured properly to be used as caching system without deploying another separate caching system to simplify the system architecture and minimize the operation cost. The cache will be emptied after TDengine is restarted, TDengine doesn't reload data from disk into cache like a real key-value caching system. + +The memory space used by TDengine cache is fixed in size, according to the configuration based on application requirement and system resources. Independent memory pool is allocated for and managed by each vnode (virtual node) in TDengine, there is no sharing of memory pools between vnodes. All the tables belonging to a vnode share all the cache memory of the vnode. + +Memory pool is divided into blocks and data is stored in row format in memory and each block follows FIFO policy. The size of each block is determined by configuration parameter `cache`, the number of blocks for each vnode is determined by `blocks`. For each vnode, the total cache size is `cache * blocks`. It's better to set the size of each block to hold at least tends of rows. + +`last_row` function can be used to retrieve the last row of a table or a STable to quickly show the current state of devices on monitoring screen. For example below SQL statement retrieves the latest voltage of all meters in Chaoyang district of Beijing. + +```sql +select last_row(voltage) from meters where location='Beijing.Chaoyang'; +``` diff --git a/docs-en/07-develop/08-udf.md b/docs-en/07-develop/08-udf.md new file mode 100644 index 0000000000000000000000000000000000000000..61639e34404477d3bb5785da129a1d922a4d020e --- /dev/null +++ b/docs-en/07-develop/08-udf.md @@ -0,0 +1,218 @@ +--- +sidebar_label: UDF +title: User Defined Functions +description: "Scalar functions and aggregate functions developed by users can be utilized by the query framework to expand the query capability" +--- + +In some use cases, the query capability required by application programs can't be achieved directly by builtin functions. With UDF, the functions developed by users can be utilized by query framework to meet some special requirements. UDF normally takes one column of data as input, but can also support the result of sub query as input. + +From version 2.2.0.0, UDF programmed in C/C++ language can be supported by TDengine. + +Two kinds of functions can be implemented by UDF: scalar function and aggregate function. + +## Define UDF + +### Scalar Function + +Below function template can be used to define your own scalar function. + +`void udfNormalFunc(char* data, short itype, short ibytes, int numOfRows, long long* ts, char* dataOutput, char* interBuf, char* tsOutput, int* numOfOutput, short otype, short obytes, SUdfInit* buf)` + +`udfNormalFunc` is the place holder of function name, a function implemented based on the above template can be used to perform scalar computation on data rows. The parameters are fixed to control the data exchange between UDF and TDengine. + +- Definitions of the parameters: + + - data:input data + - itype:the type of input data, for details please refer to [type definition in column_meta](/reference/rest-api/), for example 4 represents INT + - iBytes:the number of bytes consumed by each value in the input data + - oType:the type of output data, similar to iType + - oBytes:the number of bytes consumed by each value in the output data + - numOfRows:the number of rows in the input data + - ts: the column of timestamp corresponding to the input data + - dataOutput:the buffer for output data, total size is `oBytes * numberOfRows` + - interBuf:the buffer for intermediate result, its size is specified by `BUFSIZE` parameter when creating a UDF. It's normally used when the intermediate result is not same as the final result, it's allocated and freed by TDengine. + - tsOutput:the column of timestamps corresponding to the output data; it can be used to output timestamp together with the output data if it's not NULL + - numOfOutput:the number of rows in output data + - buf:for the state exchange between UDF and TDengine + + [add_one.c](https://github.com/taosdata/TDengine/blob/develop/tests/script/sh/add_one.c) is one example of the simplest UDF implementations, i.e. one instance of the above `udfNormalFunc` template. It adds one to each value of a column passed in which can be filtered using `where` clause and outputs the result. + +### Aggregate Function + +Below function template can be used to define your own aggregate function. + +`void abs_max_merge(char* data, int32_t numOfRows, char* dataOutput, int32_t* numOfOutput, SUdfInit* buf)` + +`udfMergeFunc` is the place holder of function name, the function implemented with the above template is used to aggregate the intermediate result, only can be used in the aggregate query for STable. + +Definitions of the parameters: + +- data:array of output data, if interBuf is used it's an array of interBuf +- numOfRows:number of rows in `data` +- dataOutput:the buffer for output data, the size is same as that of the final result; If the result is not final, it can be put in the interBuf, i.e. `data`. +- numOfOutput:number of rows in the output data +- buf:for the state exchange between UDF and TDengine + +[abs_max.c](https://github.com/taosdata/TDengine/blob/develop/tests/script/sh/abs_max.c) is an user defined aggregate function to get the maximum from the absolute value of a column. + +The internal processing is that the data affected by the select statement will be divided into multiple row blocks and `udfNormalFunc`, i.e. `abs_max` in this case, is performed on each row block to generate the intermediate of each sub table, then `udfMergeFunc`, i.e. `abs_max_merge` in this case, is performed on the intermediate result of sub tables to aggregate to generate the final or intermediate result of STable. The intermediate result of STable is finally processed by `udfFinalizeFunc` to generate the final result, which contain either 0 or 1 row. + +Other typical scenarios, like covariance, can also be achieved by aggregate UDF. + +### Finalize + +Below function template can be used to finalize the result of your own UDF, normally used when interBuf is used. + +`void abs_max_finalize(char* dataOutput, char* interBuf, int* numOfOutput, SUdfInit* buf)` + +`udfFinalizeFunc` is the place holder of function name, definitions of the parameter are as below: + +- dataOutput:buffer for output data +- interBuf:buffer for intermediate result, can be used as input for next processing step +- numOfOutput:number of output data, can only be 0 or 1 for aggregate function +- buf:for state exchange between UDF and TDengine + +## UDF Conventions + +The naming of 3 kinds of UDF, i.e. udfNormalFunc, udfMergeFunc, and udfFinalizeFunc is required to have same prefix, i.e. the actual name of udfNormalFunc, which means udfNormalFunc doesn't need a suffix following the function name. While udfMergeFunc should be udfNormalFunc followed by `_merge`, udfFinalizeFunc should be udfNormalFunc followed by `_finalize`. The naming convention is part of UDF framework, TDengine follows this convention to invoke corresponding actual functions.\ + +According to the kind of UDF to implement, the functions that need to be implemented are different. + +- Scalar function:udfNormalFunc is required +- Aggregate function:udfNormalFunc, udfMergeFunc (if query on STable) and udfFinalizeFunc are required + +To be more accurate, assuming we want to implement a UDF named "foo". If the function is a scalar function, what we really need to implement is `foo`; if the function is aggregate function, we need to implement `foo`, `foo_merge`, and `foo_finalize`. For aggregate UDF, even though one of the three functions is not necessary, there must be an empty implementation. + +## Compile UDF + +The source code of UDF in C can't be utilized by TDengine directly. UDF can only be loaded into TDengine after compiling to dynamically linked library. + +For example, the example UDF `add_one.c` mentioned in previous sections need to be compiled into DLL using below command on Linux Shell. + +```bash +gcc -g -O0 -fPIC -shared add_one.c -o add_one.so +``` + +The generated DLL file `dd_one.so` can be used later when creating UDF. It's recommended to use GCC not older than 7.5. + +## Create and Use UDF + +### Create UDF + +SQL command can be executed on the same hos where the generated UDF DLL resides to load the UDF DLL into TDengine, this operation can't be done through REST interface or web console. Once created, all the clients of the current TDengine can use these UDF functions in their SQL commands. UDF are stored in the management node of TDengine. The UDFs loaded in TDengine would be still available after TDengine is restarted. + +When creating UDF, it needs to be clarified as either scalar function or aggregate function. If the specified type is wrong, the SQL statements using the function would fail with error. Besides, the input type and output type don't need to be same in UDF, but the input data type and output data type need to be consistent with the UDF definition. + +- Create Scalar Function + +```sql +CREATE FUNCTION ids(X) AS ids(Y) OUTPUTTYPE typename(Z) [ BUFSIZE B ]; +``` + +- ids(X):the function name to be sued in SQL statement, must be consistent with the function name defined by `udfNormalFunc` +- ids(Y):the absolute path of the DLL file including the implementation of the UDF, the path needs to be quoted by single or double quotes +- typename(Z):the output data type, the value is the literal string of the type +- B:the size of intermediate buffer, in bytes; it's an optional parameter and the range is [0,512] + +For example, below SQL statement can be used to create a UDF from `add_one.so`. + +```sql +CREATE FUNCTION add_one AS "/home/taos/udf_example/add_one.so" OUTPUTTYPE INT; +``` + +- Create Aggregate Function + +```sql +CREATE AGGREGATE FUNCTION ids(X) AS ids(Y) OUTPUTTYPE typename(Z) [ BUFSIZE B ]; +``` + +- ids(X):the function name to be sued in SQL statement, must be consistent with the function name defined by `udfNormalFunc` +- ids(Y):the absolute path of the DLL file including the implementation of the UDF, the path needs to be quoted by single or double quotes +- typename(Z):the output data type, the value is the literal string of the type +- B:the size of intermediate buffer, in bytes; it's an optional parameter and the range is [0,512] + +For details about how to use intermediate result, please refer to example program [demo.c](https://github.com/taosdata/TDengine/blob/develop/tests/script/sh/demo.c). + +For example, below SQL statement can be used to create a UDF rom `demo.so`. + +```sql +CREATE AGGREGATE FUNCTION demo AS "/home/taos/udf_example/demo.so" OUTPUTTYPE DOUBLE bufsize 14; +``` + +### Manage UDF + +- Delete UDF + +``` +DROP FUNCTION ids(X); +``` + +- ids(X):same as that in `CREATE FUNCTION` statement + +```sql +DROP FUNCTION add_one; +``` + +- Show Available UDF + +```sql +SHOW FUNCTIONS; +``` + +### Use UDF + +The function name specified when creating UDF can be used directly in SQL statements, just like builtin functions. + +```sql +SELECT X(c) FROM table/STable; +``` + +The above SQL statement invokes function X for column c. + +## Restrictions for UDF + +In current version there are some restrictions for UDF + +1. Only Linux is supported when creating and invoking UDF for both client side and server side +2. UDF can't be mixed with builtin functions +3. Only one UDF can be used in a SQL statement +4. Single column is supported as input for UDF +5. Once created successfully, UDF is persisted in MNode of TDengineUDF +6. UDF can't be created through REST interface +7. The function name used when creating UDF in SQL must be consistent with the function name defined in the DLL, i.e. the name defined by `udfNormalFunc` +8. The name name of UDF name should not conflict with any of builtin functions + +## Examples + +### Scalar function example [add_one](https://github.com/taosdata/TDengine/blob/develop/tests/script/sh/add_one.c) + +
+add_one.c + +```c +{{#include tests/script/sh/add_one.c}} +``` + +
+ +### Aggregate function example [abs_max](https://github.com/taosdata/TDengine/blob/develop/tests/script/sh/abs_max.c) + +
+abs_max.c + +```c +{{#include tests/script/sh/abs_max.c}} +``` + +
+ +### Example for using intermediate result [demo](https://github.com/taosdata/TDengine/blob/develop/tests/script/sh/demo.c) + +
+demo.c + +```c +{{#include tests/script/sh/demo.c}} +``` + +
diff --git a/docs-en/07-develop/_category_.yml b/docs-en/07-develop/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..6f0d66351a5c326eb2dced998e29e668d11cd1ca --- /dev/null +++ b/docs-en/07-develop/_category_.yml @@ -0,0 +1 @@ +label: Developer Guide \ No newline at end of file diff --git a/docs-en/07-develop/_sub_c.mdx b/docs-en/07-develop/_sub_c.mdx new file mode 100644 index 0000000000000000000000000000000000000000..95fef0042d0a277f9136e6e6f8c15558487232f9 --- /dev/null +++ b/docs-en/07-develop/_sub_c.mdx @@ -0,0 +1,3 @@ +```c +{{#include docs-examples/c/subscribe_demo.c}} +``` \ No newline at end of file diff --git a/docs-en/07-develop/_sub_cs.mdx b/docs-en/07-develop/_sub_cs.mdx new file mode 100644 index 0000000000000000000000000000000000000000..80934aa4d014a076896dce7f41e520f06ffd735d --- /dev/null +++ b/docs-en/07-develop/_sub_cs.mdx @@ -0,0 +1,3 @@ +```csharp +{{#include docs-examples/csharp/SubscribeDemo.cs}} +``` \ No newline at end of file diff --git a/docs-en/07-develop/_sub_go.mdx b/docs-en/07-develop/_sub_go.mdx new file mode 100644 index 0000000000000000000000000000000000000000..cd908fc12c3a35f49ca108ee56c3951c5388a95f --- /dev/null +++ b/docs-en/07-develop/_sub_go.mdx @@ -0,0 +1,3 @@ +```go +{{#include docs-examples/go/sub/main.go}} +``` \ No newline at end of file diff --git a/docs-en/07-develop/_sub_java.mdx b/docs-en/07-develop/_sub_java.mdx new file mode 100644 index 0000000000000000000000000000000000000000..e65bc576ebed030d935ced6a4572289cd367ffac --- /dev/null +++ b/docs-en/07-develop/_sub_java.mdx @@ -0,0 +1,7 @@ +```java +{{#include docs-examples/java/src/main/java/com/taos/example/SubscribeDemo.java}} +``` +:::note +For now Java connector doesn't provide asynchronous subscription, but `TimerTask` can be used to achieve similar purpose. + +::: \ No newline at end of file diff --git a/docs-en/07-develop/_sub_node.mdx b/docs-en/07-develop/_sub_node.mdx new file mode 100644 index 0000000000000000000000000000000000000000..c93ad627ce9a77ca71a014b41d571089e6c1727b --- /dev/null +++ b/docs-en/07-develop/_sub_node.mdx @@ -0,0 +1,3 @@ +```js +{{#include docs-examples/node/nativeexample/subscribe_demo.js}} +``` \ No newline at end of file diff --git a/docs-en/07-develop/_sub_python.mdx b/docs-en/07-develop/_sub_python.mdx new file mode 100644 index 0000000000000000000000000000000000000000..b817deeba6e283a3ba16fee0d580d3823c999536 --- /dev/null +++ b/docs-en/07-develop/_sub_python.mdx @@ -0,0 +1,3 @@ +```py +{{#include docs-examples/python/subscribe_demo.py}} +``` \ No newline at end of file diff --git a/docs-en/07-develop/_sub_rust.mdx b/docs-en/07-develop/_sub_rust.mdx new file mode 100644 index 0000000000000000000000000000000000000000..4750cf7a3b871db48c9e5a26b22ab4b8a03f11be --- /dev/null +++ b/docs-en/07-develop/_sub_rust.mdx @@ -0,0 +1,3 @@ +```rs +{{#include docs-examples/rust/nativeexample/examples/subscribe_demo.rs}} +``` \ No newline at end of file diff --git a/docs-en/07-develop/index.md b/docs-en/07-develop/index.md new file mode 100644 index 0000000000000000000000000000000000000000..122dd0d870ac42b62c4f9e694cf79eec3ca122a5 --- /dev/null +++ b/docs-en/07-develop/index.md @@ -0,0 +1,25 @@ +--- +title: Developer Guide +--- + +To develop an application using TDengine to process time-series data, we recommend taking the following steps: + +1. Choose the way for connection to TDengine. No matter what programming language you use, you can always use the REST interface to access TDengine, but you can also use connectors unique to each programming language. +2. Design the data model based on your own application scenarios. Learn the [concepts](/concept/) of TDengine including "one table for one data collection point" and the "super table" concept; learn about static labels, collected metrics, and subtables. According to the data characteristics, you may decide to create one or more databases, and you should design the STable schema to fit your data. +3. Decide how to insert data. TDengine supports writing using standard SQL, but also supports schemaless writing, so that data can be written directly without creating tables manually. +4. Based on business requirements, find out what SQL query statements need to be written. +5. If you want to run real-time analysis based on time series data, including various dashboards, it is recommended that you use the TDengine continuous query feature instead of deploying complex streaming processing systems such as Spark or Flink. +6. If your application has modules that need to consume inserted data, and they need to be notified when new data is inserted, it is recommended that you use the data subscription function provided by TDengine without the need to deploy Kafka. +7. In many scenarios (such as fleet management), the application needs to obtain the latest status of each data collection point. It is recommended that you use the cache function of TDengine instead of deploying Redis separately. +8. If you find that the SQL functions of TDengine cannot meet your requirements, then you can use user-defined functions to solve the problem. + +This section is organized in the order described above. For ease of understanding, TDengine provides sample code for each supported programming language for each function. If you want to learn more about the use of SQL, please read the [SQL manual](/taos-sql/). For a more in-depth understanding of the use of each connector, please read the [Connector Reference Guide](/reference/connector/). If you also want to integrate TDengine with third-party systems, such as Grafana, please refer to the [third-party tools](/third-party/). + +If you encounter any problems during the development process, please click ["Submit an issue"](https://github.com/taosdata/TDengine/issues/new/choose) at the bottom of each page and submit it on GitHub right away. + +```mdx-code-block +import DocCardList from '@theme/DocCardList'; +import {useCurrentSidebarCategory} from '@docusaurus/theme-common'; + + +``` diff --git a/docs-en/10-cluster/01-deploy.md b/docs-en/10-cluster/01-deploy.md new file mode 100644 index 0000000000000000000000000000000000000000..8c921797ec038fb8afbf382a980b8f7a197fa898 --- /dev/null +++ b/docs-en/10-cluster/01-deploy.md @@ -0,0 +1,114 @@ +--- +title: Deployment +--- + +## Prerequisites + +### Step 1 + +The FQDN of all hosts need to be setup properly, all the FQDNs need to be configured in the /etc/hosts of each host. It must be guaranteed that each FQDN can be accessed (by ping, for example) from any other hosts. + +On each host command `hostname -f` can be executed to get the hostname. `ping` command can be executed on each host to check whether any other host is accessible from it. If any host is not accessible, the network configuration, like /etc/hosts or DNS configuration, need to be checked and revised to make any two hosts accessible to each other. + +:::note + +- The host where the client program runs also needs to configured properly for FQDN, to make sure all hosts for client or server can be accessed from any other. In other words, the hosts where the client is running are also considered as a part of the cluster. + +- It's suggested to disable the firewall for all hosts in the cluster. At least TCP/UDP for port 6030~6042 need to be open if firewall is enabled. + +::: + +### Step 2 + +If any previous version of TDengine has been installed and configured on any host, the installation needs to be removed and the data needs to be cleaned up. For details about uninstalling please refer to [Install and Uninstall](/operation/pkg-install). To clean up the data, please use `rm -rf /var/lib/taos/\*` assuming the `dataDir` is configured as `/var/lib/taos`. + +### Step 3 + +Now it's time to install TDengine on all hosts without starting `taosd`, the versions on all hosts should be same. If it's prompted to input the existing TDengine cluster, simply press carriage return to ignore it. `install.sh -e no` can also be used to disable this prompt. For details please refer to [Install and Uninstall](/operation/pkg-install). + +### Step 4 + +Now each physical node (referred to as `dnode` hereinafter, it's abbreviation for "data node") of TDengine need to be configured properly. Please be noted that one dnode doesn't stand for one host, multiple TDengine nodes can be started on single host as long as they are configured properly without conflicting. More specifically each instance of the configuration file `taos.cfg` stands for a dnode. Assuming the first dnode of TDengine cluster is "h1.taosdata.com:6030", its `taos.cfg` is configured as following. + +```c +// firstEp is the end point to connect to when any dnode starts +firstEp h1.taosdata.com:6030 + +// must be configured to the FQDN of the host where the dnode is launched +fqdn h1.taosdata.com + +// the port used by the dnode, default is 6030 +serverPort 6030 + +// only necessary when replica is configured to an even number +#arbitrator ha.taosdata.com:6042 +``` + +`firstEp` and `fqdn` must be configured properly. In `taos.cfg` of all dnodes in TDengine cluster, `firstEp` must be configured to point to same address, i.e. the first dnode of the cluster. `fqdn` and `serverPort` compose the address of each node itself. If you want to start multiple TDengine dnodes on a single host, please also make sure all other configurations like `dataDir`, `logDir`, and other resources related parameters are not conflicting. + +For all the dnodes in a TDengine cluster, below parameters must be configured as exactly same, any node whose configuration is different from dnodes already in the cluster can't join the cluster. + +| **#** | **Parameter** | **Definition** | +| ----- | ------------------ | --------------------------------------------------------------------------------- | +| 1 | numOfMnodes | The number of management nodes in the cluster | +| 2 | mnodeEqualVnodeNum | The ratio of resource consuming of mnode to vnode | +| 3 | offlineThreshold | The threshold of dnode offline, once it's reached the dnode is considered as down | +| 4 | statusInterval | The interval by which dnode reports its status to mnode | +| 5 | arbitrator | End point of the arbitrator component in the cluster | +| 6 | timezone | Timezone | +| 7 | balance | Enable load balance automatically | +| 8 | maxTablesPerVnode | Maximum number of tables that can be created in each vnode | +| 9 | maxVgroupsPerDb | Maximum number vgroups that can be used by each DB | + +:::note +Prior to version 2.0.19.0, besides the above parameters, `locale` and `charset` must be configured as same too for each dnode. + +::: + +## Start Cluster + +### Start The First DNODE + +The first dnode can be started following the instructions in [Get Started](/get-started/), for example h1.taosdata.com. Then TDengine CLI `taos` can be launched to execute command `show dnodes`, the output is as following for example: + +``` +Welcome to the TDengine shell from Linux, Client Version:2.0.0.0 + + +Copyright (c) 2017 by TAOS Data, Inc. All rights reserved. + +taos> show dnodes; + id | end_point | vnodes | cores | status | role | create_time | +===================================================================================== + 1 | h1.taos.com:6030 | 0 | 2 | ready | any | 2020-07-31 03:49:29.202 | +Query OK, 1 row(s) in set (0.006385s) + +taos> +``` + +From the above output, it is shown that the end point of the started dnode is "h1.taos.com:6030", which is the `firstEp` of the cluster. + +### Start Other DNODEs + +There are a few steps necessary to add other dnodes in the cluster. + +Firstly, start `taosd` as instructed in [Get Started](/get-started/), assuming it's for the second dnode. Before starting `taosd`, please making sure the configuration is correct, especially `firstEp`, `FQDN` and `serverPort`, `firstEp` must be same as the dnode shown in the section "Start First DNODE", i.e. "h1.taosdata.com" in this example. + +Then, on the first dnode, use TDengine CLI `taos` to execute below command to add the end point of the dnode in the cluster. In the command "fqdn:port" should be quoted using double quotes. + +```sql +CREATE DNODE "h2.taos.com:6030"; +``` + +Then on the first dnode, execute `show dnodes` in `taos` to show whether the second dnode has been added in the cluster successfully or not. + +```sql +SHOW DNODES; +``` + +If the status of the newly added dnode is offline, please check: + +- Whether the `taosd` process is running properly or not +- In the log file `taosdlog.0` to see whether the fqdn and port are correct or not + +The above process can be repeated to add more dnodes in the cluster. diff --git a/docs-en/10-cluster/02-cluster-mgmt.md b/docs-en/10-cluster/02-cluster-mgmt.md new file mode 100644 index 0000000000000000000000000000000000000000..3fcd68b29ce08519af9a0cde11d5361c6b4cd312 --- /dev/null +++ b/docs-en/10-cluster/02-cluster-mgmt.md @@ -0,0 +1,213 @@ +--- +sidebar_label: Operation +title: Manage DNODEs +--- + +It has been introduced that how to deploy and start a cluster from scratch. Once a cluster is ready, the dnode status in the cluster can be shown at any time, new dnode can be added to scale out the cluster, an existing dnode can be removed, even load balance can be performed manually.\ + +:::note +All the commands to be introduced in this chapter need to be run through TDengine CLI, sometimes it's necessary to use root privilege. + +::: + +## Show DNODEs + +below command can be executed in TDengine CLI `taos` to list all dnodes in the cluster, including ID, end point (fqdn:port), status (ready, offline), number of vnodes, number of free vnodes, etc. It's suggested to execute this command to check after adding or removing a dnode. + +```sql +SHOW DNODES; +``` + +Below is the example output of this command. + +``` +taos> show dnodes; + id | end_point | vnodes | cores | status | role | create_time | offline reason | +====================================================================================================================================== + 1 | localhost:6030 | 9 | 8 | ready | any | 2022-04-15 08:27:09.359 | | +Query OK, 1 row(s) in set (0.008298s) +``` + +## Show VGROUPs + +To utilize system resources efficiently and provide scalability, data sharding is required. The data of each database is divided into multiple shards and stored in multiple vnodes. These vnodes may be located in different dnodes, scaling out can be achieved by adding more vnodes from more dnodes. Each vnode can only be used for a single DB, but one DB can have multiple vnodes. The allocation of vnode is scheduled automatically by mnode according to system resources of the dnodes. + +Launch TDengine CLI `taos` and execute below command: + +```sql +USE SOME_DATABASE; +SHOW VGROUPS; +``` + +The example output is as below: + +``` +taos> show dnodes; + id | end_point | vnodes | cores | status | role | create_time | offline reason | +====================================================================================================================================== + 1 | localhost:6030 | 9 | 8 | ready | any | 2022-04-15 08:27:09.359 | | +Query OK, 1 row(s) in set (0.008298s) + +taos> use db; +Database changed. + +taos> show vgroups; + vgId | tables | status | onlines | v1_dnode | v1_status | compacting | +========================================================================================== + 14 | 38000 | ready | 1 | 1 | master | 0 | + 15 | 38000 | ready | 1 | 1 | master | 0 | + 16 | 38000 | ready | 1 | 1 | master | 0 | + 17 | 38000 | ready | 1 | 1 | master | 0 | + 18 | 37001 | ready | 1 | 1 | master | 0 | + 19 | 37000 | ready | 1 | 1 | master | 0 | + 20 | 37000 | ready | 1 | 1 | master | 0 | + 21 | 37000 | ready | 1 | 1 | master | 0 | +Query OK, 8 row(s) in set (0.001154s) +``` + +## Add DNODE + +Launch TDengine CLI `taos` and execute the command below to add the end point of a new dnode into the EPI (end point) list of the cluster. "fqdn:port" must be quoted using double quotes. + +```sql +CREATE DNODE "fqdn:port"; +``` + +The example output is as below: + +``` +taos> create dnode "localhost:7030"; +Query OK, 0 of 0 row(s) in database (0.008203s) + +taos> show dnodes; + id | end_point | vnodes | cores | status | role | create_time | offline reason | +====================================================================================================================================== + 1 | localhost:6030 | 9 | 8 | ready | any | 2022-04-15 08:27:09.359 | | + 2 | localhost:7030 | 0 | 0 | offline | any | 2022-04-19 08:11:42.158 | status not received | +Query OK, 2 row(s) in set (0.001017s) +``` + +It can be seen that the status of the new dnode is "offline", once the dnode is started and connects the firstEp of the cluster, execute the command again and get below example output, from which it can be seen that two dnodes are both in "ready" status. + +``` +taos> show dnodes; + id | end_point | vnodes | cores | status | role | create_time | offline reason | +====================================================================================================================================== + 1 | localhost:6030 | 3 | 8 | ready | any | 2022-04-15 08:27:09.359 | | + 2 | localhost:7030 | 6 | 8 | ready | any | 2022-04-19 08:14:59.165 | | +Query OK, 2 row(s) in set (0.001316s) +``` + +## Drop DNODE + +Launch TDengine CLI `taos` and execute the command below to drop or remove a dnode from the cluster. In the command, `dnodeId` can be gotten from `show dnodes`. + +```sql +DROP DNODE "fqdn:port"; +``` + +or + +```sql +DROP DNODE dnodeId; +``` + +The example output is as below: + +``` +taos> show dnodes; + id | end_point | vnodes | cores | status | role | create_time | offline reason | +====================================================================================================================================== + 1 | localhost:6030 | 9 | 8 | ready | any | 2022-04-15 08:27:09.359 | | + 2 | localhost:7030 | 0 | 0 | offline | any | 2022-04-19 08:11:42.158 | status not received | +Query OK, 2 row(s) in set (0.001017s) + +taos> drop dnode 2; +Query OK, 0 of 0 row(s) in database (0.000518s) + +taos> show dnodes; + id | end_point | vnodes | cores | status | role | create_time | offline reason | +====================================================================================================================================== + 1 | localhost:6030 | 9 | 8 | ready | any | 2022-04-15 08:27:09.359 | | +Query OK, 1 row(s) in set (0.001137s) +``` + +In the above example, when `show dnodes` is executed the first time, two dnodes are shown. Then `drop dnode 2` is executed, after that from the output of executing `show dnodes` again it can be seen that only the dnode with ID 1 is still in the cluster. + +:::note + +- Once a dnode is dropped, it can't rejoin the cluster. To rejoin, the dnode needs to deployed again after cleaning up the data directory. Normally, before dropping a dnode, the data belonging to the dnode needs to be migrated to other place. +- Please be noted that `drop dnode` is different from stopping `taosd` process. `drop dnode` just removes the dnode out of TDengine cluster. Only after a dnode is dropped, can the corresponding `taosd` process be stopped. +- Once a dnode is dropped, other dnodes in the cluster will be notified of the drop and will not accept the request from the dropped dnode. +- dnodeID is allocated automatically and can't be interfered manually. dnodeID is generated in ascending order without duplication. + +::: + +## Move VNODE + +A vnode can be manually moved from one dnode to another. + +Launch TDengine CLI `taos` and execute below command: + +```sql +ALTER DNODE BALANCE "VNODE:-DNODE:"; +``` + +In the above command, `source-dnodeId` is the original dnodeId where the vnode resides, `dest-dnodeId` specifies the target dnode. vgId (vgroup ID) can be shown by `SHOW VGROUPS `. + +Firstly `show vgroups` is executed to show the vgroup distribution. + +``` +taos> show vgroups; + vgId | tables | status | onlines | v1_dnode | v1_status | compacting | +========================================================================================== + 14 | 38000 | ready | 1 | 3 | master | 0 | + 15 | 38000 | ready | 1 | 3 | master | 0 | + 16 | 38000 | ready | 1 | 3 | master | 0 | + 17 | 38000 | ready | 1 | 3 | master | 0 | + 18 | 37001 | ready | 1 | 3 | master | 0 | + 19 | 37000 | ready | 1 | 1 | master | 0 | + 20 | 37000 | ready | 1 | 1 | master | 0 | + 21 | 37000 | ready | 1 | 1 | master | 0 | +Query OK, 8 row(s) in set (0.001314s) +``` + +It can be seen that there are 5 vgroups in dnode 3 and 3 vgroups in node 1, now we want to move vgId 18 from dnode 3 to dnode 1. Execute below command in `taos` + +``` +taos> alter dnode 3 balance "vnode:18-dnode:1"; + +DB error: Balance already enabled (0.00755 +``` + +However, the operation fails with error message show above, which means automatic load balancing has been enabled in the current database so manual load balance can't be performed. + +Shutdown the cluster, configure `balance` parameter in all the dnodes to 0, then restart the cluster, and execute `alter dnode` and `show vgroups` as below. + +``` +taos> alter dnode 3 balance "vnode:18-dnode:1"; +Query OK, 0 row(s) in set (0.000575s) + +taos> show vgroups; + vgId | tables | status | onlines | v1_dnode | v1_status | v2_dnode | v2_status | compacting | +================================================================================================================= + 14 | 38000 | ready | 1 | 3 | master | 0 | NULL | 0 | + 15 | 38000 | ready | 1 | 3 | master | 0 | NULL | 0 | + 16 | 38000 | ready | 1 | 3 | master | 0 | NULL | 0 | + 17 | 38000 | ready | 1 | 3 | master | 0 | NULL | 0 | + 18 | 37001 | ready | 2 | 1 | slave | 3 | master | 0 | + 19 | 37000 | ready | 1 | 1 | master | 0 | NULL | 0 | + 20 | 37000 | ready | 1 | 1 | master | 0 | NULL | 0 | + 21 | 37000 | ready | 1 | 1 | master | 0 | NULL | 0 | +Query OK, 8 row(s) in set (0.001242s) +``` + +It can be seen from above output that vgId 18 has been moved from dnode 3 to dnode 1. + +:::note + +- Manual load balancing can only be performed when the automatic load balancing is disabled, i.e. `balance` is set to 0. +- Only vnode in normal state, i.e. master or slave, can be moved. vnode can't moved when its in status offline, unsynced or syncing. +- Before moving a vnode, it's necessary to make sure the target dnode has enough resources: CPU, memory and disk. + +::: diff --git a/docs-en/10-cluster/03-ha-and-lb.md b/docs-en/10-cluster/03-ha-and-lb.md new file mode 100644 index 0000000000000000000000000000000000000000..53c95be9e995a728b2b4053e4f204df58271716e --- /dev/null +++ b/docs-en/10-cluster/03-ha-and-lb.md @@ -0,0 +1,80 @@ +--- +sidebar_label: HA & LB +title: High Availability and Load Balancing +--- + +## High Availability of Vnode + +High availability of vnode and mnode can be achieved through replicas in TDengine. + +The number of vnodes is associated with each DB, there can be multiple DBs in a TDengine cluster. For the purpose of operation, different number of replicas can be configured properly for each DB. When creating a database, the parameter `replica` is used to specify the number of replicas, the default value is 1. With single replica, the high availability of the system can't be guaranteed. Whenever one node is down, data service would be unavailable. The number of dnodes in the cluster must NOT be lower than the number of replicas set for any DB, otherwise the `create table` operation would fail with error "more dnodes are needed". Below SQL statement is used to create a database named as "demo" with 3 replicas. + +```sql +CREATE DATABASE demo replica 3; +``` + +The data in a DB is divided into multiple shards and stored in multiple vgroups. The number of vnodes in each group is determined by the number of replicas set for the DB. The vnodes in each vgroups store exactly same data. For the purpose of high availability, the vnodes in a vgroup must be located in different dnodes on different hosts. As long as over half of the vnodes in a vgroup are in online state, the vgroup is able to serve data access. Otherwise the vgroup can't handle any data access for reading or inserting data. + +There may be data for multiple DBs in a dnode. Once a dnode is down, multiple DBs may be affected. However, it's hard to say the cluster is guaranteed to work properly as long as over half of dnodes are online because vnodes are introduced and there may be complex mapping between vnodes and dnodes. + +## High Availability of Mnode + +Each TDengine cluster is managed by `mnode`, which is a module of `taosd`. For the high availability of mnode, multiple mnodes can be configured using system parameter `numOfMNodes`, the valid time range is [1,3]. To make sure the data consistency between mnodes, the data replication between mnodes is performed in synchronous way. + +There may be multiple dnodes in a cluster, but only one mnode can be started in each dnode. Which one or ones of the dnodes will be designated as mnodes is automatically determined by TDengine according to the cluster configuration and system resources. Command `show mnodes` can be executed in TDengine `taos` to show the mnodes in the cluster. + +```sql +SHOW MNODES; +``` + +The end point and role/status (master, slave, unsynced, or offline) of all mnodes can be shown by the above command. When the first dnode is started in a cluster, there must be one mnode in this dnode, because there must be at least one mnode otherwise the cluster doesn't work. If `numOfMNodes` is configured to 2, another mnode will be started when the second dnode is launched. + +For the high availability of mnode, `numOfMnodes` needs to be configured to 2 or a higher value. Because the data consistency between mnodes must be guaranteed, the replica confirmation parameter `quorum` is set to 2 automatically if `numOfMNodes` is set to 2 or higher. + +:::note +If high availability is important for your system, both vnode and mnode must be configured to have multiple replicas. How to configure for them are different and have been described. + +::: + +## Load Balance + +Load balance will be triggered in 3 cades without manual intervention. + +- When a new dnode is joined in the cluster, automatic load balancing may be triggered, some data from some dnodes may be transferred to the new dnode automatically. +- When a dnode is removed from the cluster, the data from this dnode will be transferred to other dnodes automatically. +- When a dnode is too hot, i.e. too much data has been stored in it, automatic load balancing may be triggered to migrate some vnodes from this dnode to other dnodes. +- :::tip + Automatic load balancing is controlled by parameter `balance`, 0 means disabled and 1 means enabled. + +::: + +## Dnode Offline + +When a dnode is offline, it can be detected by the TDengine cluster. There are two cases: + +- The dnode becomes online again before the threshold configured in `offlineThreshold` is reached, it is still in the cluster and data replication is started automatically. The dnode can work properly after the data syncup is finished. + +- If the dnode has been offline over the threshold configured in `offlineThreshold` in `taos.cfg`, the dnode will be removed from the cluster automatically. System alert will be generated and automatic load balancing will be triggered too if `balance` is set to 1. When the removed dnode is restarted and becomes online, it will not be joined in the cluster automatically, it can only be joined manually by the system operator. + +:::note +If all the vnodes in a vgroup (or mnodes in mnode group) are in offline or unsynced status, the master node can only be voted after all the vnodes or mnodes in the group become online and can exchange status, then the vgroup (or mnode group) is able to provide service. + +::: + +## Arbitrator + +If the number of replicas is set to an even number like 2, when half of the vnodes in a vgroup don't work master node can't be voted. Similar case is also applicable to mnode if the number of mnodes is set to an even number like 2. + +To resolve this problem, a new arbitrator component named `tarbitrator`, abbreviated for TDengine Arbitrator, was introduced. Arbitrator simulates a vnode or mnode but it's only responsible for network communication and doesn't handle any actual data access. With Arbitrator, any vgroup or mnode group can be considered as having number of member nodes and master node can be selected. + +Normally, it's suggested to configure replica number of each DB or system parameter `numOfMNodes` to an odd number. However, if a user is very sensitive to storage space, replica number of 2 plus arbitrator component can be used to achieve both lower cost of storage space and high availability. + +Arbitrator component is installed with the server package. For details about how to install, please refer to [Install](/operation/pkg-install). The `-p` parameter of `tarbitrator` can be used to specify the port on which it provides service. + +In the configuration file `taos.cfg` of each dnode, parameter `arbitrator` needs to be configured to the end point of the `tarbitrator` process. arbitrator component will be used automatically if the replica is configured to an even number and will be ignored if the replica is configured to an odd number. + +Arbitrator can be shown by executing command in TDengine CLI `taos` with its role shown as "arb". + +```sql +SHOW DNODES; +``` diff --git a/docs-en/10-cluster/_category_.yml b/docs-en/10-cluster/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..141fd7832631d69efed214293c69cee336bc854d --- /dev/null +++ b/docs-en/10-cluster/_category_.yml @@ -0,0 +1 @@ +label: Cluster diff --git a/docs-en/10-cluster/index.md b/docs-en/10-cluster/index.md new file mode 100644 index 0000000000000000000000000000000000000000..a19a54e01d5a6429e95958c2544072961b0cb66a --- /dev/null +++ b/docs-en/10-cluster/index.md @@ -0,0 +1,15 @@ +--- +title: Cluster +keywords: ["cluster", "high availability", "load balance", "scale out"] +--- + +TDengine has a native distributed design and provides the ability to scale out. A few of nodes can form a TDengine cluster. If you need to get higher processing power, you just need to add more nodes into the cluster. TDengine uses virtual node technology to virtualize a node into multiple virtual nodes to achieve load balancing. At the same time, TDengine can group virtual nodes on different nodes into virtual node groups, and use the replication mechanism to ensure the high availability of the system. The cluster feature of TDengine is completely open source. + +This chapter mainly introduces cluster deployment, maintenance, and how to achieve high availability and load balancing. + +```mdx-code-block +import DocCardList from '@theme/DocCardList'; +import {useCurrentSidebarCategory} from '@docusaurus/theme-common'; + + +``` diff --git a/docs-en/12-taos-sql/01-data-type.md b/docs-en/12-taos-sql/01-data-type.md new file mode 100644 index 0000000000000000000000000000000000000000..931e3bbac7f0601a9de79d0dfa04ffc94ecced96 --- /dev/null +++ b/docs-en/12-taos-sql/01-data-type.md @@ -0,0 +1,49 @@ +--- +title: Data Types +description: "The data types supported by TDengine include timestamp, float, JSON, etc" +--- + +When using TDengine to store and query data, the most important part of the data is timestamp. Timestamp must be specified when creating and inserting data rows or querying data, timestamp must follow below rules: + +- the format must be `YYYY-MM-DD HH:mm:ss.MS`, the default time precision is millisecond (ms), for example `2017-08-12 18:25:58.128` +- internal function `now` can be used to get the current timestamp of the client side +- the current timestamp of the client side is applied when `now` is used to insert data +- Epoch Time:timestamp can also be a long integer number, which means the number of seconds, milliseconds or nanoseconds, depending on the time precision, from 1970-01-01 00:00:00.000 (UTC/GMT) +- timestamp can be applied with add/subtract operation, for example `now-2h` means 2 hours back from the time at which query is executed,the unit can be b(nanosecond), u(microsecond), a(millisecond), s(second), m(minute), h(hour), d(day), w(week.。 So `select * from t1 where ts > now-2w and ts <= now-1w` means the data between two weeks ago and one week ago. The time unit can also be n (calendar month) or y (calendar year) when specifying the time window for down sampling operation. + +Time precision in TDengine can be set by the `PRECISION` parameter when executing `CREATE DATABASE`, like below, the default time precision is millisecond. + +```sql +CREATE DATABASE db_name PRECISION 'ns'; +``` + +In TDengine, below data types can be used when specifying a column or tag. + +| # | **type** | **Bytes** | **Description** | +| --- | :-------: | --------- | ------------------------- | +| 1 | TIMESTAMP | 8 | Default precision is millisecond, microsecond and nanosecond are also supported | +| 2 | INT | 4 | Integer, the value range is [-2^31+1, 2^31-1], while -2^31 is treated as NULL | +| 3 | BIGINT | 8 | Long integer, the value range is [-2^63+1, 2^63-1], while -2^63 is treated as NULL | +| 4 | FLOAT | 4 | Floating point number, the effective number of digits is 6-7, the value range is [-3.4E38, 3.4E38] | +| 5 | DOUBLE | 8 | double precision floating point number, the effective number of digits is 15-16, the value range is [-1.7E308, 1.7E308] | +| 6 | BINARY | User Defined | Single-byte string for ASCII visible characters. Length must be specified when defining a column or tag of binary type. The string length can be up to 16374 bytes. The string value must be quoted with single quotes. The literal single quote inside the string must be preceded with back slash like `\'` | +| 7 | SMALLINT | 2 | Short integer, the value range is [-32767, 32767], while -32768 is treated as NULL | +| 8 | TINYINT | 1 | Single-byte integer, the value range is [-127, 127], while -128 is treated as NULL | +| 9 | BOOL | 1 | Bool, the value range is {true, false} | +| 10 | NCHAR | User Defined| Multiple-Byte string that can include like Chinese characters. Each character of NCHAR type consumes 4 bytes storage. The string value should be quoted with single quotes. Literal single quote inside the string must be preceded with backslash, like `\’`. The length must be specified when defining a column or tag of NCHAR type, for example nchar(10) means it can store at most 10 characters of nchar type and will consume fixed storage of 40 bytes. Error will be reported the string value exceeds the length defined. | +| 11 | JSON | | json type can only be used on tag, a tag of json type is excluded with any other tags of any other type | + +:::tip +TDengine is case insensitive and treats any characters in the sql command as lower case by default, case sensitive strings must be quoted with single quotes. + +::: + +:::note +Only ASCII visible characters are suggested to be used in a column or tag of BINARY type. Multiple-byte characters must be stored in NCHAR type. + +::: + +:::note +Numeric values in SQL statements will be determined as integer or float type according to whether there is decimal point or whether scientific notation is used, so attention must be paid to avoid overflow. For example, 9999999999999999999 will be considered as overflow because it exceeds the upper limit of long integer, but 9999999999999999999.0 will be considered as a legal float number. + +::: diff --git a/docs-en/12-taos-sql/02-database.md b/docs-en/12-taos-sql/02-database.md new file mode 100644 index 0000000000000000000000000000000000000000..85b71bbde727ea1ff84080d3770e641d59b88c7b --- /dev/null +++ b/docs-en/12-taos-sql/02-database.md @@ -0,0 +1,127 @@ +--- +sidebar_label: Database +title: Database +description: "create and drop database, show or change database parameters" +--- + +## Create Datable + +``` +CREATE DATABASE [IF NOT EXISTS] db_name [KEEP keep] [DAYS days] [UPDATE 1]; +``` + +:::info + +1. KEEP specifies the number of days for which the data in the database to be created will be kept, the default value is 3650 days, i.e. 10 years. The data will be deleted automatically once its age exceeds this threshold. +2. UPDATE specifies whether the data can be updated and how the data can be updated. + 1. UPDATE set to 0 means update operation is not allowed, the data with an existing timestamp will be dropped silently. + 2. UPDATE set to 1 means the whole row will be updated, the columns for which no value is specified will be set to NULL + 3. UPDATE set to 2 means updating a part of columns for a row is allowed, the columns for which no value is specified will be kept as no change +3. The maximum length of database name is 33 bytes. +4. The maximum length of a SQL statement is 65,480 bytes. +5. Below are the parameters that can be used when creating a database + - cache: [Description](/reference/config/#cache) + - blocks: [Description](/reference/config/#blocks) + - days: [Description](/reference/config/#days) + - keep: [Description](/reference/config/#keep) + - minRows: [Description](/reference/config/#minrows) + - maxRows: [Description](/reference/config/#maxrows) + - wal: [Description](/reference/config/#wallevel) + - fsync: [Description](/reference/config/#fsync) + - update: [Description](/reference/config/#update) + - cacheLast: [Description](/reference/config/#cachelast) + - replica: [Description](/reference/config/#replica) + - quorum: [Description](/reference/config/#quorum) + - maxVgroupsPerDb: [Description](/reference/config/#maxvgroupsperdb) + - comp: [Description](/reference/config/#comp) + - precision: [Description](/reference/config/#precision) +6. Please be noted that all of the parameters mentioned in this section can be configured in configuration file `taosd.cfg` at server side and used by default, can be override if they are specified in `create database` statement. + +::: + +## Show Current Configuration + +``` +SHOW VARIABLES; +``` + +## Specify The Database In Use + +``` +USE db_name; +``` + +:::note +This way is not applicable when using a REST connection + +::: + +## Drop Database + +``` +DROP DATABASE [IF EXISTS] db_name; +``` + +:::note +All data in the database will be deleted too. This command must be used with caution. + +::: + +## Change Database Configuration + +Some examples are shown below to demonstrate how to change the configuration of a database. Please be noted that some configuration parameters can be changed after the database is created, but some others can't, for details of the configuration parameters of database please refer to [Configuration Parameters](/reference/config/). + +``` +ALTER DATABASE db_name COMP 2; +``` + +COMP parameter specifies whether the data is compressed and how the data is compressed. + +``` +ALTER DATABASE db_name REPLICA 2; +``` + +REPLICA parameter specifies the number of replications of the database. + +``` +ALTER DATABASE db_name KEEP 365; +``` + +KEEP parameter specifies the number of days for which the data will be kept. + +``` +ALTER DATABASE db_name QUORUM 2; +``` + +QUORUM parameter specifies the necessary number of confirmations to determine whether the data is written successfully. + +``` +ALTER DATABASE db_name BLOCKS 100; +``` + +BLOCKS parameter specifies the number of memory blocks used by each VNODE. + +``` +ALTER DATABASE db_name CACHELAST 0; +``` + +CACHELAST parameter specifies whether and how the latest data of a sub table is cached. + +:::tip +The above parameters can be changed using `ALTER DATABASE` command without restarting. For more details of all configuration parameters please refer to [Configuration Parameters](/reference/config/). + +::: + +## Show All Databases + +``` +SHOW DATABASES; +``` + +## Show The Create Statement of A Database + +``` +SHOW CREATE DATABASE db_name; +``` + +This command is useful when migrating the data from one TDengine cluster to another one. Firstly this command can be used to get the CREATE statement, which in turn can be used in another TDengine to create an exactly same database. diff --git a/docs-en/12-taos-sql/03-table.md b/docs-en/12-taos-sql/03-table.md new file mode 100644 index 0000000000000000000000000000000000000000..a1524f45f98e8435425a9a937b7f6dc4431b6e06 --- /dev/null +++ b/docs-en/12-taos-sql/03-table.md @@ -0,0 +1,127 @@ +--- +sidebar_label: Table +title: Table +description: create super table, normal table and sub table, drop tables and change tables +--- + +## Create Table + +``` +CREATE TABLE [IF NOT EXISTS] tb_name (timestamp_field_name TIMESTAMP, field1_name data_type1 [, field2_name data_type2 ...]); +``` + +:::info + +1. The first column of a table must be in TIMESTAMP type, and it will be set as primary key automatically +2. The maximum length of table name is 192 bytes. +3. The maximum length of each row is 16k bytes, please be notes that the extra 2 bytes used by each BINARY/NCHAR column are also counted in. +4. The name of sub-table can only be consisted of English characters, digits and underscore, and can't be started with digit. Table names are case insensitive. +5. The maximum length in bytes must be specified when using BINARY or NCHAR type. +6. Escape character "\`" can be used to avoid the conflict between table names and reserved keywords, above rules will be bypassed when using escape character on table names, but the upper limit for name length is still valid. The table names specified using escape character are case sensitive. Only ASCII visible characters can be used with escape character. + For example \`aBc\` and \`abc\` are different table names but `abc` and `aBc` are same table names because they are both converted to `abc` internally. + +::: + +### Create Subtable Using STable As Template + +``` +CREATE TABLE [IF NOT EXISTS] tb_name USING stb_name TAGS (tag_value1, ...); +``` + +The above command creates a subtable using the specified super table as template and the specified tab values. + +### Create Subtable Using STable As Template With A Part of Tags + +``` +CREATE TABLE [IF NOT EXISTS] tb_name USING stb_name (tag_name1, ...) TAGS (tag_value1, ...); +``` + +The tags for which no value is specified will be set to NULL. + +### Create Tables in Batch + +``` +CREATE TABLE [IF NOT EXISTS] tb_name1 USING stb_name TAGS (tag_value1, ...) [IF NOT EXISTS] tb_name2 USING stb_name TAGS (tag_value2, ...) ...; +``` + +This way can be used to create a lot of tables in a single SQL statement to accelerate the speed of the creating tables. + +:::info + +- Creating tables in batch must use super table as template. +- The length of single statement is suggested to be between 1,000 and 3,000 bytes for best performance. + +::: + +## Drop Tables + +``` +DROP TABLE [IF EXISTS] tb_name; +``` + +## Show All Tables In Current Database + +``` +SHOW TABLES [LIKE tb_name_wildcard]; +``` + +## Show Create Statement of A Table + +``` +SHOW CREATE TABLE tb_name; +``` + +This way is useful when migrating the data in one TDengine cluster to another one because it can be used to create exactly same tables in the target database. + +## Show Table Definition + +``` +DESCRIBE tb_name; +``` + +## Change Table Definition + +### Add A Column + +``` +ALTER TABLE tb_name ADD COLUMN field_name data_type; +``` + +:::info + +1. The maximum number of columns is 4096, the minimum number of columns is 2. +2. The maximum length of column name is 64 bytes. + +::: + +### Remove A Column + +``` +ALTER TABLE tb_name DROP COLUMN field_name; +``` + +:::note +If a table is created using a super table as template, the table definition can only be changed on the corresponding super table, but the change will be automatically applied to all the sub tables created using this super table as template. For tables created in normal way, the table definition can be changed directly on the table. + +::: + +### Change Column Length + +``` +ALTER TABLE tb_name MODIFY COLUMN field_name data_type(length); +``` + +The the type of a column is variable length, like BINARY or NCHAR, this way can be used to change (or increase) the length of the column. + +:::note +If a table is created using a super table as template, the table definition can only be changed on the corresponding super table, but the change will be automatically applied to all the sub tables created using this super table as template. For tables created in normal way, the table definition can be changed directly on the table. + +::: + +### Change Tag Value Of Sub Table + +``` +ALTER TABLE tb_name SET TAG tag_name=new_tag_value; +``` + +This command can be used to change the tag value if the table is created using a super table as template. diff --git a/docs-en/12-taos-sql/04-stable.md b/docs-en/12-taos-sql/04-stable.md new file mode 100644 index 0000000000000000000000000000000000000000..b7817f90287a6415bee020fb5adc8e6239cc6da4 --- /dev/null +++ b/docs-en/12-taos-sql/04-stable.md @@ -0,0 +1,118 @@ +--- +sidebar_label: STable +title: Super Table +--- + +:::note + +Keyword `STable`, abbreviated for super table, is supported since version 2.0.15. + +::: + +## Crate STable + +``` +CREATE STable [IF NOT EXISTS] stb_name (timestamp_field_name TIMESTAMP, field1_name data_type1 [, field2_name data_type2 ...]) TAGS (tag1_name tag_type1, tag2_name tag_type2 [, tag3_name tag_type3]); +``` + +The SQL statement of creating STable is similar to that of creating table, but a special column named as `TAGS` must be specified with the names and types of the tags. + +:::info + +1. The tag types specified in TAGS should NOT be timestamp. Since 2.1.3.0 timestamp type can be used in TAGS column, but its value must be fixed and arithmetic operation can't be applied on it. +2. The tag names specified in TAGS should NOT be same as other columns. +3. The tag names specified in TAGS should NOT be same as any reserved keywords.(Please refer to [keywords](/taos-sql/keywords/) +4. The maximum number of tags specified in TAGS is 128, but there must be at least one tag, and the total length of all tag columns should NOT exceed 16KB. + +::: + +## Drop STable + +``` +DROP STable [IF EXISTS] stb_name; +``` + +All the sub-tables created using the deleted STable will be deleted automatically. + +## Show All STables + +``` +SHOW STableS [LIKE tb_name_wildcard]; +``` + +This command can be used to display the information of all STables in the current database, including name, creation time, number of columns, number of tags, number of tables created using this STable. + +## Show The Create Statement of A STable + +``` +SHOW CREATE STable stb_name; +``` + +This command is useful in migrating data from one TDengine cluster to another one because it can be used to create an exactly same STable in the target database. + +## Get STable Definition + +``` +DESCRIBE stb_name; +``` + +## Change Columns Of STable + +### Add A Column + +``` +ALTER STable stb_name ADD COLUMN field_name data_type; +``` + +### Remove A Column + +``` +ALTER STable stb_name DROP COLUMN field_name; +``` + +### Change Column Length + +``` +ALTER STable stb_name MODIFY COLUMN field_name data_type(length); +``` + +This command can be used to change (or increase, more specifically) the length of a column of variable length types, like BINARY or NCHAR. + +## Change Tags of A STable + +### Add A Tag + +``` +ALTER STable stb_name ADD TAG new_tag_name tag_type; +``` + +This command is used to add a new tag for a STable and specify the tag type. + +### Remove A Tag + +``` +ALTER STable stb_name DROP TAG tag_name; +``` + +The tag will be removed automatically from all the sub tables crated using the super table as template once a tag is removed from a super table. + +### Change A Tag + +``` +ALTER STable stb_name CHANGE TAG old_tag_name new_tag_name; +``` + +The tag name will be changed automatically from all the sub tables crated using the super table as template once a tag name is changed for a super table. + +### Change Tag Length + +``` +ALTER STable stb_name MODIFY TAG tag_name data_type(length); +``` + +This command can be used to change (or increase, more specifically) the length of a tag of variable length types, like BINARY or NCHAR. + +:::note +Changing tag value can be applied to only sub tables. All other tag operations, like add tag, remove tag, however, can be applied to only STable. If a new tag is added for a STable, the tag will be added with NULL value for all its sub tables. + +::: diff --git a/docs-en/12-taos-sql/05-insert.md b/docs-en/12-taos-sql/05-insert.md new file mode 100644 index 0000000000000000000000000000000000000000..96e6a08ee17e0c72b15a35efc487a78ae4673017 --- /dev/null +++ b/docs-en/12-taos-sql/05-insert.md @@ -0,0 +1,164 @@ +--- +title: Insert +--- + +## Syntax + +```sql +INSERT INTO + tb_name + [USING stb_name [(tag1_name, ...)] TAGS (tag1_value, ...)] + [(field1_name, ...)] + VALUES (field1_value, ...) [(field1_value2, ...) ...] | FILE csv_file_path + [tb2_name + [USING stb_name [(tag1_name, ...)] TAGS (tag1_value, ...)] + [(field1_name, ...)] + VALUES (field1_value, ...) [(field1_value2, ...) ...] | FILE csv_file_path + ...]; +``` + +## Insert Single or Multiple Rows + +Single row or multiple rows specified with VALUES can be inserted into a specific table. For example + +Single row is inserted using below statement. + +```sq; +INSERT INTO d1001 VALUES (NOW, 10.2, 219, 0.32); +``` + +Double rows can be inserted using below statement. + +```sql +INSERT INTO d1001 VALUES ('2021-07-13 14:06:32.272', 10.2, 219, 0.32) (1626164208000, 10.15, 217, 0.33); +``` + +:::note + +1. In the second example above, different formats are used in the two rows to be inserted. In the first row, the timestamp format is a date and time string, which is interpreted from the string value only. In the second row, the timestamp format is a long integer, which will be interpreted based on the database time precision. +2. When trying to insert multiple rows in single statement, only the timestamp of one row can be set as NOW, otherwise there will be duplicate timestamps among the rows and the result may be out of expectation because NOW will be interpreted as the time when the statement is executed. +3. The oldest timestamp that is allowed is subtracting the KEEP parameter from current time. +4. The newest timestamp that is allowed is adding the DAYS parameter to current time. + +::: + +## Insert Into Specific Columns + +Data can be inserted into specific columns, either single row or multiple row, while other columns will be inserted as NULL value. + +``` +INSERT INTO d1001 (ts, current, phase) VALUES ('2021-07-13 14:06:33.196', 10.27, 0.31); +``` + +:::info +If no columns are explicitly specified, all the columns must be provided with values, this is called "all column mode". The insert performance of all column mode is much better than specifying a part of columns, so it's encouraged to use "all column mode" while providing NULL value explicitly for the columns for which no actual value can be provided. + +::: + +## Insert Into Multiple Tables + +One or multiple rows can be inserted into multiple tables in single SQL statement, with or without specifying specific columns. + +```sql +INSERT INTO d1001 VALUES ('2021-07-13 14:06:34.630', 10.2, 219, 0.32) ('2021-07-13 14:06:35.779', 10.15, 217, 0.33) + d1002 (ts, current, phase) VALUES ('2021-07-13 14:06:34.255', 10.27, 0.31); +``` + +## Automatically Create Table When Inserting + +If it's not sure whether the table already exists, the table can be created automatically while inserting using below SQL statement. To use this functionality, a STable must be used as template and tag values must be provided. + +```sql +INSERT INTO d21001 USING meters TAGS ('Beijing.Chaoyang', 2) VALUES ('2021-07-13 14:06:32.272', 10.2, 219, 0.32); +``` + +It's not necessary to provide values for all tag when creating tables automatically, the tags without values provided will be set to NULL. + +```sql +INSERT INTO d21001 USING meters (groupId) TAGS (2) VALUES ('2021-07-13 14:06:33.196', 10.15, 217, 0.33); +``` + +Multiple rows can also be inserted into same table in single SQL statement using this way. + +```sql +INSERT INTO d21001 USING meters TAGS ('Beijing.Chaoyang', 2) VALUES ('2021-07-13 14:06:34.630', 10.2, 219, 0.32) ('2021-07-13 14:06:35.779', 10.15, 217, 0.33) + d21002 USING meters (groupId) TAGS (2) VALUES ('2021-07-13 14:06:34.255', 10.15, 217, 0.33) + d21003 USING meters (groupId) TAGS (2) (ts, current, phase) VALUES ('2021-07-13 14:06:34.255', 10.27, 0.31); +``` + +:::info +Prior to version 2.0.20.5, when using `INSERT` to create table automatically and specify the columns, the column names must follow the table name immediately. From version 2.0.20.5, the column names can follow the table name immediately, also can be put between `TAGS` and `VALUES`. In same SQL statement, however, these two ways of specifying column names can't be mixed. +::: + +## Insert Rows From A File + +Besides using `VALUES` to insert one or multiple rows, the data to be inserted can also be prepared in a CSV file with comma as separator and each field value quoted by single quotes. Table definition is not required in the CSV file. For example, if file "/tmp/csvfile.csv" contains below data: + +``` +'2021-07-13 14:07:34.630', '10.2', '219', '0.32' +'2021-07-13 14:07:35.779', '10.15', '217', '0.33' +``` + +Then data in this file can be inserted by below SQL statement: + +```sql +INSERT INTO d1001 FILE '/tmp/csvfile.csv'; +``` + +## Create Tables Automatically and Insert Rows From File + +From version 2.1.5.0, tables can be automatically created using a super table as template when inserting data from a CSV file, Like below: + +```sql +INSERT INTO d21001 USING meters TAGS ('Beijing.Chaoyang', 2) FILE '/tmp/csvfile.csv'; +``` + +Multiple tables can be automatically created and inserted in single SQL statement, like below: + +```sql +INSERT INTO d21001 USING meters TAGS ('Beijing.Chaoyang', 2) FILE '/tmp/csvfile_21001.csv' + d21002 USING meters (groupId) TAGS (2) FILE '/tmp/csvfile_21002.csv'; +``` + +## More About Insert + +For SQL statement like `insert`, stream parsing strategy is applied. That means before an error is found and the execution is aborted, the part prior to the error point has already been executed. Below is an experiment to help understand the behavior. + +Firstly, a super table is created. + +```sql +CREATE TABLE meters(ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS(location BINARY(30), groupId INT); +``` + +It can be proved that the super table has been created by `SHOW STableS`, but no table exists by `SHOW TABLES`. + +``` +taos> SHOW STableS; + name | created_time | columns | tags | tables | +============================================================================================ + meters | 2020-08-06 17:50:27.831 | 4 | 2 | 0 | +Query OK, 1 row(s) in set (0.001029s) + +taos> SHOW TABLES; +Query OK, 0 row(s) in set (0.000946s) +``` + +Then, try to create table d1001 automatically when inserting data into it. + +```sql +INSERT INTO d1001 USING meters TAGS('Beijing.Chaoyang', 2) VALUES('a'); +``` + +The output shows the value to be inserted is invalid. But `SHOW TABLES` proves that the table has been created automatically by the `INSERT` statement. + +``` +DB error: invalid SQL: 'a' (invalid timestamp) (0.039494s) + +taos> SHOW TABLES; + table_name | created_time | columns | STable_name | +====================================================================================================== + d1001 | 2020-08-06 17:52:02.097 | 4 | meters | +Query OK, 1 row(s) in set (0.001091s) +``` + +From the above experiment, we can see that even though the value to be inserted is invalid but the table is still created. diff --git a/docs-en/12-taos-sql/06-select.md b/docs-en/12-taos-sql/06-select.md new file mode 100644 index 0000000000000000000000000000000000000000..11b181f65d4e7e0e7d47d04986b144ff362c879f --- /dev/null +++ b/docs-en/12-taos-sql/06-select.md @@ -0,0 +1,449 @@ +--- +title: Select +--- + +## Syntax + +```SQL +SELECT select_expr [, select_expr ...] + FROM {tb_name_list} + [WHERE where_condition] + [SESSION(ts_col, tol_val)] + [STATE_WINDOW(col)] + [INTERVAL(interval_val [, interval_offset]) [SLIDING sliding_val]] + [FILL(fill_mod_and_val)] + [GROUP BY col_list] + [ORDER BY col_list { DESC | ASC }] + [SLIMIT limit_val [SOFFSET offset_val]] + [LIMIT limit_val [OFFSET offset_val]] + [>> export_file]; +``` + +## Wildcard + +Wilcard \* can be used to specify all columns. The result includes only data columns for normal tables. + +``` +taos> SELECT * FROM d1001; + ts | current | voltage | phase | +====================================================================================== + 2018-10-03 14:38:05.000 | 10.30000 | 219 | 0.31000 | + 2018-10-03 14:38:15.000 | 12.60000 | 218 | 0.33000 | + 2018-10-03 14:38:16.800 | 12.30000 | 221 | 0.31000 | +Query OK, 3 row(s) in set (0.001165s) +``` + +The result includes both data columns and tag columns for super table. + +``` +taos> SELECT * FROM meters; + ts | current | voltage | phase | location | groupid | +===================================================================================================================================== + 2018-10-03 14:38:05.500 | 11.80000 | 221 | 0.28000 | Beijing.Haidian | 2 | + 2018-10-03 14:38:16.600 | 13.40000 | 223 | 0.29000 | Beijing.Haidian | 2 | + 2018-10-03 14:38:05.000 | 10.80000 | 223 | 0.29000 | Beijing.Haidian | 3 | + 2018-10-03 14:38:06.500 | 11.50000 | 221 | 0.35000 | Beijing.Haidian | 3 | + 2018-10-03 14:38:04.000 | 10.20000 | 220 | 0.23000 | Beijing.Chaoyang | 3 | + 2018-10-03 14:38:16.650 | 10.30000 | 218 | 0.25000 | Beijing.Chaoyang | 3 | + 2018-10-03 14:38:05.000 | 10.30000 | 219 | 0.31000 | Beijing.Chaoyang | 2 | + 2018-10-03 14:38:15.000 | 12.60000 | 218 | 0.33000 | Beijing.Chaoyang | 2 | + 2018-10-03 14:38:16.800 | 12.30000 | 221 | 0.31000 | Beijing.Chaoyang | 2 | +Query OK, 9 row(s) in set (0.002022s) +``` + +Wildcard can be used with table name as prefix, both below SQL statements have same effects and return all columns. + +```SQL +SELECT * FROM d1001; +SELECT d1001.* FROM d1001; +``` + +In JOIN query, however, with or without table name prefix will return different results. \* without table prefix will return all the columns of both tables, but \* with table name as prefix will return only the columns of that table. + +``` +taos> SELECT * FROM d1001, d1003 WHERE d1001.ts=d1003.ts; + ts | current | voltage | phase | ts | current | voltage | phase | +================================================================================================================================== + 2018-10-03 14:38:05.000 | 10.30000| 219 | 0.31000 | 2018-10-03 14:38:05.000 | 10.80000| 223 | 0.29000 | +Query OK, 1 row(s) in set (0.017385s) +``` + +``` +taos> SELECT d1001.* FROM d1001,d1003 WHERE d1001.ts = d1003.ts; + ts | current | voltage | phase | +====================================================================================== + 2018-10-03 14:38:05.000 | 10.30000 | 219 | 0.31000 | +Query OK, 1 row(s) in set (0.020443s) +``` + +Wilcard \* can be used with some functions, but the result may be different depending on the function being used. For example, `count(*)` returns only one column, i.e. the number of rows; `first`, `last` and `last_row` return all columns of the selected row. + +``` +taos> SELECT COUNT(*) FROM d1001; + count(*) | +======================== + 3 | +Query OK, 1 row(s) in set (0.001035s) +``` + +``` +taos> SELECT FIRST(*) FROM d1001; + first(ts) | first(current) | first(voltage) | first(phase) | +========================================================================================= + 2018-10-03 14:38:05.000 | 10.30000 | 219 | 0.31000 | +Query OK, 1 row(s) in set (0.000849s) +``` + +## Tags + +Starting from version 2.0.14, tag columns can be selected together with data columns when querying sub tables. Please be noted that, however, wildcard \* doesn't represent any tag column, that means tag columns must be specified explicitly like below example. + +``` +taos> SELECT location, groupid, current FROM d1001 LIMIT 2; + location | groupid | current | +====================================================================== + Beijing.Chaoyang | 2 | 10.30000 | + Beijing.Chaoyang | 2 | 12.60000 | +Query OK, 2 row(s) in set (0.003112s) +``` + +## Get distinct values + +`DISTINCT` keyword can be used to get all the unique values of tag columns from a super table, it can also be used to get all the unique values of data columns from a table or sub table. + +```sql +SELECT DISTINCT tag_name [, tag_name ...] FROM stb_name; +SELECT DISTINCT col_name [, col_name ...] FROM tb_name; +``` + +:::info + +1. Configuration parameter `maxNumOfDistinctRes` in `taos.cfg` is used to control the number of rows to output. The minimum configurable value is 100,000, the maximum configurable value is 100,000,000, the default value is 1000,000. If the actual number of rows exceeds the value of this parameter, only the number of rows specified by this parameter will be output. +2. It can't be guaranteed that the results selected by using `DISTINCT` on columns of `FLOAT` or `DOUBLE` are exactly unique because of the precision nature of floating numbers. +3. `DISTINCT` can't be used in the sub-query of a nested query statement, and can't be used together with aggregate functions, `GROUP BY` or `JOIN` in same SQL statement. + +::: + +## Columns Names of Result Set + +When using `SELECT`, the column names in the result set will be same as that in the select clause if `AS` is not used. `AS` can be used to rename the column names in the result set. For example + +``` +taos> SELECT ts, ts AS primary_key_ts FROM d1001; + ts | primary_key_ts | +==================================================== + 2018-10-03 14:38:05.000 | 2018-10-03 14:38:05.000 | + 2018-10-03 14:38:15.000 | 2018-10-03 14:38:15.000 | + 2018-10-03 14:38:16.800 | 2018-10-03 14:38:16.800 | +Query OK, 3 row(s) in set (0.001191s) +``` + +`AS` can't be used together with `first(*)`, `last(*)`, or `last_row(*)`. + +## Implicit Columns + +`Select_exprs` can be column names of a table, or function expression or arithmetic expression on columns. The maximum number of allowed column names and expressions is 256. Timestamp and the corresponding tag names will be returned in the result set if `interval` or `group by tags` are used, and timestamp will always be the first column in the result set. + +## Table List + +`FROM` can be followed by a number of tables or super tables, or can be followed by a sub-query. If no database is specified as current database in use, table names must be preceded with database name, like `power.d1001`. + +```SQL +SELECT * FROM power.d1001; +``` + +has same effect as + +```SQL +USE power; +SELECT * FROM d1001; +``` + +## Special Query + +Some special query functionalities can be performed without `FORM` sub-clause. For example, below statement can be used to get the current database in use. + +``` +taos> SELECT DATABASE(); + database() | +================================= + power | +Query OK, 1 row(s) in set (0.000079s) +``` + +If no database is specified upon logging in and no database is specified with `USE` after login, NULL will be returned by `select database()`. + +``` +taos> SELECT DATABASE(); + database() | +================================= + NULL | +Query OK, 1 row(s) in set (0.000184s) +``` + +Below statement can be used to get the version of client or server. + +``` +taos> SELECT CLIENT_VERSION(); + client_version() | +=================== + 2.0.0.0 | +Query OK, 1 row(s) in set (0.000070s) + +taos> SELECT SERVER_VERSION(); + server_version() | +=================== + 2.0.0.0 | +Query OK, 1 row(s) in set (0.000077s) +``` + +Below statement is used to check the server status. One integer, like `1`, is returned if the server status is OK, otherwise an error code is returned. This way is compatible with the status check for TDengine from connection pool or 3rd party tools, and can avoid the problem of losing connection from connection pool when using wrong heartbeat checking SQL statement. + +``` +taos> SELECT SERVER_STATUS(); + server_status() | +================== + 1 | +Query OK, 1 row(s) in set (0.000074s) + +taos> SELECT SERVER_STATUS() AS status; + status | +============== + 1 | +Query OK, 1 row(s) in set (0.000081s) +``` + +## \_block_dist + +**Description**: Get the data block distribution of a table or STable. + +```SQL title="Syntax" +SELECT _block_dist() FROM { tb_name | stb_name } +``` + +**Restrictions**:No argument is allowed, where clause is not allowed + +**Sub Query**:Sub query or nested query are not supported + +**Return value**: A string which includes the data block distribution of the specified table or STable, i.e. the histogram of rows stored in the data blocks of the table or STable. + +```text title="Result" +summary: +5th=[392], 10th=[392], 20th=[392], 30th=[392], 40th=[792], 50th=[792] 60th=[792], 70th=[792], 80th=[792], 90th=[792], 95th=[792], 99th=[792] Min=[392(Rows)] Max=[800(Rows)] Avg=[666(Rows)] Stddev=[2.17] Rows=[2000], Blocks=[3], Size=[5.440(Kb)] Comp=[0.23] RowsInMem=[0] SeekHeaderTime=[1(us)] +``` + +**More explanation about above example**: + +- Histogram about the rows stored in the data blocks of the table or STable: the value of rows for 5%, 10%, 20%, 30%, 40%, 50%, 60%, 70%, 80%, 90%, 95%, and 99% +- Minimum number of rows stored in a data block, i.e. Min=[392(Rows)] +- Maximum number of rows stored in a data block, i.e. Max=[800(Rows)] +- Average number of rows stored in a data block, i.e. Avg=[666(Rows)] +- stddev of number of rows, i.e. Stddev=[2.17] +- Total number of rows, i.e. Rows[2000] +- Total number of data blocks, i.e. Blocks=[3] +- Total disk size consumed, i.e. Size=[5.440(Kb)] +- Compression ratio, which means the compressed size divided by original size, i.e. Comp=[0.23] +- Total number of rows in memory, i.e. RowsInMem=[0], which means no rows in memory +- The time spent on reading head file (to retrieve data block information), i.e. SeekHeaderTime=[1(us)], which means 1 microsecond. + +## Special Keywords in TAOS SQL + +- `TBNAME`: it is treated as a special tag when selecting on a super table, representing the name of sub-tables in that super table. +- `_c0`: represents the first column of a table or super table. + +## Tips + +To get all the sub tables and corresponding tag values from a super table: + +```SQL +SELECT TBNAME, location FROM meters; +``` + +To get the number of sub tables in a super table: + +```SQL +SELECT COUNT(TBNAME) FROM meters; +``` + +Only filter on `TAGS` are allowed in the `where` clause for above two query statements. For example: + +``` +taos> SELECT TBNAME, location FROM meters; + tbname | location | +================================================================== + d1004 | Beijing.Haidian | + d1003 | Beijing.Haidian | + d1002 | Beijing.Chaoyang | + d1001 | Beijing.Chaoyang | +Query OK, 4 row(s) in set (0.000881s) + +taos> SELECT COUNT(tbname) FROM meters WHERE groupId > 2; + count(tbname) | +======================== + 2 | +Query OK, 1 row(s) in set (0.001091s) +``` + +- Wildcard \* can be used to get all columns, or specific column names can be specified. Arithmetic operation can be performed on columns of number types, columns can be renamed in the result set. +- Arithmetic operation on columns can't be used in where clause. For example, `where a*2>6;` is not allowed but `where a>6/2;` can be used instead for same purpose. +- Arithmetic operation on columns can't be used as the objectives of select statement. For example, `select min(2*a) from t;` is not allowed but `select 2*min(a) from t;` can be used instead. +- Logical operation can be used in `WHERE` clause to filter numeric values, wildcard can be used to filter string values. +- Result set are arranged in ascending order of the first column, i.e. timestamp, but it can be controlled to output as descending order of timestamp. If `order by` is used on other columns, the result may be not as expected. By the way, \_c0 is used to represent the first column, i.e. timestamp. +- `LIMIT` parameter is used to control the number of rows to output. `OFFSET` parameter is used to specify from which row to output. `LIMIT` and `OFFSET` are executed after `ORDER BY` in the query execution. A simple tip is that `LIMIT 5 OFFSET 2` can be abbreviated as `LIMIT 2, 5`. +- What is controlled by `LIMIT` is the number of rows in each group when `GROUP BY` is used. +- `SLIMIT` parameter is used to control the number of groups when `GROUP BY` is used. Similar to `LIMIT`, `SLIMIT 5 OFFSET 2` can be abbreviated as `SLIMIT 2, 5`. +- ">>" can be used to output the result set of `select` statement to the specified file. + +## Where + +Logical operations in below table can be used in `where` clause to filter the resulting rows. + +| **Operation** | **Note** | **Applicable Data Types** | +| ------------- | ------------------------ | ----------------------------------------- | +| > | larger than | all types except bool | +| < | smaller than | all types except bool | +| >= | larger than or equal to | all types except bool | +| <= | smaller than or equal to | all types except bool | +| = | equal to | all types | +| <\> | not equal to | all types | +| is [not] null | is null or is not null | all types | +| between and | within a certain range | all types except bool | +| in | match any value in a set | all types except first column `timestamp` | +| like | match a wildcard string | **`binary`** **`nchar`** | +| match/nmatch | filter regex | **`binary`** **`nchar`** | + +**Explanations**: + +- Operator `<\>` is equal to `!=`, please be noted that this operator can't be used on the first column of any table, i.e.timestamp column. +- Operator `like` is used together with wildcards to match strings + - '%' matches 0 or any number of characters, '\_' matches any single ASCII character. + - `\_` is used to match the \_ in the string. + - The maximum length of wildcard string is 100 bytes from version 2.1.6.1 (before that the maximum length is 20 bytes). `maxWildCardsLength` in `taos.cfg` can be used to control this threshold. Too long wildcard string may slowdown the execution performance of `LIKE` operator. +- `AND` keyword can be used to filter multiple columns simultaneously. AND/OR operation can be performed on single or multiple columns from version 2.3.0.0. However, before 2.3.0.0 `OR` can't be used on multiple columns. +- For timestamp column, only one condition can be used; for other columns or tags, `OR` keyword can be used to combine multiple logical operators. For example, `((value > 20 AND value < 30) OR (value < 12))`. + - From version 2.3.0.0, multiple conditions can be used on timestamp column, but the result set can only contain single time range. +- From version 2.0.17.0, operator `BETWEEN AND` can be used in where clause, for example `WHERE col2 BETWEEN 1.5 AND 3.25` means the filter condition is equal to "1.5 ≤ col2 ≤ 3.25". +- From version 2.1.4.0, operator `IN` can be used in where clause. For example, `WHERE city IN ('Beijing', 'Shanghai')`. For bool type, both `{true, false}` and `{0, 1}` are allowed, but integers other than 0 or 1 are not allowed. FLOAT and DOUBLE types are impacted by floating precision, only values that match the condition within the tolerance will be selected. Non-primary key column of timestamp type can be used with `IN`. +- From version 2.3.0.0, regular expression is supported in where clause with keyword `match` or `nmatch`, the regular expression is case insensitive. + +## Regular Expression + +### Syntax + +```SQL +WHERE (column|tbname) **match/MATCH/nmatch/NMATCH** _regex_ +``` + +### Specification + +The regular expression being used must be compliant with POSIX specification, please refer to [Regular Expressions](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html). + +### Restrictions + +Regular expression can be used against only table names, i.e. `tbname`, and tags of binary/nchar types, but can't be used against data columns. + +The maximum length of regular expression string is 128 bytes. Configuration parameter `maxRegexStringLen` can be used to set the maximum allowed regular expression. It's a configuration parameter on client side, and will take in effect after restarting the client. + +## JOIN + +From version 2.2.0.0, inner join is fully supported in TDengine. More specifically, the inner join between table and table, that between STable and STable, and that between sub query and sub query are supported. + +Only primary key, i.e. timestamp, can be used in the join operation between table and table. For example: + +```sql +SELECT * +FROM temp_tb_1 t1, pressure_tb_1 t2 +WHERE t1.ts = t2.ts +``` + +In the join operation between STable and STable, besides the primary key, i.e. timestamp, tags can also be used. For example: + +```sql +SELECT * +FROM temp_STable t1, temp_STable t2 +WHERE t1.ts = t2.ts AND t1.deviceid = t2.deviceid AND t1.status=0; +``` + +Similary, join operation can be performed on the result set of multiple sub queries. + +:::note +Restrictions on join operation: + +- The number of tables or STables in single join operation can't exceed 10. +- `FILL` is not allowed in the query statement that includes JOIN operation. +- Arithmetic operation is not allowed on the result set of join operation. +- `GROUP BY` is not allowed on a part of tables that participate in join operation. +- `OR` can't be used in the conditions for join operation +- join operation can't be performed on data columns, i.e. can only be performed on tags or primary key, i.e. timestamp + +::: + +## Nested Query + +Nested query is also called sub query, that means in a single SQL statement the result of inner query can be used as the data source of the outer query. + +From 2.2.0.0, unassociated sub query can be used in the `FROM` clause. unassociated means the sub query doesn't use the parameters in the parent query. More specifically, in the `tb_name_list` of `SELECT` statement, an independent SELECT statement can be used. So a complete nested query looks like: + +```SQL +SELECT ... FROM (SELECT ... FROM ...) ...; +``` + +:::info + +- Only one layer of nesting is allowed, that means no sub query is allowed in a sub query +- The result set returned by the inner query will be used as a "virtual table" by the outer query, the "virtual table" can be renamed using `AS` keyword for easy reference in the outer query. +- Sub query is not allowed in continuous query. +- JOIN operation is allowed between tables/STables inside both inner and outer queries. Join operation can be performed on the result set of the inner query. +- UNION operation is not allowed in either inner query or outer query. +- The functionalities that can be used in the inner query is same as non-nested query. + - `ORDER BY` inside the inner query doesn't make any sense but will slow down the query performance significantly, so please avoid such usage. +- Compared to the non-nested query, the functionalities that can be used in the outer query have such restrictions as: + - Functions + - If the result set returned by the inner query doesn't contain timestamp column, then functions relying on timestamp can't be used in the outer query, like `TOP`, `BOTTOM`, `FIRST`, `LAST`, `DIFF`. + - Functions that need to scan the data twice can't be used in the outer query, like `STDDEV`, `PERCENTILE`. + - `IN` operator is not allowed in the outer query but can be used in the inner query. + - `GROUP BY` is not supported in the outer query. + +::: + +## UNION ALL + +```SQL title=Syntax +SELECT ... +UNION ALL SELECT ... +[UNION ALL SELECT ...] +``` + +`UNION ALL` operator can be used to combine the result set from multiple select statements as long as the result set of these select statements have exactly same columns. `UNION ALL` doesn't remove redundant rows from multiple result sets. In single SQL statement, at most 100 `UNION ALL` can be supported. + +### Examples + +table `tb1` is created using below SQL statement: + +```SQL +CREATE TABLE tb1 (ts TIMESTAMP, col1 INT, col2 FLOAT, col3 BINARY(50)); +``` + +The rows in the past one hour in `tb1` can be selected using below SQL statement: + +```SQL +SELECT * FROM tb1 WHERE ts >= NOW - 1h; +``` + +The rows between 2018-06-01 08:00:00.000 and 2018-06-02 08:00:00.000 and col3 ends with 'nny' can be selected in the descending order of timestamp using below SQL statement: + +```SQL +SELECT * FROM tb1 WHERE ts > '2018-06-01 08:00:00.000' AND ts <= '2018-06-02 08:00:00.000' AND col3 LIKE '%nny' ORDER BY ts DESC; +``` + +The sum of col1 and col2 for rows later than 2018-06-01 08:00:00.000 and whose col2 is bigger than 1.2 can be selected and renamed as "complex", while only 10 rows are output from the 5th row, by below SQL statement: + +```SQL +SELECT (col1 + col2) AS 'complex' FROM tb1 WHERE ts > '2018-06-01 08:00:00.000' AND col2 > 1.2 LIMIT 10 OFFSET 5; +``` + +The rows in the past 10 minutes and whose col2 is bigger than 3.14 are selected and output to the result file `/home/testoutpu.csv` with below SQL statement: + +```SQL +SELECT COUNT(*) FROM tb1 WHERE ts >= NOW - 10m AND col2 > 3.14 >> /home/testoutpu.csv; +``` diff --git a/docs-en/12-taos-sql/07-function.md b/docs-en/12-taos-sql/07-function.md new file mode 100644 index 0000000000000000000000000000000000000000..9db5f36f92735c659a3bfae84c67089c62d577a6 --- /dev/null +++ b/docs-en/12-taos-sql/07-function.md @@ -0,0 +1,1868 @@ +--- +title: Functions +--- + +## Aggregate Functions + +Aggregate query is supported in TDengine by following aggregate functions and selection functions. + +### COUNT + +``` +SELECT COUNT([*|field_name]) FROM tb_name [WHERE clause]; +``` + +**Description**:Get the number of rows or the number of non-null values in a table or a super table. + +**Return value type**:Long integer INT64 + +**Applicable column types**:All + +**Applicable table types**: table, super table, sub table + +**More explanation**: + +- Wildcard (\*) can be used to represent all columns, it's used to get the number of all rows +- The number of non-NULL values will be returned if this function is used on a specific column + +**Examples**: + +``` +taos> SELECT COUNT(*), COUNT(voltage) FROM meters; + count(*) | count(voltage) | +================================================ + 9 | 9 | +Query OK, 1 row(s) in set (0.004475s) + +taos> SELECT COUNT(*), COUNT(voltage) FROM d1001; + count(*) | count(voltage) | +================================================ + 3 | 3 | +Query OK, 1 row(s) in set (0.001075s) +``` + +### AVG + +``` +SELECT AVG(field_name) FROM tb_name [WHERE clause]; +``` + +**Description**:Get the average value of a column in a table or STable + +**Return value type**:Double precision floating number + +**Applicable column types**:Data types except for timestamp, binary, nchar and bool + +**Applicable table types**:table, STable + +**Examples**: + +``` +taos> SELECT AVG(current), AVG(voltage), AVG(phase) FROM meters; + avg(current) | avg(voltage) | avg(phase) | +==================================================================================== + 11.466666751 | 220.444444444 | 0.293333333 | +Query OK, 1 row(s) in set (0.004135s) + +taos> SELECT AVG(current), AVG(voltage), AVG(phase) FROM d1001; + avg(current) | avg(voltage) | avg(phase) | +==================================================================================== + 11.733333588 | 219.333333333 | 0.316666673 | +Query OK, 1 row(s) in set (0.000943s) +``` + +### TWA + +``` +SELECT TWA(field_name) FROM tb_name WHERE clause; +``` + +**Description**:Time weighted average on a specific column within a time range + +**Return value type**:Double precision floating number + +**Applicable column types**:Data types except for timestamp, binary, nchar and bool + +**Applicable table types**:table, STable + +**More explanations**: + +- From version 2.1.3.0, function TWA can be used on stable with `GROUP BY`, i.e. timelines generated by `GROUP BY tbname` on a STable. + +### IRATE + +``` +SELECT IRATE(field_name) FROM tb_name WHERE clause; +``` + +**Description**:instantaneous rate on a specific column. The last two samples in the specified time range are used to calculate instantaneous rate. If the last sample value is smaller, then only the last sample value is used instead of the difference between the last two sample values. + +**Return value type**:Double precision floating number + +**Applicable column types**:Data types except for timestamp, binary, nchar and bool + +**Applicable table types**:table, STable + +**More explanations**: + +- From version 2.1.3.0, function IRATE can be used on stble with `GROUP BY`, i.e. timelines generated by `GROUP BY tbname` on a STable. + +### SUM + +``` +SELECT SUM(field_name) FROM tb_name [WHERE clause]; +``` + +**Description**:The sum of a specific column in a table or STable + +**Return value type**:Double precision floating number or long integer + +**Applicable column types**:Data types except for timestamp, binary, nchar and bool + +**Applicable table types**:table, STable + +**Examples**: + +``` +taos> SELECT SUM(current), SUM(voltage), SUM(phase) FROM meters; + sum(current) | sum(voltage) | sum(phase) | +================================================================================ + 103.200000763 | 1984 | 2.640000001 | +Query OK, 1 row(s) in set (0.001702s) + +taos> SELECT SUM(current), SUM(voltage), SUM(phase) FROM d1001; + sum(current) | sum(voltage) | sum(phase) | +================================================================================ + 35.200000763 | 658 | 0.950000018 | +Query OK, 1 row(s) in set (0.000980s) +``` + +### STDDEV + +``` +SELECT STDDEV(field_name) FROM tb_name [WHERE clause]; +``` + +**Description**:Standard deviation of a specific column in a table or STable + +**Return value type**:Double precision floating number + +**Applicable column types**:Data types except for timestamp, binary, nchar and bool + +**Applicable table types**:table, STable (starting from version 2.0.15.1) + +**Examples**: + +``` +taos> SELECT STDDEV(current) FROM d1001; + stddev(current) | +============================ + 1.020892909 | +Query OK, 1 row(s) in set (0.000915s) +``` + +### LEASTSQUARES + +``` +SELECT LEASTSQUARES(field_name, start_val, step_val) FROM tb_name [WHERE clause]; +``` + +**Description**: The linear regression function of the specified column and the timestamp column (primary key), `start_val` is the initial value and `step_val` is the step value. + +**Return value type**: A string in the format of "(slope, intercept)" + +**Applicable column types**: Data types except for timestamp, binary, nchar and bool + +**Applicable table types**: table only + +**Examples**: + +``` +taos> SELECT LEASTSQUARES(current, 1, 1) FROM d1001; + leastsquares(current, 1, 1) | +===================================================== +{slop:1.000000, intercept:9.733334} | +Query OK, 1 row(s) in set (0.000921s) +``` + +### MODE + +``` +SELECT MODE(field_name) FROM tb_name [WHERE clause]; +``` + +**Description**:The value which has the highest frequency of occurrence. NULL is returned if there are multiple values which have highest frequency of occurrence. It can't be used on timestamp column or tags. + +**Return value type**:Same as the data type of the column being operated + +**Applicable column types**:Data types except for timestamp + +**More explanations**:Considering the number of returned result set is unpredictable, it's suggested to limit the number of unique values to 100,000, otherwise error will be returned. + +**Applicable version**:From version 2.6.0.0 + +**Examples**: + +``` +taos> select voltage from d002; + voltage | +======================== + 1 | + 1 | + 2 | + 19 | +Query OK, 4 row(s) in set (0.003545s) + +taos> select mode(voltage) from d002; + mode(voltage) | +======================== + 1 | +Query OK, 1 row(s) in set (0.019393s) +``` + +### HYPERLOGLOG + +``` +SELECT HYPERLOGLOG(field_name) FROM { tb_name | stb_name } [WHERE clause]; +``` + +**Description**:The cardinal number of a specific column is returned by using hyperloglog algorithm. + +**Return value type**:Integer + +**Applicable column types**:Any data type + +**More explanations**: The benefit of using hyperloglog algorithm is that the memory usage is under control when the data volume is huge. However, when the data volume is very small, the result may be not accurate, it's recommented to use `select count(data) from (select unique(col) as data from table)` in this case. + +**Applicable versions**:From version 2.6.0.0 + +**Examples**: + +``` +taos> select dbig from shll; + dbig | +======================== + 1 | + 1 | + 1 | + NULL | + 2 | + 19 | + NULL | + 9 | +Query OK, 8 row(s) in set (0.003755s) + +taos> select hyperloglog(dbig) from shll; + hyperloglog(dbig)| +======================== + 4 | +Query OK, 1 row(s) in set (0.008388s) +``` + +## Selection Functions + +When any selective function is used, timestamp column or tag columns including `tbname` can be specified to show that the selected value are from which rows. + +### MIN + +``` +SELECT MIN(field_name) FROM {tb_name | stb_name} [WHERE clause]; +``` + +**Description**:The minimum value of a specific column in a table or STable + +**Return value type**:Same as the data type of the column being operated + +**Applicable column types**:Data types except for timestamp, binary, nchar and bool + +**Applicable table types**:table, STable + +**Examples**: + +``` +taos> SELECT MIN(current), MIN(voltage) FROM meters; + min(current) | min(voltage) | +====================================== + 10.20000 | 218 | +Query OK, 1 row(s) in set (0.001765s) + +taos> SELECT MIN(current), MIN(voltage) FROM d1001; + min(current) | min(voltage) | +====================================== + 10.30000 | 218 | +Query OK, 1 row(s) in set (0.000950s) +``` + +### MAX + +``` +SELECT MAX(field_name) FROM { tb_name | stb_name } [WHERE clause]; +``` + +**Description**:The maximum value of a specific column of a table or STable + +**Return value type**:Same as the data type of the column being operated + +**Applicable column types**:Data types except for timestamp, binary, nchar and bool + +**Applicable table types**:table, STable + +**Examples**: + +``` +taos> SELECT MAX(current), MAX(voltage) FROM meters; + max(current) | max(voltage) | +====================================== + 13.40000 | 223 | +Query OK, 1 row(s) in set (0.001123s) + +taos> SELECT MAX(current), MAX(voltage) FROM d1001; + max(current) | max(voltage) | +====================================== + 12.60000 | 221 | +Query OK, 1 row(s) in set (0.000987s) +``` + +### FIRST + +``` +SELECT FIRST(field_name) FROM { tb_name | stb_name } [WHERE clause]; +``` + +**Description**:The first non-null value of a specific column in a table or STable + +**Return value type**:Same as the column being operated + +**Applicable column types**:Any data type + +**Applicable table types**:table, STable + +**More explanations**: + +- FIRST(\*) can be used to get the first non-null value of all columns +- NULL will be returned if all the values of the specified column are all NULL +- No result will NOT be returned if all the columns in the result set are all NULL + +**Examples**: + +``` +taos> SELECT FIRST(*) FROM meters; + first(ts) | first(current) | first(voltage) | first(phase) | +========================================================================================= +2018-10-03 14:38:04.000 | 10.20000 | 220 | 0.23000 | +Query OK, 1 row(s) in set (0.004767s) + +taos> SELECT FIRST(current) FROM d1002; + first(current) | +======================= + 10.20000 | +Query OK, 1 row(s) in set (0.001023s) +``` + +### LAST + +``` +SELECT LAST(field_name) FROM { tb_name | stb_name } [WHERE clause]; +``` + +**Description**:The last non-NULL value of a specific column in a table or STable + +**Return value type**:Same as the column being operated + +**Applicable column types**:Any data type + +**Applicable table types**:table, STable + +**More explanations**: + +- LAST(\*) can be used to get the last non-NULL value of all columns +- If the values of a column in the result set are all NULL, NULL is returned for that column; if all columns in the result are all NULL, no result will be returned. +- When it's used on a STable, if there are multiple values with the timestamp in the result set, one of them will be returned randomly and it's not guaranteed that the same value is returned if the same query is run multiple times. + +**Examples**: + +``` +taos> SELECT LAST(*) FROM meters; + last(ts) | last(current) | last(voltage) | last(phase) | +======================================================================================== +2018-10-03 14:38:16.800 | 12.30000 | 221 | 0.31000 | +Query OK, 1 row(s) in set (0.001452s) + +taos> SELECT LAST(current) FROM d1002; + last(current) | +======================= + 10.30000 | +Query OK, 1 row(s) in set (0.000843s) +``` + +### TOP + +``` +SELECT TOP(field_name, K) FROM { tb_name | stb_name } [WHERE clause]; +``` + +**Description**: The greatest _k_ values of a specific column in a table or STable. If a value has multiple occurrences in the column but counting all of them in will exceed the upper limit _k_, then a part of them will be returned randomly. + +**Return value type**:Same as the column being operated + +**Applicable column types**:Data types except for timestamp, binary, nchar and bool + +**Applicable table types**:table, STable + +**More explanations**: + +- _k_ must be in range [1,100] +- The timestamp associated with the selected values are returned too +- Can't be used with `FILL` + +**Examples**: + +``` +taos> SELECT TOP(current, 3) FROM meters; + ts | top(current, 3) | +================================================= +2018-10-03 14:38:15.000 | 12.60000 | +2018-10-03 14:38:16.600 | 13.40000 | +2018-10-03 14:38:16.800 | 12.30000 | +Query OK, 3 row(s) in set (0.001548s) + +taos> SELECT TOP(current, 2) FROM d1001; + ts | top(current, 2) | +================================================= +2018-10-03 14:38:15.000 | 12.60000 | +2018-10-03 14:38:16.800 | 12.30000 | +Query OK, 2 row(s) in set (0.000810s) +``` + +### BOTTOM + +``` +SELECT BOTTOM(field_name, K) FROM { tb_name | stb_name } [WHERE clause]; +``` + +**Description**:The least _k_ values of a specific column in a table or STable. If a value has multiple occurrences in the column but counting all of them in will exceed the upper limit _k_, then a part of them will be returned randomly. + +**Return value type**:Same as the column being operated + +**Applicable column types**: Data types except for timestamp, binary, nchar and bool + +**Applicable table types**: table, STable + +**More explanations**: + +- _k_ must be in range [1,100] +- The timestamp associated with the selected values are returned too +- Can't be used with `FILL` + +**Examples**: + +``` +taos> SELECT BOTTOM(voltage, 2) FROM meters; + ts | bottom(voltage, 2) | +=============================================== +2018-10-03 14:38:15.000 | 218 | +2018-10-03 14:38:16.650 | 218 | +Query OK, 2 row(s) in set (0.001332s) + +taos> SELECT BOTTOM(current, 2) FROM d1001; + ts | bottom(current, 2) | +================================================= +2018-10-03 14:38:05.000 | 10.30000 | +2018-10-03 14:38:16.800 | 12.30000 | +Query OK, 2 row(s) in set (0.000793s) +``` + +### PERCENTILE + +``` +SELECT PERCENTILE(field_name, P) FROM { tb_name } [WHERE clause]; +``` + +**Description**: The value whose rank in a specific column matches the specified percentage. If such a value matching the specified percentage doesn't exist in the column, an interpolation value will be returned. + +**Return value type**: Double precision floating point + +**Applicable column types**: Data types except for timestamp, binary, nchar and bool + +**Applicable table types**: table + +**More explanations**: _P_ is in range [0,100], when _P_ is 0, the result is same as using function MIN; when _P_ is 100, the result is same as function MAX. + +**Examples**: + +``` +taos> SELECT PERCENTILE(current, 20) FROM d1001; +percentile(current, 20) | +============================ + 11.100000191 | +Query OK, 1 row(s) in set (0.000787s) +``` + +### APERCENTILE + +``` +SELECT APERCENTILE(field_name, P[, algo_type]) +FROM { tb_name | stb_name } [WHERE clause] +``` + +**Description**: Similar to `PERCENTILE`, but a simulated result is returned + +**Return value type**: Double precision floating point + +**Applicable column types**: Data types except for timestamp, binary, nchar and bool + +**Applicable table types**: table, STable + +**More explanations** + +- _P_ is in range [0,100], when _P_ is 0, the result is same as using function MIN; when _P_ is 100, the result is same as function MAX. +- **algo_type** can only be input as `default` or `t-digest`, if it's not specified `default` will be used, i.e. `apercentile(column_name, 50)` is same as `apercentile(column_name, 50, "default")`. +- When `t-digest` is used, `t-digest` sampling is used to calculate. It can be used from version 2.2.0.0. + +**Nested query**: It can be used in both the outer query and inner query in a nested query. + +``` +taos> SELECT APERCENTILE(current, 20) FROM d1001; +apercentile(current, 20) | +============================ + 10.300000191 | +Query OK, 1 row(s) in set (0.000645s) + +taos> select apercentile (count, 80, 'default') from stb1; + apercentile (c0, 80, 'default') | +================================== + 601920857.210056424 | +Query OK, 1 row(s) in set (0.012363s) + +taos> select apercentile (count, 80, 't-digest') from stb1; + apercentile (c0, 80, 't-digest') | +=================================== + 605869120.966666579 | +Query OK, 1 row(s) in set (0.011639s) +``` + +### LAST_ROW + +``` +SELECT LAST_ROW(field_name) FROM { tb_name | stb_name }; +``` + +**Description**: The last row of a table or STable + +**Return value type**: Same as the column being operated + +**Applicable column types**: Any data type + +**Applicable table types**: table, STable + +**More explanations**: + +- When it's used against a STable, multiple rows with the same and largest timestamp may exist, in this case one of them is returned randomly and it's not guaranteed that the result is same if the query is run multiple times. +- Can't be used with `INTERVAL`. + +**Examples**: + +``` + taos> SELECT LAST_ROW(current) FROM meters; + last_row(current) | + ======================= + 12.30000 | + Query OK, 1 row(s) in set (0.001238s) + + taos> SELECT LAST_ROW(current) FROM d1002; + last_row(current) | + ======================= + 10.30000 | + Query OK, 1 row(s) in set (0.001042s) +``` + +### INTERP [From version 2.3.1] + +``` +SELECT INTERP(field_name) FROM { tb_name | stb_name } [WHERE where_condition] [ RANGE(timestamp1,timestamp2) ] [EVERY(interval)] [FILL ({ VALUE | PREV | NULL | LINEAR | NEXT})]; +``` + +**Description**: The value that matches the specified timestamp range is returned, if existing; or an interpolation value is returned. + +**Return value type**: same as the column being operated + +**Applicable column types**: Numeric data types + +**Applicable table types**: table, STable, nested query + +**More explanations** + +- `INTERP` is used to get the value that matches the specified time slice from a column. If no such value exists an interpolation value will be returned based on `FILL` parameter. +- The input data of `INTERP` is the value of the specified column, `where` can be used to filter the original data. If no `where` condition is specified then all original data is the input. +- The output time range of `INTERP` is specified by `RANGE(timestamp1,timestamp2)` parameter, with timestamp1<=timestamp2. timestamp1 is the starting point of the output time range and must be specified. timestamp2 is the ending point of the output time range and must be specified. If `RANGE` is not specified, then the timestamp of the first row that matches the filter condition is treated as timestamp1, the timestamp of the last row that matches the filter condition is treated as timestamp2. +- The number of rows in the result set of `INTERP` is determined by the parameter `EVERY`. Starting from timestamp1, one interpolation is performed for every time interval specified `EVERY` parameter. If `EVERY` parameter is not used, the time windows will be considered as no ending timestamp, i.e. there is only one time window from timestamp1. +- Interpolation is performed based on `FILL` parameter. No interpolation is performed if `FILL` is not used, that means either the original data that matches is returned or nothing is returned. +- `INTERP` can only be used to interpolate in single timeline. So it must be used with `group by tbname` when it's used on a STable. It can't be used with `GROUP BY` when it's used in the inner query of a nested query. +- The result of `INTERP` is not influenced by `ORDER BY TIMESTAMP`, which impacts the output order only.. + +**Examples**: Based on the `meters` schema used throughout the documents + +- Single point linear interpolation between "2017-07-14 18:40:00" and "2017-07-14 18:40:00: + +``` + taos> SELECT INTERP(current) FROM t1 RANGE('2017-7-14 18:40:00','2017-7-14 18:40:00') FILL(LINEAR); +``` + +- Get an original data every 5 seconds, no interpolation, between "2017-07-14 18:00:00" and "2017-07-14 19:00:00: + +``` + taos> SELECT INTERP(current) FROM t1 RANGE('2017-7-14 18:00:00','2017-7-14 19:00:00') EVERY(5s); +``` + +- Linear interpolation every 5 seconds between "2017-07-14 18:00:00" and "2017-07-14 19:00:00: + +``` + taos> SELECT INTERP(current) FROM t1 RANGE('2017-7-14 18:00:00','2017-7-14 19:00:00') EVERY(5s) FILL(LINEAR); +``` + +- Backward interpolation every 5 seconds + +``` + taos> SELECT INTERP(current) FROM t1 EVERY(5s) FILL(NEXT); +``` + +- Linear interpolation every 5 seconds between "2017-07-14 17:00:00" and "2017-07-14 20:00:00" + +``` + taos> SELECT INTERP(current) FROM t1 where ts >= '2017-07-14 17:00:00' and ts <= '2017-07-14 20:00:00' RANGE('2017-7-14 18:00:00','2017-7-14 19:00:00') EVERY(5s) FILL(LINEAR); +``` + +### INTERP [Prior to version 2.3.1] + +``` +SELECT INTERP(field_name) FROM { tb_name | stb_name } WHERE ts='timestamp' [FILL ({ VALUE | PREV | NULL | LINEAR | NEXT})]; +``` + +**Description**: The value of a specific column that matches the specified time slice + +**Return value type**: Same as the column being operated + +**Applicable column types**: Numeric data type + +**Applicable table types**: table, STable + +**More explanations**: + +- It can be used from version 2.0.15.0 +- Time slice must be specified. If there is no data matching the specified time slice, interpolation is performed based on `FILL` parameter. Conditions such as tags or `tbname` can be used `Where` clause can be used to filter data. +- The timestamp specified must be within the time range of the data rows of the table or STable. If it is beyond the valid time range, nothing is returned even with `FILL` parameter. +- `INTERP` can be used to query only single time point once. `INTERP` can be used with `EVERY` to get the interpolation value every time interval. +- **Examples**: + +``` + taos> SELECT INTERP(*) FROM meters WHERE ts='2017-7-14 18:40:00.004'; + interp(ts) | interp(current) | interp(voltage) | interp(phase) | + ========================================================================================== + 2017-07-14 18:40:00.004 | 9.84020 | 216 | 0.32222 | + Query OK, 1 row(s) in set (0.002652s) +``` + +If there is not any data corresponding to the specified timestamp, an interpolation value is returned if interpolation policy is specified by `FILL` parameter; or nothing is returned\ + +``` + taos> SELECT INTERP(*) FROM meters WHERE tbname IN ('d636') AND ts='2017-7-14 18:40:00.005'; + Query OK, 0 row(s) in set (0.004022s) + + taos> SELECT INTERP(*) FROM meters WHERE tbname IN ('d636') AND ts='2017-7-14 18:40:00.005' FILL(PREV); + interp(ts) | interp(current) | interp(voltage) | interp(phase) | + ========================================================================================== + 2017-07-14 18:40:00.005 | 9.88150 | 217 | 0.32500 | + Query OK, 1 row(s) in set (0.003056s) +``` + +Interpolation is performed every 5 milliseconds between `['2017-7-14 18:40:00', '2017-7-14 18:40:00.014']` + +``` + taos> SELECT INTERP(current) FROM d636 WHERE ts>='2017-7-14 18:40:00' AND ts<='2017-7-14 18:40:00.014' EVERY(5a); + ts | interp(current) | + ================================================= + 2017-07-14 18:40:00.000 | 10.04179 | + 2017-07-14 18:40:00.010 | 10.16123 | + Query OK, 2 row(s) in set (0.003487s) +``` + +### TAIL + +``` +SELECT TAIL(field_name, k, offset_val) FROM {tb_name | stb_name} [WHERE clause]; +``` + +**Description**: The next _k_ rows are returned after skipping the last `offset_val` rows, NULL values are not ignored. `offset_val` is optional parameter. When it's not specified, the last _k_ rows are returned. When `offset_val` is used, the effect is same as `order by ts desc LIMIT k OFFSET offset_val`. + +**Parameter value range**: k: [1,100] offset_val: [0,100] + +**Return value type**: Same as the column being operated + +**Applicable column types**: Any data type except form timestamp, i.e. the primary key + +**Applicable versions**: From version 2.6.0.0 + +**Examples**: + +``` +taos> select ts,dbig from tail2; + ts | dbig | +================================================== +2021-10-15 00:31:33.000 | 1 | +2021-10-17 00:31:31.000 | NULL | +2021-12-24 00:31:34.000 | 2 | +2022-01-01 08:00:05.000 | 19 | +2022-01-01 08:00:06.000 | NULL | +2022-01-01 08:00:07.000 | 9 | +Query OK, 6 row(s) in set (0.001952s) + +taos> select tail(dbig,2,2) from tail2; +ts | tail(dbig,2,2) | +================================================== +2021-12-24 00:31:34.000 | 2 | +2022-01-01 08:00:05.000 | 19 | +Query OK, 2 row(s) in set (0.002307s) +``` + +### UNIQUE + +``` +SELECT UNIQUE(field_name) FROM {tb_name | stb_name} [WHERE clause]; +``` + +**Description**: The values that occur the first time in the specified column. The effect is similar to `distinct` keyword, but it can also be used to match tags or timestamp. + +**Return value type**: Same as the column or tag being operated + +**Applicable column types**: Any data types except for timestamp + +**Applicable versions**: From version 2.6.0.0 + +**More explanations**: + +- It can be used against table or STable, but can't be used together with time window, like `interval`, `state_window` or `session_window` . +- Considering the number of result sets is unpredictable, it's suggested to limit the distinct values under 100,000 to control the memory usage, otherwise error will be returned. + +**Examples**: + +``` +taos> select ts,voltage from unique1; + ts | voltage | +================================================== +2021-10-17 00:31:31.000 | 1 | +2022-01-24 00:31:31.000 | 1 | +2021-10-17 00:31:31.000 | 1 | +2021-12-24 00:31:31.000 | 2 | +2022-01-01 08:00:01.000 | 19 | +2021-10-17 00:31:31.000 | NULL | +2022-01-01 08:00:02.000 | NULL | +2022-01-01 08:00:03.000 | 9 | +Query OK, 8 row(s) in set (0.003018s) + +taos> select unique(voltage) from unique1; +ts | unique(voltage) | +================================================== +2021-10-17 00:31:31.000 | 1 | +2021-10-17 00:31:31.000 | NULL | +2021-12-24 00:31:31.000 | 2 | +2022-01-01 08:00:01.000 | 19 | +2022-01-01 08:00:03.000 | 9 | +Query OK, 5 row(s) in set (0.108458s) +``` + +## Scalar functions + +### DIFF + +```sql +SELECT {DIFF(field_name, ignore_negative) | DIFF(field_name)} FROM tb_name [WHERE clause]; +``` + +**Description**: The different of each row with its previous row for a specific column. `ignore_negative` can be specified as 0 or 1, the default value is 1 if it's not specified. `1` means negative values are ignored. + +**Return value type**: Same as the column being operated + +**Applicable column types**: Data types except for timestamp, binary, nchar and bool + +**Applicable table types**: table, STable + +**More explanations**: + +- The number of result rows is the number of rows subtracted by one, no output for the first row +- From version 2.1.30, `DIFF` can be used on STable with `GROUP by tbname` +- From version 2.6.0, `ignore_negative` parameter is supported + +**Examples**: + +```sql +taos> SELECT DIFF(current) FROM d1001; + ts | diff(current) | +================================================= +2018-10-03 14:38:15.000 | 2.30000 | +2018-10-03 14:38:16.800 | -0.30000 | +Query OK, 2 row(s) in set (0.001162s) +``` + +### DERIVATIVE + +``` +SELECT DERIVATIVE(field_name, time_interval, ignore_negative) FROM tb_name [WHERE clause]; +``` + +**Description**: The derivative of a specific column. The time rage can be specified by parameter `time_interval`, the minimum allowed time range is 1 second (1s); the value of `ignore_negative` can be 0 or 1, 1 means negative values are ignored. + +**Return value type**: Double precision floating point + +**Applicable column types**: Data types except for timestamp, binary, nchar and bool + +**Applicable table types**: table, STable + +**More explanations**: + +- It is available from version 2.1.3.0, the number of result rows is the number of total rows in the time range subtracted by one, no output for the first row.\ +- It can be used together with `GROUP BY tbname` against a STable. + +**Examples**: + +``` +taos> select derivative(current, 10m, 0) from t1; + ts | derivative(current, 10m, 0) | +======================================================== + 2021-08-20 10:11:22.790 | 0.500000000 | + 2021-08-20 11:11:22.791 | 0.166666620 | + 2021-08-20 12:11:22.791 | 0.000000000 | + 2021-08-20 13:11:22.792 | 0.166666620 | + 2021-08-20 14:11:22.792 | -0.666666667 | +Query OK, 5 row(s) in set (0.004883s) +``` + +### SPREAD + +``` +SELECT SPREAD(field_name) FROM { tb_name | stb_name } [WHERE clause]; +``` + +**Description**: The difference between the max and the min of a specific column + +**Return value type**: Double precision floating point + +**Applicable column types**: Data types except for binary, nchar, and bool + +**Applicable table types**: table, STable + +**More explanations**: Can be used on a column of TIMESTAMP type, the result is the time range size. + +**Examples**: + +``` +taos> SELECT SPREAD(voltage) FROM meters; + spread(voltage) | +============================ + 5.000000000 | +Query OK, 1 row(s) in set (0.001792s) + +taos> SELECT SPREAD(voltage) FROM d1001; + spread(voltage) | +============================ + 3.000000000 | +Query OK, 1 row(s) in set (0.000836s) +``` + +### CEIL + +``` +SELECT CEIL(field_name) FROM { tb_name | stb_name } [WHERE clause]; +``` + +**Description**: The round up value of a specific column + +**Return value type**: Same as the column being used + +**Applicable data types**: Data types except for timestamp, binary, nchar, bool + +**Applicable table types**: table, STable + +**Applicable nested query**: inner query and outer query + +**More explanations**: + +- Can't be used on any tags of any type +- Arithmetic operation can be performed on the result of `ceil` function +- Can't be used with aggregate functions + +### FLOOR + +``` +SELECT FLOOR(field_name) FROM { tb_name | stb_name } [WHERE clause]; +``` + +**Description**: The round down value of a specific column + +**More explanations**: The restrictions are same as `CEIL` function. + +### ROUND + +``` +SELECT ROUND(field_name) FROM { tb_name | stb_name } [WHERE clause]; +``` + +**Description**: The round value of a specific column. + +**More explanations**: The restrictions are same as `CEIL` function. + +### CSUM + +```sql + SELECT CSUM(field_name) FROM { tb_name | stb_name } [WHERE clause] +``` + +**Description**: The cumulative sum of each row for a specific column. The number of output rows is same as that of the input rows. + +**Return value type**: Long integer for integers; Double for floating points. Timestamp is returned for each row. + +**Applicable data types**: Data types except for timestamp, binary, nchar, and bool + +**Applicable table types**: table, STable + +**Applicable nested query**: Inner query and Outer query + +**More explanations**: + +- Can't be used on tags when it's used on STable +- Arithmetic operation can't be performed on the result of `csum` function +- Can only be used with aggregate functions +- `Group by tbname` must be used together on a STable to force the result on a single timeline + +**Applicable versions**: From 2.3.0.x + +### MAVG + +```sql + SELECT MAVG(field_name, K) FROM { tb_name | stb_name } [WHERE clause] +``` + +**Description**: The moving average of continuous _k_ values of a specific column. If the number of input rows is less than _k_, nothing is returned. The applicable range is _k_ is [1,1000]. + +**Return value type**: Double precision floating point + +**Applicable data types**: Data types except for timestamp, binary, nchar, and bool + +**Applicable nested query**: Inner query and Outer query + +**Applicable table types**: table, STable + +**More explanations**: + +- Arithmetic operation can't be performed on the result of `MAVG`. +- Can only be used with data columns, can't be used with tags. +- Can't be used with aggregate functions. +- Must be used with `GROUP BY tbname` when it's used on a STable to force the result on each single timeline. + +**Applicable versions**: From 2.3.0.x + +### SAMPLE + +```sql + SELECT SAMPLE(field_name, K) FROM { tb_name | stb_name } [WHERE clause] +``` + +**Description**: _k_ sampling values of a specific column. The applicable range of _k_ is [1,10000] + +**Return value type**: Same as the column being operated plus the associated timestamp + +**Applicable data types**: Any data type except for tags of STable + +**Applicable table types**: table, STable + +**Applicable nested query**: Inner query and Outer query + +**More explanations**: + +- Arithmetic operation can't be operated on the result of `SAMPLE` function +- Must be used with `Group by tbname` when it's used on a STable to force the result on each single timeline + +**Applicable versions**: From 2.3.0.x + +### ASIN + +```sql +SELECT ASIN(field_name) FROM { tb_name | stb_name } [WHERE clause] +``` + +**Description**: The anti-sine of a specific column + +**Return value type**: Double if the input value is not NULL; or NULL if the input value is NULL + +**Applicable data types**: Data types except for timestamp, binary, nchar, bool + +**Applicable table types**: table, STable + +**Applicable nested query**: Inner query and Outer query + +**Applicable versions**: From 2.6.0.0 + +**More explanations**: + +- Can't be used with tags +- Can't be used with aggregate functions + +### ACOS + +```sql +SELECT ACOS(field_name) FROM { tb_name | stb_name } [WHERE clause] +``` + +**Description**: The anti-cosine of a specific column + +**Return value type**: Double if the input value is not NULL; or NULL if the input value is NULL + +**Applicable data types**: Data types except for timestamp, binary, nchar, bool + +**Applicable table types**: table, STable + +**Applicable nested query**: Inner query and Outer query + +**Applicable versions**: From 2.6.0.0 + +**More explanations**: + +- Can't be used with tags +- Can't be used with aggregate functions + +### ATAN + +```sql +SELECT ATAN(field_name) FROM { tb_name | stb_name } [WHERE clause] +``` + +**Description**: anti-tangent of a specific column + +**Description**: The anti-cosine of a specific column + +**Return value type**: Double if the input value is not NULL; or NULL if the input value is NULL + +**Applicable data types**: Data types except for timestamp, binary, nchar, bool + +**Applicable table types**: table, STable + +**Applicable nested query**: Inner query and Outer query + +**Applicable versions**: From 2.6.0.0 + +**More explanations**: + +- Can't be used with tags +- Can't be used with aggregate functions + +### SIN + +```sql +SELECT SIN(field_name) FROM { tb_name | stb_name } [WHERE clause] +``` + +**Description**: The sine of a specific column + +**Description**: The anti-cosine of a specific column + +**Return value type**: Double if the input value is not NULL; or NULL if the input value is NULL + +**Applicable data types**: Data types except for timestamp, binary, nchar, bool + +**Applicable table types**: table, STable + +**Applicable nested query**: Inner query and Outer query + +**Applicable versions**: From 2.6.0.0 + +**More explanations**: + +- Can't be used with tags +- Can't be used with aggregate functions + +### COS + +```sql +SELECT COS(field_name) FROM { tb_name | stb_name } [WHERE clause] +``` + +**Description**: The cosine of a specific column + +**Description**: The anti-cosine of a specific column + +**Return value type**: Double if the input value is not NULL; or NULL if the input value is NULL + +**Applicable data types**: Data types except for timestamp, binary, nchar, bool + +**Applicable table types**: table, STable + +**Applicable nested query**: Inner query and Outer query + +**Applicable versions**: From 2.6.0.0 + +**More explanations**: + +- Can't be used with tags +- Can't be used with aggregate functions + +### TAN + +```sql +SELECT TAN(field_name) FROM { tb_name | stb_name } [WHERE clause] +``` + +**Description**: The tangent of a specific column + +**Description**: The anti-cosine of a specific column + +**Return value type**: Double if the input value is not NULL; or NULL if the input value is NULL + +**Applicable data types**: Data types except for timestamp, binary, nchar, bool + +**Applicable table types**: table, STable + +**Applicable nested query**: Inner query and Outer query + +**Applicable versions**: From 2.6.0.0 + +**More explanations**: + +- Can't be used with tags +- Can't be used with aggregate functions + +### POW + +```sql +SELECT POW(field_name, power) FROM { tb_name | stb_name } [WHERE clause] +``` + +**Description**: The power of a specific column with `power` as the index + +**Return value type**: Double if the input value is not NULL; or NULL if the input value is NULL + +**Applicable data types**: Data types except for timestamp, binary, nchar, bool + +**Applicable table types**: table, STable + +**Applicable nested query**: Inner query and Outer query + +**Applicable versions**: From 2.6.0.0 + +**More explanations**: + +- Can't be used with tags +- Can't be used with aggregate functions + +### LOG + +```sql +SELECT LOG(field_name, base) FROM { tb_name | stb_name } [WHERE clause] +``` + +**Description**: The log of a specific with `base` as the radix + +**Return value type**: Double if the input value is not NULL; or NULL if the input value is NULL + +**Applicable data types**: Data types except for timestamp, binary, nchar, bool + +**Applicable table types**: table, STable + +**Applicable nested query**: Inner query and Outer query + +**Applicable versions**: From 2.6.0.0 + +**More explanations**: + +- Can't be used with tags +- Can't be used with aggregate functions + +### ABS + +```sql +SELECT ABS(field_name) FROM { tb_name | stb_name } [WHERE clause] +``` + +**Description**: The absolute of a specific column + +**Return value type**: UBIGINT if the input value is integer; DOUBLE if the input value is FLOAT/DOUBLE + +**Applicable data types**: Data types except for timestamp, binary, nchar, bool + +**Applicable table types**: table, STable + +**Applicable nested query**: Inner query and Outer query + +**Applicable versions**: From 2.6.0.0 + +**More explanations**: + +- Can't be used with tags +- Can't be used with aggregate functions + +### SQRT + +```sql +SELECT SQRT(field_name) FROM { tb_name | stb_name } [WHERE clause] +``` + +**Description**: The square root of a specific column + +**Return value type**: Double if the input value is not NULL; or NULL if the input value is NULL + +**Applicable data types**: Data types except for timestamp, binary, nchar, bool + +**Applicable table types**: table, STable + +**Applicable nested query**: Inner query and Outer query + +**Applicable versions**: From 2.6.0.0 + +**More explanations**: + +- Can't be used with tags +- Can't be used with aggregate functions + +### CAST + +```sql +SELECT CAST(expression AS type_name) FROM { tb_name | stb_name } [WHERE clause] +``` + +**Description**: It's used for type casting. The input parameter `expression` can be data columns, constants, scalar functions or arithmetic between them. Can't be used with tags, and can only be used in `select` clause. + +**Return value type**: The type specified by parameter `type_name` + +**Applicable data types**: + +- Parameter `expression` can be any data type except for JSON, more specifically it can be any of BOOL/TINYINT/SMALLINT/INT/BIGINT/FLOAT/DOUBLE/BINARY(M)/TIMESTAMP/NCHAR(M)/TINYINT UNSIGNED/SMALLINT UNSIGNED/INT UNSIGNED/BIGINT UNSIGNED +- The output data type specified by `type_name` can only be one of BIGINT/BINARY(N)/TIMESTAMP/NCHAR(N)/BIGINT UNSIGNED + +**Applicable versions**: From 2.6.0.0 + +**More explanations**: + +- Error will be reported for unsupported type casting +- NULL will be returned if the input value is NULL +- Some values of some supported data types may not be casted, below are known issues: + 1)When casting BINARY/NCHAR to BIGINT/BIGINT UNSIGNED, some characters may be treated as illegal, for example "a" may be converted to 0. + 2)There may be overflow when casting singed integer or TIMESTAMP to unsigned BIGINT + 3)There may be overflow when casting unsigned BIGINT to BIGINT + 4)There may be overflow when casting FLOAT/DOUBLE to BIGINT or UNSIGNED BIGINT + +### CONCAT + +```sql +SELECT CONCAT(str1|column1, str2|column2, ...) FROM { tb_name | stb_name } [WHERE clause] +``` + +**Description**: The concatenation result of two or more strings, the number of strings to be concatenated is at least 2 and at most 8 + +**Return value type**: Same as the columns being operated, BINARY or NCHAR; or NULL if all the input are NULL + +**Applicable data types**: The input data must be in either all BINARY or in all NCHAR; can't be used on tag columns + +**Applicable table types**: table, STable + +**Applicable nested query**: Inner query and Outer query + +**Applicable versions**: From 2.6.0.0 + +### CONCAT_WS + +``` +SELECT CONCAT_WS(separator, str1|column1, str2|column2, ...) FROM { tb_name | stb_name } [WHERE clause] +``` + +**Description**: The concatenation result of two or more strings with separator, the number of strings to be concatenated is at least 3 and at most 9 + +**Return value type**: Same as the columns being operated, BINARY or NCHAR; or NULL if all the input are NULL + +**Applicable data types**: The input data must be in either all BINARY or in all NCHAR; can't be used on tag columns + +**Applicable table types**: table, STable + +**Applicable nested query**: Inner query and Outer query + +**Applicable versions**: From 2.6.0.0 + +**More explanations**: + +- If the value of `separator` is NULL, the output is NULL. If the value of `separator` is not NULL but other input are all NULL, the output is empty string. + +### LENGTH + +``` +SELECT LENGTH(str|column) FROM { tb_name | stb_name } [WHERE clause] +``` + +**Description**: The length in bytes of a string + +**Return value type**: Integer + +**Applicable data types**: BINARY or NCHAR, can't be used on tags + +**Applicable table types**: table, STable + +**Applicable nested query**: Inner query and Outer query + +**Applicable versions**: From 2.6.0.0 + +**More explanations** + +- If the input value is NULL, the output is NULL too + +### CHAR_LENGTH + +``` +SELECT CHAR_LENGTH(str|column) FROM { tb_name | stb_name } [WHERE clause] +``` + +**Description**: The length in number of characters of a string + +**Return value type**: Integer + +**Applicable data types**: BINARY or NCHAR, can't be used on tags + +**Applicable table types**: table, STable + +**Applicable nested query**: Inner query and Outer query + +**Applicable versions**: From 2.6.0.0 + +**More explanations** + +- If the input value is NULL, the output is NULL too + +### LOWER + +``` +SELECT LOWER(str|column) FROM { tb_name | stb_name } [WHERE clause] +``` + +**Description**: Convert the input string to lower case + +**Return value type**: Same as input + +**Applicable data types**: BINARY or NCHAR, can't be used on tags + +**Applicable table types**: table, STable + +**Applicable nested query**: Inner query and Outer query + +**Applicable versions**: From 2.6.0.0 + +**More explanations** + +- If the input value is NULL, the output is NULL too + +### UPPER + +``` +SELECT UPPER(str|column) FROM { tb_name | stb_name } [WHERE clause] +``` + +**Description**: Convert the input string to upper case + +**Return value type**: Same as input + +**Applicable data types**: BINARY or NCHAR, can't be used on tags + +**Applicable table types**: table, STable + +**Applicable nested query**: Inner query and Outer query + +**Applicable versions**: From 2.6.0.0 + +**More explanations** + +- If the input value is NULL, the output is NULL too + +### LTRIM + +``` +SELECT LTRIM(str|column) FROM { tb_name | stb_name } [WHERE clause] +``` + +**Description**: Remove the left leading blanks of a string + +**Return value type**: Same as input + +**Applicable data types**: BINARY or NCHAR, can't be used on tags + +**Applicable table types**: table, STable + +**Applicable nested query**: Inner query and Outer query + +**Applicable versions**: From 2.6.0.0 + +**More explanations** + +- If the input value is NULL, the output is NULL too + +### RTRIM + +``` +SELECT RTRIM(str|column) FROM { tb_name | stb_name } [WHERE clause] +``` + +**Description**: Remove the right tailing blanks of a string + +**Return value type**: Same as input + +**Applicable data types**: BINARY or NCHAR, can't be used on tags + +**Applicable table types**: table, STable + +**Applicable nested query**: Inner query and Outer query + +**Applicable versions**: From 2.6.0.0 + +**More explanations** + +- If the input value is NULL, the output is NULL too + +### SUBSTR + +``` +SELECT SUBSTR(str,pos[,len]) FROM { tb_name | stb_name } [WHERE clause] +``` + +**Description**: The sub-string starting from `pos` with length of `len` from the original string `str` + +**Return value type**: Same as input + +**Applicable data types**: BINARY or NCHAR, can't be used on tags + +**Applicable table types**: table, STable + +**Applicable nested query**: Inner query and Outer query + +**Applicable versions**: From 2.6.0.0 + +**More explanations**: + +- If the input is NULL, the output is NULL +- Parameter `pos` can be an positive or negative integer; If it's positive, the starting position will be counted from the beginning of the string; if it's negative, the starting position will be counted from the end of the string. +- If `len` is not specified, it means from `pos` to the end. + +### Arithmetic Operations + +``` +SELECT field_name [+|-|*|/|%][Value|field_name] FROM { tb_name | stb_name } [WHERE clause]; +``` + +**Description**: The sum, difference, product, quotient, or remainder between one or more columns + +**Return value type**: Double precision floating point + +**Applicable column types**: Data types except for timestamp, binary, nchar, bool + +**Applicable table types**: table, STable + +**More explanations**: + +- Arithmetic operations can be performed on two or more columns, `()` can be used to control the precedence +- NULL doesn't participate the operation, if one of the operands is NULL then result is NULL + +**Examples**: + +``` +taos> SELECT current + voltage * phase FROM d1001; +(current+(voltage*phase)) | +============================ + 78.190000713 | + 84.540003240 | + 80.810000718 | +Query OK, 3 row(s) in set (0.001046s) +``` + +### STATECOUNT + +``` +SELECT STATECOUNT(field_name, oper, val) FROM { tb_name | stb_name } [WHERE clause]; +``` + +**Description**: The number of continuous rows satisfying the specified conditions for a specific column. The result is shown as an extra column for each row. If the specified condition is evaluated as true, the number is increased by 1; otherwise the number is reset to -1. If the input value is NULL, then the corresponding row is skipped. + +**Applicable parameter values**: + +- oper : Can be one of LT (lower than), GT (greater than), LE (lower than or euqal to), GE (greater than or equal to), NE (not equal to), EQ (equal to), the value is case insensitive +- val : Numeric types + +**Return value type**: Integer + +**Applicable data types**: Data types excpet for timestamp, binary, nchar, bool + +**Applicable table types**: table, STable + +**Applicable nested query**: Outer query only + +**Applicable versions**: From 2.6.0.0 + +**More explanations**: + +- Must be used together with `GROUP BY tbname` when it's used on a STable to force the result into each single timeline] +- Can't be used with window operation, like interval/state_window/session_window + +**Examples**: + +``` +taos> select ts,dbig from statef2; + ts | dbig | +======================================================== +2021-10-15 00:31:33.000000000 | 1 | +2021-10-17 00:31:31.000000000 | NULL | +2021-12-24 00:31:34.000000000 | 2 | +2022-01-01 08:00:05.000000000 | 19 | +2022-01-01 08:00:06.000000000 | NULL | +2022-01-01 08:00:07.000000000 | 9 | +Query OK, 6 row(s) in set (0.002977s) + +taos> select stateCount(dbig,GT,2) from statef2; +ts | dbig | statecount(dbig,gt,2) | +================================================================================ +2021-10-15 00:31:33.000000000 | 1 | -1 | +2021-10-17 00:31:31.000000000 | NULL | NULL | +2021-12-24 00:31:34.000000000 | 2 | -1 | +2022-01-01 08:00:05.000000000 | 19 | 1 | +2022-01-01 08:00:06.000000000 | NULL | NULL | +2022-01-01 08:00:07.000000000 | 9 | 2 | +Query OK, 6 row(s) in set (0.002791s) +``` + +### STATEDURATION + +``` +SELECT stateDuration(field_name, oper, val, unit) FROM { tb_name | stb_name } [WHERE clause]; +``` + +**Description**: The length of time range in which all rows satisfy the specified condition for a specific column. The result is shown as an extra column for each row. The length for the first row that satisfies the condition is 0. Next, if the condition is evaluated as true for a row, the time interval between current row and its previous row is added up to the time range; otherwise the time range length is reset to -1. If the value of the column is NULL, the corresponding row is skipped. + +**Applicable parameter values**: + +- oper : Can be one of LT (lower than), GT (greater than), LE (lower than or euqal to), GE (greater than or equal to), NE (not equal to), EQ (equal to), the value is case insensitive +- val : Numeric types +- unit: The unit of time interval, can be [1s, 1m, 1h], default is 1s + +**Return value type**: Integer + +**Applicable data types**: Data types excpet for timestamp, binary, nchar, bool + +**Applicable table types**: table, STable + +**Applicable nested query**: Outer query only + +**Applicable versions**: From 2.6.0.0 + +**More explanations**: + +- Must be used together with `GROUP BY tbname` when it's used on a STable to force the result into each single timeline] +- Can't be used with window operation, like interval/state_window/session_window + +**Examples**: + +``` +taos> select ts,dbig from statef2; + ts | dbig | +======================================================== +2021-10-15 00:31:33.000000000 | 1 | +2021-10-17 00:31:31.000000000 | NULL | +2021-12-24 00:31:34.000000000 | 2 | +2022-01-01 08:00:05.000000000 | 19 | +2022-01-01 08:00:06.000000000 | NULL | +2022-01-01 08:00:07.000000000 | 9 | +Query OK, 6 row(s) in set (0.002407s) + +taos> select stateDuration(dbig,GT,2) from statef2; +ts | dbig | stateduration(dbig,gt,2) | +=================================================================================== +2021-10-15 00:31:33.000000000 | 1 | -1 | +2021-10-17 00:31:31.000000000 | NULL | NULL | +2021-12-24 00:31:34.000000000 | 2 | -1 | +2022-01-01 08:00:05.000000000 | 19 | 0 | +2022-01-01 08:00:06.000000000 | NULL | NULL | +2022-01-01 08:00:07.000000000 | 9 | 2 | +Query OK, 6 row(s) in set (0.002613s) +``` + +## Time Functions + +From version 2.6.0.0, below time related functions can be used in TDengine. + +### NOW + +```sql +SELECT NOW() FROM { tb_name | stb_name } [WHERE clause]; +SELECT select_expr FROM { tb_name | stb_name } WHERE ts_col cond_operatior NOW(); +INSERT INTO tb_name VALUES (NOW(), ...); +``` + +**Description**: The current time of the client side system + +**Return value type**: TIMESTAMP + +**Applicable column types**: TIMESTAMP only + +**Applicable table types**: table, STable + +**More explanations**: + +- Add and Subtract operation can be performed, for example NOW() + 1s, the time unit can be: + b(nanosecond), u(microsecond), a(millisecond)), s(second), m(minute), h(hour), d(day), w(week) +- The precision of the returned timestamp is same as the precision set for the current data base in use + +**Examples**: + +```sql +taos> SELECT NOW() FROM meters; + now() | +========================== + 2022-02-02 02:02:02.456 | +Query OK, 1 row(s) in set (0.002093s) + +taos> SELECT NOW() + 1h FROM meters; + now() + 1h | +========================== + 2022-02-02 03:02:02.456 | +Query OK, 1 row(s) in set (0.002093s) + +taos> SELECT COUNT(voltage) FROM d1001 WHERE ts < NOW(); + count(voltage) | +============================= + 5 | +Query OK, 5 row(s) in set (0.004475s) + +taos> INSERT INTO d1001 VALUES (NOW(), 10.2, 219, 0.32); +Query OK, 1 of 1 row(s) in database (0.002210s) +``` + +### TODAY + +```sql +SELECT TODAY() FROM { tb_name | stb_name } [WHERE clause]; +SELECT select_expr FROM { tb_name | stb_name } WHERE ts_col cond_operatior TODAY()]; +INSERT INTO tb_name VALUES (TODAY(), ...); +``` + +**Description**: The timestamp of 00:00:00 of the client side system + +**Return value type**: TIMESTAMP + +**Applicable column types**: TIMESTAMP only + +**Applicable table types**: table, STable + +**More explanations**: + +- Add and Subtract operation can be performed, for example NOW() + 1s, the time unit can be: + b(nanosecond), u(microsecond), a(millisecond)), s(second), m(minute), h(hour), d(day), w(week) +- The precision of the returned timestamp is same as the precision set for the current data base in use + +**Examples**: + +```sql +taos> SELECT TODAY() FROM meters; + today() | +========================== + 2022-02-02 00:00:00.000 | +Query OK, 1 row(s) in set (0.002093s) + +taos> SELECT TODAY() + 1h FROM meters; + today() + 1h | +========================== + 2022-02-02 01:00:00.000 | +Query OK, 1 row(s) in set (0.002093s) + +taos> SELECT COUNT(voltage) FROM d1001 WHERE ts < TODAY(); + count(voltage) | +============================= + 5 | +Query OK, 5 row(s) in set (0.004475s) + +taos> INSERT INTO d1001 VALUES (TODAY(), 10.2, 219, 0.32); +Query OK, 1 of 1 row(s) in database (0.002210s) +``` + +### TIMEZONE + +```sql +SELECT TIMEZONE() FROM { tb_name | stb_name } [WHERE clause]; +``` + +**Description**: The timezone of the client side system + +**Return value type**: BINARY + +**Applicable column types**: None + +**Applicable table types**: table, STable + +**Examples**: + +```sql +taos> SELECT TIMEZONE() FROM meters; + timezone() | +================================= + UTC (UTC, +0000) | +Query OK, 1 row(s) in set (0.002093s) +``` + +### TO_ISO8601 + +```sql +SELECT TO_ISO8601(ts_val | ts_col) FROM { tb_name | stb_name } [WHERE clause]; +``` + +**Description**: The ISO8601 date/time format converted from a UNIX timestamp, plus the timezone of the client side system + +**Return value type**: BINARY + +**Applicable column types**: TIMESTAMP, constant or a column + +**Applicable table types**: table, STable + +**More explanations**: + +- If the input is UNIX timestamp constant, the precision of the returned value is determined by the digits of the input timestamp +- If the input is a column of TIMESTAMP type, The precision of the returned value is same as the precision set for the current data base in use + +**Examples**: + +```sql +taos> SELECT TO_ISO8601(1643738400) FROM meters; + to_iso8601(1643738400) | +============================== + 2022-02-02T02:00:00+0800 | + +taos> SELECT TO_ISO8601(ts) FROM meters; + to_iso8601(ts) | +============================== + 2022-02-02T02:00:00+0800 | + 2022-02-02T02:00:00+0800 | + 2022-02-02T02:00:00+0800 | +``` + +### TO_UNIXTIMESTAMP + +```sql +SELECT TO_UNIXTIMESTAMP(datetime_string | ts_col) FROM { tb_name | stb_name } [WHERE clause]; +``` + +**Description**: UNIX timestamp converted from a string of date/time format + +**Return value type**: Long integer + +**Applicable column types**: Constant or column of BINARY/NCHAR + +**Applicable table types**: table, STable + +**More explanations**: + +- The input string must be compatible with ISO8601/RFC3339 standard, 0 will be returned if the string can't be converted +- The precision of the returned timestamp is same as the precision set for the current data base in use + +**Examples**: + +```sql +taos> SELECT TO_UNIXTIMESTAMP("2022-02-02T02:00:00.000Z") FROM meters; +to_unixtimestamp("2022-02-02T02:00:00.000Z") | +============================================== + 1643767200000 | + +taos> SELECT TO_UNIXTIMESTAMP(col_binary) FROM meters; + to_unixtimestamp(col_binary) | +======================================== + 1643767200000 | + 1643767200000 | + 1643767200000 | +``` + +### TIMETRUNCATE + +```sql +SELECT TIMETRUNCATE(ts_val | datetime_string | ts_col, time_unit) FROM { tb_name | stb_name } [WHERE clause]; +``` + +**Description**: Truncate the input timestamp with unit specified by `time_unit`\ + +**Return value type**: TIMESTAMP\ + +**Applicable column types**: UNIX timestamp constant, string constant of date/time format, or a column of timestamp + +**Applicable table types**: table, STable + +**More explanations**: + +- Time unit specified by `time_unit` can be: + 1u(microsecond),1a(millisecond),1s(second),1m(minute),1h(hour),1d(day). +- The precision of the returned timestamp is same as the precision set for the current data base in use + +**Examples**: + +```sql +taos> SELECT TIMETRUNCATE(1643738522000, 1h) FROM meters; + timetruncate(1643738522000, 1h) | +=================================== + 2022-02-02 02:00:00.000 | +Query OK, 1 row(s) in set (0.001499s) + +taos> SELECT TIMETRUNCATE("2022-02-02 02:02:02", 1h) FROM meters; + timetruncate("2022-02-02 02:02:02", 1h) | +=========================================== + 2022-02-02 02:00:00.000 | +Query OK, 1 row(s) in set (0.003903s) + +taos> SELECT TIMETRUNCATE(ts, 1h) FROM meters; + timetruncate(ts, 1h) | +========================== + 2022-02-02 02:00:00.000 | + 2022-02-02 02:00:00.000 | + 2022-02-02 02:00:00.000 | +Query OK, 3 row(s) in set (0.003903s) +``` + +### TIMEDIFF + +```sql +SELECT TIMEDIFF(ts_val1 | datetime_string1 | ts_col1, ts_val2 | datetime_string2 | ts_col2 [, time_unit]) FROM { tb_name | stb_name } [WHERE clause]; +``` + +**Description**: The difference between two timestamps, and rounded to the time unit specified by `time_unit` + +**Return value type**: Long Integer + +**Applicable column types**: UNIX timestamp constant, string constant of date/time format, or a column of TIMESTAMP type + +**Applicable table types**: table, STable + +**More explanations**: + +- Time unit specified by `time_unit` can be: + 1u(microsecond),1a(millisecond),1s(second),1m(minute),1h(hour),1d(day). +- The precision of the returned timestamp is same as the precision set for the current data base in use + +**Examples**: + +```sql +taos> SELECT TIMEDIFF(1643738400000, 1643742000000) FROM meters; + timediff(1643738400000, 1643742000000) | +========================================= + 3600000 | +Query OK, 1 row(s) in set (0.002553s) +taos> SELECT TIMEDIFF(1643738400000, 1643742000000, 1h) FROM meters; + timediff(1643738400000, 1643742000000, 1h) | +============================================= + 1 | +Query OK, 1 row(s) in set (0.003726s) + +taos> SELECT TIMEDIFF("2022-02-02 03:00:00", "2022-02-02 02:00:00", 1h) FROM meters; + timediff("2022-02-02 03:00:00", "2022-02-02 02:00:00", 1h) | +============================================================= + 1 | +Query OK, 1 row(s) in set (0.001937s) + +taos> SELECT TIMEDIFF(ts_col1, ts_col2, 1h) FROM meters; + timediff(ts_col1, ts_col2, 1h) | +=================================== + 1 | +Query OK, 1 row(s) in set (0.001937s) +``` diff --git a/docs-en/12-taos-sql/08-interval.md b/docs-en/12-taos-sql/08-interval.md new file mode 100644 index 0000000000000000000000000000000000000000..5cc3fa8cb43749fd40b808699f82a8761525cc6a --- /dev/null +++ b/docs-en/12-taos-sql/08-interval.md @@ -0,0 +1,112 @@ +--- +sidebar_label: Interval +title: Aggregate by Time Window +--- + +Aggregate by time window is supported in TDengine. For example, each temperature sensor reports the temperature every second, the average temperature every 10 minutes can be retrieved by query with time window. +Window related clauses are used to divide the data set to be queried into subsets and then aggregate. There are three kinds of windows, time window, status window, and session window. There are two kinds of time windows, sliding window and flip time window. + +## Time Window + +`INTERVAL` clause is used to generate time windows of same time interval, `SLIDING` is used to specify the time step for which the time window moves forward. The query is performed on one time window each time, and the time window moves forward with time. When defining continuous query both the size of time window and the step of forward sliding time need to be specified. As shown in the figure blow, [t0s, t0e] ,[t1s , t1e], [t2s, t2e] are respectively the time range of three time windows on which continuous queries are executed. The time step for which time window moves forward is marked by `sliding time`. Query, filter and aggregate operations are executed on each time window respectively. When the time step specified by `SLIDING` is same as the time interval specified by `INTERVAL`, the sliding time window is actually a flip time window. + +![Time Window](/img/sql/timewindow-1.png) + +`INTERVAL` and `SLIDING` should be used with aggregate functions and selection functions. Below SQL statement is illegal because no aggregate or selection function is used with `INTERVAL`. + +``` +SELECT * FROM temp_tb_1 INTERVAL(1m); +``` + +The time step specified by `SLIDING` can't exceed the time interval specified by `INTERVAL`. Below SQL statement is illegal because the time length specified by `SLIDING` exceeds that specified by `INTERVAL`. + +``` +SELECT COUNT(*) FROM temp_tb_1 INTERVAL(1m) SLIDING(2m); +``` + +When the time length specified by `SLIDING` is same as that specified by `INTERVAL`, sliding window is actually flip window. The minimum time range specified by `INTERVAL` is 10 milliseconds (10a) prior to version 2.1.5.0. From version 2.1.5.0, the minimum time range by `INTERVAL` can be 1 microsecond (1u). However, if the DB precision is millisecond, the minimum time range is 1 millisecond (1a). Please be noted that the `timezone` parameter should be configured to same value in the `taos.cfg` configuration file on client side and server side. + +## Status Window + +In case of using integer, bool, or string to represent the device status at a moment, the continuous rows with same status belong to same status window. Once the status changes, the status window closes. As shown in the following figure,there are two status windows according to status, [2019-04-28 14:22:07,2019-04-28 14:22:10] and [2019-04-28 14:22:11,2019-04-28 14:22:12]. Status window is not applicable to STable for now. + +![Status Window](/img/sql/timewindow-3.png) + +`STATE_WINDOW` is used to specify the column based on which to define status window, for example: + +``` +SELECT COUNT(*), FIRST(ts), status FROM temp_tb_1 STATE_WINDOW(status); +``` + +## Session Window + +```sql +SELECT COUNT(*), FIRST(ts) FROM temp_tb_1 SESSION(ts, tol_val); +``` + +The primary key, i.e. timestamp, is used to determine which session window the row belongs to. If the time interval between two adjacent rows is within the time range specified by `tol_val`, they belong to same session window; otherwise they belong to two different time windows. As shown in the figure below, if the limit of time interval for session window is specified as 12 seconds, then the 6 rows in the figure constitutes 2 time windows, [2019-04-28 14:22:10,2019-04-28 14:22:30] and [2019-04-28 14:23:10,2019-04-28 14:23:30], because the time difference between 2019-04-28 14:22:30 and 2019-04-28 14:23:10 is 40 seconds, which exceeds the time interval limit of 12 seconds. + +![Session Window](/img/sql/timewindow-2.png) + +If the time interval between two continuous rows are within the time interval specified by `tol_value` they belong to the same session window; otherwise a new session window is started automatically. Session window is not supported on STable for now. + +## More On Window Aggregate + +### Syntax + +The full syntax of aggregate by window is as following: + +```sql +SELECT function_list FROM tb_name + [WHERE where_condition] + [SESSION(ts_col, tol_val)] + [STATE_WINDOW(col)] + [INTERVAL(interval [, offset]) [SLIDING sliding]] + [FILL({NONE | VALUE | PREV | NULL | LINEAR | NEXT})] + +SELECT function_list FROM stb_name + [WHERE where_condition] + [INTERVAL(interval [, offset]) [SLIDING sliding]] + [FILL({NONE | VALUE | PREV | NULL | LINEAR | NEXT})] + [GROUP BY tags] +``` + +### Restrictions + +- Aggregate functions and selection functions can be used in `function_list`, with each function having only one output, for example COUNT, AVG, SUM, STDDEV, LEASTSQUARES, PERCENTILE, MIN, MAX, FIRST, LAST. Functions having multiple output can't be used, for example DIFF or arithmetic operations. +- `LAST_ROW` can't be used together with window aggregate. +- Scalar functions, like CEIL/FLOOR, can't be used with window aggregate. +- `WHERE` clause can be used to specify the starting and ending time and other filter conditions +- `FILL` clause is used to specify how to fill when there is data missing in any window, including: \ + 1. NONE: No fill (the default fill mode) + 2. VALUE:Fill with a fixed value, which should be specified together, for example `FILL(VALUE, 1.23)` + 3. PREV:Fill with the previous non-NULL value, `FILL(PREV)` + 4. NULL:Fill with NULL, `FILL(NULL)` + 5. LINEAR:Fill with the closest non-NULL value, `FILL(LINEAR)` + 6. NEXT:Fill with the next non-NULL value, `FILL(NEXT)` + +:::info + +1. Huge volume of interpolation output may be returned using `FILL`, so it's recommended to specify the time range when using `FILL`. The maximum interpolation values that can be returned in single query is 10,000,000. +2. The result set is in the ascending order of timestamp in aggregate by time window aggregate. +3. If aggregate by window is used on STable, the aggregate function is performed on all the rows matching the filter conditions. If `GROUP BY` is not used in the query, the result set will be returned in ascending order of timestamp; otherwise the result set is not exactly in the order of ascending timestamp in each group. + ::: + +Aggregate by time window is also used in continuous query, please refer to [Continuous Query](/develop/continuous-query). + +## Examples + +The table of intelligent meters can be created like below SQL statement: + +```sql +CREATE TABLE meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT); +``` + +The average current, maximum current and median of current in every 10 minutes of the past 24 hours can be calculated using below SQL statement, with missing value filled with the previous non-NULL value. + +``` +SELECT AVG(current), MAX(current), APERCENTILE(current, 50) FROM meters + WHERE ts>=NOW-1d and ts<=now + INTERVAL(10m) + FILL(PREV); +``` diff --git a/docs-en/12-taos-sql/09-limit.md b/docs-en/12-taos-sql/09-limit.md new file mode 100644 index 0000000000000000000000000000000000000000..873e484fbb4731294d00df323f8e0d2cbc6b1d30 --- /dev/null +++ b/docs-en/12-taos-sql/09-limit.md @@ -0,0 +1,77 @@ +--- +title: Limits & Restrictions +--- + +## Naming Rules + +1. Only English characters, digits and underscore are allowed +2. Can't be started with digits +3. Case Insensitive without escape character "\`" +4. Identifier with escape character "\`" + To support more flexible table or column names, a new escape character "\`" is introduced. For more details please refer to [escape](/taos-sql/escape). + +## Password Rule + +The legal character set is `[a-zA-Z0-9!?$%^&*()_–+={[}]:;@~#|<,>.?/]`. + +## General Limits + +- Maximum length of database name is 32 bytes +- Maximum length of table name is 192 bytes, excluding the database name prefix and the separator +- Maximum length of each data row is 48K bytes from version 2.1.7.0 , before which the limit is 16K bytes. Please be noted that the upper limit includes the extra 2 bytes consumed by each column of BINARY/NCHAR type. +- Maximum of column name is 64. +- Maximum number of columns is 4096. There must be at least 2 columns, and the first column must be timestamp. +- Maximum length of tag name is 64. +- Maximum number of tags is 128. There must be at least 1 tag. The total length of tag values should not exceed 16K bytes. +- Maximum length of singe SQL statement is 1048576, i.e. 1 MB bytes. It can be configured in the parameter `maxSQLLength` in the client side, the applicable range is [65480, 1048576]. +- At most 4096 columns (or 1024 prior to 2.1.7.0) can be returned by `SELECT`, functions in the query statement may constitute columns. Error will be returned if the limit is exceeded. +- Maximum numbers of databases, STables, tables are only depending on the system resources. +- Maximum of database name is 32 bytes, can't include "." and special characters. +- Maximum replica number of database is 3 +- Maximum length of user name is 23 bytes +- Maximum length of password is 15 bytes +- Maximum number of rows depends on the storage space only. +- Maximum number of tables depends on the number of nodes only. +- Maximum number of databases depends on the number of nodes only. +- Maximum number of vnodes for single database is 64. + +## Restrictions of `GROUP BY` + +`GROUP BY` can be performed on tags and `TBNAME`. It can be performed on data columns too, with one restriction that only one column and the number of unique values on that column is lower than 100,000. Please be noted that `GROUP BY` can't be performed on float or double type. + +## Restrictions of `IS NOT NULL` + +`IS NOT NULL` can be used on any data type of columns. The non-empty string evaluation expression, i.e. `<\>""` can only be used on non-numeric data types. + +## Restrictions of `ORDER BY` + +- Only one `order by` is allowed for normal table and sub table. +- At most two `order by` are allowed for STable, and the second one must be `ts`. +- `order by tag` must be used with `group by tag` on same tag, this rule is also applicable to `tbname`. +- `order by column` must be used with `group by column` or `top/bottom` on same column. This rule is applicable to table and STable. +- `order by ts` is applicable to table and STable. +- If `order by ts` is used with `group by`, the result set is sorted using `ts` in each group. + +## Restrictions of Table/Column Names + +### Name Restrictions of Table/Column + +The name of a table or column can only be composed of ASCII characters, digits and underscore, while digit can't be used as the beginning. The maximum length is 192 bytes. Names are case insensitive. The name mentioned in this rule doesn't include the database name prefix and the separator. + +### Name Restrictions After Escaping + +To support more flexible table or column names, new escape character "`" is introduced in TDengine to avoid the conflict between table name and keywords and break the above restrictions for table name. The escape character is not counted in the length of table name. + +With escaping, the string inside escape characters are case sensitive, i.e. will not be converted to lower case internally. + +For example: +\`aBc\` and \`abc\` are different table or column names, but "abc" and "aBc" are same names because internally they are all "abc". + +:::note +The characters inside escape characters must be printable characters. + +::: + +### Applicable Versions + +Escape character "\`" is available from version 2.3.0.1. diff --git a/docs-en/12-taos-sql/10-json.md b/docs-en/12-taos-sql/10-json.md new file mode 100644 index 0000000000000000000000000000000000000000..60468f1e0fd75cc04cae8a91b0a1a22b9bd3600b --- /dev/null +++ b/docs-en/12-taos-sql/10-json.md @@ -0,0 +1,82 @@ +--- +title: JSON Type +--- + +## Syntax + +1. Tag of JSON type + + ```sql + create STable s1 (ts timestamp, v1 int) tags (info json); + + create table s1_1 using s1 tags ('{"k1": "v1"}'); + ``` + +2. -> Operator of JSON + + ```sql + select * from s1 where info->'k1' = 'v1'; + + select info->'k1' from s1; + ``` + +3. contains Operator of JSON + + ```sql + select * from s1 where info contains 'k2'; + + select * from s1 where info contains 'k1'; + ``` + +## Applicable Operations + +1. When JSON data type is used in `where`, `match/nmatch/between and/like/and/or/is null/is no null` can be used but `in` can't be used. + + ```sql + select * from s1 where info->'k1' match 'v*'; + + select * from s1 where info->'k1' like 'v%' and info contains 'k2'; + + select * from s1 where info is null; + + select * from s1 where info->'k1' is not null; + ``` + +2. Tag of JSON type can be used in `group by`, `order by`, `join`, `union all` and sub query, for example `group by json->'key'` + +3. `Distinct` can be used with tag of JSON type + + ```sql + select distinct info->'k1' from s1; + ``` + +4. Tag Operations + + The value of JSON tag can be altered. Please be noted that the full JSON will be override when doing this. + + The name of JSON tag can be altered. A tag of JSON type can't be added or removed. The column length of a JSON tag can't be changed. + +## Other Restrictions + +- JSON type can only be used for tag. There can be only one tag of JSON type, and it's exclusive to any other types of tag. + +- The maximum length of keys in JSON is 256 bytes, and key must be printable ASCII characters. The maximum total length of a JSON is 4,096 bytes. + +- JSON format: + + - The input string for JSON can be empty, i.e. "", "\t", or NULL, but can't be non-NULL string, bool or array. + - object can be {}, and the whole JSON is empty if so. Key can be "", and it's ignored if so. + - value can be int, double, string, boll or NULL, can't be array. Nesting is not allowed, that means value can't be another JSON. + - If one key occurs twice in JSON, only the first one is valid. + - Escape characters are not allowed in JSON. + +- NULL is returned if querying a key that doesn't exist in JSON. + +- If a tag of JSON is the result of inner query, it can't be parsed and queried in the outer query. + +For example, below SQL statements are not supported. + +```sql; +select jtag->'key' from (select jtag from STable); +select jtag->'key' from (select jtag from STable) where jtag->'key'>0; +``` diff --git a/docs-en/12-taos-sql/11-escape.md b/docs-en/12-taos-sql/11-escape.md new file mode 100644 index 0000000000000000000000000000000000000000..34ce9f7848a9d60811a23286a6675e8afa4f04fe --- /dev/null +++ b/docs-en/12-taos-sql/11-escape.md @@ -0,0 +1,30 @@ +--- +title: Escape Characters +--- + +Below table is the list of escape characters used in TDengine. + +| Escape Character | **Actual Meaning** | +| :--------------: | ------------------------ | +| `\'` | Single quote ' | +| `\"` | Double quote " | +| \n | Line Break | +| \r | Carriage Return | +| \t | tab | +| `\\` | Back Slash \ | +| `\%` | % see below for details | +| `\_` | \_ see below for details | + +:::note +Escape characters are available from version 2.4.0.4 . + +::: + +## Restrictions + +1. If there are escape characters in identifiers (database name, table name, column name) + - Identifier without ``: Error will be returned because identifier must be constituted of digits, ASCII characters or underscore and can't be started with digits + - Identifier quoted with ``: Original content is kept, no escaping +2. If there are escape characters in values + - The escape characters will be escaped as the above table. If the escape character doesn't match any supported one, the escape character "\" will be ignored. + - "%" and "\_" are used as wildcards in `like`. `\%` and `\_` should be used to represent literal "%" and "\_" in `like`,. If `\%` and `\_` are used out of `like` context, the evaluation result is "`\%`"and "`\_`", instead of "%" and "\_". diff --git a/docs-en/12-taos-sql/12-keywords.md b/docs-en/12-taos-sql/12-keywords.md new file mode 100644 index 0000000000000000000000000000000000000000..fa750300b71251e1172dba13f91d05822f9ac1f4 --- /dev/null +++ b/docs-en/12-taos-sql/12-keywords.md @@ -0,0 +1,48 @@ +--- +title: Keywords +--- + +There are about 200 keywords reserved by TDengine, they can't be used as the name of database, STable or table with either upper case, lower case or mixed case. + +**Keywords List** + +| | | | | | +| ----------- | ---------- | --------- | ---------- | ------------ | +| ABORT | CREATE | IGNORE | NULL | STAR | +| ACCOUNT | CTIME | IMMEDIATE | OF | STATE | +| ACCOUNTS | DATABASE | IMPORT | OFFSET | STATEMENT | +| ADD | DATABASES | IN | OR | STATE_WINDOW | +| AFTER | DAYS | INITIALLY | ORDER | STORAGE | +| ALL | DBS | INSERT | PARTITIONS | STREAM | +| ALTER | DEFERRED | INSTEAD | PASS | STREAMS | +| AND | DELIMITERS | INT | PLUS | STRING | +| AS | DESC | INTEGER | PPS | SYNCDB | +| ASC | DESCRIBE | INTERVAL | PRECISION | TABLE | +| ATTACH | DETACH | INTO | PREV | TABLES | +| BEFORE | DISTINCT | IS | PRIVILEGE | TAG | +| BEGIN | DIVIDE | ISNULL | QTIME | TAGS | +| BETWEEN | DNODE | JOIN | QUERIES | TBNAME | +| BIGINT | DNODES | KEEP | QUERY | TIMES | +| BINARY | DOT | KEY | QUORUM | TIMESTAMP | +| BITAND | DOUBLE | KILL | RAISE | TINYINT | +| BITNOT | DROP | LE | REM | TOPIC | +| BITOR | EACH | LIKE | REPLACE | TOPICS | +| BLOCKS | END | LIMIT | REPLICA | TRIGGER | +| BOOL | EQ | LINEAR | RESET | TSERIES | +| BY | EXISTS | LOCAL | RESTRICT | UMINUS | +| CACHE | EXPLAIN | LP | ROW | UNION | +| CACHELAST | FAIL | LSHIFT | RP | UNSIGNED | +| CASCADE | FILE | LT | RSHIFT | UPDATE | +| CHANGE | FILL | MATCH | SCORES | UPLUS | +| CLUSTER | FLOAT | MAXROWS | SELECT | USE | +| COLON | FOR | MINROWS | SEMI | USER | +| COLUMN | FROM | MINUS | SESSION | USERS | +| COMMA | FSYNC | MNODES | SET | USING | +| COMP | GE | MODIFY | SHOW | VALUES | +| COMPACT | GLOB | MODULES | SLASH | VARIABLE | +| CONCAT | GRANTS | NCHAR | SLIDING | VARIABLES | +| CONFLICT | GROUP | NE | SLIMIT | VGROUPS | +| CONNECTION | GT | NONE | SMALLINT | VIEW | +| CONNECTIONS | HAVING | NOT | SOFFSET | VNODES | +| CONNS | ID | NOTNULL | STable | WAL | +| COPY | IF | NOW | STableS | WHERE | diff --git a/docs-en/12-taos-sql/_category_.yml b/docs-en/12-taos-sql/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..74a3b6309e0a4ad35feb674f544c689ae1992299 --- /dev/null +++ b/docs-en/12-taos-sql/_category_.yml @@ -0,0 +1 @@ +label: TDengine SQL diff --git a/docs-en/12-taos-sql/index.md b/docs-en/12-taos-sql/index.md new file mode 100644 index 0000000000000000000000000000000000000000..611f2bf75eb2a234ae139ce65f2e78d356483bb7 --- /dev/null +++ b/docs-en/12-taos-sql/index.md @@ -0,0 +1,33 @@ +--- +title: TDengine SQL +description: "The syntax supported by TDengine SQL " +--- + +This section explains the syntax about operating database, table, STable, inserting data, selecting data, functions and some tips that can be used in TDengine SQL. It would be easier to understand with some fundamental knowledge of SQL. + +TDengine SQL is the major interface for users to write data into or query from TDengine. For users to easily use, syntax similar to standard SQL is provided. However, please be noted that TDengine SQL is not standard SQL. Besides, because TDengine doesn't provide the functionality of deleting time series data, corresponding statements are not provided in TDengine SQL. + +TDengine SQL doesn't support abbreviation for keywords, for example `DESCRIBE` can't be abbreviated as `DESC`. + +Syntax Specifications used in this chapter: + +- The content inside <\> needs to be input by the user, excluding <\> itself. +- \[ \] means optional input, excluding [] itself. +- | means one of a few options, excluding | itself. +- … means the item prior to it can be repeated multiple times. + +To better demonstrate the syntax, usage and rules of TAOS SQL, hereinafter it's assumed that there is a data set of meters. Assuming each meter collects 3 data: current, voltage, phase. The data model is as below: + +```sql +taos> DESCRIBE meters; + Field | Type | Length | Note | +================================================================================= + ts | TIMESTAMP | 8 | | + current | FLOAT | 4 | | + voltage | INT | 4 | | + phase | FLOAT | 4 | | + location | BINARY | 64 | TAG | + groupid | INT | 4 | TAG | +``` + +The data set includes the data collected by 4 meters, the corresponding table name is d1001, d1002, d1003, d1004 respectively based on the data model of TDengine. diff --git a/docs-en/13-operation/01-pkg-install.md b/docs-en/13-operation/01-pkg-install.md new file mode 100644 index 0000000000000000000000000000000000000000..a1aad1c3c96c52689e9f68509c27ccce574d2082 --- /dev/null +++ b/docs-en/13-operation/01-pkg-install.md @@ -0,0 +1,282 @@ +--- +title: Install & Uninstall +description: Install, Uninstall, Start, Stop and Upgrade +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +TDengine community version provides dev and rpm package for users to choose based on the system environment. deb supports Debian, Ubuntu and systems derived from them. rpm supports CentOS, RHEL, SUSE and systems derived from them. Furthermore, tar.gz package is provided for enterprise customers. + +## Install + + + + +1. Download deb package from official website, for example TDengine-server-2.4.0.7-Linux-x64.deb +2. In the directory where the package is located, execute below command + +```bash +$ sudo dpkg -i TDengine-server-2.4.0.7-Linux-x64.deb +(Reading database ... 137504 files and directories currently installed.) +Preparing to unpack TDengine-server-2.4.0.7-Linux-x64.deb ... +TDengine is removed successfully! +Unpacking tdengine (2.4.0.7) over (2.4.0.7) ... +Setting up tdengine (2.4.0.7) ... +Start to install TDengine... + +System hostname is: ubuntu-1804 + +Enter FQDN:port (like h1.taosdata.com:6030) of an existing TDengine cluster node to join +OR leave it blank to build one: + +Enter your email address for priority support or enter empty to skip: +Created symlink /etc/systemd/system/multi-user.target.wants/taosd.service → /etc/systemd/system/taosd.service. + +To configure TDengine : edit /etc/taos/taos.cfg +To start TDengine : sudo systemctl start taosd +To access TDengine : taos -h ubuntu-1804 to login into TDengine server + + +TDengine is installed successfully! +``` + + + + + +1. Download rpm package from official website, for example TDengine-server-2.4.0.7-Linux-x64.rpm; +2. In the directory where the package is located, execute below command + +``` +$ sudo rpm -ivh TDengine-server-2.4.0.7-Linux-x64.rpm +Preparing... ################################# [100%] +Updating / installing... + 1:tdengine-2.4.0.7-3 ################################# [100%] +Start to install TDengine... + +System hostname is: centos7 + +Enter FQDN:port (like h1.taosdata.com:6030) of an existing TDengine cluster node to join +OR leave it blank to build one: + +Enter your email address for priority support or enter empty to skip: + +Created symlink from /etc/systemd/system/multi-user.target.wants/taosd.service to /etc/systemd/system/taosd.service. + +To configure TDengine : edit /etc/taos/taos.cfg +To start TDengine : sudo systemctl start taosd +To access TDengine : taos -h centos7 to login into TDengine server + + +TDengine is installed successfully! +``` + + + + + +1. Download the tar.gz package, for example TDengine-server-2.4.0.7-Linux-x64.tar.gz; + 2、In the directory where the package is located, firstly decompress the file, then switch to the sub-directory generated in decompressing, i.e. "TDengine-enterprise-server-2.4.0.7/" in this example, and execute the `install.sh` script. + +```bash +$ tar xvzf TDengine-enterprise-server-2.4.0.7-Linux-x64.tar.gz +TDengine-enterprise-server-2.4.0.7/ +TDengine-enterprise-server-2.4.0.7/driver/ +TDengine-enterprise-server-2.4.0.7/driver/vercomp.txt +TDengine-enterprise-server-2.4.0.7/driver/libtaos.so.2.4.0.7 +TDengine-enterprise-server-2.4.0.7/install.sh +TDengine-enterprise-server-2.4.0.7/examples/ +... + +$ ll +total 43816 +drwxrwxr-x 3 ubuntu ubuntu 4096 Feb 22 09:31 ./ +drwxr-xr-x 20 ubuntu ubuntu 4096 Feb 22 09:30 ../ +drwxrwxr-x 4 ubuntu ubuntu 4096 Feb 22 09:30 TDengine-enterprise-server-2.4.0.7/ +-rw-rw-r-- 1 ubuntu ubuntu 44852544 Feb 22 09:31 TDengine-enterprise-server-2.4.0.7-Linux-x64.tar.gz + +$ cd TDengine-enterprise-server-2.4.0.7/ + + $ ll +total 40784 +drwxrwxr-x 4 ubuntu ubuntu 4096 Feb 22 09:30 ./ +drwxrwxr-x 3 ubuntu ubuntu 4096 Feb 22 09:31 ../ +drwxrwxr-x 2 ubuntu ubuntu 4096 Feb 22 09:30 driver/ +drwxrwxr-x 10 ubuntu ubuntu 4096 Feb 22 09:30 examples/ +-rwxrwxr-x 1 ubuntu ubuntu 33294 Feb 22 09:30 install.sh* +-rw-rw-r-- 1 ubuntu ubuntu 41704288 Feb 22 09:30 taos.tar.gz + +$ sudo ./install.sh + +Start to update TDengine... +Created symlink /etc/systemd/system/multi-user.target.wants/taosd.service → /etc/systemd/system/taosd.service. +Nginx for TDengine is updated successfully! + +To configure TDengine : edit /etc/taos/taos.cfg +To configure Taos Adapter (if has) : edit /etc/taos/taosadapter.toml +To start TDengine : sudo systemctl start taosd +To access TDengine : use taos -h ubuntu-1804 in shell OR from http://127.0.0.1:6060 + +TDengine is updated successfully! +Install taoskeeper as a standalone service +taoskeeper is installed, enable it by `systemctl enable taoskeeper` +``` + +:::info +Some configuration will be prompted for users to provide when install.sh is executing, the interactive mode can be disabled by executing `./install.sh -e no`. `./install -h` can show all parameters and detailed explanation. + +::: + + + + +:::note +When installing on the first node in the cluster, when "Enter FQDN:" is prompted, nothing needs to be provided. When installing on following nodes, when "Enter FQDN:" is prompted, the end point of the first dnode in the cluster can be input if it has been already up; or just ignore it and configure later after installation is done. + +::: + +## Uninstall + + + + +Deb package of TDengine can be uninstalled as below: + +```bash +$ sudo dpkg -r tdengine +(Reading database ... 137504 files and directories currently installed.) +Removing tdengine (2.4.0.7) ... +TDengine is removed successfully! + +``` + + + + + +RPM package of TDengine can be uninstalled as below: + +``` +$ sudo rpm -e tdengine +TDengine is removed successfully! +``` + + + + + +tar.gz package of TDengine can be uninstalled as below: + +``` +$ rmtaos +Nginx for TDengine is running, stopping it... +TDengine is removed successfully! + +taosKeeper is removed successfully! +``` + + + + +:::note + +- It's strongly suggested not to use multiple kinds of installation packages on single host TDengine +- After deb package is installed, if the installation directory is removed manually so that uninstall or reinstall can't succeed, it can be resolved by cleaning up TDengine package information as below command and then reinstalling. + +```bash + $ sudo rm -f /var/lib/dpkg/info/tdengine* +``` + +- After rpm package is installed, if the installation directory is removed manually so that uninstall or reinstall can't succeed, it can be resolved by cleaning up TDengine package information as below command and then reinstalling. + +```bash + $ sudo rpm -e --noscripts tdengine +``` + +::: + +## Installation Directory + +TDengine is installed at /usr/local/taos if successful. + +```bash +$ cd /usr/local/taos +$ ll +$ ll +total 28 +drwxr-xr-x 7 root root 4096 Feb 22 09:34 ./ +drwxr-xr-x 12 root root 4096 Feb 22 09:34 ../ +drwxr-xr-x 2 root root 4096 Feb 22 09:34 bin/ +drwxr-xr-x 2 root root 4096 Feb 22 09:34 cfg/ +lrwxrwxrwx 1 root root 13 Feb 22 09:34 data -> /var/lib/taos/ +drwxr-xr-x 2 root root 4096 Feb 22 09:34 driver/ +drwxr-xr-x 10 root root 4096 Feb 22 09:34 examples/ +drwxr-xr-x 2 root root 4096 Feb 22 09:34 include/ +lrwxrwxrwx 1 root root 13 Feb 22 09:34 log -> /var/log/taos/ +``` + +During the installation process: + +- Configuration directory, data directory, and log directory are created automatically if they don't exist +- The default configuration file is located at /etc/taos/taos.cfg, which is a copy of /usr/local/taos/cfg/taos.cfg if not existing +- The default data directory is /var/lib/taos, which is a soft link to /usr/local/taos/data +- The default log directory is /var/log/taos, which is a soft link to /usr/local/taos/log +- The executables at /usr/local/taos/bin are linked to /usr/bin +- The DLL files at /usr/local/taos/driver are linked to /usr/lib +- The header files at /usr/local/taos/include are linked to /usr/include + +:::note + +- When TDengine is uninstalled, the configuration /etc/taos/taos.cfg, data directory /var/lib/taos, log directory /var/log/taos are kept. They can be deleted manually with caution because data can't be recovered once +- When reinstalling TDengine, if the default configuration file /etc/taos/taos.cfg exists, it will be kept and the configuration file in the installation package will be renamed to taos.cfg.orig and stored at /usr/local/taos/cfg to be used as configuration sample. Otherwise the configuration file in the installation package will be installed to /etc/taos/taos.cfg and used. + +## Start and Stop + +Linux system services `systemd`, `systemctl` or `service` is used to start, stop and restart TDengine. The server process of TDengine is `taosd`, which is started automatically after the Linux system is started. System operator can use `systemd`, `systemctl` or `service` to start, stop or restart TDengine server. + +For example, if using `systemctl` , the commands to start, stop, restart and check TDengine server are as below: + +- Start server:`systemctl start taosd` + +- Stop server:`systemctl stop taosd` + +- Restart server:`systemctl restart taosd` + +- Check server status:`systemctl status taosd` + +From version 2.4.0.0, a new independent component named as `taosAdapter` has been included in TDengine. `taosAdapter` should be started and stopped using `systemctl`. + +If the server process is OK, the output of `systemctl status` is like below: + +``` +Active: active (running) +``` + +Otherwise, the output is as below: + +``` +Active: inactive (dead) +``` + +## Upgrade + +There are two aspects in upgrade operation: upgrade installation package and upgrade a running server. + +Upgrading package should follow the steps mentioned previously to firstly uninstall old version then install new version. + +Upgrading a running server is much more complex. Firstly please check the version number of old version and new version. The version number of TDengine consists of 4 sections, only the first 3 section match can the old version be upgraded to the new version. The steps of upgrading a running server are as below: + +- Stop inserting data +- Make sure all data persisted into disk +- Stop the cluster of TDengine +- Uninstall old version and install new version +- Start the cluster of TDengine +- Make some simple queries to make sure no data loss +- Make some simple data insertion to make sure the cluster works well +- Restore business data + +:::warning +TDengine doesn't guarantee any lower version is compatible with the data generated by a higher version, so it's never recommended to downgrade the version. + +::: diff --git a/docs-en/13-operation/02-planning.mdx b/docs-en/13-operation/02-planning.mdx new file mode 100644 index 0000000000000000000000000000000000000000..35a34aebc088c233ed9fc39723e8890ebc56e124 --- /dev/null +++ b/docs-en/13-operation/02-planning.mdx @@ -0,0 +1,82 @@ +--- +title: Resource Planning +--- + +The computing and storage resources need to be planned if using TDengine to build an IoT platform. How to plan the CPU, memory and disk required will be described in this chapter. + +## Memory Requirement of Server Side + +The number of vgroups created for each database is same as the number of CPU cores by default and can be configured by parameter `maxVgroupsPerDb`, each vnode in a vgroup stores one replica. Each vnode consumes fixed size of memory, i.e. `blocks` \* `cache`. Besides, some memory is required for tag values associated with each table. A fixed amount of memory is required for each cluster. So, the memory required for each DB can be calculated using below formula: + +``` +Database Memory Size = maxVgroupsPerDb * replica * (blocks * cache + 10MB) + numOfTables * (tagSizePerTable + 0.5KB) +``` + +For example, assuming the default value of `maxVgroupPerDB` is 64, the default value of `cache` 16M, the default value of `blocks` is 6, there are 100,000 tables in a DB, the replica number is 1, total length of tag values is 256 bytes, the total memory required for this DB is: 64 \* 1 \* (16 \* 6 + 10) + 100000 \* (0.25 + 0.5) / 1000 = 6792M. + +In real operation of TDengine, we are more concerned about the memory used by each TDengine server process `taosd`. + +``` + taosd_memory = vnode_memory + mnode_memory + query_memory +``` + +In the above formula: + +1. "vnode_memory" of a `taosd` process is the memory used by all vnodes hosted by this `taosd` process. It can be roughly calculated by firstly adding up the total memory of all DBs whose memory usage can be derived according to the formula mentioned previously then dividing by number of dnodes and multiplying the number of replicas. + +``` + vnode_memory = sum(Database memory) / number_of_dnodes \* replica +``` + +2. "mnode_memory" of a `taosd` process is the memory consumed by a mnode. If there is one (and only one) mnode hosted in a `taosd` process, the memory consumed by "mnode" is "0.2KB \* the total number of tables in the cluster". + +3. "query_memory" is the memory used when processing query requests. Each ongoing query consumes at least "0.2 KB \* total number of involved tables". + +Please be noted that the above formulas can only be used to estimate the minimum memory requirement, instead of maximum memory usage. In a real production environment, it's better to preserve some redundance beyond the estimated minimum memory requirement. If memory is abundant, it's suggested to increase the value of parameter `blocks` to speed up data insertion and data query. + +## Memory Requirement of Client Side + +The client programs use TDengine client driver `taosc` to connect to the server side, there is also memory requirement for a client program. + +The memory consumed by a client program is mainly about the SQL statements for data insertion, caching of table metadata, and some internal use. Assuming maximum number of tables is N (the memory consumed by the metadata of each table is 256 bytes), maximum number of threads for parallel insertion is T, maximum length of a SQL statement is S (normally 1 MB), the memory required by a client program can be estimated using below formula: + +``` +M = (T * S * 3 + (N / 4096) + 100) +``` + +For example, if the number of parallel data insertion threads is 100, total number of tables is 10,000,000, then minimum memory requirement of a client program is: + +``` +100 * 3 + (10000000 / 4096) + 100 = 2741 (MBytes) +``` + +So, at least 3GB needs to be reserved for such a client. + +## CPU Requirement + +The CPU resources required depend on two aspects: + +- **Data Insertion** Each dnode of TDengine can process at least 10,000 insertion requests in one second, while each insertion request can have multiple rows. The computing resource consumed between inserting 1 row one time and inserting 10 rows one time is very small. So, the more the rows to insert one time, the higher the efficiency. Inserting in bach also exposes requirement for the client side which needs to cache rows and insert in batch once the cached rows reaches a threshold. +- **Data Query** High efficiency query is provided in TDengine, but it's hard to estimate the CPU resource required because the queries used in different use cases and the frequency of queries vary significantly. It can only be verified with the query statements, query frequency, data size to be queried, etc provided by user. + +In short words, the CPU resource required for data insertion can be estimated but it's hard to do so for query use cases. In real operation, it's suggested to control CPU usage below 50%. If this threshold is exceeded, it's a reminder for system operator to add more nodes in the cluster to expand resources. + +## Disk Requirement + +The compression ratio in TDengine is much higher than that in RDBMS. In most cases, the compression ratio in TDengine is bigger than 5, or even 10 in some cases, depending on the characteristics of the original data. The data size before compression can be calculated based on below formula: + +``` +Raw DataSize = numOfTables * rowSizePerTable * rowsPerTable +``` + +For example, there are 10,000,000 meters, while each meter collects data every 15 minutes and the data size of each collection si 128 bytes, so the raw data size of one year is: 10000000 \* 128 \* 24 \* 60 / 15 \* 365 = 44.8512(TB). Assuming compression ratio is 5, the actual disk size is: 44.851 / 5 = 8.97024(TB). + +Parameter `keep` can be used to set how long the data will be kept on disk. To further reduce storage cost, multiple storage levels can be enabled in TDengine, with the coldest data stored on the cheapest storage device, and this is transparent to application programs. + +To increase the performance, multiple disks can be setup for parallel data reading or data inserting. Please be noted that expensive disk array is not necessary because replications are used in TDengine to provide high availability. + +## Number of Hosts + +A host can be either physical or virtual. The total memory, total CPU, total disk required can be estimated according to the formulas mentioned previously. Then, according to the system resources that a single host can provide, assuming all hosts are same in resources, the number of hosts can be derived easily. + +**Quick Estimation for CPU, Memory and Disk** Please refer to [Resource Estimate](https://www.taosdata.com/config/config.html). diff --git a/docs-en/13-operation/03-tolerance.md b/docs-en/13-operation/03-tolerance.md new file mode 100644 index 0000000000000000000000000000000000000000..367474cddb7395ea84a4a33623d1643e487f9d09 --- /dev/null +++ b/docs-en/13-operation/03-tolerance.md @@ -0,0 +1,29 @@ +--- +sidebar_label: Fault Tolerance +title: Fault Tolerance & Disaster Recovery +--- + +## Fault Tolerance + +TDengine uses **WAL**, i.e. Write Ahead Log, to achieve fault tolerance and high reliability. + +When a data block is received by TDengine, the original data block is firstly written into WAL. The log in WAL will be deleted only after the data has been written into data files in the database. Data can be recovered from WAL in case the server is stopped abnormally due to any reason and then restarted. + +There are 2 configuration parameters related to WAL: + +- walLevel:0:wal is disabled; 1:wal is enabled without fsync; 2:wal is enabled with fsync. +- fsync:only valid when walLevel is set to 2, it specified the interval of invoking fsync. If set to 0, it means fsync is invoked immediately once WAL is written. + +To achieve absolutely no data loss, walLevel needs to be set to 2 and fsync needs to be set to 1. The penalty is the performance of data ingestion downgrades. However, if the concurrent threads of data insertion on the client side can reach a big enough number, for example 50, the data ingestion performance would be still good enough, our verification shows that the drop is only 30% compared to fsync is set to 3,000 milliseconds. + +## Disaster Recovery + +TDengine uses replications to provide high availability and disaster recovery capability. + +TDengine cluster is managed by mnode. To make sure the high availability of mnode, multiple replicas can be configured by system parameter `numOfMnodes`. The data replication between mnode replicas is in synchronous way to guarantee the metadata consistency. + +The number of replicas for time series data in TDengine is associated with each database, there can be a lot of databases in a cluster while each database can be configured with a different number of replicas. When creating a database, parameter `replica` is used to configure the number of replications. To achieve high availability, `replica` needs to be higher than 1. + +The number of dnodes in a TDengine cluster must NOT be lower than the number of replicas for any database, otherwise it would fail when trying to create table. + +As long as the dnodes of a TDengine cluster are deployed on different physical machines and replica number is set to bigger than 1, high availability can be achieved without any other assistance. If dnodes of TDengine cluster are deployed in geographically different data centers, disaster recovery can be achieved too. diff --git a/docs-en/13-operation/06-admin.md b/docs-en/13-operation/06-admin.md new file mode 100644 index 0000000000000000000000000000000000000000..1ca0dfeaf4a4b0b4c597e1a5ec6ece20224e2dba --- /dev/null +++ b/docs-en/13-operation/06-admin.md @@ -0,0 +1,50 @@ +--- +title: User Management +--- + +System operator can use TDengine CLI `taos` to create or remove user or change password. The SQL command is as low: + +## Create User + +```sql +CREATE USER PASS <'password'>; +``` + +When creating a user and specifying the user name and password, password needs to be quoted using single quotes. + +## Drop User + +```sql +DROP USER ; +``` + +Drop a user can only be performed by root. + +## Change Password + +```sql +ALTER USER PASS <'password'>; +``` + +To keep the case of the password when changing password, password needs to be quoted using single quotes. + +## Change Privilege + +```sql +ALTER USER PRIVILEGE ; +``` + +The privileges that can be changed to are `read` or `write` without single quotes. + +Note:there is another privilege `super`, which not allowed to be authorized to any user. + +## Show Users + +```sql +SHOW USERS; +``` + +:::note +In SQL syntax, `< >` means the part that needs to be input by user, excluding the `< >` itself. + +::: diff --git a/docs-en/13-operation/07-import.md b/docs-en/13-operation/07-import.md new file mode 100644 index 0000000000000000000000000000000000000000..befca38652abadca60b62721754de7ab718f65ea --- /dev/null +++ b/docs-en/13-operation/07-import.md @@ -0,0 +1,61 @@ +--- +title: Data Import +--- + +There are multiple ways of importing data provided byTDengine: import with script, import from data file, import using `taosdump`. + +## Import Using Script + +TDengine CLI `taos` supports `source ` command for executing the SQL statements in the file in batch. The SQL statements for creating databases, creating tables, and inserting rows can be written in single file with one statement on each line, then the file can be executed using `source` command in TDengine CLI `taos` to execute the SQL statements in order and in batch. In the script file, any line beginning with "#" is treated as comments and ignored silently. + +## Import from Data File + +In TDengine CLI, data can be imported from a CSV file into an existing table. The data in single CSV must belong to same table and must be consistent with the schema of that table. The SQL statement is as below: + +```sql +insert into tb1 file 'path/data.csv'; +``` + +:::note +If there is description in the first line of a CSV file, please remove it before importing. If there is no value for a column, please use `NULL` without quotes. + +::: + +For example, there is a sub table d1001 whose schema is as below: + +```sql +taos> DESCRIBE d1001 + Field | Type | Length | Note | +================================================================================= + ts | TIMESTAMP | 8 | | + current | FLOAT | 4 | | + voltage | INT | 4 | | + phase | FLOAT | 4 | | + location | BINARY | 64 | TAG | + groupid | INT | 4 | TAG | +``` + +The format of the CSV file to be imported, data.csv, is as below: + +```csv +'2018-10-04 06:38:05.000',10.30000,219,0.31000 +'2018-10-05 06:38:15.000',12.60000,218,0.33000 +'2018-10-06 06:38:16.800',13.30000,221,0.32000 +'2018-10-07 06:38:05.000',13.30000,219,0.33000 +'2018-10-08 06:38:05.000',14.30000,219,0.34000 +'2018-10-09 06:38:05.000',15.30000,219,0.35000 +'2018-10-10 06:38:05.000',16.30000,219,0.31000 +'2018-10-11 06:38:05.000',17.30000,219,0.32000 +'2018-10-12 06:38:05.000',18.30000,219,0.31000 +``` + +Then, below SQL statement can be used to import data from file "data.csv", assuming the file is located under the home directory of current Linux user. + +```sql +taos> insert into d1001 file '~/data.csv'; +Query OK, 9 row(s) affected (0.004763s) +``` + +## Import using taosdump + +A convenient tool for importing and exporting data is provided by TDengine, `taosdump`, which can used to export data from one TDengine cluster and import into another one. For the details of using `taosdump` please refer to [Tool for exporting and importing data: taosdump](/reference/taosdump). diff --git a/docs-en/13-operation/08-export.md b/docs-en/13-operation/08-export.md new file mode 100644 index 0000000000000000000000000000000000000000..fa9625a7c5f6b0e6706d726bff410cee647286bb --- /dev/null +++ b/docs-en/13-operation/08-export.md @@ -0,0 +1,19 @@ +--- +title: Data Export +--- + +There are two ways of exporting data from a TDengine cluster, one is SQL statement in TDengine CLI, the other one is `taosdump`. + +## Export Using SQL + +If you want to export the data of a table or a STable, please execute below SQL statement in TDengine CLI. + +```sql +select * from >> data.csv; +``` + +The data of table or STable specified by `tb_name` will be exported into a file named `data.csv` in CSV format. + +## Export Using taosdump + +With `taosdump`, you can choose to export the data of all databases, a database, a table or a STable, you can also choose export the data within a time range, or even only export the schema definition of a table. For the details of using `taosdump` please refer to [Tool for exporting and importing data: taosdump](/reference/taosdump). diff --git a/docs-en/13-operation/09-status.md b/docs-en/13-operation/09-status.md new file mode 100644 index 0000000000000000000000000000000000000000..3f3c6c9f1e86f9f33bafc7edfd79bebb175871cc --- /dev/null +++ b/docs-en/13-operation/09-status.md @@ -0,0 +1,54 @@ +--- +sidebar_label: Connections & Tasks +title: Manage Connections and Query Tasks +--- + +System operator can use TDengine CLI to show the connections, ongoing queries, stream computing, and can close connection or stop ongoing query task or stream computing. + +## Show Connections + +```sql +SHOW CONNECTIONS; +``` + +One column of the output of the above SQL command is "ip:port", which is the end point of the client. + +## Close Connections Forcedly + +```sql +KILL CONNECTION ; +``` + +In the above SQL command, `connection-id` is from the first column of the output of `SHOW CONNECTIONS`. + +## Show Ongoing Queries + +```sql +SHOW QUERIES; +``` + +The first column of the output is query ID, which is composed of the corresponding connection ID and the sequence number of the current query task started on this connection, in format of "connection-id:query-no". + +## Close Queries Forcedly + +```sql +KILL QUERY ; +``` + +In the above SQL command, `query-id` is from the first column of the output of `SHOW QUERIES `. + +## Show Continuous Query + +```sql +SHOW STREAMS; +``` + +The first column of the output is stream ID, which is composed of the connection ID and the sequence number of the current stream started on this connection, in the format of "connection-id:stream-no". + +## Close Continuous Query Forcedly + +```sql +KILL STREAM ; +``` + +The the above SQL command, `stream-id` is from the first column of the output of `SHOW STREAMS`. diff --git a/docs-en/13-operation/10-monitor.md b/docs-en/13-operation/10-monitor.md new file mode 100644 index 0000000000000000000000000000000000000000..019cf4f2948141fac79587429f1fdc3b06623945 --- /dev/null +++ b/docs-en/13-operation/10-monitor.md @@ -0,0 +1,60 @@ +--- +title: TDengine Monitoring +--- + +After TDengine is started, a database named `log` for monitoring is created automatically. The information about CPU, memory, disk, bandwidth, number of requests, disk I/O speed, slow query is written into `log` database on the basis of a predefined interval. Besides, some important system operations, like logon, create user, drop database, and alerts and warnings generated in TDengine are written into `log` database too. System operator can view the data in `log` database from TDengine CLI or from a web console. + +Collection of the monitoring information is enabled by default, but can be disabled by parameter `monitor` in configuration file. + +## TDinsight + +TDinsight is a total solution which uses the monitor database `log` mentioned previously and Grafana to monitor a TDengine cluster. + +From version 2.3.3.0, more monitoring data has been added in the `log` database. Please refer to [TDinsight Grafana Dashboard](https://grafana.com/grafana/dashboards/15167) to learn more details about using TDinsight to monitor TDengine. + +A script `TDinsight.sh` is provided to deploy TDinsight in automatic way. + +Download `TDinsight.sh` with below command: + +```bash +wget https://github.com/taosdata/grafanaplugin/raw/master/dashboards/TDinsight.sh +chmod +x TDinsight.sh +``` + +Prepare: + +1. TDengine Server + + - The URL of REST service:for example `http://localhost:6041` if TDengine is deployed locally + - User name and password + +2. Grafana Alert Notification + +There are two ways to setup Grafana alert notification. + +- An existing Grafana Notification Channel can be specified with parameter `-E`, the notifier uid of the channel can be obtained by `curl -u admin:admin localhost:3000/api/alert-notifications |jq` + + ```bash + sudo ./TDinsight.sh -a http://localhost:6041 -u root -p taosdata -E + ``` + +- The AliCloud SMS alert built in TDengine data source plugin can be enabled with parameter `-s`, the parameters of this way are as follows: + + - `-I`: AliCloud SMS Key ID + - `-K`: AliCloud SMS Key Secret + - `-S`: AliCloud SMS Signature + - `-C`: SMS notification template + - `-T`: Input parameters in JSON format for the SMS notification template, for example`{"alarm_level":"%s","time":"%s","name":"%s","content":"%s"}` + - `-B`: List of mobile numbers to be notified + + Below is an example of the full command using this way. + + ```bash + sudo ./TDinsight.sh -a http://localhost:6041 -u root -p taosdata -s \ + -I XXXXXXX -K XXXXXXXX -S taosdata -C SMS_1111111 -B 18900000000 \ + -T '{"alarm_level":"%s","time":"%s","name":"%s","content":"%s"}' + ``` + +Launch `TDinsight.sh` as above command and restart Grafana, then open Dashboard `http://localhost:3000/d/tdinsight`. + +For more use cases and restrictions please refer to [TDinsight](/reference/tdinsight/). diff --git a/docs-en/13-operation/11-optimize.md b/docs-en/13-operation/11-optimize.md new file mode 100644 index 0000000000000000000000000000000000000000..7cccfc8b0d51a4bfda9ae4827130a3747f10e649 --- /dev/null +++ b/docs-en/13-operation/11-optimize.md @@ -0,0 +1,100 @@ +--- +title: Performance Optimization +--- + +After a TDengine cluster has been running for long enough time, because of updating data, deleting tables and deleting expired data, there may be fragments in data files and query performance may be impacted. To resolve the problem of fragments, from version 2.1.3.0 a new SQL command `COMPACT` can be used to defragment the data files. + +```sql +COMPACT VNODES IN (vg_id1, vg_id2, ...) +``` + +`COMPACT` can be used to defragment one or more vgroups. The defragmentation work will be put in task queue for scheduling execution by TDengine. `SHOW VGROUPS` command can be used to get the vgroup ids to be used in `COMPACT` command. There is a column `compacting` in the output of `SHOW GROUPS` to indicate the compacting status of the vgroup: 2 means the vgroup is waiting in task queue for compacting, 1 means compacting is in progress, and 0 means the vgroup has nothing to do with compacting. + +Please be noted that a lot of disk I/O is required for defragementation operation, during which the performance may be impacted significantly for data insertion and query, data insertion may be blocked shortly in extreme cases. + +## Optimize Storage Parameters + +The data in different use cases may have different characteristics, such as the days to keep, number of replicas, collection interval, record size, number of collection points, compression or not, etc. To achieve best efficiency in storage, the parameters in below table can be used, all of them can be either configured in `taos.cfg` as default configuration or in the command `create database`. For detailed definition of these parameters please refer to [Configuration Parameters](/reference/config/). + +| # | Parameter | Unit | Definition | **Value Range** | **Default Value** | +| --- | --------- | ---- | ------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------- | ----------------- | +| 1 | days | Day | The time range of the data stored in a single data file | 1-3650 | 10 | +| 2 | keep | Day | The number of days the data is kept in the database | 1-36500 | 3650 | +| 3 | cache | MB | The size of each memory block | 1-128 | 16 | +| 4 | blocks | None | The number of memory blocks used by each vnode | 3-10000 | 6 | +| 5 | quorum | None | The number of required confirmation in case of multiple replicas | 1-2 | 1 | +| 6 | minRows | None | The minimum number of rows in a data file | 10-1000 | 100 | +| 7 | maxRows | None | The maximum number of rows in a daa file | 200-10000 | 4096 | +| 8 | comp | None | Whether to compress the data | 0:uncompressed; 1: One Phase compression; 2: Two Phase compression | 2 | +| 9 | walLevel | None | wal sync level (named as "wal" in create database ) | 1:wal enabled without fsync; 2:wal enabled with fsync | 1 | +| 10 | fsync | ms | The time to wait for invoking fsync when walLevel is set to 2; 0 means no wait | 3000 | +| 11 | replica | none | The number of replications | 1-3 | 1 | +| 12 | precision | none | Time precision | ms: millisecond; us: microsecond;ns: nanosecond | ms | +| 13 | update | none | Whether to allow updating data | 0: not allowed; 1: a row must be updated as whole; 2: a part of columns in a row can be updated | 0 | +| 14 | cacheLast | none | Whether the latest data of a table is cached in memory | 0: not cached; 1: the last row is cached; 2: the latest non-NULL value of each column is cached | 0 | + +For a specific use case, there may be multiple kinds of data with different characteristics, it's best to put data with same characteristics in same database. So there may be multiple databases in a system while each database can be configured with different storage parameters to achieve best performance. The above parameters can be used when creating a database to override the default setting in configuration file. + +```sql + CREATE DATABASE demo DAYS 10 CACHE 32 BLOCKS 8 REPLICA 3 UPDATE 1; +``` + +The above SQL statement creates a database named as `demo`, in which each data file stores data across 10 days, the size of each memory block is 32 MB and each vnode is allocated with 8 blocks, the replica is set to 3, update operation is allowed, and all other parameters not specified in the command follow the default configuration in `taos.cfg`. + +Once a database is created, only some parameters can be changed and be effective immediately while others are can't. + +| **Parameter** | **Alterable** | **Value Range** | **Syntax** | +| ------------- | ------------- | ---------------- | -------------------------------------- | +| name | | | | +| create time | | | | +| ntables | | | | +| vgroups | | | | +| replica | **YES** | 1-3 | ALTER DATABASE REPLICA _n_ | +| quorum | **YES** | 1-2 | ALTER DATABASE QUORUM _n_ | +| days | | | | +| keep | **YES** | days-365000 | ALTER DATABASE KEEP _n_ | +| cache | | | | +| blocks | **YES** | 3-1000 | ALTER DATABASE BLOCKS _n_ | +| minrows | | | | +| maxrows | | | | +| wal | | | | +| fsync | | | | +| comp | **YES** | 0-2 | ALTER DATABASE COMP _n_ | +| precision | | | | +| status | | | | +| update | | | | +| cachelast | **YES** | 0 \| 1 \| 2 \| 3 | ALTER DATABASE CACHELAST _n_ | + +**Explanation:** Prior to version 2.1.3.0, `taosd` server process needs to be restarted for these parameters to take in effect if they are changed using `ALTER DATABASE`. + +When trying to join a new dnode into a running TDengine cluster, all the parameters related to cluster in the new dnode configuration must be consistent with the cluster, otherwise it can't join the cluster. The parameters that are checked when joining a dnode are as below. For detailed definition of these parameters please refer to [Configuration Parameters](/reference/config/). + +- numOfMnodes +- mnodeEqualVnodeNum +- offlineThreshold +- statusInterval +- maxTablesPerVnode +- maxVgroupsPerDb +- arbitrator +- timezone +- balance +- flowctrl +- slaveQuery +- adjustMaster + +For the convenience of debugging, the log setting of a dnode can be changed temporarily. The temporary change will be lost once the server is restarted. + +```sql +ALTER DNODE +``` + +- dnode_id: from output of "SHOW DNODES" +- config: the parameter to be changed, as below + - resetlog: close the old log file and create the new on + - debugFlag: 131 (INFO/ERROR/WARNING), 135 (DEBUG), 143 (TRACE) + +For example + +``` +alter dnode 1 debugFlag 135; +``` diff --git a/docs-en/13-operation/17-diagnose.md b/docs-en/13-operation/17-diagnose.md new file mode 100644 index 0000000000000000000000000000000000000000..b140d925c07386f93c82d492bb8bcf4d95349f12 --- /dev/null +++ b/docs-en/13-operation/17-diagnose.md @@ -0,0 +1,122 @@ +--- +title: Problem Diagnostics +--- + +## Network Connection Diagnostics + +When the client is unable to access the server, the network connection between the client side and the server side needs to be checked to find out the root cause and resolve problems. + +The diagnostic for network connection can be executed between Linux and Linux or between Linux and Windows. + +Diagnostic steps: + +1. If the port range to be diagnosed are being occupied by a `taosd` server process, please firstly stop `taosd. +2. On the server side, execute command `taos -n server -P -l ` to monitor the port range starting from the port specified by `-P` parameter with the role of "server. +3. On the client side, execute command `taos -n client -h -P -l ` to send testing package to the specified server and port. + +-l : The size of the testing package, in bytes. The value range is [11, 64,000] and default value is 1,000. Please be noted that the package length must be same in the above 2 commands executed on server side and client side respectively. + +Output of the server side is as below for example: + +```bash +# taos -n server -P 6000 +12/21 14:50:13.522509 0x7f536f455200 UTL work as server, host:172.27.0.7 startPort:6000 endPort:6011 pkgLen:1000 + +12/21 14:50:13.522659 0x7f5352242700 UTL TCP server at port:6000 is listening +12/21 14:50:13.522727 0x7f5351240700 UTL TCP server at port:6001 is listening +... +... +... +12/21 14:50:13.523954 0x7f5342fed700 UTL TCP server at port:6011 is listening +12/21 14:50:13.523989 0x7f53437ee700 UTL UDP server at port:6010 is listening +12/21 14:50:13.524019 0x7f53427ec700 UTL UDP server at port:6011 is listening +12/21 14:50:22.192849 0x7f5352242700 UTL TCP: read:1000 bytes from 172.27.0.8 at 6000 +12/21 14:50:22.192993 0x7f5352242700 UTL TCP: write:1000 bytes to 172.27.0.8 at 6000 +12/21 14:50:22.237082 0x7f5351a41700 UTL UDP: recv:1000 bytes from 172.27.0.8 at 6000 +12/21 14:50:22.237203 0x7f5351a41700 UTL UDP: send:1000 bytes to 172.27.0.8 at 6000 +12/21 14:50:22.237450 0x7f5351240700 UTL TCP: read:1000 bytes from 172.27.0.8 at 6001 +12/21 14:50:22.237576 0x7f5351240700 UTL TCP: write:1000 bytes to 172.27.0.8 at 6001 +12/21 14:50:22.281038 0x7f5350a3f700 UTL UDP: recv:1000 bytes from 172.27.0.8 at 6001 +12/21 14:50:22.281141 0x7f5350a3f700 UTL UDP: send:1000 bytes to 172.27.0.8 at 6001 +... +... +... +12/21 14:50:22.677443 0x7f5342fed700 UTL TCP: read:1000 bytes from 172.27.0.8 at 6011 +12/21 14:50:22.677576 0x7f5342fed700 UTL TCP: write:1000 bytes to 172.27.0.8 at 6011 +12/21 14:50:22.721144 0x7f53427ec700 UTL UDP: recv:1000 bytes from 172.27.0.8 at 6011 +12/21 14:50:22.721261 0x7f53427ec700 UTL UDP: send:1000 bytes to 172.27.0.8 at 6011 +``` + +Output of the client side is as below for example: + +```bash +# taos -n client -h 172.27.0.7 -P 6000 +12/21 14:50:22.192434 0x7fc95d859200 UTL work as client, host:172.27.0.7 startPort:6000 endPort:6011 pkgLen:1000 + +12/21 14:50:22.192472 0x7fc95d859200 UTL server ip:172.27.0.7 is resolved from host:172.27.0.7 +12/21 14:50:22.236869 0x7fc95d859200 UTL successed to test TCP port:6000 +12/21 14:50:22.237215 0x7fc95d859200 UTL successed to test UDP port:6000 +... +... +... +12/21 14:50:22.676891 0x7fc95d859200 UTL successed to test TCP port:6010 +12/21 14:50:22.677240 0x7fc95d859200 UTL successed to test UDP port:6010 +12/21 14:50:22.720893 0x7fc95d859200 UTL successed to test TCP port:6011 +12/21 14:50:22.721274 0x7fc95d859200 UTL successed to test UDP port:6011 +``` + +The output needs to be checked carefully for the system operator to find out root cause and solve the problem. + +## Startup Status and RPC Diagnostic + +`taos -n startup -h ` can be used to check the startup status of a `taosd` process. This is a comman task for a system operator to do to determine whether `taosd` has been started successfully, especially in case of cluster. + +`taos -n rpc -h ` can be used to check whether the port of a started `taosd` can be accessed or not. If `taosd` process doesn't respond or work abnormally, this command can be used to initiate a rpc communication with the specified fqdn to determine whether it's network problem or `taosd` is abnormal. + +## Sync and Arbitrator Diagnostic + +```bash +taos -n sync -P 6040 -h +taos -n sync -P 6042 -h +``` + +The above commands can be executed on Linux Shell to check whether the port for sync works well and whether the sync module of the server side works well. Besides, `-P 6042` is used to check whether the arbitrator is configured properly and works well. + +## Network Speed Diagnostic + +`taos -n speed -h -P 6030 -N 10 -l 10000000 -S TCP` + +From version 2.2.0.0, the above command can be executed on Linux Shell to test the network speed, it sends uncompressed package to a running `taosd` server process or a simulated server process started by `taos -n server` to test the network speed. Parameters can be used when testing network speed are as below: + +-n:When set to "speed", it means testing network speed +-h:The FQDN or IP of the server process to be connected to; if not set, the FQDN configured in `taos.cfg` is used +-P:The port of the server process to connect to, the default value is 6030 +-N:The number of packages that will be sent in the test, range is [1,10000], default value is 100 +-l:The size of each package in bytes, range is [1024, 1024 \* 1024 \* 1024], default value is 1024 +-S:The type of network packages to send, can be either TCP or UDP, default value is + +## FQDN Resolution Diagnostic + +`taos -n fqdn -h ` + +From version 2.2.0.0, the above command can be executed on Linux Shell to test the resolution speed of FQDN. It can be used to try to resolve a FQDN to an IP address and record the time spent in this process. The parameters that can be used for this purpose are as below: + +-n:When set to "fqdn", it means testing the speed of resolving FQDN +-h:The FQDN to be resolved. If not set, the `FQDN` parameter in `taos.cfg` is used by default. + +## Server Log + +The parameter `debugFlag` is used to control the log level of `taosd` server process. The default value is 131, for debug purpose it needs to be escalated to 135 or 143. + +Once this parameter is set to 135 or 143, the log file grows very quickly especially when there is huge volume of data insertion and data query requests. If all the logs are stored together, some important information may be missed very easily, so on server side important information is stored at different place from other logs. + +- The log at level of INFO, WARNING and ERROR is stored in `taosinfo` so that it is easy to find important information +- The log at level of DEBUG (135) and TRACE (143) and other information not handled by `taosinfo` are stored in `taosdlog` + +## Client Log + +An independent log file, named as "taoslog+" is generated for each client program, i.e. a client process. The default value of `debugFlag` is also 131 and only log at level of INFO/ERROR/WARNING is recorded, it and needs to be changed to 135 or 143 so that log at DEBUG or TRACE level can be recorded for debugging purpose. + +The maximum length of a single log file is controlled by parameter `numOfLogLines` and only 2 log files are kept for each `taosd` server process. + +log file is written in async way to minimize the workload on disk, bu the penalty is that a few log lines may be lost in some extreme conditions. diff --git a/docs-en/13-operation/_category_.yml b/docs-en/13-operation/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..3231ce910dc959a0700eb3bd4dbf8c2a727dd09b --- /dev/null +++ b/docs-en/13-operation/_category_.yml @@ -0,0 +1 @@ +label: Administration diff --git a/docs-en/13-operation/index.md b/docs-en/13-operation/index.md new file mode 100644 index 0000000000000000000000000000000000000000..a9801c0390f294d6b39b1219cc4055149871ef9c --- /dev/null +++ b/docs-en/13-operation/index.md @@ -0,0 +1,12 @@ +--- +title: Administration +--- + +This chapter is mainly written for system administrators, covering download, install/uninstall, data import/export, system monitoring, user management, connection management, etc. Capacity planning and system optimization are also covered. + +```mdx-code-block +import DocCardList from '@theme/DocCardList'; +import {useCurrentSidebarCategory} from '@docusaurus/theme-common'; + + +``` diff --git a/docs-en/14-reference/02-rest-api/02-rest-api.mdx b/docs-en/14-reference/02-rest-api/02-rest-api.mdx new file mode 100644 index 0000000000000000000000000000000000000000..f405d551e530a37a5221e71a824f605fba0c0db9 --- /dev/null +++ b/docs-en/14-reference/02-rest-api/02-rest-api.mdx @@ -0,0 +1,307 @@ +--- +title: REST API +--- + +To support the development of various types of platforms, TDengine provides an API that conforms to the REST principle, namely REST API. To minimize the learning cost, different from the other database REST APIs, TDengine directly requests the SQL command contained in the request BODY through HTTP POST to operate the database and only requires a URL. + +:::note +One difference from the native connector is that the REST interface is stateless, so the `USE db_name` command has no effect. All references to table names and super table names need to specify the database name prefix. (Since version 2.2.0.0, it is supported to specify db_name in RESTful URL. If the database name prefix is not specified in the SQL command, the `db_name` specified in the URL will be used. Since version 2.4.0.0, REST service is provided by taosAdapter by default. And it requires that the `db_name` must be specified in the URL.) +::: + +## Installation + +The REST interface does not rely on any TDengine native library, so the client application does not need to install any TDengine libraries. The client application's development language supports the HTTP protocol is enough. + +## Verification + +If the TDengine server is already installed, it can be verified as follows: + +The following is an Ubuntu environment using the `curl` tool (to confirm that it is installed) to verify that the REST interface is working. + +The following example lists all databases, replacing `h1.taosdata.com` and `6041` (the default port) with the actual running TDengine service FQDN and port number. + +```html +curl -H 'Authorization: Basic cm9vdDp0YW9zZGF0YQ==' -d 'show databases;' h1.taosdata.com:6041/rest/sql +``` + +The following return value results indicate that the verification passed. + +```json +{ + "status": "succ", + "head": [ + "name", + "created_time", + "ntables", + "vgroups", + "replica", + "quorum", + "days", + "keep1,keep2,keep(D)", + "cache(MB)", + "blocks", + "minrows", + "maxrows", + "wallevel", + "fsync", + "comp", + "precision", + "status" + ], + "data": [ + [ + "log", + "2020-09-02 17:23:00.039", + 4, + 1, + 1, + 1, + 10, + "30,30,30", + 1, + 3, + 100, + 4096, + 1, + 3000, + 2, + "us", + "ready" + ] + ], + "rows": 1 +} +``` + +## HTTP request URL format + +``` +http://:/rest/sql/[db_name] +``` + +Parameter Description: + +- fqnd: FQDN or IP address of any host in the cluster +- port: httpPort configuration item in the configuration file, default is 6041 +- db_name: Optional parameter that specifies the default database name for the executed SQL command. (supported since version 2.2.0.0) + +For example, `http://h1.taos.com:6041/rest/sql/test` is a URL to `h1.taos.com:6041` and sets the default database name to `test`. + +TDengine supports both Basic authentication and custom authentication mechanisms, and subsequent versions will provide a standard secure digital signature mechanism for authentication. + +- The custom authentication information is as follows (Let's introduce token later) + + ``` + Authorization: Taosd + ``` + +- Basic authentication information is shown below + + ``` + Authorization: Basic + ``` + +The HTTP request's BODY is a complete SQL command, and the data table in the SQL statement should be provided with a database prefix, e.g., `db_name.tb_name`. If the table name does not have a database prefix and the database name is not specified in the URL, the system will response an error because the HTTP module is a simple forwarder and has no awareness of the current DB. + +Use `curl` to initiate an HTTP request with a custom authentication method, with the following syntax. + +```bash +curl -H 'Authorization: Basic ' -d '' :/rest/sql/[db_name] +``` + +Or + +```bash +curl -u username:password -d '' :/rest/sql/[db_name] +``` + +where `TOKEN` is the string after Base64 encoding of `{username}:{password}`, e.g. `root:taosdata` is encoded as `cm9vdDp0YW9zZGF0YQ==`. + +## HTTP Return Format + +The return result is in JSON format, as follows: + +```json +{ + "status": "succ", + "head": ["ts", "current", ...], + "column_meta": [["ts",9,8],["current",6,4], ...], + "data": [ + ["2018-10-03 14:38:05.000", 10.3, ...], + ["2018-10-03 14:38:15.000", 12.6, ...] + ], + "rows": 2 +} +``` + +Description: + +- status: tell if the operation result is success or failure. +- head: the definition of the table, or just one column "affected_rows" if no result set is returned. (As of version 2.0.17.0, it is recommended not to rely on the head return value to determine the data column type but rather use column_meta. In later versions, the head item may be removed from the return value.) +- column_meta: this item is added to the return value to indicate the data type of each column in the data with version 2.0.17.0 and later versions. Each column is described by three values: column name, column type, and type length. For example, `["current",6,4]` means that the column name is "current", the column type is 6, which is the float type, and the type length is 4, which is the float type with 4 bytes. If the column type is binary or nchar, the type length indicates the maximum length of content stored in the column, not the length of the specific data in this return value. When the column type is nchar, the type length indicates the number of Unicode characters that can be saved, not bytes. +- data: The exact data returned, presented row by row, or just [[affected_rows]] if no result set is returned. The order of the data columns in each row of data is the same as that of the data columns described in column_meta. +- rows: Indicates how many rows of data there are. + +The column types in column_meta are described as follows: + +- 1:BOOL +- 2:TINYINT +- 3:SMALLINT +- 4:INT +- 5:BIGINT +- 6:FLOAT +- 7:DOUBLE +- 8:BINARY +- 9:TIMESTAMP +- 10:NCHAR + +## Custom Authorization Code + +HTTP requests require an authorization code `` for identification purposes. The administrator usually provides the authorization code, and it can be obtained simply by sending an ``HTTP GET`` request as follows: + +```bash +curl http://:/rest/login// +``` + +Where `fqdn` is the FQDN or IP address of the TDengine database. `port` is the port number of the TDengine service. `username` is the database username. `password` is the database password. The return value is in `JSON` format, and the meaning of each field is as follows. + +- status: flag bit of the request result + +- code: return value code + +- desc: authorization code + +Example of getting authorization code. + +```bash +curl http://192.168.0.1:6041/rest/login/root/taosdata +``` + +Response body: + +```json +{ + "status": "succ", + "code": 0, + "desc": "/KfeAzX/f9na8qdtNZmtONryp201ma04bEl8LcvLUd7a8qdtNZmtONryp201ma04" +} +``` + +## For example + +- query all records from table d1001 of database demo + + ```bash + curl -H 'Authorization: Basic cm9vdDp0YW9zZGF0YQ==' -d 'select * from demo.d1001' 192.168.0.1:6041/rest/sql + ``` + + Response body: + + ```json + { + "status": "succ", + "head": ["ts", "current", "voltage", "phase"], + "column_meta": [ + ["ts", 9, 8], + ["current", 6, 4], + ["voltage", 4, 4], + ["phase", 6, 4] + ], + "data": [ + ["2018-10-03 14:38:05.000", 10.3, 219, 0.31], + ["2018-10-03 14:38:15.000", 12.6, 218, 0.33] + ], + "rows": 2 + } + ``` + +- Create database demo: + + ```bash + curl -H 'Authorization: Basic cm9vdDp0YW9zZGF0YQ==' -d 'create database demo' 192.168.0.1:6041/rest/sql + ``` + + Response body: + + ```json + { + "status": "succ", + "head": ["affected_rows"], + "column_meta": [["affected_rows", 4, 4]], + "data": [[1]], + "rows": 1 + } + ``` + +## Other Uses + +### Unix timestamps for result sets + +When the HTTP request URL uses `/rest/sqlt`, the returned result set's timestamp value will be in Unix timestamp format, for example: + +```bash +curl -H 'Authorization: Basic cm9vdDp0YW9zZGF0YQ==' -d 'select * from demo.d1001' 192.168.0.1:6041/rest/sqlt +``` + +Response body: + +```json +{ + "status": "succ", + "head": ["ts", "current", "voltage", "phase"], + "column_meta": [ + ["ts", 9, 8], + ["current", 6, 4], + ["voltage", 4, 4], + ["phase", 6, 4] + ], + "data": [ + [1538548685000, 10.3, 219, 0.31], + [1538548695000, 12.6, 218, 0.33] + ], + "rows": 2 +} +``` + +### UTC format for the result set + +When the HTTP request URL uses `/rest/sqlutc`, the timestamp of the returned result set will be expressed as a UTC format, for example: + +```bash + curl -H 'Authorization: Basic cm9vdDp0YW9zZGF0YQ==' -d 'select * from demo.t1' 192.168.0.1:6041/rest/sqlutc +``` + +Response body: + +```json +{ + "status": "succ", + "head": ["ts", "current", "voltage", "phase"], + "column_meta": [ + ["ts", 9, 8], + ["current", 6, 4], + ["voltage", 4, 4], + ["phase", 6, 4] + ], + "data": [ + ["2018-10-03T14:38:05.000+0800", 10.3, 219, 0.31], + ["2018-10-03T14:38:15.000+0800", 12.6, 218, 0.33] + ], + "rows": 2 +} +``` + +## Important configuration items + +Only some configuration parameters related to the RESTful interface are listed below. Please see the description in the configuration file for other system parameters. + +- The port number of the external RESTful service is bound to 6041 by default (the actual value is serverPort + 11, so it can be changed by modifying the setting of the serverPort parameter). +- httpMaxThreads: the number of threads to start, default is 2 (the default value is rounded down to half of the CPU cores with version 2.0.17.0 and later versions). +- restfulRowLimit: the maximum number of result sets (in JSON format) to return. The default value is 10240. +- httpEnableCompress: whether to support compression, the default is not supported. Currently, TDengine only supports the gzip compression format. +- httpDebugFlag: logging switch, default is 131. 131: error and alarm messages only, 135: debug messages, 143: very detailed debug messages. +- httpDbNameMandatory: users must specify the default database name in the RESTful URL. The default is 0, which turns off this check. If set to 1, users must put a default database name in every RESTful URL. Otherwise, it will return an execution error and reject this SQL statement, regardless of whether the SQL statement executed at this time requires a specified database. + +:::note +If you are using the REST API provided by taosd, you should write the above configuration in taosd's configuration file taos.cfg. If you use the REST API of taosAdapter, you need to refer to taosAdapter [corresponding configuration method](/reference/taosadapter/). +::: diff --git a/docs-en/14-reference/02-rest-api/_category_.yml b/docs-en/14-reference/02-rest-api/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..57a20d8458e937f60c41806be4392ebb2d13e0f7 --- /dev/null +++ b/docs-en/14-reference/02-rest-api/_category_.yml @@ -0,0 +1 @@ +label: REST API diff --git a/docs-en/14-reference/03-connector/03-connector.mdx b/docs-en/14-reference/03-connector/03-connector.mdx new file mode 100644 index 0000000000000000000000000000000000000000..6be914bdb4b701f478b6b8b27366d6ebb5a39ec8 --- /dev/null +++ b/docs-en/14-reference/03-connector/03-connector.mdx @@ -0,0 +1,115 @@ +--- +title: Connector +--- + +TDengine provides a rich set of APIs (application development interface). To facilitate users to develop their applications quickly, TDengine supports connectors for multiple programming languages, including official connectors for C/C++, Java, Python, Go, Node.js, C#, and Rust. These connectors support connecting to TDengine clusters using both native interfaces (taosc) and REST interfaces (not supported in a few languages yet). Community developers have also contributed several unofficial connectors, such as the ADO.NET connector, the Lua connector, and the PHP connector. + +![image-connector](/img/connector.png) + +## Supported platforms + +Currently, TDengine's native interface connectors can support platforms such as X64/X86/ARM64/ARM32/MIPS/Alpha hardware platforms and Linux/Win64/Win32 development environments. The comparison matrix is as follows. + +| **CPU** | **OS** | **JDBC** | **Python** | **Go** | **Node.js** | **C#** | **Rust** | C/C++ | +| ------- | ------ | -------- | ---------- | ------ | ----------- | ------ | -------- | ----- | +| **X86 64bit** | **Linux** | ● | ● | ● | ● | ● | ● | ● | +| **X86 64bit** | **Win64** | ● | ● | ● | ● | ● | ● | ● | +| **X86 64bit** | **Win32** | ● | ● | ● | ● | ○ | ○ | ● | +| **X86 32bit** | **Win32** | ○ | ○ | ○ | ○ | ○ | ○ | ● | +| **ARM64** | **Linux** | ● | ● | ● | ● | ○ | ○ | ● | +| **ARM32** | **Linux** | ● | ● | ● | ● | ○ | ○ | ● | +| **MIPS** | **Linux** | ○ | ○ | ○ | ○ | ○ | ○ | ○ | + +Where ● means the official test verification passed, ○ means the unofficial test verification passed, -- means no assurance. + +Using REST connection can support a broader range of operating systems as it does not rely on client drivers. + +## Version support + +TDengine version updates often add new features, and the connector versions in the list are the best-fit versions of the connector. + +| **TDengine Versions** | **Java** | **Python** | **Go** | **C#** | **Node.js** | **Rust** | +| --------------------- | -------- | ---------- | ------------ | ------------- | --------------- | -------- | +| **2.4.0.14 and up** | 2.0.38 | current version | develop branch | 1.0.2 - 1.0.6 | 2.0.10 - 2.0.12 | current version | +| **2.4.0.6 and up** | 2.0.37 | current version | develop branch | 1.0.2 - 1.0.6 | 2.0.10 - 2.0.12 | current version | +| **2.4.0.4 - 2.4.0.5** | 2.0.37 | current version | develop branch | 1.0.2 - 1.0.6 | 2.0.10 - 2.0.12 | current version | +| **2.2.x.x ** | 2.0.36 | current version | master branch | n/a | 2.0.7 - 2.0.9 | current version | +| **2.0.x.x ** | 2.0.34 | current version | master branch | n/a | 2.0.1 - 2.0.6 | current version | + +## Functional Features + +Comparing the connector support for TDengine functional features as follows. + +### Using the native interface (taosc) + +| **Functional Features** | **Java** | **Python** | **Go** | **C#** | **Node.js** | **Rust** | +| -------------- | -------- | ---------- | ------ | ------ | ----------- | -------- | +| **Connection Management** | Support | Support | Support | Support | Support | Support | +| **Regular Query** | Support | Support | Support | Support | Support | Support | +| **Continuous Query** | Support | Support | Support | Support | Support | Support | +| **Parameter Binding** | Support | Support | Support | Support | Support | Support | +| **Subscription** | Support | Support | Support | Support | Support | Not Supported | +| **Schemaless** | Support | Support | Support | Support | Support | Support | +| **DataFrame** | Not Supported | Support | Not Supported | Not Supported | Not Supported | Not Supported | + +:::info +The different database framework specifications for various programming languages do not mean that all C/C++ interfaces need a wrapper. +::: + +### Using the REST interface + +| **Functional Features** | **Java** | **Python** | **Go** | **C# (not supported yet)** | **Node.js** | **Rust** | +| ------------------------------ | -------- | ---------- | -------- | ------------------ | ----------- | -------- | +| **Connection Management** | Support | Support | Support | N/A | Support | Support | +| **Regular Query** | Support | Support | Support | N/A | Support | Support | +| **Continous Query ** | Support | Support | Support | N/A | Support | Support | +| **Parameter Binding** | Not Supported | Not Supported | Not Supported | N/A | Not Supported | Not Supported | +| **Subscription** | Not Supported | Not Supported | Not Supported | N/A | Not Supported | Not Supported | +| **Schemaless** | Not supported | Not Supported | Not Supported | N/A | Not Supported | Not supported | +| **Bulk Pulling (based on WebSocket) **| Support | Not Supported | Not Supported | N/A | Not Supported | Not Supported | +| **DataFrame** | Not supported | Support | Not supported | N/A | Not supported | Not supported | + +:::warning + +- Regardless of the programming language chosen for the connector, TDengine versions 2.0 and above recommend that each thread of a database application create a separate connection. Or create a connection pool based on threads to avoid interference between threads with the "USE statement" state within a connection (but the connection's query and write operations are thread-safe). + +::: + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import InstallOnWindows from "./_linux_install.mdx"; +import InstallOnLinux from "./_windows_install.mdx"; +import VerifyWindows from "./_verify_windows.mdx"; +import VerifyLinux from "./_verify_linux.mdx"; + +## Install Client Driver + +:::info +The client driver needs to be installed if you use the native interface connector on a system that does not have the TDengine server software installed. + +::: + +### Installation steps + + + + + + + + + + +### Installation Verification + +After completing the above installation and configuration and you have confirmed that the TDengine service is up and running, you can execute the TDengine CLI tool to log in. + + + + + + + + + + diff --git a/docs-en/14-reference/03-connector/_category_.yml b/docs-en/14-reference/03-connector/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..e470f64aa013b137f05f03db112641faf2956297 --- /dev/null +++ b/docs-en/14-reference/03-connector/_category_.yml @@ -0,0 +1 @@ +label: "connector" \ No newline at end of file diff --git a/docs-en/14-reference/03-connector/_linux_install.mdx b/docs-en/14-reference/03-connector/_linux_install.mdx new file mode 100644 index 0000000000000000000000000000000000000000..3fa123794cd8ff304a1bc13449591194e7320aa9 --- /dev/null +++ b/docs-en/14-reference/03-connector/_linux_install.mdx @@ -0,0 +1,34 @@ +import PkgList from "/components/PkgList"; + +1. Download the TDengine client installation package + + + + [All Packages](https://www.taosdata.com/en/all-downloads/) + +2. Unzip + + Download the package to any directory the current user has read/write permission. Then execute `tar -xzvf TDengine-client-VERSION.tar.gz` command. + The VERSION should be the version of the package you just downloaded. + +3. Execute the install script + + Once the package is unzipped, you will see the following files in the directory: + - _ install_client.sh_: install script + - _ taos.tar.gz_: client driver package + - _ driver_: TDengine client driver + - _examples_: some example programs of different programming languages (C/C#/go/JDBC/MATLAB/python/R) + + You can run `install_client.sh` to install it. + +4. Edit taos.cfg + + Edit `taos.cfg` file (full path is `/etc/taos/taos.cfg` by default), modify `firstEP` with actual TDengine server's End Point, for example `h1.tdengine.com:6030` + +:::tip + +1. If the computer does not run the TDengine service but installs the TDengine client driver, then you need to config `firstEP` in `taos.cfg` only, and there is no need to configure `FQDN`; + +2. If you encounter the "Unable to resolve FQDN" error, please make sure the FQDN in the `/etc/hosts` file of the current computer is correctly configured, or the DNS service is correctly configured. + +::: diff --git a/docs-en/14-reference/03-connector/_preparation.mdx b/docs-en/14-reference/03-connector/_preparation.mdx new file mode 100644 index 0000000000000000000000000000000000000000..07ebdbca3d891ff51a254bc1b83016f1404bb47e --- /dev/null +++ b/docs-en/14-reference/03-connector/_preparation.mdx @@ -0,0 +1,10 @@ +- Client driver installed (mandatory for native connections, not required for REST connections) + +:::info + +Since the TDengine client driver is written in C, using the native connection requires loading the client driver shared library file, which is usually included in the TDengine installer. You can install either standard TDengine server installation package or [TDengine client installation package](/get-started/). For Windows development, you need to install the corresponding [Windows client](https://www.taosdata.com/cn/all-downloads/#TDengine-Windows-Client) for TDengine. + +- libtaos.so: After successful installation of TDengine on a Linux system, the dependent Linux version of the client driver `libtaos.so` file will be automatically linked to `/usr/lib/libtaos.so`, which is included in the Linux scannable path and does not need to be specified separately. +- taos.dll: After installing the client on Windows, the dependent Windows version of the client driver taos.dll file will be automatically copied to the system default search path C:/Windows/System32, again without the need to specify it separately. + +::: diff --git a/docs-en/14-reference/03-connector/_verify_linux.mdx b/docs-en/14-reference/03-connector/_verify_linux.mdx new file mode 100644 index 0000000000000000000000000000000000000000..a6e5455224bc19167d79c045f79365b452a57892 --- /dev/null +++ b/docs-en/14-reference/03-connector/_verify_linux.mdx @@ -0,0 +1,14 @@ +Execute TDengine CLI program `taos` directly from the Linux shell to connect to the TDengine service and enter the TDengine CLI interface, as shown in the following example. + +```text +$ taos +Welcome to the TDengine shell from Linux, Client Version:2.0.5.0 +Copyright (c) 2017 by TAOS Data, Inc. All rights reserved. +taos> show databases; +name | created_time | ntables | vgroups | replica | quorum | days | keep1,keep2,keep(D) | cache(MB)| blocks | minrows | maxrows | wallevel | fsync | comp | precision | status | +========================================================================================================================================================================================================================= +test | 2020-10-14 10:35:48.617 | 10 | 1 | 1 | 1 | 2 | 3650,3650,3650 | 16| 6 | 100 | 4096 | 1 | 3000 | 2 | ms | ready | +log | 2020-10-12 09:08:21.651 | 4 | 1 | 1 | 1 | 10 | 30,30,30 | 1| 3 | 100 | 4096 | 1 | 3000 | 2 | us | ready | +Query OK, 2 row(s) in set (0.001198s) +taos> +``` diff --git a/docs-en/14-reference/03-connector/_verify_windows.mdx b/docs-en/14-reference/03-connector/_verify_windows.mdx new file mode 100644 index 0000000000000000000000000000000000000000..daeb151bb1252436c0ef16eab1d50a64d664e437 --- /dev/null +++ b/docs-en/14-reference/03-connector/_verify_windows.mdx @@ -0,0 +1,14 @@ +Go to the `C:\TDengine` directory from `cmd` and execute TDengine CLI program `taos.exe` directly to connect to the TDengine service and enter the TDengine CLI interface, for example, as follows: + +```text + C:\TDengine>taos + Welcome to the TDengine shell from Linux, Client Version:2.0.5.0 + Copyright (c) 2017 by TAOS Data, Inc. All rights reserved. + taos> show databases; + name | created_time | ntables | vgroups | replica | quorum | days | keep1,keep2,keep(D) | cache(MB) | blocks | minrows | maxrows | wallevel | fsync | comp | precision | status | + =================================================================================================================================================================================================================================================================== + test | 2020-10-14 10:35:48.617 | 10 | 1 | 1 | 1 | 2 | 3650,3650,3650 | 16 | 6 | 100 | 4096 | 1 | 3000 | 2 | ms | ready | + log | 2020-10-12 09:08:21.651 | 4 | 1 | 1 | 1 | 10 | 30,30,30 | 1 | 3 | 100 | 4096 | 1 | 3000 | 2 | us | ready | + Query OK, 2 row(s) in set (0.045000s) + taos> +``` diff --git a/docs-en/14-reference/03-connector/_windows_install.mdx b/docs-en/14-reference/03-connector/_windows_install.mdx new file mode 100644 index 0000000000000000000000000000000000000000..2819be615ee0a80da9f0324d8d41e9b247e8a7f6 --- /dev/null +++ b/docs-en/14-reference/03-connector/_windows_install.mdx @@ -0,0 +1,31 @@ +import PkgList from "/components/PkgList"; + +1. Download the client installation package + + + + [All downloads](https://www.taosdata.com/cn/all-downloads/) + +2. Execute the installer, select the default value as prompted, and complete the installation +3. Installation path + + The default installation path is C:\TDengine, including the following files (directories). + + - _taos.exe_ : TDengine CLI command-line program + - _cfg_ : configuration file directory + - _driver_: client driver dynamic link library + - _examples_: sample programs bash/C/C#/go/JDBC/Python/Node.js + - _include_: header files + - _log_ : log file + - _unins000.exe_: uninstaller + +4. configure taos.cfg + + Edit the taos.cfg file (default path C:\TDengine\cfg\taos.cfg) and change the firstEP to the End Point of the TDengine server, for example: `h1.tdengine.com:6030`. + +:::tip + +1. If you use FQDN to connect to the server, you must ensure the local network environment DNS is configured, or add FQDN addressing records in the `hosts` file, e.g., edit C:\Windows\system32\drivers\etc\hosts and add a record like the following: `192.168.1.99 h1.taosd.com`.. +2. Uninstall: Run unins000.exe to uninstall the TDengine client driver. + +::: diff --git a/docs-en/14-reference/03-connector/cpp.mdx b/docs-en/14-reference/03-connector/cpp.mdx new file mode 100644 index 0000000000000000000000000000000000000000..4b388d32a9050645e268bb267d16e9a5b8aa4bda --- /dev/null +++ b/docs-en/14-reference/03-connector/cpp.mdx @@ -0,0 +1,452 @@ +--- +sidebar_position: 1 +sidebar_label: C/C++ +title: C/C++ Connector +--- + +C/C++ developers can use TDengine's client driver and the C/C++ connector, to develop their applications to connect to TDengine clusters for data writing, querying, and other functions. To use it, you need to include the TDengine header file _taos.h_, which lists the function prototypes of the provided APIs; the application also needs to link to the corresponding dynamic libraries on the platform where it is located. + +```c +#include +``` + +After TDengine server or client installation, `taos.h` is located at + +- Linux: `/usr/local/taos/include` +- Windows: `C:\TDengine\include` + +The dynamic libraries for the TDengine client driver are located in. + +- Linux: `/usr/local/taos/driver/libtaos.so` +- Windows: `C:\TDengine\taos.dll` + +## Supported platforms + +Please refer to [list of supported platforms](/reference/connector#supported-platforms) + +## Supported versions + +The version number of the TDengine client driver and the version number of the TDengine server require one-to-one correspondence and recommend using the same version of client driver as what the TDengine server version is. Although a lower version of the client driver is compatible to work with a higher version of the server, if the first three version numbers are the same (i.e., only the fourth version number is different), but it is not recommended. It is strongly discouraged to use a higher version of the client driver to access a lower version of the TDengine server. + +## Installation steps + +Please refer to the [Installation Steps](/reference/connector#installation-steps) for TDengine client driver installation + +## Establishing a connection + +The basic process of accessing a TDengine cluster using the client driver is to establish a connection, query and write data, close the connection, and clear the resource. + +The following is sample code for establishing a connection, which omits the query and writing sections and shows how to establish a connection, close a connection, and clear a resource. + +```c + TAOS *taos = taos_connect("localhost:6030", "root", "taosdata", NULL, 0); + if (taos == NULL) { + printf("failed to connect to server, reason:%s\n", "null taos" /*taos_errstr(taos)*/); + exit(1); + } + + /* put your code here for query and writing */ + + taos_close(taos); + taos_cleanup(); +``` + +In the above example code, `taos_connect()` establishes a connection to port 6030 on the host where the client application is located, `taos_close()` closes the current connection, and `taos_cleanup()` clears the resources requested and used by the client driver. + +:::note + +- If not specified, when the return value of the API is an integer, _0_ means success, the others are error codes representing the reason for failure, and when the return value is a pointer, _NULL_ means failure. +- All error codes and their corresponding causes are described in the `taoserror.h` file. + +::: + +## Example program + +This section shows sample code for standard access methods to TDengine clusters using the client driver. + +### Synchronous query example + +
+Synchronous query + +```c +{{#include examples/c/demo.c}} +``` + +
+ +### Asynchronous query example + +
+Asynchronous queries + +```c +{{#include examples/c/asyncdemo.c}} +``` + +
+ +### Parameter binding example + +
+Parameter Binding + +```c +{{#include examples/c/prepare.c}} +``` + +
+ +### Pattern-free writing example + +
+Mode free write + +```c +{{#include examples/c/schemaless.c}} +``` + +
+ +### Subscription and consumption example + +
+Subscribe and consume + +```c +{{#include examples/c/subscribe.c}} +``` + +
+ +:::info +More example code and downloads are available at [GitHub](https://github.com/taosdata/TDengine/tree/develop/examples/c). +You can find it in the installation directory under the `examples/c` path. This directory has a makefile and can be compiled under Linux by executing `make` directly. +**Hint:** When compiling in an ARM environment, please remove `-msse4.2` from the makefile. This option is only supported on the x64/x86 hardware platforms. + +::: + +## API reference + +The following describes the basic API, synchronous API, asynchronous API, subscription API, and schemaless write API of TDengine client driver, respectively. + +### Basic API + +The base API is used to do things like create database connections and provide a runtime environment for the execution of other APIs. + +- `void taos_init()` + + Initializes the runtime environment. If the API is not actively called, the driver will automatically call the API when `taos_connect()` is called, so the program generally does not need to call it manually. + +- `void taos_cleanup()` + + Clean up the runtime environment and should be called before the application exits. + +- ` int taos_options(TSDB_OPTION option, const void * arg, ...) ` + + Set client options, currently supports region setting (`TSDB_OPTION_LOCALE`), character set +(`TSDB_OPTION_CHARSET`), time zone +(`TSDB_OPTION_TIMEZONE`), configuration file path (`TSDB_OPTION_CONFIGDIR`) . The region setting, character set, and time zone default to the current settings of the operating system. + +- `char *taos_get_client_info()` + + Get client version information. + +- `TAOS *taos_connect(const char *host, const char *user, const char *pass, const char *db, int port)` + + Creates a database connection and initializes the connection context. Among the parameters required from the user are: + + - host: FQDN of any node in the TDengine cluster + - user: user name + - pass: password + - db: database name, if the user does not provide, it can also be connected correctly, the user can create a new database through this connection, if the user provides the database name, it means that the database user has already created, the default use of the database + - port: the port the taosd program is listening on + + NULL indicates a failure. The application needs to save the returned parameters for subsequent use. + + :::info + The same process can connect to multiple TDengine clusters according to different host/port + + ::: + +- `char *taos_get_server_info(TAOS *taos)` + + Get server-side version information. + +- ` int taos_select_db(TAOS *taos, const char *db)` + + Set the current default database to `db`. + +- `void taos_close(TAOS *taos)` + + Closes the connection, where `taos` is the handle returned by `taos_connect()`. + +### Synchronous query APIs + +The APIs described in this subsection are all synchronous interfaces. After being called by the application, it blocks and waits for a response until it gets a return result or an error message. + +- `TAOS_RES* taos_query(TAOS *taos, const char *sql)` + + Executes an SQL command, either a DQL, DML, or DDL statement. The `taos` parameter is a handle obtained with `taos_connect()`. You can't tell if the result failed by whether the return value is `NULL`, but by parsing the error code in the result set with the `taos_errno()` function. + +- `int taos_result_precision(TAOS_RES *res)` + + Returns the precision of the result set timestamp field, `0` for milliseconds, `1` for microseconds, and `2` for nanoseconds. + +- `TAOS_ROW taos_fetch_row(TAOS_RES *res)` + + Fetch the data in the query result set by row. + +- ` int taos_fetch_block(TAOS_RES *res, TAOS_ROW *rows)` + + Batch fetches the data in the query result set. The return value is the number of rows of the fetched data. + +- `int taos_num_fields(TAOS_RES *res)` and `int taos_field_count(TAOS_RES *res)` + + These two APIs are equivalent and are used to get the number of columns in the query result set. + +- `int* taos_fetch_lengths(TAOS_RES *res)` + + Gets the lengths of each field in the result set. The return value is an array whose length is the number of columns in the result set. + +- `int taos_affected_rows(TAOS_RES *res)` + + Get the number of rows affected by the executed SQL statement. + +- ` TAOS_FIELD *taos_fetch_fields(TAOS_RES *res)` + + Gets the properties of each column of the query result set (column name, column data type, column length), used in conjunction with `taos_num_fields()` to parse a tuple (one row) of data returned by `taos_fetch_row()`. The structure of `TAOS_FIELD` is as follows. + +```c +typedef struct taosField { + char name[65]; // column name + uint8_t type; // data type + int16_t bytes; // length, in bytes +} TAOS_FIELD; +``` + +- `void taos_stop_query(TAOS_RES *res)` + + Stops the execution of the current query. + +- ` void taos_free_result(TAOS_RES *res)` + + Frees the query result set and the associated resources. Be sure to call this API to free the resources after the query is completed. Otherwise, it may lead to a memory leak in the application. However, note that the application will crash if you call a function like `taos_consume()` to get the query results after freeing the resources. + +- `char *taos_errstr(TAOS_RES *res)` + + Get the reason for the failure of the last API call. The return value is an error message identified by a string. + +- 'int taos_errno(TAOS_RES *res)` + + Get the reason for the last API call failure. The return value is the error code. + +:::note +TDengine version 2.0 and above recommends that each thread of a database application create a separate connection or a connection pool based on threads. It is not recommended to pass the connection (TAOS\*) structure to different threads for shared use in the application. Queries, writes, etc., issued based on TAOS structures are multi-thread safe, but state quantities such as "USE statement" may interfere between threads. In addition, the C connector can dynamically create new database-oriented connections on demand (this procedure is not visible to the user), and it is recommended that `taos_close()` be called only at the final exit of the program to close the connection. + +::: + +### Asynchronous query API + +TDengine also provides a set of asynchronous API to handle data insertion and query operations with a higher performance. Given the same hardware and software environment, the asynchronous API can run data insertion 2 to 4 times faster than the synchronous API. The asynchronous API is called non-blocking and returns immediately before the system completes a specific database operation. The calling thread can go to work on other tasks, which can improve the performance of the whole application. Asynchronous APIs are particularly advantageous in the case of severe network latency. + +The asynchronous APIs require the application to provide a callback function with the following parameters: the first two parameters are consistent, and the third parameter depends on the API. The first parameter, `param`, is provided to the system when the application calls the asynchronous API. It is used for the callback so that the application can retrieve the context of the specific operation, depending on the implementation. The second parameter is the result set of the SQL operation. If it is NULL, such as insert operation, it means that there are no records returned, and if it is not NULL, such as select operation, it means that there are records returned. + +The asynchronous API has relatively high user requirements, so users can use it selectively according to specific application scenarios. The following are two important asynchronous APIs. + +- `void taos_query_a(TAOS *taos, const char *sql, void (*fp)(void *param, TAOS_RES *, int code), void *param);` + + Execute SQL command asynchronously. + + - taos: the database connection returned by calling `taos_connect()` + - sql: the SQL statement to be executed + - fp: user-defined callback function whose third parameter `code` is used to indicate whether the operation was successful or not, `0` means success, a negative number means failure (call `taos_errstr()` to get the reason for failure). When defining the callback function, the application mainly handles the second parameter `TAOS_RES *`, which is the result set returned by the query + - param: the application provides a parameter for the callback + +- `void taos_fetch_rows_a(TAOS_RES *res, void (*fp)(void *param, TAOS_RES *, int numOfRows), void *param);` + + Batch get the result set of an asynchronous query, which can only be used with `taos_query_a()`. The parameters are: + + - res: the result set returned by the `taos_query_a()` callback + - fp: callback function. Its parameter `param` is a user-definable parameter structure passed to the callback function; `numOfRows` is the number of rows of the fetched data (not a function of the entire query result set). In the callback function, the application can iterate forward to fetch each row of records in the batch by calling `taos_fetch_row()`. After reading all the rows in a block, the application needs to continue calling `taos_fetch_rows_a()` in the callback function to get the next batch of rows for processing until the number of rows returned, `numOfRows`, is zero (result return complete) or the number of rows is negative (query error). + +All TDengine's asynchronous APIs use a non-blocking call pattern. Applications can open multiple tables simultaneously using multiple threads and perform queries or inserts on each open table at the same time. It is important to note that **client applications must ensure that operations on the same table are fully serialized**. i.e., no second insert or query operation can be performed while an insert or query operation on the same table is incomplete (not returned). + +### Parameter Binding API + +In addition to direct calls to `taos_query()` to perform queries, TDengine also provides a set of `bind` APIs that supports parameter binding, similar in style to MySQL, and currently only supports using a question mark `? ` to represent the parameter to be bound. + +Starting with versions 2.1.1.0 and 2.1.2.0, TDengine has significantly improved the bind APIs to support for data writing (INSERT) scenarios. This avoids the resource consumption of SQL syntax parsing when writing data through the parameter binding interface, thus significantly improving write performance in most cases. A typical operation, in this case, is as follows. + +1. call `taos_stmt_init()` to create the parameter binding object. +2. call `taos_stmt_prepare()` to parse the INSERT statement. 3. +3. call `taos_stmt_set_tbname()` to set the table name if it is reserved in the INSERT statement but not the TAGS. +4. call `taos_stmt_set_tbname_tags()` to set the table name and TAGS values if the table name and TAGS are reserved in the INSERT statement (for example, if the INSERT statement takes an automatic table build). +5. call `taos_stmt_bind_param_batch()` to set the value of VALUES in multiple columns, or call `taos_stmt_bind_param()` to set the value of VALUES in a single row. +6. call `taos_stmt_add_batch()` to add the currently bound parameters to the batch. +7. you can repeat steps 3 to 6 to add more rows of data to the batch. +8. call `taos_stmt_execute()` to execute the prepared batch instructions. +9. When execution is complete, call `taos_stmt_close()` to release all resources. + +Note: If `taos_stmt_execute()` succeeds, you can reuse the parsed result of `taos_stmt_prepare()` to bind new data in steps 3 to 6 if you don't need to change the SQL command. However, if there is an execution error, it is not recommended to continue working in the current context but release the resources and start again with `taos_stmt_init()` steps. + +The specific functions related to the interface are as follows (see also the [prepare.c](https://github.com/taosdata/TDengine/blob/develop/examples/c/prepare.c) file for the way to use the corresponding functions) + +- `TAOS_STMT* taos_stmt_init(TAOS *taos)` + + Creates a TAOS_STMT object for subsequent calls. + +- ` int taos_stmt_prepare(TAOS_STMT *stmt, const char *sql, unsigned long length)` + + Parse a SQL command, and bind the parsed result and parameter information to `stmt`. If the parameter length is greater than 0, use this parameter as the length of the SQL command. If it is equal to 0, the length of the SQL command will be determined automatically. + +- `int taos_stmt_bind_param(TAOS_STMT *stmt, TAOS_BIND *bind)` + + Not as efficient as `taos_stmt_bind_param_batch()`, but can support non-INSERT type SQL statements. + To bind parameters, bind points to an array (representing the row of data to be bound), making sure that the number and order of the elements in this array are the same as the parameters in the SQL statement. taos_bind is used similarly to MYSQL_BIND in MySQL, as defined below. + + ```c + typedef struct TAOS_BIND { + int buffer_type; + void * buffer; + uintptr_t buffer_length; // not in use + uintptr_t * length; + int * is_null; + int is_unsigned; // not in use + int * error; // not in use + } TAOS_BIND; + ``` + +- `int taos_stmt_set_tbname(TAOS_STMT* stmt, const char* name)` + + (Available in 2.1.1.0 and later versions, only supported for replacing parameter values in INSERT statements) + When the table name in the SQL command uses `? ` placeholder, you can use this function to bind a specific table name. + +- `int taos_stmt_set_tbname_tags(TAOS_STMT* stmt, const char* name, TAOS_BIND* tags)` + + (Available in 2.1.2.0 and later versions, only supported for replacing parameter values in INSERT statements) + When the table name and TAGS in the SQL command both use `? `, you can use this function to bind the specific table name and the specific TAGS value. The most typical usage scenario is an INSERT statement that uses the automatic table building function (the current version does not support specifying specific TAGS columns.) The number of columns in the TAGS parameter needs to be the same as the number of TAGS requested in the SQL command. + +- `int taos_stmt_bind_param_batch(TAOS_STMT* stmt, TAOS_MULTI_BIND* bind)` + + (Available in 2.1.1.0 and later versions, only supported for replacing parameter values in INSERT statements) + To pass the data to be bound in a multi-column manner, it is necessary to ensure that the order of the data columns and the number of columns given here are the same as the VALUES parameter in the SQL statement. The specific definition of TAOS_MULTI_BIND is as follows. + + ```c + typedef struct TAOS_MULTI_BIND { + int buffer_type; + void * buffer; + uintptr_t buffer_length; + uintptr_t * length; + char * is_null; + int num; // the number of columns + } TAOS_MULTI_BIND; + ``` + +- ` int taos_stmt_add_batch(TAOS_STMT *stmt)` + + Adds the currently bound parameter to the batch. After calling this function, you can call `taos_stmt_bind_param()` or `taos_stmt_bind_param_batch()` again to bind a new parameter. Note that this function only supports INSERT/IMPORT statements. Other SQL command such as SELECT will return an error. + +- `int taos_stmt_execute(TAOS_STMT *stmt)` + + Execute the prepared statement. Currently, a statement can only be executed once. + +- ` TAOS_RES* taos_stmt_use_result(TAOS_STMT *stmt)` + + Gets the result set of a statement. Use the result set in the same way as in the non-parametric call. When finished, `taos_free_result()` should be called on this result set to free resources. + +- `int taos_stmt_close(TAOS_STMT *stmt)` + + Finish execution and release all resources. + +- ` char * taos_stmt_errstr(TAOS_STMT *stmt)` + + (Available in 2.1.3.0 and later versions) + Used to get error information if other STMT APIs return errors (return error codes or null pointers). + +### Schemaless Writing API + +In addition to writing data using the SQL method or the parameter binding API, writing can also be done using schemaless writing, which eliminates the need to create a super table/data sub-table structure in advance and writes the data directly. The TDengine system automatically creates and maintains the required table structure based on the written data content. The use of schemaless writing is described in the chapter [Schemaless Writing](/reference/schemaless/), and the C/C++ API used with it is described here. + +- `TAOS_RES* taos_schemaless_insert(TAOS* taos, const char* lines[], int numLines, int protocol, int precision)` + + **Function description** + This interface writes the text data of the line protocol to TDengine. + + **Parameter description** + - taos: database connection, established by the `taos_connect()` function. + - lines: text data. A pattern-free text string that meets the parsing format requirements. + - numLines: the number of lines of text data, cannot be 0. + - protocol: the protocol type of the lines, used to identify the text data format. + - precision: precision string for the timestamp in the text data. + + **return value** + TAOS_RES structure, application can get error message by using `taos_errstr()` and also error code by using `taos_errno()`. + In some cases, the returned TAOS_RES is `NULL`, and it is still possible to call `taos_errno()` to safely get the error code information. + The returned TAOS_RES needs to be freed by the caller. Otherwise, a memory leak will occur. + + **Description** + The protocol type is enumerated and contains the following three formats. + + - TSDB_SML_LINE_PROTOCOL: InfluxDB line protocol (Line Protocol) + - TSDB_SML_TELNET_PROTOCOL: OpenTSDB Telnet Text Line Protocol + - TSDB_SML_JSON_PROTOCOL: OpenTSDB Json protocol format + + The timestamp resolution definitions are in the taos.h file, as follows + + - TSDB_SML_TIMESTAMP_NOT_CONFIGURED = 0, + - TSDB_SML_TIMESTAMP_HOURS, + - TSDB_SML_TIMESTAMP_MINUTES, + - TSDB_SML_TIMESTAMP_SECONDS, + - TSDB_SML_TIMESTAMP_MILLI_SECONDS, + - TSDB_SML_TIMESTAMP_MICRO_SECONDS, + - TSDB_SML_TIMESTAMP_NANO_SECONDS + + Note that the timestamp resolution parameter only takes effect when the protocol type is `SML_LINE_PROTOCOL`. + For OpenTSDB's text protocol, timestamp resolution follows its official resolution rules - time precision is confirmed by the number of characters contained in the timestamp. + + **Supported Versions** + This feature interface is supported from version 2.3.0.0. + +### Subscription and Consumption API + +The Subscription API currently supports subscribing to one or more tables and continuously fetching the latest data written to them by polling periodically. + +- `TAOS_SUB *taos_subscribe(TAOS* taos, int restart, const char* topic, const char *sql, TAOS_SUBSCRIBE_CALLBACK fp, void *param, int interval)` + + This function is responsible for starting the subscription service, returning the subscription object on success and `NULL` on failure, with the following parameters. + + - taos: the database connection that has been established + - restart: if the subscription already exists, whether to restart or continue the previous subscription + - topic: the topic of the subscription (i.e., the name). This parameter is the unique identifier of the subscription + - sql: the query statement of the subscription, this statement can only be _select_ statement, only the original data should be queried, only the data can be queried in time order + - fp: the callback function when the query result is received (the function prototype will be introduced later), only used when called asynchronously. This parameter should be passed `NULL` when called synchronously + - param: additional parameter when calling the callback function, the system API will pass it to the callback function as it is, without any processing + - interval: polling period in milliseconds. The callback function will be called periodically according to this parameter when called asynchronously. not recommended to set this parameter too small To avoid impact on system performance when called synchronously. If the interval between two calls to `taos_consume()` is less than this period, the API will block until the interval exceeds this period. + +- ` typedef void (*TAOS_SUBSCRIBE_CALLBACK)(TAOS_SUB* tsub, TAOS_RES *res, void* param, int code)` + + The prototype of the callback function in asynchronous mode with the following parameters + + - tsub: subscription object + - res: query result set, note that there may be no records in the result set + - param: additional parameters provided by the client program when calling `taos_subscribe()` + - code: error code + + :::note + The callback function should not take too long to process, especially if the returned result set has a lot of data. Otherwise, it may lead to an abnormal state, such as client blocking. If you must perform complex calculations, we recommend handling them in a separate thread. + + ::: + +- `TAOS_RES *taos_consume(TAOS_SUB *tsub)` + + In synchronous mode, this function is used to fetch the results of a subscription. The user application places it in a loop. If the interval between two calls to `taos_consume()` is less than the polling period of the subscription, the API will block until the interval exceeds this period. If a new record arrives in the database, the API returns that latest record. Otherwise, it returns an empty result set with no records. If the return value is `NULL`, there is a system error. This API should not be called by user programs in asynchronous mode. + + :::note + After calling `taos_consume()`, the user application should make sure to call `taos_fetch_row()` or `taos_fetch_block()` to process the subscription results as soon as possible. Otherwise, the server-side will keep caching the query result data waiting to be read by the client, which in extreme cases will cause the server side to run out of memory and affect the stability of the service. + + ::: + +- `void taos_unsubscribe(TAOS_SUB *tsub, int keepProgress)` + + Unsubscribe. If the parameter `keepProgress` is not 0, the API will keep the progress information of the subscription, and subsequent calls to `taos_subscribe()` will continue based on this progress; otherwise, the progress information will be deleted, and subsequent readings will have to be restarted. diff --git a/docs-en/14-reference/03-connector/csharp.mdx b/docs-en/14-reference/03-connector/csharp.mdx new file mode 100644 index 0000000000000000000000000000000000000000..2969392a0594ff0705e88bede5be90fb9dfd646d --- /dev/null +++ b/docs-en/14-reference/03-connector/csharp.mdx @@ -0,0 +1,190 @@ +--- +toc_max_heading_level: 4 +sidebar_position: 7 +sidebar_label: C# +title: C# Connector +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +import Preparation from "./_preparation.mdx" +import CSInsert from "../../07-develop/03-insert-data/_cs_sql.mdx" +import CSInfluxLine from "../../07-develop/03-insert-data/_cs_line.mdx" +import CSOpenTSDBTelnet from "../../07-develop/03-insert-data/_cs_opts_telnet.mdx" +import CSOpenTSDBJson from "../../07-develop/03-insert-data/_cs_opts_json.mdx" +import CSQuery from "../../07-develop/04-query-data/_cs.mdx" +import CSAsyncQuery from "../../07-develop/04-query-data/_cs_async.mdx" + + +`TDengine.Connector` is a C# language connector provided by TDengine that allows C# developers to develop C# applications that access TDengine cluster data. + +The `TDengine.Connector` connector supports connect to TDengine instances via the TDengine client driver (taosc), providing data writing, querying, subscription, schemaless writing, bind interface, etc. The `TDengine.Connector` currently does not provide a REST connection interface. Developers can write their RESTful application by referring to the [REST API](/reference/rest-api/) documentation. + +This article describes how to install `TDengine.Connector` in a Linux or Windows environment and connect to TDengine clusters via `TDengine.Connector` to perform basic operations such as data writing and querying. + +The source code of `TDengine.Connector` is hosted on [GitHub](https://github.com/taosdata/taos-connector-dotnet). + +## Supported Platforms + +The supported platforms are the same as those supported by the TDengine client driver. + +## Version support + +Please refer to [version support list](/reference/connector#version-support) + +## Supported features + +1. Connection Management +2. General Query +3. Continuous Query +4. Parameter Binding +5. Subscription +6. Schemaless + +## Installation Steps + +### Pre-installation preparation + +* Install the [.NET SDK](https://dotnet.microsoft.com/download) +* [Nuget Client](https://docs.microsoft.com/en-us/nuget/install-nuget-client-tools) (optional installation) +* Install TDengine client driver, please refer to [Install client driver](/reference/connector#Install client driver) for details + +### Install via dotnet CLI + + + + +You can reference the `TDengine.Connector` published in Nuget to the current project via the `dotnet` command under the path of the existing .NET project. + +``` bash +dotnet add package TDengine.Connector +``` + + + + +You can download TDengine's source code and directly reference the latest version of the TDengine.Connector library + +```bash +git clone https://github.com/taosdata/TDengine.git +cd TDengine/src/connector/C#/src/ +cp -r TDengineDriver/ myProject + +cd myProject +dotnet add TDengineDriver/TDengineDriver.csproj +``` + + + +## Create a connection + +``` C# +using TDengineDriver; + +namespace TDengineExample +{ + + internal class EstablishConnection + { + static void Main(String[] args) + { + string host = "localhost"; + short port = 6030; + string username = "root"; + string password = "taosdata"; + string dbname = ""; + + var conn = TDengine.Connect(host, username, password, dbname, port); + if (conn == IntPtr.Zero) + { + Console.WriteLine("Connect to TDengine failed"); + } + else + { + Console.WriteLine("Connect to TDengine success"); + } + TDengine.Close(conn); + TDengine.Cleanup(); + } + } +} + +``` + +## Usage examples + +### Write data + +#### SQL Write + + + +#### InfluxDB line protocol write + + + +#### OpenTSDB Telnet line protocol write + + + +#### OpenTSDB JSON line protocol write + + + +### Query data + +#### Synchronous Query + + + +#### Asynchronous query + + + +### More sample programs + +|Sample program |Sample program description | +|--------------------------------------------------------------------------------------------------------------------|------------ --------------------------------| +| [C#checker](https://github.com/taosdata/TDengine/tree/develop/examples/C%23/C%23checker) | Using TDengine.Connector, you can test C# Driver's synchronous writes and queries | +| [TDengineTest](https://github.com/taosdata/TDengine/tree/develop/examples/C%23/TDengineTest) | A simple example of writing and querying using TDengine. +| [insertCn](https://github.com/taosdata/TDengine/tree/develop/examples/C%23/insertCn) | Example of writing and querying Chinese characters using TDengine. +| [jsonTag](https://github.com/taosdata/TDengine/tree/develop/examples/C%23/jsonTag) | Example of writing and querying JSON tag type data using TDengine. +| [stmt](https://github.com/taosdata/TDengine/tree/develop/examples/C%23/stmt) | Example of parameter binding using TDengine. +| [schemaless](https://github.com/taosdata/TDengine/tree/develop/examples/C%23/schemaless) | Example of writing with schemaless implemented using TDengine. |schemaless +| [benchmark](https://github.com/taosdata/TDengine/tree/develop/examples/C%23/taosdemo) | A simple benchmark implemented using TDengine. +| [async query](https://github.com/taosdata/taos-connector-dotnet/blob/develop/examples/QueryAsyncSample.cs) | Example of an asynchronous query implemented using TDengine. Example of an asynchronous query +| [subscribe](https://github.com/taosdata/taos-connector-dotnet/blob/develop/examples/SubscribeSample.cs) | Example of subscribing to data using TDengine. Data example + +## Important update records + +| TDengine.Connector | Description | +|--------------------|--------------------------------| +| 1.0.6 | Fix schemaless bug in 1.0.4 and 1.0.5. | +| 1.0.5 | Fix Windows sync query Chinese error bug. | 1.0.4 | Fix schemaless bug. +| 1.0.4 | Add asynchronous query, subscription, and other functions. Fix the binding parameter bug. +| 1.0.3 | Add parameter binding, schemaless, JSON tag, etc. | new +| 1.0.2 | Add connection management, synchronous query, error messages, etc. ## Other + +## Other descriptions + +### Third-party driver + +`Taos` is an ADO.NET connector for TDengine, supporting Linux and Windows platforms. Community contributor `Maikebing@@maikebing contributes the connector`. Please refer to: + +* Interface download: +* Usage notes: + +## Frequently Asked Questions + +1. "Unable to establish connection", "Unable to resolve FQDN" + + Usually, it cause by the FQDN configuration is incorrect, you can refer to [How to understand TDengine's FQDN (Chinese)](https://www.taosdata.com/blog/2021/07/29/2741.html) to solve it. 2. + +Unhandled exception. System.DllNotFoundException: Unable to load DLL 'taos' or one of its dependencies: The specified module cannot be found. + + This is usually because the program did not find the dependent client driver. The solution is to copy `C:\TDengine\driver\taos.dll` to the `C:\Windows\System32\` directory on Windows, and create the following soft link on Linux `ln -s /usr/local/taos/driver/libtaos.so.x.x .x.x /usr/lib/libtaos.so` will work. + +## API Reference + +[API Reference](https://docs.taosdata.com/api/connector-csharp/html/860d2ac1-dd52-39c9-e460-0829c4e5a40b.htm) diff --git a/docs-en/14-reference/03-connector/go.mdx b/docs-en/14-reference/03-connector/go.mdx new file mode 100644 index 0000000000000000000000000000000000000000..fd5930f07ff7184bd8dd5ff19cd3860f9718eaf9 --- /dev/null +++ b/docs-en/14-reference/03-connector/go.mdx @@ -0,0 +1,412 @@ +--- +toc_max_heading_level: 4 +sidebar_position: 4 +sidebar_label: Go +title: TDengine Go Connector +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +import Preparation from "./_preparation.mdx" +import GoInsert from "../../07-develop/03-insert-data/_go_sql.mdx" +import GoInfluxLine from "../../07-develop/03-insert-data/_go_line.mdx" +import GoOpenTSDBTelnet from "../../07-develop/03-insert-data/_go_opts_telnet.mdx" +import GoOpenTSDBJson from "../../07-develop/03-insert-data/_go_opts_json.mdx" +import GoQuery from "../../07-develop/04-query-data/_go.mdx" + +`driver-go` is the official Go language connector for TDengine, which implements the interface to the Go language [database/sql](https://golang.org/pkg/database/sql/) package. Go developers can use it to develop applications that access TDengine cluster data. + +`driver-go` provides two ways to establish connections. One is **native connection**, which connects to TDengine instances natively through the TDengine client driver (taosc), supporting data writing, querying, subscriptions, schemaless writing, and bind interface. The other is the **REST connection**, which connects to TDengine instances via the REST interface provided by taosAdapter. The set of features implemented by the REST connection differs slightly from the native connection. + +This article describes how to install `driver-go` and connect to TDengine clusters and perform basic operations such as data query and data writing through `driver-go`. + +The source code of `driver-go` is hosted on [GitHub](https://github.com/taosdata/driver-go). + +## Supported Platforms + +Native connections are supported on the same platforms as the TDengine client driver. +REST connections are supported on all platforms that can run Go. + +## Version support + +Please refer to [version support list](/reference/connector#version-support) + +## Supported features + +### Native connections + +A "native connection" is established by the connector directly to the TDengine instance via the TDengine client driver (taosc). The supported functional features are: + +* Normal queries +* Continuous queries +* Subscriptions +* schemaless interface +* parameter binding interface + +### REST connection + +A "REST connection" is a connection between the application and the TDengine instance via the REST API provided by the taosAdapter component. The following features are supported: + +* General queries +* Continuous queries + +## Installation steps + +### Pre-installation + +* Install Go development environment (Go 1.14 and above, GCC 4.8.5 and above) +* If you use the native connector, please install the TDengine client driver. Please refer to [Install Client Driver](/reference/connector#Install Client Driver) for specific steps + +Configure the environment variables and check the command. + +* ```go env`` +* ```gcc -v`` + +### Use go get to install + +``go get -u github.com/taosdata/driver-go/v2@develop`` + +### Manage with go mod + +1. Initialize the project with the `go mod` command. + + ``text + go mod init taos-demo + ``` text + +2. Introduce taosSql + + ```go + import ( + "database/sql" + _ "github.com/taosdata/driver-go/v2/taosSql" + ) + ``` + +3. Update the dependency packages with `go mod tidy`. + + ```text + go mod tidy + ``` 4. + +4. Run the program with `go run taos-demo` or compile the binary with the `go build` command. + + ```text + go run taos-demo + go build + ``` + +## Create a connection + +### Data source name (DSN) + +Data source names have a standard format, e.g. [PEAR DB](http://pear.php.net/manual/en/package.database.db.intro-dsn.php), but no type prefix (square brackets indicate optionally): + +``` text +[username[:password]@][protocol[(address)]]/[dbname][?param1=value1&... ¶mN=valueN] +``` + +DSN in full form. + +```text +username:password@protocol(address)/dbname?param=value +``` + +### Connecting via connector + + + + +_taosSql_ implements Go's `database/sql/driver` interface via cgo. You can use the [`database/sql`](https://golang.org/pkg/database/sql/) interface by simply introducing the driver. + +Use `taosSql` as `driverName` and use a correct [DSN](#DSN) as `dataSourceName`, DSN supports the following parameters. + +* configPath specifies the `taos.cfg` directory + +Example. + +```go +package main + +import ( + "database/sql" + "fmt" + + _ "github.com/taosdata/driver-go/v2/taosSql" +) + +func main() { + var taosUri = "root:taosdata@tcp(localhost:6030)/" + taos, err := sql.Open("taosSql", taosUri) + if err ! = nil { + fmt.Println("failed to connect TDengine, err:", err) + return + } +} +``` + + + + +_taosRestful_ implements Go's `database/sql/driver` interface via `http client`. You can use the [`database/sql`](https://golang.org/pkg/database/sql/) interface by simply introducing the driver. + +Use `taosRestful` as `driverName` and use a correct [DSN](#DSN) as `dataSourceName` with the following parameters supported by the DSN. + +* `disableCompression` whether to accept compressed data, default is true do not accept compressed data, set to false if transferring data using gzip compression. +* `readBufferSize` The default size of the buffer for reading data is 4K (4096), which can be adjusted upwards when the query result has a lot of data. + +Example. + +```go +package main + +import ( + "database/sql" + "fmt" + + _ "github.com/taosdata/driver-go/v2/taosRestful" +) + +func main() { + var taosUri = "root:taosdata@http(localhost:6041)/" + taos, err := sql.Open("taosRestful", taosUri) + if err ! = nil { + fmt.Println("failed to connect TDengine, err:", err) + return + } +} +``` + + + +## Usage examples + +### Write data + +#### SQL Write + + + +#### InfluxDB line protocol write + + + +#### OpenTSDB Telnet line protocol write + + + +#### OpenTSDB JSON line protocol write + + + +### Query data + + + +### More sample programs + +* [sample program](https://github.com/taosdata/TDengine/tree/develop/examples/go) +* [Video tutorial](https://www.taosdata.com/blog/2020/11/11/1951.html). + +## Usage limitations + +Since the REST interface is stateless, the `use db` syntax will not work. You need to put the db name into the SQL command, e.g. `create table if not exists tb1 (ts timestamp, a int)` to `create table if not exists test.tb1 (ts timestamp, a int)` otherwise it will report the error `[0x217] Database not specified or available`. + +You can also put the db name in the DSN by changing `root:taosdata@http(localhost:6041)/` to `root:taosdata@http(localhost:6041)/test`. This method is supported by taosAdapter in TDengine 2.4.0.5. is supported since TDengine 2.4.0.5. Executing the `create database` statement when the specified db does not exist will not report an error while executing other queries or writing against that db will report an error. + +The complete example is as follows. + +```go +package main + +import ( + "database/sql" + "fmt" + "time" + + _ "github.com/taosdata/driver-go/v2/taosRestful" +) + +func main() { + var taosDSN = "root:taosdata@http(localhost:6041)/test" + taos, err := sql.Open("taosRestful", taosDSN) + if err != nil { + fmt.Println("failed to connect TDengine, err:", err) + return + } + defer taos.Close() + taos.Exec("create database if not exists test") + taos.Exec("create table if not exists tb1 (ts timestamp, a int)") + _, err = taos.Exec("insert into tb1 values(now, 0)(now+1s,1)(now+2s,2)(now+3s,3)") + if err != nil { + fmt.Println("failed to insert, err:", err) + return + } + rows, err := taos.Query("select * from tb1") + if err != nil { + fmt.Println("failed to select from table, err:", err) + return + } + + defer rows.Close() + for rows.Next() { + var r struct { + ts time.Time + a int + } + err := rows.Scan(&r.ts, &r.a) + if err != nil { + fmt.Println("scan error:\n", err) + return + } + fmt.Println(r.ts, r.a) + } +} +``` + +## Frequently Asked Questions + +1. Cannot find the package `github.com/taosdata/driver-go/v2/taosRestful` + + Change the `github.com/taosdata/driver-go/v2` line in the require block of the `go.mod` file to `github.com/taosdata/driver-go/v2 develop`, then execute `go mod tidy`. + +2. bind interface in database/sql crashes + + REST does not support parameter binding related interface. It is recommended to use `db.Exec` and `db.Query`. + +3. error `[0x217] Database not specified or available` after executing other statements with `use db` statement + + The execution of SQL command in the REST interface is not contextual, so using `use db` statement will not work, see the usage restrictions section above. + +4. use `taosSql` without error but use `taosRestful` with error `[0x217] Database not specified or available` + + Because the REST interface is stateless, using the `use db` statement will not take effect. See the usage restrictions section above. + +5. Upgrade `github.com/taosdata/driver-go/v2/taosRestful` + + Change the `github.com/taosdata/driver-go/v2` line in the `go.mod` file to `github.com/taosdata/driver-go/v2 develop`, then execute `go mod tidy`. + +6. `readBufferSize` parameter has no significant effect after being increased + + If you increase `readBufferSize` will reduce the number of `syscall` calls when fetching results. If the query result is smaller, modifying this parameter will not improve significantly. If you increase the parameter value too much, the bottleneck will be parsing JSON data. If you need to optimize the query speed, you must adjust the value according to the actual situation to achieve the best query result. + +7. `disableCompression` parameter is set to `false` when the query efficiency is reduced + + When set `disableCompression` parameter to `false`, the query result will be compressed by `gzip` and then transmitted, so you have to decompress the data by `gzip` after getting it. + +8. `go get` command can't get the package, or timeout to get the package + + Set Go proxy `go env -w GOPROXY=https://goproxy.cn,direct`. + +## Common APIs + +### database/sql API + +* `sql.Open(DRIVER_NAME string, dataSourceName string) *DB` + + Use This API to open a DB, returning an object of type \*DB. + +:::info +This API is created successfully without checking permissions, but only when you execute a Query or Exec, and check if user/password/host/port is legal. +::: + +* `func (db *DB) Exec(query string, args . .interface{}) (Result, error)` + + `sql.Open` built-in method to execute non-query related SQL. + +* `func (db *DB) Query(query string, args ... . interface{}) (*Rows, error)` + + `sql.Open` Built-in method to execute query statements. + +### Advanced functions (af) API + +The `af` package encapsulates TDengine advanced functions such as connection management, subscriptions, schemaless, parameter binding, etc. + +#### Connection management + +* `af.Open(host, user, pass, db string, port int) (*Connector, error)` + + This API creates a connection to taosd via cgo. + +* `func (conn *Connector) Close() error` + + Closes the connection. + +#### Subscribe to + +* `func (conn *Connector) Subscribe(restart bool, topic string, sql string, interval time.Duration) (Subscriber, error)` + + Subscribe to data. + +* `func (s *taosSubscriber) Consume() (driver.Rows, error)` + + Consume the subscription data, returning the `Rows` structure of the `database/sql/driver` package. + +* `func (s *taosSubscriber) Unsubscribe(keepProgress bool)` + + Unsubscribe from data. + +#### schemaless + +* `func (conn *Connector) InfluxDBInsertLines(lines []string, precision string) error` + + Write to influxDB line protocol. + +* `func (conn *Connector) OpenTSDBInsertTelnetLines(lines []string) error` + + Write OpenTDSB telnet protocol data. + +* `func (conn *Connector) OpenTSDBInsertJsonPayload(payload string) error` + + Writes OpenTSDB JSON protocol data. + +#### parameter binding + +* `func (conn *Connector) StmtExecute(sql string, params *param.Param) (res driver.Result, err error)` + + Parameter bound single row insert. + +* `func (conn *Connector) StmtQuery(sql string, params *param.Param) (rows driver.Rows, err error)` + + Parameter bound query that returns the `Rows` structure of the `database/sql/driver` package. + +* `func (conn *Connector) InsertStmt() *insertstmt. + + Initialize the parameters. + +* `func (stmt *InsertStmt) Prepare(sql string) error` + + Parameter binding preprocessing SQL statement. + +* `func (stmt *InsertStmt) SetTableName(name string) error` + + Bind the set table name parameter. + +* `func (stmt *InsertStmt) SetSubTableName(name string) error` + + Parameter binding to set the sub table name. + +* `func (stmt *InsertStmt) BindParam(params []*param.Param, bindType *param.ColumnType) error` + + Parameter bind multiple rows of data. + +* `func (stmt *InsertStmt) AddBatch() error` + + Add to a parameter-bound batch. + +* `func (stmt *InsertStmt) Execute() error` + + Execute a parameter binding. + +* `func (stmt *InsertStmt) GetAffectedRows() int` + + Gets the number of affected rows inserted by the parameter binding. + +* `func (stmt *InsertStmt) Close() error` + + Closes the parameter binding. + +## API Reference + +Full API see [driver-go documentation](https://pkg.go.dev/github.com/taosdata/driver-go/v2) diff --git a/docs-en/14-reference/03-connector/java.mdx b/docs-en/14-reference/03-connector/java.mdx new file mode 100644 index 0000000000000000000000000000000000000000..328907c4d781bdea8d30623e01d431cedbf8d0fa --- /dev/null +++ b/docs-en/14-reference/03-connector/java.mdx @@ -0,0 +1,841 @@ +--- +toc_max_heading_level: 4 +sidebar_position: 2 +sidebar_label: Java +title: TDengine Java Connector +description: TDengine Java based on JDBC API and provide both native and REST connections +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +'taos-jdbcdriver' is TDengine's official Java language connector, which allows Java developers to develop applications that access the TDengine database. 'taos-jdbcdriver' implements the interface of the JDBC driver standard and provides two forms of connectors. One is to connect to a TDengine instance natively through the TDengine client driver (taosc), which supports functions including data writing, querying, subscription, schemaless writing, and bind interface. And the other is to connect to a TDengine instance through the REST interface provided by taosAdapter (2.4.0.0 and later). REST connections implement has a slight differences to compare the set of features implemented and native connections. + +![tdengine-connector](tdengine-jdbc-connector.png) + +The preceding diagram shows two ways for a Java app to access TDengine via connector: + +- JDBC native connection: Java applications use TSDBDriver on physical node 1 (pnode1) to call client-driven directly (`libtaos.so` or `taos.dll`) APIs to send writing and query requests to taosd instances located on physical node 2 (pnode2). +- JDBC REST connection: The Java application encapsulates the SQL as a REST request via RestfulDriver, sends it to the REST server of physical node 2 (taosAdapter), requests TDengine server through the REST server, and returns the result. + +Using REST connection, which does not rely on TDengine client drivers.It can be cross-platform more convenient and flexible but introduce about 30% lower performance than native connection. + +:::info +TDengine's JDBC driver implementation is as consistent as possible with the relational database driver. Still, there are differences in the use scenarios and technical characteristics of TDengine and relational object databases, so 'taos-jdbcdriver' also has some differences from traditional JDBC drivers. You need to pay attention to the following points when using: + +- TDengine does not currently support delete operations for individual data records. +- Transactional operations are not currently supported. + +::: + +## Supported platforms + +Native connection supports the same platform as TDengine client-driven support. +REST connection supports all platforms that can run Java. + +## Version support + +Please refer to [Version Support List](/reference/connector#version-support). + +## TDengine DataType vs. Java DataType + +TDengine currently supports timestamp, number, character, Boolean type, and the corresponding type conversion with Java is as follows: + +| TDengine DataType | JDBCType (driver version < 2.0.24) | JDBCType (driver version > = 2.0.24) | +| ----------------- | --------------------------------- | ---------------------------------- | +| TIMESTAMP | java.lang.Long | java.sql.Timestamp | +| INT | java.lang.Integer | java.lang.Integer | +| BIGINT | java.lang.Long | java.lang.Long | +| FLOAT | java.lang.Float | java.lang.Float | +| DOUBLE | java.lang.Double | java.lang.Double | +| SMALLINT | java.lang.Short | java.lang.Short | +| TINYINT | java.lang.Byte | java.lang.Byte | +| BOOL | java.lang.Boolean | java.lang.Boolean | +| BINARY | java.lang.String | byte array | +| NCHAR | java.lang.String | java.lang.String | +| JSON | - | java.lang.String | + +**Note**: Only TAG supports JSON types + +## Installation steps + +### Pre-installation preparation + +Before using Java Connector to connect to the database, the following conditions are required. + +- Java 1.8 or above runtime environment and Maven 3.6 or above installed +- TDengine client driver installed (required for native connections, not required for REST connections), please refer to [Installing Client Driver](/reference/connector#Install-Client-Driver) + +### Install the connectors + + + + +- [sonatype](https://search.maven.org/artifact/com.taosdata.jdbc/taos-jdbcdriver) +- [mvnrepository](https://mvnrepository.com/artifact/com.taosdata.jdbc/taos-jdbcdriver) +- [maven.aliyun](https://maven.aliyun.com/mvn/search) + +Add following dependency in the `pom.xml` file of your Maven project: + +```xml-dtd + + com.taosdata.jdbc + taos-jdbcdriver + 2.0.** + +``` + + + + +You can build Java connector from source code after clone TDengine project: + +```shell +git clone https://github.com/taosdata/TDengine.git +cd TDengine/src/connector/jdbc +mvn clean install -Dmaven.test.skip=true +``` + +After compilation, a jar package of taos-jdbcdriver-2.0.XX-dist .jar is generated in the target directory, and the compiled jar file is automatically placed in the local Maven repository. + + + + +## Establish a connection + +TDengine's JDBC URL specification format is: +`jdbc:[TAOS| TAOS-RS]://[host_name]:[port]/[database_name]? [user={user}|&password={password}|&charset={charset}|&cfgdir={config_dir}|&locale={locale}|&timezone={timezone}]` + +For establishing connections, native connections differ slightly from REST connections. + + + + +```java +Class.forName("com.taosdata.jdbc.TSDBDriver"); +String jdbcUrl = "jdbc:TAOS://taosdemo.com:6030/test?user=root&password=taosdata"; +Connection conn = DriverManager.getConnection(jdbcUrl); +``` + +In the above example, TSDBDriver, which uses a JDBC native connection, establishes a connection to a hostname `taosdemo.com`, port `6030` (the default port for TDengine), and a database named `test`. In this URL, the user name `user` is specified as `root`, and the `password` is `taosdata`. + +Note: With JDBC native connections, taos-jdbcdriver relies on the client driver (`libtaos.so` on Linux; `taos.dll` on Windows). + +The configuration parameters in the URL are as follows: + +- user: Log in to the TDengine username. The default value is 'root'. +- password: User login password, the default value is 'taosdata'. +- cfgdir: client configuration file directory path, default '/etc/taos' on Linux OS, 'C:/TDengine/cfg' on Windows OS. +- charset: The character set used by the client, the default value is the system character set. +- locale: Client locale, by default, use the system's current locale. +- timezone: The time zone used by the client, the default value is the system's current time zone. +- batchfetch: true: pulls result sets in batches when executing queries; false: pulls result sets row by row. The default value is: false. Enabling batch pulling and obtaining a batch of data can improve query performance when the query data volume is large. +- batchErrorIgnore:true: When executing statement executeBatch, if there is a SQL execution failure in the middle, the following SQL will continue to be executed. false: No more statements after the failed SQL are executed. The default value is: false. + +For more information about JDBC native connections, see [Video Tutorial](https://www.taosdata.com/blog/2020/11/11/1955.html). + +**Connect using the TDengine client-driven configuration file ** + +When you use a JDBC native connection to connect to a TDengine cluster, you can use the TDengine client driver configuration file to specify parameters such as `firstEp` and `secondEp` of the cluster in the configuration file as below: + +1. Do not specify hostname and port in Java applications. + +```java +public Connection getConn() throws Exception{ + Class.forName("com.taosdata.jdbc.TSDBDriver"); + String jdbcUrl = "jdbc:TAOS://:/test?user=root&password=taosdata"; + Properties connProps = new Properties(); + connProps.setProperty(TSDBDriver.PROPERTY_KEY_CHARSET, "UTF-8"); + connProps.setProperty(TSDBDriver.PROPERTY_KEY_LOCALE, "en_US.UTF-8"); + connProps.setProperty(TSDBDriver.PROPERTY_KEY_TIME_ZONE, "UTC-8"); + Connection conn = DriverManager.getConnection(jdbcUrl, connProps); + return conn; +} +``` + +2. specify the firstEp and the secondEp in the configuration file taos.cfg + +```shell +# first fully qualified domain name (FQDN) for TDengine system +firstEp cluster_node1:6030 + +# second fully qualified domain name (FQDN) for TDengine system, for cluster only +secondEp cluster_node2:6030 + +# default system charset +# charset UTF-8 + +# system locale +# locale en_US.UTF-8 +``` + +In the above example, JDBC uses the client's configuration file to establish a connection to a hostname `cluster_node1`, port 6030, and a database named `test`. When the firstEp node in the cluster fails, JDBC attempts to connect to the cluster using secondEp. + +In TDengine, as long as one node in firstEp and secondEp is valid, the connection to the cluster can be established normally. + +> **Note**: The configuration file here refers to the configuration file on the machine where the application that calls the JDBC Connector is located, the default path is `/etc/taos/taos.cfg` on Linux, and the default path is `C://TDengine/cfg/taos.cfg` on Windows. + + + + +```java +Class.forName("com.taosdata.jdbc.rs.RestfulDriver"); +String jdbcUrl = "jdbc:TAOS-RS://taosdemo.com:6041/test?user=root&password=taosdata"; +Connection conn = DriverManager.getConnection(jdbcUrl); +``` + +In the above example, a RestfulDriver with a JDBC REST connection is used to establish a connection to a database named `test` with hostname `taosdemo.com` on port `6041`. The URL specifies the user name as `root` and the password as `taosdata`. + +There is no dependency on the client driver when Using a JDBC REST connection. Compared to a JDBC native connection, only the following are required: 1. + +1. driverClass specified as "com.taosdata.jdbc.rs.RestfulDriver". +2. jdbcUrl starting with "jdbc:TAOS-RS://". +3. use 6041 as the connection port. + +The configuration parameters in the URL are as follows. + +- user: Login TDengine user name, default value 'root'. +- password: user login password, default value 'taosdata'. +- batchfetch: true: pull the result set in batch when executing the query; false: pull the result set row by row. The default value is false. batchfetch uses HTTP for data transfer. The JDBC REST connection supports bulk data pulling function in taos-jdbcdriver-2.0.38 and TDengine 2.4.0.12 and later versions. taos-jdbcdriver and TDengine transfer data via WebSocket connection. Compared with HTTP, WebSocket enables JDBC REST connection to support large data volume querying and improve query performance. +- batchErrorIgnore: true: when executing executeBatch of Statement, if one SQL execution fails in the middle, continue to execute the following SQL. false: no longer execute any statement after the failed SQL. The default value is: false. + +**Note**: Some configuration items (e.g., locale, timezone) do not work in the REST connection. + +:::note + +- Unlike the native connection method, the REST interface is stateless. When using the JDBC REST connection, you need to specify the database name of the table and super table in SQL. For example. + +```sql +INSERT INTO test.t1 USING test.weather (ts, temperature) TAGS('beijing') VALUES(now, 24.6); +``` + +- Starting from taos-jdbcdriver-2.0.36 and TDengine 2.2.0.0, if dbname is specified in the URL, JDBC REST connections will use `/rest/sql/dbname` as the URL for REST requests by default, and there is no need to specify dbname in SQL. For example, if the URL is `jdbc:TAOS-RS://127.0.0.1:6041/test`, then the SQL can be executed: insert into t1 using weather(ts, temperature) tags('beijing') values(now, 24.6); + +::: + + + + +### Specify the URL and Properties to get the connection + +In addition to getting the connection from the specified URL, you can use Properties to specify parameters when the connection is established. + +**Note**: + +- The client parameter set in the application is process-level. If you want to update the parameters of the client, you need to restart the application. This is because the client parameter is a global parameter that takes effect only the first time the application is set. +- The following sample code is based on taos-jdbcdriver-2.0.36. + +```java +public Connection getConn() throws Exception{ + Class.forName("com.taosdata.jdbc.TSDBDriver"); + String jdbcUrl = "jdbc:TAOS://taosdemo.com:6030/test?user=root&password=taosdata"; + Properties connProps = new Properties(); + connProps.setProperty(TSDBDriver.PROPERTY_KEY_CHARSET, "UTF-8"); + connProps.setProperty(TSDBDriver.PROPERTY_KEY_LOCALE, "en_US.UTF-8"); + connProps.setProperty(TSDBDriver.PROPERTY_KEY_TIME_ZONE, "UTC-8"); + connProps.setProperty("debugFlag", "135"); + connProps.setProperty("maxSQLLength", "1048576"); + Connection conn = DriverManager.getConnection(jdbcUrl, connProps); + return conn; +} + +public Connection getRestConn() throws Exception{ + Class.forName("com.taosdata.jdbc.rs.RestfulDriver"); + String jdbcUrl = "jdbc:TAOS-RS://taosdemo.com:6041/test?user=root&password=taosdata"; + Properties connProps = new Properties(); + connProps.setProperty(TSDBDriver.PROPERTY_KEY_BATCH_LOAD, "true"); + Connection conn = DriverManager.getConnection(jdbcUrl, connProps); + return conn; +} +``` + +In the above example, a connection is established to `taosdemo.com`, port is 6030/6041, and database named `test`. The connection specifies the user name as `root` and the password as `taosdata` in the URL and specifies the character set, language environment, time zone, and whether to enable bulk fetching in the connProps. + +The configuration parameters in properties are as follows. + +- TSDBDriver.PROPERTY_KEY_USER: Login TDengine user name, default value 'root'. +- TSDBDriver.PROPERTY_KEY_PASSWORD: user login password, default value 'taosdata'. +- TSDBDriver.PROPERTY_KEY_BATCH_LOAD: true: pull the result set in batch when executing query; false: pull the result set row by row. The default value is: false. +- TSDBDriver.PROPERTY_KEY_BATCH_ERROR_IGNORE: true: when executing executeBatch of Statement, if there is a SQL execution failure in the middle, continue to execute the following sq. false: no longer execute any statement after the failed SQL. The default value is: false. +- TSDBDriver.PROPERTY_KEY_CONFIG_DIR: Only works when using JDBC native connection. Client configuration file directory path, default value `/etc/taos` on Linux OS, default value `C:/TDengine/cfg` on Windows OS. +- TSDBDriver.PROPERTY_KEY_CHARSET: takes effect only when using JDBC native connection. In the character set used by the client, the default value is the system character set. +- TSDBDriver.PROPERTY_KEY_LOCALE: this only takes effect when using JDBC native connection. Client language environment, the default value is system current locale. +- TSDBDriver.PROPERTY_KEY_TIME_ZONE: only takes effect when using JDBC native connection. In the time zone used by the client, the default value is the system's current time zone. +For JDBC native connections, you can specify other parameters, such as log level, SQL length, etc., by specifying URL and Properties. For more detailed configuration, please refer to [Client Configuration](/reference/config/#Client-Only). + +### Priority of configuration parameters + +If the configuration parameters are duplicated in the URL, Properties, or client configuration file, the `priority` of the parameters, from highest to lowest, are as follows: + +1. JDBC URL parameters, as described above, can be specified in the parameters of the JDBC URL. +2. Properties connProps +3. the configuration file taos.cfg of the TDengine client driver when using a native connection + +For example, if you specify the password as `taosdata` in the URL and specify the password as `taosdemo` in the Properties simultaneously. In this case, JDBC will use the password in the URL to establish the connection. + +## Usage examples + +### Create database and tables + +```java +Statement stmt = conn.createStatement(); + +// create database +stmt.executeUpdate("create database if not exists db"); + +// use database +stmt.executeUpdate("use db"); + +// create table +stmt.executeUpdate("create table if not exists tb (ts timestamp, temperature int, humidity float)"); +``` + +> **Note**: If you do not use `use db` to specify the database, all subsequent operations on the table need to add the database name as a prefix, such as db.tb. + +### Insert data + +```java +// insert data +int affectedRows = stmt.executeUpdate("insert into tb values(now, 23, 10.3) (now + 1s, 20, 9.3)"); + +System.out.println("insert " + affectedRows + " rows."); +``` + +> now is an internal function. The default is the current time of the client's computer. +> `now + 1s` represents the current time of the client plus 1 second, followed by the number representing the unit of time: a (milliseconds), s (seconds), m (minutes), h (hours), d (days), w (weeks), n (months), y (years). + +### Querying data + +```java +// query data +ResultSet resultSet = stmt.executeQuery("select * from tb"); + +Timestamp ts = null; +int temperature = 0; +float humidity = 0; +while(resultSet.next()){ + + ts = resultSet.getTimestamp(1); + temperature = resultSet.getInt(2); + humidity = resultSet.getFloat("humidity"); + + System.out.printf("%s, %d, %s\n", ts, temperature, humidity); +} +``` + +> The query is consistent with operating a relational database. When using subscripts to get the contents of the returned fields, starting from 1, it is recommended to use the field names to get them. + +### Handling exceptions + +After an error is reported, the error message and error code can be obtained through SQLException. + +```java +try (Statement statement = connection.createStatement()) { + // executeQuery + ResultSet resultSet = statement.executeQuery(sql); + // print result + printResult(resultSet); +} catch (SQLException e) { + System.out.println("ERROR Message: " + e.getMessage()); + System.out.println("ERROR Code: " + e.getErrorCode()); + e.printStackTrace(); +} +``` + +There are three types of error codes that the JDBC connector can report: + +- Error code of the JDBC driver itself (error code between 0x2301 and 0x2350) +- Error code of the native connection method (error code between 0x2351 and 0x2400) +- Error code of other TDengine function modules + +For specific error codes, please refer to. + +- [TDengine Java Connector](https://github.com/taosdata/TDengine/blob/develop/src/connector/jdbc/src/main/java/com/taosdata/jdbc/TSDBErrorNumbers.java) +- [TDengine_ERROR_CODE](https://github.com/taosdata/TDengine/blob/develop/src/inc/taoserror.h) + +### Writing data via parameter binding + +TDengine's native JDBC connection implementation has significantly improved its support for data writing (INSERT) scenarios via bind interface with version 2.1.2.0 and later versions. Writing data in this way avoids the resource consumption of SQL syntax parsing, resulting in significant write performance improvements in many cases. + +**Note**. + +- JDBC REST connections do not currently support bind interface +- The following sample code is based on taos-jdbcdriver-2.0.36 +- The setString method should be called for binary type data, and the setNString method should be called for nchar type data +- both setString and setNString require the user to declare the width of the corresponding column in the size parameter of the table definition + +```java +public class ParameterBindingDemo { + + private static final String host = "127.0.0.1"; + private static final Random random = new Random(System.currentTimeMillis()); + private static final int BINARY_COLUMN_SIZE = 20; + private static final String[] schemaList = { + "create table stable1(ts timestamp, f1 tinyint, f2 smallint, f3 int, f4 bigint) tags(t1 tinyint, t2 smallint, t3 int, t4 bigint)", + "create table stable2(ts timestamp, f1 float, f2 double) tags(t1 float, t2 double)", + "create table stable3(ts timestamp, f1 bool) tags(t1 bool)", + "create table stable4(ts timestamp, f1 binary(" + BINARY_COLUMN_SIZE + ")) tags(t1 binary(" + BINARY_COLUMN_SIZE + "))", + "create table stable5(ts timestamp, f1 nchar(" + BINARY_COLUMN_SIZE + ")) tags(t1 nchar(" + BINARY_COLUMN_SIZE + "))" + }; + private static final int numOfSubTable = 10, numOfRow = 10; + + public static void main(String[] args) throws SQLException { + + String jdbcUrl = "jdbc:TAOS://" + host + ":6030/"; + Connection conn = DriverManager.getConnection(jdbcUrl, "root", "taosdata"); + + init(conn); + + bindInteger(conn); + + bindFloat(conn); + + bindBoolean(conn); + + bindBytes(conn); + + bindString(conn); + + conn.close(); + } + + private static void init(Connection conn) throws SQLException { + try (Statement stmt = conn.createStatement()) { + stmt.execute("drop database if exists test_parabind"); + stmt.execute("create database if not exists test_parabind"); + stmt.execute("use test_parabind"); + for (int i = 0; i < schemaList.length; i++) { + stmt.execute(schemaList[i]); + } + } + } + + private static void bindInteger(Connection conn) throws SQLException { + String sql = "insert into ? using stable1 tags(?,?,?,?) values(?,?,?,?,?)"; + + try (TSDBPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSDBPreparedStatement.class)) { + + for (int i = 1; i <= numOfSubTable; i++) { + // set table name + pstmt.setTableName("t1_" + i); + // set tags + pstmt.setTagByte(0, Byte.parseByte(Integer.toString(random.nextInt(Byte.MAX_VALUE)))); + pstmt.setTagShort(1, Short.parseShort(Integer.toString(random.nextInt(Short.MAX_VALUE)))); + pstmt.setTagInt(2, random.nextInt(Integer.MAX_VALUE)); + pstmt.setTagLong(3, random.nextLong()); + // set columns + ArrayList tsList = new ArrayList<>(); + long current = System.currentTimeMillis(); + for (int j = 0; j < numOfRow; j++) + tsList.add(current + j); + pstmt.setTimestamp(0, tsList); + + ArrayList f1List = new ArrayList<>(); + for (int j = 0; j < numOfRow; j++) + f1List.add(Byte.parseByte(Integer.toString(random.nextInt(Byte.MAX_VALUE)))); + pstmt.setByte(1, f1List); + + ArrayList f2List = new ArrayList<>(); + for (int j = 0; j < numOfRow; j++) + f2List.add(Short.parseShort(Integer.toString(random.nextInt(Short.MAX_VALUE)))); + pstmt.setShort(2, f2List); + + ArrayList f3List = new ArrayList<>(); + for (int j = 0; j < numOfRow; j++) + f3List.add(random.nextInt(Integer.MAX_VALUE)); + pstmt.setInt(3, f3List); + + ArrayList f4List = new ArrayList<>(); + for (int j = 0; j < numOfRow; j++) + f4List.add(random.nextLong()); + pstmt.setLong(4, f4List); + + // add column + pstmt.columnDataAddBatch(); + } + // execute column + pstmt.columnDataExecuteBatch(); + } + } + + private static void bindFloat(Connection conn) throws SQLException { + String sql = "insert into ? using stable2 tags(?,?) values(?,?,?)"; + + TSDBPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSDBPreparedStatement.class); + + for (int i = 1; i <= numOfSubTable; i++) { + // set table name + pstmt.setTableName("t2_" + i); + // set tags + pstmt.setTagFloat(0, random.nextFloat()); + pstmt.setTagDouble(1, random.nextDouble()); + // set columns + ArrayList tsList = new ArrayList<>(); + long current = System.currentTimeMillis(); + for (int j = 0; j < numOfRow; j++) + tsList.add(current + j); + pstmt.setTimestamp(0, tsList); + + ArrayList f1List = new ArrayList<>(); + for (int j = 0; j < numOfRow; j++) + f1List.add(random.nextFloat()); + pstmt.setFloat(1, f1List); + + ArrayList f2List = new ArrayList<>(); + for (int j = 0; j < numOfRow; j++) + f2List.add(random.nextDouble()); + pstmt.setDouble(2, f2List); + + // add column + pstmt.columnDataAddBatch(); + } + // execute + pstmt.columnDataExecuteBatch(); + // close if no try-with-catch statement is used + pstmt.close(); + } + + private static void bindBoolean(Connection conn) throws SQLException { + String sql = "insert into ? using stable3 tags(?) values(?,?)"; + + try (TSDBPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSDBPreparedStatement.class)) { + for (int i = 1; i <= numOfSubTable; i++) { + // set table name + pstmt.setTableName("t3_" + i); + // set tags + pstmt.setTagBoolean(0, random.nextBoolean()); + // set columns + ArrayList tsList = new ArrayList<>(); + long current = System.currentTimeMillis(); + for (int j = 0; j < numOfRow; j++) + tsList.add(current + j); + pstmt.setTimestamp(0, tsList); + + ArrayList f1List = new ArrayList<>(); + for (int j = 0; j < numOfRow; j++) + f1List.add(random.nextBoolean()); + pstmt.setBoolean(1, f1List); + + // add column + pstmt.columnDataAddBatch(); + } + // execute + pstmt.columnDataExecuteBatch(); + } + } + + private static void bindBytes(Connection conn) throws SQLException { + String sql = "insert into ? using stable4 tags(?) values(?,?)"; + + try (TSDBPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSDBPreparedStatement.class)) { + + for (int i = 1; i <= numOfSubTable; i++) { + // set table name + pstmt.setTableName("t4_" + i); + // set tags + pstmt.setTagString(0, new String("abc")); + + // set columns + ArrayList tsList = new ArrayList<>(); + long current = System.currentTimeMillis(); + for (int j = 0; j < numOfRow; j++) + tsList.add(current + j); + pstmt.setTimestamp(0, tsList); + + ArrayList f1List = new ArrayList<>(); + for (int j = 0; j < numOfRow; j++) { + f1List.add(new String("abc")); + } + pstmt.setString(1, f1List, BINARY_COLUMN_SIZE); + + // add column + pstmt.columnDataAddBatch(); + } + // execute + pstmt.columnDataExecuteBatch(); + } + } + + private static void bindString(Connection conn) throws SQLException { + String sql = "insert into ? using stable5 tags(?) values(?,?)"; + + try (TSDBPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSDBPreparedStatement.class)) { + + for (int i = 1; i <= numOfSubTable; i++) { + // set table name + pstmt.setTableName("t5_" + i); + // set tags + pstmt.setTagNString(0, "Beijing-abc"); + + // set columns + ArrayList tsList = new ArrayList<>(); + long current = System.currentTimeMillis(); + for (int j = 0; j < numOfRow; j++) + tsList.add(current + j); + pstmt.setTimestamp(0, tsList); + + ArrayList f1List = new ArrayList<>(); + for (int j = 0; j < numOfRow; j++) { + f1List.add("Beijing-abc"); + } + pstmt.setNString(1, f1List, BINARY_COLUMN_SIZE); + + // add column + pstmt.columnDataAddBatch(); + } + // execute + pstmt.columnDataExecuteBatch(); + } + } +} +``` + +The methods to set TAGS values: + +```java +public void setTagNull(int index, int type) +public void setTagBoolean(int index, boolean value) +public void setTagInt(int index, int value) +public void setTagByte(int index, byte value) +public void setTagShort(int index, short value) +public void setTagLong(int index, long value) +public void setTagTimestamp(int index, long value) +public void setTagFloat(int index, float value) +public void setTagDouble(int index, double value) +public void setTagString(int index, String value) +public void setTagNString(int index, String value) +``` + +The methods to set VALUES columns: + +```java +public void setInt(int columnIndex, ArrayList list) throws SQLException +public void setFloat(int columnIndex, ArrayList list) throws SQLException +public void setTimestamp(int columnIndex, ArrayList list) throws SQLException +public void setLong(int columnIndex, ArrayList list) throws SQLException +public void setDouble(int columnIndex, ArrayList list) throws SQLException +public void setBoolean(int columnIndex, ArrayList list) throws SQLException +public void setByte(int columnIndex, ArrayList list) throws SQLException +public void setShort(int columnIndex, ArrayList list) throws SQLException +public void setString(int columnIndex, ArrayList list, int size) throws SQLException +public void setNString(int columnIndex, ArrayList list, int size) throws SQLException +``` + +### Schemaless Writing + +Starting with version 2.2.0.0, TDengine has added the ability to schemaless writing. It is compatible with InfluxDB's Line Protocol, OpenTSDB's telnet line protocol, and OpenTSDB's JSON format protocol. See [schemaless writing](/reference/schemaless/) for details. + +**Note**. + +- JDBC REST connections do not currently support schemaless writes +- The following sample code is based on taos-jdbcdriver-2.0.36 + +```java +public class SchemalessInsertTest { + private static final String host = "127.0.0.1"; + private static final String lineDemo = "st,t1=3i64,t2=4f64,t3=\"t3\" c1=3i64,c3=L\"passit\",c2=false,c4=4f64 1626006833639000000"; + private static final String telnetDemo = "stb0_0 1626006833 4 host=host0 interface=eth0"; + private static final String jsonDemo = "{\"metric\": \"meter_current\",\"timestamp\": 1346846400,\"value\": 10.3, \"tags\": {\"groupid\": 2, \"location\": \"Beijing\", \"id\": \"d1001\"}}"; + + public static void main(String[] args) throws SQLException { + final String url = "jdbc:TAOS://" + host + ":6030/?user=root&password=taosdata"; + try (Connection connection = DriverManager.getConnection(url)) { + init(connection); + + SchemalessWriter writer = new SchemalessWriter(connection); + writer.write(lineDemo, SchemalessProtocolType.LINE, SchemalessTimestampType.NANO_SECONDS); + writer.write(telnetDemo, SchemalessProtocolType.TELNET, SchemalessTimestampType.MILLI_SECONDS); + writer.write(jsonDemo, SchemalessProtocolType.JSON, SchemalessTimestampType.NOT_CONFIGURED); + } + } + + private static void init(Connection connection) throws SQLException { + try (Statement stmt = connection.createStatement()) { + stmt.executeUpdate("drop database if exists test_schemaless"); + stmt.executeUpdate("create database if not exists test_schemaless"); + stmt.executeUpdate("use test_schemaless"); + } + } +} +``` + +### Subscriptions + +The TDengine Java Connector supports subscription functionality with the following application API. + +#### Create subscriptions + +```java +TSDBSubscribe sub = ((TSDBConnection)conn).subscribe("topic", "select * from meters", false); +``` + +The three parameters of the `subscribe()` method have the following meanings. + +- topic: the subscribed topic (i.e., name). This parameter is the unique identifier of the subscription +- sql: the query statement of the subscription, this statement can only be `select` statement, only the original data should be queried, and you can query only the data in the positive time order +- restart: if the subscription already exists, whether to restart or continue the previous subscription + +The above example will use the SQL command `select * from meters` to create a subscription named `topic`. If the subscription exists, it will continue the progress of the previous query instead of consuming all the data from the beginning. + +#### Subscribe to consume data + +```java +int total = 0; +while(true) { + TSDBResultSet rs = sub.consume(); + int count = 0; + while(rs.next()) { + count++; + } + total += count; + System.out.printf("%d rows consumed, total %d\n", count, total); + Thread.sleep(1000); +} +``` + +The `consume()` method returns a result set containing all new data from the last `consume()`. Be sure to choose a reasonable frequency for calling `consume()` as needed (e.g. `Thread.sleep(1000)` in the example). Otherwise, it will cause unnecessary stress on the server-side. + +#### Close subscriptions + +```java +sub.close(true); +``` + +The `close()` method closes a subscription. If its argument is `true` it means that the subscription progress information is retained, and the subscription with the same name can be created to continue consuming data; if it is `false` it does not retain the subscription progress. + +### Closing resources + +```java +resultSet.close(); +stmt.close(); +conn.close(); +``` + +> **Be sure to close the connection**, otherwise, there will be a connection leak. + +### Use with connection pool + +#### HikariCP + +Example usage is as follows. + +```java + public static void main(String[] args) throws SQLException { + HikariConfig config = new HikariConfig(); + // jdbc properties + config.setJdbcUrl("jdbc:TAOS://127.0.0.1:6030/log"); + config.setUsername("root"); + config.setPassword("taosdata"); + // connection pool configurations + config.setMinimumIdle(10); //minimum number of idle connection + config.setMaximumPoolSize(10); //maximum number of connection in the pool + config.setConnectionTimeout(30000); //maximum wait milliseconds for get connection from pool + config.setMaxLifetime(0); // maximum life time for each connection + config.setIdleTimeout(0); // max idle time for recycle idle connection + config.setConnectionTestQuery("select server_status()"); //validation query + + HikariDataSource ds = new HikariDataSource(config); //create datasource + + Connection connection = ds.getConnection(); // get connection + Statement statement = connection.createStatement(); // get statement + + //query or insert + // ... + + connection.close(); // put back to connection pool +} +``` + +> getConnection(), you need to call the close() method after you finish using it. It doesn't close the connection. It just puts it back into the connection pool. +> For more questions about using HikariCP, please see the [official instructions](https://github.com/brettwooldridge/HikariCP). + +#### Druid + +Example usage is as follows. + +```java +public static void main(String[] args) throws Exception { + + DruidDataSource dataSource = new DruidDataSource(); + // jdbc properties + dataSource.setDriverClassName("com.taosdata.jdbc.TSDBDriver"); + dataSource.setUrl(url); + dataSource.setUsername("root"); + dataSource.setPassword("taosdata"); + // pool configurations + dataSource.setInitialSize(10); + dataSource.setMinIdle(10); + dataSource.setMaxActive(10); + dataSource.setMaxWait(30000); + dataSource.setValidationQuery("select server_status()"); + + Connection connection = dataSource.getConnection(); // get connection + Statement statement = connection.createStatement(); // get statement + //query or insert + // ... + + connection.close(); // put back to connection pool +} +``` + +> For more questions about using druid, please see [Official Instructions](https://github.com/alibaba/druid). + +**Caution:** + +- TDengine `v1.6.4.1` provides a special function `select server_status()` for heartbeat detection, so it is recommended to use `select server_status()` for Validation Query when using connection pooling. + +As you can see below, `select server_status()` returns `1` on successful execution. + +```sql +taos> select server_status(); +server_status()| +================ +1 | +Query OK, 1 row(s) in set (0.000141s) +``` + +### More sample programs + +The source code of the sample application is under `TDengine/examples/JDBC`: + +- JDBCDemo: JDBC sample source code. +- JDBCConnectorChecker: JDBC installation checker source and jar package. +- connectionPools: using taos-jdbcdriver in connection pools such as HikariCP, Druid, dbcp, c3p0, etc. +- SpringJdbcTemplate: using taos-jdbcdriver in Spring JdbcTemplate. +- mybatisplus-demo: using taos-jdbcdriver in Springboot + Mybatis. + +Please refer to: [JDBC example](https://github.com/taosdata/TDengine/tree/develop/examples/JDBC) + +## Recent update logs + +| taos-jdbcdriver version | major changes | +| :------------------: | :----------------------------: | +| 2.0.38 | JDBC REST connections add bulk pull function | +| 2.0.37 | Added support for json tags | +| 2.0.36 | Add support for schemaless writing | + +## Frequently Asked Questions + +1. Why is there no performance improvement when using Statement's `addBatch()` and `executeBatch()` to perform `batch data writing/update`? + + **Cause**: In TDengine's JDBC implementation, SQL statements submitted by `addBatch()` method are executed sequentially in the order they are added, which does not reduce the number of interactions with the server and does not bring performance improvement. + + **Solution**: 1. splice multiple values in a single insert statement; 2. use multi-threaded concurrent insertion; 3. use parameter-bound writing + +2. java.lang.UnsatisfiedLinkError: no taos in java.library.path + + **Cause**: The program did not find the dependent native library `taos`. + + **Solution**: On Windows you can copy `C:\TDengine\driver\taos.dll` to the `C:\Windows\System32` directory, on Linux the following soft link will be created `ln -s /usr/local/taos/driver/libtaos.so.x.x.x.x /usr/lib/libtaos.so` will work. + +3. java.lang.UnsatisfiedLinkError: taos.dll Can't load AMD 64 bit on an IA 32-bit platform + + **Cause**: Currently, TDengine only supports 64-bit JDK. + + **Solution**: Reinstall the 64-bit JDK. 4. + +For other questions, please refer to [FAQ](/train-faq/faq) + +## API Reference + +[taos-jdbcdriver doc](https://docs.taosdata.com/api/taos-jdbcdriver) diff --git a/docs-en/14-reference/03-connector/node.mdx b/docs-en/14-reference/03-connector/node.mdx new file mode 100644 index 0000000000000000000000000000000000000000..3d30148e8ed9d8f98d135fa0fa72809f1115231a --- /dev/null +++ b/docs-en/14-reference/03-connector/node.mdx @@ -0,0 +1,255 @@ +--- +toc_max_heading_level: 4 +sidebar_position: 6 +sidebar_label: Node.js +title: TDengine Node.js Connector +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +import Preparation from "./_preparation.mdx"; +import NodeInsert from "../../07-develop/03-insert-data/_js_sql.mdx"; +import NodeInfluxLine from "../../07-develop/03-insert-data/_js_line.mdx"; +import NodeOpenTSDBTelnet from "../../07-develop/03-insert-data/_js_opts_telnet.mdx"; +import NodeOpenTSDBJson from "../../07-develop/03-insert-data/_js_opts_json.mdx"; +import NodeQuery from "../../07-develop/04-query-data/_js.mdx"; +import NodeAsyncQuery from "../../07-develop/04-query-data/_js_async.mdx"; + +`td2.0-connector` and `td2.0-rest-connector` are the official Node.js language connectors for TDengine. Node.js developers can develop applications to access TDengine instance data. + +`td2.0-connector` is a **native connector** that connects to TDengine instances via the TDengine client driver (taosc) and supports data writing, querying, subscriptions, schemaless writing, and bind interface. The `td2.0-rest-connector` is a **REST connector** that connects to TDengine instances via the REST interface provided by taosAdapter. The REST connector can run on any platform, but performance is slightly degraded, and the interface implements a somewhat different set of functional features than the native interface. + +The Node.js connector source code is hosted on [GitHub](https://github.com/taosdata/taos-connector-node). + +## Supported Platforms + +The platforms supported by the native connector are the same as those supported by the TDengine client driver. +The REST connector supports all platforms that can run Node.js. + +## Version support + +Please refer to [version support list](/reference/connector#version-support) + +## Supported features + +### Native connectors + +1. connection management +2. general query +3. continuous query +4. parameter binding +5. subscription function +6. Schemaless + +### REST Connector + +1. connection management +2. general queries +3. Continuous query + +## Installation steps + +### Pre-installation + +- Install the Node.js development environment +- If you are using the REST connector, skip this step. However, if you use the native connector, please install the TDengine client driver. Please refer to [Install Client Driver](/reference/connector#Install-Client-Driver) for more details. We use [node-gyp](https://github.com/nodejs/node-gyp) to interact with TDengine instances and also need to install some dependencies mentioned below depending on the specific OS. + + + + +- `python` (recommended for `v2.7` , `v3.x.x` currently not supported) +- `td2.0-connector` 2.0.6 supports Node.js LTS v10.9.0 or later, Node.js LTS v12.8.0 or later; 2.0.5 and earlier support Node.js LTS v10.x versions. Other versions may have package compatibility issues +- `make` +- C compiler, [GCC](https://gcc.gnu.org) v4.8.5 or higher + + + + +- Installation method 1 + +Use Microsoft's [windows-build-tools](https://github.com/felixrieseberg/windows-build-tools) to execute `npm install --global --production` from the `cmd` command-line interface to install all the necessary tools. + +- Installation method 2 + +Manually install the following tools. + +- Install Visual Studio related: [Visual Studio Build Tools](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools) or [Visual Studio 2017 Community](https://visualstudio.microsoft.com/pl/thank-you-downloading-visual-studio/?sku=Community) +- Install [Python](https://www.python.org/downloads/) 2.7 (`v3.x.x` is not supported) and execute `npm config set python python2.7`. +- Go to the `cmd` command-line interface, `npm config set msvs_version 2017` + +Refer to Microsoft's Node.js User Manual [Microsoft's Node.js Guidelines for Windows](https://github.com/Microsoft/nodejs-guidelines/blob/master/windows-environment.md#compiling-native-addon-modules). + +If using ARM64 Node.js on Windows 10 ARM, you must add "Visual C++ compilers and libraries for ARM64" and "Visual C++ ATL for ARM64". + + + + +### Install via npm + + + + +```bash +npm install td2.0-connector +``` + + + + +```bash +npm i td2.0-rest-connector +``` + + + + +### Installation verification + +After installing the TDengine client, use the `nodejsChecker.js` program to verify that the current environment supports Node.js access to TDengine. + +Verification in details: + +- Create a new installation verification directory, e.g. `~/tdengine-test`, and download the [nodejsChecker.js source code](https://github.com/taosdata/TDengine/tree/develop/examples/nodejs/) from GitHub. to the work directory. + +- Execute the following command from the command-line. + +```bash +npm init -y +npm install td2.0-connector +node nodejsChecker.js host=localhost +``` + +- After executing the above steps, the command-line will output the result of `nodejsChecker.js` connecting to the TDengine instance and performing a simple insert and query. + +## Establishing a connection + +Please choose to use one of the connectors. + + + + +Install and refer to `td2.0-connector` package: + +```javascript +//A cursor also needs to be initialized in order to interact with TDengine from Node.js. +const taos = require("td2.0-connector"); +var conn = taos.connect({ + host: "127.0.0.1", + user: "root", + password: "taosdata", + config: "/etc/taos", + port: 0, +}); +var cursor = conn.cursor(); // Initializing a new cursor + +//Close a connection +conn.close(); +``` + + + + +Install and refer to `td2.0-rest-connector`package: + +```javascript +//A cursor also needs to be initialized in order to interact with TDengine from Node.js. +import { options, connect } from "td2.0-rest-connector"; +options.path = "/rest/sqlt"; +// set host +options.host = "localhost"; +// set other options like user/passwd + +let conn = connect(options); +let cursor = conn.cursor(); +``` + + + + +## Usage examples + +### Write data + +#### SQL Writing + + + +#### InfluxDB line protocol writing + + + +#### OpenTSDB Telnet line protocol writing + + + +#### OpenTSDB JSON line protocol writing + + + +### Query data + +#### Synchronous queries + + + +#### asynchronous query + + + +## More Sample Programs + +| Sample Programs | Sample Program Description | +| --------------------------------------------------------------------------------------------------------------------------------- --------- | -------------------------------------- | +| [connection](https://github.com/taosdata/taos-connector-node/tree/develop/nodejs/examples/cursorClose.js) | Example of establishing a connection. | +| [stmtBindBatch](https://github.com/taosdata/taos-connector-node/tree/develop/nodejs/examples/stmtBindParamBatchSample.js) | Example of binding a multi-line parameter Example of inserting. | +| [stmtBind](https://github.com/taosdata/taos-connector-node/tree/develop/nodejs/examples/stmtBindParamSample.js) | Example of a line-by-line bind parameter insertion. | +| [stmtBindSingleParamBatch](https://github.com/taosdata/taos-connector-node/tree/develop/nodejs/examples/) stmtBindSingleParamBatchSample.js) | Example of binding parameters by column. | +| [stmtUseResult](https://github.com/taosdata/taos-connector-node/tree/develop/nodejs/examples/stmtUseResultSample.js) | Example of a bound parameter query. | +| [json tag](https://github.com/taosdata/taos-connector-node/tree/develop/nodejs/examples/testJsonTag.js) | Example of using Json tag. | +| [Nanosecond](https://github.com/taosdata/taos-connector-node/tree/develop/nodejs/examples/testNanoseconds.js) | An example of using timestamps with nanosecond precision. | +| [Microsecond](https://github.com/taosdata/taos-connector-node/tree/develop/nodejs/examples/testMicroseconds.js) | Example of using microsecond timestamp. | +| [schemless insert](https://github.com/taosdata/taos-connector-node/tree/develop/nodejs/examples/testSchemalessInsert.js) | schemless Example of a schemless insert. | +| [subscribe](https://github.com/taosdata/taos-connector-node/tree/develop/nodejs/examples/testSubscribe.js) | Example of using subscribe. | +| [asyncQuery](https://github.com/taosdata/taos-connector-node/tree/develop/nodejs/examples/tset.js) | An example of using asynchronous queries. | +| [REST](https://github.com/taosdata/taos-connector-node/blob/develop/typescript-rest/example/example.ts) | An example of using TypeScript with REST connections. | + +## Usage restrictions + +Node.js Connector >= v2.0.6 currently supports node versions >=v12.8.0 <= v12.9.1 || >=v10.20.0 <= v10.9.0; v10.x versions are supported in 2.0.5 and earlier, other versions may have package compatibility issues. + +## Other notes + +See [video tutorial](https://www.taosdata.com/blog/2020/11/11/1957.html) for the Node.js connector usage. + +## Frequently Asked Questions + +1. Using REST connections requires starting taosadapter. + + ```bash + sudo systemctl start taosadapter + ``` + +2. "Unable to establish connection", "Unable to resolve FQDN" + + Usually, root cause is the FQDN is not configured correctly. You can refer to [How to understand TDengine's FQDN (In Chinese)](https://www.taosdata.com/blog/2021/07/29/2741.html). + +## Important Updates + +### Native connectors + +| td2.0-connector version | description | +| -------------------- | ---------------------------------------------------------------- | +| 2.0.12 | Fix bug with cursor.close() error. | 2.0.12 | Fix bug with cursor.close() error. +| 2.0.11 | Support for binding parameters, json tag, schemaless interface, etc. | +| 2.0.10 | Support connection management, general query, continuous query, get system information, subscribe function, etc. | ### REST Connector + +### REST Connector + +| td2.0-rest-connector version | Description | +| ------------------------- | ---------------------------------------------------------------- | +| 1.0.3 | Support connection management, general query, get system information, error message, continuous query, etc. |# API Reference + +## API Reference + +[API Reference](https://docs.taosdata.com/api/td2.0-connector/) diff --git a/docs-en/14-reference/03-connector/python.mdx b/docs-en/14-reference/03-connector/python.mdx new file mode 100644 index 0000000000000000000000000000000000000000..2b238173e04e3e13de36b5ac4d91d0cda290ca72 --- /dev/null +++ b/docs-en/14-reference/03-connector/python.mdx @@ -0,0 +1,346 @@ +--- +sidebar_position: 3 +sidebar_label: Python +title: TDengine Python Connector +description: "taospy is the official Python connector for TDengine. taospy provides a rich API that makes it easy for Python applications to use TDengine. tasopy wraps both the native and REST interfaces of TDengine, corresponding to the two submodules of tasopy: taos and taosrest. In addition to wrapping the native and REST interfaces, taospy also provides a programming interface that conforms to the Python Data Access Specification (PEP 249), making it easy to integrate taospy with many third-party tools, such as SQLAlchemy and pandas." +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +`taospy` is the official Python connector for TDengine. `taospy` provides a rich set of APIs that makes it easy for Python applications to access TDengine. `taospy` wraps both the [native interface](/reference/connector/cpp) and [REST interface](/reference/rest-api) of TDengine, which correspond to the `taos` and `taosrest` modules of the `taospy` package, respectively. +In addition to wrapping the native and REST interfaces, `taospy` also provides a set of programming interfaces that conforms to the [Python Data Access Specification (PEP 249)](https://peps.python.org/pep-0249/). It is easy to integrate `taospy` with many third-party tools, such as [SQLAlchemy](https://www.sqlalchemy.org/) and [pandas](https://pandas.pydata.org/). + +The connection to the server directly using the native interface provided by the client driver is referred to hereinafter as a "native connection"; the connection to the server using the REST interface provided by taosAdapter is referred to hereinafter as a "REST connection". + +The source code for the Python connector is hosted on [GitHub](https://github.com/taosdata/taos-connector-python). + +## Supported Platforms + +- The native connection [supported platforms](/reference/connector/#supported-platforms) is the same as the one supported by the TDengine client. +- REST connections are supported on all platforms that can run Python. + +## Version selection + +We recommend using the latest version of `taospy`, regardless what the version of TDengine is. + +## Supported features + +- Native connections support all the core features of TDengine, including connection management, SQL execution, bind interface, subscriptions, and schemaless writing. +- REST connections support features such as connection management and SQL execution. (SQL execution allows you to: manage databases, tables, and supertables, write data, query data, create continuous queries, etc.). + +## Installation + +### Preparation + +1. Install Python. Python >= 3.6 is recommended. If Python is not available on your system, refer to the [Python BeginnersGuide](https://wiki.python.org/moin/BeginnersGuide/Download) to install it. +2. Install [pip](https://pypi.org/project/pip/). In most cases, the Python installer comes with the pip utility. If not, please refer to [pip documentation](https://pip.pypa.io/en/stable/installation/) to install it. + +If you use a native connection, you will also need to [Install Client Driver](/reference/connector#Install-Client-Driver). The client install package includes the TDengine client dynamic link library (`libtaos.so` or `taos.dll`) and the TDengine CLI. + +### Install via pip + +#### Uninstalling an older version + +If you have installed an older version of the Python Connector, please uninstall it beforehand. + +``` +pip3 uninstall taos taospy +``` + +:::note +Earlier TDengine client software includes the Python connector. If the Python connector is installed from the client package's installation directory, the corresponding Python package name is `taos`. So the above uninstall command includes `taos`, and it doesn't matter if it doesn't exist. + +::: + +#### to install `taospy` + + + + +Install the latest version of: + +``` +pip3 install taospy +``` + +You can also specify a specific version to install: + +``` +pip3 install taospy==2.3.0 +``` + + + + +``` +pip3 install git+https://github.com/taosdata/taos-connector-python.git +``` + + + + +### Installation verification + + + + +For native connection, you need to verify that both the client driver and the Python connector itself are installed correctly. The client driver and Python connector have been installed properly if you can successfully import the `taos` module. In the Python Interactive Shell, you can type. + +```python +import taos +``` + + + + +For REST connections, verifying that the `taosrest` module can be imported successfully can be done in the Python Interactive Shell by typing. + +```python +import taosrest +``` + + + + +:::tip +If you have multiple versions of Python on your system, you may have various `pip` commands. Be sure to use the correct path for the `pip` command. Above, we installed the `pip3` command, which rules out the possibility of using the `pip` corresponding to Python 2.x versions. However, if you have more than one version of Python 3.x on your system, you still need to check that the installation path is correct. The easiest way to verify this is to type `pip3 install taospy` again in the command, and it will print out the exact location of `taospy`, for example, on Windows. + +``` +C:\> pip3 install taospy +Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple +Requirement already satisfied: taospy in c:\users\username\appdata\local\programs\python\python310\lib\site-packages (2.3.0) + +::: + +## Establish connection + +### Connectivity testing + +Before establishing a connection with the connector, we recommend testing the connectivity of the local TDengine CLI to the TDengine cluster. + + + + +Ensure that the TDengine instance is up and that the FQDN of the machines in the cluster (the FQDN defaults to hostname if you are starting a standalone version) can be resolved locally, by testing with the `ping` command. + +``` +ping +``` + +Then test if the cluster can be appropriately connected with TDengine CLI: + +``` +taos -h -p +``` + +The FQDN above can be the FQDN of any dnode in the cluster, and the PORT is the serverPort corresponding to this dnode. + + + + +For REST connections and making sure the cluster is up, make sure the taosAdapter component is up. This can be tested using the following `curl ` command. + +``` +curl -u root:taosdata http://:/rest/sql -d "select server_version()" +``` + +The FQDN above is the FQDN of the machine running taosAdapter, PORT is the port taosAdapter listening, default is `6041`. +If the test is successful, it will output the server version information, e.g. + +```json +{ + "status": "succ", + "head": ["server_version()"], + "column_meta": [["server_version()", 8, 8]], + "data": [["2.4.0.16"]], + "rows": 1 +} +``` + + + + +### Using connectors to establish connections + +The following example code assumes that TDengine is installed locally and that the default configuration is used for both FQDN and serverPort. + + + + +```python +{{#include docs-examples/python/connect_native_reference.py}} +``` + +All arguments of the `connect()` function are optional keyword arguments. The following are the connection parameters specified. + +- `host` : The FQDN of the node to connect to. There is no default value. If this parameter is not provided, the firstEP in the client configuration file will be connected. +- `user` : The TDengine user name. The default value is `root`. +- `password` : TDengine user password. The default value is `taosdata`. +- `port` : The starting port of the data node to connect to, i.e., the serverPort configuration. The default value is 6030, which will only take effect if the host parameter is provided. +- `config` : The path to the client configuration file. On Windows systems, the default is `C:\TDengine\cfg`. The default is `/etc/taos/` on Linux systems. +- `timezone` : The timezone used to convert the TIMESTAMP data in the query results to python `datetime` objects. The default is the local timezone. + +:::warning +`config` and `timezone` are both process-level configurations. We recommend that all connections made by a process use the same parameter values. Otherwise, unpredictable errors may occur. +::: + +:::tip +The `connect()` function returns a `taos.TaosConnection` instance. In client-side multi-threaded scenarios, we recommend that each thread request a separate connection instance rather than sharing a connection between multiple threads. + +::: + + + + +```python +{{#include docs-examples/python/connect_rest_examples.py:connect}} +``` + +All arguments to the `connect()` function are optional keyword arguments. The following are the connection parameters specified. + +- `host`: The host to connect to. The default is localhost. +- `user`: TDengine user name. The default is `root`. +- `password`: TDengine user password. The default is `taosdata`. +- `port`: The port on which the taosAdapter REST service listens. Default is 6041. +- `timeout`: HTTP request timeout in seconds. The default is `socket._GLOBAL_DEFAULT_TIMEOUT`. Usually, no configuration is needed. + + + + +## Sample program + +### Basic Usage + + + + +##### TaosConnection class + +The `TaosConnection` class contains both an implementation of the PEP249 Connection interface (e.g., the `cursor()` method and the `close()` method) and many extensions (e.g., the `execute()`, `query()`, `schemaless_insert()`, and `subscribe()` methods). + +```python title="execute method" +{{#include docs-examples/python/connection_usage_native_reference.py:insert}} +``` + +```python title="query method" +{{#include docs-examples/python/connection_usage_native_reference.py:query}} +``` + +:::tip +The queried results can only be fetched once. For example, only one of `fetch_all()` and `fetch_all_into_dict()` can be used in the example above. Repeated fetches will result in an empty list. +::: + +##### Use of TaosResult class + +In the above example of using the `TaosConnection` class, we have shown two ways to get the result of a query: `fetch_all()` and `fetch_all_into_dict()`. In addition, `TaosResult` also provides methods to iterate through the result set by rows (`rows_iter`) or by data blocks (`blocks_iter`). Using these two methods will be more efficient in scenarios where the query has a large amount of data. + +```python title="blocks_iter method" +{{#include docs-examples/python/result_set_examples.py}} +``` +##### Use of the TaosCursor class + +The `TaosConnection` class and the `TaosResult` class already implement all the functionality of the native interface. If you are familiar with the interfaces in the PEP249 specification, you can also use the methods provided by the `TaosCursor` class. + +```python title="Use of TaosCursor" +{{#include docs-examples/python/cursor_usage_native_reference.py}} +``` + +:::note +The TaosCursor class uses native connections for write and query operations. In a client-side multi-threaded scenario, this cursor instance must remain thread exclusive and cannot be shared across threads for use, otherwise, it will result in errors in the returned results. + +::: + + + + +##### Use of TaosRestCursor class + +The ``TaosRestCursor`` class is an implementation of the PEP249 Cursor interface. + +```python title="Use of TaosRestCursor" +{{#include docs-examples/python/connect_rest_examples.py:basic}} +``` +- `cursor.execute` : Used to execute arbitrary SQL statements. +- `cursor.rowcount` : For write operations, returns the number of successful rows written. For query operations, returns the number of rows in the result set. +- `cursor.description` : Returns the description of the field. Please refer to [TaosRestCursor](https://docs.taosdata.com/api/taospy/taosrest/cursor.html) for the specific format of the description information. + +##### Use of the RestClient class + +The `RestClient` class is a direct wrapper for the [REST API](/reference/rest-api). It contains only a `sql()` method for executing arbitrary SQL statements and returning the result. + +```python title="Use of RestClient" +{{#include docs-examples/python/rest_client_example.py}} +``` + +For a more detailed description of the `sql()` method, please refer to [RestClient](https://docs.taosdata.com/api/taospy/taosrest/restclient.html). + + + + +### Used with pandas + + + + +```python +{{#include docs-examples/python/conn_native_pandas.py}} +``` + + + + +```python +{{#include docs-examples/python/conn_rest_pandas.py}} +``` + + + + +### Other sample programs + +| Example program links | Example program content | +| ------------------------------------------------------------------------------------------------------------- | ------------------- ---- | +| [bind_multi.py](https://github.com/taosdata/taos-connector-python/blob/main/examples/bind-multi.py) | parameter binding, bind multiple rows at once | +| [bind_row.py](https://github.com/taosdata/taos-connector-python/blob/main/examples/bind-row.py) | bind_row.py +| [insert_lines.py](https://github.com/taosdata/taos-connector-python/blob/main/examples/insert-lines.py) | InfluxDB line protocol writing | +| [json_tag.py](https://github.com/taosdata/taos-connector-python/blob/main/examples/json-tag.py) | Use JSON type tags | +| [subscribe-async.py](https://github.com/taosdata/taos-connector-python/blob/main/examples/subscribe-async.py) | Asynchronous subscription | +| [subscribe-sync.py](https://github.com/taosdata/taos-connector-python/blob/main/examples/subscribe-sync.py) | synchronous-subscribe | + +## Other notes + +### Exception handling + +All database operations will be thrown directly if an exception occurs. The application is responsible for exception handling. For example: + +```python +{{#include docs-examples/python/handle_exception.py}} +``` + +### About nanoseconds + +Due to the current imperfection of Python's nanosecond support (see link below), the current implementation returns integers at nanosecond precision instead of the `datetime` type produced by `ms and `us`, which application developers will need to handle on their own. And it is recommended to use pandas' to_datetime(). The Python Connector may modify the interface in the future if Python officially supports nanoseconds in full. + +1. https://stackoverflow.com/questions/10611328/parsing-datetime-strings-containing-nanoseconds +2. https://www.python.org/dev/peps/pep-0564/ + + +## Frequently Asked Questions + +Welcome to [ask questions or report questions] (https://github.com/taosdata/taos-connector-python/issues). + +## Important Update + +| Connector version | Important Update | Release date | +| ---------- | --------------------------------------------------------------------------------- | ---------- | +| 2.3.1 | 1. support TDengine REST API
2. remove support for Python version below 3.6 | 2022-04-28 | +| 2.2.5 | support timezone option when connect | 2022-04-13 | +| 2.2.2 | support sqlalchemy dialect plugin | 2022-03-28 | + +[**Release Notes**] (https://github.com/taosdata/taos-connector-python/releases) + +## API Reference + +- [taos](https://docs.taosdata.com/api/taospy/taos/) +- [taosrest](https://docs.taosdata.com/api/taospy/taosrest) diff --git a/docs-en/14-reference/03-connector/rust.mdx b/docs-en/14-reference/03-connector/rust.mdx new file mode 100644 index 0000000000000000000000000000000000000000..2c8fe68c1ca8b091b8d685d8e20942a02ab2c5e8 --- /dev/null +++ b/docs-en/14-reference/03-connector/rust.mdx @@ -0,0 +1,384 @@ +--- +toc_max_heading_level: 4 +sidebar_position: 5 +sidebar_label: Rust +title: TDengine Rust Connector +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +import Preparation from "./_preparation.mdx" +import RustInsert from "../../07-develop/03-insert-data/_rust_sql.mdx" +import RustInfluxLine from "../../07-develop/03-insert-data/_rust_line.mdx" +import RustOpenTSDBTelnet from "../../07-develop/03-insert-data/_rust_opts_telnet.mdx" +import RustOpenTSDBJson from "../../07-develop/03-insert-data/_rust_opts_json.mdx" +import RustQuery from "../../07-develop/04-query-data/_rust.mdx" + +`libtaos` is the official Rust language connector for TDengine. Rust developers can develop applications to access the TDengine instance data. + +`libtaos` provides two ways to establish connections. One is the **Native Connection**, which connects to TDengine instances via the TDengine client driver (taosc). The other is **REST connection**, which connects to TDengine instances via taosAdapter's REST interface. + +The source code for `libtaos` is hosted on [GitHub](https://github.com/taosdata/libtaos-rs). + +## Supported platforms + +The platforms supported by native connections are the same as those supported by the TDengine client driver. +REST connections are supported on all platforms that can run Rust. + +## Version support + +Please refer to [version support list](/reference/connector#version-support). + +The Rust Connector is still under rapid development and is not guaranteed to be backward compatible before 1.0. Recommend to use TDengine version 2.4 or higher to avoid known issues. + +## Installation + +### Pre-installation +* Install the Rust development toolchain +* If using the native connection, please install the TDengine client driver. Please refer to [install client driver](/reference/connector#install-client-driver) + +### Adding libtaos dependencies + +Add the [libtaos][libtaos] dependency to the [Rust](https://rust-lang.org) project as follows, depending on the connection method selected. + + + + +Add [libtaos][libtaos] to the ``Cargo.toml`'' file. + +``toml +[dependencies] +# use default feature +libtaos = "*" +``` + + + +Add [libtaos][libtaos] to the `Cargo.toml` file and enable the `rest` feature. + +```toml +[dependencies] +# use rest feature +libtaos = { version = "*", features = ["rest"]} +``` + + + + + +### Using connection pools + +Please enable the `r2d2` feature in `Cargo.toml`. + +```toml +[dependencies] +# with taosc +libtaos = { version = "*", features = ["r2d2"] } +# or rest +libtaos = { version = "*", features = ["rest", "r2d2"] } +``` + +## Create a connection + +The [TaosCfgBuilder] provides the user with an API in the form of a constructor for the subsequent creation of connections or use of connection pools. + +```rust +let cfg: TaosCfg = TaosCfgBuilder::default() + .ip("127.0.0.1") + .user("root") + .pass("taosdata") + .db("log") // do not set if not require a default database. + .port(6030u16) + .build() + .expect("TaosCfg builder error"); +} +``` + +You can now use this object to create the connection. + +```rust +let conn = cfg.connect()? ; +``` + +The connection object can create more than one. + +```rust +let conn = cfg.connect()? ; +let conn2 = cfg.connect()? ; +``` + +You can use connection pools in applications. + +```rust +let pool = r2d2::Pool::builder() + .max_size(10000) // max connections + .build(cfg)? ; + +// ... +// Use pool to get connection +let conn = pool.get()? ; +``` + +After that, you can perform the following operations on the database. + +```rust +async fn demo() -> Result<(), Error> { + // get connection ... + + // create database + conn.exec("create database if not exists demo").await? + // change database context + conn.exec("use demo").await? + // create table + conn.exec("create table if not exists tb1 (ts timestamp, v int)").await? + // insert + conn.exec("insert into tb1 values(now, 1)").await? + // query + let rows = conn.query("select * from tb1").await? + for row in rows.rows { + println!("{}", row.into_iter().join(",")); + } +} +``` + +## Usage examples + +### Write data + +#### SQL Write + + + +#### InfluxDB line protocol write + + + +#### OpenTSDB Telnet line protocol write + + + +#### OpenTSDB JSON line protocol write + + + +### Query data + + + +### More sample programs + +| Program Path | Program Description | +| -------------- | ----------------------------------------------------------------------------- | +| [demo.rs] | Basic API Usage Examples | +| [bailongma-rs] | Using TDengine as the Prometheus remote storage API adapter for the storage backend, using the r2d2 connection pool | + +## API Reference + +### Connection constructor API + +The [Builder Pattern](https://doc.rust-lang.org/1.0.0/style/ownership/builders.html) constructor pattern is Rust's solution for handling complex data types or optional configuration types. The [libtaos] implementation uses the connection constructor [TaosCfgBuilder] as the entry point for the TDengine Rust connector. The [TaosCfgBuilder] provides optional configuration of servers, ports, databases, usernames, passwords, etc. + +Using the `default()` method, you can construct a [TaosCfg] with default parameters for subsequent connections to the database or establishing connection pools. + +```rust +let cfg = TaosCfgBuilder::default().build()? ; +``` + +Using the constructor pattern, the user can set on-demand. + +```rust +let cfg = TaosCfgBuilder::default() + .ip("127.0.0.1") + .user("root") + .pass("taosdata") + .db("log") + .port(6030u16) + .build()? ; +``` + +Create TDengine connection using [TaosCfg] object. + +```rust +let conn: Taos = cfg.connect(); +``` + +### Connection pooling + +In complex applications, recommand to enable connection pool. Connection pool for [libtaos] is implemented using [r2d2]. + +As follows, a connection pool with default parameters can be generated. + +```rust +let pool = r2d2::Pool::new(cfg)? ; +``` + +You can set the same connection pool parameters using the connection pool's constructor. + +```rust + use std::time::Duration; + let pool = r2d2::Pool::builder() + .max_size(5000) // max connections + .max_lifetime(Some(Duration::from_minutes(100))) // lifetime of each connection + .min_idle(Some(1000)) // minimal idle connections + .connection_timeout(Duration::from_minutes(2)) + .build(cfg); +``` + +In the application code, use `pool.get()? ` to get a connection object [Taos]. + +```rust +let taos = pool.get()? ; +``` + +The [Taos] structure is the connection manager in [libtaos] and provides two main APIs. + +1. `exec`: Execute some non-query SQL statements, such as `CREATE`, `ALTER`, `INSERT`, etc. + + ```rust + taos.exec().await? + ``` + +2. `query`: Execute the query statement and return the [TaosQueryData] object. + + ```rust + let q = taos.query("select * from log.logs").await? + ``` + + The [TaosQueryData] object stores the query result data and basic information about the returned columns (column name, type, length). + + Column information is stored using [ColumnMeta]. + + ``rust + let cols = &q.column_meta; + for col in cols { + println!("name: {}, type: {:?} , bytes: {}", col.name, col.type_, col.bytes); + } + ``` + + It fetches data line by line. + + ```rust + for (i, row) in q.rows.iter().enumerate() { + for (j, cell) in row.iter().enumerate() { + println!("cell({}, {}) data: {}", i, j, cell); + } + } + ``` + +Note that Rust asynchronous functions and an asynchronous runtime are required. + +[Taos] provides partial Rust methodization of SQL to reduce the frequency of `format!` code blocks. + +- `.describe(table: &str)`: Executes `DESCRIBE` and returns a Rust data structure. +- `.create_database(database: &str)`: Executes the `CREATE DATABASE` statement. +- `.use_database(database: &str)`: Executes the `USE` statement. + +In addition, this structure is also the entry point for [Parameter Binding](#Parameter Binding Interface) and [Line Protocol Interface](#Line Protocol Interface). Please refer to the specific API descriptions for usage. + +### Bind Interface + +Similar to the C interface, Rust provides the bind interface's wraping. First, create a bind object [Stmt] for a SQL command from the [Taos] object. + +```rust +let mut stmt: Stmt = taos.stmt("insert into ? values(? ,?)") ? ; +``` + +The bind object provides a set of interfaces for implementing parameter binding. + +##### `.set_tbname(tbname: impl ToCString)` + +To bind table names. + +##### `.set_tbname_tags(tbname: impl ToCString, tags: impl IntoParams)` + +Bind sub-table table names and tag values when the SQL statement uses a super table. + +```rust +let mut stmt = taos.stmt("insert into ? using stb0 tags(?) values(? ,?)") ? ; +// tags can be created with any supported type, here is an example using JSON +let v = Field::Json(serde_json::from_str("{\"tag1\":\"one, two, three, four, five, six, seven, eight, nine, ten\"}").unwrap()); +stmt.set_tbname_tags("tb0", [&tag])? ; +``` + +##### `.bind(params: impl IntoParams)` + +Bind value types. Use the [Field] structure to construct the desired type and bind. + +```rust +let ts = Field::Timestamp(Timestamp::now()); +let value = Field::Float(0.0); +stmt.bind(vec![ts, value].iter())? ; +``` + +##### `.execute()` + +Execute SQL.[Stmt] objects can be reused, re-binded, and executed after execution. + +```rust +stmt.execute()? ; + +// next bind cycle. +// stmt.set_tbname()? ; +//stmt.bind()? ; +//stmt.execute()? ; +``` + +### Line protocol interface + +The line protocol interface supports multiple modes and different precision and requires the introduction of constants in the schemaless module to set. + +```rust +use libtaos::*; +use libtaos::schemaless::*; +``` + +- InfluxDB line protocol + + ```rust + let lines = [ + "st,t1=abc,t2=def,t3=anything c1=3i64,c3=L\"pass\",c2=false 1626006833639000000" + "st,t1=abc,t2=def,t3=anything c1=3i64,c3=L\"abc\",c4=4f64 1626006833639000000" + ]; + taos.schemaless_insert(&lines, TSDB_SML_LINE_PROTOCOL, TSDB_SML_TIMESTAMP_NANOSECONDS)? ; + ``` + +- OpenTSDB Telnet Protocol + + ```rust + let lines = ["sys.if.bytes.out 1479496100 1.3E3 host=web01 interface=eth0"]; + taos.schemaless_insert(&lines, TSDB_SML_LINE_PROTOCOL, TSDB_SML_TIMESTAMP_SECONDS)? ; + ``` + +- OpenTSDB JSON protocol + + ```rust + let lines = [r#" + { + "metric": "st", + "timestamp": 1626006833, + "value": 10, + "tags": { + "t1": true, + "t2": false, + "t3": 10, + "t4": "123_abc_.! @#$%^&*:;,. /? |+-=()[]{}<>" + } + }"#]; + taos.schemaless_insert(&lines, TSDB_SML_LINE_PROTOCOL, TSDB_SML_TIMESTAMP_SECONDS)? ; + ``` + +Please move to the Rust documentation hosting page for other related structure API usage instructions: . + +[libtaos]: https://github.com/taosdata/libtaos-rs +[tdengine]: https://github.com/taosdata/TDengine +[bailongma-rs]: https://github.com/taosdata/bailongma-rs +[r2d2]: https://crates.io/crates/r2d2 +[demo.rs]: https://github.com/taosdata/libtaos-rs/blob/main/examples/demo.rs +[TaosCfgBuilder]: https://docs.rs/libtaos/latest/libtaos/struct.TaosCfgBuilder.html +[TaosCfg]: https://docs.rs/libtaos/latest/libtaos/struct.TaosCfg.html +[Taos]: https://docs.rs/libtaos/latest/libtaos/struct.Taos.html +[TaosQueryData]: https://docs.rs/libtaos/latest/libtaos/field/struct.TaosQueryData.html +[Field]: https://docs.rs/libtaos/latest/libtaos/field/enum.Field.html +[Stmt]: https://docs.rs/libtaos/latest/libtaos/stmt/struct.Stmt.html diff --git a/docs-en/14-reference/03-connector/tdengine-jdbc-connector.png b/docs-en/14-reference/03-connector/tdengine-jdbc-connector.png new file mode 100644 index 0000000000000000000000000000000000000000..7541aaf98ad73cbddac44c34bd775b32ab3a735e Binary files /dev/null and b/docs-en/14-reference/03-connector/tdengine-jdbc-connector.png differ diff --git a/docs-en/14-reference/04-taosadapter.md b/docs-en/14-reference/04-taosadapter.md new file mode 100644 index 0000000000000000000000000000000000000000..85fd2923b02189d6f3cfd73efff784d12c3bb69a --- /dev/null +++ b/docs-en/14-reference/04-taosadapter.md @@ -0,0 +1,338 @@ +--- +title: "taosAdapter" +description: "taosAdapter is a TDengine companion tool that acts as a bridge and adapter between TDengine clusters and applications. It provides an easy-to-use and efficient way to ingest data directly from data collection agent software such as Telegraf, StatsD, collectd, etc. It also provides an InfluxDB/OpenTSDB compatible data ingestion interface, allowing InfluxDB/OpenTSDB applications to be seamlessly ported to TDengine." +sidebar_label: "taosAdapter" +--- + +import Prometheus from "./_prometheus.mdx" +import CollectD from "./_collectd.mdx" +import StatsD from "./_statsd.mdx" +import Icinga2 from "./_icinga2.mdx" +import TCollector from "./_tcollector.mdx" + +taosAdapter is a TDengine companion tool that acts as a bridge and adapter between TDengine clusters and applications. It provides an easy-to-use and efficient way to ingest data directly from data collection agent software such as Telegraf, StatsD, collectd, etc. It also provides an InfluxDB/OpenTSDB compatible data ingestion interface that allows InfluxDB/OpenTSDB applications to be seamlessly ported to TDengine. + +taosAdapter provides the following features. + +- RESTful interface +- InfluxDB v1 compliant write interface +- OpenTSDB JSON and telnet format writes compatible +- Seamless connection to Telegraf +- Seamless connection to collectd +- Seamless connection to StatsD +- Supports Prometheus remote_read and remote_write + +## taosAdapter architecture diagram + +![taosAdapter Architecture](taosAdapter-architecture.png) + +## taosAdapter Deployment Method + +### Install taosAdapter + +taosAdapter has been part of TDengine server software since TDengine v2.4.0.0. If you use the TDengine server, you don't need additional steps to install taosAdapter. You can download taosAdapter from [TAOSData official website](https://taosdata.com/en/all-downloads/) to download the TDengine server installation package (taosAdapter is included in v2.4.0.0 and later version). If you need to deploy taosAdapter separately on another server other than the TDengine server, you should install the full TDengine on that server to install taosAdapter. If you need to build taosAdapter from source code, you can refer to the [Building taosAdapter]( https://github.com/taosdata/taosadapter/blob/develop/BUILD.md) documentation. + +### start/stop taosAdapter + +On Linux systems, the taosAdapter service is managed by `systemd` by default. You can use the command `systemctl start taosadapter` to start the taosAdapter service and use the command `systemctl stop taosadapter` to stop the taosAdapter service. + +### Remove taosAdapter + +Use the command `rmtaos` to remove the TDengine server software if you use tar.gz package or use package management command like rpm or apt to remove the TDengine server, including taosAdapter. + +### Upgrade taosAdapter + +taosAdapter and TDengine server need to use the same version. Please upgrade the taosAdapter by upgrading the TDengine server. +You need to upgrade the taosAdapter deployed separately from TDengine server by upgrading the TDengine server on the deployed server. + +## taosAdapter parameter list + +taosAdapter is configurable via command-line arguments, environment variables and configuration files. The default configuration file is /etc/taos/taosadapter.toml on Linux. + +Command-line arguments take precedence over environment variables over configuration files. The command-line usage is arg=val, e.g., taosadapter -p=30000 --debug=true. The detailed list is as follows: + +```shell +Usage of taosAdapter: + --collectd.db string collectd db name. Env "TAOS_ADAPTER_COLLECTD_DB" (default "collectd") + --collectd.enable enable collectd. Env "TAOS_ADAPTER_COLLECTD_ENABLE" (default true) + --collectd.password string collectd password. Env "TAOS_ADAPTER_COLLECTD_PASSWORD" (default "taosdata") + --collectd.port int collectd server port. Env "TAOS_ADAPTER_COLLECTD_PORT" (default 6045) + --collectd.user string collectd user. Env "TAOS_ADAPTER_COLLECTD_USER" (default "root") + --collectd.worker int collectd write worker. Env "TAOS_ADAPTER_COLLECTD_WORKER" (default 10) + -c, --config string config path default /etc/taos/taosadapter.toml + --cors.allowAllOrigins cors allow all origins. Env "TAOS_ADAPTER_CORS_ALLOW_ALL_ORIGINS" (default true) + --cors.allowCredentials cors allow credentials. Env "TAOS_ADAPTER_CORS_ALLOW_Credentials" + --cors.allowHeaders stringArray cors allow HEADERS. Env "TAOS_ADAPTER_ALLOW_HEADERS" + --cors.allowOrigins stringArray cors allow origins. Env "TAOS_ADAPTER_ALLOW_ORIGINS" + --cors.allowWebSockets cors allow WebSockets. Env "TAOS_ADAPTER_CORS_ALLOW_WebSockets" + --cors.exposeHeaders stringArray cors expose headers. Env "TAOS_ADAPTER_Expose_Headers" + --debug enable debug mode. Env "TAOS_ADAPTER_DEBUG" + --help Print this help message and exit + --influxdb.enable enable influxdb. Env "TAOS_ADAPTER_INFLUXDB_ENABLE" (default true) + --log.path string log path. Env "TAOS_ADAPTER_LOG_PATH" (default "/var/log/taos") + --log.rotationCount uint log rotation count. Env "TAOS_ADAPTER_LOG_ROTATION_COUNT" (default 30) + --log.rotationSize string log rotation size(KB MB GB), must be a positive integer. Env "TAOS_ADAPTER_LOG_ROTATION_SIZE" (default "1GB") + --log.rotationTime duration log rotation time. Env "TAOS_ADAPTER_LOG_ROTATION_TIME" (default 24h0m0s) + --logLevel string log level (panic fatal error warn warning info debug trace). Env "TAOS_ADAPTER_LOG_LEVEL" (default "info") + --monitor.collectDuration duration Set monitor duration. Env "TAOS_MONITOR_COLLECT_DURATION" (default 3s) + --monitor.identity string The identity of the current instance, or 'hostname:port' if it is empty. Env "TAOS_MONITOR_IDENTITY" + --monitor.incgroup Whether running in cgroup. Env "TAOS_MONITOR_INCGROUP" + --monitor.password string TDengine password. Env "TAOS_MONITOR_PASSWORD" (default "taosdata") + --monitor.pauseAllMemoryThreshold float Memory percentage threshold for pause all. Env "TAOS_MONITOR_PAUSE_ALL_MEMORY_THRESHOLD" (default 80) + --monitor.pauseQueryMemoryThreshold float Memory percentage threshold for pause query. Env "TAOS_MONITOR_PAUSE_QUERY_MEMORY_THRESHOLD" (default 70) + --monitor.user string TDengine user. Env "TAOS_MONITOR_USER" (default "root") + --monitor.writeInterval duration Set write to TDengine interval. Env "TAOS_MONITOR_WRITE_INTERVAL" (default 30s) + --monitor.writeToTD Whether write metrics to TDengine. Env "TAOS_MONITOR_WRITE_TO_TD" (default true) + --node_exporter.caCertFile string node_exporter ca cert file path. Env "TAOS_ADAPTER_NODE_EXPORTER_CA_CERT_FILE" + --node_exporter.certFile string node_exporter cert file path. Env "TAOS_ADAPTER_NODE_EXPORTER_CERT_FILE" + --node_exporter.db string node_exporter db name. Env "TAOS_ADAPTER_NODE_EXPORTER_DB" (default "node_exporter") + --node_exporter.enable enable node_exporter. Env "TAOS_ADAPTER_NODE_EXPORTER_ENABLE" + --node_exporter.gatherDuration duration node_exporter gather duration. Env "TAOS_ADAPTER_NODE_EXPORTER_GATHER_DURATION" (default 5s) + --node_exporter.httpBearerTokenString string node_exporter http bearer token. Env "TAOS_ADAPTER_NODE_EXPORTER_HTTP_BEARER_TOKEN_STRING" + --node_exporter.httpPassword string node_exporter http password. Env "TAOS_ADAPTER_NODE_EXPORTER_HTTP_PASSWORD" + --node_exporter.httpUsername string node_exporter http username. Env "TAOS_ADAPTER_NODE_EXPORTER_HTTP_USERNAME" + --node_exporter.insecureSkipVerify node_exporter skip ssl check. Env "TAOS_ADAPTER_NODE_EXPORTER_INSECURE_SKIP_VERIFY" (default true) + --node_exporter.keyFile string node_exporter cert key file path. Env "TAOS_ADAPTER_NODE_EXPORTER_KEY_FILE" + --node_exporter.password string node_exporter password. Env "TAOS_ADAPTER_NODE_EXPORTER_PASSWORD" (default "taosdata") + --node_exporter.responseTimeout duration node_exporter response timeout. Env "TAOS_ADAPTER_NODE_EXPORTER_RESPONSE_TIMEOUT" (default 5s) + --node_exporter.urls strings node_exporter urls. Env "TAOS_ADAPTER_NODE_EXPORTER_URLS" (default [http://localhost:9100]) + --node_exporter.user string node_exporter user. Env "TAOS_ADAPTER_NODE_EXPORTER_USER" (default "root") + --opentsdb.enable enable opentsdb. Env "TAOS_ADAPTER_OPENTSDB_ENABLE" (default true) + --opentsdb_telnet.dbs strings opentsdb_telnet db names. Env "TAOS_ADAPTER_OPENTSDB_TELNET_DBS" (default [opentsdb_telnet,collectd_tsdb,icinga2_tsdb,tcollector_tsdb]) + --opentsdb_telnet.enable enable opentsdb telnet,warning: without auth info(default false). Env "TAOS_ADAPTER_OPENTSDB_TELNET_ENABLE" + --opentsdb_telnet.maxTCPConnections int max tcp connections. Env "TAOS_ADAPTER_OPENTSDB_TELNET_MAX_TCP_CONNECTIONS" (default 250) + --opentsdb_telnet.password string opentsdb_telnet password. Env "TAOS_ADAPTER_OPENTSDB_TELNET_PASSWORD" (default "taosdata") + --opentsdb_telnet.ports ints opentsdb telnet tcp port. Env "TAOS_ADAPTER_OPENTSDB_TELNET_PORTS" (default [6046,6047,6048,6049]) + --opentsdb_telnet.tcpKeepAlive enable tcp keep alive. Env "TAOS_ADAPTER_OPENTSDB_TELNET_TCP_KEEP_ALIVE" + --opentsdb_telnet.user string opentsdb_telnet user. Env "TAOS_ADAPTER_OPENTSDB_TELNET_USER" (default "root") + --pool.idleTimeout duration Set idle connection timeout. Env "TAOS_ADAPTER_POOL_IDLE_TIMEOUT" (default 1h0m0s) + --pool.maxConnect int max connections to taosd. Env "TAOS_ADAPTER_POOL_MAX_CONNECT" (default 4000) + --pool.maxIdle int max idle connections to taosd. Env "TAOS_ADAPTER_POOL_MAX_IDLE" (default 4000) + -P, --port int http port. Env "TAOS_ADAPTER_PORT" (default 6041) + --prometheus.enable enable prometheus. Env "TAOS_ADAPTER_PROMETHEUS_ENABLE" (default true) + --restfulRowLimit int restful returns the maximum number of rows (-1 means no limit). Env "TAOS_ADAPTER_RESTFUL_ROW_LIMIT" (default -1) + --ssl.certFile string ssl cert file path. Env "TAOS_ADAPTER_SSL_CERT_FILE" + --ssl.enable enable ssl. Env "TAOS_ADAPTER_SSL_ENABLE" + --ssl.keyFile string ssl key file path. Env "TAOS_ADAPTER_SSL_KEY_FILE" + --statsd.allowPendingMessages int statsd allow pending messages. Env "TAOS_ADAPTER_STATSD_ALLOW_PENDING_MESSAGES" (default 50000) + --statsd.db string statsd db name. Env "TAOS_ADAPTER_STATSD_DB" (default "statsd") + --statsd.deleteCounters statsd delete counter cache after gather. Env "TAOS_ADAPTER_STATSD_DELETE_COUNTERS" (default true) + --statsd.deleteGauges statsd delete gauge cache after gather. Env "TAOS_ADAPTER_STATSD_DELETE_GAUGES" (default true) + --statsd.deleteSets statsd delete set cache after gather. Env "TAOS_ADAPTER_STATSD_DELETE_SETS" (default true) + --statsd.deleteTimings statsd delete timing cache after gather. Env "TAOS_ADAPTER_STATSD_DELETE_TIMINGS" (default true) + --statsd.enable enable statsd. Env "TAOS_ADAPTER_STATSD_ENABLE" (default true) + --statsd.gatherInterval duration statsd gather interval. Env "TAOS_ADAPTER_STATSD_GATHER_INTERVAL" (default 5s) + --statsd.maxTCPConnections int statsd max tcp connections. Env "TAOS_ADAPTER_STATSD_MAX_TCP_CONNECTIONS" (default 250) + --statsd.password string statsd password. Env "TAOS_ADAPTER_STATSD_PASSWORD" (default "taosdata") + --statsd.port int statsd server port. Env "TAOS_ADAPTER_STATSD_PORT" (default 6044) + --statsd.protocol string statsd protocol [tcp or udp]. Env "TAOS_ADAPTER_STATSD_PROTOCOL" (default "udp") + --statsd.tcpKeepAlive enable tcp keep alive. Env "TAOS_ADAPTER_STATSD_TCP_KEEP_ALIVE" + --statsd.user string statsd user. Env "TAOS_ADAPTER_STATSD_USER" (default "root") + --statsd.worker int statsd write worker. Env "TAOS_ADAPTER_STATSD_WORKER" (default 10) + --taosConfigDir string load taos client config path. Env "TAOS_ADAPTER_TAOS_CONFIG_FILE" + --version Print the version and exit +``` + +Note: +Please set the following Cross-Origin Resource Sharing (CORS) parameters according to the actual situation when using a browser for interface calls. + +```text +AllowAllOrigins +AllowOrigins +AllowHeaders +ExposeHeaders +AllowCredentials +AllowWebSockets +``` + +You do not need to care about these configurations if you do not make interface calls through the browser. + +For details on the CORS protocol, please refer to: [https://www.w3.org/wiki/CORS_Enabled](https://www.w3.org/wiki/CORS_Enabled) or [https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS). + +See [example/config/taosadapter.toml](https://github.com/taosdata/taosadapter/blob/develop/example/config/taosadapter.toml) for sample configuration files. + +## Feature List + +- Compatible with RESTful interfaces + [https://www.taosdata.com/cn/documentation/connector#restful](https://www.taosdata.com/cn/documentation/connector#restful) +- Compatible with InfluxDB v1 write interface + [https://docs.influxdata.com/influxdb/v2.0/reference/api/influxdb-1x/write/](https://docs.influxdata.com/influxdb/v2.0/reference/api/influxdb-1x/write/) +- Compatible with OpenTSDB JSON and telnet format writes + - + - +- Seamless connection to collectd + collectd is a system statistics collection daemon, please visit [https://collectd.org/](https://collectd.org/) for more information. +- Seamless connection with StatsD + StatsD is a simple yet powerful daemon for aggregating statistical information. Please visit [https://github.com/statsd/statsd](https://github.com/statsd/statsd) for more information. +- Seamless connection with icinga2 + icinga2 is a software that collects inspection result metrics and performance data. Please visit [https://icinga.com/docs/icinga-2/latest/doc/14-features/#opentsdb-writer](https://icinga.com/docs/icinga-2/latest/doc/14-features/#opentsdb-writer) for more information. +- Seamless connection to TCollector + TCollector is a client process that collects data from a local collector and pushes the data to OpenTSDB. Please visit [http://opentsdb.net/docs/build/html/user_guide/utilities/tcollector.html](http://opentsdb.net/docs/build/html/user_guide/utilities/tcollector.html) for more information. +- Seamless connection to node_exporter + node_export is an exporter for machine metrics. Please visit [https://github.com/prometheus/node_exporter](https://github.com/prometheus/node_exporter) for more information. +- Support for Prometheus remote_read and remote_write + remote_read and remote_write are interfaces for Prometheus data read and write from/to other data storage solution. Please visit [https://prometheus.io/blog/2019/10/10/remote-read-meets-streaming/#remote-apis](https://prometheus.io/blog/2019/10/10/remote-read-meets-streaming/#remote-apis) for more information. + +## Interfaces + +### TDengine RESTful interface + +You can use any client that supports the http protocol to write data to or query data from TDengine by accessing the REST interface address `http://:6041/`. See the [official documentation](/reference/connector#restful) for details. The following EndPoint is supported. + +```text +/rest/sql +/rest/sqlt +/rest/sqlutc +``` + +### InfluxDB + +You can use any client that supports the http protocol to access the Restful interface address `http://:6041/` to write data in InfluxDB compatible format to TDengine. The EndPoint is as follows: + +```text +/influxdb/v1/write +``` + +Support InfluxDB query parameters as follows. + +- `db` Specifies the database name used by TDengine +- `precision` The time precision used by TDengine +- `u` TDengine user name +- `p` TDengine password + +Note: InfluxDB token authorization is not supported at present. Only Basic authorization and query parameter validation are supported. + +### OpenTSDB + +You can use any client that supports the http protocol to access the Restful interface address `http://:6041/` to write data in OpenTSDB compatible format to TDengine. + +```text +/opentsdb/v1/put/json/:db +/opentsdb/v1/put/telnet/:db +``` + +### collectd + + + +### StatsD + + + +### icinga2 OpenTSDB writer + + + +### TCollector + + + +### node_exporter + +node_export is an exporter of hardware and OS metrics exposed by the \*NIX kernel used by Prometheus + +- Enable the taosAdapter configuration `node_exporter.enable` +- Set the configuration of the node_exporter +- Restart taosAdapter + +### Prometheus + + + +## Memory usage optimization methods + +taosAdapter will monitor its memory usage during operation and adjust it with two thresholds. Valid values range from -1 to 100 integers in percent of the system's physical memory. + +- pauseQueryMemoryThreshold +- pauseAllMemoryThreshold + +Stops processing query requests when the `pauseQueryMemoryThreshold` threshold is exceeded. + +HTTP response content. + +- code 503 +- body "query memory exceeds threshold" + +Stops processing all write and query requests when the `pauseAllMemoryThreshold` threshold is exceeded. + +HTTP response: code 503 + +- code 503 +- body "memory exceeds threshold" + +Resume the corresponding function when the memory falls back below the threshold. + +Status check interface `http://:6041/-/ping` + +- Normal returns `code 200` +- No parameter If memory exceeds pauseAllMemoryThreshold returns `code 503` +- Request parameter `action=query` returns `code 503` if memory exceeds `pauseQueryMemoryThreshold` or `pauseAllMemoryThreshold` + +Corresponding configuration parameter + +``text + monitor.collectDuration monitoring interval environment variable `TAOS_MONITOR_COLLECT_DURATION` (default value 3s) + monitor.incgroup whether to run in cgroup (set to true for running in container) environment variable `TAOS_MONITOR_INCGROUP` + monitor.pauseAllMemoryThreshold memory threshold for no more inserts and queries environment variable `TAOS_MONITOR_PAUSE_ALL_MEMORY_THRESHOLD` (default 80) + monitor.pauseQueryMemoryThreshold memory threshold for no more queries Environment variable `TAOS_MONITOR_PAUSE_QUERY_MEMORY_THRESHOLD` (default 70) +``` + +You can adjust it according to the specific application scenario and operation strategy, and it is recommended to use operation monitoring software to monitor system memory status timely. The load balancer can also check the taosAdapter running status through this interface. + +## taosAdapter Monitoring Metrics + +taosAdapter collects HTTP-related metrics, CPU percentage, and memory percentage. + +### HTTP interface + +Provides an interface conforming to [OpenMetrics](https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md). + +```text +http://:6041/metrics +``` + +### Write to TDengine + +taosAdapter supports writing the metrics of HTTP monitoring, CPU percentage, and memory percentage to TDengine. + +For configuration parameters + +| **Configuration items** | **Description** | **Default values** | +| ----------------------- | --------------------------------------------------------- | ---------- | +| monitor.collectDuration | CPU and memory collection interval | 3s | +| monitor.identity | The current taosadapter identifier will be used if not set to `hostname:port` | | +| monitor.incgroup | whether it is running in a cgroup (set to true for running in a container) | false | +| monitor.writeToTD | Whether to write to TDengine | true | +| monitor.user | TDengine connection username | root | +| monitor.password | TDengine connection password | taosdata | +| monitor.writeInterval | Write to TDengine interval | 30s | + +## Limit the number of results returned + +taosAdapter controls the number of results returned by the parameter `restfulRowLimit`, -1 means no limit, default is no limit. + +This parameter controls the number of results returned by the following interfaces: + +- `http://:6041/rest/sql` +- `http://:6041/rest/sqlt` +- `http://:6041/rest/sqlutc` +- ` http://:6041/prometheus/v1/remote_read/:db` + +## Troubleshooting + +You can check the taosAdapter running status with the `systemctl status taosadapter` command. + +You can also adjust the level of the taosAdapter log output by setting the `--logLevel` parameter or the environment variable `TAOS_ADAPTER_LOG_LEVEL`. Valid values are: panic, fatal, error, warn, warning, info, debug and trace. + +## How to migrate from older TDengine versions to taosAdapter + +In TDengine server 2.2.x.x or earlier, the TDengine server process (taosd) contains an embedded HTTP service. As mentioned earlier, taosAdapter is a standalone software managed using `systemd` and has its process ID. And there are some configuration parameters and behaviors that are different between the two. See the following table for details. + +| **#** | **embedded httpd** | **taosAdapter** | **comment** | +| ----- | ------------------- | ------------------------------------ | ------------------------------------------------------------------ ------------------------------------------------------------------------ | +| 1 | httpEnableRecordSql | --logLevel=debug | | +| 2 | httpMaxThreads | n/a | taosAdapter Automatically manages thread pools without this parameter | +| 3 | telegrafUseFieldNum | See the taosAdapter telegraf configuration method | | +| 4 | restfulRowLimit | restfulRowLimit | Embedded httpd outputs 10240 rows of data by default, the maximum allowed is 102400. taosAdapter also provides restfulRowLimit but it is not limited by default. You can configure it according to the actual scenario. +| 5 | httpDebugFlag | Not applicable | httpdDebugFlag does not work for taosAdapter | +| 6 | httpDBNameMandatory | N/A | taosAdapter requires the database name to be specified in the URL | diff --git a/docs-en/14-reference/05-taosbenchmark.md b/docs-en/14-reference/05-taosbenchmark.md new file mode 100644 index 0000000000000000000000000000000000000000..1e2b0b99f652bca0d775bebe28378600470f8661 --- /dev/null +++ b/docs-en/14-reference/05-taosbenchmark.md @@ -0,0 +1,434 @@ +--- +title: taosBenchmark +sidebar_label: taosBenchmark +toc_max_heading_level: 4 +description: "taosBenchmark (once called taosdemo ) is a tool for testing the performance of TDengine." +--- + +## Introduction + +taosBenchmark (formerly taosdemo ) is a tool for testing the performance of TDengine products. taosBenchmark can test the performance of TDengine's insert, query, and subscription functions and simulate large amounts of data generated by many devices. taosBenchmark can flexibly control the number and type of databases, supertables, tag columns, number and type of data columns, and sub-tables, and types of databases, super tables, the number and types of data columns, the number of sub-tables, the amount of data per sub-table, the time interval for inserting data, the number of working threads, whether and how to insert disordered data, and so on. The installer provides taosdemo as a soft link to taosBenchmark for compatibility with past users. + +## Installation + +There are two ways to install taosBenchmark: + +- Installing the official TDengine installer will automatically install taosBenchmark. Please refer to [TDengine installation](/operation/pkg-install) for details. + +- Compile taos-tools separately and install them. Please refer to the [taos-tools](https://github.com/taosdata/taos-tools) repository for details. + +## Run + +### Configuration and running methods + +taosBenchmark supports two configuration methods: [Command-line arguments](#Command-line arguments in detailed) and [JSON configuration file](#Configuration file arguments in detailed). These two methods are mutually exclusive, and with only one command-line parameter, users can use `-f ` to specify a configuration file when using a configuration file. When running taosBenchmark with command-line arguments and controlling its behavior, users should use other parameters for configuration rather than `-f` parameter. In addition, taosBenchmark offers a special way of running without parameters. + +taosBenchmark supports complete performance testing of TDengine. taosBenchmark supports the TDengine functions in three categories: write, query, and subscribe. These three functions are mutually exclusive, and users can select only one of them each time taosBenchmark runs. It is important to note that the type of functionality to be tested is not configurable when using the command-line configuration method, which can only test writing performance. To test the query and subscription performance of the TDengine, you must use the configuration file method and specify the function type to test via the parameter `filetype` in the configuration file. + +**Make sure that the TDengine cluster is running correctly before running taosBenchmark. ** + +### Run without command-line arguments + +Execute the following commands to quickly experience taosBenchmark's default configuration-based write performance testing of TDengine. + +```bash +taosBenchmark +``` + +When run without parameters, taosBenchmark connects to the TDengine cluster specified in `/etc/taos` by default and creates a database named test in TDengine, a super table named `meters` under the test database, and 10,000 tables under the super table with 10,000 records written to each table. Note that if there is already a test database, this table is not used. Note that if there is already a test database, this command will delete it first and create a new test database. + +### Run with command-line configuration parameters + +The `-f ` argument cannot be used when running taosBenchmark with command-line parameters and controlling its behavior. Users must specify all configuration parameters from the command-line. The following is an example of testing taosBenchmark writing performance using the command-line approach. + +```bash +taosBenchmark -I stmt -n 200 -t 100 +``` + +The above command, `taosBenchmark` will create a database named `test`, create a super table `meters` in it, create 100 sub-tables in the super table and insert 200 records for each sub-table using parameter binding. + +### Run with the configuration file + +A sample configuration file is provided in the taosBenchmark installation package under `/examples/taosbenchmark-json`. + +Use the following command-line to run taosBenchmark and control its behavior via a configuration file. + +```bash +taosBenchmark -f +``` + +**Here are a few examples of configuration files:** + +#### Example of inserting a scenario JSON configuration file + +
+insert.json + +```json +{{#include /taos-tools/example/insert.json}} +``` + +
+ +#### Query Scenario JSON Profile Example + +
+query.json + +```json +{{#include /taos-tools/example/query.json}} +``` + +
+ +#### Subscription JSON configuration example + +
+subscribe.json + +```json +{{#include /taos-tools/example/subscribe.json}} +``` + +
+ +## Command-line argument in detailed + +- **-f/--file ** : + specify the configuration file to use. This file includes All parameters. And users should not use this parameter with other parameters on the command-line. There is no default value. + +- **-c/--config-dir ** : + specify the directory where the TDengine cluster configuration file. the default path is `/etc/taos`. + +- **-h/--host ** : + Specify the FQDN of the TDengine server to connect to. The default value is localhost. + +- **-P/--port ** : + The port number of the TDengine server to connect to, the default value is 6030. + +- **-I/--interface ** : + Insert mode. Options are taosc, rest, stmt, sml, sml-rest, corresponding to normal write, restful interface writing, parameter binding interface writing, schemaless interface writing, RESTful schemaless interface writing (provided by taosAdapter). The default value is taosc. + +- **-u/--user ** : + User name to connect to the TDengine server. Default is root. + +- **-p/--password ** : + The default password to connect to the TDengine server is `taosdata`. + +- **-o/--output ** : + specify the path of the result output file, the default value is `. /output.txt`. + +- **-T/--thread ** : + The number of threads to insert data. Default is 8. + +- **-B/--interlace-rows ** : + Enables interleaved insertion mode and specifies the number of rows of data to be inserted into each child table. Interleaved insertion mode means inserting the number of rows specified by this parameter into each sub-table and repeating the process until all sub-tables have been inserted. The default value is 0, i.e., data is inserted into one sub-table before the next sub-table is inserted. + +- **-i/--insert-interval ** : + Specify the insert interval in `ms` for interleaved insert mode. The default value is 0. It only works if `-B/--interlace-rows` is greater than 0. That means that after inserting interlaced rows for each child table, the data insertion with multiple threads will wait for the interval specified by this value before proceeding to the next round of writes. + +- **-r/--rec-per-req ** : + Writing the number of rows of records per request to TDengine, the default value is 30000. + +- **-t/--tables ** : + Specify the number of sub-tables. The default is 10000. + +- **-S/--timestampstep ** : + Timestamp step for inserting data in each child table in ms, default is 1. + +- **-n/--records ** : + The default value of the number of records inserted in each sub-table is 10000. + +- **-d/--database ** : + The name of the database used, the default value is `test`. + +- **-b/--data-type ** : + specify the type of the data columns of the super table. It defaults to three columns of type FLOAT, INT, and FLOAT if not used. + +- **-l/--columns ** : + specify the number of columns in the super table. If both this parameter and `-b/--data-type` is set, the final result number of columns is the greater of the two. If the number specified by this parameter is greater than the number of columns specified by `-b/--data-type`, the unspecified column type defaults to INT, for example: `-l 5 -b float,double`, then the final column is `FLOAT,DOUBLE,INT,INT,INT`. If the number of columns specified is less than or equal to the number of columns specified by `-b/--data-type`, then the result is the column and type specified by `-b/--data-type`, e.g.: `-l 3 -b float,double,float,bigint`. The last column is `FLOAT,DOUBLE, FLOAT,BIGINT`. + +- **-A/--tag-type ** : + The tag column type of the super table. nchar and binary types can both set the length, for example: + +``` +taosBenchmark -A INT,DOUBLE,NCHAR,BINARY(16) +``` + +If users did not set tag type, the default is two tags, whose types are INT and BINARY(16). +Note: In some shells, such as bash, "()" needs to be escaped, so the above command should be + +``` +taosBenchmark -A INT,DOUBLE,NCHAR,BINARY\(16\) +``` + +- **-w/--binwidth **: + specify the default length for nchar and binary types. The default value is 64. + +- **-m/--table-prefix ** : + The prefix of the sub-table name, the default value is "d". + +- **-E/--escape-character** : + Switch parameter specifying whether to use escape characters in the super table and sub-table names. By default is not used. + +- **-C/--chinese** : + Switch specifying whether to use Unicode Chinese characters in nchar and binary. By default is not used. + +- **-N/--normal-table** : + This parameter indicates that taosBenchmark will create only normal tables instead of super tables. The default value is false. It can be used if the insert mode is taosc, stmt, and rest. + +- **-M/--random** : + This parameter indicates writing data with random values. The default is false. If users use this parameter, taosBenchmark will generate the random values. For tag/data columns of numeric type, the value is a random value within the range of values of that type. For NCHAR and BINARY type tag columns/data columns, the value is the random string within the specified length range. + +- **-x/--aggr-func** : + Switch parameter to indicate query aggregation function after insertion. The default value is false. + +- **-y/--answer-yes** : + Switch parameter that requires the user to confirm at the prompt to continue. The default value is false. + +- **-O/--disorder ** : + Specify the percentage probability of disordered data, with a value range of [0,50]. The default is 0, i.e., there is no disordered data. + +- **-R/--disorder-range ** : + Specify the timestamp range for the disordered data. It leads the resulting disorder timestamp as the ordered timestamp minus a random value in this range. Valid only if the percentage of disordered data specified by `-O/--disorder` is greater than 0. + +- **-F/--prepare_rand ** : + Specify the number of unique values in the generated random data. A value of 1 means that all data are equal. The default value is 10000. + +- **-a/--replica ** : + Specify the number of replicas when creating the database. The default value is 1. + +- **-V/--version** : + Show version information only. Users should not use it with other parameters. + +- **-? /--help** : + Show help information and exit. Users should not use it with other parameters. + +## Configuration file parameters in detailed + +### General configuration parameters + +The parameters listed in this section apply to all function modes. + +- **filetype** : The function to be tested, with optional values `insert`, `query` and `subscribe`. These correspond to the insert, query, and subscribe functions, respectively. Users can specify only one of these in each configuration file. +**cfgdir**: specify the TDengine cluster configuration file's directory. The default path is /etc/taos. + +- **host**: Specify the FQDN of the TDengine server to connect. The default value is `localhost`. + +- **port**: The port number of the TDengine server to connect to, the default value is `6030`. + +- **user**: The user name of the TDengine server to connect to, the default is `root`. + +- **password**: The password to connect to the TDengine server, the default value is `taosdata`. + +### Insert scenario configuration parameters + +`filetype` must be set to `insert` in the insertion scenario. See [General Configuration Parameters](#General Configuration Parameters) + +#### Database related configuration parameters + +The parameters related to database creation are configured in `dbinfo` in the json configuration file, as follows. These parameters correspond to the database parameters specified when `create database` in TDengine. + +- **name**: specify the name of the database. + +- **drop**: indicate whether to delete the database before inserting. The default is true. + +- **replica**: specify the number of replicas when creating the database. + +- **days**: specify the time span for storing data in a single data file. The default is 10. + +- **cache**: specify the size of the cache blocks in MB. The default value is 16. + +- **blocks**: specify the number of cache blocks in each vnode. The default is 6. + +- **precision**: specify the database time precision. The default value is "ms". + +- **keep**: specify the number of days to keep the data. The default value is 3650. + +- **minRows**: specify the minimum number of records in the file block. The default value is 100. + +- **maxRows**: specify the maximum number of records in the file block. The default value is 4096. + +- **comp**: specify the file compression level. The default value is 2. + +- **walLevel** : specify WAL level, default is 1. + +- **cacheLast**: indicate whether to allow the last record of each table to be kept in memory. The default value is 0. The value can be 0, 1, 2, or 3. + +- **quorum**: specify the number of writing acknowledgments in multi-replica mode. The default value is 1. + +- **fsync**: specify the interval of fsync in ms when users set WAL to 2. The default value is 3000. + +- **update** : indicate whether to support data update, default value is 0, optional values are 0, 1, 2. + +#### Super table related configuration parameters + +The parameters for creating super tables are configured in `super_tables` in the json configuration file, as shown below. + +- **name**: Super table name, mandatory, no default value. +- **child_table_exists** : whether the child table already exists, default value is "no", optional value is "yes" or "no". + +- **child_table_count** : The number of child tables, the default value is 10. + +- **child_table_prefix** : The prefix of the child table name, mandatory configuration item, no default value. + +- **escape_character**: specify the super table and child table names containing escape characters. By default is "no". The value can be "yes" or "no". + +- **auto_create_table**: only when insert_mode is taosc, rest, stmt, and childtable_exists is "no". "yes" means taosBenchmark will automatically create non-existent tables when inserting data; "no" means that taosBenchmark will create all tables before inserting. + +- **batch_create_tbl_num** : the number of tables per batch when creating sub-tables, default is 10. Note: the actual number of batches may not be the same as this value when the executed SQL statement is larger than the maximum length supported, it will be automatically truncated and re-executed to continue creating. + +- **data_source**: specify the source of data-generating. Default is taosBenchmark randomly generated. Users can configure it as "rand" and "sample". When "sample" is used, taosBenchmark will use the data in the file specified by the `sample_file` parameter. + +- **insert_mode**: insertion mode with options taosc, rest, stmt, sml, sml-rest, corresponding to normal write, restful interface write, parameter binding interface write, schemaless interface write, restful schemaless interface write (provided by taosAdapter). The default value is taosc. + +- **non_stop_mode**: Specify whether to keep writing. If "yes", insert_rows will be disabled, and writing will not stop until Ctrl + C stops the program. The default value is "no", i.e., taosBenchmark will stop the writing after the specified number of rows are written. Note: insert_rows must be configured as a non-zero positive integer even if it fails in continuous write mode. + +- **line_protocol**: Insert data using line protocol. Only works when insert_mode is sml or sml-rest. The value can be `line`, `telnet`, or `json`. + +- **tcp_transfer**: Communication protocol in telnet mode only takes effect when insert_mode is sml-rest, and line_protocol is telnet. If not configured, the default protocol is http. + +- **insert_rows** : The number of inserted rows per child table, default is 0. + +- **childtable_offset**: Effective only if childtable_exists is yes, specifies the offset when fetching the list of child tables from the super table, i.e., starting from the first child table. + +- **childtable_limit**: Effective only when childtable_exists is yes, specifies the upper limit for fetching the list of child tables from the super table. + +- **interlace_rows**: Enables interleaved insertion mode and specifies the number of rows of data to be inserted into each child table at a time. Staggered insertion mode means inserting the number of rows specified by this parameter into each sub-table and repeating the process until all sub-tables have been inserted. The default value is 0, i.e., data is inserted into one sub-table before the next sub-table is inserted. + +- **insert_interval** : Specifies the insertion interval in ms for interleaved insertion mode. The default value is 0. It only works if `-B/--interlace-rows` is greater than 0. After inserting interlaced rows for each child table, the data insertion thread will wait for the interval specified by this value before proceeding to the next round of writes. + +- **partial_col_num**: If this value is a positive number n, only the first n columns are written to, only if insert_mode is taosc and rest, or all columns if n is 0. + +- **disorder_ratio** : Specifies the percentage probability of disordered data in the value range [0,50]. The default is 0, which means there is no disorder data. + +- **disorder_range** : Specifies the timestamp fallback range for the disordered data. The generated disorder timestamp is the timestamp that should be used in the non-disorder case minus a random value in this range. Valid only if the percentage of disordered data specified by `-O/--disorder` is greater than 0. + +- **timestamp_step**: The timestamp step for inserting data in each child table, in units consistent with the `precision` of the database, the default value is 1. + +- **start_timestamp** : The timestamp start value of each sub-table, the default value is now. + +- **sample_format**: The type of the sample data file, now only "csv" is supported. + +- **sample_file**: Specify a CSV format file as the data source. It only works when data_source is a sample. If the number of rows in the CSV file is less than or equal to prepared_rand, then taosBenchmark will read the CSV file data cyclically until it is the same as prepared_rand; otherwise, taosBenchmark will read only the rows with the number of prepared_rand. The final number of rows of data generated is the smaller of the two. + +- **use_sample_ts**: effective only when data_source is `sample`, indicates whether the CSV file specified by sample_file contains the first timestamp column. Default is no. If set to yes, the first column of the CSV file is used as `timestamp`. Since the timestamp of the same sub-table cannot be repeated, the amount of data generated depends on the same number of rows of data in the CSV file, and insert_rows will be invalidated. + +- **tags_file** : only works when insert_mode is taosc, rest. The final tag value is related to the childtable_count. Suppose the tag data rows in the CSV file are smaller than the given number of child tables. In that case, taosBenchmark will read the CSV file data cyclically until the number of child tables specified by childtable_count is generated. Otherwise, taosBenchmark will read the childtable_count rows of tag data only. The final number of child tables generated is the smaller of the two. + +#### Tag and Data Column Configuration Parameters + +The configuration parameters for specifying super table tag columns and data columns are in `columns` and `tag` in `super_tables`, respectively. + +- **type**: Specify the column type. For optional values, please refer to the data types supported by TDengine. + Note: JSON data type is unique and can only be used for tags. When using JSON type as a tag, there is and can only be this one tag. At this time, `count` and `len` represent the meaning of the number of key-value pairs within the JSON tag and the length of the value of each KV pair. Respectively, the value is a string by default. + +- **len**: Specifies the length of this data type, valid for NCHAR, BINARY, and JSON data types. If this parameter is configured for other data types, a value of 0 means that the column is always written with a null value; if it is not 0, it is ignored. + +- **count**: Specifies the number of consecutive occurrences of the column type, e.g., "count": 4096 generates 4096 columns of the specified type. + +- **name** : The name of the column, if used together with count, e.g. "name": "current", "count":3, then the names of the 3 columns are current, current_2. current_3. + +- **min**: The minimum value of the column/label of the data type. + +- **max**: The maximum value of the column/label of the data type. + +- **values**: The value field of the nchar/binary column/label, which will be chosen randomly from the values. + +#### insertion behavior configuration parameters + +- **thread_count**: specify the number of threads to insert data. Default is 8. + +- **create_table_thread_count** : The number of threads to build the table, default is 8. + +- **connection_pool_size** : The number of pre-established connections to the TDengine server. If not configured, it is the same number of threads specified. + +- **result_file** : The path to the result output file, the default value is . /output.txt. + +- **confirm_parameter_prompt**: The switch parameter requires the user to confirm after the prompt to continue. The default value is false. + +- **interlace_rows**: Enables interleaved insertion mode and specifies the number of rows of data to be inserted into each child table at a time. Interleaved insertion mode means inserting the number of rows specified by this parameter into each sub-table and repeating the process until all sub-tables are inserted. The default value is 0, which means that data will be inserted into the following child table only after data is inserted into one child table. + This parameter can also be configured in `super_tables`, and if so, the configuration in `super_tables` takes precedence and overrides the global setting. + +- **insert_interval** : + Specifies the insertion interval in ms for interleaved insertion mode. The default value is 0. Only works if `-B/--interlace-rows` is greater than 0. It means that after inserting interlace rows for each child table, the data insertion thread will wait for the interval specified by this value before proceeding to the next round of writes. + This parameter can also be configured in `super_tables`, and if configured, the configuration in `super_tables` takes high priority, overriding the global setting. + +- **num_of_records_per_req** : + The number of rows of data to be written per request to TDengine, the default value is 30000. When it is set too large, the TDengine client driver will return the corresponding error message, so you need to lower the setting of this parameter to meet the writing requirements. + +- **prepare_rand**: The number of unique values in the generated random data. A value of 1 means that all data are the same. The default value is 10000. + +### Query scenario configuration parameters + +`filetype` must be set to `query` in the query scenario. See [General Configuration Parameters](#General Configuration Parameters) for details of this parameter and other general parameters + +#### Configuration parameters for executing the specified query statement + +The configuration parameters for querying the sub-tables or the normal tables are set in `specified_table_query`. + +- **query_interval** : The query interval in seconds, the default value is 0. + +- **threads**: The number of threads to execute the query SQL, the default value is 1. + +- **sqls**. + - **sql**: the SQL command to be executed. + - **result**: the file to save the query result. If it is unspecified, taosBenchark will not save the result. + +#### Configuration parameters of query super table + +The configuration parameters of the super table query are set in `super_table_query`. + +- **stblname**: Specify the name of the super table to be queried, required. + +- **query_interval** : The query interval in seconds, the default value is 0. + +- **threads**: The number of threads to execute the query SQL, the default value is 1. + +- **sqls** : The default value is 1. + - **sql**: The SQL command to be executed. For the query SQL of super table, keep "xxxx" in the SQL command. The program will automatically replace it with all the sub-table names of the super table. + Replace it with all the sub-table names in the super table. + - **result**: The file to save the query result. If not specified, taosBenchmark will not save result. + +### Subscription scenario configuration parameters + +`filetype` must be set to `subscribe` in the subscription scenario. See [General Configuration Parameters](#General Configuration Parameters) for details of this and other general parameters + +#### Configuration parameters for executing the specified subscription statement + +The configuration parameters for subscribing to a sub-table or a generic table are set in `specified_table_query`. + +- **threads**: The number of threads to execute SQL, default is 1. + +- **interval**: The time interval to execute the subscription, in seconds, default is 0. + +- **restart** : "yes" means start a new subscription, "no" means continue the previous subscription, the default value is "no". + +- **keepProgress**: "yes" means keep the progress of the subscription, "no" means don't keep it, and the default value is "no". + +- **resubAfterConsume**: "yes" means cancel the previous subscription and then subscribe again, "no" means continue the previous subscription, and the default value is "no". + +- **sqls** : The default value is "no". + - **sql** : The SQL command to be executed, required. + - **result** : The file to save the query result, unspecified is not saved. + +#### Configuration parameters for subscribing to supertables + +The configuration parameters for subscribing to a super table are set in `super_table_query`. + +- **stblname**: The name of the super table to subscribe. + +- **threads**: The number of threads to execute SQL, default is 1. + +- **interval**: The time interval to execute the subscription, in seconds, default is 0. + +- **restart** : "yes" means start a new subscription, "no" means continue the previous subscription, the default value is "no". + +- **keepProgress**: "yes" means keep the progress of the subscription, "no" means don't keep it, and the default value is "no". + +- **resubAfterConsume**: "yes" means cancel the previous subscription and then subscribe again, "no" means continue the previous subscription, and the default value is "no". + +- **sqls** : The default value is "no". + - **sql**: SQL command to be executed, required; for the query SQL of the super table, keep "xxxx" in the SQL command, and the program will replace it with all the sub-table names of the super table automatically. + Replace it with all the sub-table names in the super table. + - **result**: The file to save the query result, if not specified, it will not be saved. diff --git a/docs-en/14-reference/06-taosdump.md b/docs-en/14-reference/06-taosdump.md new file mode 100644 index 0000000000000000000000000000000000000000..973999704b595ea9b742f1ef759f973aa1f05649 --- /dev/null +++ b/docs-en/14-reference/06-taosdump.md @@ -0,0 +1,115 @@ +--- +title: taosdump +description: "taosdump is a tool application that supports backing up data from a running TDengine cluster and restoring the backed up data to the same or another running TDengine cluster." +--- + +## Introduction + +taosdump is a tool application that supports backing up data from a running TDengine cluster and restoring the backed up data to the same or another running TDengine cluster. + +taosdump can back up a database, a super table, or a normal table as a logical data unit or backup data records in the database, super tables, and normal tables. When using taosdump, you can specify the directory path for data backup. If you do not specify a directory, taosdump will back up the data to the current directory by default. + +Suppose the specified location already has data files. In that case, taosdump will prompt the user and exit immediately to avoid data overwriting which means that the same path can only be used for one backup. +Please be careful if you see a prompt for this. + +taosdump is a logical backup tool and should not be used to back up any raw data, environment settings, +Users should not use taosdump to back up raw data, environment settings, hardware information, server configuration, or cluster topology. taosdump uses [Apache AVRO](https://avro.apache.org/) as the data file format to store backup data. + +## Installation + +There are two ways to install taosdump: + +- Install the taosTools official installer. Please find taosTools from [All download links](https://www.taosdata.com/all-downloads) page and download and install it. + +- Compile taos-tools separately and install it. Please refer to the [taos-tools](https://github.com/taosdata/taos-tools) repository for details. + +## Common usage scenarios + +### taosdump backup data + +1. backing up all databases: specify `-A` or `-all-databases` parameter. +2. backup multiple specified databases: use `-D db1,db2,... ` parameters; 3. +3. back up some super or normal tables in the specified database: use `-dbname stbname1 stbname2 tbname1 tbname2 ... ` parameters. Note that the first parameter of this input sequence is the database name, and only one database is supported. The second and subsequent parameters are the names of super or normal tables in that database, separated by spaces. +4. back up the system log database: TDengine clusters usually contain a system database named `log`. The data in this database is the data that TDengine runs itself, and the taosdump will not back up the log database by default. If users need to back up the log database, users can use the `-a` or `-allow-sys` command-line parameter. +5. Loose mode backup: taosdump version 1.4.1 onwards provides `-n` and `-L` parameters for backing up data without using escape characters and "loose" mode, which can reduce the number of backups if table names, column names, tag names do not use This can reduce the backup data time and backup data footprint if table names, column names, and tag names do not use `escape character`. If you are unsure about using `-n` and `-L` conditions, please use the default parameters for "strict" mode backup. See the [official documentation](/taos-sql/escape) for a description of escaped characters. + +:::tip +- taosdump versions after 1.4.1 provide the `-I` argument for parsing Avro file schema and data. If users specify `-s` then only taosdump will parse schema. +- Backups after taosdump 1.4.2 use the batch count specified by the `-B` parameter. The default value is 16384. If, in some environments, low network speed or disk performance causes "Error actual dump ... batch ..." can be tried by challenging the `-B` parameter to a smaller value. + +::: + +### taosdump recover data + +Restore the data file in the specified path: use the `-i` parameter plus the path to the data file. You should not use the same directory to backup different data sets, and you should not backup the same data set multiple times in the same path. Otherwise, the backup data will cause overwriting or multiple backups. + +:::tip +taosdump internally uses TDengine stmt binding API for writing recovery data and currently uses 16384 as one write batch for better data recovery performance. If there are more columns in the backup data, it may cause a "WAL size exceeds limit" error. You can try to adjust to a smaller value by using the `-B` parameter. + +::: + +## Detailed command-line parameter list + +The following is a detailed list of taosdump command-line arguments. + +``` +Usage: taosdump [OPTION...] dbname [tbname ...] + or: taosdump [OPTION...] --databases db1,db2,... + or: taosdump [OPTION...] --all-databases + or: taosdump [OPTION...] -i inpath + or: taosdump [OPTION...] -o outpath + + -h, --host=HOST Server host dumping data from. Default is + localhost. + -p, --password User password to connect to server. Default is + taosdata. + -P, --port=PORT Port to connect + -u, --user=USER User name used to connect to server. Default is + root. + -c, --config-dir=CONFIG_DIR Configure directory. Default is /etc/taos + -i, --inpath=INPATH Input file path. + -o, --outpath=OUTPATH Output file path. + -r, --resultFile=RESULTFILE DumpOut/In Result file path and name. + -a, --allow-sys Allow to dump system database + -A, --all-databases Dump all databases. + -D, --databases=DATABASES Dump inputted databases. Use comma to separate + databases' name. + -N, --without-property Dump database without its properties. + -s, --schemaonly Only dump tables' schema. + -y, --answer-yes Input yes for prompt. It will skip data file + checking! + -d, --avro-codec=snappy Choose an avro codec among null, deflate, snappy, + and lzma. + -S, --start-time=START_TIME Start time to dump. Either epoch or + ISO8601/RFC3339 format is acceptable. ISO8601 + format example: 2017-10-01T00:00:00.000+0800 or + 2017-10-0100:00:00:000+0800 or '2017-10-01 + 00:00:00.000+0800' + -E, --end-time=END_TIME End time to dump. Either epoch or ISO8601/RFC3339 + format is acceptable. ISO8601 format example: + 2017-10-01T00:00:00.000+0800 or + 2017-10-0100:00:00.000+0800 or '2017-10-01 + 00:00:00.000+0800' + -B, --data-batch=DATA_BATCH Number of data per query/insert statement when + backup/restore. Default value is 16384. If you see + 'error actual dump .. batch ..' when backup or if + you see 'WAL size exceeds limit' error when + restore, please adjust the value to a smaller one + and try. The workable value is related to the + length of the row and type of table schema. + -I, --inspect inspect avro file content and print on screen + -L, --loose-mode Using loose mode if the table name and column name + use letter and number only. Default is NOT. + -n, --no-escape No escape char '`'. Default is using it. + -T, --thread-num=THREAD_NUM Number of thread for dump in file. Default is + 5. + -g, --debug Print debug info. + -?, --help Give this help list + --usage Give a short usage message + -V, --version Print program version + +Mandatory or optional arguments to long options are also mandatory or optional +for any corresponding short options. + +Report bugs to . +``` diff --git a/docs-en/14-reference/07-tdinsight/assets/15146-tdengine-monitor-dashboard.json b/docs-en/14-reference/07-tdinsight/assets/15146-tdengine-monitor-dashboard.json new file mode 100644 index 0000000000000000000000000000000000000000..f651983528ca824b4e6b14586aac5a5bfb4ecab8 --- /dev/null +++ b/docs-en/14-reference/07-tdinsight/assets/15146-tdengine-monitor-dashboard.json @@ -0,0 +1,3191 @@ +{ + "__inputs": [ + { + "name": "DS_TDENGINE", + "label": "TDengine", + "description": "", + "type": "datasource", + "pluginId": "tdengine-datasource", + "pluginName": "TDengine" + } + ], + "__requires": [ + { + "type": "panel", + "id": "gauge", + "name": "Gauge", + "version": "" + }, + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "7.5.10" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "panel", + "id": "piechart", + "name": "Pie chart v2", + "version": "" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + }, + { + "type": "datasource", + "id": "tdengine-datasource", + "name": "TDengine", + "version": "3.1.0" + }, + { + "type": "panel", + "id": "text", + "name": "Text", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "TDengine nodes metrics.", + "editable": true, + "gnetId": 15146, + "graphTooltip": 0, + "id": null, + "iteration": 1635263227798, + "links": [], + "panels": [ + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 57, + "panels": [], + "title": "Cluster Status", + "type": "row" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 24, + "x": 0, + "y": 1 + }, + "id": 32, + "options": { + "content": "

TDengine Cluster Dashboard

>\n", + "mode": "markdown" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "mnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show mnodes", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "-- OVERVIEW --", + "transformations": [ + { + "id": "calculateField", + "options": { + "binary": { + "left": "Time", + "operator": "+", + "reducer": "sum", + "right": "" + }, + "mode": "binary", + "reduce": { + "reducer": "sum" + } + } + } + ], + "type": "text" + }, + { + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 8, + "x": 0, + "y": 4 + }, + "id": 28, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": true + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show mnodes", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Master MNode", + "transformations": [ + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "regex", + "options": { + "value": "master" + } + }, + "fieldName": "role" + } + ], + "match": "all", + "type": "include" + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": ["dnodes"] + } + } + } + ], + "type": "stat" + }, + { + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 7, + "x": 8, + "y": 4 + }, + "id": 70, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "/^Time$/", + "values": true + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show mnodes", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Master MNode Create Time", + "transformations": [ + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "regex", + "options": { + "value": "master" + } + }, + "fieldName": "role" + } + ], + "match": "all", + "type": "include" + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": ["Time"] + } + } + }, + { + "id": "calculateField", + "options": { + "mode": "reduceRow", + "reduce": { + "reducer": "min" + } + } + } + ], + "type": "stat" + }, + { + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": null, + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 9, + "x": 15, + "y": 4 + }, + "id": 29, + "options": { + "showHeader": true + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show variables", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Variables", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": ["value", "name"] + } + } + }, + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "regex", + "options": { + "value": ".*" + } + }, + "fieldName": "name" + } + ], + "match": "all", + "type": "include" + } + } + ], + "type": "table" + }, + { + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 0, + "y": 7 + }, + "id": 33, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "/.*/", + "values": true + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Table", + "queryType": "SQL", + "refId": "A", + "sql": "select server_version()", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Server Version", + "transformations": [], + "type": "stat" + }, + { + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 2, + "y": 7 + }, + "id": 27, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": true + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show mnodes", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Number of MNodes", + "transformations": [ + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "greater", + "options": { + "value": 0 + } + }, + "fieldName": "id" + } + ], + "match": "any", + "type": "include" + } + }, + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": ["count"] + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": ["id"] + } + } + } + ], + "type": "stat" + }, + { + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 5, + "y": 7 + }, + "id": 41, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["last"], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show dnodes", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Total Dnodes", + "transformations": [ + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": ["count"] + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": ["id"] + } + } + } + ], + "type": "stat" + }, + { + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 7, + "y": 7 + }, + "id": 31, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["last"], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show dnodes", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Offline Dnodes", + "transformations": [ + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "regex", + "options": { + "value": "ready" + } + }, + "fieldName": "status" + } + ], + "match": "all", + "type": "exclude" + } + }, + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": ["count"] + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": ["id"] + } + } + } + ], + "type": "stat" + }, + { + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 9, + "y": 7 + }, + "id": 65, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": true + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show databases;", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Number of Databases", + "transformations": [ + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": ["count"] + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": ["name"] + } + } + } + ], + "type": "stat" + }, + { + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 12, + "y": 7 + }, + "id": 69, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": true + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show databases;", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Total Number of Vgroups", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": ["vgroups"] + } + } + }, + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": ["sum"] + } + } + ], + "type": "stat" + }, + { + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "displayMode": "auto", + "filterable": true + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "role" + }, + "properties": [ + { + "id": "mappings", + "value": [ + { + "from": "", + "id": 1, + "text": "", + "to": "", + "type": 2, + "value": "" + } + ] + } + ] + } + ] + }, + "gridPos": { + "h": 3, + "w": 9, + "x": 0, + "y": 10 + }, + "id": 67, + "options": { + "showHeader": true + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "formatType": "Table", + "queryType": "SQL", + "refId": "A", + "sql": "show dnodes", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Number of DNodes for each Role", + "transformations": [ + { + "id": "groupBy", + "options": { + "fields": { + "end_point": { + "aggregations": ["count"], + "operation": "aggregate" + }, + "role": { + "aggregations": [], + "operation": "groupby" + } + } + } + }, + { + "id": "filterFieldsByName", + "options": {} + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": {}, + "renameByName": { + "end_point (count)": "Number of DNodes", + "role": "Dnode Role" + } + } + } + ], + "type": "table" + }, + { + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 9, + "y": 10 + }, + "id": 55, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": true + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show connections", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Number of Connections", + "transformations": [ + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": ["count"] + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": ["connId"] + } + } + } + ], + "type": "stat" + }, + { + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 12, + "y": 10 + }, + "id": 68, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": true + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show databases;", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Total Number of Tables", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": ["ntables"] + } + } + }, + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": ["sum"] + } + } + ], + "type": "stat" + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 13 + }, + "id": 24, + "panels": [], + "title": "Dnodes Status", + "type": "row" + }, + { + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "displayMode": "auto", + "filterable": true + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "status" + }, + "properties": [ + { + "id": "custom.width", + "value": 86 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "vnodes" + }, + "properties": [ + { + "id": "custom.width", + "value": 77 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "role" + }, + "properties": [ + { + "id": "custom.width", + "value": 84 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "cores" + }, + "properties": [ + { + "id": "custom.width", + "value": 75 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "end_point" + }, + "properties": [ + { + "id": "custom.width", + "value": 205 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "id" + }, + "properties": [ + { + "id": "custom.width", + "value": 78 + } + ] + } + ] + }, + "gridPos": { + "h": 5, + "w": 16, + "x": 0, + "y": 14 + }, + "id": 36, + "options": { + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "formatType": "Table", + "queryType": "SQL", + "refId": "A", + "sql": "show dnodes", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "DNodes Status", + "type": "table" + }, + { + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 16, + "y": 14 + }, + "id": 40, + "options": { + "displayLabels": [], + "legend": { + "displayMode": "table", + "placement": "right", + "values": ["value"] + }, + "pieType": "pie", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "/.*/", + "values": false + }, + "text": { + "titleSize": 6 + } + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "formatType": "Table", + "queryType": "SQL", + "refId": "A", + "sql": "show dnodes", + "target": "select metric", + "type": "timeserie" + } + ], + "title": "Offline Reasons", + "transformations": [ + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "regex", + "options": { + "value": "ready" + } + }, + "fieldName": "status" + } + ], + "match": "all", + "type": "exclude" + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": ["offline reason", "end_point"] + } + } + }, + { + "id": "groupBy", + "options": { + "fields": { + "Time": { + "aggregations": ["count"], + "operation": "aggregate" + }, + "end_point": { + "aggregations": ["count"], + "operation": "aggregate" + }, + "offline reason": { + "aggregations": [], + "operation": "groupby" + } + } + } + } + ], + "type": "piechart" + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 19 + }, + "id": 22, + "panels": [], + "title": "Mnodes Status", + "type": "row" + }, + { + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 24, + "x": 0, + "y": 20 + }, + "id": 38, + "options": { + "showHeader": true + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "formatType": "Table", + "queryType": "SQL", + "refId": "A", + "sql": "show mnodes;", + "target": "select metric", + "type": "timeserie" + } + ], + "title": "Mnodes Status", + "type": "table" + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 25 + }, + "id": 20, + "panels": [], + "repeat": "fqdn", + "title": "节点资源占用 [ $fqdn ]", + "type": "row" + }, + { + "datasource": "${DS_TDENGINE}", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "decmbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 0, + "y": 26 + }, + "id": 66, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": ["mean"], + "fields": "/^taosd$/", + "values": false + }, + "showThresholdLabels": true, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "alias": "memory", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select last(mem_taosd) as taosd, last(mem_total) as total from log.dn where fqdn = '$fqdn' and ts >= now -5m and ts < now", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Current Memory Usage of taosd", + "type": "gauge" + }, + { + "datasource": "${DS_TDENGINE}", + "description": "taosd max memery last 10 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 0.5 + }, + { + "color": "red", + "value": 0.8 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "last(cpu_taosd)" + }, + "properties": [ + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + } + ] + } + ] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 5, + "y": 26 + }, + "id": 45, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": ["mean"], + "fields": "/^last\\(cpu_taosd\\)$/", + "values": false + }, + "showThresholdLabels": true, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "alias": "mem_taosd", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select last(cpu_taosd) from log.dn where fqdn = '$fqdn'", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Current CPU Usage of taosd", + "type": "gauge" + }, + { + "datasource": "${DS_TDENGINE}", + "description": "avg band speed last one minute", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 8192, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 4916 + }, + { + "color": "red", + "value": 6554 + } + ] + }, + "unit": "Kbits" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 4, + "x": 10, + "y": 26 + }, + "id": 14, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": ["last"], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "alias": "band_speed", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select avg(band_speed) from log.dn where fqdn='$fqdn' and ts >= now-5m and ts < now interval(1m)", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "band speed", + "type": "gauge" + }, + { + "datasource": "${DS_TDENGINE}", + "description": "io read/write rate", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 8192, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 4916 + }, + { + "color": "red", + "value": 6554 + } + ] + }, + "unit": "Kbits" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 14, + "y": 26 + }, + "id": 48, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": ["last"], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "alias": "", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select last(io_read) as io_read, last(io_write) as io_write from log.dn where fqdn='$fqdn' and ts >= now-1h and ts < now interval(1m)", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "IO Rate", + "type": "gauge" + }, + { + "datasource": "${DS_TDENGINE}", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 1, + "min": 0, + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 75 + }, + { + "color": "red", + "value": 80 + }, + { + "color": "dark-red", + "value": 95 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 19, + "y": 26 + }, + "id": 51, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": ["last"], + "fields": "/^disk_used_percent$/", + "values": false + }, + "showThresholdLabels": true, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "alias": "disk_used", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select last(disk_used) as used from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(1m)", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + }, + { + "alias": "disk_total", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "B", + "sql": "select last(disk_total) as total from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(1m)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "disk_used_percent", + "expression": "A/B", + "formatType": "Time series", + "hide": false, + "queryType": "Arithmetic", + "refId": "C", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Disk Used", + "transformations": [ + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": ["lastNotNull"] + } + } + ], + "type": "gauge" + }, + { + "datasource": "${DS_TDENGINE}", + "description": "taosd max memery last 10 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "decmbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 0, + "y": 32 + }, + "id": 12, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": ["mean"], + "fields": "/^taosd$/", + "values": false + }, + "showThresholdLabels": true, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "alias": "memory", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select max(mem_taosd) as taosd, max(mem_total) as total from log.dn where fqdn = '$fqdn' and ts >= now -5m and ts < now", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Max Memory Usage of taosd in Last 5 minute", + "type": "gauge" + }, + { + "datasource": "${DS_TDENGINE}", + "description": "taosd max memery last 10 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "max": 1, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 0.5 + }, + { + "color": "red", + "value": 0.8 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 5, + "y": 32 + }, + "id": 43, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": ["mean"], + "fields": "", + "values": false + }, + "showThresholdLabels": true, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "alias": "mem_taosd", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select max(cpu_taosd) from log.dn where fqdn = '$fqdn' and ts >= now -5m and ts < now", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Max CPU Usage of taosd in Last 5 minute", + "type": "gauge" + }, + { + "datasource": "${DS_TDENGINE}", + "description": "avg band speed last one minute", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 8192, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 4916 + }, + { + "color": "red", + "value": 6554 + } + ] + }, + "unit": "Kbits" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 4, + "x": 10, + "y": 32 + }, + "id": 50, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": ["last"], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "alias": "band_speed", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select max(band_speed) from log.dn where fqdn = '$fqdn' and ts >= now-1h", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Max band speed in last hour", + "type": "gauge" + }, + { + "datasource": "${DS_TDENGINE}", + "description": "io read/write rate", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 8192, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 4916 + }, + { + "color": "red", + "value": 6554 + } + ] + }, + "unit": "Kbits" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 14, + "y": 32 + }, + "id": 49, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": ["last"], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "alias": "", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select max(io_read) as io_read, max(io_write) as io_write from log.dn where fqdn = '$fqdn'", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Max IO Rate in last hour", + "type": "gauge" + }, + { + "datasource": "${DS_TDENGINE}", + "description": "io read/write rate", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 8192, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 4916 + }, + { + "color": "red", + "value": 6554 + } + ] + }, + "unit": "cpm" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 19, + "y": 32 + }, + "id": 52, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": ["last"], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "alias": "req-http", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select sum(req_http) as req_http from log.dn where fqdn = '$fqdn' and ts >= now - 1h interval(1m)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "req-inserts", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "B", + "sql": "select sum(req_insert) as req_insert from log.dn where fqdn = '$fqdn' and ts >= now - 1h interval(1m)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "req-selects", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "C", + "sql": "select sum(req_select) as req_select from log.dn where fqdn = '$fqdn' and ts >= now - 1h interval(1m)", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Requests in last Minute", + "type": "gauge" + }, + { + "aliasColors": {}, + "bars": false, + "cacheTimeout": null, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TDENGINE}", + "description": "monitor system cpu", + "fieldConfig": { + "defaults": { + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 11, + "w": 12, + "x": 0, + "y": 38 + }, + "hiddenSeries": false, + "hideTimeOverride": true, + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.10", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "cpu_system", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "A", + "sql": "select avg(cpu_system) from log.dn where fqdn='$fqdn' and ts >= now-1h and ts < now interval(30s)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "cpu_taosd", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "B", + "sql": "select avg(cpu_taosd) from log.dn where fqdn='$fqdn' and ts >= now-1h and ts < now interval(30s)", + "target": "select metric", + "type": "timeserie" + } + ], + "thresholds": [], + "timeFrom": "1h", + "timeRegions": [], + "timeShift": "30s", + "title": "CPU 资源占用情况", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": null, + "format": "percent", + "label": "使用占比", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "cacheTimeout": null, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TDENGINE}", + "description": "monitor system cpu", + "fieldConfig": { + "defaults": { + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 11, + "w": 12, + "x": 12, + "y": 38 + }, + "hiddenSeries": false, + "hideTimeOverride": true, + "id": 42, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.10", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "system", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "A", + "sql": "select avg(mem_system) from log.dn where fqdn = '$fqdn' and ts >= now-1h and ts < now interval(30s)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "taosd", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "B", + "sql": "select avg(mem_taosd) from log.dn where fqdn = '$fqdn' and ts >= now-1h and ts < now interval(30s)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "total", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "C", + "sql": "select avg(mem_total) from log.dn where fqdn = '$fqdn' and ts >= now-1h and ts < now interval(30s)", + "target": "select metric", + "type": "timeserie" + } + ], + "thresholds": [], + "timeFrom": "1h", + "timeRegions": [], + "timeShift": "30s", + "title": "内存资源占用情况", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": null, + "format": "decmbytes", + "label": "使用占比", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 49 + }, + "hiddenSeries": false, + "id": 54, + "legend": { + "alignAsTable": false, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.10", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "percent", + "yaxis": 2 + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "disk_used", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "A", + "sql": "select avg(disk_used) from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(30s)", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + }, + { + "alias": "disk_total", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "B", + "sql": "select avg(disk_total) from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(30s)", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + }, + { + "alias": "percent", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "D", + "sql": "select avg(disk_used)/avg(disk_total) * 100 from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(30s)", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Disk Used Percent", + "tooltip": { + "shared": false, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "gbytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "percent", + "label": "Disk Used", + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 49 + }, + "hiddenSeries": false, + "id": 64, + "legend": { + "alignAsTable": false, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.10", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "disk_used", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "A", + "sql": "select derivative(value, 1m, 0) from (select avg(disk_used) as value from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(1m))", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Disk Used Increasing Rate per Minute", + "tooltip": { + "shared": false, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "percentunit", + "label": "Disk Used", + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "cacheTimeout": null, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TDENGINE}", + "description": "total select request per minute last hour", + "fieldConfig": { + "defaults": { + "unit": "cpm" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 58 + }, + "hiddenSeries": false, + "id": 8, + "interval": null, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "maxDataPoints": 100, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.10", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "req_select", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select sum(req_select) from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(1m)", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + }, + { + "alias": "req_insert", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "B", + "sql": "select sum(req_insert) from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(1m)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "req_http", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "C", + "sql": "select sum(req_http) from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(1m)", + "target": "select metric", + "type": "timeserie" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Requets Count per Minutes $fqdn", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "cpm", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "cacheTimeout": null, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TDENGINE}", + "description": "io", + "fieldConfig": { + "defaults": { + "links": [], + "unit": "Kbits" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 58 + }, + "hiddenSeries": false, + "hideTimeOverride": true, + "id": 47, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.10", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "io-read", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "A", + "sql": "select avg(io_read) from log.dn where fqdn = '$fqdn' and ts >= now-1h and ts < now interval(1m)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "io-write", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "B", + "sql": "select avg(io_write) from log.dn where fqdn = '$fqdn' and ts >= now-1h and ts < now interval(1m)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "io-read-last-hour", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "C", + "sql": "select avg(io_read) from log.dn where fqdn = '$fqdn' and ts >= now-2h and ts < now - 1h interval(1m)", + "target": "select metric", + "timeshift": { + "period": 1, + "unit": "hours" + }, + "type": "timeserie" + }, + { + "alias": "io-write-last-hour", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "D", + "sql": "select avg(io_write) from log.dn where fqdn = '$fqdn' and ts >= now-1h and ts < now interval(1m)", + "target": "select metric", + "timeshift": { + "period": 1, + "unit": "hours" + }, + "type": "timeserie" + } + ], + "thresholds": [], + "timeFrom": "1h", + "timeRegions": [], + "timeShift": "30s", + "title": "IO", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": null, + "format": "Kbits", + "label": "IO Rate", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 67 + }, + "id": 63, + "panels": [], + "title": "Login History", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TDENGINE}", + "fieldConfig": { + "defaults": { + "displayName": "Logins Per Minute", + "unit": "cpm" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 68 + }, + "hiddenSeries": false, + "id": 61, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.10", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "logins", + "nullPointMode": "null as zero" + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "logins", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select count(*) from log.log where ts >= $from and ts < $to interval (1m)", + "target": "select metric", + "type": "timeserie" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Login Counts per Minute", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "cpm", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "1m", + "schemaVersion": 27, + "style": "dark", + "tags": ["TDengine", "multiple"], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "TDengine", + "value": "TDengine" + }, + "description": "TDengine Data Source Selector", + "error": null, + "hide": 0, + "includeAll": false, + "label": "Datasource", + "multi": false, + "name": "ds", + "options": [], + "query": "tdengine-datasource", + "queryValue": "", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "allValue": null, + "current": {}, + "datasource": "${DS_TDENGINE}", + "definition": "select fqdn from log.dn", + "description": "TDengine Nodes FQDN (Hostname)", + "error": null, + "hide": 0, + "includeAll": false, + "label": null, + "multi": false, + "name": "fqdn", + "options": [], + "query": "select fqdn from log.dn", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] + }, + "timezone": "", + "title": "Multiple TDengines Monitoring", + "uid": "tdengine-multiple", + "version": 4 +} diff --git a/docs-en/14-reference/07-tdinsight/assets/15155-tdengine-alert-demo.json b/docs-en/14-reference/07-tdinsight/assets/15155-tdengine-alert-demo.json new file mode 100644 index 0000000000000000000000000000000000000000..ab98354aaeae8c4dc945aae085d12fec50b530d1 --- /dev/null +++ b/docs-en/14-reference/07-tdinsight/assets/15155-tdengine-alert-demo.json @@ -0,0 +1,212 @@ +{ + "__inputs": [ + { + "name": "DS_TDENGINE", + "label": "TDengine", + "description": "", + "type": "datasource", + "pluginId": "tdengine-datasource", + "pluginName": "TDengine" + } + ], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "7.5.10" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "datasource", + "id": "tdengine-datasource", + "name": "TDengine", + "version": "3.1.0" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "", + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": null, + "links": [], + "panels": [ + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [5], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": ["A", "10s", "now"] + }, + "reducer": { + "params": [], + "type": "avg" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "10s", + "handler": 1, + "message": "This is a alert demo!", + "name": "cpu load average alert", + "noDataState": "no_data", + "notifications": [ + { + "uid": "4sKm9jd7k" + }, + { + "uid": "QyczKkFnk" + } + ] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_TDENGINE}", + "decimals": null, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 0 + }, + "hiddenSeries": false, + "id": 2, + "legend": { + "avg": true, + "current": true, + "hideEmpty": false, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.10", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "system", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select avg(cpu_system) as system from log.dn where ts >= now-1h and ts < now interval(30s)", + "target": "select metric", + "type": "timeserie" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 5, + "visible": true + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "CPU Load Average", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percent", + "label": "Percent", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "schemaVersion": 27, + "style": "dark", + "tags": ["TDengine"], + "templating": { + "list": [] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "TDengine Alert Demo", + "uid": "aLN9ujOnk", + "version": 15 +} diff --git a/docs-en/14-reference/07-tdinsight/assets/TDinsight-1-cluster-status.png b/docs-en/14-reference/07-tdinsight/assets/TDinsight-1-cluster-status.png new file mode 100644 index 0000000000000000000000000000000000000000..4708f836feb21980f2db7fed4a55f799b23a6ec1 Binary files /dev/null and b/docs-en/14-reference/07-tdinsight/assets/TDinsight-1-cluster-status.png differ diff --git a/docs-en/14-reference/07-tdinsight/assets/TDinsight-2-dnodes.png b/docs-en/14-reference/07-tdinsight/assets/TDinsight-2-dnodes.png new file mode 100644 index 0000000000000000000000000000000000000000..f2684e6eed70e8f56697eae42b495d6bd62815e8 Binary files /dev/null and b/docs-en/14-reference/07-tdinsight/assets/TDinsight-2-dnodes.png differ diff --git a/docs-en/14-reference/07-tdinsight/assets/TDinsight-3-mnodes.png b/docs-en/14-reference/07-tdinsight/assets/TDinsight-3-mnodes.png new file mode 100644 index 0000000000000000000000000000000000000000..74686691e4106b8646c3deee1e0ce73b2f53f1ea Binary files /dev/null and b/docs-en/14-reference/07-tdinsight/assets/TDinsight-3-mnodes.png differ diff --git a/docs-en/14-reference/07-tdinsight/assets/TDinsight-4-requests.png b/docs-en/14-reference/07-tdinsight/assets/TDinsight-4-requests.png new file mode 100644 index 0000000000000000000000000000000000000000..27964215567f9f961c0aeaf1b863188437008fb7 Binary files /dev/null and b/docs-en/14-reference/07-tdinsight/assets/TDinsight-4-requests.png differ diff --git a/docs-en/14-reference/07-tdinsight/assets/TDinsight-5-database.png b/docs-en/14-reference/07-tdinsight/assets/TDinsight-5-database.png new file mode 100644 index 0000000000000000000000000000000000000000..b0d3abbf21ec4d4bd7bfb95fcc03a5f936b22665 Binary files /dev/null and b/docs-en/14-reference/07-tdinsight/assets/TDinsight-5-database.png differ diff --git a/docs-en/14-reference/07-tdinsight/assets/TDinsight-6-dnode-usage.png b/docs-en/14-reference/07-tdinsight/assets/TDinsight-6-dnode-usage.png new file mode 100644 index 0000000000000000000000000000000000000000..2b54cbeb83bcff12f20461a4f57f882e2073f231 Binary files /dev/null and b/docs-en/14-reference/07-tdinsight/assets/TDinsight-6-dnode-usage.png differ diff --git a/docs-en/14-reference/07-tdinsight/assets/TDinsight-7-login-history.png b/docs-en/14-reference/07-tdinsight/assets/TDinsight-7-login-history.png new file mode 100644 index 0000000000000000000000000000000000000000..eb3848657f13900c856ac595c20766465157e9c4 Binary files /dev/null and b/docs-en/14-reference/07-tdinsight/assets/TDinsight-7-login-history.png differ diff --git a/docs-en/14-reference/07-tdinsight/assets/TDinsight-8-taosadapter.png b/docs-en/14-reference/07-tdinsight/assets/TDinsight-8-taosadapter.png new file mode 100644 index 0000000000000000000000000000000000000000..d94b2e02ac9855bb3d2f77d8902e068839db364f Binary files /dev/null and b/docs-en/14-reference/07-tdinsight/assets/TDinsight-8-taosadapter.png differ diff --git a/docs-en/14-reference/07-tdinsight/assets/TDinsight-full.png b/docs-en/14-reference/07-tdinsight/assets/TDinsight-full.png new file mode 100644 index 0000000000000000000000000000000000000000..654df2934597ce600a1dc2dcd0cab7e29de7076d Binary files /dev/null and b/docs-en/14-reference/07-tdinsight/assets/TDinsight-full.png differ diff --git a/docs-en/14-reference/07-tdinsight/assets/alert-manager-status.png b/docs-en/14-reference/07-tdinsight/assets/alert-manager-status.png new file mode 100644 index 0000000000000000000000000000000000000000..e3afa22c0326d70567ec4529c83101c746daac87 Binary files /dev/null and b/docs-en/14-reference/07-tdinsight/assets/alert-manager-status.png differ diff --git a/docs-en/14-reference/07-tdinsight/assets/alert-notification-channel.png b/docs-en/14-reference/07-tdinsight/assets/alert-notification-channel.png new file mode 100644 index 0000000000000000000000000000000000000000..198bf37141c86a66cdd91b47a331bcdeb83daaf8 Binary files /dev/null and b/docs-en/14-reference/07-tdinsight/assets/alert-notification-channel.png differ diff --git a/docs-en/14-reference/07-tdinsight/assets/alert-query-demo.png b/docs-en/14-reference/07-tdinsight/assets/alert-query-demo.png new file mode 100644 index 0000000000000000000000000000000000000000..ace3aa3c2f8f14fabdac54bc25ae2d9449445b69 Binary files /dev/null and b/docs-en/14-reference/07-tdinsight/assets/alert-query-demo.png differ diff --git a/docs-en/14-reference/07-tdinsight/assets/alert-rule-condition-notifications.png b/docs-en/14-reference/07-tdinsight/assets/alert-rule-condition-notifications.png new file mode 100644 index 0000000000000000000000000000000000000000..7082e49f6beb8690c36f98a3f4ff2befdb8fd014 Binary files /dev/null and b/docs-en/14-reference/07-tdinsight/assets/alert-rule-condition-notifications.png differ diff --git a/docs-en/14-reference/07-tdinsight/assets/alert-rule-test.png b/docs-en/14-reference/07-tdinsight/assets/alert-rule-test.png new file mode 100644 index 0000000000000000000000000000000000000000..ffd4911b53854c42dbf0ff11838cb604fa694138 Binary files /dev/null and b/docs-en/14-reference/07-tdinsight/assets/alert-rule-test.png differ diff --git a/docs-en/14-reference/07-tdinsight/assets/howto-add-datasource-button.png b/docs-en/14-reference/07-tdinsight/assets/howto-add-datasource-button.png new file mode 100644 index 0000000000000000000000000000000000000000..802c7366f921301bd7fbc62458e56b2d1eaf195c Binary files /dev/null and b/docs-en/14-reference/07-tdinsight/assets/howto-add-datasource-button.png differ diff --git a/docs-en/14-reference/07-tdinsight/assets/howto-add-datasource-tdengine.png b/docs-en/14-reference/07-tdinsight/assets/howto-add-datasource-tdengine.png new file mode 100644 index 0000000000000000000000000000000000000000..019ec921b6f808671f4f864ddf3380159d4a0dcc Binary files /dev/null and b/docs-en/14-reference/07-tdinsight/assets/howto-add-datasource-tdengine.png differ diff --git a/docs-en/14-reference/07-tdinsight/assets/howto-add-datasource-test.png b/docs-en/14-reference/07-tdinsight/assets/howto-add-datasource-test.png new file mode 100644 index 0000000000000000000000000000000000000000..3963abb4ea8ae0e6f5557466f7a5b746c2d2ea3c Binary files /dev/null and b/docs-en/14-reference/07-tdinsight/assets/howto-add-datasource-test.png differ diff --git a/docs-en/14-reference/07-tdinsight/assets/howto-add-datasource.png b/docs-en/14-reference/07-tdinsight/assets/howto-add-datasource.png new file mode 100644 index 0000000000000000000000000000000000000000..837100464b35a5cafac474723aef603f91945ebc Binary files /dev/null and b/docs-en/14-reference/07-tdinsight/assets/howto-add-datasource.png differ diff --git a/docs-en/14-reference/07-tdinsight/assets/howto-dashboard-display.png b/docs-en/14-reference/07-tdinsight/assets/howto-dashboard-display.png new file mode 100644 index 0000000000000000000000000000000000000000..98223df25499effac343ff5723544a3c289f18fa Binary files /dev/null and b/docs-en/14-reference/07-tdinsight/assets/howto-dashboard-display.png differ diff --git a/docs-en/14-reference/07-tdinsight/assets/howto-dashboard-import-options.png b/docs-en/14-reference/07-tdinsight/assets/howto-dashboard-import-options.png new file mode 100644 index 0000000000000000000000000000000000000000..07aba348f02b4fb8ef68e79664920c119b842d4c Binary files /dev/null and b/docs-en/14-reference/07-tdinsight/assets/howto-dashboard-import-options.png differ diff --git a/docs-en/14-reference/07-tdinsight/assets/howto-import-dashboard.png b/docs-en/14-reference/07-tdinsight/assets/howto-import-dashboard.png new file mode 100644 index 0000000000000000000000000000000000000000..7e28939ead8bf3b6e2b4330e4f9b59c2e39b5c1c Binary files /dev/null and b/docs-en/14-reference/07-tdinsight/assets/howto-import-dashboard.png differ diff --git a/docs-en/14-reference/07-tdinsight/assets/import-dashboard-15167.png b/docs-en/14-reference/07-tdinsight/assets/import-dashboard-15167.png new file mode 100644 index 0000000000000000000000000000000000000000..981f640b14d18aa6f0682768d8405a232df500f6 Binary files /dev/null and b/docs-en/14-reference/07-tdinsight/assets/import-dashboard-15167.png differ diff --git a/docs-en/14-reference/07-tdinsight/assets/import-dashboard-for-tdengine.png b/docs-en/14-reference/07-tdinsight/assets/import-dashboard-for-tdengine.png new file mode 100644 index 0000000000000000000000000000000000000000..94ef4fa5fe63e535118a81707b413c028ce01f70 Binary files /dev/null and b/docs-en/14-reference/07-tdinsight/assets/import-dashboard-for-tdengine.png differ diff --git a/docs-en/14-reference/07-tdinsight/assets/import-via-grafana-dot-com.png b/docs-en/14-reference/07-tdinsight/assets/import-via-grafana-dot-com.png new file mode 100644 index 0000000000000000000000000000000000000000..670cacc377c2801fa9437c3c132c5c7fbc361b0f Binary files /dev/null and b/docs-en/14-reference/07-tdinsight/assets/import-via-grafana-dot-com.png differ diff --git a/docs-en/14-reference/07-tdinsight/assets/import_dashboard.png b/docs-en/14-reference/07-tdinsight/assets/import_dashboard.png new file mode 100644 index 0000000000000000000000000000000000000000..d74cd36c96ee0fd24ddc6feae2da07824816f745 Binary files /dev/null and b/docs-en/14-reference/07-tdinsight/assets/import_dashboard.png differ diff --git a/docs-en/14-reference/07-tdinsight/assets/tdengine-grafana-7.x.json b/docs-en/14-reference/07-tdinsight/assets/tdengine-grafana-7.x.json new file mode 100644 index 0000000000000000000000000000000000000000..b4254c428b28a0084e54b5e3c509dd2e0ec651b9 --- /dev/null +++ b/docs-en/14-reference/07-tdinsight/assets/tdengine-grafana-7.x.json @@ -0,0 +1,3358 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "TDengine nodes metrics.", + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": 3, + "iteration": 1634275785625, + "links": [], + "panels": [ + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 57, + "panels": [], + "title": "Cluster Status", + "type": "row" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 24, + "x": 0, + "y": 1 + }, + "id": 32, + "options": { + "content": "

TDengine Cluster Dashboard

>\n", + "mode": "markdown" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "mnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show mnodes", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "-- OVERVIEW --", + "transformations": [ + { + "id": "calculateField", + "options": { + "binary": { + "left": "Time", + "operator": "+", + "reducer": "sum", + "right": "" + }, + "mode": "binary", + "reduce": { + "reducer": "sum" + } + } + } + ], + "type": "text" + }, + { + "datasource": "${ds}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 8, + "x": 0, + "y": 4 + }, + "id": 28, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": true + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show mnodes", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Master MNode", + "transformations": [ + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "regex", + "options": { + "value": "master" + } + }, + "fieldName": "role" + } + ], + "match": "all", + "type": "include" + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "dnodes" + ] + } + } + } + ], + "type": "stat" + }, + { + "datasource": "${ds}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 7, + "x": 8, + "y": 4 + }, + "id": 70, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/^Time$/", + "values": true + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show mnodes", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Master MNode Create Time", + "transformations": [ + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "regex", + "options": { + "value": "master" + } + }, + "fieldName": "role" + } + ], + "match": "all", + "type": "include" + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "Time" + ] + } + } + }, + { + "id": "calculateField", + "options": { + "mode": "reduceRow", + "reduce": { + "reducer": "min" + } + } + } + ], + "type": "stat" + }, + { + "datasource": "${ds}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": null, + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 9, + "x": 15, + "y": 4 + }, + "id": 29, + "options": { + "showHeader": true + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show variables", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Variables", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "value", + "name" + ] + } + } + }, + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "regex", + "options": { + "value": ".*" + } + }, + "fieldName": "name" + } + ], + "match": "all", + "type": "include" + } + } + ], + "type": "table" + }, + { + "datasource": "${ds}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 0, + "y": 7 + }, + "id": 33, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/.*/", + "values": true + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Table", + "queryType": "SQL", + "refId": "A", + "sql": "select server_version()", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Server Version", + "transformations": [], + "type": "stat" + }, + { + "datasource": "${ds}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 2, + "y": 7 + }, + "id": 27, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": true + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show mnodes", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Number of MNodes", + "transformations": [ + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "greater", + "options": { + "value": 0 + } + }, + "fieldName": "id" + } + ], + "match": "any", + "type": "include" + } + }, + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": [ + "count" + ] + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "id" + ] + } + } + } + ], + "type": "stat" + }, + { + "datasource": "${ds}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 5, + "y": 7 + }, + "id": 41, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show dnodes", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Total Dnodes", + "transformations": [ + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": [ + "count" + ] + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "id" + ] + } + } + } + ], + "type": "stat" + }, + { + "datasource": "${ds}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 7, + "y": 7 + }, + "id": 31, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show dnodes", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Offline Dnodes", + "transformations": [ + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "regex", + "options": { + "value": "ready" + } + }, + "fieldName": "status" + } + ], + "match": "all", + "type": "exclude" + } + }, + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": [ + "count" + ] + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "id" + ] + } + } + } + ], + "type": "stat" + }, + { + "datasource": "${ds}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 9, + "y": 7 + }, + "id": 65, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": true + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show databases;", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Number of Databases", + "transformations": [ + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": [ + "count" + ] + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "name" + ] + } + } + } + ], + "type": "stat" + }, + { + "datasource": "${ds}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 12, + "y": 7 + }, + "id": 69, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": true + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show databases;", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Total Number of Vgroups", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "vgroups" + ] + } + } + }, + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": [ + "sum" + ] + } + } + ], + "type": "stat" + }, + { + "datasource": "${ds}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "displayMode": "auto", + "filterable": true + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "role" + }, + "properties": [ + { + "id": "mappings", + "value": [ + { + "from": "", + "id": 1, + "text": "", + "to": "", + "type": 2, + "value": "" + } + ] + } + ] + } + ] + }, + "gridPos": { + "h": 3, + "w": 9, + "x": 0, + "y": 10 + }, + "id": 67, + "options": { + "showHeader": true + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "formatType": "Table", + "queryType": "SQL", + "refId": "A", + "sql": "show dnodes", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Number of DNodes for each Role", + "transformations": [ + { + "id": "groupBy", + "options": { + "fields": { + "end_point": { + "aggregations": [ + "count" + ], + "operation": "aggregate" + }, + "role": { + "aggregations": [], + "operation": "groupby" + } + } + } + }, + { + "id": "filterFieldsByName", + "options": {} + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": {}, + "renameByName": { + "end_point (count)": "Number of DNodes", + "role": "Dnode Role" + } + } + } + ], + "type": "table" + }, + { + "datasource": "${ds}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 9, + "y": 10 + }, + "id": 55, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": true + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show connections", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Number of Connections", + "transformations": [ + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": [ + "count" + ] + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "connId" + ] + } + } + } + ], + "type": "stat" + }, + { + "datasource": "${ds}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 12, + "y": 10 + }, + "id": 68, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": true + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.10", + "repeatDirection": "h", + "targets": [ + { + "alias": "dnodes", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "show databases;", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Total Number of Tables", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "ntables" + ] + } + } + }, + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": [ + "sum" + ] + } + } + ], + "type": "stat" + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 13 + }, + "id": 24, + "panels": [], + "title": "Dnodes Status", + "type": "row" + }, + { + "datasource": "${ds}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "displayMode": "auto", + "filterable": true + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "status" + }, + "properties": [ + { + "id": "custom.width", + "value": null + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "vnodes" + }, + "properties": [ + { + "id": "custom.width", + "value": null + } + ] + } + ] + }, + "gridPos": { + "h": 5, + "w": 16, + "x": 0, + "y": 14 + }, + "id": 36, + "options": { + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "formatType": "Table", + "queryType": "SQL", + "refId": "A", + "sql": "show dnodes", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "DNodes Status", + "type": "table" + }, + { + "datasource": "${ds}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 16, + "y": 14 + }, + "id": 40, + "options": { + "displayLabels": [], + "legend": { + "displayMode": "table", + "placement": "right", + "values": [ + "value" + ] + }, + "pieType": "pie", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/.*/", + "values": false + }, + "text": { + "titleSize": 6 + } + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "formatType": "Table", + "queryType": "SQL", + "refId": "A", + "sql": "show dnodes", + "target": "select metric", + "type": "timeserie" + } + ], + "title": "Offline Reasons", + "transformations": [ + { + "id": "filterByValue", + "options": { + "filters": [ + { + "config": { + "id": "regex", + "options": { + "value": "ready" + } + }, + "fieldName": "status" + } + ], + "match": "all", + "type": "exclude" + } + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "offline reason", + "end_point" + ] + } + } + }, + { + "id": "groupBy", + "options": { + "fields": { + "Time": { + "aggregations": [ + "count" + ], + "operation": "aggregate" + }, + "end_point": { + "aggregations": [ + "count" + ], + "operation": "aggregate" + }, + "offline reason": { + "aggregations": [], + "operation": "groupby" + } + } + } + } + ], + "type": "piechart" + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 19 + }, + "id": 22, + "panels": [], + "title": "Mnodes Status", + "type": "row" + }, + { + "datasource": "${ds}", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 24, + "x": 0, + "y": 20 + }, + "id": 38, + "options": { + "showHeader": true + }, + "pluginVersion": "7.5.10", + "targets": [ + { + "formatType": "Table", + "queryType": "SQL", + "refId": "A", + "sql": "show mnodes;", + "target": "select metric", + "type": "timeserie" + } + ], + "title": "Mnodes Status", + "type": "table" + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 25 + }, + "id": 20, + "panels": [], + "repeat": "fqdn", + "scopedVars": { + "fqdn": { + "selected": true, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + } + }, + "title": "节点资源占用 [ $fqdn ]", + "type": "row" + }, + { + "datasource": "${ds}", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "decmbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 0, + "y": 26 + }, + "id": 66, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "/^taosd$/", + "values": false + }, + "showThresholdLabels": true, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "scopedVars": { + "fqdn": { + "selected": true, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + } + }, + "targets": [ + { + "alias": "memory", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select last(mem_taosd) as taosd, last(mem_total) as total from log.dn where fqdn = '$fqdn' and ts >= now -5m and ts < now", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Current Memory Usage of taosd", + "type": "gauge" + }, + { + "datasource": "${ds}", + "description": "taosd max memery last 10 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 0.5 + }, + { + "color": "red", + "value": 0.8 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "last(cpu_taosd)" + }, + "properties": [ + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + } + ] + } + ] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 5, + "y": 26 + }, + "id": 45, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "/^last\\(cpu_taosd\\)$/", + "values": false + }, + "showThresholdLabels": true, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "scopedVars": { + "fqdn": { + "selected": true, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + } + }, + "targets": [ + { + "alias": "mem_taosd", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select last(cpu_taosd) from log.dn where fqdn = '$fqdn'", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Current CPU Usage of taosd", + "type": "gauge" + }, + { + "datasource": "${ds}", + "description": "avg band speed last one minute", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 8192, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 4916 + }, + { + "color": "red", + "value": 6554 + } + ] + }, + "unit": "Kbits" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 4, + "x": 10, + "y": 26 + }, + "id": 14, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "scopedVars": { + "fqdn": { + "selected": true, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + } + }, + "targets": [ + { + "alias": "band_speed", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select avg(band_speed) from log.dn where fqdn='$fqdn' and ts >= now-5m and ts < now interval(1m)", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "band speed", + "type": "gauge" + }, + { + "datasource": "${ds}", + "description": "io read/write rate", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 8192, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 4916 + }, + { + "color": "red", + "value": 6554 + } + ] + }, + "unit": "Kbits" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 14, + "y": 26 + }, + "id": 48, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "scopedVars": { + "fqdn": { + "selected": true, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + } + }, + "targets": [ + { + "alias": "", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select last(io_read) as io_read, last(io_write) as io_write from log.dn where fqdn='$fqdn' and ts >= now-1h and ts < now interval(1m)", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "IO Rate", + "type": "gauge" + }, + { + "datasource": "${ds}", + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 1, + "min": 0, + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 75 + }, + { + "color": "red", + "value": 80 + }, + { + "color": "dark-red", + "value": 95 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 19, + "y": 26 + }, + "id": 51, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "/^disk_used_percent$/", + "values": false + }, + "showThresholdLabels": true, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "scopedVars": { + "fqdn": { + "selected": true, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + } + }, + "targets": [ + { + "alias": "disk_used", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select last(disk_used) as used from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(1m)", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + }, + { + "alias": "disk_total", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "B", + "sql": "select last(disk_total) as total from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(1m)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "disk_used_percent", + "expression": "A/B", + "formatType": "Time series", + "hide": false, + "queryType": "Arithmetic", + "refId": "C", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Disk Used", + "transformations": [ + { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": [ + "lastNotNull" + ] + } + } + ], + "type": "gauge" + }, + { + "datasource": "${ds}", + "description": "taosd max memery last 10 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "decmbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 0, + "y": 32 + }, + "id": 12, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "/^taosd$/", + "values": false + }, + "showThresholdLabels": true, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "scopedVars": { + "fqdn": { + "selected": true, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + } + }, + "targets": [ + { + "alias": "memory", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select max(mem_taosd) as taosd, max(mem_total) as total from log.dn where fqdn = '$fqdn' and ts >= now -5m and ts < now", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Max Memory Usage of taosd in Last 5 minute", + "type": "gauge" + }, + { + "datasource": "${ds}", + "description": "taosd max memery last 10 minutes", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "max": 1, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 0.5 + }, + { + "color": "red", + "value": 0.8 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 5, + "y": 32 + }, + "id": 43, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": true, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "scopedVars": { + "fqdn": { + "selected": true, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + } + }, + "targets": [ + { + "alias": "mem_taosd", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select max(cpu_taosd) from log.dn where fqdn = '$fqdn' and ts >= now -5m and ts < now", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Max CPU Usage of taosd in Last 5 minute", + "type": "gauge" + }, + { + "datasource": "${ds}", + "description": "avg band speed last one minute", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 8192, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 4916 + }, + { + "color": "red", + "value": 6554 + } + ] + }, + "unit": "Kbits" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 4, + "x": 10, + "y": 32 + }, + "id": 50, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "scopedVars": { + "fqdn": { + "selected": true, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + } + }, + "targets": [ + { + "alias": "band_speed", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select max(band_speed) from log.dn where fqdn = '$fqdn' and ts >= now-1h", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Max band speed in last hour", + "type": "gauge" + }, + { + "datasource": "${ds}", + "description": "io read/write rate", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 8192, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 4916 + }, + { + "color": "red", + "value": 6554 + } + ] + }, + "unit": "Kbits" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 14, + "y": 32 + }, + "id": 49, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "scopedVars": { + "fqdn": { + "selected": true, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + } + }, + "targets": [ + { + "alias": "", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select max(io_read) as io_read, max(io_write) as io_write from log.dn where fqdn = '$fqdn'", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Max IO Rate in last hour", + "type": "gauge" + }, + { + "datasource": "${ds}", + "description": "io read/write rate", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 8192, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 4916 + }, + { + "color": "red", + "value": 6554 + } + ] + }, + "unit": "cpm" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 19, + "y": 32 + }, + "id": 52, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "7.5.10", + "scopedVars": { + "fqdn": { + "selected": true, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + } + }, + "targets": [ + { + "alias": "req-http", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select sum(req_http) as req_http from log.dn where fqdn = '$fqdn' and ts >= now - 1h interval(1m)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "req-inserts", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "B", + "sql": "select sum(req_insert) as req_insert from log.dn where fqdn = '$fqdn' and ts >= now - 1h interval(1m)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "req-selects", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "C", + "sql": "select sum(req_select) as req_select from log.dn where fqdn = '$fqdn' and ts >= now - 1h interval(1m)", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Requests in last Minute", + "type": "gauge" + }, + { + "aliasColors": {}, + "bars": false, + "cacheTimeout": null, + "dashLength": 10, + "dashes": false, + "datasource": "${ds}", + "description": "monitor system cpu", + "fieldConfig": { + "defaults": { + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 11, + "w": 12, + "x": 0, + "y": 38 + }, + "hiddenSeries": false, + "hideTimeOverride": true, + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.10", + "pointradius": 2, + "points": false, + "renderer": "flot", + "scopedVars": { + "fqdn": { + "selected": true, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + } + }, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "cpu_system", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "A", + "sql": "select avg(cpu_system) from log.dn where fqdn='$fqdn' and ts >= now-1h and ts < now interval(30s)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "cpu_taosd", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "B", + "sql": "select avg(cpu_taosd) from log.dn where fqdn='$fqdn' and ts >= now-1h and ts < now interval(30s)", + "target": "select metric", + "type": "timeserie" + } + ], + "thresholds": [], + "timeFrom": "1h", + "timeRegions": [], + "timeShift": "30s", + "title": "CPU 资源占用情况", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:58", + "decimals": null, + "format": "percent", + "label": "使用占比", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:59", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "cacheTimeout": null, + "dashLength": 10, + "dashes": false, + "datasource": "${ds}", + "description": "monitor system cpu", + "fieldConfig": { + "defaults": { + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 11, + "w": 12, + "x": 12, + "y": 38 + }, + "hiddenSeries": false, + "hideTimeOverride": true, + "id": 42, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.10", + "pointradius": 2, + "points": false, + "renderer": "flot", + "scopedVars": { + "fqdn": { + "selected": true, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + } + }, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "system", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "A", + "sql": "select avg(mem_system) from log.dn where fqdn = '$fqdn' and ts >= now-1h and ts < now interval(30s)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "taosd", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "B", + "sql": "select avg(mem_taosd) from log.dn where fqdn = '$fqdn' and ts >= now-1h and ts < now interval(30s)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "total", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "C", + "sql": "select avg(mem_total) from log.dn where fqdn = '$fqdn' and ts >= now-1h and ts < now interval(30s)", + "target": "select metric", + "type": "timeserie" + } + ], + "thresholds": [], + "timeFrom": "1h", + "timeRegions": [], + "timeShift": "30s", + "title": "内存资源占用情况", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:58", + "decimals": null, + "format": "decmbytes", + "label": "使用占比", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:59", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${ds}", + "fieldConfig": { + "defaults": { + "unit": "percent" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 49 + }, + "hiddenSeries": false, + "id": 54, + "legend": { + "alignAsTable": false, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.10", + "pointradius": 2, + "points": false, + "renderer": "flot", + "scopedVars": { + "fqdn": { + "selected": true, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + } + }, + "seriesOverrides": [ + { + "$$hashKey": "object:249", + "alias": "disk_used", + "hiddenSeries": true + }, + { + "$$hashKey": "object:256", + "alias": "disk_total", + "hiddenSeries": true + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "disk_used", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "A", + "sql": "select avg(disk_used) from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(30s)", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + }, + { + "alias": "disk_total", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "B", + "sql": "select avg(disk_total) from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(30s)", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + }, + { + "alias": "percent", + "expression": "A/B * 100", + "formatType": "Time series", + "hide": false, + "queryType": "Arithmetic", + "refId": "C", + "target": "select metric", + "type": "timeserie" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Disk Used Percent", + "tooltip": { + "shared": false, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:456", + "format": "percent", + "label": null, + "logBase": 1, + "max": "100", + "min": "0", + "show": true + }, + { + "$$hashKey": "object:457", + "format": "percentunit", + "label": "Disk Used", + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${ds}", + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 49 + }, + "hiddenSeries": false, + "id": 64, + "legend": { + "alignAsTable": false, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.10", + "pointradius": 2, + "points": false, + "renderer": "flot", + "scopedVars": { + "fqdn": { + "selected": true, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + } + }, + "seriesOverrides": [ + { + "$$hashKey": "object:834", + "alias": "percent", + "yaxis": 2 + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "disk_used", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "A", + "sql": "select avg(disk_used) from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(30s)", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + }, + { + "alias": "disk_total", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "B", + "sql": "select avg(disk_total) from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(30s)", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + }, + { + "alias": "percent", + "expression": "A/B", + "formatType": "Time series", + "hide": false, + "queryType": "Arithmetic", + "refId": "C", + "target": "select metric", + "type": "timeserie" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Disk Used", + "tooltip": { + "shared": false, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:456", + "format": "decgbytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:457", + "format": "percentunit", + "label": "Disk Used", + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "cacheTimeout": null, + "dashLength": 10, + "dashes": false, + "datasource": "${ds}", + "description": "total select request per minute last hour", + "fieldConfig": { + "defaults": { + "unit": "cpm" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 58 + }, + "hiddenSeries": false, + "id": 8, + "interval": null, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "maxDataPoints": 100, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.10", + "pointradius": 2, + "points": false, + "renderer": "flot", + "scopedVars": { + "fqdn": { + "selected": true, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + } + }, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "req_select", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select sum(req_select) from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(1m)", + "target": "select metric", + "timeshift": { + "period": null + }, + "type": "timeserie" + }, + { + "alias": "req_insert", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "B", + "sql": "select sum(req_insert) from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(1m)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "req_http", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "C", + "sql": "select sum(req_http) from log.dn where fqdn = '$fqdn' and ts >= $from and ts < $to interval(1m)", + "target": "select metric", + "type": "timeserie" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Requets Count per Minutes $fqdn", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:127", + "format": "cpm", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "$$hashKey": "object:128", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "cacheTimeout": null, + "dashLength": 10, + "dashes": false, + "datasource": "${ds}", + "description": "io", + "fieldConfig": { + "defaults": { + "links": [], + "unit": "Kbits" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 58 + }, + "hiddenSeries": false, + "hideTimeOverride": true, + "id": 47, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.10", + "pointradius": 2, + "points": false, + "renderer": "flot", + "scopedVars": { + "fqdn": { + "selected": true, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + } + }, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "io-read", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "A", + "sql": "select avg(io_read) from log.dn where fqdn = '$fqdn' and ts >= now-1h and ts < now interval(1m)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "io-write", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "B", + "sql": "select avg(io_write) from log.dn where fqdn = '$fqdn' and ts >= now-1h and ts < now interval(1m)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "io-read-last-hour", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "C", + "sql": "select avg(io_read) from log.dn where fqdn = '$fqdn' and ts >= now-2h and ts < now - 1h interval(1m)", + "target": "select metric", + "timeshift": { + "period": 1, + "unit": "hours" + }, + "type": "timeserie" + }, + { + "alias": "io-write-last-hour", + "formatType": "Time series", + "hide": false, + "queryType": "SQL", + "refId": "D", + "sql": "select avg(io_write) from log.dn where fqdn = '$fqdn' and ts >= now-1h and ts < now interval(1m)", + "target": "select metric", + "timeshift": { + "period": 1, + "unit": "hours" + }, + "type": "timeserie" + } + ], + "thresholds": [], + "timeFrom": "1h", + "timeRegions": [], + "timeShift": "30s", + "title": "IO", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:58", + "decimals": null, + "format": "Kbits", + "label": "使用占比", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:59", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 67 + }, + "id": 63, + "panels": [], + "title": "Login History", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${ds}", + "fieldConfig": { + "defaults": { + "displayName": "Logins Per Minute", + "unit": "cpm" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 68 + }, + "hiddenSeries": false, + "id": 61, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.10", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "$$hashKey": "object:756", + "alias": "logins", + "nullPointMode": "null as zero" + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "logins", + "formatType": "Time series", + "queryType": "SQL", + "refId": "A", + "sql": "select count(*) from log.log where ts >= $from and ts < $to interval (1m)", + "target": "select metric", + "type": "timeserie" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Login Counts per Minute", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:585", + "format": "cpm", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:586", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "1m", + "schemaVersion": 27, + "style": "dark", + "tags": [ + "TDengine" + ], + "templating": { + "list": [ + { + "current": { + "selected": true, + "text": "TDengine", + "value": "TDengine" + }, + "description": "TDengine Data Source Selector", + "error": null, + "hide": 0, + "includeAll": false, + "label": "Datasource", + "multi": false, + "name": "ds", + "options": [], + "query": "tdengine-datasource", + "queryValue": "", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "allValue": null, + "current": { + "selected": false, + "text": "huolinhe-TM1701:6030", + "value": "huolinhe-TM1701:6030" + }, + "datasource": "${ds}", + "definition": "select fqdn from log.dn", + "description": "TDengine Nodes FQDN (Hostname)", + "error": null, + "hide": 0, + "includeAll": false, + "label": null, + "multi": false, + "name": "fqdn", + "options": [], + "query": "select fqdn from log.dn", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "TDengine", + "uid": "tdengine", + "version": 8 +} \ No newline at end of file diff --git a/docs-en/14-reference/07-tdinsight/assets/tdengine-grafana.json b/docs-en/14-reference/07-tdinsight/assets/tdengine-grafana.json new file mode 100644 index 0000000000000000000000000000000000000000..7696cfce3fcca3dd5f9278c91d3df2c1b32b9c97 --- /dev/null +++ b/docs-en/14-reference/07-tdinsight/assets/tdengine-grafana.json @@ -0,0 +1,627 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": true, + "colors": ["#299c46", "rgba(237, 129, 40, 0.89)", "#d44a3a"], + "datasource": "TDengine", + "description": "total select request per minute last hour", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 6, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 8, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "options": {}, + "postfix": "次数/min", + "postfixFontSize": "20%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true, + "ymax": null, + "ymin": null + }, + "tableColumn": "", + "targets": [ + { + "alias": "req_select", + "refId": "A", + "sql": "select sum(req_select) from log.dn where ts >= now-1h and ts < now interval(1m)", + "target": "select metric", + "type": "timeserie" + } + ], + "thresholds": "120,240", + "timeFrom": null, + "timeShift": null, + "title": "req select", + "type": "singlestat", + "valueFontSize": "150%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "total" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": true, + "colors": ["#299c46", "rgba(237, 129, 40, 0.89)", "#d44a3a"], + "datasource": "TDengine", + "description": "total insert request per minute for last hour", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 6, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 6, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "options": {}, + "postfix": "次数/min", + "postfixFontSize": "20%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": true, + "ymax": null, + "ymin": null + }, + "tableColumn": "", + "targets": [ + { + "alias": "req_insert", + "refId": "A", + "sql": "select sum(req_insert) from log.dn where ts >= now-1h and ts < now interval(1m)", + "target": "select metric", + "type": "timeserie" + } + ], + "thresholds": "110,240", + "timeFrom": null, + "timeShift": null, + "title": "req insert", + "type": "singlestat", + "valueFontSize": "150%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "total" + }, + { + "datasource": "TDengine", + "description": "taosd max memery last 10 minutes", + "gridPos": { + "h": 6, + "w": 8, + "x": 0, + "y": 6 + }, + "id": 12, + "options": { + "fieldOptions": { + "calcs": ["mean"], + "defaults": { + "mappings": [], + "max": 4096, + "min": 0, + "thresholds": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + }, + { + "color": "#EAB839", + "value": 2048 + } + ], + "unit": "decmbytes" + }, + "thresholds": [ + { + "index": 0, + "color": "green", + "value": null + }, + { + "index": 1, + "color": "red", + "value": 80 + }, + { + "index": 2, + "color": "#EAB839", + "value": 2048 + } + ], + "override": {}, + "values": false + }, + "orientation": "auto", + "showThresholdLabels": true, + "showThresholdMarkers": true + }, + "pluginVersion": "6.4.3", + "targets": [ + { + "alias": "mem_taosd", + "refId": "A", + "sql": "select max(mem_taosd) from log.dn where ts >= now -10m and ts < now", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "taosd memery", + "type": "gauge" + }, + { + "datasource": "TDengine", + "description": "max System Memory last 1 hour", + "gridPos": { + "h": 6, + "w": 8, + "x": 8, + "y": 6 + }, + "id": 10, + "options": { + "fieldOptions": { + "calcs": ["last"], + "mappings": [], + "defaults": { + "mappings": [], + "max": 4, + "min": 0, + "thresholds": [ + { + "color": "green", + "value": null + }, + { + "color": "semi-dark-orange", + "value": 60 + }, + { + "color": "dark-red", + "value": 80 + } + ], + "title": "", + "unit": "decmbytes" + }, + "thresholds": [ + { + "index": 0, + "color": "green", + "value": null + }, + { + "index": 1, + "color": "semi-dark-orange", + "value": 60 + }, + { + "index": 2, + "color": "dark-red", + "value": 80 + } + ], + "override": {}, + "values": false + }, + "orientation": "auto", + "showThresholdLabels": true, + "showThresholdMarkers": true + }, + "pluginVersion": "6.4.3", + "targets": [ + { + "alias": "mem_system", + "refId": "A", + "sql": "select max(mem_system) from log.dn where ts >= now -10h and ts < now", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "system memory", + "type": "gauge" + }, + { + "datasource": "TDengine", + "description": "avg band speed last one minute", + "gridPos": { + "h": 6, + "w": 8, + "x": 16, + "y": 6 + }, + "id": 14, + "options": { + "fieldOptions": { + "calcs": ["last"], + "defaults": { + "mappings": [], + "max": 8192, + "min": 0, + "thresholds": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 4916 + }, + { + "color": "red", + "value": 6554 + } + ], + "unit": "Kbits" + }, + "mappings": [], + "thresholds": [ + { + "index": 0, + "color": "green", + "value": null + }, + { + "index": 1, + "color": "#EAB839", + "value": 4916 + }, + { + "index": 2, + "color": "red", + "value": 6554 + } + ], + "override": {}, + "values": false + }, + "orientation": "auto", + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "6.4.3", + "targets": [ + { + "alias": "band_speed", + "refId": "A", + "sql": "select avg(band_speed) from log.dn where ts >= now-1h and ts < now interval(1m)", + "target": "select metric", + "type": "timeserie" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "band speed", + "type": "gauge" + }, + { + "aliasColors": {}, + "bars": false, + "cacheTimeout": null, + "dashLength": 10, + "dashes": false, + "datasource": "TDengine", + "description": "monitor system cpu", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 11, + "w": 12, + "x": 0, + "y": 12 + }, + "hideTimeOverride": true, + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pluginVersion": "6.4.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "cpu_system11", + "hide": false, + "refId": "A", + "sql": "select avg(cpu_system) from log.dn where ts >= now-1h and ts < now interval(1s)", + "target": "select metric", + "type": "timeserie" + }, + { + "alias": "cpu_taosd", + "hide": false, + "refId": "B", + "sql": "select avg(cpu_taosd) from log.dn where ts >= now-1h and ts < now interval(1s)", + "target": "select metric", + "type": "timeserie" + } + ], + "thresholds": [], + "timeFrom": "1h", + "timeRegions": [], + "timeShift": "30s", + "title": "cpu_system", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": null, + "format": "percent", + "label": "使用占比", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "TDengine", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 11, + "w": 12, + "x": 12, + "y": 12 + }, + "id": 18, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "", + "refId": "A", + "sql": "select avg(disk_used) disk_used from log.dn where ts >= $from and ts < $to interval(1s) group by fqdn", + "target": "select metric", + "type": "timeserie" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "avg_disk_used", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decgbytes", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "5s", + "schemaVersion": 20, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "TDengine", + "uid": "FE-vpe0Wk", + "version": 1 +} diff --git a/docs-en/14-reference/07-tdinsight/assets/tdengine_dashboard.png b/docs-en/14-reference/07-tdinsight/assets/tdengine_dashboard.png new file mode 100644 index 0000000000000000000000000000000000000000..0101e7430cb2ef673818de8bd3af53d0d082ad3f Binary files /dev/null and b/docs-en/14-reference/07-tdinsight/assets/tdengine_dashboard.png differ diff --git a/docs-en/14-reference/07-tdinsight/index.md b/docs-en/14-reference/07-tdinsight/index.md new file mode 100644 index 0000000000000000000000000000000000000000..4850cecb334ff24cc9fcf3b9a6e394827730111c --- /dev/null +++ b/docs-en/14-reference/07-tdinsight/index.md @@ -0,0 +1,428 @@ +--- +title: TDinsight - Grafana-based Zero-Dependency Monitoring Solution for TDengine +sidebar_label: TDinsight +--- + +TDinsight is a solution for monitoring TDengine using the builtin native monitoring database and [Grafana]. + +After TDengine starts, it will automatically create a monitoring database `log`. TDengine will automatically write many metrics in specific intervals into the `log` database. The metrics may include the server's CPU, memory, hard disk space, network bandwidth, number of requests, disk read/write speed, slow queries, other information like important system operations (user login, database creation, database deletion, etc.), and error alarms. With [Grafana] and [TDengine Data Source Plugin](https://github.com/taosdata/grafanaplugin/releases), TDinsight can visualize cluster status, node information, insertion and query requests, resource usage, etc., and also vnode, dnode, and mnode status, and exception alerts. Developers monitoring TDengine cluster operation status in real-time can be very convinient. This article will guide users to install the Grafana server, automatically install the TDengine data source plug-in, and deploy the TDinsight visualization panel through `TDinsight.sh` installation script. + +## System Requirements + +To deploy TDinsight, a single-node TDengine server or a multi-nodes TDengine cluster and a [Grafana] server are required. This dashboard requires TDengine 2.3.3.0 and above, with the `log` database enabled (`monitor = 1`). + +## Installing Grafana + +We recommend using the latest [Grafana] version 7 or 8 here. You can install Grafana on any [supported operating system](https://grafana.com/docs/grafana/latest/installation/requirements/#supported-operating-systems) by following the [official Grafana documentation Instructions](https://grafana.com/docs/grafana/latest/installation/) to install [Grafana]. + +### Installing Grafana on Debian or Ubuntu + +For Debian or Ubuntu operating systems, we recommend the Grafana image repository and Use the following command to install from scratch. + +```bash +sudo apt-get install -y apt-transport-https +sudo apt-get install -y software-properties-common wget +wget -q -O - https://packages.grafana.com/gpg.key |\ + sudo apt-key add - +echo "deb https://packages.grafana.com/oss/deb stable main" |\ + sudo tee -a /etc/apt/sources.list.d/grafana.list +sudo apt-get update +sudo apt-get install grafana +``` + +### Install Grafana on CentOS / RHEL + +You can install it from its official YUM repository. + +```bash +sudo tee /etc/yum.repos.d/grafana.repo << EOF +[grafana] +name=grafana +baseurl=https://packages.grafana.com/oss/rpm +repo_gpgcheck=1 +enabled=1 +gpgcheck=1 +gpgkey=https://packages.grafana.com/gpg.key +sslverify=1 +sslcacert=/etc/pki/tls/certs/ca-bundle.crt +EOF +sudo yum install grafana +``` + +Or install it with RPM package. + +```bash +wget https://dl.grafana.com/oss/release/grafana-7.5.11-1.x86_64.rpm +sudo yum install grafana-7.5.11-1.x86_64.rpm +# or +sudo yum install \ + https://dl.grafana.com/oss/release/grafana-7.5.11-1.x86_64.rpm +``` + +## Automated deployment of TDinsight + +We provide an installation script [`TDinsight.sh`](https://github.com/taosdata/grafanaplugin/releases/latest/download/TDinsight.sh) script to allow users to configure the installation automatically and quickly. + +You can download the script via `wget` or other tools: + +```bash +wget https://github.com/taosdata/grafanaplugin/releases/latest/download/TDinsight.sh +chmod +x TDinsight.sh +./TDinsight.sh +``` + +This script will automatically download the latest [Grafana TDengine data source plugin](https://github.com/taosdata/grafanaplugin/releases/latest) and [TDinsight dashboard](https://grafana.com/grafana/dashboards/15167) with configurable parameters from the command-line options to the [Grafana Provisioning](https://grafana.com/docs/grafana/latest/administration/provisioning/) configuration file to automate deployment and updates, etc. With the alert setting options provided by this script, you can also get built-in support for AliCloud SMS alert notifications. + +Assume you use TDengine and Grafana's default services on the same host. Run `. /TDinsight.sh` and open the Grafana browser window to see the TDinsight dashboard. + +The following is a description of TDinsight.sh usage. + +```text +Usage: + ./TDinsight.sh + ./TDinsight.sh -h|--help + ./TDinsight.sh -n -a -u -p + +Install and configure TDinsight dashboard in Grafana on Ubuntu 18.04/20.04 system. + +-h, -help, --help Display help + +-V, -verbose, --verbose Run script in verbose mode. Will print out each step of execution. + +-v, --plugin-version TDengine datasource plugin version, [default: latest] + +-P, --grafana-provisioning-dir Grafana provisioning directory, [default: /etc/grafana/provisioning/] +-G, --grafana-plugins-dir Grafana plugins directory, [default: /var/lib/grafana/plugins] +-O, --grafana-org-id Grafana organization id. [default: 1] + +-n, --tdengine-ds-name TDengine datasource name, no space. [default: TDengine] +-a, --tdengine-api TDengine REST API endpoint. [default: http://127.0.0.1:6041] +-u, --tdengine-user TDengine user name. [default: root] +-p, --tdengine-password TDengine password. [default: taosdata] + +-i, --tdinsight-uid Replace with a non-space ASCII code as the dashboard id. [default: tdinsight] +-t, --tdinsight-title Dashboard title. [default: TDinsight] +-e, --tdinsight-editable If the provisioning dashboard could be editable. [default: false] + +-E, --external-notifier Apply external notifier uid to TDinsight dashboard. + +Alibaba Cloud SMS as Notifier: +-s, --sms-enabled To enable tdengine-datasource plugin builtin Alibaba Cloud SMS webhook. +-N, --sms-notifier-name Provisioning notifier name.[default: TDinsight Builtin SMS] +-U, --sms-notifier-uid Provisioning notifier uid, use lowercase notifier name by default. +-D, --sms-notifier-is-default Set notifier as default. +-I, --sms-access-key-id Alibaba Cloud SMS access key id +-K, --sms-access-key-secret Alibaba Cloud SMS access key secret +-S, --sms-sign-name Sign name +-C, --sms-template-code Template code +-T, --sms-template-param Template param, a escaped JSON string like '{"alarm_level":"%s","time":"%s","name":"%s","content":"%s"}' +-B, --sms-phone-numbers Comma-separated numbers list, eg "189xxxxxxxx,132xxxxxxxx" +-L, --sms-listen-addr [default: 127.0.0.1:9100] +``` + +Most command-line options can take effect the same as environment variables. + +| Short Options | Long Options | Environment Variables | Description | +| ------ | -------------------------- | ---------------------------- | ------------------------------------------------------------------ --------- | +| -v | --plugin-version | TDENGINE_PLUGIN_VERSION | The TDengine data source plugin version, the latest version is used by default. | -P +| -P | --grafana-provisioning-dir | GF_PROVISIONING_DIR | The Grafana configuration directory, defaults to `/etc/grafana/provisioning/` | +| -G | --grafana-plugins-dir | GF_PLUGINS_DIR | The Grafana plugin directory, defaults to `/var/lib/grafana/plugins`. | -O +| -O | --grafana-org-id | GF_ORG_ID | The Grafana organization ID, default is 1. | +| -n | --tdengine-ds-name | TDENGINE_DS_NAME | The name of the TDengine data source, defaults to TDengine. | -a | --tdengine-ds-name | The name of the TDengine data source, defaults to TDengine. +| -a | --tdengine-api | TDENGINE_API | The TDengine REST API endpoint. Defaults to `http://127.0.0.1:6041`. | -u +| -u | --tdengine-user | TDENGINE_USER | TDengine username. [default: root] | +| -p | --tdengine-password | TDENGINE_PASSWORD | TDengine password. [default: tadosdata] | -i | --tdengine-password +| -i | --tdinsight-uid | TDINSIGHT_DASHBOARD_UID | TDinsight `uid` of the dashboard. [default: tdinsight] | +| -t | --tdinsight-title | TDINSIGHT_DASHBOARD_TITLE | TDinsight dashboard title. [Default: TDinsight] | -e | -tdinsight-title +| -e | --tdinsight-editable | TDINSIGHT_DASHBOARD_EDITABLE | If the dashboard is configured to be editable. [Default: false] | -e | --external +| -E | --external-notifier | EXTERNAL_NOTIFIER | Apply the external notifier uid to the TDinsight dashboard. | -s +| -s | --sms-enabled | SMS_ENABLED | Enable the tdengine-datasource plugin built into Alibaba Cloud SMS webhook. | -s +| -N | --sms-notifier-name | SMS_NOTIFIER_NAME | The name of the provisioning notifier. [Default: `TDinsight Builtin SMS`] | -U +| -U | --sms-notifier-uid | SMS_NOTIFIER_UID | "Notification Channel" `uid`, lowercase of the program name is used by default, other characters are replaced by "-". |-sms +| -D | --sms-notifier-is-default | SMS_NOTIFIER_IS_DEFAULT | Set built-in SMS notification to default value. |-sms-notifier-is-default +| -I | --sms-access-key-id | SMS_ACCESS_KEY_ID | Alibaba Cloud SMS access key id | +| -K | --sms-access-key-secret | SMS_ACCESS_KEY_SECRET | AliCloud SMS-access-secret-key | +| -S | --sms-sign-name | SMS_SIGN_NAME | Signature | +| -C | --sms-template-code | SMS_TEMPLATE_CODE | Template code | +| -T | --sms-template-param | SMS_TEMPLATE_PARAM | JSON template for template parameters | +| -B | --sms-phone-numbers | SMS_PHONE_NUMBERS | A comma-separated list of phone numbers, e.g. `"189xxxxxxxx,132xxxxxxxx"` | +| -L | --sms-listen-addr | SMS_LISTEN_ADDR | Built-in SMS webhook listener address, default is `127.0.0.1:9100` | + +Suppose you start a TDengine database on host `tdengine` with HTTP API port `6041`, user `root1`, and password `pass5ord`. Execute the script. + +```bash +sudo . /TDinsight.sh -a http://tdengine:6041 -u root1 -p pass5ord +``` + +We provide a "-E" option to configure TDinsight to use the existing Notification Channel from the command line. Assuming your Grafana user and password is `admin:admin`, use the following command to get the `uid` of an existing notification channel. + +```bash +curl --no-progress-meter -u admin:admin http://localhost:3000/api/alert-notifications | jq +``` + +Use the `uid` value obtained above as `-E` input. + +```bash +sudo ./TDinsight.sh -a http://tdengine:6041 -u root1 -p pass5ord -E existing-notifier +``` + +If you want to use the [Alibaba Cloud SMS](https://www.aliyun.com/product/sms) service as a notification channel, you should enable it with the `-s` flag add the following parameters. + +- `-N`: Notification Channel name, default is `TDinsight Builtin SMS`. +- `-U`: Channel uid, default is lowercase of `name`, any other character is replaced with -, for the default `-N`, its uid is `tdinsight-builtin-sms`. +- `-I`: Alibaba Cloud SMS access key id. +- `-K`: Alibaba Cloud SMS access secret key. +- `-S`: Alibaba Cloud SMS signature. +- `-C`: Alibaba Cloud SMS template id. +- `-T`: Alibaba Cloud SMS template parameters, for JSON format template, example is as follows `'{"alarm_level":"%s", "time":"%s", "name":"%s", "content":"%s"}'`. There are four parameters: alarm level, time, name and alarm content. +- `-B`: a list of phone numbers, separated by a comma `,`. + +If you want to monitor multiple TDengine clusters, you need to set up numerous TDinsight dashboards. Setting up non-default TDinsight requires some changes: the `-n` `-i` `-t` options need to be changed to non-default names, and `-N` and `-L` should also be changed if using the built-in SMS alerting feature. + +```bash +sudo . /TDengine.sh -n TDengine-Env1 -a http://another:6041 -u root -p taosdata -i tdinsight-env1 -t 'TDinsight Env1' +# If using built-in SMS notifications +sudo . /TDengine.sh -n TDengine-Env1 -a http://another:6041 -u root -p taosdata -i tdinsight-env1 -t 'TDinsight Env1' \ + -s -N 'Env1 SMS' -I xx -K xx -S xx -C SMS_XX -T '' -B 00000000000 -L 127.0.0.01:10611 +``` + +Please note that the configuration data source, notification channel, and dashboard are not changeable on the front end. You should update the configuration again via this script or manually change the configuration file in the `/etc/grafana/provisioning` directory (this is the default directory for Grafana, use the `-P` option to change it as needed). + +Specifically, `-O` can be used to set the organization ID when you are using Grafana Cloud or another organization. `-G` specifies the Grafana plugin installation directory. The `-e` parameter sets the dashboard to be editable. + +## Set up TDinsight manually + +### Install the TDengine data source plugin + +Install the latest version of the TDengine Data Source plugin from GitHub. + +```bash +get_latest_release() { + curl --silent "https://api.github.com/repos/taosdata/grafanaplugin/releases/latest" | + grep '"tag_name":' | + sed -E 's/.*"v([^"]+)".*/\1/' +} +TDENGINE_PLUGIN_VERSION=$(get_latest_release) +sudo grafana-cli \ + --pluginUrl https://github.com/taosdata/grafanaplugin/releases/download/v$TDENGINE_PLUGIN_VERSION/tdengine-datasource-$TDENGINE_PLUGIN_VERSION.zip \ + plugins install tdengine-datasource +``` + +:::note +The 3.1.6 and earlier version plugins require the following setting in the configuration file `/etc/grafana/grafana.ini` to enable unsigned plugins. + +```ini +[plugins] +allow_loading_unsigned_plugins = tdengine-datasource +``` +::: + +### Start the Grafana service + +```bash +sudo systemctl start grafana-server +sudo systemctl enable grafana-server +``` + +### Logging into Grafana + +Open the default Grafana URL in a web browser: ``http://localhost:3000``. +The default username/password is `admin`. Grafana will require a password change after the first login. + +### Adding a TDengine Data Source + +Point to the **Configurations** -> **Data Sources** menu, and click the **Add data source** button. + +![Add data source button](./assets/howto-add-datasource-button.png) + +Search for and select **TDengine**. + +![Add datasource](./assets/howto-add-datasource-tdengine.png) + +Configure the TDengine datasource. + +![Datasource Configuration](./assets/howto-add-datasource.png) + +Save and test. It will report 'TDengine Data source is working' under normal circumstances. + +![datasource test](./assets/howto-add-datasource-test.png) + +### Importing dashboards + +Point to **+** / **Create** - **import** (or `/dashboard/import` url). + +![Import Dashboard and Configuration](./assets/import_dashboard.png) + +Type the dashboard ID `15167` in the **Import via grafana.com** location and **Load**. + +![Import via grafana.com](./assets/import-dashboard-15167.png) + +Once the import is complete, the full page view of TDinsight is shown below. + +![show](./assets/TDinsight-full.png) + +## TDinsight dashboard details + +The TDinsight dashboard is designed to provide the usage and status of TDengine-related resources [dnodes, mnodes, vnodes](https://www.taosdata.com/cn/documentation/architecture#cluster) or databases. + +Details of the metrics are as follows. + +### Cluster Status + +![tdinsight-mnodes-overview](./assets/TDinsight-1-cluster-status.png) + +This section contains the current information and status of the cluster, the alert information is also here (from left to right, top to bottom). + +- **First EP**: the `firstEp` setting in the current TDengine cluster. +- **Version**: TDengine server version (master mnode). +- **Master Uptime**: The time elapsed since the current Master MNode was elected as Master. +- **Expire Time** - Enterprise version expiration time. +- **Used Measuring Points** - The number of measuring points used by the Enterprise Edition. +- **Databases** - The number of databases. +- **Connections** - The number of current connections. +- **DNodes/MNodes/VGroups/VNodes** - Total number of each resource and the number of survivors. +- **DNodes/MNodes/VGroups/VNodes Alive Percent**: The ratio of the number of alive/total for each resource, enabling the alert rule and triggering it when the resource liveness rate (the average percentage of healthy resources in 1 minute) is less than 100%. +- **Measuring Points Used**: The number of measuring points used to enable the alert rule (no data available in the community version, healthy by default). +- **Grants Expire Time**: the expiration time of the enterprise version of the enabled alert rule (no data available for the community version, healthy by default). +- **Error Rate**: Aggregate error rate (average number of errors per second) for alert-enabled clusters. +- **Variables**: `show variables` table display. + +### DNodes Status + +![tdinsight-mnodes-overview](./assets/TDinsight-2-dnodes.png) + +- **DNodes Status**: simple table view of `show dnodes`. +- **DNodes Lifetime**: the time elapsed since the dnode was created. +- **DNodes Number**: the number of DNodes changes. +- **Offline Reason**: if any dnode status is offline, the reason for offline is shown as a pie chart. + +### MNode Overview + +![tdinsight-mnodes-overview](./assets/TDinsight-3-mnodes.png) + +1. **MNodes Status**: a simple table view of `show mnodes`. 2. +2. **MNodes Number**: similar to `DNodes Number`, the number of MNodes changes. + +### Request + +![tdinsight-requests](./assets/TDinsight-4-requests.png) + +1. **Requests Rate(Inserts per Second)**: average number of inserts per second. +2. **Requests (Selects)**: number of query requests and change rate (count of second). +3. **Requests (HTTP)**: number of HTTP requests and request rate (count of second). + +### Database + +![tdinsight-database](./assets/TDinsight-5-database.png) + +Database usage, repeated for each value of the variable `$database` i.e. multiple rows per database. + +1. **STables**: number of super tables. 2. +2. **Total Tables**: number of all tables. 3. +3. **Sub Tables**: the number of all super table sub-tables. 4. +4. **Tables**: graph of all normal table numbers over time. +5. **Tables Number Foreach VGroups**: The number of tables contained in each VGroups. + +### DNode Resource Usage + +![dnode-usage](./assets/TDinsight-6-dnode-usage.png) + +Data node resource usage display with repeated multiple rows for the variable `$fqdn` i.e., each data node. Includes. + +1. **Uptime**: the time elapsed since the dnode was created. +2. **Has MNodes?**: whether the current dnode is a mnode. 3. +3. **CPU Cores**: the number of CPU cores. 4. +4. **VNodes Number**: the number of VNodes in the current dnode. 5. +5. **VNodes Masters**: the number of vnodes in the master role. 6. +6. **Current CPU Usage of taosd**: CPU usage rate of taosd processes. +7. **Current Memory Usage of taosd**: memory usage of taosd processes. +8. **Disk Used**: The total disk usage percentage of the taosd data directory. +9. **CPU Usage**: Process and system CPU usage. 10. +10. **RAM Usage**: Time series view of RAM usage metrics. +11. **Disk Used**: Disks used at each level of multi-level storage (default is level0). +12. **Disk Increasing Rate per Minute**: Percentage increase or decrease in disk usage per minute. +13. **Disk IO**: Disk IO rate. 14. +14. **Net IO**: Network IO, the aggregate network IO rate in addition to the local network. + +### Login History + +![Login History](./assets/TDinsight-7-login-history.png) + +Currently, only the number of logins per minute is reported. + +### Monitoring taosAdapter + +![taosadapter](./assets/TDinsight-8-taosadapter.png) + +Support monitoring taosAdapter request statistics and status details. Includes. + +1. **http_request**: contains the total number of requests, the number of failed requests, and the number of requests being processed +2. **top 3 request endpoint**: data of the top 3 requests by endpoint group +3. **Memory Used**: taosAdapter memory usage +4. **latency_quantile(ms)**: quantile of (1, 2, 5, 9, 99) stages +5. **top 3 failed request endpoint**: data of the top 3 failed requests by endpoint grouping +6. **CPU Used**: taosAdapter CPU usage + +## Upgrade + +TDinsight installed via the `TDinsight.sh` script can be upgraded to the latest Grafana plugin and TDinsight Dashboard by re-running the script. + +In the case of a manual installation, follow the steps above to install the new Grafana plugin and Dashboard yourself. + +## Uninstall + +TDinsight installed via the `TDinsight.sh` script can be cleaned up using the command line `TDinsight.sh -R` to clean up the associated resources. + +To completely uninstall TDinsight during a manual installation, you need to clean up the following. + +1. the TDinsight Dashboard in Grafana. +2. the Data Source in Grafana. 3. +3. remove the `tdengine-datasource` plugin from the plugin installation directory. + +## Integrated Docker Example + +```bash +git clone --depth 1 https://github.com/taosdata/grafanaplugin.git +cd grafanaplugin +``` + +Change as needed in the ``docker-compose.yml`` file to + +```yaml +version: '3.7' + +services: + grafana: + image: grafana/grafana:7.5.10 + volumes: + - . /dist:/var/lib/grafana/plugins/tdengine-datasource + - . /grafana/grafana.ini:/etc/grafana/grafana.ini + - . /grafana/provisioning/:/etc/grafana/provisioning/ + - grafana-data:/var/lib/grafana + environment: + TDENGINE_API: ${TDENGINE_API} + TDENGINE_USER: ${TDENGINE_USER} + TDENGINE_PASS: ${TDENGINE_PASS} + SMS_ACCESS_KEY_ID: ${SMS_ACCESS_KEY_ID} + SMS_ACCESS_KEY_SECRET: ${SMS_ACCESS_KEY_SECRET} + SMS_SIGN_NAME: ${SMS_SIGN_NAME} + SMS_TEMPLATE_CODE: ${SMS_TEMPLATE_CODE} + SMS_TEMPLATE_PARAM: '${SMS_TEMPLATE_PARAM}' + SMS_PHONE_NUMBERS: $SMS_PHONE_NUMBERS + SMS_LISTEN_ADDR: ${SMS_LISTEN_ADDR} + ports: + - 3000:3000 +volumes: + grafana-data: +``` + +Replace the environment variables in `docker-compose.yml` or save the environment variables to the `.env` file, then start Grafana with `docker-compose up`. See [Docker Compose Reference](https://docs.docker.com/compose/) + +```bash +docker-compose up -d +``` + +Then the TDinsight was deployed via Provisioning. Go to http://localhost:3000/d/tdinsight/ to view the dashboard. + +[grafana]: https://grafana.com +[tdengine]: https://tdengine.com diff --git a/docs-en/14-reference/08-taos-shell.md b/docs-en/14-reference/08-taos-shell.md new file mode 100644 index 0000000000000000000000000000000000000000..fe5e5f2bc29509a4b96646253732076c7a6ee7ea --- /dev/null +++ b/docs-en/14-reference/08-taos-shell.md @@ -0,0 +1,86 @@ +--- +title: TDengine Command Line (CLI) +sidebar_label: TDengine CLI +description: Instructions and tips for using the TDengine CLI +--- + +The TDengine command-line application (hereafter referred to as `TDengine CLI`) is the most simplest way for users to manipulate and interact with TDengine instances. + +## Installation + +If executed on the TDengine server-side, there is no need for additional installation steps to install TDengine CLI as it is already included and installed automatically. To run TDengine CLI on the environment which no TDengine server running, the TDengine client installation package needs to be installed first. For details, please refer to [connector](/reference/connector/). + +## Execution + +To access the TDengine CLI, you can execute `taos` command-line utility from a Linux terminal or Windows terminal. + +```bash +taos +``` + +TDengine CLI will display a welcome message and version information if it successfully connected to the TDengine service. If it fails, TDengine CLI will print an error message. See [FAQ](/train-faq/faq) to solve the problem of terminal connection failure to the server. The TDengine CLI prompts as follows: + +```cmd +taos> +``` +After entering the TDengine CLI, you can execute various SQL commands, including inserts, queries, or administrative commands. + +## Execute SQL script file + +Run SQL command script file in the TDengine CLI via the `source` command. + +```sql +taos> source ; +``` + +## Adjust display width to show more characters + +Users can adjust the display width in TDengine CLI to show more characters with the following command: + +```sql +taos> SET MAX_BINARY_DISPLAY_WIDTH ; +``` + +If the displayed content is followed by `...` you can use this command to change the display width to display the full content. + +## Command Line Parameters + +You can change the behavior of TDengine CLI by specifying command-line parameters. The following parameters are commonly used. + +- -h, --host=HOST: FQDN of the server where the TDengine server is to be connected. Default is to connect to the local service +- -P, --port=PORT: Specify the port number to be used by the server. Default is `6030` +- -u, --user=USER: the user name to use when connecting. Default is `root` +- -p, --password=PASSWORD: the password to use when connecting to the server. Default is `taosdata` +- -?, --help: print out all command-line arguments + +And many more parameters. + +- -c, --config-dir: Specify the directory where configuration file exists. The default is `/etc/taos`, and the default name of the configuration file in this directory is `taos.cfg` +- -C, --dump-config: Print the configuration parameters of `taos.cfg` in the default directory or specified by -c +- -d, --database=DATABASE: Specify the database to use when connecting to the server +- -D, --directory=DIRECTORY: Import the SQL script file in the specified path +- -f, --file=FILE: Execute the SQL script file in non-interactive mode +- -k, --check=CHECK: Specify the table to be checked +- -l, --pktlen=PKTLEN: Test package size to be used for network testing +- -n, --netrole=NETROLE: test scope for network connection test, default is `startup`. The value can be `client`, `server`, `rpc`, `startup`, `sync`, `speed`, or `fqdn`. +- -r, --raw-time: output the timestamp format as unsigned 64-bits integer (uint64_t in C language) +- -s, --commands=COMMAND: execute SQL commands in non-interactive mode +- -S, --pkttype=PKTTYPE: Specify the packet type used for network testing. The default is TCP, can be specified as either TCP or UDP when `speed` is specified to `netrole` parameter +- -T, --thread=THREADNUM: The number of threads to import data in multi-threaded mode +- -s, --commands: Run TDengine CLI commands without entering the terminal +- -z, --timezone=TIMEZONE: Specify time zone. Default is the value of current configuration file +- -V, --version: Print out the current version number + +Example. + +```bash +taos -h h1.taos.com -s "use db; show tables;" +``` +## TDengine CLI tips + +- You can use the up and down keys to iterate the history of commands entered +- Change user password: use `alter user` command in TDengine CLI to change user's password. The default password is `taosdata`. +- use Ctrl+C to stop a query in progress +- Execute `RESET QUERY CACHE` to clear the local cache of the table schema +- Execute SQL statements in batches. You can store a series of shell commands (ending with ;, one line for each SQL command) in a script file and execute the command `source ` in the TDengine CLI to execute all SQL commands in that file automatically +- Enter `q` to exit TDengine CLI diff --git a/docs-en/14-reference/09-support-platform/_category_.yml b/docs-en/14-reference/09-support-platform/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..fabf2fef69fbde25266d18df92efffb52ed8a923 --- /dev/null +++ b/docs-en/14-reference/09-support-platform/_category_.yml @@ -0,0 +1 @@ +label: supported platforms diff --git a/docs-en/14-reference/09-support-platform/index.md b/docs-en/14-reference/09-support-platform/index.md new file mode 100644 index 0000000000000000000000000000000000000000..af15758225198a55765a20d232098165a1e3c7c4 --- /dev/null +++ b/docs-en/14-reference/09-support-platform/index.md @@ -0,0 +1,34 @@ +--- +title: List of supported platforms +description: "List of platforms supported by TDengine server, client, and connector" +--- + +## List of supported platforms for TDengine server + +| | **CentOS 7/8** | **Ubuntu 16/18/20** | **Other Linux** | +| ------------ | -------------- | ------------------- | --------------- | +| X64 | ● | ● | | +| MIPS64 | | | ● | +| ARM64 | | ○ | ○ | +| Alpha64 | | | ○ | + +Note: ● means officially tested and verified, ○ means unofficially tested and verified. + +## List of supported platforms for TDengine clients and connectors + +TDengine's connector can support a wide range of platforms, including X64/X86/ARM64/ARM32/MIPS/Alpha hardware platforms and Linux/Win64/Win32 development environments. + +The comparison matrix is as follows. + +| **CPU** | **X64 64bit** | | | **X86 32bit** | **ARM64** | **ARM32** | **MIPS** | **Alpha** | +| ----------- | ------------- | --------- | --------- | ------------- | --------- | --------- | --------- | --------- | +| **OS** | **Linux** | **Win64** | **Win32** | **Win32** | **Linux** | **Linux** | **Linux** | **Linux** | +| **C/C++** | ● | ● | ● | ○ | ● | ● | ● | ● | +| **JDBC** | ● | ● | ● | ○ | ● | ● | ● | ● | +| **Python** | ● | ● | ● | ○ | ● | ● | ● | -- | +| **Go** | ● | ● | ● | ○ | ● | ● | ○ | -- | +| **NodeJs** | ● | ● | ○ | ○ | ● | ● | ○ | -- | +| **C#** | ● | ● | ○ | ○ | ○ | ○ | ○ | -- | +| **RESTful** | ● | ● | ● | ● | ● | ● | ● | ● | + +Note: ● means the official test is verified, ○ means the unofficial test is verified, -- means not verified. diff --git a/docs-en/14-reference/11-docker/_category_.yml b/docs-en/14-reference/11-docker/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..f89ef7112c6d850726e5bcf20f1d1bd9f65d02fd --- /dev/null +++ b/docs-en/14-reference/11-docker/_category_.yml @@ -0,0 +1 @@ +label: TDengine Docker images \ No newline at end of file diff --git a/docs-en/14-reference/11-docker/index.md b/docs-en/14-reference/11-docker/index.md new file mode 100644 index 0000000000000000000000000000000000000000..4ca84be369e14b3223e8609e06c9ebc4e35eaa2d --- /dev/null +++ b/docs-en/14-reference/11-docker/index.md @@ -0,0 +1,515 @@ +--- +title: Deploying TDengine with Docker +Description: "This chapter focuses on starting the TDengine service in a container and accessing it." +--- + +This chapter describes how to start the TDengine service in a container and access it. Users can control the behavior of the service in the container by using environment variables on the docker run command-line or in the docker-compose file. + +## Starting TDengine + +The TDengine image starts with the HTTP service activated by default, using the following command: + +```shell +docker run -d --name tdengine -p 6041:6041 tdengine/tdengine +``` + +The above command starts a container named "tdengine" and maps the HTTP service end 6041 to the host port 6041. You can verify that the HTTP service provided in this container is available using the following command. + +```shell +curl -u root:taosdata -d "show databases" localhost:6041/rest/sql +``` + +The TDengine client taos can be executed in this container to access TDengine using the following command. + +```shell +$ docker exec -it tdengine taos + +Welcome to the TDengine shell from Linux, Client Version:2.4.0.0 +Copyright (c) 2020 by TAOS Data, Inc. + +taos> show databases; + name | created_time | ntables | vgroups | replica | quorum | days | keep | cache(MB) | blocks | minrows | maxrows | wallevel | fsync | comp | cachelast | precision | update | status | status precision | update | status | +================================================================================================================================== ================================================================================================================================== ================ + log | 2022-01-17 13:57:22.270 | 10 | 1 | 1 | 1 | 10 | 30 | 1 | 3 | 100 | 4096 | 1 | 3000 | 2 | 0 | us | 0 | ready | +Query OK, 1 row(s) in set (0.002843s) +``` + +The TDengine server running in the container uses the container's hostname to establish a connection. Using TDengine CLI or various connectors (such as JDBC-JNI) to access the TDengine inside the container from outside the container is more complicated. So the above is the simplest way to access the TDengine service in the container and is suitable for some simple scenarios. Please refer to the next section if you want to access the TDengine service in the container from containerized using TDengine CLI or various connectors in some complex scenarios. + +## Start TDengine on the host network + +```shell +docker run -d --name tdengine --network host tdengine/tdengine +``` + +The above command starts TDengine on the host network and uses the host's FQDN to establish a connection instead of the container's hostname. It works too, like using `systemctl` to start TDengine on the host. If the TDengine client is already installed on the host, you can access it directly with the following command. + +```shell +$ taos + +Welcome to the TDengine shell from Linux, Client Version:2.4.0.0 +Copyright (c) 2020 by TAOS Data, Inc. + +taos> show dnodes; + id | end_point | vnodes | cores | status | role | create_time | offline reason | +================================================================================================================================== ==== + 1 | myhost:6030 | 1 | 8 | ready | any | 2022-01-17 22:10:32.619 | | +Query OK, 1 row(s) in set (0.003233s) +``` + +## Start TDengine with the specified hostname and port + +The `TAOS_FQDN` environment variable or the `fqdn` configuration item in `taos.cfg` allows TDengine to establish a connection at the specified hostname. This approach provides greater flexibility for deployment. + +```shell +docker run -d \ + --name tdengine \ + -e TAOS_FQDN=tdengine \ + -p 6030-6049:6030-6049 \ + -p 6030-6049:6030-6049/udp \ + tdengine/tdengine +``` + +The above command starts a TDengine service in the container, which listens to the hostname tdengine, and maps the container's port segment 6030 to 6049 to the host's port segment 6030 to 6049 (both TCP and UDP ports need to be mapped). If the port segment is already occupied on the host, you can modify the above command to specify a free port segment on the host. If `rpcForceTcp` is set to `1`, you can map only the TCP protocol. + +Next, ensure the hostname "tdengine" is resolvable in `/etc/hosts`. + +```shell +echo 127.0.0.1 tdengine |sudo tee -a /etc/hosts +``` + +Finally, the TDengine service can be accessed from the taos shell or any connector with "tdengine" as the server address. + +```shell +taos -h tdengine -P 6030 +``` + +If set `TAOS_FQDN` to the same hostname, the effect is the same as "Start TDengine on host network". + +## Start TDengine on the specified network + +You can also start TDengine on a specific network. + +1. First, create a docker network named `td-net` + + ```shell + docker network create td-net + ``` Create td-net + +2. Start TDengine + + Start the TDengine service on the `td-net` network with the following command: + + ```shell + docker run -d --name tdengine --network td-net \ + -e TAOS_FQDN=tdengine \ + tdengine/tdengine + ``` + +3. Start the TDengine client in another container on the same network + + ```shell + docker run --rm -it --network td-net -e TAOS_FIRST_EP=tdengine tdengine/tdengine taos + # or + # docker run --rm -it --network td-net -e tdengine/tdengine taos -h tdengine + ``` + +## Launching a client application in a container + +If you want to start your application in a container, you need to add the corresponding dependencies on TDengine to the image as well, e.g. + +```docker +FROM ubuntu:20.04 +RUN apt-get update && apt-get install -y wget +ENV TDENGINE_VERSION=2.4.0.0 +RUN wget -c https://www.taosdata.com/assets-download/TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz \ + && tar xvf TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz \ + && cd TDengine-client-${TDENGINE_VERSION} \ + && ./install_client.sh \ + && cd ../ \ + && rm -rf TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz TDengine-client-${TDENGINE_VERSION} +## add your application next, eg. go, build it in builder stage, copy the binary to the runtime +#COPY --from=builder /path/to/build/app /usr/bin/ +#CMD ["app"] +``` + +Here is an example GO program: + +```go +/* + * In this test program, we'll create a database and insert 4 records then select out. + */ +package main + +import ( + "database/sql" + "flag" + "fmt" + "time" + + _ "github.com/taosdata/driver-go/v2/taosSql" +) + +type config struct { + hostName string + serverPort string + user string + password string +} + +var configPara config +var taosDriverName = "taosSql" +var url string + +func init() { + flag.StringVar(&configPara.hostName, "h", "", "The host to connect to TDengine server.") + flag.StringVar(&configPara.serverPort, "p", "", "The TCP/IP port number to use for the connection to TDengine server.") + flag.StringVar(&configPara.user, "u", "root", "The TDengine user name to use when connecting to the server.") + flag.StringVar(&configPara.password, "P", "taosdata", "The password to use when connecting to the server.") + flag.Parse() +} + +func printAllArgs() { + fmt.Printf("============= args parse result: =============\n") + fmt.Printf("hostName: %v\n", configPara.hostName) + fmt.Printf("serverPort: %v\n", configPara.serverPort) + fmt.Printf("usr: %v\n", configPara.user) + fmt.Printf("password: %v\n", configPara.password) + fmt.Printf("================================================\n") +} + +func main() { + printAllArgs() + + url = "root:taosdata@/tcp(" + configPara.hostName + ":" + configPara.serverPort + ")/" + + taos, err := sql.Open(taosDriverName, url) + checkErr(err, "open database error") + defer taos.Close() + + taos.Exec("create database if not exists test") + taos.Exec("use test") + taos.Exec("create table if not exists tb1 (ts timestamp, a int)") + _, err = taos.Exec("insert into tb1 values(now, 0)(now+1s,1)(now+2s,2)(now+3s,3)") + checkErr(err, "failed to insert") + rows, err := taos.Query("select * from tb1") + checkErr(err, "failed to select") + + defer rows.Close() + for rows.Next() { + var r struct { + ts time.Time + a int + } + err := rows.Scan(&r.ts, &r.a) + if err != nil { + fmt.Println("scan error:\n", err) + return + } + fmt.Println(r.ts, r.a) + } +} + +func checkErr(err error, prompt string) { + if err != nil { + fmt.Println("ERROR: %s\n", prompt) + panic(err) + } +} +``` + +Here is the full Dockerfile: + +```docker +FROM golang:1.17.6-buster as builder +ENV TDENGINE_VERSION=2.4.0.0 +RUN wget -c https://www.taosdata.com/assets-download/TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz \ + && tar xvf TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz \ + && cd TDengine-client-${TDENGINE_VERSION} \ + && ./install_client.sh \ + && cd ../ \ + && rm -rf TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz TDengine-client-${TDENGINE_VERSION} +WORKDIR /usr/src/app/ +ENV GOPROXY="https://goproxy.io,direct" +COPY ./main.go ./go.mod ./go.sum /usr/src/app/ +RUN go env +RUN go mod tidy +RUN go build + +FROM ubuntu:20.04 +RUN apt-get update && apt-get install -y wget +ENV TDENGINE_VERSION=2.4.0.0 +RUN wget -c https://www.taosdata.com/assets-download/TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz \ + && tar xvf TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz \ + && cd TDengine-client-${TDENGINE_VERSION} \ + && ./install_client.sh \ + && cd ../ \ + && rm -rf TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz TDengine-client-${TDENGINE_VERSION} + +## add your application next, eg. go, build it in builder stage, copy the binary to the runtime +COPY --from=builder /usr/src/app/app /usr/bin/ +CMD ["app"] +``` + +Now that we have `main.go`, `go.mod`, `go.sum`, `app.dockerfile`, we can build the application and start it on the `td-net` network. + +```shell +$ docker build -t app -f app.dockerfile +$ docker run --rm --network td-net app -h tdengine -p 6030 +============= args parse result: ============= +hostName: tdengine +serverPort: 6030 +usr: root +password: taosdata +================================================ +2022-01-17 15:56:55.48 +0000 UTC 0 +2022-01-17 15:56:56.48 +0000 UTC 1 +2022-01-17 15:56:57.48 +0000 UTC 2 +2022-01-17 15:56:58.48 +0000 UTC 3 +2022-01-17 15:58:01.842 +0000 UTC 0 +2022-01-17 15:58:02.842 +0000 UTC 1 +2022-01-17 15:58:03.842 +0000 UTC 2 +2022-01-17 15:58:04.842 +0000 UTC 3 +2022-01-18 01:43:48.029 +0000 UTC 0 +2022-01-18 01:43:49.029 +0000 UTC 1 +2022-01-18 01:43:50.029 +0000 UTC 2 +2022-01-18 01:43:51.029 +0000 UTC 3 +``` + +## Start the TDengine cluster with docker-compose + +1. The following docker-compose file starts a TDengine cluster with two replicas, two management nodes, two data nodes, and one arbitrator. + + ```docker + version: "3" + services: + arbitrator: + image: tdengine/tdengine:$VERSION + command: tarbitrator + td-1: + image: tdengine/tdengine:$VERSION + environment: + TAOS_FQDN: "td-1" + TAOS_FIRST_EP: "td-1" + TAOS_NUM_OF_MNODES: "2" + TAOS_REPLICA: "2" + TAOS_ARBITRATOR: arbitrator:6042 + volumes: + - taosdata-td1:/var/lib/taos/ + - taoslog-td1:/var/log/taos/ + td-2: + image: tdengine/tdengine:$VERSION + environment: + TAOS_FQDN: "td-2" + TAOS_FIRST_EP: "td-1" + TAOS_NUM_OF_MNODES: "2" + TAOS_REPLICA: "2" + TAOS_ARBITRATOR: arbitrator:6042 + volumes: + - taosdata-td2:/var/lib/taos/ + - taoslog-td2:/var/log/taos/ + volumes: + taosdata-td1: + taoslog-td1: + taosdata-td2: + taoslog-td2: + ``` + + :::note +- The `VERSION` environment variable is used to set the tdengine image tag + - `TAOS_FIRST_EP` must be set on the newly created instance so that it can join the TDengine cluster; if there is a high availability requirement, `TAOS_SECOND_EP` needs to be used at the same time + - `TAOS_REPLICA` is used to set the default number of database replicas. Its value range is [1,3] + We recommend setting with `TAOS_ARBITRATOR` to use arbitrator in a two-nodes environment. + ::: + + +2. Start the cluster + + ```shell + $ VERSION=2.4.0.0 docker-compose up -d + Creating network "test_default" with the default driver + Creating volume "test_taosdata-td1" with default driver + Creating volume "test_taoslog-td1" with default driver + Creating volume "test_taosdata-td2" with default driver + Creating volume "test_taoslog-td2" with default driver + Creating test_td-1_1 ... done + Creating test_arbitrator_1 ... done + Creating test_td-2_1 ... done + ``` + +3. Check the status of each node + + ```shell + $ docker-compose ps + Name Command State Ports + --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + test_arbitrator_1 /usr/bin/entrypoint.sh tar ... Up 6030/tcp, 6031/tcp, 6032/tcp, 6033/tcp, 6034/tcp, 6035/tcp, 6036/tcp, 6037/tcp, 6038/tcp, 6039/tcp, 6040/tcp, 6041/tcp, 6042/tcp + test_td-1_1 /usr/bin/entrypoint.sh taosd Up 6030/tcp, 6031/tcp, 6032/tcp, 6033/tcp, 6034/tcp, 6035/tcp, 6036/tcp, 6037/tcp, 6038/tcp, 6039/tcp, 6040/tcp, 6041/tcp, 6042/tcp + test_td-2_1 /usr/bin/entrypoint.sh taosd Up 6030/tcp, 6031/tcp, 6032/tcp, 6033/tcp, 6034/tcp, 6035/tcp, 6036/tcp, 6037/tcp, 6038/tcp, 6039/tcp, 6040/tcp, 6041/tcp, 6042/tcp + ``` + +4. Show dnodes via TDengine CLI + + ```shell + $ docker-compose exec td-1 taos -s "show dnodes" + + Welcome to the TDengine shell from Linux, Client Version:2.4.0.0 + Copyright (c) 2020 by TAOS Data, Inc. All rights reserved. + + taos> show dnodes + id | end_point | vnodes | cores | status | role | create_time | offline reason | + ====================================================================================================================================== + 1 | td-1:6030 | 1 | 8 | ready | any | 2022-01-18 02:47:42.871 | | + 2 | td-2:6030 | 0 | 8 | ready | any | 2022-01-18 02:47:43.518 | | + 0 | arbitrator:6042 | 0 | 0 | ready | arb | 2022-01-18 02:47:43.633 | - | + Query OK, 3 row(s) in set (0.000811s) + ``` + +## taosAdapter + +1. taosAdapter is enabled by default in the TDengine container. If you want to disable it, specify the environment variable `TAOS_DISABLE_ADAPTER=true` at startup + +2. At the same time, for flexible deployment, taosAdapter can be started in a separate container + + ```docker + services: + # ... + adapter: + image: tdengine/tdengine:$VERSION + command: taosadapter + ```` + + Suppose you want to deploy multiple taosAdapters to improve throughput and provide high availability. In that case, the recommended configuration method uses a reverse proxy such as Nginx to offer a unified access entry. For specific configuration methods, please refer to the official documentation of Nginx. Here is an example: + + ```docker + ersion: "3" + + networks: + inter: + api: + + services: + arbitrator: + image: tdengine/tdengine:$VERSION + command: tarbitrator + networks: + - inter + td-1: + image: tdengine/tdengine:$VERSION + networks: + - inter + environment: + TAOS_FQDN: "td-1" + TAOS_FIRST_EP: "td-1" + TAOS_NUM_OF_MNODES: "2" + TAOS_REPLICA: "2" + TAOS_ARBITRATOR: arbitrator:6042 + volumes: + - taosdata-td1:/var/lib/taos/ + - taoslog-td1:/var/log/taos/ + td-2: + image: tdengine/tdengine:$VERSION + networks: + - inter + environment: + TAOS_FQDN: "td-2" + TAOS_FIRST_EP: "td-1" + TAOS_NUM_OF_MNODES: "2" + TAOS_REPLICA: "2" + TAOS_ARBITRATOR: arbitrator:6042 + volumes: + - taosdata-td2:/var/lib/taos/ + - taoslog-td2:/var/log/taos/ + adapter: + image: tdengine/tdengine:$VERSION + command: taosadapter + networks: + - inter + environment: + TAOS_FIRST_EP: "td-1" + TAOS_SECOND_EP: "td-2" + deploy: + replicas: 4 + nginx: + image: nginx + depends_on: + - adapter + networks: + - inter + - api + ports: + - 6041:6041 + - 6044:6044/udp + command: [ + "sh", + "-c", + "while true; + do curl -s http://adapter:6041/-/ping >/dev/null && break; + done; + printf 'server{listen 6041;location /{proxy_pass http://adapter:6041;}}' + > /etc/nginx/conf.d/rest.conf; + printf 'stream{server{listen 6044 udp;proxy_pass adapter:6044;}}' + >> /etc/nginx/nginx.conf;cat /etc/nginx/nginx.conf; + nginx -g 'daemon off;'", + ] + volumes: + taosdata-td1: + taoslog-td1: + taosdata-td2: + taoslog-td2: + ``` + +## Deploy with docker swarm + +If you want to deploy a container-based TDengine cluster on multiple hosts, you can use docker swarm. First, to establish a docker swarm cluster on these hosts, please refer to the official docker documentation. + +The docker-compose file can refer to the previous section. Here is the command to start TDengine with docker swarm: + +```shell +$ VERSION=2.4.0 docker stack deploy -c docker-compose.yml taos +Creating network taos_inter +Creating network taos_api +Creating service taos_arbitrator +Creating service taos_td-1 +Creating service taos_td-2 +Creating service taos_adapter +Creating service taos_nginx +``` + +Checking status: + +```shell +$ docker stack ps taos +ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS +79ni8temw59n taos_nginx.1 nginx:latest TM1701 Running Running about a minute ago +3e94u72msiyg taos_adapter.1 tdengine/tdengine:2.4.0 TM1702 Running Running 56 seconds ago +100amjkwzsc6 taos_td-2.1 tdengine/tdengine:2.4.0 TM1703 Running Running about a minute ago +pkjehr2vvaaa taos_td-1.1 tdengine/tdengine:2.4.0 TM1704 Running Running 2 minutes ago +tpzvgpsr1qkt taos_arbitrator.1 tdengine/tdengine:2.4.0 TM1705 Running Running 2 minutes ago +rvss3g5yg6fa taos_adapter.2 tdengine/tdengine:2.4.0 TM1706 Running Running 56 seconds ago +i2augxamfllf taos_adapter.3 tdengine/tdengine:2.4.0 TM1707 Running Running 56 seconds ago +lmjyhzccpvpg taos_adapter.4 tdengine/tdengine:2.4.0 TM1708 Running Running 56 seconds ago +$ docker service ls +ID NAME MODE REPLICAS IMAGE PORTS +561t4lu6nfw6 taos_adapter replicated 4/4 tdengine/tdengine:2.4.0 +3hk5ct3q90sm taos_arbitrator replicated 1/1 tdengine/tdengine:2.4.0 +d8qr52envqzu taos_nginx replicated 1/1 nginx:latest *:6041->6041/tcp, *:6044->6044/udp +2isssfvjk747 taos_td-1 replicated 1/1 tdengine/tdengine:2.4.0 +9pzw7u02ichv taos_td-2 replicated 1/1 tdengine/tdengine:2.4.0 +``` + +From the above output, you can see two dnodes, two taosAdapters, and one Nginx reverse proxy service. + +Next, we can reduce the number of taosAdapter services. + +```shell +$ docker service scale taos_adapter=1 +taos_adapter scaled to 1 +overall progress: 1 out of 1 tasks +1/1: running [==================================================>] +verify: Service converged + +$ docker service ls -f name=taos_adapter +ID NAME MODE REPLICAS IMAGE PORTS +561t4lu6nfw6 taos_adapter replicated 1/1 tdengine/tdengine:2.4.0 +``` diff --git a/docs-en/14-reference/12-config/_category_.yml b/docs-en/14-reference/12-config/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..94aa9438729449e6e0d13b20019514dbbbc3a384 --- /dev/null +++ b/docs-en/14-reference/12-config/_category_.yml @@ -0,0 +1 @@ +label: Configuration \ No newline at end of file diff --git a/docs-en/14-reference/12-config/index.md b/docs-en/14-reference/12-config/index.md new file mode 100644 index 0000000000000000000000000000000000000000..c4e7cc523c400ea5be6610b64f1561246b1bfa24 --- /dev/null +++ b/docs-en/14-reference/12-config/index.md @@ -0,0 +1,1120 @@ +--- +sidebar_label: Configuration +title: Configuration Parameters +description: "Configuration parameters for client and server in TDengine" +--- + +In this chapter, all the configuration parameters on both server and client side are described thoroughly. + +## Configuration File on Server Side + +On the server side, the actual service of TDengine is provided by an executable `taosd` whose parameters can be configured in file `taos.cfg` to meet the requirements of different use cases. The default location of `taos.cfg` is `/etc/taos`, but can be changed by using `-c` parameter on the CLI of `taosd`. For example, the configuration file can be put under `/home/user` and used like below + +```bash +taosd -c /home/user +``` + +Parameter `-C` can be used on the CLI of `taosd` to show its configuration, like below: + +``` +taosd -C +``` + +## Configuration File on Client Side + +TDengine CLI `taos` is the tool for users to interact with TDengine. It can share same configuration file as `taosd` or use a separate configuration file. When launching `taos`, parameter `-c` can be used to specify the location where its configuration file is. For example `taos -c /home/cfg` means `/home/cfg/taos.cfg` will be used. If `-c` is not used, the default location of the configuration file is `/etc/taos`. For more details please use `taos --help` to get. + +From version 2.0.10.0 below commands can be used to show the configuration parameters of the client side. + +```bash +taos -C +``` + +```bash +taos --dump-config +``` + +# Configuration Parameters + +:::note +`taosd` needs to be restarted for the parameters changed in the configuration file to take effect. + +::: + +## Connection Parameters + +### firstEp + +| Attribute | Description | +| ------------- | ---------------------------------------------------------------------------------------------------- | +| Applicable | Server and Client | +| Meaning | The end point of the first dnode in the cluster to be connected to when `taosd` or `taos` is started | +| Default Value | localhost:6030 | + +### secondEp + +| Attribute | Description | +| ------------- | ---------------------------------------------------------------------------------------------------------------------- | +| Applicable | Server and Client | +| Meaning | The end point of the second dnode to be connected to if the firstEp is not available when `taosd` or `taos` is started | +| Default Value | None | + +### fqdn + +| Attribute | Description | +| ------------- | ------------------------------------------------------------------------ | +| Applicable | Server Only | +| Meaning | The FQDN of the host where `taosd` will be started. It can be IP address | +| Default Value | The first hostname configured for the hos | +| Note | It should be within 96 bytes | + +### serverPort + +| Attribute | Description | +| ------------- | ------------------------------------------------------------------------------------------------------------------------------- | +| Applicable | Server Only | +| Meaning | The port for external access after `taosd` is started | +| Default Value | 6030 | +| Note | REST service is provided by `taosd` before 2.4.0.0 but by `taosAdapter` after 2.4.0.0, the default port of REST service is 6041 | + +:::note +TDengine uses continuous 13 ports, both TCP and TCP, from the port specified by `serverPort`. These ports need to be kept as open if firewall is enabled. Below table describes the ports used by TDengine in details. + +::: + +| Protocol | Default Port | Description | How to configure | +| :------- | :----------- | :----------------------------------------------- | :--------------------------------------------------------------------------------------------- | +| TCP | 6030 | Communication between client and server | serverPort | +| TCP | 6035 | Communication among server nodes in cluster | serverPort+5 | +| TCP | 6040 | Data syncup among server nodes in cluster | serverPort+10 | +| TCP | 6041 | REST connection between client and server | Prior to 2.4.0.0: serverPort+11; After 2.4.0.0 refer to [taosAdapter](/reference/taosadapter/) | +| TCP | 6042 | Service Port of Arbitrator | The parameter of Arbitrator | +| TCP | 6043 | Service Port of TaosKeeper | The parameter of TaosKeeper | +| TCP | 6044 | Data access port for StatsD | refer to [taosAdapter](/reference/taosadapter/) | +| UDP | 6045 | Data access for statsd | refer to [taosAdapter](/reference/taosadapter/) | +| TCP | 6060 | Port of Monitoring Service in Enterprise version | | +| UDP | 6030-6034 | Communication between client and server | serverPort | +| UDP | 6035-6039 | Communication among server nodes in cluster | serverPort | + +### maxShellConns + +| Attribute | Description | +| ------------- | ---------------------------------------------------- | +| Applicable | Server Only | +| Meaning | The maximum number of connections a dnode can accept | +| Value Range | 10-50000000 | +| Default Value | 5000 | + +### maxConnections + +| Attribute | Description | +| ------------- | ----------------------------------------------------------------------------- | +| Applicable | Server Only | +| Meaning | The maximum number of connections allowed by a database | +| Value Range | 1-100000 | +| Default Value | 5000 | +| Note | The maximum number of worker threads on the client side is maxConnections/100 | + +### rpcForceTcp + +| Attribute | Description | +| ------------- | ------------------------------------------------------------------- | +| Applicable | Server and Client | +| Meaning | TCP is used by force | +| Value Range | 0: disabled 1: enabled | +| Default Value | 0 | +| Note | It's suggested to configure to enable if network is not good enough | + +## Monitoring Parameters + +### monitor + +| Attribute | Description | +| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Applicable | Server Only | +| Meaning | The switch for monitoring inside server. The workload of the hosts, including CPU, memory, disk, network, TTP requests, are collected and stored in a system builtin database `LOG` | +| Value Range | 0: monitoring disabled, 1: monitoring enabled | +| Default Value | 0 | + +### monitorInterval + +| Attribute | Description | +| ------------- | ------------------------------------------ | +| Applicable | Server Only | +| Meaning | The interval of collecting system workload | +| Unit | second | +| Value Range | 1-600 | +| Default Value | 30 | + +### telemetryReporting + +| Attribute | Description | +| ------------- | ---------------------------------------------------------------------------- | +| Applicable | Server Only | +| Meaning | Switch for allowing TDengine to collect and report service usage information | +| Value Range | 0: Not allowed; 1: Allowed | +| Default Value | 1 | + +## Query Parameters + +### queryBufferSize + +| Attribute | Description | +| ------------- | ---------------------------------------------------------------------------------------- | +| Applicable | Server Only | +| Meaning | The total memory size reserved for all queries | +| Unit | MB | +| Default Value | None | +| Note | It can be estimated by "maximum number of concurrent queries" _ "number of tables" _ 170 | + +### ratioOfQueryCores + +| Attribute | Description | +| ------------- | ----------------------------------------------------------------------------------------------------- | +| Applicable | Server Only | +| Meaning | Maximum number of query threads | +| Default Value | 1 | +| Note | value range: float number between [0, 2] 0: only 1 query thread; >0: the times of the number of cores | + +### maxNumOfDistinctRes + +| Attribute | Description | +| ------------- | -------------------------------------------- | +| Applicable | Server Only | +| Meaning | The maximum number of distinct rows returned | +| Value Range | [100,000 - 100, 000, 000] | +| Default Value | 100, 000 | +| Note | After version 2.3.0.0 | + +## Locale Parameters + +### timezone + +| Attribute | Description | +| ------------- | ------------------------------- | +| Applicable | Server and Client | +| Meaning | TimeZone | +| Default Value | TimeZone configured in the host | + +:::info +To handle the data insertion and data query from multiple timezones, Unix Timestamp is used and stored TDengine. The timestamp generated from any timezones at same time is same in Unix timestamp. To make sure the time on client side can be converted to Unix timestamp correctly, the timezone must be set properly. + +On Linux system, TDengine clients automatically obtain timezone from the host. Alternatively, the timezone can be configured explicitly in configuration file `taos.cfg` like below. + +``` +timezone UTC-8 +timezone GMT-8 +timezone Asia/Shanghai +``` + +The above examples are all proper configuration for the timezone of UTC+8. On Windows system, however, `timezone Asia/Shanghai` is not supported, it must be set as `timezone UTC-8`. + +The setting for timezone impacts the strings not in Unix timestamp, keywords or functions related to date/time, for example + +```sql +SELECT count(*) FROM table_name WHERE TS<'2019-04-11 12:01:08'; +``` + +If the timezone is UTC+8, the above SQL statement is equal to: + +```sql +SELECT count(*) FROM table_name WHERE TS<1554955268000; +``` + +If the timezone is UTC, it's equal to + +```sql +SELECT count(*) FROM table_name WHERE TS<1554984068000; +``` + +To avoid the problems of using time strings, Unix timestamp can be used directly. Furthermore, time strings with timezone can be used in SQL statement, for example "2013-04-12T15:52:01.123+08:00" in RFC3339 format or "2013-04-12T15:52:01.123+0800" in ISO-8601 format, they are not influenced by timezone setting when converted to Unix timestamp. + +::: + +### locale + +| Attribute | Description | +| ------------- | ------------------------- | +| Applicable | Server and Client | +| Meaning | Location code | +| Default Value | Locale configured in host | + +:::info +A specific type "nchar" is provided in TDengine to store non-ASCII characters such as Chinese, Japanese, Korean. The characters to be stored in nchar type are firstly encoded in UCS4-LE before sending to server side. To store non-ASCII characters correctly, the encoding format of the client side needs to be set properly. + +The characters input on the client side are encoded using the default system encoding, which is UTF-8 on Linux, or GB18030 or GBK on some systems in Chinese, POSIX in docker, CP936 on Windows in Chinese. The encoding of the operating system in use must be set correctly so that the characters in nchar type can be converted to UCS4-LE. + +The locale definition standard on Linux is: \_., for example, in "zh_CN.UTF-8", "zh" means Chinese, "CN" means China mainland, "UTF-8" means charset. On Linux andMac OSX, the charset can be set by locale in the system. On Windows system another configuration parameter `charset` must be used to configure charset because the locale used on Windows is not POSIX standard. Of course, `charset` can also be used on Linux to specify the charset. + +::: + +### charset + +| Attribute | Description | +| ------------- | ------------------------- | +| Applicable | Server and Client | +| Meaning | Character | +| Default Value | charset set in the system | + +:::info +On Linux, if `charset` is not set in `taos.cfg`, when `taos` is started, the charset is obtained from system locale. If obtaining charset from system locale fails, `taos` would fail to start. So on Linux system, if system locale is set properly, it's not necessary to set `charset` in `taos.cfg`. For example: + +``` +locale zh_CN.UTF-8 +``` + +Besides, on Linux system, if the charset contained in `locale` is not consistent with that set by `charset`, the one who comes later in the configuration file is used. + +```title="Effective charset is GBK" +locale zh_CN.UTF-8 +charset GBK +``` + +```title="Effective charset is UTF-8" +charset GBK +locale zh_CN.UTF-8 +``` + +On Windows system, it's not possible to obtain charset from system locale. If it's not set in configuration file `taos.cfg`, it would be default to CP936, same as set as below in `taos.cfg`. For example + +``` +charset CP936 +``` + +::: + +## Storage Parameters + +### dataDir + +| Attribute | Description | +| ------------- | ------------------------------------------- | +| Applicable | Server Only | +| Meaning | All data files are stored in this directory | +| Default Value | /var/lib/taos | + +### cache + +| Attribute | Description | +| ------------- | ----------------------------- | +| Applicable | Server Only | +| Meaning | The size of each memory block | +| Unit | MB | +| Default Value | 16 | + +### blocks + +| Attribute | Description | +| ------------- | -------------------------------------------------------------- | +| Applicable | Server Only | +| Meaning | The number of memory blocks of size `cache` used by each vnode | +| Default Value | 6 | + +### days + +| Attribute | Description | +| ------------- | ----------------------------------------------------- | +| Applicable | Server Only | +| Meaning | The time range of the data stored in single data file | +| Unit | day | +| Default Value | 10 | + +### keep + +| Attribute | Description | +| ------------- | -------------------------------------- | +| Applicable | Server Only | +| Meaning | The number of days for data to be kept | +| Unit | day | +| Default Value | 3650 | + +### minRows + +| Attribute | Description | +| ------------- | ------------------------------------------ | +| Applicable | Server Only | +| Meaning | minimum number of rows in single data file | +| Default Value | 100 | + +### maxRows + +| Attribute | Description | +| ------------- | ------------------------------------------ | +| Applicable | Server Only | +| Meaning | maximum number of rows in single data file | +| Default Value | 4096 | + +### walLevel + +| Attribute | Description | +| ------------- | ---------------------------------------------------------------------------------- | +| Applicable | Server Only | +| Meaning | WAL level | +| Value Range | 0: wal disabled
1: wal enabled without fsync
2: wal enabled with fsync | +| Default Value | 1 | + +### fsync + +| Attribute | Description | +| ------------- | --------------------------------------------------------------------------------------------------------------------- | +| Applicable | Server Only | +| Meaning | The waiting time for invoking fsync when walLevel is 2 | +| Unit | millisecond | +| Value Range | 0: no waiting time, fsync is performed immediately once WAL is written;
maximum value is 180000, i.e. 3 minutes | +| Default Value | 3000 | + +### update + +| Attribute | Description | +| ------------- | ------------------------------------------------------------------------------------------------------ | +| Applicable | Server Only | +| Meaning | If it's allowed to update existing data | +| Value Range | 0: not allowed
1: a row can only be updated as a whole
2: a part of columns can be updated | +| Default Value | 0 | +| Note | Not available from version 2.0.8.0 | + +### cacheLast + +| Attribute | Description | +| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Applicable | Server Only | +| Meaning | Whether to cache the latest rows of each sub table in memory | +| Value Range | 0: not cached
1: the last row of each sub table is cached
2: the last non-null value of each column is cached
3: identical to both 1 and 2 are set | +| Default Value | 0 | + +### minimalTmpDirGB + +| Attribute | Description | +| ------------- | ----------------------------------------------------------------------------------------------- | +| Applicable | Server and Client | +| Meaning | When the available disk space in tmpDir is below this threshold, writing to tmpDir is suspended | +| Unit | GB | +| Default Value | 1.0 | + +### minimalDataDirGB + +| Attribute | Description | +| ------------- | ------------------------------------------------------------------------------------------------ | +| Applicable | Server Only | +| Meaning | hen the available disk space in dataDir is below this threshold, writing to dataDir is suspended | +| Unit | GB | +| Default Value | 2.0 | + +### vnodeBak + +| Attribute | Description | +| ------------- | --------------------------------------------------------------------------- | +| Applicable | Server Only | +| Meaning | Whether to backup the corresponding vnode directory when a vnode is deleted | +| Value Range | 0: not backed up, 1: backup | +| Default Value | 1 | + +## Cluster Parameters + +### numOfMnodes + +| Attribute | Description | +| ------------- | ------------------------------ | +| Applicable | Server Only | +| Meaning | The number of management nodes | +| Default Value | 3 | + +### replica + +| Attribute | Description | +| ------------- | -------------------------- | +| Applicable | Server Only | +| Meaning | The number of replications | +| Value Range | 1-3 | +| Default Value | 1 | + +### quorum + +| Attribute | Description | +| ------------- | ------------------------------------------------------------------------------------------ | +| Applicable | Server Only | +| Meaning | The number of required confirmations for data replication in case of multiple replications | +| Value Range | 1,2 | +| Default Value | 1 | + +### role + +| Attribute | Description | +| ------------- | --------------------------------------------------------------- | +| Applicable | Server Only | +| Meaning | The role of the dnode | +| Value Range | 0: both mnode and vnode
1: mnode only
2: dnode only | +| Default Value | 0 | + +### balance + +| Attribute | Description | +| ------------- | ------------------------ | +| Applicable | Server Only | +| Meaning | Automatic load balancing | +| Value Range | 0: disabled, 1: enabled | +| Default Value | 1 | + +### balanceInterval + +| Attribute | Description | +| ------------- | ----------------------------------------------- | +| Applicable | Server Only | +| Meaning | The interval for checking load balance by mnode | +| Unit | second | +| Value Range | 1-30000 | +| Default Value | 300 | + +### arbitrator + +| Attribute | Description | +| ------------- | -------------------------------------------------- | +| Applicable | Server Only | +| Meaning | End point of arbitrator, format is same as firstEp | +| Default Value | None | + +## Time Parameters + +### precision + +| Attribute | Description | +| ------------- | ------------------------------------------------- | +| Applicable | Server only | +| Meaning | Time precision used for each database | +| Value Range | ms: millisecond; us: microsecond ; ns: nanosecond | +| Default Value | ms | + +### rpcTimer + +| Attribute | Description | +| ------------- | ------------------ | +| Applicable | Server and Client | +| Meaning | rpc retry interval | +| Unit | milliseconds | +| Value Range | 100-3000 | +| Default Value | 300 | + +### rpcMaxTime + +| Attribute | Description | +| ------------- | ---------------------------------- | +| Applicable | Server and Client | +| Meaning | maximum wait time for rpc response | +| Unit | second | +| Value Range | 100-7200 | +| Default Value | 600 | + +### statusInterval + +| Attribute | Description | +| ------------- | ----------------------------------------------- | +| Applicable | Server Only | +| Meaning | the interval of dnode reporting status to mnode | +| Unit | second | +| Value Range | 1-10 | +| Default Value | 1 | + +### shellActivityTimer + +| Attribute | Description | +| ------------- | ------------------------------------------------------ | +| Applicable | Server and Client | +| Meaning | The interval for taos shell to send heartbeat to mnode | +| Unit | second | +| Value Range | 1-120 | +| Default Value | 3 | + +### tableMetaKeepTimer + +| Attribute | Description | +| ------------- | -------------------------------------------------------------------------------------------------- | +| Applicable | Server Only | +| Meaning | The expiration time for metadata in cache, once it's reached the client would refresh the metadata | +| Unit | second | +| Value Range | 1-8640000 | +| Default Value | 7200 | + +### maxTmrCtrl + +| Attribute | Description | +| ------------- | ------------------------ | +| Applicable | Server and Client | +| Meaning | Maximum number of timers | +| Unit | None | +| Value Range | 8-2048 | +| Default Value | 512 | + +### offlineThreshold + +| Attribute | Description | +| ------------- | ----------------------------------------------------------------------------------------------------------------------------- | +| Applicable | Server Only | +| Meaning | The expiration time for dnode online status, once it's reached before receiving status from a node, the dnode becomes offline | +| Unit | second | +| Value Range | 5-7200000 | +| Default Value | 86400\*10 (i.e. 10 days) | + +## Performance Optimization Parameters + +### numOfThreadsPerCore + +| Attribute | Description | +| ------------- | ------------------------------------------- | +| Applicable | Server and Client | +| Meaning | The number of consumer threads per CPU core | +| Default Value | 1.0 | + +### ratioOfQueryThreads + +| Attribute | Description | +| ------------- | --------------------------------------------------------------------------------------------- | +| Applicable | Server Only | +| Meaning | Maximum number of query threads | +| Value Range | 0: Only one query thread
1: Same as number of CPU cores
2: two times of CPU cores | +| Default Value | 1 | +| Note | This value can be a float number, 0.5 means half of the CPU cores | + +### maxVgroupsPerDb + +| Attribute | Description | +| ------------- | ------------------------------------ | +| Applicable | Server Only | +| Meaning | Maximum number of vnodes for each DB | +| Value Range | 0-8192 | +| Default Value | | + +### maxTablesPerVnode + +| Attribute | Description | +| ------------- | -------------------------------------- | +| Applicable | Server Only | +| Meaning | Maximum number of tables in each vnode | +| Default Value | 1000000 | + +### minTablesPerVnode + +| Attribute | Description | +| ------------- | -------------------------------------- | +| Applicable | Server Only | +| Meaning | Minimum number of tables in each vnode | +| Default Value | 1000 | + +### tableIncStepPerVnode + +| Attribute | Description | +| ------------- | ------------------------------------------------------------------------------------------- | +| Applicable | Server Only | +| Meaning | When minTablesPerVnode is reached, the number of tables are allocated for a vnode each time | +| Default Value | 1000 | + +### maxNumOfOrderedRes + +| Attribute | Description | +| ------------- | ------------------------------------------- | +| Applicable | Server and Client | +| Meaning | Maximum number of rows ordered for a STable | +| Default Value | 100,000 | + +### mnodeEqualVnodeNum + +| Attribute | Description | +| ------------- | ----------------------------------------------------------------------------------------------- | +| Applicable | Server Only | +| Meaning | The number of vnodes whose system resources consumption are considered as equal to single mnode | +| Default Value | 4 | + +### numOfCommitThreads + +| Attribute | Description | +| ------------- | ----------------------------------------- | +| Applicable | Server Only | +| Meaning | Maximum of threads for committing to disk | +| Default Value | | + +## Compression Parameters + +### comp + +| Attribute | Description | +| ------------- | ------------------------------------------------------------------- | +| Applicable | Server Only | +| Meaning | Whether data is compressed | +| Value Range | 0: uncompressed, 1: One phase compression, 2: Two phase compression | +| Default Value | 2 | + +### tsdbMetaCompactRatio + +| Attribute | Description | +| ------------- | ------------------------------------------------------------------------------------------- | +| Meaning | The threshold for percentage of redundant in meta file to trigger compression for meta file | +| Value Range | 0: no compression forever, [1-100]: The threshold percentage | +| Default Value | 0 | + +### compressMsgSize + +| Attribute | Description | +| ------------- | -------------------------------------------------------------------------------- | +| Applicable | Server Only | +| Meaning | The threshold for message size to compress the message.. | +| Unit | bytes | +| Value Range | 0: already compress; >0: compress when message exceeds it; -1: always uncompress | +| Default Value | -1 | + +### compressColData + +| Attribute | Description | +| ------------- | ------------------------------------------------------------------------------------------------------------------- | +| Applicable | Server Only | +| Meaning | The threshold for size of column data to trigger compression for the query result | +| Unit | bytes | +| Value Range | 0: always compress; >0: only compress when the size of any column data exceeds the threshold; -1: always uncompress | +| Default Value | -1 | +| Note | available from version 2.3.0.0 | + +### lossyColumns + +| Attribute | Description | +| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | +| Applicable | Server Only | +| Meaning | The floating number types for lossy compression | +| Value Range | "": lossy compression is disabled
float: only for float
double: only for double
float \| double: for both float and double | +| Default Value | "" , i.e. disabled | + +### fPrecision + +| Attribute | Description | +| ------------- | ----------------------------------------------------------- | +| Applicable | Server Only | +| Meaning | Compression precision for float type | +| Value Range | 0.1 ~ 0.00000001 | +| Default Value | 0.00000001 | +| Note | The fractional part lower than this value will be discarded | + +### dPrecision + +| Attribute | Description | +| ------------- | ----------------------------------------------------------- | +| Applicable | Server Only | +| Meaning | Compression precision for double type | +| Value Range | 0.1 ~ 0.0000000000000001 | +| Default Value | 0.0000000000000001 | +| Note | The fractional part lower than this value will be discarded | + +## Continuous Query Parameters + +### stream + +| Attribute | Description | +| ------------- | ---------------------------------- | +| Applicable | Server Only | +| Meaning | Whether to enable continuous query | +| Value Range | 0: disabled
1: enabled | +| Default Value | 1 | + +### minSlidingTime + +| Attribute | Description | +| ------------- | -------------------------------------------------------- | +| Applicable | Server Only | +| Meaning | Minimum sliding time of time window | +| Unit | millisecond or microsecond , depending on time precision | +| Value Range | 10-1000000 | +| Default Value | 10 | + +### minIntervalTime + +| Attribute | Description | +| ------------- | --------------------------- | +| Applicable | Server Only | +| Meaning | Minimum size of time window | +| Unit | millisecond | +| Value Range | 1-1000000 | +| Default Value | 10 | + +### maxStreamCompDelay + +| Attribute | Description | +| ------------- | ------------------------------------------------ | +| Applicable | Server Only | +| Meaning | Maximum delay before starting a continuous query | +| Unit | millisecond | +| Value Range | 10-1000000000 | +| Default Value | 20000 | + +### maxFirstStreamCompDelay + +| Attribute | Description | +| ------------- | -------------------------------------------------------------------- | +| Applicable | Server Only | +| Meaning | Maximum delay time before starting a continuous query the first time | +| Unit | millisecond | +| Value Range | 10-1000000000 | +| Default Value | 10000 | + +### retryStreamCompDelay + +| Attribute | Description | +| ------------- | --------------------------------------------- | +| Applicable | Server Only | +| Meaning | Delay time before retrying a continuous query | +| Unit | millisecond | +| Value Range | 10-1000000000 | +| Default Value | 10 | + +### streamCompDelayRatio + +| Attribute | Description | +| ------------- | ------------------------------------------------------------------------ | +| Applicable | Server Only | +| Meaning | The delay ratio, with time window size as the base, for continuous query | +| Value Range | 0.1-0.9 | +| Default Value | 0.1 | + +:::info +To prevent system resource from being exhausted by multiple concurrent streams, a random delay is applied on each stream automatically. `maxFirstStreamCompDelay` is the maximum delay time before a continuous query is started the first time. `streamCompDelayRatio` is the ratio for calculating delay time, with the size of the time window as base. `maxStreamCompDelay` is the maximum delay time. The actual delay time is a random time not bigger than `maxStreamCompDelay`. If a continuous query fails, `retryStreamComDelay` is the delay time before retrying it, also not bigger than `maxStreamCompDelay`. + +::: + +## HTTP Parameters + +:::note +HTTP server had been provided by `taosd` prior to version 2.4.0.0, now is provided by `taosAdapter` after version 2.4.0.0. +The parameters described in this section are only application in versions prior to 2.4.0.0. If you are using any version from 2.4.0.0, please refer to [taosAdapter]](/reference/taosadapter/). + +::: + +### http + +| Attribute | Description | +| ------------- | ------------------------------ | +| Applicable | Server Only | +| Meaning | Whether to enable http service | +| Value Range | 0: disabled, 1: enabled | +| Default Value | 1 | + +### httpEnableRecordSql + +| Attribute | Description | +| ------------- | ------------------------------------------------------------------------- | +| Applicable | Server Only | +| Meaning | Whether to record the SQL invocation through REST interface | +| Default Value | 0: false; 1: true | +| Note | The resulting files, i.e. httpnote.0/httpnote.1, are located under logDir | + +### httpMaxThreads + +| Attribute | Description | +| ------------- | -------------------------------------------- | +| Applicable | Server Only | +| Meaning | The number of threads for RESTFul interface. | +| Default Value | 2 | + +### restfulRowLimit + +| Attribute | Description | +| ------------- | ------------------------------------------------------------ | +| Applicable | Server Only | +| Meaning | Maximum number of rows returned each time by REST interface. | +| Default Value | 10240 | +| Note | Maximum value is 10,000,000 | + +### httpDBNameMandatory + +| Attribute | Description | +| ------------- | ---------------------------------------- | +| Applicable | Server Only | +| Meaning | Whether database name is required in URL | +| Value Range | 0:not required, 1: required | +| Default Value | 0 | +| Note | From version 2.3.0.0 | + +## Log Parameters + +### logDir + +| Attribute | Description | +| ------------- | ----------------------------------- | +| Applicable | Server and Client | +| Meaning | The directory for writing log files | +| Default Value | /var/log/taos | + +### minimalLogDirGB + +| Attribute | Description | +| ------------- | -------------------------------------------------------------------------------------------------- | +| Applicable | Server and Client | +| Meaning | When the available disk space in logDir is below this threshold, writing to log files is suspended | +| Unit | GB | +| Default Value | 1.0 | + +### numOfLogLines + +| Attribute | Description | +| ------------- | ------------------------------------------ | +| Applicable | Server and Client | +| Meaning | Maximum number of lines in single log file | +| Default Value | 10,000,000 | + +### asyncLog + +| Attribute | Description | +| ------------- | ---------------------------- | +| Applicable | Server and Client | +| Meaning | The mode of writing log file | +| Value Range | 0: sync way; 1: async way | +| Default Value | 1 | + +### logKeepDays + +| Attribute | Description | +| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | +| Applicable | Server and Client | +| Meaning | The number of days for log files to be kept | +| Unit | day | +| Default Value | 0 | +| Note | When it's bigger than 0, the log file would be renamed to "taosdlog.xxx" in which "xxx" is the timestamp when the file is changed last time | + +### debugFlag + +| Attribute | Description | +| ------------- | --------------------------------------------------------- | +| Applicable | Server and Client | +| Meaning | Log level | +| Value Range | 131: INFO/WARNING/ERROR; 135: plus DEBUG; 143: plus TRACE | +| Default Value | 131 or 135, depending on the module | + +### mDebugFlag + +| Attribute | Description | +| ------------- | ------------------ | +| Applicable | Server Only | +| Meaning | Log level of mnode | +| Value Range | same as debugFlag | +| Default Value | 135 | + +### dDebugFlag + +| Attribute | Description | +| ------------- | ------------------ | +| Applicable | Server and Client | +| Meaning | Log level of dnode | +| Value Range | same as debugFlag | +| Default Value | 135 | + +### sDebugFlag + +| Attribute | Description | +| ------------- | ------------------------ | +| Applicable | Server and Client | +| Meaning | Log level of sync module | +| Value Range | same as debugFlag | +| Default Value | 135 | + +### wDebugFlag + +| Attribute | Description | +| ------------- | ----------------------- | +| Applicable | Server and Client | +| Meaning | Log level of WAL module | +| Value Range | same as debugFlag | +| Default Value | 135 | + +### sdbDebugFlag + +| Attribute | Description | +| ------------- | ---------------------- | +| Applicable | Server and Client | +| Meaning | logLevel of sdb module | +| Value Range | same as debugFlag | +| Default Value | 135 | + +### rpcDebugFlag + +| Attribute | Description | +| ------------- | ----------------------- | +| Applicable | Server and Client | +| Meaning | Log level of rpc module | +| Value Range | Same as debugFlag | +| Default Value | | + +### tmrDebugFlag + +| Attribute | Description | +| ------------- | ------------------------- | +| Applicable | Server and Client | +| Meaning | Log level of timer module | +| Value Range | Same as debugFlag | +| Default Value | | + +### cDebugFlag + +| Attribute | Description | +| ------------- | ------------------- | +| Applicable | Client Only | +| Meaning | Log level of Client | +| Value Range | Same as debugFlag | +| Default Value | | + +### jniDebugFlag + +| Attribute | Description | +| ------------- | ----------------------- | +| Applicable | Client Only | +| Meaning | Log level of jni module | +| Value Range | Same as debugFlag | +| Default Value | | + +### odbcDebugFlag + +| Attribute | Description | +| ------------- | ------------------------ | +| Applicable | Client Only | +| Meaning | Log level of odbc module | +| Value Range | Same as debugFlag | +| Default Value | | + +### uDebugFlag + +| Attribute | Description | +| ------------- | -------------------------- | +| Applicable | Server and Client | +| Meaning | Log level of common module | +| Value Range | Same as debugFlag | +| Default Value | | + +### httpDebugFlag + +| Attribute | Description | +| ------------- | ------------------------------------------- | +| Applicable | Server Only | +| Meaning | Log level of http module (prior to 2.4.0.0) | +| Value Range | Same as debugFlag | +| Default Value | | + +### mqttDebugFlag + +| Attribute | Description | +| ------------- | ------------------------ | +| Applicable | Server Only | +| Meaning | Log level of mqtt module | +| Value Range | Same as debugFlag | +| Default Value | | + +### monitorDebugFlag + +| Attribute | Description | +| ------------- | ------------------------------ | +| Applicable | Server Only | +| Meaning | Log level of monitoring module | +| Value Range | Same as debugFlag | +| Default Value | | + +### qDebugFlag + +| Attribute | Description | +| ------------- | ------------------------- | +| Applicable | Server and Client | +| Meaning | Log level of query module | +| Value Range | Same as debugFlag | +| Default Value | | + +### vDebugFlag + +| Attribute | Description | +| ------------- | ------------------ | +| Applicable | Server and Client | +| Meaning | Log level of vnode | +| Value Range | Same as debugFlag | +| Default Value | | + +### tsdbDebugFlag + +| Attribute | Description | +| ------------- | ------------------------ | +| Applicable | Server Only | +| Meaning | Log level of TSDB module | +| Value Range | Same as debugFlag | +| Default Value | | + +### cqDebugFlag + +| Attribute | Description | +| ------------- | ------------------------------------ | +| Applicable | Server and Client | +| Meaning | Log level of continuous query module | +| Value Range | Same as debugFlag | +| Default Value | | + +## Client Only + +### maxSQLLength + +| Attribute | Description | +| ------------- | -------------------------------------- | +| Applicable | Client Only | +| Meaning | Maximum length of single SQL statement | +| Unit | bytes | +| Value Range | 65480-1048576 | +| Default Value | 1048576 | + +### tscEnableRecordSql + +| Attribute | Description | +| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | +| Meaning | Whether to record SQL statements in file | +| Value Range | 0: false, 1: true | +| Default Value | 0 | +| Note | The generated files are named as "tscnote-xxxx.0/tscnote-xxx.1" in which "xxxx" is the pid of the client, and located at same place as client log | + +### maxBinaryDisplayWidth + +| Attribute | Description | +| ------------- | --------------------------------------------------------------------------------------------------- | +| Meaning | Maximum display width of binary and nchar in taos shell. Anything beyond this limit would be hidden | +| Value Range | 5 - | +| Default Value | 30 | + +:::info +If the length of value exceeds `maxBinaryDisplayWidth`, then the actual display width is max(column name, maxBinaryDisplayLength); otherwise the actual display width is max(length of column name, length of column value). This parameter can also be changed dynamically using `set max_binary_display_width ` in TDengine CLI `taos`. + +::: + +### maxWildCardsLength + +| Attribute | Description | +| ------------- | ----------------------------------------------------- | +| Meaning | The maximum length for wildcard string used with LIKE | +| Unit | bytes | +| Value Range | 0-16384 | +| Default Value | 100 | +| Note | From version 2.1.6.1 | + +### clientMerge + +| Attribute | Description | +| ------------- | --------------------------------------------------- | +| Meaning | Whether to filter out duplicate data on client side | +| Value Range | 0: false; 1: true | +| Default Value | 0 | +| Note | From version 2.3.0.0 | + +### maxRegexStringLen + +| Attribute | Description | +| ------------- | ------------------------------------ | +| Meaning | Maximum length of regular expression | +| Value Range | [128, 16384] | +| Default Value | 128 | +| Note | From version 2.3.0.0 | + +## Other Parameters + +### enableCoreFile + +| Attribute | Description | +| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Applicable | Server and Client | +| Meaning | Whether to generate core file when server crashes | +| Value Range | 0: false, 1: true | +| Default Value | 1 | +| Note | The core file is generated under root directory `systemctl start taosd` is used to start, or under the working directory if `taosd` is started directly on Linux Shell. | diff --git a/docs-en/14-reference/12-directory.md b/docs-en/14-reference/12-directory.md new file mode 100644 index 0000000000000000000000000000000000000000..dbdba2b715bb41baf9b70dce91a3065e585d0434 --- /dev/null +++ b/docs-en/14-reference/12-directory.md @@ -0,0 +1,40 @@ +--- +title: File directory structure +description: "TDengine installation directory description" +--- + +After TDengine is installed, the following directories or files will be created in the system by default. + +| directory/file | description | +| ------------------------- | -------------------------------------------------------------------- | +| /usr/local/taos/bin | The TDengine executable directory. The executable files are soft-linked to the /usr/bin directory. | +| /usr/local/taos/driver | The TDengine dynamic link library directory. It is soft-linked to the /usr/lib directory. | +| /usr/local/taos/examples | The TDengine various language application examples directory. | +| /usr/local/taos/include | The header files for TDengine's external C interface. | +| /etc/taos/taos.cfg | TDengine default [configuration file] | +| /var/lib/taos | TDengine's default data file directory. The location can be changed via [configuration file]. | +| /var/log/taos | TDengine default log file directory. The location can be changed via [configure file]. | + +## Executable files + +All executable files of TDengine are in the _/usr/local/taos/bin_ directory by default. These include. + +- _taosd_: TDengine server-side executable files +- _taos_: TDengine CLI executable +- _taosdump_: data import and export tool +- _taosBenchmark_: TDengine testing tool +- _remove.sh_: script to uninstall TDengine, please execute it carefully, link to the **rmtaos** command in the /usr/bin directory. Will remove the TDengine installation directory `/usr/local/taos`, but will keep `/etc/taos`, `/var/lib/taos`, `/var/log/taos` +- _taosadapter_: server-side executable that provides RESTful services and accepts writing requests from a variety of other softwares +- _tarbitrator_: provides arbitration for two-node cluster deployments +- _run_taosd_and_taosadapter.sh_: script to start both taosd and taosAdapter +- _TDinsight.sh_: script to download TDinsight and install it +- _set_core.sh_: script for setting up the system to generate core dump files for easy debugging +- _taosd-dump-cfg.gdb_: script to facilitate debugging of taosd's gdb execution. + +:::note +taosdump after version 2.4.0.0 require taosTools as a standalone installation. A few version taosBenchmark is include in taosTools too. +::: + +:::tip +You can configure different data directories and log directories by modifying the system configuration file `taos.cfg`. +::: diff --git a/docs-en/14-reference/13-schemaless/13-schemaless.md b/docs-en/14-reference/13-schemaless/13-schemaless.md new file mode 100644 index 0000000000000000000000000000000000000000..d9ce9b434dd14a89d243b2ed629f3fde64e6aba0 --- /dev/null +++ b/docs-en/14-reference/13-schemaless/13-schemaless.md @@ -0,0 +1,157 @@ +--- +title: Schemaless Writing +description: "The Schemaless write method eliminates the need to create super tables/sub tables in advance and automatically creates the storage structure corresponding to the data as it is written to the interface." +--- + +In IoT applications, many data items are often collected for intelligent control, business analysis, device monitoring, etc. Due to the version upgrade of the application logic, or the hardware adjustment of the device itself, the data collection items may change more frequently. To facilitate the data logging work in such cases, TDengine starting from version 2.2.0.0, it provides a series of interfaces to the schemaless writing method, which eliminates the need to create super tables/sub tables in advance and automatically creates the storage structure corresponding to the data as the data is written to the interface. And when necessary, Schemaless writing will automatically add the required columns to ensure that the data written by the user is stored correctly. + +The schemaless writing method creates super tables and their corresponding sub-tables completely indistinguishable from the super tables and sub-tables created directly via SQL. You can write data directly to them via SQL statements. Note that the names of tables created by schemaless writing are based on fixed mapping rules for tag values, so they are not explicitly ideographic and lack readability. + +## Schemaless Writing Line Protocol + +TDengine's schemaless writing line protocol supports to be compatible with InfluxDB's Line Protocol, OpenTSDB's telnet line protocol, and OpenTSDB's JSON format protocol. However, when using these three protocols, you need to specify in the API the standard of the parsing protocol to be used for the input content. + +For the standard writing protocols of InfluxDB and OpenTSDB, please refer to the documentation of each protocol. The following is a description of TDengine's extended protocol, based on InfluxDB's line protocol first. They allow users to control the (super table) schema more granularly. + +With the following formatting conventions, Schemaless writing uses a single string to express a data row (multiple rows can be passed into the writing API at once to enable bulk writing). + +```json +measurement,tag_set field_set timestamp +``` + +where : + +- measurement will be used as the data table name. It will be separated from tag_set by a comma. +- tag_set will be used as tag data in the format `=,=`, i.e. multiple tags' data can be separated by a comma. It is separated from field_set by space. +- field_set will be used as normal column data in the format of `=,=`, again using a comma to separate multiple normal columns of data. It is separated from the timestamp by space. +- The timestamp is the primary key corresponding to the data in this row. + +All data in tag_set is automatically converted to the NCHAR data type and does not require double quotes ("). + +In the schemaless writing data line protocol, each data item in the field_set needs to be described with its data type. Let's explain in detail: + +- If there are English double quotes on both sides, it indicates the BINARY(32) type. For example, `"abc"`. +- If there are double quotes on both sides and an L prefix, it means NCHAR(32) type. For example, `L"error message"`. +- Spaces, equal signs (=), commas (,), and double quotes (") need to be escaped with a backslash (\) in front. (All refer to the ASCII character) +- Numeric types will be distinguished from data types by the suffix. + +| **Serial number** | **Postfix** | **Mapping type** | **Size (bytes)** | +| -------- | -------- | ------------ | -------------- | +| 1 | none or f64 | double | 8 | +| 2 | f32 | float | 4 | +| 3 | i8 | TinyInt | 1 | +| 4 | i16 | SmallInt | 2 | +| 5 | i32 | Int | 4 | +| 6 | i64 or i | Bigint | 8 | + +- `t`, `T`, `true`, `True`, `TRUE`, `f`, `F`, `false`, and `False` will be handled directly as BOOL types. + +For example, the following data rows indicate that the t1 label is "3" (NCHAR), the t2 label is "4" (NCHAR), and the t3 label is "t3" to the super table named `st` labeled "t3" (NCHAR), write c1 column as 3 (BIGINT), c2 column as false (BOOL), c3 column is "passit" (BINARY), c4 column is 4 (DOUBLE), and the primary key timestamp is 1626006833639000000 in one row. + +```json +st,t1=3,t2=4,t3=t3 c1=3i64,c3="passit",c2=false,c4=4f64 1626006833639000000 +``` + +Note that if the wrong case is used when describing the data type suffix, or if the wrong data type is specified for the data, it may cause an error message and cause the data to fail to be written. + +## Main processing logic for schemaless writing + +Schemaless writes process row data according to the following principles. + +1. You can use the following rules to generate the sub-table names: first, combine the measurement name and the key and value of the label into the next string: + +```json +"measurement,tag_key1=tag_value1,tag_key2=tag_value2" +``` + +Note that tag_key1, tag_key2 are not the original order of the tags entered by the user but the result of using the tag names in ascending order of the strings. Therefore, tag_key1 is not the first tag entered in the line protocol. +The string's MD5 hash value "md5_val" is calculated after the ranking is completed. The calculation result is then combined with the string to generate the table name: "t_md5_val". "t*" is a fixed prefix that every table generated by this mapping relationship has. 2. + +2. If the super table obtained by parsing the line protocol does not exist, this super table is created. +If the sub-table obtained by the parse line protocol does not exist, Schemaless creates the sub-table according to the sub-table name determined in steps 1 or 2. 4. +4. If the specified tag or regular column in the data row does not exist, the corresponding tag or regular column is added to the super table (only incremental). +5. If there are some tag columns or regular columns in the super table that are not specified to take values in a data row, then the values of these columns are set to NULL. +6. For BINARY or NCHAR columns, if the length of the value provided in a data row exceeds the column type limit, the maximum length of characters allowed to be stored in the column is automatically increased (only incremented and not decremented) to ensure complete preservation of the data. +7. If the specified data sub-table already exists, and the specified tag column takes a value different from the saved value this time, the value in the latest data row overwrites the old tag column take value. +8. Errors encountered throughout the processing will interrupt the writing process and return an error code. + +:::tip +All processing logic of schemaless will still follow TDengine's underlying restrictions on data structures, such as the total length of each row of data cannot exceed +16k bytes. See [TAOS SQL Boundary Limits](/taos-sql/limit) for specific constraints in this area. +::: + +## Time resolution recognition + +Three specified modes are supported in the schemaless writing process, as follows: + +| **Serial** | **Value** | **Description** | +| -------- | ------------------- | ------------------------------- | +| 1 | SML_LINE_PROTOCOL | InfluxDB Line Protocol | +| 2 | SML_TELNET_PROTOCOL | OpenTSDB Text Line Protocol | | 2 | SML_TELNET_PROTOCOL | OpenTSDB Text Line Protocol +| 3 | SML_JSON_PROTOCOL | JSON protocol format | + +In the SML_LINE_PROTOCOL parsing mode, the user is required to specify the time resolution of the input timestamp. The available time resolutions are shown in the following table. + +| **Serial Number** | **Time Resolution Definition** | **Meaning** | +| -------- | --------------------------------- | -------------- | +| 1 | TSDB_SML_TIMESTAMP_NOT_CONFIGURED | Not defined (invalid) | +| 2 | TSDB_SML_TIMESTAMP_HOURS | hour | +| 3 | TSDB_SML_TIMESTAMP_MINUTES | MINUTES +| 4 | TSDB_SML_TIMESTAMP_SECONDS | SECONDS +| 5 | TSDB_SML_TIMESTAMP_MILLI_SECONDS | milliseconds +| 6 | TSDB_SML_TIMESTAMP_MICRO_SECONDS | microseconds +| 7 | TSDB_SML_TIMESTAMP_NANO_SECONDS | nanoseconds | + +In SML_TELNET_PROTOCOL and SML_JSON_PROTOCOL modes, the time precision is determined based on the length of the timestamp (in the same way as the OpenTSDB standard operation), and the user-specified time resolution is ignored at this point. + +## Data schema mapping rules + +This section describes how data for line protocols are mapped to data with a schema. The data measurement in each line protocol is mapped to +The tag name in tag_set is the name of the tag in the data schema, and the name in field_set is the column's name. The following data is used as an example to illustrate the mapping rules. + +```json +st,t1=3,t2=4,t3=t3 c1=3i64,c3="passit",c2=false,c4=4f64 1626006833639000000 +``` + +The row data mapping generates a super table: `st`, which contains three labels of type NCHAR: t1, t2, t3. Five data columns are ts (timestamp), c1 (bigint), c3 (binary), c2 (bool), c4 (bigint). The mapping becomes the following SQL statement. + +```json +create stable st (_ts timestamp, c1 bigint, c2 bool, c3 binary(6), c4 bigint) tags(t1 nchar(1), t2 nchar(1), t3 nchar(2)) +``` + +## Data schema change handling + +This section describes the impact on the data schema for different line protocol data writing cases. + +When writing to an explicitly identified field type using the line protocol, subsequent changes to the field's type definition will result in an explicit data schema error, i.e., will trigger a write API report error. As shown below, the + +```json +st,t1=3,t2=4,t3=t3 c1=3i64,c3="passit",c2=false,c4=4 1626006833639000000 +st,t1=3,t2=4,t3=t3 c1=3i64,c3="passit",c2=false,c4=4i 1626006833640000000 +``` + +The data type mapping in the first row defines column c4 as DOUBLE, but the data in the second row is declared as BIGINT by the numeric suffix, which triggers a parsing error with schemaless writing. + +If the line protocol before the column declares the data column as BINARY, the subsequent one requires a longer binary length, which triggers a super table schema change. + +```json +st,t1=3,t2=4,t3=t3 c1=3i64,c5="pass" 1626006833639000000 +st,t1=3,t2=4,t3=t3 c1=3i64,c5="passit" 1626006833640000000 +``` + +The first line of the line protocol parsing will declare column c5 is a BINARY(4) field, the second line data write will extract column c5 is still a BINARY column. Still, its width is 6, then you need to increase the width of the BINARY field to be able to accommodate the new string. + +```json +st,t1=3,t2=4,t3=t3 c1=3i64 1626006833639000000 +st,t1=3,t2=4,t3=t3 c1=3i64,c6="passit" 1626006833640000000 +``` + +The second line of data has an additional column c6 of type BINARY(6) compared to the first row. Then a column c6 of type BINARY(6) is automatically added at this point. + +## Write integrity + +TDengine provides idempotency guarantees for data writing, i.e., you can repeatedly call the API to write data with errors. However, it does not give atomicity guarantees for writing multiple rows of data. During the process of writing numerous rows of data in one batch, some data will be written successfully, and some data will fail. + +## Error code + +If it is an error in the data itself during the schemaless writing process, the application will get `TSDB_CODE_TSC_LINE_SYNTAX_ERROR` error message, which indicates that the error occurred in writing. The other error codes are consistent with the TDengine and can be obtained via the `taos_errstr()` to get the specific cause of the error. diff --git a/docs-en/14-reference/13-schemaless/_category_.yml b/docs-en/14-reference/13-schemaless/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..416b41f73cb3884421c911189a15e522493c70e7 --- /dev/null +++ b/docs-en/14-reference/13-schemaless/_category_.yml @@ -0,0 +1 @@ +label: Schemaless writing diff --git a/docs-en/14-reference/_category_.yml b/docs-en/14-reference/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..5f5466532be79153d42da0907df6336439593601 --- /dev/null +++ b/docs-en/14-reference/_category_.yml @@ -0,0 +1 @@ +label: Reference diff --git a/docs-en/14-reference/_collectd.mdx b/docs-en/14-reference/_collectd.mdx new file mode 100644 index 0000000000000000000000000000000000000000..1f57d883eec9feadc3cc460bf968b0dd43fedfe8 --- /dev/null +++ b/docs-en/14-reference/_collectd.mdx @@ -0,0 +1,84 @@ +### Configuring taosAdapter + +To configure taosAdapter to receive collectd data. + +- Enable the configuration item in the taosAdapter configuration file (default location is /etc/taos/taosadapter.toml) + +``` +... +[opentsdb_telnet] +enable = true +maxTCPConnections = 250 +tcpKeepAlive = false +dbs = ["opentsdb_telnet", "collectd", "icinga2", "tcollector"] +ports = [6046, 6047, 6048, 6049] +user = "root" +password = "taosdata" +... +``` + +The default database name written by taosAdapter is `collectd`. You can also modify the taosAdapter configuration file dbs entry to specify a different name. user and password are the values configured by the actual TDengine. After changing the configuration file, you need to restart the taosAdapter. + +- You can also enable the taosAdapter to receive collectd data by using the taosAdapter command-line parameters or by setting environment variables. + +### Configure collectd +#collectd +collectd uses a plugin mechanism to write the collected monitoring data to different data storage software in various forms. tdengine supports both direct collection plugins and write_tsdb plugins. + +#### is configured to receive data from the direct collection plugin + +Modify the relevant configuration items in the collectd configuration file (default location /etc/collectd/collectd.conf). + +```text +LoadPlugin network + + Server "" "" + +``` + +where fills in the server's domain name or IP address running taosAdapter. fills in the port that taosAdapter uses to receive collectd data (default is 6045). + +An example is as follows. + +```text +LoadPlugin network + +``` + +#### Configure write_tsdb plugin data + +Modify the relevant configuration items in the collectd configuration file (default location /etc/collectd/collectd.conf). + +```text +LoadPlugin write_tsdb + + + Host "" + Port "" + ... + + +``` + +Where fills in the server's domain name or IP address running taosAdapter. Fill in the data that taosAdapter uses to receive the collectd write_tsdb plugin (default is 6047). + +```text +LoadPlugin write_tsdb + + + Host "127.0.0.1" + Port "6047" + HostTags "status=production" + StoreRates false + AlwaysAppendDS false + +``` + +Then restart collectd. + +``` +systemctl restart collectd +``` diff --git a/docs-en/14-reference/_icinga2.mdx b/docs-en/14-reference/_icinga2.mdx new file mode 100644 index 0000000000000000000000000000000000000000..0a2bf52c2796717e9fd7afcccc7cd8f75626fdd9 --- /dev/null +++ b/docs-en/14-reference/_icinga2.mdx @@ -0,0 +1,46 @@ + + +### Configuring taosAdapter + +To configure taosAdapter to receive icinga2 data. + +- Enable the configuration item in the taosAdapter configuration file (default location /etc/taos/taosadapter.toml) + +``` +... +[opentsdb_telnet] +enable = true +maxTCPConnections = 250 +tcpKeepAlive = false +dbs = ["opentsdb_telnet", "collectd", "icinga2", "tcollector"] +ports = [6046, 6047, 6048, 6049] +user = "root" +password = "taosdata" +... +``` + +The default database name written by the taosAdapter is `icinga2`. You can also modify the taosAdapter configuration file dbs entry to specify a different name. user and password are the values configured by the actual TDengine. You need to restart the taosAdapter after modification. + +- You can also enable taosAdapter to receive icinga2 data by using the taosAdapter command-line parameters or setting environment variables. + +### Configure icinga3 + +- Enable opentsdb-writer for icinga2 (refer to the link https://icinga.com/docs/icinga-2/latest/doc/14-features/#opentsdb-writer) +- Modify the configuration file `/etc/icinga2/features-enabled/opentsdb.conf` by filling in as the domain name or IP address of the server running taosAdapter and as the corresponding port on which taosAdapter supports receiving icinga2 data (default is 6048) + +``` +object OpenTsdbWriter "opentsdb" { + host = "" + port = +} +``` + +Example file: + +``` +object OpenTsdbWriter "opentsdb" { + host = "127.0.0.1" + port = 6048 +} +``` + diff --git a/docs-en/14-reference/_prometheus.mdx b/docs-en/14-reference/_prometheus.mdx new file mode 100644 index 0000000000000000000000000000000000000000..0940e4adb20f008ac190c3ea105466236889fec8 --- /dev/null +++ b/docs-en/14-reference/_prometheus.mdx @@ -0,0 +1,31 @@ +Configuring Prometheus is done by editing the Prometheus configuration file prometheus.yml (default location `/etc/prometheus/prometheus.yml`). + +### Configuring third-party database addresses + +Point the `remote_read url` and `remote_write url` to the domain name or IP address of the server running the taosAdapter service, the REST service port (taosAdapter uses 6041 by default), and the name of the database you want to write to TDengine, and ensure that the corresponding URL form as follows. + +- remote_read url : `http://:/prometheus/v1/remote_read/` +- remote_write url : `http://:/prometheus/v1/remote_write/` + +### Configure Basic authentication + +- username: +- password: + +### Example configuration of remote_write and remote_read related sections in prometheus.yml file + +```yaml +remote_write: + - url: "http://localhost:6041/prometheus/v1/remote_write/prometheus_data" + basic_auth: + username: root + password: taosdata + +remote_read: + - url: "http://localhost:6041/prometheus/v1/remote_read/prometheus_data" + basic_auth: + username: root + password: taosdata + remote_timeout: 10s + read_recent: true +``` diff --git a/docs-en/14-reference/_statsd.mdx b/docs-en/14-reference/_statsd.mdx new file mode 100644 index 0000000000000000000000000000000000000000..d0721a16fb575ef22f04ad6cb416a31851d6cff5 --- /dev/null +++ b/docs-en/14-reference/_statsd.mdx @@ -0,0 +1,55 @@ +### Configuring taosAdapter + +To configure taosAdapter to receive StatsD data. + +- Enable the configuration item in the taosAdapter configuration file (default location /etc/taos/taosadapter.toml) + +``` +... +[statsd] +enable = true +port = 6044 +db = "statsd" +user = "root" +password = "taosdata" +worker = 10 +gatherInterval = "5s" +protocol = "udp" +maxTCPConnections = 250 +tcpKeepAlive = false +allowPendingMessages = 50000 +deleteCounters = true +deleteGauges = true +deleteSets = true +deleteTimings = true +... +``` + +The default database name written by taosAdapter is `statsd`. To specify a different name, you can also modify the taosAdapter configuration file db entry. user and password fill in the actual TDengine configuration values. After changing the configuration file, you need to restart the taosAdapter. + +- You can also enable taosAdapter to receive StatsD data by using the taosAdapter command-line parameters or setting environment variables. + +### Configuring StatsD + +To use StatsD, you need to download its [source code](https://github.com/statsd/statsd). Please refer to the example file `exampleConfig.js` in the root directory of the source download to modify the configuration file. In , please fill in the domain name or IP address of the server running taosAdapter, and , please fill in the port where taosAdapter receives StatsD data (default is 6044). + +``` +backends section add ". /backends/repeater" +Add { host:'', port: } to repeater section +``` + +Example configuration file. + +``` +{ +port: 8125 +, backends: [". /backends/repeater"] +, repeater: [{ host: '127.0.0.1', port: 6044}] +} +``` + +Start StatsD after adding the following (assuming the config file is modified to config.js) + +``` +node stats.js config.js & +``` diff --git a/docs-en/14-reference/_tcollector.mdx b/docs-en/14-reference/_tcollector.mdx new file mode 100644 index 0000000000000000000000000000000000000000..85794d54007b70acf205b1bbc897cec1d0c4f824 --- /dev/null +++ b/docs-en/14-reference/_tcollector.mdx @@ -0,0 +1,81 @@ +### Configuring taosAdapter + +To configure taosAdapter to receive TCollector data. + +- Enable the configuration item in the taosAdapter configuration file (default location /etc/taos/taosadapter.toml) + +``` +... +[opentsdb_telnet] +enable = true +maxTCPConnections = 250 +tcpKeepAlive = false +dbs = ["opentsdb_telnet", "collectd", "icinga2", "tcollector"] +ports = [6046, 6047, 6048, 6049] +user = "root" +password = "taosdata" +... +``` + +The taosAdapter writes to the database with the default name `tcollector`. You can also modify the taosAdapter configuration file dbs entry to specify a different name. user and password fill in the actual TDengine configuration values. After changing the configuration file, you need to restart the taosAdapter. + +- You can also enable taosAdapter to receive tcollector data by using the taosAdapter command-line parameters or setting environment variables. + +### Configuring TCollector + +To use TCollector, you need to download its [source code](https://github.com/OpenTSDB/tcollector). Its configuration items are in its source code. Note: TCollector differs significantly from version to version, so here is an example of the latest code for the current master branch (git commit: 37ae920). + +Modify the contents of the `collectors/etc/config.py` and `tcollector.py` files. Change the address of the OpenTSDB host to the domain name or IP address of the server where taosAdapter is deployed, and change the port to the port that taosAdapter supports TCollector on (default is 6049). + +Example of git diff output of source code changes. + +``` +index e7e7a1c..ec3e23c 100644 +--- a/collectors/etc/config.py ++++ b/collectors/etc/config.py +@@ -59,13 +59,13 @@ def get_defaults(): + 'http_password': False, + 'reconnectinterval': 0, + 'http_username': False, +- 'port': 4242, ++ 'port': 6049, + 'pidfile': '/var/run/tcollector.pid', + 'http': False, + 'http_api_path': "api/put", + 'tags': [], + 'remove_inactive_collectors': False, +- 'host': '', ++ 'host': '127.0.0.1', + 'logfile': '/var/log/tcollector.log', + 'cdir': default_cdir, + 'ssl': False, +diff --git a/tcollector.py b/tcollector.py +index 21f9b23..4c71ba2 100755 +--- a/tcollector.py ++++ b/tcollector.py +@@ -64,7 +64,7 @@ ALIVE = True + # exceptions, something is not right and tcollector will shutdown. + # Hopefully some kind of supervising daemon will then restart it. + MAX_UNCAUGHT_EXCEPTIONS = 100 +-DEFAULT_PORT = 4242 ++DEFAULT_PORT = 6049 + MAX_REASONABLE_TIMESTAMP = 2209212000 # Good until Tue 3 Jan 14:00:00 GMT 2040 + # How long to wait for datapoints before assuming + # a collector is dead and restarting it +@@ -943,13 +943,13 @@ def parse_cmdline(argv): + 'http_password': False, + 'reconnectinterval': 0, + 'http_username': False, +- 'port': 4242, ++ 'port': 6049, + 'pidfile': '/var/run/tcollector.pid', + 'http': False, + 'http_api_path': "api/put", + 'tags': [], + 'remove_inactive_collectors': False, +- 'host': '', ++ 'host': '127.0.0.1', + 'logfile': '/var/log/tcollector.log', + 'cdir': default_cdir, + 'ssl': False, +``` diff --git a/docs-en/14-reference/_telegraf.mdx b/docs-en/14-reference/_telegraf.mdx new file mode 100644 index 0000000000000000000000000000000000000000..e32fb256936a5f2c00bbb3f37529e895d260fc5c --- /dev/null +++ b/docs-en/14-reference/_telegraf.mdx @@ -0,0 +1,26 @@ + +In the Telegraf configuration file (default location `/etc/telegraf/telegraf.conf`) add an `outputs.http` section. + +``` +[[outputs.http]] + url = "http://:/influxdb/v1/write?db=" + ... + username = "" + password = "" + ... +``` + +Where please fill in the server's domain name or IP address running the taosAdapter service. please fill in the port of the REST service (default is 6041). and please fill in the actual configuration of the currently running TDengine. And please fill in the database name where you want to store Telegraf data in TDengine. + +An example is as follows. + +``` +[[outputs.http]] + url = "http://127.0.0.1:6041/influxdb/v1/write?db=telegraf" + method = "POST" + timeout = "5s" + username = "root" + password = "taosdata" + data_format = "influx" + influx_max_line_bytes = 250 +``` diff --git a/docs-en/14-reference/index.md b/docs-en/14-reference/index.md new file mode 100644 index 0000000000000000000000000000000000000000..89f675902d01ba2d2c1b322408c372429d6bda1c --- /dev/null +++ b/docs-en/14-reference/index.md @@ -0,0 +1,12 @@ +--- +title: Reference +--- + +The reference guide is the detailed introduction to TDengine, various TDengine's connectors in different languages, and the tools that come with it. + +```mdx-code-block +import DocCardList from '@theme/DocCardList'; +import {useCurrentSidebarCategory} from '@docusaurus/theme-common'; + + +``` \ No newline at end of file diff --git a/docs-en/14-reference/taosAdapter-architecture.png b/docs-en/14-reference/taosAdapter-architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..08a9018553aae6f86b42d127b372d0cecfa9bdf8 Binary files /dev/null and b/docs-en/14-reference/taosAdapter-architecture.png differ diff --git a/docs-en/20-third-party/01-grafana.mdx b/docs-en/20-third-party/01-grafana.mdx new file mode 100644 index 0000000000000000000000000000000000000000..c1bfd4a96a4576df8570d8b480d5c2afe47e20b8 --- /dev/null +++ b/docs-en/20-third-party/01-grafana.mdx @@ -0,0 +1,103 @@ +--- +sidebar_label: Grafana +title: Grafana +--- + +TDengine can be quickly integrated with the open-source data visualization system [Grafana](https://www.grafana.com/) to build a data monitoring and alerting system. The whole process does not require any code development. And you can visualize the contents of the data tables in TDengine on a DashBoard. + +You can learn more about using the TDengine plugin on [GitHub](https://github.com/taosdata/grafanaplugin/blob/master/README.md). + +## Prerequisites + +In order for Grafana to add the TDengine data source successfully, the following preparations are required: +1. The TDengine cluster is deployed and functioning properly +2. taosAdapter is installed and running properly. Please refer to the taosAdapter manual for details. + +## Installing Grafana + +TDengine currently supports Grafana versions 7.0 and above. Users can go to the Grafana official website to download the installation package and execute the installation according to the current operating system. The download address is as follows: . + +## Configuring Grafana + +You can download The Grafana plugin for TDengine from . The current latest version is 3.1.4. + +Recommend using the [``grafana-cli`` command-line tool](https://grafana.com/docs/grafana/latest/administration/cli/) for plugin installation. + +``bash +sudo -u grafana grafana-cli \ + --pluginUrl https://github.com/taosdata/grafanaplugin/releases/download/v3.1.4/tdengine-datasource-3.1.4.zip \ + plugins install tdengine-datasource +``` + +Or download it locally and extract it to the Grafana plugin directory. + +```bash +GF_VERSION=3.1.4 +wget https://github.com/taosdata/grafanaplugin/releases/download/v$GF_VERSION/tdengine-datasource-$GF_VERSION.zip +``` + +Take CentOS 7.2 for example, extract the plugin package to /var/lib/grafana/plugins directory, and restart grafana. + +```bash +sudo unzip tdengine-datasource-$GF_VERSION.zip -d /var/lib/grafana/plugins/ +``` + +Grafana versions 7.3+ / 8.x do signature checks on plugins, so you also need to add the following line to the grafana.ini file to use the plugin correctly. + +```ini +[plugins] +allow_loading_unsigned_plugins = tdengine-datasource +``` + +The TDengine plugin can be automatically installed and set up using the following environment variable settings in a Docker environment. + +```bash +GF_INSTALL_PLUGINS=https://github.com/taosdata/grafanaplugin/releases/download/v3.1.4/tdengine-datasource-3.1.4.zip;tdengine- datasource +GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS=tdengine-datasource +``` + +## Using Grafana + +### Configuring Data Sources + +Users can log in to the Grafana server (username/password: admin/admin) directly through the URL `http://localhost:3000` and add a datasource through `Configuration -> Data Sources` on the left side, as shown in the following figure. + +![img](./grafana/add_datasource1.jpg) + +Click `Add data source` to enter the Add data source page, and enter TDengine in the query box to add it, as shown in the following figure. + +![img](./grafana/add_datasource2.jpg) + +Enter the datasource configuration page, and follow the default prompts to modify the corresponding configuration. + +![img](./grafana/add_datasource3.jpg) + +- Host: IP address of the server where the components of the TDengine cluster provide REST service (offered by taosd before 2.4 and by taosAdapter since 2.4) and the port number of the TDengine REST service (6041), by default use `http://localhost:6041`. +- User: TDengine user name. +- Password: TDengine user password. + +Click `Save & Test` to test. Follows are a success. + +![img](./grafana/add_datasource4.jpg) + +### Create Dashboard + +Go back to the main interface to create the Dashboard, click Add Query to enter the panel query page: + +![img](./grafana/create_dashboard1.jpg) + +As shown above, select the `TDengine` data source in the `Query` and enter the corresponding SQL in the query box below for query. + +- INPUT SQL: enter the statement to be queried (the result set of the SQL statement should be two columns and multiple rows), for example: `select avg(mem_system) from log.dn where ts >= $from and ts < $to interval($interval)`, where, from, to and interval are built-in variables of the TDengine plugin, indicating the range and time interval of queries fetched from the Grafana plugin panel. In addition to the built-in variables, ` custom template variables are also supported. +- ALIAS BY: This allows you to set the current query alias. +- GENERATE SQL: Clicking this button will automatically replace the corresponding variables and generate the final executed statement. + +Follow the default prompt to query the average system memory usage for the specified interval on the server where the current TDengine deployment is located as follows. + +![img](./grafana/create_dashboard2.jpg) + +> For more information on how to use Grafana to create the appropriate monitoring interface and for more details on using Grafana, refer to the official Grafana [documentation](https://grafana.com/docs/). + +### Importing the Dashboard + +In version 2.3.3.0 and above, you can import the TDinsight Dashboard (Grafana Dashboard ID: [15168](https://grafana.com/grafana/dashboards/15167)) as a monitoring visualization tool for TDengine clusters. You can find installation and usage instructions in the TDinsight User Manual (/reference/tdinsight/). diff --git a/docs-en/20-third-party/02-prometheus.md b/docs-en/20-third-party/02-prometheus.md new file mode 100644 index 0000000000000000000000000000000000000000..0fb41a169632a9d2775dfb3f48ea7254cf9a8558 --- /dev/null +++ b/docs-en/20-third-party/02-prometheus.md @@ -0,0 +1,91 @@ +--- +sidebar_label: Prometheus +title: Prometheus writing and reading +--- + +import Prometheus from "../14-reference/_prometheus.mdx" + +Prometheus is a widespread open-source monitoring and alerting system. Prometheus joined the Cloud Native Computing Foundation (CNCF) in 2016 as the second incubated project after Kubernetes, which has a very active developer and user community. + +Prometheus provides `remote_write` and `remote_read` interfaces to leverage other database products as its storage engine. To enable users of the Prometheus ecosystem to take advantage of TDengine's efficient writing and querying, TDengine also provides support for these two interfaces. + +Prometheus data can be stored in TDengine via the `remote_write` interface with proper configuration. Data stored in TDengine can be queried via the `remote_read` interface, taking full advantage of TDengine's efficient storage query performance and clustering capabilities for time-series data. + +## Prerequisites + +To write Prometheus data to TDengine requires the following preparations. +- The TDengine cluster is deployed and functioning properly +- taosAdapter is installed and running properly. Please refer to the [taosAdapter manual](/reference/taosadapter) for details. +- Prometheus has been installed. Please refer to the [official documentation](https://prometheus.io/docs/prometheus/latest/installation/) for installing Prometheus + +## Configuration steps + + + +## Verification method + +After restarting Prometheus, you can refer to the following example to verify that data is written from Prometheus to TDengine and can read out correctly. + +### Query and write data using TDengine CLI + +``` +taos> show databases; + name | created_time | ntables | vgroups | replica | quorum | days | keep | cache(MB) | blocks | minrows | maxrows | wallevel | fsync | comp | cachelast | precision | update | status | +==================================================================================================================================================================================================================================================================================== + test | 2022-04-12 08:07:58.756 | 1 | 1 | 1 | 1 | 10 | 3650 | 16 | 6 | 100 | 4096 | 1 | 3000 | 2 | 0 | ms | 0 | ready | + log | 2022-04-20 07:19:50.260 | 2 | 1 | 1 | 1 | 10 | 3650 | 16 | 6 | 100 | 4096 | 1 | 3000 | 2 | 0 | ms | 0 | ready | + prometheus_data | 2022-04-20 07:21:09.202 | 158 | 1 | 1 | 1 | 10 | 3650 | 16 | 6 | 100 | 4096 | 1 | 3000 | 2 | 0 | ns | 2 | ready | + db | 2022-04-15 06:37:08.512 | 1 | 1 | 1 | 1 | 10 | 3650 | 16 | 6 | 100 | 4096 | 1 | 3000 | 2 | 0 | ms | 0 | ready | +Query OK, 4 row(s) in set (0.000585s) + +taos> use prometheus_data; +Database changed. + +taos> show stables; + name | created_time | columns | tags | tables | +============================================================================================ + metrics | 2022-04-20 07:21:09.209 | 2 | 1 | 1389 | +Query OK, 1 row(s) in set (0.000487s) + +taos> select * from metrics limit 10; + ts | value | labels | +============================================================================================= + 2022-04-20 07:21:09.193000000 | 0.000024996 | {"__name__":"go_gc_duration... | + 2022-04-20 07:21:14.193000000 | 0.000024996 | {"__name__":"go_gc_duration... | + 2022-04-20 07:21:19.193000000 | 0.000024996 | {"__name__":"go_gc_duration... | + 2022-04-20 07:21:24.193000000 | 0.000024996 | {"__name__":"go_gc_duration... | + 2022-04-20 07:21:29.193000000 | 0.000024996 | {"__name__":"go_gc_duration... | + 2022-04-20 07:21:09.193000000 | 0.000054249 | {"__name__":"go_gc_duration... | + 2022-04-20 07:21:14.193000000 | 0.000054249 | {"__name__":"go_gc_duration... | + 2022-04-20 07:21:19.193000000 | 0.000054249 | {"__name__":"go_gc_duration... | + 2022-04-20 07:21:24.193000000 | 0.000054249 | {"__name__":"go_gc_duration... | + 2022-04-20 07:21:29.193000000 | 0.000054249 | {"__name__":"go_gc_duration... | +Query OK, 10 row(s) in set (0.011146s) +``` + +### Use promql-cli to read data from TDengine via remote_read + +Install promql-cli + +``` + go install github.com/nalbury/promql-cli@latest +``` + +Query Prometheus data in the running state of TDengine and taosAdapter services + +``` +ubuntu@shuduo-1804 ~ $ promql-cli --host "http://127.0.0.1:9090" "sum(up) by (job)" +JOB VALUE TIMESTAMP +prometheus 1 2022-04-20T08:05:26Z +node 1 2022-04-20T08:05:26Z +``` + +Stop taosAdapter service and query Prometheus data to verify + +``` +ubuntu@shuduo-1804 ~ $ sudo systemctl stop taosadapter.service +ubuntu@shuduo-1804 ~ $ promql-cli --host "http://127.0.0.1:9090" "sum(up) by (job)" +VALUE TIMESTAMP + +``` + diff --git a/docs-en/20-third-party/03-telegraf.md b/docs-en/20-third-party/03-telegraf.md new file mode 100644 index 0000000000000000000000000000000000000000..0d563c9ff36268ac27e18e21fefed789789dc1a7 --- /dev/null +++ b/docs-en/20-third-party/03-telegraf.md @@ -0,0 +1,67 @@ +--- +sidebar_label: Telegraf +title: Telegraf writing +--- + +import Telegraf from "../14-reference/_telegraf.mdx" + +Telegraf is a viral metrics collection open-source software. Telegraf can collect the operation information of various components without writing any scripts to collect regularly, reducing the difficulty of data acquisition. + +Telegraf's data can be written to TDengine by simply adding the output configuration of Telegraf to the URL corresponding to taosAdapter and modifying several configuration items. The presence of Telegraf data in TDengine can take advantage of TDengine's efficient storage query performance and clustering capabilities for time-series data. + +## Prerequisites + +To write Telegraf data to TDengine requires the following preparations. +- The TDengine cluster is deployed and functioning properly +- taosAdapter is installed and running properly. Please refer to the [taosAdapter manual](/reference/taosadapter) for details. +- Telegraf has been installed. Please refer to the [official documentation](https://docs.influxdata.com/telegraf/v1.22/install/) for Telegraf installation. + +## Configuration steps + + +## Verification method + +Restart Telegraf service: + +``` +sudo systemctl restart telegraf +``` + +Use TDengine CLI to verify Telegraf correctly writing data to TDengine and read out: + +``` +taos> show databases; + name | created_time | ntables | vgroups | replica | quorum | days | keep | cache(MB) | blocks | minrows | maxrows | wallevel | fsync | comp | cachelast | precision | update | status | +==================================================================================================================================================================================================================================================================================== + telegraf | 2022-04-20 08:47:53.488 | 22 | 1 | 1 | 1 | 10 | 3650 | 16 | 6 | 100 | 4096 | 1 | 3000 | 2 | 0 | ns | 2 | ready | + log | 2022-04-20 07:19:50.260 | 9 | 1 | 1 | 1 | 10 | 3650 | 16 | 6 | 100 | 4096 | 1 | 3000 | 2 | 0 | ms | 0 | ready | +Query OK, 2 row(s) in set (0.002401s) + +taos> use telegraf; +Database changed. + +taos> show stables; + name | created_time | columns | tags | tables | +============================================================================================ + swap | 2022-04-20 08:47:53.532 | 7 | 1 | 1 | + cpu | 2022-04-20 08:48:03.488 | 11 | 2 | 5 | + system | 2022-04-20 08:47:53.512 | 8 | 1 | 1 | + diskio | 2022-04-20 08:47:53.550 | 12 | 2 | 15 | + kernel | 2022-04-20 08:47:53.503 | 6 | 1 | 1 | + mem | 2022-04-20 08:47:53.521 | 35 | 1 | 1 | + processes | 2022-04-20 08:47:53.555 | 12 | 1 | 1 | + disk | 2022-04-20 08:47:53.541 | 8 | 5 | 2 | +Query OK, 8 row(s) in set (0.000521s) + +taos> select * from telegraf.system limit 10; + ts | load1 | load5 | load15 | n_cpus | n_users | uptime | uptime_format | host +| +============================================================================================================================================================================================================================================= + 2022-04-20 08:47:50.000000000 | 0.000000000 | 0.050000000 | 0.070000000 | 4 | 1 | 5533 | 1:32 | shuduo-1804 +| + 2022-04-20 08:48:00.000000000 | 0.000000000 | 0.050000000 | 0.070000000 | 4 | 1 | 5543 | 1:32 | shuduo-1804 +| + 2022-04-20 08:48:10.000000000 | 0.000000000 | 0.040000000 | 0.070000000 | 4 | 1 | 5553 | 1:32 | shuduo-1804 +| +Query OK, 3 row(s) in set (0.013269s) +``` diff --git a/docs-en/20-third-party/05-collectd.md b/docs-en/20-third-party/05-collectd.md new file mode 100644 index 0000000000000000000000000000000000000000..609e55842ab35cdc2d394663f5450f908e49f7f7 --- /dev/null +++ b/docs-en/20-third-party/05-collectd.md @@ -0,0 +1,74 @@ +--- +sidebar_label: collectd +title: collectd writing +--- + +import CollectD from "../14-reference/_collectd.mdx" + + +collectd is a daemon used to collect system performance metric data. collectd provides various storage mechanisms to store different values. It periodically counts system performance statistics number while the system is running and storing information. You can use this information to help identify current system performance bottlenecks and predict future system load. + +You can write the data collected by collectd to TDengine by simply modifying the configuration of collectd to the domain name (or IP address) and corresponding port of the server running taosAdapter. It can take full advantage of TDengine's efficient storage query performance and clustering capability for time-series data. + +## Prerequisites + +Writing collectd data to the TDengine requires several preparations. +- The TDengine cluster is deployed and running properly +- taosAdapter is installed and running, please refer to [taosAdapter's manual](/reference/taosadapter) for details +- collectd has been installed. Please refer to the [official documentation](https://collectd.org/download.shtml) to install collectd + +## Configuration steps + + +## Verification method + +Restart collectd + +``` +sudo systemctl restart collectd +``` + +Use the TDengine CLI to verify that collectd's data is written to TDengine and can read out correctly. + +``` +taos> show databases; + name | created_time | ntables | vgroups | replica | quorum | days | keep | cache(MB) | blocks | minrows | maxrows | wallevel | fsync | comp | cachelast | precision | update | status | +==================================================================================================================================================================================================================================================================================== + collectd | 2022-04-20 09:27:45.460 | 95 | 1 | 1 | 1 | 10 | 3650 | 16 | 6 | 100 | 4096 | 1 | 3000 | 2 | 0 | ns | 2 | ready | + log | 2022-04-20 07:19:50.260 | 11 | 1 | 1 | 1 | 10 | 3650 | 16 | 6 | 100 | 4096 | 1 | 3000 | 2 | 0 | ms | 0 | ready | +Query OK, 2 row(s) in set (0.003266s) + +taos> use collectd; +Database changed. + +taos> show stables; + name | created_time | columns | tags | tables | +============================================================================================ + load_1 | 2022-04-20 09:27:45.492 | 2 | 2 | 1 | + memory_value | 2022-04-20 09:27:45.463 | 2 | 3 | 6 | + df_value | 2022-04-20 09:27:45.463 | 2 | 4 | 25 | + load_2 | 2022-04-20 09:27:45.501 | 2 | 2 | 1 | + load_0 | 2022-04-20 09:27:45.485 | 2 | 2 | 1 | + interface_1 | 2022-04-20 09:27:45.488 | 2 | 3 | 12 | + irq_value | 2022-04-20 09:27:45.476 | 2 | 3 | 31 | + interface_0 | 2022-04-20 09:27:45.480 | 2 | 3 | 12 | + entropy_value | 2022-04-20 09:27:45.473 | 2 | 2 | 1 | + swap_value | 2022-04-20 09:27:45.477 | 2 | 3 | 5 | +Query OK, 10 row(s) in set (0.002236s) + +taos> select * from collectd.memory_value limit 10; + ts | value | host | type_instance | type | +========================================================================================================================================================= + 2022-04-20 09:27:45.459653462 | 54689792.000000000 | shuduo-1804 | buffered | memory | + 2022-04-20 09:27:55.453168283 | 57212928.000000000 | shuduo-1804 | buffered | memory | + 2022-04-20 09:28:05.453004291 | 57942016.000000000 | shuduo-1804 | buffered | memory | + 2022-04-20 09:27:45.459653462 | 6381330432.000000000 | shuduo-1804 | free | memory | + 2022-04-20 09:27:55.453168283 | 6357643264.000000000 | shuduo-1804 | free | memory | + 2022-04-20 09:28:05.453004291 | 6349987840.000000000 | shuduo-1804 | free | memory | + 2022-04-20 09:27:45.459653462 | 107040768.000000000 | shuduo-1804 | slab_recl | memory | + 2022-04-20 09:27:55.453168283 | 107536384.000000000 | shuduo-1804 | slab_recl | memory | + 2022-04-20 09:28:05.453004291 | 107634688.000000000 | shuduo-1804 | slab_recl | memory | + 2022-04-20 09:27:45.459653462 | 309137408.000000000 | shuduo-1804 | used | memory | +Query OK, 10 row(s) in set (0.010348s) +``` + diff --git a/docs-en/20-third-party/06-statsd.md b/docs-en/20-third-party/06-statsd.md new file mode 100644 index 0000000000000000000000000000000000000000..bf4b6c7ab5dac4114cad0d650b2aeb026a67581c --- /dev/null +++ b/docs-en/20-third-party/06-statsd.md @@ -0,0 +1,68 @@ +--- +sidebar_label: StatsD +title: StatsD writing +--- + +import StatsD from "../14-reference/_statsd.mdx" + +StatsD is a simple daemon for aggregating application metrics, which has evolved rapidly in recent years into a unified protocol for collecting application performance metrics. + +You can write StatsD data to TDengine by simply modifying in the configuration file of StatsD with the domain name (or IP address) of the server running taosAdapter and the corresponding port. It can take full advantage of TDengine's efficient storage query performance and clustering capabilities for time-series data. + +## Prerequisites + +To write StatsD data to TDengine requires the following preparations. +- The TDengine cluster has been deployed and is working properly +- taosAdapter is installed and running properly. Please refer to the [taosAdapter manual](/reference/taosadapter) for details. +- StatsD has been installed. To install StatsD, please refer to [official documentation](https://github.com/statsd/statsd) + +## Configuration steps + + +## Verification method + +Start StatsD: + +``` +$ node stats.js config.js & +[1] 8546 +$ 20 Apr 09:54:41 - [8546] reading config file: exampleConfig.js +20 Apr 09:54:41 - server is up INFO +``` + +Using the utility software `nc` to write data for test: + +``` +$ echo "foo:1|c" | nc -u -w0 127.0.0.1 8125 +``` + +Use the TDengine CLI to verify that StatsD data is written to TDengine and can read out correctly. + +``` +Welcome to the TDengine shell from Linux, Client Version:2.4.0.0 +Copyright (c) 2020 by TAOS Data, Inc. All rights reserved. + +taos> show databases; + name | created_time | ntables | vgroups | replica | quorum | days | keep | cache(MB) | blocks | minrows | maxrows | wallevel | fsync | comp | cachelast | precision | update | status | +==================================================================================================================================================================================================================================================================================== + log | 2022-04-20 07:19:50.260 | 11 | 1 | 1 | 1 | 10 | 3650 | 16 | 6 | 100 | 4096 | 1 | 3000 | 2 | 0 | ms | 0 | ready | + statsd | 2022-04-20 09:54:51.220 | 1 | 1 | 1 | 1 | 10 | 3650 | 16 | 6 | 100 | 4096 | 1 | 3000 | 2 | 0 | ns | 2 | ready | +Query OK, 2 row(s) in set (0.003142s) + +taos> use statsd; +Database changed. + +taos> show stables; + name | created_time | columns | tags | tables | +============================================================================================ + foo | 2022-04-20 09:54:51.234 | 2 | 1 | 1 | +Query OK, 1 row(s) in set (0.002161s) + +taos> select * from foo; + ts | value | metric_type | +======================================================================================= + 2022-04-20 09:54:51.219614235 | 1 | counter | +Query OK, 1 row(s) in set (0.004179s) + +taos> +``` diff --git a/docs-en/20-third-party/07-icinga2.md b/docs-en/20-third-party/07-icinga2.md new file mode 100644 index 0000000000000000000000000000000000000000..ba9cde8cea7504ac9df871d5f6aa42cc5c94d895 --- /dev/null +++ b/docs-en/20-third-party/07-icinga2.md @@ -0,0 +1,74 @@ +--- +sidebar_label: icinga2 +title: icinga2 writing +--- + +import Icinga2 from "../14-reference/_icinga2.mdx" + +icinga2 is an open-source software monitoring host and network initially developed from the Nagios network monitoring application. Currently, icinga2 is distributed under the GNU GPL v2 license. + +You can write the data collected by icinga2 to TDengine by simply modifying the icinga2 configuration to point to the taosAdapter server and the corresponding port, taking advantage of TDengine's efficient storage and query performance and clustering capabilities for time-series data. + +## Prerequisites + +To write icinga2 data to TDengine requires the following preparations. +- The TDengine cluster is deployed and working properly +- taosAdapter is installed and running properly. Please refer to the [taosAdapter manual](/reference/taosadapter) for details. +- icinga2 has been installed. Please refer to the [official documentation](https://icinga.com/docs/icinga-2/latest/doc/02-installation/) for icinga2 installation + +## Configuration steps + + +## Verification method + +Restart taosAdapter: +``` +sudo systemctl restart taosadapter +``` + +Restart icinga2: + +``` +sudo systemctl restart icinga2 +``` + +After waiting about 10 seconds, use the TDengine CLI to query TDengine to verify that the appropriate database has been created and data are written. + +``` +taos> show databases; + name | created_time | ntables | vgroups | replica | quorum | days | keep | cache(MB) | blocks | minrows | maxrows | wallevel | fsync | comp | cachelast | precision | update | status | +==================================================================================================================================================================================================================================================================================== + log | 2022-04-20 07:19:50.260 | 11 | 1 | 1 | 1 | 10 | 3650 | 16 | 6 | 100 | 4096 | 1 | 3000 | 2 | 0 | ms | 0 | ready | + icinga2 | 2022-04-20 12:11:39.697 | 13 | 1 | 1 | 1 | 10 | 3650 | 16 | 6 | 100 | 4096 | 1 | 3000 | 2 | 0 | ns | 2 | ready | +Query OK, 2 row(s) in set (0.001867s) + +taos> use icinga2; +Database changed. + +taos> show stables; + name | created_time | columns | tags | tables | +============================================================================================ + icinga.service.users.state_... | 2022-04-20 12:11:39.726 | 2 | 1 | 1 | + icinga.service.users.acknow... | 2022-04-20 12:11:39.756 | 2 | 1 | 1 | + icinga.service.procs.downti... | 2022-04-20 12:11:44.541 | 2 | 1 | 1 | + icinga.service.users.users | 2022-04-20 12:11:39.770 | 2 | 1 | 1 | + icinga.service.procs.procs_min | 2022-04-20 12:11:44.599 | 2 | 1 | 1 | + icinga.service.users.users_min | 2022-04-20 12:11:39.809 | 2 | 1 | 1 | + icinga.check.max_check_atte... | 2022-04-20 12:11:39.847 | 2 | 3 | 2 | + icinga.service.procs.state_... | 2022-04-20 12:11:44.522 | 2 | 1 | 1 | + icinga.service.procs.procs_... | 2022-04-20 12:11:44.576 | 2 | 1 | 1 | + icinga.service.users.users_... | 2022-04-20 12:11:39.796 | 2 | 1 | 1 | + icinga.check.latency | 2022-04-20 12:11:39.869 | 2 | 3 | 2 | + icinga.service.procs.procs_... | 2022-04-20 12:11:44.588 | 2 | 1 | 1 | + icinga.service.users.downti... | 2022-04-20 12:11:39.746 | 2 | 1 | 1 | + icinga.service.users.users_... | 2022-04-20 12:11:39.783 | 2 | 1 | 1 | + icinga.service.users.reachable | 2022-04-20 12:11:39.736 | 2 | 1 | 1 | + icinga.service.procs.procs | 2022-04-20 12:11:44.565 | 2 | 1 | 1 | + icinga.service.procs.acknow... | 2022-04-20 12:11:44.554 | 2 | 1 | 1 | + icinga.service.procs.state | 2022-04-20 12:11:44.509 | 2 | 1 | 1 | + icinga.service.procs.reachable | 2022-04-20 12:11:44.532 | 2 | 1 | 1 | + icinga.check.current_attempt | 2022-04-20 12:11:39.825 | 2 | 3 | 2 | + icinga.check.execution_time | 2022-04-20 12:11:39.898 | 2 | 3 | 2 | + icinga.service.users.state | 2022-04-20 12:11:39.704 | 2 | 1 | 1 | +Query OK, 22 row(s) in set (0.002317s) +``` diff --git a/docs-en/20-third-party/08-tcollector.md b/docs-en/20-third-party/08-tcollector.md new file mode 100644 index 0000000000000000000000000000000000000000..16b73c23948ff001bab9c756c50b0fcb2cd44810 --- /dev/null +++ b/docs-en/20-third-party/08-tcollector.md @@ -0,0 +1,67 @@ +--- +sidebar_label: TCollector +title: TCollector writing +--- + +import TCollector from "../14-reference/_tcollector.mdx" + +TCollector is part of openTSDB and collects client computer's logs to send to the database. + +You can write the data collected by TCollector to TDengine by simply changing the configuration of TCollector to point to the domain name (or IP address) and corresponding port of the server running taosAdapter. It can take full advantage of TDengine's efficient storage query performance and clustering capability for time-series data. + +## Prerequisites + +To write data to the TDengine via TCollector requires the following preparations. +- The TDengine cluster has been deployed and is working properly +- taosAdapter is installed and running properly. Please refer to the [taosAdapter manual](/reference/taosadapter) for details. +- TCollector has been installed. Please refer to [official documentation](http://opentsdb.net/docs/build/html/user_guide/utilities/tcollector.html#installation-of-tcollector) for TCollector installation + +## Configuration steps + + +## Verification method + +Restart taosAdapter: + +``` +sudo systemctl restart taosadapter +``` + +Run `sudo ./tcollector.py`: + +Wait for a few seconds and then use the TDengine CLI to query whether the corresponding database has been created and data are written in TDengine. + +``` +taos> show databases; + name | created_time | ntables | vgroups | replica | quorum | days | keep | cache(MB) | blocks | minrows | maxrows | wallevel | fsync | comp | cachelast | precision | update | status | +==================================================================================================================================================================================================================================================================================== + tcollector | 2022-04-20 12:44:49.604 | 88 | 1 | 1 | 1 | 10 | 3650 | 16 | 6 | 100 | 4096 | 1 | 3000 | 2 | 0 | ns | 2 | ready | + log | 2022-04-20 07:19:50.260 | 11 | 1 | 1 | 1 | 10 | 3650 | 16 | 6 | 100 | 4096 | 1 | 3000 | 2 | 0 | ms | 0 | ready | +Query OK, 2 row(s) in set (0.002679s) + +taos> use tcollector; +Database changed. + +taos> show stables; + name | created_time | columns | tags | tables | +============================================================================================ + proc.meminfo.hugepages_rsvd | 2022-04-20 12:44:53.945 | 2 | 1 | 1 | + proc.meminfo.directmap1g | 2022-04-20 12:44:54.110 | 2 | 1 | 1 | + proc.meminfo.vmallocchunk | 2022-04-20 12:44:53.724 | 2 | 1 | 1 | + proc.meminfo.hugepagesize | 2022-04-20 12:44:54.004 | 2 | 1 | 1 | + tcollector.reader.lines_dro... | 2022-04-20 12:44:49.675 | 2 | 1 | 1 | + proc.meminfo.sunreclaim | 2022-04-20 12:44:53.437 | 2 | 1 | 1 | + proc.stat.ctxt | 2022-04-20 12:44:55.363 | 2 | 1 | 1 | + proc.meminfo.swaptotal | 2022-04-20 12:44:53.158 | 2 | 1 | 1 | + proc.uptime.total | 2022-04-20 12:44:52.813 | 2 | 1 | 1 | + tcollector.collector.lines_... | 2022-04-20 12:44:49.895 | 2 | 2 | 51 | + proc.meminfo.vmallocused | 2022-04-20 12:44:53.704 | 2 | 1 | 1 | + proc.meminfo.memavailable | 2022-04-20 12:44:52.939 | 2 | 1 | 1 | + sys.numa.foreign_allocs | 2022-04-20 12:44:57.929 | 2 | 2 | 1 | + proc.meminfo.committed_as | 2022-04-20 12:44:53.639 | 2 | 1 | 1 | + proc.vmstat.pswpin | 2022-04-20 12:44:54.177 | 2 | 1 | 1 | + proc.meminfo.cmafree | 2022-04-20 12:44:53.865 | 2 | 1 | 1 | + proc.meminfo.mapped | 2022-04-20 12:44:53.349 | 2 | 1 | 1 | + proc.vmstat.pgmajfault | 2022-04-20 12:44:54.251 | 2 | 1 | 1 | +... +``` diff --git a/docs-en/20-third-party/09-emq-broker.md b/docs-en/20-third-party/09-emq-broker.md new file mode 100644 index 0000000000000000000000000000000000000000..13562ba7f720499c23771437c5c6ba0f61819456 --- /dev/null +++ b/docs-en/20-third-party/09-emq-broker.md @@ -0,0 +1,190 @@ +--- +sidebar_label: EMQX Broker +title: EMQX Broker writing +--- + +MQTT is a popular IoT data transfer protocol, [EMQX](https://github.com/emqx/emqx) is an open-source MQTT Broker software, without any code, only need to use "rules" in EMQX Dashboard to do simple configuration. You can write MQTT data directly to TDengine. EMQX supports saving data to TDengine by sending it to web services and provides a native TDengine driver for direct saving in the Enterprise Edition. Please refer to the [EMQX official documentation](https://www.emqx.io/docs/en/v4.4/rule/rule-engine.html) for details on how to use it. tdengine). + +## Prerequisites + +The following preparations are required for EMQX to add TDengine data sources correctly. +- The TDengine cluster is deployed and working properly +- taosAdapter is installed and running properly. Please refer to the [taosAdapter manual](/reference/taosadapter) for details. +- If you use the emulated writers described later, you need to install the appropriate version of Node.js. V12 is recommended. + +## Install and start EMQX + +Depending on the current operating system, users can download the installation package from the [EMQX official website](https://www.emqx.io/downloads) and execute the installation. After installation, use `sudo emqx start` or `sudo systemctl start emqx` to start the EMQX service. + +## Create the appropriate database and table schema in TDengine for receiving MQTT data + +### Take the Docker installation of TDengine as an example + +```bash + docker exec -it tdengine bash + taos +``` + +### Create Database and Table + +```sql + CREATE DATABASE test; + USE test; + + CREATE TABLE sensor_data (ts timestamp, temperature float, humidity float, volume float, PM10 float, pm25 float, SO2 float, NO2 float, CO float, sensor_id NCHAR(255), area TINYINT, coll_time timestamp); +``` + +Note: The table schema is based on the blog [(In Chinese) Data Transfer, Storage, Presentation, EMQX + TDengine Build MQTT IoT Data Visualization Platform](https://www.taosdata.com/blog/2020/08/04/1722.html) as an example. Subsequent operations are carried out with this blog scenario too. Please modify it according to your actual application scenario. + +## Configuring EMQX Rules + +Since the configuration interface of EMQX differs from version to version, here is v4.4.3 as an example. For other versions, please refer to the corresponding official documentation. + +### Login EMQX Dashboard + +Use your browser to open the URL `http://IP:18083` and log in to EMQX Dashboard. The initial installation username is `admin` and the password is: `public`. + +![img](./emqx/login-dashboard.png) + +### Creating Rule + +Select "Rule" in the "Rule Engine" on the left and click the "Create" button: ! + +![img](./emqx/rule-engine.png) + +### Edit SQL fields + +![img](./emqx/create-rule.png) + +### Add "action handler" + +![img](./emqx/add-action-handler.png) + +### Add "Resource" + +![img](./emqx/create-resource.png) + +Select "Data to Web Service" and click the "New Resource" button. + +### Edit "Resource" + +Select "Data to Web Service" and fill in the request URL as the address and port of the server running taosAdapter (default is 6041). Leave the other properties at their default values. + +![img](./emqx/edit-resource.png) + +### Edit "action" + +Edit the resource configuration to add the key/value pairing for Authorization. Please refer to the [ TDengine REST API documentation ](https://docs.taosdata.com/reference/rest-api/) for the authorization in details. Enter the rule engine replacement template in the message body. + +![img](./emqx/edit-action.png) + +## Compose program to mock data + +```javascript + // mock.js + const mqtt = require('mqtt') + const Mock = require('mockjs') + const EMQX_SERVER = 'mqtt://localhost:1883' + const CLIENT_NUM = 10 + const STEP = 5000 // Data interval in ms + const AWAIT = 5000 // Sleep time after data be written once to avoid data writing too fast + const CLIENT_POOL = [] + startMock() + function sleep(timer = 100) { + return new Promise(resolve => { + setTimeout(resolve, timer) + }) + } + async function startMock() { + const now = Date.now() + for (let i = 0; i < CLIENT_NUM; i++) { + const client = await createClient(`mock_client_${i}`) + CLIENT_POOL.push(client) + } + // last 24h every 5s + const last = 24 * 3600 * 1000 + for (let ts = now - last; ts <= now; ts += STEP) { + for (const client of CLIENT_POOL) { + const mockData = generateMockData() + const data = { + ...mockData, + id: client.clientId, + area: 0, + ts, + } + client.publish('sensor/data', JSON.stringify(data)) + } + const dateStr = new Date(ts).toLocaleTimeString() + console.log(`${dateStr} send success.`) + await sleep(AWAIT) + } + console.log(`Done, use ${(Date.now() - now) / 1000}s`) + } + /** + * Init a virtual mqtt client + * @param {string} clientId ClientID + */ + function createClient(clientId) { + return new Promise((resolve, reject) => { + const client = mqtt.connect(EMQX_SERVER, { + clientId, + }) + client.on('connect', () => { + console.log(`client ${clientId} connected`) + resolve(client) + }) + client.on('reconnect', () => { + console.log('reconnect') + }) + client.on('error', (e) => { + console.error(e) + reject(e) + }) + }) + } + /** + * Generate mock data + */ + function generateMockData() { + return { + "temperature": parseFloat(Mock.Random.float(22, 100).toFixed(2)), + "humidity": parseFloat(Mock.Random.float(12, 86).toFixed(2)), + "volume": parseFloat(Mock.Random.float(20, 200).toFixed(2)), + "PM10": parseFloat(Mock.Random.float(0, 300).toFixed(2)), + "pm25": parseFloat(Mock.Random.float(0, 300).toFixed(2)), + "SO2": parseFloat(Mock.Random.float(0, 50).toFixed(2)), + "NO2": parseFloat(Mock.Random.float(0, 50).toFixed(2)), + "CO": parseFloat(Mock.Random.float(0, 50).toFixed(2)), + "area": Mock.Random.integer(0, 20), + "ts": 1596157444170, + } + } +``` + +Note: `CLIENT_NUM` in the code can be set to a smaller value at the beginning of the test to avoid hardware performance be not capable to handle a more significant number of concurrent clients. + +![img](./emqx/client-num.png) + +## Execute tests to simulate sending MQTT data + +``` +npm install mqtt mockjs --save ---registry=https://registry.npm.taobao.org +node mock.js +``` + +![img](./emqx/run-mock.png) + +## Verify that EMQX is receiving data + +Refresh the EMQX Dashboard rules engine interface to see how many records were received correctly: + +![img](./emqx/check-rule-matched.png) + +## Verify that data writing to TDengine + +Use the TDengine CLI program to log in and query the appropriate databases and tables to verify that the data is being written to TDengine correctly: + +![img](./emqx/check-result-in-taos.png) + +Please refer to the [TDengine official documentation](https://docs.taosdata.com/) for more details on how to use TDengine. +EMQX Please refer to the [EMQX official documentation](https://www.emqx.io/docs/en/v4.4/rule/rule-engine.html) for details on how to use EMQX. diff --git a/docs-en/20-third-party/10-hive-mq-broker.md b/docs-en/20-third-party/10-hive-mq-broker.md new file mode 100644 index 0000000000000000000000000000000000000000..333e00fa0e9b724ffbb067a83ad07d0b846b1a23 --- /dev/null +++ b/docs-en/20-third-party/10-hive-mq-broker.md @@ -0,0 +1,6 @@ +--- +sidebar_label: HiveMQ Broker +title: HiveMQ Broker writing +--- + +[HiveMQ](https://www.hivemq.com/) is an MQTT broker that provides community and enterprise editions. HiveMQ is mainly for enterprise emerging machine-to-machine M2M communication and internal transport, meeting scalability, ease of management, and security features. HiveMQ provides an open-source plug-in development kit. MQTT data can be saved to TDengine via TDengine extension for HiveMQ. Please refer to the [HiveMQ extension - TDengine documentation](https://github.com/huskar-t/hivemq-tdengine-extension/blob/b62a26ecc164a310104df57691691b237e091c89/README_EN.md) for details on how to use it. \ No newline at end of file diff --git a/docs-en/20-third-party/11-kafka.md b/docs-en/20-third-party/11-kafka.md new file mode 100644 index 0000000000000000000000000000000000000000..b9c7a3814a75a066b498438b6e632690697ae7ca --- /dev/null +++ b/docs-en/20-third-party/11-kafka.md @@ -0,0 +1,377 @@ +--- +sidebar_label: Kafka +title: TDengine Kafka Connector Tutorial +--- + +TDengine Kafka Connector contains two plugins: TDengine Source Connector and TDengine Sink Connector. Users only need to provide a simple configuration file to synchronize the data of the specified topic in Kafka (batch or real-time) to TDengine or synchronize the data (batch or real-time) of the specified database in TDengine to Kafka. + +## What is Kafka Connect? + +Kafka Connect is a component of Apache Kafka that enables other systems, such as databases, cloud services, file systems, etc., to connect to Kafka easily. Data can flow from other software to Kafka via Kafka Connect and Kafka to other systems via Kafka Connect. Plugins that read data from other software are called Source Connectors, and plugins that write data to other software are called Sink Connectors. Neither Source Connector nor Sink Connector will directly connect to Kafka Broker, and Source Connector transfers data to Kafka Connect. Sink Connector receives data from Kafka Connect. + +![](kafka/Kafka_Connect.png) + +TDengine Source Connector is used to read data from TDengine in real-time and send it to Kafka Connect. Users can use The TDengine Sink Connector to receive data from Kafka Connect and write it to TDengine. + +![](kafka/streaming-integration-with-kafka-connect.png) + +## What is Confluent? + +Confluent adds many extensions to Kafka. include: + +1. Schema Registry +2. REST Proxy +3. Non-Java Clients +4. Many packaged Kafka Connect plugins +5. GUI for managing and monitoring Kafka - Confluent Control Center + +Some of these extensions are available in the community version of Confluent. Some are only available in the enterprise version. +![](kafka/confluentPlatform.png) + +Confluent Enterprise Edition provides the `confluent` command-line tool to manage various components. + +## Prerequisites + +1. Linux operating system +2. Java 8 and Maven installed +3. Git is installed +4. TDengine is installed and started. If not, please refer to [Installation and Uninstallation](/operation/pkg-install) + +## Install Confluent + +Confluent provides two installation methods: Docker and binary packages. This article only introduces binary package installation. + +Execute in any directory: + +```` +curl -O http://packages.confluent.io/archive/7.1/confluent-7.1.1.tar.gz +tar xzf confluent-7.1.1.tar.gz -C /opt/test +```` + +Then you need to add the `$CONFLUENT_HOME/bin` directory to the PATH. + +```title=".profile" +export CONFLUENT_HOME=/opt/confluent-7.1.1 +PATH=$CONFLUENT_HOME/bin +export PATH +``` + +Users can append the above script to the current user's profile file (~/.profile or ~/.bash_profile) + +After the installation is complete, you can enter `confluent version` for simple verification: + +``` +# confluent version +confluent - Confluent CLI + +Version: v2.6.1 +Git Ref: 6d920590 +Build Date: 2022-02-18T06:14:21Z +Go Version: go1.17.6 (linux/amd64) +Development: false +``` + +## Install TDengine Connector plugin + +### Install from source code + +``` +git clone https://github.com:taosdata/kafka-connect-tdengine.git +cd kafka-connect-tdengine +mvn clean package +unzip -d $CONFLUENT_HOME/share/confluent-hub-components/ target/components/packages/taosdata-kafka-connect-tdengine-0.1.0.zip +``` + +The above script first clones the project source code and then compiles and packages it with Maven. After the package is complete, the zip package of the plugin is generated in the `target/components/packages/` directory. Unzip this zip package to the path where the plugin is installed. The path to install the plugin is in the configuration file `$CONFLUENT_HOME/etc/kafka/connect-standalone.properties`. The default path is `$CONFLUENT_HOME/share/confluent-hub-components/`. + +### Install with confluent-hub + +[Confluent Hub](https://www.confluent.io/hub) provides a service to download Kafka Connect plugins. After TDengine Kafka Connector is published to Confluent Hub, it can be installed using the command tool `confluent-hub`. +**TDengine Kafka Connector is currently not officially released and cannot be installed in this way**. + +## Start Confluent + +``` +confluent local services start +``` + +:::note +Be sure to install the plugin before starting Confluent. Otherwise, there will be a class not found error. The log of Kafka Connect (default path: /tmp/confluent.xxxx/connect/logs/connect.log) will output the successfully installed plugin, which users can use to determine whether the plugin is installed successfully. +::: + +:::tip +If a component fails to start, try clearing the data and restarting. The data directory will be printed to the console at startup, e.g.: + +```title="Console output log" {1} +Using CONFLUENT_CURRENT: /tmp/confluent.106668 +Starting ZooKeeper +ZooKeeper is [UP] +Starting Kafka +Kafka is [UP] +Starting Schema Registry +Schema Registry is [UP] +Starting Kafka REST +Kafka REST is [UP] +Starting Connect +Connect is [UP] +Starting ksqlDB Server +ksqlDB Server is [UP] +Starting Control Center +Control Center is [UP] +``` + +To clear data, execute `rm -rf /tmp/confluent.106668`. +::: + +## The use of TDengine Sink Connector + +The role of the TDengine Sink Connector is to synchronize the data of the specified topic to TDengine. Users do not need to create databases and super tables in advance. The name of the target database can be specified manually (see the configuration parameter connection.database), or it can be generated according to specific rules (see the configuration parameter connection.database.prefix). + +TDengine Sink Connector internally uses TDengine [modeless write interface](/reference/connector/cpp#modeless write-api) to write data to TDengine, currently supports data in three formats: [InfluxDB line protocol format](/develop /insert-data/influxdb-line), [OpenTSDB Telnet protocol format](/develop/insert-data/opentsdb-telnet), and [OpenTSDB JSON protocol format](/develop/insert-data/opentsdb-json). + +The following example synchronizes the data of the topic meters to the target database power. The data format is the InfluxDB Line protocol format. + +### Add configuration file + +``` +mkdir ~/test +cd ~/test +vi sink-demo.properties +``` + +sink-demo.properties' content is following: + +```ini title="sink-demo.properties" +name=tdengine-sink-demo +connector.class=com.taosdata.kafka.connect.sink.TDengineSinkConnector +tasks.max=1 +topics=meters +connection.url=jdbc:TAOS://127.0.0.1:6030 +connection.user=root +connection.password=taosdata +connection.database=power +db.schemaless=line +key.converter=org.apache.kafka.connect.storage.StringConverter +value.converter=org.apache.kafka.connect.storage.StringConverter +``` + +Key configuration instructions: + +1. `topics=meters` and `connection.database=power` means to subscribe to the data of the topic meters and write to the database power. +2. `db.schemaless=line` means the data in the InfluxDB Line protocol format. + +### Create Connector instance + +```` +confluent local services connect connector load TDengineSinkConnector --config ./sink-demo.properties +```` + +If the above command is executed successfully, the output is as follows: + +```json +{ + "name": "TDengineSinkConnector", + "config": { + "connection.database": "power", + "connection.password": "taosdata", + "connection.url": "jdbc:TAOS://127.0.0.1:6030", + "connection.user": "root", + "connector.class": "com.taosdata.kafka.connect.sink.TDengineSinkConnector", + "db.schemaless": "line", + "key.converter": "org.apache.kafka.connect.storage.StringConverter", + "tasks.max": "1", + "topics": "meters", + "value.converter": "org.apache.kafka.connect.storage.StringConverter", + "name": "TDengineSinkConnector" + }, + "tasks": [], + "type": "sink" +} +``` + +### Write test data + +Prepare text file as test data, its content is following: + +```txt title="test-data.txt" +meters,location=Beijing.Haidian,groupid=2 current=11.8,voltage=221,phase=0.28 1648432611249000000 +meters,location=Beijing.Haidian,groupid=2 current=13.4,voltage=223,phase=0.29 1648432611250000000 +meters,location=Beijing.Haidian,groupid=3 current=10.8,voltage=223,phase=0.29 1648432611249000000 +meters,location=Beijing.Haidian,groupid=3 current=11.3,voltage=221,phase=0.35 1648432611250000000 +``` + +Use kafka-console-producer to write test data to the topic `meters`. + +``` +cat test-data.txt | kafka-console-producer --broker-list localhost:9092 --topic meters +``` + +:::note +TDengine Sink Connector will automatically create the database if the target database does not exist. The time precision used to create the database automatically is nanoseconds, which requires that the timestamp precision of the written data is also nanoseconds. An exception will be thrown if the timestamp precision of the written data is not nanoseconds. +::: + +### Verify that the sync was successful + +Use the TDengine CLI to verify that the sync was successful. + +``` +taos> use power; +Database changed. + +taos> select * from meters; + ts | current | voltage | phase | groupid | location | +=============================================================================================================================================================== + 2022-03-28 09:56:51.249000000 | 11.800000000 | 221.000000000 | 0.280000000 | 2 | Beijing.Haidian | + 2022-03-28 09:56:51.250000000 | 13.400000000 | 223.000000000 | 0.290000000 | 2 | Beijing.Haidian | + 2022-03-28 09:56:51.249000000 | 10.800000000 | 223.000000000 | 0.290000000 | 3 | Beijing.Haidian | + 2022-03-28 09:56:51.250000000 | 11.300000000 | 221.000000000 | 0.350000000 | 3 | Beijing.Haidian | +Query OK, 4 row(s) in set (0.004208s) +``` + +If you see the above data, the synchronization is successful. If not, check the logs of Kafka Connect. For detailed description of configuration parameters, see [Configuration Reference](#Configuration Reference). + +## The use of TDengine Source Connector + +The role of the TDengine Source Connector is to push all the data of a specific TDengine database after a particular time to Kafka. The implementation principle of TDengine Source Connector is to first pull historical data in batches and then synchronize incremental data with the strategy of the regular query. At the same time, the changes in the table will be monitored, and the newly added table can be automatically synchronized. If Kafka Connect is restarted, synchronization will resume where it left off. + +TDengine Source Connector will convert the data in TDengine data table into [InfluxDB Line protocol format](/develop/insert-data/influxdb-line/) or [OpenTSDB JSON protocol format](/develop/insert-data/opentsdb-json ) and then write to Kafka. + +The following sample program synchronizes the data in the database test to the topic tdengine-source-test. + +### Add configuration file + +``` +vi source-demo.properties +``` + +Input following content: + +```ini title="source-demo.properties" +name=TDengineSourceConnector +connector.class=com.taosdata.kafka.connect.source.TDengineSourceConnector +tasks.max=1 +connection.url=jdbc:TAOS://127.0.0.1:6030 +connection.username=root +connection.password=taosdata +connection.database=test +connection.attempts=3 +connection.backoff.ms=5000 +topic.prefix=tdengine-source- +poll.interval.ms=1000 +fetch.max.rows=100 +out.format=line +key.converter=org.apache.kafka.connect.storage.StringConverter +value.converter=org.apache.kafka.connect.storage.StringConverter +``` + +### Prepare test data + +Prepare SQL script file to generate test data + +```sql title="prepare-source-data.sql" +DROP DATABASE IF EXISTS test; +CREATE DATABASE test; +USE test; +CREATE STABLE meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT); +INSERT INTO d1001 USING meters TAGS(Beijing.Chaoyang, 2) VALUES('2018-10-03 14:38:05.000',10.30000,219,0.31000) d1001 USING meters TAGS(Beijing.Chaoyang, 2) VALUES('2018-10-03 14:38:15.000',12.60000,218,0.33000) d1001 USING meters TAGS(Beijing.Chaoyang, 2) VALUES('2018-10-03 14:38:16.800',12.30000,221,0.31000) d1002 USING meters TAGS(Beijing.Chaoyang, 3) VALUES('2018-10-03 14:38:16.650',10.30000,218,0.25000) d1003 USING meters TAGS(Beijing.Haidian, 2) VALUES('2018-10-03 14:38:05.500',11.80000,221,0.28000) d1003 USING meters TAGS(Beijing.Haidian, 2) VALUES('2018-10-03 14:38:16.600',13.40000,223,0.29000) d1004 USING meters TAGS(Beijing.Haidian, 3) VALUES('2018-10-03 14:38:05.000',10.80000,223,0.29000) d1004 USING meters TAGS(Beijing.Haidian, 3) VALUES('2018-10-03 14:38:06.500',11.50000,221,0.35000); +``` + +Use TDengine CLI to execute SQL script + +``` +taos -f prepare-source-data.sql +``` + +### Create Connector instance + +```` +confluent local services connect connector load TDengineSourceConnector --config source-demo.properties +```` + +### View topic data + +Use the kafka-console-consumer command-line tool to monitor data in the topic tdengine-source-test. In the beginning, all historical data will be output. After inserting two new data into TDengine, kafka-console-consumer immediately outputs the two new data. + +```` +kafka-console-consumer --bootstrap-server localhost:9092 --from-beginning --topic tdengine-source-test +```` + +output: + +```` +...... +meters,location="beijing.chaoyang",groupid=2i32 current=10.3f32,voltage=219i32,phase=0.31f32 1538548685000000000 +meters,location="beijing.chaoyang",groupid=2i32 current=12.6f32,voltage=218i32,phase=0.33f32 1538548695000000000 +...... +```` + +All historical data is displayed. Switch to the TDengine CLI and insert two new pieces of data: + +```` +USE test; +INSERT INTO d1001 VALUES (now, 13.3, 229, 0.38); +INSERT INTO d1002 VALUES (now, 16.3, 233, 0.22); +```` + +Switch back to kafka-console-consumer, and the command line window has printed out the two pieces of data just inserted. + +### unload plugin + +After testing, use the unload command to stop the loaded connector. + +View currently active connectors: + +```` +confluent local services connect connector status +```` + +You should now have two active connectors if you followed the previous steps. Use the following command to unload: + +```` +confluent local services connect connector unload TDengineSourceConnector +confluent local services connect connector unload TDengineSourceConnector +```` + +## Configuration reference + +### General configuration + +The following configuration items apply to TDengine Sink Connector and TDengine Source Connector. + +1. `name`: The name of the connector. +2. `connector.class`: The full class name of the connector, for example: com.taosdata.kafka.connect.sink.TDengineSinkConnector. +3. `tasks.max`: The maximum number of tasks, the default is 1. +4. `topics`: A list of topics to be synchronized, separated by commas, such as `topic1,topic2`. +5. `connection.url`: TDengine JDBC connection string, such as `jdbc:TAOS://127.0.0.1:6030`. +6. `connection.user`: TDengine username, default root. +7. `connection.password`: TDengine user password, default taosdata. +8. `connection.attempts` : The maximum number of connection attempts. Default 3. +9. `connection.backoff.ms`: The retry interval for connection creation failure, the unit is ms. Default is 5000. + +### TDengine Sink Connector specific configuration + +1. `connection.database`: The name of the target database. If the specified database does not exist, it will be created automatically. The time precision used for automatic library building is nanoseconds. The default value is null. When it is NULL, refer to the description of the `connection.database.prefix` parameter for the naming rules of the target database +2. `connection.database.prefix`: When `connection.database` is null, the prefix of the target database. Can contain placeholder '${topic}'. For example, kafka_${topic}, for topic 'orders' will be written to database 'kafka_orders'. Default null. When null, the name of the target database is the same as the name of the topic. +3. `batch.size`: Write the number of records in each batch in batches. When the data received by the sink connector at one time is larger than this value, it will be written in some batches. +4. `max.retries`: The maximum number of retries when an error occurs. Defaults to 1. +5. `retry.backoff.ms`: The time interval for retry when sending an error. The unit is milliseconds. The default is 3000. +6. `db.schemaless`: Data format, could be one of `line`, `json`, and `telnet`. Represent InfluxDB line protocol format, OpenTSDB JSON format, and OpenTSDB Telnet line protocol format. + +### TDengine Source Connector specific configuration + +1. `connection.database`: source database name, no default value. +2. `topic.prefix`: topic name prefix after data is imported into kafka. Use `topic.prefix` + `connection.database` name as the full topic name. Defaults to the empty string "". +3. `timestamp.initial`: Data synchronization start time. The format is 'yyyy-MM-dd HH:mm:ss'. Default "1970-01-01 00:00:00". +4. `poll.interval.ms`: Pull data interval, the unit is ms. Default is 1000. +5. `fetch.max.rows`: The maximum number of rows retrieved when retrieving the database. Default is 100. +6. `out.format`: The data format. The value could be line or json. The line represents the InfluxDB Line protocol format, and json represents the OpenTSDB JSON format. Default is `line`. + +## feedback + +https://github.com/taosdata/kafka-connect-tdengine/issues + +## Reference + +1. https://www.confluent.io/what-is-apache-kafka +2. https://developer.confluent.io/learn-kafka/kafka-connect/intro +3. https://docs.confluent.io/platform/current/platform.html diff --git a/docs-en/20-third-party/_category_.yml b/docs-en/20-third-party/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..e71e65a6a365df63603c3878375c034b4b24db77 --- /dev/null +++ b/docs-en/20-third-party/_category_.yml @@ -0,0 +1,2 @@ +label: Third Party Tools + diff --git a/docs-en/20-third-party/_deploytaosadapter.mdx b/docs-en/20-third-party/_deploytaosadapter.mdx new file mode 100644 index 0000000000000000000000000000000000000000..840ca7640ab3f95143c92ae61c293e5407a07b14 --- /dev/null +++ b/docs-en/20-third-party/_deploytaosadapter.mdx @@ -0,0 +1,17 @@ +### Deploying taosAdapter + +taosAdapter can be deployed on the same system as TDengine, or separately. + +To start taosAdapter. + +``` +systemctl start taosadapter +``` + +Check the running status of taosAdapter. + +``` +systemctl status taosadapter +``` + +taosAdapter Please refer to the `taosadapter --help` command output and [reference documentation](/reference/taosadapter) for detailed configuration parameters and usage of taosAdapter. diff --git a/docs-en/20-third-party/emqx/add-action-handler.png b/docs-en/20-third-party/emqx/add-action-handler.png new file mode 100644 index 0000000000000000000000000000000000000000..97a1f933ecfadfcab399938806d73c5a5ecc6427 Binary files /dev/null and b/docs-en/20-third-party/emqx/add-action-handler.png differ diff --git a/docs-en/20-third-party/emqx/check-result-in-taos.png b/docs-en/20-third-party/emqx/check-result-in-taos.png new file mode 100644 index 0000000000000000000000000000000000000000..c17a5c1ea2b9bbd49263056c8bf09c9aabab07d5 Binary files /dev/null and b/docs-en/20-third-party/emqx/check-result-in-taos.png differ diff --git a/docs-en/20-third-party/emqx/check-rule-matched.png b/docs-en/20-third-party/emqx/check-rule-matched.png new file mode 100644 index 0000000000000000000000000000000000000000..9e9a466946a1afa857e2bbc07b14956dd0f984b6 Binary files /dev/null and b/docs-en/20-third-party/emqx/check-rule-matched.png differ diff --git a/docs-en/20-third-party/emqx/client-num.png b/docs-en/20-third-party/emqx/client-num.png new file mode 100644 index 0000000000000000000000000000000000000000..fff48cbf3b271c367079ddde425b3f9b014062f7 Binary files /dev/null and b/docs-en/20-third-party/emqx/client-num.png differ diff --git a/docs-en/20-third-party/emqx/create-resource.png b/docs-en/20-third-party/emqx/create-resource.png new file mode 100644 index 0000000000000000000000000000000000000000..58da4c391a3692b9f5fa348d952701eab8bcb746 Binary files /dev/null and b/docs-en/20-third-party/emqx/create-resource.png differ diff --git a/docs-en/20-third-party/emqx/create-rule.png b/docs-en/20-third-party/emqx/create-rule.png new file mode 100644 index 0000000000000000000000000000000000000000..73b0b6ee3e6065a142df98abe8c0dbb32b34f89d Binary files /dev/null and b/docs-en/20-third-party/emqx/create-rule.png differ diff --git a/docs-en/20-third-party/emqx/edit-action.png b/docs-en/20-third-party/emqx/edit-action.png new file mode 100644 index 0000000000000000000000000000000000000000..2a43ee369a439cf11cee23c11f25d6a84b26d7dc Binary files /dev/null and b/docs-en/20-third-party/emqx/edit-action.png differ diff --git a/docs-en/20-third-party/emqx/edit-resource.png b/docs-en/20-third-party/emqx/edit-resource.png new file mode 100644 index 0000000000000000000000000000000000000000..0a0b3560044f4ed6e0a8f040b74085a7e8948b1f Binary files /dev/null and b/docs-en/20-third-party/emqx/edit-resource.png differ diff --git a/docs-en/20-third-party/emqx/login-dashboard.png b/docs-en/20-third-party/emqx/login-dashboard.png new file mode 100644 index 0000000000000000000000000000000000000000..d6c5035c98d860faf639ef6611c6719adf80c47b Binary files /dev/null and b/docs-en/20-third-party/emqx/login-dashboard.png differ diff --git a/docs-en/20-third-party/emqx/rule-engine.png b/docs-en/20-third-party/emqx/rule-engine.png new file mode 100644 index 0000000000000000000000000000000000000000..db110a837b024c82ee9d22f02dcd3a9d06abdd55 Binary files /dev/null and b/docs-en/20-third-party/emqx/rule-engine.png differ diff --git a/docs-en/20-third-party/emqx/rule-header-key-value.png b/docs-en/20-third-party/emqx/rule-header-key-value.png new file mode 100644 index 0000000000000000000000000000000000000000..b81b9a9684aa2f98d00b7ec21e5de411fb450312 Binary files /dev/null and b/docs-en/20-third-party/emqx/rule-header-key-value.png differ diff --git a/docs-en/20-third-party/emqx/run-mock.png b/docs-en/20-third-party/emqx/run-mock.png new file mode 100644 index 0000000000000000000000000000000000000000..0da25818575247732d5d3a783aa52cf7ce24662c Binary files /dev/null and b/docs-en/20-third-party/emqx/run-mock.png differ diff --git a/docs-en/20-third-party/grafana/add_datasource1.jpg b/docs-en/20-third-party/grafana/add_datasource1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1f0f5110f312c57f3ec1788bbc02f04fac6ac142 Binary files /dev/null and b/docs-en/20-third-party/grafana/add_datasource1.jpg differ diff --git a/docs-en/20-third-party/grafana/add_datasource2.jpg b/docs-en/20-third-party/grafana/add_datasource2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fa7a83e00e96fae649910dff4edf5f5bdadd7850 Binary files /dev/null and b/docs-en/20-third-party/grafana/add_datasource2.jpg differ diff --git a/docs-en/20-third-party/grafana/add_datasource3.jpg b/docs-en/20-third-party/grafana/add_datasource3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fc850ad08ff1174de972906842e0d5ee64e6e5cb Binary files /dev/null and b/docs-en/20-third-party/grafana/add_datasource3.jpg differ diff --git a/docs-en/20-third-party/grafana/add_datasource4.jpg b/docs-en/20-third-party/grafana/add_datasource4.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3ba73e50d455111f8621f4165746078554c2d790 Binary files /dev/null and b/docs-en/20-third-party/grafana/add_datasource4.jpg differ diff --git a/docs-en/20-third-party/grafana/create_dashboard1.jpg b/docs-en/20-third-party/grafana/create_dashboard1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3b83c3a1714e9e7540e0b06239ef7c1c4f63fe2c Binary files /dev/null and b/docs-en/20-third-party/grafana/create_dashboard1.jpg differ diff --git a/docs-en/20-third-party/grafana/create_dashboard2.jpg b/docs-en/20-third-party/grafana/create_dashboard2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fe5d768ac55254251e0290bf257178f5ff28f5a5 Binary files /dev/null and b/docs-en/20-third-party/grafana/create_dashboard2.jpg differ diff --git a/docs-en/20-third-party/index.md b/docs-en/20-third-party/index.md new file mode 100644 index 0000000000000000000000000000000000000000..87bd9e075133d1182ee93d1c1c43617c766755b9 --- /dev/null +++ b/docs-en/20-third-party/index.md @@ -0,0 +1,12 @@ +--- +title: Third Party Tools +--- + +Since TDengine supports standard SQL commands, common database connector standards (e.g., JDBC), ORM, and other popular time-series database writing protocols (e.g., InfluxDB Line Protocol, OpenTSDB JSON, OpenTSDB Telnet, etc.), it is very easy to integrate TDengine with other third party tools. You only need to provide simple configuration, the integration can be done without a line of code. + +```mdx-code-block +import DocCardList from '@theme/DocCardList'; +import {useCurrentSidebarCategory} from '@docusaurus/theme-common'; + + +``` diff --git a/docs-en/20-third-party/kafka/Kafka_Connect.png b/docs-en/20-third-party/kafka/Kafka_Connect.png new file mode 100644 index 0000000000000000000000000000000000000000..f3dc02ea2a743c6e1ae5531e14f820e3adeca29a Binary files /dev/null and b/docs-en/20-third-party/kafka/Kafka_Connect.png differ diff --git a/docs-en/20-third-party/kafka/confluentPlatform.png b/docs-en/20-third-party/kafka/confluentPlatform.png new file mode 100644 index 0000000000000000000000000000000000000000..f8e69f2c7f64d809996b2d1bf1370b67b8030850 Binary files /dev/null and b/docs-en/20-third-party/kafka/confluentPlatform.png differ diff --git a/docs-en/20-third-party/kafka/streaming-integration-with-kafka-connect.png b/docs-en/20-third-party/kafka/streaming-integration-with-kafka-connect.png new file mode 100644 index 0000000000000000000000000000000000000000..26d8a866d706180c900d69bb6f57ca2dff0047dd Binary files /dev/null and b/docs-en/20-third-party/kafka/streaming-integration-with-kafka-connect.png differ diff --git a/docs-en/21-tdinternal/01-arch.md b/docs-en/21-tdinternal/01-arch.md new file mode 100644 index 0000000000000000000000000000000000000000..9607c9b38709f6a320f82a8ee250afb407492627 --- /dev/null +++ b/docs-en/21-tdinternal/01-arch.md @@ -0,0 +1,285 @@ +--- +sidebar_label: Architecture +title: Architecture +--- + +## Cluster and Primary Logic Unit + +The design of TDengine is based on the assumption that any hardware or software system is not 100% reliable and that no single node can provide sufficient computing and storage resources to process massive data. Therefore, TDengine has been designed in a distributed and high-reliability architecture since day one of the development, so that hardware failure or software failure of any single even multiple servers will not affect the availability and reliability of the system. At the same time, through node virtualization and automatic load-balancing technology, TDengine can make the most efficient use of computing and storage resources in heterogeneous clusters to reduce hardware resources significantly. + +### Primary Logic Unit + +Logical structure diagram of TDengine distributed architecture as following: + +![TDengine architecture diagram](structure.png) +
Figure 1: TDengine architecture diagram
+ +A complete TDengine system runs on one or more physical nodes. Logically, it includes data node (dnode), TDengine client driver (TAOSC) and application (app). There are one or more data nodes in the system, which form a cluster. The application interacts with the TDengine cluster through TAOSC's API. The following is a brief introduction to each logical unit. + +**Physical node (pnode)**: A pnode is a computer that runs independently and has its own computing, storage and network capabilities. It can be a physical machine, virtual machine, or Docker container installed with OS. The physical node is identified by its configured FQDN (Fully Qualified Domain Name). TDengine relies entirely on FQDN for network communication. If you don't know about FQDN, please check [wikipedia](https://en.wikipedia.org/wiki/Fully_qualified_domain_name). + +**Data node (dnode):** A dnode is a running instance of the TDengine server-side execution code taosd on a physical node. A working system must have at least one data node. A dnode contains zero to multiple logical virtual nodes (VNODE), zero or at most one logical management node (mnode). The unique identification of a dnode in the system is determined by the instance's End Point (EP). EP is a combination of FQDN (Fully Qualified Domain Name) of the physical node where the dnode is located and the network port number (Port) configured by the system. By configuring different ports, a physical node (a physical machine, virtual machine or container) can run multiple instances or have multiple data nodes. + +**Virtual node (vnode)**: To better support data sharding, load balancing and prevent data from overheating or skewing, data nodes are virtualized into multiple virtual nodes (vnode, V2, V3, V4, etc. in the figure). Each vnode is a relatively independent work unit, which is the basic unit of time-series data storage and has independent running threads, memory space and persistent storage path. A vnode contains a certain number of tables (data collection points). When a new table is created, the system checks whether a new vnode needs to be created. The number of vnodes that can be created on a data node depends on the hardware capacities of the physical node where the data node is located. A vnode belongs to only one DB, but a DB can have multiple vnodes. In addition to the stored time-series data, a vnode also stores the schema and tag values of the included tables. A virtual node is uniquely identified in the system by the EP of the data node and the VGroup ID to which it belongs and is created and managed by the management node. + +**Management node (mnode)**: A virtual logical unit responsible for monitoring and maintaining the running status of all data nodes and load balancing among nodes (M in the figure). At the same time, the management node is also responsible for the storage and management of metadata (including users, databases, tables, static tags, etc.), so it is also called Meta Node. Multiple (up to 5) mnodes can be configured in a TDengine cluster, and they are automatically constructed into a virtual management node group (M0, M1, M2 in the figure). The master/slave mechanism is adopted for the mnode group and the data synchronization is carried out in a strongly consistent way. Any data update operation can only be executed on the master. The creation of mnode cluster is completed automatically by the system without manual intervention. There is at most one mnode on each dnode, which is uniquely identified by the EP of the data node to which it belongs. Each dnode automatically obtains the EP of the dnode where all mnodes in the whole cluster are located through internal messaging interaction. + +**Virtual node group (VGroup)**: Vnodes on different data nodes can form a virtual node group to ensure the high availability of the system. The virtual node group is managed in a master/slave mechanism. Write operations can only be performed on the master vnode, and then replicated to slave vnodes, thus ensuring that one single replica of data is copied on multiple physical nodes. The number of virtual nodes in a vgroup equals the number of data replicas. If the number of replicas of a DB is N, the system must have at least N data nodes. The number of replicas can be specified by the parameter `“replica”` when creating DB, and the default is 1. Using the multi-replication feature of TDengine, the same high data reliability can be achieved without the need for expensive storage devices such as disk arrays. Virtual node group is created and managed by the management node, and the management node assigns a system unique ID, aka VGroup ID. If two virtual nodes have the same vnode group ID, means that they belong to the same group and the data is backed up to each other. The number of virtual nodes in a virtual node group can be dynamically changed, allowing only one, that is, no data replication. VGroup ID is never changed. Even if a virtual node group is deleted, its ID will not be reused. + +**TAOSC**: TAOSC is the driver provided by TDengine to applications, which is responsible for dealing with the interaction between application and cluster, and provides the native interface of C/C++ language, which is embedded in JDBC, C #, Python, Go, Node.js language connection libraries. Applications interact with the whole cluster through TAOSC instead of directly connecting to data nodes in the cluster. This module is responsible for obtaining and caching metadata; forwarding requests for insertion, query, etc. to the correct data node; when returning the results to the application, TAOSC also needs to be responsible for the final level of aggregation, sorting, filtering and other operations. For JDBC, C/C++/C #/Python/Go/Node.js interfaces, this module runs on the physical node where the application is located. At the same time, in order to support the fully distributed RESTful interface, TAOSC has a running instance on each dnode of TDengine cluster. + +### Node Communication + +**Communication mode**: The communication among each data node of TDengine system, and among the client driver and each data node is carried out through TCP/UDP. Considering an IoT scenario, the data writing packets are generally not large, so TDengine uses UDP in addition to TCP for transmission, because UDP is more efficient and is not limited by the number of connections. TDengine implements its own timeout, retransmission, confirmation and other mechanisms to ensure reliable transmission of UDP. For packets with a data volume of less than 15K, UDP is adopted for transmission, and TCP is automatically adopted for transmission of packets with a data volume of more than 15K or query operations. At the same time, TDengine will automatically compress/decompress the data, digital sign/authenticate the data according to the configuration and data packet. For data replication among data nodes, only TCP is used for data transportation. + +**FQDN configuration:** A data node has one or more FQDNs, which can be specified in the system configuration file taos.cfg with the parameter “fqdn”. If it is not specified, the system will automatically use the hostname of the computer as its FQDN. If the node is not configured with FQDN, you can directly set the configuration parameter “fqdn” of the node to its IP address. However, IP is not recommended because IP address may be changed, and once it changes, the cluster will not work properly. The EP (End Point) of a data node consists of FQDN + Port. With FQDN, it is necessary to ensure the DNS service is running, or hosts files on nodes are configured properly. + +**Port configuration**: The external port of a data node is determined by the system configuration parameter “serverPort” in TDengine, and the port for internal communication of cluster is serverPort+5. The data replication operation among data nodes in the cluster also occupies a TCP port, which is serverPort+10. In order to support multithreading and efficient processing of UDP data, each internal and external UDP connection needs to occupy 5 consecutive ports. Therefore, the total port range of a data node will be serverPort to serverPort + 10, for a total of 11 TCP/UDP ports. To run the system, make sure that the firewall keeps these ports open. Each data node can be configured with a different serverPort. + +**Cluster external connection**: TDengine cluster can accommodate one single, multiple or even thousands of data nodes. The application only needs to initiate a connection to any data node in the cluster. The network parameter required for connection is the End Point (FQDN plus configured port number) of a data node. When starting the application taos through CLI, the FQDN of the data node can be specified through the option `-h`, and the configured port number can be specified through `-p`. If the port is not configured, the system configuration parameter “serverPort” of TDengine will be adopted. + +**Inter-cluster communication**: Data nodes connect with each other through TCP/UDP. When a data node starts, it will obtain the EP information of the dnode where the mnode is located, and then establish a connection with the mnode in the system to exchange information. There are three steps to obtain EP information of the mnode: + +1. Check whether the mnodeEpList file exists, if it does not exist or cannot be opened normally to obtain EP information of the mnode, skip to the second step; +2. Check the system configuration file taos.cfg to obtain node configuration parameters “firstEp” and “secondEp” (the node specified by these two parameters can be a normal node without mnode, in this case, the node will try to redirect to the mnode node when connected). If these two configuration parameters do not exist or do not exist in taos.cfg, or are invalid, skip to the third step; +3. Set your own EP as a mnode EP and run it independently. After obtaining the mnode EP list, the data node initiates the connection. It will successfully join the working cluster after connection. If not successful, it will try the next item in the mnode EP list. If all attempts are made, but the connection still fails, sleep for a few seconds before trying again. + +**The choice of MNODE**: TDengine logically has a management node, but there is no separated execution code. The server-side only has a set of execution code taosd. So which data node will be the management node? This is determined automatically by the system without any manual intervention. The principle is as follows: when a data node starts, it will check its End Point and compare it with the obtained mnode EP List. If its EP exists in it, the data node shall start the mnode module and become a mnode. If your own EP is not in the mnode EP List, the mnode module will not start. During the system operation, due to load balancing, downtime and other reasons, mnode may migrate to the new dnode, while totally transparent without manual intervention. The modification of configuration parameters is the decision made by mnode itself according to resources usage. + +**Add new data nodes:** After the system has a data node, it has become a working system. There are two steps to add a new node into the cluster. Step1: Connect to the existing working data node using TDengine CLI, and then add the End Point of the new data node with the command "create dnode"; Step 2: In the system configuration parameter file taos.cfg of the new data node, set the “firstEp” and “secondEp” parameters to the EP of any two data nodes in the existing cluster. Please refer to the detailed user tutorial for detailed steps. In this way, the cluster will be established step by step. + +**Redirection**: No matter about dnode or TAOSC, the connection to the mnode shall be initiated first, but the mnode is automatically created and maintained by the system, so the user does not know which dnode is running the mnode. TDengine only requires a connection to any working dnode in the system. Because any running dnode maintains the currently running mnode EP List, when receiving a connecting request from the newly started dnode or TAOSC, if it’s not a mnode by self, it will reply to the mnode EP List back. After receiving this list, TAOSC or the newly started dnode will try to establish the connection again. When the mnode EP List changes, each data node quickly obtains the latest list and notifies TAOSC through messaging interaction among nodes. + +### A Typical Data Writing Process + +To explain the relationship between vnode, mnode, TAOSC and application and their respective roles, the following is an analysis of a typical data writing process. + +![typical process of TDengine](message.png) +
Figure 2: Typical process of TDengine
+ +1. Application initiates a request to insert data through JDBC, ODBC, or other APIs. +2. TAOSC checks if meta data existing for the table in the cache. If so, go straight to Step 4. If not, TAOSC sends a get meta-data request to mnode. +3. Mnode returns the meta-data of the table to TAOSC. Meta-data contains the schema of the table, and also the vgroup information to which the table belongs (the vnode ID and the End Point of the dnode where the table belongs. If the number of replicas is N, there will be N groups of End Points). If TAOSC does not receive a response from the mnode for a long time, and there are multiple mnodes, TAOSC will send a request to the next mnode. +4. TAOSC initiates an insert request to master vnode. +5. After vnode inserts the data, it gives a reply to TAOSC, indicating that the insertion is successful. If TAOSC doesn't get a response from vnode for a long time, TAOSC will treat this node as offline. In this case, if there are multiple replicas of the inserted database, TAOSC will issue an insert request to the next vnode in vgroup. +6. TAOSC notifies APP that writing is successful. + +For Step 2 and 3, when TAOSC starts, it does not know the End Point of mnode, so it will directly initiate a request to the configured serving End Point of the cluster. If the dnode that receives the request does not have a mnode configured, it will inform the mnode EP list in a reply message, so that TAOSC will re-issue a request to obtain meta-data to the EP of another new mnode. + +For Step 4 and 5, without caching, TAOSC can't recognize the master in the virtual node group, so assumes that the first vnode is the master and sends a request to it. If this vnode is not the master, it will reply to the actual master as a new target where TAOSC shall send a request to. Once the reply of successful insertion is obtained, TAOSC will cache the information of master node. + +The above is the process of inserting data, and the processes of querying and computing are the same. TAOSC encapsulates and hides all these complicated processes, and it is transparent to applications. + +Through TAOSC caching mechanism, mnode needs to be accessed only when a table is accessed for the first time, so mnode will not become a system bottleneck. However, because schema and vgroup may change (such as load balancing), TAOSC will interact with mnode regularly to automatically update the cache. + +## Storage Model and Data Partitioning/Sharding + +### Storage Model + +The data stored by TDengine include collected time-series data, metadata related to database and tables, tag data, etc. These data are specifically divided into three parts: + +- Time-series data: stored in vnode and composed of data, head and last files. The amount of data is large and query amount depends on the application scenario. Out-of-order writing is allowed, but delete operation is not supported for the time being, and update operation is only allowed when database “update” parameter is set to 1. By adopting the model with **one table for each data collection point**, the data of a given time period is continuously stored, and the writing against one single table is a simple appending operation. Multiple records can be read at one time, thus ensuring the insert and query operation of a single data collection point with the best performance. +- Tag data: meta files stored in vnode. Four standard operations of create, read, update and delete are supported. The amount of data is not large. If there are N tables, there are N records, so all can be stored in memory. To make tag filtering efficient, TDengine supports multi-core and multi-threaded concurrent queries. As long as the computing resources are sufficient, even in face of millions of tables, the tag filtering results will return in milliseconds. +- Metadata: stored in mnode, including system node, user, DB, Table Schema and other information. Four standard operations of create, delete, update and read are supported. The amount of these data are not large and can be stored in memory, moreover, the query amount is not large because of the client cache. Therefore, TDengine uses centralized storage management, however, there will be no performance bottleneck. + +Compared with the typical NoSQL storage model, TDengine stores tag data and time-series data completely separately, which has two major advantages: + +- Reduce the redundancy of tag data storage significantly: general NoSQL database or time-series database adopts K-V storage, in which Key includes a timestamp, a device ID and various tags. Each record carries these duplicated tags, so storage space is wasted. Moreover, if the application needs to add, modify or delete tags on historical data, it has to traverse the data and rewrite them again, which is extremely expensive to operate. +- Aggregate data efficiently between multiple tables: when aggregating data between multiple tables, it first finds out the tables which satisfy the filtering conditions, and then find out the corresponding data blocks of these tables to greatly reduce the data sets to be scanned, thus greatly improving the aggregation efficiency. Moreover, tag data is managed and maintained in a full-memory structure, and tag data queries in tens of millions can return in milliseconds. + +### Data Sharding + +For large-scale data management, to achieve scale-out, it is generally necessary to adopt the Partitioning or Sharding strategy. TDengine implements data sharding via vnode, and time-series data partitioning via one data file for a time range. + +VNode (Virtual Data Node) is responsible for providing writing, query and computing functions for collected time-series data. To facilitate load balancing, data recovery and support heterogeneous environments, TDengine splits a data node into multiple vnodes according to its computing and storage resources. The management of these vnodes is done automatically by TDengine and is completely transparent to the application. + +For a single data collection point, regardless of the amount of data, a vnode (or vnode group, if the number of replicas is greater than 1) has enough computing resource and storage resource to process (if a 16-byte record is generated per second, the original data generated in one year will be less than 0.5 G), so TDengine stores all the data of a table (a data collection point) in one vnode instead of distributing the data to two or more dnodes. Moreover, a vnode can store data from multiple data collection points (tables), and the upper limit of the tables’ quantity for a vnode is one million. By design, all tables in a vnode belong to the same DB. On a data node, unless specially configured, the number of vnodes owned by a DB will not exceed the number of system cores. + +When creating a DB, the system does not allocate resources immediately. However, when creating a table, the system will check if there is an allocated vnode with free tablespace. If so, the table will be created in the vacant vnode immediately. If not, the system will create a new vnode on a dnode from the cluster according to the current workload, and then a table. If there are multiple replicas of a DB, the system does not create only one vnode, but a vgroup (virtual data node group). The system has no limit on the number of vnodes, which is just limited by the computing and storage resources of physical nodes. + +The meta data of each table (including schema, tags, etc.) is also stored in vnode instead of centralized storage in mnode. In fact, this means sharding of meta data, which is good for efficient and parallel tag filtering operations. + +### Data Partitioning + +In addition to vnode sharding, TDengine partitions the time-series data by time range. Each data file contains only one time range of time-series data, and the length of the time range is determined by DB's configuration parameter `“days”`. This method of partitioning by time rang is also convenient to efficiently implement the data retention policy. As long as the data file exceeds the specified number of days (system configuration parameter `“keep”`), it will be automatically deleted. Moreover, different time ranges can be stored in different paths and storage media, so as to facilitate the tiered-storage. Cold/hot data can be stored in different storage media to reduce the storage cost. + +In general, **TDengine splits big data by vnode and time range in two dimensions** to manage the data efficiently with horizontal scalability. + +### Load Balancing + +Each dnode regularly reports its status (including hard disk space, memory size, CPU, network, number of virtual nodes, etc.) to the mnode (virtual management node), so mnode knows the status of the entire cluster. Based on the overall status, when the mnode finds a dnode is overloaded, it will migrate one or more vnodes to other dnodes. During the process, TDengine services keep running and the data insertion, query and computing operations are not affected. + +If the mnode has not received the dnode status for a period of time, the dnode will be treated as offline. When offline lasts a certain period of time (configured by parameter `“offlineThreshold”`), the dnode will be forcibly removed from the cluster by mnode. If the number of replicas of vnodes on this dnode is greater than one, the system will automatically create new replicas on other dnodes to ensure the replica number. If there are other mnodes on this dnode and the number of mnodes replicas is greater than one, the system will automatically create new mnodes on other dnodes to ensure the replica number. + +When new data nodes are added to the cluster, with new computing and storage resources are added, the system will automatically start the load balancing process. + +The load balancing process does not require any manual intervention, and it is transparent to the application. **Note: load balancing is controlled by parameter “balance”, which determines to turn on/off automatic load balancing.** + +## Data Writing and Replication Process + +If a database has N replicas, thus a virtual node group has N virtual nodes, but only one as Master and all others are slaves. When the application writes a new record to system, only the Master vnode can accept the writing request. If a slave vnode receives a writing request, the system will notifies TAOSC to redirect. + +### Master vnode Writing Process + +Master Vnode uses a writing process as follows: + +![TDengine Master Writing Process](write_master.png) +
Figure 3: TDengine Master writing process
+ +1. Master vnode receives the application data insertion request, verifies, and moves to next step; +2. If the system configuration parameter `“walLevel”` is greater than 0, vnode will write the original request packet into database log file WAL. If walLevel is set to 2 and fsync is set to 0, TDengine will make WAL data written immediately to ensure that even system goes down, all data can be recovered from database log file; +3. If there are multiple replicas, vnode will forward data packet to slave vnodes in the same virtual node group, and the forwarded packet has a version number with data; +4. Write into memory and add the record to “skip list”; +5. Master vnode returns a confirmation message to the application, indicating a successful writing. +6. If any of Step 2, 3 or 4 fails, the error will directly return to the application. + +### Slave vnode Writing Process + +For a slave vnode, the write process as follows: + +![TDengine Slave Writing Process](write_slave.png) +
Figure 4: TDengine Slave Writing Process
+ +1. Slave vnode receives a data insertion request forwarded by Master vnode; +2. If the system configuration parameter `“walLevel”` is greater than 0, vnode will write the original request packet into database log file WAL. If walLevel is set to 2 and fsync is set to 0, TDengine will make WAL data written immediately to ensure that even system goes down, all data can be recovered from database log file; +3. Write into memory and add the record to “skip list”. + +Compared with Master vnode, slave vnode has no forwarding or reply confirmation step, means two steps less. But writing into memory and WAL is exactly the same. + +### Remote Disaster Recovery and IDC Migration + +As above Master and Slave processes discussed, TDengine adopts asynchronous replication for data synchronization. This method can greatly improve the writing performance, with no obvious impact from network delay. By configuring IDC and rack number for each physical node, it can be ensured that for a virtual node group, virtual nodes are composed of physical nodes from different IDC and different racks, thus implementing remote disaster recovery without other tools. + +On the other hand, TDengine supports dynamic modification of the replicas number. Once the number of replicas increases, the newly added virtual nodes will immediately enter the data synchronization process. After synchronization completed, added virtual nodes can provide services. In the synchronization process, master and other synchronized virtual nodes keep serving. With this feature, TDengine can provide IDC migration without service interruption. It is only necessary to add new physical nodes to the existing IDC cluster, and then remove old physical nodes after the data synchronization is completed. + +However, the asynchronous replication has a tiny time window where data can be lost. The specific scenario is as follows: + +1. Master vnode has finished its 5-step operations, confirmed the success of writing to APP, and then went down; +2. Slave vnode receives the write request, then processing fails before writing to the log in Step 2; +3. Slave vnode will become the new master, thus losing one record. + +In theory, for asynchronous replication, there is no guarantee to prevent data loss. However, this window is extremely small, only if mater and slave fail at the same time, and just confirm the successful write to the application before. + +Note: Remote disaster recovery and no-downtime IDC migration are only supported by Enterprise Edition. **Hint: This function is not available yet** + +### Master/slave Selection + +Vnode maintains a version number. When memory data is persisted, the version number will also be persisted. For each data update operation, whether it is time-series data or metadata, this version number will be increased by one. + +When a vnode starts, the roles (master, slave) are uncertain, and the data is in an unsynchronized state. It’s necessary to establish TCP connections with other nodes in the virtual node group and exchange status, including version and its own roles. Through the exchange, the system implements a master-selection process. The rules are as follows: + +1. If there’s only one replica, it’s always master +2. When all replicas are online, the one with latest version is master +3. Over half of online nodes are virtual nodes, and some virtual node is slave, it will automatically become master +4. For 2 and 3, if multiple virtual nodes meet the requirement, the first vnode in virtual node group list will be selected as master + +### Synchronous Replication + +For scenarios with strong data consistency requirements, asynchronous data replication is not applicable, because there is a small probability of data loss. So, TDengine provides a synchronous replication mechanism for users. When creating a database, in addition to specifying the number of replicas, user also needs to specify a new parameter “quorum”. If quorum is greater than one, it means that every time the Master forwards a message to the replica, it needs to wait for “quorum-1” reply confirms before informing the application that data has been successfully written in slave. If “quorum-1” reply confirms are not received within a certain period of time, the master vnode will return an error to the application. + +With synchronous replication, performance of system will decrease and latency will increase. Because metadata needs strong consistent, the default for data synchronization between mnodes is synchronous replication. + +## Caching and Persistence + +### Caching + +TDengine adopts a time-driven cache management strategy (First-In-First-Out, FIFO), also known as a Write-driven Cache Management Mechanism. This strategy is different from the read-driven data caching mode (Least-Recent-Used, LRU), which directly put the most recently written data in the system buffer. When the buffer reaches a threshold, the earliest data are written to disk in batches. Generally speaking, for the use of IoT data, users are most concerned about the newly generated data, that is, the current status. TDengine takes full advantage of this feature to put the most recently arrived (current state) data in the buffer. + +TDengine provides millisecond-level data collecting capability to users through query functions. Putting the recently arrived data directly in the buffer can respond to users' analysis query for the latest piece or batch of data more quickly, and provide faster database query response capability as a whole. In this sense, **TDengine can be used as a data cache by setting appropriate configuration parameters without deploying Redis or other additional cache systems**, which can effectively simplify the system architecture and reduce the operation costs. It should be noted that after the TDengine is restarted, the buffer of the system will be emptied, the previously cached data will be written to disk in batches, and the previously cached data will not be reloaded into the buffer as so in a proprietary key-value cache system. + +Each vnode has its own independent memory, and it is composed of multiple memory blocks of fixed size, and different vnodes are completely isolated. When writing data, similar to the writing of logs, data is sequentially added to memory, but each vnode maintains its own skip list for quick search. When more than one third of the memory block are used, the disk writing operation will start, and the subsequent writing operation is carried out in a new memory block. By this design, one third of the memory blocks in a vnode keep the latest data, so as to achieve the purpose of caching and quick search. The number of memory blocks of a vnode is determined by the configuration parameter “blocks”, and the size of memory blocks is determined by the configuration parameter “cache”. + +### Persistent Storage + +TDengine uses a data-driven method to write the data from buffer into hard disk for persistent storage. When the cached data in vnode reaches a certain volume, TDengine will also pull up the disk-writing thread to write the cached data into persistent storage in order not to block subsequent data writing. TDengine will open a new database log file when the data is written, and delete the old database log file after written successfully to avoid unlimited log growth. + +To make full use of the characteristics of time-series data, TDengine splits the data stored in persistent storage by a vnode into multiple files, each file only saves data for a fixed number of days, which is determined by the system configuration parameter `“days”`. By so, for the given start and end date of a query, you can locate the data files to open immediately without any index, thus greatly speeding up reading operations. + +For time-series data, there is generally a retention policy, which is determined by the system configuration parameter `“keep”`. Data files exceeding this set number of days will be automatically deleted by the system to free up storage space. + +Given “days” and “keep” parameters, the total number of data files in a vnode is: keep/days. The total number of data files should not be too large or too small. 10 to 100 is appropriate. Based on this principle, reasonable days can be set. In the current version, parameter “keep” can be modified, but parameter “days” cannot be modified once it is set. + +In each data file, the data of a table is stored by blocks. A table can have one or more data file blocks. In a file block, data is stored in columns, occupying a continuous storage space, thus greatly improving the reading speed. The size of file block is determined by the system parameter `“maxRows”` (the maximum number of records per block), and the default value is 4096. This value should not be too large or too small. If it is too large, the data locating in search will cost longer; if too small, the index of data block is too large, and the compression efficiency will be low with slower reading speed. + +Each data file (with a .data postfix) has a corresponding index file (with a .head postfix). The index file has summary information of a data block for each table, recording the offset of each data block in the data file, start and end time of data and other information, so as to lead system quickly locate the data to be found. Each data file also has a corresponding last file (with a .last postfix), which is designed to prevent data block fragmentation when written in disk. If the number of written records from a table does not reach the system configuration parameter `“minRows”` (minimum number of records per block), it will be stored in the last file first. When write to disk next time, the newly written records will be merged with the records in last file and then written into data file. + +When data is written to disk, it is decided whether to compress the data according to system configuration parameter `“comp”`. TDengine provides three compression options: no compression, one-stage compression and two-stage compression, corresponding to comp values of 0, 1 and 2 respectively. One-stage compression is carried out according to the type of data. Compression algorithms include delta-delta coding, simple 8B method, zig-zag coding, LZ4 and other algorithms. Two-stage compression is based on one-stage compression and compressed by general compression algorithm, which has higher compression ratio. + +### Tiered Storage + +By default, TDengine saves all data in /var/lib/taos directory, and the data files of each vnode are saved in a different directory under this directory. In order to expand the storage space, minimize the bottleneck of file reading and improve the data throughput rate, TDengine can configure the system parameter “dataDir” to allow multiple mounted hard disks to be used by system at the same time. In addition, TDengine also provides the function of tiered data storage, i.e. storage on different storage media according to the time stamps of data files. For example, the latest data is stored on SSD, the data for more than one week is stored on local hard disk, and the data for more than four weeks is stored on network storage device, thus reducing the storage cost and ensuring efficient data access. The movement of data on different storage media is automatically done by the system and completely transparent to applications. Tiered storage of data is also configured through the system parameter “dataDir”. + +dataDir format is as follows: +``` +dataDir data_path [tier_level] +``` + +Where data_path is the folder path of mount point and tier_level is the media storage-tier. The higher the media storage-tier, means the older the data file. Multiple hard disks can be mounted at the same storage-tier, and data files on the same storage-tier are distributed on all hard disks within the tier. TDengine supports up to 3 tiers of storage, so tier_level values are 0, 1, and 2. When configuring dataDir, there must be only one mount path without specifying tier_level, which is called special mount disk (path). The mount path defaults to level 0 storage media and contains special file links, which cannot be removed, otherwise it will have a devastating impact on the written data. + +Suppose a physical node with six mountable hard disks/mnt/disk1,/mnt/disk2, …,/mnt/disk6, where disk1 and disk2 need to be designated as level 0 storage media, disk3 and disk4 are level 1 storage media, and disk5 and disk6 are level 2 storage media. Disk1 is a special mount disk, you can configure it in/etc/taos/taos.cfg as follows: + +``` +dataDir /mnt/disk1/taos +dataDir /mnt/disk2/taos 0 +dataDir /mnt/disk3/taos 1 +dataDir /mnt/disk4/taos 1 +dataDir /mnt/disk5/taos 2 +dataDir /mnt/disk6/taos 2 +``` + +Mounted disks can also be a non-local network disk, as long as the system can access it. + +Note: Tiered Storage is only supported in Enterprise Edition + +## Data Query + +TDengine provides a variety of query processing functions for tables and STables. In addition to common aggregation queries, TDengine also provides window queries and statistical aggregation functions for time-series data. The query processing of TDengine needs the collaboration of client, vnode and mnode. + +### Single Table Query + +The parsing and verification of SQL statements are completed on the client side. SQL statements are parsed and generate an Abstract Syntax Tree (AST), which is then checksummed. Then request metadata information (table metadata) for the table specified in the query from management node (mnode). + +According to the End Point information in metadata information, the query request is serialized and sent to the data node (dnode) where the table is located. After receiving the query, the dnode identifies the virtual node (vnode) pointed to and forwards the message to the query execution queue of the vnode. The query execution thread of vnode establishes the basic query execution environment, immediately returns the query request and starts executing the query at the same time. + +When client obtains query result, the worker thread in query execution queue of dnode will wait for the execution of vnode execution thread to complete before returning the query result to the requesting client. + +### Aggregation by Time Axis, Downsampling, Interpolation + +The remarkable feature that time-series data is different from ordinary data is that each record has a timestamp, so aggregating data with timestamps on the time axis is an important and distinct feature from common databases. From this point of view, it is similar to the window query of stream computing engine. + +The keyword `interval` is introduced into TDengine to split fixed length time windows on time axis, and the data are aggregated based on time windows, and the data within window range are aggregated as needed. For example: + +```mysql +select count(*) from d1001 interval(1h); +``` + +For the data collected by device D1001, the number of records stored per hour is returned by a 1-hour time window. + +In application scenarios where query results need to be obtained continuously, if there is data missing in a given time interval, the data results in this interval will also be lost. TDengine provides a strategy to interpolate the results of timeline aggregation calculation. The results of time axis aggregation can be interpolated by using keyword Fill. For example: + +```mysql +select count(*) from d1001 interval(1h) fill(prev); +``` + +For the data collected by device D1001, the number of records per hour is counted. If there is no data in a certain hour, statistical data of the previous hour is returned. TDengine provides forward interpolation (prev), linear interpolation (linear), NULL value populating (NULL), and specific value populating (value). + +### Multi-table Aggregation Query + +TDengine creates a separate table for each data collection point, but in practical applications, it is often necessary to aggregate data from different data collection points. In order to perform aggregation operations efficiently, TDengine introduces the concept of STable. STable is used to represent a specific type of data collection point. It is a table set containing multiple tables. The schema of each table in the set is the same, but each table has its own static tag. The tags can be multiple and be added, deleted and modified at any time. Applications can aggregate or statistically operate all or a subset of tables under a STABLE by specifying tag filters, thus greatly simplifying the development of applications. The process is shown in the following figure: + +![Diagram of multi-table aggregation query](multi_tables.png) +
Figure 5: Diagram of multi-table aggregation query
+ +1. Application sends a query condition to system; +2. TAOSC sends the STable name to Meta Node(management node); +3. Management node sends the vnode list owned by the STable back to TAOSC; +4. TAOSC sends the computing request together with tag filters to multiple data nodes corresponding to these vnodes; +5. Each vnode first finds out the set of tables within its own node that meet the tag filters from memory, then scans the stored time-series data, completes corresponding aggregation calculations, and returns result to TAOSC; +6. TAOSC finally aggregates the results returned by multiple data nodes and send them back to application. + +Since TDengine stores tag data and time-series data separately in vnode, by filtering tag data in memory, the set of tables that need to participate in aggregation operation is first found, which greatly reduces the volume of data scanned and improves aggregation speed. At the same time, because the data is distributed in multiple vnodes/dnodes, the aggregation operation is carried out concurrently in multiple vnodes, which further improves the aggregation speed. Aggregation functions for ordinary tables and most operations are applicable to STables. The syntax is exactly the same. Please see TAOS SQL for details. + +### Precomputation + +In order to effectively improve the performance of query processing, based-on the unchangeable feature of IoT data, statistical information of data stored in data block is recorded in the head of data block, including max value, min value, and sum. We call it a precomputing unit. If the query processing involves all the data of a whole data block, the pre-calculated results are directly used, and no need to read the data block contents at all. Since the amount of pre-calculated data is much smaller than the actual size of data block stored on disk, for query processing with disk IO as bottleneck, the use of pre-calculated results can greatly reduce the pressure of reading IO and accelerate the query process. The precomputation mechanism is similar to the index BRIN (Block Range Index) of PostgreSQL. + diff --git a/docs-en/21-tdinternal/30-iot-big-data.md b/docs-en/21-tdinternal/30-iot-big-data.md new file mode 100644 index 0000000000000000000000000000000000000000..4bdf5cfba98234c9d843634b5210ca3dae94d870 --- /dev/null +++ b/docs-en/21-tdinternal/30-iot-big-data.md @@ -0,0 +1,10 @@ +--- +title: IoT Big Data +description: "Characteristics of IoT Big Data, why general big data platform does not work well for IoT? The required features for an IoT Big Data Platform" +--- + +- [Characteristics of IoT Big Data](https://tdengine.com/2019/07/09/86.html) +- [Why don’t General Big Data Platforms Fit IoT Scenarios?](https://tdengine.com/2019/07/09/92.html) +- [Why TDengine is the Best Choice for IoT Big Data Processing?](https://tdengine.com/2019/07/09/94.html) +- [Why Redis, Kafka, Spark aren’t Needed if TDengine is Used in the IoT Platform?](https://tdengine.com/2019/07/09/96.html) + diff --git a/docs-en/21-tdinternal/_category_.yml b/docs-en/21-tdinternal/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..38a7f9a7641200e47868a693f3935216a333f44d --- /dev/null +++ b/docs-en/21-tdinternal/_category_.yml @@ -0,0 +1 @@ +label: Inside TDengine diff --git a/docs-en/21-tdinternal/dnode.png b/docs-en/21-tdinternal/dnode.png new file mode 100644 index 0000000000000000000000000000000000000000..cea87dcccba5d2761996e5dde998022d86487eb9 Binary files /dev/null and b/docs-en/21-tdinternal/dnode.png differ diff --git a/docs-en/21-tdinternal/index.md b/docs-en/21-tdinternal/index.md new file mode 100644 index 0000000000000000000000000000000000000000..999d6f89ff57164d6e0372620504c8ecc9de7c9b --- /dev/null +++ b/docs-en/21-tdinternal/index.md @@ -0,0 +1,10 @@ +--- +title: TDengine Inside +--- + +```mdx-code-block +import DocCardList from '@theme/DocCardList'; +import {useCurrentSidebarCategory} from '@docusaurus/theme-common'; + + +``` \ No newline at end of file diff --git a/docs-en/21-tdinternal/message.png b/docs-en/21-tdinternal/message.png new file mode 100644 index 0000000000000000000000000000000000000000..715a8bd37ee9fe7e96eacce4e7ff563fedeefbee Binary files /dev/null and b/docs-en/21-tdinternal/message.png differ diff --git a/docs-en/21-tdinternal/modules.png b/docs-en/21-tdinternal/modules.png new file mode 100644 index 0000000000000000000000000000000000000000..10ae4703a6cbbf66afea325ce4c0f919f7769a07 Binary files /dev/null and b/docs-en/21-tdinternal/modules.png differ diff --git a/docs-en/21-tdinternal/multi_tables.png b/docs-en/21-tdinternal/multi_tables.png new file mode 100644 index 0000000000000000000000000000000000000000..0cefaab6a9a4cdd671c671f7c6186dea41415ff0 Binary files /dev/null and b/docs-en/21-tdinternal/multi_tables.png differ diff --git a/docs-en/21-tdinternal/replica-forward.png b/docs-en/21-tdinternal/replica-forward.png new file mode 100644 index 0000000000000000000000000000000000000000..bf616e030b130603eceb5dccfd30b4a1dfa68ea5 Binary files /dev/null and b/docs-en/21-tdinternal/replica-forward.png differ diff --git a/docs-en/21-tdinternal/replica-master.png b/docs-en/21-tdinternal/replica-master.png new file mode 100644 index 0000000000000000000000000000000000000000..cb33f1ce98661563693215d8fc73b003235c7668 Binary files /dev/null and b/docs-en/21-tdinternal/replica-master.png differ diff --git a/docs-en/21-tdinternal/replica-restore.png b/docs-en/21-tdinternal/replica-restore.png new file mode 100644 index 0000000000000000000000000000000000000000..1558e5ed0108d23efdc6b5d9ea0e44a1dff45d28 Binary files /dev/null and b/docs-en/21-tdinternal/replica-restore.png differ diff --git a/docs-en/21-tdinternal/structure.png b/docs-en/21-tdinternal/structure.png new file mode 100644 index 0000000000000000000000000000000000000000..4fc8f47ab0a30d95b85ba1d85105726ed981e56e Binary files /dev/null and b/docs-en/21-tdinternal/structure.png differ diff --git a/docs-en/21-tdinternal/vnode.png b/docs-en/21-tdinternal/vnode.png new file mode 100644 index 0000000000000000000000000000000000000000..e6148d4907cf9a18bc52251f712d5c685651b7f5 Binary files /dev/null and b/docs-en/21-tdinternal/vnode.png differ diff --git a/docs-en/21-tdinternal/write_master.png b/docs-en/21-tdinternal/write_master.png new file mode 100644 index 0000000000000000000000000000000000000000..ff2dfc20bfc2ecf956a2aab1a8965a7bbcae4387 Binary files /dev/null and b/docs-en/21-tdinternal/write_master.png differ diff --git a/docs-en/21-tdinternal/write_slave.png b/docs-en/21-tdinternal/write_slave.png new file mode 100644 index 0000000000000000000000000000000000000000..cacb2cb6bcc4f4d934e979862387e1345bbac078 Binary files /dev/null and b/docs-en/21-tdinternal/write_slave.png differ diff --git a/docs-en/25-application/01-telegraf.md b/docs-en/25-application/01-telegraf.md new file mode 100644 index 0000000000000000000000000000000000000000..718e04ecd3dbd2a72feba3f5297d9da33a94ba83 --- /dev/null +++ b/docs-en/25-application/01-telegraf.md @@ -0,0 +1,83 @@ +--- +sidebar_label: TDengine + Telegraf + Grafana +title: Quickly Build IT DevOps Visualization System with TDengine + Telegraf + Grafana +--- + +## Background + +TDengine is a big data platform designed and optimized for IoT (Internet of Things), Vehicle Telematics, Industrial Internet, IT DevOps, etc. by TAOSData. Since it opened its source code in July 2019, it has won the favor of a large number of time-series data developers with its innovative data modeling design, convenient installation, easy-to-use programming interface, and powerful data writing and query performance. + +IT DevOps metric data usually are time sensitive, for example: + +- System resource metrics: CPU, memory, IO, bandwidth, etc. +- Software system metrics: health status, number of connections, number of requests, number of timeouts, number of errors, response time, service type, and other business-related metrics. + +Current mainstream IT DevOps system usually include a data collection module, a data persistent module, and a visualization module; Telegraf and Grafana are one of the most popular data collection modules and visualization modules, respectively. The data persistent module is available in a wide range of options, with OpenTSDB or InfluxDB being the most popular. TDengine, as an emerging time-series big data platform, has the advantages of high performance, high reliability, easy management and easy maintenance. + +This article introduces how to quickly build a TDengine + Telegraf + Grafana based IT DevOps visualization system without writing even a single line of code and by simply modifying a few lines of configuration files. The architecture is as follows. + +![IT-DevOps-Solutions-Telegraf.png](/img/IT-DevOps-Solutions-Telegraf.png) + +## Installation steps + +### Installing Telegraf, Grafana and TDengine + +To install Telegraf, Grafana, and TDengine, please refer to the relevant official documentation. + +### Telegraf + +Please refer to the [official documentation](https://portal.influxdata.com/downloads/). + +### Grafana + +Please refer to the [official documentation](https://grafana.com/grafana/download). + +### TDengine + +Download the latest TDengine-server 2.4.0.x or above from the [Downloads](http://taosdata.com/cn/all-downloads/) page on the TAOSData website and install it. + +## Data Connection Setup + +### Download TDengine plug-in to grafana plug-in directory + +```bash +1. wget -c https://github.com/taosdata/grafanaplugin/releases/download/v3.1.3/tdengine-datasource-3.1.3.zip +2. sudo unzip tdengine-datasource-3.1.3.zip -d /var/lib/grafana/plugins/ +3. sudo chown grafana:grafana -R /var/lib/grafana/plugins/tdengine +4. echo -e "[plugins]\nallow_loading_unsigned_plugins = tdengine-datasource\n" | sudo tee -a /etc/grafana/grafana.ini +5. sudo systemctl restart grafana-server.service +``` + +### Modify /etc/telegraf/telegraf.conf + +For the configuration method, add the following text to `/etc/telegraf/telegraf.conf`, where `database name` should be the name where you want to store Telegraf data in TDengine, `TDengine server/cluster host`, `username` and `password` please fill in the actual TDengine values. + +```text +[[outputs.http]] + url = "http://:6041/influxdb/v1/write?db=" + method = "POST" + timeout = "5s" + username = "" + password = "" + data_format = "influx" + influx_max_line_bytes = 250 +``` + +Then restart telegraf: + +```bash +sudo systemctl start telegraf +``` + +### Importing the Dashboard + +Log in to the Grafana interface using a web browser at `IP:3000`, with the system's initial username and password being `admin/admin`. +Click on the gear icon on the left and select `Plugins`, you should find the TDengine data source plugin icon. +Click on the plus icon on the left and select `Import` to get the data from `https://github.com/taosdata/grafanaplugin/blob/master/examples/telegraf/grafana/dashboards/telegraf-dashboard- v0.1.0.json`, download the dashboard JSON file and import it. You will then see the dashboard in the following screen. + +![IT-DevOps-Solutions-telegraf-dashboard.png](/img/IT-DevOps-Solutions-telegraf-dashboard.png) + +## Wrap-up + +The above demonstrates how to quickly build a IT DevOps visualization system. Thanks to the new schemaless protocol parsing feature in TDengine version 2.4.0.0 and the powerful ecological software adaptation capability, users can build an efficient and easy-to-use IT DevOps visualization system in just a few minutes. +Please refer to the official documentation and product implementation cases for other features. diff --git a/docs-en/25-application/02-collectd.md b/docs-en/25-application/02-collectd.md new file mode 100644 index 0000000000000000000000000000000000000000..2ac37618fafe11e71b215313e53f89b6c302f7cb --- /dev/null +++ b/docs-en/25-application/02-collectd.md @@ -0,0 +1,104 @@ +--- +sidebar_label: TDengine + collectd/StatsD + Grafana +title: Quickly build an IT DevOps visualization system using TDengine + collectd/StatsD + Grafana +--- + +## Background + +TDengine is a big data platform designed and optimized for IoT (Internet of Things), Vehicle Telematics, Industrial Internet, IT DevOps, etc. by TAOSData. Since it opened its source code in July 2019, it has won the favor of a large number of time-series data developers with its innovative data modeling design, convenient installation, easy-to-use programming interface, and powerful data writing and query performance. + +IT DevOps metric data usually are time sensitive, for example: + +- System resource metrics: CPU, memory, IO, bandwidth, etc. +- Software system metrics: health status, number of connections, number of requests, number of timeouts, number of errors, response time, service type, and other business-related metrics. + +The current mainstream IT DevOps visualization system usually contains a data collection module, a data persistent module, and a visual display module. collectd/StatsD, as an old-fashion open source data collection tool, has a wide user base. However, collectd/StatsD has limited functionality, and often needs to be combined with Telegraf, Grafana, and a time-series database to build a complete monitoring system. +The new version of TDengine supports multiple data protocols and can accept data from collectd and StatsD directly, and provides Grafana dashboard for graphical display. + +This article introduces how to quickly build an IT DevOps visualization system based on TDengine + collectd / StatsD + Grafana without writing even a single line of code but by simply modifying a few lines of configuration files. The architecture is shown in the following figure. + +![IT-DevOps-Solutions-Collectd-StatsD.png](/img/IT-DevOps-Solutions-Collectd-StatsD.png) + +## Installation Steps + +To install collectd, StatsD, Grafana, and TDengine, please refer to the official documentation. + +### Installing collectd + +Please refer to the [official documentation](https://collectd.org/documentation.shtml). + +### Installing StatsD + +Please refer to the [official documentation](https://github.com/statsd/statsd). + +### Install Grafana + +Please refer to the [official documentation](https://grafana.com/grafana/download). + +### Install TDengine + +Download the latest TDengine-server 2.4.0.x or above from the [Downloads](http://taosdata.com/cn/all-downloads/) page on the TAOSData website and install it. + +## Data Connection Setup + +### Copy the TDengine plugin to the grafana plugin directory + +```bash +1. wget -c https://github.com/taosdata/grafanaplugin/releases/download/v3.1.3/tdengine-datasource-3.1.3.zip +2. sudo unzip tdengine-datasource-3.1.3.zip -d /var/lib/grafana/plugins/ +3. sudo chown grafana:grafana -R /var/lib/grafana/plugins/tdengine +4. echo -e "[plugins]\nallow_loading_unsigned_plugins = tdengine-datasource\n" | sudo tee -a /etc/grafana/grafana.ini +5. sudo systemctl restart grafana-server.service +``` + +### Configure collectd + +Add the following to the `/etc/collectd/collectd.conf` file, where the `host` and `port` should be the actual values of the TDengine and taosAdapter configurations. + +```text +LoadPlugin network + + Server "" "" + + +sudo systemctl start collectd +``` + +### Configure StatsD + +Start StatsD after adding the following to the `config.js` file, where the `host` and `port` are the actual values of the TDengine and taosAdapter configurations. + +```text +backends section add ". /backends/repeater" +Add { host:'', port: } to the repeater section +``` + +### Importing the Dashboard + +Use a web browser to access the server running Grafana on port 3000 `host:3000` to log into the Grafana interface with the initial system username and password of `admin/admin`. +Click on the gear icon on the left and select `Plugins`, you should find the TDengine data source plugin icon. + +#### Importing the collectd dashboard + +Download the dashboard json from `https://github.com/taosdata/grafanaplugin/blob/master/examples/collectd/grafana/dashboards/collect-metrics-with-tdengine-v0.1.0.json`, click the plus icon on the left and select Import, follow the instructions to import the JSON file. After that, you can see +The dashboard can be seen in the following screen. + +![IT-DevOps-Solutions-collectd-dashboard.png](/img/IT-DevOps-Solutions-collectd-dashboard.png) + +#### import collectd dashboard + +Download the dashboard json file from `https://github.com/taosdata/grafanaplugin/blob/master/examples/collectd/grafana/dashboards/collect-metrics-with-tdengine-v0.1.0.json`. Download the dashboard json file, click the plus icon on the left side and select `Import`, and follow the interface prompts to select the JSON file to import. After that, you can see +dashboard with the following interface. + +![IT-DevOps-Solutions-collectd-dashboard.png](/img/IT-DevOps-Solutions-collectd-dashboard.png) + +#### Importing the StatsD dashboard + +Download the dashboard json from `https://github.com/taosdata/grafanaplugin/blob/master/examples/statsd/dashboards/statsd-with-tdengine-v0.1.0.json`. Click on the plus icon on the left and select `Import`, and follow the interface prompts to import the JSON file. You will then see the dashboard in the following screen. +![IT-DevOps-Solutions-statsd-dashboard.png](/img/IT-DevOps-Solutions-statsd-dashboard.png) + +## Wrap-up + +TDengine, as an emerging time-series big data platform, has the advantages of high performance, high reliability, easy management and easy maintenance. Thanks to the new schemaless protocol parsing function in TDengine version 2.4.0.0 and the powerful ecological software adaptation capability, users can build an efficient and easy-to-use IT DevOps visualization system or adapt to an existing system in just a few minutes. + +For TDengine's powerful data writing and querying performance and other features, please refer to the official documentation and successful product implementation cases. diff --git a/docs-en/25-application/03-immigrate.md b/docs-en/25-application/03-immigrate.md new file mode 100644 index 0000000000000000000000000000000000000000..4cfeb892d821a1e5b7d5250615e7122e64b9882d --- /dev/null +++ b/docs-en/25-application/03-immigrate.md @@ -0,0 +1,435 @@ +--- +sidebar_label: OpenTSDB Migration to TDengine +title: Best Practices for Migrating OpenTSDB Applications to TDengine +--- + +As a distributed, scalable, HBase-based distributed time-series database software, thanks to its first-mover advantage, OpenTSDB has been introduced and widely used in DevOps by people. However, using new technologies like cloud computing, microservices, and containerization technology with rapid development. Enterprise-level services are becoming more and more diverse. The architecture is becoming more complex. + +From this situation, it increasingly plagues to use of OpenTSDB as a DevOps backend storage for monitoring by performance issues and delayed feature upgrades. The resulting increase in application deployment costs and reduced operational efficiency. +These problems are becoming increasingly severe as the system scales up. + +To meet the fast-growing IoT big data market and technical needs, TAOSData developed an innovative big-data processing product, **TDengine**. + +After learning the advantages of many traditional relational databases and NoSQL databases, stream computing engines, and message queues, TDengine has its unique benefits in time-series big data processing. TDengine can effectively solve the problems currently encountered by OpenTSDB. + +Compared with OpenTSDB, TDengine has the following distinctive features. + +- Performance of data writing and querying far exceeds that of OpenTSDB. +- Efficient compression mechanism for time-series data, which compresses less than 1/5 of the storage space on disk. +- The installation and deployment are straightforward. A single installation package can complete the installation and deployment and does not rely on other third-party software. The entire installation and deployment process in a few seconds; +- The built-in functions cover all of OpenTSDB's query functions. And support more time-series data query functions, scalar functions, and aggregation functions. And support advanced query functions such as multiple time-window aggregations, join query, expression operation, multiple group aggregation, user-defined sorting, and user-defined functions. Adopting SQL-like syntax rules is more straightforward and has no learning cost. +- Supports up to 128 tags, with a total tag length of 16 KB. +- In addition to the REST interface, it also provides interfaces to Java, Python, C, Rust, Go, C# and other languages. Its supports a variety of enterprise-class standard connector protocols such as JDBC. + +If we migrate the applications originally running on OpenTSDB to TDengine, we will effectively reduce the compute and storage resource consumption and the number of deployed servers. And will also significantly reduce the operation and maintenance costs, making operation and maintenance management more straightforward and more accessible, and considerably reducing the total cost of ownership. Like OpenTSDB, TDengine has also been open-sourced, including the stand-alone version and the cluster version source code. So there is no need to be concerned about the vendor-lock problem. + +We will explain how to migrate OpenTSDB applications to TDengine quickly, securely, and reliably without coding, using the most typical DevOps scenarios. Subsequent chapters will go into more depth to facilitate migration for non-DevOps systems. + +## DevOps Application Quick Migration + +### 1. Typical Application Scenarios + +The following figure (Figure 1) shows the system's overall architecture for a typical DevOps application scenario. + +**Figure 1. Typical architecture in a DevOps scenario** +![IT-DevOps-Solutions-Immigrate-OpenTSDB-Arch](/img/IT-DevOps-Solutions-Immigrate-OpenTSDB-Arch.jpg "Figure 1. Typical architecture in a DevOps scenario") + +In this application scenario, there are Agent tools deployed in the application environment to collect machine metrics, network metrics, and application metrics. Data collectors to aggregate information collected by agents, systems for persistent data storage and management, and tools for monitoring data visualization (e.g., Grafana, etc.). + +The agents deployed in the application nodes are responsible for providing operational metrics from different sources to collectd/Statsd. And collectd/StatsD is accountable for pushing the aggregated data to the OpenTSDB cluster system and then visualizing the data using the visualization kanban board software, Grafana. + +### 2. Migration Services + +- **TDengine installation and deployment** + +First of all, please install TDengine. Download the latest stable version of TDengine from the official website and install it. For help with using various installation packages, please refer to the blog ["Installation and Uninstallation of TDengine Multiple Installation Packages"](https://www.taosdata.com/blog/2019/08/09/566.html). + +Note that once the installation is complete, do not start the `taosd` service immediately, but after properly configuring the parameters. + +- **Adjusting the data collector configuration** + +TDengine version 2.4 and later version includes `taosAdapter`. taosAdapter is a stateless, rapidly elastic, and scalable component. taosAdapter supports Influxdb's Line Protocol and OpenTSDB's telnet/JSON writing protocol specification, providing rich data access capabilities, effectively saving user migration costs and reducing the difficulty of user migration. + +Users can flexibly deploy taosAdapter instances according to their requirements to rapidly improve the throughput of data writes in conjunction with the needs of scenarios and provide guarantees for data writes in different application scenarios. + +Through taosAdapter, users can directly push the data collected by `collectd` or `StatsD` to TDengine to achieve seamless migration of application scenarios, which is very easy and convenient. taosAdapter also supports Telegraf, Icinga, TCollector, and node_exporter data. For more details, please refer to [taosAdapter](/reference/taosadapter/). + +If using collectd, modify the configuration file in its default location `/etc/collectd/collectd.conf` to point to the IP address and port of the node where to deploy taosAdapter. For example, assuming the taosAdapter IP address is 192.168.1.130 and port 6046, configure it as follows. + +```html +LoadPlugin write_tsdb + + + Host "192.168.1.130" Port "6046" HostTags "status=production" StoreRates + false AlwaysAppendDS false + + +``` + +You can use collectd and push the data to taosAdapter utilizing the push to OpenTSDB plugin. taosAdapter will call the API to write the data to TDengine, thus completing the writing of the data. If you are using StatsD, adjust the profile information accordingly. + +- **Tuning the Dashboard system** + +After writing the data to TDengine properly, you can adapt Grafana to visualize the data written to TDengine. To obtain and use the Grafana plugin provided by TDengine, please refer to [Links to other tools](/third-party/grafana). + +TDengine provides two sets of Dashboard templates by default, and users only need to import the templates from the Grafana directory into Grafana to activate their use. + +**Importing Grafana Templates** Figure 2. +![](/img/IT-DevOps-Solutions-Immigrate-OpenTSDB-Dashboard.jpg "Figure 2. Importing a Grafana Template") + +After the above steps, you completed the migration to replace OpenTSDB with TDengine. You can see that the whole process is straightforward, there is no need to write any code, and only some configuration files need to be adjusted to meet the migration work. + +### 3. Post-migration architecture + +After completing the migration, the figure below (Figure 3) shows the system's overall architecture. The whole process of the acquisition side, the data writing, and the monitoring and presentation side are all kept stable, except for a few configuration adjustments, which do not involve any critical changes or alterations. OpenTSDB to TDengine migration action, using TDengine more powerful processing power and query performance. + +In most DevOps scenarios, if you have a small OpenTSDB cluster (3 or fewer nodes) for providing the storage layer of DevOps and rely on OpenTSDB to give a data persistence layer and query capabilities, you can safely replace OpenTSDB with TDengine. TDengine will save more compute and storage resources. With the same compute resource allocation, a single TDengine can meet the service capacity provided by 3 to 5 OpenTSDB nodes. If the scale is more prominent, then TDengine clustering is required. + +Suppose your application is particularly complex, or the application domain is not a DevOps scenario. You can continue reading subsequent chapters for a more comprehensive and in-depth look at the advanced topics of migrating an OpenTSDB application to TDengine. + +**Figure 3. System architecture after migration** +![IT-DevOps-Solutions-Immigrate-TDengine-Arch](/img/IT-DevOps-Solutions-Immigrate-TDengine-Arch.jpg "Figure 3. System architecture after migration completion") + +## Migration evaluation and strategy for other scenarios + +### 1. Differences between TDengine and OpenTSDB + +This chapter describes the differences between OpenTSDB and TDengine at the system functionality level. After reading this chapter, you can fully evaluate whether you can migrate some complex OpenTSDB-based applications to TDengine, and what you should pay attention to after migration. + +TDengine currently only supports Grafana for visual kanban rendering, so if your application uses front-end kanban boards other than Grafana (e.g., [TSDash](https://github.com/facebook/tsdash), [Status Wolf](https://github.com/box/StatusWolf), etc.). You cannot directly migrate those front-end kanbans to TDengine, and the front-end kanban will need to be ported to Grafana to work correctly. + +TDengine version 2.3.0.x only supports collectd and StatsD as data collection aggregation software but will provide more data collection aggregation software in the future. If you use other data aggregators on the collection side, your application needs to be ported to these two data aggregation systems to write data correctly. +In addition to the two data aggregator software protocols mentioned above, TDengine also supports writing data directly via InfluxDB's line protocol and OpenTSDB's data writing protocol, JSON format. You can rewrite the logic on the data push side to write data using the line protocols supported by TDengine. + +In addition, if your application uses the following features of OpenTSDB, you need to understand the following considerations before migrating your application to TDengine. + +1. `/api/stats`: If your application uses this feature to monitor the service status of OpenTSDB, and you have built the relevant logic to link the processing in your application, then this part of the status reading and fetching logic needs to be re-adapted to TDengine. TDengine provides a new mechanism for handling cluster state monitoring to meet the monitoring and maintenance needs of your application. +2. `/api/tree`: If you rely on this feature of OpenTSDB for the hierarchical organization and maintenance of timelines, you cannot migrate it directly to TDengine, which uses a database -> super table -> sub-table hierarchy to organize and maintain timelines, with all timelines belonging to the same super table in the same system hierarchy, but it is possible to simulate a logical multi-level structure of the application through the unique construction of different tag values. +3. `Rollup And PreAggregates`: The use of Rollup and PreAggregates requires the application to decide where to access the Rollup results and, in some scenarios, to access the actual results. The opacity of this structure makes the application processing logic extraordinarily complex and not portable at all. We think this strategy is a compromise when the time-series database does not. +TDengine does not support automatic downsampling of multiple timelines and preaggregation (for a range of periods) for the time being. Still, thanks to its high-performance query processing logic can provide very high-performance query responses without relying on Rollup and preaggregation (for a range of periods), making your application query processing logic much more straightforward. +The logic is much simpler. +4. `Rate`: TDengine provides two functions to calculate the rate of change of values, namely `Derivative` (the result is consistent with the Derivative behavior of InfluxDB) and `IRate` (the result is compatible with the IRate function in Prometheus). However, the results of these two functions are slightly different from Rate, but the functions are more powerful overall. In addition, TDengine supports all the calculation functions provided by OpenTSDB, and TDengine's query functions are much more potent than those supported by OpenTSDB, which can significantly simplify the processing logic of your application. + +Through the above introduction, I believe you should be able to understand the changes brought about by the migration of OpenTSDB to TDengine. And this information will also help you correctly determine whether you would migrate your application to TDengine to experience the powerful and convenient time-series data processing capability provided by TDengine. + +### 2. Migration strategy suggestion + +First, the OpenTSDB-based system migration involves data schema design, system scale estimation, and data write end transformation, data streaming, and application adaptation; after that, the two systems will run in parallel for a while and then migrate the historical data to TDengine. Of course, if your application has some functions that strongly depend on the above OpenTSDB features and you do not want to stop using them, you can migrate the historical data to TDengine. +You can consider keeping the original OpenTSDB system running while starting TDengine to provide the primary services. + +## Data model design + +On the one hand, TDengine requires a strict schema definition for its incoming data. On the other hand, the data model of TDengine is richer than that of OpenTSDB, and the multi-valued model is compatible with all single-valued model building requirements. + +Let us now assume a DevOps scenario where we use collectd to collect the underlying metrics of the device, including memory, swap, disk, etc. The schema in OpenTSDB is as follows. + +| metric | value name | type | tag1 | tag2 | tag3 | tag4 | tag5 | +| ---- | -------------- | ------ | ------ | ---- | ----------- | -------------------- | --------- | ------ | +| 1 | memory | value | double | host | memory_type | memory_type_instance | source | n/a | +| 2 | swap | value | double | host | swap_type | swap_type_instance | source | n/a | +| 3 | disk | value | double | host | disk_point | disk_instance | disk_type | source | + +TDengine requires the data stored to have a data schema, i.e., you need to create a super table and specify the schema of the super table before writing the data. For data schema creation, you have two ways to do this: 1) Take advantage of TDengine's native data writing support for OpenTSDB by calling the TDengine API to write (text line or JSON format) +and automate the creation of single-value models. This approach does not require significant adjustments to the data writing application, nor does it require converting the written data format. + +At the C level, TDengine provides the `taos_schemaless_insert()` function to write data in OpenTSDB format directly (in early version this function was named `taos_insert_lines()`). Please refer to the sample code `schemaless.c` in the installation package directory as reference. + +(2) based on a complete understanding of TDengine's data model, to establish the mapping relationship between OpenTSDB and TDengine's data model adjustment manually. Considering that OpenTSDB is a single-value mapping model, recommended using the single-value model in TDengine. TDengine can support both multi-value and single-value models. + +- **Single-valued model**. + +The steps are as follows: use the name of the metrics as the name of the TDengine super table, which build with two basic data columns - timestamp and value, and the label of the super table is equivalent to the label information of the metrics, and the number of labels is equal to the number of labels of the metrics. The names of sub-tables are named with fixed rules: `metric + '_' + tags1_value + '_' + tag2_value + '_' + tag3_value ...` as the sub-table name. + +Create 3 super tables in TDengine. + +```sql +create stable memory(ts timestamp, val float) tags(host binary(12), memory_type binary(20), memory_type_instance binary(20), source binary(20)) ; +create stable swap(ts timestamp, val double) tags(host binary(12), swap_type binary(20), swap_type_binary binary(20), source binary(20)); +create stable disk(ts timestamp, val double) tags(host binary(12), disk_point binary(20), disk_instance binary(20), disk_type binary(20), source binary(20)); +``` + +For sub-tables use dynamic table creation as shown below. + +```sql +insert into memory_vm130_memory_buffered_collectd using memory tags('vm130', 'memory', ' buffer', 'collectd') values(1632979445, 3.0656); +``` + +The final system will have about 340 sub-tables and three super-tables. Note that if the use of concatenated tagged values causes the sub-table names to exceed the system limit (191 bytes), then some encoding (e.g., MD5) needs to be used to convert them to an acceptable length. + +- **Multi-value model** + +Suppose you want to take advantage of TDengine's multi-value modeling capabilities. In that case, you need first to meet the requirements that different collection quantities have the same collection frequency and can reach the **data write side simultaneously via a message queue**, thus ensuring writing multiple metrics at once using SQL statements. The metric's name is used as the name of the super table to create a multi-column model of data that has the same collection frequency and can arrive simultaneously. The names of the sub-tables are named using a fixed rule. Each of the above metrics contains only one measurement value, so converting it into a multi-value model is impossible. + +## Data triage and application adaptation + +Subscribe data from the message queue and start the adapted writer to write the data. + +After writing the data starts for a while, you can use SQL statements to check whether the amount of data written meets the expected writing requirements. Use the following SQL statement to count the amount of data. + +```sql +select count(*) from memory +``` + +After completing the query, if the data written does not differ from what is expected and there are no abnormal error messages from the writing program itself, you can confirm that the written data is complete and valid. + +TDengine does not support querying, or data fetching using the OpenTSDB query syntax but does provide a counterpart for each of the OpenTSDB queries. The corresponding query processing can be adapted and applied in a manner obtained by examining Appendix 1. To fully understand the types of queries supported by TDengine, refer to the TDengine user manual. + +TDengine supports the standard JDBC 3.0 interface for manipulating databases, but you can also use other types of high-level language connectors for querying and reading data to suit your application. Please read the user manual for specific operations and usage. + +## Historical Data Migration + +### 1. Use the tool to migrate data automatically + +To facilitate historical data migration, we provide a plug-in for the data synchronization tool DataX, which can automatically write data into TDengine.The automatic data migration of DataX can only support the data migration process of a single value model. + +For the specific usage of DataX and how to use DataX to write data to TDengine, please refer to [DataX-based TDengine Data Migration Tool](https://www.taosdata.com/blog/2021/10/26/3156.html). + +After migrating via DataX, we found that we can significantly improve the efficiency of migrating historical data by starting multiple processes and migrating numerous metrics simultaneously. The following are some records of the migration process. I wish to use these for application migration as a reference. + +| Number of datax instances (number of concurrent processes) | Migration record speed (pieces/second) | +| ----------------------------- | ------------------- -- | +| 1 | About 139,000 | +| 2 | About 218,000 | +| 3 | About 249,000 | +| 5 | About 295,000 | +| 10 | About 330,000 | + +
(Note: The test data comes from a single-node Intel(R) Core(TM) i7-10700 CPU@2.90GHz 16-core 64G hardware device, the channel and batchSize are 8 and 1000 respectively, and each record contains 10 tags) + +### 2. Manual data migration + +Suppose you need to use the multi-value model for data writing. In that case, you need to develop a tool to export data from OpenTSDB, confirm which timelines can be merged and imported into the same timeline, and then pass the time to import simultaneously through the SQL statement—written to the database. + +Manual migration of data requires attention to the following two issues: + +1) When storing the exported data on the disk, the disk needs to have enough storage space to accommodate the exported data files fully. Adopting the partial import mode to avoid the shortage of disk file storage after the total amount of data is exported. Preferentially export the timelines belonging to the same super table. Then the exported data files are imported into the TDengine system. + +2) Under the full load of the system, if there are enough remaining computing and IO resources, establish a multi-threaded importing to maximize the efficiency of data migration. Considering the vast load that data parsing brings to the CPU, it is necessary to control the maximum number of parallel tasks to avoid the overall overload of the system triggered by importing historical data. + +Due to the ease of operation of TDengine itself, there is no need to perform index maintenance and data format change processing in the entire process. The whole process only needs to be executed sequentially. + +When wholly importing the historical data into TDengine, the two systems run simultaneously and then switch the query request to TDengine to achieve seamless application switching. + +## Appendix 1: OpenTSDB query function correspondence table + +### Avg + +Equivalent function: avg + +Example: + +```sql +SELECT avg(val) FROM (SELECT first(val) FROM super_table WHERE ts >= startTime and ts <= endTime INTERVAL(20s) Fill(linear)) INTERVAL(20s) +``` + +Remark: + +1. The value in Interval needs to be the same as the interval value in the outer query. +2. The interpolation processing in TDengine needs to use subqueries to assist in the completion. As shown above, it is enough to specify the interpolation type in the inner query. Since the interpolation of the values ​​in OpenTSDB uses linear interpolation, use fill( in the interpolation clause. linear) to declare the interpolation type. The following functions with the exact interpolation calculation requirements are processed by this method. +3. The parameter 20s in Interval indicates that the inner query will generate results according to a time window of 20 seconds. In an actual query, it needs to adjust to the time interval between different records. It ensures that producing interpolation results equivalent to the original data. +4. Due to the particular interpolation strategy and mechanism of OpenTSDB, the method of the first interpolation and then calculation in the aggregate query (Aggregate) makes the calculation results impossible to be utterly consistent with TDengine. But in the case of downsampling (Downsample), TDengine and OpenTSDB can obtain consistent results (since OpenTSDB performs aggregation and downsampling queries). + +### Count + +Equivalent function: count + +Example: + +```sql +select count(\*) from super_table_name; +``` + +### Dev + +Equivalent function: stddev + +Example: + +```sql +Select stddev(val) from table_name +``` + +### Estimated percentiles + +Equivalent function: apercentile + +Example: + +```sql +Select apercentile(col1, 50, “t-digest”) from table_name +``` + +Remark: + +1. During the approximate query processing, OpenTSDB uses the t-digest algorithm by default, so in order to obtain the same calculation result, the algorithm used needs to be specified in the `apercentile()` function. TDengine can support two different approximation processing algorithms, declared by "default" and "t-digest" respectively. + +### First + +Equivalent function: first + +Example: + +```sql +Select first(col1) from table_name +``` + +### Last + +Equivalent function: last + +Example: + +```sql +Select last(col1) from table_name +``` + +### Max + +Equivalent function: max + +Example: + +```sql +Select max(value) from (select first(val) value from table_name interval(10s) fill(linear)) interval(10s) +``` + +Note: The Max function requires interpolation for the reasons described above. + +### Min + +Equivalent function: min + +Example: + +```sql +Select min(value) from (select first(val) value from table_name interval(10s) fill(linear)) interval(10s); +``` + +### MinMax + +Equivalent function: max + +```sql +Select max(val) from table_name +``` + +Note: This function has no interpolation requirements, so it can be directly calculated. + +### MimMin + +Equivalent function: min + +```sql +Select min(val) from table_name +``` + +Note: This function has no interpolation requirements, so it can be directly calculated. + +### Percentile + +Equivalent function: percentile + +Remark: + +### Sum + +Equivalent function: sum + +```sql +Select max(value) from (select first(val) value from table_name interval(10s) fill(linear)) interval(10s) +``` + +Note: This function has no interpolation requirements, so it can be directly calculated. + +### Zimsum + +Equivalent function: sum + +```sql +Select sum(val) from table_name +``` + +Note: This function has no interpolation requirements, so it can be directly calculated. + +Complete example: + +````json +// OpenTSDB query JSON +query = { +"start": 1510560000, +"end": 1515000009, +"queries": [{ +"aggregator": "count", +"metric": "cpu.usage_user", +}] +} + +// Equivalent query SQL: +SELECT count(*) +FROM `cpu.usage_user` +WHERE ts>=1510560000 AND ts<=1515000009 +```` + +## Appendix 2: Resource Estimation Methodology + +### Data generation environment + +We still use the hypothetical environment from Chapter 4. There are three measurements. Respectively: the data writing rate of temperature and humidity is one record every 5 seconds, and the timeline is 100,000. The writing rate of air pollution is one record every 10 seconds, the timeline is 10,000, and the query request frequency is 500 QPS. + +### Storage resource estimation + +Assuming that the number of sensor devices that generate data and need to be stored is `n`, the frequency of data generation is `t` per second, and the length of each record is `L` bytes, the scale of data generated per day is `n * t * L` bytes. Assuming the compression ratio is `C`, the daily data size is `(n * t * L)/C` bytes. The storage resources are estimated to accommodate the data scale for 1.5 years. In the production environment, the compression ratio C of TDengine is generally between 5 and 7. +With additional 20% ​​redundancy, you can calculate the required storage resources: + +```matlab +(n * t * L) * (365 * 1.5) * (1+20%)/C +```` + +Combined with the above calculation formula, bring the parameters into the formula, and the raw data scale generated every year is 11.8TB without considering the label information. Note that since tag information is associated with each timeline in TDengine, not every record. The scale of the amount of data to be recorded is somewhat reduced relative to the generated data, and this part of label data can be ignored as a whole. Assuming a compression ratio of 5, the size of the retained data ends up being 2.56 TB. + +### Storage Device Selection Considerations + +The hard disk should be capable of better random read performance. Considering using an SSD as much as possible is a better choice. A disk with better random read performance is a great help to improve the system's query performance and improve the query response performance as a whole system. To obtain better query performance, the performance index of the single-threaded random read IOPS of the hard disk device should not be lower than 1000, and it is better to reach 5000 IOPS or more. Recommend to use `fio` utility software to evaluate the running performance (please refer to Appendix 1 for specific usage) for the random IO read of the current device to confirm whether it can meet the requirements of random read of large files. + +Hard disk writing performance has little effect on TDengine. The TDengine writing process adopts the append write mode, so as long as it has good sequential write performance, both SAS hard disks and SSDs in the general sense can well meet TDengine's requirements for disk write performance. + +### Computational resource estimates + +Due to the particularity of IoT data, after the frequency of data generation is consistent, the writing process of TDengine maintains a relatively fixed amount of resource consumption (computing and storage). According to the [TDengine Operation and Maintenance Guide](/operation/) description, the system consumes less than 1 CPU core at 22,000 writes per second. + +In estimating the CPU resources consumed by the query, assuming that the application requires the database to provide 10,000 QPS, the CPU time consumed by each query is about 1 ms. The query provided by each core per second is 1,000 QPS, which satisfies 10,000 QPS. The query request requires at least 10 cores. For the system as a whole system to have less than 50% CPU load, the entire cluster needs twice as many as 10 cores or 20 cores. + +### Memory resource estimation + +The database allocates 16MB\*3 buffer memory for each Vnode by default. If the cluster system includes 22 CPU cores, TDengine will create 22 Vnodes (virtual nodes) by default. Each Vnode contains 1000 tables, which can accommodate all the tables. Then it takes about 1.5 hours to write a block, which triggers the drop, and no adjustment is required. A total of 22 Vnodes require about 1GB of memory cache. Considering the memory needed for the query, assuming that the memory overhead of each query is about 50MB, the memory required for 500 queries concurrently is about 25GB. + +In summary, using a single 16-core 32GB machine or a cluster of 2 8-core 16GB machines is enough. + +## Appendix 3: Cluster Deployment and Startup + +TDengine provides a wealth of help documents to explain many aspects of cluster installation and deployment. Here is the list of corresponding document for your reference. + +### Cluster Deployment + +The first is TDengine installation. Download the latest stable version of TDengine from the official website, and install it. Please refer to the blog ["Installation and Uninstallation of Various Installation Packages of TDengine"](https://www.taosdata.com/blog/2019/08/09/566.html) for the various installation package formats. + +Note that once the installation is complete, do not immediately start the `taosd` service, but start it after correctly configuring the parameters. + +### Set running parameters and start the service + +To ensure that the system can obtain the necessary information for regular operation. Please set the following vital parameters correctly on the server: + +FQDN, firstEp, secondEP, dataDir, logDir, tmpDir, serverPort. For the specific meaning and setting requirements of each parameter, please refer to the document "[TDengine Cluster Installation and Management](/cluster/)" + +Follow the same steps to set parameters on the nodes that need running, start the taosd service, and then add Dnodes to the cluster. + +Finally, start `taos` and execute the `show dnodes` command. If you can see all the nodes that have joined the cluster, the cluster building process was successfully completed. For specific operation procedures and precautions, please refer to the document "[TDengine Cluster Installation and Management](/cluster/)". + +## Appendix 4: Super Table Names + +Since OpenTSDB's metric name has a dot (".") in it, for example, a metric with a name like "cpu.usage_user", the dot has a special meaning in TDengine and is a separator used to separate database and table names. TDengine also provides "escape" characters to allow users to use keywords or special separators (e.g., dots) in (super)table names. To use special characters, enclose the table name in escape characters, e.g.: `cpu.usage_user`. It is a valid (super) table name. + +## Appendix 5: Reference Articles + +1. [Using TDengine + collectd/StatsD + Grafana to quickly build an IT operation and maintenance monitoring system](/application/collectd/) +2. [Write collected data directly to TDengine through collectd](/third-party/collectd/) diff --git a/docs-en/25-application/_category_.yml b/docs-en/25-application/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..772d45e6cf7042e701968fe658b53ef53bd91c62 --- /dev/null +++ b/docs-en/25-application/_category_.yml @@ -0,0 +1 @@ +label: Application Practice diff --git a/docs-en/25-application/index.md b/docs-en/25-application/index.md new file mode 100644 index 0000000000000000000000000000000000000000..5383a00c67c515dc65fd2ed1cac4b218710288b5 --- /dev/null +++ b/docs-en/25-application/index.md @@ -0,0 +1,10 @@ +--- +title: Application Practice +--- + +```mdx-code-block +import DocCardList from '@theme/DocCardList'; +import {useCurrentSidebarCategory} from '@docusaurus/theme-common'; + + +``` \ No newline at end of file diff --git a/docs-en/27-train-faq/01-faq.md b/docs-en/27-train-faq/01-faq.md new file mode 100644 index 0000000000000000000000000000000000000000..439775170937ef11fc964914232b2739d688b26f --- /dev/null +++ b/docs-en/27-train-faq/01-faq.md @@ -0,0 +1,114 @@ +--- +sidebar_label: FAQ +title: Frequently Asked Questions +--- + +## Submit an Issue + +If the tips in FAQ don't help much, please submit an issue on [GitHub](https://github.com/taosdata/TDengine) to describe your problem description, including TDengine version, hardware and OS information, the steps to reproduce the problem, etc. It would be very helpful if you package the contents in `/var/log/taos` and `/etc/taos` and upload. These two are the default directories used by TDengine, if they have been changed in your configuration, please use according to the actual configuration. It's recommended to firstly set `debugFlag` to 135 in `taos.cfg`, restart `taosd`, then reproduce the problem and collect logs. If you don't want to restart, an alternative way of setting `debugFlag` is executing `alter dnode debugFlag 135` command in TDengine CLI `taos`. During normal running, however, please make sure `debugFlag` is set to 131. + +## Frequently Asked Questions + +### 1. How to upgrade to TDengine 2.0 from older version? + +version 2.x is not compatible with version 1.x regarding configuration file and data file, please do following before upgrading: + +1. Delete configuration files: `sudo rm -rf /etc/taos/taos.cfg` +2. Delete log files: `sudo rm -rf /var/log/taos/` +3. Delete data files if the data doesn't need to be kept: `sudo rm -rf /var/lib/taos/` +4. Install latests 2.x version +5. If the data needs to be kept and migrated to newer version, please contact professional service of TDengine for assistance + +### 2. How to handle "Unable to establish connection"? + +When the client is unable to connect to the server, you can try following ways to find out why. + +1. Check the network + + - Check if the hosts where the client and server are running can be accessible to each other, for example by `ping` command. + - Check if the TCP/UDP on port 6030-6042 are open for access if firewall is enabled. It's better to firstly disable firewall for diagnostics. + - Check if the FQDN and serverPort are configured correctly in `taos.cfg` used by the server side + - Check if the `firstEp` is set properly in the `taos.cfg` used by the client side + +2. Make sure the client version and server version are same. + +3. On server side, check the running status of `taosd` by executing `systemctl status taosd` . If your server is started using another way instead of `systemctl`, use the proper method to check whether the server process is running normally. + +4. If using connector of Python, Java, Go, Rust, C#, node.JS on Linux to connect toe the server, please make sure `libtaos.so` is in directory `/usr/local/taos/driver` and `/usr/local/taos/driver` is in system lib search environment variable `LD_LIBRARY_PATH`. + +5. If using connector on Windows, please make sure `C:\TDengine\driver\taos.dll` is in your system lib search path, it's suggested to put `taos.dll` under `C:\Windows\System32`. + +6. Some advanced network diagnostics tools + + - On Linux system tool `nc` can be used to check whether the TCP/UDP can be accessible on a specified port + Check whether a UDP port is open: `nc -vuz {hostIP} {port} ` + Check whether a TCP port on server side is open: `nc -l {port}` + Check whether a TCP port on client side is open: `nc {hostIP} {port}` + + - On Windows system `Net-TestConnection -ComputerName {fqdn} -Port {port}` on PowerShell can be used to check whether the port on serer side is open for access. + +7. TDengine CLI `taos` can also be used to check network, please refer to [TDengine CLI](/reference/taos-shell). + +### 3. How to handle "Unexpected generic error in RPC" or "Unable to resolve FQDN" ? + +This error is caused because the FQDN can't be resolved. Please try following ways: + +1. Check whether the FQDN is configured properly on the server side +2. If DSN server is configured in the network, please check whether it works; otherwise, check `/etc/hosts` to see whether the FQDN is configured with correct IP +3. If the network configuration on the server side is OK, try to ping the server from the client side. +4. If TDengine has been used before with an old hostname then the hostname has been changed, please check `/var/lib/taos/taos/dnode/dnodeEps.json`. Before setting up a new TDengine cluster, it's better to cleanup the directories configured. + +### 4. "Invalid SQL" is returned even though the Syntax is correct + +"Invalid SQL" is returned when the length of SQL statement exceeds maximum allowed length or the syntax is not correct. + +### 5. Whether validation queries are supported? + +It's suggested to use a builtin database named as `log` to monitor. + + + +### 6. Can I delete a record? + +From version 2.6.0.0 Enterprise version, deleting data can be supported. + +### 7. How to create a table of over 1024 columns? + +From version 2.1.7.0, at most 4096 columns can be defined for a table. + +### 8. How to improve the efficiency of inserting data? + +Inserting data in batch is a good practice. Single SQL statement can insert data for one or multiple tables in batch. + +### 9. JDBC Error: the executed SQL is not a DML or a DDL? + +Please upgrade to latest JDBC driver, for details please refer to [Java Connector](/reference/connector/java) + +### 10. Failed to connect with error "invalid timestamp" + +The most common reason is that the time setting is not aligned on the client side and the server side. On Linux system, please use `ntpdate` command. On Windows system, please enable automatic sync in system time setting. + +### 11. Table name is not shown in full + +There is a display width setting in TDengine CLI `taos`. It can be controlled by configuration parameter `maxBinaryDisplayWidth`, or can be set using SQL command `set max_binary_display_width`. A more convenient way is to append `\G` in a SQL command to bypass this limitation. + +### 12. How to change log level temporarily? + +Below SQL command can be used to adjust log level temporarily + +```sql +ALTER LOCAL flag_name flag_value; +``` + - flag_name can be: debugFlag,cDebugFlag,tmrDebugFlag,uDebugFlag,rpcDebugFlag + - flag_value can be: 131 (INFO/WARNING/ERROR), 135 (plus DEBUG), 143 (plus TRACE) + + + +### 13. What to do if go compilation fails? + +From version 2.3.0.0, a new component named `taosAdapter` is introduced. Its' developed in Go. If you want to compile from source code and meet go compilation problems, try to do below steps to resolve Go environment problems. + +```sh +go env -w GO111MODULE=on +go env -w GOPROXY=https://goproxy.cn,direct +``` diff --git a/docs-en/27-train-faq/03-docker.md b/docs-en/27-train-faq/03-docker.md new file mode 100644 index 0000000000000000000000000000000000000000..ba435a9307c1d6595579a295df83030c58ba0f22 --- /dev/null +++ b/docs-en/27-train-faq/03-docker.md @@ -0,0 +1,285 @@ +--- +sidebar_label: TDengine in Docker +title: Deploy TDengine in Docker +--- + +Even though it's not recommended to deploy TDengine using docker in production system, docker is still very useful in development environment, especially when your host is not Linux. From version 2.0.14.0, the official image of TDengine can support X86-64, X86, arm64, and rm32 . + +In this chapter a simple step by step guide of using TDengine in docker is introduced. + +## Install Docker + +The installation of docker please refer to [Get Docker](https://docs.docker.com/get-docker/). + +After docker is installed, you can check whether Docker is installed properly by displaying Docker version. + +```bash +$ docker -v +Docker version 20.10.3, build 48d30b5 +``` + +## Launch TDengine in Docker + +### Launch TDengine Server + +```bash +$ docker run -d -p 6030-6049:6030-6049 -p 6030-6049:6030-6049/udp tdengine/tdengine +526aa188da767ae94b244226a2b2eec2b5f17dd8eff592893d9ec0cd0f3a1ccd +``` + +In the above command, a docker container is started to run TDengine server, the port range 6030-6049 of the container is mapped to host port range 6030-6049. If port range 6030-6049 has been occupied on the host, please change to an available host port range. Regarding the requirements about ports on the host, please refer to [Port Configuration](/reference/config/#serverport). + +- **docker run**: Launch a docker container +- **-d**: the container will run in background mode +- **-p**: port mapping +- **tdengine/tdengine**: The image from which to launch the container +- **526aa188da767ae94b244226a2b2eec2b5f17dd8eff592893d9ec0cd0f3a1ccd**: the container ID if successfully launched. + +Furthermore, `--name` can be used with `docker run` to specify name for the container, `--hostname` can be used to specify hostname for the container, `-v` can be used to mount local volumes to the container so that the data generated inside the container can be persisted to disk on the host. + +```bash +docker run -d --name tdengine --hostname="tdengine-server" -v ~/work/taos/log:/var/log/taos -v ~/work/taos/data:/var/lib/taos -p 6030-6049:6030-6049 -p 6030-6049:6030-6049/udp tdengine/tdengine +``` + +- **--name tdengine**: specify the name of the container, the name can be used to specify the container later +- **--hostname=tdengine-server**: specify the hostname inside the container, the hostname can be used inside the container without worrying the container IP may vary +- **-v**: volume mapping between host and container + +### Check the container + +```bash +docker ps +``` + +The output is like below: + +``` +CONTAINER ID IMAGE COMMAND CREATED STATUS ··· +c452519b0f9b tdengine/tdengine "taosd" 14 minutes ago Up 14 minutes ··· +``` + +- **docker ps**: List all the containers +- **CONTAINER ID**: Container ID +- **IMAGE**: The image used for the container +- **COMMAND**: The command used when launching the container +- **CREATED**: When the container was created +- **STATUS**: Status of the container + +### Access TDengine inside container + +```bash +$ docker exec -it tdengine /bin/bash +root@tdengine-server:~/TDengine-server-2.4.0.4# +``` + +- **docker exec**: Attach to the container +- **-i**: Interactive mode +- **-t**: Use terminal +- **tdengine**: Container name, up to the output of `docker ps` +- **/bin/bash**: The command to execute once the container is attached + +Inside the container, start TDengine CLI `taos` + +```bash +root@tdengine-server:~/TDengine-server-2.4.0.4# taos + +Welcome to the TDengine shell from Linux, Client Version:2.4.0.4 +Copyright (c) 2020 by TAOS Data, Inc. All rights reserved. + +taos> +``` + +The above example is for a successful connection. If `taos` fails to connect to the server side, error information would be shown. + +In TDengine CLI, SQL commands can be executed to create/drop databases, tables, STables, and insert or query data. For details please refer to [TAOS SQL](/taos-sql/). + +### Access TDengine from host + +If `-p` used to map ports properly between host and container, it's also able to access TDengine in container from the host as long as `firstEp` is configured correctly for the client on host. + +``` +$ taos + +Welcome to the TDengine shell from Linux, Client Version:2.4.0.4 +Copyright (c) 2020 by TAOS Data, Inc. All rights reserved. + +taos> +``` + +It's also able to access the REST interface provided by TDengine in container from the host. + +``` +curl -u root:taosdata -d 'show databases' 127.0.0.1:6041/rest/sql +``` + +Output is like below: + +``` +{"status":"succ","head":["name","created_time","ntables","vgroups","replica","quorum","days","keep0,keep1,keep(D)","cache(MB)","blocks","minrows","maxrows","wallevel","fsync","comp","cachelast","precision","update","status"],"column_meta":[["name",8,32],["created_time",9,8],["ntables",4,4],["vgroups",4,4],["replica",3,2],["quorum",3,2],["days",3,2],["keep0,keep1,keep(D)",8,24],["cache(MB)",4,4],["blocks",4,4],["minrows",4,4],["maxrows",4,4],["wallevel",2,1],["fsync",4,4],["comp",2,1],["cachelast",2,1],["precision",8,3],["update",2,1],["status",8,10]],"data":[["test","2021-08-18 06:01:11.021",10000,4,1,1,10,"3650,3650,3650",16,6,100,4096,1,3000,2,0,"ms",0,"ready"],["log","2021-08-18 05:51:51.065",4,1,1,1,10,"30,30,30",1,3,100,4096,1,3000,2,0,"us",0,"ready"]],"rows":2} +``` + +For details of REST API please refer to [REST API]](/reference/rest-api/). + +### Run TDengine server and taosAdapter inside container + +From version 2.4.0.0, in the TDengine Docker image, `taosAdapter` is enabled by default, but can be disabled using environment variable `TAOS_DISABLE_ADAPTER=true` . `taosAdapter` can also be run alone without `taosd` when launching a container. + +For the port mapping of `taosAdapter`, please refer to [taosAdapter](/reference/taosadapter/). + +- Run both `taosd` and `taosAdapter` (by default) in docker container: + +```bash +docker run -d --name tdengine-all -p 6030-6049:6030-6049 -p 6030-6049:6030-6049/udp tdengine/tdengine:2.4.0.4 +``` + +- Run `taosAdapter` only in docker container, `TAOS_FIRST_EP` environment variable needs to be used to specify the container name in which `taosd` is running: + +```bash +docker run -d --name tdengine-taosa -p 6041-6049:6041-6049 -p 6041-6049:6041-6049/udp -e TAOS_FIRST_EP=tdengine-all tdengine/tdengine:2.4.0.4 taosadapter +``` + +- Run `taosd` only in docker container: + +```bash +docker run -d --name tdengine-taosd -p 6030-6042:6030-6042 -p 6030-6042:6030-6042/udp -e TAOS_DISABLE_ADAPTER=true tdengine/tdengine:2.4.0.4 +``` + +- Verify the REST interface: + +```bash +curl -H 'Authorization: Basic cm9vdDp0YW9zZGF0YQ==' -d 'show databases;' 127.0.0.1:6041/rest/sql +``` + +Below is an example output: + +``` +{"status":"succ","head":["name","created_time","ntables","vgroups","replica","quorum","days","keep","cache(MB)","blocks","minrows","maxrows","wallevel","fsync","comp","cachelast","precision","update","status"],"column_meta":[["name",8,32],["created_time",9,8],["ntables",4,4],["vgroups",4,4],["replica",3,2],["quorum",3,2],["days",3,2],["keep",8,24],["cache(MB)",4,4],["blocks",4,4],["minrows",4,4],["maxrows",4,4],["wallevel",2,1],["fsync",4,4],["comp",2,1],["cachelast",2,1],["precision",8,3],["update",2,1],["status",8,10]],"data":[["log","2021-12-28 09:18:55.765",10,1,1,1,10,"30",1,3,100,4096,1,3000,2,0,"us",0,"ready"]],"rows":1} +``` + +### Use taosBenchmark on host to access TDengine server in container + +1. Run `taosBenchmark`, named as `taosdemo` previously, on the host: + + ```bash + $ taosBenchmark + + taosBenchmark is simulating data generated by power equipments monitoring... + + host: 127.0.0.1:6030 + user: root + password: taosdata + configDir: + resultFile: ./output.txt + thread num of insert data: 10 + thread num of create table: 10 + top insert interval: 0 + number of records per req: 30000 + max sql length: 1048576 + database count: 1 + database[0]: + database[0] name: test + drop: yes + replica: 1 + precision: ms + super table count: 1 + super table[0]: + stbName: meters + autoCreateTable: no + childTblExists: no + childTblCount: 10000 + childTblPrefix: d + dataSource: rand + iface: taosc + insertRows: 10000 + interlaceRows: 0 + disorderRange: 1000 + disorderRatio: 0 + maxSqlLen: 1048576 + timeStampStep: 1 + startTimestamp: 2017-07-14 10:40:00.000 + sampleFormat: + sampleFile: + tagsFile: + columnCount: 3 + column[0]:FLOAT column[1]:INT column[2]:FLOAT + tagCount: 2 + tag[0]:INT tag[1]:BINARY(16) + + Press enter key to continue or Ctrl-C to stop + ``` + + Once the execution is finished, a database `test` is created, a STable `meters` is created in database `test`, 10,000 sub tables are created using `meters` as template, named as "d0" to "d9999", while 10,000 rows are inserted into each table, so totally 100,000,000 rows are inserted. + +2. Check the data + + - **Check database** + + ```bash + $ taos> show databases; + name | created_time | ntables | vgroups | ··· + test | 2021-08-18 06:01:11.021 | 10000 | 6 | ··· + log | 2021-08-18 05:51:51.065 | 4 | 1 | ··· + + ``` + + - **Check STable** + + ```bash + $ taos> use test; + Database changed. + + $ taos> show stables; + name | created_time | columns | tags | tables | + ============================================================================================ + meters | 2021-08-18 06:01:11.116 | 4 | 2 | 10000 | + Query OK, 1 row(s) in set (0.003259s) + + ``` + + - **Check Tables** + + ```bash + $ taos> select * from test.t0 limit 10; + + DB error: Table does not exist (0.002857s) + taos> select * from test.d0 limit 10; + ts | current | voltage | phase | + ====================================================================================== + 2017-07-14 10:40:00.000 | 10.12072 | 223 | 0.34167 | + 2017-07-14 10:40:00.001 | 10.16103 | 224 | 0.34445 | + 2017-07-14 10:40:00.002 | 10.00204 | 220 | 0.33334 | + 2017-07-14 10:40:00.003 | 10.00030 | 220 | 0.33333 | + 2017-07-14 10:40:00.004 | 9.84029 | 216 | 0.32222 | + 2017-07-14 10:40:00.005 | 9.88028 | 217 | 0.32500 | + 2017-07-14 10:40:00.006 | 9.88110 | 217 | 0.32500 | + 2017-07-14 10:40:00.007 | 10.08137 | 222 | 0.33889 | + 2017-07-14 10:40:00.008 | 10.12063 | 223 | 0.34167 | + 2017-07-14 10:40:00.009 | 10.16086 | 224 | 0.34445 | + Query OK, 10 row(s) in set (0.016791s) + + ``` + + - **Check tag values of table d0** + + ```bash + $ taos> select groupid, location from test.d0; + groupid | location | + ================================= + 0 | shanghai | + Query OK, 1 row(s) in set (0.003490s) + ``` + +### Access TDengine from 3rd party tools + +A lot of 3rd party tools can be used to write data into TDengine through `taosAdapter` , for details please refer to [3rd party tools](/third-party/). + +There is nothing different from the 3rd party side to access TDengine server inside a container, as long as the end point is specified correctly, the end point should be the FQDN and the mapped port of the host. + +## Stop TDengine inside container + +```bash +docker stop tdengine +``` + +- **docker stop**: stop a container +- **tdengine**: container name diff --git a/docs-en/27-train-faq/_category_.yml b/docs-en/27-train-faq/_category_.yml new file mode 100644 index 0000000000000000000000000000000000000000..7a8530c129b90c4ac382168a55c339bbd627cd05 --- /dev/null +++ b/docs-en/27-train-faq/_category_.yml @@ -0,0 +1 @@ +label: FAQ & Others diff --git a/docs-en/27-train-faq/index.md b/docs-en/27-train-faq/index.md new file mode 100644 index 0000000000000000000000000000000000000000..2cb87aab005b0ecc9275b6fe10e267487d38c336 --- /dev/null +++ b/docs-en/27-train-faq/index.md @@ -0,0 +1,10 @@ +--- +title: FAQ & Others +--- + +```mdx-code-block +import DocCardList from '@theme/DocCardList'; +import {useCurrentSidebarCategory} from '@docusaurus/theme-common'; + + +``` \ No newline at end of file diff --git a/docs-examples/.gitignore b/docs-examples/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..7ed6d403bf5f64c0cb230265b4dffee609dea93b --- /dev/null +++ b/docs-examples/.gitignore @@ -0,0 +1,3 @@ +.vscode +*.lock +.idea \ No newline at end of file diff --git a/docs-examples/.gitignre b/docs-examples/.gitignre new file mode 100644 index 0000000000000000000000000000000000000000..0853156c65c2c6c1b693290e74c3ee630bcaac19 --- /dev/null +++ b/docs-examples/.gitignre @@ -0,0 +1,2 @@ +.vscode +*.lock \ No newline at end of file diff --git a/docs-examples/R/connect_native.r b/docs-examples/R/connect_native.r new file mode 100644 index 0000000000000000000000000000000000000000..18c142872be5efaa7167c10a25f62bcb9fbf5a52 --- /dev/null +++ b/docs-examples/R/connect_native.r @@ -0,0 +1,16 @@ +if (! "RJDBC" %in% installed.packages()[, "Package"]) { + install.packages('RJDBC', repos='http://cran.us.r-project.org') +} + +# ANCHOR: demo +library("DBI") +library("rJava") +library("RJDBC") + +args<- commandArgs(trailingOnly = TRUE) +driver_path = args[1] # path to jdbc-driver for example: "/root/taos-jdbcdriver-2.0.37-dist.jar" +driver = JDBC("com.taosdata.jdbc.TSDBDriver", driver_path) +conn = dbConnect(driver, "jdbc:TAOS://127.0.0.1:6030/?user=root&password=taosdata") +dbGetQuery(conn, "SELECT server_version()") +dbDisconnect(conn) +# ANCHOR_END: demo \ No newline at end of file diff --git a/docs-examples/R/connect_rest.r b/docs-examples/R/connect_rest.r new file mode 100644 index 0000000000000000000000000000000000000000..5ceec572fc26575dfc597983eeac3233bc4488ab --- /dev/null +++ b/docs-examples/R/connect_rest.r @@ -0,0 +1,12 @@ +if (! "RJDBC" %in% installed.packages()[, "Package"]) { + install.packages('RJDBC', repos='http://cran.us.r-project.org') +} + +library("DBI") +library("rJava") +library("RJDBC") +driver_path = "/home/debug/build/lib/taos-jdbcdriver-2.0.38-dist.jar" +driver = JDBC("com.taosdata.jdbc.rs.RestfulDriver", driver_path) +conn = dbConnect(driver, "jdbc:TAOS-RS://localhost:6041?user=root&password=taosdata") +dbGetQuery(conn, "SELECT server_version()") +dbDisconnect(conn) \ No newline at end of file diff --git a/docs-examples/c/.gitignore b/docs-examples/c/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..afe974314989a1e3aa4eee703738a9a960c18577 --- /dev/null +++ b/docs-examples/c/.gitignore @@ -0,0 +1,3 @@ +* +!*.c +!.gitignore diff --git a/docs-examples/c/async_query_example.c b/docs-examples/c/async_query_example.c new file mode 100644 index 0000000000000000000000000000000000000000..262757f02b5c52f2d4402d363663db80bb38a54d --- /dev/null +++ b/docs-examples/c/async_query_example.c @@ -0,0 +1,195 @@ +// compile with: +// gcc -o async_query_example async_query_example.c -ltaos + +#include +#include +#include +#include +#include +#include + +typedef int16_t VarDataLenT; + +#define TSDB_NCHAR_SIZE sizeof(int32_t) +#define VARSTR_HEADER_SIZE sizeof(VarDataLenT) + +#define GET_FLOAT_VAL(x) (*(float *)(x)) +#define GET_DOUBLE_VAL(x) (*(double *)(x)) + +#define varDataLen(v) ((VarDataLenT *)(v))[0] + +int printRow(char *str, TAOS_ROW row, TAOS_FIELD *fields, int numFields) { + int len = 0; + char split = ' '; + + for (int i = 0; i < numFields; ++i) { + if (i > 0) { + str[len++] = split; + } + + if (row[i] == NULL) { + len += sprintf(str + len, "%s", "NULL"); + continue; + } + + switch (fields[i].type) { + case TSDB_DATA_TYPE_TINYINT: + len += sprintf(str + len, "%d", *((int8_t *)row[i])); + break; + + case TSDB_DATA_TYPE_UTINYINT: + len += sprintf(str + len, "%u", *((uint8_t *)row[i])); + break; + + case TSDB_DATA_TYPE_SMALLINT: + len += sprintf(str + len, "%d", *((int16_t *)row[i])); + break; + + case TSDB_DATA_TYPE_USMALLINT: + len += sprintf(str + len, "%u", *((uint16_t *)row[i])); + break; + + case TSDB_DATA_TYPE_INT: + len += sprintf(str + len, "%d", *((int32_t *)row[i])); + break; + + case TSDB_DATA_TYPE_UINT: + len += sprintf(str + len, "%u", *((uint32_t *)row[i])); + break; + + case TSDB_DATA_TYPE_BIGINT: + len += sprintf(str + len, "%" PRId64, *((int64_t *)row[i])); + break; + + case TSDB_DATA_TYPE_UBIGINT: + len += sprintf(str + len, "%" PRIu64, *((uint64_t *)row[i])); + break; + + case TSDB_DATA_TYPE_FLOAT: { + float fv = 0; + fv = GET_FLOAT_VAL(row[i]); + len += sprintf(str + len, "%f", fv); + } break; + + case TSDB_DATA_TYPE_DOUBLE: { + double dv = 0; + dv = GET_DOUBLE_VAL(row[i]); + len += sprintf(str + len, "%lf", dv); + } break; + + case TSDB_DATA_TYPE_BINARY: + case TSDB_DATA_TYPE_NCHAR: { + int32_t charLen = varDataLen((char *)row[i] - VARSTR_HEADER_SIZE); + memcpy(str + len, row[i], charLen); + len += charLen; + } break; + + case TSDB_DATA_TYPE_TIMESTAMP: + len += sprintf(str + len, "%" PRId64, *((int64_t *)row[i])); + break; + + case TSDB_DATA_TYPE_BOOL: + len += sprintf(str + len, "%d", *((int8_t *)row[i])); + default: + break; + } + } + + return len; +} + +void printHeader(TAOS_RES *res) { + int numFields = taos_num_fields(res); + TAOS_FIELD *fields = taos_fetch_fields(res); + char header[256] = {0}; + int len = 0; + for (int i = 0; i < numFields; ++i) { + len += sprintf(header + len, "%s ", fields[i].name); + } + puts(header); +} + +// ANCHOR: demo + +/** + * @brief call back function of taos_fetch_row_a + * + * @param param : the third parameter you passed to taos_fetch_row_a + * @param res : pointer of TAOS_RES + * @param numOfRow : number of rows fetched in this batch. will be 0 if there is no more data. + * @return void* + */ +void *fetch_row_callback(void *param, TAOS_RES *res, int numOfRow) { + printf("numOfRow = %d \n", numOfRow); + int numFields = taos_num_fields(res); + TAOS_FIELD *fields = taos_fetch_fields(res); + TAOS *_taos = (TAOS *)param; + if (numOfRow > 0) { + for (int i = 0; i < numOfRow; ++i) { + TAOS_ROW row = taos_fetch_row(res); + char temp[256] = {0}; + printRow(temp, row, fields, numFields); + puts(temp); + } + taos_fetch_rows_a(res, fetch_row_callback, _taos); + } else { + printf("no more data, close the connection.\n"); + taos_free_result(res); + taos_close(_taos); + taos_cleanup(); + } +} + +/** + * @brief callback function of taos_query_a + * + * @param param: the fourth parameter you passed to taos_query_a + * @param res : the result set + * @param code : status code + * @return void* + */ +void *select_callback(void *param, TAOS_RES *res, int code) { + printf("query callback ...\n"); + TAOS *_taos = (TAOS *)param; + if (code == 0 && res) { + printHeader(res); + taos_fetch_rows_a(res, fetch_row_callback, _taos); + } else { + printf("failed to execute taos_query. error: %s\n", taos_errstr(res)); + taos_free_result(res); + taos_close(_taos); + taos_cleanup(); + exit(EXIT_FAILURE); + } +} + +int main() { + TAOS *taos = taos_connect("localhost", "root", "taosdata", "power", 6030); + if (taos == NULL) { + puts("failed to connect to server"); + exit(EXIT_FAILURE); + } + // param one is the connection returned by taos_connect. + // param two is the SQL to execute. + // param three is the callback function. + // param four can be any pointer. It will be passed to your callback function as the first parameter. we use taos + // here, because we want to close it after getting data. + taos_query_a(taos, "SELECT * FROM meters", select_callback, taos); + sleep(1); +} + +// output: +// query callback ... +// ts current voltage phase location groupid +// numOfRow = 8 +// 1538548685000 10.300000 219 0.310000 beijing.chaoyang 2 +// 1538548695000 12.600000 218 0.330000 beijing.chaoyang 2 +// 1538548696800 12.300000 221 0.310000 beijing.chaoyang 2 +// 1538548696650 10.300000 218 0.250000 beijing.chaoyang 3 +// 1538548685500 11.800000 221 0.280000 beijing.haidian 2 +// 1538548696600 13.400000 223 0.290000 beijing.haidian 2 +// 1538548685000 10.800000 223 0.290000 beijing.haidian 3 +// 1538548686500 11.500000 221 0.350000 beijing.haidian 3 +// numOfRow = 0 +// no more data, close the connection. +// ANCHOR_END: demo \ No newline at end of file diff --git a/docs-examples/c/connect_example.c b/docs-examples/c/connect_example.c new file mode 100644 index 0000000000000000000000000000000000000000..1a23df4806d7ff986898734e1971f6e0cd7c5360 --- /dev/null +++ b/docs-examples/c/connect_example.c @@ -0,0 +1,24 @@ +// compile with +// gcc connect_example.c -o connect_example -ltaos +#include +#include +#include "taos.h" + +int main() { + const char *host = "localhost"; + const char *user = "root"; + const char *passwd = "taosdata"; + // if don't want to connect to a default db, set it to NULL or "" + const char *db = NULL; + uint16_t port = 0; // 0 means use the default port + TAOS *taos = taos_connect(host, user, passwd, db, port); + if (taos == NULL) { + int errno = taos_errno(NULL); + char *msg = taos_errstr(NULL); + printf("%d, %s\n", errno, msg); + } else { + printf("connected\n"); + taos_close(taos); + } + taos_cleanup(); +} diff --git a/docs-examples/c/error_handle_example.c b/docs-examples/c/error_handle_example.c new file mode 100644 index 0000000000000000000000000000000000000000..e7dedb263df250f6634aa15fab2729cbaf4e5972 --- /dev/null +++ b/docs-examples/c/error_handle_example.c @@ -0,0 +1,24 @@ +// compile with +// gcc error_handle_example.c -o error_handle_example -ltaos +#include +#include +#include "taos.h" + +int main() { + const char *host = "localhost"; + const char *user = "root"; + const char *passwd = "taosdata"; + // if don't want to connect to a default db, set it to NULL or "" + const char *db = "notexist"; + uint16_t port = 0; // 0 means use the default port + TAOS *taos = taos_connect(host, user, passwd, db, port); + if (taos == NULL) { + int errno = taos_errno(NULL); + char *msg = taos_errstr(NULL); + printf("%d, %s\n", errno, msg); + } else { + printf("connected\n"); + taos_close(taos); + } + taos_cleanup(); +} diff --git a/docs-examples/c/insert_example.c b/docs-examples/c/insert_example.c new file mode 100644 index 0000000000000000000000000000000000000000..ca12be9314efbda707dbd05449c746794c209743 --- /dev/null +++ b/docs-examples/c/insert_example.c @@ -0,0 +1,51 @@ +// compile with +// gcc -o insert_example insert_example.c -ltaos +#include +#include +#include "taos.h" + + +/** + * @brief execute sql and print affected rows. + * + * @param taos + * @param sql + */ +void executeSQL(TAOS *taos, const char *sql) { + TAOS_RES *res = taos_query(taos, sql); + int code = taos_errno(res); + if (code != 0) { + printf("Error code: %d; Message: %s\n", code, taos_errstr(res)); + taos_free_result(res); + taos_close(taos); + exit(EXIT_FAILURE); + } + int affectedRows = taos_affected_rows(res); + printf("affected rows %d\n", affectedRows); + taos_free_result(res); +} + + + +int main() { + TAOS *taos = taos_connect("localhost", "root", "taosdata", NULL, 6030); + if (taos == NULL) { + printf("failed to connect to server\n"); + exit(EXIT_FAILURE); + } + executeSQL(taos, "CREATE DATABASE power"); + executeSQL(taos, "USE power"); + executeSQL(taos, "CREATE STABLE meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT)"); + executeSQL(taos, "INSERT INTO d1001 USING meters TAGS(Beijing.Chaoyang, 2) VALUES ('2018-10-03 14:38:05.000', 10.30000, 219, 0.31000) ('2018-10-03 14:38:15.000', 12.60000, 218, 0.33000) ('2018-10-03 14:38:16.800', 12.30000, 221, 0.31000)" + "d1002 USING meters TAGS(Beijing.Chaoyang, 3) VALUES ('2018-10-03 14:38:16.650', 10.30000, 218, 0.25000)" + "d1003 USING meters TAGS(Beijing.Haidian, 2) VALUES ('2018-10-03 14:38:05.500', 11.80000, 221, 0.28000) ('2018-10-03 14:38:16.600', 13.40000, 223, 0.29000)" + "d1004 USING meters TAGS(Beijing.Haidian, 3) VALUES ('2018-10-03 14:38:05.000', 10.80000, 223, 0.29000) ('2018-10-03 14:38:06.500', 11.50000, 221, 0.35000)"); + taos_close(taos); + taos_cleanup(); +} + +// output: +// affected rows 0 +// affected rows 0 +// affected rows 0 +// affected rows 8 \ No newline at end of file diff --git a/docs-examples/c/json_protocol_example.c b/docs-examples/c/json_protocol_example.c new file mode 100644 index 0000000000000000000000000000000000000000..182fd201308facc80c76f36cfa57580784d70413 --- /dev/null +++ b/docs-examples/c/json_protocol_example.c @@ -0,0 +1,52 @@ +// compile with +// gcc -o json_protocol_example json_protocol_example.c -ltaos +#include +#include +#include +#include "taos.h" + +void executeSQL(TAOS *taos, const char *sql) { + TAOS_RES *res = taos_query(taos, sql); + int code = taos_errno(res); + if (code != 0) { + printf("%s\n", taos_errstr(res)); + taos_free_result(res); + taos_close(taos); + exit(EXIT_FAILURE); + } + taos_free_result(res); +} + +// ANCHOR: main +int main() { + TAOS *taos = taos_connect("localhost", "root", "taosdata", "", 6030); + if (taos == NULL) { + printf("failed to connect to server\n"); + exit(EXIT_FAILURE); + } + executeSQL(taos, "DROP DATABASE IF EXISTS test"); + executeSQL(taos, "CREATE DATABASE test"); + executeSQL(taos, "USE test"); + char *line = + "[{\"metric\": \"meters.current\", \"timestamp\": 1648432611249, \"value\": 10.3, \"tags\": {\"location\": " + "\"Beijing.Chaoyang\", \"groupid\": 2}},{\"metric\": \"meters.voltage\", \"timestamp\": 1648432611249, " + "\"value\": 219, \"tags\": {\"location\": \"Beijing.Haidian\", \"groupid\": 1}},{\"metric\": \"meters.current\", " + "\"timestamp\": 1648432611250, \"value\": 12.6, \"tags\": {\"location\": \"Beijing.Chaoyang\", \"groupid\": " + "2}},{\"metric\": \"meters.voltage\", \"timestamp\": 1648432611250, \"value\": 221, \"tags\": {\"location\": " + "\"Beijing.Haidian\", \"groupid\": 1}}]"; + + char *lines[] = {line}; + TAOS_RES *res = taos_schemaless_insert(taos, lines, 1, TSDB_SML_JSON_PROTOCOL, TSDB_SML_TIMESTAMP_NOT_CONFIGURED); + if (taos_errno(res) != 0) { + printf("failed to insert schema-less data, reason: %s\n", taos_errstr(res)); + } else { + int affectedRow = taos_affected_rows(res); + printf("successfully inserted %d rows\n", affectedRow); + } + taos_free_result(res); + taos_close(taos); + taos_cleanup(); +} +// output: +// successfully inserted 4 rows +// ANCHOR_END: main diff --git a/docs-examples/c/line_example.c b/docs-examples/c/line_example.c new file mode 100644 index 0000000000000000000000000000000000000000..8dd4b1a5075369625645959da0476b76b9fbf290 --- /dev/null +++ b/docs-examples/c/line_example.c @@ -0,0 +1,47 @@ +// compile with +// gcc -o line_example line_example.c -ltaos +#include +#include +#include +#include "taos.h" + +void executeSQL(TAOS *taos, const char *sql) { + TAOS_RES *res = taos_query(taos, sql); + int code = taos_errno(res); + if (code != 0) { + printf("%s\n", taos_errstr(res)); + taos_free_result(res); + taos_close(taos); + exit(EXIT_FAILURE); + } + taos_free_result(res); +} + +// ANCHOR: main +int main() { + TAOS *taos = taos_connect("localhost", "root", "taosdata", "", 0); + if (taos == NULL) { + printf("failed to connect to server\n"); + exit(EXIT_FAILURE); + } + executeSQL(taos, "DROP DATABASE IF EXISTS test"); + executeSQL(taos, "CREATE DATABASE test"); + executeSQL(taos, "USE test"); + char *lines[] = {"meters,location=Beijing.Haidian,groupid=2 current=11.8,voltage=221,phase=0.28 1648432611249", + "meters,location=Beijing.Haidian,groupid=2 current=13.4,voltage=223,phase=0.29 1648432611250", + "meters,location=Beijing.Haidian,groupid=3 current=10.8,voltage=223,phase=0.29 1648432611249", + "meters,location=Beijing.Haidian,groupid=3 current=11.3,voltage=221,phase=0.35 1648432611250"}; + TAOS_RES *res = taos_schemaless_insert(taos, lines, 4, TSDB_SML_LINE_PROTOCOL, TSDB_SML_TIMESTAMP_MILLI_SECONDS); + if (taos_errno(res) != 0) { + printf("failed to insert schema-less data, reason: %s\n", taos_errstr(res)); + } else { + int affectedRows = taos_affected_rows(res); + printf("successfully inserted %d rows\n", affectedRows); + } + taos_free_result(res); + taos_close(taos); + taos_cleanup(); +} +// output: +// successfully inserted 4 rows +// ANCHOR_END: main \ No newline at end of file diff --git a/docs-examples/c/multi_bind_example.c b/docs-examples/c/multi_bind_example.c new file mode 100644 index 0000000000000000000000000000000000000000..fe11df9caad3e216fbd0b1ff2f40a54fe3ba86e5 --- /dev/null +++ b/docs-examples/c/multi_bind_example.c @@ -0,0 +1,147 @@ +// compile with +// gcc -o multi_bind_example multi_bind_example.c -ltaos +#include +#include +#include +#include "taos.h" + +/** + * @brief execute sql only and ignore result set + * + * @param taos + * @param sql + */ +void executeSQL(TAOS *taos, const char *sql) { + TAOS_RES *res = taos_query(taos, sql); + int code = taos_errno(res); + if (code != 0) { + printf("%s\n", taos_errstr(res)); + taos_free_result(res); + taos_close(taos); + exit(EXIT_FAILURE); + } + taos_free_result(res); +} + +/** + * @brief exit program when error occur. + * + * @param stmt + * @param code + * @param msg + */ +void checkErrorCode(TAOS_STMT *stmt, int code, const char *msg) { + if (code != 0) { + printf("%s. error: %s\n", msg, taos_stmt_errstr(stmt)); + taos_stmt_close(stmt); + exit(EXIT_FAILURE); + } +} + +/** + * @brief insert data using stmt API + * + * @param taos + */ +void insertData(TAOS *taos) { + // init + TAOS_STMT *stmt = taos_stmt_init(taos); + // prepare + const char *sql = "INSERT INTO ? USING meters TAGS(?, ?) values(?, ?, ?, ?)"; + int code = taos_stmt_prepare(stmt, sql, 0); + checkErrorCode(stmt, code, "failed to execute taos_stmt_prepare"); + // bind table name and tags + TAOS_BIND tags[2]; + char *location = "Beijing.Chaoyang"; + int groupId = 2; + tags[0].buffer_type = TSDB_DATA_TYPE_BINARY; + tags[0].buffer_length = strlen(location); + tags[0].length = &tags[0].buffer_length; + tags[0].buffer = location; + tags[0].is_null = NULL; + + tags[1].buffer_type = TSDB_DATA_TYPE_INT; + tags[1].buffer_length = sizeof(int); + tags[1].length = &tags[1].buffer_length; + tags[1].buffer = &groupId; + tags[1].is_null = NULL; + + code = taos_stmt_set_tbname_tags(stmt, "d1001", tags); + checkErrorCode(stmt, code, "failed to execute taos_stmt_set_tbname_tags"); + + // highlight-start + // insert two rows with multi binds + TAOS_MULTI_BIND params[4]; + // values to bind + int64_t ts[] = {1648432611249, 1648432611749}; + float current[] = {10.3, 12.6}; + int voltage[] = {219, 218}; + float phase[] = {0.31, 0.33}; + // is_null array + char is_null[2] = {0}; + // length array + int32_t int64Len[2] = {sizeof(int64_t)}; + int32_t floatLen[2] = {sizeof(float)}; + int32_t intLen[2] = {sizeof(int)}; + + params[0].buffer_type = TSDB_DATA_TYPE_TIMESTAMP; + params[0].buffer_length = sizeof(int64_t); + params[0].buffer = ts; + params[0].length = int64Len; + params[0].is_null = is_null; + params[0].num = 2; + + params[1].buffer_type = TSDB_DATA_TYPE_FLOAT; + params[1].buffer_length = sizeof(float); + params[1].buffer = current; + params[1].length = floatLen; + params[1].is_null = is_null; + params[1].num = 2; + + params[2].buffer_type = TSDB_DATA_TYPE_INT; + params[2].buffer_length = sizeof(int); + params[2].buffer = voltage; + params[2].length = intLen; + params[2].is_null = is_null; + params[2].num = 2; + + params[3].buffer_type = TSDB_DATA_TYPE_FLOAT; + params[3].buffer_length = sizeof(float); + params[3].buffer = phase; + params[3].length = floatLen; + params[3].is_null = is_null; + params[3].num = 2; + + code = taos_stmt_bind_param_batch(stmt, params); // bind batch + checkErrorCode(stmt, code, "failed to execute taos_stmt_bind_param_batch"); + code = taos_stmt_add_batch(stmt); // add batch + checkErrorCode(stmt, code, "failed to execute taos_stmt_add_batch"); + // highlight-end + // execute + code = taos_stmt_execute(stmt); + checkErrorCode(stmt, code, "failed to execute taos_stmt_execute"); + int affectedRows = taos_stmt_affected_rows(stmt); + printf("successfully inserted %d rows\n", affectedRows); + // close + taos_stmt_close(stmt); +} + +int main() { + TAOS *taos = taos_connect("localhost", "root", "taosdata", NULL, 6030); + if (taos == NULL) { + printf("failed to connect to server\n"); + exit(EXIT_FAILURE); + } + executeSQL(taos, "DROP DATABASE IF EXISTS power"); + executeSQL(taos, "CREATE DATABASE power"); + executeSQL(taos, "USE power"); + executeSQL(taos, + "CREATE STABLE meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), " + "groupId INT)"); + insertData(taos); + taos_close(taos); + taos_cleanup(); +} + +// output: +// successfully inserted 2 rows \ No newline at end of file diff --git a/docs-examples/c/query_example.c b/docs-examples/c/query_example.c new file mode 100644 index 0000000000000000000000000000000000000000..f88b2467ceb3d9bbeaf6b3beb6a24befd3e398c6 --- /dev/null +++ b/docs-examples/c/query_example.c @@ -0,0 +1,143 @@ +// compile with: +// gcc -o query_example query_example.c -ltaos +#include +#include +#include +#include +#include + +typedef int16_t VarDataLenT; + +#define TSDB_NCHAR_SIZE sizeof(int32_t) +#define VARSTR_HEADER_SIZE sizeof(VarDataLenT) + +#define GET_FLOAT_VAL(x) (*(float *)(x)) +#define GET_DOUBLE_VAL(x) (*(double *)(x)) + +#define varDataLen(v) ((VarDataLenT *)(v))[0] + +int printRow(char *str, TAOS_ROW row, TAOS_FIELD *fields, int numFields) { + int len = 0; + char split = ' '; + + for (int i = 0; i < numFields; ++i) { + if (i > 0) { + str[len++] = split; + } + + if (row[i] == NULL) { + len += sprintf(str + len, "%s", "NULL"); + continue; + } + + switch (fields[i].type) { + case TSDB_DATA_TYPE_TINYINT: + len += sprintf(str + len, "%d", *((int8_t *)row[i])); + break; + + case TSDB_DATA_TYPE_UTINYINT: + len += sprintf(str + len, "%u", *((uint8_t *)row[i])); + break; + + case TSDB_DATA_TYPE_SMALLINT: + len += sprintf(str + len, "%d", *((int16_t *)row[i])); + break; + + case TSDB_DATA_TYPE_USMALLINT: + len += sprintf(str + len, "%u", *((uint16_t *)row[i])); + break; + + case TSDB_DATA_TYPE_INT: + len += sprintf(str + len, "%d", *((int32_t *)row[i])); + break; + + case TSDB_DATA_TYPE_UINT: + len += sprintf(str + len, "%u", *((uint32_t *)row[i])); + break; + + case TSDB_DATA_TYPE_BIGINT: + len += sprintf(str + len, "%" PRId64, *((int64_t *)row[i])); + break; + + case TSDB_DATA_TYPE_UBIGINT: + len += sprintf(str + len, "%" PRIu64, *((uint64_t *)row[i])); + break; + + case TSDB_DATA_TYPE_FLOAT: { + float fv = 0; + fv = GET_FLOAT_VAL(row[i]); + len += sprintf(str + len, "%f", fv); + } break; + + case TSDB_DATA_TYPE_DOUBLE: { + double dv = 0; + dv = GET_DOUBLE_VAL(row[i]); + len += sprintf(str + len, "%lf", dv); + } break; + + case TSDB_DATA_TYPE_BINARY: + case TSDB_DATA_TYPE_NCHAR: { + int32_t charLen = varDataLen((char *)row[i] - VARSTR_HEADER_SIZE); + memcpy(str + len, row[i], charLen); + len += charLen; + } break; + + case TSDB_DATA_TYPE_TIMESTAMP: + len += sprintf(str + len, "%" PRId64, *((int64_t *)row[i])); + break; + + case TSDB_DATA_TYPE_BOOL: + len += sprintf(str + len, "%d", *((int8_t *)row[i])); + default: + break; + } + } + + return len; +} + +/** + * @brief print column name and values of each row + * + * @param res + * @return int + */ +static int printResult(TAOS_RES *res) { + int numFields = taos_num_fields(res); + TAOS_FIELD *fields = taos_fetch_fields(res); + char header[256] = {0}; + int len = 0; + for (int i = 0; i < numFields; ++i) { + len += sprintf(header + len, "%s ", fields[i].name); + } + puts(header); + + TAOS_ROW row = NULL; + while ((row = taos_fetch_row(res))) { + char temp[256] = {0}; + printRow(temp, row, fields, numFields); + puts(temp); + } +} + +int main() { + TAOS *taos = taos_connect("localhost", "root", "taosdata", "power", 6030); + if (taos == NULL) { + puts("failed to connect to server"); + exit(EXIT_FAILURE); + } + TAOS_RES *res = taos_query(taos, "SELECT * FROM meters LIMIT 2"); + if (taos_errno(res) != 0) { + printf("failed to execute taos_query. error: %s\n", taos_errstr(res)); + exit(EXIT_FAILURE); + } + printResult(res); + taos_free_result(res); + taos_close(taos); + taos_cleanup(); +} + +// output: +// ts current voltage phase location groupid +// 1648432611249 10.300000 219 0.310000 Beijing.Chaoyang 2 +// 1648432611749 12.600000 218 0.330000 Beijing.Chaoyang 2 \ No newline at end of file diff --git a/docs-examples/c/stmt_example.c b/docs-examples/c/stmt_example.c new file mode 100644 index 0000000000000000000000000000000000000000..fab1506f953ef68050e4318406fa2ba1a0202929 --- /dev/null +++ b/docs-examples/c/stmt_example.c @@ -0,0 +1,141 @@ +// compile with +// gcc -o stmt_example stmt_example.c -ltaos +#include +#include +#include +#include "taos.h" + +/** + * @brief execute sql only. + * + * @param taos + * @param sql + */ +void executeSQL(TAOS *taos, const char *sql) { + TAOS_RES *res = taos_query(taos, sql); + int code = taos_errno(res); + if (code != 0) { + printf("%s\n", taos_errstr(res)); + taos_free_result(res); + taos_close(taos); + exit(EXIT_FAILURE); + } + taos_free_result(res); +} + +/** + * @brief check return status and exit program when error occur. + * + * @param stmt + * @param code + * @param msg + */ +void checkErrorCode(TAOS_STMT *stmt, int code, const char* msg) { + if (code != 0) { + printf("%s. error: %s\n", msg, taos_stmt_errstr(stmt)); + taos_stmt_close(stmt); + exit(EXIT_FAILURE); + } +} + +typedef struct { + int64_t ts; + float current; + int voltage; + float phase; +} Row; + +/** + * @brief insert data using stmt API + * + * @param taos + */ +void insertData(TAOS *taos) { + // init + TAOS_STMT *stmt = taos_stmt_init(taos); + // prepare + const char *sql = "INSERT INTO ? USING meters TAGS(?, ?) VALUES(?, ?, ?, ?)"; + int code = taos_stmt_prepare(stmt, sql, 0); + checkErrorCode(stmt, code, "failed to execute taos_stmt_prepare"); + // bind table name and tags + TAOS_BIND tags[2]; + char* location = "Beijing.Chaoyang"; + int groupId = 2; + tags[0].buffer_type = TSDB_DATA_TYPE_BINARY; + tags[0].buffer_length = strlen(location); + tags[0].length = &tags[0].buffer_length; + tags[0].buffer = location; + tags[0].is_null = NULL; + + tags[1].buffer_type = TSDB_DATA_TYPE_INT; + tags[1].buffer_length = sizeof(int); + tags[1].length = &tags[1].buffer_length; + tags[1].buffer = &groupId; + tags[1].is_null = NULL; + + code = taos_stmt_set_tbname_tags(stmt, "d1001", tags); + checkErrorCode(stmt, code, "failed to execute taos_stmt_set_tbname_tags"); + + // insert two rows + Row rows[2] = { + {1648432611249, 10.3, 219, 0.31}, + {1648432611749, 12.6, 218, 0.33}, + }; + + TAOS_BIND values[4]; + values[0].buffer_type = TSDB_DATA_TYPE_TIMESTAMP; + values[0].buffer_length = sizeof(int64_t); + values[0].length = &values[0].buffer_length; + values[0].is_null = NULL; + + values[1].buffer_type = TSDB_DATA_TYPE_FLOAT; + values[1].buffer_length = sizeof(float); + values[1].length = &values[1].buffer_length; + values[1].is_null = NULL; + + values[2].buffer_type = TSDB_DATA_TYPE_INT; + values[2].buffer_length = sizeof(int); + values[2].length = &values[2].buffer_length; + values[2].is_null = NULL; + + values[3].buffer_type = TSDB_DATA_TYPE_FLOAT; + values[3].buffer_length = sizeof(float); + values[3].length = &values[3].buffer_length; + values[3].is_null = NULL; + + for (int i = 0; i < 2; ++i) { + values[0].buffer = &rows[i].ts; + values[1].buffer = &rows[i].current; + values[2].buffer = &rows[i].voltage; + values[3].buffer = &rows[i].phase; + code = taos_stmt_bind_param(stmt, values); // bind param + checkErrorCode(stmt, code, "failed to execute taos_stmt_bind_param"); + code = taos_stmt_add_batch(stmt); // add batch + checkErrorCode(stmt, code, "failed to execute taos_stmt_add_batch"); + } + // execute + code = taos_stmt_execute(stmt); + checkErrorCode(stmt, code, "failed to execute taos_stmt_execute"); + int affectedRows = taos_stmt_affected_rows(stmt); + printf("successfully inserted %d rows\n", affectedRows); + // close + taos_stmt_close(stmt); +} + +int main() { + TAOS *taos = taos_connect("localhost", "root", "taosdata", NULL, 6030); + if (taos == NULL) { + printf("failed to connect to server\n"); + exit(EXIT_FAILURE); + } + executeSQL(taos, "CREATE DATABASE power"); + executeSQL(taos, "USE power"); + executeSQL(taos, "CREATE STABLE meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT)"); + insertData(taos); + taos_close(taos); + taos_cleanup(); +} + + +// output: +// successfully inserted 2 rows \ No newline at end of file diff --git a/docs-examples/c/subscribe_demo.c b/docs-examples/c/subscribe_demo.c new file mode 100644 index 0000000000000000000000000000000000000000..2fe62c24eb92d2f57c24b40fc16f47d62ea5e378 --- /dev/null +++ b/docs-examples/c/subscribe_demo.c @@ -0,0 +1,66 @@ +// A simple demo for asynchronous subscription. +// compile with: +// gcc -o subscribe_demo subscribe_demo.c -ltaos + +#include +#include +#include +#include + +int nTotalRows; + +/** + * @brief callback function of subscription. + * + * @param tsub + * @param res + * @param param. the additional parameter passed to taos_subscribe + * @param code. error code + */ +void subscribe_callback(TAOS_SUB* tsub, TAOS_RES* res, void* param, int code) { + if (code != 0) { + printf("error: %d\n", code); + exit(EXIT_FAILURE); + } + + TAOS_ROW row = NULL; + int num_fields = taos_num_fields(res); + TAOS_FIELD* fields = taos_fetch_fields(res); + int nRows = 0; + + while ((row = taos_fetch_row(res))) { + char buf[4096] = {0}; + taos_print_row(buf, row, fields, num_fields); + puts(buf); + nRows++; + } + + nTotalRows += nRows; + printf("%d rows consumed.\n", nRows); +} + +int main() { + TAOS* taos = taos_connect("localhost", "root", "taosdata", NULL, 6030); + if (taos == NULL) { + printf("failed to connect to server\n"); + exit(EXIT_FAILURE); + } + + int restart = 1; // if the topic already exists, where to subscribe from the begin. + const char* topic = "topic-meter-current-bg-10"; + const char* sql = "select * from power.meters where current > 10"; + void* param = NULL; // additional parameter. + int interval = 2000; // consumption interval in microseconds. + TAOS_SUB* tsub = taos_subscribe(taos, restart, topic, sql, subscribe_callback, NULL, interval); + + // wait for insert from others process. you can open TDengine CLI to insert some records for test. + + getchar(); // press Enter to stop + + printf("total rows consumed: %d\n", nTotalRows); + int keep = 0; // whether to keep subscribe process + taos_unsubscribe(tsub, keep); + + taos_close(taos); + taos_cleanup(); +} diff --git a/docs-examples/c/telnet_line_example.c b/docs-examples/c/telnet_line_example.c new file mode 100644 index 0000000000000000000000000000000000000000..913d433f6aec07b3bce115d45536ffa4b45a0481 --- /dev/null +++ b/docs-examples/c/telnet_line_example.c @@ -0,0 +1,54 @@ +// compile with +// gcc -o telnet_line_example telnet_line_example.c -ltaos +#include +#include +#include +#include "taos.h" + +void executeSQL(TAOS *taos, const char *sql) { + TAOS_RES *res = taos_query(taos, sql); + int code = taos_errno(res); + if (code != 0) { + printf("%s\n", taos_errstr(res)); + taos_free_result(res); + taos_close(taos); + exit(EXIT_FAILURE); + } + taos_free_result(res); +} + +// ANCHOR: main +int main() { + TAOS *taos = taos_connect("localhost", "root", "taosdata", "", 6030); + if (taos == NULL) { + printf("failed to connect to server\n"); + exit(EXIT_FAILURE); + } + executeSQL(taos, "DROP DATABASE IF EXISTS test"); + executeSQL(taos, "CREATE DATABASE test"); + executeSQL(taos, "USE test"); + char *lines[] = { + "meters.current 1648432611249 10.3 location=Beijing.Chaoyang groupid=2", + "meters.current 1648432611250 12.6 location=Beijing.Chaoyang groupid=2", + "meters.current 1648432611249 10.8 location=Beijing.Haidian groupid=3", + "meters.current 1648432611250 11.3 location=Beijing.Haidian groupid=3", + "meters.voltage 1648432611249 219 location=Beijing.Chaoyang groupid=2", + "meters.voltage 1648432611250 218 location=Beijing.Chaoyang groupid=2", + "meters.voltage 1648432611249 221 location=Beijing.Haidian groupid=3", + "meters.voltage 1648432611250 217 location=Beijing.Haidian groupid=3", + }; + TAOS_RES *res = taos_schemaless_insert(taos, lines, 8, TSDB_SML_TELNET_PROTOCOL, TSDB_SML_TIMESTAMP_NOT_CONFIGURED); + if (taos_errno(res) != 0) { + printf("failed to insert schema-less data, reason: %s\n", taos_errstr(res)); + } else { + int affectedRow = taos_affected_rows(res); + printf("successfully inserted %d rows\n", affectedRow); + } + + taos_free_result(res); + taos_close(taos); + taos_cleanup(); +} +// output: +// successfully inserted 8 rows +// ANCHOR_END: main diff --git a/docs-examples/csharp/.gitignore b/docs-examples/csharp/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..b3aff79f3706e23aa74199a7f521f7912d2b0e45 --- /dev/null +++ b/docs-examples/csharp/.gitignore @@ -0,0 +1,4 @@ +bin +obj +.vs +*.sln \ No newline at end of file diff --git a/docs-examples/csharp/AsyncQueryExample.cs b/docs-examples/csharp/AsyncQueryExample.cs new file mode 100644 index 0000000000000000000000000000000000000000..fe30d21efe82e8d1dc414bd4723227ca93bc944f --- /dev/null +++ b/docs-examples/csharp/AsyncQueryExample.cs @@ -0,0 +1,238 @@ +using TDengineDriver; +using System.Runtime.InteropServices; + +namespace TDengineExample +{ + public class AsyncQueryExample + { + static void Main() + { + IntPtr conn = GetConnection(); + QueryAsyncCallback queryAsyncCallback = new QueryAsyncCallback(QueryCallback); + TDengine.QueryAsync(conn, "select * from meters", queryAsyncCallback, IntPtr.Zero); + Thread.Sleep(2000); + TDengine.Close(conn); + TDengine.Cleanup(); + } + + static void QueryCallback(IntPtr param, IntPtr taosRes, int code) + { + if (code == 0 && taosRes != IntPtr.Zero) + { + FetchRowAsyncCallback fetchRowAsyncCallback = new FetchRowAsyncCallback(FetchRowCallback); + TDengine.FetchRowAsync(taosRes, fetchRowAsyncCallback, param); + } + else + { + Console.WriteLine($"async query data failed, failed code {code}"); + } + } + + static void FetchRowCallback(IntPtr param, IntPtr taosRes, int numOfRows) + { + if (numOfRows > 0) + { + Console.WriteLine($"{numOfRows} rows async retrieved"); + DisplayRes(taosRes); + TDengine.FetchRowAsync(taosRes, FetchRowCallback, param); + } + else + { + if (numOfRows == 0) + { + Console.WriteLine("async retrieve complete."); + + } + else + { + Console.WriteLine($"FetchRowAsync callback error, error code {numOfRows}"); + } + TDengine.FreeResult(taosRes); + } + } + + public static void DisplayRes(IntPtr res) + { + if (!IsValidResult(res)) + { + TDengine.Cleanup(); + System.Environment.Exit(1); + } + + List metaList = TDengine.FetchFields(res); + int fieldCount = metaList.Count; + // metaList.ForEach((item) => { Console.Write("{0} ({1}) \t|\t", item.name, item.size); }); + + List dataList = QueryRes(res, metaList); + for (int index = 0; index < dataList.Count; index++) + { + if (index % fieldCount == 0 && index != 0) + { + Console.WriteLine(""); + } + Console.Write("{0} \t|\t", dataList[index].ToString()); + + } + Console.WriteLine(""); + } + + public static bool IsValidResult(IntPtr res) + { + if ((res == IntPtr.Zero) || (TDengine.ErrorNo(res) != 0)) + { + if (res != IntPtr.Zero) + { + Console.Write("reason: " + TDengine.Error(res)); + return false; + } + Console.WriteLine(""); + return false; + } + return true; + } + + private static List QueryRes(IntPtr res, List meta) + { + IntPtr taosRow; + List dataRaw = new(); + while ((taosRow = TDengine.FetchRows(res)) != IntPtr.Zero) + { + dataRaw.AddRange(FetchRow(taosRow, res)); + } + if (TDengine.ErrorNo(res) != 0) + { + Console.Write("Query is not complete, Error {0} {1}", TDengine.ErrorNo(res), TDengine.Error(res)); + } + TDengine.FreeResult(res); + Console.WriteLine(""); + return dataRaw; + } + + public static List FetchRow(IntPtr taosRow, IntPtr taosRes)//, List metaList, int numOfFiled + { + List metaList = TDengine.FetchFields(taosRes); + int numOfFiled = TDengine.FieldCount(taosRes); + + + List dataRaw = new(); + + IntPtr colLengthPrt = TDengine.FetchLengths(taosRes); + int[] colLengthArr = new int[numOfFiled]; + Marshal.Copy(colLengthPrt, colLengthArr, 0, numOfFiled); + + for (int i = 0; i < numOfFiled; i++) + { + TDengineMeta meta = metaList[i]; + IntPtr data = Marshal.ReadIntPtr(taosRow, IntPtr.Size * i); + + if (data == IntPtr.Zero) + { + dataRaw.Add("NULL"); + continue; + } + switch ((TDengineDataType)meta.type) + { + case TDengineDataType.TSDB_DATA_TYPE_BOOL: + bool v1 = Marshal.ReadByte(data) != 0; + dataRaw.Add(v1); + break; + case TDengineDataType.TSDB_DATA_TYPE_TINYINT: + sbyte v2 = (sbyte)Marshal.ReadByte(data); + dataRaw.Add(v2); + break; + case TDengineDataType.TSDB_DATA_TYPE_SMALLINT: + short v3 = Marshal.ReadInt16(data); + dataRaw.Add(v3); + break; + case TDengineDataType.TSDB_DATA_TYPE_INT: + int v4 = Marshal.ReadInt32(data); + dataRaw.Add(v4); + break; + case TDengineDataType.TSDB_DATA_TYPE_BIGINT: + long v5 = Marshal.ReadInt64(data); + dataRaw.Add(v5); + break; + case TDengineDataType.TSDB_DATA_TYPE_FLOAT: + float v6 = (float)Marshal.PtrToStructure(data, typeof(float)); + dataRaw.Add(v6); + break; + case TDengineDataType.TSDB_DATA_TYPE_DOUBLE: + double v7 = (double)Marshal.PtrToStructure(data, typeof(double)); + dataRaw.Add(v7); + break; + case TDengineDataType.TSDB_DATA_TYPE_BINARY: + string v8 = Marshal.PtrToStringUTF8(data, colLengthArr[i]); + dataRaw.Add(v8); + break; + case TDengineDataType.TSDB_DATA_TYPE_TIMESTAMP: + long v9 = Marshal.ReadInt64(data); + dataRaw.Add(v9); + break; + case TDengineDataType.TSDB_DATA_TYPE_NCHAR: + string v10 = Marshal.PtrToStringUTF8(data, colLengthArr[i]); + dataRaw.Add(v10); + break; + case TDengineDataType.TSDB_DATA_TYPE_UTINYINT: + byte v12 = Marshal.ReadByte(data); + dataRaw.Add(v12.ToString()); + break; + case TDengineDataType.TSDB_DATA_TYPE_USMALLINT: + ushort v13 = (ushort)Marshal.ReadInt16(data); + dataRaw.Add(v13); + break; + case TDengineDataType.TSDB_DATA_TYPE_UINT: + uint v14 = (uint)Marshal.ReadInt32(data); + dataRaw.Add(v14); + break; + case TDengineDataType.TSDB_DATA_TYPE_UBIGINT: + ulong v15 = (ulong)Marshal.ReadInt64(data); + dataRaw.Add(v15); + break; + case TDengineDataType.TSDB_DATA_TYPE_JSONTAG: + string v16 = Marshal.PtrToStringUTF8(data, colLengthArr[i]); + dataRaw.Add(v16); + break; + default: + dataRaw.Add("nonsupport data type"); + break; + } + + } + return dataRaw; + } + + static IntPtr GetConnection() + { + string host = "localhost"; + short port = 6030; + string username = "root"; + string password = "taosdata"; + string dbname = "power"; + var conn = TDengine.Connect(host, username, password, dbname, port); + if (conn == IntPtr.Zero) + { + Console.WriteLine("Connect to TDengine failed"); + Environment.Exit(0); + } + else + { + Console.WriteLine("Connect to TDengine success"); + } + return conn; + } + } +} + +//output: +//Connect to TDengine success +//8 rows async retrieved + +//1538548685000 | 10.3 | 219 | 0.31 | beijing.chaoyang | 2 | +//1538548695000 | 12.6 | 218 | 0.33 | beijing.chaoyang | 2 | +//1538548696800 | 12.3 | 221 | 0.31 | beijing.chaoyang | 2 | +//1538548696650 | 10.3 | 218 | 0.25 | beijing.chaoyang | 3 | +//1538548685500 | 11.8 | 221 | 0.28 | beijing.haidian | 2 | +//1538548696600 | 13.4 | 223 | 0.29 | beijing.haidian | 2 | +//1538548685000 | 10.8 | 223 | 0.29 | beijing.haidian | 3 | +//1538548686500 | 11.5 | 221 | 0.35 | beijing.haidian | 3 | +//async retrieve complete. \ No newline at end of file diff --git a/docs-examples/csharp/ConnectExample.cs b/docs-examples/csharp/ConnectExample.cs new file mode 100644 index 0000000000000000000000000000000000000000..f3548ee65daab8a59695499339a8f89b0aa33a10 --- /dev/null +++ b/docs-examples/csharp/ConnectExample.cs @@ -0,0 +1,29 @@ +using TDengineDriver; + +namespace TDengineExample +{ + + internal class ConnectExample + { + static void Main(String[] args) + { + string host = "localhost"; + short port = 6030; + string username = "root"; + string password = "taosdata"; + string dbname = ""; + + var conn = TDengine.Connect(host, username, password, dbname, port); + if (conn == IntPtr.Zero) + { + Console.WriteLine("Connect to TDengine failed"); + } + else + { + Console.WriteLine("Connect to TDengine success"); + } + TDengine.Close(conn); + TDengine.Cleanup(); + } + } +} diff --git a/docs-examples/csharp/InfluxDBLineExample.cs b/docs-examples/csharp/InfluxDBLineExample.cs new file mode 100644 index 0000000000000000000000000000000000000000..7aad08825209db568d61e5963ec7a00034ab7ca7 --- /dev/null +++ b/docs-examples/csharp/InfluxDBLineExample.cs @@ -0,0 +1,77 @@ +using TDengineDriver; + +namespace TDengineExample +{ + internal class InfluxDBLineExample + { + static void Main() + { + IntPtr conn = GetConnection(); + PrepareDatabase(conn); + string[] lines = { + "meters,location=Beijing.Haidian,groupid=2 current=11.8,voltage=221,phase=0.28 1648432611249", + "meters,location=Beijing.Haidian,groupid=2 current=13.4,voltage=223,phase=0.29 1648432611250", + "meters,location=Beijing.Haidian,groupid=3 current=10.8,voltage=223,phase=0.29 1648432611249", + "meters,location=Beijing.Haidian,groupid=3 current=11.3,voltage=221,phase=0.35 1648432611250" + }; + IntPtr res = TDengine.SchemalessInsert(conn, lines, lines.Length, (int)TDengineSchemalessProtocol.TSDB_SML_LINE_PROTOCOL, (int)TDengineSchemalessPrecision.TSDB_SML_TIMESTAMP_MILLI_SECONDS); + if (TDengine.ErrorNo(res) != 0) + { + Console.WriteLine("SchemalessInsert failed since " + TDengine.Error(res)); + ExitProgram(conn, 1); + } + else + { + int affectedRows = TDengine.AffectRows(res); + Console.WriteLine($"SchemalessInsert success, affected {affectedRows} rows"); + } + TDengine.FreeResult(res); + ExitProgram(conn, 0); + + } + static IntPtr GetConnection() + { + string host = "localhost"; + short port = 6030; + string username = "root"; + string password = "taosdata"; + string dbname = ""; + var conn = TDengine.Connect(host, username, password, dbname, port); + if (conn == IntPtr.Zero) + { + Console.WriteLine("Connect to TDengine failed"); + TDengine.Cleanup(); + Environment.Exit(1); + } + else + { + Console.WriteLine("Connect to TDengine success"); + } + return conn; + } + + static void PrepareDatabase(IntPtr conn) + { + IntPtr res = TDengine.Query(conn, "CREATE DATABASE test"); + if (TDengine.ErrorNo(res) != 0) + { + Console.WriteLine("failed to create database, reason: " + TDengine.Error(res)); + ExitProgram(conn, 1); + } + res = TDengine.Query(conn, "USE test"); + if (TDengine.ErrorNo(res) != 0) + { + Console.WriteLine("failed to change database, reason: " + TDengine.Error(res)); + ExitProgram(conn, 1); + } + } + + static void ExitProgram(IntPtr conn, int exitCode) + { + TDengine.Close(conn); + TDengine.Cleanup(); + Environment.Exit(exitCode); + } + } + +} diff --git a/docs-examples/csharp/OptsJsonExample.cs b/docs-examples/csharp/OptsJsonExample.cs new file mode 100644 index 0000000000000000000000000000000000000000..d774a325afa1a8d93eb858f23dcd97dd29f8653d --- /dev/null +++ b/docs-examples/csharp/OptsJsonExample.cs @@ -0,0 +1,76 @@ +using TDengineDriver; + +namespace TDengineExample +{ + internal class OptsJsonExample + { + static void Main() + { + IntPtr conn = GetConnection(); + PrepareDatabase(conn); + string[] lines = { "[{\"metric\": \"meters.current\", \"timestamp\": 1648432611249, \"value\": 10.3, \"tags\": {\"location\": \"Beijing.Chaoyang\", \"groupid\": 2}}," + + " {\"metric\": \"meters.voltage\", \"timestamp\": 1648432611249, \"value\": 219, \"tags\": {\"location\": \"Beijing.Haidian\", \"groupid\": 1}}, " + + "{\"metric\": \"meters.current\", \"timestamp\": 1648432611250, \"value\": 12.6, \"tags\": {\"location\": \"Beijing.Chaoyang\", \"groupid\": 2}}," + + " {\"metric\": \"meters.voltage\", \"timestamp\": 1648432611250, \"value\": 221, \"tags\": {\"location\": \"Beijing.Haidian\", \"groupid\": 1}}]" + }; + + IntPtr res = TDengine.SchemalessInsert(conn, lines, 1, (int)TDengineSchemalessProtocol.TSDB_SML_JSON_PROTOCOL, (int)TDengineSchemalessPrecision.TSDB_SML_TIMESTAMP_NOT_CONFIGURED); + if (TDengine.ErrorNo(res) != 0) + { + Console.WriteLine("SchemalessInsert failed since " + TDengine.Error(res)); + ExitProgram(conn, 1); + } + else + { + int affectedRows = TDengine.AffectRows(res); + Console.WriteLine($"SchemalessInsert success, affected {affectedRows} rows"); + } + TDengine.FreeResult(res); + ExitProgram(conn, 0); + + } + static IntPtr GetConnection() + { + string host = "localhost"; + short port = 6030; + string username = "root"; + string password = "taosdata"; + string dbname = ""; + var conn = TDengine.Connect(host, username, password, dbname, port); + if (conn == IntPtr.Zero) + { + Console.WriteLine("Connect to TDengine failed"); + TDengine.Cleanup(); + Environment.Exit(1); + } + else + { + Console.WriteLine("Connect to TDengine success"); + } + return conn; + } + + static void PrepareDatabase(IntPtr conn) + { + IntPtr res = TDengine.Query(conn, "CREATE DATABASE test"); + if (TDengine.ErrorNo(res) != 0) + { + Console.WriteLine("failed to create database, reason: " + TDengine.Error(res)); + ExitProgram(conn, 1); + } + res = TDengine.Query(conn, "USE test"); + if (TDengine.ErrorNo(res) != 0) + { + Console.WriteLine("failed to change database, reason: " + TDengine.Error(res)); + ExitProgram(conn, 1); + } + } + + static void ExitProgram(IntPtr conn, int exitCode) + { + TDengine.Close(conn); + TDengine.Cleanup(); + Environment.Exit(exitCode); + } + } +} diff --git a/docs-examples/csharp/OptsTelnetExample.cs b/docs-examples/csharp/OptsTelnetExample.cs new file mode 100644 index 0000000000000000000000000000000000000000..81608c32213fa0618a2ca6e0769aacf8e9c8e64d --- /dev/null +++ b/docs-examples/csharp/OptsTelnetExample.cs @@ -0,0 +1,80 @@ +using TDengineDriver; + +namespace TDengineExample +{ + internal class OptsTelnetExample + { + static void Main() + { + IntPtr conn = GetConnection(); + PrepareDatabase(conn); + string[] lines = { + "meters.current 1648432611249 10.3 location=Beijing.Chaoyang groupid=2", + "meters.current 1648432611250 12.6 location=Beijing.Chaoyang groupid=2", + "meters.current 1648432611249 10.8 location=Beijing.Haidian groupid=3", + "meters.current 1648432611250 11.3 location=Beijing.Haidian groupid=3", + "meters.voltage 1648432611249 219 location=Beijing.Chaoyang groupid=2", + "meters.voltage 1648432611250 218 location=Beijing.Chaoyang groupid=2", + "meters.voltage 1648432611249 221 location=Beijing.Haidian groupid=3", + "meters.voltage 1648432611250 217 location=Beijing.Haidian groupid=3", + }; + IntPtr res = TDengine.SchemalessInsert(conn, lines, lines.Length, (int)TDengineSchemalessProtocol.TSDB_SML_TELNET_PROTOCOL, (int)TDengineSchemalessPrecision.TSDB_SML_TIMESTAMP_NOT_CONFIGURED); + if (TDengine.ErrorNo(res) != 0) + { + Console.WriteLine("SchemalessInsert failed since " + TDengine.Error(res)); + ExitProgram(conn, 1); + } + else + { + int affectedRows = TDengine.AffectRows(res); + Console.WriteLine($"SchemalessInsert success, affected {affectedRows} rows"); + } + TDengine.FreeResult(res); + ExitProgram(conn, 0); + + } + static IntPtr GetConnection() + { + string host = "localhost"; + short port = 6030; + string username = "root"; + string password = "taosdata"; + string dbname = ""; + var conn = TDengine.Connect(host, username, password, dbname, port); + if (conn == IntPtr.Zero) + { + Console.WriteLine("Connect to TDengine failed"); + TDengine.Cleanup(); + Environment.Exit(1); + } + else + { + Console.WriteLine("Connect to TDengine success"); + } + return conn; + } + + static void PrepareDatabase(IntPtr conn) + { + IntPtr res = TDengine.Query(conn, "CREATE DATABASE test"); + if (TDengine.ErrorNo(res) != 0) + { + Console.WriteLine("failed to create database, reason: " + TDengine.Error(res)); + ExitProgram(conn, 1); + } + res = TDengine.Query(conn, "USE test"); + if (TDengine.ErrorNo(res) != 0) + { + Console.WriteLine("failed to change database, reason: " + TDengine.Error(res)); + ExitProgram(conn, 1); + } + } + + static void ExitProgram(IntPtr conn, int exitCode) + { + TDengine.Close(conn); + TDengine.Cleanup(); + Environment.Exit(exitCode); + } + } +} diff --git a/docs-examples/csharp/QueryExample.cs b/docs-examples/csharp/QueryExample.cs new file mode 100644 index 0000000000000000000000000000000000000000..f00e391100c7ce42177e2987f5b0b32dc02262c4 --- /dev/null +++ b/docs-examples/csharp/QueryExample.cs @@ -0,0 +1,162 @@ +using TDengineDriver; +using System.Runtime.InteropServices; + +namespace TDengineExample +{ + internal class QueryExample + { + static void Main() + { + IntPtr conn = GetConnection(); + // run query + IntPtr res = TDengine.Query(conn, "SELECT * FROM test.meters LIMIT 2"); + if (TDengine.ErrorNo(res) != 0) + { + Console.WriteLine("Failed to query since: " + TDengine.Error(res)); + TDengine.Close(conn); + TDengine.Cleanup(); + return; + } + + // get filed count + int fieldCount = TDengine.FieldCount(res); + Console.WriteLine("fieldCount=" + fieldCount); + + // print column names + List metas = TDengine.FetchFields(res); + for (int i = 0; i < metas.Count; i++) + { + Console.Write(metas[i].name + "\t"); + } + Console.WriteLine(); + + // print values + IntPtr row; + while ((row = TDengine.FetchRows(res)) != IntPtr.Zero) + { + List metaList = TDengine.FetchFields(res); + int numOfFiled = TDengine.FieldCount(res); + + List dataRaw = new List(); + + IntPtr colLengthPrt = TDengine.FetchLengths(res); + int[] colLengthArr = new int[numOfFiled]; + Marshal.Copy(colLengthPrt, colLengthArr, 0, numOfFiled); + + for (int i = 0; i < numOfFiled; i++) + { + TDengineMeta meta = metaList[i]; + IntPtr data = Marshal.ReadIntPtr(row, IntPtr.Size * i); + + if (data == IntPtr.Zero) + { + Console.Write("NULL\t"); + continue; + } + switch ((TDengineDataType)meta.type) + { + case TDengineDataType.TSDB_DATA_TYPE_BOOL: + bool v1 = Marshal.ReadByte(data) == 0 ? false : true; + Console.Write(v1.ToString() + "\t"); + break; + case TDengineDataType.TSDB_DATA_TYPE_TINYINT: + sbyte v2 = (sbyte)Marshal.ReadByte(data); + Console.Write(v2.ToString() + "\t"); + break; + case TDengineDataType.TSDB_DATA_TYPE_SMALLINT: + short v3 = Marshal.ReadInt16(data); + Console.Write(v3.ToString() + "\t"); + break; + case TDengineDataType.TSDB_DATA_TYPE_INT: + int v4 = Marshal.ReadInt32(data); + Console.Write(v4.ToString() + "\t"); + break; + case TDengineDataType.TSDB_DATA_TYPE_BIGINT: + long v5 = Marshal.ReadInt64(data); + Console.Write(v5.ToString() + "\t"); + break; + case TDengineDataType.TSDB_DATA_TYPE_FLOAT: + float v6 = (float)Marshal.PtrToStructure(data, typeof(float)); + Console.Write(v6.ToString() + "\t"); + break; + case TDengineDataType.TSDB_DATA_TYPE_DOUBLE: + double v7 = (double)Marshal.PtrToStructure(data, typeof(double)); + Console.Write(v7.ToString() + "\t"); + break; + case TDengineDataType.TSDB_DATA_TYPE_BINARY: + string v8 = Marshal.PtrToStringUTF8(data, colLengthArr[i]); + Console.Write(v8 + "\t"); + break; + case TDengineDataType.TSDB_DATA_TYPE_TIMESTAMP: + long v9 = Marshal.ReadInt64(data); + Console.Write(v9.ToString() + "\t"); + break; + case TDengineDataType.TSDB_DATA_TYPE_NCHAR: + string v10 = Marshal.PtrToStringUTF8(data, colLengthArr[i]); + Console.Write(v10 + "\t"); + break; + case TDengineDataType.TSDB_DATA_TYPE_UTINYINT: + byte v12 = Marshal.ReadByte(data); + Console.Write(v12.ToString() + "\t"); + break; + case TDengineDataType.TSDB_DATA_TYPE_USMALLINT: + ushort v13 = (ushort)Marshal.ReadInt16(data); + Console.Write(v13.ToString() + "\t"); + break; + case TDengineDataType.TSDB_DATA_TYPE_UINT: + uint v14 = (uint)Marshal.ReadInt32(data); + Console.Write(v14.ToString() + "\t"); + break; + case TDengineDataType.TSDB_DATA_TYPE_UBIGINT: + ulong v15 = (ulong)Marshal.ReadInt64(data); + Console.Write(v15.ToString() + "\t"); + break; + case TDengineDataType.TSDB_DATA_TYPE_JSONTAG: + string v16 = Marshal.PtrToStringUTF8(data, colLengthArr[i]); + Console.Write(v16 + "\t"); + break; + default: + Console.Write("nonsupport data type value"); + break; + } + + } + Console.WriteLine(); + } + if (TDengine.ErrorNo(res) != 0) + { + Console.WriteLine($"Query is not complete, Error {TDengine.ErrorNo(res)} {TDengine.Error(res)}"); + } + // exit + TDengine.FreeResult(res); + TDengine.Close(conn); + TDengine.Cleanup(); + } + static IntPtr GetConnection() + { + string host = "localhost"; + short port = 6030; + string username = "root"; + string password = "taosdata"; + string dbname = "power"; + var conn = TDengine.Connect(host, username, password, dbname, port); + if (conn == IntPtr.Zero) + { + Console.WriteLine("Connect to TDengine failed"); + System.Environment.Exit(0); + } + else + { + Console.WriteLine("Connect to TDengine success"); + } + return conn; + } + } +} + +// output: +// Connect to TDengine success +// fieldCount=6 +// ts current voltage phase location groupid +// 1648432611249 10.3 219 0.31 Beijing.Chaoyang 2 +// 1648432611749 12.6 218 0.33 Beijing.Chaoyang 2 \ No newline at end of file diff --git a/docs-examples/csharp/SQLInsertExample.cs b/docs-examples/csharp/SQLInsertExample.cs new file mode 100644 index 0000000000000000000000000000000000000000..fa2e2a50daf06f4d948479e7f5b0df82c517f809 --- /dev/null +++ b/docs-examples/csharp/SQLInsertExample.cs @@ -0,0 +1,69 @@ +using TDengineDriver; + + +namespace TDengineExample +{ + internal class SQLInsertExample + { + + static void Main() + { + IntPtr conn = GetConnection(); + IntPtr res = TDengine.Query(conn, "CREATE DATABASE power"); + CheckRes(conn, res, "failed to create database"); + res = TDengine.Query(conn, "USE power"); + CheckRes(conn, res, "failed to change database"); + res = TDengine.Query(conn, "CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT)"); + CheckRes(conn, res, "failed to create stable"); + var sql = "INSERT INTO d1001 USING meters TAGS(Beijing.Chaoyang, 2) VALUES ('2018-10-03 14:38:05.000', 10.30000, 219, 0.31000) ('2018-10-03 14:38:15.000', 12.60000, 218, 0.33000) ('2018-10-03 14:38:16.800', 12.30000, 221, 0.31000) " + + "d1002 USING power.meters TAGS(Beijing.Chaoyang, 3) VALUES('2018-10-03 14:38:16.650', 10.30000, 218, 0.25000) " + + "d1003 USING power.meters TAGS(Beijing.Haidian, 2) VALUES('2018-10-03 14:38:05.500', 11.80000, 221, 0.28000)('2018-10-03 14:38:16.600', 13.40000, 223, 0.29000) " + + "d1004 USING power.meters TAGS(Beijing.Haidian, 3) VALUES('2018-10-03 14:38:05.000', 10.80000, 223, 0.29000)('2018-10-03 14:38:06.500', 11.50000, 221, 0.35000)"; + res = TDengine.Query(conn, sql); + CheckRes(conn, res, "failed to insert data"); + int affectedRows = TDengine.AffectRows(res); + Console.WriteLine("affectedRows " + affectedRows); + ExitProgram(conn, 0); + } + + static IntPtr GetConnection() + { + string host = "localhost"; + short port = 6030; + string username = "root"; + string password = "taosdata"; + string dbname = ""; + var conn = TDengine.Connect(host, username, password, dbname, port); + if (conn == IntPtr.Zero) + { + Console.WriteLine("Connect to TDengine failed"); + Environment.Exit(0); + } + else + { + Console.WriteLine("Connect to TDengine success"); + } + return conn; + } + + static void CheckRes(IntPtr conn, IntPtr res, String errorMsg) + { + if (TDengine.ErrorNo(res) != 0) + { + Console.Write(errorMsg + " since: " + TDengine.Error(res)); + ExitProgram(conn, 1); + } + } + + static void ExitProgram(IntPtr conn, int exitCode) + { + TDengine.Close(conn); + TDengine.Cleanup(); + Environment.Exit(exitCode); + } + } +} + +// output: +// Connect to TDengine success +// affectedRows 8 diff --git a/docs-examples/csharp/StmtInsertExample.cs b/docs-examples/csharp/StmtInsertExample.cs new file mode 100644 index 0000000000000000000000000000000000000000..d6e00dd4ac54ab8dbfc33b93896d19fc585e7642 --- /dev/null +++ b/docs-examples/csharp/StmtInsertExample.cs @@ -0,0 +1,115 @@ +using TDengineDriver; + +namespace TDengineExample +{ + internal class StmtInsertExample + { + private static IntPtr conn; + private static IntPtr stmt; + static void Main() + { + conn = GetConnection(); + PrepareSTable(); + // 1. init and prepare + stmt = TDengine.StmtInit(conn); + if (stmt == IntPtr.Zero) + { + Console.WriteLine("failed to init stmt, " + TDengine.Error(stmt)); + ExitProgram(); + } + int res = TDengine.StmtPrepare(stmt, "INSERT INTO ? USING meters TAGS(?, ?) VALUES(?, ?, ?, ?)"); + CheckStmtRes(res, "failed to prepare stmt"); + + // 2. bind table name and tags + TAOS_BIND[] tags = new TAOS_BIND[2] { TaosBind.BindBinary("Beijing.Chaoyang"), TaosBind.BindInt(2) }; + res = TDengine.StmtSetTbnameTags(stmt, "d1001", tags); + CheckStmtRes(res, "failed to bind table name and tags"); + + // 3. bind values + TAOS_MULTI_BIND[] values = new TAOS_MULTI_BIND[4] { + TaosMultiBind.MultiBindTimestamp(new long[2] { 1648432611249, 1648432611749}), + TaosMultiBind.MultiBindFloat(new float?[2] { 10.3f, 12.6f}), + TaosMultiBind.MultiBindInt(new int?[2] { 219, 218}), + TaosMultiBind.MultiBindFloat(new float?[2]{ 0.31f, 0.33f}) + }; + res = TDengine.StmtBindParamBatch(stmt, values); + CheckStmtRes(res, "failed to bind params"); + + // 4. add batch + res = TDengine.StmtAddBatch(stmt); + CheckStmtRes(res, "failed to add batch"); + + // 5. execute + res = TDengine.StmtExecute(stmt); + CheckStmtRes(res, "faild to execute"); + + // 6. free + TaosBind.FreeTaosBind(tags); + TaosMultiBind.FreeTaosBind(values); + TDengine.Close(conn); + TDengine.Cleanup(); + } + + static IntPtr GetConnection() + { + string host = "localhost"; + short port = 6030; + string username = "root"; + string password = "taosdata"; + string dbname = ""; + var conn = TDengine.Connect(host, username, password, dbname, port); + if (conn == IntPtr.Zero) + { + Console.WriteLine("Connect to TDengine failed"); + Environment.Exit(0); + } + else + { + Console.WriteLine("Connect to TDengine success"); + } + return conn; + } + + + + static void PrepareSTable() + { + IntPtr res = TDengine.Query(conn, "CREATE DATABASE power"); + CheckResPtr(res, "failed to create database"); + res = TDengine.Query(conn, "USE power"); + CheckResPtr(res, "failed to change database"); + res = TDengine.Query(conn, "CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT)"); + CheckResPtr(res, "failed to create stable"); + } + + static void CheckStmtRes(int res, string errorMsg) + { + if (res != 0) + { + Console.WriteLine(errorMsg + ", " + TDengine.StmtErrorStr(stmt)); + int code = TDengine.StmtClose(stmt); + if (code != 0) + { + Console.WriteLine($"falied to close stmt, {code} reason: {TDengine.StmtErrorStr(stmt)} "); + } + ExitProgram(); + } + } + + static void CheckResPtr(IntPtr res, string errorMsg) + { + if (TDengine.ErrorNo(res) != 0) + { + Console.WriteLine(errorMsg + " since:" + TDengine.Error(res)); + ExitProgram(); + } + } + + static void ExitProgram() + { + TDengine.Close(conn); + TDengine.Cleanup(); + Environment.Exit(1); + } + } +} diff --git a/docs-examples/csharp/SubscribeDemo.cs b/docs-examples/csharp/SubscribeDemo.cs new file mode 100644 index 0000000000000000000000000000000000000000..34509215da73ea6369eb95f458d622cd95a97932 --- /dev/null +++ b/docs-examples/csharp/SubscribeDemo.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace csharp +{ + internal class SubscribeDemo + { + } +} diff --git a/docs-examples/csharp/asyncquery.csproj b/docs-examples/csharp/asyncquery.csproj new file mode 100644 index 0000000000000000000000000000000000000000..7a952fe7abd15798bc704de52aa72624e59061eb --- /dev/null +++ b/docs-examples/csharp/asyncquery.csproj @@ -0,0 +1,15 @@ + + + + Exe + net6.0 + enable + enable + TDengineExample.AsyncQueryExample + + + + + + + diff --git a/docs-examples/csharp/connect.csproj b/docs-examples/csharp/connect.csproj new file mode 100644 index 0000000000000000000000000000000000000000..27cffa30ae5a98277b74c63cf3d2c749ec67ce8d --- /dev/null +++ b/docs-examples/csharp/connect.csproj @@ -0,0 +1,15 @@ + + + + Exe + net6.0 + enable + enable + TDengineExample.ConnectExample + + + + + + + diff --git a/docs-examples/csharp/influxdbline.csproj b/docs-examples/csharp/influxdbline.csproj new file mode 100644 index 0000000000000000000000000000000000000000..a8b197dc7167f229b903bfd010a9ff7282c9173b --- /dev/null +++ b/docs-examples/csharp/influxdbline.csproj @@ -0,0 +1,15 @@ + + + + Exe + net6.0 + enable + enable + TDengineExample.InfluxDBLineExample + + + + + + + diff --git a/docs-examples/csharp/optsjson.csproj b/docs-examples/csharp/optsjson.csproj new file mode 100644 index 0000000000000000000000000000000000000000..b1bd83405efb1086b23134a9c875912a9c09e3f8 --- /dev/null +++ b/docs-examples/csharp/optsjson.csproj @@ -0,0 +1,15 @@ + + + + Exe + net6.0 + enable + enable + TDengineExample.OptsJsonExample + + + + + + + diff --git a/docs-examples/csharp/optstelnet.csproj b/docs-examples/csharp/optstelnet.csproj new file mode 100644 index 0000000000000000000000000000000000000000..1ab41067715f8cb070e78aec2be59d686bac0e86 --- /dev/null +++ b/docs-examples/csharp/optstelnet.csproj @@ -0,0 +1,15 @@ + + + + Exe + net6.0 + enable + enable + TDengineExample.OptsTelnetExample + + + + + + + diff --git a/docs-examples/csharp/query.csproj b/docs-examples/csharp/query.csproj new file mode 100644 index 0000000000000000000000000000000000000000..63f13c3ddbf13be9ffe5c39054cc6e4cae6001e7 --- /dev/null +++ b/docs-examples/csharp/query.csproj @@ -0,0 +1,15 @@ + + + + Exe + net6.0 + enable + enable + TDengineExample.QueryExample + + + + + + + diff --git a/docs-examples/csharp/sqlinsert.csproj b/docs-examples/csharp/sqlinsert.csproj new file mode 100644 index 0000000000000000000000000000000000000000..0380395a5ad00d96418410b2f7974b294bf2bb07 --- /dev/null +++ b/docs-examples/csharp/sqlinsert.csproj @@ -0,0 +1,15 @@ + + + + Exe + net6.0 + enable + enable + TDengineExample.SQLInsertExample + + + + + + + diff --git a/docs-examples/csharp/stmtinsert.csproj b/docs-examples/csharp/stmtinsert.csproj new file mode 100644 index 0000000000000000000000000000000000000000..8defb895eb2894c680fe1b9dd7346223f0f92df1 --- /dev/null +++ b/docs-examples/csharp/stmtinsert.csproj @@ -0,0 +1,15 @@ + + + + Exe + net6.0 + enable + enable + TDengineExample.StmtInsertExample + + + + + + + diff --git a/docs-examples/csharp/subscribe.csproj b/docs-examples/csharp/subscribe.csproj new file mode 100644 index 0000000000000000000000000000000000000000..8286922c6f926b166f14983525e604ce9e13d74a --- /dev/null +++ b/docs-examples/csharp/subscribe.csproj @@ -0,0 +1,15 @@ + + + + Exe + net6.0 + enable + enable + TDengineExample.SubscribeDemo + + + + + + + diff --git a/docs-examples/go/.gitignore b/docs-examples/go/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..b6128386c306834cddeb425f365720bb9c9ce790 --- /dev/null +++ b/docs-examples/go/.gitignore @@ -0,0 +1,3 @@ +.idea +.vscode +tmp/ \ No newline at end of file diff --git a/docs-examples/go/connect/afconn/main.go b/docs-examples/go/connect/afconn/main.go new file mode 100644 index 0000000000000000000000000000000000000000..2d36ef03ab049a313ed0416f552397ff252e022e --- /dev/null +++ b/docs-examples/go/connect/afconn/main.go @@ -0,0 +1,17 @@ +package main + +import ( + "fmt" + + "github.com/taosdata/driver-go/v2/af" +) + +func main() { + conn, err := af.Open("localhost", "root", "taosdata", "", 6030) + defer conn.Close() + if err != nil { + fmt.Println("failed to connect, err:", err) + } else { + fmt.Println("connected") + } +} diff --git a/docs-examples/go/connect/cgoexample/main.go b/docs-examples/go/connect/cgoexample/main.go new file mode 100644 index 0000000000000000000000000000000000000000..8b9aba4ce4217c00605bc8796c788f3dd52805e6 --- /dev/null +++ b/docs-examples/go/connect/cgoexample/main.go @@ -0,0 +1,23 @@ +package main + +import ( + "database/sql" + "fmt" + + _ "github.com/taosdata/driver-go/v2/taosSql" +) + +func main() { + var taosDSN = "root:taosdata@tcp(localhost:6030)/" + taos, err := sql.Open("taosSql", taosDSN) + if err != nil { + fmt.Println("failed to connect TDengine, err:", err) + return + } + fmt.Println("Connected") + defer taos.Close() +} + +// use +// var taosDSN = "root:taosdata@tcp(localhost:6030)/dbName" +// if you want to connect to a default database. diff --git a/docs-examples/go/connect/restexample/main.go b/docs-examples/go/connect/restexample/main.go new file mode 100644 index 0000000000000000000000000000000000000000..9c05e7eed80dee4ae7e6b20637d265f388d7438d --- /dev/null +++ b/docs-examples/go/connect/restexample/main.go @@ -0,0 +1,23 @@ +package main + +import ( + "database/sql" + "fmt" + + _ "github.com/taosdata/driver-go/v2/taosRestful" +) + +func main() { + var taosDSN = "root:taosdata@http(localhost:6041)/" + taos, err := sql.Open("taosRestful", taosDSN) + if err != nil { + fmt.Println("failed to connect TDengine, err:", err) + return + } + fmt.Println("Connected") + defer taos.Close() +} + +// use +// var taosDSN = "root:taosdata@http(localhost:6041)/dbName" +// if you want to connect to a default database. diff --git a/docs-examples/go/connect/wrapper/main.go b/docs-examples/go/connect/wrapper/main.go new file mode 100644 index 0000000000000000000000000000000000000000..d7e71a8baf229012b6944aad0eaac232924dd667 --- /dev/null +++ b/docs-examples/go/connect/wrapper/main.go @@ -0,0 +1,17 @@ +package main + +import ( + "fmt" + + "github.com/taosdata/driver-go/v2/wrapper" +) + +func main() { + conn, err := wrapper.TaosConnect("localhost", "root", "taosdata", "", 6030) + defer wrapper.TaosClose(conn) + if err != nil { + fmt.Println("fail to connect, err:", err) + } else { + fmt.Println("connected") + } +} diff --git a/docs-examples/go/go.mod b/docs-examples/go/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..5945e395e93b373d47fe71f3584c37fed9526638 --- /dev/null +++ b/docs-examples/go/go.mod @@ -0,0 +1,6 @@ +module goexample + +go 1.17 + +require github.com/taosdata/driver-go/v2 develop + diff --git a/docs-examples/go/insert/json/main.go b/docs-examples/go/insert/json/main.go new file mode 100644 index 0000000000000000000000000000000000000000..47d9e9984adc05896fb9954ad3deffde3764b836 --- /dev/null +++ b/docs-examples/go/insert/json/main.go @@ -0,0 +1,37 @@ +package main + +import ( + "fmt" + + "github.com/taosdata/driver-go/v2/af" +) + +func prepareDatabase(conn *af.Connector) { + _, err := conn.Exec("CREATE DATABASE test") + if err != nil { + panic(err) + } + _, err = conn.Exec("USE test") + if err != nil { + panic(err) + } +} + +func main() { + conn, err := af.Open("localhost", "root", "taosdata", "", 6030) + if err != nil { + fmt.Println("fail to connect, err:", err) + } + defer conn.Close() + prepareDatabase(conn) + + payload := `[{"metric": "meters.current", "timestamp": 1648432611249, "value": 10.3, "tags": {"location": "Beijing.Chaoyang", "groupid": 2}}, + {"metric": "meters.voltage", "timestamp": 1648432611249, "value": 219, "tags": {"location": "Beijing.Haidian", "groupid": 1}}, + {"metric": "meters.current", "timestamp": 1648432611250, "value": 12.6, "tags": {"location": "Beijing.Chaoyang", "groupid": 2}}, + {"metric": "meters.voltage", "timestamp": 1648432611250, "value": 221, "tags": {"location": "Beijing.Haidian", "groupid": 1}}]` + + err = conn.OpenTSDBInsertJsonPayload(payload) + if err != nil { + fmt.Println("insert error:", err) + } +} diff --git a/docs-examples/go/insert/line/main.go b/docs-examples/go/insert/line/main.go new file mode 100644 index 0000000000000000000000000000000000000000..bbc41468fe5f13d3e6f896445bb88f3eba584d0f --- /dev/null +++ b/docs-examples/go/insert/line/main.go @@ -0,0 +1,38 @@ +package main + +import ( + "fmt" + + "github.com/taosdata/driver-go/v2/af" +) + +func prepareDatabase(conn *af.Connector) { + _, err := conn.Exec("CREATE DATABASE test") + if err != nil { + panic(err) + } + _, err = conn.Exec("USE test") + if err != nil { + panic(err) + } +} + +func main() { + conn, err := af.Open("localhost", "root", "taosdata", "", 6030) + if err != nil { + fmt.Println("fail to connect, err:", err) + } + defer conn.Close() + prepareDatabase(conn) + var lines = []string{ + "meters,location=Beijing.Haidian,groupid=2 current=11.8,voltage=221,phase=0.28 1648432611249", + "meters,location=Beijing.Haidian,groupid=2 current=13.4,voltage=223,phase=0.29 1648432611250", + "meters,location=Beijing.Haidian,groupid=3 current=10.8,voltage=223,phase=0.29 1648432611249", + "meters,location=Beijing.Haidian,groupid=3 current=11.3,voltage=221,phase=0.35 1648432611250", + } + + err = conn.InfluxDBInsertLines(lines, "ms") + if err != nil { + fmt.Println("insert error:", err) + } +} diff --git a/docs-examples/go/insert/sql/main.go b/docs-examples/go/insert/sql/main.go new file mode 100644 index 0000000000000000000000000000000000000000..91386855334c1930af721e0b4f43395c6a6d8e82 --- /dev/null +++ b/docs-examples/go/insert/sql/main.go @@ -0,0 +1,49 @@ +package main + +import ( + "database/sql" + "fmt" + + _ "github.com/taosdata/driver-go/v2/taosRestful" +) + +func createStable(taos *sql.DB) { + _, err := taos.Exec("CREATE DATABASE power") + if err != nil { + fmt.Println("failed to create database, err:", err) + } + _, err = taos.Exec("CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT)") + if err != nil { + fmt.Println("failed to create stable, err:", err) + } +} + +func insertData(taos *sql.DB) { + sql := `INSERT INTO power.d1001 USING power.meters TAGS(Beijing.Chaoyang, 2) VALUES ('2018-10-03 14:38:05.000', 10.30000, 219, 0.31000) ('2018-10-03 14:38:15.000', 12.60000, 218, 0.33000) ('2018-10-03 14:38:16.800', 12.30000, 221, 0.31000) + power.d1002 USING power.meters TAGS(Beijing.Chaoyang, 3) VALUES ('2018-10-03 14:38:16.650', 10.30000, 218, 0.25000) + power.d1003 USING power.meters TAGS(Beijing.Haidian, 2) VALUES ('2018-10-03 14:38:05.500', 11.80000, 221, 0.28000) ('2018-10-03 14:38:16.600', 13.40000, 223, 0.29000) + power.d1004 USING power.meters TAGS(Beijing.Haidian, 3) VALUES ('2018-10-03 14:38:05.000', 10.80000, 223, 0.29000) ('2018-10-03 14:38:06.500', 11.50000, 221, 0.35000)` + result, err := taos.Exec(sql) + if err != nil { + fmt.Println("failed to insert, err:", err) + return + } + rowsAffected, err := result.RowsAffected() + if err != nil { + fmt.Println("failed to get affected rows, err:", err) + return + } + fmt.Println("RowsAffected", rowsAffected) +} + +func main() { + var taosDSN = "root:taosdata@http(localhost:6041)/" + taos, err := sql.Open("taosRestful", taosDSN) + if err != nil { + fmt.Println("failed to connect TDengine, err:", err) + return + } + defer taos.Close() + createStable(taos) + insertData(taos) +} diff --git a/docs-examples/go/insert/stmt/main.go b/docs-examples/go/insert/stmt/main.go new file mode 100644 index 0000000000000000000000000000000000000000..c50200ebb427c4c64c2737cb8fe4c3d287551a34 --- /dev/null +++ b/docs-examples/go/insert/stmt/main.go @@ -0,0 +1,73 @@ +package main + +import ( + "fmt" + "time" + + "github.com/taosdata/driver-go/v2/af" + "github.com/taosdata/driver-go/v2/af/param" + "github.com/taosdata/driver-go/v2/common" +) + +func checkErr(err error, prompt string) { + if err != nil { + fmt.Printf("%s\n", prompt) + panic(err) + } +} + +func prepareStable(conn *af.Connector) { + _, err := conn.Exec("CREATE DATABASE power") + checkErr(err, "failed to create database") + _, err = conn.Exec("CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT)") + checkErr(err, "failed to create stable") + _, err = conn.Exec("USE power") + checkErr(err, "failed to change database") +} + +func main() { + conn, err := af.Open("localhost", "root", "taosdata", "", 6030) + checkErr(err, "fail to connect") + defer conn.Close() + prepareStable(conn) + // create stmt + stmt := conn.InsertStmt() + defer stmt.Close() + err = stmt.Prepare("INSERT INTO ? USING meters TAGS(?, ?) VALUES(?, ?, ?, ?)") + checkErr(err, "failed to create prepare statement") + + // bind table name and tags + tagParams := param.NewParam(2).AddBinary([]byte("Beijing.Chaoyang")).AddInt(2) + err = stmt.SetTableNameWithTags("d1001", tagParams) + checkErr(err, "failed to execute SetTableNameWithTags") + + // specify ColumnType + var bindType *param.ColumnType = param.NewColumnType(4).AddTimestamp().AddFloat().AddInt().AddFloat() + + // bind values. note: can only bind one row each time. + valueParams := []*param.Param{ + param.NewParam(1).AddTimestamp(time.Unix(1648432611, 249300000), common.PrecisionMilliSecond), + param.NewParam(1).AddFloat(10.3), + param.NewParam(1).AddInt(219), + param.NewParam(1).AddFloat(0.31), + } + err = stmt.BindParam(valueParams, bindType) + checkErr(err, "BindParam error") + err = stmt.AddBatch() + checkErr(err, "AddBatch error") + + // bind one more row + valueParams = []*param.Param{ + param.NewParam(1).AddTimestamp(time.Unix(1648432611, 749300000), common.PrecisionMilliSecond), + param.NewParam(1).AddFloat(12.6), + param.NewParam(1).AddInt(218), + param.NewParam(1).AddFloat(0.33), + } + err = stmt.BindParam(valueParams, bindType) + checkErr(err, "BindParam error") + err = stmt.AddBatch() + checkErr(err, "AddBatch error") + // execute + err = stmt.Execute() + checkErr(err, "Execute batch error") +} diff --git a/docs-examples/go/insert/telnet/main.go b/docs-examples/go/insert/telnet/main.go new file mode 100644 index 0000000000000000000000000000000000000000..879e6d5cece74fd0b7c815dd34614dca3c9d4544 --- /dev/null +++ b/docs-examples/go/insert/telnet/main.go @@ -0,0 +1,42 @@ +package main + +import ( + "fmt" + + "github.com/taosdata/driver-go/v2/af" +) + +func prepareDatabase(conn *af.Connector) { + _, err := conn.Exec("CREATE DATABASE test") + if err != nil { + panic(err) + } + _, err = conn.Exec("USE test") + if err != nil { + panic(err) + } +} + +func main() { + conn, err := af.Open("localhost", "root", "taosdata", "", 6030) + if err != nil { + fmt.Println("fail to connect, err:", err) + } + defer conn.Close() + prepareDatabase(conn) + var lines = []string{ + "meters.current 1648432611249 10.3 location=Beijing.Chaoyang groupid=2", + "meters.current 1648432611250 12.6 location=Beijing.Chaoyang groupid=2", + "meters.current 1648432611249 10.8 location=Beijing.Haidian groupid=3", + "meters.current 1648432611250 11.3 location=Beijing.Haidian groupid=3", + "meters.voltage 1648432611249 219 location=Beijing.Chaoyang groupid=2", + "meters.voltage 1648432611250 218 location=Beijing.Chaoyang groupid=2", + "meters.voltage 1648432611249 221 location=Beijing.Haidian groupid=3", + "meters.voltage 1648432611250 217 location=Beijing.Haidian groupid=3", + } + + err = conn.OpenTSDBInsertTelnetLines(lines) + if err != nil { + fmt.Println("insert error:", err) + } +} diff --git a/docs-examples/go/query/async/main.go b/docs-examples/go/query/async/main.go new file mode 100644 index 0000000000000000000000000000000000000000..7776ec8d3f268a3ef393d2097b71dc5681cf48d9 --- /dev/null +++ b/docs-examples/go/query/async/main.go @@ -0,0 +1,7 @@ +package main + +import "fmt" + +func main() { + fmt.Println("hello world!") +} diff --git a/docs-examples/go/query/sync/main.go b/docs-examples/go/query/sync/main.go new file mode 100644 index 0000000000000000000000000000000000000000..0326c7a7da5da212f66b1421bb46e5972bef9609 --- /dev/null +++ b/docs-examples/go/query/sync/main.go @@ -0,0 +1,38 @@ +package main + +import ( + "database/sql" + "fmt" + "time" + + _ "github.com/taosdata/driver-go/v2/taosRestful" +) + +func main() { + var taosDSN = "root:taosdata@http(localhost:6041)/power" + taos, err := sql.Open("taosRestful", taosDSN) + if err != nil { + fmt.Println("failed to connect TDengine, err:", err) + return + } + defer taos.Close() + rows, err := taos.Query("SELECT ts, current FROM meters LIMIT 2") + if err != nil { + fmt.Println("failed to select from table, err:", err) + return + } + + defer rows.Close() + for rows.Next() { + var r struct { + ts time.Time + current float32 + } + err := rows.Scan(&r.ts, &r.current) + if err != nil { + fmt.Println("scan error:\n", err) + return + } + fmt.Println(r.ts, r.current) + } +} diff --git a/docs-examples/go/rest/opentsdbjson/main.go b/docs-examples/go/rest/opentsdbjson/main.go new file mode 100644 index 0000000000000000000000000000000000000000..f4f5792e29436e539f301c14ce76607ad3e5593d --- /dev/null +++ b/docs-examples/go/rest/opentsdbjson/main.go @@ -0,0 +1,45 @@ +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "time" +) + +type Tags struct { + Location string `json:"location"` + Groupid int32 `json:"groupid"` +} + +type Metric struct { + Metric string `json:"metric"` + Timestamp int64 `json:"timestamp"` + Value int32 `json:"value"` + Tags Tags `json:"tags"` +} + +func main() { + client := http.Client{} + for i := 0; i < 10; i++ { + metric := Metric{"voltage", time.Now().UnixMilli(), 1, Tags{"A", 1}} + json, err := json.Marshal(metric) + if err != nil { + panic(err) + } + req, err := http.NewRequest("POST", "http://localhost:6041/opentsdb/v1/put/json/rest_go", bytes.NewBuffer(json)) + if err != nil { + panic(err) + } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Basic cm9vdDp0YW9zZGF0YQ==") + resp, err := client.Do(req) + if err != nil { + panic(err) + } + fmt.Printf("%v\n", resp) + time.Sleep(time.Second) + + } +} diff --git a/docs-examples/go/sub/main.go b/docs-examples/go/sub/main.go new file mode 100644 index 0000000000000000000000000000000000000000..c44521b34026ebf6f84f70022a7c2d1f83e9b781 --- /dev/null +++ b/docs-examples/go/sub/main.go @@ -0,0 +1,53 @@ +package main + +import ( + "database/sql/driver" + "fmt" + "io" + "os" + "time" + + taos "github.com/taosdata/driver-go/v2/af" +) + +func main() { + db, err := taos.Open("", "", "", "log", 0) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + defer db.Close() + topic, err := db.Subscribe(false, "taoslogtail", "select ts, level, ipaddr, content from log", time.Second) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(2) + } + defer topic.Unsubscribe(true) + for { + func() { + rows, err := topic.Consume() + defer func() { rows.Close(); time.Sleep(time.Second) }() + if err != nil { + fmt.Println(err) + os.Exit(3) + } + for { + values := make([]driver.Value, 4) + err := rows.Next(values) + if err == io.EOF { + break + } else if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(4) + } + ts := values[0].(time.Time) + level := values[1].(int8) + ipaddr := values[2].(string) + content := values[3].(string) + fmt.Printf("%s %d %s %s\n", ts.Format(time.StampMilli), level, ipaddr, content) + } + }() + } +} + +// 未完成 diff --git a/docs-examples/java/.gitignore b/docs-examples/java/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..d48c759d6c990444f193c0043ec9b316029d31bc --- /dev/null +++ b/docs-examples/java/.gitignore @@ -0,0 +1,2 @@ +.idea +.vscode \ No newline at end of file diff --git a/docs-examples/java/pom.xml b/docs-examples/java/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..a48ba398da92f401235819d067aa2ba6f8b173ea --- /dev/null +++ b/docs-examples/java/pom.xml @@ -0,0 +1,35 @@ + + + + 4.0.0 + + com.taos + javaexample + 1.0 + + JavaExample + + + UTF-8 + 1.8 + 1.8 + + + + + + com.taosdata.jdbc + taos-jdbcdriver + 2.0.38 + + + + junit + junit + 4.13.1 + test + + + + diff --git a/docs-examples/java/src/main/java/com/taos/example/JNIConnectExample.java b/docs-examples/java/src/main/java/com/taos/example/JNIConnectExample.java new file mode 100644 index 0000000000000000000000000000000000000000..c6ce2ef9785a010daa55ad29415f81711760cd57 --- /dev/null +++ b/docs-examples/java/src/main/java/com/taos/example/JNIConnectExample.java @@ -0,0 +1,25 @@ +package com.taos.example; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.Properties; + +import com.taosdata.jdbc.TSDBDriver; + +public class JNIConnectExample { + public static void main(String[] args) throws SQLException { + String jdbcUrl = "jdbc:TAOS://localhost:6030?user=root&password=taosdata"; + Properties connProps = new Properties(); + connProps.setProperty(TSDBDriver.PROPERTY_KEY_CHARSET, "UTF-8"); + connProps.setProperty(TSDBDriver.PROPERTY_KEY_LOCALE, "en_US.UTF-8"); + connProps.setProperty(TSDBDriver.PROPERTY_KEY_TIME_ZONE, "UTC-8"); + Connection conn = DriverManager.getConnection(jdbcUrl, connProps); + System.out.println("Connected"); + conn.close(); + } +} + +// use +// String jdbcUrl = "jdbc:TAOS://localhost:6030/dbName?user=root&password=taosdata"; +// if you want to connect to a default database. \ No newline at end of file diff --git a/docs-examples/java/src/main/java/com/taos/example/JSONProtocolExample.java b/docs-examples/java/src/main/java/com/taos/example/JSONProtocolExample.java new file mode 100644 index 0000000000000000000000000000000000000000..cb83424576a4fd7dfa09ea297294ed77b66bd12d --- /dev/null +++ b/docs-examples/java/src/main/java/com/taos/example/JSONProtocolExample.java @@ -0,0 +1,40 @@ +package com.taos.example; + +import com.taosdata.jdbc.SchemalessWriter; +import com.taosdata.jdbc.enums.SchemalessProtocolType; +import com.taosdata.jdbc.enums.SchemalessTimestampType; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; + +public class JSONProtocolExample { + private static Connection getConnection() throws SQLException { + String jdbcUrl = "jdbc:TAOS://localhost:6030?user=root&password=taosdata"; + return DriverManager.getConnection(jdbcUrl); + } + + private static void createDatabase(Connection conn) throws SQLException { + try (Statement stmt = conn.createStatement()) { + stmt.execute("CREATE DATABASE IF NOT EXISTS test"); + stmt.execute("USE test"); + } + } + + private static String getJSONData() { + return "[{\"metric\": \"meters.current\", \"timestamp\": 1648432611249, \"value\": 10.3, \"tags\": {\"location\": \"Beijing.Chaoyang\", \"groupid\": 2}}," + + " {\"metric\": \"meters.voltage\", \"timestamp\": 1648432611249, \"value\": 219, \"tags\": {\"location\": \"Beijing.Haidian\", \"groupid\": 1}}, " + + "{\"metric\": \"meters.current\", \"timestamp\": 1648432611250, \"value\": 12.6, \"tags\": {\"location\": \"Beijing.Chaoyang\", \"groupid\": 2}}," + + " {\"metric\": \"meters.voltage\", \"timestamp\": 1648432611250, \"value\": 221, \"tags\": {\"location\": \"Beijing.Haidian\", \"groupid\": 1}}]"; + } + + public static void main(String[] args) throws SQLException { + try (Connection conn = getConnection()) { + createDatabase(conn); + SchemalessWriter writer = new SchemalessWriter(conn); + String jsonData = getJSONData(); + writer.write(jsonData, SchemalessProtocolType.JSON, SchemalessTimestampType.NOT_CONFIGURED); + } + } +} diff --git a/docs-examples/java/src/main/java/com/taos/example/LineProtocolExample.java b/docs-examples/java/src/main/java/com/taos/example/LineProtocolExample.java new file mode 100644 index 0000000000000000000000000000000000000000..8a2eabe0a91f7966cc3cc6b7dfeeb71b71b88d92 --- /dev/null +++ b/docs-examples/java/src/main/java/com/taos/example/LineProtocolExample.java @@ -0,0 +1,42 @@ +package com.taos.example; + +import com.taosdata.jdbc.SchemalessWriter; +import com.taosdata.jdbc.enums.SchemalessProtocolType; +import com.taosdata.jdbc.enums.SchemalessTimestampType; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; + +public class LineProtocolExample { + // format: measurement,tag_set field_set timestamp + private static String[] lines = { + "meters,location=Beijing.Haidian,groupid=2 current=11.8,voltage=221,phase=0.28 1648432611249000", // micro + // seconds + "meters,location=Beijing.Haidian,groupid=2 current=13.4,voltage=223,phase=0.29 1648432611249500", + "meters,location=Beijing.Haidian,groupid=3 current=10.8,voltage=223,phase=0.29 1648432611249300", + "meters,location=Beijing.Haidian,groupid=3 current=11.3,voltage=221,phase=0.35 1648432611249800", + }; + + private static Connection getConnection() throws SQLException { + String jdbcUrl = "jdbc:TAOS://localhost:6030?user=root&password=taosdata"; + return DriverManager.getConnection(jdbcUrl); + } + + private static void createDatabase(Connection conn) throws SQLException { + try (Statement stmt = conn.createStatement()) { + // the default precision is ms (microsecond), but we use us(microsecond) here. + stmt.execute("CREATE DATABASE IF NOT EXISTS test PRECISION 'us'"); + stmt.execute("USE test"); + } + } + + public static void main(String[] args) throws SQLException { + try (Connection conn = getConnection()) { + createDatabase(conn); + SchemalessWriter writer = new SchemalessWriter(conn); + writer.write(lines, SchemalessProtocolType.LINE, SchemalessTimestampType.MICRO_SECONDS); + } + } +} diff --git a/docs-examples/java/src/main/java/com/taos/example/RESTConnectExample.java b/docs-examples/java/src/main/java/com/taos/example/RESTConnectExample.java new file mode 100644 index 0000000000000000000000000000000000000000..9d077812e0ff9d0de2952d97aa12f870e6412885 --- /dev/null +++ b/docs-examples/java/src/main/java/com/taos/example/RESTConnectExample.java @@ -0,0 +1,16 @@ +package com.taos.example; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +public class RESTConnectExample { + // ANCHOR: main + public static void main(String[] args) throws SQLException { + String jdbcUrl = "jdbc:TAOS-RS://localhost:6041?user=root&password=taosdata"; + Connection conn = DriverManager.getConnection(jdbcUrl); + System.out.println("Connected"); + conn.close(); + } + // ANCHOR_END: main +} \ No newline at end of file diff --git a/docs-examples/java/src/main/java/com/taos/example/RestInsertExample.java b/docs-examples/java/src/main/java/com/taos/example/RestInsertExample.java new file mode 100644 index 0000000000000000000000000000000000000000..de89f26cbe38f9343d60aeb8d3e9ce7f67c2e764 --- /dev/null +++ b/docs-examples/java/src/main/java/com/taos/example/RestInsertExample.java @@ -0,0 +1,74 @@ +package com.taos.example; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Arrays; +import java.util.List; + + +public class RestInsertExample { + private static Connection getConnection() throws SQLException { + String jdbcUrl = "jdbc:TAOS-RS://localhost:6041?user=root&password=taosdata"; + return DriverManager.getConnection(jdbcUrl); + } + + private static List getRawData() { + return Arrays.asList( + "d1001,2018-10-03 14:38:05.000,10.30000,219,0.31000,Beijing.Chaoyang,2", + "d1001,2018-10-03 14:38:15.000,12.60000,218,0.33000,Beijing.Chaoyang,2", + "d1001,2018-10-03 14:38:16.800,12.30000,221,0.31000,Beijing.Chaoyang,2", + "d1002,2018-10-03 14:38:16.650,10.30000,218,0.25000,Beijing.Chaoyang,3", + "d1003,2018-10-03 14:38:05.500,11.80000,221,0.28000,Beijing.Haidian,2", + "d1003,2018-10-03 14:38:16.600,13.40000,223,0.29000,Beijing.Haidian,2", + "d1004,2018-10-03 14:38:05.000,10.80000,223,0.29000,Beijing.Haidian,3", + "d1004,2018-10-03 14:38:06.500,11.50000,221,0.35000,Beijing.Haidian,3" + ); + } + + + /** + * The generated SQL is: + * INSERT INTO power.d1001 USING power.meters TAGS(Beijing.Chaoyang, 2) VALUES('2018-10-03 14:38:05.000',10.30000,219,0.31000) + * power.d1001 USING power.meters TAGS(Beijing.Chaoyang, 2) VALUES('2018-10-03 14:38:15.000',12.60000,218,0.33000) + * power.d1001 USING power.meters TAGS(Beijing.Chaoyang, 2) VALUES('2018-10-03 14:38:16.800',12.30000,221,0.31000) + * power.d1002 USING power.meters TAGS(Beijing.Chaoyang, 3) VALUES('2018-10-03 14:38:16.650',10.30000,218,0.25000) + * power.d1003 USING power.meters TAGS(Beijing.Haidian, 2) VALUES('2018-10-03 14:38:05.500',11.80000,221,0.28000) + * power.d1003 USING power.meters TAGS(Beijing.Haidian, 2) VALUES('2018-10-03 14:38:16.600',13.40000,223,0.29000) + * power.d1004 USING power.meters TAGS(Beijing.Haidian, 3) VALUES('2018-10-03 14:38:05.000',10.80000,223,0.29000) + * power.d1004 USING power.meters TAGS(Beijing.Haidian, 3) VALUES('2018-10-03 14:38:06.500',11.50000,221,0.35000) + */ + private static String getSQL() { + StringBuilder sb = new StringBuilder("INSERT INTO "); + for (String line : getRawData()) { + String[] ps = line.split(","); + sb.append("power." + ps[0]).append(" USING power.meters TAGS(") + .append(ps[5]).append(", ") // tag: location + .append(ps[6]) // tag: groupId + .append(") VALUES(") + .append('\'').append(ps[1]).append('\'').append(",") // ts + .append(ps[2]).append(",") // current + .append(ps[3]).append(",") // voltage + .append(ps[4]).append(") "); // phase + } + return sb.toString(); + } + + public static void insertData() throws SQLException { + try (Connection conn = getConnection()) { + try (Statement stmt = conn.createStatement()) { + stmt.execute("CREATE DATABASE power KEEP 3650"); + stmt.execute("CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) " + + "TAGS (location BINARY(64), groupId INT)"); + String sql = getSQL(); + int rowCount = stmt.executeUpdate(sql); + System.out.println("rowCount=" + rowCount); // rowCount=8 + } + } + } + + public static void main(String[] args) throws SQLException { + insertData(); + } +} diff --git a/docs-examples/java/src/main/java/com/taos/example/RestQueryExample.java b/docs-examples/java/src/main/java/com/taos/example/RestQueryExample.java new file mode 100644 index 0000000000000000000000000000000000000000..b1a1d224c6d9af2b83ac039726dcdb49a33ec2b0 --- /dev/null +++ b/docs-examples/java/src/main/java/com/taos/example/RestQueryExample.java @@ -0,0 +1,55 @@ +package com.taos.example; + +import java.sql.*; + +public class RestQueryExample { + private static Connection getConnection() throws SQLException { + String jdbcUrl = "jdbc:TAOS-RS://localhost:6041/power?user=root&password=taosdata"; + return DriverManager.getConnection(jdbcUrl); + } + + private static void printRow(ResultSet rs) throws SQLException { + ResultSetMetaData meta = rs.getMetaData(); + for (int i = 1; i <= meta.getColumnCount(); i++) { + String value = rs.getString(i); + System.out.print(value); + System.out.print("\t"); + } + System.out.println(); + } + + private static void printColName(ResultSet rs) throws SQLException { + ResultSetMetaData meta = rs.getMetaData(); + for (int i = 1; i <= meta.getColumnCount(); i++) { + String colLabel = meta.getColumnLabel(i); + System.out.print(colLabel); + System.out.print("\t"); + } + System.out.println(); + } + + private static void processResult(ResultSet rs) throws SQLException { + printColName(rs); + while (rs.next()) { + printRow(rs); + } + } + + private static void queryData() throws SQLException { + try (Connection conn = getConnection()) { + try (Statement stmt = conn.createStatement()) { + ResultSet rs = stmt.executeQuery("SELECT AVG(voltage) FROM meters GROUP BY location"); + processResult(rs); + } + } + } + + public static void main(String[] args) throws SQLException { + queryData(); + } +} + +// possible output: +// avg(voltage) location +// 222.0 Beijing.Haidian +// 219.0 Beijing.Chaoyang diff --git a/docs-examples/java/src/main/java/com/taos/example/StmtInsertExample.java b/docs-examples/java/src/main/java/com/taos/example/StmtInsertExample.java new file mode 100644 index 0000000000000000000000000000000000000000..2a7ccebf41cae1a22d7516966e2c6ffb10011b64 --- /dev/null +++ b/docs-examples/java/src/main/java/com/taos/example/StmtInsertExample.java @@ -0,0 +1,84 @@ +package com.taos.example; + +import com.taosdata.jdbc.TSDBPreparedStatement; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class StmtInsertExample { + private static ArrayList tsToLongArray(String ts) { + ArrayList result = new ArrayList<>(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); + LocalDateTime localDateTime = LocalDateTime.parse(ts, formatter); + result.add(localDateTime.toInstant(ZoneOffset.of("+8")).toEpochMilli()); + return result; + } + + private static ArrayList toArray(T v) { + ArrayList result = new ArrayList<>(); + result.add(v); + return result; + } + + private static List getRawData() { + return Arrays.asList( + "d1001,2018-10-03 14:38:05.000,10.30000,219,0.31000,Beijing.Chaoyang,2", + "d1001,2018-10-03 14:38:15.000,12.60000,218,0.33000,Beijing.Chaoyang,2", + "d1001,2018-10-03 14:38:16.800,12.30000,221,0.31000,Beijing.Chaoyang,2", + "d1002,2018-10-03 14:38:16.650,10.30000,218,0.25000,Beijing.Chaoyang,3", + "d1003,2018-10-03 14:38:05.500,11.80000,221,0.28000,Beijing.Haidian,2", + "d1003,2018-10-03 14:38:16.600,13.40000,223,0.29000,Beijing.Haidian,2", + "d1004,2018-10-03 14:38:05.000,10.80000,223,0.29000,Beijing.Haidian,3", + "d1004,2018-10-03 14:38:06.500,11.50000,221,0.35000,Beijing.Haidian,3" + ); + } + + private static Connection getConnection() throws SQLException { + String jdbcUrl = "jdbc:TAOS://localhost:6030?user=root&password=taosdata"; + return DriverManager.getConnection(jdbcUrl); + } + + private static void createTable(Connection conn) throws SQLException { + try (Statement stmt = conn.createStatement()) { + stmt.execute("CREATE DATABASE power KEEP 3650"); + stmt.executeUpdate("USE power"); + stmt.execute("CREATE STABLE meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) " + + "TAGS (location BINARY(64), groupId INT)"); + } + } + + private static void insertData() throws SQLException { + try (Connection conn = getConnection()) { + createTable(conn); + String psql = "INSERT INTO ? USING meters TAGS(?, ?) VALUES(?, ?, ?, ?)"; + try (TSDBPreparedStatement pst = (TSDBPreparedStatement) conn.prepareStatement(psql)) { + for (String line : getRawData()) { + String[] ps = line.split(","); + // bind table name and tags + pst.setTableName(ps[0]); + pst.setTagString(0, ps[5]); + pst.setTagInt(1, Integer.valueOf(ps[6])); + // bind values + pst.setTimestamp(0, tsToLongArray(ps[1])); //ps[1] looks like: 2018-10-03 14:38:05.000 + pst.setFloat(1, toArray(Float.valueOf(ps[2]))); + pst.setInt(2, toArray(Integer.valueOf(ps[3]))); + pst.setFloat(3, toArray(Float.valueOf(ps[4]))); + pst.columnDataAddBatch(); + } + pst.columnDataExecuteBatch(); + } + } + } + + public static void main(String[] args) throws SQLException { + insertData(); + } +} diff --git a/docs-examples/java/src/main/java/com/taos/example/SubscribeDemo.java b/docs-examples/java/src/main/java/com/taos/example/SubscribeDemo.java new file mode 100644 index 0000000000000000000000000000000000000000..d82d03b9de561e3ea6a8e7d40a48ce9dd3f2a20d --- /dev/null +++ b/docs-examples/java/src/main/java/com/taos/example/SubscribeDemo.java @@ -0,0 +1,65 @@ +package com.taos.example; + +import com.taosdata.jdbc.TSDBConnection; +import com.taosdata.jdbc.TSDBDriver; +import com.taosdata.jdbc.TSDBResultSet; +import com.taosdata.jdbc.TSDBSubscribe; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.util.Properties; +import java.util.concurrent.TimeUnit; + +public class SubscribeDemo { + private static final String topic = "topic-meter-current-bg-10"; + private static final String sql = "select * from meters where current > 10"; + + public static void main(String[] args) { + Connection connection = null; + TSDBSubscribe subscribe = null; + + try { + Class.forName("com.taosdata.jdbc.TSDBDriver"); + Properties properties = new Properties(); + properties.setProperty(TSDBDriver.PROPERTY_KEY_CHARSET, "UTF-8"); + properties.setProperty(TSDBDriver.PROPERTY_KEY_TIME_ZONE, "UTC-8"); + String jdbcUrl = "jdbc:TAOS://127.0.0.1:6030/power?user=root&password=taosdata"; + connection = DriverManager.getConnection(jdbcUrl, properties); + // create subscribe + subscribe = ((TSDBConnection) connection).subscribe(topic, sql, true); + int count = 0; + while (count < 10) { + // wait 1 second to avoid frequent calls to consume + TimeUnit.SECONDS.sleep(1); + // consume + TSDBResultSet resultSet = subscribe.consume(); + if (resultSet == null) { + continue; + } + ResultSetMetaData metaData = resultSet.getMetaData(); + while (resultSet.next()) { + int columnCount = metaData.getColumnCount(); + for (int i = 1; i <= columnCount; i++) { + System.out.print(metaData.getColumnLabel(i) + ": " + resultSet.getString(i) + "\t"); + } + System.out.println(); + count++; + } + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (null != subscribe) + // close subscribe + subscribe.close(true); + if (connection != null) + connection.close(); + } catch (SQLException throwable) { + throwable.printStackTrace(); + } + } + } +} \ No newline at end of file diff --git a/docs-examples/java/src/main/java/com/taos/example/TelnetLineProtocolExample.java b/docs-examples/java/src/main/java/com/taos/example/TelnetLineProtocolExample.java new file mode 100644 index 0000000000000000000000000000000000000000..1431eccf16dabaac20f60ae7e971ef49707ba509 --- /dev/null +++ b/docs-examples/java/src/main/java/com/taos/example/TelnetLineProtocolExample.java @@ -0,0 +1,45 @@ +package com.taos.example; + +import com.taosdata.jdbc.SchemalessWriter; +import com.taosdata.jdbc.enums.SchemalessProtocolType; +import com.taosdata.jdbc.enums.SchemalessTimestampType; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; + +public class TelnetLineProtocolExample { + // format: =[ =] + private static String[] lines = { "meters.current 1648432611249 10.3 location=Beijing.Chaoyang groupid=2", + "meters.current 1648432611250 12.6 location=Beijing.Chaoyang groupid=2", + "meters.current 1648432611249 10.8 location=Beijing.Haidian groupid=3", + "meters.current 1648432611250 11.3 location=Beijing.Haidian groupid=3", + "meters.voltage 1648432611249 219 location=Beijing.Chaoyang groupid=2", + "meters.voltage 1648432611250 218 location=Beijing.Chaoyang groupid=2", + "meters.voltage 1648432611249 221 location=Beijing.Haidian groupid=3", + "meters.voltage 1648432611250 217 location=Beijing.Haidian groupid=3", + }; + + private static Connection getConnection() throws SQLException { + String jdbcUrl = "jdbc:TAOS://localhost:6030?user=root&password=taosdata"; + return DriverManager.getConnection(jdbcUrl); + } + + private static void createDatabase(Connection conn) throws SQLException { + try (Statement stmt = conn.createStatement()) { + // the default precision is ms (microsecond), but we use us(microsecond) here. + stmt.execute("CREATE DATABASE IF NOT EXISTS test precision 'us'"); + stmt.execute("USE test"); + } + } + + public static void main(String[] args) throws SQLException { + try (Connection conn = getConnection()) { + createDatabase(conn); + SchemalessWriter writer = new SchemalessWriter(conn); + writer.write(lines, SchemalessProtocolType.TELNET, SchemalessTimestampType.NOT_CONFIGURED); + } + } + +} diff --git a/docs-examples/java/src/main/java/com/taos/example/WSConnectExample.java b/docs-examples/java/src/main/java/com/taos/example/WSConnectExample.java new file mode 100644 index 0000000000000000000000000000000000000000..48d1dde593cc347632efb6612946aa16e82e8b94 --- /dev/null +++ b/docs-examples/java/src/main/java/com/taos/example/WSConnectExample.java @@ -0,0 +1,21 @@ +package com.taos.example; + +import com.taosdata.jdbc.TSDBDriver; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.Properties; + +public class WSConnectExample { + // ANCHOR: main + public static void main(String[] args) throws SQLException { + String jdbcUrl = "jdbc:TAOS-RS://localhost:6041?user=root&password=taosdata"; + Properties connProps = new Properties(); + connProps.setProperty(TSDBDriver.PROPERTY_KEY_BATCH_LOAD, "true"); + Connection conn = DriverManager.getConnection(jdbcUrl, connProps); + System.out.println("Connected"); + conn.close(); + } + // ANCHOR_END: main +} diff --git a/docs-examples/java/src/test/java/com/taos/test/TestAll.java b/docs-examples/java/src/test/java/com/taos/test/TestAll.java new file mode 100644 index 0000000000000000000000000000000000000000..92fe14a49d5f5ea5d7ea5f1d809867b3de0cc9d2 --- /dev/null +++ b/docs-examples/java/src/test/java/com/taos/test/TestAll.java @@ -0,0 +1,91 @@ +package com.taos.test; + +import com.taos.example.*; +import org.junit.FixMethodOrder; +import org.junit.Test; + +import java.sql.*; + +@FixMethodOrder +public class TestAll { + private String[] args = new String[]{}; + + public void dropDB(String dbName) throws SQLException { + String jdbcUrl = "jdbc:TAOS://localhost:6030?user=root&password=taosdata"; + try (Connection conn = DriverManager.getConnection(jdbcUrl)) { + try (Statement stmt = conn.createStatement()) { + stmt.execute("drop database if exists " + dbName); + } + } + } + + public void insertData() throws SQLException { + String jdbcUrl = "jdbc:TAOS://localhost:6030?user=root&password=taosdata"; + try (Connection conn = DriverManager.getConnection(jdbcUrl)) { + try (Statement stmt = conn.createStatement()) { + String sql = "INSERT INTO power.d1001 USING power.meters TAGS(Beijing.Chaoyang, 2) VALUES('2018-10-03 14:38:05.000',10.30000,219,0.31000)\n" + + " power.d1001 USING power.meters TAGS(Beijing.Chaoyang, 2) VALUES('2018-10-03 15:38:15.000',12.60000,218,0.33000)\n" + + " power.d1001 USING power.meters TAGS(Beijing.Chaoyang, 2) VALUES('2018-10-03 15:38:16.800',12.30000,221,0.31000)\n" + + " power.d1002 USING power.meters TAGS(Beijing.Chaoyang, 3) VALUES('2018-10-03 15:38:16.650',10.30000,218,0.25000)\n" + + " power.d1003 USING power.meters TAGS(Beijing.Haidian, 2) VALUES('2018-10-03 15:38:05.500',11.80000,221,0.28000)\n" + + " power.d1003 USING power.meters TAGS(Beijing.Haidian, 2) VALUES('2018-10-03 15:38:16.600',13.40000,223,0.29000)\n" + + " power.d1004 USING power.meters TAGS(Beijing.Haidian, 3) VALUES('2018-10-03 15:38:05.000',10.80000,223,0.29000)\n" + + " power.d1004 USING power.meters TAGS(Beijing.Haidian, 3) VALUES('2018-10-03 15:38:06.000',10.80000,223,0.29000)\n" + + " power.d1004 USING power.meters TAGS(Beijing.Haidian, 3) VALUES('2018-10-03 15:38:07.000',10.80000,223,0.29000)\n" + + " power.d1004 USING power.meters TAGS(Beijing.Haidian, 3) VALUES('2018-10-03 15:38:08.500',11.50000,221,0.35000)"; + + stmt.execute(sql); + } + } + } + + @Test + public void testJNIConnect() throws SQLException { + JNIConnectExample.main(args); + } + + @Test + public void testRestConnect() throws SQLException { + RESTConnectExample.main(args); + } + + @Test + public void testRestInsert() throws SQLException { + dropDB("power"); + RestInsertExample.main(args); + RestQueryExample.main(args); + } + + @Test + public void testStmtInsert() throws SQLException { + dropDB("power"); + StmtInsertExample.main(args); + } + + @Test + public void testSubscribe() { + + Thread thread = new Thread(() -> { + try { + Thread.sleep(1000); + insertData(); + } catch (SQLException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + }); + thread.start(); + SubscribeDemo.main(args); + } + + @Test + public void testSchemaless() throws SQLException { + LineProtocolExample.main(args); + TelnetLineProtocolExample.main(args); + // for json protocol, tags may be double type. but for telnet protocol tag must be nchar type. + // To avoid type mismatch, we delete database test. + dropDB("test"); + JSONProtocolExample.main(args); + } +} diff --git a/docs-examples/node/.gitignore b/docs-examples/node/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..1071ed2b9f91854f7a51424aa6c05544e803fc08 --- /dev/null +++ b/docs-examples/node/.gitignore @@ -0,0 +1,3 @@ +node_modules +package-lock.json +yarn-lock.json \ No newline at end of file diff --git a/docs-examples/node/nativeexample/async_query_example.js b/docs-examples/node/nativeexample/async_query_example.js new file mode 100644 index 0000000000000000000000000000000000000000..25b78bc48a82454cc0f9db69b77323a00f3852b4 --- /dev/null +++ b/docs-examples/node/nativeexample/async_query_example.js @@ -0,0 +1,21 @@ +const taos = require("td2.0-connector"); +const conn = taos.connect({ host: "localhost", database: "power" }); +const cursor = conn.cursor(); + +function queryExample() { + cursor + .query("SELECT ts, current FROM meters LIMIT 2") + .execute_a() + .then((result) => { + result.pretty(); + }); +} + +try { + queryExample(); +} finally { + setTimeout(() => { + conn.close(); + }, 2000); +} +// bug here: jira 14506 diff --git a/docs-examples/node/nativeexample/connect.js b/docs-examples/node/nativeexample/connect.js new file mode 100644 index 0000000000000000000000000000000000000000..da791c566e3aa40315b6f35cfeca704040cd8107 --- /dev/null +++ b/docs-examples/node/nativeexample/connect.js @@ -0,0 +1,13 @@ +const taos = require("td2.0-connector"); + +var conn = taos.connect({ + host: "localhost", + port: 6030, + user: "root", + password: "taosdata", +}); +conn.close(); + +// run with: node connect.js +// output: +// Successfully connected to TDengine diff --git a/docs-examples/node/nativeexample/influxdb_line_example.js b/docs-examples/node/nativeexample/influxdb_line_example.js new file mode 100644 index 0000000000000000000000000000000000000000..a9fc6d11df0b335b92bb3292baaa017cb4bc42ea --- /dev/null +++ b/docs-examples/node/nativeexample/influxdb_line_example.js @@ -0,0 +1,34 @@ +const taos = require("td2.0-connector"); + +const conn = taos.connect({ + host: "localhost", +}); + +const cursor = conn.cursor(); + +function createDatabase() { + cursor.execute("CREATE DATABASE test"); + cursor.execute("USE test"); +} + +function insertData() { + const lines = [ + "meters,location=Beijing.Haidian,groupid=2 current=11.8,voltage=221,phase=0.28 1648432611249", + "meters,location=Beijing.Haidian,groupid=2 current=13.4,voltage=223,phase=0.29 1648432611250", + "meters,location=Beijing.Haidian,groupid=3 current=10.8,voltage=223,phase=0.29 1648432611249", + "meters,location=Beijing.Haidian,groupid=3 current=11.3,voltage=221,phase=0.35 1648432611250", + ]; + cursor.schemalessInsert( + lines, + taos.SCHEMALESS_PROTOCOL.TSDB_SML_LINE_PROTOCOL, + taos.SCHEMALESS_PRECISION.TSDB_SML_TIMESTAMP_MILLI_SECONDS + ); +} + +try { + createDatabase(); + insertData(); +} finally { + cursor.close(); + conn.close(); +} diff --git a/docs-examples/node/nativeexample/insert_example.js b/docs-examples/node/nativeexample/insert_example.js new file mode 100644 index 0000000000000000000000000000000000000000..85a353f889176655654d8c39c9a905054d3b6622 --- /dev/null +++ b/docs-examples/node/nativeexample/insert_example.js @@ -0,0 +1,31 @@ +const taos = require("td2.0-connector"); + +const conn = taos.connect({ + host: "localhost", +}); + +const cursor = conn.cursor(); +try { + cursor.execute("CREATE DATABASE power"); + cursor.execute("USE power"); + cursor.execute( + "CREATE STABLE meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT)" + ); + var sql = `INSERT INTO power.d1001 USING power.meters TAGS(Beijing.Chaoyang, 2) VALUES ('2018-10-03 14:38:05.000', 10.30000, 219, 0.31000) ('2018-10-03 14:38:15.000', 12.60000, 218, 0.33000) ('2018-10-03 14:38:16.800', 12.30000, 221, 0.31000) +power.d1002 USING power.meters TAGS(Beijing.Chaoyang, 3) VALUES ('2018-10-03 14:38:16.650', 10.30000, 218, 0.25000) +power.d1003 USING power.meters TAGS(Beijing.Haidian, 2) VALUES ('2018-10-03 14:38:05.500', 11.80000, 221, 0.28000) ('2018-10-03 14:38:16.600', 13.40000, 223, 0.29000) +power.d1004 USING power.meters TAGS(Beijing.Haidian, 3) VALUES ('2018-10-03 14:38:05.000', 10.80000, 223, 0.29000) ('2018-10-03 14:38:06.500', 11.50000, 221, 0.35000)`; + cursor.execute(sql); +} finally { + cursor.close(); + conn.close(); +} + +// run with: node insert_example.js +// output: +// Successfully connected to TDengine +// Query OK, 0 row(s) affected (0.00509570s) +// Query OK, 0 row(s) affected (0.00130880s) +// Query OK, 0 row(s) affected (0.00467900s) +// Query OK, 8 row(s) affected (0.04043550s) +// Connection is closed diff --git a/docs-examples/node/nativeexample/multi_bind_example.js b/docs-examples/node/nativeexample/multi_bind_example.js new file mode 100644 index 0000000000000000000000000000000000000000..d52581ec8e10c6edfbc8fc8f7ca78512b5c93d74 --- /dev/null +++ b/docs-examples/node/nativeexample/multi_bind_example.js @@ -0,0 +1,53 @@ +const taos = require("td2.0-connector"); + +const conn = taos.connect({ + host: "localhost", +}); + +const cursor = conn.cursor(); + +function prepareSTable() { + cursor.execute("CREATE DATABASE power"); + cursor.execute("USE power"); + cursor.execute( + "CREATE STABLE meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT)" + ); +} + +//ANCHOR: insertData +function insertData() { + // init + cursor.stmtInit(); + // prepare + cursor.stmtPrepare( + "INSERT INTO ? USING meters TAGS(?, ?) VALUES(?, ?, ?, ?)" + ); + + // bind table name and tags + let tagBind = new taos.TaosBind(2); + tagBind.bindBinary("Beijing.Chaoyang"); + tagBind.bindInt(2); + cursor.stmtSetTbnameTags("d1001", tagBind.getBind()); + + // bind values + let valueBind = new taos.TaosMultiBindArr(4); + valueBind.multiBindTimestamp([1648432611249, 1648432611749]); + valueBind.multiBindFloat([10.3, 12.6]); + valueBind.multiBindInt([219, 218]); + valueBind.multiBindFloat([0.31, 0.33]); + cursor.stmtBindParamBatch(valueBind.getMultiBindArr()); + cursor.stmtAddBatch(); + + // execute + cursor.stmtExecute(); + cursor.stmtClose(); +} +//ANCHOR_END: insertData + +try { + prepareSTable(); + insertData(); +} finally { + cursor.close(); + conn.close(); +} diff --git a/docs-examples/node/nativeexample/opentsdb_json_example.js b/docs-examples/node/nativeexample/opentsdb_json_example.js new file mode 100644 index 0000000000000000000000000000000000000000..6d436a8e9ebe0230bba22064e8fb6c180c14b5d1 --- /dev/null +++ b/docs-examples/node/nativeexample/opentsdb_json_example.js @@ -0,0 +1,55 @@ +const taos = require("td2.0-connector"); + +const conn = taos.connect({ + host: "localhost", +}); + +const cursor = conn.cursor(); + +function createDatabase() { + cursor.execute("CREATE DATABASE test"); + cursor.execute("USE test"); +} + +function insertData() { + const lines = [ + { + metric: "meters.current", + timestamp: 1648432611249, + value: 10.3, + tags: { location: "Beijing.Chaoyang", groupid: 2 }, + }, + { + metric: "meters.voltage", + timestamp: 1648432611249, + value: 219, + tags: { location: "Beijing.Haidian", groupid: 1 }, + }, + { + metric: "meters.current", + timestamp: 1648432611250, + value: 12.6, + tags: { location: "Beijing.Chaoyang", groupid: 2 }, + }, + { + metric: "meters.voltage", + timestamp: 1648432611250, + value: 221, + tags: { location: "Beijing.Haidian", groupid: 1 }, + }, + ]; + + cursor.schemalessInsert( + [JSON.stringify(lines)], + taos.SCHEMALESS_PROTOCOL.TSDB_SML_JSON_PROTOCOL, + taos.SCHEMALESS_PRECISION.TSDB_SML_TIMESTAMP_NOT_CONFIGURED + ); +} + +try { + createDatabase(); + insertData(); +} finally { + cursor.close(); + conn.close(); +} diff --git a/docs-examples/node/nativeexample/opentsdb_telnet_example.js b/docs-examples/node/nativeexample/opentsdb_telnet_example.js new file mode 100644 index 0000000000000000000000000000000000000000..01e79c2dcacd923cd708d1d228959a628d0ff26a --- /dev/null +++ b/docs-examples/node/nativeexample/opentsdb_telnet_example.js @@ -0,0 +1,38 @@ +const taos = require("td2.0-connector"); + +const conn = taos.connect({ + host: "localhost", +}); + +const cursor = conn.cursor(); + +function createDatabase() { + cursor.execute("CREATE DATABASE test"); + cursor.execute("USE test"); +} + +function insertData() { + const lines = [ + "meters.current 1648432611249 10.3 location=Beijing.Chaoyang groupid=2", + "meters.current 1648432611250 12.6 location=Beijing.Chaoyang groupid=2", + "meters.current 1648432611249 10.8 location=Beijing.Haidian groupid=3", + "meters.current 1648432611250 11.3 location=Beijing.Haidian groupid=3", + "meters.voltage 1648432611249 219 location=Beijing.Chaoyang groupid=2", + "meters.voltage 1648432611250 218 location=Beijing.Chaoyang groupid=2", + "meters.voltage 1648432611249 221 location=Beijing.Haidian groupid=3", + "meters.voltage 1648432611250 217 location=Beijing.Haidian groupid=3", + ]; + cursor.schemalessInsert( + lines, + taos.SCHEMALESS_PROTOCOL.TSDB_SML_TELNET_PROTOCOL, + taos.SCHEMALESS_PRECISION.TSDB_SML_TIMESTAMP_NOT_CONFIGURED + ); +} + +try { + createDatabase(); + insertData(); +} finally { + cursor.close(); + conn.close(); +} diff --git a/docs-examples/node/nativeexample/param_bind_example.js b/docs-examples/node/nativeexample/param_bind_example.js new file mode 100644 index 0000000000000000000000000000000000000000..9117f46c3eeabd9009b72fa9d4a8503e65884242 --- /dev/null +++ b/docs-examples/node/nativeexample/param_bind_example.js @@ -0,0 +1,57 @@ +const taos = require("td2.0-connector"); + +const conn = taos.connect({ + host: "localhost", +}); + +const cursor = conn.cursor(); + +function prepareSTable() { + cursor.execute("CREATE DATABASE power"); + cursor.execute("USE power"); + cursor.execute( + "CREATE STABLE meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT)" + ); +} + +function insertData() { + // init + cursor.stmtInit(); + // prepare + cursor.stmtPrepare( + "INSERT INTO ? USING meters TAGS(?, ?) VALUES(?, ?, ?, ?)" + ); + + // bind table name and tags + let tagBind = new taos.TaosBind(2); + tagBind.bindBinary("Beijing.Chaoyang"); + tagBind.bindInt(2); + cursor.stmtSetTbnameTags("d1001", tagBind.getBind()); + + // bind values + let rows = [ + [1648432611249, 10.3, 219, 0.31], + [1648432611749, 12.6, 218, 0.33], + ]; + for (let row of rows) { + let valueBind = new taos.TaosBind(4); + valueBind.bindTimestamp(row[0]); + valueBind.bindFloat(row[1]); + valueBind.bindInt(row[2]); + valueBind.bindFloat(row[3]); + cursor.stmtBindParam(valueBind.getBind()); + cursor.stmtAddBatch(); + } + + // execute + cursor.stmtExecute(); + cursor.stmtClose(); +} + +try { + prepareSTable(); + insertData(); +} finally { + cursor.close(); + conn.close(); +} diff --git a/docs-examples/node/nativeexample/query_example.js b/docs-examples/node/nativeexample/query_example.js new file mode 100644 index 0000000000000000000000000000000000000000..a51bc939ae8dc458f019ac3bf006d9320dfdee81 --- /dev/null +++ b/docs-examples/node/nativeexample/query_example.js @@ -0,0 +1,17 @@ +const taos = require("td2.0-connector"); + +const conn = taos.connect({ host: "localhost", database: "power" }); +const cursor = conn.cursor(); +const query = cursor.query("SELECT ts, current FROM meters LIMIT 2"); +query.execute().then(function (result) { + result.pretty(); +}); + +// output: +// Successfully connected to TDengine +// Query OK, 2 row(s) in set (0.00317767s) + +// ts | current | +// ======================================================= +// 2018-10-03 14:38:05.000 | 10.3 | +// 2018-10-03 14:38:15.000 | 12.6 | diff --git a/docs-examples/node/nativeexample/subscribe_demo.js b/docs-examples/node/nativeexample/subscribe_demo.js new file mode 100644 index 0000000000000000000000000000000000000000..bdf0c98687c359ba20664e869a25690dffbabd29 --- /dev/null +++ b/docs-examples/node/nativeexample/subscribe_demo.js @@ -0,0 +1,4 @@ +const taos = require("td2.0-connector"); + +const conn = taos.connect({ host: "localhost", database: "power" }); +// 未完成 \ No newline at end of file diff --git a/docs-examples/node/package.json b/docs-examples/node/package.json new file mode 100644 index 0000000000000000000000000000000000000000..f56196d2e5011b1c56e59d78d2ce5fdda98205e9 --- /dev/null +++ b/docs-examples/node/package.json @@ -0,0 +1,10 @@ +{ + "name": "examples", + "version": "1.0.0", + "main": "index.js", + "license": "MIT", + "dependencies": { + "td2.0-connector": "^2.0.12", + "td2.0-rest-connector": "^1.0.0" + } +} diff --git a/docs-examples/node/restexample/connect.js b/docs-examples/node/restexample/connect.js new file mode 100644 index 0000000000000000000000000000000000000000..b84ce2fadf9177983701582b05200986ea9d5ba7 --- /dev/null +++ b/docs-examples/node/restexample/connect.js @@ -0,0 +1,20 @@ +const { options, connect } = require("td2.0-rest-connector"); + +async function test() { + options.path = "/rest/sqlt"; + options.host = "localhost"; + let conn = connect(options); + let cursor = conn.cursor(); + try { + let res = await cursor.query("SELECT server_version()"); + res.toString(); + } catch (err) { + console.log(err); + } +} +test(); + +// output: +// server_version() | +// =================== +// 2.4.0.12 | diff --git a/docs-examples/php/connect.php b/docs-examples/php/connect.php new file mode 100644 index 0000000000000000000000000000000000000000..5af77b9768e5c5ac4b774b433479a4ac8902beda --- /dev/null +++ b/docs-examples/php/connect.php @@ -0,0 +1,20 @@ +connect(); +} catch (TDengineException $e) { + // 连接失败捕获异常 + throw $e; +} diff --git a/docs-examples/php/insert.php b/docs-examples/php/insert.php new file mode 100644 index 0000000000000000000000000000000000000000..0d9cfc4843a2ec3e72d0ad128fa4c2650d6b9cf6 --- /dev/null +++ b/docs-examples/php/insert.php @@ -0,0 +1,33 @@ +connect(); + + // 插入 + $connection->query('CREATE DATABASE if not exists power'); + $connection->query('CREATE STABLE if not exists meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT)'); + $resource = $connection->query(<<<'SQL' + INSERT INTO power.d1001 USING power.meters TAGS(Beijing.Chaoyang, 2) VALUES ('2018-10-03 14:38:05.000', 10.30000, 219, 0.31000) ('2018-10-03 14:38:15.000', 12.60000, 218, 0.33000) ('2018-10-03 14:38:16.800', 12.30000, 221, 0.31000) + power.d1002 USING power.meters TAGS(Beijing.Chaoyang, 3) VALUES ('2018-10-03 14:38:16.650', 10.30000, 218, 0.25000) + power.d1003 USING power.meters TAGS(Beijing.Haidian, 2) VALUES ('2018-10-03 14:38:05.500', 11.80000, 221, 0.28000) ('2018-10-03 14:38:16.600', 13.40000, 223, 0.29000) + power.d1004 USING power.meters TAGS(Beijing.Haidian, 3) VALUES ('2018-10-03 14:38:05.000', 10.80000, 223, 0.29000) ('2018-10-03 14:38:06.500', 11.50000, 221, 0.35000) + SQL); + + // 影响行数 + var_dump($resource->affectedRows()); +} catch (TDengineException $e) { + // 捕获异常 + throw $e; +} diff --git a/docs-examples/php/insert_stmt.php b/docs-examples/php/insert_stmt.php new file mode 100644 index 0000000000000000000000000000000000000000..5d4b4809d215d781807c21172982feff2171fe07 --- /dev/null +++ b/docs-examples/php/insert_stmt.php @@ -0,0 +1,49 @@ +connect(); + + // 插入 + $connection->query('CREATE DATABASE if not exists power'); + $connection->query('CREATE STABLE if not exists meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT)'); + $stmt = $connection->prepare('INSERT INTO ? USING meters TAGS(?, ?) VALUES(?, ?, ?, ?)'); + + // 设置表名和标签 + $stmt->setTableNameTags('d1001', [ + // 支持格式同参数绑定 + [TDengine\TSDB_DATA_TYPE_BINARY, 'Beijing.Chaoyang'], + [TDengine\TSDB_DATA_TYPE_INT, 2], + ]); + + $stmt->bindParams([ + [TDengine\TSDB_DATA_TYPE_TIMESTAMP, 1648432611249], + [TDengine\TSDB_DATA_TYPE_FLOAT, 10.3], + [TDengine\TSDB_DATA_TYPE_INT, 219], + [TDengine\TSDB_DATA_TYPE_FLOAT, 0.31], + ]); + $stmt->bindParams([ + [TDengine\TSDB_DATA_TYPE_TIMESTAMP, 1648432611749], + [TDengine\TSDB_DATA_TYPE_FLOAT, 12.6], + [TDengine\TSDB_DATA_TYPE_INT, 218], + [TDengine\TSDB_DATA_TYPE_FLOAT, 0.33], + ]); + $resource = $stmt->execute(); + + // 影响行数 + var_dump($resource->affectedRows()); +} catch (TDengineException $e) { + // 捕获异常 + throw $e; +} diff --git a/docs-examples/php/query.php b/docs-examples/php/query.php new file mode 100644 index 0000000000000000000000000000000000000000..4e86a2cec7426887686049977a8647e786ac2744 --- /dev/null +++ b/docs-examples/php/query.php @@ -0,0 +1,23 @@ +connect(); + + $resource = $connection->query('SELECT ts, current FROM meters LIMIT 2'); + var_dump($resource->fetch()); +} catch (TDengineException $e) { + // 捕获异常 + throw $e; +} diff --git a/docs-examples/python/.gitignore b/docs-examples/python/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..723ef36f4e4f32c4560383aa5987c575a30c6535 --- /dev/null +++ b/docs-examples/python/.gitignore @@ -0,0 +1 @@ +.idea \ No newline at end of file diff --git a/docs-examples/python/.gitkeep b/docs-examples/python/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/docs-examples/python/async_query_example.py b/docs-examples/python/async_query_example.py new file mode 100644 index 0000000000000000000000000000000000000000..a78f44230a34d40a5326dea367bbad11a6e51da2 --- /dev/null +++ b/docs-examples/python/async_query_example.py @@ -0,0 +1,72 @@ +import time +from ctypes import * + +from taos import * + + +def fetch_callback(p_param, p_result, num_of_rows): + print("fetched ", num_of_rows, "rows") + p = cast(p_param, POINTER(Counter)) + result = TaosResult(p_result) + + if num_of_rows == 0: + print("fetching completed") + p.contents.done = True + result.close() + return + if num_of_rows < 0: + p.contents.done = True + result.check_error(num_of_rows) + result.close() + return None + + for row in result.rows_iter(num_of_rows): + print(row) + p.contents.count += result.row_count + result.fetch_rows_a(fetch_callback, p_param) + + +def query_callback(p_param, p_result, code): + if p_result is None: + return + result = TaosResult(p_result) + if code == 0: + result.fetch_rows_a(fetch_callback, p_param) + result.check_error(code) + + +class Counter(Structure): + _fields_ = [("count", c_int), ("done", c_bool)] + + def __str__(self): + return "{ count: %d, done: %s }" % (self.count, self.done) + + +def test_query(conn): + counter = Counter(count=0) + conn.query_a("select ts, current, voltage from power.meters", query_callback, byref(counter)) + + while not counter.done: + print(counter) + time.sleep(1) + print(counter) + conn.close() + + +if __name__ == "__main__": + test_query(connect()) + +# possible output: +# { count: 0, done: False } +# fetched 8 rows +# 1538548685000 10.300000 219 +# 1538548695000 12.600000 218 +# 1538548696800 12.300000 221 +# 1538548696650 10.300000 218 +# 1538548685500 11.800000 221 +# 1538548696600 13.400000 223 +# 1538548685500 10.800000 223 +# 1538548686500 11.500000 221 +# fetched 0 rows +# fetching completed +# { count: 8, done: True } diff --git a/docs-examples/python/bind_param_example.py b/docs-examples/python/bind_param_example.py new file mode 100644 index 0000000000000000000000000000000000000000..503a2eb5dd91a3516f87a4d3c1c3218cb6505236 --- /dev/null +++ b/docs-examples/python/bind_param_example.py @@ -0,0 +1,60 @@ +import taos +from datetime import datetime + +# note: lines have already been sorted by table name +lines = [('d1001', '2018-10-03 14:38:05.000', 10.30000, 219, 0.31000, 'Beijing.Chaoyang', 2), + ('d1001', '2018-10-03 14:38:15.000', 12.60000, 218, 0.33000, 'Beijing.Chaoyang', 2), + ('d1001', '2018-10-03 14:38:16.800', 12.30000, 221, 0.31000, 'Beijing.Chaoyang', 2), + ('d1002', '2018-10-03 14:38:16.650', 10.30000, 218, 0.25000, 'Beijing.Chaoyang', 3), + ('d1003', '2018-10-03 14:38:05.500', 11.80000, 221, 0.28000, 'Beijing.Haidian', 2), + ('d1003', '2018-10-03 14:38:16.600', 13.40000, 223, 0.29000, 'Beijing.Haidian', 2), + ('d1004', '2018-10-03 14:38:05.000', 10.80000, 223, 0.29000, 'Beijing.Haidian', 3), + ('d1004', '2018-10-03 14:38:06.500', 11.50000, 221, 0.35000, 'Beijing.Haidian', 3)] + + +def get_ts(ts: str): + dt = datetime.strptime(ts, '%Y-%m-%d %H:%M:%S.%f') + return int(dt.timestamp() * 1000) + + +def create_stable(): + conn = taos.connect() + try: + conn.execute("CREATE DATABASE power") + conn.execute("CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) " + "TAGS (location BINARY(64), groupId INT)") + finally: + conn.close() + + +def bind_row_by_row(stmt: taos.TaosStmt): + tb_name = None + for row in lines: + if tb_name != row[0]: + tb_name = row[0] + tags: taos.TaosBind = taos.new_bind_params(2) # 2 is count of tags + tags[0].binary(row[5]) # location + tags[1].int(row[6]) # groupId + stmt.set_tbname_tags(tb_name, tags) + values: taos.TaosBind = taos.new_bind_params(4) # 4 is count of columns + values[0].timestamp(get_ts(row[1])) + values[1].float(row[2]) + values[2].int(row[3]) + values[3].float(row[4]) + stmt.bind_param(values) + + +def insert_data(): + conn = taos.connect(database="power") + try: + stmt = conn.statement("INSERT INTO ? USING meters TAGS(?, ?) VALUES(?, ?, ?, ?)") + bind_row_by_row(stmt) + stmt.execute() + stmt.close() + finally: + conn.close() + + +if __name__ == '__main__': + create_stable() + insert_data() diff --git a/docs-examples/python/conn_native_pandas.py b/docs-examples/python/conn_native_pandas.py new file mode 100644 index 0000000000000000000000000000000000000000..314759f7662c7bf4c9df2c8b3396ad3101c91cd4 --- /dev/null +++ b/docs-examples/python/conn_native_pandas.py @@ -0,0 +1,19 @@ +import pandas +from sqlalchemy import create_engine + +engine = create_engine("taos://root:taosdata@localhost:6030/power") +df = pandas.read_sql("SELECT * FROM meters", engine) + +# print index +print(df.index) +# print data type of element in ts column +print(type(df.ts[0])) +print(df.head(3)) + +# output: +# RangeIndex(start=0, stop=8, step=1) +# +# ts current voltage phase location groupid +# 0 2018-10-03 14:38:05.000 10.3 219 0.31 beijing.chaoyang 2 +# 1 2018-10-03 14:38:15.000 12.6 218 0.33 beijing.chaoyang 2 +# 2 2018-10-03 14:38:16.800 12.3 221 0.31 beijing.chaoyang 2 diff --git a/docs-examples/python/conn_rest_pandas.py b/docs-examples/python/conn_rest_pandas.py new file mode 100644 index 0000000000000000000000000000000000000000..143e4275fa4eda685766297e4b90cba3935a574d --- /dev/null +++ b/docs-examples/python/conn_rest_pandas.py @@ -0,0 +1,19 @@ +import pandas +from sqlalchemy import create_engine + +engine = create_engine("taosrest://root:taosdata@localhost:6041") +df: pandas.DataFrame = pandas.read_sql("SELECT * FROM power.meters", engine) + +# print index +print(df.index) +# print data type of element in ts column +print(type(df.ts[0])) +print(df.head(3)) + +# output: +# +# RangeIndex(start=0, stop=8, step=1) +# ts current ... location groupid +# 0 2018-10-03 14:38:05+08:00 10.3 ... beijing.chaoyang 2 +# 1 2018-10-03 14:38:15+08:00 12.6 ... beijing.chaoyang 2 +# 2 2018-10-03 14:38:16.800000+08:00 12.3 ... beijing.chaoyang 2 diff --git a/docs-examples/python/connect_example.py b/docs-examples/python/connect_example.py new file mode 100644 index 0000000000000000000000000000000000000000..719af79fd12c14cc16e4e3e3c4d6362da5a7520d --- /dev/null +++ b/docs-examples/python/connect_example.py @@ -0,0 +1,19 @@ +import taos + + +def test_connection(): + # all parameters are optional. + # if database is specified, + # then it must exist. + conn = taos.connect(host="localhost", + port=6030, + user="root", + password="taosdata", + database="log") + print('client info:', conn.client_info) + print('server info:', conn.server_info) + conn.close() + + +if __name__ == "__main__": + test_connection() diff --git a/docs-examples/python/connect_native_reference.py b/docs-examples/python/connect_native_reference.py new file mode 100644 index 0000000000000000000000000000000000000000..c17e9795b58724f6646b8d7c0f84047098a93d69 --- /dev/null +++ b/docs-examples/python/connect_native_reference.py @@ -0,0 +1,20 @@ +import taos + +conn: taos.TaosConnection = taos.connect(host="localhost", + user="root", + password="taosdata", + database="test", + port=6030, + config="/etc/taos", # for windows the default value is C:\TDengine\cfg + timezone="Asia/Shanghai") # default your host's timezone + +server_version = conn.server_info +print("server_version", server_version) +client_version = conn.client_info +print("client_version", client_version) # 2.4.0.16 + +conn.close() + +# possible output: +# 2.4.0.16 +# 2.4.0.16 diff --git a/docs-examples/python/connect_rest_examples.py b/docs-examples/python/connect_rest_examples.py new file mode 100644 index 0000000000000000000000000000000000000000..a043d506b965bc31179dbb6f38749d196ab338ff --- /dev/null +++ b/docs-examples/python/connect_rest_examples.py @@ -0,0 +1,45 @@ +# ANCHOR: connect +from taosrest import connect, TaosRestConnection, TaosRestCursor + +conn: TaosRestConnection = connect(host="localhost", + user="root", + password="taosdata", + port=6041, + timeout=30) + +# ANCHOR_END: connect +# ANCHOR: basic +# create STable +cursor: TaosRestCursor = conn.cursor() +cursor.execute("DROP DATABASE IF EXISTS power") +cursor.execute("CREATE DATABASE power") +cursor.execute("CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT)") + +# insert data +cursor.execute("""INSERT INTO power.d1001 USING power.meters TAGS(Beijing.Chaoyang, 2) VALUES ('2018-10-03 14:38:05.000', 10.30000, 219, 0.31000) ('2018-10-03 14:38:15.000', 12.60000, 218, 0.33000) ('2018-10-03 14:38:16.800', 12.30000, 221, 0.31000) + power.d1002 USING power.meters TAGS(Beijing.Chaoyang, 3) VALUES ('2018-10-03 14:38:16.650', 10.30000, 218, 0.25000) + power.d1003 USING power.meters TAGS(Beijing.Haidian, 2) VALUES ('2018-10-03 14:38:05.500', 11.80000, 221, 0.28000) ('2018-10-03 14:38:16.600', 13.40000, 223, 0.29000) + power.d1004 USING power.meters TAGS(Beijing.Haidian, 3) VALUES ('2018-10-03 14:38:05.000', 10.80000, 223, 0.29000) ('2018-10-03 14:38:06.500', 11.50000, 221, 0.35000)""") +print("inserted row count:", cursor.rowcount) + +# query data +cursor.execute("SELECT * FROM power.meters LIMIT 3") +# get total rows +print("queried row count:", cursor.rowcount) +# get column names from cursor +column_names = [meta[0] for meta in cursor.description] +# get rows +data: list[tuple] = cursor.fetchall() +print(column_names) +for row in data: + print(row) + +# output: +# inserted row count: 8 +# queried row count: 3 +# ['ts', 'current', 'voltage', 'phase', 'location', 'groupid'] +# [datetime.datetime(2018, 10, 3, 14, 38, 5, tzinfo=datetime.timezone(datetime.timedelta(seconds=28800), '+08:00')), 10.3, 219, 0.31, 'beijing.chaoyang', 2] +# [datetime.datetime(2018, 10, 3, 14, 38, 15, tzinfo=datetime.timezone(datetime.timedelta(seconds=28800), '+08:00')), 12.6, 218, 0.33, 'beijing.chaoyang', 2] +# [datetime.datetime(2018, 10, 3, 14, 38, 16, 800000, tzinfo=datetime.timezone(datetime.timedelta(seconds=28800), '+08:00')), 12.3, 221, 0.31, 'beijing.chaoyang', 2] + +# ANCHOR_END: basic diff --git a/docs-examples/python/connection_usage_native_reference.py b/docs-examples/python/connection_usage_native_reference.py new file mode 100644 index 0000000000000000000000000000000000000000..4803511e427bf4d906fd3a14ff6faf5a000da96c --- /dev/null +++ b/docs-examples/python/connection_usage_native_reference.py @@ -0,0 +1,45 @@ +import taos + +# ANCHOR: insert +conn = taos.connect() +# Execute a sql, ignore the result set, just get affected rows. It's useful for DDL and DML statement. +conn.execute("DROP DATABASE IF EXISTS test") +conn.execute("CREATE DATABASE test") +# change database. same as execute "USE db" +conn.select_db("test") +conn.execute("CREATE STABLE weather(ts TIMESTAMP, temperature FLOAT) TAGS (location INT)") +affected_row: int = conn.execute("INSERT INTO t1 USING weather TAGS(1) VALUES (now, 23.5) (now+1m, 23.5) (now+2m 24.4)") +print("affected_row", affected_row) +# output: +# affected_row 3 +# ANCHOR_END: insert + +# ANCHOR: query +# Execute a sql and get its result set. It's useful for SELECT statement +result: taos.TaosResult = conn.query("SELECT * from weather") + +# Get fields from result +fields: taos.field.TaosFields = result.fields +for field in fields: + print(field) # {name: ts, type: 9, bytes: 8} + +# output: +# {name: ts, type: 9, bytes: 8} +# {name: temperature, type: 6, bytes: 4} +# {name: location, type: 4, bytes: 4} + +# Get data from result as list of tuple +data = result.fetch_all() +print(data) +# output: +# [(datetime.datetime(2022, 4, 27, 9, 4, 25, 367000), 23.5, 1), (datetime.datetime(2022, 4, 27, 9, 5, 25, 367000), 23.5, 1), (datetime.datetime(2022, 4, 27, 9, 6, 25, 367000), 24.399999618530273, 1)] + +# Or get data from result as a list of dict +# map_data = result.fetch_all_into_dict() +# print(map_data) +# output: +# [{'ts': datetime.datetime(2022, 4, 27, 9, 1, 15, 343000), 'temperature': 23.5, 'location': 1}, {'ts': datetime.datetime(2022, 4, 27, 9, 2, 15, 343000), 'temperature': 23.5, 'location': 1}, {'ts': datetime.datetime(2022, 4, 27, 9, 3, 15, 343000), 'temperature': 24.399999618530273, 'location': 1}] +# ANCHOR_END: query + + +conn.close() diff --git a/docs-examples/python/cursor_usage_native_reference.py b/docs-examples/python/cursor_usage_native_reference.py new file mode 100644 index 0000000000000000000000000000000000000000..a5103810f0199708f94b5c36e1dc080b7998e420 --- /dev/null +++ b/docs-examples/python/cursor_usage_native_reference.py @@ -0,0 +1,32 @@ +import taos + +conn = taos.connect() +cursor = conn.cursor() + +cursor.execute("DROP DATABASE IF EXISTS test") +cursor.execute("CREATE DATABASE test") +cursor.execute("USE test") +cursor.execute("CREATE STABLE weather(ts TIMESTAMP, temperature FLOAT) TAGS (location INT)") + +for i in range(1000): + location = str(i % 10) + tb = "t" + location + cursor.execute(f"INSERT INTO {tb} USING weather TAGS({location}) VALUES (now+{i}a, 23.5) (now+{i + 1}a, 23.5)") + +cursor.execute("SELECT count(*) FROM weather") +data = cursor.fetchall() +print("count:", data[0][0]) +cursor.execute("SELECT tbname, * FROM weather LIMIT 2") +col_names = [meta[0] for meta in cursor.description] +print(col_names) +rows = cursor.fetchall() +print(rows) + +cursor.close() +conn.close() + +# output: +# count: 2000 +# ['tbname', 'ts', 'temperature', 'location'] +# row_count: -1 +# [('t0', datetime.datetime(2022, 4, 27, 14, 54, 24, 392000), 23.5, 0), ('t0', datetime.datetime(2022, 4, 27, 14, 54, 24, 393000), 23.5, 0)] diff --git a/docs-examples/python/handle_exception.py b/docs-examples/python/handle_exception.py new file mode 100644 index 0000000000000000000000000000000000000000..59554465c0891c80f0507656fcc79538c3811fa7 --- /dev/null +++ b/docs-examples/python/handle_exception.py @@ -0,0 +1,19 @@ +import taos + +try: + conn = taos.connect() + conn.execute("CREATE TABLE 123") # wrong sql +except taos.Error as e: + print(e) + print("exception class: ", e.__class__.__name__) + print("error number:", e.errno) + print("error message:", e.msg) +except BaseException as other: + print("exception occur") + print(other) + +# output: +# [0x0216]: syntax error near 'Incomplete SQL statement' +# exception class: ProgrammingError +# error number: -2147483114 +# error message: syntax error near 'Incomplete SQL statement' diff --git a/docs-examples/python/json_protocol_example.py b/docs-examples/python/json_protocol_example.py new file mode 100644 index 0000000000000000000000000000000000000000..5bb4d629bccf3d79e74b381d6259de86d6522315 --- /dev/null +++ b/docs-examples/python/json_protocol_example.py @@ -0,0 +1,38 @@ +import json + +import taos +from taos import SmlProtocol, SmlPrecision + +lines = [{"metric": "meters.current", "timestamp": 1648432611249, "value": 10.3, "tags": {"location": "Beijing.Chaoyang", "groupid": 2}}, + {"metric": "meters.voltage", "timestamp": 1648432611249, "value": 219, + "tags": {"location": "Beijing.Haidian", "groupid": 1}}, + {"metric": "meters.current", "timestamp": 1648432611250, "value": 12.6, + "tags": {"location": "Beijing.Chaoyang", "groupid": 2}}, + {"metric": "meters.voltage", "timestamp": 1648432611250, "value": 221, "tags": {"location": "Beijing.Haidian", "groupid": 1}}] + + +def get_connection(): + return taos.connect() + + +def create_database(conn): + conn.execute("CREATE DATABASE test") + conn.execute("USE test") + + +def insert_lines(conn): + global lines + lines = json.dumps(lines) + # note: the first parameter must be a list with only one element. + affected_rows = conn.schemaless_insert( + [lines], SmlProtocol.JSON_PROTOCOL, SmlPrecision.NOT_CONFIGURED) + print(affected_rows) # 4 + + +if __name__ == '__main__': + connection = get_connection() + try: + create_database(connection) + insert_lines(connection) + finally: + connection.close() diff --git a/docs-examples/python/line_protocol_example.py b/docs-examples/python/line_protocol_example.py new file mode 100644 index 0000000000000000000000000000000000000000..02baeb2104f9f48984b4d34afb5e67af641d4e32 --- /dev/null +++ b/docs-examples/python/line_protocol_example.py @@ -0,0 +1,34 @@ +import taos +from taos import SmlProtocol, SmlPrecision + +lines = ["meters,location=Beijing.Haidian,groupid=2 current=11.8,voltage=221,phase=0.28 1648432611249000", + "meters,location=Beijing.Haidian,groupid=2 current=13.4,voltage=223,phase=0.29 1648432611249500", + "meters,location=Beijing.Haidian,groupid=3 current=10.8,voltage=223,phase=0.29 1648432611249300", + "meters,location=Beijing.Haidian,groupid=3 current=11.3,voltage=221,phase=0.35 1648432611249800", + ] + + +def get_connection(): + # create connection use firstEP in taos.cfg. + return taos.connect() + + +def create_database(conn): + # the default precision is ms (microsecond), but we use us(microsecond) here. + conn.execute("CREATE DATABASE test precision 'us'") + conn.execute("USE test") + + +def insert_lines(conn): + affected_rows = conn.schemaless_insert( + lines, SmlProtocol.LINE_PROTOCOL, SmlPrecision.MICRO_SECONDS) + print(affected_rows) # 8 + + +if __name__ == '__main__': + connection = get_connection() + try: + create_database(connection) + insert_lines(connection) + finally: + connection.close() diff --git a/docs-examples/python/multi_bind_example.py b/docs-examples/python/multi_bind_example.py new file mode 100644 index 0000000000000000000000000000000000000000..1714121d72705ab8d619a41f3463af4aa3193871 --- /dev/null +++ b/docs-examples/python/multi_bind_example.py @@ -0,0 +1,88 @@ +import taos +from datetime import datetime + +# ANCHOR: bind_batch +table_tags = { + "d1001": ('Beijing.Chaoyang', 2), + "d1002": ('Beijing.Chaoyang', 3), + "d1003": ('Beijing.Haidian', 2), + "d1004": ('Beijing.Haidian', 3) +} + +table_values = { + "d1001": [ + ['2018-10-03 14:38:05.000', '2018-10-03 14:38:15.000', '2018-10-03 14:38:16.800'], + [10.3, 12.6, 12.3], + [219, 218, 221], + [0.31, 0.33, 0.32] + ], + "d1002": [ + ['2018-10-03 14:38:16.650'], [10.3], [218], [0.25] + ], + "d1003": [ + ['2018-10-03 14:38:05.500', '2018-10-03 14:38:16.600'], + [11.8, 13.4], + [221, 223], + [0.28, 0.29] + ], + "d1004": [ + ['2018-10-03 14:38:05.500', '2018-10-03 14:38:06.500'], + [10.8, 11.5], + [223, 221], + [0.29, 0.35] + ] +} + + +def bind_multi_rows(stmt: taos.TaosStmt): + """ + batch bind example + """ + for tb_name in table_values.keys(): + tags = table_tags[tb_name] + tag_params = taos.new_bind_params(2) + tag_params[0].binary(tags[0]) + tag_params[1].int(tags[1]) + stmt.set_tbname_tags(tb_name, tag_params) + + values = table_values[tb_name] + value_params = taos.new_multi_binds(4) + value_params[0].timestamp([get_ts(t) for t in values[0]]) + value_params[1].float(values[1]) + value_params[2].int(values[2]) + value_params[3].float(values[3]) + stmt.bind_param_batch(value_params) + + +def insert_data(): + conn = taos.connect(database="power") + try: + stmt = conn.statement("INSERT INTO ? USING meters TAGS(?, ?) VALUES(?, ?, ?, ?)") + bind_multi_rows(stmt) + stmt.execute() + stmt.close() + finally: + conn.close() + + +# ANCHOR_END: bind_batch + + +def create_stable(): + conn = taos.connect() + try: + conn.execute("CREATE DATABASE power") + conn.execute("CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) " + "TAGS (location BINARY(64), groupId INT)") + finally: + conn.close() + + +def get_ts(ts: str): + dt = datetime.strptime(ts, '%Y-%m-%d %H:%M:%S.%f') + return int(dt.timestamp() * 1000) + + +if __name__ == '__main__': + create_stable() + insert_data() diff --git a/docs-examples/python/native_insert_example.py b/docs-examples/python/native_insert_example.py new file mode 100644 index 0000000000000000000000000000000000000000..94d4888a8f5330b9e39d5ae051fcb68f9825505f --- /dev/null +++ b/docs-examples/python/native_insert_example.py @@ -0,0 +1,60 @@ +import taos + +lines = ["d1001,2018-10-03 14:38:05.000,10.30000,219,0.31000,Beijing.Chaoyang,2", + "d1004,2018-10-03 14:38:05.000,10.80000,223,0.29000,Beijing.Haidian,3", + "d1003,2018-10-03 14:38:05.500,11.80000,221,0.28000,Beijing.Haidian,2", + "d1004,2018-10-03 14:38:06.500,11.50000,221,0.35000,Beijing.Haidian,3", + "d1002,2018-10-03 14:38:16.650,10.30000,218,0.25000,Beijing.Chaoyang,3", + "d1001,2018-10-03 14:38:15.000,12.60000,218,0.33000,Beijing.Chaoyang,2", + "d1001,2018-10-03 14:38:16.800,12.30000,221,0.31000,Beijing.Chaoyang,2", + "d1003,2018-10-03 14:38:16.600,13.40000,223,0.29000,Beijing.Haidian,2"] + + +def get_connection() -> taos.TaosConnection: + """ + create connection use firstEp in taos.cfg and use default user and password. + """ + return taos.connect() + + +def create_stable(conn: taos.TaosConnection): + conn.execute("CREATE DATABASE power") + conn.execute("USE power") + conn.execute("CREATE STABLE meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) " + "TAGS (location BINARY(64), groupId INT)") + + +# The generated SQL is: +# INSERT INTO d1001 USING meters TAGS(Beijing.Chaoyang, 2) VALUES ('2018-10-03 14:38:05.000', 10.30000, 219, 0.31000) ('2018-10-03 14:38:15.000', 12.60000, 218, 0.33000) ('2018-10-03 14:38:16.800', 12.30000, 221, 0.31000) +# d1002 USING meters TAGS(Beijing.Chaoyang, 3) VALUES ('2018-10-03 14:38:16.650', 10.30000, 218, 0.25000) +# d1003 USING meters TAGS(Beijing.Haidian, 2) VALUES ('2018-10-03 14:38:05.500', 11.80000, 221, 0.28000) ('2018-10-03 14:38:16.600', 13.40000, 223, 0.29000) +# d1004 USING meters TAGS(Beijing.Haidian, 3) VALUES ('2018-10-03 14:38:05.000', 10.80000, 223, 0.29000) ('2018-10-03 14:38:06.500', 11.50000, 221, 0.35000) + +def get_sql(): + global lines + lines = map(lambda line: line.split(','), lines) # [['d1001', ...]...] + lines = sorted(lines, key=lambda ls: ls[0]) # sort by table name + sql = "INSERT INTO " + tb_name = None + for ps in lines: + tmp_tb_name = ps[0] + if tb_name != tmp_tb_name: + tb_name = tmp_tb_name + sql += f"{tb_name} USING meters TAGS({ps[5]}, {ps[6]}) VALUES " + sql += f"('{ps[1]}', {ps[2]}, {ps[3]}, {ps[4]}) " + return sql + + +def insert_data(conn: taos.TaosConnection): + sql = get_sql() + affected_rows = conn.execute(sql) + print("affected_rows", affected_rows) # 8 + + +if __name__ == '__main__': + connection = get_connection() + try: + create_stable(connection) + insert_data(connection) + finally: + connection.close() diff --git a/docs-examples/python/query_example.py b/docs-examples/python/query_example.py new file mode 100644 index 0000000000000000000000000000000000000000..6d33c49c968d9210b475931b5d8cecca0ceff3e3 --- /dev/null +++ b/docs-examples/python/query_example.py @@ -0,0 +1,42 @@ +import taos + + +# ANCHOR: iter +def query_api_demo(conn: taos.TaosConnection): + result: taos.TaosResult = conn.query("SELECT tbname, * FROM meters LIMIT 2") + print("field count:", result.field_count) + print("meta of fields[1]:", result.fields[1]) + print("======================Iterate on result=========================") + for row in result: + print(row) + + +# field count: 7 +# meta of files[1]: {name: ts, type: 9, bytes: 8} +# ======================Iterate on result========================= +# ('d1001', datetime.datetime(2018, 10, 3, 14, 38, 5), 10.300000190734863, 219, 0.3100000023841858, 'Beijing.Chaoyang', 2) +# ('d1001', datetime.datetime(2018, 10, 3, 14, 38, 15), 12.600000381469727, 218, 0.33000001311302185, 'Beijing.Chaoyang', 2) +# ANCHOR_END: iter + +# ANCHOR: fetch_all +def fetch_all_demo(conn: taos.TaosConnection): + result: taos.TaosResult = conn.query("SELECT ts, current FROM meters LIMIT 2") + rows = result.fetch_all_into_dict() + print("row count:", result.row_count) + print("===============all data===================") + print(rows) + + +# row count: 2 +# ===============all data=================== +# [{'ts': datetime.datetime(2018, 10, 3, 14, 38, 5), 'current': 10.300000190734863}, +# {'ts': datetime.datetime(2018, 10, 3, 14, 38, 15), 'current': 12.600000381469727}] +# ANCHOR_END: fetch_all + +if __name__ == '__main__': + connection = taos.connect(database="power") + try: + query_api_demo(connection) + fetch_all_demo(connection) + finally: + connection.close() diff --git a/docs-examples/python/rest_client_example.py b/docs-examples/python/rest_client_example.py new file mode 100644 index 0000000000000000000000000000000000000000..46d33a1d795f8c8dbb0b830061d43ed4510046ba --- /dev/null +++ b/docs-examples/python/rest_client_example.py @@ -0,0 +1,9 @@ +from taosrest import RestClient + +client = RestClient("localhost", 6041, "root", "taosdata") +res: dict = client.sql("SELECT ts, current FROM power.meters LIMIT 1") +print(res) + +# output: +# {'status': 'succ', 'head': ['ts', 'current'], 'column_meta': [['ts', 9, 8], ['current', 6, 4]], 'data': [[datetime.datetime(2018, 10, 3, 14, 38, 5, tzinfo=datetime.timezone(datetime.timedelta(seconds=28800), '+08:00')), 10.3]], 'rows': 1} + diff --git a/docs-examples/python/result_set_examples.py b/docs-examples/python/result_set_examples.py new file mode 100644 index 0000000000000000000000000000000000000000..6cba0d3f73cdaa408309de93652ed8f346c8f759 --- /dev/null +++ b/docs-examples/python/result_set_examples.py @@ -0,0 +1,33 @@ +import taos + +conn = taos.connect() +conn.execute("DROP DATABASE IF EXISTS test") +conn.execute("CREATE DATABASE test") +conn.select_db("test") +conn.execute("CREATE STABLE weather(ts TIMESTAMP, temperature FLOAT) TAGS (location INT)") +# prepare data +for i in range(2000): + location = str(i % 10) + tb = "t" + location + conn.execute(f"INSERT INTO {tb} USING weather TAGS({location}) VALUES (now+{i}a, 23.5) (now+{i + 1}a, 23.5)") + +result: taos.TaosResult = conn.query("SELECT * FROM weather") + +block_index = 0 +blocks: taos.TaosBlocks = result.blocks_iter() +for rows, length in blocks: + print("block ", block_index, " length", length) + print("first row in this block:", rows[0]) + block_index += 1 + +conn.close() + +# possible output: +# block 0 length 1200 +# first row in this block: (datetime.datetime(2022, 4, 27, 15, 14, 52, 46000), 23.5, 0) +# block 1 length 1200 +# first row in this block: (datetime.datetime(2022, 4, 27, 15, 14, 52, 76000), 23.5, 3) +# block 2 length 1200 +# first row in this block: (datetime.datetime(2022, 4, 27, 15, 14, 52, 99000), 23.5, 6) +# block 3 length 400 +# first row in this block: (datetime.datetime(2022, 4, 27, 15, 14, 52, 122000), 23.5, 9) diff --git a/docs-examples/python/subscribe_demo.py b/docs-examples/python/subscribe_demo.py new file mode 100644 index 0000000000000000000000000000000000000000..db9d49c3f4f8122634800c02a683d4cb022a7ba0 --- /dev/null +++ b/docs-examples/python/subscribe_demo.py @@ -0,0 +1,38 @@ +""" +Python asynchronous subscribe demo. +run on Linux system with: python3 subscribe_demo.py +""" + +from ctypes import c_void_p + +import taos +import time + + +def query_callback(p_sub, p_result, p_param, code): + """ + :param p_sub: pointer returned by native API -- taos_subscribe + :param p_result: pointer to native TAOS_RES + :param p_param: None + :param code: error code + :return: None + """ + print("in callback") + result = taos.TaosResult(c_void_p(p_result)) + # raise exception if error occur + result.check_error(code) + for row in result.rows_iter(): + print(row) + print(f"{result.row_count} rows consumed.") + + +if __name__ == '__main__': + conn = taos.connect() + restart = True + topic = "topic-meter-current-bg" + sql = "select * from power.meters where current > 10" # Error sql + interval = 2000 # consumption interval in microseconds. + _ = conn.subscribe(restart, topic, sql, interval, query_callback) + # Note: we received the return value as _ above, to avoid the TaosSubscription object to be deleted by gc. + while True: + time.sleep(10) # use Ctrl + C to interrupt diff --git a/docs-examples/python/telnet_line_protocol_example.py b/docs-examples/python/telnet_line_protocol_example.py new file mode 100644 index 0000000000000000000000000000000000000000..072835109ee238940e6fe5880b72b2b04e0157fa --- /dev/null +++ b/docs-examples/python/telnet_line_protocol_example.py @@ -0,0 +1,38 @@ +import taos +from taos import SmlProtocol, SmlPrecision + +# format: =[ =] +lines = ["meters.current 1648432611249 10.3 location=Beijing.Chaoyang groupid=2", + "meters.current 1648432611250 12.6 location=Beijing.Chaoyang groupid=2", + "meters.current 1648432611249 10.8 location=Beijing.Haidian groupid=3", + "meters.current 1648432611250 11.3 location=Beijing.Haidian groupid=3", + "meters.voltage 1648432611249 219 location=Beijing.Chaoyang groupid=2", + "meters.voltage 1648432611250 218 location=Beijing.Chaoyang groupid=2", + "meters.voltage 1648432611249 221 location=Beijing.Haidian groupid=3", + "meters.voltage 1648432611250 217 location=Beijing.Haidian groupid=3", + ] + + +# create connection use firstEp in taos.cfg. +def get_connection(): + return taos.connect() + + +def create_database(conn): + conn.execute("CREATE DATABASE test") + conn.execute("USE test") + + +def insert_lines(conn): + affected_rows = conn.schemaless_insert( + lines, SmlProtocol.TELNET_PROTOCOL, SmlPrecision.NOT_CONFIGURED) + print(affected_rows) # 8 + + +if __name__ == '__main__': + connection = get_connection() + try: + create_database(connection) + insert_lines(connection) + finally: + connection.close() diff --git a/docs-examples/rust/Cargo.toml b/docs-examples/rust/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..114407e69edcb94fdeef77e6ae581569c5451160 --- /dev/null +++ b/docs-examples/rust/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["restexample", "nativeexample", "schemalessexample"] diff --git a/docs-examples/rust/nativeexample/Cargo.toml b/docs-examples/rust/nativeexample/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..64fd10a3e915a39c321b56b6f38be51417d8d18e --- /dev/null +++ b/docs-examples/rust/nativeexample/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "nativeexample" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[dependencies] +libtaos = { version = "0.4.3" } +tokio = { version = "*", features = ["rt", "macros", "rt-multi-thread"] } +bstr = { version = "*" } diff --git a/docs-examples/rust/nativeexample/examples/connect.rs b/docs-examples/rust/nativeexample/examples/connect.rs new file mode 100644 index 0000000000000000000000000000000000000000..8e27458de5d428479668a0e5133ca904cf27c213 --- /dev/null +++ b/docs-examples/rust/nativeexample/examples/connect.rs @@ -0,0 +1,19 @@ +use libtaos::*; + +fn taos_connect() -> Result { + TaosCfgBuilder::default() + .ip("localhost") + .user("root") + .pass("taosdata") + // .db("log") // remove comment if you want to connect to database log by default. + .port(6030u16) + .build() + .expect("TaosCfg builder error") + .connect() +} + +fn main() { + #[allow(unused_variables)] + let taos = taos_connect().unwrap(); + println!("Connected") +} diff --git a/docs-examples/rust/nativeexample/examples/stmt_example.rs b/docs-examples/rust/nativeexample/examples/stmt_example.rs new file mode 100644 index 0000000000000000000000000000000000000000..a791a4135984a33dded145e8175d7ade57de8d77 --- /dev/null +++ b/docs-examples/rust/nativeexample/examples/stmt_example.rs @@ -0,0 +1,38 @@ +use bstr::BString; +use libtaos::*; + +#[tokio::main] +async fn main() -> Result<(), Error> { + let taos = TaosCfg::default().connect().expect("fail to connect"); + taos.create_database("power").await?; + taos.use_database("power").await?; + taos.exec("CREATE STABLE meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT)").await?; + let mut stmt = taos.stmt("INSERT INTO ? USING meters TAGS(?, ?) VALUES(?, ?, ?, ?)")?; + // bind table name and tags + stmt.set_tbname_tags( + "d1001", + [ + Field::Binary(BString::from("Beijing.Chaoyang")), + Field::Int(2), + ], + )?; + // bind values. + let values = vec![ + Field::Timestamp(Timestamp::new(1648432611249, TimestampPrecision::Milli)), + Field::Float(10.3), + Field::Int(219), + Field::Float(0.31), + ]; + stmt.bind(&values)?; + // bind one more row + let values2 = vec![ + Field::Timestamp(Timestamp::new(1648432611749, TimestampPrecision::Milli)), + Field::Float(12.6), + Field::Int(218), + Field::Float(0.33), + ]; + stmt.bind(&values2)?; + // execute + stmt.execute()?; + Ok(()) +} diff --git a/docs-examples/rust/nativeexample/examples/subscribe_demo.rs b/docs-examples/rust/nativeexample/examples/subscribe_demo.rs new file mode 100644 index 0000000000000000000000000000000000000000..4febff9be7bd5771db449fbfb184a9f208e61d8a --- /dev/null +++ b/docs-examples/rust/nativeexample/examples/subscribe_demo.rs @@ -0,0 +1,3 @@ +fn main() { + +} \ No newline at end of file diff --git a/docs-examples/rust/nativeexample/src/main.rs b/docs-examples/rust/nativeexample/src/main.rs new file mode 100644 index 0000000000000000000000000000000000000000..e7a11a969c037e00a796aafeff6258501ec15e9a --- /dev/null +++ b/docs-examples/rust/nativeexample/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/docs-examples/rust/restexample/Cargo.toml b/docs-examples/rust/restexample/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..a5f89f8a3be3baabd298a70947f6c5d3df088aae --- /dev/null +++ b/docs-examples/rust/restexample/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "restexample" +version = "0.1.0" +edition = "2021" + +[dependencies] +libtaos = { version = "0.4.3", features = ["rest"] } +tokio = { version = "*", features = ["rt", "macros", "rt-multi-thread"] } diff --git a/docs-examples/rust/restexample/examples/connect.rs b/docs-examples/rust/restexample/examples/connect.rs new file mode 100644 index 0000000000000000000000000000000000000000..b3718342c4a142786572a7eec46d6fa36f651566 --- /dev/null +++ b/docs-examples/rust/restexample/examples/connect.rs @@ -0,0 +1,20 @@ +use libtaos::*; + +fn taos_connect() -> Result { + TaosCfgBuilder::default() + .ip("localhost") + .user("root") + .pass("taosdata") + // .db("log") // remove comment if you want to connect to database log by default. + .port(6030u16) + .build() + .expect("TaosCfg builder error") + .connect() +} + +#[tokio::main] +async fn main() { + #[allow(unused_variables)] + let taos = taos_connect().expect("connect error"); + println!("Connected") +} diff --git a/docs-examples/rust/restexample/examples/insert_example.rs b/docs-examples/rust/restexample/examples/insert_example.rs new file mode 100644 index 0000000000000000000000000000000000000000..d7acc98d096fb3cd6bea22d6c5f6f0f5caea50af --- /dev/null +++ b/docs-examples/rust/restexample/examples/insert_example.rs @@ -0,0 +1,18 @@ +use libtaos::*; + +#[tokio::main] +async fn main() -> Result<(), Error> { + let taos = TaosCfg::default().connect().expect("fail to connect"); + taos.create_database("power").await?; + taos.exec("CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT)").await?; + let sql = "INSERT INTO power.d1001 USING power.meters TAGS(Beijing.Chaoyang, 2) VALUES ('2018-10-03 14:38:05.000', 10.30000, 219, 0.31000) ('2018-10-03 14:38:15.000', 12.60000, 218, 0.33000) ('2018-10-03 14:38:16.800', 12.30000, 221, 0.31000) + power.d1002 USING power.meters TAGS(Beijing.Chaoyang, 3) VALUES ('2018-10-03 14:38:16.650', 10.30000, 218, 0.25000) + power.d1003 USING power.meters TAGS(Beijing.Haidian, 2) VALUES ('2018-10-03 14:38:05.500', 11.80000, 221, 0.28000) ('2018-10-03 14:38:16.600', 13.40000, 223, 0.29000) + power.d1004 USING power.meters TAGS(Beijing.Haidian, 3) VALUES ('2018-10-03 14:38:05.000', 10.80000, 223, 0.29000) ('2018-10-03 14:38:06.500', 11.50000, 221, 0.35000)"; + let result = taos.query(sql).await?; + println!("{:?}", result); + Ok(()) +} + +// output: +// TaosQueryData { column_meta: [ColumnMeta { name: "affected_rows", type_: Int, bytes: 4 }], rows: [[Int(8)]] } diff --git a/docs-examples/rust/restexample/examples/query_example.rs b/docs-examples/rust/restexample/examples/query_example.rs new file mode 100644 index 0000000000000000000000000000000000000000..bbe0cfaabf0d3b078606e823dd504be4153832e7 --- /dev/null +++ b/docs-examples/rust/restexample/examples/query_example.rs @@ -0,0 +1,39 @@ +use libtaos::*; + +fn taos_connect() -> Result { + TaosCfgBuilder::default() + .ip("localhost") + .user("root") + .pass("taosdata") + .db("power") + .port(6030u16) + .build() + .expect("TaosCfg builder error") + .connect() +} + +#[tokio::main] +async fn main() -> Result<(), Error> { + let taos = taos_connect().expect("connect error"); + let result = taos.query("SELECT ts, current FROM meters LIMIT 2").await?; + // print column names + let meta: Vec = result.column_meta; + for column in meta { + print!("{}\t", column.name) + } + println!(); + // print rows + let rows: Vec> = result.rows; + for row in rows { + for field in row { + print!("{}\t", field); + } + println!(); + } + Ok(()) +} + +// output: +// ts current +// 2022-03-28 09:56:51.249 10.3 +// 2022-03-28 09:56:51.749 12.6 diff --git a/docs-examples/rust/restexample/src/main.rs b/docs-examples/rust/restexample/src/main.rs new file mode 100644 index 0000000000000000000000000000000000000000..e7a11a969c037e00a796aafeff6258501ec15e9a --- /dev/null +++ b/docs-examples/rust/restexample/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/docs-examples/rust/schemalessexample/Cargo.toml b/docs-examples/rust/schemalessexample/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..32c6a122318d16f488210da54f1600ba8f6a8b7c --- /dev/null +++ b/docs-examples/rust/schemalessexample/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "schemalessexample" +version = "0.1.0" +edition = "2021" + +[dependencies] +libtaos = { version = "0.4.3" } diff --git a/docs-examples/rust/schemalessexample/examples/influxdb_line_example.rs b/docs-examples/rust/schemalessexample/examples/influxdb_line_example.rs new file mode 100644 index 0000000000000000000000000000000000000000..e93888cc83d12f3bec7370a66e8a85d38cec42ad --- /dev/null +++ b/docs-examples/rust/schemalessexample/examples/influxdb_line_example.rs @@ -0,0 +1,22 @@ +use libtaos::schemaless::*; +use libtaos::*; + +fn main() { + let taos = TaosCfg::default().connect().expect("fail to connect"); + taos.raw_query("CREATE DATABASE test").unwrap(); + taos.raw_query("USE test").unwrap(); + let lines = ["meters,location=Beijing.Haidian,groupid=2 current=11.8,voltage=221,phase=0.28 1648432611249", + "meters,location=Beijing.Haidian,groupid=2 current=13.4,voltage=223,phase=0.29 1648432611250", + "meters,location=Beijing.Haidian,groupid=3 current=10.8,voltage=223,phase=0.29 1648432611249", + "meters,location=Beijing.Haidian,groupid=3 current=11.3,voltage=221,phase=0.35 1648432611250"]; + let affected_rows = taos + .schemaless_insert( + &lines, + TSDB_SML_LINE_PROTOCOL, + TSDB_SML_TIMESTAMP_MILLISECONDS, + ) + .unwrap(); + println!("affected_rows={}", affected_rows); +} + +// run with: cargo run --example influxdb_line_example diff --git a/docs-examples/rust/schemalessexample/examples/opentsdb_json_example.rs b/docs-examples/rust/schemalessexample/examples/opentsdb_json_example.rs new file mode 100644 index 0000000000000000000000000000000000000000..1d66bd1f2b1bcbe82dc3ee3e8e25ea4c521c81f0 --- /dev/null +++ b/docs-examples/rust/schemalessexample/examples/opentsdb_json_example.rs @@ -0,0 +1,25 @@ +use libtaos::schemaless::*; +use libtaos::*; + +fn main() { + let taos = TaosCfg::default().connect().expect("fail to connect"); + taos.raw_query("CREATE DATABASE test").unwrap(); + taos.raw_query("USE test").unwrap(); + let lines = [ + r#"[{"metric": "meters.current", "timestamp": 1648432611249, "value": 10.3, "tags": {"location": "Beijing.Chaoyang", "groupid": 2}}, + {"metric": "meters.voltage", "timestamp": 1648432611249, "value": 219, "tags": {"location": "Beijing.Haidian", "groupid": 1}}, + {"metric": "meters.current", "timestamp": 1648432611250, "value": 12.6, "tags": {"location": "Beijing.Chaoyang", "groupid": 2}}, + {"metric": "meters.voltage", "timestamp": 1648432611250, "value": 221, "tags": {"location": "Beijing.Haidian", "groupid": 1}}]"#, + ]; + + let affected_rows = taos + .schemaless_insert( + &lines, + TSDB_SML_JSON_PROTOCOL, + TSDB_SML_TIMESTAMP_NOT_CONFIGURED, + ) + .unwrap(); + println!("affected_rows={}", affected_rows); // affected_rows=4 +} + +// run with: cargo run --example opentsdb_json_example diff --git a/docs-examples/rust/schemalessexample/examples/opentsdb_telnet_example.rs b/docs-examples/rust/schemalessexample/examples/opentsdb_telnet_example.rs new file mode 100644 index 0000000000000000000000000000000000000000..18d7500714d9e41b1bebd490199d296ead3dc7c4 --- /dev/null +++ b/docs-examples/rust/schemalessexample/examples/opentsdb_telnet_example.rs @@ -0,0 +1,28 @@ +use libtaos::schemaless::*; +use libtaos::*; + +fn main() { + let taos = TaosCfg::default().connect().expect("fail to connect"); + taos.raw_query("CREATE DATABASE test").unwrap(); + taos.raw_query("USE test").unwrap(); + let lines = [ + "meters.current 1648432611249 10.3 location=Beijing.Chaoyang groupid=2", + "meters.current 1648432611250 12.6 location=Beijing.Chaoyang groupid=2", + "meters.current 1648432611249 10.8 location=Beijing.Haidian groupid=3", + "meters.current 1648432611250 11.3 location=Beijing.Haidian groupid=3", + "meters.voltage 1648432611249 219 location=Beijing.Chaoyang groupid=2", + "meters.voltage 1648432611250 218 location=Beijing.Chaoyang groupid=2", + "meters.voltage 1648432611249 221 location=Beijing.Haidian groupid=3", + "meters.voltage 1648432611250 217 location=Beijing.Haidian groupid=3", + ]; + let affected_rows = taos + .schemaless_insert( + &lines, + TSDB_SML_TELNET_PROTOCOL, + TSDB_SML_TIMESTAMP_NOT_CONFIGURED, + ) + .unwrap(); + println!("affected_rows={}", affected_rows); // affected_rows=8 +} + +// run with: cargo run --example opentsdb_telnet_example diff --git a/docs-examples/rust/schemalessexample/src/main.rs b/docs-examples/rust/schemalessexample/src/main.rs new file mode 100644 index 0000000000000000000000000000000000000000..e7a11a969c037e00a796aafeff6258501ec15e9a --- /dev/null +++ b/docs-examples/rust/schemalessexample/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/example/src/tmq.c b/example/src/tmq.c index 1abce3f188fcefd77b4f0d0037284b55d79892b0..913096ee90294cf65ba81d605ed3e7d4f2fa803c 100644 --- a/example/src/tmq.c +++ b/example/src/tmq.c @@ -106,8 +106,8 @@ int32_t create_topic() { } taos_free_result(pRes); - pRes = taos_query(pConn, "create topic topic_ctb_column as abc1"); - /*pRes = taos_query(pConn, "create topic topic_ctb_column as select ts, c1, c2, c3 from st1");*/ + /*pRes = taos_query(pConn, "create topic topic_ctb_column as abc1");*/ + pRes = taos_query(pConn, "create topic topic_ctb_column as select ts, c1, c2, c3 from st1"); if (taos_errno(pRes) != 0) { printf("failed to create topic topic_ctb_column, reason:%s\n", taos_errstr(pRes)); return -1; @@ -167,9 +167,11 @@ tmq_t* build_consumer() { tmq_conf_set(conf, "td.connect.pass", "taosdata"); /*tmq_conf_set(conf, "td.connect.db", "abc1");*/ tmq_conf_set(conf, "msg.with.table.name", "true"); + tmq_conf_set(conf, "enable.auto.commit", "false"); tmq_conf_set_auto_commit_cb(conf, tmq_commit_cb_print, NULL); tmq_t* tmq = tmq_consumer_new(conf, NULL, 0); assert(tmq); + tmq_conf_destroy(conf); return tmq; } @@ -239,7 +241,7 @@ void sync_consume_loop(tmq_t* tmq, tmq_list_t* topics) { msg_process(tmqmessage); taos_free_result(tmqmessage); - tmq_commit_async(tmq, NULL, tmq_commit_cb_print, NULL); + /*tmq_commit_async(tmq, NULL, tmq_commit_cb_print, NULL);*/ /*if ((++msg_count % MIN_COMMIT_COUNT) == 0) tmq_commit(tmq, NULL, 0);*/ } } diff --git a/example/src/tstream.c b/example/src/tstream.c index 537bfebededab2d807b2df1a73a6c53ed98a96dd..97ff2886fcb95fcfaca19ba4baf70b64160855ca 100644 --- a/example/src/tstream.c +++ b/example/src/tstream.c @@ -82,9 +82,7 @@ int32_t create_stream() { /*const char* sql = "select sum(k) from tu1 interval(10m)";*/ /*pRes = tmq_create_stream(pConn, "stream1", "out1", sql);*/ pRes = taos_query( - pConn, - "create stream stream1 trigger at_once into outstb as select _wstartts, min(k), max(k), sum(k) as sum_of_k " - "from tu1 interval(10m)"); + pConn, "create stream stream1 trigger at_once into outstb as select _wstartts, sum(k) from tu1 interval(10m)"); if (taos_errno(pRes) != 0) { printf("failed to create stream stream1, reason:%s\n", taos_errstr(pRes)); return -1; diff --git a/include/common/taosdef.h b/include/common/taosdef.h index 72d2c142d2dafd55fac72ae8f0b0b58aad69b40d..d39c7a121593e6feeb5cfbf104d07642bdbfaff7 100644 --- a/include/common/taosdef.h +++ b/include/common/taosdef.h @@ -86,11 +86,17 @@ typedef enum { TSDB_RETENTION_MAX = 3 } ERetentionLevel; +typedef enum { + TSDB_BITMODE_DEFAULT = 0, // 2 bits + TSDB_BITMODE_ONE_BIT = 1, // 1 bit +} EBitmapMode; + extern char *qtypeStr[]; #define TSDB_PORT_HTTP 11 #undef TD_DEBUG_PRINT_ROW +#undef TD_DEBUG_PRINT_TSDB_LOAD_DCOLS #ifdef __cplusplus } diff --git a/include/common/tdatablock.h b/include/common/tdatablock.h index cea40f4785be8e32a07fadf165a7407d56fddf8e..db8644ecfed50f354e61ff20b424f93dc559f8d7 100644 --- a/include/common/tdatablock.h +++ b/include/common/tdatablock.h @@ -190,7 +190,7 @@ int32_t colDataAppend(SColumnInfoData* pColumnInfoData, uint32_t currentRow, con int32_t colDataMergeCol(SColumnInfoData* pColumnInfoData, uint32_t numOfRow1, int32_t* capacity, const SColumnInfoData* pSource, uint32_t numOfRow2); int32_t colDataAssign(SColumnInfoData* pColumnInfoData, const SColumnInfoData* pSource, int32_t numOfRows); -int32_t blockDataUpdateTsWindow(SSDataBlock* pDataBlock); +int32_t blockDataUpdateTsWindow(SSDataBlock* pDataBlock, int32_t tsColumnIndex); int32_t colDataGetLength(const SColumnInfoData* pColumnInfoData, int32_t numOfRows); void colDataTrim(SColumnInfoData* pColumnInfoData); diff --git a/include/common/tdataformat.h b/include/common/tdataformat.h index e13705d403f9acbd295c238af4d3fe524d3eb5ca..f1f96bfedd880466bea08d2e87ad8f22341f70bb 100644 --- a/include/common/tdataformat.h +++ b/include/common/tdataformat.h @@ -313,8 +313,9 @@ typedef struct { SDataCol *cols; } SDataCols; -static FORCE_INLINE bool tdDataColsIsBitmapI(SDataCols *pCols) { return pCols->bitmapMode != 0; } -static FORCE_INLINE void tdDataColsSetBitmapI(SDataCols *pCols) { pCols->bitmapMode = 1; } +static FORCE_INLINE bool tdDataColsIsBitmapI(SDataCols *pCols) { return pCols->bitmapMode != TSDB_BITMODE_DEFAULT; } +static FORCE_INLINE void tdDataColsSetBitmapI(SDataCols *pCols) { pCols->bitmapMode = TSDB_BITMODE_ONE_BIT; } +static FORCE_INLINE bool tdIsBitmapModeI(int8_t bitmapMode) { return bitmapMode != TSDB_BITMODE_DEFAULT; } #define keyCol(pCols) (&((pCols)->cols[0])) // Key column #define dataColsTKeyAt(pCols, idx) ((TKEY *)(keyCol(pCols)->pData))[(idx)] // the idx row of column-wised data diff --git a/include/common/tglobal.h b/include/common/tglobal.h index 111241cf03a9da14f4f62c47206b8a990a6a529b..2a4ef565dd1c6b6742446adee2daf953665b99e5 100644 --- a/include/common/tglobal.h +++ b/include/common/tglobal.h @@ -128,6 +128,7 @@ extern bool tsStartUdfd; // schemaless extern char tsSmlChildTableName[]; +extern char tsSmlTagName[]; extern bool tsSmlDataFormat; // internal diff --git a/include/common/tmsg.h b/include/common/tmsg.h index addb84046c1d49fe9e450519e63035266c1e9d5f..acf2587d9d005a1f9ff87252b0385f09dd9d3c90 100644 --- a/include/common/tmsg.h +++ b/include/common/tmsg.h @@ -244,12 +244,12 @@ typedef struct { const void* pMsg; } SSubmitMsgIter; -int32_t tInitSubmitMsgIter(const SSubmitReq* pMsg, SSubmitMsgIter* pIter); +int32_t tInitSubmitMsgIter(SSubmitReq* pMsg, SSubmitMsgIter* pIter); int32_t tGetSubmitMsgNext(SSubmitMsgIter* pIter, SSubmitBlk** pPBlock); int32_t tInitSubmitBlkIter(SSubmitMsgIter* pMsgIter, SSubmitBlk* pBlock, SSubmitBlkIter* pIter); STSRow* tGetSubmitBlkNext(SSubmitBlkIter* pIter); // for debug -int32_t tPrintFixedSchemaSubmitReq(const SSubmitReq* pReq, STSchema* pSchema); +int32_t tPrintFixedSchemaSubmitReq(SSubmitReq* pReq, STSchema* pSchema); typedef struct { int32_t code; @@ -1352,6 +1352,9 @@ typedef struct { typedef struct { int32_t code; + char tbFName[TSDB_TABLE_FNAME_LEN]; + int32_t sversion; + int32_t tversion; } SResReadyRsp; typedef struct { @@ -1694,7 +1697,7 @@ int32_t tDecodeSRSmaParam(SDecoder* pCoder, SRSmaParam* pRSmaParam); // TDMT_VND_CREATE_STB ============== typedef struct SVCreateStbReq { - const char* name; + char* name; tb_uid_t suid; int8_t rollup; SSchemaWrapper schema; @@ -1707,8 +1710,8 @@ int tDecodeSVCreateStbReq(SDecoder* pCoder, SVCreateStbReq* pReq); // TDMT_VND_DROP_STB ============== typedef struct SVDropStbReq { - const char* name; - tb_uid_t suid; + char* name; + tb_uid_t suid; } SVDropStbReq; int32_t tEncodeSVDropStbReq(SEncoder* pCoder, const SVDropStbReq* pReq); @@ -1717,16 +1720,16 @@ int32_t tDecodeSVDropStbReq(SDecoder* pCoder, SVDropStbReq* pReq); // TDMT_VND_CREATE_TABLE ============== #define TD_CREATE_IF_NOT_EXISTS 0x1 typedef struct SVCreateTbReq { - int32_t flags; - tb_uid_t uid; - int64_t ctime; - const char* name; - int32_t ttl; - int8_t type; + int32_t flags; + tb_uid_t uid; + int64_t ctime; + char* name; + int32_t ttl; + int8_t type; union { struct { - tb_uid_t suid; - const uint8_t* pTag; + tb_uid_t suid; + uint8_t* pTag; } ctb; struct { SSchemaWrapper schema; @@ -1774,8 +1777,8 @@ int32_t tDeserializeSVCreateTbBatchRsp(void* buf, int32_t bufLen, SVCreateTbBatc // TDMT_VND_DROP_TABLE ================= typedef struct { - const char* name; - int8_t igNotExists; + char* name; + int8_t igNotExists; } SVDropTbReq; typedef struct { @@ -1806,9 +1809,9 @@ int32_t tDecodeSVDropTbBatchRsp(SDecoder* pCoder, SVDropTbBatchRsp* pRsp); // TDMT_VND_ALTER_TABLE ===================== typedef struct { - const char* tbName; - int8_t action; - const char* colName; + char* tbName; + int8_t action; + char* colName; // TSDB_ALTER_TABLE_ADD_COLUMN int8_t type; int8_t flags; @@ -1817,17 +1820,17 @@ typedef struct { // TSDB_ALTER_TABLE_UPDATE_COLUMN_BYTES int32_t colModBytes; // TSDB_ALTER_TABLE_UPDATE_COLUMN_NAME - const char* colNewName; + char* colNewName; // TSDB_ALTER_TABLE_UPDATE_TAG_VAL - const char* tagName; - int8_t isNull; - uint32_t nTagVal; - const uint8_t* pTagVal; + char* tagName; + int8_t isNull; + uint32_t nTagVal; + uint8_t* pTagVal; // TSDB_ALTER_TABLE_UPDATE_OPTIONS - int8_t updateTTL; - int32_t newTTL; - int8_t updateComment; - const char* newComment; + int8_t updateTTL; + int32_t newTTL; + int8_t updateComment; + char* newComment; } SVAlterTbReq; int32_t tEncodeSVAlterTbReq(SEncoder* pEncoder, const SVAlterTbReq* pReq); @@ -1984,19 +1987,16 @@ static FORCE_INLINE void tFreeClientHbReq(void* pReq) { if (req->info) { tFreeReqKvHash(req->info); taosHashCleanup(req->info); + req->info = NULL; } } int32_t tSerializeSClientHbBatchReq(void* buf, int32_t bufLen, const SClientHbBatchReq* pReq); int32_t tDeserializeSClientHbBatchReq(void* buf, int32_t bufLen, SClientHbBatchReq* pReq); -static FORCE_INLINE void tFreeClientHbBatchReq(void* pReq, bool deep) { +static FORCE_INLINE void tFreeClientHbBatchReq(void* pReq) { SClientHbBatchReq* req = (SClientHbBatchReq*)pReq; - if (deep) { - taosArrayDestroyEx(req->reqs, tFreeClientHbReq); - } else { - taosArrayDestroy(req->reqs); - } + taosArrayDestroyEx(req->reqs, tFreeClientHbReq); taosMemoryFree(pReq); } @@ -2020,6 +2020,7 @@ static FORCE_INLINE void tFreeClientHbBatchRsp(void* pRsp) { int32_t tSerializeSClientHbBatchRsp(void* buf, int32_t bufLen, const SClientHbBatchRsp* pBatchRsp); int32_t tDeserializeSClientHbBatchRsp(void* buf, int32_t bufLen, SClientHbBatchRsp* pBatchRsp); +void tFreeSClientHbBatchRsp(SClientHbBatchRsp* pBatchRsp); static FORCE_INLINE int32_t tEncodeSKv(SEncoder* pEncoder, const SKv* pKv) { if (tEncodeI32(pEncoder, pKv->key) < 0) return -1; @@ -2254,20 +2255,20 @@ int32_t tSerializeSMDropSmaReq(void* buf, int32_t bufLen, SMDropSmaReq* pReq); int32_t tDeserializeSMDropSmaReq(void* buf, int32_t bufLen, SMDropSmaReq* pReq); typedef struct { - int8_t version; // for compatibility(default 0) - int8_t intervalUnit; // MACRO: TIME_UNIT_XXX - int8_t slidingUnit; // MACRO: TIME_UNIT_XXX - int8_t timezoneInt; // sma data expired if timezone changes. - char indexName[TSDB_INDEX_NAME_LEN]; - int32_t exprLen; - int32_t tagsFilterLen; - int64_t indexUid; - tb_uid_t tableUid; // super/child/common table uid - int64_t interval; - int64_t offset; // use unit by precision of DB - int64_t sliding; - const char* expr; // sma expression - const char* tagsFilter; + int8_t version; // for compatibility(default 0) + int8_t intervalUnit; // MACRO: TIME_UNIT_XXX + int8_t slidingUnit; // MACRO: TIME_UNIT_XXX + int8_t timezoneInt; // sma data expired if timezone changes. + char indexName[TSDB_INDEX_NAME_LEN]; + int32_t exprLen; + int32_t tagsFilterLen; + int64_t indexUid; + tb_uid_t tableUid; // super/child/common table uid + int64_t interval; + int64_t offset; // use unit by precision of DB + int64_t sliding; + char* expr; // sma expression + char* tagsFilter; } STSma; // Time-range-wise SMA typedef STSma SVCreateTSmaReq; @@ -2520,11 +2521,9 @@ static FORCE_INLINE void* tDecodeSMqDataBlkRsp(const void* buf, SMqDataBlkRsp* p buf = taosDecodeFixedI64(buf, &pRsp->rspOffset); buf = taosDecodeFixedI32(buf, &pRsp->skipLogNum); buf = taosDecodeFixedI32(buf, &pRsp->blockNum); - pRsp->blockData = taosArrayInit(pRsp->blockNum, sizeof(void*)); - pRsp->blockDataLen = taosArrayInit(pRsp->blockNum, sizeof(void*)); - pRsp->blockTbName = taosArrayInit(pRsp->blockNum, sizeof(void*)); - pRsp->blockSchema = taosArrayInit(pRsp->blockNum, sizeof(void*)); if (pRsp->blockNum != 0) { + pRsp->blockData = taosArrayInit(pRsp->blockNum, sizeof(void*)); + pRsp->blockDataLen = taosArrayInit(pRsp->blockNum, sizeof(int32_t)); buf = taosDecodeFixedI8(buf, &pRsp->withTbName); buf = taosDecodeFixedI8(buf, &pRsp->withSchema); buf = taosDecodeFixedI8(buf, &pRsp->withTag); @@ -2537,14 +2536,20 @@ static FORCE_INLINE void* tDecodeSMqDataBlkRsp(const void* buf, SMqDataBlkRsp* p taosArrayPush(pRsp->blockDataLen, &bLen); taosArrayPush(pRsp->blockData, &data); if (pRsp->withSchema) { + pRsp->blockSchema = taosArrayInit(pRsp->blockNum, sizeof(void*)); SSchemaWrapper* pSW = (SSchemaWrapper*)taosMemoryMalloc(sizeof(SSchemaWrapper)); buf = taosDecodeSSchemaWrapper(buf, pSW); taosArrayPush(pRsp->blockSchema, &pSW); + } else { + pRsp->blockSchema = NULL; } if (pRsp->withTbName) { + pRsp->blockTbName = taosArrayInit(pRsp->blockNum, sizeof(void*)); char* name = NULL; buf = taosDecodeString(buf, &name); taosArrayPush(pRsp->blockTbName, &name); + } else { + pRsp->blockTbName = NULL; } } } @@ -2589,26 +2594,14 @@ static FORCE_INLINE void tDeleteSMqAskEpRsp(SMqAskEpRsp* pRsp) { taosArrayDestroyEx(pRsp->topics, (void (*)(void*))tDeleteSMqSubTopicEp); } -typedef struct { - int64_t streamId; - int32_t taskId; - int32_t sourceVg; - int64_t sourceVer; - SArray* data; // SArray -} SStreamDispatchReq; - -typedef struct { - int8_t inputStatus; -} SStreamDispatchRsp; - #define TD_AUTO_CREATE_TABLE 0x1 typedef struct { - int64_t suid; - int64_t uid; - int32_t sver; - uint32_t nData; - const uint8_t* pData; - SVCreateTbReq cTbReq; + int64_t suid; + int64_t uid; + int32_t sver; + uint32_t nData; + uint8_t* pData; + SVCreateTbReq cTbReq; } SVSubmitBlk; typedef struct { diff --git a/include/common/tmsgdef.h b/include/common/tmsgdef.h index 93b2e7536054115c48ed2aa6b286650db8cbb8bb..455898585aaec2935d72aab0cdf6dfab6a0aac48 100644 --- a/include/common/tmsgdef.h +++ b/include/common/tmsgdef.h @@ -200,6 +200,10 @@ enum { TD_DEF_MSG_TYPE(TDMT_VND_TASK_WRITE_EXEC, "vnode-task-write-exec", SStreamTaskExecReq, SStreamTaskExecRsp) TD_DEF_MSG_TYPE(TDMT_VND_STREAM_TRIGGER, "vnode-stream-trigger", NULL, NULL) + TD_DEF_MSG_TYPE(TDMT_VND_TASK_RUN, "vnode-stream-task-run", NULL, NULL) + TD_DEF_MSG_TYPE(TDMT_VND_TASK_DISPATCH, "vnode-stream-task-dispatch", NULL, NULL) + TD_DEF_MSG_TYPE(TDMT_VND_TASK_RECOVER, "vnode-stream-task-recover", NULL, NULL) + TD_DEF_MSG_TYPE(TDMT_VND_CREATE_SMA, "vnode-create-sma", NULL, NULL) TD_DEF_MSG_TYPE(TDMT_VND_CANCEL_SMA, "vnode-cancel-sma", NULL, NULL) TD_DEF_MSG_TYPE(TDMT_VND_DROP_SMA, "vnode-drop-sma", NULL, NULL) diff --git a/include/common/ttypes.h b/include/common/ttypes.h index 14428bfc432d4d74baad48bdce0832c2f138df6e..31cdb28690caeb6610d4b5e4ec6307952a0760aa 100644 --- a/include/common/ttypes.h +++ b/include/common/ttypes.h @@ -30,7 +30,7 @@ typedef uint64_t TDRowVerT; typedef int16_t col_id_t; typedef int8_t col_type_t; typedef int32_t col_bytes_t; -typedef uint16_t schema_ver_t; +typedef int32_t schema_ver_t; typedef int32_t func_id_t; #pragma pack(push, 1) diff --git a/include/dnode/qnode/qnode.h b/include/dnode/qnode/qnode.h index 89553f978beaacd7f54cdce5ab786d510ecd9fc7..1ab101f705ac3f71fad134c200a22f903e4a8e86 100644 --- a/include/dnode/qnode/qnode.h +++ b/include/dnode/qnode/qnode.h @@ -72,7 +72,6 @@ int32_t qndGetLoad(SQnode *pQnode, SQnodeLoad *pLoad); * @param pMsg The request message */ int32_t qndProcessQueryMsg(SQnode *pQnode, SRpcMsg *pMsg); -int32_t qndProcessFetchMsg(SQnode *pQnode, SRpcMsg *pMsg); #ifdef __cplusplus } diff --git a/include/libs/catalog/catalog.h b/include/libs/catalog/catalog.h index f9d8fc0de19fccd48a7d3e115f7e33e3a328ef03..e64fb4235cc6c9c765f7aff4285683b7d2d2cbbd 100644 --- a/include/libs/catalog/catalog.h +++ b/include/libs/catalog/catalog.h @@ -46,24 +46,34 @@ typedef enum { AUTH_TYPE_OTHER, } AUTH_TYPE; +typedef struct SUserAuthInfo { + char user[TSDB_USER_LEN]; + char dbFName[TSDB_DB_FNAME_LEN]; + AUTH_TYPE type; +} SUserAuthInfo; + typedef struct SCatalogReq { - SArray *pTableName; // element is SNAME - SArray *pUdf; // udf name + SArray *pTableMeta; // element is SNAME + SArray *pDbVgroup; // element is db full name + SArray *pTableHash; // element is SNAME + SArray *pUdf; // element is udf name + SArray *pDbCfg; // element is db full name + SArray *pIndex; // element is index name + SArray *pUser; // element is SUserAuthInfo bool qNodeRequired; // valid qnode } SCatalogReq; typedef struct SMetaData { - SArray *pTableMeta; // STableMeta array - SArray *pVgroupInfo; // SVgroupInfo list - SArray *pUdfList; // udf info list - SArray *pQnodeList; // qnode list, SArray + SArray *pTableMeta; // SArray + SArray *pDbVgroup; // SArray*> + SArray *pTableHash; // SArray + SArray *pUdfList; // SArray + SArray *pDbCfg; // SArray + SArray *pIndex; // SArray + SArray *pUser; // SArray + SArray *pQnodeList; // SArray } SMetaData; -typedef struct STbSVersion { - char* tbFName; - int32_t sver; -} STbSVersion; - typedef struct SCatalogCfg { uint32_t maxTblCacheNum; uint32_t maxDBCacheNum; @@ -88,6 +98,11 @@ typedef struct SDbVgVersion { int32_t numOfTable; // unit is TSDB_TABLE_NUM_UNIT } SDbVgVersion; +typedef struct STbSVersion { + char* tbFName; + int32_t sver; +} STbSVersion; + typedef struct SUserAuthVersion { char user[TSDB_USER_LEN]; int32_t version; @@ -96,6 +111,8 @@ typedef struct SUserAuthVersion { typedef SDbCfgRsp SDbCfgInfo; typedef SUserIndexRsp SIndexInfo; +typedef void (*catalogCallback)(SMetaData* pResult, void* param, int32_t code); + int32_t catalogInit(SCatalogCfg *cfg); /** @@ -131,7 +148,7 @@ int32_t catalogUpdateDBVgInfo(SCatalog* pCatalog, const char* dbName, uint64_t d int32_t catalogRemoveDB(SCatalog* pCatalog, const char* dbName, uint64_t dbId); -int32_t catalogRemoveTableMeta(SCatalog* pCtg, const SName* pTableName); +int32_t catalogRemoveTableMeta(SCatalog* pCtg, SName* pTableName); int32_t catalogRemoveStbMeta(SCatalog* pCtg, const char* dbFName, uint64_t dbId, const char* stbName, uint64_t suid); @@ -241,9 +258,9 @@ int32_t catalogGetExpiredUsers(SCatalog* pCtg, SUserAuthVersion **users, uint32_ int32_t catalogGetDBCfg(SCatalog* pCtg, void *pRpc, const SEpSet* pMgmtEps, const char* dbFName, SDbCfgInfo* pDbCfg); -int32_t catalogGetIndexInfo(SCatalog* pCtg, void *pRpc, const SEpSet* pMgmtEps, const char* indexName, SIndexInfo* pInfo); +int32_t catalogGetIndexMeta(SCatalog* pCtg, void *pRpc, const SEpSet* pMgmtEps, const char* indexName, SIndexInfo* pInfo); -int32_t catalogGetUdfInfo(SCatalog* pCtg, void *pRpc, const SEpSet* pMgmtEps, const char* funcName, SFuncInfo** pInfo); +int32_t catalogGetUdfInfo(SCatalog* pCtg, void *pRpc, const SEpSet* pMgmtEps, const char* funcName, SFuncInfo* pInfo); int32_t catalogChkAuth(SCatalog* pCtg, void *pRpc, const SEpSet* pMgmtEps, const char* user, const char* dbFName, AUTH_TYPE type, bool *pass); diff --git a/include/libs/function/functionMgt.h b/include/libs/function/functionMgt.h index 89fbc92992bf783b1d8896fbf636f2468b6fa4c6..3d86adb573cd27dfce3b93409b96a11b47b7aaf5 100644 --- a/include/libs/function/functionMgt.h +++ b/include/libs/function/functionMgt.h @@ -146,7 +146,8 @@ bool fmIsBuiltinFunc(const char* pFunc); bool fmIsAggFunc(int32_t funcId); bool fmIsScalarFunc(int32_t funcId); -bool fmIsNonstandardSQLFunc(int32_t funcId); +bool fmIsVectorFunc(int32_t funcId); +bool fmIsIndefiniteRowsFunc(int32_t funcId); bool fmIsStringFunc(int32_t funcId); bool fmIsDatetimeFunc(int32_t funcId); bool fmIsSelectFunc(int32_t funcId); diff --git a/include/libs/index/index.h b/include/libs/index/index.h index fa4cb1d2bdae0c24b2293f0b376d30f1d4175fc7..05db99db0f199169ce71e4a76d56899361aa403b 100644 --- a/include/libs/index/index.h +++ b/include/libs/index/index.h @@ -16,9 +16,11 @@ #ifndef _TD_INDEX_H_ #define _TD_INDEX_H_ +#include "nodes.h" #include "os.h" #include "taoserror.h" #include "tarray.h" +#include "tglobal.h" #ifdef __cplusplus extern "C" { @@ -189,6 +191,12 @@ void indexTermDestroy(SIndexTerm* p); */ void indexInit(); +/* index filter */ +typedef enum { SFLT_NOT_INDEX, SFLT_COARSE_INDEX, SFLT_ACCURATE_INDEX } SIdxFltStatus; + +SIdxFltStatus idxGetFltStatus(SNode* pFilterNode); + +int32_t doFilterTag(const SNode* pFilterNode, SArray* result); /* * destory index env * diff --git a/include/libs/nodes/nodes.h b/include/libs/nodes/nodes.h index 291e08fdbf2ba28a6a5ea1c0d71d64f6e00a6029..b9cb708c9c172fc522cfef3f7c41bdbd46149cae 100644 --- a/include/libs/nodes/nodes.h +++ b/include/libs/nodes/nodes.h @@ -59,10 +59,10 @@ extern "C" { for (SListCell* cell = (NULL != (list) ? (list)->pHead : NULL); \ (NULL != cell ? (node = &(cell->pNode), true) : (node = NULL, false)); cell = cell->pNext) -#define DESTORY_LIST(list) \ - do { \ - nodesDestroyList(list); \ - list = NULL; \ +#define DESTORY_LIST(list) \ + do { \ + nodesDestroyList((list)); \ + (list) = NULL; \ } while (0) typedef enum ENodeType { @@ -96,6 +96,7 @@ typedef enum ENodeType { QUERY_NODE_EXPLAIN_OPTIONS, QUERY_NODE_STREAM_OPTIONS, QUERY_NODE_TOPIC_OPTIONS, + QUERY_NODE_LEFT_VALUE, // Statement nodes are used in parser and planner module. QUERY_NODE_SET_OPERATOR, diff --git a/include/libs/nodes/plannodes.h b/include/libs/nodes/plannodes.h index 7ca4ca917297e5042ae87c9aaba244a881e6f0a3..6c4d14ffa10be13974e4651868fda955e41cebb7 100644 --- a/include/libs/nodes/plannodes.h +++ b/include/libs/nodes/plannodes.h @@ -54,6 +54,7 @@ typedef struct SScanLogicNode { int64_t sliding; int8_t intervalUnit; int8_t slidingUnit; + SNode* pTagCond; } SScanLogicNode; typedef struct SJoinLogicNode { @@ -343,6 +344,7 @@ typedef struct SSubplan { SNodeList* pParents; // the data destination subplan, get data from current subplan SPhysiNode* pNode; // physical plan of current subplan SDataSinkNode* pDataSink; // data of the subplan flow into the datasink + SNode* pTagCond; } SSubplan; typedef enum EExplainMode { EXPLAIN_MODE_DISABLE = 1, EXPLAIN_MODE_STATIC, EXPLAIN_MODE_ANALYZE } EExplainMode; diff --git a/include/libs/nodes/querynodes.h b/include/libs/nodes/querynodes.h index ee986d5aab7d17666eb6584e2bbb7fac0361c3c7..606c0acd5b1261aae72dc52b041d35d9dbdb3933 100644 --- a/include/libs/nodes/querynodes.h +++ b/include/libs/nodes/querynodes.h @@ -81,6 +81,7 @@ typedef struct SValueNode { char* literal; bool isDuration; bool translate; + bool notReserved; int16_t placeholderNo; union { bool b; @@ -93,6 +94,10 @@ typedef struct SValueNode { char unit; } SValueNode; +typedef struct SLeftValueNode { + ENodeType type; +} SLeftValueNode; + typedef struct SOperatorNode { SExprNode node; // QUERY_NODE_OPERATOR EOperatorType opType; @@ -236,6 +241,7 @@ typedef struct SSelectStmt { bool isTimeOrderQuery; bool hasAggFuncs; bool hasRepeatScanFuncs; + bool hasIndefiniteRowsFunc; } SSelectStmt; typedef enum ESetOperatorType { SET_OP_TYPE_UNION_ALL = 1, SET_OP_TYPE_UNION } ESetOperatorType; @@ -350,9 +356,6 @@ bool nodesIsComparisonOp(const SOperatorNode* pOp); bool nodesIsJsonOp(const SOperatorNode* pOp); bool nodesIsRegularOp(const SOperatorNode* pOp); -bool nodesIsTimeorderQuery(const SNode* pQuery); -bool nodesIsTimelineQuery(const SNode* pQuery); - void* nodesGetValueFromNode(SValueNode* pNode); int32_t nodesSetValueNodeValue(SValueNode* pNode, void* value); char* nodesGetStrValueFromNode(SValueNode* pNode); diff --git a/include/libs/qcom/query.h b/include/libs/qcom/query.h index daf008108be605ca3ba50ce733061233b3ad3829..68a1e08f518f5c5e230076cd56344ea1161804cb 100644 --- a/include/libs/qcom/query.h +++ b/include/libs/qcom/query.h @@ -57,6 +57,12 @@ typedef struct SIndexMeta { } SIndexMeta; +typedef struct STbVerInfo { + char tbFName[TSDB_TABLE_FNAME_LEN]; + int32_t sversion; + int32_t tversion; +} STbVerInfo; + /* * ASSERT(sizeof(SCTableMeta) == 24) * ASSERT(tableType == TSDB_CHILD_TABLE) @@ -182,19 +188,23 @@ extern int32_t (*queryProcessMsgRsp[TDMT_MAX])(void* output, char* msg, int32_t #define SET_META_TYPE_TABLE(t) (t) = META_TYPE_TABLE #define SET_META_TYPE_BOTH_TABLE(t) (t) = META_TYPE_BOTH_TABLE -#define NEED_CLIENT_RM_TBLMETA_ERROR(_code) \ - ((_code) == TSDB_CODE_PAR_TABLE_NOT_EXIST || (_code) == TSDB_CODE_VND_TB_NOT_EXIST) +#define NEED_CLIENT_RM_TBLMETA_ERROR(_code) \ + ((_code) == TSDB_CODE_PAR_TABLE_NOT_EXIST || (_code) == TSDB_CODE_VND_TB_NOT_EXIST || \ + (_code) == TSDB_CODE_PAR_INVALID_COLUMNS_NUM || (_code) == TSDB_CODE_PAR_INVALID_COLUMN || \ + (_code) == TSDB_CODE_PAR_TAGS_NOT_MATCHED) #define NEED_CLIENT_REFRESH_VG_ERROR(_code) \ ((_code) == TSDB_CODE_VND_HASH_MISMATCH || (_code) == TSDB_CODE_VND_INVALID_VGROUP_ID) #define NEED_CLIENT_REFRESH_TBLMETA_ERROR(_code) ((_code) == TSDB_CODE_TDB_TABLE_RECREATED) #define NEED_CLIENT_HANDLE_ERROR(_code) \ (NEED_CLIENT_RM_TBLMETA_ERROR(_code) || NEED_CLIENT_REFRESH_VG_ERROR(_code) || \ NEED_CLIENT_REFRESH_TBLMETA_ERROR(_code)) +#define NEED_CLIENT_RM_TBLMETA_REQ(_type) ((_type) == TDMT_VND_CREATE_TABLE || (_type) == TDMT_VND_CREATE_STB \ + || (_type) == TDMT_VND_DROP_TABLE || (_type) == TDMT_VND_DROP_STB) #define NEED_SCHEDULER_RETRY_ERROR(_code) \ ((_code) == TSDB_CODE_RPC_REDIRECT || (_code) == TSDB_CODE_RPC_NETWORK_UNAVAIL) -#define REQUEST_MAX_TRY_TIMES 5 +#define REQUEST_MAX_TRY_TIMES 1 #define qFatal(...) \ do { \ @@ -220,23 +230,23 @@ extern int32_t (*queryProcessMsgRsp[TDMT_MAX])(void* output, char* msg, int32_t taosPrintLog("QRY ", DEBUG_INFO, tsLogEmbedded ? 255 : qDebugFlag, __VA_ARGS__); \ } \ } while (0) -#define qDebug(...) \ - do { \ - if (qDebugFlag & DEBUG_DEBUG) { \ - taosPrintLog("QRY ", DEBUG_DEBUG, qDebugFlag, __VA_ARGS__); \ - } \ +#define qDebug(...) \ + do { \ + if (qDebugFlag & DEBUG_DEBUG) { \ + taosPrintLog("QRY ", DEBUG_DEBUG, tsLogEmbedded ? 255 : qDebugFlag, __VA_ARGS__); \ + } \ } while (0) -#define qTrace(...) \ - do { \ - if (qDebugFlag & DEBUG_TRACE) { \ - taosPrintLog("QRY ", DEBUG_TRACE, qDebugFlag, __VA_ARGS__); \ - } \ +#define qTrace(...) \ + do { \ + if (qDebugFlag & DEBUG_TRACE) { \ + taosPrintLog("QRY ", DEBUG_TRACE, tsLogEmbedded ? 255 : qDebugFlag, __VA_ARGS__); \ + } \ } while (0) -#define qDebugL(...) \ - do { \ - if (qDebugFlag & DEBUG_DEBUG) { \ - taosPrintLongString("QRY ", DEBUG_DEBUG, qDebugFlag, __VA_ARGS__); \ - } \ +#define qDebugL(...) \ + do { \ + if (qDebugFlag & DEBUG_DEBUG) { \ + taosPrintLongString("QRY ", DEBUG_DEBUG, tsLogEmbedded ? 255 : qDebugFlag, __VA_ARGS__); \ + } \ } while (0) #define QRY_ERR_RET(c) \ diff --git a/include/libs/stream/tstream.h b/include/libs/stream/tstream.h index 4460327b88d465879fd9d0fe31cc52b4a33ec811..d18f609d543e375eee495f0516aa93a25c649653 100644 --- a/include/libs/stream/tstream.h +++ b/include/libs/stream/tstream.h @@ -107,23 +107,19 @@ static FORCE_INLINE void streamDataSubmitRefDec(SStreamDataSubmit* pDataSubmit) if (ref == 0) { taosMemoryFree(pDataSubmit->data); taosMemoryFree(pDataSubmit->dataRef); + // taosFreeQitem(pDataSubmit); } } int32_t streamDataBlockEncode(void** buf, const SStreamDataBlock* pOutput); void* streamDataBlockDecode(const void* buf, SStreamDataBlock* pInput); -typedef struct { - void* inputHandle; - void* executor; -} SStreamRunner; - typedef struct { int8_t parallelizable; char* qmsg; // followings are not applicable to encoder and decoder - int8_t numOfRunners; - SStreamRunner* runners; + void* inputHandle; + void* executor; } STaskExec; typedef struct { @@ -279,16 +275,56 @@ typedef struct { SArray* res; // SArray } SStreamSinkReq; +typedef struct { + SMsgHead head; + int64_t streamId; + int32_t taskId; +} SStreamTaskRunReq; + +typedef struct { + int64_t streamId; + int32_t taskId; + int32_t sourceTaskId; + int32_t sourceVg; +#if 0 + int64_t sourceVer; +#endif + SArray* data; // SArray +} SStreamDispatchReq; + +typedef struct { + int64_t streamId; + int32_t taskId; + int8_t inputStatus; +} SStreamDispatchRsp; + +typedef struct { + int64_t streamId; + int32_t taskId; + int32_t sourceTaskId; + int32_t sourceVg; +} SStreamTaskRecoverReq; + +typedef struct { + int64_t streamId; + int32_t taskId; + int8_t inputStatus; +} SStreamTaskRecoverRsp; + int32_t streamEnqueueDataSubmit(SStreamTask* pTask, SStreamDataSubmit* input); int32_t streamEnqueueDataBlk(SStreamTask* pTask, SStreamDataBlock* input); int32_t streamDequeueOutput(SStreamTask* pTask, void** output); -int32_t streamExecTask(SStreamTask* pTask, SMsgCb* pMsgCb, const void* input, int32_t inputType, int32_t workId); - int32_t streamTaskRun(SStreamTask* pTask); int32_t streamTaskHandleInput(SStreamTask* pTask, void* data); +int32_t streamTaskProcessRunReq(SStreamTask* pTask, SMsgCb* pMsgCb); +int32_t streamProcessDispatchReq(SStreamTask* pTask, SMsgCb* pMsgCb, SStreamDispatchReq* pReq, SRpcMsg* pMsg); +int32_t streamProcessDispatchRsp(SStreamTask* pTask, SMsgCb* pMsgCb, SStreamDispatchRsp* pRsp); +int32_t streamProcessRecoverReq(SStreamTask* pTask, SMsgCb* pMsgCb, SStreamTaskRecoverReq* pReq, SRpcMsg* pMsg); +int32_t streamProcessRecoverRsp(SStreamTask* pTask, SStreamTaskRecoverRsp* pRsp); + #ifdef __cplusplus } #endif diff --git a/include/libs/sync/sync.h b/include/libs/sync/sync.h index 551e0fc7b84b5847755ff2ca1d3edeab2cb87d2d..9b6593e4b5bb4c8aad5018b3b92f73c7e1d52794 100644 --- a/include/libs/sync/sync.h +++ b/include/libs/sync/sync.h @@ -20,20 +20,23 @@ extern "C" { #endif -#include -#include -//#include #include "cJSON.h" #include "tdef.h" -//#include "taosdef.h" -//#include "trpc.h" -//#include "wal.h" +#include "tmsgcb.h" + +#define SYNC_INDEX_BEGIN 0 +#define SYNC_INDEX_INVALID -1 typedef uint64_t SyncNodeId; typedef int32_t SyncGroupId; typedef int64_t SyncIndex; typedef uint64_t SyncTerm; +typedef struct SSyncNode SSyncNode; +typedef struct SSyncBuffer SSyncBuffer; +typedef struct SWal SWal; +typedef struct SSyncRaftEntry SSyncRaftEntry; + typedef enum { TAOS_SYNC_STATE_FOLLOWER = 100, TAOS_SYNC_STATE_CANDIDATE = 101, @@ -41,6 +44,17 @@ typedef enum { TAOS_SYNC_STATE_ERROR = 103, } ESyncState; +typedef enum { + TAOS_SYNC_PROPOSE_SUCCESS = 0, + TAOS_SYNC_PROPOSE_NOT_LEADER = 1, + TAOS_SYNC_PROPOSE_OTHER_ERROR = 2, +} ESyncProposeCode; + +typedef enum { + TAOS_SYNC_FSM_CB_SUCCESS = 0, + TAOS_SYNC_FSM_CB_OTHER_ERROR = 1, +} ESyncFsmCbCode; + typedef struct SNodeInfo { uint16_t nodePort; char nodeFqdn[TSDB_FQDN_LEN]; @@ -58,11 +72,6 @@ typedef struct SSnapshot { SyncTerm lastApplyTerm; } SSnapshot; -typedef enum { - TAOS_SYNC_FSM_CB_SUCCESS = 0, - TAOS_SYNC_FSM_CB_OTHER_ERROR, -} ESyncFsmCbCode; - typedef struct SFsmCbMeta { SyncIndex index; bool isWeak; @@ -71,27 +80,15 @@ typedef struct SFsmCbMeta { uint64_t seqNum; } SFsmCbMeta; -struct SRpcMsg; -typedef struct SRpcMsg SRpcMsg; - typedef struct SSyncFSM { void* data; - void (*FpCommitCb)(struct SSyncFSM* pFsm, const SRpcMsg* pMsg, SFsmCbMeta cbMeta); void (*FpPreCommitCb)(struct SSyncFSM* pFsm, const SRpcMsg* pMsg, SFsmCbMeta cbMeta); void (*FpRollBackCb)(struct SSyncFSM* pFsm, const SRpcMsg* pMsg, SFsmCbMeta cbMeta); - int32_t (*FpGetSnapshot)(struct SSyncFSM* pFsm, SSnapshot* pSnapshot); int32_t (*FpRestoreSnapshot)(struct SSyncFSM* pFsm, const SSnapshot* snapshot); - } SSyncFSM; -struct SSyncRaftEntry; -typedef struct SSyncRaftEntry SSyncRaftEntry; - -#define SYNC_INDEX_BEGIN 0 -#define SYNC_INDEX_INVALID -1 - // abstract definition of log store in raft // SWal implements it typedef struct SSyncLogStore { @@ -120,11 +117,6 @@ typedef struct SSyncLogStore { } SSyncLogStore; -struct SWal; -typedef struct SWal SWal; - -struct SEpSet; -typedef struct SEpSet SEpSet; typedef struct SSyncInfo { SyncGroupId vgId; @@ -132,12 +124,9 @@ typedef struct SSyncInfo { char path[TSDB_FILENAME_LEN]; SWal* pWal; SSyncFSM* pFsm; - - void* rpcClient; - int32_t (*FpSendMsg)(void* rpcClient, const SEpSet* pEpSet, SRpcMsg* pMsg); - void* queue; - int32_t (*FpEqMsg)(void* queue, SRpcMsg* pMsg); - + SMsgCb* msgcb; + int32_t (*FpSendMsg)(const SEpSet* pEpSet, SRpcMsg* pMsg); + int32_t (*FpEqMsg)(const SMsgCb* msgcb, SRpcMsg* pMsg); } SSyncInfo; int32_t syncInit(); @@ -152,27 +141,8 @@ const char* syncGetMyRoleStr(int64_t rid); SyncTerm syncGetMyTerm(int64_t rid); void syncGetEpSet(int64_t rid, SEpSet* pEpSet); int32_t syncGetVgId(int64_t rid); - -typedef enum { - TAOS_SYNC_PROPOSE_SUCCESS = 0, - TAOS_SYNC_PROPOSE_NOT_LEADER, - TAOS_SYNC_PROPOSE_OTHER_ERROR, -} ESyncProposeCode; - -int32_t syncPropose(int64_t rid, const SRpcMsg* pMsg, bool isWeak); - -bool syncEnvIsStart(); - -extern int32_t sDebugFlag; - -//----------------------------------------- -struct SSyncNode; -typedef struct SSyncNode SSyncNode; - -struct SSyncBuffer; -typedef struct SSyncBuffer SSyncBuffer; -//----------------------------------------- - +int32_t syncPropose(int64_t rid, const SRpcMsg* pMsg, bool isWeak); +bool syncEnvIsStart(); const char* syncStr(ESyncState state); #ifdef __cplusplus diff --git a/include/libs/sync/syncTools.h b/include/libs/sync/syncTools.h index 8de4c7cd103a4bfd7a918be44a12acf276ef5af6..4b160c9e6163946edd6fee236ca99f4c665a0f15 100644 --- a/include/libs/sync/syncTools.h +++ b/include/libs/sync/syncTools.h @@ -20,13 +20,7 @@ extern "C" { #endif -#include -#include -//#include -#include "cJSON.h" -//#include "taosdef.h" #include "trpc.h" -//#include "wal.h" // ------------------ ds ------------------- typedef struct SRaftId { @@ -35,16 +29,12 @@ typedef struct SRaftId { } SRaftId; // ------------------ control ------------------- -struct SSyncNode; -typedef struct SSyncNode SSyncNode; - SSyncNode* syncNodeAcquire(int64_t rid); void syncNodeRelease(SSyncNode* pNode); int32_t syncGetRespRpc(int64_t rid, uint64_t index, SRpcMsg* msg); int32_t syncGetAndDelRespRpc(int64_t rid, uint64_t index, SRpcMsg* msg); -void syncSetQ(int64_t rid, void* queueHandle); -void syncSetRpc(int64_t rid, void* rpcHandle); +void syncSetMsgCb(int64_t rid, const SMsgCb* msgcb); char* sync2SimpleStr(int64_t rid); // set timer ms diff --git a/include/libs/transport/rpcHead.h b/include/libs/transport/rpcHead.h deleted file mode 100644 index 5ddf1a83c96271abba9aefc254b9cee442c375cc..0000000000000000000000000000000000000000 --- a/include/libs/transport/rpcHead.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2019 TAOS Data, Inc. - * - * This program is free software: you can use, redistribute, and/or modify - * it under the terms of the GNU Affero General Public License, version 3 - * or later ("AGPL"), as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#ifndef TDENGINE_RPCHEAD_H -#define TDENGINE_RPCHEAD_H - -#include -#ifdef __cplusplus -extern "C" { -#endif - -#define RPC_CONN_TCP 2 - -extern int tsRpcOverhead; - -typedef struct { - void* msg; - int msgLen; - uint32_t ip; - uint16_t port; - int connType; - void* shandle; - void* thandle; - void* chandle; -} SRecvInfo; - -#pragma pack(push, 1) - -typedef struct { - char version : 4; // RPC version - char comp : 4; // compression algorithm, 0:no compression 1:lz4 - char resflag : 2; // reserved bits - char spi : 3; // security parameter index - char encrypt : 3; // encrypt algorithm, 0: no encryption - uint16_t tranId; // transcation ID - uint32_t linkUid; // for unique connection ID assigned by client - uint64_t ahandle; // ahandle assigned by client - uint32_t sourceId; // source ID, an index for connection list - uint32_t destId; // destination ID, an index for connection list - uint32_t destIp; // destination IP address, for NAT scenario - char user[TSDB_UNI_LEN]; // user ID - uint16_t port; // for UDP only, port may be changed - char empty[1]; // reserved - uint16_t msgType; // message type - int32_t msgLen; // message length including the header iteslf - uint32_t msgVer; - int32_t code; // code in response message - uint8_t content[0]; // message body starts from here -} SRpcHead; - -typedef struct { - int32_t reserved; - int32_t contLen; -} SRpcComp; - -typedef struct { - uint32_t timeStamp; - uint8_t auth[TSDB_AUTH_LEN]; -} SRpcDigest; - -#pragma pack(pop) - -#ifdef __cplusplus -} -#endif - -#endif // TDENGINE_RPCHEAD_H diff --git a/include/libs/transport/transport.h b/include/libs/transport/transport.h index f5ffc125eaccfcd6431d29cc379b04ed88657d4d..136e8b35cde07e299cceb0d9268bd066cf40b2a3 100644 --- a/include/libs/transport/transport.h +++ b/include/libs/transport/transport.h @@ -20,9 +20,8 @@ extern "C" { #endif - #ifdef __cplusplus } #endif -#endif /*_TD_TRANSPORT_H_*/ \ No newline at end of file +#endif /*_TD_TRANSPORT_H_*/ diff --git a/include/libs/transport/trpc.h b/include/libs/transport/trpc.h index b6864bd38d571a44d4edd4e7d1934921b64d04b6..fcb00ddf019d09866cba28d7865d800d970bf1f4 100644 --- a/include/libs/transport/trpc.h +++ b/include/libs/transport/trpc.h @@ -38,7 +38,7 @@ typedef struct { typedef struct SRpcHandleInfo { // rpc info - void *handle; // rpc handle returned to app + void * handle; // rpc handle returned to app int64_t refId; // refid, used by server int32_t noResp; // has response or not(default 0, 0: resp, 1: no resp); int32_t persistHandle; // persist handle or not @@ -49,13 +49,13 @@ typedef struct SRpcHandleInfo { void *node; // node mgmt handle // resp info - void *rsp; + void * rsp; int32_t rspLen; } SRpcHandleInfo; typedef struct SRpcMsg { tmsg_t msgType; - void *pCont; + void * pCont; int32_t contLen; int32_t code; SRpcHandleInfo info; @@ -63,11 +63,6 @@ typedef struct SRpcMsg { } SRpcMsg; typedef void (*RpcCfp)(void *parent, SRpcMsg *, SEpSet *rf); -typedef int (*RpcAfp)(void *parent, char *tableId, char *spi, char *encrypt, char *secret, char *ckey); -/// -// // SRpcMsg code -// REDIERE, -// NOT READY, EpSet typedef bool (*RpcRfp)(int32_t code); typedef struct SRpcInit { @@ -80,18 +75,11 @@ typedef struct SRpcInit { int idleTime; // milliseconds, 0 means idle timer is disabled // the following is for client app ecurity only - char *user; // user name - char spi; // security parameter index - char encrypt; // encrypt algorithm - char *secret; // key for authentication - char *ckey; // ciphering key + char *user; // user name // call back to process incoming msg, code shall be ignored by server app RpcCfp cfp; - // call back to retrieve the client auth info, for server app only - RpcAfp afp; - // user defined retry func RpcRfp rfp; diff --git a/include/os/osDir.h b/include/os/osDir.h index b549acde3706bd68e4b3f18aa24fb056eb96a189..a4c686e2807ee3d1fb9a8a0e1e05066d1b616c0b 100644 --- a/include/os/osDir.h +++ b/include/os/osDir.h @@ -31,6 +31,12 @@ extern "C" { #endif +#ifdef WINDOWS +#define TD_TMP_DIR_PATH "C:\\Windows\\Temp\\" +#else +#define TD_TMP_DIR_PATH "/tmp/" +#endif + typedef struct TdDir *TdDirPtr; typedef struct TdDirEntry *TdDirEntryPtr; diff --git a/include/util/taoserror.h b/include/util/taoserror.h index fd3e008e676dfc640c31d8999b2b57e275738d12..e318978339316de794cb8d455f7e1f68a82800a1 100644 --- a/include/util/taoserror.h +++ b/include/util/taoserror.h @@ -39,64 +39,50 @@ int32_t* taosGetErrno(); #define TSDB_CODE_SUCCESS 0 #define TSDB_CODE_FAILED -1 // unknown or needn't tell detail error -// rpc -#define TSDB_CODE_RPC_ACTION_IN_PROGRESS TAOS_DEF_ERROR_CODE(0, 0x0001) -#define TSDB_CODE_RPC_AUTH_REQUIRED TAOS_DEF_ERROR_CODE(0, 0x0002) -#define TSDB_CODE_RPC_AUTH_FAILURE TAOS_DEF_ERROR_CODE(0, 0x0003) -#define TSDB_CODE_RPC_REDIRECT TAOS_DEF_ERROR_CODE(0, 0x0004) -#define TSDB_CODE_RPC_NOT_READY TAOS_DEF_ERROR_CODE(0, 0x0005) -#define TSDB_CODE_RPC_ALREADY_PROCESSED TAOS_DEF_ERROR_CODE(0, 0x0006) -#define TSDB_CODE_RPC_LAST_SESSION_NOT_FINISHED TAOS_DEF_ERROR_CODE(0, 0x0007) -#define TSDB_CODE_RPC_MISMATCHED_LINK_ID TAOS_DEF_ERROR_CODE(0, 0x0008) -#define TSDB_CODE_RPC_TOO_SLOW TAOS_DEF_ERROR_CODE(0, 0x0009) -#define TSDB_CODE_RPC_MAX_SESSIONS TAOS_DEF_ERROR_CODE(0, 0x000A) -#define TSDB_CODE_RPC_NETWORK_UNAVAIL TAOS_DEF_ERROR_CODE(0, 0x000B) -#define TSDB_CODE_RPC_APP_ERROR TAOS_DEF_ERROR_CODE(0, 0x000C) -#define TSDB_CODE_RPC_UNEXPECTED_RESPONSE TAOS_DEF_ERROR_CODE(0, 0x000D) -#define TSDB_CODE_RPC_INVALID_VALUE TAOS_DEF_ERROR_CODE(0, 0x000E) -#define TSDB_CODE_RPC_INVALID_TRAN_ID TAOS_DEF_ERROR_CODE(0, 0x000F) -#define TSDB_CODE_RPC_INVALID_SESSION_ID TAOS_DEF_ERROR_CODE(0, 0x0010) -#define TSDB_CODE_RPC_INVALID_MSG_TYPE TAOS_DEF_ERROR_CODE(0, 0x0011) -#define TSDB_CODE_RPC_INVALID_RESPONSE_TYPE TAOS_DEF_ERROR_CODE(0, 0x0012) -#define TSDB_CODE_RPC_INVALID_TIME_STAMP TAOS_DEF_ERROR_CODE(0, 0x0013) -#define TSDB_CODE_APP_NOT_READY TAOS_DEF_ERROR_CODE(0, 0x0014) -#define TSDB_CODE_RPC_FQDN_ERROR TAOS_DEF_ERROR_CODE(0, 0x0015) -#define TSDB_CODE_RPC_INVALID_VERSION TAOS_DEF_ERROR_CODE(0, 0x0016) -#define TSDB_CODE_RPC_PORT_EADDRINUSE TAOS_DEF_ERROR_CODE(0, 0x0017) - //common & util -#define TSDB_CODE_OUT_OF_MEMORY TAOS_DEF_ERROR_CODE(0, 0x0100) -#define TSDB_CODE_OUT_OF_RANGE TAOS_DEF_ERROR_CODE(0, 0x0101) -#define TSDB_CODE_OUT_OF_SHM_MEM TAOS_DEF_ERROR_CODE(0, 0x0102) -#define TSDB_CODE_INVALID_SHM_ID TAOS_DEF_ERROR_CODE(0, 0x0103) -#define TSDB_CODE_INVALID_PTR TAOS_DEF_ERROR_CODE(0, 0x0104) -#define TSDB_CODE_INVALID_MSG TAOS_DEF_ERROR_CODE(0, 0x0105) -#define TSDB_CODE_INVALID_MSG_LEN TAOS_DEF_ERROR_CODE(0, 0x0106) -#define TSDB_CODE_INVALID_PARA TAOS_DEF_ERROR_CODE(0, 0x0107) -#define TSDB_CODE_INVALID_CFG TAOS_DEF_ERROR_CODE(0, 0x0108) -#define TSDB_CODE_INVALID_OPTION TAOS_DEF_ERROR_CODE(0, 0x0109) -#define TSDB_CODE_INVALID_JSON_FORMAT TAOS_DEF_ERROR_CODE(0, 0x010A) -#define TSDB_CODE_INVALID_VERSION_NUMBER TAOS_DEF_ERROR_CODE(0, 0x010B) -#define TSDB_CODE_INVALID_VERSION_STRING TAOS_DEF_ERROR_CODE(0, 0x010C) -#define TSDB_CODE_VERSION_NOT_COMPATIBLE TAOS_DEF_ERROR_CODE(0, 0x010D) -#define TSDB_CODE_MEMORY_CORRUPTED TAOS_DEF_ERROR_CODE(0, 0x010E) -#define TSDB_CODE_FILE_CORRUPTED TAOS_DEF_ERROR_CODE(0, 0x010F) -#define TSDB_CODE_CHECKSUM_ERROR TAOS_DEF_ERROR_CODE(0, 0x0110) -#define TSDB_CODE_COMPRESS_ERROR TAOS_DEF_ERROR_CODE(0, 0x0111) -#define TSDB_CODE_OPS_NOT_SUPPORT TAOS_DEF_ERROR_CODE(0, 0x0112) -#define TSDB_CODE_MSG_NOT_PROCESSED TAOS_DEF_ERROR_CODE(0, 0x0113) -#define TSDB_CODE_CFG_NOT_FOUND TAOS_DEF_ERROR_CODE(0, 0x0114) -#define TSDB_CODE_REPEAT_INIT TAOS_DEF_ERROR_CODE(0, 0x0115) -#define TSDB_CODE_DUP_KEY TAOS_DEF_ERROR_CODE(0, 0x0116) -#define TSDB_CODE_NEED_RETRY TAOS_DEF_ERROR_CODE(0, 0x0117) -#define TSDB_CODE_OUT_OF_RPC_MEMORY_QUEUE TAOS_DEF_ERROR_CODE(0, 0x0118) - -#define TSDB_CODE_REF_NO_MEMORY TAOS_DEF_ERROR_CODE(0, 0x0140) -#define TSDB_CODE_REF_FULL TAOS_DEF_ERROR_CODE(0, 0x0141) -#define TSDB_CODE_REF_ID_REMOVED TAOS_DEF_ERROR_CODE(0, 0x0152) -#define TSDB_CODE_REF_INVALID_ID TAOS_DEF_ERROR_CODE(0, 0x0153) -#define TSDB_CODE_REF_ALREADY_EXIST TAOS_DEF_ERROR_CODE(0, 0x0154) -#define TSDB_CODE_REF_NOT_EXIST TAOS_DEF_ERROR_CODE(0, 0x0155) +#define TSDB_CODE_ACTION_IN_PROGRESS TAOS_DEF_ERROR_CODE(0, 0x0001) +#define TSDB_CODE_APP_ERROR TAOS_DEF_ERROR_CODE(0, 0x0002) +#define TSDB_CODE_APP_NOT_READY TAOS_DEF_ERROR_CODE(0, 0x0003) +#define TSDB_CODE_OUT_OF_MEMORY TAOS_DEF_ERROR_CODE(0, 0x0010) +#define TSDB_CODE_OUT_OF_RANGE TAOS_DEF_ERROR_CODE(0, 0x0011) +#define TSDB_CODE_OUT_OF_SHM_MEM TAOS_DEF_ERROR_CODE(0, 0x0012) +#define TSDB_CODE_INVALID_SHM_ID TAOS_DEF_ERROR_CODE(0, 0x0013) +#define TSDB_CODE_INVALID_MSG TAOS_DEF_ERROR_CODE(0, 0x0014) +#define TSDB_CODE_INVALID_MSG_LEN TAOS_DEF_ERROR_CODE(0, 0x0015) +#define TSDB_CODE_INVALID_MSG_TYPE TAOS_DEF_ERROR_CODE(0, 0x0016) +#define TSDB_CODE_INVALID_PTR TAOS_DEF_ERROR_CODE(0, 0x0017) +#define TSDB_CODE_INVALID_PARA TAOS_DEF_ERROR_CODE(0, 0x0018) +#define TSDB_CODE_INVALID_CFG TAOS_DEF_ERROR_CODE(0, 0x0019) +#define TSDB_CODE_INVALID_OPTION TAOS_DEF_ERROR_CODE(0, 0x001A) +#define TSDB_CODE_INVALID_JSON_FORMAT TAOS_DEF_ERROR_CODE(0, 0x001B) +#define TSDB_CODE_INVALID_VERSION_NUMBER TAOS_DEF_ERROR_CODE(0, 0x001C) +#define TSDB_CODE_INVALID_VERSION_STRING TAOS_DEF_ERROR_CODE(0, 0x001D) +#define TSDB_CODE_VERSION_NOT_COMPATIBLE TAOS_DEF_ERROR_CODE(0, 0x001E) +#define TSDB_CODE_MEMORY_CORRUPTED TAOS_DEF_ERROR_CODE(0, 0x001F) +#define TSDB_CODE_FILE_CORRUPTED TAOS_DEF_ERROR_CODE(0, 0x0020) +#define TSDB_CODE_CHECKSUM_ERROR TAOS_DEF_ERROR_CODE(0, 0x0021) +#define TSDB_CODE_COMPRESS_ERROR TAOS_DEF_ERROR_CODE(0, 0x0022) +#define TSDB_CODE_OPS_NOT_SUPPORT TAOS_DEF_ERROR_CODE(0, 0x0023) +#define TSDB_CODE_MSG_NOT_PROCESSED TAOS_DEF_ERROR_CODE(0, 0x0024) +#define TSDB_CODE_CFG_NOT_FOUND TAOS_DEF_ERROR_CODE(0, 0x0025) +#define TSDB_CODE_REPEAT_INIT TAOS_DEF_ERROR_CODE(0, 0x0026) +#define TSDB_CODE_DUP_KEY TAOS_DEF_ERROR_CODE(0, 0x0027) +#define TSDB_CODE_NEED_RETRY TAOS_DEF_ERROR_CODE(0, 0x0028) +#define TSDB_CODE_OUT_OF_RPC_MEMORY_QUEUE TAOS_DEF_ERROR_CODE(0, 0x0029) + +#define TSDB_CODE_REF_NO_MEMORY TAOS_DEF_ERROR_CODE(0, 0x0040) +#define TSDB_CODE_REF_FULL TAOS_DEF_ERROR_CODE(0, 0x0041) +#define TSDB_CODE_REF_ID_REMOVED TAOS_DEF_ERROR_CODE(0, 0x0042) +#define TSDB_CODE_REF_INVALID_ID TAOS_DEF_ERROR_CODE(0, 0x0043) +#define TSDB_CODE_REF_ALREADY_EXIST TAOS_DEF_ERROR_CODE(0, 0x0044) +#define TSDB_CODE_REF_NOT_EXIST TAOS_DEF_ERROR_CODE(0, 0x0045) + +// rpc +#define TSDB_CODE_RPC_REDIRECT TAOS_DEF_ERROR_CODE(0, 0x0100) +#define TSDB_CODE_RPC_AUTH_FAILURE TAOS_DEF_ERROR_CODE(0, 0x0101) +#define TSDB_CODE_RPC_NETWORK_UNAVAIL TAOS_DEF_ERROR_CODE(0, 0x0102) +#define TSDB_CODE_RPC_FQDN_ERROR TAOS_DEF_ERROR_CODE(0, 0x0103) +#define TSDB_CODE_RPC_PORT_EADDRINUSE TAOS_DEF_ERROR_CODE(0, 0x0104) //client #define TSDB_CODE_TSC_INVALID_OPERATION TAOS_DEF_ERROR_CODE(0, 0x0200) @@ -143,9 +129,8 @@ int32_t* taosGetErrno(); // mnode-common #define TSDB_CODE_MND_APP_ERROR TAOS_DEF_ERROR_CODE(0, 0x0300) #define TSDB_CODE_MND_NOT_READY TAOS_DEF_ERROR_CODE(0, 0x0301) -#define TSDB_CODE_MND_ACTION_IN_PROGRESS TAOS_DEF_ERROR_CODE(0, 0x0302) -#define TSDB_CODE_MND_NO_RIGHTS TAOS_DEF_ERROR_CODE(0, 0x0303) -#define TSDB_CODE_MND_INVALID_CONNECTION TAOS_DEF_ERROR_CODE(0, 0x0304) +#define TSDB_CODE_MND_NO_RIGHTS TAOS_DEF_ERROR_CODE(0, 0x0302) +#define TSDB_CODE_MND_INVALID_CONNECTION TAOS_DEF_ERROR_CODE(0, 0x0303) // mnode-show #define TSDB_CODE_MND_INVALID_SHOWOBJ TAOS_DEF_ERROR_CODE(0, 0x0310) @@ -268,6 +253,7 @@ int32_t* taosGetErrno(); #define TSDB_CODE_MND_TRANS_INVALID_STAGE TAOS_DEF_ERROR_CODE(0, 0x03D2) #define TSDB_CODE_MND_TRANS_CONFLICT TAOS_DEF_ERROR_CODE(0, 0x03D3) #define TSDB_CODE_MND_TRANS_UNKNOW_ERROR TAOS_DEF_ERROR_CODE(0, 0x03D4) +#define TSDB_CODE_MND_TRANS_CLOG_IS_NULL TAOS_DEF_ERROR_CODE(0, 0x03D5) // mnode-mq #define TSDB_CODE_MND_TOPIC_ALREADY_EXIST TAOS_DEF_ERROR_CODE(0, 0x03E0) @@ -296,8 +282,8 @@ int32_t* taosGetErrno(); // dnode #define TSDB_CODE_NODE_REDIRECT TAOS_DEF_ERROR_CODE(0, 0x0400) #define TSDB_CODE_NODE_OFFLINE TAOS_DEF_ERROR_CODE(0, 0x0401) -#define TSDB_CODE_NODE_ALREADY_DEPLOYED TAOS_DEF_ERROR_CODE(0, 0x0403) -#define TSDB_CODE_NODE_NOT_DEPLOYED TAOS_DEF_ERROR_CODE(0, 0x0404) +#define TSDB_CODE_NODE_ALREADY_DEPLOYED TAOS_DEF_ERROR_CODE(0, 0x0402) +#define TSDB_CODE_NODE_NOT_DEPLOYED TAOS_DEF_ERROR_CODE(0, 0x0403) // vnode #define TSDB_CODE_VND_ACTION_IN_PROGRESS TAOS_DEF_ERROR_CODE(0, 0x0500) @@ -434,6 +420,7 @@ int32_t* taosGetErrno(); #define TSDB_CODE_TQ_META_KEY_NOT_IN_TXN TAOS_DEF_ERROR_CODE(0, 0x0A09) #define TSDB_CODE_TQ_META_KEY_DUP_IN_TXN TAOS_DEF_ERROR_CODE(0, 0x0A0A) #define TSDB_CODE_TQ_GROUP_NOT_SET TAOS_DEF_ERROR_CODE(0, 0x0A0B) +#define TSDB_CODE_TQ_TABLE_SCHEMA_NOT_FOUND TAOS_DEF_ERROR_CODE(0, 0x0A0B) // wal #define TSDB_CODE_WAL_APP_ERROR TAOS_DEF_ERROR_CODE(0, 0x1000) @@ -649,6 +636,10 @@ int32_t* taosGetErrno(); #define TSDB_CODE_PAR_INVALID_TBNAME TAOS_DEF_ERROR_CODE(0, 0x264C) #define TSDB_CODE_PAR_INVALID_FUNCTION_NAME TAOS_DEF_ERROR_CODE(0, 0x264D) #define TSDB_CODE_PAR_COMMENT_TOO_LONG TAOS_DEF_ERROR_CODE(0, 0x264E) +#define TSDB_CODE_PAR_NOT_ALLOWED_FUNC TAOS_DEF_ERROR_CODE(0, 0x264F) +#define TSDB_CODE_PAR_NOT_ALLOWED_WIN_QUERY TAOS_DEF_ERROR_CODE(0, 0x2650) +#define TSDB_CODE_PAR_INVALID_DROP_COL TAOS_DEF_ERROR_CODE(0, 0x2651) +#define TSDB_CODE_PAR_INVALID_COL_JSON TAOS_DEF_ERROR_CODE(0, 0x2652) //planner #define TSDB_CODE_PLAN_INTERNAL_ERROR TAOS_DEF_ERROR_CODE(0, 0x2700) diff --git a/include/util/tdef.h b/include/util/tdef.h index 5cc687d7ab141c0eedabfcf6331f56af1a6175e5..808fcf01526f003bae9878a997cb338438528121 100644 --- a/include/util/tdef.h +++ b/include/util/tdef.h @@ -132,6 +132,7 @@ typedef enum EOperatorType { OP_TYPE_MOD, // unary arithmetic operator OP_TYPE_MINUS, + OP_TYPE_ASSIGN, // bit operator OP_TYPE_BIT_AND, @@ -233,6 +234,7 @@ typedef enum ELogicConditionType { #define TSDB_MAX_TAG_CONDITIONS 1024 #define TSDB_MAX_JSON_TAG_LEN 16384 +#define TSDB_MAX_JSON_KEY_LEN 256 #define TSDB_AUTH_LEN 16 #define TSDB_PASSWORD_LEN 32 diff --git a/include/util/tencode.h b/include/util/tencode.h index 938e3018a86168d099db9e6ff2398b3cb4ebf1d5..cbacd59fa7873c4cb05b8fdaefb321ae3f854e5b 100644 --- a/include/util/tencode.h +++ b/include/util/tencode.h @@ -39,11 +39,11 @@ typedef struct { } SEncoder; typedef struct { - const uint8_t* data; - uint32_t size; - uint32_t pos; - SCoderMem* mList; - SDecoderNode* dStack; + uint8_t* data; + uint32_t size; + uint32_t pos; + SCoderMem* mList; + SDecoderNode* dStack; } SDecoder; #define tPut(TYPE, BUF, VAL) ((TYPE*)(BUF))[0] = (VAL) @@ -82,7 +82,7 @@ typedef struct { do { \ SEncoder coder = {0}; \ tEncoderInit(&coder, NULL, 0); \ - if ((E)(&coder, S) == 0) { \ + if ((E)(&coder, S) >= 0) { \ SIZE = coder.pos; \ RET = 0; \ } else { \ @@ -120,7 +120,7 @@ static int32_t tEncodeCStrWithLen(SEncoder* pCoder, const char* val, uint32_t le static int32_t tEncodeCStr(SEncoder* pCoder, const char* val); /* ------------------------ DECODE ------------------------ */ -void tDecoderInit(SDecoder* pCoder, const uint8_t* data, uint32_t size); +void tDecoderInit(SDecoder* pCoder, uint8_t* data, uint32_t size); void tDecoderClear(SDecoder* SDecoder); int32_t tStartDecode(SDecoder* pCoder); void tEndDecode(SDecoder* pCoder); @@ -141,9 +141,9 @@ static int32_t tDecodeU64v(SDecoder* pCoder, uint64_t* val); static int32_t tDecodeI64v(SDecoder* pCoder, int64_t* val); static int32_t tDecodeFloat(SDecoder* pCoder, float* val); static int32_t tDecodeDouble(SDecoder* pCoder, double* val); -static int32_t tDecodeBinary(SDecoder* pCoder, const uint8_t** val, uint32_t* len); -static int32_t tDecodeCStrAndLen(SDecoder* pCoder, const char** val, uint32_t* len); -static int32_t tDecodeCStr(SDecoder* pCoder, const char** val); +static int32_t tDecodeBinary(SDecoder* pCoder, uint8_t** val, uint32_t* len); +static int32_t tDecodeCStrAndLen(SDecoder* pCoder, char** val, uint32_t* len); +static int32_t tDecodeCStr(SDecoder* pCoder, char** val); static int32_t tDecodeCStrTo(SDecoder* pCoder, char* val); /* ------------------------ IMPL ------------------------ */ @@ -317,7 +317,7 @@ static FORCE_INLINE int32_t tDecodeI16v(SDecoder* pCoder, int16_t* val) { if (tDecodeU16v(pCoder, &tval) < 0) { return -1; } - *val = ZIGZAGD(int16_t, tval); + if (val) *val = ZIGZAGD(int16_t, tval); return 0; } @@ -331,7 +331,7 @@ static FORCE_INLINE int32_t tDecodeI32v(SDecoder* pCoder, int32_t* val) { if (tDecodeU32v(pCoder, &tval) < 0) { return -1; } - *val = ZIGZAGD(int32_t, tval); + if (val) *val = ZIGZAGD(int32_t, tval); return 0; } @@ -345,7 +345,7 @@ static FORCE_INLINE int32_t tDecodeI64v(SDecoder* pCoder, int64_t* val) { if (tDecodeU64v(pCoder, &tval) < 0) { return -1; } - *val = ZIGZAGD(int64_t, tval); + if (val) *val = ZIGZAGD(int64_t, tval); return 0; } @@ -377,7 +377,7 @@ static FORCE_INLINE int32_t tDecodeDouble(SDecoder* pCoder, double* val) { return 0; } -static FORCE_INLINE int32_t tDecodeBinary(SDecoder* pCoder, const uint8_t** val, uint32_t* len) { +static FORCE_INLINE int32_t tDecodeBinary(SDecoder* pCoder, uint8_t** val, uint32_t* len) { if (tDecodeU32v(pCoder, len) < 0) return -1; if (TD_CODER_CHECK_CAPACITY_FAILED(pCoder, *len)) return -1; @@ -389,20 +389,20 @@ static FORCE_INLINE int32_t tDecodeBinary(SDecoder* pCoder, const uint8_t** val, return 0; } -static FORCE_INLINE int32_t tDecodeCStrAndLen(SDecoder* pCoder, const char** val, uint32_t* len) { - if (tDecodeBinary(pCoder, (const uint8_t**)val, len) < 0) return -1; +static FORCE_INLINE int32_t tDecodeCStrAndLen(SDecoder* pCoder, char** val, uint32_t* len) { + if (tDecodeBinary(pCoder, (uint8_t**)val, len) < 0) return -1; (*len) -= 1; return 0; } -static FORCE_INLINE int32_t tDecodeCStr(SDecoder* pCoder, const char** val) { +static FORCE_INLINE int32_t tDecodeCStr(SDecoder* pCoder, char** val) { uint32_t len; return tDecodeCStrAndLen(pCoder, val, &len); } static int32_t tDecodeCStrTo(SDecoder* pCoder, char* val) { - const char* pStr; - uint32_t len; + char* pStr; + uint32_t len; if (tDecodeCStrAndLen(pCoder, &pStr, &len) < 0) return -1; memcpy(val, pStr, len + 1); diff --git a/packaging/cfg/nginxd.service b/packaging/cfg/nginxd.service index 97c0e2f82934474946fe3af1e4d9a776c1de21ac..50bbc1a21de5e6645404ec1d4e9bcd6f177f69d2 100644 --- a/packaging/cfg/nginxd.service +++ b/packaging/cfg/nginxd.service @@ -1,22 +1,22 @@ -[Unit] -Description=Nginx For TDengine Service -After=network-online.target -Wants=network-online.target - -[Service] -Type=forking -PIDFile=/usr/local/nginxd/logs/nginx.pid -ExecStart=/usr/local/nginxd/sbin/nginx -ExecStop=/usr/local/nginxd/sbin/nginx -s stop -TimeoutStopSec=1000000s -LimitNOFILE=infinity -LimitNPROC=infinity -LimitCORE=infinity -TimeoutStartSec=0 -StandardOutput=null -Restart=always -StartLimitBurst=3 -StartLimitInterval=60s - -[Install] -WantedBy=multi-user.target +[Unit] +Description=Nginx For TDengine Service +After=network-online.target +Wants=network-online.target + +[Service] +Type=forking +PIDFile=/usr/local/nginxd/logs/nginx.pid +ExecStart=/usr/local/nginxd/sbin/nginx +ExecStop=/usr/local/nginxd/sbin/nginx -s stop +TimeoutStopSec=1000000s +LimitNOFILE=infinity +LimitNPROC=infinity +LimitCORE=infinity +TimeoutStartSec=0 +StandardOutput=null +Restart=always +StartLimitBurst=3 +StartLimitInterval=60s + +[Install] +WantedBy=multi-user.target diff --git a/packaging/cfg/taos.cfg b/packaging/cfg/taos.cfg index 4570f2d1240d7953c28459f3d21284c9e3e4611b..7d77a0b23e70782f1a8a0160812820c91640f9dc 100644 --- a/packaging/cfg/taos.cfg +++ b/packaging/cfg/taos.cfg @@ -1,312 +1,312 @@ -######################################################## -# # -# TDengine Configuration # -# Any questions, please email support@taosdata.com # -# # -######################################################## - -# first fully qualified domain name (FQDN) for TDengine system -# firstEp hostname:6030 - -# local fully qualified domain name (FQDN) -# fqdn hostname - -# first port number for the connection (12 continuous UDP/TCP port number are used) -# serverPort 6030 - -# log file's directory -# logDir /var/log/taos - -# data file's directory -# dataDir /var/lib/taos - -# temporary file's directory -# tempDir /tmp/ - -# the arbitrator's fully qualified domain name (FQDN) for TDengine system, for cluster only -# arbitrator arbitrator_hostname:6042 - -# number of threads per CPU core -# numOfThreadsPerCore 1.0 - -# number of threads to commit cache data -# numOfCommitThreads 4 - -# the proportion of total CPU cores available for query processing -# 2.0: the query threads will be set to double of the CPU cores. -# 1.0: all CPU cores are available for query processing [default]. -# 0.5: only half of the CPU cores are available for query. -# 0.0: only one core available. -# ratioOfQueryCores 1.0 - -# the last_row/first/last aggregator will not change the original column name in the result fields -keepColumnName 1 - -# number of management nodes in the system -# numOfMnodes 1 - -# enable/disable backuping vnode directory when removing vnode -# vnodeBak 1 - -# enable/disable installation / usage report -# telemetryReporting 1 - -# enable/disable load balancing -# balance 1 - -# role for dnode. 0 - any, 1 - mnode, 2 - dnode -# role 0 - -# max timer control blocks -# maxTmrCtrl 512 - -# time interval of system monitor, seconds -# monitorInterval 30 - -# number of seconds allowed for a dnode to be offline, for cluster only -# offlineThreshold 864000 - -# RPC re-try timer, millisecond -# rpcTimer 300 - -# RPC maximum time for ack, seconds. -# rpcMaxTime 600 - -# time interval of dnode status reporting to mnode, seconds, for cluster only -# statusInterval 1 - -# time interval of heart beat from shell to dnode, seconds -# shellActivityTimer 3 - -# minimum sliding window time, milli-second -# minSlidingTime 10 - -# minimum time window, milli-second -# minIntervalTime 10 - -# maximum delay before launching a stream computation, milli-second -# maxStreamCompDelay 20000 - -# maximum delay before launching a stream computation for the first time, milli-second -# maxFirstStreamCompDelay 10000 - -# retry delay when a stream computation fails, milli-second -# retryStreamCompDelay 10 - -# the delayed time for launching a stream computation, from 0.1(default, 10% of whole computing time window) to 0.9 -# streamCompDelayRatio 0.1 - -# max number of vgroups per db, 0 means configured automatically -# maxVgroupsPerDb 0 - -# max number of tables per vnode -# maxTablesPerVnode 1000000 - -# cache block size (Mbyte) -# cache 16 - -# number of cache blocks per vnode -# blocks 6 - -# number of days per DB file -# days 10 - -# number of days to keep DB file -# keep 3650 - -# minimum rows of records in file block -# minRows 100 - -# maximum rows of records in file block -# maxRows 4096 - -# the number of acknowledgments required for successful data writing -# quorum 1 - -# enable/disable compression -# comp 2 - -# write ahead log (WAL) level, 0: no wal; 1: write wal, but no fysnc; 2: write wal, and call fsync -# walLevel 1 - -# if walLevel is set to 2, the cycle of fsync being executed, if set to 0, fsync is called right away -# fsync 3000 - -# number of replications, for cluster only -# replica 1 - -# the compressed rpc message, option: -# -1 (no compression) -# 0 (all message compressed), -# > 0 (rpc message body which larger than this value will be compressed) -# compressMsgSize -1 - -# query retrieved column data compression option: -# -1 (no compression) -# 0 (all retrieved column data compressed), -# > 0 (any retrieved column size greater than this value all data will be compressed.) -# compressColData -1 - -# max length of an SQL -# maxSQLLength 65480 - -# max length of WildCards -# maxWildCardsLength 100 - -# the maximum number of records allowed for super table time sorting -# maxNumOfOrderedRes 100000 - -# system time zone -# timezone Asia/Shanghai (CST, +0800) -# system time zone (for windows 10) -# timezone UTC-8 - -# system locale -# locale en_US.UTF-8 - -# default system charset -# charset UTF-8 - -# max number of connections allowed in dnode -# maxShellConns 5000 - -# max number of connections allowed in client -# maxConnections 5000 - -# stop writing logs when the disk size of the log folder is less than this value -# minimalLogDirGB 1.0 - -# stop writing temporary files when the disk size of the tmp folder is less than this value -# minimalTmpDirGB 1.0 - -# if disk free space is less than this value, taosd service exit directly within startup process -# minimalDataDirGB 2.0 - -# One mnode is equal to the number of vnode consumed -# mnodeEqualVnodeNum 4 - -# enbale/disable http service -# http 1 - -# enable/disable system monitor -# monitor 1 - -# enable/disable recording the SQL statements via restful interface -# httpEnableRecordSql 0 - -# number of threads used to process http requests -# httpMaxThreads 2 - -# maximum number of rows returned by the restful interface -# restfulRowLimit 10240 - -# database name must be specified in restful interface if the following parameter is set, off by default -# httpDbNameMandatory 1 - -# http keep alive, default is 30 seconds -# httpKeepAlive 30000 - -# The following parameter is used to limit the maximum number of lines in log files. -# max number of lines per log filters -# numOfLogLines 10000000 - -# enable/disable async log -# asyncLog 1 - -# time of keeping log files, days -# logKeepDays 0 - - -# The following parameters are used for debug purpose only. -# debugFlag 8 bits mask: FILE-SCREEN-UNUSED-HeartBeat-DUMP-TRACE_WARN-ERROR -# 131: output warning and error -# 135: output debug, warning and error -# 143: output trace, debug, warning and error to log -# 199: output debug, warning and error to both screen and file -# 207: output trace, debug, warning and error to both screen and file - -# debug flag for all log type, take effect when non-zero value -# debugFlag 0 - -# debug flag for meta management messages -# mDebugFlag 135 - -# debug flag for dnode messages -# dDebugFlag 135 - -# debug flag for sync module -# sDebugFlag 135 - -# debug flag for WAL -# wDebugFlag 135 - -# debug flag for SDB -# sdbDebugFlag 135 - -# debug flag for RPC -# rpcDebugFlag 131 - -# debug flag for TAOS TIMER -# tmrDebugFlag 131 - -# debug flag for TDengine client -# cDebugFlag 131 - -# debug flag for JNI -# jniDebugFlag 131 - -# debug flag for storage -# uDebugFlag 131 - -# debug flag for http server -# httpDebugFlag 131 - -# debug flag for monitor -# monDebugFlag 131 - -# debug flag for query -# qDebugFlag 131 - -# debug flag for vnode -# vDebugFlag 131 - -# debug flag for TSDB -# tsdbDebugFlag 131 - -# debug flag for continue query -# cqDebugFlag 131 - -# enable/disable recording the SQL in taos client -# enableRecordSql 0 - -# generate core file when service crash -# enableCoreFile 1 - -# maximum display width of binary and nchar fields in the shell. The parts exceeding this limit will be hidden -# maxBinaryDisplayWidth 30 - -# enable/disable stream (continuous query) -# stream 1 - -# in retrieve blocking model, only in 50% query threads will be used in query processing in dnode -# retrieveBlockingModel 0 - -# the maximum allowed query buffer size in MB during query processing for each data node -# -1 no limit (default) -# 0 no query allowed, queries are disabled -# queryBufferSize -1 - -# percent of redundant data in tsdb meta will compact meta data,0 means donot compact -# tsdbMetaCompactRatio 0 - -# default string type used for storing JSON String, options can be binary/nchar, default is nchar -# defaultJSONStrType nchar - -# force TCP transmission -# rpcForceTcp 0 - -# unit MB. Flush vnode wal file if walSize > walFlushSize and walSize > cache*0.5*blocks -# walFlushSize 1024 - -# unit Hour. Latency of data migration -# keepTimeOffset 0 +######################################################## +# # +# TDengine Configuration # +# Any questions, please email support@taosdata.com # +# # +######################################################## + +# first fully qualified domain name (FQDN) for TDengine system +# firstEp hostname:6030 + +# local fully qualified domain name (FQDN) +# fqdn hostname + +# first port number for the connection (12 continuous UDP/TCP port number are used) +# serverPort 6030 + +# log file's directory +# logDir /var/log/taos + +# data file's directory +# dataDir /var/lib/taos + +# temporary file's directory +# tempDir /tmp/ + +# the arbitrator's fully qualified domain name (FQDN) for TDengine system, for cluster only +# arbitrator arbitrator_hostname:6042 + +# number of threads per CPU core +# numOfThreadsPerCore 1.0 + +# number of threads to commit cache data +# numOfCommitThreads 4 + +# the proportion of total CPU cores available for query processing +# 2.0: the query threads will be set to double of the CPU cores. +# 1.0: all CPU cores are available for query processing [default]. +# 0.5: only half of the CPU cores are available for query. +# 0.0: only one core available. +# ratioOfQueryCores 1.0 + +# the last_row/first/last aggregator will not change the original column name in the result fields +keepColumnName 1 + +# number of management nodes in the system +# numOfMnodes 1 + +# enable/disable backuping vnode directory when removing vnode +# vnodeBak 1 + +# enable/disable installation / usage report +# telemetryReporting 1 + +# enable/disable load balancing +# balance 1 + +# role for dnode. 0 - any, 1 - mnode, 2 - dnode +# role 0 + +# max timer control blocks +# maxTmrCtrl 512 + +# time interval of system monitor, seconds +# monitorInterval 30 + +# number of seconds allowed for a dnode to be offline, for cluster only +# offlineThreshold 864000 + +# RPC re-try timer, millisecond +# rpcTimer 300 + +# RPC maximum time for ack, seconds. +# rpcMaxTime 600 + +# time interval of dnode status reporting to mnode, seconds, for cluster only +# statusInterval 1 + +# time interval of heart beat from shell to dnode, seconds +# shellActivityTimer 3 + +# minimum sliding window time, milli-second +# minSlidingTime 10 + +# minimum time window, milli-second +# minIntervalTime 10 + +# maximum delay before launching a stream computation, milli-second +# maxStreamCompDelay 20000 + +# maximum delay before launching a stream computation for the first time, milli-second +# maxFirstStreamCompDelay 10000 + +# retry delay when a stream computation fails, milli-second +# retryStreamCompDelay 10 + +# the delayed time for launching a stream computation, from 0.1(default, 10% of whole computing time window) to 0.9 +# streamCompDelayRatio 0.1 + +# max number of vgroups per db, 0 means configured automatically +# maxVgroupsPerDb 0 + +# max number of tables per vnode +# maxTablesPerVnode 1000000 + +# cache block size (Mbyte) +# cache 16 + +# number of cache blocks per vnode +# blocks 6 + +# number of days per DB file +# days 10 + +# number of days to keep DB file +# keep 3650 + +# minimum rows of records in file block +# minRows 100 + +# maximum rows of records in file block +# maxRows 4096 + +# the number of acknowledgments required for successful data writing +# quorum 1 + +# enable/disable compression +# comp 2 + +# write ahead log (WAL) level, 0: no wal; 1: write wal, but no fysnc; 2: write wal, and call fsync +# walLevel 1 + +# if walLevel is set to 2, the cycle of fsync being executed, if set to 0, fsync is called right away +# fsync 3000 + +# number of replications, for cluster only +# replica 1 + +# the compressed rpc message, option: +# -1 (no compression) +# 0 (all message compressed), +# > 0 (rpc message body which larger than this value will be compressed) +# compressMsgSize -1 + +# query retrieved column data compression option: +# -1 (no compression) +# 0 (all retrieved column data compressed), +# > 0 (any retrieved column size greater than this value all data will be compressed.) +# compressColData -1 + +# max length of an SQL +# maxSQLLength 65480 + +# max length of WildCards +# maxWildCardsLength 100 + +# the maximum number of records allowed for super table time sorting +# maxNumOfOrderedRes 100000 + +# system time zone +# timezone Asia/Shanghai (CST, +0800) +# system time zone (for windows 10) +# timezone UTC-8 + +# system locale +# locale en_US.UTF-8 + +# default system charset +# charset UTF-8 + +# max number of connections allowed in dnode +# maxShellConns 5000 + +# max number of connections allowed in client +# maxConnections 5000 + +# stop writing logs when the disk size of the log folder is less than this value +# minimalLogDirGB 1.0 + +# stop writing temporary files when the disk size of the tmp folder is less than this value +# minimalTmpDirGB 1.0 + +# if disk free space is less than this value, taosd service exit directly within startup process +# minimalDataDirGB 2.0 + +# One mnode is equal to the number of vnode consumed +# mnodeEqualVnodeNum 4 + +# enbale/disable http service +# http 1 + +# enable/disable system monitor +# monitor 1 + +# enable/disable recording the SQL statements via restful interface +# httpEnableRecordSql 0 + +# number of threads used to process http requests +# httpMaxThreads 2 + +# maximum number of rows returned by the restful interface +# restfulRowLimit 10240 + +# database name must be specified in restful interface if the following parameter is set, off by default +# httpDbNameMandatory 1 + +# http keep alive, default is 30 seconds +# httpKeepAlive 30000 + +# The following parameter is used to limit the maximum number of lines in log files. +# max number of lines per log filters +# numOfLogLines 10000000 + +# enable/disable async log +# asyncLog 1 + +# time of keeping log files, days +# logKeepDays 0 + + +# The following parameters are used for debug purpose only. +# debugFlag 8 bits mask: FILE-SCREEN-UNUSED-HeartBeat-DUMP-TRACE_WARN-ERROR +# 131: output warning and error +# 135: output debug, warning and error +# 143: output trace, debug, warning and error to log +# 199: output debug, warning and error to both screen and file +# 207: output trace, debug, warning and error to both screen and file + +# debug flag for all log type, take effect when non-zero value +# debugFlag 0 + +# debug flag for meta management messages +# mDebugFlag 135 + +# debug flag for dnode messages +# dDebugFlag 135 + +# debug flag for sync module +# sDebugFlag 135 + +# debug flag for WAL +# wDebugFlag 135 + +# debug flag for SDB +# sdbDebugFlag 135 + +# debug flag for RPC +# rpcDebugFlag 131 + +# debug flag for TAOS TIMER +# tmrDebugFlag 131 + +# debug flag for TDengine client +# cDebugFlag 131 + +# debug flag for JNI +# jniDebugFlag 131 + +# debug flag for storage +# uDebugFlag 131 + +# debug flag for http server +# httpDebugFlag 131 + +# debug flag for monitor +# monDebugFlag 131 + +# debug flag for query +# qDebugFlag 131 + +# debug flag for vnode +# vDebugFlag 131 + +# debug flag for TSDB +# tsdbDebugFlag 131 + +# debug flag for continue query +# cqDebugFlag 131 + +# enable/disable recording the SQL in taos client +# enableRecordSql 0 + +# generate core file when service crash +# enableCoreFile 1 + +# maximum display width of binary and nchar fields in the shell. The parts exceeding this limit will be hidden +# maxBinaryDisplayWidth 30 + +# enable/disable stream (continuous query) +# stream 1 + +# in retrieve blocking model, only in 50% query threads will be used in query processing in dnode +# retrieveBlockingModel 0 + +# the maximum allowed query buffer size in MB during query processing for each data node +# -1 no limit (default) +# 0 no query allowed, queries are disabled +# queryBufferSize -1 + +# percent of redundant data in tsdb meta will compact meta data,0 means donot compact +# tsdbMetaCompactRatio 0 + +# default string type used for storing JSON String, options can be binary/nchar, default is nchar +# defaultJSONStrType nchar + +# force TCP transmission +# rpcForceTcp 0 + +# unit MB. Flush vnode wal file if walSize > walFlushSize and walSize > cache*0.5*blocks +# walFlushSize 1024 + +# unit Hour. Latency of data migration +# keepTimeOffset 0 diff --git a/packaging/cfg/taosd.service b/packaging/cfg/taosd.service index db08001df9a78105d367b96cf62f6c7e8721b34d..fff4b74e62a6da8f2bda9a6306a79132d7585e42 100644 --- a/packaging/cfg/taosd.service +++ b/packaging/cfg/taosd.service @@ -1,21 +1,21 @@ -[Unit] -Description=TDengine server service -After=network-online.target -Wants=network-online.target - -[Service] -Type=simple -ExecStart=/usr/bin/taosd -ExecStartPre=/usr/local/taos/bin/startPre.sh -TimeoutStopSec=1000000s -LimitNOFILE=infinity -LimitNPROC=infinity -LimitCORE=infinity -TimeoutStartSec=0 -StandardOutput=null -Restart=always -StartLimitBurst=3 -StartLimitInterval=60s - -[Install] -WantedBy=multi-user.target +[Unit] +Description=TDengine server service +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +ExecStart=/usr/bin/taosd +ExecStartPre=/usr/local/taos/bin/startPre.sh +TimeoutStopSec=1000000s +LimitNOFILE=infinity +LimitNPROC=infinity +LimitCORE=infinity +TimeoutStartSec=0 +StandardOutput=null +Restart=always +StartLimitBurst=3 +StartLimitInterval=60s + +[Install] +WantedBy=multi-user.target diff --git a/packaging/cfg/tarbitratord.service b/packaging/cfg/tarbitratord.service index 051b1ae44fb5e9b885e2f4ddaa399b40bd9ac7dd..d60cb536b094fe6b6c472d55076dc4d1db669d68 100644 --- a/packaging/cfg/tarbitratord.service +++ b/packaging/cfg/tarbitratord.service @@ -1,20 +1,20 @@ -[Unit] -Description=TDengine arbitrator service -After=network-online.target -Wants=network-online.target - -[Service] -Type=simple -ExecStart=/usr/bin/tarbitrator -TimeoutStopSec=1000000s -LimitNOFILE=infinity -LimitNPROC=infinity -LimitCORE=infinity -TimeoutStartSec=0 -StandardOutput=null -Restart=always -StartLimitBurst=3 -StartLimitInterval=60s - -[Install] -WantedBy=multi-user.target +[Unit] +Description=TDengine arbitrator service +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +ExecStart=/usr/bin/tarbitrator +TimeoutStopSec=1000000s +LimitNOFILE=infinity +LimitNPROC=infinity +LimitCORE=infinity +TimeoutStartSec=0 +StandardOutput=null +Restart=always +StartLimitBurst=3 +StartLimitInterval=60s + +[Install] +WantedBy=multi-user.target diff --git a/packaging/check_package.sh b/packaging/check_package.sh index 7b6165016292996d8ccda416aedbadbc961fccd7..81abff57a586d116ead0137f96697ece04250d6b 100644 --- a/packaging/check_package.sh +++ b/packaging/check_package.sh @@ -1,252 +1,252 @@ -#!/bin/bash -# -# This file is used to install database on linux systems. The operating system -# is required to use systemd to manage services at boot - -set -e -#set -x - -verMode=edge -pagMode=full - -iplist="" -serverFqdn="" - -# -----------------------Variables definition--------------------- -script_dir="../release" -# Dynamic directory -data_dir="/var/lib/taos" -log_dir="/var/log/taos" - -data_link_dir="/usr/local/taos/data" -log_link_dir="/usr/local/taos/log" - -cfg_install_dir="/etc/taos" - -bin_link_dir="/usr/bin" -lib_link_dir="/usr/lib" -lib64_link_dir="/usr/lib64" -inc_link_dir="/usr/include" - -#install main path -install_main_dir="/usr/local/taos" - -# old bin dir -sbin_dir="/usr/local/taos/bin" - -temp_version="" -fin_result="" - -service_config_dir="/etc/systemd/system" -nginx_port=6060 -nginx_dir="/usr/local/nginxd" - -# Color setting -RED='\033[0;31m' -GREEN='\033[1;32m' -GREEN_DARK='\033[0;32m' -GREEN_UNDERLINE='\033[4;32m' -NC='\033[0m' - -csudo="" -if command -v sudo > /dev/null; then - csudo="sudo " -fi - -# ============================= get input parameters ================================================= - -# install.sh -v [server | client] -e [yes | no] -i [systemd | service | ...] - -# set parameters by default value -interactiveFqdn=yes # [yes | no] -verType=server # [server | client] -initType=systemd # [systemd | service | ...] - -while getopts "hv:d:" arg -do - case $arg in - d) - #echo "interactiveFqdn=$OPTARG" - script_dir=$( echo $OPTARG ) - ;; - h) - echo "Usage: `basename $0` -d scripy_path" - exit 0 - ;; - ?) #unknow option - echo "unkonw argument" - exit 1 - ;; - esac -done - -#echo "verType=${verType} interactiveFqdn=${interactiveFqdn}" - -function kill_process() { - pid=$(ps -ef | grep "$1" | grep -v "grep" | awk '{print $2}') - if [ -n "$pid" ]; then - ${csudo}kill -9 $pid || : - fi -} - -function check_file() { - #check file whether exists - if [ ! -e $1/$2 ];then - echo -e "$1/$2 \033[31mnot exists\033[0m!quit" - fin_result=$fin_result"\033[31m$temp_version\033[0m test failed!\n" - echo -e $fin_result - exit 8 - fi -} - -function get_package_name() { - var=$1 - if [[ $1 =~ 'aarch' ]];then - echo ${var::-21} - else - echo ${var::-17} - fi -} - -function check_link() { - #check Link whether exists or broken - if [ -L $1 ] ; then - if [ ! -e $1 ] ; then - echo -e "$1 \033[31Broken link\033[0m" - fin_result=$fin_result"\033[31m$temp_version\033[0m test failed!\n" - echo -e $fin_result - exit 8 - fi - else - echo -e "$1 \033[31mnot exists\033[0m!quit" - fin_result=$fin_result"\033[31m$temp_version\033[0m test failed!\n" - echo -e $fin_result - exit 8 - fi -} - -function check_main_path() { - #check install main dir and all sub dir - main_dir=("" "cfg" "bin" "connector" "driver" "examples" "include" "init.d") - for i in "${main_dir[@]}";do - check_file ${install_main_dir} $i - done - if [ "$verMode" == "cluster" ]; then - nginx_main_dir=("admin" "conf" "html" "sbin" "logs") - for i in "${nginx_main_dir[@]}";do - check_file ${nginx_dir} $i - done - fi - echo -e "Check main path:\033[32mOK\033[0m!" -} - -function check_bin_path() { - # check install bin dir and all sub dir - bin_dir=("taos" "taosd" "taosadapter" "taosdemo" "remove.sh" "tarbitrator" "set_core.sh") - for i in "${bin_dir[@]}";do - check_file ${sbin_dir} $i - done - lbin_dir=("taos" "taosd" "taosadapter" "taosdemo" "rmtaos" "tarbitrator" "set_core") - for i in "${lbin_dir[@]}";do - check_link ${bin_link_dir}/$i - done - if [ "$verMode" == "cluster" ]; then - check_file ${nginx_dir}/sbin nginx - fi - echo -e "Check bin path:\033[32mOK\033[0m!" -} - -function check_lib_path() { - # check all links - check_link ${lib_link_dir}/libtaos.so - check_link ${lib_link_dir}/libtaos.so.1 - - if [[ -d ${lib64_link_dir} ]]; then - check_link ${lib64_link_dir}/libtaos.so - check_link ${lib64_link_dir}/libtaos.so.1 - fi - echo -e "Check lib path:\033[32mOK\033[0m!" -} - -function check_header_path() { - # check all header - header_dir=("taos.h" "taosdef.h" "taoserror.h") - for i in "${header_dir[@]}";do - check_link ${inc_link_dir}/$i - done - echo -e "Check bin path:\033[32mOK\033[0m!" -} - -function check_taosadapter_config_dir() { - # check all config - check_file ${cfg_install_dir} taosadapter.toml - check_file ${cfg_install_dir} taosadapter.service - check_file ${install_main_dir}/cfg taosadapter.toml.org - echo -e "Check conf path:\033[32mOK\033[0m!" -} - -function check_config_dir() { - # check all config - check_file ${cfg_install_dir} taos.cfg - check_file ${install_main_dir}/cfg taos.cfg.org - echo -e "Check conf path:\033[32mOK\033[0m!" -} - -function check_log_path() { - # check log path - check_file ${log_dir} - echo -e "Check log path:\033[32mOK\033[0m!" -} - -function check_data_path() { - # check data path - check_file ${data_dir} - echo -e "Check data path:\033[32mOK\033[0m!" -} - -function install_TDengine() { - cd ${script_dir} - tar zxf $1 - temp_version=$(get_package_name $1) - cd $(get_package_name $1) - echo -e "\033[32muninstall TDengine && install TDengine...\033[0m" - rmtaos >/dev/null 2>&1 || echo 'taosd not installed' && echo -e '\n\n' |./install.sh >/dev/null 2>&1 - echo -e "\033[32mTDengine has been installed!\033[0m" - echo -e "\033[32mTDengine is starting...\033[0m" - kill_process taos && systemctl start taosd && sleep 10 -} - -function test_TDengine() { - check_main_path - check_bin_path - check_lib_path - check_header_path - check_config_dir - check_taosadapter_config_dir - check_log_path - check_data_path - result=`taos -s 'create database test ;create table test.tt(ts timestamp ,i int);insert into test.tt values(now,11);select * from test.tt' 2>&1 ||:` - if [[ $result =~ "Unable to establish" ]];then - echo -e "\033[31mTDengine connect failed\033[0m" - fin_result=$fin_result"\033[31m$temp_version\033[0m test failed!\n" - echo -e $fin_result - exit 8 - fi - echo -e "Check TDengine connect:\033[32mOK\033[0m!" - fin_result=$fin_result"\033[32m$temp_version\033[0m test OK!\n" -} -# ## ==============================Main program starts from here============================ -TD_package_name=`ls ${script_dir}/*server*gz |awk -F '/' '{print $NF}' ` -temp=`pwd` -for i in $TD_package_name;do - if [[ $i =~ 'enterprise' ]];then - verMode="cluster" - else - verMode="" - fi - cd $temp - install_TDengine $i - test_TDengine -done -echo "============================================================" -echo -e $fin_result +#!/bin/bash +# +# This file is used to install database on linux systems. The operating system +# is required to use systemd to manage services at boot + +set -e +#set -x + +verMode=edge +pagMode=full + +iplist="" +serverFqdn="" + +# -----------------------Variables definition--------------------- +script_dir="../release" +# Dynamic directory +data_dir="/var/lib/taos" +log_dir="/var/log/taos" + +data_link_dir="/usr/local/taos/data" +log_link_dir="/usr/local/taos/log" + +cfg_install_dir="/etc/taos" + +bin_link_dir="/usr/bin" +lib_link_dir="/usr/lib" +lib64_link_dir="/usr/lib64" +inc_link_dir="/usr/include" + +#install main path +install_main_dir="/usr/local/taos" + +# old bin dir +sbin_dir="/usr/local/taos/bin" + +temp_version="" +fin_result="" + +service_config_dir="/etc/systemd/system" +nginx_port=6060 +nginx_dir="/usr/local/nginxd" + +# Color setting +RED='\033[0;31m' +GREEN='\033[1;32m' +GREEN_DARK='\033[0;32m' +GREEN_UNDERLINE='\033[4;32m' +NC='\033[0m' + +csudo="" +if command -v sudo > /dev/null; then + csudo="sudo " +fi + +# ============================= get input parameters ================================================= + +# install.sh -v [server | client] -e [yes | no] -i [systemd | service | ...] + +# set parameters by default value +interactiveFqdn=yes # [yes | no] +verType=server # [server | client] +initType=systemd # [systemd | service | ...] + +while getopts "hv:d:" arg +do + case $arg in + d) + #echo "interactiveFqdn=$OPTARG" + script_dir=$( echo $OPTARG ) + ;; + h) + echo "Usage: `basename $0` -d scripy_path" + exit 0 + ;; + ?) #unknow option + echo "unkonw argument" + exit 1 + ;; + esac +done + +#echo "verType=${verType} interactiveFqdn=${interactiveFqdn}" + +function kill_process() { + pid=$(ps -ef | grep "$1" | grep -v "grep" | awk '{print $2}') + if [ -n "$pid" ]; then + ${csudo}kill -9 $pid || : + fi +} + +function check_file() { + #check file whether exists + if [ ! -e $1/$2 ];then + echo -e "$1/$2 \033[31mnot exists\033[0m!quit" + fin_result=$fin_result"\033[31m$temp_version\033[0m test failed!\n" + echo -e $fin_result + exit 8 + fi +} + +function get_package_name() { + var=$1 + if [[ $1 =~ 'aarch' ]];then + echo ${var::-21} + else + echo ${var::-17} + fi +} + +function check_link() { + #check Link whether exists or broken + if [ -L $1 ] ; then + if [ ! -e $1 ] ; then + echo -e "$1 \033[31Broken link\033[0m" + fin_result=$fin_result"\033[31m$temp_version\033[0m test failed!\n" + echo -e $fin_result + exit 8 + fi + else + echo -e "$1 \033[31mnot exists\033[0m!quit" + fin_result=$fin_result"\033[31m$temp_version\033[0m test failed!\n" + echo -e $fin_result + exit 8 + fi +} + +function check_main_path() { + #check install main dir and all sub dir + main_dir=("" "cfg" "bin" "connector" "driver" "examples" "include" "init.d") + for i in "${main_dir[@]}";do + check_file ${install_main_dir} $i + done + if [ "$verMode" == "cluster" ]; then + nginx_main_dir=("admin" "conf" "html" "sbin" "logs") + for i in "${nginx_main_dir[@]}";do + check_file ${nginx_dir} $i + done + fi + echo -e "Check main path:\033[32mOK\033[0m!" +} + +function check_bin_path() { + # check install bin dir and all sub dir + bin_dir=("taos" "taosd" "taosadapter" "taosdemo" "remove.sh" "tarbitrator" "set_core.sh") + for i in "${bin_dir[@]}";do + check_file ${sbin_dir} $i + done + lbin_dir=("taos" "taosd" "taosadapter" "taosdemo" "rmtaos" "tarbitrator" "set_core") + for i in "${lbin_dir[@]}";do + check_link ${bin_link_dir}/$i + done + if [ "$verMode" == "cluster" ]; then + check_file ${nginx_dir}/sbin nginx + fi + echo -e "Check bin path:\033[32mOK\033[0m!" +} + +function check_lib_path() { + # check all links + check_link ${lib_link_dir}/libtaos.so + check_link ${lib_link_dir}/libtaos.so.1 + + if [[ -d ${lib64_link_dir} ]]; then + check_link ${lib64_link_dir}/libtaos.so + check_link ${lib64_link_dir}/libtaos.so.1 + fi + echo -e "Check lib path:\033[32mOK\033[0m!" +} + +function check_header_path() { + # check all header + header_dir=("taos.h" "taosdef.h" "taoserror.h") + for i in "${header_dir[@]}";do + check_link ${inc_link_dir}/$i + done + echo -e "Check bin path:\033[32mOK\033[0m!" +} + +function check_taosadapter_config_dir() { + # check all config + check_file ${cfg_install_dir} taosadapter.toml + check_file ${cfg_install_dir} taosadapter.service + check_file ${install_main_dir}/cfg taosadapter.toml.org + echo -e "Check conf path:\033[32mOK\033[0m!" +} + +function check_config_dir() { + # check all config + check_file ${cfg_install_dir} taos.cfg + check_file ${install_main_dir}/cfg taos.cfg.org + echo -e "Check conf path:\033[32mOK\033[0m!" +} + +function check_log_path() { + # check log path + check_file ${log_dir} + echo -e "Check log path:\033[32mOK\033[0m!" +} + +function check_data_path() { + # check data path + check_file ${data_dir} + echo -e "Check data path:\033[32mOK\033[0m!" +} + +function install_TDengine() { + cd ${script_dir} + tar zxf $1 + temp_version=$(get_package_name $1) + cd $(get_package_name $1) + echo -e "\033[32muninstall TDengine && install TDengine...\033[0m" + rmtaos >/dev/null 2>&1 || echo 'taosd not installed' && echo -e '\n\n' |./install.sh >/dev/null 2>&1 + echo -e "\033[32mTDengine has been installed!\033[0m" + echo -e "\033[32mTDengine is starting...\033[0m" + kill_process taos && systemctl start taosd && sleep 10 +} + +function test_TDengine() { + check_main_path + check_bin_path + check_lib_path + check_header_path + check_config_dir + check_taosadapter_config_dir + check_log_path + check_data_path + result=`taos -s 'create database test ;create table test.tt(ts timestamp ,i int);insert into test.tt values(now,11);select * from test.tt' 2>&1 ||:` + if [[ $result =~ "Unable to establish" ]];then + echo -e "\033[31mTDengine connect failed\033[0m" + fin_result=$fin_result"\033[31m$temp_version\033[0m test failed!\n" + echo -e $fin_result + exit 8 + fi + echo -e "Check TDengine connect:\033[32mOK\033[0m!" + fin_result=$fin_result"\033[32m$temp_version\033[0m test OK!\n" +} +# ## ==============================Main program starts from here============================ +TD_package_name=`ls ${script_dir}/*server*gz |awk -F '/' '{print $NF}' ` +temp=`pwd` +for i in $TD_package_name;do + if [[ $i =~ 'enterprise' ]];then + verMode="cluster" + else + verMode="" + fi + cd $temp + install_TDengine $i + test_TDengine +done +echo "============================================================" +echo -e $fin_result diff --git a/packaging/deb/DEBIAN/control b/packaging/deb/DEBIAN/control index c3136e8f0d443f05be2905702883e065dd1b9eaa..fd3f81ba082d11f6ff3979382a63597b5806fa1f 100644 --- a/packaging/deb/DEBIAN/control +++ b/packaging/deb/DEBIAN/control @@ -1,13 +1,13 @@ -Package: tdengine -Version: 1.0.0 -Section: utils -Priority: optional -#Essential: no -#Depends: no -#Suggests: no -Architecture: amd64 -Installed-Size: 66666 -Maintainer: support@taosdata.com -Provides: taosdata -Homepage: http://taosdata.com -Description: Big Data Platform Designed and Optimized for IoT. +Package: tdengine +Version: 1.0.0 +Section: utils +Priority: optional +#Essential: no +#Depends: no +#Suggests: no +Architecture: amd64 +Installed-Size: 66666 +Maintainer: support@taosdata.com +Provides: taosdata +Homepage: http://taosdata.com +Description: Big Data Platform Designed and Optimized for IoT. diff --git a/packaging/deb/DEBIAN/postinst b/packaging/deb/DEBIAN/postinst index a6c2817082f484d1a533ff1d82a3500cba1b5c19..2638f096250b8fa52277573f14a6f0f07b335b32 100644 --- a/packaging/deb/DEBIAN/postinst +++ b/packaging/deb/DEBIAN/postinst @@ -1,13 +1,13 @@ -#!/bin/bash -#set -x -#path=`pwd` -insmetaPath="/usr/local/taos/script" - -csudo="" -if command -v sudo > /dev/null; then - csudo="sudo " -fi - -${csudo}chmod -R 744 ${insmetaPath} -cd ${insmetaPath} -${csudo}./post.sh +#!/bin/bash +#set -x +#path=`pwd` +insmetaPath="/usr/local/taos/script" + +csudo="" +if command -v sudo > /dev/null; then + csudo="sudo " +fi + +${csudo}chmod -R 744 ${insmetaPath} +cd ${insmetaPath} +${csudo}./post.sh diff --git a/packaging/deb/DEBIAN/postrm b/packaging/deb/DEBIAN/postrm index 98b0a844a8b7c1707e629a72962308e450ef510a..05a7907cf5a4a5927d09b1e964aae97a096908f6 100644 --- a/packaging/deb/DEBIAN/postrm +++ b/packaging/deb/DEBIAN/postrm @@ -1,2 +1,2 @@ -#!/bin/bash - +#!/bin/bash + diff --git a/packaging/deb/DEBIAN/preinst b/packaging/deb/DEBIAN/preinst index 061f35538be4f48d0fcfc3d53ba7b61c2505a0fd..5217a8229571bf993c6c3df8f82beb1ed67c3e96 100644 --- a/packaging/deb/DEBIAN/preinst +++ b/packaging/deb/DEBIAN/preinst @@ -1,40 +1,40 @@ -#!/bin/bash - -csudo="" -if command -v sudo > /dev/null; then - csudo="sudo " -fi - -# Stop the service if running -if pidof taosd &> /dev/null; then - if pidof systemd &> /dev/null; then - ${csudo}systemctl stop taosd || : - elif $(which service &> /dev/null); then - ${csudo}service taosd stop || : - else - pid=$(ps -ef | grep "taosd" | grep -v "grep" | awk '{print $2}') - if [ -n "$pid" ]; then - ${csudo}kill -9 $pid || : - fi - fi - echo "Stop taosd service success!" - sleep 1 -fi - -# if taos.cfg already softlink, remove it -cfg_install_dir="/etc/taos" -install_main_dir="/usr/local/taos" -if [ -f "${install_main_dir}/taos.cfg" ]; then - ${csudo}rm -f ${install_main_dir}/cfg/taos.cfg || : -fi - -if [ -f "${install_main_dir}/taosadapter.toml" ]; then - ${csudo}rm -f ${install_main_dir}/cfg/taosadapter.toml || : -fi - -if [ -f "${install_main_dir}/taosadapter.service" ]; then - ${csudo}rm -f ${install_main_dir}/cfg/taosadapter.service || : -fi - -# there can not libtaos.so*, otherwise ln -s error -${csudo}rm -f ${install_main_dir}/driver/libtaos* || : +#!/bin/bash + +csudo="" +if command -v sudo > /dev/null; then + csudo="sudo " +fi + +# Stop the service if running +if pidof taosd &> /dev/null; then + if pidof systemd &> /dev/null; then + ${csudo}systemctl stop taosd || : + elif $(which service &> /dev/null); then + ${csudo}service taosd stop || : + else + pid=$(ps -ef | grep "taosd" | grep -v "grep" | awk '{print $2}') + if [ -n "$pid" ]; then + ${csudo}kill -9 $pid || : + fi + fi + echo "Stop taosd service success!" + sleep 1 +fi + +# if taos.cfg already softlink, remove it +cfg_install_dir="/etc/taos" +install_main_dir="/usr/local/taos" +if [ -f "${install_main_dir}/taos.cfg" ]; then + ${csudo}rm -f ${install_main_dir}/cfg/taos.cfg || : +fi + +if [ -f "${install_main_dir}/taosadapter.toml" ]; then + ${csudo}rm -f ${install_main_dir}/cfg/taosadapter.toml || : +fi + +if [ -f "${install_main_dir}/taosadapter.service" ]; then + ${csudo}rm -f ${install_main_dir}/cfg/taosadapter.service || : +fi + +# there can not libtaos.so*, otherwise ln -s error +${csudo}rm -f ${install_main_dir}/driver/libtaos* || : diff --git a/packaging/deb/DEBIAN/prerm b/packaging/deb/DEBIAN/prerm index 4298b33355ede507190ca891989ba6cd1fcefbb1..c01db74701f99e52cc45b589d8fe7b07c4c8afe1 100644 --- a/packaging/deb/DEBIAN/prerm +++ b/packaging/deb/DEBIAN/prerm @@ -1,42 +1,42 @@ -#!/bin/bash - -insmetaPath="/usr/local/taos/script" - -csudo="" -if command -v sudo > /dev/null; then - csudo="sudo " -fi - -${csudo}chmod -R 744 ${insmetaPath} || : -#cd ${insmetaPath} -#${csudo}./preun.sh -if [ -f ${insmetaPath}/preun.sh ]; then - cd ${insmetaPath} - ${csudo}./preun.sh -else - bin_link_dir="/usr/bin" - lib_link_dir="/usr/lib" - inc_link_dir="/usr/include" - - data_link_dir="/usr/local/taos/data" - log_link_dir="/usr/local/taos/log" - cfg_link_dir="/usr/local/taos/cfg" - - # Remove all links - ${csudo}rm -f ${bin_link_dir}/taos || : - ${csudo}rm -f ${bin_link_dir}/taosd || : - ${csudo}rm -f ${bin_link_dir}/taosadapter || : - ${csudo}rm -f ${bin_link_dir}/taosdemo || : - ${csudo}rm -f ${cfg_link_dir}/* || : - ${csudo}rm -f ${inc_link_dir}/taos.h || : - ${csudo}rm -f ${lib_link_dir}/libtaos.* || : - - ${csudo}rm -f ${log_link_dir} || : - ${csudo}rm -f ${data_link_dir} || : - - pid=$(ps -ef | grep "taosd" | grep -v "grep" | awk '{print $2}') - if [ -n "$pid" ]; then - ${csudo}kill -9 $pid || : - fi -fi - +#!/bin/bash + +insmetaPath="/usr/local/taos/script" + +csudo="" +if command -v sudo > /dev/null; then + csudo="sudo " +fi + +${csudo}chmod -R 744 ${insmetaPath} || : +#cd ${insmetaPath} +#${csudo}./preun.sh +if [ -f ${insmetaPath}/preun.sh ]; then + cd ${insmetaPath} + ${csudo}./preun.sh +else + bin_link_dir="/usr/bin" + lib_link_dir="/usr/lib" + inc_link_dir="/usr/include" + + data_link_dir="/usr/local/taos/data" + log_link_dir="/usr/local/taos/log" + cfg_link_dir="/usr/local/taos/cfg" + + # Remove all links + ${csudo}rm -f ${bin_link_dir}/taos || : + ${csudo}rm -f ${bin_link_dir}/taosd || : + ${csudo}rm -f ${bin_link_dir}/taosadapter || : + ${csudo}rm -f ${bin_link_dir}/taosdemo || : + ${csudo}rm -f ${cfg_link_dir}/* || : + ${csudo}rm -f ${inc_link_dir}/taos.h || : + ${csudo}rm -f ${lib_link_dir}/libtaos.* || : + + ${csudo}rm -f ${log_link_dir} || : + ${csudo}rm -f ${data_link_dir} || : + + pid=$(ps -ef | grep "taosd" | grep -v "grep" | awk '{print $2}') + if [ -n "$pid" ]; then + ${csudo}kill -9 $pid || : + fi +fi + diff --git a/packaging/deb/makedeb.sh b/packaging/deb/makedeb.sh old mode 100644 new mode 100755 index e43c1a35698f24e776ae87d0e7ff0737889caeb4..5a14aea4ec14d64913845cf7efb0615abd7b6f93 --- a/packaging/deb/makedeb.sh +++ b/packaging/deb/makedeb.sh @@ -1,147 +1,147 @@ -#!/bin/bash -# -# Generate deb package for ubuntu -set -e -# set -x - -#curr_dir=$(pwd) -compile_dir=$1 -output_dir=$2 -tdengine_ver=$3 -cpuType=$4 -osType=$5 -verMode=$6 -verType=$7 - -script_dir="$(dirname $(readlink -f $0))" -top_dir="$(readlink -f ${script_dir}/../..)" -pkg_dir="${top_dir}/debworkroom" - -#echo "curr_dir: ${curr_dir}" -#echo "top_dir: ${top_dir}" -#echo "script_dir: ${script_dir}" -echo "compile_dir: ${compile_dir}" -echo "pkg_dir: ${pkg_dir}" - -if [ -d ${pkg_dir} ]; then - rm -rf ${pkg_dir} -fi -mkdir -p ${pkg_dir} -cd ${pkg_dir} - -libfile="libtaos.so.${tdengine_ver}" - -# create install dir -install_home_path="/usr/local/taos" -mkdir -p ${pkg_dir}${install_home_path} -mkdir -p ${pkg_dir}${install_home_path}/bin -mkdir -p ${pkg_dir}${install_home_path}/cfg -#mkdir -p ${pkg_dir}${install_home_path}/connector -mkdir -p ${pkg_dir}${install_home_path}/driver -mkdir -p ${pkg_dir}${install_home_path}/examples -mkdir -p ${pkg_dir}${install_home_path}/include -#mkdir -p ${pkg_dir}${install_home_path}/init.d -mkdir -p ${pkg_dir}${install_home_path}/script - -cp ${compile_dir}/../packaging/cfg/taos.cfg ${pkg_dir}${install_home_path}/cfg -if [ -f "${compile_dir}/test/cfg/taosadapter.toml" ]; then - cp ${compile_dir}/test/cfg/taosadapter.toml ${pkg_dir}${install_home_path}/cfg || : -fi -if [ -f "${compile_dir}/test/cfg/taosadapter.service" ]; then - cp ${compile_dir}/test/cfg/taosadapter.service ${pkg_dir}${install_home_path}/cfg || : -fi - -#cp ${compile_dir}/../packaging/deb/taosd ${pkg_dir}${install_home_path}/init.d -cp ${compile_dir}/../packaging/tools/post.sh ${pkg_dir}${install_home_path}/script -cp ${compile_dir}/../packaging/tools/preun.sh ${pkg_dir}${install_home_path}/script -cp ${compile_dir}/../packaging/tools/startPre.sh ${pkg_dir}${install_home_path}/bin -cp ${compile_dir}/../packaging/tools/set_core.sh ${pkg_dir}${install_home_path}/bin -cp ${compile_dir}/../packaging/tools/taosd-dump-cfg.gdb ${pkg_dir}${install_home_path}/bin - -cp ${compile_dir}/build/bin/taosd ${pkg_dir}${install_home_path}/bin -#cp ${compile_dir}/build/bin/taosBenchmark ${pkg_dir}${install_home_path}/bin - -if [ -f "${compile_dir}/build/bin/taosadapter" ]; then - cp ${compile_dir}/build/bin/taosadapter ${pkg_dir}${install_home_path}/bin ||: -fi - -cp ${compile_dir}/build/bin/taos ${pkg_dir}${install_home_path}/bin -cp ${compile_dir}/build/lib/${libfile} ${pkg_dir}${install_home_path}/driver -cp ${compile_dir}/../src/inc/taos.h ${pkg_dir}${install_home_path}/include -cp ${compile_dir}/../src/inc/taosdef.h ${pkg_dir}${install_home_path}/include -cp ${compile_dir}/../src/inc/taoserror.h ${pkg_dir}${install_home_path}/include -cp -r ${top_dir}/examples/* ${pkg_dir}${install_home_path}/examples -#cp -r ${top_dir}/src/connector/python ${pkg_dir}${install_home_path}/connector -#cp -r ${top_dir}/src/connector/go ${pkg_dir}${install_home_path}/connector -#cp -r ${top_dir}/src/connector/nodejs ${pkg_dir}${install_home_path}/connector -#cp ${compile_dir}/build/lib/taos-jdbcdriver*.* ${pkg_dir}${install_home_path}/connector ||: - -install_user_local_path="/usr/local" - -if [ -f ${compile_dir}/build/bin/jemalloc-config ]; then - mkdir -p ${pkg_dir}${install_user_local_path}/{bin,lib,lib/pkgconfig,include/jemalloc,share/doc/jemalloc,share/man/man3} - cp ${compile_dir}/build/bin/jemalloc-config ${pkg_dir}${install_user_local_path}/bin/ - if [ -f ${compile_dir}/build/bin/jemalloc.sh ]; then - cp ${compile_dir}/build/bin/jemalloc.sh ${pkg_dir}${install_user_local_path}/bin/ - fi - if [ -f ${compile_dir}/build/bin/jeprof ]; then - cp ${compile_dir}/build/bin/jeprof ${pkg_dir}${install_user_local_path}/bin/ - fi - if [ -f ${compile_dir}/build/include/jemalloc/jemalloc.h ]; then - cp ${compile_dir}/build/include/jemalloc/jemalloc.h ${pkg_dir}${install_user_local_path}/include/jemalloc/ - fi - if [ -f ${compile_dir}/build/lib/libjemalloc.so.2 ]; then - cp ${compile_dir}/build/lib/libjemalloc.so.2 ${pkg_dir}${install_user_local_path}/lib/ - ln -sf libjemalloc.so.2 ${pkg_dir}${install_user_local_path}/lib/libjemalloc.so - fi - if [ -f ${compile_dir}/build/lib/libjemalloc.a ]; then - cp ${compile_dir}/build/lib/libjemalloc.a ${pkg_dir}${install_user_local_path}/lib/ - fi - if [ -f ${compile_dir}/build/lib/libjemalloc_pic.a ]; then - cp ${compile_dir}/build/lib/libjemalloc_pic.a ${pkg_dir}${install_user_local_path}/lib/ - fi - if [ -f ${compile_dir}/build/lib/pkgconfig/jemalloc.pc ]; then - cp ${compile_dir}/build/lib/pkgconfig/jemalloc.pc ${pkg_dir}${install_user_local_path}/lib/pkgconfig/ - fi - if [ -f ${compile_dir}/build/share/doc/jemalloc/jemalloc.html ]; then - cp ${compile_dir}/build/share/doc/jemalloc/jemalloc.html ${pkg_dir}${install_user_local_path}/share/doc/jemalloc/ - fi - if [ -f ${compile_dir}/build/share/man/man3/jemalloc.3 ]; then - cp ${compile_dir}/build/share/man/man3/jemalloc.3 ${pkg_dir}${install_user_local_path}/share/man/man3/ - fi -fi - -cp -r ${compile_dir}/../packaging/deb/DEBIAN ${pkg_dir}/ -chmod 755 ${pkg_dir}/DEBIAN/* - -# modify version of control -debver="Version: "$tdengine_ver -sed -i "2c$debver" ${pkg_dir}/DEBIAN/control - -#get taos version, then set deb name -if [ "$verMode" == "cluster" ]; then - debname="TDengine-server-"${tdengine_ver}-${osType}-${cpuType} -elif [ "$verMode" == "edge" ]; then - debname="TDengine-server"-${tdengine_ver}-${osType}-${cpuType} -else - echo "unknow verMode, nor cluster or edge" - exit 1 -fi - -if [ "$verType" == "beta" ]; then - debname="TDengine-server-"${tdengine_ver}-${verType}-${osType}-${cpuType}".deb" -elif [ "$verType" == "stable" ]; then - debname=${debname}".deb" -else - echo "unknow verType, nor stabel or beta" - exit 1 -fi - -# make deb package -dpkg -b ${pkg_dir} $debname -echo "make deb package success!" - -cp ${pkg_dir}/*.deb ${output_dir} - -# clean temp dir -rm -rf ${pkg_dir} +#!/bin/bash +# +# Generate deb package for ubuntu +set -e +# set -x + +#curr_dir=$(pwd) +compile_dir=$1 +output_dir=$2 +tdengine_ver=$3 +cpuType=$4 +osType=$5 +verMode=$6 +verType=$7 + +script_dir="$(dirname $(readlink -f $0))" +top_dir="$(readlink -f ${script_dir}/../..)" +pkg_dir="${top_dir}/debworkroom" + +#echo "curr_dir: ${curr_dir}" +#echo "top_dir: ${top_dir}" +#echo "script_dir: ${script_dir}" +echo "compile_dir: ${compile_dir}" +echo "pkg_dir: ${pkg_dir}" + +if [ -d ${pkg_dir} ]; then + rm -rf ${pkg_dir} +fi +mkdir -p ${pkg_dir} +cd ${pkg_dir} + +libfile="libtaos.so.${tdengine_ver}" + +# create install dir +install_home_path="/usr/local/taos" +mkdir -p ${pkg_dir}${install_home_path} +mkdir -p ${pkg_dir}${install_home_path}/bin +mkdir -p ${pkg_dir}${install_home_path}/cfg +#mkdir -p ${pkg_dir}${install_home_path}/connector +mkdir -p ${pkg_dir}${install_home_path}/driver +mkdir -p ${pkg_dir}${install_home_path}/examples +mkdir -p ${pkg_dir}${install_home_path}/include +#mkdir -p ${pkg_dir}${install_home_path}/init.d +mkdir -p ${pkg_dir}${install_home_path}/script + +cp ${compile_dir}/../packaging/cfg/taos.cfg ${pkg_dir}${install_home_path}/cfg +if [ -f "${compile_dir}/test/cfg/taosadapter.toml" ]; then + cp ${compile_dir}/test/cfg/taosadapter.toml ${pkg_dir}${install_home_path}/cfg || : +fi +if [ -f "${compile_dir}/test/cfg/taosadapter.service" ]; then + cp ${compile_dir}/test/cfg/taosadapter.service ${pkg_dir}${install_home_path}/cfg || : +fi + +#cp ${compile_dir}/../packaging/deb/taosd ${pkg_dir}${install_home_path}/init.d +cp ${compile_dir}/../packaging/tools/post.sh ${pkg_dir}${install_home_path}/script +cp ${compile_dir}/../packaging/tools/preun.sh ${pkg_dir}${install_home_path}/script +cp ${compile_dir}/../packaging/tools/startPre.sh ${pkg_dir}${install_home_path}/bin +cp ${compile_dir}/../packaging/tools/set_core.sh ${pkg_dir}${install_home_path}/bin +cp ${compile_dir}/../packaging/tools/taosd-dump-cfg.gdb ${pkg_dir}${install_home_path}/bin + +cp ${compile_dir}/build/bin/taosd ${pkg_dir}${install_home_path}/bin +#cp ${compile_dir}/build/bin/taosBenchmark ${pkg_dir}${install_home_path}/bin + +if [ -f "${compile_dir}/build/bin/taosadapter" ]; then + cp ${compile_dir}/build/bin/taosadapter ${pkg_dir}${install_home_path}/bin ||: +fi + +cp ${compile_dir}/build/bin/taos ${pkg_dir}${install_home_path}/bin +cp ${compile_dir}/build/lib/${libfile} ${pkg_dir}${install_home_path}/driver +cp ${compile_dir}/../include/client/taos.h ${pkg_dir}${install_home_path}/include +cp ${compile_dir}/../include/common/taosdef.h ${pkg_dir}${install_home_path}/include +cp ${compile_dir}/../include/util/taoserror.h ${pkg_dir}${install_home_path}/include +cp -r ${top_dir}/examples/* ${pkg_dir}${install_home_path}/examples +#cp -r ${top_dir}/src/connector/python ${pkg_dir}${install_home_path}/connector +#cp -r ${top_dir}/src/connector/go ${pkg_dir}${install_home_path}/connector +#cp -r ${top_dir}/src/connector/nodejs ${pkg_dir}${install_home_path}/connector +#cp ${compile_dir}/build/lib/taos-jdbcdriver*.* ${pkg_dir}${install_home_path}/connector ||: + +install_user_local_path="/usr/local" + +if [ -f ${compile_dir}/build/bin/jemalloc-config ]; then + mkdir -p ${pkg_dir}${install_user_local_path}/{bin,lib,lib/pkgconfig,include/jemalloc,share/doc/jemalloc,share/man/man3} + cp ${compile_dir}/build/bin/jemalloc-config ${pkg_dir}${install_user_local_path}/bin/ + if [ -f ${compile_dir}/build/bin/jemalloc.sh ]; then + cp ${compile_dir}/build/bin/jemalloc.sh ${pkg_dir}${install_user_local_path}/bin/ + fi + if [ -f ${compile_dir}/build/bin/jeprof ]; then + cp ${compile_dir}/build/bin/jeprof ${pkg_dir}${install_user_local_path}/bin/ + fi + if [ -f ${compile_dir}/build/include/jemalloc/jemalloc.h ]; then + cp ${compile_dir}/build/include/jemalloc/jemalloc.h ${pkg_dir}${install_user_local_path}/include/jemalloc/ + fi + if [ -f ${compile_dir}/build/lib/libjemalloc.so.2 ]; then + cp ${compile_dir}/build/lib/libjemalloc.so.2 ${pkg_dir}${install_user_local_path}/lib/ + ln -sf libjemalloc.so.2 ${pkg_dir}${install_user_local_path}/lib/libjemalloc.so + fi + if [ -f ${compile_dir}/build/lib/libjemalloc.a ]; then + cp ${compile_dir}/build/lib/libjemalloc.a ${pkg_dir}${install_user_local_path}/lib/ + fi + if [ -f ${compile_dir}/build/lib/libjemalloc_pic.a ]; then + cp ${compile_dir}/build/lib/libjemalloc_pic.a ${pkg_dir}${install_user_local_path}/lib/ + fi + if [ -f ${compile_dir}/build/lib/pkgconfig/jemalloc.pc ]; then + cp ${compile_dir}/build/lib/pkgconfig/jemalloc.pc ${pkg_dir}${install_user_local_path}/lib/pkgconfig/ + fi + if [ -f ${compile_dir}/build/share/doc/jemalloc/jemalloc.html ]; then + cp ${compile_dir}/build/share/doc/jemalloc/jemalloc.html ${pkg_dir}${install_user_local_path}/share/doc/jemalloc/ + fi + if [ -f ${compile_dir}/build/share/man/man3/jemalloc.3 ]; then + cp ${compile_dir}/build/share/man/man3/jemalloc.3 ${pkg_dir}${install_user_local_path}/share/man/man3/ + fi +fi + +cp -r ${compile_dir}/../packaging/deb/DEBIAN ${pkg_dir}/ +chmod 755 ${pkg_dir}/DEBIAN/* + +# modify version of control +debver="Version: "$tdengine_ver +sed -i "2c$debver" ${pkg_dir}/DEBIAN/control + +#get taos version, then set deb name +if [ "$verMode" == "cluster" ]; then + debname="TDengine-server-"${tdengine_ver}-${osType}-${cpuType} +elif [ "$verMode" == "edge" ]; then + debname="TDengine-server"-${tdengine_ver}-${osType}-${cpuType} +else + echo "unknow verMode, nor cluster or edge" + exit 1 +fi + +if [ "$verType" == "beta" ]; then + debname="TDengine-server-"${tdengine_ver}-${verType}-${osType}-${cpuType}".deb" +elif [ "$verType" == "stable" ]; then + debname=${debname}".deb" +else + echo "unknow verType, nor stabel or beta" + exit 1 +fi + +# make deb package +dpkg -b ${pkg_dir} $debname +echo "make deb package success!" + +cp ${pkg_dir}/*.deb ${output_dir} + +# clean temp dir +rm -rf ${pkg_dir} diff --git a/packaging/deb/taosd b/packaging/deb/taosd index 4dd176d0975f552ce44fd989d4c49bcee2ba895b..fe356ca6565c916086273e5669918b04065964cd 100644 --- a/packaging/deb/taosd +++ b/packaging/deb/taosd @@ -1,95 +1,95 @@ -#!/bin/bash -# -# Modified from original source: Elastic Search -# https://github.com/elasticsearch/elasticsearch -# Thank you to the Elastic Search authors -# -# chkconfig: 2345 99 01 -# -### BEGIN INIT INFO -# Provides: TDengine -# Required-Start: $local_fs $network $syslog -# Required-Stop: $local_fs $network $syslog -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: Starts TDengine taosd -# Description: Starts TDengine taosd, a time-series database engine -### END INIT INFO - -set -e - -PATH="/bin:/usr/bin:/sbin:/usr/sbin" -NAME="TDengine" -USER="root" -GROUP="root" -DAEMON="/usr/local/taos/bin/taosd" -DAEMON_OPTS="" - -HTTPD_NAME="taosadapter" -DAEMON_HTTPD_NAME=$HTTPD_NAME -DAEMON_HTTPD="/usr/local/taos/bin/$HTTPD_NAME" - -PID_FILE="/var/run/$NAME.pid" -APPARGS="" - -# Maximum number of open files -MAX_OPEN_FILES=65535 - -. /lib/lsb/init-functions - -case "$1" in - start) - - log_action_begin_msg "Starting TDengine..." - $DAEMON_HTTPD & - if start-stop-daemon --test --start --chuid "$USER:$GROUP" --background --make-pidfile --pidfile "$PID_FILE" --exec "$DAEMON" -- $APPARGS &> /dev/null; then - - touch "$PID_FILE" && chown "$USER":"$GROUP" "$PID_FILE" - - if [ -n "$MAX_OPEN_FILES" ]; then - ulimit -n $MAX_OPEN_FILES - fi - - start-stop-daemon --start --chuid "$USER:$GROUP" --background --make-pidfile --pidfile "$PID_FILE" --exec "$DAEMON" -- $APPARGS - - log_end_msg $? - fi - ;; - - stop) - log_action_begin_msg "Stopping TDengine..." - pkill -9 $DAEMON_HTTPD_NAME - set +e - if [ -f "$PID_FILE" ]; then - start-stop-daemon --stop --pidfile "$PID_FILE" --user "$USER" --retry=TERM/120/KILL/5 > /dev/null - if [ $? -eq 1 ]; then - log_action_cont_msg "TSD is not running but pid file exists, cleaning up" - elif [ $? -eq 3 ]; then - PID="`cat $PID_FILE`" - log_failure_msg "Failed to stop TDengine (pid $PID)" - exit 1 - fi - rm -f "$PID_FILE" - else - log_action_cont_msg "TDengine was not running" - fi - log_action_end_msg 0 - set -e - ;; - - restart|force-reload) - if [ -f "$PID_FILE" ]; then - $0 stop - sleep 1 - fi - $0 start - ;; - status) - status_of_proc -p "$PID_FILE" "$DAEMON" "$NAME" - ;; - *) - exit 1 - ;; -esac - -exit 0 +#!/bin/bash +# +# Modified from original source: Elastic Search +# https://github.com/elasticsearch/elasticsearch +# Thank you to the Elastic Search authors +# +# chkconfig: 2345 99 01 +# +### BEGIN INIT INFO +# Provides: TDengine +# Required-Start: $local_fs $network $syslog +# Required-Stop: $local_fs $network $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Starts TDengine taosd +# Description: Starts TDengine taosd, a time-series database engine +### END INIT INFO + +set -e + +PATH="/bin:/usr/bin:/sbin:/usr/sbin" +NAME="TDengine" +USER="root" +GROUP="root" +DAEMON="/usr/local/taos/bin/taosd" +DAEMON_OPTS="" + +HTTPD_NAME="taosadapter" +DAEMON_HTTPD_NAME=$HTTPD_NAME +DAEMON_HTTPD="/usr/local/taos/bin/$HTTPD_NAME" + +PID_FILE="/var/run/$NAME.pid" +APPARGS="" + +# Maximum number of open files +MAX_OPEN_FILES=65535 + +. /lib/lsb/init-functions + +case "$1" in + start) + + log_action_begin_msg "Starting TDengine..." + $DAEMON_HTTPD & + if start-stop-daemon --test --start --chuid "$USER:$GROUP" --background --make-pidfile --pidfile "$PID_FILE" --exec "$DAEMON" -- $APPARGS &> /dev/null; then + + touch "$PID_FILE" && chown "$USER":"$GROUP" "$PID_FILE" + + if [ -n "$MAX_OPEN_FILES" ]; then + ulimit -n $MAX_OPEN_FILES + fi + + start-stop-daemon --start --chuid "$USER:$GROUP" --background --make-pidfile --pidfile "$PID_FILE" --exec "$DAEMON" -- $APPARGS + + log_end_msg $? + fi + ;; + + stop) + log_action_begin_msg "Stopping TDengine..." + pkill -9 $DAEMON_HTTPD_NAME + set +e + if [ -f "$PID_FILE" ]; then + start-stop-daemon --stop --pidfile "$PID_FILE" --user "$USER" --retry=TERM/120/KILL/5 > /dev/null + if [ $? -eq 1 ]; then + log_action_cont_msg "TSD is not running but pid file exists, cleaning up" + elif [ $? -eq 3 ]; then + PID="`cat $PID_FILE`" + log_failure_msg "Failed to stop TDengine (pid $PID)" + exit 1 + fi + rm -f "$PID_FILE" + else + log_action_cont_msg "TDengine was not running" + fi + log_action_end_msg 0 + set -e + ;; + + restart|force-reload) + if [ -f "$PID_FILE" ]; then + $0 stop + sleep 1 + fi + $0 start + ;; + status) + status_of_proc -p "$PID_FILE" "$DAEMON" "$NAME" + ;; + *) + exit 1 + ;; +esac + +exit 0 diff --git a/packaging/deb/tarbitratord b/packaging/deb/tarbitratord index 8ece6cef062d082614827ab50627327aafc79e86..3f97c3c0c2143817ad4ecbb13fabd8c09fb44c69 100644 --- a/packaging/deb/tarbitratord +++ b/packaging/deb/tarbitratord @@ -1,88 +1,88 @@ -#!/bin/bash -# -# Modified from original source: Elastic Search -# https://github.com/elasticsearch/elasticsearch -# Thank you to the Elastic Search authors -# -# chkconfig: 2345 99 01 -# -### BEGIN INIT INFO -# Provides: taoscluster -# Required-Start: $local_fs $network $syslog -# Required-Stop: $local_fs $network $syslog -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: Starts taoscluster tarbitrator -# Description: Starts taoscluster tarbitrator, a arbitrator -### END INIT INFO - -set -e - -PATH="/bin:/usr/bin:/sbin:/usr/sbin" -NAME="taoscluster" -USER="root" -GROUP="root" -DAEMON="/usr/local/taos/bin/tarbitrator" -DAEMON_OPTS="" -PID_FILE="/var/run/$NAME.pid" -APPARGS="" - -# Maximum number of open files -MAX_OPEN_FILES=65535 - -. /lib/lsb/init-functions - -case "$1" in - start) - - log_action_begin_msg "Starting tarbitrator..." - if start-stop-daemon --test --start --chuid "$USER:$GROUP" --background --make-pidfile --pidfile "$PID_FILE" --exec "$DAEMON" -- $APPARGS &> /dev/null; then - - touch "$PID_FILE" && chown "$USER":"$GROUP" "$PID_FILE" - - if [ -n "$MAX_OPEN_FILES" ]; then - ulimit -n $MAX_OPEN_FILES - fi - - start-stop-daemon --start --chuid "$USER:$GROUP" --background --make-pidfile --pidfile "$PID_FILE" --exec "$DAEMON" -- $APPARGS - - log_end_msg $? - fi - ;; - - stop) - log_action_begin_msg "Stopping tarbitrator..." - set +e - if [ -f "$PID_FILE" ]; then - start-stop-daemon --stop --pidfile "$PID_FILE" --user "$USER" --retry=TERM/120/KILL/5 > /dev/null - if [ $? -eq 1 ]; then - log_action_cont_msg "TSD is not running but pid file exists, cleaning up" - elif [ $? -eq 3 ]; then - PID="`cat $PID_FILE`" - log_failure_msg "Failed to stop tarbitrator (pid $PID)" - exit 1 - fi - rm -f "$PID_FILE" - else - log_action_cont_msg "tarbitrator was not running" - fi - log_action_end_msg 0 - set -e - ;; - - restart|force-reload) - if [ -f "$PID_FILE" ]; then - $0 stop - sleep 1 - fi - $0 start - ;; - status) - status_of_proc -p "$PID_FILE" "$DAEMON" "$NAME" - ;; - *) - exit 1 - ;; -esac - -exit 0 +#!/bin/bash +# +# Modified from original source: Elastic Search +# https://github.com/elasticsearch/elasticsearch +# Thank you to the Elastic Search authors +# +# chkconfig: 2345 99 01 +# +### BEGIN INIT INFO +# Provides: taoscluster +# Required-Start: $local_fs $network $syslog +# Required-Stop: $local_fs $network $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Starts taoscluster tarbitrator +# Description: Starts taoscluster tarbitrator, a arbitrator +### END INIT INFO + +set -e + +PATH="/bin:/usr/bin:/sbin:/usr/sbin" +NAME="taoscluster" +USER="root" +GROUP="root" +DAEMON="/usr/local/taos/bin/tarbitrator" +DAEMON_OPTS="" +PID_FILE="/var/run/$NAME.pid" +APPARGS="" + +# Maximum number of open files +MAX_OPEN_FILES=65535 + +. /lib/lsb/init-functions + +case "$1" in + start) + + log_action_begin_msg "Starting tarbitrator..." + if start-stop-daemon --test --start --chuid "$USER:$GROUP" --background --make-pidfile --pidfile "$PID_FILE" --exec "$DAEMON" -- $APPARGS &> /dev/null; then + + touch "$PID_FILE" && chown "$USER":"$GROUP" "$PID_FILE" + + if [ -n "$MAX_OPEN_FILES" ]; then + ulimit -n $MAX_OPEN_FILES + fi + + start-stop-daemon --start --chuid "$USER:$GROUP" --background --make-pidfile --pidfile "$PID_FILE" --exec "$DAEMON" -- $APPARGS + + log_end_msg $? + fi + ;; + + stop) + log_action_begin_msg "Stopping tarbitrator..." + set +e + if [ -f "$PID_FILE" ]; then + start-stop-daemon --stop --pidfile "$PID_FILE" --user "$USER" --retry=TERM/120/KILL/5 > /dev/null + if [ $? -eq 1 ]; then + log_action_cont_msg "TSD is not running but pid file exists, cleaning up" + elif [ $? -eq 3 ]; then + PID="`cat $PID_FILE`" + log_failure_msg "Failed to stop tarbitrator (pid $PID)" + exit 1 + fi + rm -f "$PID_FILE" + else + log_action_cont_msg "tarbitrator was not running" + fi + log_action_end_msg 0 + set -e + ;; + + restart|force-reload) + if [ -f "$PID_FILE" ]; then + $0 stop + sleep 1 + fi + $0 start + ;; + status) + status_of_proc -p "$PID_FILE" "$DAEMON" "$NAME" + ;; + *) + exit 1 + ;; +esac + +exit 0 diff --git a/packaging/docker/Dockerfile b/packaging/docker/Dockerfile index 58ba6e3f42af9f11ccb0e71333979826c20cfe85..26349e257676d99d0ea81e03509c8b09c20a2248 100644 --- a/packaging/docker/Dockerfile +++ b/packaging/docker/Dockerfile @@ -1,32 +1,32 @@ -FROM ubuntu:18.04 - -WORKDIR /root - -ARG pkgFile -ARG dirName -ARG cpuType -RUN echo ${pkgFile} && echo ${dirName} - -COPY ${pkgFile} /root/ -RUN tar -zxf ${pkgFile} -WORKDIR /root/ -RUN cd /root/${dirName}/ && /bin/bash install.sh -e no && cd /root -RUN rm /root/${pkgFile} -RUN rm -rf /root/${dirName} - -ENV DEBIAN_FRONTEND=noninteractive -RUN apt-get clean && apt-get update && apt-get install -y locales tzdata netcat && locale-gen en_US.UTF-8 -ENV LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/lib" \ - LC_CTYPE=en_US.UTF-8 \ - LANG=en_US.UTF-8 \ - LC_ALL=en_US.UTF-8 - -COPY ./bin/* /usr/bin/ - -ENV TINI_VERSION v0.19.0 -RUN bash -c 'echo -e "Downloading tini-${cpuType} ..."' -ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-${cpuType} /tini -RUN chmod +x /tini -ENTRYPOINT ["/tini", "--", "/usr/bin/entrypoint.sh"] -CMD ["taosd"] -VOLUME [ "/var/lib/taos", "/var/log/taos", "/corefile" ] +FROM ubuntu:18.04 + +WORKDIR /root + +ARG pkgFile +ARG dirName +ARG cpuType +RUN echo ${pkgFile} && echo ${dirName} + +COPY ${pkgFile} /root/ +RUN tar -zxf ${pkgFile} +WORKDIR /root/ +RUN cd /root/${dirName}/ && /bin/bash install.sh -e no && cd /root +RUN rm /root/${pkgFile} +RUN rm -rf /root/${dirName} + +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get clean && apt-get update && apt-get install -y locales tzdata netcat && locale-gen en_US.UTF-8 +ENV LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/lib" \ + LC_CTYPE=en_US.UTF-8 \ + LANG=en_US.UTF-8 \ + LC_ALL=en_US.UTF-8 + +COPY ./bin/* /usr/bin/ + +ENV TINI_VERSION v0.19.0 +RUN bash -c 'echo -e "Downloading tini-${cpuType} ..."' +ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-${cpuType} /tini +RUN chmod +x /tini +ENTRYPOINT ["/tini", "--", "/usr/bin/entrypoint.sh"] +CMD ["taosd"] +VOLUME [ "/var/lib/taos", "/var/log/taos", "/corefile" ] diff --git a/packaging/docker/README.md b/packaging/docker/README.md index 2722dbfef5c45343d5fdd8f777762e63ae31bec7..e41182f471050af6b4d47b696eb237e319b2dd80 100644 --- a/packaging/docker/README.md +++ b/packaging/docker/README.md @@ -1,664 +1,664 @@ -# TDengine Docker Image Quick Reference - -## What is TDengine? - -TDengine is an open-sourced big data platform under [GNU AGPL v3.0](http://www.gnu.org/licenses/agpl-3.0.html), designed and optimized for the Internet of Things (IoT), Connected Cars, Industrial IoT, and IT Infrastructure and Application Monitoring. Besides the 10x faster time-series database, it provides caching, stream computing, message queuing and other functionalities to reduce the complexity and cost of development and operation. - -- **10x Faster on Insert/Query Speeds**: Through the innovative design on storage, on a single-core machine, over 20K requests can be processed, millions of data points can be ingested, and over 10 million data points can be retrieved in a second. It is 10 times faster than other databases. - -- **1/5 Hardware/Cloud Service Costs**: Compared with typical big data solutions, less than 1/5 of computing resources are required. Via column-based storage and tuned compression algorithms for different data types, less than 1/10 of storage space is needed. - -- **Full Stack for Time-Series Data**: By integrating a database with message queuing, caching, and stream computing features together, it is no longer necessary to integrate Kafka/Redis/HBase/Spark or other software. It makes the system architecture much simpler and more robust. - -- **Powerful Data Analysis**: Whether it is 10 years or one minute ago, data can be queried just by specifying the time range. Data can be aggregated over time, multiple time streams or both. Ad Hoc queries or analyses can be executed via TDengine shell, Python, R or Matlab. - -- **Seamless Integration with Other Tools**: Telegraf, Grafana, Matlab, R, and other tools can be integrated with TDengine without a line of code. MQTT, OPC, Hadoop, Spark, and many others will be integrated soon. - -- **Zero Management, No Learning Curve**: It takes only seconds to download, install, and run it successfully; there are no other dependencies. Automatic partitioning on tables or DBs. Standard SQL is used, with C/C++, Python, JDBC, Go and RESTful connectors. - -## How to use this image - -### Start a TDengine instance with RESTful API exposed - -Simply, you can use `docker run` to start a TDengine instance and connect it with restful connectors(eg. [JDBC-RESTful](https://www.taosdata.com/cn/documentation/connector/java)). - -```bash -docker run -d --name tdengine -p 6041:6041 tdengine/tdengine -``` - -This command starts a docker container by name `tdengine` with TDengine server running, and maps the container's HTTP port 6041 to the host's port 6041. If you have `curl` in your host, you can list the databases by the command: - -```bash -curl -u root:taosdata -d "show databases" localhost:6041/rest/sql -``` - -You can execute the `taos` shell command in the container: - -```bash -$ docker exec -it tdengine taos - -Welcome to the TDengine shell from Linux, Client Version:2.4.0.0 -Copyright (c) 2020 by TAOS Data, Inc. All rights reserved. - -taos> show databases; - name | created_time | ntables | vgroups | replica | quorum | days | keep | cache(MB) | blocks | minrows | maxrows | wallevel | fsync | comp | cachelast | precision | update | status | -==================================================================================================================================================================================================================================================================================== - log | 2022-01-17 13:57:22.270 | 10 | 1 | 1 | 1 | 10 | 30 | 1 | 3 | 100 | 4096 | 1 | 3000 | 2 | 0 | us | 0 | ready | -Query OK, 1 row(s) in set (0.002843s) -``` - -Since TDengine use container hostname to establish connections, it's a bit more complex to use taos shell and native connectors(such as JDBC-JNI) with TDengine container instance. This is the recommended way to expose ports and use TDengine with docker in simple cases. If you want to use taos shell or taosc/connectors smoothly outside the `tdengine` container, see next use cases that match you need. - -### Start with host network - -```bash -docker run -d --name tdengine --network host tdengine/tdengine -``` - -Starts container with `host` network will use host's hostname as fqdn instead of container id. It's much like starting natively with `systemd` in host. After installing the client, you can use `taos` shell as normal in host path. - -```bash -$ taos - -Welcome to the TDengine shell from Linux, Client Version:2.4.0.0 -Copyright (c) 2020 by TAOS Data, Inc. All rights reserved. - -taos> show dnodes; - id | end_point | vnodes | cores | status | role | create_time | offline reason | -====================================================================================================================================== - 1 | host:6030 | 1 | 8 | ready | any | 2022-01-17 22:10:32.619 | | -Query OK, 1 row(s) in set (0.003233s) -``` - -### Start with exposed ports and specified hostname - -Set the fqdn explicitly will help you to use in other environment or applications. We provide environment variable `TAOS_FQDN` or `fqdn` config option to explicitly set the hostname used by TDengine container instance(s). - -Use `TAOS_FQDN` variable within `docker run` command: - -```bash -docker run -d \ - --name tdengine \ - -e TAOS_FQDN=tdengine \ - -p 6030-6049:6030-6049 \ - -p 6030-6049:6030-6049/udp \ - tdengine/tdengine -``` - -This command starts a docker container with TDengine server running and maps the container's TCP ports from 6030 to 6049 to the host's ports from 6030 to 6049 with TCP protocol and UDP ports range 6030-6039 to the host's UDP ports 6030-6039. If the host is already running TDengine server and occupying the same port(s), you need to map the container's port to a different unused port segment. (Please see TDengine 2.0 Port Description for details). In order to support TDengine clients accessing TDengine server services, both TCP and UDP ports need to be exposed by default(unless `rpcForceTcp` is set to `1`). - -If you want to use taos shell or native connectors([JDBC-JNI](https://www.taosdata.com/cn/documentation/connector/java), or [driver-go](https://github.com/taosdata/driver-go)), you need to make sure the `TAOS_FQDN` is resolvable at `/etc/hosts` or with custom DNS service. - -If you set the `TAOS_FQDN` to host's hostname, it will works as using `hosts` network like previous use case. Otherwise, like in `-e TAOS_FQDN=tdengine`, you can add the hostname record `tdengine` into `/etc/hosts` (use `127.0.0.1` here in host path, if use TDengine client/application in other hosts, you should set the right ip to the host eg. `192.168.10.1`(check the real ip in host with `hostname -i` or `ip route list default`) to make the TDengine endpoint resolvable): - -```bash -echo 127.0.0.1 tdengine |sudo tee -a /etc/hosts -``` - -Then you can use `taos` with the host `tdengine`: - -```bash -taos -h tdengine -``` - -Or develop/test applications with native connectors. As in python: - -```python -import taos; -conn = taos.connect(host = "tdengine") -res = conn.query("show databases") -for row in res.fetch_all_into_dict(): - print(row) -``` - -See the results: - -```bash -Python 3.8.10 (default, Nov 26 2021, 20:14:08) -[GCC 9.3.0] on linux -Type "help", "copyright", "credits" or "license" for more information. ->>> import taos; ->>> conn = taos.connect(host = "tdengine") ->>> res = conn.query("show databases") ->>> for row in res.fetch_all_into_dict(): -... print(row) -... -{'name': 'log', 'created_time': datetime.datetime(2022, 1, 17, 22, 56, 2, 490000), 'ntables': 11, 'vgroups': 1, 'replica': 1, 'quorum': 1, 'days': 10, 'keep': '30', 'cache(MB)': 1, 'blocks': 3, 'minrows': 100, 'maxrows': 4096, 'wallevel': 1, 'fsync': 3000, 'comp': 2, 'cachelast': 0, 'precision': 'us', 'update': 0, 'status': 'ready'} -``` - -### Start with specific network - -Alternatively, you can use TDengine natively by using specific network. - -First, create network for TDengine server and client/application. - -```bash -docker network create td-net -``` - -Start TDengine instance with service name as fqdn (explicitly set with `TAOS_FQDN`): - -```bash -docker run -d --name tdengine --network td-net \ - -e TAOS_FQDN=tdengine \ - tdengine/tdengine -``` - -Start TDengine client in another container with the specific network: - -```bash -docker run --rm -it --network td-net -e TAOS_FIRST_EP=tdengine tdengine/tdengine taos -# or -docker run --rm -it --network td-net -e tdengine/tdengine taos -h tdengine -``` - -When you build your application with docker, you should add the TDengine client in the dockerfile, as based on `ubuntu:20.04` image, install the client like this: - -```dockerfile -FROM ubuntu:20.04 -RUN apt-get update && apt-get install -y wget -ENV TDENGINE_VERSION=2.4.0.0 -RUN wget -c https://www.taosdata.com/assets-download/TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz \ - && tar xvf TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz \ - && cd TDengine-client-${TDENGINE_VERSION} \ - && ./install_client.sh \ - && cd ../ \ - && rm -rf TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz TDengine-client-${TDENGINE_VERSION} -## add your application next, eg. go, build it in builder stage, copy the binary to the runtime -#COPY --from=builder /path/to/build/app /usr/bin/ -#CMD ["app"] -``` - -Here is an Go example app: - - - - -```go -/* - * In this test program, we'll create a database and insert 4 records then select out. - */ -package main - -import ( - "database/sql" - "flag" - "fmt" - "time" - - _ "github.com/taosdata/driver-go/v2/taosSql" -) - -type config struct { - hostName string - serverPort string - user string - password string -} - -var configPara config -var taosDriverName = "taosSql" -var url string - -func init() { - flag.StringVar(&configPara.hostName, "h", "", "The host to connect to TDengine server.") - flag.StringVar(&configPara.serverPort, "p", "", "The TCP/IP port number to use for the connection to TDengine server.") - flag.StringVar(&configPara.user, "u", "root", "The TDengine user name to use when connecting to the server.") - flag.StringVar(&configPara.password, "P", "taosdata", "The password to use when connecting to the server.") - flag.Parse() -} - -func printAllArgs() { - fmt.Printf("============= args parse result: =============\n") - fmt.Printf("hostName: %v\n", configPara.hostName) - fmt.Printf("serverPort: %v\n", configPara.serverPort) - fmt.Printf("usr: %v\n", configPara.user) - fmt.Printf("password: %v\n", configPara.password) - fmt.Printf("================================================\n") -} - -func main() { - printAllArgs() - - url = "root:taosdata@/tcp(" + configPara.hostName + ":" + configPara.serverPort + ")/" - - taos, err := sql.Open(taosDriverName, url) - checkErr(err, "open database error") - defer taos.Close() - - taos.Exec("create database if not exists test") - taos.Exec("use test") - taos.Exec("create table if not exists tb1 (ts timestamp, a int)") - _, err = taos.Exec("insert into tb1 values(now, 0)(now+1s,1)(now+2s,2)(now+3s,3)") - checkErr(err, "failed to insert") - rows, err := taos.Query("select * from tb1") - checkErr(err, "failed to select") - - defer rows.Close() - for rows.Next() { - var r struct { - ts time.Time - a int - } - err := rows.Scan(&r.ts, &r.a) - if err != nil { - fmt.Println("scan error:\n", err) - return - } - fmt.Println(r.ts, r.a) - } -} - -func checkErr(err error, prompt string) { - if err != nil { - fmt.Println("ERROR: %s\n", prompt) - panic(err) - } -} -``` - - - - -Full version of dockerfile could be: - -```dockerfile -FROM golang:1.17.6-buster as builder -ENV TDENGINE_VERSION=2.4.0.0 -RUN wget -c https://www.taosdata.com/assets-download/TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz \ - && tar xvf TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz \ - && cd TDengine-client-${TDENGINE_VERSION} \ - && ./install_client.sh \ - && cd ../ \ - && rm -rf TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz TDengine-client-${TDENGINE_VERSION} -WORKDIR /usr/src/app/ -ENV GOPROXY="https://goproxy.io,direct" -COPY ./main.go ./go.mod ./go.sum /usr/src/app/ -RUN go env && go mod tidy && go build - -FROM ubuntu:20.04 -RUN apt-get update && apt-get install -y wget -ENV TDENGINE_VERSION=2.4.0.0 -RUN wget -c https://www.taosdata.com/assets-download/TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz \ - && tar xvf TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz \ - && cd TDengine-client-${TDENGINE_VERSION} \ - && ./install_client.sh \ - && cd ../ \ - && rm -rf TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz TDengine-client-${TDENGINE_VERSION} - -## add your application next, eg. go, build it in builder stage, copy the binary to the runtime -COPY --from=builder /usr/src/app/app /usr/bin/ -CMD ["app"] -``` - -Suppose you have `main.go`, `go.mod` `go.sum`, `app.dockerfile`, build the app and run it with network `td-net`: - -```bash -$ docker build -t app -f app.dockerfile -$ docker run --rm --network td-net app -h tdengine -p 6030 -============= args parse result: ============= -hostName: tdengine -serverPort: 6030 -usr: root -password: taosdata -================================================ -2022-01-17 15:56:55.48 +0000 UTC 0 -2022-01-17 15:56:56.48 +0000 UTC 1 -2022-01-17 15:56:57.48 +0000 UTC 2 -2022-01-17 15:56:58.48 +0000 UTC 3 -2022-01-17 15:58:01.842 +0000 UTC 0 -2022-01-17 15:58:02.842 +0000 UTC 1 -2022-01-17 15:58:03.842 +0000 UTC 2 -2022-01-17 15:58:04.842 +0000 UTC 3 -2022-01-18 01:43:48.029 +0000 UTC 0 -2022-01-18 01:43:49.029 +0000 UTC 1 -2022-01-18 01:43:50.029 +0000 UTC 2 -2022-01-18 01:43:51.029 +0000 UTC 3 -``` - -Now you must be much familiar with developing and testing with TDengine, let's see some more complex cases. - -### Start with docker-compose with multiple nodes(instances) - -Start a 2-replicas-2-mnodes-2-dnodes-1-arbitrator TDengine cluster with `docker-compose` is quite simple. Save the file as `docker-compose.yml`: - -```yaml -version: "3" -services: - arbitrator: - image: tdengine/tdengine:$VERSION - command: tarbitrator - td-1: - image: tdengine/tdengine:$VERSION - environment: - TAOS_FQDN: "td-1" - TAOS_FIRST_EP: "td-1" - TAOS_NUM_OF_MNODES: "2" - TAOS_REPLICA: "2" - TAOS_ARBITRATOR: arbitrator:6042 - volumes: - - taosdata-td1:/var/lib/taos/ - - taoslog-td1:/var/log/taos/ - td-2: - image: tdengine/tdengine:$VERSION - environment: - TAOS_FQDN: "td-2" - TAOS_FIRST_EP: "td-1" - TAOS_NUM_OF_MNODES: "2" - TAOS_REPLICA: "2" - TAOS_ARBITRATOR: arbitrator:6042 - volumes: - - taosdata-td2:/var/lib/taos/ - - taoslog-td2:/var/log/taos/ -volumes: - taosdata-td1: - taoslog-td1: - taosdata-td2: - taoslog-td2: -``` - -You may notice that: - -- We use `VERSION` environment variable to set `tdengine` image tag version once. -- **`TAOS_FIRST_EP`** **MUST** be set to join the newly created instances into an existing TDengine cluster. If you want more instances, use `TAOS_SECOND_EP` in case of HA(High Availability) concerns. -- `TAOS_NUM_OF_MNODES` is for setting number of mnodes for the cluster. -- `TAOS_REPLICA` set the default database replicas, `2` means there're one master and one slave copy of data. The `replica` option should be `1 <= replica <= 3`, and not greater than dnodes number. -- `TAOS_ARBITRATOR` set the arbitrator entrypoint of the cluster for failover/election stuff. It's better to use arbitrator in a two nodes cluster. -- The way to start an arbitrator service is as easy as abc: just add command name `tarbitrator`(which is the binary name of arbitrator daemon) in docker-compose service option: `command: tarbitrator`, and everything is ok now. - -Now run `docker-compose up -d` with version specified: - -```bash -$ VERSION=2.4.0.0 docker-compose up -d -Creating network "test_default" with the default driver -Creating volume "test_taosdata-td1" with default driver -Creating volume "test_taoslog-td1" with default driver -Creating volume "test_taosdata-td2" with default driver -Creating volume "test_taoslog-td2" with default driver -Creating test_td-1_1 ... done -Creating test_arbitrator_1 ... done -Creating test_td-2_1 ... done -``` - -Check the status: - -```bash -$ docker-compose ps - Name Command State Ports ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -test_arbitrator_1 /usr/bin/entrypoint.sh tar ... Up 6030/tcp, 6031/tcp, 6032/tcp, 6033/tcp, 6034/tcp, 6035/tcp, 6036/tcp, 6037/tcp, 6038/tcp, 6039/tcp, 6040/tcp, 6041/tcp, 6042/tcp -test_td-1_1 /usr/bin/entrypoint.sh taosd Up 6030/tcp, 6031/tcp, 6032/tcp, 6033/tcp, 6034/tcp, 6035/tcp, 6036/tcp, 6037/tcp, 6038/tcp, 6039/tcp, 6040/tcp, 6041/tcp, 6042/tcp -test_td-2_1 /usr/bin/entrypoint.sh taosd Up 6030/tcp, 6031/tcp, 6032/tcp, 6033/tcp, 6034/tcp, 6035/tcp, 6036/tcp, 6037/tcp, 6038/tcp, 6039/tcp, 6040/tcp, 6041/tcp, 6042/tcp -``` - -Check dnodes with taos shell: - -```bash -$ docker-compose exec td-1 taos -s "show dnodes" - -Welcome to the TDengine shell from Linux, Client Version:2.4.0.0 -Copyright (c) 2020 by TAOS Data, Inc. All rights reserved. - -taos> show dnodes - id | end_point | vnodes | cores | status | role | create_time | offline reason | -====================================================================================================================================== - 1 | td-1:6030 | 1 | 8 | ready | any | 2022-01-18 02:47:42.871 | | - 2 | td-2:6030 | 0 | 8 | ready | any | 2022-01-18 02:47:43.518 | | - 0 | arbitrator:6042 | 0 | 0 | ready | arb | 2022-01-18 02:47:43.633 | - | -Query OK, 3 row(s) in set (0.000811s) -``` - -### Start a TDengine cluster with scaled taosadapter service - -In previous use case, you could see the way to start other services built with TDengine(`taosd` as the default command). There's another important service you should know: - -> **taosAdapter** is a TDengine’s companion tool and is a bridge/adapter between TDengine cluster and application. It provides an easy-to-use and efficient way to ingest data from data collections agents(like Telegraf, StatsD, CollectD) directly. It also provides InfluxDB/OpenTSDB compatible data ingestion interface to allow InfluxDB/OpenTSDB applications to immigrate to TDengine seamlessly. - -`taosadapter` is running inside `tdengine` image by default, you can disable it by `TAOS_DISABLE_ADAPTER=true`. Running `taosadapter` in a separate container is like how `arbitrator` does: - -```yaml -services: - # ... - adapter: - image: tdengine/tdengine:$VERSION - command: taosadapter -``` - -`taosadapter` could be scaled with docker-compose, so that you can manage the `taosadapter` nodes easily. Here is an example shows 4-`taosadapter` instances in a TDengine cluster(much like previous use cases): - -```yaml -version: "3" - -networks: - inter: - api: - -services: - arbitrator: - image: tdengine/tdengine:$VERSION - command: tarbitrator - networks: - - inter - td-1: - image: tdengine/tdengine:$VERSION - networks: - - inter - environment: - TAOS_FQDN: "td-1" - TAOS_FIRST_EP: "td-1" - TAOS_NUM_OF_MNODES: "2" - TAOS_REPLICA: "2" - TAOS_ARBITRATOR: arbitrator:6042 - volumes: - - taosdata-td1:/var/lib/taos/ - - taoslog-td1:/var/log/taos/ - td-2: - image: tdengine/tdengine:$VERSION - networks: - - inter - environment: - TAOS_FQDN: "td-2" - TAOS_FIRST_EP: "td-1" - TAOS_NUM_OF_MNODES: "2" - TAOS_REPLICA: "2" - TAOS_ARBITRATOR: arbitrator:6042 - volumes: - - taosdata-td2:/var/lib/taos/ - - taoslog-td2:/var/log/taos/ - adapter: - image: tdengine/tdengine:$VERSION - command: taosadapter - networks: - - inter - environment: - TAOS_FIRST_EP: "td-1" - TAOS_SECOND_EP: "td-2" - deploy: - replicas: 4 - nginx: - image: nginx - depends_on: - - adapter - networks: - - inter - - api - ports: - - 6041:6041 - - 6044:6044/udp - command: [ - "sh", - "-c", - "while true; - do curl -s http://adapter:6041/-/ping >/dev/null && break; - done; - printf 'server{listen 6041;location /{proxy_pass http://adapter:6041;}}' - > /etc/nginx/conf.d/rest.conf; - printf 'stream{server{listen 6044 udp;proxy_pass adapter:6044;}}' - >> /etc/nginx/nginx.conf;cat /etc/nginx/nginx.conf; - nginx -g 'daemon off;'", - ] -volumes: - taosdata-td1: - taoslog-td1: - taosdata-td2: - taoslog-td2: -``` - -Start the cluster: - -```bash -$ VERSION=2.4.0.0 docker-compose up -d -Creating network "docker_inter" with the default driver -Creating network "docker_api" with the default driver -Creating volume "docker_taosdata-td1" with default driver -Creating volume "docker_taoslog-td1" with default driver -Creating volume "docker_taosdata-td2" with default driver -Creating volume "docker_taoslog-td2" with default driver -Creating docker_td-2_1 ... done -Creating docker_arbitrator_1 ... done -Creating docker_td-1_1 ... done -Creating docker_adapter_1 ... done -Creating docker_adapter_2 ... done -Creating docker_adapter_3 ... done -``` - -It will start a TDengine cluster with two dnodes and four taosadapter instances, expose ports 6041/tcp and 6044/udp to host. - -`6041` is the RESTful API endpoint port, you can verify that the RESTful interface taosAdapter provides working using the `curl` command. - -```bash -$ curl -H 'Authorization: Basic cm9vdDp0YW9zZGF0YQ==' -d 'show databases;' 127.0.0.1:6041/rest/sql -{"status":"succ","head":["name","created_time","ntables","vgroups","replica","quorum","days","keep","cache(MB)","blocks","minrows","maxrows","wallevel","fsync","comp","cachelast","precision","update","status"],"column_meta":[["name",8,32],["created_time",9,8],["ntables",4,4],["vgroups",4,4],["replica",3,2],["quorum",3,2],["days",3,2],["keep",8,24],["cache(MB)",4,4],["blocks",4,4],["minrows",4,4],["maxrows",4,4],["wallevel",2,1],["fsync",4,4],["comp",2,1],["cachelast",2,1],["precision",8,3],["update",2,1],["status",8,10]],"data":[["log","2022-01-18 04:37:42.902",16,1,1,1,10,"30",1,3,100,4096,1,3000,2,0,"us",0,"ready"]],"rows":1} -``` - -If you run curl in batch(here we use [hyperfine](https://github.com/sharkdp/hyperfine) - a command-line benchmarking tool), the requests are balanced into 4 adapter instances. - -```bash -hyperfine -m10 'curl -u root:taosdata localhost:6041/rest/sql -d "describe log.log"' -``` - -View the logs with `docker-compose logs`: - -```bash -$ docker-compose logs adapter -# some logs skipped -adapter_2 | 01/18 04:57:44.616529 00000039 TAOS_ADAPTER info "| 200 | 162.185µs | 172.21.0.9 | POST | /rest/sql " model=web sessionID=18 -adapter_1 | 01/18 04:57:44.627695 00000039 TAOS_ADAPTER info "| 200 | 145.485µs | 172.21.0.9 | POST | /rest/sql " model=web sessionID=17 -adapter_3 | 01/18 04:57:44.639165 00000040 TAOS_ADAPTER info "| 200 | 146.913µs | 172.21.0.9 | POST | /rest/sql " sessionID=17 model=web -adapter_4 | 01/18 04:57:44.650829 00000039 TAOS_ADAPTER info "| 200 | 153.201µs | 172.21.0.9 | POST | /rest/sql " sessionID=17 model=web -adapter_2 | 01/18 04:57:44.662422 00000039 TAOS_ADAPTER info "| 200 | 211.393µs | 172.21.0.9 | POST | /rest/sql " model=web sessionID=19 -adapter_1 | 01/18 04:57:44.673426 00000039 TAOS_ADAPTER info "| 200 | 154.714µs | 172.21.0.9 | POST | /rest/sql " model=web sessionID=18 -adapter_3 | 01/18 04:57:44.684788 00000040 TAOS_ADAPTER info "| 200 | 131.876µs | 172.21.0.9 | POST | /rest/sql " model=web sessionID=18 -adapter_4 | 01/18 04:57:44.696261 00000039 TAOS_ADAPTER info "| 200 | 162.173µs | 172.21.0.9 | POST | /rest/sql " model=web sessionID=18 -adapter_2 | 01/18 04:57:44.707414 00000039 TAOS_ADAPTER info "| 200 | 164.419µs | 172.21.0.9 | POST | /rest/sql " model=web sessionID=20 -adapter_1 | 01/18 04:57:44.720842 00000039 TAOS_ADAPTER info "| 200 | 179.374µs | 172.21.0.9 | POST | /rest/sql " model=web sessionID=19 -adapter_3 | 01/18 04:57:44.732184 00000040 TAOS_ADAPTER info "| 200 | 141.174µs | 172.21.0.9 | POST | /rest/sql " sessionID=19 model=web -adapter_4 | 01/18 04:57:44.744024 00000039 TAOS_ADAPTER info "| 200 | 159.774µs | 172.21.0.9 | POST | /rest/sql " model=web sessionID=19 -adapter_2 | 01/18 04:57:44.773732 00000039 TAOS_ADAPTER info "| 200 | 178.993µs | 172.21.0.9 | POST | /rest/sql " model=web sessionID=21 -adapter_1 | 01/18 04:57:44.796518 00000039 TAOS_ADAPTER info "| 200 | 238.24µs | 172.21.0.9 | POST | /rest/sql " model=web sessionID=20 -adapter_3 | 01/18 04:57:44.810744 00000040 TAOS_ADAPTER info "| 200 | 176.133µs | 172.21.0.9 | POST | /rest/sql " model=web sessionID=20 -adapter_4 | 01/18 04:57:44.826395 00000039 TAOS_ADAPTER info "| 200 | 149.215µs | 172.21.0.9 | POST | /rest/sql " model=web sessionID=20 -``` - -`6044/udp` is the [StatsD](https://github.com/statsd/statsd)-compatible port, you can verify this feature with `nc` command(usually provided by `netcat` package). - -```bash -echo "foo:1|c" | nc -u -w0 127.0.0.1 6044 -``` - -Check the result in `taos` shell with `docker-compose exec`: - -```bash -$ dc exec td-1 taos - -Welcome to the TDengine shell from Linux, Client Version:2.4.0.0 -Copyright (c) 2020 by TAOS Data, Inc. All rights reserved. - -taos> show databases; - name | created_time | ntables | vgroups | replica | quorum | days | keep | cache(MB) | blocks | minrows | maxrows | wallevel | fsync | comp | cachelast | precision | update | status | -==================================================================================================================================================================================================================================================================================== - log | 2022-01-18 04:37:42.902 | 17 | 1 | 1 | 1 | 10 | 30 | 1 | 3 | 100 | 4096 | 1 | 3000 | 2 | 0 | us | 0 | ready | - statsd | 2022-01-18 04:45:02.563 | 1 | 1 | 2 | 1 | 10 | 3650 | 16 | 6 | 100 | 4096 | 1 | 3000 | 2 | 0 | ns | 2 | ready | -Query OK, 2 row(s) in set (0.001838s) - -taos> select * from statsd.foo; - ts | value | metric_type | -======================================================================================= - 2022-01-18 04:45:02.563422822 | 1 | counter | -Query OK, 1 row(s) in set (0.003854s) -``` - -Use `docker-compose up -d adapter=1 to reduce the instances to 1 - -### Deploy TDengine cluster in Docker Swarm with `docker-compose.yml` - -If you use docker swarm mode, it will schedule arbitrator/taosd/taosadapter services into different hosts automatically. If you've no experience with k8s/kubernetes, this is the most convenient way to scale out the TDengine cluster with multiple hosts/servers. - -Use the `docker-compose.yml` file in previous use case, and deploy with `docker stack` or `docker deploy`: - -```bash -$ VERSION=2.4.0 docker stack deploy -c docker-compose.yml taos -Creating network taos_inter -Creating network taos_api -Creating service taos_arbitrator -Creating service taos_td-1 -Creating service taos_td-2 -Creating service taos_adapter -Creating service taos_nginx -``` - -Now you've created a TDengine cluster with multiple host servers. - -Use `docker service` or `docker stack` to manage the cluster: - - - -```bash -$ docker stack ps taos -ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS -79ni8temw59n taos_nginx.1 nginx:latest TM1701 Running Running about a minute ago -3e94u72msiyg taos_adapter.1 tdengine/tdengine:2.4.0 TM1702 Running Running 56 seconds ago -100amjkwzsc6 taos_td-2.1 tdengine/tdengine:2.4.0 TM1703 Running Running about a minute ago -pkjehr2vvaaa taos_td-1.1 tdengine/tdengine:2.4.0 TM1704 Running Running 2 minutes ago -tpzvgpsr1qkt taos_arbitrator.1 tdengine/tdengine:2.4.0 TM1705 Running Running 2 minutes ago -rvss3g5yg6fa taos_adapter.2 tdengine/tdengine:2.4.0 TM1706 Running Running 56 seconds ago -i2augxamfllf taos_adapter.3 tdengine/tdengine:2.4.0 TM1707 Running Running 56 seconds ago -lmjyhzccpvpg taos_adapter.4 tdengine/tdengine:2.4.0 TM1708 Running Running 56 seconds ago -$ docker service ls -ID NAME MODE REPLICAS IMAGE PORTS -561t4lu6nfw6 taos_adapter replicated 4/4 tdengine/tdengine:2.4.0 -3hk5ct3q90sm taos_arbitrator replicated 1/1 tdengine/tdengine:2.4.0 -d8qr52envqzu taos_nginx replicated 1/1 nginx:latest *:6041->6041/tcp, *:6044->6044/udp -2isssfvjk747 taos_td-1 replicated 1/1 tdengine/tdengine:2.4.0 -9pzw7u02ichv taos_td-2 replicated 1/1 tdengine/tdengine:2.4.0 -``` - - - -It shows that there are two dnodes, one arbitrator, four taosadapter and one nginx reverse-forward service in this cluster. - -You can scale down the taosadapter replicas to `1` by `docker service`: - -```bash -$ docker service scale taos_adapter=1 -taos_adapter scaled to 1 -overall progress: 1 out of 1 tasks -1/1: running [==================================================>] -verify: Service converged - -$ docker service ls -f name=taos_adapter -ID NAME MODE REPLICAS IMAGE PORTS -561t4lu6nfw6 taos_adapter replicated 1/1 tdengine/tdengine:2.4.0 -``` - -Now it remains only 1 taosadapter instance in the cluster. - -When you want to remove the cluster, just type: - -```bash -docker stack rm taos -``` - -### Environment Variables - -When you start `tdengine` image, you can adjust the configuration of TDengine by passing environment variables on the `docker run` command line or in the docker compose file. You can use all of the environment variables that passed to taosd or taosadapter. +# TDengine Docker Image Quick Reference + +## What is TDengine? + +TDengine is an open-sourced big data platform under [GNU AGPL v3.0](http://www.gnu.org/licenses/agpl-3.0.html), designed and optimized for the Internet of Things (IoT), Connected Cars, Industrial IoT, and IT Infrastructure and Application Monitoring. Besides the 10x faster time-series database, it provides caching, stream computing, message queuing and other functionalities to reduce the complexity and cost of development and operation. + +- **10x Faster on Insert/Query Speeds**: Through the innovative design on storage, on a single-core machine, over 20K requests can be processed, millions of data points can be ingested, and over 10 million data points can be retrieved in a second. It is 10 times faster than other databases. + +- **1/5 Hardware/Cloud Service Costs**: Compared with typical big data solutions, less than 1/5 of computing resources are required. Via column-based storage and tuned compression algorithms for different data types, less than 1/10 of storage space is needed. + +- **Full Stack for Time-Series Data**: By integrating a database with message queuing, caching, and stream computing features together, it is no longer necessary to integrate Kafka/Redis/HBase/Spark or other software. It makes the system architecture much simpler and more robust. + +- **Powerful Data Analysis**: Whether it is 10 years or one minute ago, data can be queried just by specifying the time range. Data can be aggregated over time, multiple time streams or both. Ad Hoc queries or analyses can be executed via TDengine shell, Python, R or Matlab. + +- **Seamless Integration with Other Tools**: Telegraf, Grafana, Matlab, R, and other tools can be integrated with TDengine without a line of code. MQTT, OPC, Hadoop, Spark, and many others will be integrated soon. + +- **Zero Management, No Learning Curve**: It takes only seconds to download, install, and run it successfully; there are no other dependencies. Automatic partitioning on tables or DBs. Standard SQL is used, with C/C++, Python, JDBC, Go and RESTful connectors. + +## How to use this image + +### Start a TDengine instance with RESTful API exposed + +Simply, you can use `docker run` to start a TDengine instance and connect it with restful connectors(eg. [JDBC-RESTful](https://www.taosdata.com/cn/documentation/connector/java)). + +```bash +docker run -d --name tdengine -p 6041:6041 tdengine/tdengine +``` + +This command starts a docker container by name `tdengine` with TDengine server running, and maps the container's HTTP port 6041 to the host's port 6041. If you have `curl` in your host, you can list the databases by the command: + +```bash +curl -u root:taosdata -d "show databases" localhost:6041/rest/sql +``` + +You can execute the `taos` shell command in the container: + +```bash +$ docker exec -it tdengine taos + +Welcome to the TDengine shell from Linux, Client Version:2.4.0.0 +Copyright (c) 2020 by TAOS Data, Inc. All rights reserved. + +taos> show databases; + name | created_time | ntables | vgroups | replica | quorum | days | keep | cache(MB) | blocks | minrows | maxrows | wallevel | fsync | comp | cachelast | precision | update | status | +==================================================================================================================================================================================================================================================================================== + log | 2022-01-17 13:57:22.270 | 10 | 1 | 1 | 1 | 10 | 30 | 1 | 3 | 100 | 4096 | 1 | 3000 | 2 | 0 | us | 0 | ready | +Query OK, 1 row(s) in set (0.002843s) +``` + +Since TDengine use container hostname to establish connections, it's a bit more complex to use taos shell and native connectors(such as JDBC-JNI) with TDengine container instance. This is the recommended way to expose ports and use TDengine with docker in simple cases. If you want to use taos shell or taosc/connectors smoothly outside the `tdengine` container, see next use cases that match you need. + +### Start with host network + +```bash +docker run -d --name tdengine --network host tdengine/tdengine +``` + +Starts container with `host` network will use host's hostname as fqdn instead of container id. It's much like starting natively with `systemd` in host. After installing the client, you can use `taos` shell as normal in host path. + +```bash +$ taos + +Welcome to the TDengine shell from Linux, Client Version:2.4.0.0 +Copyright (c) 2020 by TAOS Data, Inc. All rights reserved. + +taos> show dnodes; + id | end_point | vnodes | cores | status | role | create_time | offline reason | +====================================================================================================================================== + 1 | host:6030 | 1 | 8 | ready | any | 2022-01-17 22:10:32.619 | | +Query OK, 1 row(s) in set (0.003233s) +``` + +### Start with exposed ports and specified hostname + +Set the fqdn explicitly will help you to use in other environment or applications. We provide environment variable `TAOS_FQDN` or `fqdn` config option to explicitly set the hostname used by TDengine container instance(s). + +Use `TAOS_FQDN` variable within `docker run` command: + +```bash +docker run -d \ + --name tdengine \ + -e TAOS_FQDN=tdengine \ + -p 6030-6049:6030-6049 \ + -p 6030-6049:6030-6049/udp \ + tdengine/tdengine +``` + +This command starts a docker container with TDengine server running and maps the container's TCP ports from 6030 to 6049 to the host's ports from 6030 to 6049 with TCP protocol and UDP ports range 6030-6039 to the host's UDP ports 6030-6039. If the host is already running TDengine server and occupying the same port(s), you need to map the container's port to a different unused port segment. (Please see TDengine 2.0 Port Description for details). In order to support TDengine clients accessing TDengine server services, both TCP and UDP ports need to be exposed by default(unless `rpcForceTcp` is set to `1`). + +If you want to use taos shell or native connectors([JDBC-JNI](https://www.taosdata.com/cn/documentation/connector/java), or [driver-go](https://github.com/taosdata/driver-go)), you need to make sure the `TAOS_FQDN` is resolvable at `/etc/hosts` or with custom DNS service. + +If you set the `TAOS_FQDN` to host's hostname, it will works as using `hosts` network like previous use case. Otherwise, like in `-e TAOS_FQDN=tdengine`, you can add the hostname record `tdengine` into `/etc/hosts` (use `127.0.0.1` here in host path, if use TDengine client/application in other hosts, you should set the right ip to the host eg. `192.168.10.1`(check the real ip in host with `hostname -i` or `ip route list default`) to make the TDengine endpoint resolvable): + +```bash +echo 127.0.0.1 tdengine |sudo tee -a /etc/hosts +``` + +Then you can use `taos` with the host `tdengine`: + +```bash +taos -h tdengine +``` + +Or develop/test applications with native connectors. As in python: + +```python +import taos; +conn = taos.connect(host = "tdengine") +res = conn.query("show databases") +for row in res.fetch_all_into_dict(): + print(row) +``` + +See the results: + +```bash +Python 3.8.10 (default, Nov 26 2021, 20:14:08) +[GCC 9.3.0] on linux +Type "help", "copyright", "credits" or "license" for more information. +>>> import taos; +>>> conn = taos.connect(host = "tdengine") +>>> res = conn.query("show databases") +>>> for row in res.fetch_all_into_dict(): +... print(row) +... +{'name': 'log', 'created_time': datetime.datetime(2022, 1, 17, 22, 56, 2, 490000), 'ntables': 11, 'vgroups': 1, 'replica': 1, 'quorum': 1, 'days': 10, 'keep': '30', 'cache(MB)': 1, 'blocks': 3, 'minrows': 100, 'maxrows': 4096, 'wallevel': 1, 'fsync': 3000, 'comp': 2, 'cachelast': 0, 'precision': 'us', 'update': 0, 'status': 'ready'} +``` + +### Start with specific network + +Alternatively, you can use TDengine natively by using specific network. + +First, create network for TDengine server and client/application. + +```bash +docker network create td-net +``` + +Start TDengine instance with service name as fqdn (explicitly set with `TAOS_FQDN`): + +```bash +docker run -d --name tdengine --network td-net \ + -e TAOS_FQDN=tdengine \ + tdengine/tdengine +``` + +Start TDengine client in another container with the specific network: + +```bash +docker run --rm -it --network td-net -e TAOS_FIRST_EP=tdengine tdengine/tdengine taos +# or +docker run --rm -it --network td-net -e tdengine/tdengine taos -h tdengine +``` + +When you build your application with docker, you should add the TDengine client in the dockerfile, as based on `ubuntu:20.04` image, install the client like this: + +```dockerfile +FROM ubuntu:20.04 +RUN apt-get update && apt-get install -y wget +ENV TDENGINE_VERSION=2.4.0.0 +RUN wget -c https://www.taosdata.com/assets-download/TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz \ + && tar xvf TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz \ + && cd TDengine-client-${TDENGINE_VERSION} \ + && ./install_client.sh \ + && cd ../ \ + && rm -rf TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz TDengine-client-${TDENGINE_VERSION} +## add your application next, eg. go, build it in builder stage, copy the binary to the runtime +#COPY --from=builder /path/to/build/app /usr/bin/ +#CMD ["app"] +``` + +Here is an Go example app: + + + + +```go +/* + * In this test program, we'll create a database and insert 4 records then select out. + */ +package main + +import ( + "database/sql" + "flag" + "fmt" + "time" + + _ "github.com/taosdata/driver-go/v2/taosSql" +) + +type config struct { + hostName string + serverPort string + user string + password string +} + +var configPara config +var taosDriverName = "taosSql" +var url string + +func init() { + flag.StringVar(&configPara.hostName, "h", "", "The host to connect to TDengine server.") + flag.StringVar(&configPara.serverPort, "p", "", "The TCP/IP port number to use for the connection to TDengine server.") + flag.StringVar(&configPara.user, "u", "root", "The TDengine user name to use when connecting to the server.") + flag.StringVar(&configPara.password, "P", "taosdata", "The password to use when connecting to the server.") + flag.Parse() +} + +func printAllArgs() { + fmt.Printf("============= args parse result: =============\n") + fmt.Printf("hostName: %v\n", configPara.hostName) + fmt.Printf("serverPort: %v\n", configPara.serverPort) + fmt.Printf("usr: %v\n", configPara.user) + fmt.Printf("password: %v\n", configPara.password) + fmt.Printf("================================================\n") +} + +func main() { + printAllArgs() + + url = "root:taosdata@/tcp(" + configPara.hostName + ":" + configPara.serverPort + ")/" + + taos, err := sql.Open(taosDriverName, url) + checkErr(err, "open database error") + defer taos.Close() + + taos.Exec("create database if not exists test") + taos.Exec("use test") + taos.Exec("create table if not exists tb1 (ts timestamp, a int)") + _, err = taos.Exec("insert into tb1 values(now, 0)(now+1s,1)(now+2s,2)(now+3s,3)") + checkErr(err, "failed to insert") + rows, err := taos.Query("select * from tb1") + checkErr(err, "failed to select") + + defer rows.Close() + for rows.Next() { + var r struct { + ts time.Time + a int + } + err := rows.Scan(&r.ts, &r.a) + if err != nil { + fmt.Println("scan error:\n", err) + return + } + fmt.Println(r.ts, r.a) + } +} + +func checkErr(err error, prompt string) { + if err != nil { + fmt.Println("ERROR: %s\n", prompt) + panic(err) + } +} +``` + + + + +Full version of dockerfile could be: + +```dockerfile +FROM golang:1.17.6-buster as builder +ENV TDENGINE_VERSION=2.4.0.0 +RUN wget -c https://www.taosdata.com/assets-download/TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz \ + && tar xvf TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz \ + && cd TDengine-client-${TDENGINE_VERSION} \ + && ./install_client.sh \ + && cd ../ \ + && rm -rf TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz TDengine-client-${TDENGINE_VERSION} +WORKDIR /usr/src/app/ +ENV GOPROXY="https://goproxy.io,direct" +COPY ./main.go ./go.mod ./go.sum /usr/src/app/ +RUN go env && go mod tidy && go build + +FROM ubuntu:20.04 +RUN apt-get update && apt-get install -y wget +ENV TDENGINE_VERSION=2.4.0.0 +RUN wget -c https://www.taosdata.com/assets-download/TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz \ + && tar xvf TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz \ + && cd TDengine-client-${TDENGINE_VERSION} \ + && ./install_client.sh \ + && cd ../ \ + && rm -rf TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz TDengine-client-${TDENGINE_VERSION} + +## add your application next, eg. go, build it in builder stage, copy the binary to the runtime +COPY --from=builder /usr/src/app/app /usr/bin/ +CMD ["app"] +``` + +Suppose you have `main.go`, `go.mod` `go.sum`, `app.dockerfile`, build the app and run it with network `td-net`: + +```bash +$ docker build -t app -f app.dockerfile +$ docker run --rm --network td-net app -h tdengine -p 6030 +============= args parse result: ============= +hostName: tdengine +serverPort: 6030 +usr: root +password: taosdata +================================================ +2022-01-17 15:56:55.48 +0000 UTC 0 +2022-01-17 15:56:56.48 +0000 UTC 1 +2022-01-17 15:56:57.48 +0000 UTC 2 +2022-01-17 15:56:58.48 +0000 UTC 3 +2022-01-17 15:58:01.842 +0000 UTC 0 +2022-01-17 15:58:02.842 +0000 UTC 1 +2022-01-17 15:58:03.842 +0000 UTC 2 +2022-01-17 15:58:04.842 +0000 UTC 3 +2022-01-18 01:43:48.029 +0000 UTC 0 +2022-01-18 01:43:49.029 +0000 UTC 1 +2022-01-18 01:43:50.029 +0000 UTC 2 +2022-01-18 01:43:51.029 +0000 UTC 3 +``` + +Now you must be much familiar with developing and testing with TDengine, let's see some more complex cases. + +### Start with docker-compose with multiple nodes(instances) + +Start a 2-replicas-2-mnodes-2-dnodes-1-arbitrator TDengine cluster with `docker-compose` is quite simple. Save the file as `docker-compose.yml`: + +```yaml +version: "3" +services: + arbitrator: + image: tdengine/tdengine:$VERSION + command: tarbitrator + td-1: + image: tdengine/tdengine:$VERSION + environment: + TAOS_FQDN: "td-1" + TAOS_FIRST_EP: "td-1" + TAOS_NUM_OF_MNODES: "2" + TAOS_REPLICA: "2" + TAOS_ARBITRATOR: arbitrator:6042 + volumes: + - taosdata-td1:/var/lib/taos/ + - taoslog-td1:/var/log/taos/ + td-2: + image: tdengine/tdengine:$VERSION + environment: + TAOS_FQDN: "td-2" + TAOS_FIRST_EP: "td-1" + TAOS_NUM_OF_MNODES: "2" + TAOS_REPLICA: "2" + TAOS_ARBITRATOR: arbitrator:6042 + volumes: + - taosdata-td2:/var/lib/taos/ + - taoslog-td2:/var/log/taos/ +volumes: + taosdata-td1: + taoslog-td1: + taosdata-td2: + taoslog-td2: +``` + +You may notice that: + +- We use `VERSION` environment variable to set `tdengine` image tag version once. +- **`TAOS_FIRST_EP`** **MUST** be set to join the newly created instances into an existing TDengine cluster. If you want more instances, use `TAOS_SECOND_EP` in case of HA(High Availability) concerns. +- `TAOS_NUM_OF_MNODES` is for setting number of mnodes for the cluster. +- `TAOS_REPLICA` set the default database replicas, `2` means there're one master and one slave copy of data. The `replica` option should be `1 <= replica <= 3`, and not greater than dnodes number. +- `TAOS_ARBITRATOR` set the arbitrator entrypoint of the cluster for failover/election stuff. It's better to use arbitrator in a two nodes cluster. +- The way to start an arbitrator service is as easy as abc: just add command name `tarbitrator`(which is the binary name of arbitrator daemon) in docker-compose service option: `command: tarbitrator`, and everything is ok now. + +Now run `docker-compose up -d` with version specified: + +```bash +$ VERSION=2.4.0.0 docker-compose up -d +Creating network "test_default" with the default driver +Creating volume "test_taosdata-td1" with default driver +Creating volume "test_taoslog-td1" with default driver +Creating volume "test_taosdata-td2" with default driver +Creating volume "test_taoslog-td2" with default driver +Creating test_td-1_1 ... done +Creating test_arbitrator_1 ... done +Creating test_td-2_1 ... done +``` + +Check the status: + +```bash +$ docker-compose ps + Name Command State Ports +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +test_arbitrator_1 /usr/bin/entrypoint.sh tar ... Up 6030/tcp, 6031/tcp, 6032/tcp, 6033/tcp, 6034/tcp, 6035/tcp, 6036/tcp, 6037/tcp, 6038/tcp, 6039/tcp, 6040/tcp, 6041/tcp, 6042/tcp +test_td-1_1 /usr/bin/entrypoint.sh taosd Up 6030/tcp, 6031/tcp, 6032/tcp, 6033/tcp, 6034/tcp, 6035/tcp, 6036/tcp, 6037/tcp, 6038/tcp, 6039/tcp, 6040/tcp, 6041/tcp, 6042/tcp +test_td-2_1 /usr/bin/entrypoint.sh taosd Up 6030/tcp, 6031/tcp, 6032/tcp, 6033/tcp, 6034/tcp, 6035/tcp, 6036/tcp, 6037/tcp, 6038/tcp, 6039/tcp, 6040/tcp, 6041/tcp, 6042/tcp +``` + +Check dnodes with taos shell: + +```bash +$ docker-compose exec td-1 taos -s "show dnodes" + +Welcome to the TDengine shell from Linux, Client Version:2.4.0.0 +Copyright (c) 2020 by TAOS Data, Inc. All rights reserved. + +taos> show dnodes + id | end_point | vnodes | cores | status | role | create_time | offline reason | +====================================================================================================================================== + 1 | td-1:6030 | 1 | 8 | ready | any | 2022-01-18 02:47:42.871 | | + 2 | td-2:6030 | 0 | 8 | ready | any | 2022-01-18 02:47:43.518 | | + 0 | arbitrator:6042 | 0 | 0 | ready | arb | 2022-01-18 02:47:43.633 | - | +Query OK, 3 row(s) in set (0.000811s) +``` + +### Start a TDengine cluster with scaled taosadapter service + +In previous use case, you could see the way to start other services built with TDengine(`taosd` as the default command). There's another important service you should know: + +> **taosAdapter** is a TDengine’s companion tool and is a bridge/adapter between TDengine cluster and application. It provides an easy-to-use and efficient way to ingest data from data collections agents(like Telegraf, StatsD, CollectD) directly. It also provides InfluxDB/OpenTSDB compatible data ingestion interface to allow InfluxDB/OpenTSDB applications to immigrate to TDengine seamlessly. + +`taosadapter` is running inside `tdengine` image by default, you can disable it by `TAOS_DISABLE_ADAPTER=true`. Running `taosadapter` in a separate container is like how `arbitrator` does: + +```yaml +services: + # ... + adapter: + image: tdengine/tdengine:$VERSION + command: taosadapter +``` + +`taosadapter` could be scaled with docker-compose, so that you can manage the `taosadapter` nodes easily. Here is an example shows 4-`taosadapter` instances in a TDengine cluster(much like previous use cases): + +```yaml +version: "3" + +networks: + inter: + api: + +services: + arbitrator: + image: tdengine/tdengine:$VERSION + command: tarbitrator + networks: + - inter + td-1: + image: tdengine/tdengine:$VERSION + networks: + - inter + environment: + TAOS_FQDN: "td-1" + TAOS_FIRST_EP: "td-1" + TAOS_NUM_OF_MNODES: "2" + TAOS_REPLICA: "2" + TAOS_ARBITRATOR: arbitrator:6042 + volumes: + - taosdata-td1:/var/lib/taos/ + - taoslog-td1:/var/log/taos/ + td-2: + image: tdengine/tdengine:$VERSION + networks: + - inter + environment: + TAOS_FQDN: "td-2" + TAOS_FIRST_EP: "td-1" + TAOS_NUM_OF_MNODES: "2" + TAOS_REPLICA: "2" + TAOS_ARBITRATOR: arbitrator:6042 + volumes: + - taosdata-td2:/var/lib/taos/ + - taoslog-td2:/var/log/taos/ + adapter: + image: tdengine/tdengine:$VERSION + command: taosadapter + networks: + - inter + environment: + TAOS_FIRST_EP: "td-1" + TAOS_SECOND_EP: "td-2" + deploy: + replicas: 4 + nginx: + image: nginx + depends_on: + - adapter + networks: + - inter + - api + ports: + - 6041:6041 + - 6044:6044/udp + command: [ + "sh", + "-c", + "while true; + do curl -s http://adapter:6041/-/ping >/dev/null && break; + done; + printf 'server{listen 6041;location /{proxy_pass http://adapter:6041;}}' + > /etc/nginx/conf.d/rest.conf; + printf 'stream{server{listen 6044 udp;proxy_pass adapter:6044;}}' + >> /etc/nginx/nginx.conf;cat /etc/nginx/nginx.conf; + nginx -g 'daemon off;'", + ] +volumes: + taosdata-td1: + taoslog-td1: + taosdata-td2: + taoslog-td2: +``` + +Start the cluster: + +```bash +$ VERSION=2.4.0.0 docker-compose up -d +Creating network "docker_inter" with the default driver +Creating network "docker_api" with the default driver +Creating volume "docker_taosdata-td1" with default driver +Creating volume "docker_taoslog-td1" with default driver +Creating volume "docker_taosdata-td2" with default driver +Creating volume "docker_taoslog-td2" with default driver +Creating docker_td-2_1 ... done +Creating docker_arbitrator_1 ... done +Creating docker_td-1_1 ... done +Creating docker_adapter_1 ... done +Creating docker_adapter_2 ... done +Creating docker_adapter_3 ... done +``` + +It will start a TDengine cluster with two dnodes and four taosadapter instances, expose ports 6041/tcp and 6044/udp to host. + +`6041` is the RESTful API endpoint port, you can verify that the RESTful interface taosAdapter provides working using the `curl` command. + +```bash +$ curl -H 'Authorization: Basic cm9vdDp0YW9zZGF0YQ==' -d 'show databases;' 127.0.0.1:6041/rest/sql +{"status":"succ","head":["name","created_time","ntables","vgroups","replica","quorum","days","keep","cache(MB)","blocks","minrows","maxrows","wallevel","fsync","comp","cachelast","precision","update","status"],"column_meta":[["name",8,32],["created_time",9,8],["ntables",4,4],["vgroups",4,4],["replica",3,2],["quorum",3,2],["days",3,2],["keep",8,24],["cache(MB)",4,4],["blocks",4,4],["minrows",4,4],["maxrows",4,4],["wallevel",2,1],["fsync",4,4],["comp",2,1],["cachelast",2,1],["precision",8,3],["update",2,1],["status",8,10]],"data":[["log","2022-01-18 04:37:42.902",16,1,1,1,10,"30",1,3,100,4096,1,3000,2,0,"us",0,"ready"]],"rows":1} +``` + +If you run curl in batch(here we use [hyperfine](https://github.com/sharkdp/hyperfine) - a command-line benchmarking tool), the requests are balanced into 4 adapter instances. + +```bash +hyperfine -m10 'curl -u root:taosdata localhost:6041/rest/sql -d "describe log.log"' +``` + +View the logs with `docker-compose logs`: + +```bash +$ docker-compose logs adapter +# some logs skipped +adapter_2 | 01/18 04:57:44.616529 00000039 TAOS_ADAPTER info "| 200 | 162.185µs | 172.21.0.9 | POST | /rest/sql " model=web sessionID=18 +adapter_1 | 01/18 04:57:44.627695 00000039 TAOS_ADAPTER info "| 200 | 145.485µs | 172.21.0.9 | POST | /rest/sql " model=web sessionID=17 +adapter_3 | 01/18 04:57:44.639165 00000040 TAOS_ADAPTER info "| 200 | 146.913µs | 172.21.0.9 | POST | /rest/sql " sessionID=17 model=web +adapter_4 | 01/18 04:57:44.650829 00000039 TAOS_ADAPTER info "| 200 | 153.201µs | 172.21.0.9 | POST | /rest/sql " sessionID=17 model=web +adapter_2 | 01/18 04:57:44.662422 00000039 TAOS_ADAPTER info "| 200 | 211.393µs | 172.21.0.9 | POST | /rest/sql " model=web sessionID=19 +adapter_1 | 01/18 04:57:44.673426 00000039 TAOS_ADAPTER info "| 200 | 154.714µs | 172.21.0.9 | POST | /rest/sql " model=web sessionID=18 +adapter_3 | 01/18 04:57:44.684788 00000040 TAOS_ADAPTER info "| 200 | 131.876µs | 172.21.0.9 | POST | /rest/sql " model=web sessionID=18 +adapter_4 | 01/18 04:57:44.696261 00000039 TAOS_ADAPTER info "| 200 | 162.173µs | 172.21.0.9 | POST | /rest/sql " model=web sessionID=18 +adapter_2 | 01/18 04:57:44.707414 00000039 TAOS_ADAPTER info "| 200 | 164.419µs | 172.21.0.9 | POST | /rest/sql " model=web sessionID=20 +adapter_1 | 01/18 04:57:44.720842 00000039 TAOS_ADAPTER info "| 200 | 179.374µs | 172.21.0.9 | POST | /rest/sql " model=web sessionID=19 +adapter_3 | 01/18 04:57:44.732184 00000040 TAOS_ADAPTER info "| 200 | 141.174µs | 172.21.0.9 | POST | /rest/sql " sessionID=19 model=web +adapter_4 | 01/18 04:57:44.744024 00000039 TAOS_ADAPTER info "| 200 | 159.774µs | 172.21.0.9 | POST | /rest/sql " model=web sessionID=19 +adapter_2 | 01/18 04:57:44.773732 00000039 TAOS_ADAPTER info "| 200 | 178.993µs | 172.21.0.9 | POST | /rest/sql " model=web sessionID=21 +adapter_1 | 01/18 04:57:44.796518 00000039 TAOS_ADAPTER info "| 200 | 238.24µs | 172.21.0.9 | POST | /rest/sql " model=web sessionID=20 +adapter_3 | 01/18 04:57:44.810744 00000040 TAOS_ADAPTER info "| 200 | 176.133µs | 172.21.0.9 | POST | /rest/sql " model=web sessionID=20 +adapter_4 | 01/18 04:57:44.826395 00000039 TAOS_ADAPTER info "| 200 | 149.215µs | 172.21.0.9 | POST | /rest/sql " model=web sessionID=20 +``` + +`6044/udp` is the [StatsD](https://github.com/statsd/statsd)-compatible port, you can verify this feature with `nc` command(usually provided by `netcat` package). + +```bash +echo "foo:1|c" | nc -u -w0 127.0.0.1 6044 +``` + +Check the result in `taos` shell with `docker-compose exec`: + +```bash +$ dc exec td-1 taos + +Welcome to the TDengine shell from Linux, Client Version:2.4.0.0 +Copyright (c) 2020 by TAOS Data, Inc. All rights reserved. + +taos> show databases; + name | created_time | ntables | vgroups | replica | quorum | days | keep | cache(MB) | blocks | minrows | maxrows | wallevel | fsync | comp | cachelast | precision | update | status | +==================================================================================================================================================================================================================================================================================== + log | 2022-01-18 04:37:42.902 | 17 | 1 | 1 | 1 | 10 | 30 | 1 | 3 | 100 | 4096 | 1 | 3000 | 2 | 0 | us | 0 | ready | + statsd | 2022-01-18 04:45:02.563 | 1 | 1 | 2 | 1 | 10 | 3650 | 16 | 6 | 100 | 4096 | 1 | 3000 | 2 | 0 | ns | 2 | ready | +Query OK, 2 row(s) in set (0.001838s) + +taos> select * from statsd.foo; + ts | value | metric_type | +======================================================================================= + 2022-01-18 04:45:02.563422822 | 1 | counter | +Query OK, 1 row(s) in set (0.003854s) +``` + +Use `docker-compose up -d adapter=1 to reduce the instances to 1 + +### Deploy TDengine cluster in Docker Swarm with `docker-compose.yml` + +If you use docker swarm mode, it will schedule arbitrator/taosd/taosadapter services into different hosts automatically. If you've no experience with k8s/kubernetes, this is the most convenient way to scale out the TDengine cluster with multiple hosts/servers. + +Use the `docker-compose.yml` file in previous use case, and deploy with `docker stack` or `docker deploy`: + +```bash +$ VERSION=2.4.0 docker stack deploy -c docker-compose.yml taos +Creating network taos_inter +Creating network taos_api +Creating service taos_arbitrator +Creating service taos_td-1 +Creating service taos_td-2 +Creating service taos_adapter +Creating service taos_nginx +``` + +Now you've created a TDengine cluster with multiple host servers. + +Use `docker service` or `docker stack` to manage the cluster: + + + +```bash +$ docker stack ps taos +ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS +79ni8temw59n taos_nginx.1 nginx:latest TM1701 Running Running about a minute ago +3e94u72msiyg taos_adapter.1 tdengine/tdengine:2.4.0 TM1702 Running Running 56 seconds ago +100amjkwzsc6 taos_td-2.1 tdengine/tdengine:2.4.0 TM1703 Running Running about a minute ago +pkjehr2vvaaa taos_td-1.1 tdengine/tdengine:2.4.0 TM1704 Running Running 2 minutes ago +tpzvgpsr1qkt taos_arbitrator.1 tdengine/tdengine:2.4.0 TM1705 Running Running 2 minutes ago +rvss3g5yg6fa taos_adapter.2 tdengine/tdengine:2.4.0 TM1706 Running Running 56 seconds ago +i2augxamfllf taos_adapter.3 tdengine/tdengine:2.4.0 TM1707 Running Running 56 seconds ago +lmjyhzccpvpg taos_adapter.4 tdengine/tdengine:2.4.0 TM1708 Running Running 56 seconds ago +$ docker service ls +ID NAME MODE REPLICAS IMAGE PORTS +561t4lu6nfw6 taos_adapter replicated 4/4 tdengine/tdengine:2.4.0 +3hk5ct3q90sm taos_arbitrator replicated 1/1 tdengine/tdengine:2.4.0 +d8qr52envqzu taos_nginx replicated 1/1 nginx:latest *:6041->6041/tcp, *:6044->6044/udp +2isssfvjk747 taos_td-1 replicated 1/1 tdengine/tdengine:2.4.0 +9pzw7u02ichv taos_td-2 replicated 1/1 tdengine/tdengine:2.4.0 +``` + + + +It shows that there are two dnodes, one arbitrator, four taosadapter and one nginx reverse-forward service in this cluster. + +You can scale down the taosadapter replicas to `1` by `docker service`: + +```bash +$ docker service scale taos_adapter=1 +taos_adapter scaled to 1 +overall progress: 1 out of 1 tasks +1/1: running [==================================================>] +verify: Service converged + +$ docker service ls -f name=taos_adapter +ID NAME MODE REPLICAS IMAGE PORTS +561t4lu6nfw6 taos_adapter replicated 1/1 tdengine/tdengine:2.4.0 +``` + +Now it remains only 1 taosadapter instance in the cluster. + +When you want to remove the cluster, just type: + +```bash +docker stack rm taos +``` + +### Environment Variables + +When you start `tdengine` image, you can adjust the configuration of TDengine by passing environment variables on the `docker run` command line or in the docker compose file. You can use all of the environment variables that passed to taosd or taosadapter. diff --git a/packaging/docker/bin/entrypoint.sh b/packaging/docker/bin/entrypoint.sh index 7173a35140d86b64161f57ec685f3fb1f7d4373d..5fb441004d8b454de1039eb3f4b23eb51f32be64 100644 --- a/packaging/docker/bin/entrypoint.sh +++ b/packaging/docker/bin/entrypoint.sh @@ -1,83 +1,83 @@ -#!/bin/sh -set -e -# for TZ awareness -if [ "$TZ" != "" ]; then - ln -sf /usr/share/zoneinfo/$TZ /etc/localtime - echo $TZ >/etc/timezone -fi - -# option to disable taosadapter, default is no -DISABLE_ADAPTER=${TAOS_DISABLE_ADAPTER:-0} -unset TAOS_DISABLE_ADAPTER - -# to get mnodeEpSet from data dir -DATA_DIR=${TAOS_DATA_DIR:-/var/lib/taos} - -# append env to custom taos.cfg -CFG_DIR=/tmp/taos -CFG_FILE=$CFG_DIR/taos.cfg - -mkdir -p $CFG_DIR >/dev/null 2>&1 - -[ -f /etc/taos/taos.cfg ] && cat /etc/taos/taos.cfg | grep -E -v "^#|^\s*$" >$CFG_FILE -env-to-cfg >>$CFG_FILE - -FQDN=$(cat $CFG_FILE | grep -E -v "^#|^$" | grep fqdn | tail -n1 | sed -E 's/.*fqdn\s+//') - -# ensure the fqdn is resolved as localhost -grep "$FQDN" /etc/hosts >/dev/null || echo "127.0.0.1 $FQDN" >>/etc/hosts - -# parse first ep host and port -FIRST_EP_HOST=${TAOS_FIRST_EP%:*} -FIRST_EP_PORT=${TAOS_FIRST_EP#*:} - -# in case of custom server port -SERVER_PORT=$(cat $CFG_FILE | grep -E -v "^#|^$" | grep serverPort | tail -n1 | sed -E 's/.*serverPort\s+//') -SERVER_PORT=${SERVER_PORT:-6030} - -# for other binaries like interpreters -if echo $1 | grep -E "taosd$" - >/dev/null; then - true # will run taosd -else - cp -f $CFG_FILE /etc/taos/taos.cfg || true - $@ - exit $? -fi - -set +e -ulimit -c unlimited -# set core files pattern, maybe failed -sysctl -w kernel.core_pattern=/corefile/core-$FQDN-%e-%p >/dev/null >&1 -set -e - -if [ "$DISABLE_ADAPTER" = "0" ]; then - which taosadapter >/dev/null && taosadapter & - # wait for 6041 port ready - for _ in $(seq 1 20); do - nc -z localhost 6041 && break - sleep 0.5 - done -fi - -# if has mnode ep set or the host is first ep or not for cluster, just start. -if [ -f "$DATA_DIR/dnode/mnodeEpSet.json" ] || - [ "$TAOS_FQDN" = "$FIRST_EP_HOST" ]; then - $@ -c $CFG_DIR -# others will first wait the first ep ready. -else - if [ "$TAOS_FIRST_EP" = "" ]; then - echo "run TDengine with single node." - $@ -c $CFG_DIR - exit $? - fi - while true; do - es=0 - taos -h $FIRST_EP_HOST -P $FIRST_EP_PORT -n startup >/dev/null || es=$? - if [ "$es" -eq 0 ]; then - taos -h $FIRST_EP_HOST -P $FIRST_EP_PORT -s "create dnode \"$FQDN:$SERVER_PORT\";" - break - fi - sleep 1s - done - $@ -c $CFG_DIR -fi +#!/bin/sh +set -e +# for TZ awareness +if [ "$TZ" != "" ]; then + ln -sf /usr/share/zoneinfo/$TZ /etc/localtime + echo $TZ >/etc/timezone +fi + +# option to disable taosadapter, default is no +DISABLE_ADAPTER=${TAOS_DISABLE_ADAPTER:-0} +unset TAOS_DISABLE_ADAPTER + +# to get mnodeEpSet from data dir +DATA_DIR=${TAOS_DATA_DIR:-/var/lib/taos} + +# append env to custom taos.cfg +CFG_DIR=/tmp/taos +CFG_FILE=$CFG_DIR/taos.cfg + +mkdir -p $CFG_DIR >/dev/null 2>&1 + +[ -f /etc/taos/taos.cfg ] && cat /etc/taos/taos.cfg | grep -E -v "^#|^\s*$" >$CFG_FILE +env-to-cfg >>$CFG_FILE + +FQDN=$(cat $CFG_FILE | grep -E -v "^#|^$" | grep fqdn | tail -n1 | sed -E 's/.*fqdn\s+//') + +# ensure the fqdn is resolved as localhost +grep "$FQDN" /etc/hosts >/dev/null || echo "127.0.0.1 $FQDN" >>/etc/hosts + +# parse first ep host and port +FIRST_EP_HOST=${TAOS_FIRST_EP%:*} +FIRST_EP_PORT=${TAOS_FIRST_EP#*:} + +# in case of custom server port +SERVER_PORT=$(cat $CFG_FILE | grep -E -v "^#|^$" | grep serverPort | tail -n1 | sed -E 's/.*serverPort\s+//') +SERVER_PORT=${SERVER_PORT:-6030} + +# for other binaries like interpreters +if echo $1 | grep -E "taosd$" - >/dev/null; then + true # will run taosd +else + cp -f $CFG_FILE /etc/taos/taos.cfg || true + $@ + exit $? +fi + +set +e +ulimit -c unlimited +# set core files pattern, maybe failed +sysctl -w kernel.core_pattern=/corefile/core-$FQDN-%e-%p >/dev/null >&1 +set -e + +if [ "$DISABLE_ADAPTER" = "0" ]; then + which taosadapter >/dev/null && taosadapter & + # wait for 6041 port ready + for _ in $(seq 1 20); do + nc -z localhost 6041 && break + sleep 0.5 + done +fi + +# if has mnode ep set or the host is first ep or not for cluster, just start. +if [ -f "$DATA_DIR/dnode/mnodeEpSet.json" ] || + [ "$TAOS_FQDN" = "$FIRST_EP_HOST" ]; then + $@ -c $CFG_DIR +# others will first wait the first ep ready. +else + if [ "$TAOS_FIRST_EP" = "" ]; then + echo "run TDengine with single node." + $@ -c $CFG_DIR + exit $? + fi + while true; do + es=0 + taos -h $FIRST_EP_HOST -P $FIRST_EP_PORT -n startup >/dev/null || es=$? + if [ "$es" -eq 0 ]; then + taos -h $FIRST_EP_HOST -P $FIRST_EP_PORT -s "create dnode \"$FQDN:$SERVER_PORT\";" + break + fi + sleep 1s + done + $@ -c $CFG_DIR +fi diff --git a/packaging/docker/bin/env-to-cfg b/packaging/docker/bin/env-to-cfg index b7741d02e1f0f902f4711571ce7eb7440f899525..07be63e0a9aba74e271ccc20758cd2ab09fb44ed 100644 --- a/packaging/docker/bin/env-to-cfg +++ b/packaging/docker/bin/env-to-cfg @@ -1,13 +1,13 @@ -#!/bin/sh -set -e -self=$0 - -snake_to_camel_case() { - echo $1 | awk -F _ '{printf "%s", $1; for(i=2; i<=NF; i++) printf "%s", toupper(substr($i,1,1)) substr($i,2); print"";}' -} - -if echo $1 | grep -E "^$" - >/dev/null; then - export |grep -E 'TAOS_.*' -o| sed 's/TAOS_//' |tr A-Z a-z | awk -F"=" '{print "name=$(""'$self' " $1"); echo $name "$2}' |sh -else - snake_to_camel_case $1 -fi +#!/bin/sh +set -e +self=$0 + +snake_to_camel_case() { + echo $1 | awk -F _ '{printf "%s", $1; for(i=2; i<=NF; i++) printf "%s", toupper(substr($i,1,1)) substr($i,2); print"";}' +} + +if echo $1 | grep -E "^$" - >/dev/null; then + export |grep -E 'TAOS_.*' -o| sed 's/TAOS_//' |tr A-Z a-z | awk -F"=" '{print "name=$(""'$self' " $1"); echo $name "$2}' |sh +else + snake_to_camel_case $1 +fi diff --git a/packaging/docker/docker-compose.yml b/packaging/docker/docker-compose.yml index e15ad0cf4f240ca6d51e621416c1d9ce945bc59d..301b41e7d43c2a894d866c1f0d45cf8d13328585 100644 --- a/packaging/docker/docker-compose.yml +++ b/packaging/docker/docker-compose.yml @@ -1,77 +1,77 @@ -version: "3" - -networks: - inter: - api: - -services: - arbitrator: - image: tdengine/tdengine:$VERSION - command: tarbitrator - networks: - - inter - td-1: - image: tdengine/tdengine:$VERSION - networks: - - inter - environment: - TAOS_FQDN: "td-1" - TAOS_FIRST_EP: "td-1" - TAOS_NUM_OF_MNODES: "2" - TAOS_REPLICA: "2" - TAOS_ARBITRATOR: arbitrator:6042 - volumes: - - taosdata-td1:/var/lib/taos/ - - taoslog-td1:/var/log/taos/ - td-2: - image: tdengine/tdengine:$VERSION - networks: - - inter - environment: - TAOS_FQDN: "td-2" - TAOS_FIRST_EP: "td-1" - TAOS_NUM_OF_MNODES: "2" - TAOS_REPLICA: "2" - TAOS_ARBITRATOR: arbitrator:6042 - volumes: - - taosdata-td2:/var/lib/taos/ - - taoslog-td2:/var/log/taos/ - adapter: - image: tdengine/tdengine:$VERSION - command: taosadapter - networks: - - inter - environment: - TAOS_FIRST_EP: "td-1" - TOAS_SECOND_EP: "td-2" - deploy: - replicas: 4 - update_config: - parallelism: 4 - nginx: - image: nginx - depends_on: - - adapter - networks: - - inter - - api - ports: - - 6041:6041 - - 6044:6044/udp - command: [ - "sh", - "-c", - "while true; - do curl -s http://adapter:6041/-/ping >/dev/null && break; - done; - printf 'server{listen 6041;location /{proxy_pass http://adapter:6041;}}' - > /etc/nginx/conf.d/rest.conf; - printf 'stream{server{listen 6044 udp;proxy_pass adapter:6044;}}' - >> /etc/nginx/nginx.conf;cat /etc/nginx/nginx.conf; - nginx -g 'daemon off;'", - ] -volumes: - taosdata-td1: - taoslog-td1: - taosdata-td2: - taoslog-td2: +version: "3" + +networks: + inter: + api: + +services: + arbitrator: + image: tdengine/tdengine:$VERSION + command: tarbitrator + networks: + - inter + td-1: + image: tdengine/tdengine:$VERSION + networks: + - inter + environment: + TAOS_FQDN: "td-1" + TAOS_FIRST_EP: "td-1" + TAOS_NUM_OF_MNODES: "2" + TAOS_REPLICA: "2" + TAOS_ARBITRATOR: arbitrator:6042 + volumes: + - taosdata-td1:/var/lib/taos/ + - taoslog-td1:/var/log/taos/ + td-2: + image: tdengine/tdengine:$VERSION + networks: + - inter + environment: + TAOS_FQDN: "td-2" + TAOS_FIRST_EP: "td-1" + TAOS_NUM_OF_MNODES: "2" + TAOS_REPLICA: "2" + TAOS_ARBITRATOR: arbitrator:6042 + volumes: + - taosdata-td2:/var/lib/taos/ + - taoslog-td2:/var/log/taos/ + adapter: + image: tdengine/tdengine:$VERSION + command: taosadapter + networks: + - inter + environment: + TAOS_FIRST_EP: "td-1" + TOAS_SECOND_EP: "td-2" + deploy: + replicas: 4 + update_config: + parallelism: 4 + nginx: + image: nginx + depends_on: + - adapter + networks: + - inter + - api + ports: + - 6041:6041 + - 6044:6044/udp + command: [ + "sh", + "-c", + "while true; + do curl -s http://adapter:6041/-/ping >/dev/null && break; + done; + printf 'server{listen 6041;location /{proxy_pass http://adapter:6041;}}' + > /etc/nginx/conf.d/rest.conf; + printf 'stream{server{listen 6044 udp;proxy_pass adapter:6044;}}' + >> /etc/nginx/nginx.conf;cat /etc/nginx/nginx.conf; + nginx -g 'daemon off;'", + ] +volumes: + taosdata-td1: + taoslog-td1: + taosdata-td2: + taoslog-td2: diff --git a/packaging/docker/dockerManifest.sh b/packaging/docker/dockerManifest.sh index b90e0e9a6561289bb64deb584a307d9c558c0886..71788423f6e58b2788346ef2804cd4d03ee54b02 100644 --- a/packaging/docker/dockerManifest.sh +++ b/packaging/docker/dockerManifest.sh @@ -1,82 +1,82 @@ -#!/bin/bash -set -e -#set -x - -# dockerbuild.sh -# -n [version number] -# -p [xxxx] -# -V [stable | beta] - -# set parameters by default value -version="" -passWord="" -verType="" - -while getopts "hn:p:V:" arg -do - case $arg in - n) - #echo "version=$OPTARG" - version=$(echo $OPTARG) - ;; - p) - #echo "passWord=$OPTARG" - passWord=$(echo $OPTARG) - ;; - V) - #echo "verType=$OPTARG" - verType=$(echo $OPTARG) - ;; - h) - echo "Usage: `basename $0` -n [version number] " - echo " -p [password for docker hub] " - exit 0 - ;; - ?) #unknow option - echo "unkonw argument" - exit 1 - ;; - esac -done - -echo "version=${version}" - -#docker manifest rm tdengine/tdengine -#docker manifest rm tdengine/tdengine:${version} -if [ "$verType" == "beta" ]; then - docker manifest create -a tdengine/tdengine-beta:${version} tdengine/tdengine-amd64-beta:${version} tdengine/tdengine-aarch64-beta:${version} tdengine/tdengine-aarch32-beta:${version} - docker manifest create -a tdengine/tdengine-beta:latest tdengine/tdengine-amd64-beta:latest tdengine/tdengine-aarch64-beta:latest tdengine/tdengine-aarch32-beta:latest - docker manifest rm tdengine/tdengine-beta:${version} - docker manifest rm tdengine/tdengine-beta:latest - docker manifest create -a tdengine/tdengine-beta:${version} tdengine/tdengine-amd64-beta:${version} tdengine/tdengine-aarch64-beta:${version} tdengine/tdengine-aarch32-beta:${version} - docker manifest create -a tdengine/tdengine-beta:latest tdengine/tdengine-amd64-beta:latest tdengine/tdengine-aarch64-beta:latest tdengine/tdengine-aarch32-beta:latest - docker manifest inspect tdengine/tdengine:latest - docker manifest inspect tdengine/tdengine:${version} - docker login -u tdengine -p ${passWord} #replace the docker registry username and password - docker manifest push tdengine/tdengine-beta:${version} - docker manifest push tdengine/tdengine-beta:latest -elif [ "$verType" == "stable" ]; then - docker manifest create -a tdengine/tdengine:${version} tdengine/tdengine-amd64:${version} tdengine/tdengine-aarch64:${version} tdengine/tdengine-aarch32:${version} - docker manifest create -a tdengine/tdengine:latest tdengine/tdengine-amd64:latest tdengine/tdengine-aarch64:latest tdengine/tdengine-aarch32:latest - docker manifest rm tdengine/tdengine:latest - docker manifest rm tdengine/tdengine:${version} - docker manifest create -a tdengine/tdengine:${version} tdengine/tdengine-amd64:${version} tdengine/tdengine-aarch64:${version} tdengine/tdengine-aarch32:${version} - docker manifest create -a tdengine/tdengine:latest tdengine/tdengine-amd64:latest tdengine/tdengine-aarch64:latest tdengine/tdengine-aarch32:latest - docker manifest inspect tdengine/tdengine:latest - docker manifest inspect tdengine/tdengine:${version} - docker login -u tdengine -p ${passWord} #replace the docker registry username and password - docker manifest push tdengine/tdengine:${version} - docker manifest push tdengine/tdengine:latest -else - echo "unknow verType, nor stabel or beta" - exit 1 -fi - -# docker manifest create -a tdengine/${dockername}:${version} tdengine/tdengine-amd64:${version} tdengine/tdengine-aarch64:${version} tdengine/tdengine-aarch32:${version} -# docker manifest create -a tdengine/${dockername}:latest tdengine/tdengine-amd64:latest tdengine/tdengine-aarch64:latest tdengine/tdengine-aarch32:latest - -# docker login -u tdengine -p ${passWord} #replace the docker registry username and password - -# docker manifest push tdengine/tdengine:latest - -# # how set latest version ??? +#!/bin/bash +set -e +#set -x + +# dockerbuild.sh +# -n [version number] +# -p [xxxx] +# -V [stable | beta] + +# set parameters by default value +version="" +passWord="" +verType="" + +while getopts "hn:p:V:" arg +do + case $arg in + n) + #echo "version=$OPTARG" + version=$(echo $OPTARG) + ;; + p) + #echo "passWord=$OPTARG" + passWord=$(echo $OPTARG) + ;; + V) + #echo "verType=$OPTARG" + verType=$(echo $OPTARG) + ;; + h) + echo "Usage: `basename $0` -n [version number] " + echo " -p [password for docker hub] " + exit 0 + ;; + ?) #unknow option + echo "unkonw argument" + exit 1 + ;; + esac +done + +echo "version=${version}" + +#docker manifest rm tdengine/tdengine +#docker manifest rm tdengine/tdengine:${version} +if [ "$verType" == "beta" ]; then + docker manifest create -a tdengine/tdengine-beta:${version} tdengine/tdengine-amd64-beta:${version} tdengine/tdengine-aarch64-beta:${version} tdengine/tdengine-aarch32-beta:${version} + docker manifest create -a tdengine/tdengine-beta:latest tdengine/tdengine-amd64-beta:latest tdengine/tdengine-aarch64-beta:latest tdengine/tdengine-aarch32-beta:latest + docker manifest rm tdengine/tdengine-beta:${version} + docker manifest rm tdengine/tdengine-beta:latest + docker manifest create -a tdengine/tdengine-beta:${version} tdengine/tdengine-amd64-beta:${version} tdengine/tdengine-aarch64-beta:${version} tdengine/tdengine-aarch32-beta:${version} + docker manifest create -a tdengine/tdengine-beta:latest tdengine/tdengine-amd64-beta:latest tdengine/tdengine-aarch64-beta:latest tdengine/tdengine-aarch32-beta:latest + docker manifest inspect tdengine/tdengine:latest + docker manifest inspect tdengine/tdengine:${version} + docker login -u tdengine -p ${passWord} #replace the docker registry username and password + docker manifest push tdengine/tdengine-beta:${version} + docker manifest push tdengine/tdengine-beta:latest +elif [ "$verType" == "stable" ]; then + docker manifest create -a tdengine/tdengine:${version} tdengine/tdengine-amd64:${version} tdengine/tdengine-aarch64:${version} tdengine/tdengine-aarch32:${version} + docker manifest create -a tdengine/tdengine:latest tdengine/tdengine-amd64:latest tdengine/tdengine-aarch64:latest tdengine/tdengine-aarch32:latest + docker manifest rm tdengine/tdengine:latest + docker manifest rm tdengine/tdengine:${version} + docker manifest create -a tdengine/tdengine:${version} tdengine/tdengine-amd64:${version} tdengine/tdengine-aarch64:${version} tdengine/tdengine-aarch32:${version} + docker manifest create -a tdengine/tdengine:latest tdengine/tdengine-amd64:latest tdengine/tdengine-aarch64:latest tdengine/tdengine-aarch32:latest + docker manifest inspect tdengine/tdengine:latest + docker manifest inspect tdengine/tdengine:${version} + docker login -u tdengine -p ${passWord} #replace the docker registry username and password + docker manifest push tdengine/tdengine:${version} + docker manifest push tdengine/tdengine:latest +else + echo "unknow verType, nor stabel or beta" + exit 1 +fi + +# docker manifest create -a tdengine/${dockername}:${version} tdengine/tdengine-amd64:${version} tdengine/tdengine-aarch64:${version} tdengine/tdengine-aarch32:${version} +# docker manifest create -a tdengine/${dockername}:latest tdengine/tdengine-amd64:latest tdengine/tdengine-aarch64:latest tdengine/tdengine-aarch32:latest + +# docker login -u tdengine -p ${passWord} #replace the docker registry username and password + +# docker manifest push tdengine/tdengine:latest + +# # how set latest version ??? diff --git a/packaging/docker/dockerbuild.sh b/packaging/docker/dockerbuild.sh index c5e3ce15fb639bf58d96474e6a7f1f1454d27dbc..541ae6ec1398ae40a450382d25aa53bec18a8ced 100644 --- a/packaging/docker/dockerbuild.sh +++ b/packaging/docker/dockerbuild.sh @@ -1,174 +1,174 @@ -#!/bin/bash -# - -set -e -#set -x - -# dockerbuild.sh -# -c [aarch32 | aarch64 | amd64 | x86 | mips64 ...] -# -n [version number] -# -p [password for docker hub] -# -V [stable | beta] -# -f [pkg file] - -# set parameters by default value -cpuType="" -cpuTypeAlias="" -version="" -passWord="" -pkgFile="" -verType="stable" -dockerLatest="n" - -while getopts "hc:n:p:f:V:a:b:" arg -do - case $arg in - c) - #echo "cpuType=$OPTARG" - cpuType=$(echo $OPTARG) - ;; - n) - #echo "version=$OPTARG" - version=$(echo $OPTARG) - ;; - p) - #echo "passWord=$OPTARG" - passWord=$(echo $OPTARG) - ;; - f) - #echo "pkgFile=$OPTARG" - pkgFile=$(echo $OPTARG) - ;; - b) - #echo "branchName=$OPTARG" - branchName=$(echo $OPTARG) - ;; - V) - #echo "verType=$OPTARG" - verType=$(echo $OPTARG) - ;; - a) - #echo "dockerLatest=$OPTARG" - dockerLatest=$(echo $OPTARG) - ;; - h) - echo "Usage: `basename $0` -c [aarch32 | aarch64 | amd64 | x86 | mips64 ...] " - echo " -n [version number] " - echo " -p [password for docker hub] " - echo " -V [stable | beta] " - echo " -f [pkg file] " - echo " -a [y | n ] " - exit 0 - ;; - ?) #unknow option - echo "unkonw argument" - exit 1 - ;; - esac -done - - -# Check_verison() -# { -# } - - -if [ "$verType" == "beta" ]; then - dockername=${cpuType}-${verType} - dirName=${pkgFile%-beta*} -elif [ "$verType" == "stable" ]; then - dockername=${cpuType} - dirName=${pkgFile%-Linux*} -else - echo "unknow verType, nor stabel or beta" - exit 1 -fi - - -echo "cpuType=${cpuType} version=${version} pkgFile=${pkgFile} verType=${verType} " -echo "$(pwd)" -echo "====NOTES: ${pkgFile} must be in the same directory as dockerbuild.sh====" - -scriptDir=$(dirname $(readlink -f $0)) -comunityArchiveDir=/nas/TDengine/v$version/community # community version’package directory -communityDir=${scriptDir}/../../../community -DockerfilePath=${communityDir}/packaging/docker/ -Dockerfile=${communityDir}/packaging/docker/Dockerfile -cd ${scriptDir} -cp -f ${comunityArchiveDir}/${pkgFile} . - -echo "dirName=${dirName}" - -if [[ "${cpuType}" == "x64" ]] || [[ "${cpuType}" == "amd64" ]]; then - cpuTypeAlias="amd64" -elif [[ "${cpuType}" == "aarch64" ]]; then - cpuTypeAlias="arm64" -elif [[ "${cpuType}" == "aarch32" ]]; then - cpuTypeAlias="armhf" -else - echo "Unknown cpuType: ${cpuType}" - exit 1 -fi - -docker build --rm -f "${Dockerfile}" --network=host -t tdengine/tdengine-${dockername}:${version} "." --build-arg pkgFile=${pkgFile} --build-arg dirName=${dirName} --build-arg cpuType=${cpuTypeAlias} -docker login -u tdengine -p ${passWord} #replace the docker registry username and password -docker push tdengine/tdengine-${dockername}:${version} - -if [ -n "$(docker ps -aq)" ] ;then - echo "delete docker process" - docker stop $(docker ps -aq) - docker rm $(docker ps -aq) -fi - -if [ -n "$(pidof taosd)" ] ;then - echo "kill taosd " - kill -9 $(pidof taosd) -fi - -if [ -n "$(pidof power)" ] ;then - echo "kill power " - kill -9 $(pidof power) -fi - - -echo ">>>>>>>>>>>>> check whether tdengine/tdengine-${dockername}:${version} has been published" -docker run -d --name doctest -p 6030-6049:6030-6049 -p 6030-6049:6030-6049/udp tdengine/tdengine-${dockername}:${version} -sleep 2 -curl -u root:taosdata -d 'show variables;' 127.0.0.1:6041/rest/sql > temp1.data -data_version=$( cat temp1.data |jq .data| jq '.[]' |grep "version" -A 2 -B 1 | jq ".[1]") -echo "${data_version}" -if [ "${data_version}" == "\"${version}\"" ] ; then - echo "docker version is right " -else - echo "docker version is wrong " - exit 1 -fi -rm -rf temp1.data - -# set this version to latest version -if [ ${dockerLatest} == 'y' ] ;then - docker tag tdengine/tdengine-${dockername}:${version} tdengine/tdengine-${dockername}:latest - docker push tdengine/tdengine-${dockername}:latest - echo ">>>>>>>>>>>>> check whether tdengine/tdengine-${dockername}:latest has been published correctly" - docker run -d --name doctestla -p 7030-7049:6030-6049 -p 7030-7049:6030-6049/udp tdengine/tdengine-${dockername}:latest - sleep 2 - curl -u root:taosdata -d 'show variables;' 127.0.0.1:7041/rest/sql > temp2.data - version_latest=` cat temp2.data |jq .data| jq '.[]' |grep "version" -A 2 -B 1 | jq ".[1]" ` - echo "${version_latest}" - if [ "${version_latest}" == "\"${version}\"" ] ; then - echo "docker version is right " - else - echo "docker version is wrong " - exit 1 - fi -fi -rm -rf temp2.data - -if [ -n "$(docker ps -aq)" ] ;then - echo "delte docker process" - docker stop $(docker ps -aq) - docker rm $(docker ps -aq) -fi - -cd ${scriptDir} -rm -f ${pkgFile} +#!/bin/bash +# + +set -e +#set -x + +# dockerbuild.sh +# -c [aarch32 | aarch64 | amd64 | x86 | mips64 ...] +# -n [version number] +# -p [password for docker hub] +# -V [stable | beta] +# -f [pkg file] + +# set parameters by default value +cpuType="" +cpuTypeAlias="" +version="" +passWord="" +pkgFile="" +verType="stable" +dockerLatest="n" + +while getopts "hc:n:p:f:V:a:b:" arg +do + case $arg in + c) + #echo "cpuType=$OPTARG" + cpuType=$(echo $OPTARG) + ;; + n) + #echo "version=$OPTARG" + version=$(echo $OPTARG) + ;; + p) + #echo "passWord=$OPTARG" + passWord=$(echo $OPTARG) + ;; + f) + #echo "pkgFile=$OPTARG" + pkgFile=$(echo $OPTARG) + ;; + b) + #echo "branchName=$OPTARG" + branchName=$(echo $OPTARG) + ;; + V) + #echo "verType=$OPTARG" + verType=$(echo $OPTARG) + ;; + a) + #echo "dockerLatest=$OPTARG" + dockerLatest=$(echo $OPTARG) + ;; + h) + echo "Usage: `basename $0` -c [aarch32 | aarch64 | amd64 | x86 | mips64 ...] " + echo " -n [version number] " + echo " -p [password for docker hub] " + echo " -V [stable | beta] " + echo " -f [pkg file] " + echo " -a [y | n ] " + exit 0 + ;; + ?) #unknow option + echo "unkonw argument" + exit 1 + ;; + esac +done + + +# Check_verison() +# { +# } + + +if [ "$verType" == "beta" ]; then + dockername=${cpuType}-${verType} + dirName=${pkgFile%-beta*} +elif [ "$verType" == "stable" ]; then + dockername=${cpuType} + dirName=${pkgFile%-Linux*} +else + echo "unknow verType, nor stabel or beta" + exit 1 +fi + + +echo "cpuType=${cpuType} version=${version} pkgFile=${pkgFile} verType=${verType} " +echo "$(pwd)" +echo "====NOTES: ${pkgFile} must be in the same directory as dockerbuild.sh====" + +scriptDir=$(dirname $(readlink -f $0)) +comunityArchiveDir=/nas/TDengine/v$version/community # community version’package directory +communityDir=${scriptDir}/../../../community +DockerfilePath=${communityDir}/packaging/docker/ +Dockerfile=${communityDir}/packaging/docker/Dockerfile +cd ${scriptDir} +cp -f ${comunityArchiveDir}/${pkgFile} . + +echo "dirName=${dirName}" + +if [[ "${cpuType}" == "x64" ]] || [[ "${cpuType}" == "amd64" ]]; then + cpuTypeAlias="amd64" +elif [[ "${cpuType}" == "aarch64" ]]; then + cpuTypeAlias="arm64" +elif [[ "${cpuType}" == "aarch32" ]]; then + cpuTypeAlias="armhf" +else + echo "Unknown cpuType: ${cpuType}" + exit 1 +fi + +docker build --rm -f "${Dockerfile}" --network=host -t tdengine/tdengine-${dockername}:${version} "." --build-arg pkgFile=${pkgFile} --build-arg dirName=${dirName} --build-arg cpuType=${cpuTypeAlias} +docker login -u tdengine -p ${passWord} #replace the docker registry username and password +docker push tdengine/tdengine-${dockername}:${version} + +if [ -n "$(docker ps -aq)" ] ;then + echo "delete docker process" + docker stop $(docker ps -aq) + docker rm $(docker ps -aq) +fi + +if [ -n "$(pidof taosd)" ] ;then + echo "kill taosd " + kill -9 $(pidof taosd) +fi + +if [ -n "$(pidof power)" ] ;then + echo "kill power " + kill -9 $(pidof power) +fi + + +echo ">>>>>>>>>>>>> check whether tdengine/tdengine-${dockername}:${version} has been published" +docker run -d --name doctest -p 6030-6049:6030-6049 -p 6030-6049:6030-6049/udp tdengine/tdengine-${dockername}:${version} +sleep 2 +curl -u root:taosdata -d 'show variables;' 127.0.0.1:6041/rest/sql > temp1.data +data_version=$( cat temp1.data |jq .data| jq '.[]' |grep "version" -A 2 -B 1 | jq ".[1]") +echo "${data_version}" +if [ "${data_version}" == "\"${version}\"" ] ; then + echo "docker version is right " +else + echo "docker version is wrong " + exit 1 +fi +rm -rf temp1.data + +# set this version to latest version +if [ ${dockerLatest} == 'y' ] ;then + docker tag tdengine/tdengine-${dockername}:${version} tdengine/tdengine-${dockername}:latest + docker push tdengine/tdengine-${dockername}:latest + echo ">>>>>>>>>>>>> check whether tdengine/tdengine-${dockername}:latest has been published correctly" + docker run -d --name doctestla -p 7030-7049:6030-6049 -p 7030-7049:6030-6049/udp tdengine/tdengine-${dockername}:latest + sleep 2 + curl -u root:taosdata -d 'show variables;' 127.0.0.1:7041/rest/sql > temp2.data + version_latest=` cat temp2.data |jq .data| jq '.[]' |grep "version" -A 2 -B 1 | jq ".[1]" ` + echo "${version_latest}" + if [ "${version_latest}" == "\"${version}\"" ] ; then + echo "docker version is right " + else + echo "docker version is wrong " + exit 1 + fi +fi +rm -rf temp2.data + +if [ -n "$(docker ps -aq)" ] ;then + echo "delte docker process" + docker stop $(docker ps -aq) + docker rm $(docker ps -aq) +fi + +cd ${scriptDir} +rm -f ${pkgFile} diff --git a/packaging/docker/dockerbuildi.sh b/packaging/docker/dockerbuildi.sh index 318947929006958d52f9cd1585c85fd2c1baf84b..a0a954e30fe9c3637abe4d219001235d793466e0 100644 --- a/packaging/docker/dockerbuildi.sh +++ b/packaging/docker/dockerbuildi.sh @@ -1,56 +1,56 @@ -#!/bin/bash -# - -set -e -#set -x - -# dockerbuild.sh -# -c [aarch32 | aarch64 | amd64 | x86 | mips64 ...] -# -n [version number] -# -p [password for docker hub] - -# set parameters by default value -cpuType=aarch64 -verNumber="" -passWord="" - -while getopts "hc:n:p:f:" arg -do - case $arg in - c) - #echo "cpuType=$OPTARG" - cpuType=$(echo $OPTARG) - ;; - n) - #echo "verNumber=$OPTARG" - verNumber=$(echo $OPTARG) - ;; - p) - #echo "passWord=$OPTARG" - passWord=$(echo $OPTARG) - ;; - h) - echo "Usage: `basename $0` -c [aarch32 | aarch64 | amd64 | x86 | mips64 ...] " - echo " -n [version number] " - echo " -p [password for docker hub] " - exit 0 - ;; - ?) #unknow option - echo "unkonw argument" - exit 1 - ;; - esac -done - -pkgFile=TDengine-server-${verNumber}-Linux-${cpuType}.tar.gz - -echo "cpuType=${cpuType} verNumber=${verNumber} pkgFile=${pkgFile} " - -scriptDir=`pwd` -pkgDir=$scriptDir/../../release/ - -cp -f ${pkgDir}/${pkgFile} . - -./dockerbuild.sh -c ${cpuType} -f ${pkgFile} -n ${verNumber} -p ${passWord} - -rm -f ${pkgFile} +#!/bin/bash +# + +set -e +#set -x + +# dockerbuild.sh +# -c [aarch32 | aarch64 | amd64 | x86 | mips64 ...] +# -n [version number] +# -p [password for docker hub] + +# set parameters by default value +cpuType=aarch64 +verNumber="" +passWord="" + +while getopts "hc:n:p:f:" arg +do + case $arg in + c) + #echo "cpuType=$OPTARG" + cpuType=$(echo $OPTARG) + ;; + n) + #echo "verNumber=$OPTARG" + verNumber=$(echo $OPTARG) + ;; + p) + #echo "passWord=$OPTARG" + passWord=$(echo $OPTARG) + ;; + h) + echo "Usage: `basename $0` -c [aarch32 | aarch64 | amd64 | x86 | mips64 ...] " + echo " -n [version number] " + echo " -p [password for docker hub] " + exit 0 + ;; + ?) #unknow option + echo "unkonw argument" + exit 1 + ;; + esac +done + +pkgFile=TDengine-server-${verNumber}-Linux-${cpuType}.tar.gz + +echo "cpuType=${cpuType} verNumber=${verNumber} pkgFile=${pkgFile} " + +scriptDir=`pwd` +pkgDir=$scriptDir/../../release/ + +cp -f ${pkgDir}/${pkgFile} . + +./dockerbuild.sh -c ${cpuType} -f ${pkgFile} -n ${verNumber} -p ${passWord} + +rm -f ${pkgFile} diff --git a/packaging/release.bat b/packaging/release.bat index a3fa575837d8bc95b2c4d6800c7bd119bdfc9c25..c1cf7875a505852ce3f8c0b78029fedf481aed8f 100644 --- a/packaging/release.bat +++ b/packaging/release.bat @@ -1,62 +1,62 @@ -@echo off - -set internal_dir=%~dp0\..\..\ -set community_dir=%~dp0\.. -cd %community_dir% -git checkout -- . -cd %community_dir%\packaging - -:: %1 name %2 version -if !%1==! GOTO USAGE -if !%2==! GOTO USAGE -if %1 == taos GOTO TAOS -if %1 == power GOTO POWER -if %1 == tq GOTO TQ -if %1 == pro GOTO PRO -if %1 == kh GOTO KH -if %1 == jh GOTO JH -GOTO USAGE - -:TAOS -goto RELEASE - -:POWER -call sed_power.bat %community_dir% -goto RELEASE - -:TQ -call sed_tq.bat %community_dir% -goto RELEASE - -:PRO -call sed_pro.bat %community_dir% -goto RELEASE - -:KH -call sed_kh.bat %community_dir% -goto RELEASE - -:JH -call sed_jh.bat %community_dir% -goto RELEASE - -:RELEASE -echo release windows-client-64 for %1, version: %2 -if not exist %internal_dir%\debug\ver-%2-64bit-%1 ( - md %internal_dir%\debug\ver-%2-64bit-%1 -) else ( - rd /S /Q %internal_dir%\debug\ver-%2-64bit-%1 - md %internal_dir%\debug\ver-%2-64bit-%1 -) -cd %internal_dir%\debug\ver-%2-64bit-%1 -call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64 -cmake ../../ -G "NMake Makefiles" -DVERNUMBER=%2 -DCPUTYPE=x64 -set CL=/MP4 -nmake install -goto EXIT0 - -:USAGE -echo Usage: release.bat $productName $version -goto EXIT0 - +@echo off + +set internal_dir=%~dp0\..\..\ +set community_dir=%~dp0\.. +cd %community_dir% +git checkout -- . +cd %community_dir%\packaging + +:: %1 name %2 version +if !%1==! GOTO USAGE +if !%2==! GOTO USAGE +if %1 == taos GOTO TAOS +if %1 == power GOTO POWER +if %1 == tq GOTO TQ +if %1 == pro GOTO PRO +if %1 == kh GOTO KH +if %1 == jh GOTO JH +GOTO USAGE + +:TAOS +goto RELEASE + +:POWER +call sed_power.bat %community_dir% +goto RELEASE + +:TQ +call sed_tq.bat %community_dir% +goto RELEASE + +:PRO +call sed_pro.bat %community_dir% +goto RELEASE + +:KH +call sed_kh.bat %community_dir% +goto RELEASE + +:JH +call sed_jh.bat %community_dir% +goto RELEASE + +:RELEASE +echo release windows-client-64 for %1, version: %2 +if not exist %internal_dir%\debug\ver-%2-64bit-%1 ( + md %internal_dir%\debug\ver-%2-64bit-%1 +) else ( + rd /S /Q %internal_dir%\debug\ver-%2-64bit-%1 + md %internal_dir%\debug\ver-%2-64bit-%1 +) +cd %internal_dir%\debug\ver-%2-64bit-%1 +call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64 +cmake ../../ -G "NMake Makefiles" -DVERNUMBER=%2 -DCPUTYPE=x64 +set CL=/MP4 +nmake install +goto EXIT0 + +:USAGE +echo Usage: release.bat $productName $version +goto EXIT0 + :EXIT0 \ No newline at end of file diff --git a/packaging/release.sh b/packaging/release.sh index 9230cafa85bb684c0f5dda533fb2dcda1c288156..00a4ad7009d9b5293bc5b0ee0efd566159c69450 100755 --- a/packaging/release.sh +++ b/packaging/release.sh @@ -1,94 +1,315 @@ #!/bin/bash # -# Generate the tar.gz package for linux os +# Generate the deb package for ubuntu, or rpm package for centos, or tar.gz package for other linux os set -e #set -x +# release.sh -v [cluster | edge] +# -c [aarch32 | aarch64 | x64 | x86 | mips64 ...] +# -o [Linux | Kylin | Alpine | Raspberrypi | Darwin | Windows | Ningsi60 | Ningsi80 |...] +# -V [stable | beta] +# -l [full | lite] +# -s [static | dynamic] +# -d [taos | ...] +# -n [2.0.0.3] +# -m [2.0.0.0] +# -H [ false | true] + # set parameters by default value -version="3.0.0.0" +verMode=edge # [cluster, edge] +verType=stable # [stable, beta] +cpuType=x64 # [aarch32 | aarch64 | x64 | x86 | mips64 ...] +osType=Linux # [Linux | Kylin | Alpine | Raspberrypi | Darwin | Windows | Ningsi60 | Ningsi80 |...] +pagMode=full # [full | lite] +soMode=dynamic # [static | dynamic] +dbName=taos # [taos | ...] +allocator=glibc # [glibc | jemalloc] +verNumber="" +verNumberComp="2.0.0.0" +httpdBuild=false + +while getopts "hv:V:c:o:l:s:d:a:n:m:H:" arg; do + case $arg in + v) + #echo "verMode=$OPTARG" + verMode=$(echo $OPTARG) + ;; + V) + #echo "verType=$OPTARG" + verType=$(echo $OPTARG) + ;; + c) + #echo "cpuType=$OPTARG" + cpuType=$(echo $OPTARG) + ;; + l) + #echo "pagMode=$OPTARG" + pagMode=$(echo $OPTARG) + ;; + s) + #echo "soMode=$OPTARG" + soMode=$(echo $OPTARG) + ;; + d) + #echo "dbName=$OPTARG" + dbName=$(echo $OPTARG) + ;; + a) + #echo "allocator=$OPTARG" + allocator=$(echo $OPTARG) + ;; + n) + #echo "verNumber=$OPTARG" + verNumber=$(echo $OPTARG) + ;; + m) + #echo "verNumberComp=$OPTARG" + verNumberComp=$(echo $OPTARG) + ;; + o) + #echo "osType=$OPTARG" + osType=$(echo $OPTARG) + ;; + H) + #echo "httpdBuild=$OPTARG" + httpdBuild=$(echo $OPTARG) + ;; + h) + echo "Usage: $(basename $0) -v [cluster | edge] " + echo " -c [aarch32 | aarch64 | x64 | x86 | mips64 ...] " + echo " -o [Linux | Kylin | Alpine | Raspberrypi | Darwin | Windows | Ningsi60 | Ningsi80 |...] " + echo " -V [stable | beta] " + echo " -l [full | lite] " + echo " -a [glibc | jemalloc] " + echo " -s [static | dynamic] " + echo " -d [taos | ...] " + echo " -n [version number] " + echo " -m [compatible version number] " + echo " -H [false | true] " + exit 0 + ;; + ?) #unknow option + echo "unkonw argument" + exit 1 + ;; + esac +done + +echo "verMode=${verMode} verType=${verType} cpuType=${cpuType} osType=${osType} pagMode=${pagMode} soMode=${soMode} dbName=${dbName} allocator=${allocator} verNumber=${verNumber} verNumberComp=${verNumberComp} httpdBuild=${httpdBuild}" curr_dir=$(pwd) -script_dir="$(dirname $(readlink -f $0))" -top_dir="$(readlink -f ${script_dir}/..)" +if [ "$osType" == "Darwin" ]; then + script_dir=$(dirname $0) + cd ${script_dir} + script_dir="$(pwd)" + top_dir=${script_dir}/.. +else + script_dir="$(dirname $(readlink -f $0))" + top_dir="$(readlink -f ${script_dir}/..)" +fi + +csudo="" +if command -v sudo > /dev/null; then + csudo="sudo " +fi + +function is_valid_version() { + [ -z $1 ] && return 1 || : + + rx='^([0-9]+\.){3}(\*|[0-9]+)$' + if [[ $1 =~ $rx ]]; then + return 0 + fi + return 1 +} + +function vercomp() { + if [[ $1 == $2 ]]; then + echo 0 + exit 0 + fi + + local IFS=. + local i ver1=($1) ver2=($2) + + # fill empty fields in ver1 with zeros + for ((i = ${#ver1[@]}; i < ${#ver2[@]}; i++)); do + ver1[i]=0 + done + + for ((i = 0; i < ${#ver1[@]}; i++)); do + if [[ -z ${ver2[i]} ]]; then + # fill empty fields in ver2 with zeros + ver2[i]=0 + fi + if ((10#${ver1[i]} > 10#${ver2[i]})); then + echo 1 + exit 0 + fi + if ((10#${ver1[i]} < 10#${ver2[i]})); then + echo 2 + exit 0 + fi + done + echo 0 +} + +# 1. check version information +if ( (! is_valid_version $verNumber) || (! is_valid_version $verNumberComp) || [[ "$(vercomp $verNumber $verNumberComp)" == '2' ]]); then + echo "please enter correct version" + exit 0 +fi -echo "=======================new version number: ${verNumber}======================================" +echo "=======================new version number: ${verNumber}, compatible version: ${verNumberComp}======================================" build_time=$(date +"%F %R") -echo "script_dir: ${script_dir}" -echo "top_dir: ${top_dir}" +# get commint id from git +gitinfo=$(git rev-parse --verify HEAD) -cd ${top_dir} -# git checkout -- . -# git checkout 3.0 -# git pull || : +if [[ "$verMode" == "cluster" ]]; then + enterprise_dir="${top_dir}/../enterprise" + cd ${enterprise_dir} + gitinfoOfInternal=$(git rev-parse --verify HEAD) +else + gitinfoOfInternal=NULL +fi -echo "curr_dir: ${curr_dir}" +cd "${curr_dir}" # 2. cmake executable file compile_dir="${top_dir}/debug" -# if [ -d ${compile_dir} ]; then -# rm -rf ${compile_dir} -# fi - -mkdir -p ${compile_dir} +if [ -d ${compile_dir} ]; then + ${csudo}rm -rf ${compile_dir} +fi +if [ "$osType" != "Darwin" ]; then + ${csudo}mkdir -p ${compile_dir} +else + mkdir -p ${compile_dir} +fi cd ${compile_dir} -echo "compile_dir: ${compile_dir}" +if [[ "$allocator" == "jemalloc" ]]; then + allocator_macro="-DJEMALLOC_ENABLED=true" +else + allocator_macro="" +fi + +if [[ "$dbName" != "taos" ]]; then + source ${enterprise_dir}/packaging/oem/sed_$dbName.sh + replace_community_$dbName +fi + +if [[ "$httpdBuild" == "true" ]]; then + BUILD_HTTP=true +else + BUILD_HTTP=false +fi -cmake .. -DBUILD_TOOLS=true -make -j32 +if [[ "$verMode" == "cluster" ]]; then + BUILD_HTTP=internal +fi -release_dir="${top_dir}/release" -if [ -d ${release_dir} ]; then - rm -rf ${release_dir} +if [[ "$pagMode" == "full" ]]; then + BUILD_TOOLS=true +else + BUILD_TOOLS=false fi -mkdir -p ${release_dir} -cd ${release_dir} +# check support cpu type +if [[ "$cpuType" == "x64" ]] || [[ "$cpuType" == "aarch64" ]] || [[ "$cpuType" == "aarch32" ]] || [[ "$cpuType" == "mips64" ]]; then + if [ "$verMode" != "cluster" ]; then + # community-version compile + cmake ../ -DCPUTYPE=${cpuType} -DOSTYPE=${osType} -DSOMODE=${soMode} -DDBNAME=${dbName} -DVERTYPE=${verType} -DVERDATE="${build_time}" -DGITINFO=${gitinfo} -DGITINFOI=${gitinfoOfInternal} -DVERNUMBER=${verNumber} -DVERCOMPATIBLE=${verNumberComp} -DPAGMODE=${pagMode} -DBUILD_HTTP=${BUILD_HTTP} -DBUILD_TOOLS=${BUILD_TOOLS} ${allocator_macro} + else + if [[ "$dbName" != "taos" ]]; then + replace_enterprise_$dbName + fi + cmake ../../ -DCPUTYPE=${cpuType} -DOSTYPE=${osType} -DSOMODE=${soMode} -DDBNAME=${dbName} -DVERTYPE=${verType} -DVERDATE="${build_time}" -DGITINFO=${gitinfo} -DGITINFOI=${gitinfoOfInternal} -DVERNUMBER=${verNumber} -DVERCOMPATIBLE=${verNumberComp} -DBUILD_HTTP=${BUILD_HTTP} -DBUILD_TOOLS=${BUILD_TOOLS} ${allocator_macro} + fi +else + echo "input cpuType=${cpuType} error!!!" + exit 1 +fi -install_dir="${release_dir}/TDengine-server-${version}" -mkdir -p ${install_dir} -mkdir -p ${install_dir}/bin -mkdir -p ${install_dir}/lib -mkdir -p ${install_dir}/inc +CORES=$(grep -c ^processor /proc/cpuinfo) -install_files="${script_dir}/tools/install.sh" -chmod a+x ${script_dir}/tools/install.sh || : -cp ${install_files} ${install_dir} +if [[ "$allocator" == "jemalloc" ]]; then + # jemalloc need compile first, so disable parallel build + make -j ${CORES} && ${csudo}make install +else + make -j ${CORES} && ${csudo}make install +fi + +cd ${curr_dir} -header_files="${top_dir}/include/client/taos.h ${top_dir}/include/util/taoserror.h" -cp ${header_files} ${install_dir}/inc - -bin_files="${compile_dir}/build/bin/taosd ${compile_dir}/build/bin/taos ${compile_dir}/build/bin/create_table ${compile_dir}/build/bin/tmq_sim ${script_dir}/tools/remove.sh ${compile_dir}/build/bin/taosBenchmark ${compile_dir}/build/bin/taosdump" -cp -rf ${bin_files} ${install_dir}/bin && chmod a+x ${install_dir}/bin/* || : +# 3. Call the corresponding script for packaging +if [ "$osType" != "Darwin" ]; then + if [[ "$verMode" != "cluster" ]] && [[ "$pagMode" == "full" ]] && [[ "$cpuType" == "x64" ]] && [[ "$dbName" == "taos" ]]; then + ret='0' + command -v dpkg >/dev/null 2>&1 || { ret='1'; } + if [ "$ret" -eq 0 ]; then + echo "====do deb package for the ubuntu system====" + output_dir="${top_dir}/debs" + if [ -d ${output_dir} ]; then + ${csudo}rm -rf ${output_dir} + fi + ${csudo}mkdir -p ${output_dir} + cd ${script_dir}/deb + ${csudo}./makedeb.sh ${compile_dir} ${output_dir} ${verNumber} ${cpuType} ${osType} ${verMode} ${verType} -cp ${compile_dir}/build/lib/libtaos.so ${install_dir}/lib/ -cp ${compile_dir}/build/lib/libavro* ${install_dir}/lib/ > /dev/null || echo -e "failed to copy avro libraries" -cp -rf ${compile_dir}/build/lib/pkgconfig ${install_dir}/lib/ > /dev/null || echo -e "failed to copy pkgconfig directory" + if [[ "$pagMode" == "full" ]]; then + if [ -d ${top_dir}/tools/taos-tools/packaging/deb ]; then + cd ${top_dir}/tools/taos-tools/packaging/deb + [ -z "$taos_tools_ver" ] && taos_tools_ver="0.1.0" + taos_tools_ver=$(git describe --tags | sed -e 's/ver-//g' | awk -F '-' '{print $1}') + ${csudo}./make-taos-tools-deb.sh ${top_dir} \ + ${compile_dir} ${output_dir} ${taos_tools_ver} ${cpuType} ${osType} ${verMode} ${verType} + fi + fi + else + echo "==========dpkg command not exist, so not release deb package!!!" + fi + ret='0' + command -v rpmbuild >/dev/null 2>&1 || { ret='1'; } + if [ "$ret" -eq 0 ]; then + echo "====do rpm package for the centos system====" + output_dir="${top_dir}/rpms" + if [ -d ${output_dir} ]; then + ${csudo}rm -rf ${output_dir} + fi + ${csudo}mkdir -p ${output_dir} + cd ${script_dir}/rpm + ${csudo}./makerpm.sh ${compile_dir} ${output_dir} ${verNumber} ${cpuType} ${osType} ${verMode} ${verType} -#cp ${compile_dir}/source/dnode/mnode/impl/libmnode.so ${install_dir}/lib/ -#cp ${compile_dir}/source/dnode/qnode/libqnode.so ${install_dir}/lib/ -#cp ${compile_dir}/source/dnode/snode/libsnode.so ${install_dir}/lib/ -#cp ${compile_dir}/source/dnode/bnode/libbnode.so ${install_dir}/lib/ -#cp ${compile_dir}/source/libs/wal/libwal.so ${install_dir}/lib/ -#cp ${compile_dir}/source/libs/scheduler/libscheduler.so ${install_dir}/lib/ -#cp ${compile_dir}/source/libs/planner/libplanner.so ${install_dir}/lib/ -#cp ${compile_dir}/source/libs/parser/libparser.so ${install_dir}/lib/ -#cp ${compile_dir}/source/libs/qcom/libqcom.so ${install_dir}/lib/ -#cp ${compile_dir}/source/libs/transport/libtransport.so ${install_dir}/lib/ -#cp ${compile_dir}/source/libs/function/libfunction.so ${install_dir}/lib/ -#cp ${compile_dir}/source/common/libcommon.so ${install_dir}/lib/ -#cp ${compile_dir}/source/os/libos.so ${install_dir}/lib/ -#cp ${compile_dir}/source/dnode/mnode/sdb/libsdb.so ${install_dir}/lib/ -#cp ${compile_dir}/source/libs/catalog/libcatalog.so ${install_dir}/lib/ + if [[ "$pagMode" == "full" ]]; then + if [ -d ${top_dir}/tools/taos-tools/packaging/rpm ]; then + cd ${top_dir}/tools/taos-tools/packaging/rpm + [ -z "$taos_tools_ver" ] && taos_tools_ver="0.1.0" -pkg_name=${install_dir}-Linux-x64 + taos_tools_ver=$(git describe --tags | sed -e 's/ver-//g' | awk -F '-' '{print $1}' | sed -e 's/-/_/g') + ${csudo}./make-taos-tools-rpm.sh ${top_dir} \ + ${compile_dir} ${output_dir} ${taos_tools_ver} ${cpuType} ${osType} ${verMode} ${verType} + fi + fi + else + echo "==========rpmbuild command not exist, so not release rpm package!!!" + fi + fi -tar -zcv -f "$(basename ${pkg_name}).tar.gz" $(basename ${install_dir}) --remove-files || : + echo "====do tar.gz package for all systems====" + cd ${script_dir}/tools + ${csudo}./makepkg.sh ${compile_dir} ${verNumber} "${build_time}" ${cpuType} ${osType} ${verMode} ${verType} ${pagMode} ${verNumberComp} ${dbName} + ${csudo}./makeclient.sh ${compile_dir} ${verNumber} "${build_time}" ${cpuType} ${osType} ${verMode} ${verType} ${pagMode} ${dbName} + # ${csudo}./makearbi.sh ${compile_dir} ${verNumber} "${build_time}" ${cpuType} ${osType} ${verMode} ${verType} ${pagMode} +else + # only make client for Darwin + cd ${script_dir}/tools + ./makeclient.sh ${compile_dir} ${verNumber} "${build_time}" ${cpuType} ${osType} ${verMode} ${verType} ${pagMode} ${dbName} +fi diff --git a/packaging/rpm/makerpm.sh b/packaging/rpm/makerpm.sh index 31673488e6fc8b4b226168f2ad95eb63af53fd72..4ac67ec754ce230f9a777570c42a9300c757978d 100644 --- a/packaging/rpm/makerpm.sh +++ b/packaging/rpm/makerpm.sh @@ -1,87 +1,87 @@ -#!/bin/bash -# -# Generate rpm package for centos - -set -e -# set -x - -#curr_dir=$(pwd) -compile_dir=$1 -output_dir=$2 -tdengine_ver=$3 -cpuType=$4 -osType=$5 -verMode=$6 -verType=$7 - -script_dir="$(dirname $(readlink -f $0))" -top_dir="$(readlink -f ${script_dir}/../..)" -pkg_dir="${top_dir}/rpmworkroom" -spec_file="${script_dir}/tdengine.spec" - -#echo "curr_dir: ${curr_dir}" -#echo "top_dir: ${top_dir}" -#echo "script_dir: ${script_dir}" -echo "compile_dir: ${compile_dir}" -echo "pkg_dir: ${pkg_dir}" -echo "spec_file: ${spec_file}" - -csudo="" -if command -v sudo > /dev/null; then - csudo="sudo " -fi - -function cp_rpm_package() { - local cur_dir - cd $1 - cur_dir=$(pwd) - - for dirlist in "$(ls ${cur_dir})"; do - if test -d ${dirlist}; then - cd ${dirlist} - cp_rpm_package ${cur_dir}/${dirlist} - cd .. - fi - if test -e ${dirlist}; then - cp ${cur_dir}/${dirlist} ${output_dir}/TDengine-${tdengine_ver}.rpm - fi - done -} - -if [ -d ${pkg_dir} ]; then - ${csudo}rm -rf ${pkg_dir} -fi -${csudo}mkdir -p ${pkg_dir} -cd ${pkg_dir} - -${csudo}mkdir -p BUILD BUILDROOT RPMS SOURCES SPECS SRPMS - -${csudo}rpmbuild --define="_version ${tdengine_ver}" --define="_topdir ${pkg_dir}" --define="_compiledir ${compile_dir}" -bb ${spec_file} - -# copy rpm package to output_dir, and modify package name, then clean temp dir -#${csudo}cp -rf RPMS/* ${output_dir} -cp_rpm_package ${pkg_dir}/RPMS - - -if [ "$verMode" == "cluster" ]; then - rpmname="TDengine-server-"${tdengine_ver}-${osType}-${cpuType} -elif [ "$verMode" == "edge" ]; then - rpmname="TDengine-server"-${tdengine_ver}-${osType}-${cpuType} -else - echo "unknow verMode, nor cluster or edge" - exit 1 -fi - -if [ "$verType" == "beta" ]; then - rpmname="TDengine-server-"${tdengine_ver}-${verType}-${osType}-${cpuType}".rpm" -elif [ "$verType" == "stable" ]; then - rpmname=${rpmname}".rpm" -else - echo "unknow verType, nor stabel or beta" - exit 1 -fi - -mv ${output_dir}/TDengine-${tdengine_ver}.rpm ${output_dir}/${rpmname} - -cd .. -${csudo}rm -rf ${pkg_dir} +#!/bin/bash +# +# Generate rpm package for centos + +set -e +# set -x + +#curr_dir=$(pwd) +compile_dir=$1 +output_dir=$2 +tdengine_ver=$3 +cpuType=$4 +osType=$5 +verMode=$6 +verType=$7 + +script_dir="$(dirname $(readlink -f $0))" +top_dir="$(readlink -f ${script_dir}/../..)" +pkg_dir="${top_dir}/rpmworkroom" +spec_file="${script_dir}/tdengine.spec" + +#echo "curr_dir: ${curr_dir}" +#echo "top_dir: ${top_dir}" +#echo "script_dir: ${script_dir}" +echo "compile_dir: ${compile_dir}" +echo "pkg_dir: ${pkg_dir}" +echo "spec_file: ${spec_file}" + +csudo="" +if command -v sudo > /dev/null; then + csudo="sudo " +fi + +function cp_rpm_package() { + local cur_dir + cd $1 + cur_dir=$(pwd) + + for dirlist in "$(ls ${cur_dir})"; do + if test -d ${dirlist}; then + cd ${dirlist} + cp_rpm_package ${cur_dir}/${dirlist} + cd .. + fi + if test -e ${dirlist}; then + cp ${cur_dir}/${dirlist} ${output_dir}/TDengine-${tdengine_ver}.rpm + fi + done +} + +if [ -d ${pkg_dir} ]; then + ${csudo}rm -rf ${pkg_dir} +fi +${csudo}mkdir -p ${pkg_dir} +cd ${pkg_dir} + +${csudo}mkdir -p BUILD BUILDROOT RPMS SOURCES SPECS SRPMS + +${csudo}rpmbuild --define="_version ${tdengine_ver}" --define="_topdir ${pkg_dir}" --define="_compiledir ${compile_dir}" -bb ${spec_file} + +# copy rpm package to output_dir, and modify package name, then clean temp dir +#${csudo}cp -rf RPMS/* ${output_dir} +cp_rpm_package ${pkg_dir}/RPMS + + +if [ "$verMode" == "cluster" ]; then + rpmname="TDengine-server-"${tdengine_ver}-${osType}-${cpuType} +elif [ "$verMode" == "edge" ]; then + rpmname="TDengine-server"-${tdengine_ver}-${osType}-${cpuType} +else + echo "unknow verMode, nor cluster or edge" + exit 1 +fi + +if [ "$verType" == "beta" ]; then + rpmname="TDengine-server-"${tdengine_ver}-${verType}-${osType}-${cpuType}".rpm" +elif [ "$verType" == "stable" ]; then + rpmname=${rpmname}".rpm" +else + echo "unknow verType, nor stabel or beta" + exit 1 +fi + +mv ${output_dir}/TDengine-${tdengine_ver}.rpm ${output_dir}/${rpmname} + +cd .. +${csudo}rm -rf ${pkg_dir} diff --git a/packaging/rpm/taosd b/packaging/rpm/taosd index b79361f36e0f85085590acd81178bf7294222a54..f8a5a2357ea1e8f399d0692f1b0e0d6398e8f855 100644 --- a/packaging/rpm/taosd +++ b/packaging/rpm/taosd @@ -1,145 +1,145 @@ -#!/bin/bash -# -# taosd This shell script takes care of starting and stopping TDengine. -# -# chkconfig: 2345 99 01 -# description: TDengine is a districuted, scalable, high-performance Time Series Database -# (TSDB). More than just a pure database, TDengine also provides the ability -# to do stream computing, aggregation etc. -# -# -### BEGIN INIT INFO -# Provides: taosd -# Required-Start: $network $local_fs $remote_fs -# Required-Stop: $network $local_fs $remote_fs -# Short-Description: start and stop taosd -# Description: TDengine is a districuted, scalable, high-performance Time Series Database -# (TSDB). More than just a pure database, TDengine also provides the ability -# to do stream computing, aggregation etc. -### END INIT INFO - -# Source init functions -. /etc/init.d/functions - -# Maximum number of open files -MAX_OPEN_FILES=65535 - -# Default program options -NAME=taosd -PROG=/usr/local/taos/bin/taosd -USER=root -GROUP=root - -# Default directories -LOCK_DIR=/var/lock/subsys -PID_DIR=/var/run/$NAME - -# Set file names -LOCK_FILE=$LOCK_DIR/$NAME -PID_FILE=$PID_DIR/$NAME.pid - -[ -e $PID_DIR ] || mkdir -p $PID_DIR - -PROG_OPTS="" - -start() { - echo -n "Starting ${NAME}: " - # check identity - curid="`id -u -n`" - if [ "$curid" != root ] && [ "$curid" != "$USER" ] ; then - echo "Must be run as root or $USER, but was run as $curid" - return 1 - fi - # Sets the maximum number of open file descriptors allowed. - ulimit -n $MAX_OPEN_FILES - curulimit="`ulimit -n`" - if [ "$curulimit" -lt $MAX_OPEN_FILES ] ; then - echo "'ulimit -n' must be greater than or equal to $MAX_OPEN_FILES, is $curulimit" - return 1 - fi - - if [ "`id -u -n`" == root ] ; then - # Changes the owner of the lock, and the pid files to allow - # non-root OpenTSDB daemons to run /usr/share/opentsdb/bin/opentsdb_restart.py. - touch $LOCK_FILE && chown $USER:$GROUP $LOCK_FILE - touch $PID_FILE && chown $USER:$GROUP $PID_FILE - daemon --user $USER --pidfile $PID_FILE "$PROG $PROG_OPTS &> /dev/null &" - else - # Don't have to change user. - daemon --pidfile $PID_FILE "$PROG $PROG_OPTS &> /dev/null &" - fi - retval=$? - sleep 2 - echo - [ $retval -eq 0 ] && (findproc > $PID_FILE && touch $LOCK_FILE) - return $retval -} - -stop() { - echo -n "Stopping ${NAME}: " - killproc -p $PID_FILE $NAME - retval=$? - echo - # Non-root users don't have enough permission to remove pid and lock files. - # So, the opentsdb_restart.py cannot get rid of the files, and the command - # "service opentsdb status" will complain about the existing pid file. - # Makes the pid file empty. - echo > $PID_FILE - [ $retval -eq 0 ] && (rm -f $PID_FILE && rm -f $LOCK_FILE) - return $retval -} - -restart() { - stop - start -} - -reload() { - restart -} - -force_reload() { - restart -} - -rh_status() { - # run checks to determine if the service is running or use generic status - status -p $PID_FILE -l $LOCK_FILE $NAME -} - -rh_status_q() { - rh_status >/dev/null 2>&1 -} - -case "$1" in - start) - rh_status_q && exit 0 - $1 - ;; - stop) - rh_status_q || exit 0 - $1 - ;; - restart) - $1 - ;; - reload) - rh_status_q || exit 7 - $1 - ;; - force-reload) - force_reload - ;; - status) - rh_status - ;; - condrestart|try-restart) - rh_status_q || exit 0 - restart - ;; - *) - echo "Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}" - exit 2 -esac - -exit $? +#!/bin/bash +# +# taosd This shell script takes care of starting and stopping TDengine. +# +# chkconfig: 2345 99 01 +# description: TDengine is a districuted, scalable, high-performance Time Series Database +# (TSDB). More than just a pure database, TDengine also provides the ability +# to do stream computing, aggregation etc. +# +# +### BEGIN INIT INFO +# Provides: taosd +# Required-Start: $network $local_fs $remote_fs +# Required-Stop: $network $local_fs $remote_fs +# Short-Description: start and stop taosd +# Description: TDengine is a districuted, scalable, high-performance Time Series Database +# (TSDB). More than just a pure database, TDengine also provides the ability +# to do stream computing, aggregation etc. +### END INIT INFO + +# Source init functions +. /etc/init.d/functions + +# Maximum number of open files +MAX_OPEN_FILES=65535 + +# Default program options +NAME=taosd +PROG=/usr/local/taos/bin/taosd +USER=root +GROUP=root + +# Default directories +LOCK_DIR=/var/lock/subsys +PID_DIR=/var/run/$NAME + +# Set file names +LOCK_FILE=$LOCK_DIR/$NAME +PID_FILE=$PID_DIR/$NAME.pid + +[ -e $PID_DIR ] || mkdir -p $PID_DIR + +PROG_OPTS="" + +start() { + echo -n "Starting ${NAME}: " + # check identity + curid="`id -u -n`" + if [ "$curid" != root ] && [ "$curid" != "$USER" ] ; then + echo "Must be run as root or $USER, but was run as $curid" + return 1 + fi + # Sets the maximum number of open file descriptors allowed. + ulimit -n $MAX_OPEN_FILES + curulimit="`ulimit -n`" + if [ "$curulimit" -lt $MAX_OPEN_FILES ] ; then + echo "'ulimit -n' must be greater than or equal to $MAX_OPEN_FILES, is $curulimit" + return 1 + fi + + if [ "`id -u -n`" == root ] ; then + # Changes the owner of the lock, and the pid files to allow + # non-root OpenTSDB daemons to run /usr/share/opentsdb/bin/opentsdb_restart.py. + touch $LOCK_FILE && chown $USER:$GROUP $LOCK_FILE + touch $PID_FILE && chown $USER:$GROUP $PID_FILE + daemon --user $USER --pidfile $PID_FILE "$PROG $PROG_OPTS &> /dev/null &" + else + # Don't have to change user. + daemon --pidfile $PID_FILE "$PROG $PROG_OPTS &> /dev/null &" + fi + retval=$? + sleep 2 + echo + [ $retval -eq 0 ] && (findproc > $PID_FILE && touch $LOCK_FILE) + return $retval +} + +stop() { + echo -n "Stopping ${NAME}: " + killproc -p $PID_FILE $NAME + retval=$? + echo + # Non-root users don't have enough permission to remove pid and lock files. + # So, the opentsdb_restart.py cannot get rid of the files, and the command + # "service opentsdb status" will complain about the existing pid file. + # Makes the pid file empty. + echo > $PID_FILE + [ $retval -eq 0 ] && (rm -f $PID_FILE && rm -f $LOCK_FILE) + return $retval +} + +restart() { + stop + start +} + +reload() { + restart +} + +force_reload() { + restart +} + +rh_status() { + # run checks to determine if the service is running or use generic status + status -p $PID_FILE -l $LOCK_FILE $NAME +} + +rh_status_q() { + rh_status >/dev/null 2>&1 +} + +case "$1" in + start) + rh_status_q && exit 0 + $1 + ;; + stop) + rh_status_q || exit 0 + $1 + ;; + restart) + $1 + ;; + reload) + rh_status_q || exit 7 + $1 + ;; + force-reload) + force_reload + ;; + status) + rh_status + ;; + condrestart|try-restart) + rh_status_q || exit 0 + restart + ;; + *) + echo "Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}" + exit 2 +esac + +exit $? diff --git a/packaging/rpm/tarbitratord b/packaging/rpm/tarbitratord index 34fedab3d9330b0f6b88bf110e21a4d6f08955db..68138f5c1d5d4491b5fda52b08cfd51a039ffc64 100644 --- a/packaging/rpm/tarbitratord +++ b/packaging/rpm/tarbitratord @@ -1,141 +1,141 @@ -#!/bin/bash -# -# tarbitratord This shell script takes care of starting and stopping tarbitrator. -# -# chkconfig: 2345 99 01 -# description: tarbitrator is a arbitrator used in TDengine cluster. -# -# -### BEGIN INIT INFO -# Provides: taoscluster -# Required-Start: $network $local_fs $remote_fs -# Required-Stop: $network $local_fs $remote_fs -# Short-Description: start and stop tarbitrator -# Description: tarbitrator is a arbitrator used in TDengine cluster. -### END INIT INFO - -# Source init functions -. /etc/init.d/functions - -# Maximum number of open files -MAX_OPEN_FILES=65535 - -# Default program options -NAME=tarbitrator -PROG=/usr/local/taos/bin/tarbitrator -USER=root -GROUP=root - -# Default directories -LOCK_DIR=/var/lock/subsys -PID_DIR=/var/run/$NAME - -# Set file names -LOCK_FILE=$LOCK_DIR/$NAME -PID_FILE=$PID_DIR/$NAME.pid - -[ -e $PID_DIR ] || mkdir -p $PID_DIR - -PROG_OPTS="" - -start() { - echo -n "Starting ${NAME}: " - # check identity - curid="`id -u -n`" - if [ "$curid" != root ] && [ "$curid" != "$USER" ] ; then - echo "Must be run as root or $USER, but was run as $curid" - return 1 - fi - # Sets the maximum number of open file descriptors allowed. - ulimit -n $MAX_OPEN_FILES - curulimit="`ulimit -n`" - if [ "$curulimit" -lt $MAX_OPEN_FILES ] ; then - echo "'ulimit -n' must be greater than or equal to $MAX_OPEN_FILES, is $curulimit" - return 1 - fi - - if [ "`id -u -n`" == root ] ; then - # Changes the owner of the lock, and the pid files to allow - # non-root OpenTSDB daemons to run /usr/share/opentsdb/bin/opentsdb_restart.py. - touch $LOCK_FILE && chown $USER:$GROUP $LOCK_FILE - touch $PID_FILE && chown $USER:$GROUP $PID_FILE - daemon --user $USER --pidfile $PID_FILE "$PROG $PROG_OPTS &> /dev/null &" - else - # Don't have to change user. - daemon --pidfile $PID_FILE "$PROG $PROG_OPTS &> /dev/null &" - fi - retval=$? - sleep 2 - echo - [ $retval -eq 0 ] && (findproc > $PID_FILE && touch $LOCK_FILE) - return $retval -} - -stop() { - echo -n "Stopping ${NAME}: " - killproc -p $PID_FILE $NAME - retval=$? - echo - # Non-root users don't have enough permission to remove pid and lock files. - # So, the opentsdb_restart.py cannot get rid of the files, and the command - # "service opentsdb status" will complain about the existing pid file. - # Makes the pid file empty. - echo > $PID_FILE - [ $retval -eq 0 ] && (rm -f $PID_FILE && rm -f $LOCK_FILE) - return $retval -} - -restart() { - stop - start -} - -reload() { - restart -} - -force_reload() { - restart -} - -rh_status() { - # run checks to determine if the service is running or use generic status - status -p $PID_FILE -l $LOCK_FILE $NAME -} - -rh_status_q() { - rh_status >/dev/null 2>&1 -} - -case "$1" in - start) - rh_status_q && exit 0 - $1 - ;; - stop) - rh_status_q || exit 0 - $1 - ;; - restart) - $1 - ;; - reload) - rh_status_q || exit 7 - $1 - ;; - force-reload) - force_reload - ;; - status) - rh_status - ;; - condrestart|try-restart) - rh_status_q || exit 0 - restart - ;; - *) - echo "Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}" - exit 2 -esac - -exit $? +#!/bin/bash +# +# tarbitratord This shell script takes care of starting and stopping tarbitrator. +# +# chkconfig: 2345 99 01 +# description: tarbitrator is a arbitrator used in TDengine cluster. +# +# +### BEGIN INIT INFO +# Provides: taoscluster +# Required-Start: $network $local_fs $remote_fs +# Required-Stop: $network $local_fs $remote_fs +# Short-Description: start and stop tarbitrator +# Description: tarbitrator is a arbitrator used in TDengine cluster. +### END INIT INFO + +# Source init functions +. /etc/init.d/functions + +# Maximum number of open files +MAX_OPEN_FILES=65535 + +# Default program options +NAME=tarbitrator +PROG=/usr/local/taos/bin/tarbitrator +USER=root +GROUP=root + +# Default directories +LOCK_DIR=/var/lock/subsys +PID_DIR=/var/run/$NAME + +# Set file names +LOCK_FILE=$LOCK_DIR/$NAME +PID_FILE=$PID_DIR/$NAME.pid + +[ -e $PID_DIR ] || mkdir -p $PID_DIR + +PROG_OPTS="" + +start() { + echo -n "Starting ${NAME}: " + # check identity + curid="`id -u -n`" + if [ "$curid" != root ] && [ "$curid" != "$USER" ] ; then + echo "Must be run as root or $USER, but was run as $curid" + return 1 + fi + # Sets the maximum number of open file descriptors allowed. + ulimit -n $MAX_OPEN_FILES + curulimit="`ulimit -n`" + if [ "$curulimit" -lt $MAX_OPEN_FILES ] ; then + echo "'ulimit -n' must be greater than or equal to $MAX_OPEN_FILES, is $curulimit" + return 1 + fi + + if [ "`id -u -n`" == root ] ; then + # Changes the owner of the lock, and the pid files to allow + # non-root OpenTSDB daemons to run /usr/share/opentsdb/bin/opentsdb_restart.py. + touch $LOCK_FILE && chown $USER:$GROUP $LOCK_FILE + touch $PID_FILE && chown $USER:$GROUP $PID_FILE + daemon --user $USER --pidfile $PID_FILE "$PROG $PROG_OPTS &> /dev/null &" + else + # Don't have to change user. + daemon --pidfile $PID_FILE "$PROG $PROG_OPTS &> /dev/null &" + fi + retval=$? + sleep 2 + echo + [ $retval -eq 0 ] && (findproc > $PID_FILE && touch $LOCK_FILE) + return $retval +} + +stop() { + echo -n "Stopping ${NAME}: " + killproc -p $PID_FILE $NAME + retval=$? + echo + # Non-root users don't have enough permission to remove pid and lock files. + # So, the opentsdb_restart.py cannot get rid of the files, and the command + # "service opentsdb status" will complain about the existing pid file. + # Makes the pid file empty. + echo > $PID_FILE + [ $retval -eq 0 ] && (rm -f $PID_FILE && rm -f $LOCK_FILE) + return $retval +} + +restart() { + stop + start +} + +reload() { + restart +} + +force_reload() { + restart +} + +rh_status() { + # run checks to determine if the service is running or use generic status + status -p $PID_FILE -l $LOCK_FILE $NAME +} + +rh_status_q() { + rh_status >/dev/null 2>&1 +} + +case "$1" in + start) + rh_status_q && exit 0 + $1 + ;; + stop) + rh_status_q || exit 0 + $1 + ;; + restart) + $1 + ;; + reload) + rh_status_q || exit 7 + $1 + ;; + force-reload) + force_reload + ;; + status) + rh_status + ;; + condrestart|try-restart) + rh_status_q || exit 0 + restart + ;; + *) + echo "Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}" + exit 2 +esac + +exit $? diff --git a/packaging/rpm/tdengine.spec b/packaging/rpm/tdengine.spec index 022482c86749739427d532d13db8260ef78995fd..d61d12932f219e16911a998125518205b91664d2 100644 --- a/packaging/rpm/tdengine.spec +++ b/packaging/rpm/tdengine.spec @@ -1,236 +1,236 @@ -%define homepath /usr/local/taos -%define userlocalpath /usr/local -%define cfg_install_dir /etc/taos -%define __strip /bin/true - -Name: tdengine -Version: %{_version} -Release: 3%{?dist} -Summary: tdengine from taosdata -Group: Application/Database -License: AGPL -URL: www.taosdata.com -AutoReqProv: no - -#BuildRoot: %_topdir/BUILDROOT -BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root - -#Prefix: /usr/local/taos - -#BuildRequires: -#Requires: - -%description -Big Data Platform Designed and Optimized for IoT - -#"prep" Nothing needs to be done -#%prep -#%setup -q -#%setup -T - -#"build" Nothing needs to be done -#%build -#%configure -#make %{?_smp_mflags} - -%install -#make install DESTDIR=%{buildroot} -rm -rf %{buildroot} - -echo topdir: %{_topdir} -echo version: %{_version} -echo buildroot: %{buildroot} - -libfile="libtaos.so.%{_version}" - -# create install path, and cp file -mkdir -p %{buildroot}%{homepath}/bin -mkdir -p %{buildroot}%{homepath}/cfg -#mkdir -p %{buildroot}%{homepath}/connector -mkdir -p %{buildroot}%{homepath}/driver -mkdir -p %{buildroot}%{homepath}/examples -mkdir -p %{buildroot}%{homepath}/include -#mkdir -p %{buildroot}%{homepath}/init.d -mkdir -p %{buildroot}%{homepath}/script - -cp %{_compiledir}/../packaging/cfg/taos.cfg %{buildroot}%{homepath}/cfg -if [ -f %{_compiledir}/test/cfg/taosadapter.toml ]; then - cp %{_compiledir}/test/cfg/taosadapter.toml %{buildroot}%{homepath}/cfg -fi -if [ -f %{_compiledir}/test/cfg/taosadapter.service ]; then - cp %{_compiledir}/test/cfg/taosadapter.service %{buildroot}%{homepath}/cfg -fi -#cp %{_compiledir}/../packaging/rpm/taosd %{buildroot}%{homepath}/init.d -cp %{_compiledir}/../packaging/tools/post.sh %{buildroot}%{homepath}/script -cp %{_compiledir}/../packaging/tools/preun.sh %{buildroot}%{homepath}/script -cp %{_compiledir}/../packaging/tools/startPre.sh %{buildroot}%{homepath}/bin -cp %{_compiledir}/../packaging/tools/set_core.sh %{buildroot}%{homepath}/bin -cp %{_compiledir}/../packaging/tools/taosd-dump-cfg.gdb %{buildroot}%{homepath}/bin -cp %{_compiledir}/build/bin/taos %{buildroot}%{homepath}/bin -cp %{_compiledir}/build/bin/taosd %{buildroot}%{homepath}/bin -#cp %{_compiledir}/build/bin/taosBenchmark %{buildroot}%{homepath}/bin - -if [ -f %{_compiledir}/build/bin/taosadapter ]; then - cp %{_compiledir}/build/bin/taosadapter %{buildroot}%{homepath}/bin ||: -fi -cp %{_compiledir}/build/lib/${libfile} %{buildroot}%{homepath}/driver -cp %{_compiledir}/../src/inc/taos.h %{buildroot}%{homepath}/include -cp %{_compiledir}/../src/inc/taosdef.h %{buildroot}%{homepath}/include -cp %{_compiledir}/../src/inc/taoserror.h %{buildroot}%{homepath}/include -#cp -r %{_compiledir}/../src/connector/python %{buildroot}%{homepath}/connector -#cp -r %{_compiledir}/../src/connector/go %{buildroot}%{homepath}/connector -#cp -r %{_compiledir}/../src/connector/nodejs %{buildroot}%{homepath}/connector -#cp %{_compiledir}/build/lib/taos-jdbcdriver*.* %{buildroot}%{homepath}/connector ||: -cp -r %{_compiledir}/../examples/* %{buildroot}%{homepath}/examples - -if [ -f %{_compiledir}/build/bin/jemalloc-config ]; then - mkdir -p %{buildroot}%{userlocalpath}/bin - mkdir -p %{buildroot}%{userlocalpath}/lib - mkdir -p %{buildroot}%{userlocalpath}/lib/pkgconfig - mkdir -p %{buildroot}%{userlocalpath}/include - mkdir -p %{buildroot}%{userlocalpath}/include/jemalloc - mkdir -p %{buildroot}%{userlocalpath}/share - mkdir -p %{buildroot}%{userlocalpath}/share/doc - mkdir -p %{buildroot}%{userlocalpath}/share/doc/jemalloc - mkdir -p %{buildroot}%{userlocalpath}/share/man - mkdir -p %{buildroot}%{userlocalpath}/share/man/man3 - - cp %{_compiledir}/build/bin/jemalloc-config %{buildroot}%{userlocalpath}/bin/ - if [ -f %{_compiledir}/build/bin/jemalloc.sh ]; then - cp %{_compiledir}/build/bin/jemalloc.sh %{buildroot}%{userlocalpath}/bin/ - fi - if [ -f %{_compiledir}/build/bin/jeprof ]; then - cp %{_compiledir}/build/bin/jeprof %{buildroot}%{userlocalpath}/bin/ - fi - if [ -f %{_compiledir}/build/include/jemalloc/jemalloc.h ]; then - cp %{_compiledir}/build/include/jemalloc/jemalloc.h %{buildroot}%{userlocalpath}/include/jemalloc/ - fi - if [ -f %{_compiledir}/build/lib/libjemalloc.so.2 ]; then - cp %{_compiledir}/build/lib/libjemalloc.so.2 %{buildroot}%{userlocalpath}/lib/ - ln -sf libjemalloc.so.2 %{buildroot}%{userlocalpath}/lib/libjemalloc.so - fi - if [ -f %{_compiledir}/build/lib/libjemalloc.a ]; then - cp %{_compiledir}/build/lib/libjemalloc.a %{buildroot}%{userlocalpath}/lib/ - fi - if [ -f %{_compiledir}/build/lib/libjemalloc_pic.a ]; then - cp %{_compiledir}/build/lib/libjemalloc_pic.a %{buildroot}%{userlocalpath}/lib/ - fi - if [ -f %{_compiledir}/build/lib/pkgconfig/jemalloc.pc ]; then - cp %{_compiledir}/build/lib/pkgconfig/jemalloc.pc %{buildroot}%{userlocalpath}/lib/pkgconfig/ - fi - if [ -f %{_compiledir}/build/share/doc/jemalloc/jemalloc.html ]; then - cp %{_compiledir}/build/share/doc/jemalloc/jemalloc.html %{buildroot}%{userlocalpath}/share/doc/jemalloc/ - fi - if [ -f %{_compiledir}/build/share/man/man3/jemalloc.3 ]; then - cp %{_compiledir}/build/share/man/man3/jemalloc.3 %{buildroot}%{userlocalpath}/share/man/man3/ - fi -fi - -#Scripts executed before installation -%pre -csudo="" -if command -v sudo > /dev/null; then - csudo="sudo " -fi - -# Stop the service if running -if pidof taosd &> /dev/null; then - if pidof systemd &> /dev/null; then - ${csudo}systemctl stop taosd || : - elif $(which service &> /dev/null); then - ${csudo}service taosd stop || : - else - pid=$(ps -ef | grep "taosd" | grep -v "grep" | awk '{print $2}') - if [ -n "$pid" ]; then - ${csudo}kill -9 $pid || : - fi - fi - echo "Stop taosd service success!" - sleep 1 -fi -# if taos.cfg already exist, remove it -if [ -f %{cfg_install_dir}/taos.cfg ]; then - ${csudo}rm -f %{cfg_install_dir}/cfg/taos.cfg || : -fi - -# if taosadapter.toml already exist, remove it -if [ -f %{cfg_install_dir}/taosadapter.toml ]; then - ${csudo}rm -f %{cfg_install_dir}/cfg/taosadapter.toml || : -fi - -# there can not libtaos.so*, otherwise ln -s error -${csudo}rm -f %{homepath}/driver/libtaos* || : - -#Scripts executed after installation -%post -csudo="" -if command -v sudo > /dev/null; then - csudo="sudo " -fi -cd %{homepath}/script -${csudo}./post.sh - -# Scripts executed before uninstall -%preun -csudo="" -if command -v sudo > /dev/null; then - csudo="sudo " -fi -# only remove package to call preun.sh, not but update(2) -if [ $1 -eq 0 ];then - #cd %{homepath}/script - #${csudo}./preun.sh - - if [ -f %{homepath}/script/preun.sh ]; then - cd %{homepath}/script - ${csudo}./preun.sh - else - bin_link_dir="/usr/bin" - lib_link_dir="/usr/lib" - inc_link_dir="/usr/include" - - data_link_dir="/usr/local/taos/data" - log_link_dir="/usr/local/taos/log" - cfg_link_dir="/usr/local/taos/cfg" - - # Remove all links - ${csudo}rm -f ${bin_link_dir}/taos || : - ${csudo}rm -f ${bin_link_dir}/taosd || : - ${csudo}rm -f ${bin_link_dir}/taosadapter || : - ${csudo}rm -f ${cfg_link_dir}/* || : - ${csudo}rm -f ${inc_link_dir}/taos.h || : - ${csudo}rm -f ${inc_link_dir}/taosdef.h || : - ${csudo}rm -f ${inc_link_dir}/taoserror.h || : - ${csudo}rm -f ${lib_link_dir}/libtaos.* || : - - ${csudo}rm -f ${log_link_dir} || : - ${csudo}rm -f ${data_link_dir} || : - - pid=$(ps -ef | grep "taosd" | grep -v "grep" | awk '{print $2}') - if [ -n "$pid" ]; then - ${csudo}kill -9 $pid || : - fi - fi -fi - -# Scripts executed after uninstall -%postun - -# clean build dir -%clean -csudo="" -if command -v sudo > /dev/null; then - csudo="sudo " -fi -${csudo}rm -rf %{buildroot} - -#Specify the files to be packaged -%files -/* -#%doc - -#Setting default permissions -%defattr (-,root,root,0755) -#%{prefix} - -#%changelog +%define homepath /usr/local/taos +%define userlocalpath /usr/local +%define cfg_install_dir /etc/taos +%define __strip /bin/true + +Name: tdengine +Version: %{_version} +Release: 3%{?dist} +Summary: tdengine from taosdata +Group: Application/Database +License: AGPL +URL: www.taosdata.com +AutoReqProv: no + +#BuildRoot: %_topdir/BUILDROOT +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root + +#Prefix: /usr/local/taos + +#BuildRequires: +#Requires: + +%description +Big Data Platform Designed and Optimized for IoT + +#"prep" Nothing needs to be done +#%prep +#%setup -q +#%setup -T + +#"build" Nothing needs to be done +#%build +#%configure +#make %{?_smp_mflags} + +%install +#make install DESTDIR=%{buildroot} +rm -rf %{buildroot} + +echo topdir: %{_topdir} +echo version: %{_version} +echo buildroot: %{buildroot} + +libfile="libtaos.so.%{_version}" + +# create install path, and cp file +mkdir -p %{buildroot}%{homepath}/bin +mkdir -p %{buildroot}%{homepath}/cfg +#mkdir -p %{buildroot}%{homepath}/connector +mkdir -p %{buildroot}%{homepath}/driver +mkdir -p %{buildroot}%{homepath}/examples +mkdir -p %{buildroot}%{homepath}/include +#mkdir -p %{buildroot}%{homepath}/init.d +mkdir -p %{buildroot}%{homepath}/script + +cp %{_compiledir}/../packaging/cfg/taos.cfg %{buildroot}%{homepath}/cfg +if [ -f %{_compiledir}/test/cfg/taosadapter.toml ]; then + cp %{_compiledir}/test/cfg/taosadapter.toml %{buildroot}%{homepath}/cfg +fi +if [ -f %{_compiledir}/test/cfg/taosadapter.service ]; then + cp %{_compiledir}/test/cfg/taosadapter.service %{buildroot}%{homepath}/cfg +fi +#cp %{_compiledir}/../packaging/rpm/taosd %{buildroot}%{homepath}/init.d +cp %{_compiledir}/../packaging/tools/post.sh %{buildroot}%{homepath}/script +cp %{_compiledir}/../packaging/tools/preun.sh %{buildroot}%{homepath}/script +cp %{_compiledir}/../packaging/tools/startPre.sh %{buildroot}%{homepath}/bin +cp %{_compiledir}/../packaging/tools/set_core.sh %{buildroot}%{homepath}/bin +cp %{_compiledir}/../packaging/tools/taosd-dump-cfg.gdb %{buildroot}%{homepath}/bin +cp %{_compiledir}/build/bin/taos %{buildroot}%{homepath}/bin +cp %{_compiledir}/build/bin/taosd %{buildroot}%{homepath}/bin +#cp %{_compiledir}/build/bin/taosBenchmark %{buildroot}%{homepath}/bin + +if [ -f %{_compiledir}/build/bin/taosadapter ]; then + cp %{_compiledir}/build/bin/taosadapter %{buildroot}%{homepath}/bin ||: +fi +cp %{_compiledir}/build/lib/${libfile} %{buildroot}%{homepath}/driver +cp %{_compiledir}/../include/client/taos.h %{buildroot}%{homepath}/include +cp %{_compiledir}/../include/common/taosdef.h %{buildroot}%{homepath}/include +cp %{_compiledir}/../include/util/taoserror.h %{buildroot}%{homepath}/include +#cp -r %{_compiledir}/../src/connector/python %{buildroot}%{homepath}/connector +#cp -r %{_compiledir}/../src/connector/go %{buildroot}%{homepath}/connector +#cp -r %{_compiledir}/../src/connector/nodejs %{buildroot}%{homepath}/connector +#cp %{_compiledir}/build/lib/taos-jdbcdriver*.* %{buildroot}%{homepath}/connector ||: +cp -r %{_compiledir}/../examples/* %{buildroot}%{homepath}/examples + +if [ -f %{_compiledir}/build/bin/jemalloc-config ]; then + mkdir -p %{buildroot}%{userlocalpath}/bin + mkdir -p %{buildroot}%{userlocalpath}/lib + mkdir -p %{buildroot}%{userlocalpath}/lib/pkgconfig + mkdir -p %{buildroot}%{userlocalpath}/include + mkdir -p %{buildroot}%{userlocalpath}/include/jemalloc + mkdir -p %{buildroot}%{userlocalpath}/share + mkdir -p %{buildroot}%{userlocalpath}/share/doc + mkdir -p %{buildroot}%{userlocalpath}/share/doc/jemalloc + mkdir -p %{buildroot}%{userlocalpath}/share/man + mkdir -p %{buildroot}%{userlocalpath}/share/man/man3 + + cp %{_compiledir}/build/bin/jemalloc-config %{buildroot}%{userlocalpath}/bin/ + if [ -f %{_compiledir}/build/bin/jemalloc.sh ]; then + cp %{_compiledir}/build/bin/jemalloc.sh %{buildroot}%{userlocalpath}/bin/ + fi + if [ -f %{_compiledir}/build/bin/jeprof ]; then + cp %{_compiledir}/build/bin/jeprof %{buildroot}%{userlocalpath}/bin/ + fi + if [ -f %{_compiledir}/build/include/jemalloc/jemalloc.h ]; then + cp %{_compiledir}/build/include/jemalloc/jemalloc.h %{buildroot}%{userlocalpath}/include/jemalloc/ + fi + if [ -f %{_compiledir}/build/lib/libjemalloc.so.2 ]; then + cp %{_compiledir}/build/lib/libjemalloc.so.2 %{buildroot}%{userlocalpath}/lib/ + ln -sf libjemalloc.so.2 %{buildroot}%{userlocalpath}/lib/libjemalloc.so + fi + if [ -f %{_compiledir}/build/lib/libjemalloc.a ]; then + cp %{_compiledir}/build/lib/libjemalloc.a %{buildroot}%{userlocalpath}/lib/ + fi + if [ -f %{_compiledir}/build/lib/libjemalloc_pic.a ]; then + cp %{_compiledir}/build/lib/libjemalloc_pic.a %{buildroot}%{userlocalpath}/lib/ + fi + if [ -f %{_compiledir}/build/lib/pkgconfig/jemalloc.pc ]; then + cp %{_compiledir}/build/lib/pkgconfig/jemalloc.pc %{buildroot}%{userlocalpath}/lib/pkgconfig/ + fi + if [ -f %{_compiledir}/build/share/doc/jemalloc/jemalloc.html ]; then + cp %{_compiledir}/build/share/doc/jemalloc/jemalloc.html %{buildroot}%{userlocalpath}/share/doc/jemalloc/ + fi + if [ -f %{_compiledir}/build/share/man/man3/jemalloc.3 ]; then + cp %{_compiledir}/build/share/man/man3/jemalloc.3 %{buildroot}%{userlocalpath}/share/man/man3/ + fi +fi + +#Scripts executed before installation +%pre +csudo="" +if command -v sudo > /dev/null; then + csudo="sudo " +fi + +# Stop the service if running +if pidof taosd &> /dev/null; then + if pidof systemd &> /dev/null; then + ${csudo}systemctl stop taosd || : + elif $(which service &> /dev/null); then + ${csudo}service taosd stop || : + else + pid=$(ps -ef | grep "taosd" | grep -v "grep" | awk '{print $2}') + if [ -n "$pid" ]; then + ${csudo}kill -9 $pid || : + fi + fi + echo "Stop taosd service success!" + sleep 1 +fi +# if taos.cfg already exist, remove it +if [ -f %{cfg_install_dir}/taos.cfg ]; then + ${csudo}rm -f %{cfg_install_dir}/cfg/taos.cfg || : +fi + +# if taosadapter.toml already exist, remove it +if [ -f %{cfg_install_dir}/taosadapter.toml ]; then + ${csudo}rm -f %{cfg_install_dir}/cfg/taosadapter.toml || : +fi + +# there can not libtaos.so*, otherwise ln -s error +${csudo}rm -f %{homepath}/driver/libtaos* || : + +#Scripts executed after installation +%post +csudo="" +if command -v sudo > /dev/null; then + csudo="sudo " +fi +cd %{homepath}/script +${csudo}./post.sh + +# Scripts executed before uninstall +%preun +csudo="" +if command -v sudo > /dev/null; then + csudo="sudo " +fi +# only remove package to call preun.sh, not but update(2) +if [ $1 -eq 0 ];then + #cd %{homepath}/script + #${csudo}./preun.sh + + if [ -f %{homepath}/script/preun.sh ]; then + cd %{homepath}/script + ${csudo}./preun.sh + else + bin_link_dir="/usr/bin" + lib_link_dir="/usr/lib" + inc_link_dir="/usr/include" + + data_link_dir="/usr/local/taos/data" + log_link_dir="/usr/local/taos/log" + cfg_link_dir="/usr/local/taos/cfg" + + # Remove all links + ${csudo}rm -f ${bin_link_dir}/taos || : + ${csudo}rm -f ${bin_link_dir}/taosd || : + ${csudo}rm -f ${bin_link_dir}/taosadapter || : + ${csudo}rm -f ${cfg_link_dir}/* || : + ${csudo}rm -f ${inc_link_dir}/taos.h || : + ${csudo}rm -f ${inc_link_dir}/taosdef.h || : + ${csudo}rm -f ${inc_link_dir}/taoserror.h || : + ${csudo}rm -f ${lib_link_dir}/libtaos.* || : + + ${csudo}rm -f ${log_link_dir} || : + ${csudo}rm -f ${data_link_dir} || : + + pid=$(ps -ef | grep "taosd" | grep -v "grep" | awk '{print $2}') + if [ -n "$pid" ]; then + ${csudo}kill -9 $pid || : + fi + fi +fi + +# Scripts executed after uninstall +%postun + +# clean build dir +%clean +csudo="" +if command -v sudo > /dev/null; then + csudo="sudo " +fi +${csudo}rm -rf %{buildroot} + +#Specify the files to be packaged +%files +/* +#%doc + +#Setting default permissions +%defattr (-,root,root,0755) +#%{prefix} + +#%changelog diff --git a/packaging/tools/check_os.sh b/packaging/tools/check_os.sh old mode 100644 new mode 100755 index d5ec3326b03d93924c38c0e1373742f2fdf817d9..cc8c6e0e9366232deb9013db62b29afebd179135 --- a/packaging/tools/check_os.sh +++ b/packaging/tools/check_os.sh @@ -1,52 +1,52 @@ -#!/bin/bash -# -CSI=$(echo -e "\033[") -CRED="${CSI}1;31m" -CFAILURE="$CRED" -CEND="${CSI}0m" -if [ -n "$(grep 'Aliyun Linux release' /etc/issue)" -o -e /etc/redhat-release ]; then - OS=CentOS - [ -n "$(grep ' 7\.' /etc/redhat-release 2> /dev/null)" ] && CentOS_RHEL_version=7 - [ -n "$(grep ' 6\.' /etc/redhat-release 2> /dev/null)" -o -n "$(grep 'Aliyun Linux release6 15' /etc/issue)" ] && CentOS_RHEL_version=6 - [ -n "$(grep ' 5\.' /etc/redhat-release 2> /dev/null)" -o -n "$(grep 'Aliyun Linux release5' /etc/issue)" ] && CentOS_RHEL_version=5 -elif [ -n "$(grep 'Amazon Linux AMI release' /etc/issue)" -o -e /etc/system-release ]; then - OS=CentOS - CentOS_RHEL_version=6 -elif [ -n "$(grep 'bian' /etc/issue)" -o "$(lsb_release -is 2>/dev/null)" == "Debian" ]; then - OS=Debian - [ ! -e "$(which lsb_release)" ] && { apt-get -y update; apt-get -y install lsb-release; clear; } - Debian_version=$(lsb_release -sr | awk -F. '{print $1}') -elif [ -n "$(grep 'Deepin' /etc/issue)" -o "$(lsb_release -is 2>/dev/null)" == "Deepin" ]; then - OS=Debian - [ ! -e "$(which lsb_release)" ] && { apt-get -y update; apt-get -y install lsb-release; clear; } - Debian_version=$(lsb_release -sr | awk -F. '{print $1}') -elif [ -n "$(grep 'Kali GNU/Linux Rolling' /etc/issue)" -o "$(lsb_release -is 2>/dev/null)" == "Kali" ]; then - OS=Debian - [ ! -e "$(which lsb_release)" ] && { apt-get -y update; apt-get -y install lsb-release; clear; } - if [ -n "$(grep 'VERSION="2016.*"' /etc/os-release)" ]; then - Debian_version=8 - else - echo "${CFAILURE}Does not support this OS, Please contact the author! ${CEND}" - kill -9 $$ - fi -elif [ -n "$(grep 'Ubuntu' /etc/issue)" -o "$(lsb_release -is 2>/dev/null)" == "Ubuntu" -o -n "$(grep 'Linux Mint' /etc/issue)" ]; then - OS=Ubuntu - [ ! -e "$(which lsb_release)" ] && { apt-get -y update; apt-get -y install lsb-release; clear; } - Ubuntu_version=$(lsb_release -sr | awk -F. '{print $1}') - [ -n "$(grep 'Linux Mint 18' /etc/issue)" ] && Ubuntu_version=16 -elif [ -n "$(grep 'elementary' /etc/issue)" -o "$(lsb_release -is 2>/dev/null)" == 'elementary' ]; then - OS=Ubuntu - [ ! -e "$(which lsb_release)" ] && { apt-get -y update; apt-get -y install lsb-release; clear; } - Ubuntu_version=16 -else - echo "${CFAILURE}Does not support this OS, Please contact the author! ${CEND}" - kill -9 $$ -fi - -echo "${CFAILURE}${OS}${CEND}" -if [ "$OS" == 'CentOS' ]; then - echo ${CentOS_RHEL_version} -else - echo ${Ubuntu_version} -fi - +#!/bin/bash +# +CSI=$(echo -e "\033[") +CRED="${CSI}1;31m" +CFAILURE="$CRED" +CEND="${CSI}0m" +if [ -n "$(grep 'Aliyun Linux release' /etc/issue)" -o -e /etc/redhat-release ]; then + OS=CentOS + [ -n "$(grep ' 7\.' /etc/redhat-release 2> /dev/null)" ] && CentOS_RHEL_version=7 + [ -n "$(grep ' 6\.' /etc/redhat-release 2> /dev/null)" -o -n "$(grep 'Aliyun Linux release6 15' /etc/issue)" ] && CentOS_RHEL_version=6 + [ -n "$(grep ' 5\.' /etc/redhat-release 2> /dev/null)" -o -n "$(grep 'Aliyun Linux release5' /etc/issue)" ] && CentOS_RHEL_version=5 +elif [ -n "$(grep 'Amazon Linux AMI release' /etc/issue)" -o -e /etc/system-release ]; then + OS=CentOS + CentOS_RHEL_version=6 +elif [ -n "$(grep 'bian' /etc/issue)" -o "$(lsb_release -is 2>/dev/null)" == "Debian" ]; then + OS=Debian + [ ! -e "$(which lsb_release)" ] && { apt-get -y update; apt-get -y install lsb-release; clear; } + Debian_version=$(lsb_release -sr | awk -F. '{print $1}') +elif [ -n "$(grep 'Deepin' /etc/issue)" -o "$(lsb_release -is 2>/dev/null)" == "Deepin" ]; then + OS=Debian + [ ! -e "$(which lsb_release)" ] && { apt-get -y update; apt-get -y install lsb-release; clear; } + Debian_version=$(lsb_release -sr | awk -F. '{print $1}') +elif [ -n "$(grep 'Kali GNU/Linux Rolling' /etc/issue)" -o "$(lsb_release -is 2>/dev/null)" == "Kali" ]; then + OS=Debian + [ ! -e "$(which lsb_release)" ] && { apt-get -y update; apt-get -y install lsb-release; clear; } + if [ -n "$(grep 'VERSION="2016.*"' /etc/os-release)" ]; then + Debian_version=8 + else + echo "${CFAILURE}Does not support this OS, Please contact the author! ${CEND}" + kill -9 $$ + fi +elif [ -n "$(grep 'Ubuntu' /etc/issue)" -o "$(lsb_release -is 2>/dev/null)" == "Ubuntu" -o -n "$(grep 'Linux Mint' /etc/issue)" ]; then + OS=Ubuntu + [ ! -e "$(which lsb_release)" ] && { apt-get -y update; apt-get -y install lsb-release; clear; } + Ubuntu_version=$(lsb_release -sr | awk -F. '{print $1}') + [ -n "$(grep 'Linux Mint 18' /etc/issue)" ] && Ubuntu_version=16 +elif [ -n "$(grep 'elementary' /etc/issue)" -o "$(lsb_release -is 2>/dev/null)" == 'elementary' ]; then + OS=Ubuntu + [ ! -e "$(which lsb_release)" ] && { apt-get -y update; apt-get -y install lsb-release; clear; } + Ubuntu_version=16 +else + echo "${CFAILURE}Does not support this OS, Please contact the author! ${CEND}" + kill -9 $$ +fi + +echo "${CFAILURE}${OS}${CEND}" +if [ "$OS" == 'CentOS' ]; then + echo ${CentOS_RHEL_version} +else + echo ${Ubuntu_version} +fi + diff --git a/packaging/tools/get_client.sh b/packaging/tools/get_client.sh old mode 100644 new mode 100755 index c29e3e79e83520f370d99ef94e0be472eee23782..0d34ecb311fb4b941d6f6773d1c3c921a9bd9886 --- a/packaging/tools/get_client.sh +++ b/packaging/tools/get_client.sh @@ -1,21 +1,21 @@ -#!/bin/bash -# - -log_dir=$1 -result_file=$2 - -if [ ! -n "$1" ];then - echo "Pleas input the director of taosdlog." - echo "usage: ./get_client.sh " - exit 1 -else - log_dir=$1 -fi - -if [ ! -n "$2" ];then - result_file=clientInfo.txt -else - result_file=$2 -fi - -grep "new TCP connection" ${log_dir}/taosdlog.* | sed -e "s/0x.* from / /"|sed -e "s/,.*$//"|sed -e "s/:[0-9]*$//"|sort -r|uniq -f 2|sort -k 3 -r|uniq -f 2 > ${result_file} +#!/bin/bash +# + +log_dir=$1 +result_file=$2 + +if [ ! -n "$1" ];then + echo "Pleas input the director of taosdlog." + echo "usage: ./get_client.sh " + exit 1 +else + log_dir=$1 +fi + +if [ ! -n "$2" ];then + result_file=clientInfo.txt +else + result_file=$2 +fi + +grep "new TCP connection" ${log_dir}/taosdlog.* | sed -e "s/0x.* from / /"|sed -e "s/,.*$//"|sed -e "s/:[0-9]*$//"|sort -r|uniq -f 2|sort -k 3 -r|uniq -f 2 > ${result_file} diff --git a/packaging/tools/get_os.sh b/packaging/tools/get_os.sh old mode 100644 new mode 100755 index 1216f7a6a6fa418357c59d00a1e8d09b83a19d27..f74b63f9805e937933000d097c24bc6b85663288 --- a/packaging/tools/get_os.sh +++ b/packaging/tools/get_os.sh @@ -1,14 +1,14 @@ -#!/bin/bash -# -# This file is used to install TAOS time-series database on linux systems. The operating system -# is required to use systemd to manage services at boot - -set -e -# set -x - -# -----------------------Variables definition--------------------- -OS=$(cat /etc/*-release | grep "^NAME=" | cut -d= -f2) -len=$(echo ${#OS}) -len=$((len-2)) -retval=$(echo -ne ${OS:1:${len}} | cut -d" " -f1) -echo -ne $retval +#!/bin/bash +# +# This file is used to install TAOS time-series database on linux systems. The operating system +# is required to use systemd to manage services at boot + +set -e +# set -x + +# -----------------------Variables definition--------------------- +OS=$(cat /etc/*-release | grep "^NAME=" | cut -d= -f2) +len=$(echo ${#OS}) +len=$((len-2)) +retval=$(echo -ne ${OS:1:${len}} | cut -d" " -f1) +echo -ne $retval diff --git a/packaging/tools/get_version.sh b/packaging/tools/get_version.sh old mode 100644 new mode 100755 index 44c8c6bf21d93bbb9eba1157bd0fecf3e402b40c..0fe61e3dcb0841a3f6e2193f9b451534a71fecb7 --- a/packaging/tools/get_version.sh +++ b/packaging/tools/get_version.sh @@ -1,15 +1,15 @@ -#!/bin/bash -# -# This file is used to install TAOS time-series database on linux systems. The operating system -# is required to use systemd to manage services at boot - -set -e -# set -x - -# -----------------------Variables definition--------------------- -verinfo=$(cat $1 | grep " version" | cut -d '"' -f2) -verinfo=$(echo $verinfo | tr "\n" " ") -len=$(echo ${#verinfo}) -len=$((len-1)) -retval=$(echo -ne ${verinfo:0:${len}}) -echo -ne $retval +#!/bin/bash +# +# This file is used to install TAOS time-series database on linux systems. The operating system +# is required to use systemd to manage services at boot + +set -e +# set -x + +# -----------------------Variables definition--------------------- +verinfo=$(cat $1 | grep " version" | cut -d '"' -f2) +verinfo=$(echo $verinfo | tr "\n" " ") +len=$(echo ${#verinfo}) +len=$((len-1)) +retval=$(echo -ne ${verinfo:0:${len}}) +echo -ne $retval diff --git a/packaging/tools/install.sh b/packaging/tools/install.sh index d2d52af955f8a4048125831f4f6c32e69f774afa..f07705ff442b65f1295431e59b48ef50a76cadc0 100755 --- a/packaging/tools/install.sh +++ b/packaging/tools/install.sh @@ -6,16 +6,36 @@ set -e #set -x +verMode=edge +pagMode=full + +iplist="" +serverFqdn="" + # -----------------------Variables definition--------------------- script_dir=$(dirname $(readlink -f "$0")) # Dynamic directory -data_dir="/var/lib/taos" -log_dir="/var/log/taos" -data_link_dir="/usr/local/taos/data" -log_link_dir="/usr/local/taos/log" +clientName="taos" +serverName="taosd" +configFile="taos.cfg" +productName="TDengine" +emailName="taosdata.com" +uninstallScript="rmtaos" +historyFile="taos_history" +tarName="taos.tar.gz" +dataDir="/var/lib/taos" +logDir="/var/log/taos" +configDir="/etc/taos" +installDir="/usr/local/taos" +adapterName="taosadapter" +benchmarkName="taosBenchmark" +dumpName="taosdump" +demoName="taosdemo" -cfg_install_dir="/etc/taos" +data_dir=${dataDir} +log_dir=${logDir} +cfg_install_dir=${configDir} bin_link_dir="/usr/bin" lib_link_dir="/usr/lib" @@ -23,21 +43,13 @@ lib64_link_dir="/usr/lib64" inc_link_dir="/usr/include" #install main path -install_main_dir="/usr/local/taos" - +install_main_dir=${installDir} # old bin dir -bin_dir="/usr/local/taos/bin" +bin_dir="${installDir}/bin" service_config_dir="/etc/systemd/system" - -#taos-tools para -demoName="taosdemo" -benchmarkName="taosBenchmark" -dumpName="taosdump" -emailName="taosdata.com" -taosName="taos" -toolsName="taostools" - +nginx_port=6060 +nginx_dir="/usr/local/nginxd" # Color setting RED='\033[0;31m' @@ -47,8 +59,8 @@ GREEN_UNDERLINE='\033[4;32m' NC='\033[0m' csudo="" -if command -v sudo > /dev/null; then - csudo="sudo" +if command -v sudo >/dev/null; then + csudo="sudo " fi update_flag=0 @@ -56,52 +68,51 @@ prompt_force=0 initd_mod=0 service_mod=2 -if pidof systemd &> /dev/null; then - service_mod=0 -elif $(which service &> /dev/null); then - service_mod=1 - service_config_dir="/etc/init.d" - if $(which chkconfig &> /dev/null); then - initd_mod=1 - elif $(which insserv &> /dev/null); then - initd_mod=2 - elif $(which update-rc.d &> /dev/null); then - initd_mod=3 - else - service_mod=2 - fi -else +if pidof systemd &>/dev/null; then + service_mod=0 +elif $(which service &>/dev/null); then + service_mod=1 + service_config_dir="/etc/init.d" + if $(which chkconfig &>/dev/null); then + initd_mod=1 + elif $(which insserv &>/dev/null); then + initd_mod=2 + elif $(which update-rc.d &>/dev/null); then + initd_mod=3 + else service_mod=2 + fi +else + service_mod=2 fi - # get the operating system type for using the corresponding init file # ubuntu/debian(deb), centos/fedora(rpm), others: opensuse, redhat, ..., no verification #osinfo=$(awk -F= '/^NAME/{print $2}' /etc/os-release) if [[ -e /etc/os-release ]]; then - osinfo=$(cat /etc/os-release | grep "NAME" | cut -d '"' -f2) ||: + osinfo=$(cat /etc/os-release | grep "NAME" | cut -d '"' -f2) || : else osinfo="" fi #echo "osinfo: ${osinfo}" os_type=0 -if echo $osinfo | grep -qwi "ubuntu" ; then -# echo "This is ubuntu system" +if echo $osinfo | grep -qwi "ubuntu"; then + # echo "This is ubuntu system" os_type=1 -elif echo $osinfo | grep -qwi "debian" ; then -# echo "This is debian system" +elif echo $osinfo | grep -qwi "debian"; then + # echo "This is debian system" os_type=1 -elif echo $osinfo | grep -qwi "Kylin" ; then -# echo "This is Kylin system" +elif echo $osinfo | grep -qwi "Kylin"; then + # echo "This is Kylin system" os_type=1 -elif echo $osinfo | grep -qwi "centos" ; then -# echo "This is centos system" +elif echo $osinfo | grep -qwi "centos"; then + # echo "This is centos system" os_type=2 -elif echo $osinfo | grep -qwi "fedora" ; then -# echo "This is fedora system" +elif echo $osinfo | grep -qwi "fedora"; then + # echo "This is fedora system" os_type=2 -elif echo $osinfo | grep -qwi "Linx" ; then -# echo "This is Linx system" +elif echo $osinfo | grep -qwi "Linx"; then + # echo "This is Linx system" os_type=1 service_mod=0 initd_mod=0 @@ -110,43 +121,41 @@ else echo " osinfo: ${osinfo}" echo " This is an officially unverified linux system," echo " if there are any problems with the installation and operation, " - echo " please feel free to contact taosdata.com for support." + echo " please feel free to contact ${emailName} for support." os_type=1 fi - # ============================= get input parameters ================================================= # install.sh -v [server | client] -e [yes | no] -i [systemd | service | ...] # set parameters by default value -interactiveFqdn=yes # [yes | no] -verType=server # [server | client] -initType=systemd # [systemd | service | ...] +interactiveFqdn=yes # [yes | no] +verType=server # [server | client] +initType=systemd # [systemd | service | ...] -while getopts "hv:e:i:" arg -do +while getopts "hv:e:i:" arg; do case $arg in - e) - #echo "interactiveFqdn=$OPTARG" - interactiveFqdn=$( echo $OPTARG ) - ;; - v) - #echo "verType=$OPTARG" - verType=$(echo $OPTARG) - ;; - i) - #echo "initType=$OPTARG" - initType=$(echo $OPTARG) - ;; - h) - echo "Usage: `basename $0` -v [server | client] -e [yes | no]" - exit 0 - ;; - ?) #unknow option - echo "unkonw argument" - exit 1 - ;; + e) + #echo "interactiveFqdn=$OPTARG" + interactiveFqdn=$(echo $OPTARG) + ;; + v) + #echo "verType=$OPTARG" + verType=$(echo $OPTARG) + ;; + i) + #echo "initType=$OPTARG" + initType=$(echo $OPTARG) + ;; + h) + echo "Usage: $(basename $0) -v [server | client] -e [yes | no]" + exit 0 + ;; + ?) #unknow option + echo "unkonw argument" + exit 1 + ;; esac done @@ -155,98 +164,163 @@ done function kill_process() { pid=$(ps -ef | grep "$1" | grep -v "grep" | awk '{print $2}') if [ -n "$pid" ]; then - ${csudo} kill -9 $pid || : + ${csudo}kill -9 $pid || : fi } function install_main_path() { - #create install main dir and all sub dir - ${csudo} rm -rf ${install_main_dir} || : - ${csudo} mkdir -p ${install_main_dir} - ${csudo} mkdir -p ${install_main_dir}/cfg - ${csudo} mkdir -p ${install_main_dir}/bin - ${csudo} mkdir -p ${install_main_dir}/connector - ${csudo} mkdir -p ${install_main_dir}/lib - ${csudo} mkdir -p ${install_main_dir}/examples - ${csudo} mkdir -p ${install_main_dir}/include - ${csudo} mkdir -p ${install_main_dir}/init.d - if [ "$verMode" == "cluster" ]; then - ${csudo} mkdir -p ${nginx_dir} - fi + #create install main dir and all sub dir + ${csudo}rm -rf ${install_main_dir} || : + ${csudo}mkdir -p ${install_main_dir} + ${csudo}mkdir -p ${install_main_dir}/cfg + ${csudo}mkdir -p ${install_main_dir}/bin + # ${csudo}mkdir -p ${install_main_dir}/connector + ${csudo}mkdir -p ${install_main_dir}/driver + ${csudo}mkdir -p ${install_main_dir}/examples + ${csudo}mkdir -p ${install_main_dir}/include + # ${csudo}mkdir -p ${install_main_dir}/init.d + if [ "$verMode" == "cluster" ]; then + ${csudo}mkdir -p ${nginx_dir} + fi - if [[ -e ${script_dir}/email ]]; then - ${csudo} cp ${script_dir}/email ${install_main_dir}/ ||: - fi + if [[ -e ${script_dir}/email ]]; then + ${csudo}cp ${script_dir}/email ${install_main_dir}/ || : + fi } function install_bin() { - # Remove links - ${csudo} rm -f ${bin_link_dir}/taos || : - ${csudo} rm -f ${bin_link_dir}/taosd || : - ${csudo} rm -f ${bin_link_dir}/taosadapter || : - ${csudo} rm -f ${bin_link_dir}/create_table || : - ${csudo} rm -f ${bin_link_dir}/tmq_sim || : - ${csudo} rm -f ${bin_link_dir}/taosdump || : - ${csudo} rm -f ${bin_link_dir}/rmtaos || : - #${csudo} rm -f ${bin_link_dir}/set_core || : - - ${csudo} cp -r ${script_dir}/bin/* ${install_main_dir}/bin && ${csudo} chmod 0555 ${install_main_dir}/bin/* - - #Make link - [ -x ${install_main_dir}/bin/taos ] && ${csudo} ln -s ${install_main_dir}/bin/taos ${bin_link_dir}/taos || : - [ -x ${install_main_dir}/bin/taosd ] && ${csudo} ln -s ${install_main_dir}/bin/taosd ${bin_link_dir}/taosd || : - [ -x ${install_main_dir}/bin/create_table ] && ${csudo} ln -s ${install_main_dir}/bin/create_table ${bin_link_dir}/create_table || : - [ -x ${install_main_dir}/bin/tmq_sim ] && ${csudo} ln -s ${install_main_dir}/bin/tmq_sim ${bin_link_dir}/tmq_sim || : -# [ -x ${install_main_dir}/bin/taosdemo ] && ${csudo} ln -s ${install_main_dir}/bin/taosdemo ${bin_link_dir}/taosdemo || : -# [ -x ${install_main_dir}/bin/taosdump ] && ${csudo} ln -s ${install_main_dir}/bin/taosdump ${bin_link_dir}/taosdump || : - [ -x ${install_main_dir}/bin/remove.sh ] && ${csudo} ln -s ${install_main_dir}/bin/remove.sh ${bin_link_dir}/rmtaos || : -# [ -x ${install_main_dir}/bin/set_core.sh ] && ${csudo} ln -s ${install_main_dir}/bin/set_core.sh ${bin_link_dir}/set_core || : + # Remove links + ${csudo}rm -f ${bin_link_dir}/${clientName} || : + ${csudo}rm -f ${bin_link_dir}/${serverName} || : + ${csudo}rm -f ${bin_link_dir}/${adapterName} || : + ${csudo}rm -f ${bin_link_dir}/${uninstallScript} || : + ${csudo}rm -f ${bin_link_dir}/tarbitrator || : + ${csudo}rm -f ${bin_link_dir}/set_core || : + ${csudo}rm -f ${bin_link_dir}/run_${serverName}_and_${adapterName}.sh || : + ${csudo}rm -f ${bin_link_dir}/TDinsight.sh || : + + ${csudo}cp -r ${script_dir}/bin/* ${install_main_dir}/bin && ${csudo}chmod 0555 ${install_main_dir}/bin/* + + #Make link + [ -x ${install_main_dir}/bin/${clientName} ] && ${csudo}ln -s ${install_main_dir}/bin/${clientName} ${bin_link_dir}/${clientName} || : + [ -x ${install_main_dir}/bin/${serverName} ] && ${csudo}ln -s ${install_main_dir}/bin/${serverName} ${bin_link_dir}/${serverName} || : + [ -x ${install_main_dir}/bin/${adapterName} ] && ${csudo}ln -s ${install_main_dir}/bin/${adapterName} ${bin_link_dir}/${adapterName} || : + [ -x ${install_main_dir}/bin/${benchmarkName} ] && ${csudo}ln -s ${install_main_dir}/bin/${benchmarkName} ${bin_link_dir}/${demoName} || : + [ -x ${install_main_dir}/bin/${benchmarkName} ] && ${csudo}ln -s ${install_main_dir}/bin/${benchmarkName} ${bin_link_dir}/${benchmarkName} || : + [ -x ${install_main_dir}/bin/${dumpName} ] && ${csudo}ln -s ${install_main_dir}/bin/${dumpName} ${bin_link_dir}/${dumpName} || : + [ -x ${install_main_dir}/bin/TDinsight.sh ] && ${csudo}ln -s ${install_main_dir}/bin/TDinsight.sh ${bin_link_dir}/TDinsight.sh || : + [ -x ${install_main_dir}/bin/remove.sh ] && ${csudo}ln -s ${install_main_dir}/bin/remove.sh ${bin_link_dir}/${uninstallScript} || : + [ -x ${install_main_dir}/bin/set_core.sh ] && ${csudo}ln -s ${install_main_dir}/bin/set_core.sh ${bin_link_dir}/set_core || : + [ -x ${install_main_dir}/bin/run_${serverName}_and_${adapterName}.sh ] && ${csudo}ln -s ${install_main_dir}/bin/run_${serverName}_and_${adapterName}.sh ${bin_link_dir}/run_${serverName}_and_${adapterName}.sh || : + [ -x ${install_main_dir}/bin/tarbitrator ] && ${csudo}ln -s ${install_main_dir}/bin/tarbitrator ${bin_link_dir}/tarbitrator || : + + if [ "$verMode" == "cluster" ]; then + ${csudo}cp -r ${script_dir}/nginxd/* ${nginx_dir} && ${csudo}chmod 0555 ${nginx_dir}/* + ${csudo}mkdir -p ${nginx_dir}/logs + ${csudo}chmod 777 ${nginx_dir}/sbin/nginx + fi } function install_lib() { - # Remove links - ${csudo} rm -f ${lib_link_dir}/libtaos.* || : - ${csudo} rm -f ${lib64_link_dir}/libtaos.* || : - ${csudo} rm -f ${lib_link_dir}/libtdb.* || : - ${csudo} rm -f ${lib64_link_dir}/libtdb.* || : + # Remove links + ${csudo}rm -f ${lib_link_dir}/libtaos.* || : + ${csudo}rm -f ${lib64_link_dir}/libtaos.* || : + #${csudo}rm -rf ${v15_java_app_dir} || : + ${csudo}cp -rf ${script_dir}/driver/* ${install_main_dir}/driver && ${csudo}chmod 777 ${install_main_dir}/driver/* + + ${csudo}ln -s ${install_main_dir}/driver/libtaos.* ${lib_link_dir}/libtaos.so.1 + ${csudo}ln -s ${lib_link_dir}/libtaos.so.1 ${lib_link_dir}/libtaos.so + + if [[ -d ${lib64_link_dir} && ! -e ${lib64_link_dir}/libtaos.so ]]; then + ${csudo}ln -s ${install_main_dir}/driver/libtaos.* ${lib64_link_dir}/libtaos.so.1 || : + ${csudo}ln -s ${lib64_link_dir}/libtaos.so.1 ${lib64_link_dir}/libtaos.so || : + fi - ${csudo} cp -rf ${script_dir}/lib/* ${install_main_dir}/lib && ${csudo} chmod 777 ${install_main_dir}/lib/* + ${csudo}ldconfig +} - ${csudo} ln -s ${install_main_dir}/lib/libtaos.* ${lib_link_dir}/libtaos.so.1 - ${csudo} ln -s ${lib_link_dir}/libtaos.so.1 ${lib_link_dir}/libtaos.so +function install_avro() { + if [ "$osType" != "Darwin" ]; then + avro_dir=${script_dir}/avro + if [ -f "${avro_dir}/lib/libavro.so.23.0.0" ] && [ -d /usr/local/$1 ]; then + ${csudo}/usr/bin/install -c -d /usr/local/$1 + ${csudo}/usr/bin/install -c -m 755 ${avro_dir}/lib/libavro.so.23.0.0 /usr/local/$1 + ${csudo}ln -sf /usr/local/$1/libavro.so.23.0.0 /usr/local/$1/libavro.so.23 + ${csudo}ln -sf /usr/local/$1/libavro.so.23 /usr/local/$1/libavro.so + + ${csudo}/usr/bin/install -c -d /usr/local/$1 + [ -f ${avro_dir}/lib/libavro.a ] && + ${csudo}/usr/bin/install -c -m 755 ${avro_dir}/lib/libavro.a /usr/local/$1 + + if [ -d /etc/ld.so.conf.d ]; then + echo "/usr/local/$1" | ${csudo}tee /etc/ld.so.conf.d/libavro.conf >/dev/null || echo -e "failed to write /etc/ld.so.conf.d/libavro.conf" + ${csudo}ldconfig + else + echo "/etc/ld.so.conf.d not found!" + fi + fi + fi +} - if [[ -d ${lib64_link_dir} && ! -e ${lib64_link_dir}/libtaos.so ]]; then - ${csudo} ln -s ${install_main_dir}/lib/libtaos.* ${lib64_link_dir}/libtaos.so.1 || : - ${csudo} ln -s ${lib64_link_dir}/libtaos.so.1 ${lib64_link_dir}/libtaos.so || : +function install_jemalloc() { + jemalloc_dir=${script_dir}/jemalloc + + if [ -d ${jemalloc_dir} ]; then + ${csudo}/usr/bin/install -c -d /usr/local/bin + + if [ -f ${jemalloc_dir}/bin/jemalloc-config ]; then + ${csudo}/usr/bin/install -c -m 755 ${jemalloc_dir}/bin/jemalloc-config /usr/local/bin + fi + if [ -f ${jemalloc_dir}/bin/jemalloc.sh ]; then + ${csudo}/usr/bin/install -c -m 755 ${jemalloc_dir}/bin/jemalloc.sh /usr/local/bin + fi + if [ -f ${jemalloc_dir}/bin/jeprof ]; then + ${csudo}/usr/bin/install -c -m 755 ${jemalloc_dir}/bin/jeprof /usr/local/bin + fi + if [ -f ${jemalloc_dir}/include/jemalloc/jemalloc.h ]; then + ${csudo}/usr/bin/install -c -d /usr/local/include/jemalloc + ${csudo}/usr/bin/install -c -m 644 ${jemalloc_dir}/include/jemalloc/jemalloc.h /usr/local/include/jemalloc + fi + if [ -f ${jemalloc_dir}/lib/libjemalloc.so.2 ]; then + ${csudo}/usr/bin/install -c -d /usr/local/lib + ${csudo}/usr/bin/install -c -m 755 ${jemalloc_dir}/lib/libjemalloc.so.2 /usr/local/lib + ${csudo}ln -sf libjemalloc.so.2 /usr/local/lib/libjemalloc.so + ${csudo}/usr/bin/install -c -d /usr/local/lib + if [ -f ${jemalloc_dir}/lib/libjemalloc.a ]; then + ${csudo}/usr/bin/install -c -m 755 ${jemalloc_dir}/lib/libjemalloc.a /usr/local/lib + fi + if [ -f ${jemalloc_dir}/lib/libjemalloc_pic.a ]; then + ${csudo}/usr/bin/install -c -m 755 ${jemalloc_dir}/lib/libjemalloc_pic.a /usr/local/lib + fi + if [ -f ${jemalloc_dir}/lib/libjemalloc_pic.a ]; then + ${csudo}/usr/bin/install -c -d /usr/local/lib/pkgconfig + ${csudo}/usr/bin/install -c -m 644 ${jemalloc_dir}/lib/pkgconfig/jemalloc.pc /usr/local/lib/pkgconfig + fi + fi + if [ -f ${jemalloc_dir}/share/doc/jemalloc/jemalloc.html ]; then + ${csudo}/usr/bin/install -c -d /usr/local/share/doc/jemalloc + ${csudo}/usr/bin/install -c -m 644 ${jemalloc_dir}/share/doc/jemalloc/jemalloc.html /usr/local/share/doc/jemalloc + fi + if [ -f ${jemalloc_dir}/share/man/man3/jemalloc.3 ]; then + ${csudo}/usr/bin/install -c -d /usr/local/share/man/man3 + ${csudo}/usr/bin/install -c -m 644 ${jemalloc_dir}/share/man/man3/jemalloc.3 /usr/local/share/man/man3 fi - ${csudo} ldconfig + if [ -d /etc/ld.so.conf.d ]; then + echo "/usr/local/lib" | ${csudo}tee /etc/ld.so.conf.d/jemalloc.conf >/dev/null || echo -e "failed to write /etc/ld.so.conf.d/jemalloc.conf" + ${csudo}ldconfig + else + echo "/etc/ld.so.conf.d not found!" + fi + fi } function install_header() { - ${csudo} rm -f ${inc_link_dir}/taos.h ${inc_link_dir}/taosdef.h ${inc_link_dir}/taoserror.h || : - ${csudo} cp -f ${script_dir}/inc/* ${install_main_dir}/include && ${csudo} chmod 644 ${install_main_dir}/include/* - ${csudo} ln -s ${install_main_dir}/include/taos.h ${inc_link_dir}/taos.h -# ${csudo} ln -s ${install_main_dir}/include/taosdef.h ${inc_link_dir}/taosdef.h - ${csudo} ln -s ${install_main_dir}/include/taoserror.h ${inc_link_dir}/taoserror.h -} - -# temp install taosBenchmark -function install_taosTools() { - ${csudo} rm -f ${bin_link_dir}/${benchmarkName} || : - ${csudo} rm -f ${bin_link_dir}/${dumpName} || : - ${csudo} rm -f ${bin_link_dir}/rm${toolsName} || : - - ${csudo} /usr/bin/install -c -m 755 ${script_dir}/bin/${dumpName} ${install_main_dir}/bin/${dumpName} - ${csudo} /usr/bin/install -c -m 755 ${script_dir}/bin/${benchmarkName} ${install_main_dir}/bin/${benchmarkName} - ${csudo} ln -sf ${install_main_dir}/bin/${benchmarkName} ${install_main_dir}/bin/${demoName} - #Make link - [[ -x ${install_main_dir}/bin/${benchmarkName} ]] && \ - ${csudo} ln -s ${install_main_dir}/bin/${benchmarkName} ${bin_link_dir}/${benchmarkName} || : - [[ -x ${install_main_dir}/bin/${demoName} ]] && \ - ${csudo} ln -s ${install_main_dir}/bin/${demoName} ${bin_link_dir}/${demoName} || : - [[ -x ${install_main_dir}/bin/${dumpName} ]] && \ - ${csudo} ln -s ${install_main_dir}/bin/${dumpName} ${bin_link_dir}/${dumpName} || : + ${csudo}rm -f ${inc_link_dir}/taos.h ${inc_link_dir}/taosdef.h ${inc_link_dir}/taoserror.h || : + ${csudo}cp -f ${script_dir}/inc/* ${install_main_dir}/include && ${csudo}chmod 644 ${install_main_dir}/include/* + ${csudo}ln -s ${install_main_dir}/include/taos.h ${inc_link_dir}/taos.h + ${csudo}ln -s ${install_main_dir}/include/taosdef.h ${inc_link_dir}/taosdef.h + ${csudo}ln -s ${install_main_dir}/include/taoserror.h ${inc_link_dir}/taoserror.h } function add_newHostname_to_hosts() { @@ -256,18 +330,17 @@ function add_newHostname_to_hosts() { iphost=$(cat /etc/hosts | grep $1 | awk '{print $1}') arr=($iphost) IFS="$OLD_IFS" - for s in "${arr[@]}" - do + for s in "${arr[@]}"; do if [[ "$s" == "$localIp" ]]; then return fi done - ${csudo} echo "127.0.0.1 $1" >> /etc/hosts ||: + ${csudo}echo "127.0.0.1 $1" >>/etc/hosts || : } function set_hostname() { echo -e -n "${GREEN}Please enter one hostname(must not be 'localhost')${NC}:" - read newHostname + read newHostname while true; do if [[ ! -z "$newHostname" && "$newHostname" != "localhost" ]]; then break @@ -276,28 +349,25 @@ function set_hostname() { fi done - ${csudo} hostname $newHostname ||: - retval=`echo $?` + ${csudo}hostname $newHostname || : + retval=$(echo $?) if [[ $retval != 0 ]]; then - echo - echo "set hostname fail!" - return + echo + echo "set hostname fail!" + return fi - #echo -e -n "$(hostnamectl status --static)" - #echo -e -n "$(hostnamectl status --transient)" - #echo -e -n "$(hostnamectl status --pretty)" #ubuntu/centos /etc/hostname if [[ -e /etc/hostname ]]; then - ${csudo} echo $newHostname > /etc/hostname ||: + ${csudo}echo $newHostname >/etc/hostname || : fi #debian: #HOSTNAME=yourname if [[ -e /etc/sysconfig/network ]]; then - ${csudo} sed -i -r "s/#*\s*(HOSTNAME=\s*).*/\1$newHostname/" /etc/sysconfig/network ||: + ${csudo}sed -i -r "s/#*\s*(HOSTNAME=\s*).*/\1$newHostname/" /etc/sysconfig/network || : fi - ${csudo} sed -i -r "s/#*\s*(fqdn\s*).*/\1$newHostname/" ${cfg_install_dir}/taos.cfg + ${csudo}sed -i -r "s/#*\s*(fqdn\s*).*/\1$newHostname/" ${cfg_install_dir}/${configFile} serverFqdn=$newHostname if [[ -e /etc/hosts ]]; then @@ -311,20 +381,19 @@ function is_correct_ipaddr() { IFS=" " arr=($iplist) IFS="$OLD_IFS" - for s in "${arr[@]}" - do - if [[ "$s" == "$newIp" ]]; then - return 0 - fi + for s in "${arr[@]}"; do + if [[ "$s" == "$newIp" ]]; then + return 0 + fi done return 1 } function set_ipAsFqdn() { - iplist=$(ip address |grep inet |grep -v inet6 |grep -v 127.0.0.1 |awk '{print $2}' |awk -F "/" '{print $1}') ||: + iplist=$(ip address | grep inet | grep -v inet6 | grep -v 127.0.0.1 | awk '{print $2}' | awk -F "/" '{print $1}') || : if [ -z "$iplist" ]; then - iplist=$(ifconfig |grep inet |grep -v inet6 |grep -v 127.0.0.1 |awk '{print $2}' |awk -F ":" '{print $2}') ||: + iplist=$(ifconfig | grep inet | grep -v inet6 | grep -v 127.0.0.1 | awk '{print $2}' | awk -F ":" '{print $2}') || : fi if [ -z "$iplist" ]; then @@ -332,7 +401,7 @@ function set_ipAsFqdn() { echo -e -n "${GREEN}Unable to get local ip, use 127.0.0.1${NC}" localFqdn="127.0.0.1" # Write the local FQDN to configuration file - ${csudo} sed -i -r "s/#*\s*(fqdn\s*).*/\1$localFqdn/" ${cfg_install_dir}/taos.cfg + ${csudo}sed -i -r "s/#*\s*(fqdn\s*).*/\1$localFqdn/" ${cfg_install_dir}/${configFile} serverFqdn=$localFqdn echo return @@ -345,23 +414,23 @@ function set_ipAsFqdn() { echo echo -e -n "${GREEN}Notes: if IP is used as the node name, data can NOT be migrated to other machine directly${NC}:" read localFqdn - while true; do - if [ ! -z "$localFqdn" ]; then - # Check if correct ip address - is_correct_ipaddr $localFqdn - retval=`echo $?` - if [[ $retval != 0 ]]; then - read -p "Please choose an IP from local IP list:" localFqdn - else - # Write the local FQDN to configuration file - ${csudo} sed -i -r "s/#*\s*(fqdn\s*).*/\1$localFqdn/" ${cfg_install_dir}/taos.cfg - serverFqdn=$localFqdn - break - fi - else + while true; do + if [ ! -z "$localFqdn" ]; then + # Check if correct ip address + is_correct_ipaddr $localFqdn + retval=$(echo $?) + if [[ $retval != 0 ]]; then read -p "Please choose an IP from local IP list:" localFqdn + else + # Write the local FQDN to configuration file + ${csudo}sed -i -r "s/#*\s*(fqdn\s*).*/\1$localFqdn/" ${cfg_install_dir}/${configFile} + serverFqdn=$localFqdn + break fi - done + else + read -p "Please choose an IP from local IP list:" localFqdn + fi + done } function local_fqdn_check() { @@ -369,194 +438,553 @@ function local_fqdn_check() { echo echo -e -n "System hostname is: ${GREEN}$serverFqdn${NC}" echo - if [[ "$serverFqdn" == "" ]] || [[ "$serverFqdn" == "localhost" ]]; then + if [[ "$serverFqdn" == "" ]] || [[ "$serverFqdn" == "localhost" ]]; then echo -e -n "${GREEN}It is strongly recommended to configure a hostname for this machine ${NC}" echo - while true - do - read -r -p "Set hostname now? [Y/n] " input - if [ ! -n "$input" ]; then - set_hostname - break - else - case $input in - [yY][eE][sS]|[yY]) - set_hostname - break - ;; - - [nN][oO]|[nN]) - set_ipAsFqdn - break - ;; - - *) - echo "Invalid input..." - ;; - esac - fi + while true; do + read -r -p "Set hostname now? [Y/n] " input + if [ ! -n "$input" ]; then + set_hostname + break + else + case $input in + [yY][eE][sS] | [yY]) + set_hostname + break + ;; + + [nN][oO] | [nN]) + set_ipAsFqdn + break + ;; + + *) + echo "Invalid input..." + ;; + esac + fi done fi } +function install_adapter_config() { + if [ ! -f "${cfg_install_dir}/${adapterName}.toml" ]; then + ${csudo}mkdir -p ${cfg_install_dir} + [ -f ${script_dir}/cfg/${adapterName}.toml ] && ${csudo}cp ${script_dir}/cfg/${adapterName}.toml ${cfg_install_dir} + [ -f ${cfg_install_dir}/${adapterName}.toml ] && ${csudo}chmod 644 ${cfg_install_dir}/${adapterName}.toml + fi + + [ -f ${script_dir}/cfg/${adapterName}.toml ] && + ${csudo}cp -f ${script_dir}/cfg/${adapterName}.toml ${cfg_install_dir}/${adapterName}.toml.new + + [ -f ${cfg_install_dir}/${adapterName}.toml ] && + ${csudo}ln -s ${cfg_install_dir}/${adapterName}.toml ${install_main_dir}/cfg/${adapterName}.toml + + [ ! -z $1 ] && return 0 || : # only install client + +} + +function install_config() { + + if [ ! -f "${cfg_install_dir}/${configFile}" ]; then + ${csudo}mkdir -p ${cfg_install_dir} + [ -f ${script_dir}/cfg/${configFile} ] && ${csudo}cp ${script_dir}/cfg/${configFile} ${cfg_install_dir} + ${csudo}chmod 644 ${cfg_install_dir}/* + fi + + ${csudo}cp -f ${script_dir}/cfg/${configFile} ${cfg_install_dir}/${configFile}.new + ${csudo}ln -s ${cfg_install_dir}/${configFile} ${install_main_dir}/cfg + + [ ! -z $1 ] && return 0 || : # only install client + + if ((${update_flag} == 1)); then + return 0 + fi + + if [ "$interactiveFqdn" == "no" ]; then + return 0 + fi + + local_fqdn_check + + echo + echo -e -n "${GREEN}Enter FQDN:port (like h1.${emailName}:6030) of an existing ${productName} cluster node to join${NC}" + echo + echo -e -n "${GREEN}OR leave it blank to build one${NC}:" + read firstEp + while true; do + if [ ! -z "$firstEp" ]; then + ${csudo}sed -i -r "s/#*\s*(firstEp\s*).*/\1$firstEp/" ${cfg_install_dir}/${configFile} + break + else + break + fi + done + + echo + echo -e -n "${GREEN}Enter your email address for priority support or enter empty to skip${NC}: " + read emailAddr + while true; do + if [ ! -z "$emailAddr" ]; then + email_file="${install_main_dir}/email" + ${csudo}bash -c "echo $emailAddr > ${email_file}" + break + else + break + fi + done +} + function install_log() { - ${csudo} rm -rf ${log_dir} || : - ${csudo} mkdir -p ${log_dir} && ${csudo} chmod 777 ${log_dir} + ${csudo}rm -rf ${log_dir} || : + ${csudo}mkdir -p ${log_dir} && ${csudo}chmod 777 ${log_dir} - ${csudo} ln -s ${log_dir} ${install_main_dir}/log + ${csudo}ln -s ${log_dir} ${install_main_dir}/log } function install_data() { - ${csudo} mkdir -p ${data_dir} + ${csudo}mkdir -p ${data_dir} - ${csudo} ln -s ${data_dir} ${install_main_dir}/data + ${csudo}ln -s ${data_dir} ${install_main_dir}/data } -function clean_service_on_systemd() { - taosd_service_config="${service_config_dir}/taosd.service" - if systemctl is-active --quiet taosd; then - echo "TDengine is running, stopping it..." - ${csudo} systemctl stop taosd &> /dev/null || echo &> /dev/null - fi - ${csudo} systemctl disable taosd &> /dev/null || echo &> /dev/null - ${csudo} rm -f ${taosd_service_config} +function install_connector() { + [ -d "${script_dir}/connector/" ] && ${csudo}cp -rf ${script_dir}/connector/ ${install_main_dir}/ +} + +function install_examples() { + if [ -d ${script_dir}/examples ]; then + ${csudo}cp -rf ${script_dir}/examples/* ${install_main_dir}/examples + fi +} + +function clean_service_on_sysvinit() { + if pidof ${serverName} &>/dev/null; then + ${csudo}service ${serverName} stop || : + fi + + if pidof tarbitrator &>/dev/null; then + ${csudo}service tarbitratord stop || : + fi - tarbitratord_service_config="${service_config_dir}/tarbitratord.service" - if systemctl is-active --quiet tarbitratord; then - echo "tarbitrator is running, stopping it..." - ${csudo} systemctl stop tarbitratord &> /dev/null || echo &> /dev/null + if ((${initd_mod} == 1)); then + if [ -e ${service_config_dir}/${serverName} ]; then + ${csudo}chkconfig --del ${serverName} || : fi - ${csudo} systemctl disable tarbitratord &> /dev/null || echo &> /dev/null - ${csudo} rm -f ${tarbitratord_service_config} - if [ "$verMode" == "cluster" ]; then - nginx_service_config="${service_config_dir}/nginxd.service" - if systemctl is-active --quiet nginxd; then - echo "Nginx for TDengine is running, stopping it..." - ${csudo} systemctl stop nginxd &> /dev/null || echo &> /dev/null - fi - ${csudo} systemctl disable nginxd &> /dev/null || echo &> /dev/null - ${csudo} rm -f ${nginx_service_config} + if [ -e ${service_config_dir}/tarbitratord ]; then + ${csudo}chkconfig --del tarbitratord || : + fi + elif ((${initd_mod} == 2)); then + if [ -e ${service_config_dir}/${serverName} ]; then + ${csudo}insserv -r ${serverName} || : + fi + if [ -e ${service_config_dir}/tarbitratord ]; then + ${csudo}insserv -r tarbitratord || : fi + elif ((${initd_mod} == 3)); then + if [ -e ${service_config_dir}/${serverName} ]; then + ${csudo}update-rc.d -f ${serverName} remove || : + fi + if [ -e ${service_config_dir}/tarbitratord ]; then + ${csudo}update-rc.d -f tarbitratord remove || : + fi + fi + + ${csudo}rm -f ${service_config_dir}/${serverName} || : + ${csudo}rm -f ${service_config_dir}/tarbitratord || : + + if $(which init &>/dev/null); then + ${csudo}init q || : + fi +} + +function install_service_on_sysvinit() { + clean_service_on_sysvinit + sleep 1 + + if ((${os_type} == 1)); then + # ${csudo}cp -f ${script_dir}/init.d/${serverName}.deb ${install_main_dir}/init.d/${serverName} + ${csudo}cp ${script_dir}/init.d/${serverName}.deb ${service_config_dir}/${serverName} && ${csudo}chmod a+x ${service_config_dir}/${serverName} + # ${csudo}cp -f ${script_dir}/init.d/tarbitratord.deb ${install_main_dir}/init.d/tarbitratord + ${csudo}cp ${script_dir}/init.d/tarbitratord.deb ${service_config_dir}/tarbitratord && ${csudo}chmod a+x ${service_config_dir}/tarbitratord + elif ((${os_type} == 2)); then + # ${csudo}cp -f ${script_dir}/init.d/${serverName}.rpm ${install_main_dir}/init.d/${serverName} + ${csudo}cp ${script_dir}/init.d/${serverName}.rpm ${service_config_dir}/${serverName} && ${csudo}chmod a+x ${service_config_dir}/${serverName} + # ${csudo}cp -f ${script_dir}/init.d/tarbitratord.rpm ${install_main_dir}/init.d/tarbitratord + ${csudo}cp ${script_dir}/init.d/tarbitratord.rpm ${service_config_dir}/tarbitratord && ${csudo}chmod a+x ${service_config_dir}/tarbitratord + fi + + if ((${initd_mod} == 1)); then + ${csudo}chkconfig --add ${serverName} || : + ${csudo}chkconfig --level 2345 ${serverName} on || : + ${csudo}chkconfig --add tarbitratord || : + ${csudo}chkconfig --level 2345 tarbitratord on || : + elif ((${initd_mod} == 2)); then + ${csudo}insserv ${serverName} || : + ${csudo}insserv -d ${serverName} || : + ${csudo}insserv tarbitratord || : + ${csudo}insserv -d tarbitratord || : + elif ((${initd_mod} == 3)); then + ${csudo}update-rc.d ${serverName} defaults || : + ${csudo}update-rc.d tarbitratord defaults || : + fi } -# taos:2345:respawn:/etc/init.d/taosd start +function clean_service_on_systemd() { + taosd_service_config="${service_config_dir}/${serverName}.service" + if systemctl is-active --quiet ${serverName}; then + echo "${productName} is running, stopping it..." + ${csudo}systemctl stop ${serverName} &>/dev/null || echo &>/dev/null + fi + ${csudo}systemctl disable ${serverName} &>/dev/null || echo &>/dev/null + ${csudo}rm -f ${taosd_service_config} + + tarbitratord_service_config="${service_config_dir}/tarbitratord.service" + if systemctl is-active --quiet tarbitratord; then + echo "tarbitrator is running, stopping it..." + ${csudo}systemctl stop tarbitratord &>/dev/null || echo &>/dev/null + fi + ${csudo}systemctl disable tarbitratord &>/dev/null || echo &>/dev/null + ${csudo}rm -f ${tarbitratord_service_config} + + if [ "$verMode" == "cluster" ]; then + nginx_service_config="${service_config_dir}/nginxd.service" + if systemctl is-active --quiet nginxd; then + echo "Nginx for ${productName} is running, stopping it..." + ${csudo}systemctl stop nginxd &>/dev/null || echo &>/dev/null + fi + ${csudo}systemctl disable nginxd &>/dev/null || echo &>/dev/null + ${csudo}rm -f ${nginx_service_config} + fi +} function install_service_on_systemd() { - clean_service_on_systemd - - taosd_service_config="${service_config_dir}/taosd.service" - ${csudo} bash -c "echo '[Unit]' >> ${taosd_service_config}" - ${csudo} bash -c "echo 'Description=TDengine server service' >> ${taosd_service_config}" - ${csudo} bash -c "echo 'After=network-online.target taosadapter.service' >> ${taosd_service_config}" - ${csudo} bash -c "echo 'Wants=network-online.target taosadapter.service' >> ${taosd_service_config}" - ${csudo} bash -c "echo >> ${taosd_service_config}" - ${csudo} bash -c "echo '[Service]' >> ${taosd_service_config}" - ${csudo} bash -c "echo 'Type=simple' >> ${taosd_service_config}" - ${csudo} bash -c "echo 'ExecStart=/usr/bin/taosd' >> ${taosd_service_config}" - ${csudo} bash -c "echo 'ExecStartPre=/usr/local/taos/bin/startPre.sh' >> ${taosd_service_config}" - ${csudo} bash -c "echo 'TimeoutStopSec=1000000s' >> ${taosd_service_config}" - ${csudo} bash -c "echo 'LimitNOFILE=infinity' >> ${taosd_service_config}" - ${csudo} bash -c "echo 'LimitNPROC=infinity' >> ${taosd_service_config}" - ${csudo} bash -c "echo 'LimitCORE=infinity' >> ${taosd_service_config}" - ${csudo} bash -c "echo 'TimeoutStartSec=0' >> ${taosd_service_config}" - ${csudo} bash -c "echo 'StandardOutput=null' >> ${taosd_service_config}" - ${csudo} bash -c "echo 'Restart=always' >> ${taosd_service_config}" - ${csudo} bash -c "echo 'StartLimitBurst=3' >> ${taosd_service_config}" - ${csudo} bash -c "echo 'StartLimitInterval=60s' >> ${taosd_service_config}" - #${csudo} bash -c "echo 'StartLimitIntervalSec=60s' >> ${taosd_service_config}" - ${csudo} bash -c "echo >> ${taosd_service_config}" - ${csudo} bash -c "echo '[Install]' >> ${taosd_service_config}" - ${csudo} bash -c "echo 'WantedBy=multi-user.target' >> ${taosd_service_config}" - ${csudo} systemctl enable taosd - - ${csudo} systemctl daemon-reload + clean_service_on_systemd + + [ -f ${script_dir}/cfg/${serverName}.service ] && + ${csudo}cp ${script_dir}/cfg/${serverName}.service \ + ${service_config_dir}/ || : + ${csudo}systemctl daemon-reload + + ${csudo}systemctl enable ${serverName} + + [ -f ${script_dir}/cfg/tarbitratord.service ] && + ${csudo}cp ${script_dir}/cfg/tarbitratord.service \ + ${service_config_dir}/ || : + ${csudo}systemctl daemon-reload + + if [ "$verMode" == "cluster" ]; then + [ -f ${script_dir}/cfg/nginxd.service ] && + ${csudo}cp ${script_dir}/cfg/nginxd.service \ + ${service_config_dir}/ || : + ${csudo}systemctl daemon-reload + + if ! ${csudo}systemctl enable nginxd &>/dev/null; then + ${csudo}systemctl daemon-reexec + ${csudo}systemctl enable nginxd + fi + ${csudo}systemctl start nginxd + fi +} + +function install_adapter_service() { + if ((${service_mod} == 0)); then + [ -f ${script_dir}/cfg/${adapterName}.service ] && + ${csudo}cp ${script_dir}/cfg/${adapterName}.service \ + ${service_config_dir}/ || : + ${csudo}systemctl daemon-reload + fi } function install_service() { - # if ((${service_mod}==0)); then - # install_service_on_systemd - # elif ((${service_mod}==1)); then - # install_service_on_sysvinit - # else - # # must manual stop taosd - kill_process taosd - # fi + if ((${service_mod} == 0)); then + install_service_on_systemd + elif ((${service_mod} == 1)); then + install_service_on_sysvinit + else + kill_process ${serverName} + fi } -function install_TDengine() { - # Start to install - echo -e "${GREEN}Start to install TDengine...${NC}" +vercomp() { + if [[ $1 == $2 ]]; then + return 0 + fi + local IFS=. + local i ver1=($1) ver2=($2) + # fill empty fields in ver1 with zeros + for ((i = ${#ver1[@]}; i < ${#ver2[@]}; i++)); do + ver1[i]=0 + done - install_main_path - install_data - install_log - install_header - install_lib - install_taosTools - - if [ -z $1 ]; then # install service and client - # For installing new - install_bin - install_service - #install_config - - # Ask if to start the service - #echo - #echo -e "\033[44;32;1mTDengine is installed successfully!${NC}" - echo - echo -e "${GREEN_DARK}To configure TDengine ${NC}: edit /etc/taos/taos.cfg" - if ((${service_mod}==0)); then - echo -e "${GREEN_DARK}To start TDengine ${NC}: ${csudo} systemctl start taosd${NC}" - elif ((${service_mod}==1)); then - echo -e "${GREEN_DARK}To start TDengine ${NC}: ${csudo} service taosd start${NC}" + for ((i = 0; i < ${#ver1[@]}; i++)); do + if [[ -z ${ver2[i]} ]]; then + # fill empty fields in ver2 with zeros + ver2[i]=0 + fi + if ((10#${ver1[i]} > 10#${ver2[i]})); then + return 1 + fi + if ((10#${ver1[i]} < 10#${ver2[i]})); then + return 2 + fi + done + return 0 +} + +function is_version_compatible() { + + curr_version=$(ls ${script_dir}/driver/libtaos.so* | awk -F 'libtaos.so.' '{print $2}') + + if [ -f ${script_dir}/driver/vercomp.txt ]; then + min_compatible_version=$(cat ${script_dir}/driver/vercomp.txt) + else + min_compatible_version=$(${script_dir}/bin/${serverName} -V | head -1 | cut -d ' ' -f 5) + fi + + exist_version=$(${installDir}/bin/${serverName} -V | head -1 | cut -d ' ' -f 3) + vercomp $exist_version "2.0.16.0" + case $? in + 2) + prompt_force=1 + ;; + esac + + vercomp $curr_version $min_compatible_version + echo "" # avoid $? value not update + + case $? in + 0) return 0 ;; + 1) return 0 ;; + 2) return 1 ;; + esac +} + +function updateProduct() { + # Check if version compatible + if ! is_version_compatible; then + echo -e "${RED}Version incompatible${NC}" + return 1 + fi + + # Start to update + if [ ! -e ${tarName} ]; then + echo "File ${tarName} does not exist" + exit 1 + fi + tar -zxf ${tarName} + install_jemalloc + + echo -e "${GREEN}Start to update ${productName}...${NC}" + # Stop the service if running + if pidof ${serverName} &>/dev/null; then + if ((${service_mod} == 0)); then + ${csudo}systemctl stop ${serverName} || : + elif ((${service_mod} == 1)); then + ${csudo}service ${serverName} stop || : + else + kill_process ${serverName} + fi + sleep 1 + fi + + if [ "$verMode" == "cluster" ]; then + if pidof nginx &>/dev/null; then + if ((${service_mod} == 0)); then + ${csudo}systemctl stop nginxd || : + elif ((${service_mod} == 1)); then + ${csudo}service nginxd stop || : + else + kill_process nginx + fi + sleep 1 + fi + fi + + install_main_path + + install_log + install_header + install_lib + + if [ "$verMode" == "cluster" ]; then + install_connector + fi + + install_examples + if [ -z $1 ]; then + install_bin + install_service + install_adapter_service + install_config + install_adapter_config + + openresty_work=false + if [ "$verMode" == "cluster" ]; then + # Check if openresty is installed + # Check if nginx is installed successfully + if type curl &>/dev/null; then + if curl -sSf http://127.0.0.1:${nginx_port} &>/dev/null; then + echo -e "\033[44;32;1mNginx for ${productName} is updated successfully!${NC}" + openresty_work=true else - echo -e "${GREEN_DARK}To start TDengine ${NC}: taosd${NC}" + echo -e "\033[44;31;5mNginx for ${productName} does not work! Please try again!\033[0m" fi + fi + fi - if [ ! -z "$firstEp" ]; then - tmpFqdn=${firstEp%%:*} - substr=":" - if [[ $firstEp =~ $substr ]];then - tmpPort=${firstEp#*:} - else - tmpPort="" - fi - if [[ "$tmpPort" != "" ]];then - echo -e "${GREEN_DARK}To access TDengine ${NC}: taos -h $tmpFqdn -P $tmpPort${GREEN_DARK} to login into cluster, then${NC}" - else - echo -e "${GREEN_DARK}To access TDengine ${NC}: taos -h $tmpFqdn${GREEN_DARK} to login into cluster, then${NC}" - fi - echo -e "${GREEN_DARK}execute ${NC}: create dnode 'newDnodeFQDN:port'; ${GREEN_DARK}to add this new node${NC}" - echo - elif [ ! -z "$serverFqdn" ]; then - echo -e "${GREEN_DARK}To access TDengine ${NC}: taos -h $serverFqdn${GREEN_DARK} to login into TDengine server${NC}" - echo - fi + echo + echo -e "${GREEN_DARK}To configure ${productName} ${NC}: edit ${cfg_install_dir}/${configFile}" + echo -e "${GREEN_DARK}To configure Adapter (if has) ${NC}: edit ${cfg_install_dir}/${adapterName}.toml" + if ((${service_mod} == 0)); then + echo -e "${GREEN_DARK}To start ${productName} ${NC}: ${csudo}systemctl start ${serverName}${NC}" + elif ((${service_mod} == 1)); then + echo -e "${GREEN_DARK}To start ${productName} ${NC}: ${csudo}service ${serverName} start${NC}" + else + echo -e "${GREEN_DARK}To start Adapter (if has)${NC}: ${adapterName} &${NC}" + echo -e "${GREEN_DARK}To start ${productName} ${NC}: ./${serverName}${NC}" + fi + + if [ ${openresty_work} = 'true' ]; then + echo -e "${GREEN_DARK}To access ${productName} ${NC}: use ${GREEN_UNDERLINE}${clientName} -h $serverFqdn${NC} in shell OR from ${GREEN_UNDERLINE}http://127.0.0.1:${nginx_port}${NC}" + else + echo -e "${GREEN_DARK}To access ${productName} ${NC}: use ${GREEN_UNDERLINE}${clientName} -h $serverFqdn${NC} in shell${NC}" + fi - echo -e "\033[44;32;1mTDengine is installed successfully!${NC}" - echo - else # Only install client - install_bin - #install_config - echo - echo -e "\033[44;32;1mTDengine client is installed successfully!${NC}" + if ((${prompt_force} == 1)); then + echo "" + echo -e "${RED}Please run '${serverName} --force-keep-file' at first time for the exist ${productName} $exist_version!${NC}" fi + echo + echo -e "\033[44;32;1m${productName} is updated successfully!${NC}" + else + install_bin + install_config - touch ~/.taos_history + echo + echo -e "\033[44;32;1m${productName} client is updated successfully!${NC}" + fi + + rm -rf $(tar -tf ${tarName} | grep -v "^\./$") } +function installProduct() { + # Start to install + if [ ! -e ${tarName} ]; then + echo "File ${tarName} does not exist" + exit 1 + fi + tar -zxf ${tarName} + + echo -e "${GREEN}Start to install ${productName}...${NC}" + + install_main_path + + if [ -z $1 ]; then + install_data + fi + + install_log + install_header + install_lib + install_jemalloc + #install_avro lib + #install_avro lib64 + + if [ "$verMode" == "cluster" ]; then + install_connector + fi + install_examples + + if [ -z $1 ]; then # install service and client + # For installing new + install_bin + install_service + install_adapter_service + install_adapter_config + + openresty_work=false + if [ "$verMode" == "cluster" ]; then + # Check if nginx is installed successfully + if type curl &>/dev/null; then + if curl -sSf http://127.0.0.1:${nginx_port} &>/dev/null; then + echo -e "\033[44;32;1mNginx for ${productName} is installed successfully!${NC}" + openresty_work=true + else + echo -e "\033[44;31;5mNginx for ${productName} does not work! Please try again!\033[0m" + fi + fi + fi + + install_config + + # Ask if to start the service + echo + echo -e "${GREEN_DARK}To configure ${productName} ${NC}: edit ${cfg_install_dir}/${configFile}" + echo -e "${GREEN_DARK}To configure ${adapterName} (if has) ${NC}: edit ${cfg_install_dir}/${adapterName}.toml" + if ((${service_mod} == 0)); then + echo -e "${GREEN_DARK}To start ${productName} ${NC}: ${csudo}systemctl start ${serverName}${NC}" + elif ((${service_mod} == 1)); then + echo -e "${GREEN_DARK}To start ${productName} ${NC}: ${csudo}service ${serverName} start${NC}" + else + echo -e "${GREEN_DARK}To start Adapter (if has)${NC}: ${adapterName} &${NC}" + echo -e "${GREEN_DARK}To start ${productName} ${NC}: ${serverName}${NC}" + fi + + if [ ! -z "$firstEp" ]; then + tmpFqdn=${firstEp%%:*} + substr=":" + if [[ $firstEp =~ $substr ]]; then + tmpPort=${firstEp#*:} + else + tmpPort="" + fi + if [[ "$tmpPort" != "" ]]; then + echo -e "${GREEN_DARK}To access ${productName} ${NC}: ${clientName} -h $tmpFqdn -P $tmpPort${GREEN_DARK} to login into cluster, then${NC}" + else + echo -e "${GREEN_DARK}To access ${productName} ${NC}: ${clientName} -h $tmpFqdn${GREEN_DARK} to login into cluster, then${NC}" + fi + echo -e "${GREEN_DARK}execute ${NC}: create dnode 'newDnodeFQDN:port'; ${GREEN_DARK}to add this new node${NC}" + echo + elif [ ! -z "$serverFqdn" ]; then + echo -e "${GREEN_DARK}To access ${productName} ${NC}: ${clientName} -h $serverFqdn${GREEN_DARK} to login into ${productName} server${NC}" + echo + fi + + echo -e "\033[44;32;1m${productName} is installed successfully!${NC}" + echo + else # Only install client + install_bin + install_config + echo + echo -e "\033[44;32;1m${productName} client is installed successfully!${NC}" + fi + + touch ~/.${historyFile} + rm -rf $(tar -tf ${tarName} | grep -v "^\./$") +} ## ==============================Main program starts from here============================ serverFqdn=$(hostname) if [ "$verType" == "server" ]; then - # Install server and client - install_TDengine + # Install server and client + if [ -x ${bin_dir}/${serverName} ]; then + update_flag=1 + updateProduct + else + installProduct + fi elif [ "$verType" == "client" ]; then - interactiveFqdn=no - # Only install client - install_TDengine client + interactiveFqdn=no + # Only install client + if [ -x ${bin_dir}/${clientName} ]; then + update_flag=1 + updateProduct client + else + installProduct client + fi else - echo "please input correct verType" + echo "please input correct verType" fi diff --git a/packaging/tools/install_arbi.sh b/packaging/tools/install_arbi.sh old mode 100644 new mode 100755 index 22874f058a919a0452fc303c9e69b3355444f604..e3c63965d4beee31cea91d2f8fd84e3d2bdd00d3 --- a/packaging/tools/install_arbi.sh +++ b/packaging/tools/install_arbi.sh @@ -1,339 +1,339 @@ -#!/bin/bash -# -# This file is used to install database on linux systems. The operating system -# is required to use systemd to manage services at boot - -set -e -#set -x - -# -----------------------Variables definition--------------------- -script_dir=$(dirname $(readlink -f "$0")) - -bin_link_dir="/usr/bin" -#inc_link_dir="/usr/include" - -#install main path -install_main_dir="/usr/local/tarbitrator" - -# old bin dir -bin_dir="/usr/local/tarbitrator/bin" - -service_config_dir="/etc/systemd/system" - -# Color setting -RED='\033[0;31m' -GREEN='\033[1;32m' -GREEN_DARK='\033[0;32m' -GREEN_UNDERLINE='\033[4;32m' -NC='\033[0m' - -csudo="" -if command -v sudo >/dev/null; then - csudo="sudo " -fi - -update_flag=0 - -initd_mod=0 -service_mod=2 -if pidof systemd &>/dev/null; then - service_mod=0 -elif $(which service &>/dev/null); then - service_mod=1 - service_config_dir="/etc/init.d" - if $(which chkconfig &>/dev/null); then - initd_mod=1 - elif $(which insserv &>/dev/null); then - initd_mod=2 - elif $(which update-rc.d &>/dev/null); then - initd_mod=3 - else - service_mod=2 - fi -else - service_mod=2 -fi - -# get the operating system type for using the corresponding init file -# ubuntu/debian(deb), centos/fedora(rpm), others: opensuse, redhat, ..., no verification -#osinfo=$(awk -F= '/^NAME/{print $2}' /etc/os-release) -if [[ -e /etc/os-release ]]; then - osinfo=$(cat /etc/os-release | grep "NAME" | cut -d '"' -f2) || : -else - osinfo="" -fi -#echo "osinfo: ${osinfo}" -os_type=0 -if echo $osinfo | grep -qwi "ubuntu"; then - # echo "This is ubuntu system" - os_type=1 -elif echo $osinfo | grep -qwi "debian"; then - # echo "This is debian system" - os_type=1 -elif echo $osinfo | grep -qwi "Kylin"; then - # echo "This is Kylin system" - os_type=1 -elif echo $osinfo | grep -qwi "centos"; then - # echo "This is centos system" - os_type=2 -elif echo $osinfo | grep -qwi "fedora"; then - # echo "This is fedora system" - os_type=2 -else - echo " osinfo: ${osinfo}" - echo " This is an officially unverified linux system," - echo " if there are any problems with the installation and operation, " - echo " please feel free to contact taosdata.com for support." - os_type=1 -fi - -function kill_tarbitrator() { - pid=$(ps -ef | grep "tarbitrator" | grep -v "grep" | awk '{print $2}') - if [ -n "$pid" ]; then - ${csudo}kill -9 $pid || : - fi -} - -function install_main_path() { - #create install main dir and all sub dir - ${csudo}rm -rf ${install_main_dir} || : - ${csudo}mkdir -p ${install_main_dir} - ${csudo}mkdir -p ${install_main_dir}/bin - #${csudo}mkdir -p ${install_main_dir}/include - ${csudo}mkdir -p ${install_main_dir}/init.d -} - -function install_bin() { - # Remove links - ${csudo}rm -f ${bin_link_dir}/rmtarbitrator || : - ${csudo}rm -f ${bin_link_dir}/tarbitrator || : - ${csudo}cp -r ${script_dir}/bin/* ${install_main_dir}/bin && ${csudo}chmod 0555 ${install_main_dir}/bin/* - - #Make link - [ -x ${install_main_dir}/bin/remove_arbi.sh ] && ${csudo}ln -s ${install_main_dir}/bin/remove_arbi.sh ${bin_link_dir}/rmtarbitrator || : - [ -x ${install_main_dir}/bin/tarbitrator ] && ${csudo}ln -s ${install_main_dir}/bin/tarbitrator ${bin_link_dir}/tarbitrator || : -} - -function install_header() { - ${csudo}rm -f ${inc_link_dir}/taos.h ${inc_link_dir}/taosdef.h ${inc_link_dir}/taoserror.h || : - ${csudo}cp -f ${script_dir}/inc/* ${install_main_dir}/include && ${csudo}chmod 644 ${install_main_dir}/include/* - ${csudo}ln -s ${install_main_dir}/include/taos.h ${inc_link_dir}/taos.h - ${csudo}ln -s ${install_main_dir}/include/taosdef.h ${inc_link_dir}/taosdef.h - ${csudo}ln -s ${install_main_dir}/include/taoserror.h ${inc_link_dir}/taoserror.h -} - -function install_jemalloc() { - jemalloc_dir=${script_dir}/jemalloc - - if [ -d ${jemalloc_dir} ]; then - ${csudo}/usr/bin/install -c -d /usr/local/bin - - if [ -f ${jemalloc_dir}/bin/jemalloc-config ]; then - ${csudo}/usr/bin/install -c -m 755 ${jemalloc_dir}/bin/jemalloc-config /usr/local/bin - fi - if [ -f ${jemalloc_dir}/bin/jemalloc.sh ]; then - ${csudo}/usr/bin/install -c -m 755 ${jemalloc_dir}/bin/jemalloc.sh /usr/local/bin - fi - if [ -f ${jemalloc_dir}/bin/jeprof ]; then - ${csudo}/usr/bin/install -c -m 755 ${jemalloc_dir}/bin/jeprof /usr/local/bin - fi - if [ -f ${jemalloc_dir}/include/jemalloc/jemalloc.h ]; then - ${csudo}/usr/bin/install -c -d /usr/local/include/jemalloc - ${csudo}/usr/bin/install -c -m 644 ${jemalloc_dir}/include/jemalloc/jemalloc.h /usr/local/include/jemalloc - fi - if [ -f ${jemalloc_dir}/lib/libjemalloc.so.2 ]; then - ${csudo}/usr/bin/install -c -d /usr/local/lib - ${csudo}/usr/bin/install -c -m 755 ${jemalloc_dir}/lib/libjemalloc.so.2 /usr/local/lib - ${csudo}ln -sf libjemalloc.so.2 /usr/local/lib/libjemalloc.so - ${csudo}/usr/bin/install -c -d /usr/local/lib - if [ -f ${jemalloc_dir}/lib/libjemalloc.a ]; then - ${csudo}/usr/bin/install -c -m 755 ${jemalloc_dir}/lib/libjemalloc.a /usr/local/lib - fi - if [ -f ${jemalloc_dir}/lib/libjemalloc_pic.a ]; then - ${csudo}/usr/bin/install -c -m 755 ${jemalloc_dir}/lib/libjemalloc_pic.a /usr/local/lib - fi - if [ -f ${jemalloc_dir}/lib/libjemalloc_pic.a ]; then - ${csudo}/usr/bin/install -c -d /usr/local/lib/pkgconfig - ${csudo}/usr/bin/install -c -m 644 ${jemalloc_dir}/lib/pkgconfig/jemalloc.pc /usr/local/lib/pkgconfig - fi - fi - if [ -f ${jemalloc_dir}/share/doc/jemalloc/jemalloc.html ]; then - ${csudo}/usr/bin/install -c -d /usr/local/share/doc/jemalloc - ${csudo}/usr/bin/install -c -m 644 ${jemalloc_dir}/share/doc/jemalloc/jemalloc.html /usr/local/share/doc/jemalloc - fi - if [ -f ${jemalloc_dir}/share/man/man3/jemalloc.3 ]; then - ${csudo}/usr/bin/install -c -d /usr/local/share/man/man3 - ${csudo}/usr/bin/install -c -m 644 ${jemalloc_dir}/share/man/man3/jemalloc.3 /usr/local/share/man/man3 - fi - - if [ -d /etc/ld.so.conf.d ]; then - echo "/usr/local/lib" | ${csudo}tee /etc/ld.so.conf.d/jemalloc.conf >/dev/null || echo -e "failed to write /etc/ld.so.conf.d/jemalloc.conf" - ${csudo}ldconfig - else - echo "/etc/ld.so.conf.d not found!" - fi - fi -} - -function clean_service_on_sysvinit() { - if pidof tarbitrator &>/dev/null; then - ${csudo}service tarbitratord stop || : - fi - - if ((${initd_mod} == 1)); then - if [ -e ${service_config_dir}/tarbitratord ]; then - ${csudo}chkconfig --del tarbitratord || : - fi - elif ((${initd_mod} == 2)); then - if [ -e ${service_config_dir}/tarbitratord ]; then - ${csudo}insserv -r tarbitratord || : - fi - elif ((${initd_mod} == 3)); then - if [ -e ${service_config_dir}/tarbitratord ]; then - ${csudo}update-rc.d -f tarbitratord remove || : - fi - fi - - ${csudo}rm -f ${service_config_dir}/tarbitratord || : - - if $(which init &>/dev/null); then - ${csudo}init q || : - fi -} - -function install_service_on_sysvinit() { - clean_service_on_sysvinit - sleep 1 - - if ((${os_type} == 1)); then - ${csudo}cp -f ${script_dir}/init.d/tarbitratord.deb ${install_main_dir}/init.d/tarbitratord - ${csudo}cp ${script_dir}/init.d/tarbitratord.deb ${service_config_dir}/tarbitratord && ${csudo}chmod a+x ${service_config_dir}/tarbitratord - elif ((${os_type} == 2)); then - ${csudo}cp -f ${script_dir}/init.d/tarbitratord.rpm ${install_main_dir}/init.d/tarbitratord - ${csudo}cp ${script_dir}/init.d/tarbitratord.rpm ${service_config_dir}/tarbitratord && ${csudo}chmod a+x ${service_config_dir}/tarbitratord - fi - - if ((${initd_mod} == 1)); then - ${csudo}chkconfig --add tarbitratord || : - ${csudo}chkconfig --level 2345 tarbitratord on || : - elif ((${initd_mod} == 2)); then - ${csudo}insserv tarbitratord || : - ${csudo}insserv -d tarbitratord || : - elif ((${initd_mod} == 3)); then - ${csudo}update-rc.d tarbitratord defaults || : - fi -} - -function clean_service_on_systemd() { - tarbitratord_service_config="${service_config_dir}/tarbitratord.service" - if systemctl is-active --quiet tarbitratord; then - echo "tarbitrator is running, stopping it..." - ${csudo}systemctl stop tarbitratord &>/dev/null || echo &>/dev/null - fi - ${csudo}systemctl disable tarbitratord &>/dev/null || echo &>/dev/null - - ${csudo}rm -f ${tarbitratord_service_config} -} - -function install_service_on_systemd() { - clean_service_on_systemd - - tarbitratord_service_config="${service_config_dir}/tarbitratord.service" - - ${csudo}bash -c "echo '[Unit]' >> ${tarbitratord_service_config}" - ${csudo}bash -c "echo 'Description=TDengine arbitrator service' >> ${tarbitratord_service_config}" - ${csudo}bash -c "echo 'After=network-online.target' >> ${tarbitratord_service_config}" - ${csudo}bash -c "echo 'Wants=network-online.target' >> ${tarbitratord_service_config}" - ${csudo}bash -c "echo >> ${tarbitratord_service_config}" - ${csudo}bash -c "echo '[Service]' >> ${tarbitratord_service_config}" - ${csudo}bash -c "echo 'Type=simple' >> ${tarbitratord_service_config}" - ${csudo}bash -c "echo 'ExecStart=/usr/bin/tarbitrator' >> ${tarbitratord_service_config}" - ${csudo}bash -c "echo 'TimeoutStopSec=1000000s' >> ${tarbitratord_service_config}" - ${csudo}bash -c "echo 'LimitNOFILE=infinity' >> ${tarbitratord_service_config}" - ${csudo}bash -c "echo 'LimitNPROC=infinity' >> ${tarbitratord_service_config}" - ${csudo}bash -c "echo 'LimitCORE=infinity' >> ${tarbitratord_service_config}" - ${csudo}bash -c "echo 'TimeoutStartSec=0' >> ${tarbitratord_service_config}" - ${csudo}bash -c "echo 'StandardOutput=null' >> ${tarbitratord_service_config}" - ${csudo}bash -c "echo 'Restart=always' >> ${tarbitratord_service_config}" - ${csudo}bash -c "echo 'StartLimitBurst=3' >> ${tarbitratord_service_config}" - ${csudo}bash -c "echo 'StartLimitInterval=60s' >> ${tarbitratord_service_config}" - ${csudo}bash -c "echo >> ${tarbitratord_service_config}" - ${csudo}bash -c "echo '[Install]' >> ${tarbitratord_service_config}" - ${csudo}bash -c "echo 'WantedBy=multi-user.target' >> ${tarbitratord_service_config}" - ${csudo}systemctl enable tarbitratord -} - -function install_service() { - if ((${service_mod} == 0)); then - install_service_on_systemd - elif ((${service_mod} == 1)); then - install_service_on_sysvinit - else - kill_tarbitrator - fi -} - -function update_TDengine() { - # Start to update - echo -e "${GREEN}Start to update TDengine's arbitrator ...${NC}" - # Stop the service if running - if pidof tarbitrator &>/dev/null; then - if ((${service_mod} == 0)); then - ${csudo}systemctl stop tarbitratord || : - elif ((${service_mod} == 1)); then - ${csudo}service tarbitratord stop || : - else - kill_tarbitrator - fi - sleep 1 - fi - - install_main_path - #install_header - install_bin - install_service - install_jemalloc - - echo - if ((${service_mod} == 0)); then - echo -e "${GREEN_DARK}To start arbitrator ${NC}: ${csudo}systemctl start tarbitratord${NC}" - elif ((${service_mod} == 1)); then - echo -e "${GREEN_DARK}To start arbitrator ${NC}: ${csudo}service tarbitratord start${NC}" - else - echo -e "${GREEN_DARK}To start arbitrator ${NC}: ./tarbitrator${NC}" - fi - echo - echo -e "\033[44;32;1mTDengine's arbitrator is updated successfully!${NC}" -} - -function install_TDengine() { - # Start to install - echo -e "${GREEN}Start to install TDengine's arbitrator ...${NC}" - - install_main_path - #install_header - install_bin - install_service - install_jemalloc - - echo - if ((${service_mod} == 0)); then - echo -e "${GREEN_DARK}To start arbitrator ${NC}: ${csudo}systemctl start tarbitratord${NC}" - elif ((${service_mod} == 1)); then - echo -e "${GREEN_DARK}To start arbitrator ${NC}: ${csudo}service tarbitratord start${NC}" - else - echo -e "${GREEN_DARK}To start arbitrator ${NC}: tarbitrator${NC}" - fi - - echo -e "\033[44;32;1mTDengine's arbitrator is installed successfully!${NC}" - echo -} - -## ==============================Main program starts from here============================ -# Install server and client -if [ -x ${bin_dir}/tarbitrator ]; then - update_flag=1 - update_TDengine -else - install_TDengine -fi +#!/bin/bash +# +# This file is used to install database on linux systems. The operating system +# is required to use systemd to manage services at boot + +set -e +#set -x + +# -----------------------Variables definition--------------------- +script_dir=$(dirname $(readlink -f "$0")) + +bin_link_dir="/usr/bin" +#inc_link_dir="/usr/include" + +#install main path +install_main_dir="/usr/local/tarbitrator" + +# old bin dir +bin_dir="/usr/local/tarbitrator/bin" + +service_config_dir="/etc/systemd/system" + +# Color setting +RED='\033[0;31m' +GREEN='\033[1;32m' +GREEN_DARK='\033[0;32m' +GREEN_UNDERLINE='\033[4;32m' +NC='\033[0m' + +csudo="" +if command -v sudo >/dev/null; then + csudo="sudo " +fi + +update_flag=0 + +initd_mod=0 +service_mod=2 +if pidof systemd &>/dev/null; then + service_mod=0 +elif $(which service &>/dev/null); then + service_mod=1 + service_config_dir="/etc/init.d" + if $(which chkconfig &>/dev/null); then + initd_mod=1 + elif $(which insserv &>/dev/null); then + initd_mod=2 + elif $(which update-rc.d &>/dev/null); then + initd_mod=3 + else + service_mod=2 + fi +else + service_mod=2 +fi + +# get the operating system type for using the corresponding init file +# ubuntu/debian(deb), centos/fedora(rpm), others: opensuse, redhat, ..., no verification +#osinfo=$(awk -F= '/^NAME/{print $2}' /etc/os-release) +if [[ -e /etc/os-release ]]; then + osinfo=$(cat /etc/os-release | grep "NAME" | cut -d '"' -f2) || : +else + osinfo="" +fi +#echo "osinfo: ${osinfo}" +os_type=0 +if echo $osinfo | grep -qwi "ubuntu"; then + # echo "This is ubuntu system" + os_type=1 +elif echo $osinfo | grep -qwi "debian"; then + # echo "This is debian system" + os_type=1 +elif echo $osinfo | grep -qwi "Kylin"; then + # echo "This is Kylin system" + os_type=1 +elif echo $osinfo | grep -qwi "centos"; then + # echo "This is centos system" + os_type=2 +elif echo $osinfo | grep -qwi "fedora"; then + # echo "This is fedora system" + os_type=2 +else + echo " osinfo: ${osinfo}" + echo " This is an officially unverified linux system," + echo " if there are any problems with the installation and operation, " + echo " please feel free to contact taosdata.com for support." + os_type=1 +fi + +function kill_tarbitrator() { + pid=$(ps -ef | grep "tarbitrator" | grep -v "grep" | awk '{print $2}') + if [ -n "$pid" ]; then + ${csudo}kill -9 $pid || : + fi +} + +function install_main_path() { + #create install main dir and all sub dir + ${csudo}rm -rf ${install_main_dir} || : + ${csudo}mkdir -p ${install_main_dir} + ${csudo}mkdir -p ${install_main_dir}/bin + #${csudo}mkdir -p ${install_main_dir}/include + ${csudo}mkdir -p ${install_main_dir}/init.d +} + +function install_bin() { + # Remove links + ${csudo}rm -f ${bin_link_dir}/rmtarbitrator || : + ${csudo}rm -f ${bin_link_dir}/tarbitrator || : + ${csudo}cp -r ${script_dir}/bin/* ${install_main_dir}/bin && ${csudo}chmod 0555 ${install_main_dir}/bin/* + + #Make link + [ -x ${install_main_dir}/bin/remove_arbi.sh ] && ${csudo}ln -s ${install_main_dir}/bin/remove_arbi.sh ${bin_link_dir}/rmtarbitrator || : + [ -x ${install_main_dir}/bin/tarbitrator ] && ${csudo}ln -s ${install_main_dir}/bin/tarbitrator ${bin_link_dir}/tarbitrator || : +} + +function install_header() { + ${csudo}rm -f ${inc_link_dir}/taos.h ${inc_link_dir}/taosdef.h ${inc_link_dir}/taoserror.h || : + ${csudo}cp -f ${script_dir}/inc/* ${install_main_dir}/include && ${csudo}chmod 644 ${install_main_dir}/include/* + ${csudo}ln -s ${install_main_dir}/include/taos.h ${inc_link_dir}/taos.h + ${csudo}ln -s ${install_main_dir}/include/taosdef.h ${inc_link_dir}/taosdef.h + ${csudo}ln -s ${install_main_dir}/include/taoserror.h ${inc_link_dir}/taoserror.h +} + +function install_jemalloc() { + jemalloc_dir=${script_dir}/jemalloc + + if [ -d ${jemalloc_dir} ]; then + ${csudo}/usr/bin/install -c -d /usr/local/bin + + if [ -f ${jemalloc_dir}/bin/jemalloc-config ]; then + ${csudo}/usr/bin/install -c -m 755 ${jemalloc_dir}/bin/jemalloc-config /usr/local/bin + fi + if [ -f ${jemalloc_dir}/bin/jemalloc.sh ]; then + ${csudo}/usr/bin/install -c -m 755 ${jemalloc_dir}/bin/jemalloc.sh /usr/local/bin + fi + if [ -f ${jemalloc_dir}/bin/jeprof ]; then + ${csudo}/usr/bin/install -c -m 755 ${jemalloc_dir}/bin/jeprof /usr/local/bin + fi + if [ -f ${jemalloc_dir}/include/jemalloc/jemalloc.h ]; then + ${csudo}/usr/bin/install -c -d /usr/local/include/jemalloc + ${csudo}/usr/bin/install -c -m 644 ${jemalloc_dir}/include/jemalloc/jemalloc.h /usr/local/include/jemalloc + fi + if [ -f ${jemalloc_dir}/lib/libjemalloc.so.2 ]; then + ${csudo}/usr/bin/install -c -d /usr/local/lib + ${csudo}/usr/bin/install -c -m 755 ${jemalloc_dir}/lib/libjemalloc.so.2 /usr/local/lib + ${csudo}ln -sf libjemalloc.so.2 /usr/local/lib/libjemalloc.so + ${csudo}/usr/bin/install -c -d /usr/local/lib + if [ -f ${jemalloc_dir}/lib/libjemalloc.a ]; then + ${csudo}/usr/bin/install -c -m 755 ${jemalloc_dir}/lib/libjemalloc.a /usr/local/lib + fi + if [ -f ${jemalloc_dir}/lib/libjemalloc_pic.a ]; then + ${csudo}/usr/bin/install -c -m 755 ${jemalloc_dir}/lib/libjemalloc_pic.a /usr/local/lib + fi + if [ -f ${jemalloc_dir}/lib/libjemalloc_pic.a ]; then + ${csudo}/usr/bin/install -c -d /usr/local/lib/pkgconfig + ${csudo}/usr/bin/install -c -m 644 ${jemalloc_dir}/lib/pkgconfig/jemalloc.pc /usr/local/lib/pkgconfig + fi + fi + if [ -f ${jemalloc_dir}/share/doc/jemalloc/jemalloc.html ]; then + ${csudo}/usr/bin/install -c -d /usr/local/share/doc/jemalloc + ${csudo}/usr/bin/install -c -m 644 ${jemalloc_dir}/share/doc/jemalloc/jemalloc.html /usr/local/share/doc/jemalloc + fi + if [ -f ${jemalloc_dir}/share/man/man3/jemalloc.3 ]; then + ${csudo}/usr/bin/install -c -d /usr/local/share/man/man3 + ${csudo}/usr/bin/install -c -m 644 ${jemalloc_dir}/share/man/man3/jemalloc.3 /usr/local/share/man/man3 + fi + + if [ -d /etc/ld.so.conf.d ]; then + echo "/usr/local/lib" | ${csudo}tee /etc/ld.so.conf.d/jemalloc.conf >/dev/null || echo -e "failed to write /etc/ld.so.conf.d/jemalloc.conf" + ${csudo}ldconfig + else + echo "/etc/ld.so.conf.d not found!" + fi + fi +} + +function clean_service_on_sysvinit() { + if pidof tarbitrator &>/dev/null; then + ${csudo}service tarbitratord stop || : + fi + + if ((${initd_mod} == 1)); then + if [ -e ${service_config_dir}/tarbitratord ]; then + ${csudo}chkconfig --del tarbitratord || : + fi + elif ((${initd_mod} == 2)); then + if [ -e ${service_config_dir}/tarbitratord ]; then + ${csudo}insserv -r tarbitratord || : + fi + elif ((${initd_mod} == 3)); then + if [ -e ${service_config_dir}/tarbitratord ]; then + ${csudo}update-rc.d -f tarbitratord remove || : + fi + fi + + ${csudo}rm -f ${service_config_dir}/tarbitratord || : + + if $(which init &>/dev/null); then + ${csudo}init q || : + fi +} + +function install_service_on_sysvinit() { + clean_service_on_sysvinit + sleep 1 + + if ((${os_type} == 1)); then + ${csudo}cp -f ${script_dir}/init.d/tarbitratord.deb ${install_main_dir}/init.d/tarbitratord + ${csudo}cp ${script_dir}/init.d/tarbitratord.deb ${service_config_dir}/tarbitratord && ${csudo}chmod a+x ${service_config_dir}/tarbitratord + elif ((${os_type} == 2)); then + ${csudo}cp -f ${script_dir}/init.d/tarbitratord.rpm ${install_main_dir}/init.d/tarbitratord + ${csudo}cp ${script_dir}/init.d/tarbitratord.rpm ${service_config_dir}/tarbitratord && ${csudo}chmod a+x ${service_config_dir}/tarbitratord + fi + + if ((${initd_mod} == 1)); then + ${csudo}chkconfig --add tarbitratord || : + ${csudo}chkconfig --level 2345 tarbitratord on || : + elif ((${initd_mod} == 2)); then + ${csudo}insserv tarbitratord || : + ${csudo}insserv -d tarbitratord || : + elif ((${initd_mod} == 3)); then + ${csudo}update-rc.d tarbitratord defaults || : + fi +} + +function clean_service_on_systemd() { + tarbitratord_service_config="${service_config_dir}/tarbitratord.service" + if systemctl is-active --quiet tarbitratord; then + echo "tarbitrator is running, stopping it..." + ${csudo}systemctl stop tarbitratord &>/dev/null || echo &>/dev/null + fi + ${csudo}systemctl disable tarbitratord &>/dev/null || echo &>/dev/null + + ${csudo}rm -f ${tarbitratord_service_config} +} + +function install_service_on_systemd() { + clean_service_on_systemd + + tarbitratord_service_config="${service_config_dir}/tarbitratord.service" + + ${csudo}bash -c "echo '[Unit]' >> ${tarbitratord_service_config}" + ${csudo}bash -c "echo 'Description=TDengine arbitrator service' >> ${tarbitratord_service_config}" + ${csudo}bash -c "echo 'After=network-online.target' >> ${tarbitratord_service_config}" + ${csudo}bash -c "echo 'Wants=network-online.target' >> ${tarbitratord_service_config}" + ${csudo}bash -c "echo >> ${tarbitratord_service_config}" + ${csudo}bash -c "echo '[Service]' >> ${tarbitratord_service_config}" + ${csudo}bash -c "echo 'Type=simple' >> ${tarbitratord_service_config}" + ${csudo}bash -c "echo 'ExecStart=/usr/bin/tarbitrator' >> ${tarbitratord_service_config}" + ${csudo}bash -c "echo 'TimeoutStopSec=1000000s' >> ${tarbitratord_service_config}" + ${csudo}bash -c "echo 'LimitNOFILE=infinity' >> ${tarbitratord_service_config}" + ${csudo}bash -c "echo 'LimitNPROC=infinity' >> ${tarbitratord_service_config}" + ${csudo}bash -c "echo 'LimitCORE=infinity' >> ${tarbitratord_service_config}" + ${csudo}bash -c "echo 'TimeoutStartSec=0' >> ${tarbitratord_service_config}" + ${csudo}bash -c "echo 'StandardOutput=null' >> ${tarbitratord_service_config}" + ${csudo}bash -c "echo 'Restart=always' >> ${tarbitratord_service_config}" + ${csudo}bash -c "echo 'StartLimitBurst=3' >> ${tarbitratord_service_config}" + ${csudo}bash -c "echo 'StartLimitInterval=60s' >> ${tarbitratord_service_config}" + ${csudo}bash -c "echo >> ${tarbitratord_service_config}" + ${csudo}bash -c "echo '[Install]' >> ${tarbitratord_service_config}" + ${csudo}bash -c "echo 'WantedBy=multi-user.target' >> ${tarbitratord_service_config}" + ${csudo}systemctl enable tarbitratord +} + +function install_service() { + if ((${service_mod} == 0)); then + install_service_on_systemd + elif ((${service_mod} == 1)); then + install_service_on_sysvinit + else + kill_tarbitrator + fi +} + +function update_TDengine() { + # Start to update + echo -e "${GREEN}Start to update TDengine's arbitrator ...${NC}" + # Stop the service if running + if pidof tarbitrator &>/dev/null; then + if ((${service_mod} == 0)); then + ${csudo}systemctl stop tarbitratord || : + elif ((${service_mod} == 1)); then + ${csudo}service tarbitratord stop || : + else + kill_tarbitrator + fi + sleep 1 + fi + + install_main_path + #install_header + install_bin + install_service + install_jemalloc + + echo + if ((${service_mod} == 0)); then + echo -e "${GREEN_DARK}To start arbitrator ${NC}: ${csudo}systemctl start tarbitratord${NC}" + elif ((${service_mod} == 1)); then + echo -e "${GREEN_DARK}To start arbitrator ${NC}: ${csudo}service tarbitratord start${NC}" + else + echo -e "${GREEN_DARK}To start arbitrator ${NC}: ./tarbitrator${NC}" + fi + echo + echo -e "\033[44;32;1mTDengine's arbitrator is updated successfully!${NC}" +} + +function install_TDengine() { + # Start to install + echo -e "${GREEN}Start to install TDengine's arbitrator ...${NC}" + + install_main_path + #install_header + install_bin + install_service + install_jemalloc + + echo + if ((${service_mod} == 0)); then + echo -e "${GREEN_DARK}To start arbitrator ${NC}: ${csudo}systemctl start tarbitratord${NC}" + elif ((${service_mod} == 1)); then + echo -e "${GREEN_DARK}To start arbitrator ${NC}: ${csudo}service tarbitratord start${NC}" + else + echo -e "${GREEN_DARK}To start arbitrator ${NC}: tarbitrator${NC}" + fi + + echo -e "\033[44;32;1mTDengine's arbitrator is installed successfully!${NC}" + echo +} + +## ==============================Main program starts from here============================ +# Install server and client +if [ -x ${bin_dir}/tarbitrator ]; then + update_flag=1 + update_TDengine +else + install_TDengine +fi diff --git a/packaging/tools/install_client.sh b/packaging/tools/install_client.sh old mode 100644 new mode 100755 index 5508f2b5d0d07ae1e967cae162bf351a43c5578f..4cf95454e022da6f8d3e497d335175d86da486c5 --- a/packaging/tools/install_client.sh +++ b/packaging/tools/install_client.sh @@ -1,320 +1,305 @@ -#!/bin/bash -# -# This file is used to install TDengine client on linux systems. The operating system -# is required to use systemd to manage services at boot - -set -e -#set -x - -# -----------------------Variables definition--------------------- - -dataDir="/var/lib/taos" -logDir="/var/log/taos" -productName="TDengine" -installDir="/usr/local/taos" -configDir="/etc/taos" -serverName="taosd" -clientName="taos" -uninstallScript="rmtaos" -configFile="taos.cfg" -tarName="taos.tar.gz" - -osType=Linux -pagMode=full -verMode=edge - -if [ "$osType" != "Darwin" ]; then - script_dir=$(dirname $(readlink -f "$0")) - # Dynamic directory - data_dir=${dataDir} - log_dir=${logDir} -else - script_dir=`dirname $0` - cd ${script_dir} - script_dir="$(pwd)" - data_dir=${dataDir} - log_dir=~/${productName}/log -fi - -log_link_dir="${installDir}/log" - -cfg_install_dir=${configDir} - -if [ "$osType" != "Darwin" ]; then - bin_link_dir="/usr/bin" - lib_link_dir="/usr/lib" - lib64_link_dir="/usr/lib64" - inc_link_dir="/usr/include" -else - bin_link_dir="/usr/local/bin" - lib_link_dir="/usr/local/lib" - inc_link_dir="/usr/local/include" -fi - -#install main path -install_main_dir="${installDir}" - -# old bin dir -bin_dir="${installDir}/bin" - -# Color setting -RED='\033[0;31m' -GREEN='\033[1;32m' -GREEN_DARK='\033[0;32m' -GREEN_UNDERLINE='\033[4;32m' -NC='\033[0m' - -csudo="" -if command -v sudo > /dev/null; then - csudo="sudo " -fi - -update_flag=0 - -function kill_client() { - pid=$(ps -ef | grep "${clientName}" | grep -v "grep" | awk '{print $2}') - if [ -n "$pid" ]; then - ${csudo}kill -9 $pid || : - fi -} - -function install_main_path() { - #create install main dir and all sub dir - ${csudo}rm -rf ${install_main_dir} || : - ${csudo}mkdir -p ${install_main_dir} - ${csudo}mkdir -p ${install_main_dir}/cfg - ${csudo}mkdir -p ${install_main_dir}/bin - ${csudo}mkdir -p ${install_main_dir}/driver - if [ $productName == "TDengine" ]; then - ${csudo}mkdir -p ${install_main_dir}/examples - fi - ${csudo}mkdir -p ${install_main_dir}/include - if [ "$verMode" == "cluster" ]; then - ${csudo}mkdir -p ${install_main_dir}/connector - fi -} - -function install_bin() { - # Remove links - ${csudo}rm -f ${bin_link_dir}/${clientName} || : - if [ "$osType" != "Darwin" ]; then - ${csudo}rm -f ${bin_link_dir}/taosdemo || : - fi - ${csudo}rm -f ${bin_link_dir}/${uninstallScript} || : - ${csudo}rm -f ${bin_link_dir}/set_core || : - - ${csudo}cp -r ${script_dir}/bin/* ${install_main_dir}/bin && ${csudo}chmod 0555 ${install_main_dir}/bin/* - - #Make link - [ -x ${install_main_dir}/bin/${clientName} ] && ${csudo}ln -s ${install_main_dir}/bin/${clientName} ${bin_link_dir}/${clientName} || : - if [ "$osType" != "Darwin" ]; then - [ -x ${install_main_dir}/bin/taosdemo ] && ${csudo}ln -s ${install_main_dir}/bin/taosdemo ${bin_link_dir}/taosdemo || : - fi - [ -x ${install_main_dir}/bin/remove_client.sh ] && ${csudo}ln -s ${install_main_dir}/bin/remove_client.sh ${bin_link_dir}/${uninstallScript} || : - [ -x ${install_main_dir}/bin/set_core.sh ] && ${csudo}ln -s ${install_main_dir}/bin/set_core.sh ${bin_link_dir}/set_core || : -} - -function clean_lib() { - sudo rm -f /usr/lib/libtaos.* || : - sudo rm -rf ${lib_dir} || : -} - -function install_lib() { - # Remove links - ${csudo}rm -f ${lib_link_dir}/libtaos.* || : - ${csudo}rm -f ${lib64_link_dir}/libtaos.* || : - #${csudo}rm -rf ${v15_java_app_dir} || : - - ${csudo}cp -rf ${script_dir}/driver/* ${install_main_dir}/driver && ${csudo}chmod 777 ${install_main_dir}/driver/* - - if [ "$osType" != "Darwin" ]; then - ${csudo}ln -s ${install_main_dir}/driver/libtaos.* ${lib_link_dir}/libtaos.so.1 - ${csudo}ln -s ${lib_link_dir}/libtaos.so.1 ${lib_link_dir}/libtaos.so - - if [ -d "${lib64_link_dir}" ]; then - ${csudo}ln -s ${install_main_dir}/driver/libtaos.* ${lib64_link_dir}/libtaos.so.1 || : - ${csudo}ln -s ${lib64_link_dir}/libtaos.so.1 ${lib64_link_dir}/libtaos.so || : - fi - else - ${csudo}ln -s ${install_main_dir}/driver/libtaos.* ${lib_link_dir}/libtaos.1.dylib - ${csudo}ln -s ${lib_link_dir}/libtaos.1.dylib ${lib_link_dir}/libtaos.dylib - fi - - if [ "$osType" != "Darwin" ]; then - ${csudo}ldconfig - else - ${csudo}update_dyld_shared_cache - fi -} - -function install_header() { - ${csudo}rm -f ${inc_link_dir}/taos.h ${inc_link_dir}/taosdef.h ${inc_link_dir}/taoserror.h || : - ${csudo}cp -f ${script_dir}/inc/* ${install_main_dir}/include && ${csudo}chmod 644 ${install_main_dir}/include/* - ${csudo}ln -s ${install_main_dir}/include/taos.h ${inc_link_dir}/taos.h - ${csudo}ln -s ${install_main_dir}/include/taosdef.h ${inc_link_dir}/taosdef.h - ${csudo}ln -s ${install_main_dir}/include/taoserror.h ${inc_link_dir}/taoserror.h -} - -function install_jemalloc() { - jemalloc_dir=${script_dir}/jemalloc - - if [ -d ${jemalloc_dir} ]; then - ${csudo}/usr/bin/install -c -d /usr/local/bin - - if [ -f ${jemalloc_dir}/bin/jemalloc-config ]; then - ${csudo}/usr/bin/install -c -m 755 ${jemalloc_dir}/bin/jemalloc-config /usr/local/bin - fi - if [ -f ${jemalloc_dir}/bin/jemalloc.sh ]; then - ${csudo}/usr/bin/install -c -m 755 ${jemalloc_dir}/bin/jemalloc.sh /usr/local/bin - fi - if [ -f ${jemalloc_dir}/bin/jeprof ]; then - ${csudo}/usr/bin/install -c -m 755 ${jemalloc_dir}/bin/jeprof /usr/local/bin - fi - if [ -f ${jemalloc_dir}/include/jemalloc/jemalloc.h ]; then - ${csudo}/usr/bin/install -c -d /usr/local/include/jemalloc - ${csudo}/usr/bin/install -c -m 644 ${jemalloc_dir}/include/jemalloc/jemalloc.h /usr/local/include/jemalloc - fi - if [ -f ${jemalloc_dir}/lib/libjemalloc.so.2 ]; then - ${csudo}/usr/bin/install -c -d /usr/local/lib - ${csudo}/usr/bin/install -c -m 755 ${jemalloc_dir}/lib/libjemalloc.so.2 /usr/local/lib - ${csudo}ln -sf libjemalloc.so.2 /usr/local/lib/libjemalloc.so - ${csudo}/usr/bin/install -c -d /usr/local/lib - if [ -f ${jemalloc_dir}/lib/libjemalloc.a ]; then - ${csudo}/usr/bin/install -c -m 755 ${jemalloc_dir}/lib/libjemalloc.a /usr/local/lib - fi - if [ -f ${jemalloc_dir}/lib/libjemalloc_pic.a ]; then - ${csudo}/usr/bin/install -c -m 755 ${jemalloc_dir}/lib/libjemalloc_pic.a /usr/local/lib - fi - if [ -f ${jemalloc_dir}/lib/libjemalloc_pic.a ]; then - ${csudo}/usr/bin/install -c -d /usr/local/lib/pkgconfig - ${csudo}/usr/bin/install -c -m 644 ${jemalloc_dir}/lib/pkgconfig/jemalloc.pc /usr/local/lib/pkgconfig - fi - fi - if [ -f ${jemalloc_dir}/share/doc/jemalloc/jemalloc.html ]; then - ${csudo}/usr/bin/install -c -d /usr/local/share/doc/jemalloc - ${csudo}/usr/bin/install -c -m 644 ${jemalloc_dir}/share/doc/jemalloc/jemalloc.html /usr/local/share/doc/jemalloc - fi - if [ -f ${jemalloc_dir}/share/man/man3/jemalloc.3 ]; then - ${csudo}/usr/bin/install -c -d /usr/local/share/man/man3 - ${csudo}/usr/bin/install -c -m 644 ${jemalloc_dir}/share/man/man3/jemalloc.3 /usr/local/share/man/man3 - fi - - if [ -d /etc/ld.so.conf.d ]; then - echo "/usr/local/lib" | ${csudo}tee /etc/ld.so.conf.d/jemalloc.conf > /dev/null || echo -e "failed to write /etc/ld.so.conf.d/jemalloc.conf" - ${csudo}ldconfig - else - echo "/etc/ld.so.conf.d not found!" - fi - fi -} - -function install_config() { - if [ ! -f ${cfg_install_dir}/${configFile} ]; then - ${csudo}mkdir -p ${cfg_install_dir} - [ -f ${script_dir}/cfg/${configFile} ] && ${csudo}cp ${script_dir}/cfg/${configFile} ${cfg_install_dir} - ${csudo}chmod 644 ${cfg_install_dir}/* - fi - - ${csudo}cp -f ${script_dir}/cfg/${configFile} ${install_main_dir}/cfg/${configFile}.org - ${csudo}ln -s ${cfg_install_dir}/${configFile} ${install_main_dir}/cfg -} - - -function install_log() { - ${csudo}rm -rf ${log_dir} || : - - if [ "$osType" != "Darwin" ]; then - ${csudo}mkdir -p ${log_dir} && ${csudo}chmod 777 ${log_dir} - else - mkdir -p ${log_dir} && ${csudo}chmod 777 ${log_dir} - fi - ${csudo}ln -s ${log_dir} ${install_main_dir}/log -} - -function install_connector() { - ${csudo}cp -rf ${script_dir}/connector/ ${install_main_dir}/ -} - -function install_examples() { - if [ -d ${script_dir}/examples ]; then - ${csudo}cp -rf ${script_dir}/examples/* ${install_main_dir}/examples - fi -} - -function update_TDengine() { - # Start to update - if [ ! -e ${tarName} ]; then - echo "File ${tarName} does not exist" - exit 1 - fi - tar -zxf ${tarName} - - echo -e "${GREEN}Start to update ${productName} client...${NC}" - # Stop the client shell if running - if pidof ${clientName} &> /dev/null; then - kill_client - sleep 1 - fi - - install_main_path - - install_log - install_header - install_lib - install_jemalloc - if [ "$verMode" == "cluster" ]; then - install_connector - fi - install_examples - install_bin - install_config - - echo - echo -e "\033[44;32;1m${productName} client is updated successfully!${NC}" - - rm -rf $(tar -tf ${tarName}) -} - -function install_TDengine() { - # Start to install - if [ ! -e ${tarName} ]; then - echo "File ${tarName} does not exist" - exit 1 - fi - tar -zxf ${tarName} - - echo -e "${GREEN}Start to install ${productName} client...${NC}" - - install_main_path - install_log - install_header - install_lib - install_jemalloc - if [ "$verMode" == "cluster" ]; then - install_connector - fi - install_examples - install_bin - install_config - - echo - echo -e "\033[44;32;1m${productName} client is installed successfully!${NC}" - - rm -rf $(tar -tf ${tarName}) -} - - -## ==============================Main program starts from here============================ -# Install or updata client and client -# if server is already install, don't install client - if [ -e ${bin_dir}/${serverName} ]; then - echo -e "\033[44;32;1mThere are already installed ${productName} server, so don't need install client!${NC}" - exit 0 - fi - - if [ -x ${bin_dir}/${clientName} ]; then - update_flag=1 - update_TDengine - else - install_TDengine - fi +#!/bin/bash +# +# This file is used to install TDengine client on linux systems. The operating system +# is required to use systemd to manage services at boot + +set -e +#set -x + +# -----------------------Variables definition--------------------- + +dataDir="/var/lib/taos" +logDir="/var/log/taos" +productName="TDengine" +installDir="/usr/local/taos" +configDir="/etc/taos" +serverName="taosd" +clientName="taos" +uninstallScript="rmtaos" +configFile="taos.cfg" + +osType=Linux +pagMode=full +verMode=edge + +if [ "$osType" != "Darwin" ]; then + script_dir=$(dirname $(readlink -f "$0")) + # Dynamic directory + data_dir=${dataDir} + log_dir=${logDir} +else + script_dir=`dirname $0` + cd ${script_dir} + script_dir="$(pwd)" + data_dir=${dataDir} + log_dir=~/${productName}/log +fi + +log_link_dir="${installDir}/log" + +cfg_install_dir=${configDir} + +if [ "$osType" != "Darwin" ]; then + bin_link_dir="/usr/bin" + lib_link_dir="/usr/lib" + lib64_link_dir="/usr/lib64" + inc_link_dir="/usr/include" +else + bin_link_dir="/usr/local/bin" + lib_link_dir="/usr/local/lib" + inc_link_dir="/usr/local/include" +fi + +#install main path +install_main_dir="${installDir}" + +# old bin dir +bin_dir="${installDir}/bin" + +# Color setting +RED='\033[0;31m' +GREEN='\033[1;32m' +GREEN_DARK='\033[0;32m' +GREEN_UNDERLINE='\033[4;32m' +NC='\033[0m' + +csudo="" +if command -v sudo > /dev/null; then + csudo="sudo " +fi + +update_flag=0 + +function kill_client() { + pid=$(ps -ef | grep "${clientName}" | grep -v "grep" | awk '{print $2}') + if [ -n "$pid" ]; then + ${csudo}kill -9 $pid || : + fi +} + +function install_main_path() { + #create install main dir and all sub dir + ${csudo}rm -rf ${install_main_dir} || : + ${csudo}mkdir -p ${install_main_dir} + ${csudo}mkdir -p ${install_main_dir}/cfg + ${csudo}mkdir -p ${install_main_dir}/bin + ${csudo}mkdir -p ${install_main_dir}/driver + if [ $productName == "TDengine" ]; then + ${csudo}mkdir -p ${install_main_dir}/examples + fi + ${csudo}mkdir -p ${install_main_dir}/include + if [ "$verMode" == "cluster" ]; then + ${csudo}mkdir -p ${install_main_dir}/connector + fi +} + +function install_bin() { + # Remove links + ${csudo}rm -f ${bin_link_dir}/${clientName} || : + if [ "$osType" != "Darwin" ]; then + ${csudo}rm -f ${bin_link_dir}/taosdemo || : + fi + ${csudo}rm -f ${bin_link_dir}/${uninstallScript} || : + ${csudo}rm -f ${bin_link_dir}/set_core || : + + ${csudo}cp -r ${script_dir}/bin/* ${install_main_dir}/bin && ${csudo}chmod 0555 ${install_main_dir}/bin/* + + #Make link + [ -x ${install_main_dir}/bin/${clientName} ] && ${csudo}ln -s ${install_main_dir}/bin/${clientName} ${bin_link_dir}/${clientName} || : + if [ "$osType" != "Darwin" ]; then + [ -x ${install_main_dir}/bin/taosdemo ] && ${csudo}ln -s ${install_main_dir}/bin/taosdemo ${bin_link_dir}/taosdemo || : + fi + [ -x ${install_main_dir}/bin/remove_client.sh ] && ${csudo}ln -s ${install_main_dir}/bin/remove_client.sh ${bin_link_dir}/${uninstallScript} || : + [ -x ${install_main_dir}/bin/set_core.sh ] && ${csudo}ln -s ${install_main_dir}/bin/set_core.sh ${bin_link_dir}/set_core || : +} + +function clean_lib() { + sudo rm -f /usr/lib/libtaos.* || : + sudo rm -rf ${lib_dir} || : +} + +function install_lib() { + # Remove links + ${csudo}rm -f ${lib_link_dir}/libtaos.* || : + ${csudo}rm -f ${lib64_link_dir}/libtaos.* || : + #${csudo}rm -rf ${v15_java_app_dir} || : + + ${csudo}cp -rf ${script_dir}/driver/* ${install_main_dir}/driver && ${csudo}chmod 777 ${install_main_dir}/driver/* + + if [ "$osType" != "Darwin" ]; then + ${csudo}ln -s ${install_main_dir}/driver/libtaos.* ${lib_link_dir}/libtaos.so.1 + ${csudo}ln -s ${lib_link_dir}/libtaos.so.1 ${lib_link_dir}/libtaos.so + + if [ -d "${lib64_link_dir}" ]; then + ${csudo}ln -s ${install_main_dir}/driver/libtaos.* ${lib64_link_dir}/libtaos.so.1 || : + ${csudo}ln -s ${lib64_link_dir}/libtaos.so.1 ${lib64_link_dir}/libtaos.so || : + fi + else + ${csudo}ln -s ${install_main_dir}/driver/libtaos.* ${lib_link_dir}/libtaos.1.dylib + ${csudo}ln -s ${lib_link_dir}/libtaos.1.dylib ${lib_link_dir}/libtaos.dylib + fi + + if [ "$osType" != "Darwin" ]; then + ${csudo}ldconfig + else + ${csudo}update_dyld_shared_cache + fi +} + +function install_header() { + ${csudo}rm -f ${inc_link_dir}/taos.h ${inc_link_dir}/taosdef.h ${inc_link_dir}/taoserror.h || : + ${csudo}cp -f ${script_dir}/inc/* ${install_main_dir}/include && ${csudo}chmod 644 ${install_main_dir}/include/* + ${csudo}ln -s ${install_main_dir}/include/taos.h ${inc_link_dir}/taos.h + ${csudo}ln -s ${install_main_dir}/include/taosdef.h ${inc_link_dir}/taosdef.h + ${csudo}ln -s ${install_main_dir}/include/taoserror.h ${inc_link_dir}/taoserror.h +} + +function install_jemalloc() { + jemalloc_dir=${script_dir}/jemalloc + + if [ -d ${jemalloc_dir} ]; then + ${csudo}/usr/bin/install -c -d /usr/local/bin + + if [ -f ${jemalloc_dir}/bin/jemalloc-config ]; then + ${csudo}/usr/bin/install -c -m 755 ${jemalloc_dir}/bin/jemalloc-config /usr/local/bin + fi + if [ -f ${jemalloc_dir}/bin/jemalloc.sh ]; then + ${csudo}/usr/bin/install -c -m 755 ${jemalloc_dir}/bin/jemalloc.sh /usr/local/bin + fi + if [ -f ${jemalloc_dir}/bin/jeprof ]; then + ${csudo}/usr/bin/install -c -m 755 ${jemalloc_dir}/bin/jeprof /usr/local/bin + fi + if [ -f ${jemalloc_dir}/include/jemalloc/jemalloc.h ]; then + ${csudo}/usr/bin/install -c -d /usr/local/include/jemalloc + ${csudo}/usr/bin/install -c -m 644 ${jemalloc_dir}/include/jemalloc/jemalloc.h /usr/local/include/jemalloc + fi + if [ -f ${jemalloc_dir}/lib/libjemalloc.so.2 ]; then + ${csudo}/usr/bin/install -c -d /usr/local/lib + ${csudo}/usr/bin/install -c -m 755 ${jemalloc_dir}/lib/libjemalloc.so.2 /usr/local/lib + ${csudo}ln -sf libjemalloc.so.2 /usr/local/lib/libjemalloc.so + ${csudo}/usr/bin/install -c -d /usr/local/lib + if [ -f ${jemalloc_dir}/lib/libjemalloc.a ]; then + ${csudo}/usr/bin/install -c -m 755 ${jemalloc_dir}/lib/libjemalloc.a /usr/local/lib + fi + if [ -f ${jemalloc_dir}/lib/libjemalloc_pic.a ]; then + ${csudo}/usr/bin/install -c -m 755 ${jemalloc_dir}/lib/libjemalloc_pic.a /usr/local/lib + fi + if [ -f ${jemalloc_dir}/lib/libjemalloc_pic.a ]; then + ${csudo}/usr/bin/install -c -d /usr/local/lib/pkgconfig + ${csudo}/usr/bin/install -c -m 644 ${jemalloc_dir}/lib/pkgconfig/jemalloc.pc /usr/local/lib/pkgconfig + fi + fi + if [ -f ${jemalloc_dir}/share/doc/jemalloc/jemalloc.html ]; then + ${csudo}/usr/bin/install -c -d /usr/local/share/doc/jemalloc + ${csudo}/usr/bin/install -c -m 644 ${jemalloc_dir}/share/doc/jemalloc/jemalloc.html /usr/local/share/doc/jemalloc + fi + if [ -f ${jemalloc_dir}/share/man/man3/jemalloc.3 ]; then + ${csudo}/usr/bin/install -c -d /usr/local/share/man/man3 + ${csudo}/usr/bin/install -c -m 644 ${jemalloc_dir}/share/man/man3/jemalloc.3 /usr/local/share/man/man3 + fi + + if [ -d /etc/ld.so.conf.d ]; then + echo "/usr/local/lib" | ${csudo}tee /etc/ld.so.conf.d/jemalloc.conf > /dev/null || echo -e "failed to write /etc/ld.so.conf.d/jemalloc.conf" + ${csudo}ldconfig + else + echo "/etc/ld.so.conf.d not found!" + fi + fi +} + +function install_config() { + if [ ! -f ${cfg_install_dir}/${configFile} ]; then + ${csudo}mkdir -p ${cfg_install_dir} + [ -f ${script_dir}/cfg/${configFile} ] && ${csudo}cp ${script_dir}/cfg/${configFile} ${cfg_install_dir} + ${csudo}chmod 644 ${cfg_install_dir}/* + fi + + ${csudo}cp -f ${script_dir}/cfg/${configFile} ${install_main_dir}/cfg/${configFile}.org + ${csudo}ln -s ${cfg_install_dir}/${configFile} ${install_main_dir}/cfg +} + + +function install_log() { + ${csudo}rm -rf ${log_dir} || : + + if [ "$osType" != "Darwin" ]; then + ${csudo}mkdir -p ${log_dir} && ${csudo}chmod 777 ${log_dir} + else + mkdir -p ${log_dir} && ${csudo}chmod 777 ${log_dir} + fi + ${csudo}ln -s ${log_dir} ${install_main_dir}/log +} + +function install_connector() { + ${csudo}cp -rf ${script_dir}/connector/ ${install_main_dir}/ +} + +function install_examples() { + if [ -d ${script_dir}/examples ]; then + ${csudo}cp -rf ${script_dir}/examples/* ${install_main_dir}/examples + fi +} + +function update_TDengine() { + # Start to update + echo -e "${GREEN}Start to update ${productName} client...${NC}" + # Stop the client shell if running + if pidof ${clientName} &> /dev/null; then + kill_client + sleep 1 + fi + + install_main_path + + install_log + install_header + install_lib + install_jemalloc + if [ "$verMode" == "cluster" ]; then + install_connector + fi + install_examples + install_bin + install_config + + echo + echo -e "\033[44;32;1m${productName} client is updated successfully!${NC}" +} + +function install_TDengine() { + # Start to install + echo -e "${GREEN}Start to install ${productName} client...${NC}" + + install_main_path + install_log + install_header + install_lib + install_jemalloc + if [ "$verMode" == "cluster" ]; then + install_connector + fi + install_examples + install_bin + install_config + + echo + echo -e "\033[44;32;1m${productName} client is installed successfully!${NC}" + + rm -rf $(tar -tf ${tarName}) +} + + +## ==============================Main program starts from here============================ +# Install or updata client and client +# if server is already install, don't install client + if [ -e ${bin_dir}/${serverName} ]; then + echo -e "\033[44;32;1mThere are already installed ${productName} server, so don't need install client!${NC}" + exit 0 + fi + + if [ -x ${bin_dir}/${clientName} ]; then + update_flag=1 + update_TDengine + else + install_TDengine + fi diff --git a/packaging/tools/makearbi.sh b/packaging/tools/makearbi.sh old mode 100644 new mode 100755 index f0cc54a355d792c15c977d0276e3db891e9701f0..65a6dae9a4d558da06a3f49bb9e6aa478c762916 --- a/packaging/tools/makearbi.sh +++ b/packaging/tools/makearbi.sh @@ -1,71 +1,71 @@ -#!/bin/bash -# -# Generate arbitrator's tar.gz setup package for all os system - -set -e -#set -x - -curr_dir=$(pwd) -compile_dir=$1 -version=$2 -build_time=$3 -cpuType=$4 -osType=$5 -verMode=$6 -verType=$7 -pagMode=$8 - -script_dir="$(dirname $(readlink -f $0))" -top_dir="$(readlink -f ${script_dir}/../..)" - -productName="TDengine" - -# create compressed install file. -build_dir="${compile_dir}/build" -code_dir="${top_dir}/src" -release_dir="${top_dir}/release" - -#package_name='linux' -if [ "$verMode" == "cluster" ]; then - install_dir="${release_dir}/${productName}-enterprise-arbitrator-${version}" -else - install_dir="${release_dir}/${productName}-arbitrator-${version}" -fi - -# Directories and files. -bin_files="${build_dir}/bin/tarbitrator ${script_dir}/remove_arbi.sh" -install_files="${script_dir}/install_arbi.sh" - -#header_files="${code_dir}/inc/taos.h ${code_dir}/inc/taosdef.h ${code_dir}/inc/taoserror.h" -init_file_tarbitrator_deb=${script_dir}/../deb/tarbitratord -init_file_tarbitrator_rpm=${script_dir}/../rpm/tarbitratord - -# make directories. -mkdir -p ${install_dir} && cp ${install_files} ${install_dir} && chmod a+x ${install_dir}/install_arbi.sh || : -#mkdir -p ${install_dir}/inc && cp ${header_files} ${install_dir}/inc || : -mkdir -p ${install_dir}/bin && cp ${bin_files} ${install_dir}/bin && chmod a+x ${install_dir}/bin/* || : -mkdir -p ${install_dir}/init.d && cp ${init_file_tarbitrator_deb} ${install_dir}/init.d/tarbitratord.deb || : -mkdir -p ${install_dir}/init.d && cp ${init_file_tarbitrator_rpm} ${install_dir}/init.d/tarbitratord.rpm || : - -cd ${release_dir} - -# install_dir has been distinguishes cluster from edege, so comments this code -pkg_name=${install_dir}-${osType}-${cpuType} - -if [[ "$verType" == "beta" ]] || [[ "$verType" == "preRelease" ]]; then - pkg_name=${install_dir}-${verType}-${osType}-${cpuType} -elif [ "$verType" == "stable" ]; then - pkg_name=${pkg_name} -else - echo "unknow verType, nor stabel or beta" - exit 1 -fi - -tar -zcv -f "$(basename ${pkg_name}).tar.gz" $(basename ${install_dir}) --remove-files || : -exitcode=$? -if [ "$exitcode" != "0" ]; then - echo "tar ${pkg_name}.tar.gz error !!!" - exit $exitcode -fi - -cd ${curr_dir} +#!/bin/bash +# +# Generate arbitrator's tar.gz setup package for all os system + +set -e +#set -x + +curr_dir=$(pwd) +compile_dir=$1 +version=$2 +build_time=$3 +cpuType=$4 +osType=$5 +verMode=$6 +verType=$7 +pagMode=$8 + +script_dir="$(dirname $(readlink -f $0))" +top_dir="$(readlink -f ${script_dir}/../..)" + +productName="TDengine" + +# create compressed install file. +build_dir="${compile_dir}/build" +code_dir="${top_dir}" +release_dir="${top_dir}/release" + +#package_name='linux' +if [ "$verMode" == "cluster" ]; then + install_dir="${release_dir}/${productName}-enterprise-arbitrator-${version}" +else + install_dir="${release_dir}/${productName}-arbitrator-${version}" +fi + +# Directories and files. +bin_files="${build_dir}/bin/tarbitrator ${script_dir}/remove_arbi.sh" +install_files="${script_dir}/install_arbi.sh" + +#header_files="${code_dir}/include/client/taos.h ${code_dir}/include/common/taosdef.h ${code_dir}/include/util/taoserror.h" +init_file_tarbitrator_deb=${script_dir}/../deb/tarbitratord +init_file_tarbitrator_rpm=${script_dir}/../rpm/tarbitratord + +# make directories. +mkdir -p ${install_dir} && cp ${install_files} ${install_dir} && chmod a+x ${install_dir}/install_arbi.sh || : +#mkdir -p ${install_dir}/inc && cp ${header_files} ${install_dir}/inc || : +mkdir -p ${install_dir}/bin && cp ${bin_files} ${install_dir}/bin && chmod a+x ${install_dir}/bin/* || : +mkdir -p ${install_dir}/init.d && cp ${init_file_tarbitrator_deb} ${install_dir}/init.d/tarbitratord.deb || : +mkdir -p ${install_dir}/init.d && cp ${init_file_tarbitrator_rpm} ${install_dir}/init.d/tarbitratord.rpm || : + +cd ${release_dir} + +# install_dir has been distinguishes cluster from edege, so comments this code +pkg_name=${install_dir}-${osType}-${cpuType} + +if [[ "$verType" == "beta" ]] || [[ "$verType" == "preRelease" ]]; then + pkg_name=${install_dir}-${verType}-${osType}-${cpuType} +elif [ "$verType" == "stable" ]; then + pkg_name=${pkg_name} +else + echo "unknow verType, nor stabel or beta" + exit 1 +fi + +tar -zcv -f "$(basename ${pkg_name}).tar.gz" $(basename ${install_dir}) --remove-files || : +exitcode=$? +if [ "$exitcode" != "0" ]; then + echo "tar ${pkg_name}.tar.gz error !!!" + exit $exitcode +fi + +cd ${curr_dir} diff --git a/packaging/tools/makeclient.sh b/packaging/tools/makeclient.sh old mode 100644 new mode 100755 index a86f62a32cb69207baaf2cb4daccd28bedbe2921..9e7f01300602ec01656c7862b2a918b5889a53b2 --- a/packaging/tools/makeclient.sh +++ b/packaging/tools/makeclient.sh @@ -1,246 +1,246 @@ -#!/bin/bash -# -# Generate tar.gz package for linux client in all os system -set -e -#set -x - -curr_dir=$(pwd) -compile_dir=$1 -version=$2 -build_time=$3 -cpuType=$4 -osType=$5 -verMode=$6 -verType=$7 -pagMode=$8 -dbName=$9 - -productName="TDengine" -clientName="taos" -configFile="taos.cfg" -tarName="taos.tar.gz" - -if [ "$osType" != "Darwin" ]; then - script_dir="$(dirname $(readlink -f $0))" - top_dir="$(readlink -f ${script_dir}/../..)" -else - script_dir=$(dirname $0) - cd ${script_dir} - script_dir="$(pwd)" - top_dir=${script_dir}/../.. -fi - -# create compressed install file. -build_dir="${compile_dir}/build" -code_dir="${top_dir}/src" -release_dir="${top_dir}/release" - -#package_name='linux' - -if [ "$verMode" == "cluster" ]; then - install_dir="${release_dir}/${productName}-enterprise-client-${version}" -else - install_dir="${release_dir}/${productName}-client-${version}" -fi - -# Directories and files. - -if [ "$osType" != "Darwin" ]; then - if [ "$pagMode" == "lite" ]; then - strip ${build_dir}/bin/${clientName} - bin_files="${build_dir}/bin/${clientName} \ - ${script_dir}/remove_client.sh" - else - bin_files="${build_dir}/bin/${clientName} \ - ${script_dir}/remove_client.sh \ - ${script_dir}/set_core.sh \ - ${script_dir}/get_client.sh" - fi - lib_files="${build_dir}/lib/libtaos.so.${version}" -else - bin_files="${build_dir}/bin/${clientName} ${script_dir}/remove_client.sh" - lib_files="${build_dir}/lib/libtaos.${version}.dylib" -fi - -header_files="${code_dir}/inc/taos.h ${code_dir}/inc/taosdef.h ${code_dir}/inc/taoserror.h" -if [ "$dbName" != "taos" ]; then - cfg_dir="${top_dir}/../enterprise/packaging/cfg" -else - cfg_dir="${top_dir}/packaging/cfg" -fi - -install_files="${script_dir}/install_client.sh" - -# make directories. -mkdir -p ${install_dir} -mkdir -p ${install_dir}/inc && cp ${header_files} ${install_dir}/inc -mkdir -p ${install_dir}/cfg && cp ${cfg_dir}/${configFile} ${install_dir}/cfg/${configFile} -mkdir -p ${install_dir}/bin && cp ${bin_files} ${install_dir}/bin && chmod a+x ${install_dir}/bin/* - -if [ -f ${build_dir}/bin/jemalloc-config ]; then - mkdir -p ${install_dir}/jemalloc/{bin,lib,lib/pkgconfig,include/jemalloc,share/doc/jemalloc,share/man/man3} - cp ${build_dir}/bin/jemalloc-config ${install_dir}/jemalloc/bin - if [ -f ${build_dir}/bin/jemalloc.sh ]; then - cp ${build_dir}/bin/jemalloc.sh ${install_dir}/jemalloc/bin - fi - if [ -f ${build_dir}/bin/jeprof ]; then - cp ${build_dir}/bin/jeprof ${install_dir}/jemalloc/bin - fi - if [ -f ${build_dir}/include/jemalloc/jemalloc.h ]; then - cp ${build_dir}/include/jemalloc/jemalloc.h ${install_dir}/jemalloc/include/jemalloc - fi - if [ -f ${build_dir}/lib/libjemalloc.so.2 ]; then - cp ${build_dir}/lib/libjemalloc.so.2 ${install_dir}/jemalloc/lib - ln -sf libjemalloc.so.2 ${install_dir}/jemalloc/lib/libjemalloc.so - fi - if [ -f ${build_dir}/lib/libjemalloc.a ]; then - cp ${build_dir}/lib/libjemalloc.a ${install_dir}/jemalloc/lib - fi - if [ -f ${build_dir}/lib/libjemalloc_pic.a ]; then - cp ${build_dir}/lib/libjemalloc_pic.a ${install_dir}/jemalloc/lib - fi - if [ -f ${build_dir}/lib/pkgconfig/jemalloc.pc ]; then - cp ${build_dir}/lib/pkgconfig/jemalloc.pc ${install_dir}/jemalloc/lib/pkgconfig - fi - if [ -f ${build_dir}/share/doc/jemalloc/jemalloc.html ]; then - cp ${build_dir}/share/doc/jemalloc/jemalloc.html ${install_dir}/jemalloc/share/doc/jemalloc - fi - if [ -f ${build_dir}/share/man/man3/jemalloc.3 ]; then - cp ${build_dir}/share/man/man3/jemalloc.3 ${install_dir}/jemalloc/share/man/man3 - fi -fi - -cd ${install_dir} - -if [ "$osType" != "Darwin" ]; then - tar -zcv -f ${tarName} * --remove-files || : -else - tar -zcv -f ${tarName} * || : - mv ${tarName} .. - rm -rf ./* - mv ../${tarName} . -fi - -cd ${curr_dir} -cp ${install_files} ${install_dir} -if [ "$osType" == "Darwin" ]; then - sed 's/osType=Linux/osType=Darwin/g' ${install_dir}/install_client.sh >>install_client_temp.sh - mv install_client_temp.sh ${install_dir}/install_client.sh -fi - -if [ "$verMode" == "cluster" ]; then - sed 's/verMode=edge/verMode=cluster/g' ${install_dir}/install_client.sh >>install_client_temp.sh - mv install_client_temp.sh ${install_dir}/install_client.sh -fi - -if [ "$pagMode" == "lite" ]; then - sed 's/pagMode=full/pagMode=lite/g' ${install_dir}/install_client.sh >>install_client_temp.sh - mv install_client_temp.sh ${install_dir}/install_client.sh -fi -chmod a+x ${install_dir}/install_client.sh - -if [[ $productName == "TDengine" ]]; then - # Copy example code - mkdir -p ${install_dir}/examples - examples_dir="${top_dir}/examples" - cp -r ${examples_dir}/c ${install_dir}/examples - if [[ "$pagMode" != "lite" ]] && [[ "$cpuType" != "aarch32" ]]; then - cp -r ${examples_dir}/JDBC ${install_dir}/examples - cp -r ${examples_dir}/matlab ${install_dir}/examples - cp -r ${examples_dir}/python ${install_dir}/examples - cp -r ${examples_dir}/R ${install_dir}/examples - cp -r ${examples_dir}/go ${install_dir}/examples - cp -r ${examples_dir}/nodejs ${install_dir}/examples - cp -r ${examples_dir}/C# ${install_dir}/examples - mkdir -p ${install_dir}/examples/taosbenchmark-json && cp ${examples_dir}/../src/kit/taos-tools/example/* ${install_dir}/examples/taosbenchmark-json - fi - - if [ "$verMode" == "cluster" ]; then - # Copy connector - connector_dir="${code_dir}/connector" - mkdir -p ${install_dir}/connector - if [[ "$pagMode" != "lite" ]] && [[ "$cpuType" != "aarch32" ]]; then - if [ "$osType" != "Darwin" ]; then - cp ${build_dir}/lib/*.jar ${install_dir}/connector || : - fi - if find ${connector_dir}/go -mindepth 1 -maxdepth 1 | read; then - cp -r ${connector_dir}/go ${install_dir}/connector - else - echo "WARNING: go connector not found, please check if want to use it!" - fi - git clone --depth 1 https://github.com/taosdata/taos-connector-python ${install_dir}/connector/python - rm -rf ${install_dir}/connector/python/.git ||: -# cp -r ${connector_dir}/python ${install_dir}/connector - git clone --depth 1 https://github.com/taosdata/taos-connector-node ${install_dir}/connector/nodejs - rm -rf ${install_dir}/connector/nodejs/.git ||: - - git clone --depth 1 https://github.com/taosdata/taos-connector-dotnet ${install_dir}/connector/dotnet - rm -rf ${install_dir}/connector/dotnet/.git ||: -# cp -r ${connector_dir}/nodejs ${install_dir}/connector - git clone --depth 1 https://github.com/taosdata/libtaos-rs ${install_dir}/connector/rust - rm -rf ${install_dir}/connector/rust/.git ||: - fi - fi -fi -# Copy driver -mkdir -p ${install_dir}/driver -cp ${lib_files} ${install_dir}/driver - -# Copy connector -connector_dir="${code_dir}/connector" -mkdir -p ${install_dir}/connector - -if [[ "$pagMode" != "lite" ]] && [[ "$cpuType" != "aarch32" ]]; then - if [ "$osType" != "Darwin" ]; then - cp ${build_dir}/lib/*.jar ${install_dir}/connector || : - fi - if find ${connector_dir}/go -mindepth 1 -maxdepth 1 | read; then - cp -r ${connector_dir}/go ${install_dir}/connector - else - echo "WARNING: go connector not found, please check if want to use it!" - fi - cp -r ${connector_dir}/python ${install_dir}/connector - cp -r ${connector_dir}/nodejs ${install_dir}/connector -fi -# Copy release note -# cp ${script_dir}/release_note ${install_dir} - -# exit 1 - -cd ${release_dir} - -# install_dir has been distinguishes cluster from edege, so comments this code -pkg_name=${install_dir}-${osType}-${cpuType} - -# if [ "$verMode" == "cluster" ]; then -# pkg_name=${install_dir}-${osType}-${cpuType} -# elif [ "$verMode" == "edge" ]; then -# pkg_name=${install_dir}-${osType}-${cpuType} -# else -# echo "unknow verMode, nor cluster or edge" -# exit 1 -# fi - -if [[ "$verType" == "beta" ]] || [[ "$verType" == "preRelease" ]]; then - pkg_name=${install_dir}-${verType}-${osType}-${cpuType} -elif [ "$verType" == "stable" ]; then - pkg_name=${pkg_name} -else - echo "unknow verType, nor stabel or beta" - exit 1 -fi - -if [ "$pagMode" == "lite" ]; then - pkg_name=${pkg_name}-Lite -fi - -if [ "$osType" != "Darwin" ]; then - tar -zcv -f "$(basename ${pkg_name}).tar.gz" $(basename ${install_dir}) --remove-files || : -else - tar -zcv -f "$(basename ${pkg_name}).tar.gz" $(basename ${install_dir}) || : - mv "$(basename ${pkg_name}).tar.gz" .. - rm -rf ./* - mv ../"$(basename ${pkg_name}).tar.gz" . -fi - -cd ${curr_dir} +#!/bin/bash +# +# Generate tar.gz package for linux client in all os system +set -e +#set -x + +curr_dir=$(pwd) +compile_dir=$1 +version=$2 +build_time=$3 +cpuType=$4 +osType=$5 +verMode=$6 +verType=$7 +pagMode=$8 +dbName=$9 + +productName="TDengine" +clientName="taos" +configFile="taos.cfg" +tarName="taos.tar.gz" + +if [ "$osType" != "Darwin" ]; then + script_dir="$(dirname $(readlink -f $0))" + top_dir="$(readlink -f ${script_dir}/../..)" +else + script_dir=$(dirname $0) + cd ${script_dir} + script_dir="$(pwd)" + top_dir=${script_dir}/../.. +fi + +# create compressed install file. +build_dir="${compile_dir}/build" +code_dir="${top_dir}" +release_dir="${top_dir}/release" + +#package_name='linux' + +if [ "$verMode" == "cluster" ]; then + install_dir="${release_dir}/${productName}-enterprise-client-${version}" +else + install_dir="${release_dir}/${productName}-client-${version}" +fi + +# Directories and files. + +if [ "$osType" != "Darwin" ]; then + if [ "$pagMode" == "lite" ]; then + strip ${build_dir}/bin/${clientName} + bin_files="${build_dir}/bin/${clientName} \ + ${script_dir}/remove_client.sh" + else + bin_files="${build_dir}/bin/${clientName} \ + ${script_dir}/remove_client.sh \ + ${script_dir}/set_core.sh \ + ${script_dir}/get_client.sh" + fi + lib_files="${build_dir}/lib/libtaos.so.${version}" +else + bin_files="${build_dir}/bin/${clientName} ${script_dir}/remove_client.sh" + lib_files="${build_dir}/lib/libtaos.${version}.dylib" +fi + +header_files="${code_dir}/include/client/taos.h ${code_dir}/include/common/taosdef.h ${code_dir}/include/util/taoserror.h" +if [ "$dbName" != "taos" ]; then + cfg_dir="${top_dir}/../enterprise/packaging/cfg" +else + cfg_dir="${top_dir}/packaging/cfg" +fi + +install_files="${script_dir}/install_client.sh" + +# make directories. +mkdir -p ${install_dir} +mkdir -p ${install_dir}/inc && cp ${header_files} ${install_dir}/inc +mkdir -p ${install_dir}/cfg && cp ${cfg_dir}/${configFile} ${install_dir}/cfg/${configFile} +mkdir -p ${install_dir}/bin && cp ${bin_files} ${install_dir}/bin && chmod a+x ${install_dir}/bin/* + +if [ -f ${build_dir}/bin/jemalloc-config ]; then + mkdir -p ${install_dir}/jemalloc/{bin,lib,lib/pkgconfig,include/jemalloc,share/doc/jemalloc,share/man/man3} + cp ${build_dir}/bin/jemalloc-config ${install_dir}/jemalloc/bin + if [ -f ${build_dir}/bin/jemalloc.sh ]; then + cp ${build_dir}/bin/jemalloc.sh ${install_dir}/jemalloc/bin + fi + if [ -f ${build_dir}/bin/jeprof ]; then + cp ${build_dir}/bin/jeprof ${install_dir}/jemalloc/bin + fi + if [ -f ${build_dir}/include/jemalloc/jemalloc.h ]; then + cp ${build_dir}/include/jemalloc/jemalloc.h ${install_dir}/jemalloc/include/jemalloc + fi + if [ -f ${build_dir}/lib/libjemalloc.so.2 ]; then + cp ${build_dir}/lib/libjemalloc.so.2 ${install_dir}/jemalloc/lib + ln -sf libjemalloc.so.2 ${install_dir}/jemalloc/lib/libjemalloc.so + fi + if [ -f ${build_dir}/lib/libjemalloc.a ]; then + cp ${build_dir}/lib/libjemalloc.a ${install_dir}/jemalloc/lib + fi + if [ -f ${build_dir}/lib/libjemalloc_pic.a ]; then + cp ${build_dir}/lib/libjemalloc_pic.a ${install_dir}/jemalloc/lib + fi + if [ -f ${build_dir}/lib/pkgconfig/jemalloc.pc ]; then + cp ${build_dir}/lib/pkgconfig/jemalloc.pc ${install_dir}/jemalloc/lib/pkgconfig + fi + if [ -f ${build_dir}/share/doc/jemalloc/jemalloc.html ]; then + cp ${build_dir}/share/doc/jemalloc/jemalloc.html ${install_dir}/jemalloc/share/doc/jemalloc + fi + if [ -f ${build_dir}/share/man/man3/jemalloc.3 ]; then + cp ${build_dir}/share/man/man3/jemalloc.3 ${install_dir}/jemalloc/share/man/man3 + fi +fi + +cd ${install_dir} + +if [ "$osType" != "Darwin" ]; then + tar -zcv -f ${tarName} * --remove-files || : +else + tar -zcv -f ${tarName} * || : + mv ${tarName} .. + rm -rf ./* + mv ../${tarName} . +fi + +cd ${curr_dir} +cp ${install_files} ${install_dir} +if [ "$osType" == "Darwin" ]; then + sed 's/osType=Linux/osType=Darwin/g' ${install_dir}/install_client.sh >>install_client_temp.sh + mv install_client_temp.sh ${install_dir}/install_client.sh +fi + +if [ "$verMode" == "cluster" ]; then + sed 's/verMode=edge/verMode=cluster/g' ${install_dir}/install_client.sh >>install_client_temp.sh + mv install_client_temp.sh ${install_dir}/install_client.sh +fi + +if [ "$pagMode" == "lite" ]; then + sed 's/pagMode=full/pagMode=lite/g' ${install_dir}/install_client.sh >>install_client_temp.sh + mv install_client_temp.sh ${install_dir}/install_client.sh +fi +chmod a+x ${install_dir}/install_client.sh + +if [[ $productName == "TDengine" ]]; then + # Copy example code + mkdir -p ${install_dir}/examples + examples_dir="${top_dir}/examples" + cp -r ${examples_dir}/c ${install_dir}/examples + if [[ "$pagMode" != "lite" ]] && [[ "$cpuType" != "aarch32" ]]; then + cp -r ${examples_dir}/JDBC ${install_dir}/examples + cp -r ${examples_dir}/matlab ${install_dir}/examples + cp -r ${examples_dir}/python ${install_dir}/examples + cp -r ${examples_dir}/R ${install_dir}/examples + cp -r ${examples_dir}/go ${install_dir}/examples + cp -r ${examples_dir}/nodejs ${install_dir}/examples + cp -r ${examples_dir}/C# ${install_dir}/examples + mkdir -p ${install_dir}/examples/taosbenchmark-json && cp ${examples_dir}/../tools/taos-tools/example/* ${install_dir}/examples/taosbenchmark-json + fi + + if [ "$verMode" == "cluster" ]; then + # Copy connector + connector_dir="${code_dir}/connector" + mkdir -p ${install_dir}/connector + if [[ "$pagMode" != "lite" ]] && [[ "$cpuType" != "aarch32" ]]; then + if [ "$osType" != "Darwin" ]; then + cp ${build_dir}/lib/*.jar ${install_dir}/connector || : + fi + if find ${connector_dir}/go -mindepth 1 -maxdepth 1 | read; then + cp -r ${connector_dir}/go ${install_dir}/connector + else + echo "WARNING: go connector not found, please check if want to use it!" + fi + git clone --depth 1 https://github.com/taosdata/taos-connector-python ${install_dir}/connector/python + rm -rf ${install_dir}/connector/python/.git ||: +# cp -r ${connector_dir}/python ${install_dir}/connector + git clone --depth 1 https://github.com/taosdata/taos-connector-node ${install_dir}/connector/nodejs + rm -rf ${install_dir}/connector/nodejs/.git ||: + + git clone --depth 1 https://github.com/taosdata/taos-connector-dotnet ${install_dir}/connector/dotnet + rm -rf ${install_dir}/connector/dotnet/.git ||: +# cp -r ${connector_dir}/nodejs ${install_dir}/connector + git clone --depth 1 https://github.com/taosdata/libtaos-rs ${install_dir}/connector/rust + rm -rf ${install_dir}/connector/rust/.git ||: + fi + fi +fi +# Copy driver +mkdir -p ${install_dir}/driver +cp ${lib_files} ${install_dir}/driver + +# Copy connector +connector_dir="${code_dir}/connector" +mkdir -p ${install_dir}/connector + +if [[ "$pagMode" != "lite" ]] && [[ "$cpuType" != "aarch32" ]]; then + if [ "$osType" != "Darwin" ]; then + cp ${build_dir}/lib/*.jar ${install_dir}/connector || : + fi + if find ${connector_dir}/go -mindepth 1 -maxdepth 1 | read; then + cp -r ${connector_dir}/go ${install_dir}/connector + else + echo "WARNING: go connector not found, please check if want to use it!" + fi + cp -r ${connector_dir}/python ${install_dir}/connector || : + cp -r ${connector_dir}/nodejs ${install_dir}/connector || : +fi +# Copy release note +# cp ${script_dir}/release_note ${install_dir} + +# exit 1 + +cd ${release_dir} + +# install_dir has been distinguishes cluster from edege, so comments this code +pkg_name=${install_dir}-${osType}-${cpuType} + +# if [ "$verMode" == "cluster" ]; then +# pkg_name=${install_dir}-${osType}-${cpuType} +# elif [ "$verMode" == "edge" ]; then +# pkg_name=${install_dir}-${osType}-${cpuType} +# else +# echo "unknow verMode, nor cluster or edge" +# exit 1 +# fi + +if [[ "$verType" == "beta" ]] || [[ "$verType" == "preRelease" ]]; then + pkg_name=${install_dir}-${verType}-${osType}-${cpuType} +elif [ "$verType" == "stable" ]; then + pkg_name=${pkg_name} +else + echo "unknow verType, nor stabel or beta" + exit 1 +fi + +if [ "$pagMode" == "lite" ]; then + pkg_name=${pkg_name}-Lite +fi + +if [ "$osType" != "Darwin" ]; then + tar -zcv -f "$(basename ${pkg_name}).tar.gz" $(basename ${install_dir}) --remove-files || : +else + tar -zcv -f "$(basename ${pkg_name}).tar.gz" $(basename ${install_dir}) || : + mv "$(basename ${pkg_name}).tar.gz" .. + rm -rf ./* + mv ../"$(basename ${pkg_name}).tar.gz" . +fi + +cd ${curr_dir} diff --git a/packaging/tools/makepkg.sh b/packaging/tools/makepkg.sh old mode 100644 new mode 100755 index b4e60c13f5061f401bb4cd68382cb1fc3c0be6de..ea8ebba4509f14f59be2a711c540c06e3a00702c --- a/packaging/tools/makepkg.sh +++ b/packaging/tools/makepkg.sh @@ -1,378 +1,378 @@ -#!/bin/bash -# -# Generate tar.gz package for all os system - -set -e -#set -x - -curr_dir=$(pwd) -compile_dir=$1 -version=$2 -build_time=$3 -cpuType=$4 -osType=$5 -verMode=$6 -verType=$7 -pagMode=$8 -versionComp=$9 -dbName=${10} - -script_dir="$(dirname $(readlink -f $0))" -top_dir="$(readlink -f ${script_dir}/../..)" - -productName="TDengine" -serverName="taosd" -clientName="taos" -configFile="taos.cfg" -tarName="taos.tar.gz" -dumpName="taosdump" -benchmarkName="taosBenchmark" -toolsName="taostools" -adapterName="taosadapter" -defaultPasswd="taosdata" - -# create compressed install file. -build_dir="${compile_dir}/build" -code_dir="${top_dir}/src" -release_dir="${top_dir}/release" - -#package_name='linux' -if [ "$verMode" == "cluster" ]; then - install_dir="${release_dir}/${productName}-enterprise-server-${version}" -else - install_dir="${release_dir}/${productName}-server-${version}" -fi - -if [ -d ${top_dir}/src/kit/taos-tools/packaging/deb ]; then - cd ${top_dir}/src/kit/taos-tools/packaging/deb - [ -z "$taos_tools_ver" ] && taos_tools_ver="0.1.0" - - taostools_ver=$(git describe --tags | sed -e 's/ver-//g' | awk -F '-' '{print $1}') - taostools_install_dir="${release_dir}/${clientName}Tools-${taostools_ver}" - - cd ${curr_dir} -else - taostools_install_dir="${release_dir}/${clientName}Tools-${version}" -fi - -# Directories and files -if [ "$pagMode" == "lite" ]; then - strip ${build_dir}/bin/${serverName} - strip ${build_dir}/bin/${clientName} - # lite version doesn't include taosadapter, which will lead to no restful interface - bin_files="${build_dir}/bin/${serverName} ${build_dir}/bin/${clientName} ${script_dir}/remove.sh ${script_dir}/startPre.sh ${build_dir}/bin/taosBenchmark" - taostools_bin_files="" -else - - wget https://github.com/taosdata/grafanaplugin/releases/latest/download/TDinsight.sh -O ${build_dir}/bin/TDinsight.sh \ - && echo "TDinsight.sh downloaded!" \ - || echo "failed to download TDinsight.sh" - # download TDinsight caches - orig_pwd=$(pwd) - tdinsight_caches="" - cd ${build_dir}/bin/ && \ - chmod +x TDinsight.sh - tdinsight_caches=$(./TDinsight.sh --download-only | xargs -i printf "${build_dir}/bin/{} ") - cd $orig_pwd - echo "TDinsight caches: $tdinsight_caches" - - taostools_bin_files=" ${build_dir}/bin/taosdump \ - ${build_dir}/bin/taosBenchmark \ - ${build_dir}/bin/TDinsight.sh \ - $tdinsight_caches" - - bin_files="${build_dir}/bin/${serverName} \ - ${build_dir}/bin/${clientName} \ - ${taostools_bin_files} \ - ${build_dir}/bin/taosadapter \ - ${build_dir}/bin/tarbitrator\ - ${script_dir}/remove.sh \ - ${script_dir}/set_core.sh \ - ${script_dir}/run_taosd_and_taosadapter.sh \ - ${script_dir}/startPre.sh \ - ${script_dir}/taosd-dump-cfg.gdb" -fi - -lib_files="${build_dir}/lib/libtaos.so.${version}" -header_files="${code_dir}/inc/taos.h ${code_dir}/inc/taosdef.h ${code_dir}/inc/taoserror.h" - -if [ "$dbName" != "taos" ]; then - cfg_dir="${top_dir}/../enterprise/packaging/cfg" -else - cfg_dir="${top_dir}/packaging/cfg" -fi - -install_files="${script_dir}/install.sh" -nginx_dir="${code_dir}/../../enterprise/src/plugins/web" - -init_file_deb=${script_dir}/../deb/taosd -init_file_rpm=${script_dir}/../rpm/taosd -init_file_tarbitrator_deb=${script_dir}/../deb/tarbitratord -init_file_tarbitrator_rpm=${script_dir}/../rpm/tarbitratord - -# make directories. -mkdir -p ${install_dir} -mkdir -p ${install_dir}/inc && cp ${header_files} ${install_dir}/inc -mkdir -p ${install_dir}/cfg && cp ${cfg_dir}/${configFile} ${install_dir}/cfg/${configFile} - -if [ -f "${compile_dir}/test/cfg/taosadapter.toml" ]; then - cp ${compile_dir}/test/cfg/taosadapter.toml ${install_dir}/cfg || : -fi - -if [ -f "${compile_dir}/test/cfg/taosadapter.service" ]; then - cp ${compile_dir}/test/cfg/taosadapter.service ${install_dir}/cfg || : -fi - -if [ -f "${cfg_dir}/${serverName}.service" ]; then - cp ${cfg_dir}/${serverName}.service ${install_dir}/cfg || : -fi - -if [ -f "${top_dir}/packaging/cfg/tarbitratord.service" ]; then - cp ${top_dir}/packaging/cfg/tarbitratord.service ${install_dir}/cfg || : -fi - -if [ -f "${top_dir}/packaging/cfg/nginxd.service" ]; then - cp ${top_dir}/packaging/cfg/nginxd.service ${install_dir}/cfg || : -fi - -mkdir -p ${install_dir}/bin && cp ${bin_files} ${install_dir}/bin && chmod a+x ${install_dir}/bin/* || : -mkdir -p ${install_dir}/init.d && cp ${init_file_deb} ${install_dir}/init.d/${serverName}.deb -mkdir -p ${install_dir}/init.d && cp ${init_file_rpm} ${install_dir}/init.d/${serverName}.rpm -mkdir -p ${install_dir}/init.d && cp ${init_file_tarbitrator_deb} ${install_dir}/init.d/tarbitratord.deb || : -mkdir -p ${install_dir}/init.d && cp ${init_file_tarbitrator_rpm} ${install_dir}/init.d/tarbitratord.rpm || : - -if [ $adapterName != "taosadapter" ]; then - mv ${install_dir}/cfg/taosadapter.toml ${install_dir}/cfg/$adapterName.toml - sed -i "s/path = \"\/var\/log\/taos\"/path = \"\/var\/log\/${productName}\"/g" ${install_dir}/cfg/$adapterName.toml - sed -i "s/password = \"taosdata\"/password = \"${defaultPasswd}\"/g" ${install_dir}/cfg/$adapterName.toml - - mv ${install_dir}/cfg/taosadapter.service ${install_dir}/cfg/$adapterName.service - sed -i "s/TDengine/${productName}/g" ${install_dir}/cfg/$adapterName.service - sed -i "s/taosAdapter/${adapterName}/g" ${install_dir}/cfg/$adapterName.service - sed -i "s/taosadapter/${adapterName}/g" ${install_dir}/cfg/$adapterName.service - - mv ${install_dir}/bin/taosadapter ${install_dir}/bin/${adapterName} - mv ${install_dir}/bin/run_taosd_and_taosadapter.sh ${install_dir}/bin/run_${serverName}_and_${adapterName}.sh - mv ${install_dir}/bin/taosd-dump-cfg.gdb ${install_dir}/bin/${serverName}-dump-cfg.gdb -fi - -if [ -n "${taostools_bin_files}" ]; then - mkdir -p ${taostools_install_dir} || echo -e "failed to create ${taostools_install_dir}" - mkdir -p ${taostools_install_dir}/bin \ - && cp ${taostools_bin_files} ${taostools_install_dir}/bin \ - && chmod a+x ${taostools_install_dir}/bin/* || : - - if [ -f ${top_dir}/src/kit/taos-tools/packaging/tools/install-taostools.sh ]; then - cp ${top_dir}/src/kit/taos-tools/packaging/tools/install-taostools.sh \ - ${taostools_install_dir}/ > /dev/null \ - && chmod a+x ${taostools_install_dir}/install-taostools.sh \ - || echo -e "failed to copy install-taostools.sh" - else - echo -e "install-taostools.sh not found" - fi - - if [ -f ${top_dir}/src/kit/taos-tools/packaging/tools/uninstall-taostools.sh ]; then - cp ${top_dir}/src/kit/taos-tools/packaging/tools/uninstall-taostools.sh \ - ${taostools_install_dir}/ > /dev/null \ - && chmod a+x ${taostools_install_dir}/uninstall-taostools.sh \ - || echo -e "failed to copy uninstall-taostools.sh" - else - echo -e "uninstall-taostools.sh not found" - fi - - if [ -f ${build_dir}/lib/libavro.so.23.0.0 ]; then - mkdir -p ${taostools_install_dir}/avro/{lib,lib/pkgconfig} || echo -e "failed to create ${taostools_install_dir}/avro" - cp ${build_dir}/lib/libavro.* ${taostools_install_dir}/avro/lib - cp ${build_dir}/lib/pkgconfig/avro-c.pc ${taostools_install_dir}/avro/lib/pkgconfig - fi -fi - -if [ -f ${build_dir}/bin/jemalloc-config ]; then - mkdir -p ${install_dir}/jemalloc/{bin,lib,lib/pkgconfig,include/jemalloc,share/doc/jemalloc,share/man/man3} - cp ${build_dir}/bin/jemalloc-config ${install_dir}/jemalloc/bin - if [ -f ${build_dir}/bin/jemalloc.sh ]; then - cp ${build_dir}/bin/jemalloc.sh ${install_dir}/jemalloc/bin - fi - if [ -f ${build_dir}/bin/jeprof ]; then - cp ${build_dir}/bin/jeprof ${install_dir}/jemalloc/bin - fi - if [ -f ${build_dir}/include/jemalloc/jemalloc.h ]; then - cp ${build_dir}/include/jemalloc/jemalloc.h ${install_dir}/jemalloc/include/jemalloc - fi - if [ -f ${build_dir}/lib/libjemalloc.so.2 ]; then - cp ${build_dir}/lib/libjemalloc.so.2 ${install_dir}/jemalloc/lib - ln -sf libjemalloc.so.2 ${install_dir}/jemalloc/lib/libjemalloc.so - fi - if [ -f ${build_dir}/lib/libjemalloc.a ]; then - cp ${build_dir}/lib/libjemalloc.a ${install_dir}/jemalloc/lib - fi - if [ -f ${build_dir}/lib/libjemalloc_pic.a ]; then - cp ${build_dir}/lib/libjemalloc_pic.a ${install_dir}/jemalloc/lib - fi - if [ -f ${build_dir}/lib/pkgconfig/jemalloc.pc ]; then - cp ${build_dir}/lib/pkgconfig/jemalloc.pc ${install_dir}/jemalloc/lib/pkgconfig - fi - if [ -f ${build_dir}/share/doc/jemalloc/jemalloc.html ]; then - cp ${build_dir}/share/doc/jemalloc/jemalloc.html ${install_dir}/jemalloc/share/doc/jemalloc - fi - if [ -f ${build_dir}/share/man/man3/jemalloc.3 ]; then - cp ${build_dir}/share/man/man3/jemalloc.3 ${install_dir}/jemalloc/share/man/man3 - fi -fi - -if [ "$verMode" == "cluster" ]; then - sed 's/verMode=edge/verMode=cluster/g' ${install_dir}/bin/remove.sh >>remove_temp.sh - mv remove_temp.sh ${install_dir}/bin/remove.sh - - mkdir -p ${install_dir}/nginxd && cp -r ${nginx_dir}/* ${install_dir}/nginxd - cp ${nginx_dir}/png/taos.png ${install_dir}/nginxd/admin/images/taos.png - rm -rf ${install_dir}/nginxd/png - - if [ "$cpuType" == "aarch64" ]; then - cp -f ${install_dir}/nginxd/sbin/arm/64bit/nginx ${install_dir}/nginxd/sbin/ - elif [ "$cpuType" == "aarch32" ]; then - cp -f ${install_dir}/nginxd/sbin/arm/32bit/nginx ${install_dir}/nginxd/sbin/ - fi - rm -rf ${install_dir}/nginxd/sbin/arm -fi - -cd ${install_dir} -tar -zcv -f ${tarName} * --remove-files || : -exitcode=$? -if [ "$exitcode" != "0" ]; then - echo "tar ${tarName} error !!!" - exit $exitcode -fi - -cd ${curr_dir} -cp ${install_files} ${install_dir} -if [ "$verMode" == "cluster" ]; then - sed 's/verMode=edge/verMode=cluster/g' ${install_dir}/install.sh >>install_temp.sh - mv install_temp.sh ${install_dir}/install.sh -fi -if [ "$pagMode" == "lite" ]; then - sed 's/pagMode=full/pagMode=lite/g' ${install_dir}/install.sh >>install_temp.sh - mv install_temp.sh ${install_dir}/install.sh -fi -chmod a+x ${install_dir}/install.sh - -if [[ $dbName == "taos" ]]; then - # Copy example code - mkdir -p ${install_dir}/examples - examples_dir="${top_dir}/examples" - cp -r ${examples_dir}/c ${install_dir}/examples - if [[ "$pagMode" != "lite" ]] && [[ "$cpuType" != "aarch32" ]]; then - if [ -d ${examples_dir}/JDBC/connectionPools/target ]; then - rm -rf ${examples_dir}/JDBC/connectionPools/target - fi - if [ -d ${examples_dir}/JDBC/JDBCDemo/target ]; then - rm -rf ${examples_dir}/JDBC/JDBCDemo/target - fi - if [ -d ${examples_dir}/JDBC/mybatisplus-demo/target ]; then - rm -rf ${examples_dir}/JDBC/mybatisplus-demo/target - fi - if [ -d ${examples_dir}/JDBC/springbootdemo/target ]; then - rm -rf ${examples_dir}/JDBC/springbootdemo/target - fi - if [ -d ${examples_dir}/JDBC/SpringJdbcTemplate/target ]; then - rm -rf ${examples_dir}/JDBC/SpringJdbcTemplate/target - fi - if [ -d ${examples_dir}/JDBC/taosdemo/target ]; then - rm -rf ${examples_dir}/JDBC/taosdemo/target - fi - - cp -r ${examples_dir}/JDBC ${install_dir}/examples - cp -r ${examples_dir}/matlab ${install_dir}/examples - cp -r ${examples_dir}/python ${install_dir}/examples - cp -r ${examples_dir}/R ${install_dir}/examples - cp -r ${examples_dir}/go ${install_dir}/examples - cp -r ${examples_dir}/nodejs ${install_dir}/examples - cp -r ${examples_dir}/C# ${install_dir}/examples - mkdir -p ${install_dir}/examples/taosbenchmark-json && cp ${examples_dir}/../src/kit/taos-tools/example/* ${install_dir}/examples/taosbenchmark-json - fi -fi - -# Copy driver -mkdir -p ${install_dir}/driver && cp ${lib_files} ${install_dir}/driver && echo "${versionComp}" >${install_dir}/driver/vercomp.txt - -# Copy connector -if [ "$verMode" == "cluster" ]; then - connector_dir="${code_dir}/connector" - mkdir -p ${install_dir}/connector - if [[ "$pagMode" != "lite" ]] && [[ "$cpuType" != "aarch32" ]]; then - cp ${build_dir}/lib/*.jar ${install_dir}/connector || : - if find ${connector_dir}/go -mindepth 1 -maxdepth 1 | read; then - cp -r ${connector_dir}/go ${install_dir}/connector - else - echo "WARNING: go connector not found, please check if want to use it!" - fi - git clone --depth 1 https://github.com/taosdata/taos-connector-python ${install_dir}/connector/python - rm -rf ${install_dir}/connector/python/.git ||: - - git clone --depth 1 https://github.com/taosdata/taos-connector-node ${install_dir}/connector/nodejs - rm -rf ${install_dir}/connector/nodejs/.git ||: - - git clone --depth 1 https://github.com/taosdata/taos-connector-dotnet ${install_dir}/connector/dotnet - rm -rf ${install_dir}/connector/dotnet/.git ||: - - git clone --depth 1 https://github.com/taosdata/libtaos-rs ${install_dir}/connector/rust - rm -rf ${install_dir}/connector/rust/.git ||: - # cp -r ${connector_dir}/python ${install_dir}/connector - # cp -r ${connector_dir}/nodejs ${install_dir}/connector - fi -fi - -# Copy release note -cp ${script_dir}/release_note ${install_dir} - -# exit 1 - -cd ${release_dir} - -# install_dir has been distinguishes cluster from edege, so comments this code -pkg_name=${install_dir}-${osType}-${cpuType} - -taostools_pkg_name=${taostools_install_dir}-${osType}-${cpuType} - -# if [ "$verMode" == "cluster" ]; then -# pkg_name=${install_dir}-${osType}-${cpuType} -# elif [ "$verMode" == "edge" ]; then -# pkg_name=${install_dir}-${osType}-${cpuType} -# else -# echo "unknow verMode, nor cluster or edge" -# exit 1 -# fi - -if [[ "$verType" == "beta" ]] || [[ "$verType" == "preRelease" ]]; then - pkg_name=${install_dir}-${verType}-${osType}-${cpuType} - taostools_pkg_name=${taostools_install_dir}-${verType}-${osType}-${cpuType} -elif [ "$verType" == "stable" ]; then - pkg_name=${pkg_name} - taostools_pkg_name=${taostools_pkg_name} -else - echo "unknow verType, nor stabel or beta" - exit 1 -fi - -if [ "$pagMode" == "lite" ]; then - pkg_name=${pkg_name}-Lite -fi - -tar -zcv -f "$(basename ${pkg_name}).tar.gz" "$(basename ${install_dir})" --remove-files || : -exitcode=$? -if [ "$exitcode" != "0" ]; then - echo "tar ${pkg_name}.tar.gz error !!!" - exit $exitcode -fi - -if [ -n "${taostools_bin_files}" ]; then - wget https://github.com/taosdata/grafanaplugin/releases/latest/download/TDinsight.sh -O ${taostools_install_dir}/bin/TDinsight.sh && echo "TDinsight.sh downloaded!"|| echo "failed to download TDinsight.sh" - tar -zcv -f "$(basename ${taostools_pkg_name}).tar.gz" "$(basename ${taostools_install_dir})" --remove-files || : - exitcode=$? - if [ "$exitcode" != "0" ]; then - echo "tar ${taostools_pkg_name}.tar.gz error !!!" - exit $exitcode - fi -fi - -cd ${curr_dir} +#!/bin/bash +# +# Generate tar.gz package for all os system + +set -e +#set -x + +curr_dir=$(pwd) +compile_dir=$1 +version=$2 +build_time=$3 +cpuType=$4 +osType=$5 +verMode=$6 +verType=$7 +pagMode=$8 +versionComp=$9 +dbName=${10} + +script_dir="$(dirname $(readlink -f $0))" +top_dir="$(readlink -f ${script_dir}/../..)" + +productName="TDengine" +serverName="taosd" +clientName="taos" +configFile="taos.cfg" +tarName="taos.tar.gz" +dumpName="taosdump" +benchmarkName="taosBenchmark" +toolsName="taostools" +adapterName="taosadapter" +defaultPasswd="taosdata" + +# create compressed install file. +build_dir="${compile_dir}/build" +code_dir="${top_dir}" +release_dir="${top_dir}/release" + +#package_name='linux' +if [ "$verMode" == "cluster" ]; then + install_dir="${release_dir}/${productName}-enterprise-server-${version}" +else + install_dir="${release_dir}/${productName}-server-${version}" +fi + +if [ -d ${top_dir}/tools/taos-tools/packaging/deb ]; then + cd ${top_dir}/tools/taos-tools/packaging/deb + [ -z "$taos_tools_ver" ] && taos_tools_ver="0.1.0" + + taostools_ver=$(git describe --tags | sed -e 's/ver-//g' | awk -F '-' '{print $1}') + taostools_install_dir="${release_dir}/${clientName}Tools-${taostools_ver}" + + cd ${curr_dir} +else + taostools_install_dir="${release_dir}/${clientName}Tools-${version}" +fi + +# Directories and files +if [ "$pagMode" == "lite" ]; then + strip ${build_dir}/bin/${serverName} + strip ${build_dir}/bin/${clientName} + # lite version doesn't include taosadapter, which will lead to no restful interface + bin_files="${build_dir}/bin/${serverName} ${build_dir}/bin/${clientName} ${script_dir}/remove.sh ${script_dir}/startPre.sh ${build_dir}/bin/taosBenchmark" + taostools_bin_files="" +else + + wget https://github.com/taosdata/grafanaplugin/releases/latest/download/TDinsight.sh -O ${build_dir}/bin/TDinsight.sh \ + && echo "TDinsight.sh downloaded!" \ + || echo "failed to download TDinsight.sh" + # download TDinsight caches + orig_pwd=$(pwd) + tdinsight_caches="" + cd ${build_dir}/bin/ && \ + chmod +x TDinsight.sh + tdinsight_caches=$(./TDinsight.sh --download-only | xargs -i printf "${build_dir}/bin/{} ") + cd $orig_pwd + echo "TDinsight caches: $tdinsight_caches" + + taostools_bin_files=" ${build_dir}/bin/taosdump \ + ${build_dir}/bin/taosBenchmark \ + ${build_dir}/bin/TDinsight.sh \ + $tdinsight_caches" + + bin_files="${build_dir}/bin/${serverName} \ + ${build_dir}/bin/${clientName} \ + ${taostools_bin_files} \ + ${build_dir}/bin/taosadapter \ + ${build_dir}/bin/tarbitrator\ + ${script_dir}/remove.sh \ + ${script_dir}/set_core.sh \ + ${script_dir}/run_taosd_and_taosadapter.sh \ + ${script_dir}/startPre.sh \ + ${script_dir}/taosd-dump-cfg.gdb" +fi + +lib_files="${build_dir}/lib/libtaos.so.${version}" +header_files="${code_dir}/include/client/taos.h ${code_dir}/include/common/taosdef.h ${code_dir}/include/util/taoserror.h" + +if [ "$dbName" != "taos" ]; then + cfg_dir="${top_dir}/../enterprise/packaging/cfg" +else + cfg_dir="${top_dir}/packaging/cfg" +fi + +install_files="${script_dir}/install.sh" +nginx_dir="${top_dir}/../enterprise/src/plugins/web" + +init_file_deb=${script_dir}/../deb/taosd +init_file_rpm=${script_dir}/../rpm/taosd +init_file_tarbitrator_deb=${script_dir}/../deb/tarbitratord +init_file_tarbitrator_rpm=${script_dir}/../rpm/tarbitratord + +# make directories. +mkdir -p ${install_dir} +mkdir -p ${install_dir}/inc && cp ${header_files} ${install_dir}/inc +mkdir -p ${install_dir}/cfg && cp ${cfg_dir}/${configFile} ${install_dir}/cfg/${configFile} + +if [ -f "${compile_dir}/test/cfg/taosadapter.toml" ]; then + cp ${compile_dir}/test/cfg/taosadapter.toml ${install_dir}/cfg || : +fi + +if [ -f "${compile_dir}/test/cfg/taosadapter.service" ]; then + cp ${compile_dir}/test/cfg/taosadapter.service ${install_dir}/cfg || : +fi + +if [ -f "${cfg_dir}/${serverName}.service" ]; then + cp ${cfg_dir}/${serverName}.service ${install_dir}/cfg || : +fi + +if [ -f "${top_dir}/packaging/cfg/tarbitratord.service" ]; then + cp ${top_dir}/packaging/cfg/tarbitratord.service ${install_dir}/cfg || : +fi + +if [ -f "${top_dir}/packaging/cfg/nginxd.service" ]; then + cp ${top_dir}/packaging/cfg/nginxd.service ${install_dir}/cfg || : +fi + +mkdir -p ${install_dir}/bin && cp ${bin_files} ${install_dir}/bin && chmod a+x ${install_dir}/bin/* || : +mkdir -p ${install_dir}/init.d && cp ${init_file_deb} ${install_dir}/init.d/${serverName}.deb +mkdir -p ${install_dir}/init.d && cp ${init_file_rpm} ${install_dir}/init.d/${serverName}.rpm +mkdir -p ${install_dir}/init.d && cp ${init_file_tarbitrator_deb} ${install_dir}/init.d/tarbitratord.deb || : +mkdir -p ${install_dir}/init.d && cp ${init_file_tarbitrator_rpm} ${install_dir}/init.d/tarbitratord.rpm || : + +if [ $adapterName != "taosadapter" ]; then + mv ${install_dir}/cfg/taosadapter.toml ${install_dir}/cfg/$adapterName.toml + sed -i "s/path = \"\/var\/log\/taos\"/path = \"\/var\/log\/${productName}\"/g" ${install_dir}/cfg/$adapterName.toml + sed -i "s/password = \"taosdata\"/password = \"${defaultPasswd}\"/g" ${install_dir}/cfg/$adapterName.toml + + mv ${install_dir}/cfg/taosadapter.service ${install_dir}/cfg/$adapterName.service + sed -i "s/TDengine/${productName}/g" ${install_dir}/cfg/$adapterName.service + sed -i "s/taosAdapter/${adapterName}/g" ${install_dir}/cfg/$adapterName.service + sed -i "s/taosadapter/${adapterName}/g" ${install_dir}/cfg/$adapterName.service + + mv ${install_dir}/bin/taosadapter ${install_dir}/bin/${adapterName} + mv ${install_dir}/bin/run_taosd_and_taosadapter.sh ${install_dir}/bin/run_${serverName}_and_${adapterName}.sh + mv ${install_dir}/bin/taosd-dump-cfg.gdb ${install_dir}/bin/${serverName}-dump-cfg.gdb +fi + +if [ -n "${taostools_bin_files}" ]; then + mkdir -p ${taostools_install_dir} || echo -e "failed to create ${taostools_install_dir}" + mkdir -p ${taostools_install_dir}/bin \ + && cp ${taostools_bin_files} ${taostools_install_dir}/bin \ + && chmod a+x ${taostools_install_dir}/bin/* || : + + if [ -f ${top_dir}/tools/taos-tools/packaging/tools/install-taostools.sh ]; then + cp ${top_dir}/tools/taos-tools/packaging/tools/install-taostools.sh \ + ${taostools_install_dir}/ > /dev/null \ + && chmod a+x ${taostools_install_dir}/install-taostools.sh \ + || echo -e "failed to copy install-taostools.sh" + else + echo -e "install-taostools.sh not found" + fi + + if [ -f ${top_dir}/tools/taos-tools/packaging/tools/uninstall-taostools.sh ]; then + cp ${top_dir}/tools/taos-tools/packaging/tools/uninstall-taostools.sh \ + ${taostools_install_dir}/ > /dev/null \ + && chmod a+x ${taostools_install_dir}/uninstall-taostools.sh \ + || echo -e "failed to copy uninstall-taostools.sh" + else + echo -e "uninstall-taostools.sh not found" + fi + + if [ -f ${build_dir}/lib/libavro.so.23.0.0 ]; then + mkdir -p ${taostools_install_dir}/avro/{lib,lib/pkgconfig} || echo -e "failed to create ${taostools_install_dir}/avro" + cp ${build_dir}/lib/libavro.* ${taostools_install_dir}/avro/lib + cp ${build_dir}/lib/pkgconfig/avro-c.pc ${taostools_install_dir}/avro/lib/pkgconfig + fi +fi + +if [ -f ${build_dir}/bin/jemalloc-config ]; then + mkdir -p ${install_dir}/jemalloc/{bin,lib,lib/pkgconfig,include/jemalloc,share/doc/jemalloc,share/man/man3} + cp ${build_dir}/bin/jemalloc-config ${install_dir}/jemalloc/bin + if [ -f ${build_dir}/bin/jemalloc.sh ]; then + cp ${build_dir}/bin/jemalloc.sh ${install_dir}/jemalloc/bin + fi + if [ -f ${build_dir}/bin/jeprof ]; then + cp ${build_dir}/bin/jeprof ${install_dir}/jemalloc/bin + fi + if [ -f ${build_dir}/include/jemalloc/jemalloc.h ]; then + cp ${build_dir}/include/jemalloc/jemalloc.h ${install_dir}/jemalloc/include/jemalloc + fi + if [ -f ${build_dir}/lib/libjemalloc.so.2 ]; then + cp ${build_dir}/lib/libjemalloc.so.2 ${install_dir}/jemalloc/lib + ln -sf libjemalloc.so.2 ${install_dir}/jemalloc/lib/libjemalloc.so + fi + if [ -f ${build_dir}/lib/libjemalloc.a ]; then + cp ${build_dir}/lib/libjemalloc.a ${install_dir}/jemalloc/lib + fi + if [ -f ${build_dir}/lib/libjemalloc_pic.a ]; then + cp ${build_dir}/lib/libjemalloc_pic.a ${install_dir}/jemalloc/lib + fi + if [ -f ${build_dir}/lib/pkgconfig/jemalloc.pc ]; then + cp ${build_dir}/lib/pkgconfig/jemalloc.pc ${install_dir}/jemalloc/lib/pkgconfig + fi + if [ -f ${build_dir}/share/doc/jemalloc/jemalloc.html ]; then + cp ${build_dir}/share/doc/jemalloc/jemalloc.html ${install_dir}/jemalloc/share/doc/jemalloc + fi + if [ -f ${build_dir}/share/man/man3/jemalloc.3 ]; then + cp ${build_dir}/share/man/man3/jemalloc.3 ${install_dir}/jemalloc/share/man/man3 + fi +fi + +if [ "$verMode" == "cluster" ]; then + sed 's/verMode=edge/verMode=cluster/g' ${install_dir}/bin/remove.sh >>remove_temp.sh + mv remove_temp.sh ${install_dir}/bin/remove.sh + + mkdir -p ${install_dir}/nginxd && cp -r ${nginx_dir}/* ${install_dir}/nginxd + cp ${nginx_dir}/png/taos.png ${install_dir}/nginxd/admin/images/taos.png + rm -rf ${install_dir}/nginxd/png + + if [ "$cpuType" == "aarch64" ]; then + cp -f ${install_dir}/nginxd/sbin/arm/64bit/nginx ${install_dir}/nginxd/sbin/ + elif [ "$cpuType" == "aarch32" ]; then + cp -f ${install_dir}/nginxd/sbin/arm/32bit/nginx ${install_dir}/nginxd/sbin/ + fi + rm -rf ${install_dir}/nginxd/sbin/arm +fi + +cd ${install_dir} +tar -zcv -f ${tarName} * --remove-files || : +exitcode=$? +if [ "$exitcode" != "0" ]; then + echo "tar ${tarName} error !!!" + exit $exitcode +fi + +cd ${curr_dir} +cp ${install_files} ${install_dir} +if [ "$verMode" == "cluster" ]; then + sed 's/verMode=edge/verMode=cluster/g' ${install_dir}/install.sh >>install_temp.sh + mv install_temp.sh ${install_dir}/install.sh +fi +if [ "$pagMode" == "lite" ]; then + sed 's/pagMode=full/pagMode=lite/g' ${install_dir}/install.sh >>install_temp.sh + mv install_temp.sh ${install_dir}/install.sh +fi +chmod a+x ${install_dir}/install.sh + +if [[ $dbName == "taos" ]]; then + # Copy example code + mkdir -p ${install_dir}/examples + examples_dir="${top_dir}/examples" + cp -r ${examples_dir}/c ${install_dir}/examples + if [[ "$pagMode" != "lite" ]] && [[ "$cpuType" != "aarch32" ]]; then + if [ -d ${examples_dir}/JDBC/connectionPools/target ]; then + rm -rf ${examples_dir}/JDBC/connectionPools/target + fi + if [ -d ${examples_dir}/JDBC/JDBCDemo/target ]; then + rm -rf ${examples_dir}/JDBC/JDBCDemo/target + fi + if [ -d ${examples_dir}/JDBC/mybatisplus-demo/target ]; then + rm -rf ${examples_dir}/JDBC/mybatisplus-demo/target + fi + if [ -d ${examples_dir}/JDBC/springbootdemo/target ]; then + rm -rf ${examples_dir}/JDBC/springbootdemo/target + fi + if [ -d ${examples_dir}/JDBC/SpringJdbcTemplate/target ]; then + rm -rf ${examples_dir}/JDBC/SpringJdbcTemplate/target + fi + if [ -d ${examples_dir}/JDBC/taosdemo/target ]; then + rm -rf ${examples_dir}/JDBC/taosdemo/target + fi + + cp -r ${examples_dir}/JDBC ${install_dir}/examples + cp -r ${examples_dir}/matlab ${install_dir}/examples + cp -r ${examples_dir}/python ${install_dir}/examples + cp -r ${examples_dir}/R ${install_dir}/examples + cp -r ${examples_dir}/go ${install_dir}/examples + cp -r ${examples_dir}/nodejs ${install_dir}/examples + cp -r ${examples_dir}/C# ${install_dir}/examples + mkdir -p ${install_dir}/examples/taosbenchmark-json && cp ${examples_dir}/../tools/taos-tools/example/* ${install_dir}/examples/taosbenchmark-json + fi +fi + +# Copy driver +mkdir -p ${install_dir}/driver && cp ${lib_files} ${install_dir}/driver && echo "${versionComp}" >${install_dir}/driver/vercomp.txt + +# Copy connector +if [ "$verMode" == "cluster" ]; then + connector_dir="${code_dir}/connector" + mkdir -p ${install_dir}/connector + if [[ "$pagMode" != "lite" ]] && [[ "$cpuType" != "aarch32" ]]; then + cp ${build_dir}/lib/*.jar ${install_dir}/connector || : + if find ${connector_dir}/go -mindepth 1 -maxdepth 1 | read; then + cp -r ${connector_dir}/go ${install_dir}/connector + else + echo "WARNING: go connector not found, please check if want to use it!" + fi + git clone --depth 1 https://github.com/taosdata/taos-connector-python ${install_dir}/connector/python + rm -rf ${install_dir}/connector/python/.git ||: + + git clone --depth 1 https://github.com/taosdata/taos-connector-node ${install_dir}/connector/nodejs + rm -rf ${install_dir}/connector/nodejs/.git ||: + + git clone --depth 1 https://github.com/taosdata/taos-connector-dotnet ${install_dir}/connector/dotnet + rm -rf ${install_dir}/connector/dotnet/.git ||: + + git clone --depth 1 https://github.com/taosdata/libtaos-rs ${install_dir}/connector/rust + rm -rf ${install_dir}/connector/rust/.git ||: + # cp -r ${connector_dir}/python ${install_dir}/connector + # cp -r ${connector_dir}/nodejs ${install_dir}/connector + fi +fi + +# Copy release note +cp ${script_dir}/release_note ${install_dir} + +# exit 1 + +cd ${release_dir} + +# install_dir has been distinguishes cluster from edege, so comments this code +pkg_name=${install_dir}-${osType}-${cpuType} + +taostools_pkg_name=${taostools_install_dir}-${osType}-${cpuType} + +# if [ "$verMode" == "cluster" ]; then +# pkg_name=${install_dir}-${osType}-${cpuType} +# elif [ "$verMode" == "edge" ]; then +# pkg_name=${install_dir}-${osType}-${cpuType} +# else +# echo "unknow verMode, nor cluster or edge" +# exit 1 +# fi + +if [[ "$verType" == "beta" ]] || [[ "$verType" == "preRelease" ]]; then + pkg_name=${install_dir}-${verType}-${osType}-${cpuType} + taostools_pkg_name=${taostools_install_dir}-${verType}-${osType}-${cpuType} +elif [ "$verType" == "stable" ]; then + pkg_name=${pkg_name} + taostools_pkg_name=${taostools_pkg_name} +else + echo "unknow verType, nor stabel or beta" + exit 1 +fi + +if [ "$pagMode" == "lite" ]; then + pkg_name=${pkg_name}-Lite +fi + +tar -zcv -f "$(basename ${pkg_name}).tar.gz" "$(basename ${install_dir})" --remove-files || : +exitcode=$? +if [ "$exitcode" != "0" ]; then + echo "tar ${pkg_name}.tar.gz error !!!" + exit $exitcode +fi + +if [ -n "${taostools_bin_files}" ]; then + wget https://github.com/taosdata/grafanaplugin/releases/latest/download/TDinsight.sh -O ${taostools_install_dir}/bin/TDinsight.sh && echo "TDinsight.sh downloaded!"|| echo "failed to download TDinsight.sh" + tar -zcv -f "$(basename ${taostools_pkg_name}).tar.gz" "$(basename ${taostools_install_dir})" --remove-files || : + exitcode=$? + if [ "$exitcode" != "0" ]; then + echo "tar ${taostools_pkg_name}.tar.gz error !!!" + exit $exitcode + fi +fi + +cd ${curr_dir} diff --git a/packaging/tools/post.sh b/packaging/tools/post.sh old mode 100644 new mode 100755 index 59315dbe994c406310a43b62ced771c89ca27294..93849dd4ebef00512854b4dfff8b57f4b44f7797 --- a/packaging/tools/post.sh +++ b/packaging/tools/post.sh @@ -1,539 +1,539 @@ -#!/bin/bash -# -# This file is used to install tdengine rpm package on centos systems. The operating system -# is required to use systemd to manage services at boot -# set -x - -iplist="" -serverFqdn="" - -# -----------------------Variables definition--------------------- -script_dir=$(dirname $(readlink -f "$0")) -# Dynamic directory -data_dir="/var/lib/taos" -log_dir="/var/log/taos" -data_link_dir="/usr/local/taos/data" -log_link_dir="/usr/local/taos/log" -install_main_dir="/usr/local/taos" - -# static directory -cfg_dir="/usr/local/taos/cfg" -bin_dir="/usr/local/taos/bin" -lib_dir="/usr/local/taos/driver" -init_d_dir="/usr/local/taos/init.d" -inc_dir="/usr/local/taos/include" - -cfg_install_dir="/etc/taos" -bin_link_dir="/usr/bin" -lib_link_dir="/usr/lib" -lib64_link_dir="/usr/lib64" -inc_link_dir="/usr/include" - -service_config_dir="/etc/systemd/system" - - -# Color setting -RED='\033[0;31m' -GREEN='\033[1;32m' -GREEN_DARK='\033[0;32m' -GREEN_UNDERLINE='\033[4;32m' -NC='\033[0m' - -csudo="" -if command -v sudo > /dev/null; then - csudo="sudo " -fi - -initd_mod=0 -service_mod=2 -if pidof systemd &> /dev/null; then - service_mod=0 -elif $(which service &> /dev/null); then - service_mod=1 - service_config_dir="/etc/init.d" - if $(which chkconfig &> /dev/null); then - initd_mod=1 - elif $(which insserv &> /dev/null); then - initd_mod=2 - elif $(which update-rc.d &> /dev/null); then - initd_mod=3 - else - service_mod=2 - fi -else - service_mod=2 -fi - -function kill_taosadapter() { -# ${csudo}pkill -f taosadapter || : - pid=$(ps -ef | grep "taosadapter" | grep -v "grep" | awk '{print $2}') - if [ -n "$pid" ]; then - ${csudo}kill -9 $pid || : - fi -} - -function kill_taosd() { -# ${csudo}pkill -f taosd || : - pid=$(ps -ef | grep "taosd" | grep -v "grep" | awk '{print $2}') - if [ -n "$pid" ]; then - ${csudo}kill -9 $pid || : - fi -} - -function install_include() { - ${csudo}rm -f ${inc_link_dir}/taos.h ${inc_link_dir}/taosdef.h ${inc_link_dir}/taoserror.h|| : - ${csudo}ln -s ${inc_dir}/taos.h ${inc_link_dir}/taos.h - ${csudo}ln -s ${inc_dir}/taosdef.h ${inc_link_dir}/taosdef.h - ${csudo}ln -s ${inc_dir}/taoserror.h ${inc_link_dir}/taoserror.h -} - -function install_lib() { - ${csudo}rm -f ${lib_link_dir}/libtaos* || : - ${csudo}rm -f ${lib64_link_dir}/libtaos* || : - - ${csudo}ln -s ${lib_dir}/libtaos.* ${lib_link_dir}/libtaos.so.1 - ${csudo}ln -s ${lib_link_dir}/libtaos.so.1 ${lib_link_dir}/libtaos.so - - if [[ -d ${lib64_link_dir} && ! -e ${lib64_link_dir}/libtaos.so ]]; then - ${csudo}ln -s ${lib_dir}/libtaos.* ${lib64_link_dir}/libtaos.so.1 || : - ${csudo}ln -s ${lib64_link_dir}/libtaos.so.1 ${lib64_link_dir}/libtaos.so || : - fi - - ${csudo}ldconfig -} - -function install_bin() { - # Remove links - ${csudo}rm -f ${bin_link_dir}/taos || : - ${csudo}rm -f ${bin_link_dir}/taosd || : - ${csudo}rm -f ${bin_link_dir}/taosadapter || : - ${csudo}rm -f ${bin_link_dir}/taosBenchmark || : - ${csudo}rm -f ${bin_link_dir}/taosdemo || : - ${csudo}rm -f ${bin_link_dir}/taosdump || : - ${csudo}rm -f ${bin_link_dir}/rmtaos || : - ${csudo}rm -f ${bin_link_dir}/set_core || : - - ${csudo}chmod 0555 ${bin_dir}/* - - #Make link - [ -x ${bin_dir}/taos ] && ${csudo}ln -s ${bin_dir}/taos ${bin_link_dir}/taos || : - [ -x ${bin_dir}/taosd ] && ${csudo}ln -s ${bin_dir}/taosd ${bin_link_dir}/taosd || : - [ -x ${bin_dir}/taosadapter ] && ${csudo}ln -s ${bin_dir}/taosadapter ${bin_link_dir}/taosadapter || : - [ -x ${bin_dir}/taosBenchmark ] && ${csudo}ln -sf ${bin_dir}/taosBenchmark ${bin_link_dir}/taosdemo || : - [ -x ${bin_dir}/TDinsight.sh ] && ${csudo}ln -sf ${bin_dir}/TDinsight.sh ${bin_link_dir}/TDinsight.sh || : - [ -x ${bin_dir}/taosdump ] && ${csudo}ln -s ${bin_dir}/taosdump ${bin_link_dir}/taosdump || : - [ -x ${bin_dir}/set_core.sh ] && ${csudo}ln -s ${bin_dir}/set_core.sh ${bin_link_dir}/set_core || : -} - -function add_newHostname_to_hosts() { - localIp="127.0.0.1" - OLD_IFS="$IFS" - IFS=" " - iphost=$(cat /etc/hosts | grep $1 | awk '{print $1}') - arr=($iphost) - IFS="$OLD_IFS" - for s in "${arr[@]}" - do - if [[ "$s" == "$localIp" ]]; then - return - fi - done - ${csudo}echo "127.0.0.1 $1" >> /etc/hosts ||: -} - -function set_hostname() { - echo -e -n "${GREEN}Please enter one hostname(must not be 'localhost')${NC}:" - read newHostname - while true; do - if [[ ! -z "$newHostname" && "$newHostname" != "localhost" ]]; then - break - else - read -p "Please enter one hostname(must not be 'localhost'):" newHostname - fi - done - - ${csudo}hostname $newHostname ||: - retval=`echo $?` - if [[ $retval != 0 ]]; then - echo - echo "set hostname fail!" - return - fi - #echo -e -n "$(hostnamectl status --static)" - #echo -e -n "$(hostnamectl status --transient)" - #echo -e -n "$(hostnamectl status --pretty)" - - #ubuntu/centos /etc/hostname - if [[ -e /etc/hostname ]]; then - ${csudo}echo $newHostname > /etc/hostname ||: - fi - - #debian: #HOSTNAME=yourname - if [[ -e /etc/sysconfig/network ]]; then - ${csudo}sed -i -r "s/#*\s*(HOSTNAME=\s*).*/\1$newHostname/" /etc/sysconfig/network ||: - fi - - ${csudo}sed -i -r "s/#*\s*(fqdn\s*).*/\1$newHostname/" ${cfg_install_dir}/taos.cfg - serverFqdn=$newHostname - - if [[ -e /etc/hosts ]]; then - add_newHostname_to_hosts $newHostname - fi -} - -function is_correct_ipaddr() { - newIp=$1 - OLD_IFS="$IFS" - IFS=" " - arr=($iplist) - IFS="$OLD_IFS" - for s in "${arr[@]}" - do - if [[ "$s" == "$newIp" ]]; then - return 0 - fi - done - - return 1 -} - -function set_ipAsFqdn() { - iplist=$(ip address |grep inet |grep -v inet6 |grep -v 127.0.0.1 |awk '{print $2}' |awk -F "/" '{print $1}') ||: - if [ -z "$iplist" ]; then - iplist=$(ifconfig |grep inet |grep -v inet6 |grep -v 127.0.0.1 |awk '{print $2}' |awk -F ":" '{print $2}') ||: - fi - - if [ -z "$iplist" ]; then - echo - echo -e -n "${GREEN}Unable to get local ip, use 127.0.0.1${NC}" - localFqdn="127.0.0.1" - # Write the local FQDN to configuration file - ${csudo}sed -i -r "s/#*\s*(fqdn\s*).*/\1$localFqdn/" ${cfg_install_dir}/taos.cfg - serverFqdn=$localFqdn - echo - return - fi - - echo -e -n "${GREEN}Please choose an IP from local IP list${NC}:" - echo - echo -e -n "${GREEN}$iplist${NC}" - echo - echo - echo -e -n "${GREEN}Notes: if IP is used as the node name, data can NOT be migrated to other machine directly${NC}:" - read localFqdn - while true; do - if [ ! -z "$localFqdn" ]; then - # Check if correct ip address - is_correct_ipaddr $localFqdn - retval=`echo $?` - if [[ $retval != 0 ]]; then - read -p "Please choose an IP from local IP list:" localFqdn - else - # Write the local FQDN to configuration file - ${csudo}sed -i -r "s/#*\s*(fqdn\s*).*/\1$localFqdn/" ${cfg_install_dir}/taos.cfg - serverFqdn=$localFqdn - break - fi - else - read -p "Please choose an IP from local IP list:" localFqdn - fi - done -} - -function local_fqdn_check() { - #serverFqdn=$(hostname) - echo - echo -e -n "System hostname is: ${GREEN}$serverFqdn${NC}" - echo - if [[ "$serverFqdn" == "" ]] || [[ "$serverFqdn" == "localhost" ]]; then - echo -e -n "${GREEN}It is strongly recommended to configure a hostname for this machine ${NC}" - echo - - while true - do - read -r -p "Set hostname now? [Y/n] " input - if [ ! -n "$input" ]; then - set_hostname - break - else - case $input in - [yY][eE][sS]|[yY]) - set_hostname - break - ;; - - [nN][oO]|[nN]) - set_ipAsFqdn - break - ;; - - *) - echo "Invalid input..." - ;; - esac - fi - done - fi -} - -function install_taosadapter_config() { - if [ ! -f "${cfg_install_dir}/taosadapter.toml" ]; then - [ ! -d %{cfg_install_dir} ] && - ${csudo}${csudo}mkdir -p ${cfg_install_dir} - [ -f ${cfg_dir}/taosadapter.toml ] && ${csudo}cp ${cfg_dir}/taosadapter.toml ${cfg_install_dir} - [ -f ${cfg_install_dir}/taosadapter.toml ] && - ${csudo}chmod 644 ${cfg_install_dir}/taosadapter.toml - fi - - [ -f ${cfg_dir}/taosadapter.toml ] && - ${csudo}mv ${cfg_dir}/taosadapter.toml ${cfg_dir}/taosadapter.toml.new - - [ -f ${cfg_install_dir}/taosadapter.toml ] && - ${csudo}ln -s ${cfg_install_dir}/taosadapter.toml ${cfg_dir} -} - -function install_config() { - if [ ! -f "${cfg_install_dir}/taos.cfg" ]; then - ${csudo}${csudo}mkdir -p ${cfg_install_dir} - [ -f ${cfg_dir}/taos.cfg ] && ${csudo}cp ${cfg_dir}/taos.cfg ${cfg_install_dir} - ${csudo}chmod 644 ${cfg_install_dir}/* - fi - - # Save standard input to 6 and open / dev / TTY on standard input - exec 6<&0 0 ${email_file}" - break - #else - # read -p "Please enter the correct email address: " emailAddr - #fi - else - break - fi - done -} - -function clean_service_on_sysvinit() { - #restart_config_str="taos:2345:respawn:${service_config_dir}/taosd start" - #${csudo}sed -i "\|${restart_config_str}|d" /etc/inittab || : - - if pidof taosd &> /dev/null; then - ${csudo}service taosd stop || : - fi - - if ((${initd_mod}==1)); then - ${csudo}chkconfig --del taosd || : - elif ((${initd_mod}==2)); then - ${csudo}insserv -r taosd || : - elif ((${initd_mod}==3)); then - ${csudo}update-rc.d -f taosd remove || : - fi - - ${csudo}rm -f ${service_config_dir}/taosd || : - - if $(which init &> /dev/null); then - ${csudo}init q || : - fi -} - -function install_service_on_sysvinit() { - clean_service_on_sysvinit - - sleep 1 - - # Install taosd service - ${csudo}cp %{init_d_dir}/taosd ${service_config_dir} && ${csudo}chmod a+x ${service_config_dir}/taosd - - #restart_config_str="taos:2345:respawn:${service_config_dir}/taosd start" - #${csudo}grep -q -F "$restart_config_str" /etc/inittab || ${csudo}bash -c "echo '${restart_config_str}' >> /etc/inittab" - - if ((${initd_mod}==1)); then - ${csudo}chkconfig --add taosd || : - ${csudo}chkconfig --level 2345 taosd on || : - elif ((${initd_mod}==2)); then - ${csudo}insserv taosd || : - ${csudo}insserv -d taosd || : - elif ((${initd_mod}==3)); then - ${csudo}update-rc.d taosd defaults || : - fi -} - -function clean_service_on_systemd() { - taosd_service_config="${service_config_dir}/taosd.service" - - # taosd service already is stoped before install in preinst script - #if systemctl is-active --quiet taosd; then - # echo "TDengine is running, stopping it..." - # ${csudo}systemctl stop taosd &> /dev/null || echo &> /dev/null - #fi - ${csudo}systemctl disable taosd &> /dev/null || echo &> /dev/null - - ${csudo}rm -f ${taosd_service_config} -} - -# taos:2345:respawn:/etc/init.d/taosd start - -function install_service_on_systemd() { - clean_service_on_systemd - - taosd_service_config="${service_config_dir}/taosd.service" - - ${csudo}bash -c "echo '[Unit]' >> ${taosd_service_config}" - ${csudo}bash -c "echo 'Description=TDengine server service' >> ${taosd_service_config}" - ${csudo}bash -c "echo 'After=network-online.target' >> ${taosd_service_config}" - ${csudo}bash -c "echo 'Wants=network-online.target' >> ${taosd_service_config}" - ${csudo}bash -c "echo >> ${taosd_service_config}" - ${csudo}bash -c "echo '[Service]' >> ${taosd_service_config}" - ${csudo}bash -c "echo 'Type=simple' >> ${taosd_service_config}" - ${csudo}bash -c "echo 'ExecStart=/usr/bin/taosd' >> ${taosd_service_config}" - ${csudo}bash -c "echo 'ExecStartPre=/usr/local/taos/bin/startPre.sh' >> ${taosd_service_config}" - ${csudo}bash -c "echo 'TimeoutStopSec=1000000s' >> ${taosd_service_config}" - ${csudo}bash -c "echo 'LimitNOFILE=infinity' >> ${taosd_service_config}" - ${csudo}bash -c "echo 'LimitNPROC=infinity' >> ${taosd_service_config}" - ${csudo}bash -c "echo 'LimitCORE=infinity' >> ${taosd_service_config}" - ${csudo}bash -c "echo 'TimeoutStartSec=0' >> ${taosd_service_config}" - ${csudo}bash -c "echo 'StandardOutput=null' >> ${taosd_service_config}" - ${csudo}bash -c "echo 'Restart=always' >> ${taosd_service_config}" - ${csudo}bash -c "echo 'StartLimitBurst=3' >> ${taosd_service_config}" - ${csudo}bash -c "echo 'StartLimitInterval=60s' >> ${taosd_service_config}" - ${csudo}bash -c "echo >> ${taosd_service_config}" - ${csudo}bash -c "echo '[Install]' >> ${taosd_service_config}" - ${csudo}bash -c "echo 'WantedBy=multi-user.target' >> ${taosd_service_config}" - ${csudo}systemctl enable taosd -} - -function install_taosadapter_service() { - if ((${service_mod}==0)); then - [ -f ${script_dir}/../cfg/taosadapter.service ] &&\ - ${csudo}cp ${script_dir}/../cfg/taosadapter.service \ - ${service_config_dir}/ || : - ${csudo}systemctl daemon-reload - fi -} - -function install_service() { - if ((${service_mod}==0)); then - install_service_on_systemd - elif ((${service_mod}==1)); then - install_service_on_sysvinit - else - # manual start taosd - kill_taosadapter - kill_taosd - fi -} - -function install_TDengine() { - echo -e "${GREEN}Start to install TDengine...${NC}" - - #install log and data dir , then ln to /usr/local/taos - ${csudo}mkdir -p ${log_dir} && ${csudo}chmod 777 ${log_dir} - ${csudo}mkdir -p ${data_dir} - - ${csudo}rm -rf ${log_link_dir} || : - ${csudo}rm -rf ${data_link_dir} || : - - ${csudo}ln -s ${log_dir} ${log_link_dir} || : - ${csudo}ln -s ${data_dir} ${data_link_dir} || : - - # Install include, lib, binary and service - install_include - install_lib - install_bin - install_config - install_taosadapter_config - install_taosadapter_service - install_service - - # Ask if to start the service - #echo - #echo -e "\033[44;32;1mTDengine is installed successfully!${NC}" - echo - echo -e "${GREEN_DARK}To configure TDengine ${NC}: edit /etc/taos/taos.cfg" - if ((${service_mod}==0)); then - echo -e "${GREEN_DARK}To start TDengine ${NC}: ${csudo}systemctl start taosd${NC}" - elif ((${service_mod}==1)); then - echo -e "${GREEN_DARK}To start TDengine ${NC}: ${csudo}update-rc.d taosd default ${RED} for the first time${NC}" - echo -e " : ${csudo}service taosd start ${RED} after${NC}" - else - echo -e "${GREEN_DARK}To start TDengine ${NC}: ./taosd${NC}" - fi - - - - if [ ! -z "$firstEp" ]; then - tmpFqdn=${firstEp%%:*} - substr=":" - if [[ $firstEp =~ $substr ]];then - tmpPort=${firstEp#*:} - else - tmpPort="" - fi - if [[ "$tmpPort" != "" ]];then - echo -e "${GREEN_DARK}To access TDengine ${NC}: taos -h $tmpFqdn -P $tmpPort${GREEN_DARK} to login into cluster, then${NC}" - else - echo -e "${GREEN_DARK}To access TDengine ${NC}: taos -h $tmpFqdn${GREEN_DARK} to login into cluster, then${NC}" - fi - echo -e "${GREEN_DARK}execute ${NC}: create dnode 'newDnodeFQDN:port'; ${GREEN_DARK}to add this new node${NC}" - echo - elif [ ! -z "$serverFqdn" ]; then - echo -e "${GREEN_DARK}To access TDengine ${NC}: taos -h $serverFqdn${GREEN_DARK} to login into TDengine server${NC}" - echo - fi - echo - echo -e "\033[44;32;1mTDengine is installed successfully!${NC}" -} - - -## ==============================Main program starts from here============================ -serverFqdn=$(hostname) -install_TDengine +#!/bin/bash +# +# This file is used to install tdengine rpm package on centos systems. The operating system +# is required to use systemd to manage services at boot +# set -x + +iplist="" +serverFqdn="" + +# -----------------------Variables definition--------------------- +script_dir=$(dirname $(readlink -f "$0")) +# Dynamic directory +data_dir="/var/lib/taos" +log_dir="/var/log/taos" +data_link_dir="/usr/local/taos/data" +log_link_dir="/usr/local/taos/log" +install_main_dir="/usr/local/taos" + +# static directory +cfg_dir="/usr/local/taos/cfg" +bin_dir="/usr/local/taos/bin" +lib_dir="/usr/local/taos/driver" +init_d_dir="/usr/local/taos/init.d" +inc_dir="/usr/local/taos/include" + +cfg_install_dir="/etc/taos" +bin_link_dir="/usr/bin" +lib_link_dir="/usr/lib" +lib64_link_dir="/usr/lib64" +inc_link_dir="/usr/include" + +service_config_dir="/etc/systemd/system" + + +# Color setting +RED='\033[0;31m' +GREEN='\033[1;32m' +GREEN_DARK='\033[0;32m' +GREEN_UNDERLINE='\033[4;32m' +NC='\033[0m' + +csudo="" +if command -v sudo > /dev/null; then + csudo="sudo " +fi + +initd_mod=0 +service_mod=2 +if pidof systemd &> /dev/null; then + service_mod=0 +elif $(which service &> /dev/null); then + service_mod=1 + service_config_dir="/etc/init.d" + if $(which chkconfig &> /dev/null); then + initd_mod=1 + elif $(which insserv &> /dev/null); then + initd_mod=2 + elif $(which update-rc.d &> /dev/null); then + initd_mod=3 + else + service_mod=2 + fi +else + service_mod=2 +fi + +function kill_taosadapter() { +# ${csudo}pkill -f taosadapter || : + pid=$(ps -ef | grep "taosadapter" | grep -v "grep" | awk '{print $2}') + if [ -n "$pid" ]; then + ${csudo}kill -9 $pid || : + fi +} + +function kill_taosd() { +# ${csudo}pkill -f taosd || : + pid=$(ps -ef | grep "taosd" | grep -v "grep" | awk '{print $2}') + if [ -n "$pid" ]; then + ${csudo}kill -9 $pid || : + fi +} + +function install_include() { + ${csudo}rm -f ${inc_link_dir}/taos.h ${inc_link_dir}/taosdef.h ${inc_link_dir}/taoserror.h|| : + ${csudo}ln -s ${inc_dir}/taos.h ${inc_link_dir}/taos.h + ${csudo}ln -s ${inc_dir}/taosdef.h ${inc_link_dir}/taosdef.h + ${csudo}ln -s ${inc_dir}/taoserror.h ${inc_link_dir}/taoserror.h +} + +function install_lib() { + ${csudo}rm -f ${lib_link_dir}/libtaos* || : + ${csudo}rm -f ${lib64_link_dir}/libtaos* || : + + ${csudo}ln -s ${lib_dir}/libtaos.* ${lib_link_dir}/libtaos.so.1 + ${csudo}ln -s ${lib_link_dir}/libtaos.so.1 ${lib_link_dir}/libtaos.so + + if [[ -d ${lib64_link_dir} && ! -e ${lib64_link_dir}/libtaos.so ]]; then + ${csudo}ln -s ${lib_dir}/libtaos.* ${lib64_link_dir}/libtaos.so.1 || : + ${csudo}ln -s ${lib64_link_dir}/libtaos.so.1 ${lib64_link_dir}/libtaos.so || : + fi + + ${csudo}ldconfig +} + +function install_bin() { + # Remove links + ${csudo}rm -f ${bin_link_dir}/taos || : + ${csudo}rm -f ${bin_link_dir}/taosd || : + ${csudo}rm -f ${bin_link_dir}/taosadapter || : + ${csudo}rm -f ${bin_link_dir}/taosBenchmark || : + ${csudo}rm -f ${bin_link_dir}/taosdemo || : + ${csudo}rm -f ${bin_link_dir}/taosdump || : + ${csudo}rm -f ${bin_link_dir}/rmtaos || : + ${csudo}rm -f ${bin_link_dir}/set_core || : + + ${csudo}chmod 0555 ${bin_dir}/* + + #Make link + [ -x ${bin_dir}/taos ] && ${csudo}ln -s ${bin_dir}/taos ${bin_link_dir}/taos || : + [ -x ${bin_dir}/taosd ] && ${csudo}ln -s ${bin_dir}/taosd ${bin_link_dir}/taosd || : + [ -x ${bin_dir}/taosadapter ] && ${csudo}ln -s ${bin_dir}/taosadapter ${bin_link_dir}/taosadapter || : + [ -x ${bin_dir}/taosBenchmark ] && ${csudo}ln -sf ${bin_dir}/taosBenchmark ${bin_link_dir}/taosdemo || : + [ -x ${bin_dir}/TDinsight.sh ] && ${csudo}ln -sf ${bin_dir}/TDinsight.sh ${bin_link_dir}/TDinsight.sh || : + [ -x ${bin_dir}/taosdump ] && ${csudo}ln -s ${bin_dir}/taosdump ${bin_link_dir}/taosdump || : + [ -x ${bin_dir}/set_core.sh ] && ${csudo}ln -s ${bin_dir}/set_core.sh ${bin_link_dir}/set_core || : +} + +function add_newHostname_to_hosts() { + localIp="127.0.0.1" + OLD_IFS="$IFS" + IFS=" " + iphost=$(cat /etc/hosts | grep $1 | awk '{print $1}') + arr=($iphost) + IFS="$OLD_IFS" + for s in "${arr[@]}" + do + if [[ "$s" == "$localIp" ]]; then + return + fi + done + ${csudo}echo "127.0.0.1 $1" >> /etc/hosts ||: +} + +function set_hostname() { + echo -e -n "${GREEN}Please enter one hostname(must not be 'localhost')${NC}:" + read newHostname + while true; do + if [[ ! -z "$newHostname" && "$newHostname" != "localhost" ]]; then + break + else + read -p "Please enter one hostname(must not be 'localhost'):" newHostname + fi + done + + ${csudo}hostname $newHostname ||: + retval=`echo $?` + if [[ $retval != 0 ]]; then + echo + echo "set hostname fail!" + return + fi + #echo -e -n "$(hostnamectl status --static)" + #echo -e -n "$(hostnamectl status --transient)" + #echo -e -n "$(hostnamectl status --pretty)" + + #ubuntu/centos /etc/hostname + if [[ -e /etc/hostname ]]; then + ${csudo}echo $newHostname > /etc/hostname ||: + fi + + #debian: #HOSTNAME=yourname + if [[ -e /etc/sysconfig/network ]]; then + ${csudo}sed -i -r "s/#*\s*(HOSTNAME=\s*).*/\1$newHostname/" /etc/sysconfig/network ||: + fi + + ${csudo}sed -i -r "s/#*\s*(fqdn\s*).*/\1$newHostname/" ${cfg_install_dir}/taos.cfg + serverFqdn=$newHostname + + if [[ -e /etc/hosts ]]; then + add_newHostname_to_hosts $newHostname + fi +} + +function is_correct_ipaddr() { + newIp=$1 + OLD_IFS="$IFS" + IFS=" " + arr=($iplist) + IFS="$OLD_IFS" + for s in "${arr[@]}" + do + if [[ "$s" == "$newIp" ]]; then + return 0 + fi + done + + return 1 +} + +function set_ipAsFqdn() { + iplist=$(ip address |grep inet |grep -v inet6 |grep -v 127.0.0.1 |awk '{print $2}' |awk -F "/" '{print $1}') ||: + if [ -z "$iplist" ]; then + iplist=$(ifconfig |grep inet |grep -v inet6 |grep -v 127.0.0.1 |awk '{print $2}' |awk -F ":" '{print $2}') ||: + fi + + if [ -z "$iplist" ]; then + echo + echo -e -n "${GREEN}Unable to get local ip, use 127.0.0.1${NC}" + localFqdn="127.0.0.1" + # Write the local FQDN to configuration file + ${csudo}sed -i -r "s/#*\s*(fqdn\s*).*/\1$localFqdn/" ${cfg_install_dir}/taos.cfg + serverFqdn=$localFqdn + echo + return + fi + + echo -e -n "${GREEN}Please choose an IP from local IP list${NC}:" + echo + echo -e -n "${GREEN}$iplist${NC}" + echo + echo + echo -e -n "${GREEN}Notes: if IP is used as the node name, data can NOT be migrated to other machine directly${NC}:" + read localFqdn + while true; do + if [ ! -z "$localFqdn" ]; then + # Check if correct ip address + is_correct_ipaddr $localFqdn + retval=`echo $?` + if [[ $retval != 0 ]]; then + read -p "Please choose an IP from local IP list:" localFqdn + else + # Write the local FQDN to configuration file + ${csudo}sed -i -r "s/#*\s*(fqdn\s*).*/\1$localFqdn/" ${cfg_install_dir}/taos.cfg + serverFqdn=$localFqdn + break + fi + else + read -p "Please choose an IP from local IP list:" localFqdn + fi + done +} + +function local_fqdn_check() { + #serverFqdn=$(hostname) + echo + echo -e -n "System hostname is: ${GREEN}$serverFqdn${NC}" + echo + if [[ "$serverFqdn" == "" ]] || [[ "$serverFqdn" == "localhost" ]]; then + echo -e -n "${GREEN}It is strongly recommended to configure a hostname for this machine ${NC}" + echo + + while true + do + read -r -p "Set hostname now? [Y/n] " input + if [ ! -n "$input" ]; then + set_hostname + break + else + case $input in + [yY][eE][sS]|[yY]) + set_hostname + break + ;; + + [nN][oO]|[nN]) + set_ipAsFqdn + break + ;; + + *) + echo "Invalid input..." + ;; + esac + fi + done + fi +} + +function install_taosadapter_config() { + if [ ! -f "${cfg_install_dir}/taosadapter.toml" ]; then + [ ! -d %{cfg_install_dir} ] && + ${csudo}${csudo}mkdir -p ${cfg_install_dir} + [ -f ${cfg_dir}/taosadapter.toml ] && ${csudo}cp ${cfg_dir}/taosadapter.toml ${cfg_install_dir} + [ -f ${cfg_install_dir}/taosadapter.toml ] && + ${csudo}chmod 644 ${cfg_install_dir}/taosadapter.toml + fi + + [ -f ${cfg_dir}/taosadapter.toml ] && + ${csudo}mv ${cfg_dir}/taosadapter.toml ${cfg_dir}/taosadapter.toml.new + + [ -f ${cfg_install_dir}/taosadapter.toml ] && + ${csudo}ln -s ${cfg_install_dir}/taosadapter.toml ${cfg_dir} +} + +function install_config() { + if [ ! -f "${cfg_install_dir}/taos.cfg" ]; then + ${csudo}${csudo}mkdir -p ${cfg_install_dir} + [ -f ${cfg_dir}/taos.cfg ] && ${csudo}cp ${cfg_dir}/taos.cfg ${cfg_install_dir} + ${csudo}chmod 644 ${cfg_install_dir}/* + fi + + # Save standard input to 6 and open / dev / TTY on standard input + exec 6<&0 0 ${email_file}" + break + #else + # read -p "Please enter the correct email address: " emailAddr + #fi + else + break + fi + done +} + +function clean_service_on_sysvinit() { + #restart_config_str="taos:2345:respawn:${service_config_dir}/taosd start" + #${csudo}sed -i "\|${restart_config_str}|d" /etc/inittab || : + + if pidof taosd &> /dev/null; then + ${csudo}service taosd stop || : + fi + + if ((${initd_mod}==1)); then + ${csudo}chkconfig --del taosd || : + elif ((${initd_mod}==2)); then + ${csudo}insserv -r taosd || : + elif ((${initd_mod}==3)); then + ${csudo}update-rc.d -f taosd remove || : + fi + + ${csudo}rm -f ${service_config_dir}/taosd || : + + if $(which init &> /dev/null); then + ${csudo}init q || : + fi +} + +function install_service_on_sysvinit() { + clean_service_on_sysvinit + + sleep 1 + + # Install taosd service + ${csudo}cp %{init_d_dir}/taosd ${service_config_dir} && ${csudo}chmod a+x ${service_config_dir}/taosd + + #restart_config_str="taos:2345:respawn:${service_config_dir}/taosd start" + #${csudo}grep -q -F "$restart_config_str" /etc/inittab || ${csudo}bash -c "echo '${restart_config_str}' >> /etc/inittab" + + if ((${initd_mod}==1)); then + ${csudo}chkconfig --add taosd || : + ${csudo}chkconfig --level 2345 taosd on || : + elif ((${initd_mod}==2)); then + ${csudo}insserv taosd || : + ${csudo}insserv -d taosd || : + elif ((${initd_mod}==3)); then + ${csudo}update-rc.d taosd defaults || : + fi +} + +function clean_service_on_systemd() { + taosd_service_config="${service_config_dir}/taosd.service" + + # taosd service already is stoped before install in preinst script + #if systemctl is-active --quiet taosd; then + # echo "TDengine is running, stopping it..." + # ${csudo}systemctl stop taosd &> /dev/null || echo &> /dev/null + #fi + ${csudo}systemctl disable taosd &> /dev/null || echo &> /dev/null + + ${csudo}rm -f ${taosd_service_config} +} + +# taos:2345:respawn:/etc/init.d/taosd start + +function install_service_on_systemd() { + clean_service_on_systemd + + taosd_service_config="${service_config_dir}/taosd.service" + + ${csudo}bash -c "echo '[Unit]' >> ${taosd_service_config}" + ${csudo}bash -c "echo 'Description=TDengine server service' >> ${taosd_service_config}" + ${csudo}bash -c "echo 'After=network-online.target' >> ${taosd_service_config}" + ${csudo}bash -c "echo 'Wants=network-online.target' >> ${taosd_service_config}" + ${csudo}bash -c "echo >> ${taosd_service_config}" + ${csudo}bash -c "echo '[Service]' >> ${taosd_service_config}" + ${csudo}bash -c "echo 'Type=simple' >> ${taosd_service_config}" + ${csudo}bash -c "echo 'ExecStart=/usr/bin/taosd' >> ${taosd_service_config}" + ${csudo}bash -c "echo 'ExecStartPre=/usr/local/taos/bin/startPre.sh' >> ${taosd_service_config}" + ${csudo}bash -c "echo 'TimeoutStopSec=1000000s' >> ${taosd_service_config}" + ${csudo}bash -c "echo 'LimitNOFILE=infinity' >> ${taosd_service_config}" + ${csudo}bash -c "echo 'LimitNPROC=infinity' >> ${taosd_service_config}" + ${csudo}bash -c "echo 'LimitCORE=infinity' >> ${taosd_service_config}" + ${csudo}bash -c "echo 'TimeoutStartSec=0' >> ${taosd_service_config}" + ${csudo}bash -c "echo 'StandardOutput=null' >> ${taosd_service_config}" + ${csudo}bash -c "echo 'Restart=always' >> ${taosd_service_config}" + ${csudo}bash -c "echo 'StartLimitBurst=3' >> ${taosd_service_config}" + ${csudo}bash -c "echo 'StartLimitInterval=60s' >> ${taosd_service_config}" + ${csudo}bash -c "echo >> ${taosd_service_config}" + ${csudo}bash -c "echo '[Install]' >> ${taosd_service_config}" + ${csudo}bash -c "echo 'WantedBy=multi-user.target' >> ${taosd_service_config}" + ${csudo}systemctl enable taosd +} + +function install_taosadapter_service() { + if ((${service_mod}==0)); then + [ -f ${script_dir}/../cfg/taosadapter.service ] &&\ + ${csudo}cp ${script_dir}/../cfg/taosadapter.service \ + ${service_config_dir}/ || : + ${csudo}systemctl daemon-reload + fi +} + +function install_service() { + if ((${service_mod}==0)); then + install_service_on_systemd + elif ((${service_mod}==1)); then + install_service_on_sysvinit + else + # manual start taosd + kill_taosadapter + kill_taosd + fi +} + +function install_TDengine() { + echo -e "${GREEN}Start to install TDengine...${NC}" + + #install log and data dir , then ln to /usr/local/taos + ${csudo}mkdir -p ${log_dir} && ${csudo}chmod 777 ${log_dir} + ${csudo}mkdir -p ${data_dir} + + ${csudo}rm -rf ${log_link_dir} || : + ${csudo}rm -rf ${data_link_dir} || : + + ${csudo}ln -s ${log_dir} ${log_link_dir} || : + ${csudo}ln -s ${data_dir} ${data_link_dir} || : + + # Install include, lib, binary and service + install_include + install_lib + install_bin + install_config + install_taosadapter_config + install_taosadapter_service + install_service + + # Ask if to start the service + #echo + #echo -e "\033[44;32;1mTDengine is installed successfully!${NC}" + echo + echo -e "${GREEN_DARK}To configure TDengine ${NC}: edit /etc/taos/taos.cfg" + if ((${service_mod}==0)); then + echo -e "${GREEN_DARK}To start TDengine ${NC}: ${csudo}systemctl start taosd${NC}" + elif ((${service_mod}==1)); then + echo -e "${GREEN_DARK}To start TDengine ${NC}: ${csudo}update-rc.d taosd default ${RED} for the first time${NC}" + echo -e " : ${csudo}service taosd start ${RED} after${NC}" + else + echo -e "${GREEN_DARK}To start TDengine ${NC}: ./taosd${NC}" + fi + + + + if [ ! -z "$firstEp" ]; then + tmpFqdn=${firstEp%%:*} + substr=":" + if [[ $firstEp =~ $substr ]];then + tmpPort=${firstEp#*:} + else + tmpPort="" + fi + if [[ "$tmpPort" != "" ]];then + echo -e "${GREEN_DARK}To access TDengine ${NC}: taos -h $tmpFqdn -P $tmpPort${GREEN_DARK} to login into cluster, then${NC}" + else + echo -e "${GREEN_DARK}To access TDengine ${NC}: taos -h $tmpFqdn${GREEN_DARK} to login into cluster, then${NC}" + fi + echo -e "${GREEN_DARK}execute ${NC}: create dnode 'newDnodeFQDN:port'; ${GREEN_DARK}to add this new node${NC}" + echo + elif [ ! -z "$serverFqdn" ]; then + echo -e "${GREEN_DARK}To access TDengine ${NC}: taos -h $serverFqdn${GREEN_DARK} to login into TDengine server${NC}" + echo + fi + echo + echo -e "\033[44;32;1mTDengine is installed successfully!${NC}" +} + + +## ==============================Main program starts from here============================ +serverFqdn=$(hostname) +install_TDengine diff --git a/packaging/tools/preun.sh b/packaging/tools/preun.sh old mode 100644 new mode 100755 index bc1727c4e470a25f99f248bcb370834415f375f5..2f35e41a48a438d86a7dc6ca71511ce967ba7ebf --- a/packaging/tools/preun.sh +++ b/packaging/tools/preun.sh @@ -1,142 +1,142 @@ -#!/bin/bash -# -# Script to stop the service and uninstall TSDB - -RED='\033[0;31m' -GREEN='\033[1;32m' -NC='\033[0m' - -bin_link_dir="/usr/bin" -lib_link_dir="/usr/lib" -lib64_link_dir="/usr/lib64" -inc_link_dir="/usr/include" - -data_link_dir="/usr/local/taos/data" -log_link_dir="/usr/local/taos/log" -cfg_link_dir="/usr/local/taos/cfg" - -service_config_dir="/etc/systemd/system" -taos_service_name="taosd" - -csudo="" -if command -v sudo > /dev/null; then - csudo="sudo " -fi - -initd_mod=0 -service_mod=2 -if pidof systemd &> /dev/null; then - service_mod=0 -elif $(which service &> /dev/null); then - service_mod=1 - service_config_dir="/etc/init.d" - if $(which chkconfig &> /dev/null); then - initd_mod=1 - elif $(which insserv &> /dev/null); then - initd_mod=2 - elif $(which update-rc.d &> /dev/null); then - initd_mod=3 - else - service_mod=2 - fi -else - service_mod=2 -fi - -function kill_taosadapter() { - pid=$(ps -ef | grep "taosadapter" | grep -v "grep" | awk '{print $2}') - if [ -n "$pid" ]; then - ${csudo}kill -9 $pid || : - fi -} - -function kill_taosd() { - pid=$(ps -ef | grep "taosd" | grep -v "grep" | awk '{print $2}') - if [ -n "$pid" ]; then - ${csudo}kill -9 $pid || : - fi -} - -function clean_service_on_systemd() { - taosadapter_service_config="${service_config_dir}/taosadapter.service" - if systemctl is-active --quiet taosadapter; then - echo "taosadapter is running, stopping it..." - ${csudo}systemctl stop taosadapter &> /dev/null || echo &> /dev/null - fi - - taosd_service_config="${service_config_dir}/${taos_service_name}.service" - - if systemctl is-active --quiet ${taos_service_name}; then - echo "TDengine taosd is running, stopping it..." - ${csudo}systemctl stop ${taos_service_name} &> /dev/null || echo &> /dev/null - fi - ${csudo}systemctl disable ${taos_service_name} &> /dev/null || echo &> /dev/null - - ${csudo}rm -f ${taosd_service_config} - - [ -f ${taosadapter_service_config} ] && ${csudo}rm -f ${taosadapter_service_config} - -} - -function clean_service_on_sysvinit() { - #restart_config_str="taos:2345:respawn:${service_config_dir}/taosd start" - #${csudo}sed -i "\|${restart_config_str}|d" /etc/inittab || : - - if pidof taosd &> /dev/null; then - echo "TDengine taosd is running, stopping it..." - ${csudo}service taosd stop || : - fi - - if ((${initd_mod}==1)); then - ${csudo}chkconfig --del taosd || : - elif ((${initd_mod}==2)); then - ${csudo}insserv -r taosd || : - elif ((${initd_mod}==3)); then - ${csudo}update-rc.d -f taosd remove || : - fi - - ${csudo}rm -f ${service_config_dir}/taosd || : - - if $(which init &> /dev/null); then - ${csudo}init q || : - fi -} - -function clean_service() { - if ((${service_mod}==0)); then - clean_service_on_systemd - elif ((${service_mod}==1)); then - clean_service_on_sysvinit - else - # must manual stop taosd - kill_taosadapter - kill_taosd - fi -} - -# Stop service and disable booting start. -clean_service - -# Remove all links -${csudo}rm -f ${bin_link_dir}/taos || : -${csudo}rm -f ${bin_link_dir}/taosd || : -${csudo}rm -f ${bin_link_dir}/taosadapter || : -${csudo}rm -f ${bin_link_dir}/taosBenchmark || : -${csudo}rm -f ${bin_link_dir}/taosdemo || : -${csudo}rm -f ${bin_link_dir}/set_core || : -${csudo}rm -f ${cfg_link_dir}/*.new || : -${csudo}rm -f ${inc_link_dir}/taos.h || : -${csudo}rm -f ${inc_link_dir}/taosdef.h || : -${csudo}rm -f ${inc_link_dir}/taoserror.h || : -${csudo}rm -f ${lib_link_dir}/libtaos.* || : -${csudo}rm -f ${lib64_link_dir}/libtaos.* || : - -${csudo}rm -f ${log_link_dir} || : -${csudo}rm -f ${data_link_dir} || : - -if ((${service_mod}==2)); then - kill_taosadapter - kill_taosd -fi - -echo -e "${GREEN}TDengine is removed successfully!${NC}" +#!/bin/bash +# +# Script to stop the service and uninstall TSDB + +RED='\033[0;31m' +GREEN='\033[1;32m' +NC='\033[0m' + +bin_link_dir="/usr/bin" +lib_link_dir="/usr/lib" +lib64_link_dir="/usr/lib64" +inc_link_dir="/usr/include" + +data_link_dir="/usr/local/taos/data" +log_link_dir="/usr/local/taos/log" +cfg_link_dir="/usr/local/taos/cfg" + +service_config_dir="/etc/systemd/system" +taos_service_name="taosd" + +csudo="" +if command -v sudo > /dev/null; then + csudo="sudo " +fi + +initd_mod=0 +service_mod=2 +if pidof systemd &> /dev/null; then + service_mod=0 +elif $(which service &> /dev/null); then + service_mod=1 + service_config_dir="/etc/init.d" + if $(which chkconfig &> /dev/null); then + initd_mod=1 + elif $(which insserv &> /dev/null); then + initd_mod=2 + elif $(which update-rc.d &> /dev/null); then + initd_mod=3 + else + service_mod=2 + fi +else + service_mod=2 +fi + +function kill_taosadapter() { + pid=$(ps -ef | grep "taosadapter" | grep -v "grep" | awk '{print $2}') + if [ -n "$pid" ]; then + ${csudo}kill -9 $pid || : + fi +} + +function kill_taosd() { + pid=$(ps -ef | grep "taosd" | grep -v "grep" | awk '{print $2}') + if [ -n "$pid" ]; then + ${csudo}kill -9 $pid || : + fi +} + +function clean_service_on_systemd() { + taosadapter_service_config="${service_config_dir}/taosadapter.service" + if systemctl is-active --quiet taosadapter; then + echo "taosadapter is running, stopping it..." + ${csudo}systemctl stop taosadapter &> /dev/null || echo &> /dev/null + fi + + taosd_service_config="${service_config_dir}/${taos_service_name}.service" + + if systemctl is-active --quiet ${taos_service_name}; then + echo "TDengine taosd is running, stopping it..." + ${csudo}systemctl stop ${taos_service_name} &> /dev/null || echo &> /dev/null + fi + ${csudo}systemctl disable ${taos_service_name} &> /dev/null || echo &> /dev/null + + ${csudo}rm -f ${taosd_service_config} + + [ -f ${taosadapter_service_config} ] && ${csudo}rm -f ${taosadapter_service_config} + +} + +function clean_service_on_sysvinit() { + #restart_config_str="taos:2345:respawn:${service_config_dir}/taosd start" + #${csudo}sed -i "\|${restart_config_str}|d" /etc/inittab || : + + if pidof taosd &> /dev/null; then + echo "TDengine taosd is running, stopping it..." + ${csudo}service taosd stop || : + fi + + if ((${initd_mod}==1)); then + ${csudo}chkconfig --del taosd || : + elif ((${initd_mod}==2)); then + ${csudo}insserv -r taosd || : + elif ((${initd_mod}==3)); then + ${csudo}update-rc.d -f taosd remove || : + fi + + ${csudo}rm -f ${service_config_dir}/taosd || : + + if $(which init &> /dev/null); then + ${csudo}init q || : + fi +} + +function clean_service() { + if ((${service_mod}==0)); then + clean_service_on_systemd + elif ((${service_mod}==1)); then + clean_service_on_sysvinit + else + # must manual stop taosd + kill_taosadapter + kill_taosd + fi +} + +# Stop service and disable booting start. +clean_service + +# Remove all links +${csudo}rm -f ${bin_link_dir}/taos || : +${csudo}rm -f ${bin_link_dir}/taosd || : +${csudo}rm -f ${bin_link_dir}/taosadapter || : +${csudo}rm -f ${bin_link_dir}/taosBenchmark || : +${csudo}rm -f ${bin_link_dir}/taosdemo || : +${csudo}rm -f ${bin_link_dir}/set_core || : +${csudo}rm -f ${cfg_link_dir}/*.new || : +${csudo}rm -f ${inc_link_dir}/taos.h || : +${csudo}rm -f ${inc_link_dir}/taosdef.h || : +${csudo}rm -f ${inc_link_dir}/taoserror.h || : +${csudo}rm -f ${lib_link_dir}/libtaos.* || : +${csudo}rm -f ${lib64_link_dir}/libtaos.* || : + +${csudo}rm -f ${log_link_dir} || : +${csudo}rm -f ${data_link_dir} || : + +if ((${service_mod}==2)); then + kill_taosadapter + kill_taosd +fi + +echo -e "${GREEN}TDengine is removed successfully!${NC}" diff --git a/packaging/tools/release_note b/packaging/tools/release_note index 4a954b1f1249f3eb90fd7a021a05478ee5465b65..4578a4523c50f3b0764ce05add5920cf0be72172 100644 --- a/packaging/tools/release_note +++ b/packaging/tools/release_note @@ -1,136 +1,136 @@ -taos-1.6.4.0 (Release on 2019-12-01) -Bug fixed: - 1.Look for possible causes of file corruption and fix them - 2.Encapsulate memory allocation functions to reduce the possibility of crashes - 3.Increase Arm64 compilation options - 4.Remove most of the warnings in the code - 5.Provide a variety of connector usage documents - 6.Network connection can be selected in udp and tcp - 7.Allow the maximum number of Tags to be 32 - 8.Bugs reported by the user - -taos-1.5.2.6 (Release on 2019-05-13) -Bug fixed: - - Nchar strings sometimes were wrongly truncated on Window - - Importing data from file throws an error of "invalid SQL" - -taos-1.5.2.5 (Release on 2019-05-13) -Bug fixed: - - Long timespan data import sometimes affects query result - - Synchronzation of cluster dnodes worked incorrectly when importing - -taos-1.5.2.4 (Release on 2019-05-10) -New Features: - - Optimized Windows client installation: now users don't need to copy taos.dll manually - - Changed the priority of taos.cfg and JDBC URL: parameters in JDCB URL now has a higher priority than parameters in taos.cfg -Bug fixed: - - Expired data files were not deleted corrected - - Occasionally importing returned "affected rows" which larger than 0, but 0 row was actually written into db - - Commit log is occupied by too many import-to-file requests, which blocked further data importing - - Cloud service shows a wrong number of available days with current balance - - Other minor issues - -taos-1.5.1 (Release on 2019-04-09) -New Features: - - Maximum number of rows returned by "top/bottom" methods increased from 20 to 100 - - Improved the performance of "first/last" methods - - Increased system stability -Bug fixed: - - Connection failure when query on huge STables through TPC - - The primary timestamp is occasionally returned as NULL in some queries - - Operation failure when updating a tag value to NULL - - Stream calculation couldn't start at certain occasions - -taos-1.5.0 (Release on 2019-03-11) -New Features: - - New syntax to automatically create tables when inserting values into non-existing tables - - New syntax "slimit/soffset" to pagenate groups in a query result set - - Support "top/bottom" queries on a supertable - - High performance statistic aggregation function "apercentile" - - Remove "first_t/last_t" functions; improve the performance of "first/last" function - - Add pre-aggregation for bool type values - - Supports fixed-length streaming computation, i.e. users may define an end time for a stream - - New JAVA API for SQL subscription, supports table/supertable/SQL query subscription -Bug fixed: - - Data file broken issue when frequently using "import" - - Using "spread" on a super table may return negative values - - RPC bug that random network packets might cause the RPC module to crush - -taos-1.4.15 (Released on 2019-01-23) -New Features: - - JDBC Driver now supports configuring timezone, locale, cfgdir in JDBC url - - A new API is added to validate if a table creation sql statement is correct in syntax without actually creating that table -Bugs Fixed: - - "select last(*) from STable" sometimest returned incorrect number of rows - - JDBC driver method ResultSetMetaData.getColumnClassName() returned wrong values. - - Web shell automatically changed query string to lower case - -taos-1.4.14 (Released on 2018-12-22) -New Features: - - C Driver support for integration with Python - - JDBC Driver support for integration with R and MATLAB - -taos-1.4.13 (Released on 2018-12-14) -Bugs Fixed: - - Clients failed to connect to server due to unexpected and invalid packets recieved by the server. -Features Added: - - Add support to HikariCP in TSDB JDBC driver. - -taos-1.4.12 (Released on 2018-12-08) -Bugs Fixed: - - Querying data while inserting into the database might return incomplete resultsets. -Features Added: - - A new python driver is added. - - Increased system stability. - - Changed meaning of database configuration paramerter 'ablocks'. 'ablocks' used to refer to the number of total cache blocks in memory, now it refers to average number of cache blocks for each table in memory. - -taos-1.4.11 (Released on 2018-11-23) -Bugs Fixed: - - Thread memory leaking during high-frequency committing. - - Master dnode selection failure caused by accidental network issues. -Features Added: - - Change keyword "metrics" to "stables", i.e. supertables; the previous query "show metrics" is now changed to "show stables". - - Add an error message mechanism in C# driver. An error with message "Failed to connect to server" is thrown when fetching data experienced a network connection interruption during data transmitting. - -taos-1.4.10 (Released on 2018-11-13) -Bugs Fixed: - - Taosdump failed while exporting extremely large datasets to a .sql file. - - Commit status did not change correctly if the last commit was triggered by commit threshold time (ctime) and no more new data was written to DB during the next ctime period. -Features Added: - - Support importing historical data from Telegraf interface. - - Support MyBatis framework in TSDB JDBC Driver. - - Change result set row indexing in JDBC Driver. Result set row indexes now starts from 1 instead of 0. - -taos-1.4.9 (Released on 2018-11-02) -Bugs Fixed: - - Dumping data using UTF-8 format in client shell failed. - - Tag query failed using C# Driver. - - Committing data to disk failed if DB files were corrupted. - - Continuously pressing Ctrl+c in client shell for multiple times produced a segmentation fault. -Features Added: - - Changed the display pattern in shell for taosdump. - - Add a check to the status of an existing resultset before firing a new query in a single JDBC connection. A connection can only have a single open resultset, and the resultset must be closed before one can execute new queries. - - -taos-1.4.7 (Released on 2018-10-25) -Bug Fixed: - - UTF-8 encoding in JDBC Driver did not give the correct Chinese characters. - - Fix crash error when where clause is too long. -Features Added: - - Add check on database properties, force ablocks to be at least (4 * tables) in a vnode. - - Check if pVgroup is empty in sdb. - -taos-1.4.6 (Released on 2018-10-21) -Bug Fixed: - - Fix wrong symbol addition while export csv file. -Features Added: - - Update grafana plugins. - - Update python drivers. - - Add error code explanation in JDBC Driver. - - Prohibit login while the version of server and client are not match. - -taos-1.4.5 (Released on 2018-10-17) -Bug Fixed: - - Fix HTTP request truncation bug in Telegraf interface. -Features Added: - - Support nchar and null object in JDBC Driver. +taos-1.6.4.0 (Release on 2019-12-01) +Bug fixed: + 1.Look for possible causes of file corruption and fix them + 2.Encapsulate memory allocation functions to reduce the possibility of crashes + 3.Increase Arm64 compilation options + 4.Remove most of the warnings in the code + 5.Provide a variety of connector usage documents + 6.Network connection can be selected in udp and tcp + 7.Allow the maximum number of Tags to be 32 + 8.Bugs reported by the user + +taos-1.5.2.6 (Release on 2019-05-13) +Bug fixed: + - Nchar strings sometimes were wrongly truncated on Window + - Importing data from file throws an error of "invalid SQL" + +taos-1.5.2.5 (Release on 2019-05-13) +Bug fixed: + - Long timespan data import sometimes affects query result + - Synchronzation of cluster dnodes worked incorrectly when importing + +taos-1.5.2.4 (Release on 2019-05-10) +New Features: + - Optimized Windows client installation: now users don't need to copy taos.dll manually + - Changed the priority of taos.cfg and JDBC URL: parameters in JDCB URL now has a higher priority than parameters in taos.cfg +Bug fixed: + - Expired data files were not deleted corrected + - Occasionally importing returned "affected rows" which larger than 0, but 0 row was actually written into db + - Commit log is occupied by too many import-to-file requests, which blocked further data importing + - Cloud service shows a wrong number of available days with current balance + - Other minor issues + +taos-1.5.1 (Release on 2019-04-09) +New Features: + - Maximum number of rows returned by "top/bottom" methods increased from 20 to 100 + - Improved the performance of "first/last" methods + - Increased system stability +Bug fixed: + - Connection failure when query on huge STables through TPC + - The primary timestamp is occasionally returned as NULL in some queries + - Operation failure when updating a tag value to NULL + - Stream calculation couldn't start at certain occasions + +taos-1.5.0 (Release on 2019-03-11) +New Features: + - New syntax to automatically create tables when inserting values into non-existing tables + - New syntax "slimit/soffset" to pagenate groups in a query result set + - Support "top/bottom" queries on a supertable + - High performance statistic aggregation function "apercentile" + - Remove "first_t/last_t" functions; improve the performance of "first/last" function + - Add pre-aggregation for bool type values + - Supports fixed-length streaming computation, i.e. users may define an end time for a stream + - New JAVA API for SQL subscription, supports table/supertable/SQL query subscription +Bug fixed: + - Data file broken issue when frequently using "import" + - Using "spread" on a super table may return negative values + - RPC bug that random network packets might cause the RPC module to crush + +taos-1.4.15 (Released on 2019-01-23) +New Features: + - JDBC Driver now supports configuring timezone, locale, cfgdir in JDBC url + - A new API is added to validate if a table creation sql statement is correct in syntax without actually creating that table +Bugs Fixed: + - "select last(*) from STable" sometimest returned incorrect number of rows + - JDBC driver method ResultSetMetaData.getColumnClassName() returned wrong values. + - Web shell automatically changed query string to lower case + +taos-1.4.14 (Released on 2018-12-22) +New Features: + - C Driver support for integration with Python + - JDBC Driver support for integration with R and MATLAB + +taos-1.4.13 (Released on 2018-12-14) +Bugs Fixed: + - Clients failed to connect to server due to unexpected and invalid packets recieved by the server. +Features Added: + - Add support to HikariCP in TSDB JDBC driver. + +taos-1.4.12 (Released on 2018-12-08) +Bugs Fixed: + - Querying data while inserting into the database might return incomplete resultsets. +Features Added: + - A new python driver is added. + - Increased system stability. + - Changed meaning of database configuration paramerter 'ablocks'. 'ablocks' used to refer to the number of total cache blocks in memory, now it refers to average number of cache blocks for each table in memory. + +taos-1.4.11 (Released on 2018-11-23) +Bugs Fixed: + - Thread memory leaking during high-frequency committing. + - Master dnode selection failure caused by accidental network issues. +Features Added: + - Change keyword "metrics" to "stables", i.e. supertables; the previous query "show metrics" is now changed to "show stables". + - Add an error message mechanism in C# driver. An error with message "Failed to connect to server" is thrown when fetching data experienced a network connection interruption during data transmitting. + +taos-1.4.10 (Released on 2018-11-13) +Bugs Fixed: + - Taosdump failed while exporting extremely large datasets to a .sql file. + - Commit status did not change correctly if the last commit was triggered by commit threshold time (ctime) and no more new data was written to DB during the next ctime period. +Features Added: + - Support importing historical data from Telegraf interface. + - Support MyBatis framework in TSDB JDBC Driver. + - Change result set row indexing in JDBC Driver. Result set row indexes now starts from 1 instead of 0. + +taos-1.4.9 (Released on 2018-11-02) +Bugs Fixed: + - Dumping data using UTF-8 format in client shell failed. + - Tag query failed using C# Driver. + - Committing data to disk failed if DB files were corrupted. + - Continuously pressing Ctrl+c in client shell for multiple times produced a segmentation fault. +Features Added: + - Changed the display pattern in shell for taosdump. + - Add a check to the status of an existing resultset before firing a new query in a single JDBC connection. A connection can only have a single open resultset, and the resultset must be closed before one can execute new queries. + + +taos-1.4.7 (Released on 2018-10-25) +Bug Fixed: + - UTF-8 encoding in JDBC Driver did not give the correct Chinese characters. + - Fix crash error when where clause is too long. +Features Added: + - Add check on database properties, force ablocks to be at least (4 * tables) in a vnode. + - Check if pVgroup is empty in sdb. + +taos-1.4.6 (Released on 2018-10-21) +Bug Fixed: + - Fix wrong symbol addition while export csv file. +Features Added: + - Update grafana plugins. + - Update python drivers. + - Add error code explanation in JDBC Driver. + - Prohibit login while the version of server and client are not match. + +taos-1.4.5 (Released on 2018-10-17) +Bug Fixed: + - Fix HTTP request truncation bug in Telegraf interface. +Features Added: + - Support nchar and null object in JDBC Driver. diff --git a/packaging/tools/remove.sh b/packaging/tools/remove.sh old mode 100644 new mode 100755 diff --git a/packaging/tools/remove_arbi.sh b/packaging/tools/remove_arbi.sh old mode 100644 new mode 100755 index 9b8e49d511d2e93a967b1397ce9a4de670422466..0a1162cd7a6793d8542ad5079b8c8cce1659724a --- a/packaging/tools/remove_arbi.sh +++ b/packaging/tools/remove_arbi.sh @@ -1,130 +1,130 @@ -#!/bin/bash -# -# Script to stop the service and uninstall TDengine's arbitrator - -set -e -#set -x - -verMode=edge - -RED='\033[0;31m' -GREEN='\033[1;32m' -NC='\033[0m' - -#install main path -install_main_dir="/usr/local/tarbitrator" -bin_link_dir="/usr/bin" -#inc_link_dir="/usr/include" - -service_config_dir="/etc/systemd/system" -tarbitrator_service_name="tarbitratord" -csudo="" -if command -v sudo > /dev/null; then - csudo="sudo " -fi - -initd_mod=0 -service_mod=2 -if pidof systemd &> /dev/null; then - service_mod=0 -elif $(which service &> /dev/null); then - service_mod=1 - service_config_dir="/etc/init.d" - if $(which chkconfig &> /dev/null); then - initd_mod=1 - elif $(which insserv &> /dev/null); then - initd_mod=2 - elif $(which update-rc.d &> /dev/null); then - initd_mod=3 - else - service_mod=2 - fi -else - service_mod=2 -fi - -function kill_tarbitrator() { - pid=$(ps -ef | grep "tarbitrator" | grep -v "grep" | awk '{print $2}') - if [ -n "$pid" ]; then - ${csudo}kill -9 $pid || : - fi -} -function clean_bin() { - # Remove link - ${csudo}rm -f ${bin_link_dir}/tarbitrator || : -} - -function clean_header() { - # Remove link - ${csudo}rm -f ${inc_link_dir}/taos.h || : - ${csudo}rm -f ${inc_link_dir}/taosdef.h || : - ${csudo}rm -f ${inc_link_dir}/taoserror.h || : -} - -function clean_log() { - # Remove link - ${csudo}rm -rf /arbitrator.log || : -} - -function clean_service_on_systemd() { - tarbitratord_service_config="${service_config_dir}/${tarbitrator_service_name}.service" - - if systemctl is-active --quiet ${tarbitrator_service_name}; then - echo "TDengine tarbitrator is running, stopping it..." - ${csudo}systemctl stop ${tarbitrator_service_name} &> /dev/null || echo &> /dev/null - fi - ${csudo}systemctl disable ${tarbitrator_service_name} &> /dev/null || echo &> /dev/null - - ${csudo}rm -f ${tarbitratord_service_config} -} - -function clean_service_on_sysvinit() { - if pidof tarbitrator &> /dev/null; then - echo "TDengine's tarbitrator is running, stopping it..." - ${csudo}service tarbitratord stop || : - fi - - if ((${initd_mod}==1)); then - if [ -e ${service_config_dir}/tarbitratord ]; then - ${csudo}chkconfig --del tarbitratord || : - fi - elif ((${initd_mod}==2)); then - if [ -e ${service_config_dir}/tarbitratord ]; then - ${csudo}insserv -r tarbitratord || : - fi - elif ((${initd_mod}==3)); then - if [ -e ${service_config_dir}/tarbitratord ]; then - ${csudo}update-rc.d -f tarbitratord remove || : - fi - fi - - ${csudo}rm -f ${service_config_dir}/tarbitratord || : - - if $(which init &> /dev/null); then - ${csudo}init q || : - fi -} - -function clean_service() { - if ((${service_mod}==0)); then - clean_service_on_systemd - elif ((${service_mod}==1)); then - clean_service_on_sysvinit - else - # must manual stop - kill_tarbitrator - fi -} - -# Stop service and disable booting start. -clean_service -# Remove binary file and links -clean_bin -# Remove header file. -##clean_header -# Remove log file -clean_log - -${csudo}rm -rf ${install_main_dir} - -echo -e "${GREEN}TDengine's arbitrator is removed successfully!${NC}" +#!/bin/bash +# +# Script to stop the service and uninstall TDengine's arbitrator + +set -e +#set -x + +verMode=edge + +RED='\033[0;31m' +GREEN='\033[1;32m' +NC='\033[0m' + +#install main path +install_main_dir="/usr/local/tarbitrator" +bin_link_dir="/usr/bin" +#inc_link_dir="/usr/include" + +service_config_dir="/etc/systemd/system" +tarbitrator_service_name="tarbitratord" +csudo="" +if command -v sudo > /dev/null; then + csudo="sudo " +fi + +initd_mod=0 +service_mod=2 +if pidof systemd &> /dev/null; then + service_mod=0 +elif $(which service &> /dev/null); then + service_mod=1 + service_config_dir="/etc/init.d" + if $(which chkconfig &> /dev/null); then + initd_mod=1 + elif $(which insserv &> /dev/null); then + initd_mod=2 + elif $(which update-rc.d &> /dev/null); then + initd_mod=3 + else + service_mod=2 + fi +else + service_mod=2 +fi + +function kill_tarbitrator() { + pid=$(ps -ef | grep "tarbitrator" | grep -v "grep" | awk '{print $2}') + if [ -n "$pid" ]; then + ${csudo}kill -9 $pid || : + fi +} +function clean_bin() { + # Remove link + ${csudo}rm -f ${bin_link_dir}/tarbitrator || : +} + +function clean_header() { + # Remove link + ${csudo}rm -f ${inc_link_dir}/taos.h || : + ${csudo}rm -f ${inc_link_dir}/taosdef.h || : + ${csudo}rm -f ${inc_link_dir}/taoserror.h || : +} + +function clean_log() { + # Remove link + ${csudo}rm -rf /arbitrator.log || : +} + +function clean_service_on_systemd() { + tarbitratord_service_config="${service_config_dir}/${tarbitrator_service_name}.service" + + if systemctl is-active --quiet ${tarbitrator_service_name}; then + echo "TDengine tarbitrator is running, stopping it..." + ${csudo}systemctl stop ${tarbitrator_service_name} &> /dev/null || echo &> /dev/null + fi + ${csudo}systemctl disable ${tarbitrator_service_name} &> /dev/null || echo &> /dev/null + + ${csudo}rm -f ${tarbitratord_service_config} +} + +function clean_service_on_sysvinit() { + if pidof tarbitrator &> /dev/null; then + echo "TDengine's tarbitrator is running, stopping it..." + ${csudo}service tarbitratord stop || : + fi + + if ((${initd_mod}==1)); then + if [ -e ${service_config_dir}/tarbitratord ]; then + ${csudo}chkconfig --del tarbitratord || : + fi + elif ((${initd_mod}==2)); then + if [ -e ${service_config_dir}/tarbitratord ]; then + ${csudo}insserv -r tarbitratord || : + fi + elif ((${initd_mod}==3)); then + if [ -e ${service_config_dir}/tarbitratord ]; then + ${csudo}update-rc.d -f tarbitratord remove || : + fi + fi + + ${csudo}rm -f ${service_config_dir}/tarbitratord || : + + if $(which init &> /dev/null); then + ${csudo}init q || : + fi +} + +function clean_service() { + if ((${service_mod}==0)); then + clean_service_on_systemd + elif ((${service_mod}==1)); then + clean_service_on_sysvinit + else + # must manual stop + kill_tarbitrator + fi +} + +# Stop service and disable booting start. +clean_service +# Remove binary file and links +clean_bin +# Remove header file. +##clean_header +# Remove log file +clean_log + +${csudo}rm -rf ${install_main_dir} + +echo -e "${GREEN}TDengine's arbitrator is removed successfully!${NC}" diff --git a/packaging/tools/remove_client.sh b/packaging/tools/remove_client.sh old mode 100644 new mode 100755 index 9e22f894ac99c9930a35ddeb1c4994a469da5a98..f2cbccb45f738c058236e5625a86fc40c161f488 --- a/packaging/tools/remove_client.sh +++ b/packaging/tools/remove_client.sh @@ -1,85 +1,85 @@ -#!/bin/bash -# -# Script to stop the client and uninstall database, but retain the config and log files. -set -e -# set -x - -RED='\033[0;31m' -GREEN='\033[1;32m' -NC='\033[0m' - -installDir="/usr/local/taos" -clientName="taos" -uninstallScript="rmtaos" - -#install main path -install_main_dir=${installDir} - -log_link_dir=${installDir}/log -cfg_link_dir=${installDir}/cfg -bin_link_dir="/usr/bin" -lib_link_dir="/usr/lib" -lib64_link_dir="/usr/lib64" -inc_link_dir="/usr/include" - -csudo="" -if command -v sudo > /dev/null; then - csudo="sudo " -fi - -function kill_client() { - if [ -n "$(pidof ${clientName})" ]; then - ${csudo}kill -9 $pid || : - fi -} - -function clean_bin() { - # Remove link - ${csudo}rm -f ${bin_link_dir}/${clientName} || : - ${csudo}rm -f ${bin_link_dir}/taosdemo || : - ${csudo}rm -f ${bin_link_dir}/taosdump || : - ${csudo}rm -f ${bin_link_dir}/${uninstallScript} || : - ${csudo}rm -f ${bin_link_dir}/set_core || : -} - -function clean_lib() { - # Remove link - ${csudo}rm -f ${lib_link_dir}/libtaos.* || : - ${csudo}rm -f ${lib64_link_dir}/libtaos.* || : - #${csudo}rm -rf ${v15_java_app_dir} || : -} - -function clean_header() { - # Remove link - ${csudo}rm -f ${inc_link_dir}/taos.h || : - ${csudo}rm -f ${inc_link_dir}/taosdef.h || : - ${csudo}rm -f ${inc_link_dir}/taoserror.h || : -} - -function clean_config() { - # Remove link - ${csudo}rm -f ${cfg_link_dir}/* || : -} - -function clean_log() { - # Remove link - ${csudo}rm -rf ${log_link_dir} || : -} - -# Stop client. -kill_client -# Remove binary file and links -clean_bin -# Remove header file. -clean_header -# Remove lib file -clean_lib -# Remove link log directory -clean_log -# Remove link configuration file -clean_config - -${csudo}rm -rf ${install_main_dir} - -echo -e "${GREEN}TDengine client is removed successfully!${NC}" -echo +#!/bin/bash +# +# Script to stop the client and uninstall database, but retain the config and log files. +set -e +# set -x + +RED='\033[0;31m' +GREEN='\033[1;32m' +NC='\033[0m' + +installDir="/usr/local/taos" +clientName="taos" +uninstallScript="rmtaos" + +#install main path +install_main_dir=${installDir} + +log_link_dir=${installDir}/log +cfg_link_dir=${installDir}/cfg +bin_link_dir="/usr/bin" +lib_link_dir="/usr/lib" +lib64_link_dir="/usr/lib64" +inc_link_dir="/usr/include" + +csudo="" +if command -v sudo > /dev/null; then + csudo="sudo " +fi + +function kill_client() { + if [ -n "$(pidof ${clientName})" ]; then + ${csudo}kill -9 $pid || : + fi +} + +function clean_bin() { + # Remove link + ${csudo}rm -f ${bin_link_dir}/${clientName} || : + ${csudo}rm -f ${bin_link_dir}/taosdemo || : + ${csudo}rm -f ${bin_link_dir}/taosdump || : + ${csudo}rm -f ${bin_link_dir}/${uninstallScript} || : + ${csudo}rm -f ${bin_link_dir}/set_core || : +} + +function clean_lib() { + # Remove link + ${csudo}rm -f ${lib_link_dir}/libtaos.* || : + ${csudo}rm -f ${lib64_link_dir}/libtaos.* || : + #${csudo}rm -rf ${v15_java_app_dir} || : +} + +function clean_header() { + # Remove link + ${csudo}rm -f ${inc_link_dir}/taos.h || : + ${csudo}rm -f ${inc_link_dir}/taosdef.h || : + ${csudo}rm -f ${inc_link_dir}/taoserror.h || : +} + +function clean_config() { + # Remove link + ${csudo}rm -f ${cfg_link_dir}/* || : +} + +function clean_log() { + # Remove link + ${csudo}rm -rf ${log_link_dir} || : +} + +# Stop client. +kill_client +# Remove binary file and links +clean_bin +# Remove header file. +clean_header +# Remove lib file +clean_lib +# Remove link log directory +clean_log +# Remove link configuration file +clean_config + +${csudo}rm -rf ${install_main_dir} + +echo -e "${GREEN}TDengine client is removed successfully!${NC}" +echo diff --git a/packaging/tools/repair_link.sh b/packaging/tools/repair_link.sh old mode 100644 new mode 100755 index 8507b639ba80dc062b7c859b4b166a637d29c031..7fd503f27013a9fce7208ece4335a1f427e05c9d --- a/packaging/tools/repair_link.sh +++ b/packaging/tools/repair_link.sh @@ -1,39 +1,39 @@ -#!/bin/bash - -# This script is used to repaire links when you what to move TDengine -# data to other places and to access data. - -# Read link path -read -p "Please enter link directory such as /var/lib/taos/tsdb: " linkDir - -while true; do - if [ ! -d $linkDir ]; then - read -p "Paht not exists, please enter the correct link path:" linkDir - continue - fi - break -done - -declare -A dirHash - -for linkFile in $(find -L $linkDir -xtype l); do - targetFile=$(readlink -f $linkFile) - echo "targetFile: ${targetFile}" - # TODO : Extract directory part and basename part - dirName=$(dirname $(dirname ${targetFile})) - baseName=$(basename $(dirname ${targetFile}))/$(basename ${targetFile}) - - # TODO : - newDir="${dirHash["$dirName"]}" - if [ -z "${dirHash["$dirName"]}" ]; then - read -p "Please enter the directory to replace ${dirName}:" newDir - - read -p "Do you want to replcace all[y/N]?" replcaceAll - if [[ ( "${replcaceAll}" == "y") || ( "${replcaceAll}" == "Y") ]]; then - dirHash["$dirName"]="$newDir" - fi - fi - - # Replcace the file - ln -sf "${newDir}/${baseName}" "${linkFile}" -done +#!/bin/bash + +# This script is used to repaire links when you what to move TDengine +# data to other places and to access data. + +# Read link path +read -p "Please enter link directory such as /var/lib/taos/tsdb: " linkDir + +while true; do + if [ ! -d $linkDir ]; then + read -p "Paht not exists, please enter the correct link path:" linkDir + continue + fi + break +done + +declare -A dirHash + +for linkFile in $(find -L $linkDir -xtype l); do + targetFile=$(readlink -f $linkFile) + echo "targetFile: ${targetFile}" + # TODO : Extract directory part and basename part + dirName=$(dirname $(dirname ${targetFile})) + baseName=$(basename $(dirname ${targetFile}))/$(basename ${targetFile}) + + # TODO : + newDir="${dirHash["$dirName"]}" + if [ -z "${dirHash["$dirName"]}" ]; then + read -p "Please enter the directory to replace ${dirName}:" newDir + + read -p "Do you want to replcace all[y/N]?" replcaceAll + if [[ ( "${replcaceAll}" == "y") || ( "${replcaceAll}" == "Y") ]]; then + dirHash["$dirName"]="$newDir" + fi + fi + + # Replcace the file + ln -sf "${newDir}/${baseName}" "${linkFile}" +done diff --git a/packaging/tools/run_taosd_and_taosadapter.sh b/packaging/tools/run_taosd_and_taosadapter.sh old mode 100644 new mode 100755 index 3dfab87d72896f7b51bcabd5e001a8fb07550255..9ab9eb484a4a5bbc4e3d3994d97b61e0f4bd328d --- a/packaging/tools/run_taosd_and_taosadapter.sh +++ b/packaging/tools/run_taosd_and_taosadapter.sh @@ -1,3 +1,3 @@ -#!/bin/bash -[[ -x /usr/bin/taosadapter ]] && /usr/bin/taosadapter & -taosd +#!/bin/bash +[[ -x /usr/bin/taosadapter ]] && /usr/bin/taosadapter & +taosd diff --git a/packaging/tools/set_core.sh b/packaging/tools/set_core.sh old mode 100644 new mode 100755 index 41e8aebec3b878f073a2a078c01bb0ef978b2f4e..db95aeb34346b161d979b074e0cb50c017e0bc6d --- a/packaging/tools/set_core.sh +++ b/packaging/tools/set_core.sh @@ -1,40 +1,40 @@ -#!/bin/bash -# -# This file is used to set config for core when taosd crash - -# Color setting -RED='\033[0;31m' -GREEN='\033[1;32m' -GREEN_DARK='\033[0;32m' -GREEN_UNDERLINE='\033[4;32m' -NC='\033[0m' - -# set -e -# set -x -corePath=$1 - -csudo="" -if command -v sudo > /dev/null; then - csudo="sudo " -fi - -if [[ ! -n ${corePath} ]]; then - echo -e -n "${GREEN}Please enter a file directory to save the coredump file${NC}:" - read corePath - while true; do - if [[ ! -z "$corePath" ]]; then - break - else - read -p "Please enter a file directory to save the coredump file:" corePath - fi - done -fi - -ulimit -c unlimited -${csudo}sed -i '/ulimit -c unlimited/d' /etc/profile ||: -${csudo}sed -i '$a\ulimit -c unlimited' /etc/profile ||: -source /etc/profile - -${csudo}mkdir -p ${corePath} ||: -${csudo}sysctl -w kernel.core_pattern=${corePath}/core-%e-%p ||: -${csudo}echo "${corePath}/core-%e-%p" | ${csudo}tee /proc/sys/kernel/core_pattern ||: +#!/bin/bash +# +# This file is used to set config for core when taosd crash + +# Color setting +RED='\033[0;31m' +GREEN='\033[1;32m' +GREEN_DARK='\033[0;32m' +GREEN_UNDERLINE='\033[4;32m' +NC='\033[0m' + +# set -e +# set -x +corePath=$1 + +csudo="" +if command -v sudo > /dev/null; then + csudo="sudo " +fi + +if [[ ! -n ${corePath} ]]; then + echo -e -n "${GREEN}Please enter a file directory to save the coredump file${NC}:" + read corePath + while true; do + if [[ ! -z "$corePath" ]]; then + break + else + read -p "Please enter a file directory to save the coredump file:" corePath + fi + done +fi + +ulimit -c unlimited +${csudo}sed -i '/ulimit -c unlimited/d' /etc/profile ||: +${csudo}sed -i '$a\ulimit -c unlimited' /etc/profile ||: +source /etc/profile + +${csudo}mkdir -p ${corePath} ||: +${csudo}sysctl -w kernel.core_pattern=${corePath}/core-%e-%p ||: +${csudo}echo "${corePath}/core-%e-%p" | ${csudo}tee /proc/sys/kernel/core_pattern ||: diff --git a/packaging/tools/startPre.sh b/packaging/tools/startPre.sh old mode 100644 new mode 100755 diff --git a/packaging/tools/taosd-dump-cfg.gdb b/packaging/tools/taosd-dump-cfg.gdb index a7b143221d22c59236be701e68e4ff3a027212e8..9774ccf82283817edea5f49b59e0c6cb6f529577 100644 --- a/packaging/tools/taosd-dump-cfg.gdb +++ b/packaging/tools/taosd-dump-cfg.gdb @@ -1,144 +1,144 @@ -# Usage: -# sudo gdb -x ./taosd-dump-cfg.gdb - -define attach_pidof - if $argc != 1 - help attach_pidof - else - shell echo -e "\ -set \$PID = "$(echo $(pidof $arg0) 0 | cut -d " " -f 1)"\n\ -if \$PID > 0\n\ - attach "$(pidof -s $arg0)"\n\ -else\n\ - print \"Process '"$arg0"' not found\"\n\ -end" > /tmp/gdb.pidof - source /tmp/gdb.pidof - end -end - -document attach_pidof -Attach to process by name -Usage: attach_pidof PROG_NAME -end - -set $TAOS_CFG_VTYPE_INT8 = 0 -set $TAOS_CFG_VTYPE_INT16 = 1 -set $TAOS_CFG_VTYPE_INT32 = 2 -set $TAOS_CFG_VTYPE_FLOAT = 3 -set $TAOS_CFG_VTYPE_STRING = 4 -set $TAOS_CFG_VTYPE_IPSTR = 5 -set $TAOS_CFG_VTYPE_DIRECTORY = 6 - -set $TSDB_CFG_CTYPE_B_CONFIG = 1U -set $TSDB_CFG_CTYPE_B_SHOW = 2U -set $TSDB_CFG_CTYPE_B_LOG = 4U -set $TSDB_CFG_CTYPE_B_CLIENT = 8U -set $TSDB_CFG_CTYPE_B_OPTION = 16U -set $TSDB_CFG_CTYPE_B_NOT_PRINT = 32U - -set $TSDB_CFG_PRINT_LEN = 53 - -define print_blank - if $argc == 1 - set $blank_len = $arg0 - while $blank_len > 0 - printf "%s", " " - set $blank_len = $blank_len - 1 - end - end -end - -define dump_cfg - if $argc != 1 - help dump_cfg - else - set $blen = $TSDB_CFG_PRINT_LEN - (int)strlen($arg0.option) - if $blen < 0 - $blen = 0 - end - #printf "%s: %d\n", "******blen: ", $blen - printf "%s: ", $arg0.option - print_blank $blen - - if $arg0.valType == $TAOS_CFG_VTYPE_INT8 - printf "%d\n", *((int8_t *) $arg0.ptr) - else - if $arg0.valType == $TAOS_CFG_VTYPE_INT16 - printf "%d\n", *((int16_t *) $arg0.ptr) - else - if $arg0.valType == $TAOS_CFG_VTYPE_INT32 - printf "%d\n", *((int32_t *) $arg0.ptr) - else - if $arg0.valType == $TAOS_CFG_VTYPE_FLOAT - printf "%f\n", *((float *) $arg0.ptr) - else - printf "%s\n", $arg0.ptr - end - end - end - end - end -end - -document dump_cfg -Dump a cfg entry -Usage: dump_cfg cfg -end - -set pagination off - -attach_pidof taosd - -set $idx=0 -#print tsGlobalConfigNum -#set $end=$1 -set $end=tsGlobalConfigNum - -p "*=*=*=*=*=*=*=*=*= taos global config:" -#while ($idx .lt. $end) -while ($idx < $end) - # print tsGlobalConfig[$idx].option - set $cfg = tsGlobalConfig[$idx] - set $tsce = tscEmbedded -# p "1" - if ($tsce == 0) - if !($cfg.cfgType & $TSDB_CFG_CTYPE_B_CLIENT) - end - else - if $cfg.cfgType & $TSDB_CFG_CTYPE_B_NOT_PRINT - else - if !($cfg.cfgType & $TSDB_CFG_CTYPE_B_SHOW) - else - dump_cfg $cfg - end - end - end - - set $idx=$idx+1 -end - -set $idx=0 - -p "*=*=*=*=*=*=*=*=*= taos local config:" -while ($idx < $end) - set $cfg = tsGlobalConfig[$idx] - set $tsce = tscEmbedded - if ($tsce == 0) - if !($cfg.cfgType & $TSDB_CFG_CTYPE_B_CLIENT) - end - else - if $cfg.cfgType & $TSDB_CFG_CTYPE_B_NOT_PRINT - else - if ($cfg.cfgType & $TSDB_CFG_CTYPE_B_SHOW) - else - dump_cfg $cfg - end - end - end - - set $idx=$idx+1 -end - -detach - -quit +# Usage: +# sudo gdb -x ./taosd-dump-cfg.gdb + +define attach_pidof + if $argc != 1 + help attach_pidof + else + shell echo -e "\ +set \$PID = "$(echo $(pidof $arg0) 0 | cut -d " " -f 1)"\n\ +if \$PID > 0\n\ + attach "$(pidof -s $arg0)"\n\ +else\n\ + print \"Process '"$arg0"' not found\"\n\ +end" > /tmp/gdb.pidof + source /tmp/gdb.pidof + end +end + +document attach_pidof +Attach to process by name +Usage: attach_pidof PROG_NAME +end + +set $TAOS_CFG_VTYPE_INT8 = 0 +set $TAOS_CFG_VTYPE_INT16 = 1 +set $TAOS_CFG_VTYPE_INT32 = 2 +set $TAOS_CFG_VTYPE_FLOAT = 3 +set $TAOS_CFG_VTYPE_STRING = 4 +set $TAOS_CFG_VTYPE_IPSTR = 5 +set $TAOS_CFG_VTYPE_DIRECTORY = 6 + +set $TSDB_CFG_CTYPE_B_CONFIG = 1U +set $TSDB_CFG_CTYPE_B_SHOW = 2U +set $TSDB_CFG_CTYPE_B_LOG = 4U +set $TSDB_CFG_CTYPE_B_CLIENT = 8U +set $TSDB_CFG_CTYPE_B_OPTION = 16U +set $TSDB_CFG_CTYPE_B_NOT_PRINT = 32U + +set $TSDB_CFG_PRINT_LEN = 53 + +define print_blank + if $argc == 1 + set $blank_len = $arg0 + while $blank_len > 0 + printf "%s", " " + set $blank_len = $blank_len - 1 + end + end +end + +define dump_cfg + if $argc != 1 + help dump_cfg + else + set $blen = $TSDB_CFG_PRINT_LEN - (int)strlen($arg0.option) + if $blen < 0 + $blen = 0 + end + #printf "%s: %d\n", "******blen: ", $blen + printf "%s: ", $arg0.option + print_blank $blen + + if $arg0.valType == $TAOS_CFG_VTYPE_INT8 + printf "%d\n", *((int8_t *) $arg0.ptr) + else + if $arg0.valType == $TAOS_CFG_VTYPE_INT16 + printf "%d\n", *((int16_t *) $arg0.ptr) + else + if $arg0.valType == $TAOS_CFG_VTYPE_INT32 + printf "%d\n", *((int32_t *) $arg0.ptr) + else + if $arg0.valType == $TAOS_CFG_VTYPE_FLOAT + printf "%f\n", *((float *) $arg0.ptr) + else + printf "%s\n", $arg0.ptr + end + end + end + end + end +end + +document dump_cfg +Dump a cfg entry +Usage: dump_cfg cfg +end + +set pagination off + +attach_pidof taosd + +set $idx=0 +#print tsGlobalConfigNum +#set $end=$1 +set $end=tsGlobalConfigNum + +p "*=*=*=*=*=*=*=*=*= taos global config:" +#while ($idx .lt. $end) +while ($idx < $end) + # print tsGlobalConfig[$idx].option + set $cfg = tsGlobalConfig[$idx] + set $tsce = tscEmbedded +# p "1" + if ($tsce == 0) + if !($cfg.cfgType & $TSDB_CFG_CTYPE_B_CLIENT) + end + else + if $cfg.cfgType & $TSDB_CFG_CTYPE_B_NOT_PRINT + else + if !($cfg.cfgType & $TSDB_CFG_CTYPE_B_SHOW) + else + dump_cfg $cfg + end + end + end + + set $idx=$idx+1 +end + +set $idx=0 + +p "*=*=*=*=*=*=*=*=*= taos local config:" +while ($idx < $end) + set $cfg = tsGlobalConfig[$idx] + set $tsce = tscEmbedded + if ($tsce == 0) + if !($cfg.cfgType & $TSDB_CFG_CTYPE_B_CLIENT) + end + else + if $cfg.cfgType & $TSDB_CFG_CTYPE_B_NOT_PRINT + else + if ($cfg.cfgType & $TSDB_CFG_CTYPE_B_SHOW) + else + dump_cfg $cfg + end + end + end + + set $idx=$idx+1 +end + +detach + +quit diff --git a/source/client/inc/clientInt.h b/source/client/inc/clientInt.h index 516b289f08d1c36dcbc3e7f513da6d72f727fa81..d9f33510088cf228215edf0f77368334edd4b956 100644 --- a/source/client/inc/clientInt.h +++ b/source/client/inc/clientInt.h @@ -121,7 +121,7 @@ struct SAppInstInfo { SCorEpSet mgmtEp; SInstanceSummary summary; SList* pConnList; // STscObj linked list - int64_t clusterId; + uint64_t clusterId; void* pTransporter; SAppHbMgr* pAppHbMgr; }; @@ -286,6 +286,8 @@ void initMsgHandleFp(); TAOS* taos_connect_internal(const char* ip, const char* user, const char* pass, const char* auth, const char* db, uint16_t port, int connType); +SRequestObj* launchQuery(STscObj* pTscObj, const char* sql, int sqlLen); + int32_t parseSql(SRequestObj* pRequest, bool topicQuery, SQuery** pQuery, SStmtCallback* pStmtCb); int32_t getPlan(SRequestObj* pRequest, SQuery* pQuery, SQueryPlan** pPlan, SArray* pNodeList); diff --git a/source/client/src/clientEnv.c b/source/client/src/clientEnv.c index 30997def746a2f0c4e893831c2462520ce308eaf..669b2bc97eb3e6fab04701aebbf80402432b44c1 100644 --- a/source/client/src/clientEnv.c +++ b/source/client/src/clientEnv.c @@ -91,7 +91,6 @@ static bool clientRpcRfp(int32_t code) { } } - // TODO refactor void *openTransporter(const char *user, const char *auth, int32_t numOfThread) { SRpcInit rpcInit; @@ -105,10 +104,6 @@ void *openTransporter(const char *user, const char *auth, int32_t numOfThread) { rpcInit.connType = TAOS_CONN_CLIENT; rpcInit.user = (char *)user; rpcInit.idleTime = tsShellActivityTimer * 1000; - rpcInit.ckey = "key"; - rpcInit.spi = 1; - rpcInit.secret = (char *)auth; - void *pDnodeConn = rpcOpen(&rpcInit); if (pDnodeConn == NULL) { tscError("failed to init connection to server"); diff --git a/source/client/src/clientHb.c b/source/client/src/clientHb.c index fc39e80c1e783914518d0b931f20310aad73df18..d01ec501ba215dae820a72a8dfa8ab473d5b8950 100644 --- a/source/client/src/clientHb.c +++ b/source/client/src/clientHb.c @@ -45,7 +45,7 @@ static int32_t hbProcessUserAuthInfoRsp(void *value, int32_t valueLen, struct SC catalogUpdateUserAuthInfo(pCatalog, rsp); } - tFreeSUserAuthBatchRsp(&batchRsp); + taosArrayDestroy(batchRsp.pArray); return TSDB_CODE_SUCCESS; } @@ -285,6 +285,7 @@ int32_t hbBuildQueryDesc(SQueryHbReqBasic *hbBasic, STscObj *pObj) { int64_t *rid = pIter; SRequestObj *pRequest = acquireRequest(*rid); if (NULL == pRequest) { + pIter = taosHashIterate(pObj->pRequests, pIter); continue; } @@ -309,6 +310,8 @@ int32_t hbBuildQueryDesc(SQueryHbReqBasic *hbBasic, STscObj *pObj) { taosArrayDestroy(desc.subDesc); desc.subDesc = NULL; } + } else { + desc.subDesc = NULL; } releaseRequest(*rid); @@ -393,6 +396,10 @@ int32_t hbGetExpiredUserInfo(SClientHbKey *connKey, struct SCatalog *pCatalog, S tscDebug("hb got %d expired users, valueLen:%d", userNum, kv.valueLen); + if (NULL == req->info) { + req->info = taosHashInit(64, hbKeyHashFunc, 1, HASH_ENTRY_LOCK); + } + taosHashPut(req->info, &kv.key, sizeof(kv.key), &kv, sizeof(kv)); return TSDB_CODE_SUCCESS; @@ -428,6 +435,10 @@ int32_t hbGetExpiredDBInfo(SClientHbKey *connKey, struct SCatalog *pCatalog, SCl tscDebug("hb got %d expired db, valueLen:%d", dbNum, kv.valueLen); + if (NULL == req->info) { + req->info = taosHashInit(64, hbKeyHashFunc, 1, HASH_ENTRY_LOCK); + } + taosHashPut(req->info, &kv.key, sizeof(kv.key), &kv, sizeof(kv)); return TSDB_CODE_SUCCESS; @@ -462,6 +473,10 @@ int32_t hbGetExpiredStbInfo(SClientHbKey *connKey, struct SCatalog *pCatalog, SC tscDebug("hb got %d expired stb, valueLen:%d", stbNum, kv.valueLen); + if (NULL == req->info) { + req->info = taosHashInit(64, hbKeyHashFunc, 1, HASH_ENTRY_LOCK); + } + taosHashPut(req->info, &kv.key, sizeof(kv.key), &kv, sizeof(kv)); return TSDB_CODE_SUCCESS; @@ -510,16 +525,6 @@ static FORCE_INLINE void hbMgrInitHandle() { hbMgrInitMqHbHandle(); } -void hbFreeReq(void *req) { - SClientHbReq *pReq = (SClientHbReq *)req; - tFreeReqKvHash(pReq->info); -} - -void hbClearClientHbReq(SClientHbReq *pReq) { - pReq->query = NULL; - pReq->info = NULL; -} - SClientHbBatchReq *hbGatherAllInfo(SAppHbMgr *pAppHbMgr) { SClientHbBatchReq *pBatchReq = taosMemoryCalloc(1, sizeof(SClientHbBatchReq)); if (pBatchReq == NULL) { @@ -534,6 +539,8 @@ SClientHbBatchReq *hbGatherAllInfo(SAppHbMgr *pAppHbMgr) { while (pIter != NULL) { SClientHbReq *pOneReq = pIter; + pOneReq = taosArrayPush(pBatchReq->reqs, pOneReq); + SHbConnInfo *info = taosHashGet(pAppHbMgr->connInfo, &pOneReq->connKey, sizeof(SClientHbKey)); if (info) { code = (*clientHbMgr.reqHandle[pOneReq->connKey.connType])(&pOneReq->connKey, info->param, pOneReq); @@ -543,8 +550,7 @@ SClientHbBatchReq *hbGatherAllInfo(SAppHbMgr *pAppHbMgr) { } } - taosArrayPush(pBatchReq->reqs, pOneReq); - hbClearClientHbReq(pOneReq); + //hbClearClientHbReq(pOneReq); pIter = taosHashIterate(pAppHbMgr->activeInfo, pIter); } @@ -565,6 +571,11 @@ void hbClearReqInfo(SAppHbMgr *pAppHbMgr) { tFreeReqKvHash(pOneReq->info); taosHashClear(pOneReq->info); + if (pOneReq->query) { + taosArrayDestroy(pOneReq->query->queryDesc); + taosMemoryFreeClear(pOneReq->query); + } + pIter = taosHashIterate(pAppHbMgr->activeInfo, pIter); } } @@ -595,8 +606,8 @@ static void *hbThreadFunc(void *param) { void *buf = taosMemoryMalloc(tlen); if (buf == NULL) { terrno = TSDB_CODE_TSC_OUT_OF_MEMORY; - tFreeClientHbBatchReq(pReq, false); - hbClearReqInfo(pAppHbMgr); + tFreeClientHbBatchReq(pReq); + //hbClearReqInfo(pAppHbMgr); break; } @@ -605,8 +616,8 @@ static void *hbThreadFunc(void *param) { if (pInfo == NULL) { terrno = TSDB_CODE_TSC_OUT_OF_MEMORY; - tFreeClientHbBatchReq(pReq, false); - hbClearReqInfo(pAppHbMgr); + tFreeClientHbBatchReq(pReq); + //hbClearReqInfo(pAppHbMgr); taosMemoryFree(buf); break; } @@ -622,8 +633,8 @@ static void *hbThreadFunc(void *param) { int64_t transporterId = 0; SEpSet epSet = getEpSet_s(&pAppInstInfo->mgmtEp); asyncSendMsgToServer(pAppInstInfo->pTransporter, &epSet, &transporterId, pInfo); - tFreeClientHbBatchReq(pReq, false); - hbClearReqInfo(pAppHbMgr); + tFreeClientHbBatchReq(pReq); + //hbClearReqInfo(pAppHbMgr); atomic_add_fetch_32(&pAppHbMgr->reportCnt, 1); } @@ -649,6 +660,9 @@ static int32_t hbCreateThread() { } static void hbStopThread() { + if (0 == atomic_load_8(&clientHbMgr.inited)) { + return; + } if (atomic_val_compare_exchange_8(&clientHbMgr.threadStop, 0, 1)) { tscDebug("hb thread already stopped"); return; @@ -712,8 +726,7 @@ void appHbMgrCleanup(void) { void *pIter = taosHashIterate(pTarget->activeInfo, NULL); while (pIter != NULL) { SClientHbReq *pOneReq = pIter; - hbFreeReq(pOneReq); - taosHashCleanup(pOneReq->info); + tFreeClientHbReq(pOneReq); pIter = taosHashIterate(pTarget->activeInfo, pIter); } taosHashCleanup(pTarget->activeInfo); @@ -745,13 +758,13 @@ int hbMgrInit() { hbMgrInitHandle(); // init backgroud thread - /*hbCreateThread();*/ + hbCreateThread(); return 0; } void hbMgrCleanUp() { - // hbStopThread(); + hbStopThread(); // destroy all appHbMgr int8_t old = atomic_val_compare_exchange_8(&clientHbMgr.inited, 1, 0); @@ -773,7 +786,7 @@ int hbRegisterConnImpl(SAppHbMgr *pAppHbMgr, SClientHbKey connKey, SHbConnInfo * } SClientHbReq hbReq = {0}; hbReq.connKey = connKey; - hbReq.info = taosHashInit(64, hbKeyHashFunc, 1, HASH_ENTRY_LOCK); + //hbReq.info = taosHashInit(64, hbKeyHashFunc, 1, HASH_ENTRY_LOCK); taosHashPut(pAppHbMgr->activeInfo, &connKey, sizeof(SClientHbKey), &hbReq, sizeof(SClientHbReq)); @@ -814,8 +827,7 @@ int hbRegisterConn(SAppHbMgr *pAppHbMgr, int64_t tscRefId, int64_t clusterId, in void hbDeregisterConn(SAppHbMgr *pAppHbMgr, SClientHbKey connKey) { SClientHbReq *pReq = taosHashGet(pAppHbMgr->activeInfo, &connKey, sizeof(SClientHbKey)); if (pReq) { - hbFreeReq(pReq); - taosHashCleanup(pReq->info); + tFreeClientHbReq(pReq); taosHashRemove(pAppHbMgr->activeInfo, &connKey, sizeof(SClientHbKey)); } diff --git a/source/client/src/clientImpl.c b/source/client/src/clientImpl.c index 7693d26d3fd450ed6e120fcf06d14dbc41e2f8a5..daa5887127ae5df63fe600b59b9ef5c8da7a592a 100644 --- a/source/client/src/clientImpl.c +++ b/source/client/src/clientImpl.c @@ -13,11 +13,13 @@ * along with this program. If not, see . */ +#include "cJSON.h" #include "clientInt.h" #include "clientLog.h" #include "command.h" #include "scheduler.h" #include "tdatablock.h" +#include "tdataformat.h" #include "tdef.h" #include "tglobal.h" #include "tmsgtype.h" @@ -187,7 +189,8 @@ int32_t parseSql(SRequestObj* pRequest, bool topicQuery, SQuery** pQuery, SStmtC setResSchemaInfo(&pRequest->body.resInfo, (*pQuery)->pResSchema, (*pQuery)->numOfResCols); setResPrecision(&pRequest->body.resInfo, (*pQuery)->precision); } - + } + if (TSDB_CODE_SUCCESS == code || NEED_CLIENT_HANDLE_ERROR(code)) { TSWAP(pRequest->dbList, (*pQuery)->pDbList); TSWAP(pRequest->tableList, (*pQuery)->pTableList); } @@ -268,7 +271,7 @@ void setResSchemaInfo(SReqResultInfo* pResInfo, const SSchema* pSchema, int32_t if (pSchema[i].type == TSDB_DATA_TYPE_VARCHAR) { pResInfo->userFields[i].bytes -= VARSTR_HEADER_SIZE; - } else if (pSchema[i].type == TSDB_DATA_TYPE_NCHAR) { + } else if (pSchema[i].type == TSDB_DATA_TYPE_NCHAR || pSchema[i].type == TSDB_DATA_TYPE_JSON) { pResInfo->userFields[i].bytes = (pResInfo->userFields[i].bytes - VARSTR_HEADER_SIZE) / TSDB_NCHAR_SIZE; } @@ -297,6 +300,8 @@ int32_t scheduleQuery(SRequestObj* pRequest, SQueryPlan* pDag, SArray* pNodeList schedulerFreeJob(pRequest->body.queryJob); } + *pRes = res.res; + pRequest->code = code; terrno = code; return pRequest->code; @@ -325,7 +330,7 @@ int32_t getQueryPlan(SRequestObj* pRequest, SQuery* pQuery, SArray** pNodeList) int32_t validateSversion(SRequestObj* pRequest, void* res) { SArray* pArray = NULL; int32_t code = 0; - + if (TDMT_VND_SUBMIT == pRequest->type) { SSubmitRsp* pRsp = (SSubmitRsp*)res; if (pRsp->nBlocks <= 0) { @@ -337,14 +342,34 @@ int32_t validateSversion(SRequestObj* pRequest, void* res) { terrno = TSDB_CODE_OUT_OF_MEMORY; return TSDB_CODE_OUT_OF_MEMORY; } - + for (int32_t i = 0; i < pRsp->nBlocks; ++i) { - SSubmitBlkRsp *blk = pRsp->pBlocks + i; - STbSVersion tbSver = {.tbFName = blk->tblFName, .sver = blk->sver}; + SSubmitBlkRsp* blk = pRsp->pBlocks + i; + if (NULL == blk->tblFName || 0 == blk->tblFName[0]) { + continue; + } + + STbSVersion tbSver = {.tbFName = blk->tblFName, .sver = blk->sver}; taosArrayPush(pArray, &tbSver); } } else if (TDMT_VND_QUERY == pRequest->type) { + SArray* pTbArray = (SArray*)res; + int32_t tbNum = taosArrayGetSize(pTbArray); + if (tbNum <= 0) { + return TSDB_CODE_SUCCESS; + } + + pArray = taosArrayInit(tbNum, sizeof(STbSVersion)); + if (NULL == pArray) { + terrno = TSDB_CODE_OUT_OF_MEMORY; + return TSDB_CODE_OUT_OF_MEMORY; + } + for (int32_t i = 0; i < tbNum; ++i) { + STbVerInfo* tbInfo = taosArrayGet(pTbArray, i); + STbSVersion tbSver = {.tbFName = tbInfo->tbFName, .sver = tbInfo->sversion}; + taosArrayPush(pArray, &tbSver); + } } SCatalog* pCatalog = NULL; @@ -362,14 +387,14 @@ _return: } void freeRequestRes(SRequestObj* pRequest, void* res) { - if (NULL == res) { + if (NULL == pRequest || NULL == res) { return; } - + if (TDMT_VND_SUBMIT == pRequest->type) { tFreeSSubmitRsp((SSubmitRsp*)res); } else if (TDMT_VND_QUERY == pRequest->type) { - + taosArrayDestroy((SArray*)res); } } @@ -410,12 +435,13 @@ SRequestObj* launchQueryImpl(SRequestObj* pRequest, SQuery* pQuery, int32_t code if (NULL != pRequest && TSDB_CODE_SUCCESS != code) { pRequest->code = terrno; - freeRequestRes(pRequest, pRes); - pRes = NULL; } if (res) { *res = pRes; + } else { + freeRequestRes(pRequest, pRes); + pRes = NULL; } return pRequest; @@ -478,12 +504,30 @@ int32_t refreshMeta(STscObj* pTscObj, SRequestObj* pRequest) { return code; } +int32_t removeMeta(STscObj* pTscObj, SArray* tbList) { + SCatalog* pCatalog = NULL; + int32_t tbNum = taosArrayGetSize(tbList); + int32_t code = catalogGetHandle(pTscObj->pAppInfo->clusterId, &pCatalog); + if (code != TSDB_CODE_SUCCESS) { + return code; + } + + for (int32_t i = 0; i < tbNum; ++i) { + SName* pTbName = taosArrayGet(tbList, i); + catalogRemoveTableMeta(pCatalog, pTbName); + } + + return TSDB_CODE_SUCCESS; +} + + SRequestObj* execQuery(STscObj* pTscObj, const char* sql, int sqlLen) { SRequestObj* pRequest = NULL; int32_t retryNum = 0; int32_t code = 0; - while (retryNum++ < REQUEST_MAX_TRY_TIMES) { + do { + destroyRequest(pRequest); pRequest = launchQuery(pTscObj, sql, sqlLen); if (pRequest == NULL || TSDB_CODE_SUCCESS == pRequest->code || !NEED_CLIENT_HANDLE_ERROR(pRequest->code)) { break; @@ -494,10 +538,12 @@ SRequestObj* execQuery(STscObj* pTscObj, const char* sql, int sqlLen) { pRequest->code = code; break; } + } while (retryNum++ < REQUEST_MAX_TRY_TIMES); - destroyRequest(pRequest); + if (NEED_CLIENT_RM_TBLMETA_REQ(pRequest->type)) { + removeMeta(pTscObj, pRequest->tableList); } - + return pRequest; } @@ -805,6 +851,94 @@ static int32_t doPrepareResPtr(SReqResultInfo* pResInfo) { return TSDB_CODE_SUCCESS; } +static char* parseTagDatatoJson(void* p) { + char* string = NULL; + cJSON* json = cJSON_CreateObject(); + if (json == NULL) { + goto end; + } + + int16_t nCols = kvRowNCols(p); + char tagJsonKey[256] = {0}; + for (int j = 0; j < nCols; ++j) { + SColIdx* pColIdx = kvRowColIdxAt(p, j); + char* val = (char*)(kvRowColVal(p, pColIdx)); + if (j == 0) { + if (*val == TSDB_DATA_TYPE_NULL) { + string = taosMemoryCalloc(1, 8); + sprintf(string, "%s", TSDB_DATA_NULL_STR_L); + goto end; + } + continue; + } + + // json key encode by binary + memset(tagJsonKey, 0, sizeof(tagJsonKey)); + memcpy(tagJsonKey, varDataVal(val), varDataLen(val)); + // json value + val += varDataTLen(val); + char* realData = POINTER_SHIFT(val, CHAR_BYTES); + char type = *val; + if (type == TSDB_DATA_TYPE_NULL) { + cJSON* value = cJSON_CreateNull(); + if (value == NULL) { + goto end; + } + cJSON_AddItemToObject(json, tagJsonKey, value); + } else if (type == TSDB_DATA_TYPE_NCHAR) { + cJSON* value = NULL; + if (varDataLen(realData) > 0) { + char* tagJsonValue = taosMemoryCalloc(varDataLen(realData), 1); + int32_t length = taosUcs4ToMbs((TdUcs4*)varDataVal(realData), varDataLen(realData), tagJsonValue); + if (length < 0) { + tscError("charset:%s to %s. val:%s convert json value failed.", DEFAULT_UNICODE_ENCODEC, tsCharset, val); + taosMemoryFree(tagJsonValue); + goto end; + } + value = cJSON_CreateString(tagJsonValue); + taosMemoryFree(tagJsonValue); + if (value == NULL) { + goto end; + } + } else if (varDataLen(realData) == 0) { + value = cJSON_CreateString(""); + } else { + ASSERT(0); + } + + cJSON_AddItemToObject(json, tagJsonKey, value); + } else if (type == TSDB_DATA_TYPE_DOUBLE) { + double jsonVd = *(double*)(realData); + cJSON* value = cJSON_CreateNumber(jsonVd); + if (value == NULL) { + goto end; + } + cJSON_AddItemToObject(json, tagJsonKey, value); + // }else if(type == TSDB_DATA_TYPE_BIGINT){ + // int64_t jsonVd = *(int64_t*)(realData); + // cJSON* value = cJSON_CreateNumber((double)jsonVd); + // if (value == NULL) + // { + // goto end; + // } + // cJSON_AddItemToObject(json, tagJsonKey, value); + } else if (type == TSDB_DATA_TYPE_BOOL) { + char jsonVd = *(char*)(realData); + cJSON* value = cJSON_CreateBool(jsonVd); + if (value == NULL) { + goto end; + } + cJSON_AddItemToObject(json, tagJsonKey, value); + } else { + ASSERT(0); + } + } + string = cJSON_PrintUnformatted(json); +end: + cJSON_Delete(json); + return string; +} + static int32_t doConvertUCS4(SReqResultInfo* pResultInfo, int32_t numOfRows, int32_t numOfCols, int32_t* colLength) { for (int32_t i = 0; i < numOfCols; ++i) { int32_t type = pResultInfo->fields[i].type; @@ -835,9 +969,7 @@ static int32_t doConvertUCS4(SReqResultInfo* pResultInfo, int32_t numOfRows, int pResultInfo->pCol[i].pData = pResultInfo->convertBuf[i]; pResultInfo->row[i] = pResultInfo->pCol[i].pData; - } - - if (type == TSDB_DATA_TYPE_JSON) { + } else if (type == TSDB_DATA_TYPE_JSON && colLength[i] > 0) { char* p = taosMemoryRealloc(pResultInfo->convertBuf[i], colLength[i]); if (p == NULL) { return TSDB_CODE_OUT_OF_MEMORY; @@ -857,15 +989,9 @@ static int32_t doConvertUCS4(SReqResultInfo* pResultInfo, int32_t numOfRows, int sprintf(varDataVal(dst), "%s", TSDB_DATA_NULL_STR_L); varDataSetLen(dst, strlen(varDataVal(dst))); } else if (jsonInnerType == TSDB_DATA_TYPE_JSON) { - int32_t length = - taosUcs4ToMbs((TdUcs4*)varDataVal(jsonInnerData), varDataLen(jsonInnerData), varDataVal(dst)); - - if (length <= 0) { - tscError("charset:%s to %s. val:%s convert failed.", DEFAULT_UNICODE_ENCODEC, tsCharset, - varDataVal(jsonInnerData)); - length = 0; - } - varDataSetLen(dst, length); + char* jsonString = parseTagDatatoJson(jsonInnerData); + STR_TO_VARSTR(dst, jsonString); + taosMemoryFree(jsonString); } else if (jsonInnerType == TSDB_DATA_TYPE_NCHAR) { // value -> "value" *(char*)varDataVal(dst) = '\"'; int32_t length = taosUcs4ToMbs((TdUcs4*)varDataVal(jsonInnerData), varDataLen(jsonInnerData), @@ -876,7 +1002,7 @@ static int32_t doConvertUCS4(SReqResultInfo* pResultInfo, int32_t numOfRows, int length = 0; } varDataSetLen(dst, length + CHAR_BYTES * 2); - *(char*)(varDataVal(dst), length + CHAR_BYTES) = '\"'; + *(char*)POINTER_SHIFT(varDataVal(dst), length + CHAR_BYTES) = '\"'; } else if (jsonInnerType == TSDB_DATA_TYPE_DOUBLE) { double jsonVd = *(double*)(jsonInnerData); sprintf(varDataVal(dst), "%.9lf", jsonVd); @@ -1022,7 +1148,6 @@ TSDB_SERVER_STATUS taos_check_server_status(const char* fqdn, int port, char* de SRpcInit rpcInit = {0}; char pass[TSDB_PASSWORD_LEN + 1] = {0}; - taosEncryptPass_c((uint8_t*)("_pwd"), strlen("_pwd"), pass); rpcInit.label = "CHK"; rpcInit.numOfThreads = 1; rpcInit.cfp = NULL; @@ -1030,9 +1155,6 @@ TSDB_SERVER_STATUS taos_check_server_status(const char* fqdn, int port, char* de rpcInit.connType = TAOS_CONN_CLIENT; rpcInit.idleTime = tsShellActivityTimer * 1000; rpcInit.user = "_dnd"; - rpcInit.ckey = "_key"; - rpcInit.spi = 1; - rpcInit.secret = pass; clientRpc = rpcOpen(&rpcInit); if (clientRpc == NULL) { diff --git a/source/client/src/clientMain.c b/source/client/src/clientMain.c index 7360b054e2516c696a316156f73f3474346fa7b2..53eb443b36b05393b22667a6f623892008f14ebb 100644 --- a/source/client/src/clientMain.c +++ b/source/client/src/clientMain.c @@ -146,10 +146,10 @@ void taos_free_result(TAOS_RES *res) { SMqRspObj *pRsp = (SMqRspObj *)res; if (pRsp->rsp.blockData) taosArrayDestroyP(pRsp->rsp.blockData, taosMemoryFree); if (pRsp->rsp.blockDataLen) taosArrayDestroy(pRsp->rsp.blockDataLen); - if (pRsp->rsp.blockSchema) taosArrayDestroy(pRsp->rsp.blockSchema); - if (pRsp->rsp.blockTbName) taosArrayDestroy(pRsp->rsp.blockTbName); if (pRsp->rsp.blockTags) taosArrayDestroy(pRsp->rsp.blockTags); if (pRsp->rsp.blockTagSchema) taosArrayDestroy(pRsp->rsp.blockTagSchema); + if (pRsp->rsp.withTbName) taosArrayDestroyP(pRsp->rsp.blockTbName, taosMemoryFree); + if (pRsp->rsp.withSchema) taosArrayDestroyP(pRsp->rsp.blockSchema, (FDelete)tDeleteSSchemaWrapper); pRsp->resInfo.pRspMsg = NULL; doFreeReqResultInfo(&pRsp->resInfo); } @@ -565,10 +565,32 @@ const char *taos_get_server_info(TAOS *taos) { void taos_query_a(TAOS *taos, const char *sql, __taos_async_fn_t fp, void *param) { if (taos == NULL || sql == NULL) { - // todo directly call fp + fp(param, NULL, TSDB_CODE_INVALID_PARA); + return; + } + + SRequestObj* pRequest = NULL; + int32_t retryNum = 0; + int32_t code = 0; + + size_t sqlLen = strlen(sql); + + while (retryNum++ < REQUEST_MAX_TRY_TIMES) { + pRequest = launchQuery(taos, sql, sqlLen); + if (pRequest == NULL || TSDB_CODE_SUCCESS == pRequest->code || !NEED_CLIENT_HANDLE_ERROR(pRequest->code)) { + break; + } + + code = refreshMeta(taos, pRequest); + if (code) { + pRequest->code = code; + break; + } + + destroyRequest(pRequest); } - taos_query_l(taos, sql, (int32_t)strlen(sql)); + fp(param, pRequest, code); } void taos_fetch_rows_a(TAOS_RES *res, __taos_async_fn_t fp, void *param) { diff --git a/source/client/src/clientMsgHandler.c b/source/client/src/clientMsgHandler.c index 11c6971e3d250e2f07f3ec48c0e6ef4bdf339a15..dfce01dd6356f19da8dce1b8de9c2eb9e9ca42e4 100644 --- a/source/client/src/clientMsgHandler.c +++ b/source/client/src/clientMsgHandler.c @@ -125,10 +125,10 @@ int32_t processUseDbRsp(void* param, const SDataBuf* pMsg, int32_t code) { struct SCatalog* pCatalog = NULL; if (usedbRsp.vgVersion >= 0) { - int32_t code1 = catalogGetHandle(pRequest->pTscObj->pAppInfo->clusterId, &pCatalog); + uint64_t clusterId = pRequest->pTscObj->pAppInfo->clusterId; + int32_t code1 = catalogGetHandle(clusterId, &pCatalog); if (code1 != TSDB_CODE_SUCCESS) { - tscWarn("catalogGetHandle failed, clusterId:%" PRIx64 ", error:%s", pRequest->pTscObj->pAppInfo->clusterId, - tstrerror(code1)); + tscWarn("0x%" PRIx64 "catalogGetHandle failed, clusterId:%" PRIx64 ", error:%s", pRequest->requestId, clusterId, tstrerror(code1)); } else { catalogRemoveDB(pCatalog, usedbRsp.db, usedbRsp.uid); } @@ -158,7 +158,7 @@ int32_t processUseDbRsp(void* param, const SDataBuf* pMsg, int32_t code) { if (output.dbVgroup) taosHashCleanup(output.dbVgroup->vgHash); taosMemoryFreeClear(output.dbVgroup); - tscError("failed to build use db output since %s", terrstr()); + tscError("0x%" PRIx64" failed to build use db output since %s", pRequest->requestId, terrstr()); } else if (output.dbVgroup) { struct SCatalog* pCatalog = NULL; diff --git a/source/client/src/clientSml.c b/source/client/src/clientSml.c index d5377c99a653b35c0b19faba92e98ec812b6d65b..68c47c2d13421cd34e327db37e31ae76774985ac 100644 --- a/source/client/src/clientSml.c +++ b/source/client/src/clientSml.c @@ -58,19 +58,13 @@ for (int i = 1; i < keyLen; ++i) { \ #define IS_INVALID_COL_LEN(len) ((len) <= 0 || (len) >= TSDB_COL_NAME_LEN) #define IS_INVALID_TABLE_LEN(len) ((len) <= 0 || (len) >= TSDB_TABLE_NAME_LEN) -#define OTD_MAX_FIELDS_NUM 2 #define OTD_JSON_SUB_FIELDS_NUM 2 #define OTD_JSON_FIELDS_NUM 4 -#define OTD_TIMESTAMP_COLUMN_NAME "ts" -#define OTD_METRIC_VALUE_COLUMN_NAME "value" - -#define TS "_ts" -#define TS_LEN 3 -#define TAG "_tagNone" -#define TAG_LEN 8 -#define VALUE "value" -#define VALUE_LEN 5 +#define TS "_ts" +#define TS_LEN 3 +#define VALUE "value" +#define VALUE_LEN 5 #define BINARY_ADD_LEN 2 // "binary" 2 means " " #define NCHAR_ADD_LEN 3 // L"nchar" 3 means L" " @@ -265,7 +259,7 @@ static int32_t smlBuildColumnDescription(SSmlKv* field, char* buf, int32_t bufSi memcpy(tname, field->key, field->keyLen); if (type == TSDB_DATA_TYPE_BINARY || type == TSDB_DATA_TYPE_NCHAR) { int32_t bytes = field->length > CHAR_SAVE_LENGTH ? (2*field->length) : CHAR_SAVE_LENGTH; - int out = snprintf(buf, bufSize,"`%s` %s(%d)", + int out = snprintf(buf, bufSize, "`%s` %s(%d)", tname, tDataTypes[field->type].name, bytes); *outBytes = out; } else { @@ -402,6 +396,12 @@ static int32_t smlApplySchemaAction(SSmlHandle* info, SSchemaAction* action) { pos += outBytes; freeBytes -= outBytes; *pos = ','; ++pos; --freeBytes; } + if(taosArrayGetSize(cols) == 0){ + outBytes = snprintf(pos, freeBytes,"`%s` %s(%d)", + tsSmlTagName, tDataTypes[TSDB_DATA_TYPE_NCHAR].name, CHAR_SAVE_LENGTH); + pos += outBytes; freeBytes -= outBytes; + *pos = ','; ++pos; --freeBytes; + } pos--; ++freeBytes; outBytes = snprintf(pos, freeBytes, ")"); TAOS_RES* res = taos_query(info->taos, result); @@ -598,25 +598,33 @@ static bool smlParseNumber(SSmlKv *kvVal, SSmlMsgBuf *msg){ kvVal->type = TSDB_DATA_TYPE_FLOAT; kvVal->f = (float)result; }else if ((left == 1 && *endptr == 'i') || (left == 3 && strncasecmp(endptr, "i64", left) == 0)){ - if(result >= (double)INT64_MAX){ - kvVal->i = INT64_MAX; - }else if(result <= (double)INT64_MIN){ - kvVal->i = INT64_MIN; - }else{ - kvVal->i = result; + if(smlDoubleToInt64OverFlow(result)){ + errno = 0; + int64_t tmp = taosStr2Int64(pVal, &endptr, 10); + if(errno == ERANGE){ + smlBuildInvalidDataMsg(msg, "big int out of range[-9223372036854775808,9223372036854775807]", pVal); + return false; + } + kvVal->type = TSDB_DATA_TYPE_BIGINT; + kvVal->i = tmp; + return true; } kvVal->type = TSDB_DATA_TYPE_BIGINT; + kvVal->i = (int64_t)result; }else if ((left == 3 && strncasecmp(endptr, "u64", left) == 0)){ - if(result < 0){ - smlBuildInvalidDataMsg(msg, "unsigned big int is too large, out of precision", pVal); - return false; - } - if(result >= (double)UINT64_MAX){ - kvVal->u = UINT64_MAX; - }else{ - kvVal->u = result; + if(result >= (double)UINT64_MAX || result < 0){ + errno = 0; + uint64_t tmp = taosStr2UInt64(pVal, &endptr, 10); + if(errno == ERANGE || result < 0){ + smlBuildInvalidDataMsg(msg, "unsigned big int out of range[0,18446744073709551615]", pVal); + return false; + } + kvVal->type = TSDB_DATA_TYPE_UBIGINT; + kvVal->u = tmp; + return true; } kvVal->type = TSDB_DATA_TYPE_UBIGINT; + kvVal->u = result; }else if (left == 3 && strncasecmp(endptr, "i32", left) == 0){ if(!IS_VALID_INT(result)){ smlBuildInvalidDataMsg(msg, "int out of range[-2147483648,2147483647]", pVal); @@ -721,54 +729,37 @@ static int64_t smlGetTimeValue(const char *value, int32_t len, int8_t type) { double ts = tsInt64; switch (type) { case TSDB_TIME_PRECISION_HOURS: - ts *= (3600 * 1e9); - tsInt64 *= (3600 * 1e9); + ts *= NANOSECOND_PER_HOUR; + tsInt64 *= NANOSECOND_PER_HOUR; break; case TSDB_TIME_PRECISION_MINUTES: - ts *= (60 * 1e9); - tsInt64 *= (60 * 1e9); + ts *= NANOSECOND_PER_MINUTE; + tsInt64 *= NANOSECOND_PER_MINUTE; break; case TSDB_TIME_PRECISION_SECONDS: - ts *= (1e9); - tsInt64 *= (1e9); + ts *= NANOSECOND_PER_SEC; + tsInt64 *= NANOSECOND_PER_SEC; break; case TSDB_TIME_PRECISION_MILLI: - ts *= (1e6); - tsInt64 *= (1e6); + ts *= NANOSECOND_PER_MSEC; + tsInt64 *= NANOSECOND_PER_MSEC; break; case TSDB_TIME_PRECISION_MICRO: - ts *= (1e3); - tsInt64 *= (1e3); + ts *= NANOSECOND_PER_USEC; + tsInt64 *= NANOSECOND_PER_USEC; break; case TSDB_TIME_PRECISION_NANO: break; default: ASSERT(0); } - if(ts >= (double)INT64_MAX || ts <= 0){ + if(ts >= (double)INT64_MAX || ts < 0){ return -1; } return tsInt64; } -static int64_t smlGetTimeNow(int8_t precision) { - switch (precision) { - case TSDB_TIME_PRECISION_HOURS: - return taosGetTimestampMs()/1000/3600; - case TSDB_TIME_PRECISION_MINUTES: - return taosGetTimestampMs()/1000/60; - case TSDB_TIME_PRECISION_SECONDS: - return taosGetTimestampMs()/1000; - case TSDB_TIME_PRECISION_MILLI: - case TSDB_TIME_PRECISION_MICRO: - case TSDB_TIME_PRECISION_NANO: - return taosGetTimestamp(precision); - default: - ASSERT(0); - } -} - static int8_t smlGetTsTypeByLen(int32_t len) { if (len == TSDB_TIME_PRECISION_SEC_DIGITS) { return TSDB_TIME_PRECISION_SECONDS; @@ -800,14 +791,15 @@ static int8_t smlGetTsTypeByPrecision(int8_t precision) { } static int64_t smlParseInfluxTime(SSmlHandle* info, const char* data, int32_t len){ + if(len == 0 || (len == 1 && data[0] == '0')){ + return taosGetTimestampNs(); + } + int8_t tsType = smlGetTsTypeByPrecision(info->precision); if (tsType == -1) { smlBuildInvalidDataMsg(&info->msgBuf, "invalid timestamp precision", NULL); return -1; } - if(len == 0){ - return smlGetTimeNow(tsType); - } int64_t ts = smlGetTimeValue(data, len, tsType); if(ts == -1){ @@ -822,6 +814,9 @@ static int64_t smlParseOpenTsdbTime(SSmlHandle* info, const char* data, int32_t smlBuildInvalidDataMsg(&info->msgBuf, "timestamp can not be null", NULL); return -1; } + if(len == 1 && data[0] == '0'){ + return taosGetTimestampNs(); + } int8_t tsType = smlGetTsTypeByLen(len); if (tsType == -1) { smlBuildInvalidDataMsg(&info->msgBuf, "timestamp precision can only be seconds(10 digits) or milli seconds(13 digits)", data); @@ -1103,8 +1098,7 @@ static int32_t smlParseTelnetString(SSmlHandle *info, const char* sql, SSmlTable kv->keyLen = VALUE_LEN; kv->value = value; kv->length = valueLen; - if(!smlParseValue(kv, &info->msgBuf) || kv->type == TSDB_DATA_TYPE_BINARY - || kv->type == TSDB_DATA_TYPE_NCHAR || kv->type == TSDB_DATA_TYPE_BOOL){ + if(!smlParseValue(kv, &info->msgBuf)){ return TSDB_CODE_SML_INVALID_DATA; } @@ -1120,14 +1114,6 @@ static int32_t smlParseTelnetString(SSmlHandle *info, const char* sql, SSmlTable static int32_t smlParseCols(const char* data, int32_t len, SArray *cols, char *childTableName, bool isTag, SHashObj *dumplicateKey, SSmlMsgBuf *msg){ if(isTag && len == 0){ - SSmlKv *kv = (SSmlKv *)taosMemoryCalloc(sizeof(SSmlKv), 1); - if(!kv) return TSDB_CODE_OUT_OF_MEMORY; - kv->key = TAG; - kv->keyLen = TAG_LEN; - kv->value = TAG; - kv->length = TAG_LEN; - kv->type = TSDB_DATA_TYPE_NCHAR; - if(cols) taosArrayPush(cols, &kv); return TSDB_CODE_SUCCESS; } @@ -1610,7 +1596,8 @@ static int32_t smlParseTSFromJSON(SSmlHandle *info, cJSON *root, SArray *cols) { smlBuildInvalidDataMsg(&info->msgBuf, "timestamp is too large", NULL); return TSDB_CODE_TSC_INVALID_TIME_STAMP; } - if(timeDouble <= 0){ + + if(timeDouble < 0){ return TSDB_CODE_TSC_INVALID_TIME_STAMP; } uint8_t tsLen = smlGetTimestampLen((int64_t)timeDouble); @@ -1628,7 +1615,9 @@ static int32_t smlParseTSFromJSON(SSmlHandle *info, cJSON *root, SArray *cols) { return TSDB_CODE_TSC_INVALID_TIME_STAMP; } tsVal = timeDouble; - } else { + } else if(timeDouble == 0){ + tsVal = taosGetTimestampNs(); + }else { return TSDB_CODE_TSC_INVALID_TIME_STAMP; } } else if (cJSON_IsObject(timestamp)) { @@ -2264,6 +2253,7 @@ static int32_t smlParseLine(SSmlHandle *info, char* lines[], int numLines){ uError("SML:0x%" PRIx64 " smlParseJSON failed:%s", info->id, *lines); return code; } + return code; } for (int32_t i = 0; i < numLines; ++i) { diff --git a/source/client/src/clientStmt.c b/source/client/src/clientStmt.c index 764571e71e5f3ddf89f58183a1d7b26a73cfc79d..17468584822361d8a3f7ff048cb797a8174b1836 100644 --- a/source/client/src/clientStmt.c +++ b/source/client/src/clientStmt.c @@ -47,8 +47,14 @@ int32_t stmtSwitchStatus(STscStmt* pStmt, STMT_STATUS newStatus) { } break; case STMT_EXECUTE: - if (STMT_STATUS_NE(ADD_BATCH) && STMT_STATUS_NE(FETCH_FIELDS)) { - code = TSDB_CODE_TSC_STMT_API_ERROR; + if (STMT_TYPE_QUERY == pStmt->sql.type) { + if (STMT_STATUS_NE(ADD_BATCH) && STMT_STATUS_NE(FETCH_FIELDS) && STMT_STATUS_NE(BIND) && STMT_STATUS_NE(BIND_COL)) { + code = TSDB_CODE_TSC_STMT_API_ERROR; + } + } else { + if (STMT_STATUS_NE(ADD_BATCH) && STMT_STATUS_NE(FETCH_FIELDS)) { + code = TSDB_CODE_TSC_STMT_API_ERROR; + } } break; default: @@ -794,6 +800,7 @@ int stmtExec(TAOS_STMT* stmt) { if (code) { pStmt->exec.pRequest->code = code; } else { + tFreeSSubmitRsp(pRsp); STMT_ERR_RET(stmtResetStmt(pStmt)); STMT_ERR_RET(TSDB_CODE_NEED_RETRY); } @@ -811,11 +818,13 @@ _return: if (TSDB_CODE_SUCCESS == code && autoCreateTbl) { if (NULL == pRsp) { tscError("no submit resp got for auto create table"); - STMT_ERR_RET(TSDB_CODE_TSC_APP_ERROR); + code = TSDB_CODE_TSC_APP_ERROR; + } else { + code = stmtUpdateTableUid(pStmt, pRsp); } - - STMT_ERR_RET(stmtUpdateTableUid(pStmt, pRsp)); } + + tFreeSSubmitRsp(pRsp); ++pStmt->sql.runTimes; diff --git a/source/client/src/tmq.c b/source/client/src/tmq.c index 36c0d8156c5fd82b55517e87d2fab06a80e2a4a3..dfa56f80c457783eb58255f3a0d494936b475bad 100644 --- a/source/client/src/tmq.c +++ b/source/client/src/tmq.c @@ -202,7 +202,12 @@ tmq_conf_t* tmq_conf_new() { } void tmq_conf_destroy(tmq_conf_t* conf) { - if (conf) taosMemoryFree(conf); + if (conf) { + if (conf->ip) taosMemoryFree(conf->ip); + if (conf->user) taosMemoryFree(conf->user); + if (conf->pass) taosMemoryFree(conf->pass); + taosMemoryFree(conf); + } } tmq_conf_res_t tmq_conf_set(tmq_conf_t* conf, const char* key, const char* value) { @@ -497,6 +502,7 @@ int32_t tmqHandleAllDelayedTask(tmq_t* tmq) { } else { ASSERT(0); } + taosFreeQitem(pTaskType); } taosFreeQall(qall); return 0; @@ -954,8 +960,12 @@ int32_t tmqPollCb(void* param, const SDataBuf* pMsg, int32_t code) { SMqClientVg* pVg = pParam->pVg; SMqClientTopic* pTopic = pParam->pTopic; tmq_t* tmq = pParam->tmq; + int32_t vgId = pParam->vgId; + int32_t epoch = pParam->epoch; + taosMemoryFree(pParam); if (code != 0) { - tscWarn("msg discard from vg %d, epoch %d, code:%x", pParam->vgId, pParam->epoch, code); + tscWarn("msg discard from vg %d, epoch %d, code:%x", vgId, epoch, code); + if (pMsg->pData) taosMemoryFree(pMsg->pData); goto CREATE_MSG_FAIL; } @@ -963,19 +973,21 @@ int32_t tmqPollCb(void* param, const SDataBuf* pMsg, int32_t code) { int32_t tmqEpoch = atomic_load_32(&tmq->epoch); if (msgEpoch < tmqEpoch) { // do not write into queue since updating epoch reset - tscWarn("msg discard from vg %d since from earlier epoch, rsp epoch %d, current epoch %d", pParam->vgId, msgEpoch, + tscWarn("msg discard from vg %d since from earlier epoch, rsp epoch %d, current epoch %d", vgId, msgEpoch, tmqEpoch); tsem_post(&tmq->rspSem); + taosMemoryFree(pMsg->pData); return 0; } if (msgEpoch != tmqEpoch) { - tscWarn("mismatch rsp from vg %d, epoch %d, current epoch %d", pParam->vgId, msgEpoch, tmqEpoch); + tscWarn("mismatch rsp from vg %d, epoch %d, current epoch %d", vgId, msgEpoch, tmqEpoch); } SMqPollRspWrapper* pRspWrapper = taosAllocateQitem(sizeof(SMqPollRspWrapper), DEF_QITEM); if (pRspWrapper == NULL) { - tscWarn("msg discard from vg %d, epoch %d since out of memory", pParam->vgId, pParam->epoch); + taosMemoryFree(pMsg->pData); + tscWarn("msg discard from vg %d, epoch %d since out of memory", vgId, epoch); goto CREATE_MSG_FAIL; } @@ -986,6 +998,7 @@ int32_t tmqPollCb(void* param, const SDataBuf* pMsg, int32_t code) { memcpy(&pRspWrapper->msg, pMsg->pData, sizeof(SMqRspHead)); tDecodeSMqDataBlkRsp(POINTER_SHIFT(pMsg->pData, sizeof(SMqRspHead)), &pRspWrapper->msg); + taosMemoryFree(pMsg->pData); tscDebug("consumer %ld recv poll: vg %d, req offset %ld, rsp offset %ld", tmq->consumerId, pVg->vgId, pRspWrapper->msg.reqOffset, pRspWrapper->msg.rspOffset); @@ -995,7 +1008,7 @@ int32_t tmqPollCb(void* param, const SDataBuf* pMsg, int32_t code) { return 0; CREATE_MSG_FAIL: - if (pParam->epoch == tmq->epoch) { + if (epoch == tmq->epoch) { atomic_store_32(&pVg->vgStatus, TMQ_VG_STATUS__IDLE); } tsem_post(&tmq->rspSem); @@ -1088,6 +1101,7 @@ bool tmqUpdateEp(tmq_t* tmq, int32_t epoch, SMqAskEpRsp* pRsp) { int32_t tmqAskEpCb(void* param, const SDataBuf* pMsg, int32_t code) { SMqAskEpCbParam* pParam = (SMqAskEpCbParam*)param; tmq_t* tmq = pParam->tmq; + int8_t async = pParam->async; pParam->code = code; if (code != 0) { tscError("consumer %ld get topic endpoint error, not ready, wait:%d", tmq->consumerId, pParam->async); @@ -1104,7 +1118,7 @@ int32_t tmqAskEpCb(void* param, const SDataBuf* pMsg, int32_t code) { goto END; } - if (!pParam->async) { + if (!async) { SMqAskEpRsp rsp; tDecodeSMqAskEpRsp(POINTER_SHIFT(pMsg->pData, sizeof(SMqRspHead)), &rsp); /*printf("rsp epoch %ld sz %ld\n", rsp.epoch, rsp.topics->size);*/ @@ -1125,13 +1139,14 @@ int32_t tmqAskEpCb(void* param, const SDataBuf* pMsg, int32_t code) { taosWriteQitem(tmq->mqueue, pWrapper); tsem_post(&tmq->rspSem); - taosMemoryFree(pParam); } END: /*atomic_store_8(&tmq->epStatus, 0);*/ - if (!pParam->async) { + if (!async) { tsem_post(&pParam->rspSem); + } else { + taosMemoryFree(pParam); } return code; } @@ -1279,7 +1294,6 @@ SMqRspObj* tmqBuildRspFromWrapper(SMqPollRspWrapper* pWrapper) { setResSchemaInfo(&pRspObj->resInfo, pWrapper->topicHandle->schema.pSchema, pWrapper->topicHandle->schema.nCols); } - taosFreeQitem(pWrapper); return pRspObj; } @@ -1401,6 +1415,7 @@ SMqRspObj* tmqHandleAllRsp(tmq_t* tmq, int64_t waitTime, bool pollIfReset) { } // build rsp SMqRspObj* pRsp = tmqBuildRspFromWrapper(pollRspWrapper); + taosFreeQitem(pollRspWrapper); return pRsp; } else { /*printf("epoch mismatch\n");*/ diff --git a/source/client/test/clientTests.cpp b/source/client/test/clientTests.cpp index fc5781cb4d1e13f54c61415f060db46c50613ed5..d67a361c21777e0dd164f4cdf89bd90145968bf8 100644 --- a/source/client/test/clientTests.cpp +++ b/source/client/test/clientTests.cpp @@ -567,6 +567,7 @@ TEST(testCase, insert_test) { taos_free_result(pRes); taos_close(pConn); } +#endif TEST(testCase, projection_query_tables) { TAOS* pConn = taos_connect("localhost", "root", "taosdata", NULL, 0); @@ -605,7 +606,7 @@ TEST(testCase, projection_query_tables) { } taos_free_result(pRes); - for(int32_t i = 0; i < 10000000; i += 20) { + for(int32_t i = 0; i < 100000; i += 20) { char sql[1024] = {0}; sprintf(sql, "insert into tu values(now+%da, %d)(now+%da, %d)(now+%da, %d)(now+%da, %d)" @@ -625,7 +626,7 @@ TEST(testCase, projection_query_tables) { printf("start to insert next table\n"); - for(int32_t i = 0; i < 10000000; i += 20) { + for(int32_t i = 0; i < 100000; i += 20) { char sql[1024] = {0}; sprintf(sql, "insert into tu2 values(now+%da, %d)(now+%da, %d)(now+%da, %d)(now+%da, %d)" @@ -692,8 +693,6 @@ TEST(testCase, projection_query_stables) { taos_close(pConn); } -#endif - TEST(testCase, agg_query_tables) { TAOS* pConn = taos_connect("localhost", "root", "taosdata", NULL, 0); ASSERT_NE(pConn, nullptr); diff --git a/source/client/test/smlTest.cpp b/source/client/test/smlTest.cpp index d9a81ad3e67e9a7ed793345a061eb94eb6e05439..217699e36071e1e4c5e93e391e77a95c4f857af8 100644 --- a/source/client/test/smlTest.cpp +++ b/source/client/test/smlTest.cpp @@ -208,6 +208,7 @@ TEST(testCase, smlParseCols_Error_Test) { memcpy(sql, data[i], len + 1); SArray *cols = taosArrayInit(8, POINTER_BYTES); int32_t ret = smlParseCols(sql, len, cols, NULL, false, dumplicateKey, &msgBuf); + printf("i:%d\n",i); ASSERT_NE(ret, TSDB_CODE_SUCCESS); taosHashClear(dumplicateKey); taosMemoryFree(sql); @@ -268,16 +269,7 @@ TEST(testCase, smlParseCols_tag_Test) { ret = smlParseCols(data, len, cols, NULL, true, dumplicateKey, &msgBuf); ASSERT_EQ(ret, TSDB_CODE_SUCCESS); size = taosArrayGetSize(cols); - ASSERT_EQ(size, 1); - - // nchar - kv = (SSmlKv *)taosArrayGetP(cols, 0); - ASSERT_EQ(strncasecmp(kv->key, TAG, strlen(TAG)), 0); - ASSERT_EQ(kv->keyLen, strlen(TAG)); - ASSERT_EQ(kv->type, TSDB_DATA_TYPE_NCHAR); - ASSERT_EQ(kv->length, strlen(TAG)); - ASSERT_EQ(strncasecmp(kv->value, TAG, strlen(TAG)), 0); - taosMemoryFree(kv); + ASSERT_EQ(size, 0); taosArrayDestroy(cols); taosHashCleanup(dumplicateKey); @@ -506,7 +498,7 @@ TEST(testCase, smlProcess_influx_Test) { "readings,name=truck_0,fleet=South,driver=Trish,model=H-2,device_version=v2.3 load_capacity=1500,fuel_capacity=150,nominal_fuel_consumption=12,latitude=52.31854,longitude=4.72037,elevation=124,heading=221,grade=0,fuel_consumption=25 1451608403000000000", "readings,name=truck_0,fleet=South,driver=Trish,model=H-2,device_version=v2.3 fuel_capacity=150,nominal_fuel_consumption=12,latitude=52.31854,longitude=4.72037,elevation=124,velocity=0,heading=221,grade=0,fuel_consumption=25 1451609404000000000", "readings,name=truck_0,fleet=South,driver=Trish,model=H-2,device_version=v2.3 fuel_consumption=25,grade=0 1451619405000000000", - "readings,name=truck_1,fleet=South,driver=Albert,model=F-150,device_version=v1.5 load_capacity=2000,fuel_capacity=200,nominal_fuel_consumption=15,latitude=72.45258,longitude=68.83761,elevation=255,velocity=0,heading=181,grade=0,fuel_consumption=25 145160640600000000", + "readings,name=truck_1,fleet=South,driver=Albert,model=F-150,device_version=v1.5 load_capacity=2000,fuel_capacity=200,nominal_fuel_consumption=15,latitude=72.45258,longitude=68.83761,elevation=255,velocity=0,heading=181,grade=0,fuel_consumption=25 1451606406000000000", "readings,name=truck_2,driver=Derek,model=F-150,device_version=v1.5 load_capacity=2000,fuel_capacity=200,nominal_fuel_consumption=15,latitude=24.5208,longitude=28.09377,elevation=428,velocity=0,heading=304,grade=0,fuel_consumption=25 1451606407000000000", "readings,name=truck_2,fleet=North,driver=Derek,model=F-150 load_capacity=2000,fuel_capacity=200,nominal_fuel_consumption=15,latitude=24.5208,longitude=28.09377,elevation=428,velocity=0,heading=304,grade=0,fuel_consumption=25 1451609408000000000", "readings,fleet=South,name=truck_0,driver=Trish,model=H-2,device_version=v2.3 fuel_consumption=25,grade=0 1451629409000000000", @@ -745,7 +737,7 @@ TEST(testCase, smlProcess_json1_Test) { " }\n" " }\n" "]"; - int ret = smlProcess(info, (char **)(&sql), -1); + int ret = smlProcess(info, (char **)(&sql), 1); ASSERT_EQ(ret, 0); // case 1 @@ -1202,22 +1194,68 @@ TEST(testCase, sml_TD15662_Test) { SRequestObj *request = (SRequestObj *)createRequest((STscObj *)taos, NULL, NULL, TSDB_SQL_INSERT); ASSERT_NE(request, nullptr); - SSmlHandle *info = smlBuildSmlInfo(taos, request, TSDB_SML_LINE_PROTOCOL, TSDB_SML_TIMESTAMP_NANO_SECONDS); + SSmlHandle *info = smlBuildSmlInfo(taos, request, TSDB_SML_LINE_PROTOCOL, TSDB_SML_TIMESTAMP_MILLI_SECONDS); ASSERT_NE(info, nullptr); const char *sql[] = { - "iyyyje,id=iyyyje_41943_1303,t0=t,t1=127i8,t2=32767i16,t3=2147483647i32,t4=9223372036854775807i64,t5=11.12345f32,t6=22.123456789f64,t7=\"binaryTagValue\",t8=L\"ncharTagValue\" c0=false,c1=127i8,c2=32767i16,c3=2147483647i32,c4=9223372036854775807i64,c5=11.12345f32,c6=22.123456789f64,c7=\"binaryColValue\",c8=L\"ncharColValue\",c9=7u64 1626006833639000000", + "hetrey c0=f,c1=127i8 1626006833639", + "hetrey,t1=r c0=f,c1=127i8 1626006833640", }; int ret = smlProcess(info, (char **)sql, sizeof(sql) / sizeof(sql[0])); ASSERT_EQ(ret, 0); - // case 1 - TAOS_RES *res = taos_query(taos, "select * from t_a5615048edae55218a22a149edebdc82"); - ASSERT_NE(res, nullptr); + destroyRequest(request); + smlDestroyInfo(info); +} - TAOS_ROW row = taos_fetch_row(res); - int64_t ts = *(int64_t*)row[0]; - ASSERT_EQ(ts, 1626006833639000000); +TEST(testCase, sml_TD15735_Test) { + TAOS *taos = taos_connect("localhost", "root", "taosdata", NULL, 0); + ASSERT_NE(taos, nullptr); - taos_free_result(res); + TAOS_RES* pRes = taos_query(taos, "create database if not exists sml_db"); + taos_free_result(pRes); + + pRes = taos_query(taos, "use sml_db"); + taos_free_result(pRes); + + SRequestObj *request = (SRequestObj *)createRequest((STscObj*)taos, NULL, NULL, TSDB_SQL_INSERT); + ASSERT_NE(request, nullptr); + + SSmlHandle *info = smlBuildSmlInfo(taos, request, TSDB_SML_TELNET_PROTOCOL, TSDB_SML_TIMESTAMP_NANO_SECONDS); + ASSERT_NE(info, nullptr); + + const char *sql[1] = { + "{'metric': 'pekoiw', 'timestamp': {'value': 1626006833639000000, 'type': 'ns'}, 'value': {'value': False, 'type': 'bool'}, 'tags': {'t0': {'value': True, 'type': 'bool'}, 't1': {'value': 127, 'type': 'tinyint'}, 't2': {'value': 32767, 'type': 'smallint'}, 't3': {'value': 2147483647, 'type': 'int'}, 't4': {'value': 9223372036854775807, 'type': 'bigint'}, 't5': {'value': 11.12345027923584, 'type': 'float'}, 't6': {'value': 22.123456789, 'type': 'double'}, 't7': {'value': 'binaryTagValue', 'type': 'binary'}, 't8': {'value': 'ncharTagValue', 'type': 'nchar'}}}", + }; + int32_t ret = smlProcess(info, (char**)sql, sizeof(sql)/sizeof(sql[0])); + ASSERT_NE(ret, 0); + + destroyRequest(request); + smlDestroyInfo(info); +} + +TEST(testCase, sml_TD15742_Test) { + TAOS *taos = taos_connect("localhost", "root", "taosdata", NULL, 0); + ASSERT_NE(taos, nullptr); + + TAOS_RES* pRes = taos_query(taos, "create database if not exists TD15742"); + taos_free_result(pRes); + + pRes = taos_query(taos, "use TD15742"); + taos_free_result(pRes); + + SRequestObj *request = (SRequestObj *)createRequest((STscObj*)taos, NULL, NULL, TSDB_SQL_INSERT); + ASSERT_NE(request, nullptr); + + SSmlHandle *info = smlBuildSmlInfo(taos, request, TSDB_SML_LINE_PROTOCOL, TSDB_SML_TIMESTAMP_MILLI_SECONDS); + ASSERT_NE(info, nullptr); + + const char *sql[] = { + "test_ms,t0=t c0=f 1626006833641", + }; + int ret = smlProcess(info, (char**)sql, sizeof(sql)/sizeof(sql[0])); + ASSERT_EQ(ret, 0); + + destroyRequest(request); + smlDestroyInfo(info); } \ No newline at end of file diff --git a/source/common/src/tdatablock.c b/source/common/src/tdatablock.c index 4d77f4eb71e3a2e59b6d3fca3996507e12eac8c5..51bcd05ea12a32eb9ac94d1aadea72c37ab531bb 100644 --- a/source/common/src/tdatablock.c +++ b/source/common/src/tdatablock.c @@ -122,10 +122,14 @@ int32_t colDataAppend(SColumnInfoData* pColumnInfoData, uint32_t currentRow, con dataLen = 0; } else if (*pData == TSDB_DATA_TYPE_NCHAR) { dataLen = varDataTLen(pData + CHAR_BYTES); - } else if (*pData == TSDB_DATA_TYPE_BIGINT || *pData == TSDB_DATA_TYPE_DOUBLE) { - dataLen = LONG_BYTES; + } else if (*pData == TSDB_DATA_TYPE_DOUBLE) { + dataLen = DOUBLE_BYTES; } else if (*pData == TSDB_DATA_TYPE_BOOL) { dataLen = CHAR_BYTES; + } else if (*pData == TSDB_DATA_TYPE_JSON) { + dataLen = kvRowLen(pData + CHAR_BYTES); + } else { + ASSERT(0); } dataLen += CHAR_BYTES; } @@ -337,7 +341,7 @@ size_t blockDataGetNumOfCols(const SSDataBlock* pBlock) { size_t blockDataGetNumOfRows(const SSDataBlock* pBlock) { return pBlock->info.rows; } -int32_t blockDataUpdateTsWindow(SSDataBlock* pDataBlock) { +int32_t blockDataUpdateTsWindow(SSDataBlock* pDataBlock, int32_t tsColumnIndex) { if (pDataBlock == NULL || pDataBlock->info.rows <= 0) { return 0; } @@ -346,7 +350,8 @@ int32_t blockDataUpdateTsWindow(SSDataBlock* pDataBlock) { return -1; } - SColumnInfoData* pColInfoData = taosArrayGet(pDataBlock->pDataBlock, 0); + int32_t index = (tsColumnIndex == -1)? 0:tsColumnIndex; + SColumnInfoData* pColInfoData = taosArrayGet(pDataBlock->pDataBlock, index); if (pColInfoData->info.type != TSDB_DATA_TYPE_TIMESTAMP) { return 0; } @@ -1533,7 +1538,7 @@ int32_t buildSubmitReqFromDataBlock(SSubmitReq** pReq, const SArray* pDataBlocks int32_t msgLen = sizeof(SSubmitReq); int32_t numOfBlks = 0; SRowBuilder rb = {0}; - tdSRowInit(&rb, 0); // TODO: use the latest version + tdSRowInit(&rb, pTSchema->version); // TODO: use the latest version for (int32_t i = 0; i < sz; ++i) { SSDataBlock* pDataBlock = taosArrayGet(pDataBlocks, i); diff --git a/source/common/src/tdataformat.c b/source/common/src/tdataformat.c index 8aa8ed2f14c3fbdc060d0529a34d6f7588d88026..f82df0d9bc5b670f12f896406a8b3df399a52a60 100644 --- a/source/common/src/tdataformat.c +++ b/source/common/src/tdataformat.c @@ -855,7 +855,7 @@ SDataCols *tdNewDataCols(int maxCols, int maxRows) { pCols->maxCols = maxCols; pCols->numOfRows = 0; pCols->numOfCols = 0; - // pCols->bitmapMode = 0; // calloc already set 0 + pCols->bitmapMode = TSDB_BITMODE_DEFAULT; if (maxCols > 0) { pCols->cols = (SDataCol *)taosMemoryCalloc(maxCols, sizeof(SDataCol)); @@ -899,7 +899,7 @@ int tdInitDataCols(SDataCols *pCols, STSchema *pSchema) { #endif pCols->numOfRows = 0; - pCols->bitmapMode = 0; + pCols->bitmapMode = TSDB_BITMODE_DEFAULT; pCols->numOfCols = schemaNCols(pSchema); for (i = 0; i < schemaNCols(pSchema); ++i) { @@ -1077,7 +1077,7 @@ void tdResetKVRowBuilder(SKVRowBuilder *pBuilder) { SKVRow tdGetKVRowFromBuilder(SKVRowBuilder *pBuilder) { int tlen = sizeof(SColIdx) * pBuilder->nCols + pBuilder->size; - if (tlen == 0) return NULL; + // if (tlen == 0) return NULL; // nCols == 0 means no tags tlen += TD_KV_ROW_HEAD_SIZE; @@ -1087,8 +1087,10 @@ SKVRow tdGetKVRowFromBuilder(SKVRowBuilder *pBuilder) { kvRowSetNCols(row, pBuilder->nCols); kvRowSetLen(row, tlen); - memcpy(kvRowColIdx(row), pBuilder->pColIdx, sizeof(SColIdx) * pBuilder->nCols); - memcpy(kvRowValues(row), pBuilder->buf, pBuilder->size); + if(pBuilder->nCols > 0){ + memcpy(kvRowColIdx(row), pBuilder->pColIdx, sizeof(SColIdx) * pBuilder->nCols); + memcpy(kvRowValues(row), pBuilder->buf, pBuilder->size); + } return row; } diff --git a/source/common/src/tglobal.c b/source/common/src/tglobal.c index 7b5663c0a92845c0e81bde044ea42cf2f7e18303..1b61a0bc606aa9fd479cf996668756d2b88f4702 100644 --- a/source/common/src/tglobal.c +++ b/source/common/src/tglobal.c @@ -40,11 +40,11 @@ bool tsPrintAuth = false; // multi process int32_t tsMultiProcess = 0; -int32_t tsMnodeShmSize = TSDB_MAX_WAL_SIZE * 2 + 128; -int32_t tsVnodeShmSize = TSDB_MAX_WAL_SIZE * 10 + 128; -int32_t tsQnodeShmSize = TSDB_MAX_WAL_SIZE * 4 + 128; -int32_t tsSnodeShmSize = TSDB_MAX_WAL_SIZE * 4 + 128; -int32_t tsBnodeShmSize = TSDB_MAX_WAL_SIZE * 4 + 128; +int32_t tsMnodeShmSize = TSDB_MAX_WAL_SIZE * 2 + 1024; +int32_t tsVnodeShmSize = TSDB_MAX_WAL_SIZE * 10 + 1024; +int32_t tsQnodeShmSize = TSDB_MAX_WAL_SIZE * 4 + 1024; +int32_t tsSnodeShmSize = TSDB_MAX_WAL_SIZE * 4 + 1024; +int32_t tsBnodeShmSize = TSDB_MAX_WAL_SIZE * 4 + 1024; int32_t tsNumOfShmThreads = 1; // queue & threads @@ -78,6 +78,7 @@ char tsTelemServer[TSDB_FQDN_LEN] = "telemetry.taosdata.com"; uint16_t tsTelemPort = 80; // schemaless +char tsSmlTagName[TSDB_COL_NAME_LEN] = "_tag_null"; char tsSmlChildTableName[TSDB_TABLE_NAME_LEN] = ""; //user defined child table name can be specified in tag value. //If set to empty system will generate table name using MD5 hash. bool tsSmlDataFormat = true; // true means that the name and order of cols in each line are the same(only for influx protocol) @@ -326,6 +327,7 @@ static int32_t taosAddClientCfg(SConfig *pCfg) { if (cfgAddBool(pCfg, "keepColumnName", tsKeepOriginalColumnName, 1) != 0) return -1; if (cfgAddInt32(pCfg, "queryPolicy", tsQueryPolicy, 1, 3, 1) != 0) return -1; if (cfgAddString(pCfg, "smlChildTableName", "", 1) != 0) return -1; + if (cfgAddString(pCfg, "smlTagNullName", tsSmlTagName, 1) != 0) return -1; if (cfgAddBool(pCfg, "smlDataFormat", tsSmlDataFormat, 1) != 0) return -1; tsNumOfTaskQueueThreads = tsNumOfCores / 4; @@ -380,11 +382,11 @@ static int32_t taosAddServerCfg(SConfig *pCfg) { if (cfgAddBool(pCfg, "deadLockKillQuery", tsDeadLockKillQuery, 0) != 0) return -1; if (cfgAddInt32(pCfg, "multiProcess", tsMultiProcess, 0, 2, 0) != 0) return -1; - if (cfgAddInt32(pCfg, "mnodeShmSize", tsMnodeShmSize, TSDB_MAX_WAL_SIZE + 128, INT32_MAX, 0) != 0) return -1; - if (cfgAddInt32(pCfg, "vnodeShmSize", tsVnodeShmSize, TSDB_MAX_WAL_SIZE + 128, INT32_MAX, 0) != 0) return -1; - if (cfgAddInt32(pCfg, "qnodeShmSize", tsQnodeShmSize, TSDB_MAX_WAL_SIZE + 128, INT32_MAX, 0) != 0) return -1; - if (cfgAddInt32(pCfg, "snodeShmSize", tsSnodeShmSize, TSDB_MAX_WAL_SIZE + 128, INT32_MAX, 0) != 0) return -1; - if (cfgAddInt32(pCfg, "bnodeShmSize", tsBnodeShmSize, TSDB_MAX_WAL_SIZE + 128, INT32_MAX, 0) != 0) return -1; + if (cfgAddInt32(pCfg, "mnodeShmSize", tsMnodeShmSize, TSDB_MAX_WAL_SIZE * 2 + 1024, INT32_MAX, 0) != 0) return -1; + if (cfgAddInt32(pCfg, "vnodeShmSize", tsVnodeShmSize, TSDB_MAX_WAL_SIZE * 2 + 1024, INT32_MAX, 0) != 0) return -1; + if (cfgAddInt32(pCfg, "qnodeShmSize", tsQnodeShmSize, TSDB_MAX_WAL_SIZE * 2 + 1024, INT32_MAX, 0) != 0) return -1; + if (cfgAddInt32(pCfg, "snodeShmSize", tsSnodeShmSize, TSDB_MAX_WAL_SIZE * 2 + 1024, INT32_MAX, 0) != 0) return -1; + if (cfgAddInt32(pCfg, "bnodeShmSize", tsBnodeShmSize, TSDB_MAX_WAL_SIZE * 2 + 1024, INT32_MAX, 0) != 0) return -1; if (cfgAddInt32(pCfg, "mumOfShmThreads", tsNumOfShmThreads, 1, 1024, 0) != 0) return -1; tsNumOfRpcThreads = tsNumOfCores / 2; @@ -522,6 +524,7 @@ static int32_t taosSetClientCfg(SConfig *pCfg) { } tstrncpy(tsSmlChildTableName, cfgGetItem(pCfg, "smlChildTableName")->str, TSDB_TABLE_NAME_LEN); + tstrncpy(tsSmlTagName, cfgGetItem(pCfg, "smlTagNullName")->str, TSDB_COL_NAME_LEN); tsSmlDataFormat = cfgGetItem(pCfg, "smlDataFormat")->bval; tsShellActivityTimer = cfgGetItem(pCfg, "shellActivityTimer")->i32; diff --git a/source/common/src/tmsg.c b/source/common/src/tmsg.c index cc333ae5c8cb730390120e4d5d25a36318bfaf31..1108ea1e9fea8cba6ca593f1ca7695a28f6ba003 100644 --- a/source/common/src/tmsg.c +++ b/source/common/src/tmsg.c @@ -28,7 +28,7 @@ #undef TD_MSG_SEG_CODE_ #include "tmsgdef.h" -int32_t tInitSubmitMsgIter(const SSubmitReq *pMsg, SSubmitMsgIter *pIter) { +int32_t tInitSubmitMsgIter(SSubmitReq *pMsg, SSubmitMsgIter *pIter) { if (pMsg == NULL) { terrno = TSDB_CODE_TDB_SUBMIT_MSG_MSSED_UP; return -1; @@ -102,7 +102,7 @@ STSRow *tGetSubmitBlkNext(SSubmitBlkIter *pIter) { } } -int32_t tPrintFixedSchemaSubmitReq(const SSubmitReq *pReq, STSchema *pTschema) { +int32_t tPrintFixedSchemaSubmitReq(SSubmitReq *pReq, STSchema *pTschema) { SSubmitMsgIter msgIter = {0}; if (tInitSubmitMsgIter(pReq, &msgIter) < 0) return -1; while (true) { @@ -3817,7 +3817,7 @@ int tDecodeSVCreateStbReq(SDecoder *pCoder, SVCreateStbReq *pReq) { STSchema *tdGetSTSChemaFromSSChema(SSchema **pSchema, int32_t nCols) { STSchemaBuilder schemaBuilder = {0}; - if (tdInitTSchemaBuilder(&schemaBuilder, 0) < 0) { + if (tdInitTSchemaBuilder(&schemaBuilder, 1) < 0) { return NULL; } diff --git a/source/common/src/tname.c b/source/common/src/tname.c index 0764ea84b9f54b0485e90a2c0bef5b3b5e771a1e..104dee261c9f64c7c8859228dcb0595f4b4df2c0 100644 --- a/source/common/src/tname.c +++ b/source/common/src/tname.c @@ -308,13 +308,10 @@ static int compareKv(const void* p1, const void* p2) { * use stable name and tags to grearate child table name */ void buildChildTableName(RandTableName* rName) { - int32_t size = taosArrayGetSize(rName->tags); - ASSERT(size > 0); - taosArraySort(rName->tags, compareKv); - SStringBuilder sb = {0}; taosStringBuilderAppendStringLen(&sb, rName->sTableName, rName->sTableNameLen); - for (int j = 0; j < size; ++j) { + taosArraySort(rName->tags, compareKv); + for (int j = 0; j < taosArrayGetSize(rName->tags); ++j) { SSmlKv* tagKv = taosArrayGetP(rName->tags, j); taosStringBuilderAppendStringLen(&sb, tagKv->key, tagKv->keyLen); if(IS_VAR_DATA_TYPE(tagKv->type)){ diff --git a/source/common/src/trow.c b/source/common/src/trow.c index 22bdd960eac7b2e6bcfb6ded211effbdc692f64d..4d0846f6c2957a6d2a1b74dabf60ee76af57287c 100644 --- a/source/common/src/trow.c +++ b/source/common/src/trow.c @@ -341,18 +341,19 @@ int32_t tdSetBitmapValTypeN(void *pBitmap, int16_t nEle, TDRowValT valType, int8 bool tdIsBitmapBlkNorm(const void *pBitmap, int32_t numOfBits, int8_t bitmapMode) { int32_t nBytes = (bitmapMode == 0 ? numOfBits / TD_VTYPE_PARTS : numOfBits / TD_VTYPE_PARTS_I); uint8_t vTypeByte = tdVTypeByte[bitmapMode][TD_VTYPE_NORM]; + uint8_t *qBitmap = (uint8_t*)pBitmap; for (int i = 0; i < nBytes; ++i) { - if (*((uint8_t *)pBitmap) != vTypeByte) { + if (*qBitmap != vTypeByte) { return false; } - pBitmap = POINTER_SHIFT(pBitmap, i); + qBitmap = (uint8_t *)POINTER_SHIFT(pBitmap, i); } int32_t nLeft = numOfBits - nBytes * (bitmapMode == 0 ? TD_VTYPE_BITS : TD_VTYPE_BITS_I); for (int j = 0; j < nLeft; ++j) { uint8_t vType; - tdGetBitmapValType(pBitmap, j, &vType, bitmapMode); + tdGetBitmapValType(qBitmap, j, &vType, bitmapMode); if (vType != TD_VTYPE_NORM) { return false; } @@ -923,7 +924,7 @@ void tdSRowPrint(STSRow *row, STSchema *pSchema, const char *tag) { STSRowIter iter = {0}; tdSTSRowIterInit(&iter, pSchema); tdSTSRowIterReset(&iter, row); - printf("%s >>>", tag); + printf("%s >>>type:%d,sver:%d ", tag, (int32_t)TD_ROW_TYPE(row), (int32_t)TD_ROW_SVER(row)); for (int i = 0; i < pSchema->numOfCols; ++i) { STColumn *stCol = pSchema->columns + i; SCellVal sVal = {255, NULL}; diff --git a/source/dnode/mgmt/exe/dmMain.c b/source/dnode/mgmt/exe/dmMain.c index 6a03ff1bba5d01f201baeb3d824cbb9384f2825b..1a04e83f8127c0c71570c2cef4f300f05da11e67 100644 --- a/source/dnode/mgmt/exe/dmMain.c +++ b/source/dnode/mgmt/exe/dmMain.c @@ -188,13 +188,14 @@ int main(int argc, char const *argv[]) { } if (dmInitLog() != 0) { - printf("failed to start since init log error"); + printf("failed to start since init log error\n"); taosCleanupArgs(); return -1; } if (taosInitCfg(configDir, global.envCmd, global.envFile, global.apolloUrl, global.pArgs, 0) != 0) { dError("failed to start since read config error"); + taosCloseLog(); taosCleanupArgs(); return -1; } diff --git a/source/dnode/mgmt/mgmt_bnode/src/bmWorker.c b/source/dnode/mgmt/mgmt_bnode/src/bmWorker.c index e01992426862492623d2d977120fc5b40e21ed4e..08c9edd854d6a2fa5b1e8ffe98078c86d6436777 100644 --- a/source/dnode/mgmt/mgmt_bnode/src/bmWorker.c +++ b/source/dnode/mgmt/mgmt_bnode/src/bmWorker.c @@ -47,10 +47,8 @@ static inline void bmSendRsp(SRpcMsg *pMsg, int32_t code) { static void bmProcessMonitorQueue(SQueueInfo *pInfo, SRpcMsg *pMsg) { SBnodeMgmt *pMgmt = pInfo->ahandle; - + int32_t code = -1; dTrace("msg:%p, get from bnode-monitor queue", pMsg); - SRpcMsg *pRpc = pMsg; - int32_t code = -1; if (pMsg->msgType == TDMT_MON_BM_INFO) { code = bmProcessGetMonBmInfoReq(pMgmt, pMsg); @@ -58,13 +56,13 @@ static void bmProcessMonitorQueue(SQueueInfo *pInfo, SRpcMsg *pMsg) { terrno = TSDB_CODE_MSG_NOT_PROCESSED; } - if (pRpc->msgType & 1U) { + if (IsReq(pMsg)) { if (code != 0 && terrno != 0) code = terrno; bmSendRsp(pMsg, code); } dTrace("msg:%p, is freed, code:0x%x", pMsg, code); - rpcFreeCont(pRpc->pCont); + rpcFreeCont(pMsg->pCont); taosFreeQitem(pMsg); } diff --git a/source/dnode/mgmt/mgmt_dnode/src/dmWorker.c b/source/dnode/mgmt/mgmt_dnode/src/dmWorker.c index 6a7e0ad322efca98991c362d90d602c3ca692c26..bd78fe46effa60c8286cbd901b299f2506de6533 100644 --- a/source/dnode/mgmt/mgmt_dnode/src/dmWorker.c +++ b/source/dnode/mgmt/mgmt_dnode/src/dmWorker.c @@ -19,7 +19,6 @@ static void *dmStatusThreadFp(void *param) { SDnodeMgmt *pMgmt = param; int64_t lastTime = taosGetTimestampMs(); - setThreadName("dnode-status"); while (1) { @@ -40,7 +39,6 @@ static void *dmStatusThreadFp(void *param) { static void *dmMonitorThreadFp(void *param) { SDnodeMgmt *pMgmt = param; int64_t lastTime = taosGetTimestampMs(); - setThreadName("dnode-monitor"); while (1) { @@ -103,11 +101,9 @@ void dmStopMonitorThread(SDnodeMgmt *pMgmt) { static void dmProcessMgmtQueue(SQueueInfo *pInfo, SRpcMsg *pMsg) { SDnodeMgmt *pMgmt = pInfo->ahandle; int32_t code = -1; - tmsg_t msgType = pMsg->msgType; - bool isRequest = msgType & 1u; - dTrace("msg:%p, will be processed in dnode-mgmt queue, type:%s", pMsg, TMSG_INFO(msgType)); + dTrace("msg:%p, will be processed in dnode queue, type:%s", pMsg, TMSG_INFO(pMsg->msgType)); - switch (msgType) { + switch (pMsg->msgType) { case TDMT_DND_CONFIG_DNODE: code = dmProcessConfigReq(pMgmt, pMsg); break; @@ -149,7 +145,7 @@ static void dmProcessMgmtQueue(SQueueInfo *pInfo, SRpcMsg *pMsg) { break; } - if (isRequest) { + if (IsReq(pMsg)) { if (code != 0 && terrno != 0) code = terrno; SRpcMsg rsp = { .code = code, diff --git a/source/dnode/mgmt/mgmt_mnode/src/mmWorker.c b/source/dnode/mgmt/mgmt_mnode/src/mmWorker.c index cf76d3a167576d6801ed505afc83cdc5e52b14b4..43ee7cd80a5aa1880092a4ca3abe4add2d42249c 100644 --- a/source/dnode/mgmt/mgmt_mnode/src/mmWorker.c +++ b/source/dnode/mgmt/mgmt_mnode/src/mmWorker.c @@ -46,7 +46,7 @@ static void mmProcessQueue(SQueueInfo *pInfo, SRpcMsg *pMsg) { code = mndProcessMsg(pMsg); } - if (IsReq(pMsg) && pMsg->info.handle != NULL && code != TSDB_CODE_MND_ACTION_IN_PROGRESS) { + if (IsReq(pMsg) && pMsg->info.handle != NULL && code != TSDB_CODE_ACTION_IN_PROGRESS) { if (code != 0 && terrno != 0) code = terrno; mmSendRsp(pMsg, code); } @@ -56,28 +56,6 @@ static void mmProcessQueue(SQueueInfo *pInfo, SRpcMsg *pMsg) { taosFreeQitem(pMsg); } -static void mmProcessQueryQueue(SQueueInfo *pInfo, SRpcMsg *pMsg) { - SMnodeMgmt *pMgmt = pInfo->ahandle; - int32_t code = -1; - tmsg_t msgType = pMsg->msgType; - bool isRequest = msgType & 1U; - dTrace("msg:%p, get from mnode-query queue", pMsg); - - pMsg->info.node = pMgmt->pMnode; - code = mndProcessMsg(pMsg); - - if (isRequest) { - if (pMsg->info.handle != NULL && code != 0) { - if (code != 0 && terrno != 0) code = terrno; - mmSendRsp(pMsg, code); - } - } - - dTrace("msg:%p, is freed, code:0x%x", pMsg, code); - rpcFreeCont(pMsg->pCont); - taosFreeQitem(pMsg); -} - static int32_t mmPutNodeMsgToWorker(SSingleWorker *pWorker, SRpcMsg *pMsg) { dTrace("msg:%p, put into worker %s, type:%s", pMsg, pWorker->name, TMSG_INFO(pMsg->msgType)); taosWriteQitem(pWorker->queue, pMsg); @@ -126,14 +104,16 @@ int32_t mmPutRpcMsgToReadQueue(SMnodeMgmt *pMgmt, SRpcMsg *pMsg) { return mmPutRpcMsgToWorker(&pMgmt->readWorker, pMsg); } -int32_t mmPutRpcMsgToSyncQueue(SMnodeMgmt *pMgmt, SRpcMsg *pMsg) { return mmPutRpcMsgToWorker(&pMgmt->syncWorker, pMsg); } +int32_t mmPutRpcMsgToSyncQueue(SMnodeMgmt *pMgmt, SRpcMsg *pMsg) { + return mmPutRpcMsgToWorker(&pMgmt->syncWorker, pMsg); +} int32_t mmStartWorker(SMnodeMgmt *pMgmt) { SSingleWorkerCfg qCfg = { .min = tsNumOfMnodeQueryThreads, .max = tsNumOfMnodeQueryThreads, .name = "mnode-query", - .fp = (FItem)mmProcessQueryQueue, + .fp = (FItem)mmProcessQueue, .param = pMgmt, }; if (tSingleWorkerInit(&pMgmt->queryWorker, &qCfg) != 0) { diff --git a/source/dnode/mgmt/mgmt_qnode/src/qmWorker.c b/source/dnode/mgmt/mgmt_qnode/src/qmWorker.c index 444c42717afdbfa8559fdbd083fc5720f7d4d682..35c94b7fbe786434cfb59191c8899949099d0325 100644 --- a/source/dnode/mgmt/mgmt_qnode/src/qmWorker.c +++ b/source/dnode/mgmt/mgmt_qnode/src/qmWorker.c @@ -19,70 +19,39 @@ static inline void qmSendRsp(SRpcMsg *pMsg, int32_t code) { SRpcMsg rsp = { .code = code, - .info = pMsg->info, .pCont = pMsg->info.rsp, .contLen = pMsg->info.rspLen, + .info = pMsg->info, }; tmsgSendRsp(&rsp); } -static void qmProcessMonitorQueue(SQueueInfo *pInfo, SRpcMsg *pMsg) { +static void qmProcessQueue(SQueueInfo *pInfo, SRpcMsg *pMsg) { SQnodeMgmt *pMgmt = pInfo->ahandle; + int32_t code = -1; + dTrace("msg:%p, get from qnode queue", pMsg); - dTrace("msg:%p, get from qnode-monitor queue", pMsg); - SRpcMsg *pRpc = pMsg; - int32_t code = -1; - - if (pMsg->msgType == TDMT_MON_QM_INFO) { - code = qmProcessGetMonitorInfoReq(pMgmt, pMsg); - } else { - terrno = TSDB_CODE_MSG_NOT_PROCESSED; + switch (pMsg->msgType) { + case TDMT_MON_QM_INFO: + code = qmProcessGetMonitorInfoReq(pMgmt, pMsg); + break; + default: + code = qndProcessQueryMsg(pMgmt->pQnode, pMsg); + break; } - if (pRpc->msgType & 1U) { + if (IsReq(pMsg) && code != TSDB_CODE_ACTION_IN_PROGRESS) { if (code != 0 && terrno != 0) code = terrno; qmSendRsp(pMsg, code); } - dTrace("msg:%p, is freed, code:0x%x", pMsg, code); - rpcFreeCont(pRpc->pCont); - taosFreeQitem(pMsg); -} - -static void qmProcessQueryQueue(SQueueInfo *pInfo, SRpcMsg *pMsg) { - SQnodeMgmt *pMgmt = pInfo->ahandle; - - dTrace("msg:%p, get from qnode-query queue", pMsg); - SRpcMsg *pRpc = pMsg; - int32_t code = qndProcessQueryMsg(pMgmt->pQnode, pRpc); - - if (pRpc->msgType & 1U && code != 0) { - qmSendRsp(pMsg, code); - } - - dTrace("msg:%p, is freed, code:0x%x", pMsg, code); - rpcFreeCont(pMsg->pCont); - taosFreeQitem(pMsg); -} - -static void qmProcessFetchQueue(SQueueInfo *pInfo, SRpcMsg *pMsg) { - SQnodeMgmt *pMgmt = pInfo->ahandle; - - dTrace("msg:%p, get from qnode-fetch queue", pMsg); - SRpcMsg *pRpc = pMsg; - int32_t code = qndProcessFetchMsg(pMgmt->pQnode, pRpc); - - if (pRpc->msgType & 1U && code != 0) { - qmSendRsp(pMsg, code); - } - dTrace("msg:%p, is freed, code:0x%x", pMsg, code); rpcFreeCont(pMsg->pCont); taosFreeQitem(pMsg); } static int32_t qmPutNodeMsgToWorker(SSingleWorker *pWorker, SRpcMsg *pMsg) { - dTrace("msg:%p, put into worker %s", pMsg, pWorker->name); + dTrace("msg:%p, put into worker %s, type:%s", pMsg, pWorker->name, TMSG_INFO(pMsg->msgType)); taosWriteQitem(pWorker->queue, pMsg); return 0; } @@ -101,9 +70,7 @@ int32_t qmPutNodeMsgToMonitorQueue(SQnodeMgmt *pMgmt, SRpcMsg *pMsg) { static int32_t qmPutRpcMsgToWorker(SQnodeMgmt *pMgmt, SSingleWorker *pWorker, SRpcMsg *pRpc) { SRpcMsg *pMsg = taosAllocateQitem(sizeof(SRpcMsg), RPC_QITEM); - if (pMsg == NULL) { - return -1; - } + if (pMsg == NULL) return -1; dTrace("msg:%p, create and put into worker:%s, type:%s", pMsg, pWorker->name, TMSG_INFO(pRpc->msgType)); memcpy(pMsg, pRpc, sizeof(SRpcMsg)); @@ -141,7 +108,7 @@ int32_t qmStartWorker(SQnodeMgmt *pMgmt) { .min = tsNumOfVnodeQueryThreads, .max = tsNumOfVnodeQueryThreads, .name = "qnode-query", - .fp = (FItem)qmProcessQueryQueue, + .fp = (FItem)qmProcessQueue, .param = pMgmt, }; @@ -154,7 +121,7 @@ int32_t qmStartWorker(SQnodeMgmt *pMgmt) { .min = tsNumOfQnodeFetchThreads, .max = tsNumOfQnodeFetchThreads, .name = "qnode-fetch", - .fp = (FItem)qmProcessFetchQueue, + .fp = (FItem)qmProcessQueue, .param = pMgmt, }; @@ -167,7 +134,7 @@ int32_t qmStartWorker(SQnodeMgmt *pMgmt) { .min = 1, .max = 1, .name = "qnode-monitor", - .fp = (FItem)qmProcessMonitorQueue, + .fp = (FItem)qmProcessQueue, .param = pMgmt, }; if (tSingleWorkerInit(&pMgmt->monitorWorker, &mCfg) != 0) { diff --git a/source/dnode/mgmt/mgmt_snode/src/smWorker.c b/source/dnode/mgmt/mgmt_snode/src/smWorker.c index fcfc4f4cee561e00bf4cc31a706898ee75fa2cc4..34a205232e69f057d879188fe4ed98df5b378e3a 100644 --- a/source/dnode/mgmt/mgmt_snode/src/smWorker.c +++ b/source/dnode/mgmt/mgmt_snode/src/smWorker.c @@ -28,10 +28,8 @@ static inline void smSendRsp(SRpcMsg *pMsg, int32_t code) { static void smProcessMonitorQueue(SQueueInfo *pInfo, SRpcMsg *pMsg) { SSnodeMgmt *pMgmt = pInfo->ahandle; - + int32_t code = -1; dTrace("msg:%p, get from snode-monitor queue", pMsg); - SRpcMsg *pRpc = pMsg; - int32_t code = -1; if (pMsg->msgType == TDMT_MON_SM_INFO) { code = smProcessGetMonitorInfoReq(pMgmt, pMsg); @@ -39,13 +37,13 @@ static void smProcessMonitorQueue(SQueueInfo *pInfo, SRpcMsg *pMsg) { terrno = TSDB_CODE_MSG_NOT_PROCESSED; } - if (pRpc->msgType & 1U) { + if (IsReq(pMsg)) { if (code != 0 && terrno != 0) code = terrno; smSendRsp(pMsg, code); } dTrace("msg:%p, is freed, code:0x%x", pMsg, code); - rpcFreeCont(pRpc->pCont); + rpcFreeCont(pMsg->pCont); taosFreeQitem(pMsg); } diff --git a/source/dnode/mgmt/mgmt_vnode/src/vmHandle.c b/source/dnode/mgmt/mgmt_vnode/src/vmHandle.c index 3da3d90ae17acf8f20234c0ef72fc5cd882b1af0..f28209f9828062f8ed27f194914b4ac11848735a 100644 --- a/source/dnode/mgmt/mgmt_vnode/src/vmHandle.c +++ b/source/dnode/mgmt/mgmt_vnode/src/vmHandle.c @@ -314,6 +314,10 @@ SArray *vmGetMsgHandles() { if (dmSetMgmtHandle(pArray, TDMT_VND_TASK_MERGE_EXEC, vmPutNodeMsgToMergeQueue, 0) == NULL) goto _OVER; if (dmSetMgmtHandle(pArray, TDMT_VND_TASK_WRITE_EXEC, vmPutNodeMsgToWriteQueue, 0) == NULL) goto _OVER; if (dmSetMgmtHandle(pArray, TDMT_VND_STREAM_TRIGGER, vmPutNodeMsgToFetchQueue, 0) == NULL) goto _OVER; + if (dmSetMgmtHandle(pArray, TDMT_VND_TASK_RUN, vmPutNodeMsgToFetchQueue, 0) == NULL) goto _OVER; + if (dmSetMgmtHandle(pArray, TDMT_VND_TASK_DISPATCH, vmPutNodeMsgToFetchQueue, 0) == NULL) goto _OVER; + if (dmSetMgmtHandle(pArray, TDMT_VND_TASK_RECOVER, vmPutNodeMsgToFetchQueue, 0) == NULL) goto _OVER; + if (dmSetMgmtHandle(pArray, TDMT_VND_ALTER_VNODE, vmPutNodeMsgToWriteQueue, 0) == NULL) goto _OVER; if (dmSetMgmtHandle(pArray, TDMT_VND_COMPACT_VNODE, vmPutNodeMsgToWriteQueue, 0) == NULL) goto _OVER; if (dmSetMgmtHandle(pArray, TDMT_DND_CREATE_VNODE, vmPutNodeMsgToMgmtQueue, 0) == NULL) goto _OVER; diff --git a/source/dnode/mgmt/mgmt_vnode/src/vmWorker.c b/source/dnode/mgmt/mgmt_vnode/src/vmWorker.c index 9f77180cd890f73c514c22be813c538c8564e513..6183794bdd9c87da091a64c5333ad42f70dd824e 100644 --- a/source/dnode/mgmt/mgmt_vnode/src/vmWorker.c +++ b/source/dnode/mgmt/mgmt_vnode/src/vmWorker.c @@ -22,21 +22,19 @@ static inline void vmSendRsp(SRpcMsg *pMsg, int32_t code) { SRpcMsg rsp = { .code = code, - .info = pMsg->info, .pCont = pMsg->info.rsp, .contLen = pMsg->info.rspLen, + .info = pMsg->info, }; tmsgSendRsp(&rsp); } -static void vmProcessMgmtMonitorQueue(SQueueInfo *pInfo, SRpcMsg *pMsg) { +static void vmProcessQueue(SQueueInfo *pInfo, SRpcMsg *pMsg) { SVnodeMgmt *pMgmt = pInfo->ahandle; + int32_t code = -1; + dTrace("msg:%p, get from vnode queue, type:%s", pMsg, TMSG_INFO(pMsg->msgType)); - int32_t code = -1; - tmsg_t msgType = pMsg->msgType; - dTrace("msg:%p, get from vnode queue, type:%s", pMsg, TMSG_INFO(msgType)); - - switch (msgType) { + switch (pMsg->msgType) { case TDMT_MON_VM_INFO: code = vmProcessGetMonitorInfoReq(pMgmt, pMsg); break; @@ -54,7 +52,7 @@ static void vmProcessMgmtMonitorQueue(SQueueInfo *pInfo, SRpcMsg *pMsg) { dError("msg:%p, not processed in vnode queue", pMsg); } - if (msgType & 1u) { + if (IsReq(pMsg)) { if (code != 0 && terrno != 0) code = terrno; vmSendRsp(pMsg, code); } @@ -72,11 +70,10 @@ static void vmProcessQueryQueue(SQueueInfo *pInfo, SRpcMsg *pMsg) { if (code != 0) { if (terrno != 0) code = terrno; vmSendRsp(pMsg, code); - - dTrace("msg:%p, is freed, code:0x%x", pMsg, code); - rpcFreeCont(pMsg->pCont); - taosFreeQitem(pMsg); } + dTrace("msg:%p, is freed, code:0x%x", pMsg, code); + rpcFreeCont(pMsg->pCont); + taosFreeQitem(pMsg); } static void vmProcessFetchQueue(SQueueInfo *pInfo, SRpcMsg *pMsg) { @@ -87,17 +84,15 @@ static void vmProcessFetchQueue(SQueueInfo *pInfo, SRpcMsg *pMsg) { if (code != 0) { if (terrno != 0) code = terrno; vmSendRsp(pMsg, code); - - dTrace("msg:%p, is freed, code:0x%x", pMsg, code); - rpcFreeCont(pMsg->pCont); - taosFreeQitem(pMsg); } + dTrace("msg:%p, is freed, code:0x%x", pMsg, code); + rpcFreeCont(pMsg->pCont); + taosFreeQitem(pMsg); } static void vmProcessWriteQueue(SQueueInfo *pInfo, STaosQall *qall, int32_t numOfMsgs) { SVnodeObj *pVnode = pInfo->ahandle; - - SArray *pArray = taosArrayInit(numOfMsgs, sizeof(SRpcMsg *)); + SArray *pArray = taosArrayInit(numOfMsgs, sizeof(SRpcMsg *)); if (pArray == NULL) { dError("failed to process %d msgs in write-queue since %s", numOfMsgs, terrstr()); return; @@ -116,7 +111,9 @@ static void vmProcessWriteQueue(SQueueInfo *pInfo, STaosQall *qall, int32_t numO for (int i = 0; i < taosArrayGetSize(pArray); i++) { SRpcMsg *pMsg = *(SRpcMsg **)taosArrayGet(pArray, i); - SRpcMsg rsp = {.info = pMsg->info, .pCont = NULL, .contLen = 0}; + SRpcMsg rsp = {.info = pMsg->info}; + + vnodePreprocessReq(pVnode->pImpl, pMsg); int32_t ret = syncPropose(vnodeGetSyncHandle(pVnode->pImpl), pMsg, false); if (ret == TAOS_SYNC_PROPOSE_NOT_LEADER) { @@ -126,12 +123,10 @@ static void vmProcessWriteQueue(SQueueInfo *pInfo, STaosQall *qall, int32_t numO syncGetEpSet(vnodeGetSyncHandle(pVnode->pImpl), &newEpSet); newEpSet.inUse = (newEpSet.inUse + 1) % newEpSet.numOfEps; tmsgSendRedirectRsp(&rsp, &newEpSet); - } else if (ret == TAOS_SYNC_PROPOSE_OTHER_ERROR) { rsp.code = TSDB_CODE_SYN_INTERNAL_ERROR; tmsgSendRsp(&rsp); } else if (ret == TAOS_SYNC_PROPOSE_SUCCESS) { - // ok // send response in applyQ } else { assert(0); @@ -150,16 +145,13 @@ static void vmProcessWriteQueue(SQueueInfo *pInfo, STaosQall *qall, int32_t numO static void vmProcessApplyQueue(SQueueInfo *pInfo, STaosQall *qall, int32_t numOfMsgs) { SVnodeObj *pVnode = pInfo->ahandle; - SRpcMsg *pMsg = NULL; - SRpcMsg rsp; for (int32_t i = 0; i < numOfMsgs; ++i) { + SRpcMsg *pMsg = NULL; taosGetQitem(qall, (void **)&pMsg); // init response rpc msg - rsp.code = 0; - rsp.pCont = NULL; - rsp.contLen = 0; + SRpcMsg rsp = {0}; // get original rpc msg assert(pMsg->msgType == TDMT_VND_SYNC_APPLY_MSG); @@ -178,7 +170,6 @@ static void vmProcessApplyQueue(SQueueInfo *pInfo, STaosQall *qall, int32_t numO rpcFreeCont(originalRpcMsg.pCont); // if leader, send response - // if (pMsg->rpcMsg.handle != NULL && pMsg->rpcMsg.ahandle != NULL) { if (pMsg->info.handle != NULL) { rsp.info = pMsg->info; tmsgSendRsp(&rsp); @@ -191,21 +182,19 @@ static void vmProcessApplyQueue(SQueueInfo *pInfo, STaosQall *qall, int32_t numO static void vmProcessSyncQueue(SQueueInfo *pInfo, STaosQall *qall, int32_t numOfMsgs) { SVnodeObj *pVnode = pInfo->ahandle; - SRpcMsg *pMsg = NULL; for (int32_t i = 0; i < numOfMsgs; ++i) { + SRpcMsg *pMsg = NULL; taosGetQitem(qall, (void **)&pMsg); - // todo - SRpcMsg *pRsp = NULL; - int32_t ret = vnodeProcessSyncReq(pVnode->pImpl, pMsg, &pRsp); - if (ret != 0) { - // if leader, send response + int32_t code = vnodeProcessSyncReq(pVnode->pImpl, pMsg, NULL); + if (code != 0) { if (pMsg->info.handle != NULL) { - SRpcMsg rsp = {0}; - rsp.code = terrno; - rsp.info = pMsg->info; - dTrace("msg:%p, process sync queue error since code:%s", pMsg, terrstr()); + SRpcMsg rsp = { + .code = (terrno < 0) ? terrno : code, + .info = pMsg->info, + }; + dTrace("msg:%p, failed to process sync queue since %s", pMsg, terrstr()); tmsgSendRsp(&rsp); } } @@ -217,9 +206,9 @@ static void vmProcessSyncQueue(SQueueInfo *pInfo, STaosQall *qall, int32_t numOf static void vmProcessMergeQueue(SQueueInfo *pInfo, STaosQall *qall, int32_t numOfMsgs) { SVnodeObj *pVnode = pInfo->ahandle; - SRpcMsg *pMsg = NULL; for (int32_t i = 0; i < numOfMsgs; ++i) { + SRpcMsg *pMsg = NULL; taosGetQitem(qall, (void **)&pMsg); dTrace("msg:%p, get from vnode-merge queue", pMsg); @@ -227,17 +216,15 @@ static void vmProcessMergeQueue(SQueueInfo *pInfo, STaosQall *qall, int32_t numO if (code != 0) { if (terrno != 0) code = terrno; vmSendRsp(pMsg, code); - - dTrace("msg:%p, is freed, code:0x%x", pMsg, code); - rpcFreeCont(pMsg->pCont); - taosFreeQitem(pMsg); } + dTrace("msg:%p, is freed, code:0x%x", pMsg, code); + rpcFreeCont(pMsg->pCont); + taosFreeQitem(pMsg); } } static int32_t vmPutNodeMsgToQueue(SVnodeMgmt *pMgmt, SRpcMsg *pMsg, EQueueType qtype) { - SRpcMsg *pRpc = pMsg; - SMsgHead *pHead = pRpc->pCont; + SMsgHead *pHead = pMsg->pCont; int32_t code = 0; pHead->contLen = ntohl(pHead->contLen); @@ -251,23 +238,23 @@ static int32_t vmPutNodeMsgToQueue(SVnodeMgmt *pMgmt, SRpcMsg *pMsg, EQueueType switch (qtype) { case QUERY_QUEUE: - dTrace("msg:%p, put into vnode-query worker, type:%s", pMsg, TMSG_INFO(pRpc->msgType)); + dTrace("msg:%p, put into vnode-query worker, type:%s", pMsg, TMSG_INFO(pMsg->msgType)); taosWriteQitem(pVnode->pQueryQ, pMsg); break; case FETCH_QUEUE: - dTrace("msg:%p, put into vnode-fetch worker, type:%s", pMsg, TMSG_INFO(pRpc->msgType)); + dTrace("msg:%p, put into vnode-fetch worker, type:%s", pMsg, TMSG_INFO(pMsg->msgType)); taosWriteQitem(pVnode->pFetchQ, pMsg); break; case WRITE_QUEUE: - dTrace("msg:%p, put into vnode-write worker, type:%s", pMsg, TMSG_INFO(pRpc->msgType)); + dTrace("msg:%p, put into vnode-write worker, type:%s", pMsg, TMSG_INFO(pMsg->msgType)); taosWriteQitem(pVnode->pWriteQ, pMsg); break; case SYNC_QUEUE: - dTrace("msg:%p, put into vnode-sync worker, type:%s", pMsg, TMSG_INFO(pRpc->msgType)); + dTrace("msg:%p, put into vnode-sync worker, type:%s", pMsg, TMSG_INFO(pMsg->msgType)); taosWriteQitem(pVnode->pSyncQ, pMsg); break; case MERGE_QUEUE: - dTrace("msg:%p, put into vnode-merge worker, type:%s", pMsg, TMSG_INFO(pRpc->msgType)); + dTrace("msg:%p, put into vnode-merge worker, type:%s", pMsg, TMSG_INFO(pMsg->msgType)); taosWriteQitem(pVnode->pMergeQ, pMsg); break; default: @@ -309,22 +296,24 @@ int32_t vmPutNodeMsgToMgmtQueue(SVnodeMgmt *pMgmt, SRpcMsg *pMsg) { int32_t vmPutNodeMsgToMonitorQueue(SVnodeMgmt *pMgmt, SRpcMsg *pMsg) { SSingleWorker *pWorker = &pMgmt->monitorWorker; - dTrace("msg:%p, put into vnode-monitor worker, type:%s", pMsg, TMSG_INFO(pMsg->msgType)); taosWriteQitem(pWorker->queue, pMsg); return 0; } static int32_t vmPutRpcMsgToQueue(SVnodeMgmt *pMgmt, SRpcMsg *pRpc, EQueueType qtype) { - SMsgHead *pHead = pRpc->pCont; - + SMsgHead *pHead = pRpc->pCont; SVnodeObj *pVnode = vmAcquireVnode(pMgmt, pHead->vgId); if (pVnode == NULL) return -1; SRpcMsg *pMsg = taosAllocateQitem(sizeof(SRpcMsg), RPC_QITEM); int32_t code = 0; - if (pMsg != NULL) { + if (pMsg == NULL) { + rpcFreeCont(pRpc->pCont); + pRpc->pCont = NULL; + code = -1; + } else { memcpy(pMsg, pRpc, sizeof(SRpcMsg)); switch (qtype) { case WRITE_QUEUE: @@ -429,7 +418,7 @@ int32_t vmAllocQueue(SVnodeMgmt *pMgmt, SVnodeObj *pVnode) { return -1; } - dDebug("vgId:%d, vnode queue is alloced", pVnode->vgId); + dDebug("vgId:%d, queue is alloced", pVnode->vgId); return 0; } @@ -446,7 +435,7 @@ void vmFreeQueue(SVnodeMgmt *pMgmt, SVnodeObj *pVnode) { pVnode->pQueryQ = NULL; pVnode->pFetchQ = NULL; pVnode->pMergeQ = NULL; - dDebug("vgId:%d, vnode queue is freed", pVnode->vgId); + dDebug("vgId:%d, queue is freed", pVnode->vgId); } int32_t vmStartWorker(SVnodeMgmt *pMgmt) { @@ -481,7 +470,7 @@ int32_t vmStartWorker(SVnodeMgmt *pMgmt) { .min = 1, .max = 1, .name = "vnode-mgmt", - .fp = (FItem)vmProcessMgmtMonitorQueue, + .fp = (FItem)vmProcessQueue, .param = pMgmt, }; if (tSingleWorkerInit(&pMgmt->mgmtWorker, &cfg) != 0) { @@ -493,11 +482,11 @@ int32_t vmStartWorker(SVnodeMgmt *pMgmt) { .min = 1, .max = 1, .name = "vnode-monitor", - .fp = (FItem)vmProcessMgmtMonitorQueue, + .fp = (FItem)vmProcessQueue, .param = pMgmt, }; if (tSingleWorkerInit(&pMgmt->monitorWorker, &mCfg) != 0) { - dError("failed to start mnode vnode-monitor worker since %s", terrstr()); + dError("failed to start vnode-monitor worker since %s", terrstr()); return -1; } diff --git a/source/dnode/mgmt/node_mgmt/inc/dmMgmt.h b/source/dnode/mgmt/node_mgmt/inc/dmMgmt.h index 5818b5880190f5db86e4e72557989544db012245..27f1140f2379f2db9a5856ff72ad0fbc0f42d9f2 100644 --- a/source/dnode/mgmt/node_mgmt/inc/dmMgmt.h +++ b/source/dnode/mgmt/node_mgmt/inc/dmMgmt.h @@ -134,10 +134,9 @@ SMgmtWrapper *dmAcquireWrapper(SDnode *pDnode, EDndNodeType nType); int32_t dmMarkWrapper(SMgmtWrapper *pWrapper); void dmReleaseWrapper(SMgmtWrapper *pWrapper); SMgmtInputOpt dmBuildMgmtInputOpt(SMgmtWrapper *pWrapper); - -void dmSetStatus(SDnode *pDnode, EDndRunStatus stype); -void dmProcessServerStartupStatus(SDnode *pDnode, SRpcMsg *pMsg); -void dmProcessNetTestReq(SDnode *pDnode, SRpcMsg *pMsg); +void dmSetStatus(SDnode *pDnode, EDndRunStatus stype); +void dmProcessServerStartupStatus(SDnode *pDnode, SRpcMsg *pMsg); +void dmProcessNetTestReq(SDnode *pDnode, SRpcMsg *pMsg); // dmNodes.c int32_t dmOpenNode(SMgmtWrapper *pWrapper); diff --git a/source/dnode/mgmt/node_mgmt/src/dmMgmt.c b/source/dnode/mgmt/node_mgmt/src/dmMgmt.c index 96285bbe1ca58bc1e3c900cb787aae0e3387e234..787f5e50190fb099d5998d4f604decd8b17feb68 100644 --- a/source/dnode/mgmt/node_mgmt/src/dmMgmt.c +++ b/source/dnode/mgmt/node_mgmt/src/dmMgmt.c @@ -16,6 +16,7 @@ #define _DEFAULT_SOURCE #include "dmMgmt.h" #include "dmNodes.h" +#include "qworker.h" static bool dmRequireNode(SDnode *pDnode, SMgmtWrapper *pWrapper) { SMgmtInputOpt input = dmBuildMgmtInputOpt(pWrapper); @@ -279,38 +280,37 @@ static void dmGetServerStartupStatus(SDnode *pDnode, SServerStatusRsp *pStatus) void dmProcessNetTestReq(SDnode *pDnode, SRpcMsg *pMsg) { dDebug("msg:%p, net test req will be processed", pMsg); - SRpcMsg rsp = {.code = 0, .info = pMsg->info}; + + SRpcMsg rsp = {.info = pMsg->info}; rsp.pCont = rpcMallocCont(pMsg->contLen); if (rsp.pCont == NULL) { rsp.code = TSDB_CODE_OUT_OF_MEMORY; } else { rsp.contLen = pMsg->contLen; } + rpcSendResponse(&rsp); + rpcFreeCont(pMsg->pCont); } void dmProcessServerStartupStatus(SDnode *pDnode, SRpcMsg *pMsg) { dDebug("msg:%p, server startup status req will be processed", pMsg); + SServerStatusRsp statusRsp = {0}; dmGetServerStartupStatus(pDnode, &statusRsp); - SRpcMsg rspMsg = {.info = pMsg->info}; - int32_t rspLen = tSerializeSServerStatusRsp(NULL, 0, &statusRsp); - if (rspLen < 0) { - rspMsg.code = TSDB_CODE_OUT_OF_MEMORY; - goto _OVER; - } - - void *pRsp = rpcMallocCont(rspLen); - if (pRsp == NULL) { - rspMsg.code = TSDB_CODE_OUT_OF_MEMORY; - goto _OVER; + SRpcMsg rsp = {.info = pMsg->info}; + int32_t contLen = tSerializeSServerStatusRsp(NULL, 0, &statusRsp); + if (contLen < 0) { + rsp.code = TSDB_CODE_OUT_OF_MEMORY; + } else { + rsp.pCont = rpcMallocCont(contLen); + if (rsp.pCont != NULL) { + tSerializeSServerStatusRsp(rsp.pCont, contLen, &statusRsp); + rsp.contLen = contLen; + } } - tSerializeSServerStatusRsp(pRsp, rspLen, &statusRsp); - rspMsg.pCont = pRsp; - rspMsg.contLen = rspLen; - -_OVER: - rpcSendResponse(&rspMsg); + rpcSendResponse(&rsp); + rpcFreeCont(pMsg->pCont); } diff --git a/source/dnode/mgmt/node_mgmt/src/dmMonitor.c b/source/dnode/mgmt/node_mgmt/src/dmMonitor.c index 2497da13ec3313429a6f418ab503c8d5cb06f9b9..0b74d865fd5680311c483003a58da1785813a275 100644 --- a/source/dnode/mgmt/node_mgmt/src/dmMonitor.c +++ b/source/dnode/mgmt/node_mgmt/src/dmMonitor.c @@ -161,10 +161,12 @@ void dmGetVnodeLoads(SMonVloadInfo *pInfo) { void dmGetMnodeLoads(SMonMloadInfo *pInfo) { SDnode *pDnode = dmInstance(); SMgmtWrapper *pWrapper = &pDnode->wrappers[MNODE]; - if (tsMultiProcess) { - dmSendLocalRecv(pDnode, TDMT_MON_MM_LOAD, tDeserializeSMonMloadInfo, pInfo); - } else if (pWrapper->pMgmt != NULL) { - mmGetMnodeLoads(pWrapper->pMgmt, pInfo); + if (dmMarkWrapper(pWrapper) == 0) { + if (tsMultiProcess) { + dmSendLocalRecv(pDnode, TDMT_MON_MM_LOAD, tDeserializeSMonMloadInfo, pInfo); + } else if (pWrapper->pMgmt != NULL) { + mmGetMnodeLoads(pWrapper->pMgmt, pInfo); + } + dmReleaseWrapper(pWrapper); } - dmReleaseWrapper(pWrapper); } diff --git a/source/dnode/mgmt/node_mgmt/src/dmProc.c b/source/dnode/mgmt/node_mgmt/src/dmProc.c index d486707c5eee16a77f2e78ee1f804fd0cebe42b6..72878d0d853e2067bdd73d42bb4b001ccfdb5295 100644 --- a/source/dnode/mgmt/node_mgmt/src/dmProc.c +++ b/source/dnode/mgmt/node_mgmt/src/dmProc.c @@ -96,6 +96,11 @@ static inline int32_t dmPushToProcQueue(SProc *proc, SProcQueue *queue, SRpcMsg const int32_t fullLen = headLen + bodyLen + 8; const int64_t handle = (int64_t)pMsg->info.handle; + if (fullLen > queue->total) { + terrno = TSDB_CODE_OUT_OF_RANGE; + return -1; + } + taosThreadMutexLock(&queue->mutex); if (fullLen > queue->avail) { taosThreadMutexUnlock(&queue->mutex); @@ -103,7 +108,7 @@ static inline int32_t dmPushToProcQueue(SProc *proc, SProcQueue *queue, SRpcMsg return -1; } - if (ftype == DND_FUNC_REQ && IsReq(pMsg) && pMsg->code == 0 && handle != 0) { + if (ftype == DND_FUNC_REQ && IsReq(pMsg) && pMsg->code == 0 && handle != 0 && pMsg->info.noResp == 0) { if (taosHashPut(proc->hash, &handle, sizeof(int64_t), &pMsg->info, sizeof(SRpcConnInfo)) != 0) { taosThreadMutexUnlock(&queue->mutex); return -1; @@ -137,13 +142,14 @@ static inline int32_t dmPushToProcQueue(SProc *proc, SProcQueue *queue, SRpcMsg queue->tail = headLen + bodyLen; } else if (remain < 8 + headLen) { memcpy(queue->pBuffer + queue->tail + 8, pHead, remain - 8); - memcpy(queue->pBuffer, (char*)pHead + remain - 8, rawHeadLen - (remain - 8)); + memcpy(queue->pBuffer, (char *)pHead + remain - 8, rawHeadLen - (remain - 8)); if (rawBodyLen > 0) memcpy(queue->pBuffer + headLen - (remain - 8), pBody, rawBodyLen); queue->tail = headLen - (remain - 8) + bodyLen; } else if (remain < 8 + headLen + bodyLen) { memcpy(queue->pBuffer + queue->tail + 8, pHead, rawHeadLen); if (rawBodyLen > 0) memcpy(queue->pBuffer + queue->tail + 8 + headLen, pBody, remain - 8 - headLen); - if (rawBodyLen > 0) memcpy(queue->pBuffer, (char*)pBody + remain - 8 - headLen, rawBodyLen - (remain - 8 - headLen)); + if (rawBodyLen > 0) + memcpy(queue->pBuffer, (char *)pBody + remain - 8 - headLen, rawBodyLen - (remain - 8 - headLen)); queue->tail = bodyLen - (remain - 8 - headLen); } else { memcpy(queue->pBuffer + queue->tail + 8, pHead, rawHeadLen); @@ -307,12 +313,7 @@ static void *dmConsumChildQueue(void *param) { code = dmProcessNodeMsg(pWrapper, pMsg); if (code != 0) { dError("node:%s, failed to process msg:%p since %s, put into pqueue", proc->name, pMsg, terrstr()); - SRpcMsg rsp = { - .code = (terrno != 0 ? terrno : code), - .pCont = pMsg->info.rsp, - .contLen = pMsg->info.rspLen, - .info = pMsg->info, - }; + SRpcMsg rsp = {.code = (terrno != 0 ? terrno : code), .info = pMsg->info}; dmPutToProcPQueue(proc, &rsp, DND_FUNC_RSP); rpcFreeCont(pMsg->pCont); taosFreeQitem(pMsg); @@ -448,7 +449,7 @@ void dmPutToProcPQueue(SProc *proc, SRpcMsg *pMsg, EProcFuncType ftype) { break; } - if (retry == 10) { + if (terrno != TSDB_CODE_OUT_OF_SHM_MEM) { pMsg->code = terrno; if (pMsg->contLen > 0) { rpcFreeCont(pMsg->pCont); @@ -464,8 +465,18 @@ void dmPutToProcPQueue(SProc *proc, SRpcMsg *pMsg, EProcFuncType ftype) { taosMsleep(retry); } } + + rpcFreeCont(pMsg->pCont); + pMsg->pCont = NULL; + pMsg->contLen = 0; } int32_t dmPutToProcCQueue(SProc *proc, SRpcMsg *pMsg, EProcFuncType ftype) { - return dmPushToProcQueue(proc, proc->cqueue, pMsg, ftype); + int32_t code = dmPushToProcQueue(proc, proc->cqueue, pMsg, ftype); + if (code == 0) { + dTrace("msg:%p, is freed after push to cqueue", pMsg); + rpcFreeCont(pMsg->pCont); + taosFreeQitem(pMsg); + } + return code; } diff --git a/source/dnode/mgmt/node_mgmt/src/dmTransport.c b/source/dnode/mgmt/node_mgmt/src/dmTransport.c index ee9008b1982cb57f858b184d099badcb61c38ef4..6fbfae8b416efc68a0be9b101f1308aeba723752 100644 --- a/source/dnode/mgmt/node_mgmt/src/dmTransport.c +++ b/source/dnode/mgmt/node_mgmt/src/dmTransport.c @@ -17,9 +17,9 @@ #include "dmMgmt.h" #include "qworker.h" -#define INTERNAL_USER "_dnd" -#define INTERNAL_CKEY "_key" -#define INTERNAL_SECRET "_pwd" +static void dmSendRedirectRsp(SRpcMsg *pMsg, const SEpSet *pNewEpSet); +static void dmSendRsp(SRpcMsg *pMsg); +static void dmBuildMnodeRedirectRsp(SDnode *pDnode, SRpcMsg *pMsg); static inline int32_t dmBuildNodeMsg(SRpcMsg *pMsg, SRpcMsg *pRpc) { SRpcConnInfo connInfo = {0}; @@ -52,46 +52,39 @@ static void dmProcessRpcMsg(SDnode *pDnode, SRpcMsg *pRpc, SEpSet *pEpSet) { SDnodeTrans *pTrans = &pDnode->trans; int32_t code = -1; SRpcMsg *pMsg = NULL; - bool needRelease = false; - SDnodeHandle *pHandle = &pTrans->msgHandles[TMSG_INDEX(pRpc->msgType)]; SMgmtWrapper *pWrapper = NULL; + SDnodeHandle *pHandle = &pTrans->msgHandles[TMSG_INDEX(pRpc->msgType)]; dTrace("msg:%s is received, handle:%p len:%d code:0x%x app:%p refId:%" PRId64, TMSG_INFO(pRpc->msgType), pRpc->info.handle, pRpc->contLen, pRpc->code, pRpc->info.ahandle, pRpc->info.refId); - pRpc->info.noResp = 0; - pRpc->info.persistHandle = 0; - pRpc->info.wrapper = NULL; - pRpc->info.node = NULL; - pRpc->info.rsp = NULL; - pRpc->info.rspLen = 0; if (pRpc->msgType == TDMT_DND_NET_TEST) { dmProcessNetTestReq(pDnode, pRpc); - goto _OVER_JUST_FREE; + return; } else if (pRpc->msgType == TDMT_MND_SYSTABLE_RETRIEVE_RSP || pRpc->msgType == TDMT_VND_FETCH_RSP) { qWorkerProcessFetchRsp(NULL, NULL, pRpc); - goto _OVER_JUST_FREE; + return; } else { } if (pDnode->status != DND_STAT_RUNNING) { if (pRpc->msgType == TDMT_DND_SERVER_STATUS) { dmProcessServerStartupStatus(pDnode, pRpc); - goto _OVER_JUST_FREE; + return; } else { terrno = TSDB_CODE_APP_NOT_READY; - goto _OVER_RSP_FREE; + goto _OVER; } } if (IsReq(pRpc) && pRpc->pCont == NULL) { terrno = TSDB_CODE_INVALID_MSG_LEN; - goto _OVER_RSP_FREE; + goto _OVER; } if (pHandle->defaultNtype == NODE_END) { terrno = TSDB_CODE_MSG_NOT_PROCESSED; - goto _OVER_RSP_FREE; + goto _OVER; } else { pWrapper = &pDnode->wrappers[pHandle->defaultNtype]; if (pHandle->needCheckVgId) { @@ -106,15 +99,15 @@ static void dmProcessRpcMsg(SDnode *pDnode, SRpcMsg *pRpc, SEpSet *pEpSet) { } } else { terrno = TSDB_CODE_INVALID_MSG_LEN; - goto _OVER_RSP_FREE; + goto _OVER; } } } if (dmMarkWrapper(pWrapper) != 0) { - goto _OVER_RSP_FREE; + pWrapper = NULL; + goto _OVER; } else { - needRelease = true; pRpc->info.wrapper = pWrapper; } @@ -134,24 +127,23 @@ static void dmProcessRpcMsg(SDnode *pDnode, SRpcMsg *pRpc, SEpSet *pEpSet) { } _OVER: - if (code == 0) { - if (pWrapper != NULL && InParentProc(pWrapper)) { - dTrace("msg:%p, is freed after push to cqueue", pMsg); - taosFreeQitem(pMsg); - rpcFreeCont(pRpc->pCont); - } - } else { + if (code != 0) { dError("msg:%p, failed to process since %s", pMsg, terrstr()); if (terrno != 0) code = terrno; if (IsReq(pRpc)) { - if (code == TSDB_CODE_NODE_NOT_DEPLOYED || code == TSDB_CODE_NODE_OFFLINE) { - if (pRpc->msgType > TDMT_MND_MSG && pRpc->msgType < TDMT_VND_MSG) { - code = TSDB_CODE_NODE_REDIRECT; - } + SRpcMsg rsp = {.code = code, .info = pRpc->info}; + + if ((code == TSDB_CODE_NODE_NOT_DEPLOYED || code == TSDB_CODE_APP_NOT_READY) && pRpc->msgType > TDMT_MND_MSG && + pRpc->msgType < TDMT_VND_MSG) { + dmBuildMnodeRedirectRsp(pDnode, &rsp); + } + + if (pWrapper != NULL) { + dmSendRsp(&rsp); + } else { + rpcSendResponse(&rsp); } - SRpcMsg rspMsg = {.code = code, .info = pRpc->info}; - tmsgSendRsp(&rspMsg); } dTrace("msg:%p, is freed", pMsg); @@ -159,19 +151,7 @@ _OVER: rpcFreeCont(pRpc->pCont); } - if (needRelease) { - dmReleaseWrapper(pWrapper); - } - return; - -_OVER_JUST_FREE: - rpcFreeCont(pRpc->pCont); - return; - -_OVER_RSP_FREE: - rpcFreeCont(pRpc->pCont); - SRpcMsg simpleRsp = {.code = terrno, .info = pRpc->info}; - rpcSendResponse(&simpleRsp); + dmReleaseWrapper(pWrapper); } int32_t dmInitMsgHandle(SDnode *pDnode) { @@ -200,47 +180,6 @@ int32_t dmInitMsgHandle(SDnode *pDnode) { return 0; } -static void dmSendRpcRedirectRsp(const SRpcMsg *pMsg) { - SDnode *pDnode = dmInstance(); - SEpSet epSet = {0}; - dmGetMnodeEpSet(&pDnode->data, &epSet); - - dDebug("RPC %p, req is redirected, num:%d use:%d", pMsg->info.handle, epSet.numOfEps, epSet.inUse); - for (int32_t i = 0; i < epSet.numOfEps; ++i) { - dDebug("mnode index:%d %s:%u", i, epSet.eps[i].fqdn, epSet.eps[i].port); - if (strcmp(epSet.eps[i].fqdn, tsLocalFqdn) == 0 && epSet.eps[i].port == tsServerPort) { - epSet.inUse = (i + 1) % epSet.numOfEps; - } - - epSet.eps[i].port = htons(epSet.eps[i].port); - } - - SMEpSet msg = {.epSet = epSet}; - int32_t len = tSerializeSMEpSet(NULL, 0, &msg); - - SRpcMsg rsp = { - .code = TSDB_CODE_RPC_REDIRECT, - .info = pMsg->info, - .contLen = len, - }; - rsp.pCont = rpcMallocCont(len); - tSerializeSMEpSet(rsp.pCont, len, &msg); - rpcSendResponse(&rsp); - - rpcFreeCont(pMsg->pCont); -} - -static inline void dmSendRecv(SEpSet *pEpSet, SRpcMsg *pReq, SRpcMsg *pRsp) { - SDnode *pDnode = dmInstance(); - if (pDnode->status != DND_STAT_RUNNING) { - pRsp->code = TSDB_CODE_NODE_OFFLINE; - rpcFreeCont(pReq->pCont); - pReq->pCont = NULL; - } else { - rpcSendRecv(pDnode->trans.clientRpc, pEpSet, pReq, pRsp); - } -} - static inline int32_t dmSendReq(const SEpSet *pEpSet, SRpcMsg *pMsg) { SDnode *pDnode = dmInstance(); if (pDnode->status != DND_STAT_RUNNING) { @@ -257,33 +196,42 @@ static inline int32_t dmSendReq(const SEpSet *pEpSet, SRpcMsg *pMsg) { static inline void dmSendRsp(SRpcMsg *pMsg) { SMgmtWrapper *pWrapper = pMsg->info.wrapper; - if (pMsg->code == TSDB_CODE_NODE_REDIRECT) { - dmSendRpcRedirectRsp(pMsg); + if (InChildProc(pWrapper)) { + dmPutToProcPQueue(&pWrapper->proc, pMsg, DND_FUNC_RSP); } else { - if (InChildProc(pWrapper)) { - dmPutToProcPQueue(&pWrapper->proc, pMsg, DND_FUNC_RSP); - } else { - rpcSendResponse(pMsg); - } + rpcSendResponse(pMsg); + } +} + +static void dmBuildMnodeRedirectRsp(SDnode *pDnode, SRpcMsg *pMsg) { + SMEpSet msg = {0}; + dmGetMnodeEpSetForRedirect(&pDnode->data, pMsg, &msg.epSet); + + int32_t contLen = tSerializeSMEpSet(NULL, 0, &msg); + pMsg->pCont = rpcMallocCont(contLen); + if (pMsg->pCont == NULL) { + pMsg->code = TSDB_CODE_OUT_OF_MEMORY; + } else { + tSerializeSMEpSet(pMsg->pCont, contLen, &msg); + pMsg->contLen = contLen; } } static inline void dmSendRedirectRsp(SRpcMsg *pMsg, const SEpSet *pNewEpSet) { - SMgmtWrapper *pWrapper = pMsg->info.wrapper; - if (InChildProc(pWrapper)) { - dmPutToProcPQueue(&pWrapper->proc, pMsg, DND_FUNC_RSP); + SRpcMsg rsp = {.code = TSDB_CODE_RPC_REDIRECT, .info = pMsg->info}; + SMEpSet msg = {.epSet = *pNewEpSet}; + int32_t contLen = tSerializeSMEpSet(NULL, 0, &msg); + + rsp.pCont = rpcMallocCont(contLen); + if (rsp.pCont == NULL) { + terrno = TSDB_CODE_OUT_OF_MEMORY; } else { - SRpcMsg rsp = {0}; - SMEpSet msg = {.epSet = *pNewEpSet}; - int32_t len = tSerializeSMEpSet(NULL, 0, &msg); - rsp.pCont = rpcMallocCont(len); - rsp.contLen = len; - tSerializeSMEpSet(rsp.pCont, len, &msg); - - rsp.code = TSDB_CODE_RPC_REDIRECT; - rsp.info = pMsg->info; - rpcSendResponse(&rsp); + tSerializeSMEpSet(rsp.pCont, contLen, &msg); + rsp.contLen = contLen; } + dmSendRsp(&rsp); + rpcFreeCont(pMsg->pCont); + pMsg->pCont = NULL; } static inline void dmRegisterBrokenLinkArg(SRpcMsg *pMsg) { @@ -317,16 +265,9 @@ int32_t dmInitClient(SDnode *pDnode) { rpcInit.sessions = 1024; rpcInit.connType = TAOS_CONN_CLIENT; rpcInit.idleTime = tsShellActivityTimer * 1000; - rpcInit.user = INTERNAL_USER; - rpcInit.ckey = INTERNAL_CKEY; - rpcInit.spi = 1; rpcInit.parent = pDnode; rpcInit.rfp = rpcRfp; - char pass[TSDB_PASSWORD_LEN + 1] = {0}; - taosEncryptPass_c((uint8_t *)(INTERNAL_SECRET), strlen(INTERNAL_SECRET), pass); - rpcInit.secret = pass; - pTrans->clientRpc = rpcOpen(&rpcInit); if (pTrans->clientRpc == NULL) { dError("failed to init dnode rpc client"); diff --git a/source/dnode/mgmt/node_util/inc/dmUtil.h b/source/dnode/mgmt/node_util/inc/dmUtil.h index e7256a3a87526d8c4b2b1e67189f8aa45935acf5..4946669678cd0fd17a22b935aa9e2613e58d73db 100644 --- a/source/dnode/mgmt/node_util/inc/dmUtil.h +++ b/source/dnode/mgmt/node_util/inc/dmUtil.h @@ -173,6 +173,7 @@ int32_t dmReadEps(SDnodeData *pData); int32_t dmWriteEps(SDnodeData *pData); void dmUpdateEps(SDnodeData *pData, SArray *pDnodeEps); void dmGetMnodeEpSet(SDnodeData *pData, SEpSet *pEpSet); +void dmGetMnodeEpSetForRedirect(SDnodeData *pData, SRpcMsg *pMsg, SEpSet *pEpSet); void dmSetMnodeEpSet(SDnodeData *pData, SEpSet *pEpSet); #ifdef __cplusplus diff --git a/source/dnode/mgmt/node_util/src/dmEps.c b/source/dnode/mgmt/node_util/src/dmEps.c index 94fa5695578ccfbcdfc6b7a7c1d4ee834c1f99f9..e0af20e41bfef194d90d30316c16042522e7f87d 100644 --- a/source/dnode/mgmt/node_util/src/dmEps.c +++ b/source/dnode/mgmt/node_util/src/dmEps.c @@ -314,6 +314,17 @@ void dmGetMnodeEpSet(SDnodeData *pData, SEpSet *pEpSet) { taosThreadRwlockUnlock(&pData->lock); } +void dmGetMnodeEpSetForRedirect(SDnodeData *pData, SRpcMsg *pMsg, SEpSet *pEpSet) { + dmGetMnodeEpSet(pData, pEpSet); + dDebug("msg:%p, is redirected, num:%d use:%d", pMsg, pEpSet->numOfEps, pEpSet->inUse); + for (int32_t i = 0; i < pEpSet->numOfEps; ++i) { + dDebug("mnode index:%d %s:%u", i, pEpSet->eps[i].fqdn, pEpSet->eps[i].port); + if (strcmp(pEpSet->eps[i].fqdn, tsLocalFqdn) == 0 && pEpSet->eps[i].port == tsServerPort) { + pEpSet->inUse = (i + 1) % pEpSet->numOfEps; + } + } +} + void dmSetMnodeEpSet(SDnodeData *pData, SEpSet *pEpSet) { taosThreadRwlockWrlock(&pData->lock); pData->mnodeEps = *pEpSet; diff --git a/source/dnode/mgmt/test/bnode/dbnode.cpp b/source/dnode/mgmt/test/bnode/dbnode.cpp index 0568b30245d77e2a8dd7d2d227484477790b3270..c2a9873e5b0e4b2ddca8007e76d10e4e69fa3567 100644 --- a/source/dnode/mgmt/test/bnode/dbnode.cpp +++ b/source/dnode/mgmt/test/bnode/dbnode.cpp @@ -14,7 +14,7 @@ class DndTestBnode : public ::testing::Test { protected: static void SetUpTestSuite() { - test.Init("/tmp/dbnodeTest", 9112); + test.Init(TD_TMP_DIR_PATH "dbnodeTest", 9112); taosMsleep(1100); } static void TearDownTestSuite() { test.Cleanup(); } diff --git a/source/dnode/mgmt/test/mnode/dmnode.cpp b/source/dnode/mgmt/test/mnode/dmnode.cpp index 98b50e96cfc84168ebecdb7d76ba23884c001d45..8c945b50ac48b4b1e290875c58a98f168971bc37 100644 --- a/source/dnode/mgmt/test/mnode/dmnode.cpp +++ b/source/dnode/mgmt/test/mnode/dmnode.cpp @@ -13,7 +13,7 @@ class DndTestMnode : public ::testing::Test { protected: - static void SetUpTestSuite() { test.Init("/tmp/dmnodeTest", 9114); } + static void SetUpTestSuite() { test.Init(TD_TMP_DIR_PATH "dmnodeTest", 9114); } static void TearDownTestSuite() { test.Cleanup(); } static Testbase test; diff --git a/source/dnode/mgmt/test/qnode/dqnode.cpp b/source/dnode/mgmt/test/qnode/dqnode.cpp index 2430419bef4e45e625ce60060849baa63eee3934..ef51be47a68d8772507dcee7ae8fae5b29d82bf6 100644 --- a/source/dnode/mgmt/test/qnode/dqnode.cpp +++ b/source/dnode/mgmt/test/qnode/dqnode.cpp @@ -13,7 +13,7 @@ class DndTestQnode : public ::testing::Test { protected: - static void SetUpTestSuite() { test.Init("/tmp/dqnodeTest", 9111); } + static void SetUpTestSuite() { test.Init(TD_TMP_DIR_PATH "dqnodeTest", 9111); } static void TearDownTestSuite() { test.Cleanup(); } static Testbase test; diff --git a/source/dnode/mgmt/test/snode/dsnode.cpp b/source/dnode/mgmt/test/snode/dsnode.cpp index 9ade616f191be10af24474fbf0f5581f30a1c061..9ae0fbdc542bb145c3b6e8d45fe355f1b187450d 100644 --- a/source/dnode/mgmt/test/snode/dsnode.cpp +++ b/source/dnode/mgmt/test/snode/dsnode.cpp @@ -13,7 +13,7 @@ class DndTestSnode : public ::testing::Test { protected: - static void SetUpTestSuite() { test.Init("/tmp/dsnodeTest", 9113); } + static void SetUpTestSuite() { test.Init(TD_TMP_DIR_PATH "dsnodeTest", 9113); } static void TearDownTestSuite() { test.Cleanup(); } static Testbase test; diff --git a/source/dnode/mgmt/test/sut/src/client.cpp b/source/dnode/mgmt/test/sut/src/client.cpp index d7b38d6d72a8eadfe75d77a53c3cb59b1c98b282..6b4c23c0de9e8374a8c7399c7c401c103aad8b64 100644 --- a/source/dnode/mgmt/test/sut/src/client.cpp +++ b/source/dnode/mgmt/test/sut/src/client.cpp @@ -48,10 +48,10 @@ void TestClient::DoInit() { rpcInit.connType = TAOS_CONN_CLIENT; rpcInit.idleTime = 30 * 1000; rpcInit.user = (char*)this->user; - rpcInit.ckey = (char*)"key"; + // rpcInit.ckey = (char*)"key"; rpcInit.parent = this; - rpcInit.secret = (char*)secretEncrypt; - rpcInit.spi = 1; + // rpcInit.secret = (char*)secretEncrypt; + // rpcInit.spi = 1; clientRpc = rpcOpen(&rpcInit); ASSERT(clientRpc); diff --git a/source/dnode/mgmt/test/sut/src/sut.cpp b/source/dnode/mgmt/test/sut/src/sut.cpp index 7bfa0417afbd957f81603371a3bb8f476868689e..6ef94481ea8edd66317f4d2a7b01bc3234ccd4d9 100644 --- a/source/dnode/mgmt/test/sut/src/sut.cpp +++ b/source/dnode/mgmt/test/sut/src/sut.cpp @@ -48,7 +48,7 @@ void Testbase::Init(const char* path, int16_t port) { strcpy(tsDataDir, path); taosRemoveDir(path); taosMkDir(path); - InitLog("/tmp/td"); + InitLog(TD_TMP_DIR_PATH "td"); server.Start(); client.Init("root", "taosdata"); diff --git a/source/dnode/mgmt/test/vnode/vnode.cpp b/source/dnode/mgmt/test/vnode/vnode.cpp index bddf9518195d162c3934f8bb33fc34953f2f7830..8aba4f81b59ee35ff61616f0ada5056c8d76074a 100644 --- a/source/dnode/mgmt/test/vnode/vnode.cpp +++ b/source/dnode/mgmt/test/vnode/vnode.cpp @@ -13,7 +13,7 @@ class DndTestVnode : public ::testing::Test { protected: - static void SetUpTestSuite() { test.Init("/tmp/dvnodeTest", 9115); } + static void SetUpTestSuite() { test.Init(TD_TMP_DIR_PATH "dvnodeTest", 9115); } static void TearDownTestSuite() { test.Cleanup(); } static Testbase test; diff --git a/source/dnode/mnode/impl/inc/mndTopic.h b/source/dnode/mnode/impl/inc/mndTopic.h index d7e6f9c87bee034856e7d512cb1e38a900e1412e..c5c4800e0295fa48ee4bf9669200f7ce7a31eff8 100644 --- a/source/dnode/mnode/impl/inc/mndTopic.h +++ b/source/dnode/mnode/impl/inc/mndTopic.h @@ -35,7 +35,7 @@ int32_t mndDropTopicByDB(SMnode *pMnode, STrans *pTrans, SDbObj *pDb); const char *mndTopicGetShowName(const char topic[TSDB_TOPIC_FNAME_LEN]); -int32_t mndSetTopicRedoLogs(SMnode *pMnode, STrans *pTrans, SMqTopicObj *pTopic); +int32_t mndSetTopicCommitLogs(SMnode *pMnode, STrans *pTrans, SMqTopicObj *pTopic); #ifdef __cplusplus } diff --git a/source/dnode/mnode/impl/src/mndBnode.c b/source/dnode/mnode/impl/src/mndBnode.c index 924b6db8f7c57d18b6ab1fc47b43a785449a9eca..3316a09462ff1d5ff7c940e623941c7abe72a76c 100644 --- a/source/dnode/mnode/impl/src/mndBnode.c +++ b/source/dnode/mnode/impl/src/mndBnode.c @@ -304,10 +304,10 @@ static int32_t mndProcessCreateBnodeReq(SRpcMsg *pReq) { } code = mndCreateBnode(pMnode, pReq, pDnode, &createReq); - if (code == 0) code = TSDB_CODE_MND_ACTION_IN_PROGRESS; + if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; _OVER: - if (code != 0 && code != TSDB_CODE_MND_ACTION_IN_PROGRESS) { + if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { mError("bnode:%d, failed to create since %s", createReq.dnodeId, terrstr()); } @@ -414,10 +414,10 @@ static int32_t mndProcessDropBnodeReq(SRpcMsg *pReq) { } code = mndDropBnode(pMnode, pReq, pObj); - if (code == 0) code = TSDB_CODE_MND_ACTION_IN_PROGRESS; + if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; _OVER: - if (code != 0 && code != TSDB_CODE_MND_ACTION_IN_PROGRESS) { + if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { mError("bnode:%d, failed to drop since %s", dropReq.dnodeId, terrstr()); } diff --git a/source/dnode/mnode/impl/src/mndConsumer.c b/source/dnode/mnode/impl/src/mndConsumer.c index 57f7b341d460ba52e7e44678d0397929ab320914..1bb003bab9f526da988945c1025d09aebcffd39d 100644 --- a/source/dnode/mnode/impl/src/mndConsumer.c +++ b/source/dnode/mnode/impl/src/mndConsumer.c @@ -419,7 +419,7 @@ static int32_t mndProcessSubscribeReq(SRpcMsg *pMsg) { SMqTopicObj topicObj = {0}; memcpy(&topicObj, pTopic, sizeof(SMqTopicObj)); topicObj.refConsumerCnt = pTopic->refConsumerCnt + 1; - if (mndSetTopicRedoLogs(pMnode, pTrans, &topicObj) != 0) goto SUBSCRIBE_OVER; + if (mndSetTopicCommitLogs(pMnode, pTrans, &topicObj) != 0) goto SUBSCRIBE_OVER; mndReleaseTopic(pMnode, pTopic); } @@ -511,7 +511,7 @@ static int32_t mndProcessSubscribeReq(SRpcMsg *pMsg) { if (mndTransPrepare(pMnode, pTrans) != 0) goto SUBSCRIBE_OVER; } - code = TSDB_CODE_MND_ACTION_IN_PROGRESS; + code = TSDB_CODE_ACTION_IN_PROGRESS; SUBSCRIBE_OVER: mndTransDrop(pTrans); diff --git a/source/dnode/mnode/impl/src/mndDb.c b/source/dnode/mnode/impl/src/mndDb.c index 0bf7b240b26e545be633e2c6d923847e50bff5a1..6921235f8bfdb007811dc92ccff0514a68999824 100644 --- a/source/dnode/mnode/impl/src/mndDb.c +++ b/source/dnode/mnode/impl/src/mndDb.c @@ -525,7 +525,6 @@ static int32_t mndCreateDb(SMnode *pMnode, SRpcMsg *pReq, SCreateDbReq *pCreate, dbObj.cfg.numOfRetensions = pCreate->numOfRetensions; dbObj.cfg.pRetensions = pCreate->pRetensions; - pCreate->pRetensions = NULL; mndSetDefaultDbCfg(&dbObj.cfg); @@ -605,10 +604,10 @@ static int32_t mndProcessCreateDbReq(SRpcMsg *pReq) { } code = mndCreateDb(pMnode, pReq, &createReq, pUser); - if (code == 0) code = TSDB_CODE_MND_ACTION_IN_PROGRESS; + if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; _OVER: - if (code != 0 && code != TSDB_CODE_MND_ACTION_IN_PROGRESS) { + if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { mError("db:%s, failed to create since %s", createReq.db, terrstr()); } @@ -839,10 +838,10 @@ static int32_t mndProcessAlterDbReq(SRpcMsg *pReq) { dbObj.cfgVersion++; dbObj.updateTime = taosGetTimestampMs(); code = mndAlterDb(pMnode, pReq, pDb, &dbObj); - if (code == 0) code = TSDB_CODE_MND_ACTION_IN_PROGRESS; + if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; _OVER: - if (code != 0 && code != TSDB_CODE_MND_ACTION_IN_PROGRESS) { + if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { mError("db:%s, failed to alter since %s", alterReq.db, terrstr()); } @@ -1110,10 +1109,10 @@ static int32_t mndProcessDropDbReq(SRpcMsg *pReq) { } code = mndDropDb(pMnode, pReq, pDb); - if (code == 0) code = TSDB_CODE_MND_ACTION_IN_PROGRESS; + if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; _OVER: - if (code != 0 && code != TSDB_CODE_MND_ACTION_IN_PROGRESS) { + if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { mError("db:%s, failed to drop since %s", dropReq.db, terrstr()); } diff --git a/source/dnode/mnode/impl/src/mndDnode.c b/source/dnode/mnode/impl/src/mndDnode.c index 2a4cf011156e777c4834a40a614d1e62a5b99465..0cac7fd86b3649928fcd76da2098964426f2a065 100644 --- a/source/dnode/mnode/impl/src/mndDnode.c +++ b/source/dnode/mnode/impl/src/mndDnode.c @@ -448,13 +448,13 @@ static int32_t mndCreateDnode(SMnode *pMnode, SRpcMsg *pReq, SCreateDnodeReq *pC } mDebug("trans:%d, used to create dnode:%s", pTrans->id, dnodeObj.ep); - SSdbRaw *pRedoRaw = mndDnodeActionEncode(&dnodeObj); - if (pRedoRaw == NULL || mndTransAppendRedolog(pTrans, pRedoRaw) != 0) { - mError("trans:%d, failed to append redo log since %s", pTrans->id, terrstr()); + SSdbRaw *pCommitRaw = mndDnodeActionEncode(&dnodeObj); + if (pCommitRaw == NULL || mndTransAppendCommitlog(pTrans, pCommitRaw) != 0) { + mError("trans:%d, failed to append commit log since %s", pTrans->id, terrstr()); mndTransDrop(pTrans); return -1; } - sdbSetRawStatus(pRedoRaw, SDB_STATUS_READY); + sdbSetRawStatus(pCommitRaw, SDB_STATUS_READY); if (mndTransPrepare(pMnode, pTrans) != 0) { mError("trans:%d, failed to prepare since %s", pTrans->id, terrstr()); @@ -504,10 +504,10 @@ static int32_t mndProcessCreateDnodeReq(SRpcMsg *pReq) { } code = mndCreateDnode(pMnode, pReq, &createReq); - if (code == 0) code = TSDB_CODE_MND_ACTION_IN_PROGRESS; + if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; CREATE_DNODE_OVER: - if (code != 0 && code != TSDB_CODE_MND_ACTION_IN_PROGRESS) { + if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { mError("dnode:%s:%d, failed to create since %s", createReq.fqdn, createReq.port, terrstr()); } @@ -524,13 +524,13 @@ static int32_t mndDropDnode(SMnode *pMnode, SRpcMsg *pReq, SDnodeObj *pDnode) { } mDebug("trans:%d, used to drop dnode:%d", pTrans->id, pDnode->id); - SSdbRaw *pRedoRaw = mndDnodeActionEncode(pDnode); - if (pRedoRaw == NULL || mndTransAppendRedolog(pTrans, pRedoRaw) != 0) { - mError("trans:%d, failed to append redo log since %s", pTrans->id, terrstr()); + SSdbRaw *pCommitRaw = mndDnodeActionEncode(pDnode); + if (pCommitRaw == NULL || mndTransAppendCommitlog(pTrans, pCommitRaw) != 0) { + mError("trans:%d, failed to append commit log since %s", pTrans->id, terrstr()); mndTransDrop(pTrans); return -1; } - sdbSetRawStatus(pRedoRaw, SDB_STATUS_DROPPED); + sdbSetRawStatus(pCommitRaw, SDB_STATUS_DROPPED); if (mndTransPrepare(pMnode, pTrans) != 0) { mError("trans:%d, failed to prepare since %s", pTrans->id, terrstr()); @@ -585,10 +585,10 @@ static int32_t mndProcessDropDnodeReq(SRpcMsg *pReq) { } code = mndDropDnode(pMnode, pReq, pDnode); - if (code == 0) code = TSDB_CODE_MND_ACTION_IN_PROGRESS; + if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; DROP_DNODE_OVER: - if (code != 0 && code != TSDB_CODE_MND_ACTION_IN_PROGRESS) { + if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { mError("dnode:%d, failed to drop since %s", dropReq.dnodeId, terrstr()); } diff --git a/source/dnode/mnode/impl/src/mndFunc.c b/source/dnode/mnode/impl/src/mndFunc.c index cf2edb57842c7eb0985ba8e3a27fe93e0bf28b5d..9107dab693d4c9eb6adc6599d03126d5a59a5a69 100644 --- a/source/dnode/mnode/impl/src/mndFunc.c +++ b/source/dnode/mnode/impl/src/mndFunc.c @@ -330,10 +330,10 @@ static int32_t mndProcessCreateFuncReq(SRpcMsg *pReq) { } code = mndCreateFunc(pMnode, pReq, &createReq); - if (code == 0) code = TSDB_CODE_MND_ACTION_IN_PROGRESS; + if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; _OVER: - if (code != 0 && code != TSDB_CODE_MND_ACTION_IN_PROGRESS) { + if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { mError("func:%s, failed to create since %s", createReq.name, terrstr()); } @@ -386,10 +386,10 @@ static int32_t mndProcessDropFuncReq(SRpcMsg *pReq) { } code = mndDropFunc(pMnode, pReq, pFunc); - if (code == 0) code = TSDB_CODE_MND_ACTION_IN_PROGRESS; + if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; _OVER: - if (code != 0 && code != TSDB_CODE_MND_ACTION_IN_PROGRESS) { + if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { mError("func:%s, failed to drop since %s", dropReq.name, terrstr()); } diff --git a/source/dnode/mnode/impl/src/mndMnode.c b/source/dnode/mnode/impl/src/mndMnode.c index 04c0a934858823cb7bc2d4a9e18637e72f9dfacf..7f86eb8b3292508c9b903c68fd6306b766ac074f 100644 --- a/source/dnode/mnode/impl/src/mndMnode.c +++ b/source/dnode/mnode/impl/src/mndMnode.c @@ -402,10 +402,10 @@ static int32_t mndProcessCreateMnodeReq(SRpcMsg *pReq) { } code = mndCreateMnode(pMnode, pReq, pDnode, &createReq); - if (code == 0) code = TSDB_CODE_MND_ACTION_IN_PROGRESS; + if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; _OVER: - if (code != 0 && code != TSDB_CODE_MND_ACTION_IN_PROGRESS) { + if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { mError("mnode:%d, failed to create since %s", createReq.dnodeId, terrstr()); } @@ -574,10 +574,10 @@ static int32_t mndProcessDropMnodeReq(SRpcMsg *pReq) { } code = mndDropMnode(pMnode, pReq, pObj); - if (code == 0) code = TSDB_CODE_MND_ACTION_IN_PROGRESS; + if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; _OVER: - if (code != 0 && code != TSDB_CODE_MND_ACTION_IN_PROGRESS) { + if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { mError("mnode:%d, failed to drop since %s", dropReq.dnodeId, terrstr()); } diff --git a/source/dnode/mnode/impl/src/mndOffset.c b/source/dnode/mnode/impl/src/mndOffset.c index b109ede81999e62b14bc7f95af486bdb830cdd33..dca07f6a6d2910630a939d119b6d21e287112866 100644 --- a/source/dnode/mnode/impl/src/mndOffset.c +++ b/source/dnode/mnode/impl/src/mndOffset.c @@ -153,6 +153,7 @@ int32_t mndCreateOffsets(STrans *pTrans, const char *cgroup, const char *topicNa return -1; } sdbSetRawStatus(pOffsetRaw, SDB_STATUS_READY); + // commit log or redo log? if (mndTransAppendRedolog(pTrans, pOffsetRaw) < 0) { return -1; } @@ -188,7 +189,7 @@ static int32_t mndProcessCommitOffsetReq(SRpcMsg *pMsg) { pOffsetObj->offset = pOffset->offset; SSdbRaw *pOffsetRaw = mndOffsetActionEncode(pOffsetObj); sdbSetRawStatus(pOffsetRaw, SDB_STATUS_READY); - mndTransAppendRedolog(pTrans, pOffsetRaw); + mndTransAppendCommitlog(pTrans, pOffsetRaw); if (create) { taosMemoryFree(pOffsetObj); } else { @@ -205,7 +206,7 @@ static int32_t mndProcessCommitOffsetReq(SRpcMsg *pMsg) { } mndTransDrop(pTrans); - return TSDB_CODE_MND_ACTION_IN_PROGRESS; + return TSDB_CODE_ACTION_IN_PROGRESS; } static int32_t mndOffsetActionInsert(SSdb *pSdb, SMqOffsetObj *pOffset) { diff --git a/source/dnode/mnode/impl/src/mndProfile.c b/source/dnode/mnode/impl/src/mndProfile.c index 7e99c5583d69fcf708d39206aab3ccca448ddeb8..b9ac82d890a12f2355834f415f4bbca65461873b 100644 --- a/source/dnode/mnode/impl/src/mndProfile.c +++ b/source/dnode/mnode/impl/src/mndProfile.c @@ -128,7 +128,8 @@ static SConnObj *mndCreateConn(SMnode *pMnode, const char *user, int8_t connType } static void mndFreeConn(SConnObj *pConn) { - taosMemoryFreeClear(pConn->pQueries); + taosArrayDestroyEx(pConn->pQueries, tFreeClientHbQueryDesc); + mTrace("conn:%u, is destroyed, data:%p", pConn->id, pConn); } @@ -340,8 +341,7 @@ static int32_t mndProcessQueryHeartBeat(SMnode *pMnode, SRpcMsg *pMsg, SClientHb if (pHbReq->query) { SQueryHbReqBasic *pBasic = pHbReq->query; - SRpcConnInfo connInfo = {0}; - rpcGetConnInfo(pMsg->info.handle, &connInfo); + SRpcConnInfo connInfo = pMsg->conn; SConnObj *pConn = mndAcquireConn(pMnode, pBasic->connId); if (pConn == NULL) { @@ -397,6 +397,7 @@ static int32_t mndProcessQueryHeartBeat(SMnode *pMnode, SRpcMsg *pMsg, SClientHb if (NULL == hbRsp.info) { mError("taosArrayInit %d rsp kv failed", kvNum); terrno = TSDB_CODE_OUT_OF_MEMORY; + tFreeClientHbRsp(&hbRsp); return -1; } @@ -454,6 +455,7 @@ static int32_t mndProcessHeartBeatReq(SRpcMsg *pReq) { SClientHbBatchReq batchReq = {0}; if (tDeserializeSClientHbBatchReq(pReq->pCont, pReq->contLen, &batchReq) != 0) { + taosArrayDestroyEx(batchReq.reqs, tFreeClientHbReq); terrno = TSDB_CODE_INVALID_MSG; return -1; } @@ -480,18 +482,7 @@ static int32_t mndProcessHeartBeatReq(SRpcMsg *pReq) { void *buf = rpcMallocCont(tlen); tSerializeSClientHbBatchRsp(buf, tlen, &batchRsp); - int32_t rspNum = (int32_t)taosArrayGetSize(batchRsp.rsps); - for (int32_t i = 0; i < rspNum; ++i) { - SClientHbRsp *rsp = taosArrayGet(batchRsp.rsps, i); - int32_t kvNum = (rsp->info) ? taosArrayGetSize(rsp->info) : 0; - for (int32_t n = 0; n < kvNum; ++n) { - SKv *kv = taosArrayGet(rsp->info, n); - taosMemoryFreeClear(kv->value); - } - taosArrayDestroy(rsp->info); - } - - taosArrayDestroy(batchRsp.rsps); + tFreeClientHbBatchRsp(&batchRsp); pReq->info.rspLen = tlen; pReq->info.rsp = buf; diff --git a/source/dnode/mnode/impl/src/mndQnode.c b/source/dnode/mnode/impl/src/mndQnode.c index c153d865523623f2e5350d12c98ce6d71e2516ba..3dc6200229b8a519fcf193393535500e98f4df20 100644 --- a/source/dnode/mnode/impl/src/mndQnode.c +++ b/source/dnode/mnode/impl/src/mndQnode.c @@ -306,10 +306,10 @@ static int32_t mndProcessCreateQnodeReq(SRpcMsg *pReq) { } code = mndCreateQnode(pMnode, pReq, pDnode, &createReq); - if (code == 0) code = TSDB_CODE_MND_ACTION_IN_PROGRESS; + if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; _OVER: - if (code != 0 && code != TSDB_CODE_MND_ACTION_IN_PROGRESS) { + if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { mError("qnode:%d, failed to create since %s", createReq.dnodeId, terrstr()); } @@ -416,10 +416,10 @@ static int32_t mndProcessDropQnodeReq(SRpcMsg *pReq) { } code = mndDropQnode(pMnode, pReq, pObj); - if (code == 0) code = TSDB_CODE_MND_ACTION_IN_PROGRESS; + if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; _OVER: - if (code != 0 && code != TSDB_CODE_MND_ACTION_IN_PROGRESS) { + if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { mError("qnode:%d, failed to drop since %s", dropReq.dnodeId, terrstr()); } diff --git a/source/dnode/mnode/impl/src/mndQuery.c b/source/dnode/mnode/impl/src/mndQuery.c index 5c7089e1aa65f1de308f8bdfb770c866018f36ec..78b70c9a74133b859b4175b195d4a939c37ebccc 100644 --- a/source/dnode/mnode/impl/src/mndQuery.c +++ b/source/dnode/mnode/impl/src/mndQuery.c @@ -18,37 +18,35 @@ #include "mndMnode.h" #include "qworker.h" -int32_t mndProcessQueryMsg(SRpcMsg *pReq) { - SMnode *pMnode = pReq->info.node; +int32_t mndProcessQueryMsg(SRpcMsg *pMsg) { + int32_t code = -1; + SMnode *pMnode = pMsg->info.node; SReadHandle handle = {.mnd = pMnode, .pMsgCb = &pMnode->msgCb}; - mTrace("msg:%p, in query queue is processing", pReq); - switch (pReq->msgType) { + mTrace("msg:%p, in query queue is processing", pMsg); + switch (pMsg->msgType) { case TDMT_VND_QUERY: - return qWorkerProcessQueryMsg(&handle, pMnode->pQuery, pReq); + code = qWorkerProcessQueryMsg(&handle, pMnode->pQuery, pMsg); + break; case TDMT_VND_QUERY_CONTINUE: - return qWorkerProcessCQueryMsg(&handle, pMnode->pQuery, pReq); - default: - mError("unknown msg type:%d in query queue", pReq->msgType); - return TSDB_CODE_VND_APP_ERROR; - } -} - -int32_t mndProcessFetchMsg(SRpcMsg *pMsg) { - SMnode *pMnode = pMsg->info.node; - mTrace("msg:%p, in fetch queue is processing", pMsg); - - switch (pMsg->msgType) { + code = qWorkerProcessCQueryMsg(&handle, pMnode->pQuery, pMsg); + break; case TDMT_VND_FETCH: - return qWorkerProcessFetchMsg(pMnode, pMnode->pQuery, pMsg); + code = qWorkerProcessFetchMsg(pMnode, pMnode->pQuery, pMsg); + break; case TDMT_VND_DROP_TASK: - return qWorkerProcessDropMsg(pMnode, pMnode->pQuery, pMsg); + code = qWorkerProcessDropMsg(pMnode, pMnode->pQuery, pMsg); + break; case TDMT_VND_QUERY_HEARTBEAT: - return qWorkerProcessHbMsg(pMnode, pMnode->pQuery, pMsg); + code = qWorkerProcessHbMsg(pMnode, pMnode->pQuery, pMsg); + break; default: - mError("unknown msg type:%d in fetch queue", pMsg->msgType); - return TSDB_CODE_VND_APP_ERROR; + terrno = TSDB_CODE_VND_APP_ERROR; + mError("unknown msg type:%d in query queue", pMsg->msgType); } + + if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; + return code; } int32_t mndInitQuery(SMnode *pMnode) { @@ -59,9 +57,9 @@ int32_t mndInitQuery(SMnode *pMnode) { mndSetMsgHandle(pMnode, TDMT_VND_QUERY, mndProcessQueryMsg); mndSetMsgHandle(pMnode, TDMT_VND_QUERY_CONTINUE, mndProcessQueryMsg); - mndSetMsgHandle(pMnode, TDMT_VND_FETCH, mndProcessFetchMsg); - mndSetMsgHandle(pMnode, TDMT_VND_DROP_TASK, mndProcessFetchMsg); - mndSetMsgHandle(pMnode, TDMT_VND_QUERY_HEARTBEAT, mndProcessFetchMsg); + mndSetMsgHandle(pMnode, TDMT_VND_FETCH, mndProcessQueryMsg); + mndSetMsgHandle(pMnode, TDMT_VND_DROP_TASK, mndProcessQueryMsg); + mndSetMsgHandle(pMnode, TDMT_VND_QUERY_HEARTBEAT, mndProcessQueryMsg); return 0; } diff --git a/source/dnode/mnode/impl/src/mndSma.c b/source/dnode/mnode/impl/src/mndSma.c index 0f52f00d4e2059983fdf82cb3bde7fd16fae4b27..b38e901d49dee8e6c93dc629824e51bf44c71e0f 100644 --- a/source/dnode/mnode/impl/src/mndSma.c +++ b/source/dnode/mnode/impl/src/mndSma.c @@ -562,10 +562,10 @@ static int32_t mndProcessMCreateSmaReq(SRpcMsg *pReq) { } code = mndCreateSma(pMnode, pReq, &createReq, pDb, pStb); - if (code == 0) code = TSDB_CODE_MND_ACTION_IN_PROGRESS; + if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; _OVER: - if (code != 0 && code != TSDB_CODE_MND_ACTION_IN_PROGRESS) { + if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { mError("sma:%s, failed to create since %s", createReq.name, terrstr(terrno)); } @@ -706,10 +706,10 @@ static int32_t mndProcessMDropSmaReq(SRpcMsg *pReq) { } code = mndDropSma(pMnode, pReq, pDb, pSma); - if (code == 0) code = TSDB_CODE_MND_ACTION_IN_PROGRESS; + if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; _OVER: - if (code != 0 && code != TSDB_CODE_MND_ACTION_IN_PROGRESS) { + if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { mError("sma:%s, failed to drop since %s", dropReq.name, terrstr()); } diff --git a/source/dnode/mnode/impl/src/mndSnode.c b/source/dnode/mnode/impl/src/mndSnode.c index 5f58f2c8904f6958202d5decc4ffb693ee97070c..87b61f59ecb088692941a9f57ebf89db2cefa054 100644 --- a/source/dnode/mnode/impl/src/mndSnode.c +++ b/source/dnode/mnode/impl/src/mndSnode.c @@ -312,10 +312,10 @@ static int32_t mndProcessCreateSnodeReq(SRpcMsg *pReq) { } code = mndCreateSnode(pMnode, pReq, pDnode, &createReq); - if (code == 0) code = TSDB_CODE_MND_ACTION_IN_PROGRESS; + if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; _OVER: - if (code != 0 && code != TSDB_CODE_MND_ACTION_IN_PROGRESS) { + if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { mError("snode:%d, failed to create since %s", createReq.dnodeId, terrstr()); return -1; } @@ -424,10 +424,10 @@ static int32_t mndProcessDropSnodeReq(SRpcMsg *pReq) { } code = mndDropSnode(pMnode, pReq, pObj); - if (code == 0) code = TSDB_CODE_MND_ACTION_IN_PROGRESS; + if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; _OVER: - if (code != 0 && code != TSDB_CODE_MND_ACTION_IN_PROGRESS) { + if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { mError("snode:%d, failed to drop since %s", dropReq.dnodeId, terrstr()); } diff --git a/source/dnode/mnode/impl/src/mndStb.c b/source/dnode/mnode/impl/src/mndStb.c index 7485510bc6f5db1873dced18e911adfe493a5d24..61f115e2bab32b64ee6a57e967fb0c8e5c287d0f 100644 --- a/source/dnode/mnode/impl/src/mndStb.c +++ b/source/dnode/mnode/impl/src/mndStb.c @@ -743,9 +743,7 @@ static int32_t mndCreateStb(SMnode *pMnode, SRpcMsg *pReq, SMCreateStbReq *pCrea mDebug("trans:%d, used to create stb:%s", pTrans->id, pCreate->name); - if (mndBuildStbFromReq(pMnode, &stbObj, pCreate, pDb) != 0) { - goto _OVER; - } + if (mndBuildStbFromReq(pMnode, &stbObj, pCreate, pDb) != 0) goto _OVER; if (mndAddStbToTrans(pMnode, pTrans, pDb, &stbObj) < 0) goto _OVER; @@ -827,10 +825,10 @@ static int32_t mndProcessMCreateStbReq(SRpcMsg *pReq) { } code = mndCreateStb(pMnode, pReq, &createReq, pDb); - if (code == 0) code = TSDB_CODE_MND_ACTION_IN_PROGRESS; + if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; _OVER: - if (code != 0 && code != TSDB_CODE_MND_ACTION_IN_PROGRESS) { + if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { mError("stb:%s, failed to create since %s", createReq.name, terrstr()); } @@ -1334,10 +1332,10 @@ static int32_t mndProcessMAlterStbReq(SRpcMsg *pReq) { } code = mndAlterStb(pMnode, pReq, &alterReq, pDb, pStb); - if (code == 0) code = TSDB_CODE_MND_ACTION_IN_PROGRESS; + if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; _OVER: - if (code != 0 && code != TSDB_CODE_MND_ACTION_IN_PROGRESS) { + if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { mError("stb:%s, failed to alter since %s", alterReq.name, terrstr()); } @@ -1475,10 +1473,10 @@ static int32_t mndProcessMDropStbReq(SRpcMsg *pReq) { } code = mndDropStb(pMnode, pReq, pDb, pStb); - if (code == 0) code = TSDB_CODE_MND_ACTION_IN_PROGRESS; + if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; _OVER: - if (code != 0 && code != TSDB_CODE_MND_ACTION_IN_PROGRESS) { + if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { mError("stb:%s, failed to drop since %s", dropReq.name, terrstr()); } @@ -1642,6 +1640,8 @@ int32_t mndValidateStbInfo(SMnode *pMnode, SSTableMetaVersion *pStbVersions, int if (pStbVersion->sversion != metaRsp.sversion) { taosArrayPush(batchMetaRsp.pArray, &metaRsp); + } else { + tFreeSTableMetaRsp(&metaRsp); } } @@ -1660,6 +1660,7 @@ int32_t mndValidateStbInfo(SMnode *pMnode, SSTableMetaVersion *pStbVersions, int } tSerializeSTableMetaBatchRsp(pRsp, rspLen, &batchMetaRsp); + tFreeSTableMetaBatchRsp(&batchMetaRsp); *ppRsp = pRsp; *pRspLen = rspLen; return 0; diff --git a/source/dnode/mnode/impl/src/mndStream.c b/source/dnode/mnode/impl/src/mndStream.c index 61a84fc95c335326a5f4172f66b4a1bd93ce1b41..9de6138689b43b195ea004c6ba6a7fa489d4060f 100644 --- a/source/dnode/mnode/impl/src/mndStream.c +++ b/source/dnode/mnode/impl/src/mndStream.c @@ -279,13 +279,13 @@ int32_t mndAddStreamToTrans(SMnode *pMnode, SStreamObj *pStream, const char *ast } mDebug("trans:%d, used to create stream:%s", pTrans->id, pStream->name); - SSdbRaw *pRedoRaw = mndStreamActionEncode(pStream); - if (pRedoRaw == NULL || mndTransAppendRedolog(pTrans, pRedoRaw) != 0) { - mError("trans:%d, failed to append redo log since %s", pTrans->id, terrstr()); + SSdbRaw *pCommitRaw = mndStreamActionEncode(pStream); + if (pCommitRaw == NULL || mndTransAppendCommitlog(pTrans, pCommitRaw) != 0) { + mError("trans:%d, failed to append commit log since %s", pTrans->id, terrstr()); mndTransDrop(pTrans); return -1; } - sdbSetRawStatus(pRedoRaw, SDB_STATUS_READY); + sdbSetRawStatus(pCommitRaw, SDB_STATUS_READY); return 0; } @@ -472,10 +472,10 @@ static int32_t mndProcessCreateStreamReq(SRpcMsg *pReq) { } code = mndCreateStream(pMnode, pReq, &createStreamReq, pDb); - if (code == 0) code = TSDB_CODE_MND_ACTION_IN_PROGRESS; + if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; CREATE_STREAM_OVER: - if (code != 0 && code != TSDB_CODE_MND_ACTION_IN_PROGRESS) { + if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { mError("stream:%s, failed to create since %s", createStreamReq.name, terrstr()); } diff --git a/source/dnode/mnode/impl/src/mndSubscribe.c b/source/dnode/mnode/impl/src/mndSubscribe.c index c82472eec05c0f5104dd9f3a9697078e27799948..3713bd501a42e5e450a0cb2dc80e0ebe14c32417 100644 --- a/source/dnode/mnode/impl/src/mndSubscribe.c +++ b/source/dnode/mnode/impl/src/mndSubscribe.c @@ -479,7 +479,7 @@ static int32_t mndPersistRebResult(SMnode *pMnode, SRpcMsg *pMsg, const SMqRebOu SMqTopicObj topicObj = {0}; memcpy(&topicObj, pTopic, sizeof(SMqTopicObj)); topicObj.refConsumerCnt = pTopic->refConsumerCnt - consumerNum; - if (mndSetTopicRedoLogs(pMnode, pTrans, &topicObj) != 0) goto REB_FAIL; + if (mndSetTopicCommitLogs(pMnode, pTrans, &topicObj) != 0) goto REB_FAIL; } } diff --git a/source/dnode/mnode/impl/src/mndTopic.c b/source/dnode/mnode/impl/src/mndTopic.c index c6eebb5c5d9600420806728066086699f6080b9b..ec3d30ff07b2eeb169cc214cc24fe43be4fe9f15 100644 --- a/source/dnode/mnode/impl/src/mndTopic.c +++ b/source/dnode/mnode/impl/src/mndTopic.c @@ -386,14 +386,14 @@ static int32_t mndCreateTopic(SMnode *pMnode, SRpcMsg *pReq, SCMCreateTopicReq * } mDebug("trans:%d, used to create topic:%s", pTrans->id, pCreate->name); - SSdbRaw *pRedoRaw = mndTopicActionEncode(&topicObj); - if (pRedoRaw == NULL || mndTransAppendRedolog(pTrans, pRedoRaw) != 0) { - mError("trans:%d, failed to append redo log since %s", pTrans->id, terrstr()); + SSdbRaw *pCommitRaw = mndTopicActionEncode(&topicObj); + if (pCommitRaw == NULL || mndTransAppendCommitlog(pTrans, pCommitRaw) != 0) { + mError("trans:%d, failed to append commit log since %s", pTrans->id, terrstr()); taosMemoryFreeClear(topicObj.physicalPlan); mndTransDrop(pTrans); return -1; } - sdbSetRawStatus(pRedoRaw, SDB_STATUS_READY); + sdbSetRawStatus(pCommitRaw, SDB_STATUS_READY); if (mndTransPrepare(pMnode, pTrans) != 0) { mError("trans:%d, failed to prepare since %s", pTrans->id, terrstr()); @@ -457,10 +457,10 @@ static int32_t mndProcessCreateTopicReq(SRpcMsg *pReq) { } code = mndCreateTopic(pMnode, pReq, &createTopicReq, pDb); - if (code == 0) code = TSDB_CODE_MND_ACTION_IN_PROGRESS; + if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; CREATE_TOPIC_OVER: - if (code != 0 && code != TSDB_CODE_MND_ACTION_IN_PROGRESS) { + if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { mError("topic:%s, failed to create since %s", createTopicReq.name, terrstr()); } @@ -473,13 +473,13 @@ CREATE_TOPIC_OVER: } static int32_t mndDropTopic(SMnode *pMnode, STrans *pTrans, SRpcMsg *pReq, SMqTopicObj *pTopic) { - SSdbRaw *pRedoRaw = mndTopicActionEncode(pTopic); - if (pRedoRaw == NULL || mndTransAppendRedolog(pTrans, pRedoRaw) != 0) { - mError("trans:%d, failed to append redo log since %s", pTrans->id, terrstr()); + SSdbRaw *pCommitRaw = mndTopicActionEncode(pTopic); + if (pCommitRaw == NULL || mndTransAppendCommitlog(pTrans, pCommitRaw) != 0) { + mError("trans:%d, failed to append commit log since %s", pTrans->id, terrstr()); mndTransDrop(pTrans); return -1; } - sdbSetRawStatus(pRedoRaw, SDB_STATUS_DROPPED); + sdbSetRawStatus(pCommitRaw, SDB_STATUS_DROPPED); if (mndTransPrepare(pMnode, pTrans) != 0) { mError("trans:%d, failed to prepare since %s", pTrans->id, terrstr()); @@ -547,7 +547,7 @@ static int32_t mndProcessDropTopicReq(SRpcMsg *pReq) { return -1; } - return TSDB_CODE_MND_ACTION_IN_PROGRESS; + return TSDB_CODE_ACTION_IN_PROGRESS; } static int32_t mndProcessDropTopicInRsp(SRpcMsg *pRsp) { @@ -627,11 +627,11 @@ static int32_t mndRetrieveTopic(SRpcMsg *pReq, SShowObj *pShow, SSDataBlock *pBl return numOfRows; } -int32_t mndSetTopicRedoLogs(SMnode *pMnode, STrans *pTrans, SMqTopicObj *pTopic) { - SSdbRaw *pRedoRaw = mndTopicActionEncode(pTopic); - if (pRedoRaw == NULL) return -1; - if (mndTransAppendCommitlog(pTrans, pRedoRaw) != 0) return -1; - if (sdbSetRawStatus(pRedoRaw, SDB_STATUS_READY) != 0) return -1; +int32_t mndSetTopicCommitLogs(SMnode *pMnode, STrans *pTrans, SMqTopicObj *pTopic) { + SSdbRaw *pCommitRaw = mndTopicActionEncode(pTopic); + if (pCommitRaw == NULL) return -1; + if (mndTransAppendCommitlog(pTrans, pCommitRaw) != 0) return -1; + if (sdbSetRawStatus(pCommitRaw, SDB_STATUS_READY) != 0) return -1; return 0; } diff --git a/source/dnode/mnode/impl/src/mndTrans.c b/source/dnode/mnode/impl/src/mndTrans.c index 35ecaa748ecdbdce486814f063b7678004f68909..2a500829e5039e87b072e7e624df3dca159b57c3 100644 --- a/source/dnode/mnode/impl/src/mndTrans.c +++ b/source/dnode/mnode/impl/src/mndTrans.c @@ -768,6 +768,12 @@ int32_t mndTransPrepare(SMnode *pMnode, STrans *pTrans) { return -1; } + if (taosArrayGetSize(pTrans->commitLogs) <= 0) { + terrno = TSDB_CODE_MND_TRANS_CLOG_IS_NULL; + mError("trans:%d, failed to prepare since %s", pTrans->id, terrstr()); + return -1; + } + mDebug("trans:%d, prepare transaction", pTrans->id); if (mndTransSync(pMnode, pTrans) != 0) { mError("trans:%d, failed to prepare since %s", pTrans->id, terrstr()); @@ -994,9 +1000,6 @@ static int32_t mndTransSendActionMsg(SMnode *pMnode, STrans *pTrans, SArray *pAr pAction->msgSent = 0; pAction->msgReceived = 0; pAction->errCode = terrno; - if (terrno == TSDB_CODE_INVALID_PTR || terrno == TSDB_CODE_NODE_OFFLINE) { - rpcFreeCont(rpcMsg.pCont); - } mError("trans:%d, action:%d not send since %s", pTrans->id, action, terrstr()); return -1; } @@ -1038,13 +1041,13 @@ static int32_t mndTransExecuteActions(SMnode *pMnode, STrans *pTrans, SArray *pA } } else { mDebug("trans:%d, %d of %d actions executed", pTrans->id, numOfReceived, numOfActions); - return TSDB_CODE_MND_ACTION_IN_PROGRESS; + return TSDB_CODE_ACTION_IN_PROGRESS; } } static int32_t mndTransExecuteRedoActions(SMnode *pMnode, STrans *pTrans) { int32_t code = mndTransExecuteActions(pMnode, pTrans, pTrans->redoActions); - if (code != 0 && code != TSDB_CODE_MND_ACTION_IN_PROGRESS) { + if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { mError("failed to execute redoActions since:%s, code:0x%x", terrstr(), terrno); } return code; @@ -1052,7 +1055,7 @@ static int32_t mndTransExecuteRedoActions(SMnode *pMnode, STrans *pTrans) { static int32_t mndTransExecuteUndoActions(SMnode *pMnode, STrans *pTrans) { int32_t code = mndTransExecuteActions(pMnode, pTrans, pTrans->undoActions); - if (code != 0 && code != TSDB_CODE_MND_ACTION_IN_PROGRESS) { + if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { mError("failed to execute undoActions since %s", terrstr()); } return code; @@ -1083,6 +1086,8 @@ static bool mndTransPerformRedoLogStage(SMnode *pMnode, STrans *pTrans) { } static bool mndTransPerformRedoActionStage(SMnode *pMnode, STrans *pTrans) { + if (!mndIsMaster(pMnode)) return false; + bool continueExec = true; int32_t code = mndTransExecuteRedoActions(pMnode, pTrans); @@ -1091,7 +1096,7 @@ static bool mndTransPerformRedoActionStage(SMnode *pMnode, STrans *pTrans) { pTrans->stage = TRN_STAGE_COMMIT; mDebug("trans:%d, stage from redoAction to commit", pTrans->id); continueExec = true; - } else if (code == TSDB_CODE_MND_ACTION_IN_PROGRESS) { + } else if (code == TSDB_CODE_ACTION_IN_PROGRESS) { mDebug("trans:%d, stage keep on redoAction since %s", pTrans->id, tstrerror(code)); continueExec = false; } else { @@ -1172,6 +1177,8 @@ static bool mndTransPerformUndoLogStage(SMnode *pMnode, STrans *pTrans) { } static bool mndTransPerformUndoActionStage(SMnode *pMnode, STrans *pTrans) { + if (!mndIsMaster(pMnode)) return false; + bool continueExec = true; int32_t code = mndTransExecuteUndoActions(pMnode, pTrans); @@ -1179,7 +1186,7 @@ static bool mndTransPerformUndoActionStage(SMnode *pMnode, STrans *pTrans) { pTrans->stage = TRN_STAGE_UNDO_LOG; mDebug("trans:%d, stage from undoAction to undoLog", pTrans->id); continueExec = true; - } else if (code == TSDB_CODE_MND_ACTION_IN_PROGRESS) { + } else if (code == TSDB_CODE_ACTION_IN_PROGRESS) { mDebug("trans:%d, stage keep on undoAction since %s", pTrans->id, tstrerror(code)); continueExec = false; } else { diff --git a/source/dnode/mnode/impl/src/mndUser.c b/source/dnode/mnode/impl/src/mndUser.c index b59175d86cabd85f28334b95e42afd4b84ab1938..5f2147a5fe95f03873f8be2bc89df25e90092a9e 100644 --- a/source/dnode/mnode/impl/src/mndUser.c +++ b/source/dnode/mnode/impl/src/mndUser.c @@ -233,6 +233,7 @@ static int32_t mndUserActionUpdate(SSdb *pSdb, SUserObj *pOld, SUserObj *pNew) { mTrace("user:%s, perform update action, old row:%p new row:%p", pOld->user, pOld, pNew); taosWLockLatch(&pOld->lock); pOld->updateTime = pNew->updateTime; + pOld->authVersion = pNew->authVersion; memcpy(pOld->pass, pNew->pass, TSDB_PASSWORD_LEN); TSWAP(pOld->readDbs, pNew->readDbs); TSWAP(pOld->writeDbs, pNew->writeDbs); @@ -271,13 +272,13 @@ static int32_t mndCreateUser(SMnode *pMnode, char *acct, SCreateUserReq *pCreate } mDebug("trans:%d, used to create user:%s", pTrans->id, pCreate->user); - SSdbRaw *pRedoRaw = mndUserActionEncode(&userObj); - if (pRedoRaw == NULL || mndTransAppendRedolog(pTrans, pRedoRaw) != 0) { - mError("trans:%d, failed to append redo log since %s", pTrans->id, terrstr()); + SSdbRaw *pCommitRaw = mndUserActionEncode(&userObj); + if (pCommitRaw == NULL || mndTransAppendCommitlog(pTrans, pCommitRaw) != 0) { + mError("trans:%d, failed to commit redo log since %s", pTrans->id, terrstr()); mndTransDrop(pTrans); return -1; } - sdbSetRawStatus(pRedoRaw, SDB_STATUS_READY); + sdbSetRawStatus(pCommitRaw, SDB_STATUS_READY); if (mndTransPrepare(pMnode, pTrans) != 0) { mError("trans:%d, failed to prepare since %s", pTrans->id, terrstr()); @@ -330,10 +331,10 @@ static int32_t mndProcessCreateUserReq(SRpcMsg *pReq) { } code = mndCreateUser(pMnode, pOperUser->acct, &createReq, pReq); - if (code == 0) code = TSDB_CODE_MND_ACTION_IN_PROGRESS; + if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; _OVER: - if (code != 0 && code != TSDB_CODE_MND_ACTION_IN_PROGRESS) { + if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { mError("user:%s, failed to create since %s", createReq.user, terrstr()); } @@ -351,13 +352,13 @@ static int32_t mndAlterUser(SMnode *pMnode, SUserObj *pOld, SUserObj *pNew, SRpc } mDebug("trans:%d, used to alter user:%s", pTrans->id, pOld->user); - SSdbRaw *pRedoRaw = mndUserActionEncode(pNew); - if (pRedoRaw == NULL || mndTransAppendRedolog(pTrans, pRedoRaw) != 0) { - mError("trans:%d, failed to append redo log since %s", pTrans->id, terrstr()); + SSdbRaw *pCommitRaw = mndUserActionEncode(pNew); + if (pCommitRaw == NULL || mndTransAppendCommitlog(pTrans, pCommitRaw) != 0) { + mError("trans:%d, failed to append commit log since %s", pTrans->id, terrstr()); mndTransDrop(pTrans); return -1; } - sdbSetRawStatus(pRedoRaw, SDB_STATUS_READY); + sdbSetRawStatus(pCommitRaw, SDB_STATUS_READY); if (mndTransPrepare(pMnode, pTrans) != 0) { mError("trans:%d, failed to prepare since %s", pTrans->id, terrstr()); @@ -535,10 +536,10 @@ static int32_t mndProcessAlterUserReq(SRpcMsg *pReq) { } code = mndAlterUser(pMnode, pUser, &newUser, pReq); - if (code == 0) code = TSDB_CODE_MND_ACTION_IN_PROGRESS; + if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; _OVER: - if (code != 0 && code != TSDB_CODE_MND_ACTION_IN_PROGRESS) { + if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { mError("user:%s, failed to alter since %s", alterReq.user, terrstr()); } @@ -558,13 +559,13 @@ static int32_t mndDropUser(SMnode *pMnode, SRpcMsg *pReq, SUserObj *pUser) { } mDebug("trans:%d, used to drop user:%s", pTrans->id, pUser->user); - SSdbRaw *pRedoRaw = mndUserActionEncode(pUser); - if (pRedoRaw == NULL || mndTransAppendRedolog(pTrans, pRedoRaw) != 0) { - mError("trans:%d, failed to append redo log since %s", pTrans->id, terrstr()); + SSdbRaw *pCommitRaw = mndUserActionEncode(pUser); + if (pCommitRaw == NULL || mndTransAppendCommitlog(pTrans, pCommitRaw) != 0) { + mError("trans:%d, failed to append commit log since %s", pTrans->id, terrstr()); mndTransDrop(pTrans); return -1; } - sdbSetRawStatus(pRedoRaw, SDB_STATUS_DROPPED); + sdbSetRawStatus(pCommitRaw, SDB_STATUS_DROPPED); if (mndTransPrepare(pMnode, pTrans) != 0) { mError("trans:%d, failed to prepare since %s", pTrans->id, terrstr()); @@ -612,10 +613,10 @@ static int32_t mndProcessDropUserReq(SRpcMsg *pReq) { } code = mndDropUser(pMnode, pReq, pUser); - if (code == 0) code = TSDB_CODE_MND_ACTION_IN_PROGRESS; + if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; _OVER: - if (code != 0 && code != TSDB_CODE_MND_ACTION_IN_PROGRESS) { + if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) { mError("user:%s, failed to drop since %s", dropReq.user, terrstr()); } @@ -765,6 +766,7 @@ int32_t mndValidateUserAuthInfo(SMnode *pMnode, SUserAuthVersion *pUsers, int32_ continue; } + pUsers[i].version = ntohl(pUsers[i].version); if (pUser->authVersion <= pUsers[i].version) { mndReleaseUser(pMnode, pUser); continue; diff --git a/source/dnode/mnode/impl/src/mnode.c b/source/dnode/mnode/impl/src/mnode.c index 8c805dd8c705d41f62a5728c8bf978d0f30924d5..23abd1503b7de1734f6a5e2238a1606d10b2c42f 100644 --- a/source/dnode/mnode/impl/src/mnode.c +++ b/source/dnode/mnode/impl/src/mnode.c @@ -86,7 +86,6 @@ static void *mndThreadFp(void *param) { lastTime++; taosMsleep(100); if (pMnode->stopped) break; - if (!mndIsMaster(pMnode)) continue; if (lastTime % (tsTransPullupInterval * 10) == 0) { mndPullupTrans(pMnode); @@ -346,7 +345,8 @@ int32_t mndProcessMsg(SRpcMsg *pMsg) { mTrace("msg:%p, will be processed, type:%s app:%p", pMsg, TMSG_INFO(pMsg->msgType), ahandle); if (IsReq(pMsg)) { - if (!mndIsMaster(pMnode)) { + if (!mndIsMaster(pMnode) && pMsg->msgType != TDMT_MND_TRANS_TIMER && pMsg->msgType != TDMT_MND_MQ_TIMER && + pMsg->msgType != TDMT_MND_TELEM_TIMER) { terrno = TSDB_CODE_APP_NOT_READY; mDebug("msg:%p, failed to process since %s, app:%p", pMsg, terrstr(), ahandle); return -1; @@ -367,7 +367,7 @@ int32_t mndProcessMsg(SRpcMsg *pMsg) { } int32_t code = (*fp)(pMsg); - if (code == TSDB_CODE_MND_ACTION_IN_PROGRESS) { + if (code == TSDB_CODE_ACTION_IN_PROGRESS) { terrno = code; mTrace("msg:%p, in progress, app:%p", pMsg, ahandle); } else if (code != 0) { diff --git a/source/dnode/mnode/impl/test/acct/acct.cpp b/source/dnode/mnode/impl/test/acct/acct.cpp index 6dcb931ed5e59c0a98904792cf25dfe762248ee7..46a9a465ebe631665fa753c6de17569160a158d1 100644 --- a/source/dnode/mnode/impl/test/acct/acct.cpp +++ b/source/dnode/mnode/impl/test/acct/acct.cpp @@ -13,7 +13,7 @@ class MndTestAcct : public ::testing::Test { protected: - static void SetUpTestSuite() { test.Init("/tmp/acctTest", 9012); } + static void SetUpTestSuite() { test.Init(TD_TMP_DIR_PATH "acctTest", 9012); } static void TearDownTestSuite() { test.Cleanup(); } static Testbase test; diff --git a/source/dnode/mnode/impl/test/bnode/mbnode.cpp b/source/dnode/mnode/impl/test/bnode/mbnode.cpp index 316ac8cc3628e3b8cf747ab2e814c8dd6fcd50ff..c93e2142d02de318da323fa00ee7c04531d58874 100644 --- a/source/dnode/mnode/impl/test/bnode/mbnode.cpp +++ b/source/dnode/mnode/impl/test/bnode/mbnode.cpp @@ -18,11 +18,11 @@ class MndTestBnode : public ::testing::Test { public: static void SetUpTestSuite() { - test.Init("/tmp/mnode_test_bnode1", 9018); + test.Init(TD_TMP_DIR_PATH "mnode_test_bnode1", 9018); const char* fqdn = "localhost"; const char* firstEp = "localhost:9018"; - server2.Start("/tmp/mnode_test_bnode2", 9019); + server2.Start(TD_TMP_DIR_PATH "mnode_test_bnode2", 9019); taosMsleep(300); } diff --git a/source/dnode/mnode/impl/test/db/db.cpp b/source/dnode/mnode/impl/test/db/db.cpp index 545f9f22bb869dbc432e81620425905ed89f73f6..a1bab5d1d4786bdcfba5774f657264f46e6f471c 100644 --- a/source/dnode/mnode/impl/test/db/db.cpp +++ b/source/dnode/mnode/impl/test/db/db.cpp @@ -13,7 +13,7 @@ class MndTestDb : public ::testing::Test { protected: - static void SetUpTestSuite() { test.Init("/tmp/mnode_test_db", 9030); } + static void SetUpTestSuite() { test.Init(TD_TMP_DIR_PATH "mnode_test_db", 9030); } static void TearDownTestSuite() { test.Cleanup(); } static Testbase test; diff --git a/source/dnode/mnode/impl/test/dnode/mdnode.cpp b/source/dnode/mnode/impl/test/dnode/mdnode.cpp index e63536d4940a4779aa50a8947102eaf7c6904b34..0b42b2821988a4fe560d9d4660c8c70a3e9cbb4b 100644 --- a/source/dnode/mnode/impl/test/dnode/mdnode.cpp +++ b/source/dnode/mnode/impl/test/dnode/mdnode.cpp @@ -18,14 +18,14 @@ class MndTestDnode : public ::testing::Test { public: static void SetUpTestSuite() { - test.Init("/tmp/dnode_test_dnode1", 9023); + test.Init(TD_TMP_DIR_PATH "dnode_test_dnode1", 9023); const char* fqdn = "localhost"; const char* firstEp = "localhost:9023"; - // server2.Start("/tmp/dnode_test_dnode2", fqdn, 9024, firstEp); - // server3.Start("/tmp/dnode_test_dnode3", fqdn, 9025, firstEp); - // server4.Start("/tmp/dnode_test_dnode4", fqdn, 9026, firstEp); - // server5.Start("/tmp/dnode_test_dnode5", fqdn, 9027, firstEp); + // server2.Start(TD_TMP_DIR_PATH "dnode_test_dnode2", fqdn, 9024, firstEp); + // server3.Start(TD_TMP_DIR_PATH "dnode_test_dnode3", fqdn, 9025, firstEp); + // server4.Start(TD_TMP_DIR_PATH "dnode_test_dnode4", fqdn, 9026, firstEp); + // server5.Start(TD_TMP_DIR_PATH "dnode_test_dnode5", fqdn, 9027, firstEp); taosMsleep(300); } diff --git a/source/dnode/mnode/impl/test/func/func.cpp b/source/dnode/mnode/impl/test/func/func.cpp index c8f832160b4addc852fcf3f610dcd8d608cfef29..2bebe7ef199c8e61d1204a611b84b66051002087 100644 --- a/source/dnode/mnode/impl/test/func/func.cpp +++ b/source/dnode/mnode/impl/test/func/func.cpp @@ -13,7 +13,7 @@ class MndTestFunc : public ::testing::Test { protected: - static void SetUpTestSuite() { test.Init("/tmp/mnode_test_func", 9038); } + static void SetUpTestSuite() { test.Init(TD_TMP_DIR_PATH "mnode_test_func", 9038); } static void TearDownTestSuite() { test.Cleanup(); } static Testbase test; diff --git a/source/dnode/mnode/impl/test/mnode/mnode.cpp b/source/dnode/mnode/impl/test/mnode/mnode.cpp index d953bdfdcba16c1987c2fb48980b2989b56bc400..1ed613c723ca504777929b8a6715e9d3b7594e06 100644 --- a/source/dnode/mnode/impl/test/mnode/mnode.cpp +++ b/source/dnode/mnode/impl/test/mnode/mnode.cpp @@ -18,11 +18,11 @@ class MndTestMnode : public ::testing::Test { public: static void SetUpTestSuite() { - test.Init("/tmp/mnode_test_mnode1", 9028); + test.Init(TD_TMP_DIR_PATH "mnode_test_mnode1", 9028); const char* fqdn = "localhost"; const char* firstEp = "localhost:9028"; - // server2.Start("/tmp/mnode_test_mnode2", fqdn, 9029, firstEp); + // server2.Start(TD_TMP_DIR_PATH "mnode_test_mnode2", fqdn, 9029, firstEp); taosMsleep(300); } diff --git a/source/dnode/mnode/impl/test/profile/profile.cpp b/source/dnode/mnode/impl/test/profile/profile.cpp index 9c8e0298aa336ee5634480395a9e33ce3390d065..794374a91d51dfa9746028cebab6af3e394f2798 100644 --- a/source/dnode/mnode/impl/test/profile/profile.cpp +++ b/source/dnode/mnode/impl/test/profile/profile.cpp @@ -13,7 +13,7 @@ class MndTestProfile : public ::testing::Test { protected: - static void SetUpTestSuite() { test.Init("/tmp/mnode_test_profile", 9031); } + static void SetUpTestSuite() { test.Init(TD_TMP_DIR_PATH "mnode_test_profile", 9031); } static void TearDownTestSuite() { test.Cleanup(); } static Testbase test; diff --git a/source/dnode/mnode/impl/test/qnode/qnode.cpp b/source/dnode/mnode/impl/test/qnode/qnode.cpp index 87ba7caa4edf888057aad3d3b1ab1e157e54b5e4..57b38e55c1d392bda809b094b0f1bf918aae8888 100644 --- a/source/dnode/mnode/impl/test/qnode/qnode.cpp +++ b/source/dnode/mnode/impl/test/qnode/qnode.cpp @@ -18,11 +18,11 @@ class MndTestQnode : public ::testing::Test { public: static void SetUpTestSuite() { - test.Init("/tmp/mnode_test_qnode1", 9014); + test.Init(TD_TMP_DIR_PATH "mnode_test_qnode1", 9014); const char* fqdn = "localhost"; const char* firstEp = "localhost:9014"; - // server2.Start("/tmp/mnode_test_qnode2", fqdn, 9015, firstEp); + // server2.Start(TD_TMP_DIR_PATH "mnode_test_qnode2", fqdn, 9015, firstEp); taosMsleep(300); } diff --git a/source/dnode/mnode/impl/test/sdb/sdbTest.cpp b/source/dnode/mnode/impl/test/sdb/sdbTest.cpp index b93adf99305492fec346a1fb981aef7e4e55979f..f67ca2fb91d1847ad1491802a0ee69052c90abed 100644 --- a/source/dnode/mnode/impl/test/sdb/sdbTest.cpp +++ b/source/dnode/mnode/impl/test/sdb/sdbTest.cpp @@ -31,7 +31,7 @@ class MndTestSdb : public ::testing::Test { tsLogEmbedded = 1; tsAsyncLog = 0; - const char *path = "/tmp/td"; + const char *path = TD_TMP_DIR_PATH "td"; taosRemoveDir(path); taosMkDir(path); tstrncpy(tsLogDir, path, PATH_MAX); @@ -385,7 +385,7 @@ TEST_F(MndTestSdb, 01_Write_Str) { mnode.v100 = 100; mnode.v200 = 200; opt.pMnode = &mnode; - opt.path = "/tmp/mnode_test_sdb"; + opt.path = TD_TMP_DIR_PATH "mnode_test_sdb"; taosRemoveDir(opt.path); SSdbTable strTable1; @@ -730,7 +730,7 @@ TEST_F(MndTestSdb, 01_Read_Str) { mnode.v100 = 100; mnode.v200 = 200; opt.pMnode = &mnode; - opt.path = "/tmp/mnode_test_sdb"; + opt.path = TD_TMP_DIR_PATH "mnode_test_sdb"; SSdbTable strTable1; memset(&strTable1, 0, sizeof(SSdbTable)); diff --git a/source/dnode/mnode/impl/test/show/show.cpp b/source/dnode/mnode/impl/test/show/show.cpp index 5c431f65d3a5eb5663eb3a92005a4635e6f7f384..0de8c9dca8cc69ce46bc41d439571fa8c7ad046b 100644 --- a/source/dnode/mnode/impl/test/show/show.cpp +++ b/source/dnode/mnode/impl/test/show/show.cpp @@ -13,7 +13,7 @@ class MndTestShow : public ::testing::Test { protected: - static void SetUpTestSuite() { test.Init("/tmp/mnode_test_show", 9021); } + static void SetUpTestSuite() { test.Init(TD_TMP_DIR_PATH "mnode_test_show", 9021); } static void TearDownTestSuite() { test.Cleanup(); } static Testbase test; diff --git a/source/dnode/mnode/impl/test/sma/sma.cpp b/source/dnode/mnode/impl/test/sma/sma.cpp index 4dc4e0477947d647961acd1859720f1013d6da45..d795816f57b5e65a7bedaef6c14af6db84680703 100644 --- a/source/dnode/mnode/impl/test/sma/sma.cpp +++ b/source/dnode/mnode/impl/test/sma/sma.cpp @@ -13,7 +13,7 @@ class MndTestSma : public ::testing::Test { protected: - static void SetUpTestSuite() { test.Init("/tmp/mnode_test_sma", 9035); } + static void SetUpTestSuite() { test.Init(TD_TMP_DIR_PATH "mnode_test_sma", 9035); } static void TearDownTestSuite() { test.Cleanup(); } static Testbase test; diff --git a/source/dnode/mnode/impl/test/snode/snode.cpp b/source/dnode/mnode/impl/test/snode/snode.cpp index 0b1d3c38b25a204902e2b884c22006c89476dfb3..1828fbd570499107e3e3131631c35602bead983f 100644 --- a/source/dnode/mnode/impl/test/snode/snode.cpp +++ b/source/dnode/mnode/impl/test/snode/snode.cpp @@ -18,11 +18,11 @@ class MndTestSnode : public ::testing::Test { public: static void SetUpTestSuite() { - test.Init("/tmp/mnode_test_snode1", 9016); + test.Init(TD_TMP_DIR_PATH "mnode_test_snode1", 9016); const char* fqdn = "localhost"; const char* firstEp = "localhost:9016"; - // server2.Start("/tmp/mnode_test_snode2", fqdn, 9017, firstEp); + // server2.Start(TD_TMP_DIR_PATH "mnode_test_snode2", fqdn, 9017, firstEp); taosMsleep(300); } diff --git a/source/dnode/mnode/impl/test/stb/stb.cpp b/source/dnode/mnode/impl/test/stb/stb.cpp index b8873210ab995bde06f6c2baf17d14975bc591e1..56f1b8240d0ee28a7023a204ed2e10ca9f93cc7e 100644 --- a/source/dnode/mnode/impl/test/stb/stb.cpp +++ b/source/dnode/mnode/impl/test/stb/stb.cpp @@ -13,7 +13,7 @@ class MndTestStb : public ::testing::Test { protected: - static void SetUpTestSuite() { test.Init("/tmp/mnode_test_stb", 9034); } + static void SetUpTestSuite() { test.Init(TD_TMP_DIR_PATH "mnode_test_stb", 9034); } static void TearDownTestSuite() { test.Cleanup(); } static Testbase test; diff --git a/source/dnode/mnode/impl/test/topic/topic.cpp b/source/dnode/mnode/impl/test/topic/topic.cpp index eccc1b99d3f3dc8189712776e4ee852c6169cdfa..433a0ab5cc100d0fc96ac575a128f350df5e8bf3 100644 --- a/source/dnode/mnode/impl/test/topic/topic.cpp +++ b/source/dnode/mnode/impl/test/topic/topic.cpp @@ -13,7 +13,7 @@ class MndTestTopic : public ::testing::Test { protected: - static void SetUpTestSuite() { test.Init("/tmp/mnode_test_topic", 9039); } + static void SetUpTestSuite() { test.Init(TD_TMP_DIR_PATH "mnode_test_topic", 9039); } static void TearDownTestSuite() { test.Cleanup(); } static Testbase test; diff --git a/source/dnode/mnode/impl/test/trans/CMakeLists.txt b/source/dnode/mnode/impl/test/trans/CMakeLists.txt index 55fc3abbc26feec948aeffcd8168259b22c07068..023c8caa627777233536b16c94e4ede71ccf6e6d 100644 --- a/source/dnode/mnode/impl/test/trans/CMakeLists.txt +++ b/source/dnode/mnode/impl/test/trans/CMakeLists.txt @@ -31,7 +31,7 @@ target_include_directories( PUBLIC "${TD_SOURCE_DIR}/include/dnode/mnode" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../inc" ) -add_test( - NAME transTest2 - COMMAND transTest2 -) +# add_test( +# NAME transTest2 +# COMMAND transTest2 +# ) diff --git a/source/dnode/mnode/impl/test/trans/trans1.cpp b/source/dnode/mnode/impl/test/trans/trans1.cpp index 80109a39b24c452f1a656b4bf172c8e2ad38acd5..5a470fc900f418c429befe12d6cfae643522a2e3 100644 --- a/source/dnode/mnode/impl/test/trans/trans1.cpp +++ b/source/dnode/mnode/impl/test/trans/trans1.cpp @@ -14,10 +14,10 @@ class MndTestTrans1 : public ::testing::Test { protected: static void SetUpTestSuite() { - test.Init("/tmp/mnode_test_trans1", 9013); + test.Init(TD_TMP_DIR_PATH "mnode_test_trans1", 9013); const char* fqdn = "localhost"; const char* firstEp = "localhost:9013"; - // server2.Start("/tmp/mnode_test_trans2", fqdn, 9020, firstEp); + // server2.Start(TD_TMP_DIR_PATH "mnode_test_trans2", fqdn, 9020, firstEp); } static void TearDownTestSuite() { @@ -26,7 +26,7 @@ class MndTestTrans1 : public ::testing::Test { } static void KillThenRestartServer() { - char file[PATH_MAX] = "/tmp/mnode_test_trans1/mnode/data/sdb.data"; + char file[PATH_MAX] = TD_TMP_DIR_PATH "mnode_test_trans1/mnode/data/sdb.data"; TdFilePtr pFile = taosOpenFile(file, TD_FILE_READ); int32_t size = 3 * 1024 * 1024; void* buffer = taosMemoryMalloc(size); diff --git a/source/dnode/mnode/impl/test/trans/trans2.cpp b/source/dnode/mnode/impl/test/trans/trans2.cpp index c4ed48fe60f069b05ca445c271a54d53bff810da..ec972cf784ce8f43e40def54b09d94eb13844c47 100644 --- a/source/dnode/mnode/impl/test/trans/trans2.cpp +++ b/source/dnode/mnode/impl/test/trans/trans2.cpp @@ -41,7 +41,7 @@ class MndTestTrans2 : public ::testing::Test { tsLogEmbedded = 1; tsAsyncLog = 0; - const char *logpath = "/tmp/td"; + const char *logpath = TD_TMP_DIR_PATH "td"; taosRemoveDir(logpath); taosMkDir(logpath); tstrncpy(tsLogDir, logpath, PATH_MAX); @@ -68,7 +68,7 @@ class MndTestTrans2 : public ::testing::Test { tsTransPullupInterval = 1; - const char *mnodepath = "/tmp/mnode_test_trans"; + const char *mnodepath = TD_TMP_DIR_PATH "mnode_test_trans"; taosRemoveDir(mnodepath); pMnode = mndOpen(mnodepath, &opt); mndStart(pMnode); diff --git a/source/dnode/mnode/impl/test/user/user.cpp b/source/dnode/mnode/impl/test/user/user.cpp index 9e4bd79274a9da6810d1ab7f03edcd170d348183..6aa28a9007e8bc8e7c45621eb7e29280da964823 100644 --- a/source/dnode/mnode/impl/test/user/user.cpp +++ b/source/dnode/mnode/impl/test/user/user.cpp @@ -13,7 +13,7 @@ class MndTestUser : public ::testing::Test { protected: - static void SetUpTestSuite() { test.Init("/tmp/mnode_test_user", 9011); } + static void SetUpTestSuite() { test.Init(TD_TMP_DIR_PATH "mnode_test_user", 9011); } static void TearDownTestSuite() { test.Cleanup(); } static Testbase test; diff --git a/source/dnode/mnode/sdb/inc/sdbInt.h b/source/dnode/mnode/sdb/inc/sdbInt.h index 23c0f8a01c19ffa1b64dd15fd6d38dc893c8b130..c49d6e8fb287619d9503282dd2e164ed432ce823 100644 --- a/source/dnode/mnode/sdb/inc/sdbInt.h +++ b/source/dnode/mnode/sdb/inc/sdbInt.h @@ -24,12 +24,14 @@ extern "C" { #endif +// clang-format off #define mFatal(...) { if (mDebugFlag & DEBUG_FATAL) { taosPrintLog("MND FATAL ", DEBUG_FATAL, 255, __VA_ARGS__); }} #define mError(...) { if (mDebugFlag & DEBUG_ERROR) { taosPrintLog("MND ERROR ", DEBUG_ERROR, 255, __VA_ARGS__); }} #define mWarn(...) { if (mDebugFlag & DEBUG_WARN) { taosPrintLog("MND WARN ", DEBUG_WARN, 255, __VA_ARGS__); }} #define mInfo(...) { if (mDebugFlag & DEBUG_INFO) { taosPrintLog("MND ", DEBUG_INFO, 255, __VA_ARGS__); }} #define mDebug(...) { if (mDebugFlag & DEBUG_DEBUG) { taosPrintLog("MND ", DEBUG_DEBUG, mDebugFlag, __VA_ARGS__); }} #define mTrace(...) { if (mDebugFlag & DEBUG_TRACE) { taosPrintLog("MND ", DEBUG_TRACE, mDebugFlag, __VA_ARGS__); }} +// clang-format on typedef struct SSdbRaw { int8_t type; diff --git a/source/dnode/mnode/sdb/src/sdbFile.c b/source/dnode/mnode/sdb/src/sdbFile.c index ad1429f667cbdc7ea2b18320e6aeb589795a2035..a391ea8d03c6e2119fb21e0d0178ee9096883d48 100644 --- a/source/dnode/mnode/sdb/src/sdbFile.c +++ b/source/dnode/mnode/sdb/src/sdbFile.c @@ -20,6 +20,7 @@ #define SDB_TABLE_SIZE 24 #define SDB_RESERVE_SIZE 512 +#define SDB_FILE_VER 1 static int32_t sdbRunDeployFp(SSdb *pSdb) { mDebug("start to deploy sdb"); @@ -39,7 +40,22 @@ static int32_t sdbRunDeployFp(SSdb *pSdb) { } static int32_t sdbReadFileHead(SSdb *pSdb, TdFilePtr pFile) { - int32_t ret = taosReadFile(pFile, &pSdb->curVer, sizeof(int64_t)); + int64_t sver = 0; + int32_t ret = taosReadFile(pFile, &sver, sizeof(int64_t)); + if (ret < 0) { + terrno = TAOS_SYSTEM_ERROR(errno); + return -1; + } + if (ret != sizeof(int64_t)) { + terrno = TSDB_CODE_FILE_CORRUPTED; + return -1; + } + if (sver != SDB_FILE_VER) { + terrno = TSDB_CODE_FILE_CORRUPTED; + return -1; + } + + ret = taosReadFile(pFile, &pSdb->curVer, sizeof(int64_t)); if (ret < 0) { terrno = TAOS_SYSTEM_ERROR(errno); return -1; @@ -96,6 +112,12 @@ static int32_t sdbReadFileHead(SSdb *pSdb, TdFilePtr pFile) { } static int32_t sdbWriteFileHead(SSdb *pSdb, TdFilePtr pFile) { + int64_t sver = SDB_FILE_VER; + if (taosWriteFile(pFile, &sver, sizeof(int64_t)) != sizeof(int64_t)) { + terrno = TAOS_SYSTEM_ERROR(errno); + return -1; + } + if (taosWriteFile(pFile, &pSdb->curVer, sizeof(int64_t)) != sizeof(int64_t)) { terrno = TAOS_SYSTEM_ERROR(errno); return -1; @@ -256,7 +278,7 @@ static int32_t sdbWriteFileImp(SSdb *pSdb) { mTrace("write %s to file, total %d rows", sdbTableName(i), sdbGetSize(pSdb, i)); - SHashObj *hash = pSdb->hashObjs[i]; + SHashObj *hash = pSdb->hashObjs[i]; TdThreadRwlock *pLock = &pSdb->locks[i]; taosThreadRwlockWrlock(pLock); diff --git a/source/dnode/qnode/src/qnode.c b/source/dnode/qnode/src/qnode.c index 1259363f9468c2667536a17652010914dd4e2eac..929643fcdf91ef7ba0d6a02b8a07de34f0209d54 100644 --- a/source/dnode/qnode/src/qnode.c +++ b/source/dnode/qnode/src/qnode.c @@ -43,44 +43,49 @@ void qndClose(SQnode *pQnode) { int32_t qndGetLoad(SQnode *pQnode, SQnodeLoad *pLoad) { return 0; } int32_t qndProcessQueryMsg(SQnode *pQnode, SRpcMsg *pMsg) { - qTrace("message in qnode query queue is processing"); + int32_t code = -1; SReadHandle handle = {.pMsgCb = &pQnode->msgCb}; + qTrace("message in qnode queue is processing"); switch (pMsg->msgType) { - case TDMT_VND_QUERY: { - return qWorkerProcessQueryMsg(&handle, pQnode->pQuery, pMsg); - } + case TDMT_VND_QUERY: + code = qWorkerProcessQueryMsg(&handle, pQnode->pQuery, pMsg); + break; case TDMT_VND_QUERY_CONTINUE: - return qWorkerProcessCQueryMsg(&handle, pQnode->pQuery, pMsg); - default: - qError("unknown msg type:%d in query queue", pMsg->msgType); - return TSDB_CODE_VND_APP_ERROR; - } -} - -int32_t qndProcessFetchMsg(SQnode *pQnode, SRpcMsg *pMsg) { - qTrace("message in fetch queue is processing"); - switch (pMsg->msgType) { + code = qWorkerProcessCQueryMsg(&handle, pQnode->pQuery, pMsg); + break; case TDMT_VND_FETCH: - return qWorkerProcessFetchMsg(pQnode, pQnode->pQuery, pMsg); + code = qWorkerProcessFetchMsg(pQnode, pQnode->pQuery, pMsg); + break; case TDMT_VND_FETCH_RSP: - return qWorkerProcessFetchRsp(pQnode, pQnode->pQuery, pMsg); + code = qWorkerProcessFetchRsp(pQnode, pQnode->pQuery, pMsg); + break; case TDMT_VND_RES_READY: - return qWorkerProcessReadyMsg(pQnode, pQnode->pQuery, pMsg); + code = qWorkerProcessReadyMsg(pQnode, pQnode->pQuery, pMsg); + break; case TDMT_VND_TASKS_STATUS: - return qWorkerProcessStatusMsg(pQnode, pQnode->pQuery, pMsg); + code = qWorkerProcessStatusMsg(pQnode, pQnode->pQuery, pMsg); + break; case TDMT_VND_CANCEL_TASK: - return qWorkerProcessCancelMsg(pQnode, pQnode->pQuery, pMsg); + code = qWorkerProcessCancelMsg(pQnode, pQnode->pQuery, pMsg); + break; case TDMT_VND_DROP_TASK: - return qWorkerProcessDropMsg(pQnode, pQnode->pQuery, pMsg); + code = qWorkerProcessDropMsg(pQnode, pQnode->pQuery, pMsg); + break; case TDMT_VND_TABLE_META: - // return vnodeGetTableMeta(pQnode, pMsg); + // code = vnodeGetTableMeta(pQnode, pMsg); + // break; case TDMT_VND_CONSUME: - // return tqProcessConsumeReq(pQnode->pTq, pMsg); + // code = tqProcessConsumeReq(pQnode->pTq, pMsg); + // break; case TDMT_VND_QUERY_HEARTBEAT: - return qWorkerProcessHbMsg(pQnode, pQnode->pQuery, pMsg); + code = qWorkerProcessHbMsg(pQnode, pQnode->pQuery, pMsg); + break; default: - qError("unknown msg type:%d in fetch queue", pMsg->msgType); - return TSDB_CODE_VND_APP_ERROR; + qError("unknown msg type:%d in qnode queue", pMsg->msgType); + terrno = TSDB_CODE_VND_APP_ERROR; } + + if (code == 0) return TSDB_CODE_ACTION_IN_PROGRESS; + return code; } diff --git a/source/dnode/snode/src/snode.c b/source/dnode/snode/src/snode.c index 7d7c01a870a3b2a07e1fe27ffb56bbc079dfb73b..ec75ffcae1b2b00bf2881b1fbf6e32c5d4f8a481 100644 --- a/source/dnode/snode/src/snode.c +++ b/source/dnode/snode/src/snode.c @@ -57,9 +57,7 @@ void sndMetaDelete(SStreamMeta *pMeta) { } int32_t sndMetaDeployTask(SStreamMeta *pMeta, SStreamTask *pTask) { - for (int i = 0; i < pTask->exec.numOfRunners; i++) { - pTask->exec.runners[i].executor = qCreateStreamExecTaskInfo(pTask->exec.qmsg, NULL); - } + pTask->exec.executor = qCreateStreamExecTaskInfo(pTask->exec.qmsg, NULL); return taosHashPut(pMeta->pHash, &pTask->taskId, sizeof(int32_t), pTask, sizeof(void *)); } diff --git a/source/dnode/vnode/CMakeLists.txt b/source/dnode/vnode/CMakeLists.txt index a8e3860ed14035d5bedd5e1b096c45e1f03ec6e3..4141485d28baf839a222676b4e9eb50286156280 100644 --- a/source/dnode/vnode/CMakeLists.txt +++ b/source/dnode/vnode/CMakeLists.txt @@ -48,7 +48,6 @@ target_sources( # tq "src/tq/tq.c" "src/tq/tqCommit.c" - "src/tq/tqMetaStore.c" "src/tq/tqOffset.c" "src/tq/tqPush.c" "src/tq/tqRead.c" @@ -76,9 +75,14 @@ target_link_libraries( #PUBLIC scalar PUBLIC transport PUBLIC stream + PUBLIC index ) target_compile_definitions(vnode PUBLIC -DMETA_REFACT) - +if (${BUILD_WITH_INVERTEDINDEX}) + add_definitions(-DUSE_INVERTED_INDEX) +endif(${BUILD_WITH_INVERTEDINDEX}) if(${BUILD_TEST}) add_subdirectory(test) endif(${BUILD_TEST}) + + diff --git a/source/dnode/vnode/inc/vnode.h b/source/dnode/vnode/inc/vnode.h index b48a8775ce7924976ab3863d82c1907a404b8354..9e33973c05de18139b642d1af2e854f2f6dc712c 100644 --- a/source/dnode/vnode/inc/vnode.h +++ b/source/dnode/vnode/inc/vnode.h @@ -45,20 +45,20 @@ typedef struct SVnodeCfg SVnodeCfg; extern const SVnodeCfg vnodeCfgDefault; -int vnodeInit(int nthreads); +int32_t vnodeInit(int32_t nthreads); void vnodeCleanup(); -int vnodeCreate(const char *path, SVnodeCfg *pCfg, STfs *pTfs); +int32_t vnodeCreate(const char *path, SVnodeCfg *pCfg, STfs *pTfs); void vnodeDestroy(const char *path, STfs *pTfs); SVnode *vnodeOpen(const char *path, STfs *pTfs, SMsgCb msgCb); void vnodeClose(SVnode *pVnode); -int vnodePreprocessWriteReqs(SVnode *pVnode, SArray *pMsgs, int64_t *version); -int vnodeProcessWriteReq(SVnode *pVnode, SRpcMsg *pMsg, int64_t version, SRpcMsg *pRsp); -int vnodeProcessCMsg(SVnode *pVnode, SRpcMsg *pMsg, SRpcMsg **pRsp); -int vnodeProcessSyncReq(SVnode *pVnode, SRpcMsg *pMsg, SRpcMsg **pRsp); -int vnodeProcessQueryMsg(SVnode *pVnode, SRpcMsg *pMsg); -int vnodeProcessFetchMsg(SVnode *pVnode, SRpcMsg *pMsg, SQueueInfo *pInfo); +int32_t vnodePreprocessReq(SVnode *pVnode, SRpcMsg *pMsg); +int32_t vnodeProcessWriteReq(SVnode *pVnode, SRpcMsg *pMsg, int64_t version, SRpcMsg *pRsp); +int32_t vnodeProcessCMsg(SVnode *pVnode, SRpcMsg *pMsg, SRpcMsg **pRsp); +int32_t vnodeProcessSyncReq(SVnode *pVnode, SRpcMsg *pMsg, SRpcMsg **pRsp); +int32_t vnodeProcessQueryMsg(SVnode *pVnode, SRpcMsg *pMsg); +int32_t vnodeProcessFetchMsg(SVnode *pVnode, SRpcMsg *pMsg, SQueueInfo *pInfo); int32_t vnodeGetLoad(SVnode *pVnode, SVnodeLoad *pLoad); -int vnodeValidateTableHash(SVnode *pVnode, char *tableFName); +int32_t vnodeValidateTableHash(SVnode *pVnode, char *tableFName); int32_t vnodeStart(SVnode *pVnode); void vnodeStop(SVnode *pVnode); @@ -74,8 +74,8 @@ typedef struct SMetaEntry SMetaEntry; void metaReaderInit(SMetaReader *pReader, SMeta *pMeta, int32_t flags); void metaReaderClear(SMetaReader *pReader); -int metaGetTableEntryByUid(SMetaReader *pReader, tb_uid_t uid); -int metaReadNext(SMetaReader *pReader); +int32_t metaGetTableEntryByUid(SMetaReader *pReader, tb_uid_t uid); +int32_t metaReadNext(SMetaReader *pReader); const void *metaGetTableTagVal(SMetaEntry *pEntry, int16_t cid); #if 1 // refact APIs below (TODO) @@ -86,7 +86,7 @@ typedef struct SMTbCursor SMTbCursor; SMTbCursor *metaOpenTbCursor(SMeta *pMeta); void metaCloseTbCursor(SMTbCursor *pTbCur); -int metaTbCursorNext(SMTbCursor *pTbCur); +int32_t metaTbCursorNext(SMTbCursor *pTbCur); #endif // tsdb @@ -124,8 +124,10 @@ typedef struct STqReadHandle STqReadHandle; STqReadHandle *tqInitSubmitMsgScanner(SMeta *pMeta); void tqReadHandleSetColIdList(STqReadHandle *pReadHandle, SArray *pColIdList); -int tqReadHandleSetTbUidList(STqReadHandle *pHandle, const SArray *tbUidList); -int tqReadHandleAddTbUidList(STqReadHandle *pHandle, const SArray *tbUidList); +int32_t tqReadHandleSetTbUidList(STqReadHandle *pHandle, const SArray *tbUidList); +int32_t tqReadHandleAddTbUidList(STqReadHandle *pHandle, const SArray *tbUidList); +int32_t tqReadHandleRemoveTbUidList(STqReadHandle *pHandle, const SArray *tbUidList); + int32_t tqReadHandleSetMsg(STqReadHandle *pHandle, SSubmitReq *pMsg, int64_t ver); bool tqNextDataBlock(STqReadHandle *pHandle); bool tqNextDataBlockFilterOut(STqReadHandle *pHandle, SHashObj *filterOutUids); @@ -172,20 +174,20 @@ typedef struct { } STableKeyInfo; struct SMetaEntry { - int64_t version; - int8_t type; - tb_uid_t uid; - const char *name; + int64_t version; + int8_t type; + tb_uid_t uid; + char *name; union { struct { SSchemaWrapper schema; SSchemaWrapper schemaTag; } stbEntry; struct { - int64_t ctime; - int32_t ttlDays; - tb_uid_t suid; - const uint8_t *pTags; + int64_t ctime; + int32_t ttlDays; + tb_uid_t suid; + uint8_t *pTags; } ctbEntry; struct { int64_t ctime; @@ -207,15 +209,15 @@ struct SMetaReader { SDecoder coder; SMetaEntry me; void *pBuf; - int szBuf; + int32_t szBuf; }; struct SMTbCursor { TBC *pDbc; void *pKey; void *pVal; - int kLen; - int vLen; + int32_t kLen; + int32_t vLen; SMetaReader mr; }; diff --git a/source/dnode/vnode/src/inc/meta.h b/source/dnode/vnode/src/inc/meta.h index 693f4a0a2b4e0223f956990c59316b696b8a1c9a..3340bbb91ce0f8ed29b2ef48fc325472676b56e1 100644 --- a/source/dnode/vnode/src/inc/meta.h +++ b/source/dnode/vnode/src/inc/meta.h @@ -17,6 +17,7 @@ #define _TD_VNODE_META_H_ #include "vnodeInt.h" +#include "index.h" #ifdef __cplusplus extern "C" { @@ -61,16 +62,20 @@ static FORCE_INLINE tb_uid_t metaGenerateUid(SMeta* pMeta) { return tGenIdPI64() struct SMeta { TdThreadRwlock lock; - char* path; - SVnode* pVnode; - TDB* pEnv; - TXN txn; - TTB* pTbDb; - TTB* pSkmDb; - TTB* pUidIdx; - TTB* pNameIdx; - TTB* pCtbIdx; - TTB* pTagIdx; + char* path; + SVnode* pVnode; + TDB* pEnv; + TXN txn; + TTB* pTbDb; + TTB* pSkmDb; + TTB* pUidIdx; + TTB* pNameIdx; + TTB* pCtbIdx; +#ifdef USE_INVERTED_INDEX + void* pTagIvtIdx; +#else + TTB* pTagIdx; +#endif TTB* pTtlIdx; TTB* pSmaIdx; SMetaIdx* pIdx; diff --git a/source/dnode/vnode/src/inc/tq.h b/source/dnode/vnode/src/inc/tq.h index a8a3e4f601ed40bc3d7b2e618da08d1145c84a53..ad3f8cc869fe61bc2a265d630863b2bd481c3de7 100644 --- a/source/dnode/vnode/src/inc/tq.h +++ b/source/dnode/vnode/src/inc/tq.h @@ -20,9 +20,9 @@ #include "executor.h" #include "os.h" -#include "tcache.h" #include "thash.h" #include "tmsg.h" +#include "tqueue.h" #include "trpc.h" #include "ttimer.h" #include "wal.h" @@ -41,45 +41,6 @@ extern "C" { #define tqTrace(...) do { if (tqDebugFlag & DEBUG_TRACE) { taosPrintLog("TQ ", DEBUG_TRACE, tqDebugFlag, __VA_ARGS__); }} while(0) // clang-format on -#define TQ_BUFFER_SIZE 4 - -#define TQ_BUCKET_MASK 0xFF -#define TQ_BUCKET_SIZE 256 - -#define TQ_PAGE_SIZE 4096 -// key + offset + size -#define TQ_IDX_SIZE 24 -// 4096 / 24 -#define TQ_MAX_IDX_ONE_PAGE 170 -// 24 * 170 -#define TQ_IDX_PAGE_BODY_SIZE 4080 -// 4096 - 4080 -#define TQ_IDX_PAGE_HEAD_SIZE 16 - -#define TQ_ACTION_CONST 0 -#define TQ_ACTION_INUSE 1 -#define TQ_ACTION_INUSE_CONT 2 -#define TQ_ACTION_INTXN 3 - -#define TQ_SVER 0 - -// TODO: inplace mode is not implemented -#define TQ_UPDATE_INPLACE 0 -#define TQ_UPDATE_APPEND 1 - -#define TQ_DUP_INTXN_REWRITE 0 -#define TQ_DUP_INTXN_REJECT 2 - -static inline bool tqUpdateAppend(int32_t tqConfigFlag) { return tqConfigFlag & TQ_UPDATE_APPEND; } - -static inline bool tqDupIntxnReject(int32_t tqConfigFlag) { return tqConfigFlag & TQ_DUP_INTXN_REJECT; } - -static const int8_t TQ_CONST_DELETE = TQ_ACTION_CONST; - -#define TQ_DELETE_TOKEN (void*)&TQ_CONST_DELETE - -typedef enum { TQ_ITEM_READY, TQ_ITEM_PROCESS, TQ_ITEM_EMPTY } STqItemStatus; - typedef struct STqOffsetCfg STqOffsetCfg; typedef struct STqOffsetStore STqOffsetStore; @@ -98,53 +59,6 @@ struct STqReadHandle { STSchema* pSchema; }; -typedef struct { - int16_t ver; - int16_t action; - int32_t checksum; - int64_t ssize; - char content[]; -} STqSerializedHead; - -typedef int32_t (*FTqSerialize)(const void* pObj, STqSerializedHead** ppHead); -typedef int32_t (*FTqDeserialize)(void* self, const STqSerializedHead* pHead, void** ppObj); -typedef void (*FTqDelete)(void*); - -typedef struct { - int64_t key; - int64_t offset; - int64_t serializedSize; - void* valueInUse; - void* valueInTxn; -} STqMetaHandle; - -typedef struct STqMetaList { - STqMetaHandle handle; - struct STqMetaList* next; - // struct STqMetaList* inTxnPrev; - // struct STqMetaList* inTxnNext; - struct STqMetaList* unpersistPrev; - struct STqMetaList* unpersistNext; -} STqMetaList; - -typedef struct { - STQ* pTq; - STqMetaList* bucket[TQ_BUCKET_SIZE]; - // a table head - STqMetaList* unpersistHead; - // topics that are not connectted - STqMetaList* unconnectTopic; - - TdFilePtr pFile; - TdFilePtr pIdxFile; - - char* dirPath; - int32_t tqConfigFlag; - FTqSerialize pSerializer; - FTqDeserialize pDeserializer; - FTqDelete pDeleter; -} STqMetaStore; - typedef struct { int64_t consumerId; int32_t epoch; @@ -172,15 +86,17 @@ typedef struct { qTaskInfo_t task[5]; } STqExec; +int32_t tEncodeSTqExec(SEncoder* pEncoder, const STqExec* pExec); +int32_t tDecodeSTqExec(SDecoder* pDecoder, STqExec* pExec); + struct STQ { - char* path; - // STqMetaStore* tqMeta; + char* path; SHashObj* pushMgr; // consumerId -> STqExec* SHashObj* execs; // subKey -> STqExec SHashObj* pStreamTasks; SVnode* pVnode; SWal* pWal; - // TDB* pTdb; + TDB* pTdb; }; typedef struct { @@ -188,89 +104,12 @@ typedef struct { tmr_h timer; } STqMgmt; -static STqMgmt tqMgmt; - -typedef struct { - int8_t status; - int64_t offset; - qTaskInfo_t task; - STqReadHandle* pReadHandle; -} STqTaskItem; - -// new version -typedef struct { - int64_t firstOffset; - int64_t lastOffset; - STqTaskItem output[TQ_BUFFER_SIZE]; -} STqBuffer; - -typedef struct { - char topicName[TSDB_TOPIC_FNAME_LEN]; - char* sql; - char* logicalPlan; - char* physicalPlan; - char* qmsg; - STqBuffer buffer; - SWalReadHandle* pReadhandle; -} STqTopic; - -typedef struct { - int64_t consumerId; - int32_t epoch; - char cgroup[TSDB_TOPIC_FNAME_LEN]; - SArray* topics; // SArray -} STqConsumer; - -typedef struct { - int8_t type; - int8_t nodeType; - int8_t reserved[6]; - int64_t streamId; - qTaskInfo_t task; - // TODO sync function -} STqStreamPusher; - -typedef struct { - int8_t inited; - tmr_h timer; -} STqPushMgmt; - -static STqPushMgmt tqPushMgmt; +static STqMgmt tqMgmt = {0}; // init once int tqInit(); void tqCleanUp(); -// open in each vnode -// required by vnode - -int32_t tqSerializeConsumer(const STqConsumer*, STqSerializedHead**); -int32_t tqDeserializeConsumer(STQ*, const STqSerializedHead*, STqConsumer**); - -static int FORCE_INLINE tqQueryExecuting(int32_t status) { return status; } - -// tqMetaStore.h -STqMetaStore* tqStoreOpen(STQ* pTq, const char* path, FTqSerialize pSerializer, FTqDeserialize pDeserializer, - FTqDelete pDeleter, int32_t tqConfigFlag); -int32_t tqStoreClose(STqMetaStore*); -// int32_t tqStoreDelete(TqMetaStore*); -// int32_t tqStoreCommitAll(TqMetaStore*); -int32_t tqStorePersist(STqMetaStore*); -// clean deleted idx and data from persistent file -int32_t tqStoreCompact(STqMetaStore*); - -void* tqHandleGet(STqMetaStore*, int64_t key); -// make it unpersist -void* tqHandleTouchGet(STqMetaStore*, int64_t key); -int32_t tqHandleMovePut(STqMetaStore*, int64_t key, void* value); -int32_t tqHandleCopyPut(STqMetaStore*, int64_t key, void* value, size_t vsize); -// delete committed kv pair -// notice that a delete action still needs to be committed -int32_t tqHandleDel(STqMetaStore*, int64_t key); -int32_t tqHandlePurge(STqMetaStore*, int64_t key); -int32_t tqHandleCommit(STqMetaStore*, int64_t key); -int32_t tqHandleAbort(STqMetaStore*, int64_t key); - // tqOffset STqOffsetStore* STqOffsetOpen(STqOffsetCfg*); void STqOffsetClose(STqOffsetStore*); diff --git a/source/dnode/vnode/src/inc/vnd.h b/source/dnode/vnode/src/inc/vnd.h index 3f5435ee47c43c9170042d65ba077fa14e7edd4a..eb3382ac4cd46a602a214b09b5a8debeaf15087f 100644 --- a/source/dnode/vnode/src/inc/vnd.h +++ b/source/dnode/vnode/src/inc/vnd.h @@ -24,7 +24,6 @@ extern "C" { #endif -// vnodeDebug ==================== // clang-format off #define vFatal(...) do { if (vDebugFlag & DEBUG_FATAL) { taosPrintLog("VND FATAL ", DEBUG_FATAL, 255, __VA_ARGS__); }} while(0) #define vError(...) do { if (vDebugFlag & DEBUG_ERROR) { taosPrintLog("VND ERROR ", DEBUG_ERROR, 255, __VA_ARGS__); }} while(0) @@ -34,17 +33,17 @@ extern "C" { #define vTrace(...) do { if (vDebugFlag & DEBUG_TRACE) { taosPrintLog("VND ", DEBUG_TRACE, vDebugFlag, __VA_ARGS__); }} while(0) // clang-format on -// vnodeCfg ==================== +// vnodeCfg.c extern const SVnodeCfg vnodeCfgDefault; -int vnodeCheckCfg(const SVnodeCfg*); -int vnodeEncodeConfig(const void* pObj, SJson* pJson); -int vnodeDecodeConfig(const SJson* pJson, void* pObj); +int32_t vnodeCheckCfg(const SVnodeCfg*); +int32_t vnodeEncodeConfig(const void* pObj, SJson* pJson); +int32_t vnodeDecodeConfig(const SJson* pJson, void* pObj); -// vnodeModule ==================== -int vnodeScheduleTask(int (*execute)(void*), void* arg); +// vnodeModule.c +int32_t vnodeScheduleTask(int32_t (*execute)(void*), void* arg); -// vnodeBufPool ==================== +// vnodeBufPool.c typedef struct SVBufPoolNode SVBufPoolNode; struct SVBufPoolNode { SVBufPoolNode* prev; @@ -62,38 +61,29 @@ struct SVBufPool { SVBufPoolNode node; }; -int vnodeOpenBufPool(SVnode* pVnode, int64_t size); -int vnodeCloseBufPool(SVnode* pVnode); -void vnodeBufPoolReset(SVBufPool* pPool); +int32_t vnodeOpenBufPool(SVnode* pVnode, int64_t size); +int32_t vnodeCloseBufPool(SVnode* pVnode); +void vnodeBufPoolReset(SVBufPool* pPool); -// vnodeQuery ==================== -int vnodeQueryOpen(SVnode* pVnode); -void vnodeQueryClose(SVnode* pVnode); -int vnodeGetTableMeta(SVnode* pVnode, SRpcMsg* pMsg); +// vnodeQuery.c +int32_t vnodeQueryOpen(SVnode* pVnode); +void vnodeQueryClose(SVnode* pVnode); +int32_t vnodeGetTableMeta(SVnode* pVnode, SRpcMsg* pMsg); -// vnodeCommit ==================== -int vnodeBegin(SVnode* pVnode); -int vnodeShouldCommit(SVnode* pVnode); -int vnodeCommit(SVnode* pVnode); -int vnodeSaveInfo(const char* dir, const SVnodeInfo* pCfg); -int vnodeCommitInfo(const char* dir, const SVnodeInfo* pInfo); -int vnodeLoadInfo(const char* dir, SVnodeInfo* pInfo); -int vnodeSyncCommit(SVnode* pVnode); -int vnodeAsyncCommit(SVnode* pVnode); +// vnodeCommit.c +int32_t vnodeBegin(SVnode* pVnode); +int32_t vnodeShouldCommit(SVnode* pVnode); +int32_t vnodeCommit(SVnode* pVnode); +int32_t vnodeSaveInfo(const char* dir, const SVnodeInfo* pCfg); +int32_t vnodeCommitInfo(const char* dir, const SVnodeInfo* pInfo); +int32_t vnodeLoadInfo(const char* dir, SVnodeInfo* pInfo); +int32_t vnodeSyncCommit(SVnode* pVnode); +int32_t vnodeAsyncCommit(SVnode* pVnode); -// vnodeCommit ==================== +// vnodeSync.c int32_t vnodeSyncOpen(SVnode* pVnode, char* path); -int32_t vnodeSyncStart(SVnode* pVnode); +void vnodeSyncStart(SVnode* pVnode); void vnodeSyncClose(SVnode* pVnode); -void vnodeSyncSetQ(SVnode* pVnode, void* qHandle); -void vnodeSyncSetRpc(SVnode* pVnode, void* rpcHandle); -int32_t vnodeSyncEqMsg(void* qHandle, SRpcMsg* pMsg); -int32_t vnodeSendMsg(void* rpcHandle, const SEpSet* pEpSet, SRpcMsg* pMsg); -void vnodeSyncCommitCb(struct SSyncFSM* pFsm, const SRpcMsg* pMsg, SFsmCbMeta cbMeta); -void vnodeSyncPreCommitCb(struct SSyncFSM* pFsm, const SRpcMsg* pMsg, SFsmCbMeta cbMeta); -void vnodeSyncRollBackCb(struct SSyncFSM* pFsm, const SRpcMsg* pMsg, SFsmCbMeta cbMeta); -int32_t vnodeSyncGetSnapshotCb(struct SSyncFSM* pFsm, SSnapshot* pSnapshot); -SSyncFSM* syncVnodeMakeFsm(); #ifdef __cplusplus } diff --git a/source/dnode/vnode/src/inc/vnodeInt.h b/source/dnode/vnode/src/inc/vnodeInt.h index 1c896493137d6246a248ebd7702b95e34dca8dec..24b3f458b1962089e05d8eadcf2b23b177956109 100644 --- a/source/dnode/vnode/src/inc/vnodeInt.h +++ b/source/dnode/vnode/src/inc/vnodeInt.h @@ -104,7 +104,7 @@ int tsdbOpen(SVnode* pVnode, STsdb** ppTsdb, const char* dir, STsdbKeep int tsdbClose(STsdb** pTsdb); int tsdbBegin(STsdb* pTsdb); int tsdbCommit(STsdb* pTsdb); -int tsdbScanAndConvertSubmitMsg(STsdb* pTsdb, const SSubmitReq* pMsg); +int tsdbScanAndConvertSubmitMsg(STsdb* pTsdb, SSubmitReq* pMsg); int tsdbInsertData(STsdb* pTsdb, int64_t version, SSubmitReq* pMsg, SSubmitRsp* pRsp); int tsdbInsertTableData(STsdb* pTsdb, SSubmitMsgIter* pMsgIter, SSubmitBlk* pBlock, SSubmitBlkRsp* pRsp); tsdbReaderT* tsdbQueryTables(SVnode* pVnode, SQueryTableDataCond* pCond, STableGroupInfo* groupList, uint64_t qId, @@ -121,10 +121,14 @@ int tqCommit(STQ*); int32_t tqUpdateTbUidList(STQ* pTq, const SArray* tbUidList, bool isAdd); int32_t tqProcessVgChangeReq(STQ* pTq, char* msg, int32_t msgLen); int32_t tqProcessVgDeleteReq(STQ* pTq, char* msg, int32_t msgLen); -int32_t tqProcessTaskExec(STQ* pTq, char* msg, int32_t msgLen, int32_t workerId); -int32_t tqProcessTaskDeploy(STQ* pTq, char* msg, int32_t msgLen); -int32_t tqProcessStreamTrigger(STQ* pTq, void* data, int32_t dataLen, int32_t workerId); int32_t tqProcessPollReq(STQ* pTq, SRpcMsg* pMsg, int32_t workerId); +int32_t tqProcessTaskDeploy(STQ* pTq, char* msg, int32_t msgLen); +int32_t tqProcessStreamTrigger(STQ* pTq, SSubmitReq* data); +int32_t tqProcessTaskRunReq(STQ* pTq, SRpcMsg* pMsg); +int32_t tqProcessTaskDispatchReq(STQ* pTq, SRpcMsg* pMsg); +int32_t tqProcessTaskRecoverReq(STQ* pTq, SRpcMsg* pMsg); +int32_t tqProcessTaskDispatchRsp(STQ* pTq, SRpcMsg* pMsg); +int32_t tqProcessTaskRecoverRsp(STQ* pTq, SRpcMsg* pMsg); // sma int32_t smaOpen(SVnode* pVnode); diff --git a/source/dnode/vnode/src/meta/metaEntry.c b/source/dnode/vnode/src/meta/metaEntry.c index ae915b26f96efc2c20950c8a5ef0e496f18cc26b..8a4db3100d31695af2889367088c9a0e16bb6236 100644 --- a/source/dnode/vnode/src/meta/metaEntry.c +++ b/source/dnode/vnode/src/meta/metaEntry.c @@ -70,7 +70,7 @@ int metaDecodeEntry(SDecoder *pCoder, SMetaEntry *pME) { if (tDecodeSSchemaWrapperEx(pCoder, &pME->ntbEntry.schema) < 0) return -1; } else if (pME->type == TSDB_TSMA_TABLE) { pME->smaEntry.tsma = tDecoderMalloc(pCoder, sizeof(STSma)); - if(!pME->smaEntry.tsma) { + if (!pME->smaEntry.tsma) { terrno = TSDB_CODE_OUT_OF_MEMORY; return -1; } diff --git a/source/dnode/vnode/src/meta/metaIdx.c b/source/dnode/vnode/src/meta/metaIdx.c index 3f52071315bf15ca15beb2a14105c757d1b2eb25..efa06d2d1fea29995522e95b49789c6df3c2c435 100644 --- a/source/dnode/vnode/src/meta/metaIdx.c +++ b/source/dnode/vnode/src/meta/metaIdx.c @@ -53,10 +53,10 @@ int metaOpenIdx(SMeta *pMeta) { #endif #ifdef USE_INVERTED_INDEX - SIndexOpts opts; - if (indexOpen(&opts, pMeta->path, &pMeta->pIdx->pIdx) != 0) { - return -1; - } + // SIndexOpts opts; + // if (indexOpen(&opts, pMeta->path, &pMeta->pIdx->pIdx) != 0) { + // return -1; + //} #endif return 0; @@ -71,36 +71,37 @@ void metaCloseIdx(SMeta *pMeta) { /* TODO */ #endif #ifdef USE_INVERTED_INDEX - SIndexOpts opts; - if (indexClose(pMeta->pIdx->pIdx) != 0) { - return -1; - } + // SIndexOpts opts; + // if (indexClose(pMeta->pIdx->pIdx) != 0) { + // return -1; + //} + // return 0; #endif } int metaSaveTableToIdx(SMeta *pMeta, const STbCfg *pTbCfg) { #ifdef USE_INVERTED_INDEX - if (pTbCfgs->type == META_CHILD_TABLE) { - char buf[8] = {0}; - int16_t colId = (kvRowColIdx(pTbCfg->ctbCfg.pTag))[0].colId; - sprintf(buf, "%d", colId); // colname - - char *pTagVal = (char *)tdGetKVRowValOfCol(pTbCfg->ctbCfg.pTag, (kvRowColIdx(pTbCfg->ctbCfg.pTag))[0].colId); - - tb_uid_t suid = pTbCfg->ctbCfg.suid; // super id - tb_uid_t tuid = 0; // child table uid - SIndexMultiTerm *terms = indexMultiTermCreate(); - SIndexTerm *term = - indexTermCreate(suid, ADD_VALUE, TSDB_DATA_TYPE_BINARY, buf, strlen(buf), pTagVal, strlen(pTagVal), tuid); - indexMultiTermAdd(terms, term); - - int ret = indexPut(pMeta->pIdx->pIdx, terms); - indexMultiTermDestroy(terms); - return ret; - } else { - return DB_DONOTINDEX; - } + // if (pTbCfgs->type == META_CHILD_TABLE) { + // char buf[8] = {0}; + // int16_t colId = (kvRowColIdx(pTbCfg->ctbCfg.pTag))[0].colId; + // sprintf(buf, "%d", colId); // colname + + // char *pTagVal = (char *)tdGetKVRowValOfCol(pTbCfg->ctbCfg.pTag, (kvRowColIdx(pTbCfg->ctbCfg.pTag))[0].colId); + + // tb_uid_t suid = pTbCfg->ctbCfg.suid; // super id + // tb_uid_t tuid = 0; // child table uid + // SIndexMultiTerm *terms = indexMultiTermCreate(); + // SIndexTerm *term = + // indexTermCreate(suid, ADD_VALUE, TSDB_DATA_TYPE_BINARY, buf, strlen(buf), pTagVal, strlen(pTagVal), tuid); + // indexMultiTermAdd(terms, term); + + // int ret = indexPut(pMeta->pIdx->pIdx, terms); + // indexMultiTermDestroy(terms); + // return ret; + //} else { + // return DB_DONOTINDEX; + //} #endif // TODO return 0; @@ -112,4 +113,4 @@ int metaRemoveTableFromIdx(SMeta *pMeta, tb_uid_t uid) { #endif // TODO return 0; -} \ No newline at end of file +} diff --git a/source/dnode/vnode/src/meta/metaOpen.c b/source/dnode/vnode/src/meta/metaOpen.c index 9a97357b97ac8e637d7a8cf72139b6615e7fdb05..f23e7f88056d6a397a5979bda11dd4f080ba0212 100644 --- a/source/dnode/vnode/src/meta/metaOpen.c +++ b/source/dnode/vnode/src/meta/metaOpen.c @@ -93,11 +93,24 @@ int metaOpen(SVnode *pVnode, SMeta **ppMeta) { } // open pTagIdx +#ifdef USE_INVERTED_INDEX + // TODO(yihaoDeng), refactor later + char indexFullPath[128] = {0}; + sprintf(indexFullPath, "%s/%s", pMeta->path, "invert"); + taosMkDir(indexFullPath); + ret = indexOpen(indexOptsCreate(), indexFullPath, (SIndex **)&pMeta->pTagIvtIdx); + if (ret < 0) { + metaError("vgId:%d failed to open meta tag index since %s", TD_VID(pVnode), tstrerror(terrno)); + goto _err; + } + +#else ret = tdbTbOpen("tag.idx", -1, 0, tagIdxKeyCmpr, pMeta->pEnv, &pMeta->pTagIdx); if (ret < 0) { metaError("vgId:%d failed to open meta tag index since %s", TD_VID(pVnode), tstrerror(terrno)); goto _err; } +#endif // open pTtlIdx ret = tdbTbOpen("ttl.idx", sizeof(STtlIdxKey), 0, ttlIdxKeyCmpr, pMeta->pEnv, &pMeta->pTtlIdx); @@ -128,7 +141,11 @@ _err: if (pMeta->pIdx) metaCloseIdx(pMeta); if (pMeta->pSmaIdx) tdbTbClose(pMeta->pSmaIdx); if (pMeta->pTtlIdx) tdbTbClose(pMeta->pTtlIdx); +#ifdef USE_INVERTED_INDEX + if (pMeta->pTagIvtIdx) indexClose(pMeta->pTagIvtIdx); +#else if (pMeta->pTagIdx) tdbTbClose(pMeta->pTagIdx); +#endif if (pMeta->pCtbIdx) tdbTbClose(pMeta->pCtbIdx); if (pMeta->pNameIdx) tdbTbClose(pMeta->pNameIdx); if (pMeta->pUidIdx) tdbTbClose(pMeta->pUidIdx); @@ -145,7 +162,11 @@ int metaClose(SMeta *pMeta) { if (pMeta->pIdx) metaCloseIdx(pMeta); if (pMeta->pSmaIdx) tdbTbClose(pMeta->pSmaIdx); if (pMeta->pTtlIdx) tdbTbClose(pMeta->pTtlIdx); +#ifdef USE_INVERTED_INDEX + if (pMeta->pTagIvtIdx) indexClose(pMeta->pTagIvtIdx); +#else if (pMeta->pTagIdx) tdbTbClose(pMeta->pTagIdx); +#endif if (pMeta->pCtbIdx) tdbTbClose(pMeta->pCtbIdx); if (pMeta->pNameIdx) tdbTbClose(pMeta->pNameIdx); if (pMeta->pUidIdx) tdbTbClose(pMeta->pUidIdx); diff --git a/source/dnode/vnode/src/meta/metaQuery.c b/source/dnode/vnode/src/meta/metaQuery.c index 2bcb68c82a4dc9b4eabbe05e84d754fec860ea72..c19190e68a6bd54a106a9de1278d8870989864dc 100644 --- a/source/dnode/vnode/src/meta/metaQuery.c +++ b/source/dnode/vnode/src/meta/metaQuery.c @@ -278,12 +278,13 @@ STSchema *metaGetTbTSchema(SMeta *pMeta, tb_uid_t uid, int32_t sver) { pSW = metaGetTableSchema(pMeta, quid, sver, 0); if (!pSW) return NULL; - tdInitTSchemaBuilder(&sb, 0); + tdInitTSchemaBuilder(&sb, sver); for (int i = 0; i < pSW->nCols; i++) { pSchema = pSW->pSchema + i; tdAddColToSchema(&sb, pSchema->type, pSchema->flags, pSchema->colId, pSchema->bytes); } pTSchema = tdGetSchemaFromBuilder(&sb); + tdDestroyTSchemaBuilder(&sb); taosMemoryFree(pSW->pSchema); diff --git a/source/dnode/vnode/src/meta/metaTable.c b/source/dnode/vnode/src/meta/metaTable.c index 2bcfd01904dbe32357e8f6afe1e81294ec9de50e..a792343380aa5799d0ae303eb5b37ace65419f04 100644 --- a/source/dnode/vnode/src/meta/metaTable.c +++ b/source/dnode/vnode/src/meta/metaTable.c @@ -165,7 +165,9 @@ int metaAlterSTable(SMeta *pMeta, int64_t version, SVCreateStbReq *pReq) { ret = tdbTbcGet(pTbDbc, NULL, NULL, &pData, &nData); ASSERT(ret == 0); - tDecoderInit(&dc, pData, nData); + oStbEntry.pBuf = taosMemoryMalloc(nData); + memcpy(oStbEntry.pBuf, pData, nData); + tDecoderInit(&dc, oStbEntry.pBuf, nData); metaDecodeEntry(&dc, &oStbEntry); nStbEntry.version = version; @@ -193,6 +195,7 @@ int metaAlterSTable(SMeta *pMeta, int64_t version, SVCreateStbReq *pReq) { // update uid index tdbTbcUpsert(pUidIdxc, &pReq->suid, sizeof(tb_uid_t), &version, sizeof(version), 0); + if (oStbEntry.pBuf) taosMemoryFree(oStbEntry.pBuf); metaULock(pMeta); tDecoderClear(&dc); tdbTbcClose(pTbDbc); @@ -220,9 +223,6 @@ int metaCreateTable(SMeta *pMeta, int64_t version, SVCreateTbReq *pReq) { terrno = TSDB_CODE_TDB_TABLE_ALREADY_EXIST; metaReaderClear(&mr); return -1; - } else { - pReq->uid = tGenIdPI64(); - pReq->ctime = taosGetTimestampMs(); } metaReaderClear(&mr); @@ -420,7 +420,9 @@ static int metaAlterTableColumn(SMeta *pMeta, int64_t version, SVAlterTbReq *pAl // get table entry SDecoder dc = {0}; - tDecoderInit(&dc, pData, nData); + entry.pBuf = taosMemoryMalloc(nData); + memcpy(entry.pBuf, pData, nData); + tDecoderInit(&dc, entry.pBuf, nData); ret = metaDecodeEntry(&dc, &entry); ASSERT(ret == 0); @@ -483,12 +485,12 @@ static int metaAlterTableColumn(SMeta *pMeta, int64_t version, SVAlterTbReq *pAl terrno = TSDB_CODE_VND_TABLE_COL_NOT_EXISTS; goto _err; } - if (!IS_VAR_DATA_TYPE(pColumn->type) || pColumn->bytes <= pAlterTbReq->bytes) { + if (!IS_VAR_DATA_TYPE(pColumn->type) || pColumn->bytes > pAlterTbReq->colModBytes) { terrno = TSDB_CODE_VND_INVALID_TABLE_ACTION; goto _err; } pSchema->sver++; - pColumn->bytes = pAlterTbReq->bytes; + pColumn->bytes = pAlterTbReq->colModBytes; break; case TSDB_ALTER_TABLE_UPDATE_COLUMN_NAME: if (pColumn == NULL) { @@ -562,7 +564,8 @@ static int metaUpdateTableTagVal(SMeta *pMeta, int64_t version, SVAlterTbReq *pA // search table.db TBC *pTbDbc = NULL; - SDecoder dc = {0}; + SDecoder dc1 = {0}; + SDecoder dc2 = {0}; /* get ctbEntry */ tdbTbcOpen(pMeta->pTbDb, &pTbDbc, &pMeta->txn); @@ -572,18 +575,16 @@ static int metaUpdateTableTagVal(SMeta *pMeta, int64_t version, SVAlterTbReq *pA ctbEntry.pBuf = taosMemoryMalloc(nData); memcpy(ctbEntry.pBuf, pData, nData); - tDecoderInit(&dc, ctbEntry.pBuf, nData); - metaDecodeEntry(&dc, &ctbEntry); - tDecoderClear(&dc); + tDecoderInit(&dc1, ctbEntry.pBuf, nData); + metaDecodeEntry(&dc1, &ctbEntry); /* get stbEntry*/ tdbTbGet(pMeta->pUidIdx, &ctbEntry.ctbEntry.suid, sizeof(tb_uid_t), &pVal, &nVal); tdbTbGet(pMeta->pTbDb, &((STbDbKey){.uid = ctbEntry.ctbEntry.suid, .version = *(int64_t *)pVal}), sizeof(STbDbKey), (void **)&stbEntry.pBuf, &nVal); tdbFree(pVal); - tDecoderInit(&dc, stbEntry.pBuf, nVal); - metaDecodeEntry(&dc, &stbEntry); - tDecoderClear(&dc); + tDecoderInit(&dc2, stbEntry.pBuf, nVal); + metaDecodeEntry(&dc2, &stbEntry); SSchemaWrapper *pTagSchema = &stbEntry.stbEntry.schemaTag; SSchema *pColumn = NULL; @@ -606,31 +607,39 @@ static int metaUpdateTableTagVal(SMeta *pMeta, int64_t version, SVAlterTbReq *pA if (iCol == 0) { // TODO : need to update tag index } - ctbEntry.version = version; - SKVRowBuilder kvrb = {0}; - const SKVRow pOldTag = (const SKVRow)ctbEntry.ctbEntry.pTags; - SKVRow pNewTag = NULL; - - tdInitKVRowBuilder(&kvrb); - for (int32_t i = 0; i < pTagSchema->nCols; i++) { - SSchema *pCol = &pTagSchema->pSchema[i]; - if (iCol == i) { - tdAddColToKVRow(&kvrb, pCol->colId, pAlterTbReq->pTagVal, pAlterTbReq->nTagVal); - } else { - void *p = tdGetKVRowValOfCol(pOldTag, pCol->colId); - if (p) { - if (IS_VAR_DATA_TYPE(pCol->type)) { - tdAddColToKVRow(&kvrb, pCol->colId, p, varDataTLen(p)); - } else { - tdAddColToKVRow(&kvrb, pCol->colId, p, pCol->bytes); + if(pTagSchema->nCols == 1 && pTagSchema->pSchema[0].type == TSDB_DATA_TYPE_JSON){ + ctbEntry.ctbEntry.pTags = taosMemoryMalloc(pAlterTbReq->nTagVal); + if(ctbEntry.ctbEntry.pTags == NULL){ + terrno = TSDB_CODE_OUT_OF_MEMORY; + goto _err; + } + memcpy((void*)ctbEntry.ctbEntry.pTags, pAlterTbReq->pTagVal, pAlterTbReq->nTagVal); + }else{ + SKVRowBuilder kvrb = {0}; + const SKVRow pOldTag = (const SKVRow)ctbEntry.ctbEntry.pTags; + SKVRow pNewTag = NULL; + + tdInitKVRowBuilder(&kvrb); + for (int32_t i = 0; i < pTagSchema->nCols; i++) { + SSchema *pCol = &pTagSchema->pSchema[i]; + if (iCol == i) { + tdAddColToKVRow(&kvrb, pCol->colId, pAlterTbReq->pTagVal, pAlterTbReq->nTagVal); + } else { + void *p = tdGetKVRowValOfCol(pOldTag, pCol->colId); + if (p) { + if (IS_VAR_DATA_TYPE(pCol->type)) { + tdAddColToKVRow(&kvrb, pCol->colId, p, varDataTLen(p)); + } else { + tdAddColToKVRow(&kvrb, pCol->colId, p, pCol->bytes); + } } } } - } - ctbEntry.ctbEntry.pTags = tdGetKVRowFromBuilder(&kvrb); - tdDestroyKVRowBuilder(&kvrb); + ctbEntry.ctbEntry.pTags = tdGetKVRowFromBuilder(&kvrb); + tdDestroyKVRowBuilder(&kvrb); + } // save to table.db metaSaveToTbDb(pMeta, &ctbEntry); @@ -638,6 +647,9 @@ static int metaUpdateTableTagVal(SMeta *pMeta, int64_t version, SVAlterTbReq *pA // save to uid.idx tdbTbUpsert(pMeta->pUidIdx, &ctbEntry.uid, sizeof(tb_uid_t), &version, sizeof(version), &pMeta->txn); + tDecoderClear(&dc1); + tDecoderClear(&dc2); + if (ctbEntry.ctbEntry.pTags) taosMemoryFree((void*)ctbEntry.ctbEntry.pTags); if (ctbEntry.pBuf) taosMemoryFree(ctbEntry.pBuf); if (stbEntry.pBuf) tdbFree(stbEntry.pBuf); tdbTbcClose(pTbDbc); @@ -645,6 +657,8 @@ static int metaUpdateTableTagVal(SMeta *pMeta, int64_t version, SVAlterTbReq *pA return 0; _err: + tDecoderClear(&dc1); + tDecoderClear(&dc2); if (ctbEntry.pBuf) taosMemoryFree(ctbEntry.pBuf); if (stbEntry.pBuf) tdbFree(stbEntry.pBuf); tdbTbcClose(pTbDbc); @@ -817,16 +831,27 @@ static int metaUpdateTagIdx(SMeta *pMeta, const SMetaEntry *pCtbEntry) { pTagData = tdGetKVRowValOfCol((const SKVRow)pCtbEntry->ctbEntry.pTags, pTagColumn->colId); // update tag index +#ifdef USE_INVERTED_INDEX + tb_uid_t suid = pCtbEntry->ctbEntry.suid; + tb_uid_t tuid = pCtbEntry->uid; + + SIndexMultiTerm *tmGroup = indexMultiTermCreate(); + + SIndexTerm *tm = indexTermCreate(suid, ADD_VALUE, pTagColumn->type, pTagColumn->name, sizeof(pTagColumn->name), + pTagData, pTagData == NULL ? 0 : strlen(pTagData)); + indexMultiTermAdd(tmGroup, tm); + int ret = indexPut((SIndex *)pMeta->pTagIvtIdx, tmGroup, tuid); + indexMultiTermDestroy(tmGroup); +#else if (metaCreateTagIdxKey(pCtbEntry->ctbEntry.suid, pTagColumn->colId, pTagData, pTagColumn->type, pCtbEntry->uid, &pTagIdxKey, &nTagIdxKey) < 0) { return -1; } tdbTbInsert(pMeta->pTagIdx, pTagIdxKey, nTagIdxKey, NULL, 0, &pMeta->txn); metaDestroyTagIdxKey(pTagIdxKey); - +#endif tDecoderClear(&dc); tdbFree(pData); - return 0; } diff --git a/source/dnode/vnode/src/sma/smaOpen.c b/source/dnode/vnode/src/sma/smaOpen.c index 1c7db28e189d918fb68e81dabb6249ea246e2bea..2a74fe78cbc66a3873857347df010190554e1e76 100644 --- a/source/dnode/vnode/src/sma/smaOpen.c +++ b/source/dnode/vnode/src/sma/smaOpen.c @@ -132,6 +132,7 @@ int32_t smaClose(SSma *pSma) { if SMA_RSMA_TSDB0 (pSma) tsdbClose(&SMA_RSMA_TSDB0(pSma)); if SMA_RSMA_TSDB1 (pSma) tsdbClose(&SMA_RSMA_TSDB1(pSma)); if SMA_RSMA_TSDB2 (pSma) tsdbClose(&SMA_RSMA_TSDB2(pSma)); + taosMemoryFree(pSma); } return 0; } \ No newline at end of file diff --git a/source/dnode/vnode/src/tq/tq.c b/source/dnode/vnode/src/tq/tq.c index 212834f7d7aa88908039412c0889a6d64d9c10d8..bd48ed9b4c3cf0f91bec701e7167964b4473ad07 100644 --- a/source/dnode/vnode/src/tq/tq.c +++ b/source/dnode/vnode/src/tq/tq.c @@ -14,14 +14,37 @@ */ #include "tq.h" -#include "tqueue.h" int32_t tqInit() { - // + int8_t old; + while (1) { + old = atomic_val_compare_exchange_8(&tqMgmt.inited, 0, 2); + if (old != 2) break; + } + + if (old == 0) { + tqMgmt.timer = taosTmrInit(10000, 100, 10000, "TQ"); + if (tqMgmt.timer == NULL) { + atomic_store_8(&tqMgmt.inited, 0); + return -1; + } + atomic_store_8(&tqMgmt.inited, 1); + } return 0; } -void tqCleanUp() {} +void tqCleanUp() { + int8_t old; + while (1) { + old = atomic_val_compare_exchange_8(&tqMgmt.inited, 1, 2); + if (old != 2) break; + } + + if (old == 1) { + taosTmrCleanUp(tqMgmt.timer); + atomic_store_8(&tqMgmt.inited, 0); + } +} STQ* tqOpen(const char* path, SVnode* pVnode, SWal* pWal) { STQ* pTq = taosMemoryMalloc(sizeof(STQ)); @@ -32,18 +55,9 @@ STQ* tqOpen(const char* path, SVnode* pVnode, SWal* pWal) { pTq->path = strdup(path); pTq->pVnode = pVnode; pTq->pWal = pWal; - /*if (tdbOpen(path, 4096, 1, &pTq->pTdb) < 0) {*/ - /*ASSERT(0);*/ - /*}*/ - -#if 0 - pTq->tqMeta = tqStoreOpen(pTq, path, (FTqSerialize)tqSerializeConsumer, (FTqDeserialize)tqDeserializeConsumer, - (FTqDelete)taosMemoryFree, 0); - if (pTq->tqMeta == NULL) { - taosMemoryFree(pTq); - return NULL; + if (tdbOpen(path, 4096, 1, &pTq->pTdb) < 0) { + ASSERT(0); } -#endif pTq->execs = taosHashInit(64, MurmurHash3_32, true, HASH_ENTRY_LOCK); @@ -57,80 +71,73 @@ STQ* tqOpen(const char* path, SVnode* pVnode, SWal* pWal) { void tqClose(STQ* pTq) { if (pTq) { taosMemoryFreeClear(pTq->path); + taosHashCleanup(pTq->execs); + taosHashCleanup(pTq->pStreamTasks); + taosHashCleanup(pTq->pushMgr); + tdbClose(pTq->pTdb); taosMemoryFree(pTq); } // TODO } -static void tdSRowDemo() { -#define DEMO_N_COLS 3 - - int16_t schemaVersion = 0; - int32_t numOfCols = DEMO_N_COLS; // ts + int - SRowBuilder rb = {0}; - - SSchema schema[DEMO_N_COLS] = { - {.type = TSDB_DATA_TYPE_TIMESTAMP, .colId = 1, .name = "ts", .bytes = 8, .flags = COL_SMA_ON}, - {.type = TSDB_DATA_TYPE_INT, .colId = 2, .name = "c1", .bytes = 4, .flags = COL_SMA_ON}, - {.type = TSDB_DATA_TYPE_INT, .colId = 3, .name = "c2", .bytes = 4, .flags = COL_SMA_ON}}; - - SSchema* pSchema = schema; - STSchema* pTSChema = tdGetSTSChemaFromSSChema(&pSchema, numOfCols); - - tdSRowInit(&rb, schemaVersion); - tdSRowSetTpInfo(&rb, numOfCols, pTSChema->flen); - int32_t maxLen = TD_ROW_MAX_BYTES_FROM_SCHEMA(pTSChema); - void* row = taosMemoryCalloc(1, maxLen); // make sure the buffer is enough - - // set row buf - tdSRowResetBuf(&rb, row); - - for (int32_t idx = 0; idx < pTSChema->numOfCols; ++idx) { - STColumn* pColumn = pTSChema->columns + idx; - if (idx == 0) { - int64_t tsKey = 1651234567; - tdAppendColValToRow(&rb, pColumn->colId, pColumn->type, TD_VTYPE_NORM, &tsKey, true, pColumn->offset, idx); - } else if (idx == 1) { - int32_t val1 = 10; - tdAppendColValToRow(&rb, pColumn->colId, pColumn->type, TD_VTYPE_NORM, &val1, true, pColumn->offset, idx); - } else { - tdAppendColValToRow(&rb, pColumn->colId, pColumn->type, TD_VTYPE_NONE, NULL, true, pColumn->offset, idx); - } - } - - // print - tdSRowPrint(row, pTSChema, __func__); - - taosMemoryFree(pTSChema); +int32_t tEncodeSTqExec(SEncoder* pEncoder, const STqExec* pExec) { + if (tStartEncode(pEncoder) < 0) return -1; + if (tEncodeCStr(pEncoder, pExec->subKey) < 0) return -1; + if (tEncodeI64(pEncoder, pExec->consumerId) < 0) return -1; + if (tEncodeI32(pEncoder, pExec->epoch) < 0) return -1; + if (tEncodeI8(pEncoder, pExec->subType) < 0) return -1; + if (tEncodeI8(pEncoder, pExec->withTbName) < 0) return -1; + if (tEncodeI8(pEncoder, pExec->withSchema) < 0) return -1; + if (tEncodeI8(pEncoder, pExec->withTag) < 0) return -1; + if (pExec->subType == TOPIC_SUB_TYPE__TABLE) { + if (tEncodeCStr(pEncoder, pExec->qmsg) < 0) return -1; + // TODO encode modified exec + } + tEndEncode(pEncoder); + return pEncoder->pos; } +int32_t tDecodeSTqExec(SDecoder* pDecoder, STqExec* pExec) { + if (tStartDecode(pDecoder) < 0) return -1; + if (tDecodeCStrTo(pDecoder, pExec->subKey) < 0) return -1; + if (tDecodeI64(pDecoder, &pExec->consumerId) < 0) return -1; + if (tDecodeI32(pDecoder, &pExec->epoch) < 0) return -1; + if (tDecodeI8(pDecoder, &pExec->subType) < 0) return -1; + if (tDecodeI8(pDecoder, &pExec->withTbName) < 0) return -1; + if (tDecodeI8(pDecoder, &pExec->withSchema) < 0) return -1; + if (tDecodeI8(pDecoder, &pExec->withTag) < 0) return -1; + if (pExec->subType == TOPIC_SUB_TYPE__TABLE) { + if (tDecodeCStrAlloc(pDecoder, &pExec->qmsg) < 0) return -1; + // TODO decode modified exec + } + tEndDecode(pDecoder); + return 0; +} int32_t tqUpdateTbUidList(STQ* pTq, const SArray* tbUidList, bool isAdd) { - void* pIter = NULL; - STqExec* pExec = NULL; + void* pIter = NULL; while (1) { pIter = taosHashIterate(pTq->execs, pIter); if (pIter == NULL) break; - pExec = (STqExec*)pIter; + STqExec* pExec = (STqExec*)pIter; if (pExec->subType == TOPIC_SUB_TYPE__DB) { - if (isAdd) { - continue; - } else { + if (!isAdd) { int32_t sz = taosArrayGetSize(tbUidList); for (int32_t i = 0; i < sz; i++) { int64_t tbUid = *(int64_t*)taosArrayGet(tbUidList, i); taosHashPut(pExec->pDropTbUid, &tbUid, sizeof(int64_t), NULL, 0); } } - } - for (int32_t i = 0; i < 5; i++) { - int32_t code = qUpdateQualifiedTableId(pExec->task[i], tbUidList, isAdd); - ASSERT(code == 0); + } else { + for (int32_t i = 0; i < 5; i++) { + int32_t code = qUpdateQualifiedTableId(pExec->task[i], tbUidList, isAdd); + ASSERT(code == 0); + } } } return 0; } -int32_t tqPushMsgNew(STQ* pTq, void* msg, int32_t msgLen, tmsg_t msgType, int64_t ver) { +int32_t tqPushMsgNew(STQ* pTq, void* msg, int32_t msgLen, tmsg_t msgType, int64_t ver, SRpcHandleInfo handleInfo) { if (msgType != TDMT_VND_SUBMIT) return 0; void* pIter = NULL; STqExec* pExec = NULL; @@ -240,10 +247,9 @@ int32_t tqPushMsgNew(STQ* pTq, void* msg, int32_t msgLen, tmsg_t msgType, int64_ void* abuf = POINTER_SHIFT(buf, sizeof(SMqRspHead)); tEncodeSMqDataBlkRsp(&abuf, &rsp); - pMsg->pCont = buf; - pMsg->contLen = tlen; - pMsg->code = 0; - tmsgSendRsp(pMsg); + + SRpcMsg resp = {.info = handleInfo, .pCont = buf, .contLen = tlen, .code = 0}; + tmsgSendRsp(&resp); atomic_store_ptr(&pExec->pushHandle.handle, NULL); taosWUnLockLatch(&pExec->pushHandle.lock); @@ -261,162 +267,26 @@ int32_t tqPushMsgNew(STQ* pTq, void* msg, int32_t msgLen, tmsg_t msgType, int64_ } int tqPushMsg(STQ* pTq, void* msg, int32_t msgLen, tmsg_t msgType, int64_t ver) { - if (msgType != TDMT_VND_SUBMIT) return 0; + if (msgType == TDMT_VND_SUBMIT) { + if (taosHashGetSize(pTq->pStreamTasks) == 0) return 0; - // make sure msgType == TDMT_VND_SUBMIT - if (tdUpdateExpireWindow(pTq->pVnode->pSma, msg, ver) != 0) { - return -1; - } - - if (taosHashGetSize(pTq->pStreamTasks) == 0) return 0; + if (tdUpdateExpireWindow(pTq->pVnode->pSma, msg, ver) != 0) { + // TODO handle sma error + } + void* data = taosMemoryMalloc(msgLen); + if (data == NULL) { + return -1; + } + memcpy(data, msg, msgLen); - void* data = taosMemoryMalloc(msgLen); - if (data == NULL) { - return -1; + tqProcessStreamTrigger(pTq, data); } - memcpy(data, msg, msgLen); - - SRpcMsg req = { - .msgType = TDMT_VND_STREAM_TRIGGER, - .pCont = data, - .contLen = msgLen, - }; - - tmsgPutToQueue(&pTq->pVnode->msgCb, FETCH_QUEUE, &req); return 0; } int tqCommit(STQ* pTq) { // do nothing - /*return tqStorePersist(pTq->tqMeta);*/ - return 0; -} - -int32_t tqGetTopicHandleSize(const STqTopic* pTopic) { - return strlen(pTopic->topicName) + strlen(pTopic->sql) + strlen(pTopic->physicalPlan) + strlen(pTopic->qmsg) + - sizeof(int64_t) * 3; -} - -int32_t tqGetConsumerHandleSize(const STqConsumer* pConsumer) { - int num = taosArrayGetSize(pConsumer->topics); - int32_t sz = 0; - for (int i = 0; i < num; i++) { - STqTopic* pTopic = taosArrayGet(pConsumer->topics, i); - sz += tqGetTopicHandleSize(pTopic); - } - return sz; -} - -static FORCE_INLINE int32_t tEncodeSTqTopic(void** buf, const STqTopic* pTopic) { - int32_t tlen = 0; - tlen += taosEncodeString(buf, pTopic->topicName); - /*tlen += taosEncodeString(buf, pTopic->sql);*/ - /*tlen += taosEncodeString(buf, pTopic->physicalPlan);*/ - tlen += taosEncodeString(buf, pTopic->qmsg); - /*tlen += taosEncodeFixedI64(buf, pTopic->persistedOffset);*/ - /*tlen += taosEncodeFixedI64(buf, pTopic->committedOffset);*/ - /*tlen += taosEncodeFixedI64(buf, pTopic->currentOffset);*/ - return tlen; -} - -static FORCE_INLINE const void* tDecodeSTqTopic(const void* buf, STqTopic* pTopic) { - buf = taosDecodeStringTo(buf, pTopic->topicName); - /*buf = taosDecodeString(buf, &pTopic->sql);*/ - /*buf = taosDecodeString(buf, &pTopic->physicalPlan);*/ - buf = taosDecodeString(buf, &pTopic->qmsg); - /*buf = taosDecodeFixedI64(buf, &pTopic->persistedOffset);*/ - /*buf = taosDecodeFixedI64(buf, &pTopic->committedOffset);*/ - /*buf = taosDecodeFixedI64(buf, &pTopic->currentOffset);*/ - return buf; -} - -static FORCE_INLINE int32_t tEncodeSTqConsumer(void** buf, const STqConsumer* pConsumer) { - int32_t sz; - - int32_t tlen = 0; - tlen += taosEncodeFixedI64(buf, pConsumer->consumerId); - tlen += taosEncodeFixedI32(buf, pConsumer->epoch); - tlen += taosEncodeString(buf, pConsumer->cgroup); - sz = taosArrayGetSize(pConsumer->topics); - tlen += taosEncodeFixedI32(buf, sz); - for (int32_t i = 0; i < sz; i++) { - STqTopic* pTopic = taosArrayGet(pConsumer->topics, i); - tlen += tEncodeSTqTopic(buf, pTopic); - } - return tlen; -} - -static FORCE_INLINE const void* tDecodeSTqConsumer(const void* buf, STqConsumer* pConsumer) { - int32_t sz; - - buf = taosDecodeFixedI64(buf, &pConsumer->consumerId); - buf = taosDecodeFixedI32(buf, &pConsumer->epoch); - buf = taosDecodeStringTo(buf, pConsumer->cgroup); - buf = taosDecodeFixedI32(buf, &sz); - pConsumer->topics = taosArrayInit(sz, sizeof(STqTopic)); - if (pConsumer->topics == NULL) return NULL; - for (int32_t i = 0; i < sz; i++) { - STqTopic pTopic; - buf = tDecodeSTqTopic(buf, &pTopic); - taosArrayPush(pConsumer->topics, &pTopic); - } - return buf; -} - -int tqSerializeConsumer(const STqConsumer* pConsumer, STqSerializedHead** ppHead) { - int32_t sz = tEncodeSTqConsumer(NULL, pConsumer); - - if (sz > (*ppHead)->ssize) { - void* tmpPtr = taosMemoryRealloc(*ppHead, sizeof(STqSerializedHead) + sz); - if (tmpPtr == NULL) { - taosMemoryFree(*ppHead); - terrno = TSDB_CODE_TQ_OUT_OF_MEMORY; - return -1; - } - *ppHead = tmpPtr; - (*ppHead)->ssize = sz; - } - - void* ptr = (*ppHead)->content; - void* abuf = ptr; - tEncodeSTqConsumer(&abuf, pConsumer); - - return 0; -} - -int32_t tqDeserializeConsumer(STQ* pTq, const STqSerializedHead* pHead, STqConsumer** ppConsumer) { - const void* str = pHead->content; - *ppConsumer = taosMemoryCalloc(1, sizeof(STqConsumer)); - if (*ppConsumer == NULL) { - terrno = TSDB_CODE_TQ_OUT_OF_MEMORY; - return -1; - } - if (tDecodeSTqConsumer(str, *ppConsumer) == NULL) { - terrno = TSDB_CODE_TQ_OUT_OF_MEMORY; - return -1; - } - STqConsumer* pConsumer = *ppConsumer; - int32_t sz = taosArrayGetSize(pConsumer->topics); - for (int32_t i = 0; i < sz; i++) { - STqTopic* pTopic = taosArrayGet(pConsumer->topics, i); - pTopic->pReadhandle = walOpenReadHandle(pTq->pWal); - if (pTopic->pReadhandle == NULL) { - ASSERT(false); - } - for (int j = 0; j < TQ_BUFFER_SIZE; j++) { - pTopic->buffer.output[j].status = 0; - STqReadHandle* pReadHandle = tqInitSubmitMsgScanner(pTq->pVnode->pMeta); - SReadHandle handle = { - .reader = pReadHandle, - .meta = pTq->pVnode->pMeta, - .pMsgCb = &pTq->pVnode->msgCb, - }; - pTopic->buffer.output[j].pReadHandle = pReadHandle; - pTopic->buffer.output[j].task = qCreateStreamExecTaskInfo(pTopic->qmsg, &handle); - } - } - return 0; } @@ -596,6 +466,7 @@ int32_t tqProcessPollReq(STQ* pTq, SRpcMsg* pMsg, int32_t workerId) { SSDataBlock block = {0}; if (tqRetrieveDataBlock(&block.pDataBlock, pReader, &block.info.groupId, &block.info.uid, &block.info.rows, &block.info.numOfCols) < 0) { + if (terrno == TSDB_CODE_TQ_TABLE_SCHEMA_NOT_FOUND) continue; ASSERT(0); } int32_t dataStrLen = sizeof(SRetrieveTableRsp) + blockGetEncodeSize(&block); @@ -664,10 +535,9 @@ int32_t tqProcessPollReq(STQ* pTq, SRpcMsg* pMsg, int32_t workerId) { void* abuf = POINTER_SHIFT(buf, sizeof(SMqRspHead)); tEncodeSMqDataBlkRsp(&abuf, &rsp); - pMsg->pCont = buf; - pMsg->contLen = tlen; - pMsg->code = 0; - tmsgSendRsp(pMsg); + + SRpcMsg resp = {.info = pMsg->info, .pCont = buf, .contLen = tlen, .code = 0}; + tmsgSendRsp(&resp); tqDebug("vg %d offset %ld from consumer %ld (epoch %d) send rsp, block num: %d, reqOffset: %ld, rspOffset: %ld", TD_VID(pTq->pVnode), fetchOffset, consumerId, pReq->epoch, rsp.blockNum, rsp.reqOffset, rsp.rspOffset); @@ -681,216 +551,6 @@ int32_t tqProcessPollReq(STQ* pTq, SRpcMsg* pMsg, int32_t workerId) { return 0; } -#if 0 -int32_t tqProcessPollReq(STQ* pTq, SRpcMsg* pMsg, int32_t workerId) { - SMqPollReq* pReq = pMsg->pCont; - int64_t consumerId = pReq->consumerId; - int64_t fetchOffset; - int64_t blockingTime = pReq->blockingTime; - int32_t reqEpoch = pReq->epoch; - - if (pReq->currentOffset == TMQ_CONF__RESET_OFFSET__EARLIEAST) { - fetchOffset = walGetFirstVer(pTq->pWal); - } else if (pReq->currentOffset == TMQ_CONF__RESET_OFFSET__LATEST) { - fetchOffset = walGetLastVer(pTq->pWal); - } else { - fetchOffset = pReq->currentOffset + 1; - } - - tqDebug("tmq poll: consumer %ld (epoch %d) recv poll req in vg %d, req %ld %ld", consumerId, pReq->epoch, - TD_VID(pTq->pVnode), pReq->currentOffset, fetchOffset); - - SMqPollRspV2 rspV2 = {0}; - rspV2.dataLen = 0; - - STqConsumer* pConsumer = tqHandleGet(pTq->tqMeta, consumerId); - if (pConsumer == NULL) { - vWarn("tmq poll: consumer %ld (epoch %d) not found in vg %d", consumerId, pReq->epoch, TD_VID(pTq->pVnode)); - pMsg->pCont = NULL; - pMsg->contLen = 0; - pMsg->code = -1; - tmsgSendRsp(pMsg); - return 0; - } - - int32_t consumerEpoch = atomic_load_32(&pConsumer->epoch); - while (consumerEpoch < reqEpoch) { - consumerEpoch = atomic_val_compare_exchange_32(&pConsumer->epoch, consumerEpoch, reqEpoch); - } - - STqTopic* pTopic = NULL; - int32_t topicSz = taosArrayGetSize(pConsumer->topics); - for (int32_t i = 0; i < topicSz; i++) { - STqTopic* topic = taosArrayGet(pConsumer->topics, i); - // TODO race condition - ASSERT(pConsumer->consumerId == consumerId); - if (strcmp(topic->topicName, pReq->topic) == 0) { - pTopic = topic; - break; - } - } - if (pTopic == NULL) { - vWarn("tmq poll: consumer %ld (epoch %d) topic %s not found in vg %d", consumerId, pReq->epoch, pReq->topic, - TD_VID(pTq->pVnode)); - pMsg->pCont = NULL; - pMsg->contLen = 0; - pMsg->code = -1; - tmsgSendRsp(pMsg); - return 0; - } - - tqDebug("poll topic %s from consumer %ld (epoch %d) vg %d", pTopic->topicName, consumerId, pReq->epoch, - TD_VID(pTq->pVnode)); - - rspV2.reqOffset = pReq->currentOffset; - rspV2.skipLogNum = 0; - - while (1) { - /*if (fetchOffset > walGetLastVer(pTq->pWal) || walReadWithHandle(pTopic->pReadhandle, fetchOffset) < 0) {*/ - // TODO - consumerEpoch = atomic_load_32(&pConsumer->epoch); - if (consumerEpoch > reqEpoch) { - tqDebug("tmq poll: consumer %ld (epoch %d) vg %d offset %ld, found new consumer epoch %d discard req epoch %d", - consumerId, pReq->epoch, TD_VID(pTq->pVnode), fetchOffset, consumerEpoch, reqEpoch); - break; - } - SWalReadHead* pHead; - if (walReadWithHandle_s(pTopic->pReadhandle, fetchOffset, &pHead) < 0) { - // TODO: no more log, set timer to wait blocking time - // if data inserted during waiting, launch query and - // response to user - tqDebug("tmq poll: consumer %ld (epoch %d) vg %d offset %ld, no more log to return", consumerId, pReq->epoch, - TD_VID(pTq->pVnode), fetchOffset); - break; - } - tqDebug("tmq poll: consumer %ld (epoch %d) iter log, vg %d offset %ld msgType %d", consumerId, pReq->epoch, - TD_VID(pTq->pVnode), fetchOffset, pHead->msgType); - /*int8_t pos = fetchOffset % TQ_BUFFER_SIZE;*/ - /*pHead = pTopic->pReadhandle->pHead;*/ - if (pHead->msgType == TDMT_VND_SUBMIT) { - SSubmitReq* pCont = (SSubmitReq*)&pHead->body; - qTaskInfo_t task = pTopic->buffer.output[workerId].task; - ASSERT(task); - qSetStreamInput(task, pCont, STREAM_DATA_TYPE_SUBMIT_BLOCK); - SArray* pRes = taosArrayInit(0, sizeof(SSDataBlock)); - while (1) { - SSDataBlock* pDataBlock = NULL; - uint64_t ts; - if (qExecTask(task, &pDataBlock, &ts) < 0) { - ASSERT(false); - } - if (pDataBlock == NULL) { - /*pos = fetchOffset % TQ_BUFFER_SIZE;*/ - break; - } - - taosArrayPush(pRes, pDataBlock); - } - - if (taosArrayGetSize(pRes) == 0) { - tqDebug("tmq poll: consumer %ld (epoch %d) iter log, vg %d skip log %ld since not wanted", consumerId, - pReq->epoch, TD_VID(pTq->pVnode), fetchOffset); - fetchOffset++; - rspV2.skipLogNum++; - taosArrayDestroy(pRes); - continue; - } - rspV2.rspOffset = fetchOffset; - - int32_t blockSz = taosArrayGetSize(pRes); - int32_t dataBlockStrLen = 0; - for (int32_t i = 0; i < blockSz; i++) { - SSDataBlock* pBlock = taosArrayGet(pRes, i); - dataBlockStrLen += sizeof(SRetrieveTableRsp) + blockGetEncodeSize(pBlock); - } - - void* dataBlockBuf = taosMemoryMalloc(dataBlockStrLen); - if (dataBlockBuf == NULL) { - pMsg->code = -1; - taosMemoryFree(pHead); - } - - rspV2.blockData = dataBlockBuf; - - int32_t pos; - rspV2.blockPos = taosArrayInit(blockSz, sizeof(int32_t)); - for (int32_t i = 0; i < blockSz; i++) { - pos = 0; - SSDataBlock* pBlock = taosArrayGet(pRes, i); - SRetrieveTableRsp* pRetrieve = (SRetrieveTableRsp*)dataBlockBuf; - pRetrieve->useconds = 0; - pRetrieve->precision = 0; - pRetrieve->compressed = 0; - pRetrieve->completed = 1; - pRetrieve->numOfRows = htonl(pBlock->info.rows); - blockCompressEncode(pBlock, pRetrieve->data, &pos, pBlock->info.numOfCols, false); - taosArrayPush(rspV2.blockPos, &rspV2.dataLen); - - int32_t totLen = sizeof(SRetrieveTableRsp) + pos; - pRetrieve->compLen = htonl(totLen); - rspV2.dataLen += totLen; - dataBlockBuf = POINTER_SHIFT(dataBlockBuf, totLen); - } - ASSERT(POINTER_DISTANCE(dataBlockBuf, rspV2.blockData) <= dataBlockStrLen); - - int32_t msgLen = sizeof(SMqRspHead) + tEncodeSMqPollRspV2(NULL, &rspV2); - void* buf = rpcMallocCont(msgLen); - - ((SMqRspHead*)buf)->mqMsgType = TMQ_MSG_TYPE__POLL_RSP; - ((SMqRspHead*)buf)->epoch = pReq->epoch; - ((SMqRspHead*)buf)->consumerId = consumerId; - - void* msgBodyBuf = POINTER_SHIFT(buf, sizeof(SMqRspHead)); - tEncodeSMqPollRspV2(&msgBodyBuf, &rspV2); - - /*rsp.pBlockData = pRes;*/ - - /*taosArrayDestroyEx(rsp.pBlockData, (void (*)(void*))tDeleteSSDataBlock);*/ - pMsg->pCont = buf; - pMsg->contLen = msgLen; - pMsg->code = 0; - tqDebug("vg %d offset %ld msgType %d from consumer %ld (epoch %d) actual rsp", TD_VID(pTq->pVnode), fetchOffset, - pHead->msgType, consumerId, pReq->epoch); - tmsgSendRsp(pMsg); - taosMemoryFree(pHead); - return 0; - } else { - taosMemoryFree(pHead); - fetchOffset++; - rspV2.skipLogNum++; - } - } - - /*if (blockingTime != 0) {*/ - /*tqAddClientPusher(pTq->tqPushMgr, pMsg, consumerId, blockingTime);*/ - /*} else {*/ - - rspV2.rspOffset = fetchOffset - 1; - - int32_t tlen = sizeof(SMqRspHead) + tEncodeSMqPollRspV2(NULL, &rspV2); - void* buf = rpcMallocCont(tlen); - if (buf == NULL) { - pMsg->code = -1; - return -1; - } - ((SMqRspHead*)buf)->mqMsgType = TMQ_MSG_TYPE__POLL_RSP; - ((SMqRspHead*)buf)->epoch = pReq->epoch; - ((SMqRspHead*)buf)->consumerId = consumerId; - - void* abuf = POINTER_SHIFT(buf, sizeof(SMqRspHead)); - tEncodeSMqPollRspV2(&abuf, &rspV2); - pMsg->pCont = buf; - pMsg->contLen = tlen; - pMsg->code = 0; - tmsgSendRsp(pMsg); - tqDebug("vg %d offset %ld from consumer %ld (epoch %d) not rsp", TD_VID(pTq->pVnode), fetchOffset, consumerId, - pReq->epoch); - /*}*/ - - return 0; -} -#endif - int32_t tqProcessVgDeleteReq(STQ* pTq, char* msg, int32_t msgLen) { SMqVDeleteReq* pReq = (SMqVDeleteReq*)msg; @@ -980,36 +640,6 @@ void tqTableSink(SStreamTask* pTask, void* vnode, int64_t ver, void* data) { ASSERT(tmsgPutToQueue(&pVnode->msgCb, WRITE_QUEUE, &msg) == 0); } -int32_t tqExpandTask(STQ* pTq, SStreamTask* pTask, int32_t parallel) { - if (pTask->execType != TASK_EXEC__NONE) { - // expand runners - pTask->exec.numOfRunners = parallel; - pTask->exec.runners = taosMemoryCalloc(parallel, sizeof(SStreamRunner)); - if (pTask->exec.runners == NULL) { - return -1; - } - for (int32_t i = 0; i < parallel; i++) { - STqReadHandle* pStreamReader = tqInitSubmitMsgScanner(pTq->pVnode->pMeta); - SReadHandle handle = { - .reader = pStreamReader, - .meta = pTq->pVnode->pMeta, - .pMsgCb = &pTq->pVnode->msgCb, - .vnode = pTq->pVnode, - }; - pTask->exec.runners[i].inputHandle = pStreamReader; - pTask->exec.runners[i].executor = qCreateStreamExecTaskInfo(pTask->exec.qmsg, &handle); - ASSERT(pTask->exec.runners[i].executor); - } - } - - if (pTask->sinkType == TASK_SINK__TABLE) { - pTask->tbSink.vnode = pTq->pVnode; - pTask->tbSink.tbSinkFunc = tqTableSink; - } - - return 0; -} - int32_t tqProcessTaskDeploy(STQ* pTq, char* msg, int32_t msgLen) { SStreamTask* pTask = taosMemoryCalloc(1, sizeof(SStreamTask)); if (pTask == NULL) { @@ -1022,9 +652,31 @@ int32_t tqProcessTaskDeploy(STQ* pTq, char* msg, int32_t msgLen) { } tDecoderClear(&decoder); + pTask->status = TASK_STATUS__IDLE; + pTask->inputStatus = TASK_INPUT_STATUS__NORMAL; + pTask->outputStatus = TASK_OUTPUT_STATUS__NORMAL; + + pTask->inputQ = taosOpenQueue(); + pTask->outputQ = taosOpenQueue(); + pTask->inputQAll = taosAllocateQall(); + pTask->outputQAll = taosAllocateQall(); + + if (pTask->inputQ == NULL || pTask->outputQ == NULL || pTask->inputQAll == NULL || pTask->outputQAll == NULL) + goto FAIL; + // exec - if (tqExpandTask(pTq, pTask, 4) < 0) { - ASSERT(0); + if (pTask->execType != TASK_EXEC__NONE) { + // expand runners + STqReadHandle* pStreamReader = tqInitSubmitMsgScanner(pTq->pVnode->pMeta); + SReadHandle handle = { + .reader = pStreamReader, + .meta = pTq->pVnode->pMeta, + .pMsgCb = &pTq->pVnode->msgCb, + .vnode = pTq->pVnode, + }; + pTask->exec.inputHandle = pStreamReader; + pTask->exec.executor = qCreateStreamExecTaskInfo(pTask->exec.qmsg, &handle); + ASSERT(pTask->exec.executor); } // sink @@ -1032,8 +684,12 @@ int32_t tqProcessTaskDeploy(STQ* pTq, char* msg, int32_t msgLen) { if (pTask->sinkType == TASK_SINK__SMA) { pTask->smaSink.smaSink = smaHandleRes; } else if (pTask->sinkType == TASK_SINK__TABLE) { + pTask->tbSink.vnode = pTq->pVnode; + pTask->tbSink.tbSinkFunc = tqTableSink; + ASSERT(pTask->tbSink.pSchemaWrapper); ASSERT(pTask->tbSink.pSchemaWrapper->pSchema); + pTask->tbSink.pTSchema = tdGetSTSChemaFromSSChema(&pTask->tbSink.pSchemaWrapper->pSchema, pTask->tbSink.pSchemaWrapper->nCols); ASSERT(pTask->tbSink.pTSchema); @@ -1042,58 +698,37 @@ int32_t tqProcessTaskDeploy(STQ* pTq, char* msg, int32_t msgLen) { taosHashPut(pTq->pStreamTasks, &pTask->taskId, sizeof(int32_t), pTask, sizeof(SStreamTask)); return 0; +FAIL: + if (pTask->inputQ) taosCloseQueue(pTask->inputQ); + if (pTask->outputQ) taosCloseQueue(pTask->outputQ); + if (pTask->inputQAll) taosFreeQall(pTask->inputQAll); + if (pTask->outputQAll) taosFreeQall(pTask->outputQAll); + if (pTask) taosMemoryFree(pTask); + return -1; } -int32_t tqProcessStreamTrigger(STQ* pTq, void* data, int32_t dataLen, int32_t workerId) { - void* pIter = NULL; - - while (1) { - pIter = taosHashIterate(pTq->pStreamTasks, pIter); - if (pIter == NULL) break; - SStreamTask* pTask = (SStreamTask*)pIter; - - if (streamExecTask(pTask, &pTq->pVnode->msgCb, data, STREAM_DATA_TYPE_SUBMIT_BLOCK, workerId) < 0) { - // TODO - } - } - return 0; -} - -int32_t tqProcessTaskExec(STQ* pTq, char* msg, int32_t msgLen, int32_t workerId) { - SStreamTaskExecReq req; - tDecodeSStreamTaskExecReq(msg, &req); - - int32_t taskId = req.taskId; - ASSERT(taskId); - - SStreamTask* pTask = taosHashGet(pTq->pStreamTasks, &taskId, sizeof(int32_t)); - ASSERT(pTask); - - if (streamExecTask(pTask, &pTq->pVnode->msgCb, req.data, STREAM_DATA_TYPE_SSDATA_BLOCK, workerId) < 0) { - // TODO - } - return 0; -} - -int32_t tqProcessStreamTrigger2(STQ* pTq, SSubmitReq* pReq, int64_t ver) { +int32_t tqProcessStreamTrigger(STQ* pTq, SSubmitReq* pReq) { void* pIter = NULL; bool failed = false; SStreamDataSubmit* pSubmit = taosAllocateQitem(sizeof(SStreamDataSubmit), DEF_QITEM); if (pSubmit == NULL) { failed = true; + goto SET_TASK_FAIL; } pSubmit->dataRef = taosMemoryMalloc(sizeof(int32_t)); if (pSubmit->dataRef == NULL) { failed = true; + goto SET_TASK_FAIL; } - pSubmit->type = STREAM_DATA_TYPE_SUBMIT_BLOCK; - pSubmit->sourceVer = ver; - pSubmit->sourceVg = pTq->pVnode->config.vgId; + pSubmit->type = STREAM_INPUT__DATA_SUBMIT; + /*pSubmit->sourceVer = ver;*/ + /*pSubmit->sourceVg = pTq->pVnode->config.vgId;*/ pSubmit->data = pReq; *pSubmit->dataRef = 1; +SET_TASK_FAIL: while (1) { pIter = taosHashIterate(pTq->pStreamTasks, pIter); if (pIter == NULL) break; @@ -1112,7 +747,18 @@ int32_t tqProcessStreamTrigger2(STQ* pTq, SSubmitReq* pReq, int64_t ver) { int8_t execStatus = atomic_load_8(&pTask->status); if (execStatus == TASK_STATUS__IDLE || execStatus == TASK_STATUS__CLOSING) { - // TODO dispatch task launch msg to fetch queue + SStreamTaskRunReq* pRunReq = taosMemoryMalloc(sizeof(SStreamTaskRunReq)); + if (pRunReq == NULL) continue; + // TODO: do we need htonl? + pRunReq->head.vgId = pTq->pVnode->config.vgId; + pRunReq->streamId = pTask->streamId; + pRunReq->taskId = pTask->taskId; + SRpcMsg msg = { + .msgType = TDMT_VND_TASK_RUN, + .pCont = pRunReq, + .contLen = sizeof(SStreamTaskRunReq), + }; + tmsgPutToQueue(&pTq->pVnode->msgCb, FETCH_QUEUE, &msg); } } else { @@ -1124,11 +770,53 @@ int32_t tqProcessStreamTrigger2(STQ* pTq, SSubmitReq* pReq, int64_t ver) { streamDataSubmitRefDec(pSubmit); return 0; } else { + if (pSubmit) { + if (pSubmit->dataRef) { + taosMemoryFree(pSubmit->dataRef); + } + taosFreeQitem(pSubmit); + } return -1; } } -int32_t tqProcessTaskExec2(STQ* pTq, char* msg, int32_t msgLen) { +int32_t tqProcessTaskRunReq(STQ* pTq, SRpcMsg* pMsg) { // + SStreamTaskRunReq* pReq = pMsg->pCont; + int32_t taskId = pReq->taskId; + SStreamTask* pTask = taosHashGet(pTq->pStreamTasks, &taskId, sizeof(int32_t)); + streamTaskProcessRunReq(pTask, &pTq->pVnode->msgCb); + return 0; +} + +int32_t tqProcessTaskDispatchReq(STQ* pTq, SRpcMsg* pMsg) { + SStreamDispatchReq* pReq = pMsg->pCont; + int32_t taskId = pReq->taskId; + SStreamTask* pTask = taosHashGet(pTq->pStreamTasks, &taskId, sizeof(int32_t)); + streamProcessDispatchReq(pTask, &pTq->pVnode->msgCb, pReq, pMsg); + return 0; +} + +int32_t tqProcessTaskRecoverReq(STQ* pTq, SRpcMsg* pMsg) { + SStreamTaskRecoverReq* pReq = pMsg->pCont; + int32_t taskId = pReq->taskId; + SStreamTask* pTask = taosHashGet(pTq->pStreamTasks, &taskId, sizeof(int32_t)); + streamProcessRecoverReq(pTask, &pTq->pVnode->msgCb, pReq, pMsg); + return 0; +} + +int32_t tqProcessTaskDispatchRsp(STQ* pTq, SRpcMsg* pMsg) { + SStreamDispatchRsp* pRsp = pMsg->pCont; + int32_t taskId = pRsp->taskId; + SStreamTask* pTask = taosHashGet(pTq->pStreamTasks, &taskId, sizeof(int32_t)); + streamProcessDispatchRsp(pTask, &pTq->pVnode->msgCb, pRsp); + return 0; +} + +int32_t tqProcessTaskRecoverRsp(STQ* pTq, SRpcMsg* pMsg) { + SStreamTaskRecoverRsp* pRsp = pMsg->pCont; + int32_t taskId = pRsp->taskId; + SStreamTask* pTask = taosHashGet(pTq->pStreamTasks, &taskId, sizeof(int32_t)); + streamProcessRecoverRsp(pTask, pRsp); return 0; } diff --git a/source/dnode/vnode/src/tq/tqMetaStore.c b/source/dnode/vnode/src/tq/tqMetaStore.c deleted file mode 100644 index ca09cc1dc13094cf8ba53e685f8c973a1c0250b7..0000000000000000000000000000000000000000 --- a/source/dnode/vnode/src/tq/tqMetaStore.c +++ /dev/null @@ -1,622 +0,0 @@ -/* - * Copyright (c) 2019 TAOS Data, Inc. - * - * This program is free software: you can use, redistribute, and/or modify - * it under the terms of the GNU Affero General Public License, version 3 - * or later ("AGPL"), as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -#include "tq.h" -// #include -// #include -// #include -// #include "osDir.h" - -#define TQ_META_NAME "tq.meta" -#define TQ_IDX_NAME "tq.idx" - -static int32_t tqHandlePutCommitted(STqMetaStore*, int64_t key, void* value); -static void* tqHandleGetUncommitted(STqMetaStore*, int64_t key); - -static inline void tqLinkUnpersist(STqMetaStore* pMeta, STqMetaList* pNode) { - if (pNode->unpersistNext == NULL) { - pNode->unpersistNext = pMeta->unpersistHead->unpersistNext; - pNode->unpersistPrev = pMeta->unpersistHead; - pMeta->unpersistHead->unpersistNext->unpersistPrev = pNode; - pMeta->unpersistHead->unpersistNext = pNode; - } -} - -static inline int64_t tqSeekLastPage(TdFilePtr pFile) { - int offset = taosLSeekFile(pFile, 0, SEEK_END); - int pageNo = offset / TQ_PAGE_SIZE; - int curPageOffset = pageNo * TQ_PAGE_SIZE; - return taosLSeekFile(pFile, curPageOffset, SEEK_SET); -} - -// TODO: the struct is tightly coupled with index entry -typedef struct STqIdxPageHead { - int16_t writeOffset; - int8_t unused[14]; -} STqIdxPageHead; - -typedef struct STqIdxPageBuf { - STqIdxPageHead head; - char buffer[TQ_IDX_PAGE_BODY_SIZE]; -} STqIdxPageBuf; - -static inline int tqReadLastPage(TdFilePtr pFile, STqIdxPageBuf* pBuf) { - int offset = tqSeekLastPage(pFile); - int nBytes; - if ((nBytes = taosReadFile(pFile, pBuf, TQ_PAGE_SIZE)) == -1) { - terrno = TAOS_SYSTEM_ERROR(errno); - return -1; - } - if (nBytes == 0) { - memset(pBuf, 0, TQ_PAGE_SIZE); - pBuf->head.writeOffset = TQ_IDX_PAGE_HEAD_SIZE; - } - ASSERT(nBytes == 0 || nBytes == pBuf->head.writeOffset); - - return taosLSeekFile(pFile, offset, SEEK_SET); -} - -STqMetaStore* tqStoreOpen(STQ* pTq, const char* path, FTqSerialize serializer, FTqDeserialize deserializer, - FTqDelete deleter, int32_t tqConfigFlag) { - STqMetaStore* pMeta = taosMemoryCalloc(1, sizeof(STqMetaStore)); - if (pMeta == NULL) { - terrno = TSDB_CODE_TQ_OUT_OF_MEMORY; - return NULL; - } - pMeta->pTq = pTq; - - // concat data file name and index file name - size_t pathLen = strlen(path); - pMeta->dirPath = taosMemoryMalloc(pathLen + 1); - if (pMeta->dirPath == NULL) { - terrno = TSDB_CODE_TQ_OUT_OF_MEMORY; - taosMemoryFree(pMeta); - return NULL; - } - strcpy(pMeta->dirPath, path); - - char* name = taosMemoryMalloc(pathLen + 10); - - strcpy(name, path); - if (!taosDirExist(name) && taosMkDir(name) != 0) { - terrno = TSDB_CODE_TQ_FAILED_TO_CREATE_DIR; - tqError("failed to create dir:%s since %s ", name, terrstr()); - } - strcat(name, "/" TQ_IDX_NAME); - TdFilePtr pIdxFile = taosOpenFile(name, TD_FILE_CREATE | TD_FILE_WRITE | TD_FILE_READ); - if (pIdxFile == NULL) { - terrno = TAOS_SYSTEM_ERROR(errno); - tqError("failed to open file:%s since %s ", name, terrstr()); - // free memory - taosMemoryFree(name); - return NULL; - } - - pMeta->pIdxFile = pIdxFile; - pMeta->unpersistHead = taosMemoryCalloc(1, sizeof(STqMetaList)); - if (pMeta->unpersistHead == NULL) { - terrno = TSDB_CODE_TQ_OUT_OF_MEMORY; - taosMemoryFree(name); - return NULL; - } - pMeta->unpersistHead->unpersistNext = pMeta->unpersistHead->unpersistPrev = pMeta->unpersistHead; - - strcpy(name, path); - strcat(name, "/" TQ_META_NAME); - TdFilePtr pFile = taosOpenFile(name, TD_FILE_CREATE | TD_FILE_WRITE | TD_FILE_READ); - if (pFile == NULL) { - terrno = TAOS_SYSTEM_ERROR(errno); - tqError("failed to open file:%s since %s", name, terrstr()); - taosMemoryFree(name); - return NULL; - } - taosMemoryFree(name); - - pMeta->pFile = pFile; - - pMeta->pSerializer = serializer; - pMeta->pDeserializer = deserializer; - pMeta->pDeleter = deleter; - pMeta->tqConfigFlag = tqConfigFlag; - - // read idx file and load into memory - STqIdxPageBuf idxBuf; - STqSerializedHead* serializedObj = taosMemoryMalloc(TQ_PAGE_SIZE); - if (serializedObj == NULL) { - terrno = TSDB_CODE_TQ_OUT_OF_MEMORY; - } - int idxRead; - int allocated = TQ_PAGE_SIZE; - bool readEnd = false; - while ((idxRead = taosReadFile(pIdxFile, &idxBuf, TQ_PAGE_SIZE))) { - if (idxRead == -1) { - // TODO: handle error - terrno = TAOS_SYSTEM_ERROR(errno); - tqError("failed to read tq index file since %s", terrstr()); - } - ASSERT(idxBuf.head.writeOffset == idxRead); - // loop read every entry - for (int i = 0; i < idxBuf.head.writeOffset - TQ_IDX_PAGE_HEAD_SIZE; i += TQ_IDX_SIZE) { - STqMetaList* pNode = taosMemoryCalloc(1, sizeof(STqMetaList)); - if (pNode == NULL) { - terrno = TSDB_CODE_TQ_OUT_OF_MEMORY; - // TODO: free memory - } - memcpy(&pNode->handle, &idxBuf.buffer[i], TQ_IDX_SIZE); - - taosLSeekFile(pFile, pNode->handle.offset, SEEK_SET); - if (allocated < pNode->handle.serializedSize) { - void* ptr = taosMemoryRealloc(serializedObj, pNode->handle.serializedSize); - if (ptr == NULL) { - terrno = TSDB_CODE_TQ_OUT_OF_MEMORY; - // TODO: free memory - } - serializedObj = ptr; - allocated = pNode->handle.serializedSize; - } - serializedObj->ssize = pNode->handle.serializedSize; - if (taosReadFile(pFile, serializedObj, pNode->handle.serializedSize) != pNode->handle.serializedSize) { - // TODO: read error - } - if (serializedObj->action == TQ_ACTION_INUSE) { - if (serializedObj->ssize != sizeof(STqSerializedHead)) { - pMeta->pDeserializer(pTq, serializedObj, &pNode->handle.valueInUse); - } else { - pNode->handle.valueInUse = TQ_DELETE_TOKEN; - } - } else if (serializedObj->action == TQ_ACTION_INTXN) { - if (serializedObj->ssize != sizeof(STqSerializedHead)) { - pMeta->pDeserializer(pTq, serializedObj, &pNode->handle.valueInTxn); - } else { - pNode->handle.valueInTxn = TQ_DELETE_TOKEN; - } - } else if (serializedObj->action == TQ_ACTION_INUSE_CONT) { - if (serializedObj->ssize != sizeof(STqSerializedHead)) { - pMeta->pDeserializer(pTq, serializedObj, &pNode->handle.valueInUse); - } else { - pNode->handle.valueInUse = TQ_DELETE_TOKEN; - } - STqSerializedHead* ptr = POINTER_SHIFT(serializedObj, serializedObj->ssize); - if (ptr->ssize != sizeof(STqSerializedHead)) { - pMeta->pDeserializer(pTq, ptr, &pNode->handle.valueInTxn); - } else { - pNode->handle.valueInTxn = TQ_DELETE_TOKEN; - } - } else { - ASSERT(0); - } - - // put into list - int bucketKey = pNode->handle.key & TQ_BUCKET_MASK; - STqMetaList* pBucketNode = pMeta->bucket[bucketKey]; - if (pBucketNode == NULL) { - pMeta->bucket[bucketKey] = pNode; - } else if (pBucketNode->handle.key == pNode->handle.key) { - pNode->next = pBucketNode->next; - pMeta->bucket[bucketKey] = pNode; - } else { - while (pBucketNode->next && pBucketNode->next->handle.key != pNode->handle.key) { - pBucketNode = pBucketNode->next; - } - if (pBucketNode->next) { - ASSERT(pBucketNode->next->handle.key == pNode->handle.key); - STqMetaList* pNodeFound = pBucketNode->next; - pNode->next = pNodeFound->next; - pBucketNode->next = pNode; - pBucketNode = pNodeFound; - } else { - pNode->next = pMeta->bucket[bucketKey]; - pMeta->bucket[bucketKey] = pNode; - pBucketNode = NULL; - } - } - if (pBucketNode) { - if (pBucketNode->handle.valueInUse && pBucketNode->handle.valueInUse != TQ_DELETE_TOKEN) { - pMeta->pDeleter(pBucketNode->handle.valueInUse); - } - if (pBucketNode->handle.valueInTxn && pBucketNode->handle.valueInTxn != TQ_DELETE_TOKEN) { - pMeta->pDeleter(pBucketNode->handle.valueInTxn); - } - taosMemoryFree(pBucketNode); - } - } - } - taosMemoryFree(serializedObj); - return pMeta; -} - -int32_t tqStoreClose(STqMetaStore* pMeta) { - // commit data and idx - tqStorePersist(pMeta); - ASSERT(pMeta->unpersistHead && pMeta->unpersistHead->next == NULL); - taosCloseFile(&pMeta->pFile); - taosCloseFile(&pMeta->pIdxFile); - // free memory - for (int i = 0; i < TQ_BUCKET_SIZE; i++) { - STqMetaList* pNode = pMeta->bucket[i]; - while (pNode) { - ASSERT(pNode->unpersistNext == NULL); - ASSERT(pNode->unpersistPrev == NULL); - if (pNode->handle.valueInTxn && pNode->handle.valueInTxn != TQ_DELETE_TOKEN) { - pMeta->pDeleter(pNode->handle.valueInTxn); - } - if (pNode->handle.valueInUse && pNode->handle.valueInUse != TQ_DELETE_TOKEN) { - pMeta->pDeleter(pNode->handle.valueInUse); - } - STqMetaList* next = pNode->next; - taosMemoryFree(pNode); - pNode = next; - } - } - taosMemoryFree(pMeta->dirPath); - taosMemoryFree(pMeta->unpersistHead); - taosMemoryFree(pMeta); - return 0; -} - -int32_t tqStoreDelete(STqMetaStore* pMeta) { - taosCloseFile(&pMeta->pFile); - taosCloseFile(&pMeta->pIdxFile); - // free memory - for (int i = 0; i < TQ_BUCKET_SIZE; i++) { - STqMetaList* pNode = pMeta->bucket[i]; - pMeta->bucket[i] = NULL; - while (pNode) { - if (pNode->handle.valueInTxn && pNode->handle.valueInTxn != TQ_DELETE_TOKEN) { - pMeta->pDeleter(pNode->handle.valueInTxn); - } - if (pNode->handle.valueInUse && pNode->handle.valueInUse != TQ_DELETE_TOKEN) { - pMeta->pDeleter(pNode->handle.valueInUse); - } - STqMetaList* next = pNode->next; - taosMemoryFree(pNode); - pNode = next; - } - } - taosMemoryFree(pMeta->unpersistHead); - taosRemoveDir(pMeta->dirPath); - taosMemoryFree(pMeta->dirPath); - taosMemoryFree(pMeta); - return 0; -} - -int32_t tqStorePersist(STqMetaStore* pMeta) { - STqIdxPageBuf idxBuf; - int64_t* bufPtr = (int64_t*)idxBuf.buffer; - STqMetaList* pHead = pMeta->unpersistHead; - STqMetaList* pNode = pHead->unpersistNext; - STqSerializedHead* pSHead = taosMemoryMalloc(sizeof(STqSerializedHead)); - if (pSHead == NULL) { - terrno = TSDB_CODE_TQ_OUT_OF_MEMORY; - return -1; - } - pSHead->ver = TQ_SVER; - pSHead->checksum = 0; - pSHead->ssize = sizeof(STqSerializedHead); - /*int allocatedSize = sizeof(STqSerializedHead);*/ - int offset = taosLSeekFile(pMeta->pFile, 0, SEEK_CUR); - - tqReadLastPage(pMeta->pIdxFile, &idxBuf); - - if (idxBuf.head.writeOffset == TQ_PAGE_SIZE) { - taosLSeekFile(pMeta->pIdxFile, 0, SEEK_END); - memset(&idxBuf, 0, TQ_PAGE_SIZE); - idxBuf.head.writeOffset = TQ_IDX_PAGE_HEAD_SIZE; - } else { - bufPtr = POINTER_SHIFT(&idxBuf, idxBuf.head.writeOffset); - } - - while (pHead != pNode) { - int nBytes = 0; - - if (pNode->handle.valueInUse) { - if (pNode->handle.valueInTxn) { - pSHead->action = TQ_ACTION_INUSE_CONT; - } else { - pSHead->action = TQ_ACTION_INUSE; - } - - if (pNode->handle.valueInUse == TQ_DELETE_TOKEN) { - pSHead->ssize = sizeof(STqSerializedHead); - } else { - pMeta->pSerializer(pNode->handle.valueInUse, &pSHead); - } - nBytes = taosWriteFile(pMeta->pFile, pSHead, pSHead->ssize); - ASSERT(nBytes == pSHead->ssize); - } - - if (pNode->handle.valueInTxn) { - pSHead->action = TQ_ACTION_INTXN; - if (pNode->handle.valueInTxn == TQ_DELETE_TOKEN) { - pSHead->ssize = sizeof(STqSerializedHead); - } else { - pMeta->pSerializer(pNode->handle.valueInTxn, &pSHead); - } - int nBytesTxn = taosWriteFile(pMeta->pFile, pSHead, pSHead->ssize); - ASSERT(nBytesTxn == pSHead->ssize); - nBytes += nBytesTxn; - } - pNode->handle.offset = offset; - offset += nBytes; - - // write idx file - // TODO: endian check and convert - *(bufPtr++) = pNode->handle.key; - *(bufPtr++) = pNode->handle.offset; - *(bufPtr++) = (int64_t)nBytes; - idxBuf.head.writeOffset += TQ_IDX_SIZE; - - if (idxBuf.head.writeOffset >= TQ_PAGE_SIZE) { - nBytes = taosWriteFile(pMeta->pIdxFile, &idxBuf, TQ_PAGE_SIZE); - // TODO: handle error with tfile - ASSERT(nBytes == TQ_PAGE_SIZE); - memset(&idxBuf, 0, TQ_PAGE_SIZE); - idxBuf.head.writeOffset = TQ_IDX_PAGE_HEAD_SIZE; - bufPtr = (int64_t*)&idxBuf.buffer; - } - // remove from unpersist list - pHead->unpersistNext = pNode->unpersistNext; - pHead->unpersistNext->unpersistPrev = pHead; - pNode->unpersistPrev = pNode->unpersistNext = NULL; - pNode = pHead->unpersistNext; - - // remove from bucket - if (pNode->handle.valueInUse == TQ_DELETE_TOKEN && pNode->handle.valueInTxn == NULL) { - int bucketKey = pNode->handle.key & TQ_BUCKET_MASK; - STqMetaList* pBucketHead = pMeta->bucket[bucketKey]; - if (pBucketHead == pNode) { - pMeta->bucket[bucketKey] = pNode->next; - } else { - STqMetaList* pBucketNode = pBucketHead; - while (pBucketNode->next != NULL && pBucketNode->next != pNode) { - pBucketNode = pBucketNode->next; - } - // impossible for pBucket->next == NULL - ASSERT(pBucketNode->next == pNode); - pBucketNode->next = pNode->next; - } - taosMemoryFree(pNode); - } - } - - // write left bytes - taosMemoryFree(pSHead); - // TODO: write new version in tfile - if ((char*)bufPtr != idxBuf.buffer) { - int nBytes = taosWriteFile(pMeta->pIdxFile, &idxBuf, idxBuf.head.writeOffset); - // TODO: handle error in tfile - ASSERT(nBytes == idxBuf.head.writeOffset); - } - // TODO: using fsync in tfile - taosFsyncFile(pMeta->pIdxFile); - taosFsyncFile(pMeta->pFile); - return 0; -} - -static int32_t tqHandlePutCommitted(STqMetaStore* pMeta, int64_t key, void* value) { - int64_t bucketKey = key & TQ_BUCKET_MASK; - STqMetaList* pNode = pMeta->bucket[bucketKey]; - while (pNode) { - if (pNode->handle.key == key) { - if (pNode->handle.valueInUse && pNode->handle.valueInUse != TQ_DELETE_TOKEN) { - pMeta->pDeleter(pNode->handle.valueInUse); - } - // change pointer ownership - pNode->handle.valueInUse = value; - return 0; - } else { - pNode = pNode->next; - } - } - STqMetaList* pNewNode = taosMemoryCalloc(1, sizeof(STqMetaList)); - if (pNewNode == NULL) { - terrno = TSDB_CODE_TQ_OUT_OF_MEMORY; - return -1; - } - pNewNode->handle.key = key; - pNewNode->handle.valueInUse = value; - pNewNode->next = pMeta->bucket[bucketKey]; - // put into unpersist list - pNewNode->unpersistPrev = pMeta->unpersistHead; - pNewNode->unpersistNext = pMeta->unpersistHead->unpersistNext; - pMeta->unpersistHead->unpersistNext->unpersistPrev = pNewNode; - pMeta->unpersistHead->unpersistNext = pNewNode; - return 0; -} - -void* tqHandleGet(STqMetaStore* pMeta, int64_t key) { - int64_t bucketKey = key & TQ_BUCKET_MASK; - STqMetaList* pNode = pMeta->bucket[bucketKey]; - while (pNode) { - if (pNode->handle.key == key) { - if (pNode->handle.valueInUse != NULL && pNode->handle.valueInUse != TQ_DELETE_TOKEN) { - return pNode->handle.valueInUse; - } else { - return NULL; - } - } else { - pNode = pNode->next; - } - } - return NULL; -} - -void* tqHandleTouchGet(STqMetaStore* pMeta, int64_t key) { - int64_t bucketKey = key & TQ_BUCKET_MASK; - STqMetaList* pNode = pMeta->bucket[bucketKey]; - while (pNode) { - if (pNode->handle.key == key) { - if (pNode->handle.valueInUse != NULL && pNode->handle.valueInUse != TQ_DELETE_TOKEN) { - tqLinkUnpersist(pMeta, pNode); - return pNode->handle.valueInUse; - } else { - return NULL; - } - } else { - pNode = pNode->next; - } - } - return NULL; -} - -static inline int32_t tqHandlePutImpl(STqMetaStore* pMeta, int64_t key, void* value) { - int64_t bucketKey = key & TQ_BUCKET_MASK; - STqMetaList* pNode = pMeta->bucket[bucketKey]; - while (pNode) { - if (pNode->handle.key == key) { - if (pNode->handle.valueInTxn) { - if (tqDupIntxnReject(pMeta->tqConfigFlag)) { - terrno = TSDB_CODE_TQ_META_KEY_DUP_IN_TXN; - return -1; - } - if (pNode->handle.valueInTxn != TQ_DELETE_TOKEN) { - pMeta->pDeleter(pNode->handle.valueInTxn); - } - } - pNode->handle.valueInTxn = value; - tqLinkUnpersist(pMeta, pNode); - return 0; - } else { - pNode = pNode->next; - } - } - STqMetaList* pNewNode = taosMemoryCalloc(1, sizeof(STqMetaList)); - if (pNewNode == NULL) { - terrno = TSDB_CODE_TQ_OUT_OF_MEMORY; - return -1; - } - pNewNode->handle.key = key; - pNewNode->handle.valueInTxn = value; - pNewNode->next = pMeta->bucket[bucketKey]; - pMeta->bucket[bucketKey] = pNewNode; - tqLinkUnpersist(pMeta, pNewNode); - return 0; -} - -int32_t tqHandleMovePut(STqMetaStore* pMeta, int64_t key, void* value) { return tqHandlePutImpl(pMeta, key, value); } - -int32_t tqHandleCopyPut(STqMetaStore* pMeta, int64_t key, void* value, size_t vsize) { - void* vmem = taosMemoryMalloc(vsize); - if (vmem == NULL) { - terrno = TSDB_CODE_TQ_OUT_OF_MEMORY; - return -1; - } - memcpy(vmem, value, vsize); - return tqHandlePutImpl(pMeta, key, vmem); -} - -static void* tqHandleGetUncommitted(STqMetaStore* pMeta, int64_t key) { - int64_t bucketKey = key & TQ_BUCKET_MASK; - STqMetaList* pNode = pMeta->bucket[bucketKey]; - while (pNode) { - if (pNode->handle.key == key) { - if (pNode->handle.valueInTxn != NULL && pNode->handle.valueInTxn != TQ_DELETE_TOKEN) { - return pNode->handle.valueInTxn; - } else { - return NULL; - } - } else { - pNode = pNode->next; - } - } - return NULL; -} - -int32_t tqHandleCommit(STqMetaStore* pMeta, int64_t key) { - int64_t bucketKey = key & TQ_BUCKET_MASK; - STqMetaList* pNode = pMeta->bucket[bucketKey]; - while (pNode) { - if (pNode->handle.key == key) { - if (pNode->handle.valueInTxn == NULL) { - terrno = TSDB_CODE_TQ_META_KEY_NOT_IN_TXN; - return -1; - } - if (pNode->handle.valueInUse && pNode->handle.valueInUse != TQ_DELETE_TOKEN) { - pMeta->pDeleter(pNode->handle.valueInUse); - } - pNode->handle.valueInUse = pNode->handle.valueInTxn; - pNode->handle.valueInTxn = NULL; - tqLinkUnpersist(pMeta, pNode); - return 0; - } else { - pNode = pNode->next; - } - } - terrno = TSDB_CODE_TQ_META_NO_SUCH_KEY; - return -1; -} - -int32_t tqHandleAbort(STqMetaStore* pMeta, int64_t key) { - int64_t bucketKey = key & TQ_BUCKET_MASK; - STqMetaList* pNode = pMeta->bucket[bucketKey]; - while (pNode) { - if (pNode->handle.key == key) { - if (pNode->handle.valueInTxn) { - if (pNode->handle.valueInTxn != TQ_DELETE_TOKEN) { - pMeta->pDeleter(pNode->handle.valueInTxn); - } - pNode->handle.valueInTxn = NULL; - tqLinkUnpersist(pMeta, pNode); - return 0; - } - terrno = TSDB_CODE_TQ_META_KEY_NOT_IN_TXN; - return -1; - } else { - pNode = pNode->next; - } - } - terrno = TSDB_CODE_TQ_META_NO_SUCH_KEY; - return -1; -} - -int32_t tqHandleDel(STqMetaStore* pMeta, int64_t key) { - int64_t bucketKey = key & TQ_BUCKET_MASK; - STqMetaList* pNode = pMeta->bucket[bucketKey]; - while (pNode) { - if (pNode->handle.key == key) { - if (pNode->handle.valueInTxn != TQ_DELETE_TOKEN) { - if (pNode->handle.valueInTxn) { - pMeta->pDeleter(pNode->handle.valueInTxn); - } - - pNode->handle.valueInTxn = TQ_DELETE_TOKEN; - tqLinkUnpersist(pMeta, pNode); - return 0; - } - } else { - pNode = pNode->next; - } - } - terrno = TSDB_CODE_TQ_META_NO_SUCH_KEY; - return -1; -} - -int32_t tqHandlePurge(STqMetaStore* pMeta, int64_t key) { - int64_t bucketKey = key & TQ_BUCKET_MASK; - STqMetaList* pNode = pMeta->bucket[bucketKey]; - while (pNode) { - if (pNode->handle.key == key) { - pNode->handle.valueInUse = TQ_DELETE_TOKEN; - tqLinkUnpersist(pMeta, pNode); - return 0; - } else { - pNode = pNode->next; - } - } - terrno = TSDB_CODE_TQ_META_NO_SUCH_KEY; - return -1; -} - -// TODO: clean deleted idx and data from persistent file -int32_t tqStoreCompact(STqMetaStore* pMeta) { return 0; } diff --git a/source/dnode/vnode/src/tq/tqRead.c b/source/dnode/vnode/src/tq/tqRead.c index 28344de897aad987115779be903c1d04e63452d4..be8d786de2e7c015f938e87431129f5b2d067d00 100644 --- a/source/dnode/vnode/src/tq/tqRead.c +++ b/source/dnode/vnode/src/tq/tqRead.c @@ -34,21 +34,11 @@ STqReadHandle* tqInitSubmitMsgScanner(SMeta* pMeta) { int32_t tqReadHandleSetMsg(STqReadHandle* pReadHandle, SSubmitReq* pMsg, int64_t ver) { pReadHandle->pMsg = pMsg; - // pMsg->length = htonl(pMsg->length); - // pMsg->numOfBlocks = htonl(pMsg->numOfBlocks); - // iterate and convert if (tInitSubmitMsgIter(pMsg, &pReadHandle->msgIter) < 0) return -1; while (true) { if (tGetSubmitMsgNext(&pReadHandle->msgIter, &pReadHandle->pBlock) < 0) return -1; if (pReadHandle->pBlock == NULL) break; - - // pReadHandle->pBlock->uid = htobe64(pReadHandle->pBlock->uid); - // pReadHandle->pBlock->suid = htobe64(pReadHandle->pBlock->suid); - // pReadHandle->pBlock->sversion = htonl(pReadHandle->pBlock->sversion); - // pReadHandle->pBlock->dataLen = htonl(pReadHandle->pBlock->dataLen); - // pReadHandle->pBlock->schemaLen = htonl(pReadHandle->pBlock->schemaLen); - // pReadHandle->pBlock->numOfRows = htons(pReadHandle->pBlock->numOfRows); } if (tInitSubmitMsgIter(pMsg, &pReadHandle->msgIter) < 0) return -1; @@ -100,9 +90,23 @@ int32_t tqRetrieveDataBlock(SArray** ppCols, STqReadHandle* pHandle, uint64_t* p int32_t sversion = 1; if (pHandle->sver != sversion || pHandle->cachedSchemaUid != pHandle->msgIter.suid) { pHandle->pSchema = metaGetTbTSchema(pHandle->pVnodeMeta, pHandle->msgIter.uid, sversion); + if (pHandle->pSchema == NULL) { + tqWarn("cannot found tsschema for table: uid: %ld (suid: %ld), version %d, possibly dropped table", + pHandle->msgIter.uid, pHandle->msgIter.suid, pHandle->sver); + /*ASSERT(0);*/ + terrno = TSDB_CODE_TQ_TABLE_SCHEMA_NOT_FOUND; + return -1; + } // this interface use suid instead of uid pHandle->pSchemaWrapper = metaGetTableSchema(pHandle->pVnodeMeta, pHandle->msgIter.suid, sversion, true); + if (pHandle->pSchemaWrapper == NULL) { + tqWarn("cannot found schema wrapper for table: suid: %ld, version %d, possibly dropped table", + pHandle->msgIter.suid, pHandle->sver); + /*ASSERT(0);*/ + terrno = TSDB_CODE_TQ_TABLE_SCHEMA_NOT_FOUND; + return -1; + } pHandle->sver = sversion; pHandle->cachedSchemaUid = pHandle->msgIter.suid; } @@ -200,7 +204,7 @@ int32_t tqRetrieveDataBlock(SArray** ppCols, STqReadHandle* pHandle, uint64_t* p } return 0; FAIL: - taosArrayDestroy(*ppCols); + if (*ppCols) taosArrayDestroy(*ppCols); return -1; } @@ -241,3 +245,14 @@ int tqReadHandleAddTbUidList(STqReadHandle* pHandle, const SArray* tbUidList) { return 0; } + +int tqReadHandleRemoveTbUidList(STqReadHandle* pHandle, const SArray* tbUidList) { + ASSERT(pHandle->tbIdHash != NULL); + + for (int32_t i = 0; i < taosArrayGetSize(tbUidList); i++) { + int64_t* pKey = (int64_t*)taosArrayGet(tbUidList, i); + taosHashRemove(pHandle->tbIdHash, pKey, sizeof(int64_t)); + } + + return 0; +} diff --git a/source/dnode/vnode/src/tsdb/tsdbCommit.c b/source/dnode/vnode/src/tsdb/tsdbCommit.c index 76d5c3cb3a9e869a62ae0df3222d1e66ff3ac646..93ec6028f86e10dfcf91db7a79ed03e64d2f55db 100644 --- a/source/dnode/vnode/src/tsdb/tsdbCommit.c +++ b/source/dnode/vnode/src/tsdb/tsdbCommit.c @@ -477,6 +477,7 @@ static int tsdbCreateCommitIters(SCommitH *pCommith) { pCommitIter->pTable->pSchema = pTSchema; // metaGetTbTSchema(REPO_META(pRepo), pTbData->uid, 0); } } + tSkipListDestroyIter(pSlIter); return 0; } @@ -1137,6 +1138,9 @@ int tsdbWriteBlockImpl(STsdb *pRepo, STable *pTable, SDFile *pDFile, SDFile *pDF memcpy(tptr, pDataCol->pData, flen); if (tBitmaps > 0) { bptr = POINTER_SHIFT(pBlockData, lsize + flen); + if (isSuper && !tdDataColsIsBitmapI(pDataCols)) { + tdMergeBitmap((uint8_t *)pDataCol->pBitmap, rowsToWrite, (uint8_t *)pDataCol->pBitmap); + } memcpy(bptr, pDataCol->pBitmap, tBitmaps); tBitmapsLen = tBitmaps; flen += tBitmapsLen; @@ -1502,13 +1506,16 @@ static void tsdbLoadAndMergeFromCache(SDataCols *pDataCols, int *iter, SCommitIt tSkipListIterNext(pCommitIter->pIter); } else { if (lastKey != key1) { + if (lastKey != TSKEY_INITIAL_VAL) { + ++pTarget->numOfRows; + } lastKey = key1; - ++pTarget->numOfRows; } // copy disk data for (int i = 0; i < pDataCols->numOfCols; ++i) { SCellVal sVal = {0}; + // no duplicated TS keys in pDataCols from file if (tdGetColDataOfRow(&sVal, pDataCols->cols + i, *iter, pDataCols->bitmapMode) < 0) { TASSERT(0); } diff --git a/source/dnode/vnode/src/tsdb/tsdbMemTable2.c b/source/dnode/vnode/src/tsdb/tsdbMemTable2.c index 2acca738fbc3f2950f537f3b4492fd56b03ccee3..025b2ab580163cf3e9b9031b24f1b07881d3ec61 100644 --- a/source/dnode/vnode/src/tsdb/tsdbMemTable2.c +++ b/source/dnode/vnode/src/tsdb/tsdbMemTable2.c @@ -38,7 +38,7 @@ struct SMemTable { struct SMemSkipListNode { int8_t level; - SMemSkipListNode *forwards[1]; // Windows does not allow 0 + SMemSkipListNode *forwards[1]; // Windows does not allow 0 }; struct SMemSkipList { @@ -46,7 +46,7 @@ struct SMemSkipList { int8_t maxLevel; int8_t level; int32_t size; - SMemSkipListNode pHead[1]; // Windows does not allow 0 + SMemSkipListNode pHead[1]; // Windows does not allow 0 }; struct SMemData { @@ -217,7 +217,7 @@ int32_t tsdbInsertData2(SMemTable *pMemTb, int64_t version, const SVSubmitBlk *p if (tDecodeIsEnd(&dc)) break; // decode row - if (tDecodeBinary(&dc, (const uint8_t **)&tRow.pRow, &tRow.szRow) < 0) { + if (tDecodeBinary(&dc, (uint8_t **)&tRow.pRow, &tRow.szRow) < 0) { terrno = TSDB_CODE_INVALID_MSG; return -1; } @@ -273,7 +273,7 @@ static FORCE_INLINE int32_t tsdbEncodeRow(SEncoder *pEncoder, const STsdbRow *pR static FORCE_INLINE int32_t tsdbDecodeRow(SDecoder *pDecoder, STsdbRow *pRow) { if (tDecodeI64(pDecoder, &pRow->version) < 0) return -1; - if (tDecodeBinary(pDecoder, (const uint8_t **)&pRow->pRow, &pRow->szRow) < 0) return -1; + if (tDecodeBinary(pDecoder, (uint8_t **)&pRow->pRow, &pRow->szRow) < 0) return -1; return 0; } diff --git a/source/dnode/vnode/src/tsdb/tsdbRead.c b/source/dnode/vnode/src/tsdb/tsdbRead.c index 06c3b2913206722a5bb5f66b4c38ecc0f04387d2..ee216cb2ab61ecddd538aae2a05ca27f4b98aa96 100644 --- a/source/dnode/vnode/src/tsdb/tsdbRead.c +++ b/source/dnode/vnode/src/tsdb/tsdbRead.c @@ -425,6 +425,12 @@ static STsdbReadHandle* tsdbQueryTablesImpl(SVnode* pVnode, SQueryTableDataCond* rowLen += pCond->colList[i].bytes; } + // make sure the output SSDataBlock size be less than 2MB. + int32_t TWOMB = 2 * 1024 * 1024; + if (pReadHandle->outputCapacity * rowLen > TWOMB) { + pReadHandle->outputCapacity = TWOMB / rowLen; + } + // allocate buffer in order to load data blocks from file pReadHandle->suppInfo.pstatis = taosMemoryCalloc(pCond->numOfCols, sizeof(SColumnDataAgg)); if (pReadHandle->suppInfo.pstatis == NULL) { @@ -1302,20 +1308,22 @@ static int32_t handleDataMergeIfNeeded(STsdbReadHandle* pTsdbReadHandle, SBlock* if ((ascScan && (key != TSKEY_INITIAL_VAL && key <= binfo.window.ekey)) || (!ascScan && (key != TSKEY_INITIAL_VAL && key >= binfo.window.skey))) { - if ((ascScan && (key != TSKEY_INITIAL_VAL && key < binfo.window.skey)) || - (!ascScan && (key != TSKEY_INITIAL_VAL && key > binfo.window.ekey))) { + + bool cacheDataInFileBlockHole = (ascScan && (key != TSKEY_INITIAL_VAL && key < binfo.window.skey)) || + (!ascScan && (key != TSKEY_INITIAL_VAL && key > binfo.window.ekey)); + if (cacheDataInFileBlockHole) { // do not load file block into buffer int32_t step = ascScan ? 1 : -1; - TSKEY maxKey = - ASCENDING_TRAVERSE(pTsdbReadHandle->order) ? (binfo.window.skey - step) : (binfo.window.ekey - step); + TSKEY maxKey = ascScan ? (binfo.window.skey - step) : (binfo.window.ekey - step); cur->rows = tsdbReadRowsFromCache(pCheckInfo, maxKey, pTsdbReadHandle->outputCapacity, &cur->win, pTsdbReadHandle); pTsdbReadHandle->realNumOfRows = cur->rows; // update the last key value pCheckInfo->lastKey = cur->win.ekey + step; - if (!ASCENDING_TRAVERSE(pTsdbReadHandle->order)) { + + if (!ascScan) { TSWAP(cur->win.skey, cur->win.ekey); } @@ -1334,18 +1342,16 @@ static int32_t handleDataMergeIfNeeded(STsdbReadHandle* pTsdbReadHandle, SBlock* /* * no data in cache, only load data from file * during the query processing, data in cache will not be checked anymore. - * * Here the buffer is not enough, so only part of file block can be loaded into memory buffer */ - assert(pTsdbReadHandle->outputCapacity >= binfo.rows); int32_t endPos = getEndPosInDataBlock(pTsdbReadHandle, &binfo); - if ((cur->pos == 0 && endPos == binfo.rows - 1 && ascScan) || - (cur->pos == (binfo.rows - 1) && endPos == 0 && (!ascScan))) { + bool wholeBlockReturned = ((abs(cur->pos - endPos) + 1) == binfo.rows); + if (wholeBlockReturned) { pTsdbReadHandle->realNumOfRows = binfo.rows; cur->rows = binfo.rows; - cur->win = binfo.window; + cur->win = binfo.window; cur->mixBlock = false; cur->blockCompleted = true; @@ -1356,12 +1362,24 @@ static int32_t handleDataMergeIfNeeded(STsdbReadHandle* pTsdbReadHandle, SBlock* cur->lastKey = binfo.window.skey - 1; cur->pos = -1; } - } else { // partially copy to dest buffer + } else { // partially copy to dest buffer + // make sure to only load once + bool firstTimeExtract = ((cur->pos == 0 && ascScan) || (cur->pos == binfo.rows -1 && (!ascScan))); + if (pTsdbReadHandle->outputCapacity < binfo.rows && firstTimeExtract) { + code = doLoadFileDataBlock(pTsdbReadHandle, pBlock, pCheckInfo, cur->slot); + if (code != TSDB_CODE_SUCCESS) { + return code; + } + } + copyAllRemainRowsFromFileBlock(pTsdbReadHandle, pCheckInfo, &binfo, endPos); cur->mixBlock = true; } - assert(cur->blockCompleted); + if (pTsdbReadHandle->outputCapacity >= binfo.rows) { + ASSERT(cur->blockCompleted); + } + if (cur->rows == binfo.rows) { tsdbDebug("%p whole file block qualified, brange:%" PRId64 "-%" PRId64 ", rows:%d, lastKey:%" PRId64 ", %s", pTsdbReadHandle, cur->win.skey, cur->win.ekey, cur->rows, cur->lastKey, pTsdbReadHandle->idStr); @@ -1620,9 +1638,7 @@ static int32_t mergeTwoRowFromMem(STsdbReadHandle* pTsdbReadHandle, int32_t capa int32_t numOfColsOfRow1 = 0; if (pSchema1 == NULL) { - // pSchema1 = metaGetTbTSchema(REPO_META(pTsdbReadHandle->pTsdb), uid, TD_ROW_SVER(row1)); - // TODO: use the real schemaVersion - pSchema1 = metaGetTbTSchema(REPO_META(pTsdbReadHandle->pTsdb), uid, 1); + pSchema1 = metaGetTbTSchema(REPO_META(pTsdbReadHandle->pTsdb), uid, TD_ROW_SVER(row1)); } #ifdef TD_DEBUG_PRINT_ROW @@ -1639,9 +1655,7 @@ static int32_t mergeTwoRowFromMem(STsdbReadHandle* pTsdbReadHandle, int32_t capa if (row2) { isRow2DataRow = TD_IS_TP_ROW(row2); if (pSchema2 == NULL) { - // pSchema2 = metaGetTbTSchema(REPO_META(pTsdbReadHandle->pTsdb), uid, TD_ROW_SVER(row2)); - // TODO: use the real schemaVersion - pSchema2 = metaGetTbTSchema(REPO_META(pTsdbReadHandle->pTsdb), uid, 1); + pSchema2 = metaGetTbTSchema(REPO_META(pTsdbReadHandle->pTsdb), uid, TD_ROW_SVER(row2)); } if (isRow2DataRow) { numOfColsOfRow2 = schemaNCols(pSchema2); @@ -1714,6 +1728,7 @@ static int32_t mergeTwoRowFromMem(STsdbReadHandle* pTsdbReadHandle, int32_t capa if (*lastRowKey != TSKEY_INITIAL_VAL) { ++(*curRow); } + *lastRowKey = rowKey; ++nResult; } else if (update) { mergeOption = 2; @@ -1721,8 +1736,6 @@ static int32_t mergeTwoRowFromMem(STsdbReadHandle* pTsdbReadHandle, int32_t capa mergeOption = 0; break; } - - *lastRowKey = rowKey; } } else { // TODO: use STSRowIter @@ -1735,6 +1748,7 @@ static int32_t mergeTwoRowFromMem(STsdbReadHandle* pTsdbReadHandle, int32_t capa if (*lastRowKey != TSKEY_INITIAL_VAL) { ++(*curRow); } + *lastRowKey = rowKey; ++nResult; } else if (update) { mergeOption = 2; @@ -1742,7 +1756,6 @@ static int32_t mergeTwoRowFromMem(STsdbReadHandle* pTsdbReadHandle, int32_t capa mergeOption = 0; break; } - *lastRowKey = rowKey; } else { SKvRowIdx* pColIdx = tdKvRowColIdxAt(row, chosen_itr - 1); colId = pColIdx->colId; @@ -1858,15 +1871,14 @@ static void copyAllRemainRowsFromFileBlock(STsdbReadHandle* pTsdbReadHandle, STa SDataCols* pCols = pTsdbReadHandle->rhelper.pDCols[0]; TSKEY* tsArray = pCols->cols[0].pData; - int32_t step = ASCENDING_TRAVERSE(pTsdbReadHandle->order) ? 1 : -1; - int32_t numOfCols = (int32_t)(QH_GET_NUM_OF_COLS(pTsdbReadHandle)); + bool ascScan = ASCENDING_TRAVERSE(pTsdbReadHandle->order); - int32_t pos = cur->pos; + int32_t step = ascScan? 1 : -1; int32_t start = cur->pos; int32_t end = endPos; - if (!ASCENDING_TRAVERSE(pTsdbReadHandle->order)) { + if (!ascScan) { TSWAP(start, end); } @@ -1876,11 +1888,11 @@ static void copyAllRemainRowsFromFileBlock(STsdbReadHandle* pTsdbReadHandle, STa // the time window should always be ascending order: skey <= ekey cur->win = (STimeWindow){.skey = tsArray[start], .ekey = tsArray[end]}; cur->mixBlock = (numOfRows != pBlockInfo->rows); - cur->lastKey = tsArray[endPos] + step; - cur->blockCompleted = true; + cur->lastKey = tsArray[endPos] + step; + cur->blockCompleted = (ascScan? (endPos == pBlockInfo->rows - 1):(endPos == 0)); // The value of pos may be -1 or pBlockInfo->rows, and it is invalid in both cases. - pos = endPos + step; + int32_t pos = endPos + step; updateInfoAfterMerge(pTsdbReadHandle, pCheckInfo, numOfRows, pos); doCheckGeneratedBlockRange(pTsdbReadHandle); @@ -1892,20 +1904,44 @@ static void copyAllRemainRowsFromFileBlock(STsdbReadHandle* pTsdbReadHandle, STa int32_t getEndPosInDataBlock(STsdbReadHandle* pTsdbReadHandle, SDataBlockInfo* pBlockInfo) { // NOTE: reverse the order to find the end position in data block int32_t endPos = -1; - int32_t order = ASCENDING_TRAVERSE(pTsdbReadHandle->order) ? TSDB_ORDER_DESC : TSDB_ORDER_ASC; + bool ascScan = ASCENDING_TRAVERSE(pTsdbReadHandle->order); + int32_t order = ascScan? TSDB_ORDER_DESC : TSDB_ORDER_ASC; SQueryFilePos* cur = &pTsdbReadHandle->cur; SDataCols* pCols = pTsdbReadHandle->rhelper.pDCols[0]; - if (ASCENDING_TRAVERSE(pTsdbReadHandle->order) && pTsdbReadHandle->window.ekey >= pBlockInfo->window.ekey) { - endPos = pBlockInfo->rows - 1; - cur->mixBlock = (cur->pos != 0); - } else if (!ASCENDING_TRAVERSE(pTsdbReadHandle->order) && pTsdbReadHandle->window.ekey <= pBlockInfo->window.skey) { - endPos = 0; - cur->mixBlock = (cur->pos != pBlockInfo->rows - 1); + if (pTsdbReadHandle->outputCapacity >= pBlockInfo->rows) { + if (ascScan && pTsdbReadHandle->window.ekey >= pBlockInfo->window.ekey) { + endPos = pBlockInfo->rows - 1; + cur->mixBlock = (cur->pos != 0); + } else if ((!ascScan) && pTsdbReadHandle->window.ekey <= pBlockInfo->window.skey) { + endPos = 0; + cur->mixBlock = (cur->pos != pBlockInfo->rows - 1); + } else { + assert(pCols->numOfRows > 0); + endPos = doBinarySearchKey(pCols->cols[0].pData, pCols->numOfRows, pTsdbReadHandle->window.ekey, order); + cur->mixBlock = true; + } } else { - assert(pCols->numOfRows > 0); - endPos = doBinarySearchKey(pCols->cols[0].pData, pCols->numOfRows, pTsdbReadHandle->window.ekey, order); + if (ascScan && pTsdbReadHandle->window.ekey >= pBlockInfo->window.ekey) { + endPos = TMIN(cur->pos + pTsdbReadHandle->outputCapacity - 1, pBlockInfo->rows - 1); + } else if ((!ascScan) && pTsdbReadHandle->window.ekey <= pBlockInfo->window.skey) { + endPos = TMAX(cur->pos - pTsdbReadHandle->outputCapacity + 1, 0); + } else { + ASSERT(pCols->numOfRows > 0); + endPos = doBinarySearchKey(pCols->cols[0].pData, pCols->numOfRows, pTsdbReadHandle->window.ekey, order); + + // current data is more than the capacity + int32_t size = abs(cur->pos - endPos) + 1; + if (size > pTsdbReadHandle->outputCapacity) { + int32_t delta = size - pTsdbReadHandle->outputCapacity; + if (ascScan) { + endPos -= delta; + } else { + endPos += delta; + } + } + } cur->mixBlock = true; } @@ -1924,7 +1960,7 @@ static void doMergeTwoLevelData(STsdbReadHandle* pTsdbReadHandle, STableCheckInf SDataCols* pCols = pTsdbReadHandle->rhelper.pDCols[0]; assert(pCols->cols[0].type == TSDB_DATA_TYPE_TIMESTAMP && pCols->cols[0].colId == PRIMARYKEY_TIMESTAMP_COL_ID && cur->pos >= 0 && cur->pos < pBlock->numOfRows); - + // Even Multi-Version supported, the records with duplicated TSKEY would be merged inside of tsdbLoadData interface. TSKEY* tsArray = pCols->cols[0].pData; assert(pCols->numOfRows == pBlock->numOfRows && tsArray[0] == pBlock->keyFirst && tsArray[pBlock->numOfRows - 1] == pBlock->keyLast); @@ -1954,6 +1990,7 @@ static void doMergeTwoLevelData(STsdbReadHandle* pTsdbReadHandle, STableCheckInf int32_t pos = cur->pos; cur->win = TSWINDOW_INITIALIZER; + bool adjustPos = false; // no data in buffer, load data from file directly if (pCheckInfo->iiter == NULL && pCheckInfo->iter == NULL) { @@ -1975,6 +2012,13 @@ static void doMergeTwoLevelData(STsdbReadHandle* pTsdbReadHandle, STableCheckInf break; } + if (adjustPos) { + if (key == lastKeyAppend) { + pos -= step; + } + adjustPos = false; + } + if (((pos > endPos || tsArray[pos] > pTsdbReadHandle->window.ekey) && ascScan) || ((pos < endPos || tsArray[pos] < pTsdbReadHandle->window.ekey) && !ascScan)) { break; @@ -2035,8 +2079,14 @@ static void doMergeTwoLevelData(STsdbReadHandle* pTsdbReadHandle, STableCheckInf } #endif if (TD_SUPPORT_UPDATE(pCfg->update)) { + if (lastKeyAppend != key) { + if (lastKeyAppend != TSKEY_INITIAL_VAL) { + ++curRow; + } + lastKeyAppend = key; + } + // load data from file firstly numOfRows = doCopyRowsFromFileBlock(pTsdbReadHandle, pTsdbReadHandle->outputCapacity, curRow, pos, pos); - lastKeyAppend = key; if (rv1 != TD_ROW_SVER(row1)) { rv1 = TD_ROW_SVER(row1); @@ -2046,7 +2096,7 @@ static void doMergeTwoLevelData(STsdbReadHandle* pTsdbReadHandle, STableCheckInf } // still assign data into current row - mergeTwoRowFromMem(pTsdbReadHandle, pTsdbReadHandle->outputCapacity, &curRow, row1, row2, numOfCols, + numOfRows += mergeTwoRowFromMem(pTsdbReadHandle, pTsdbReadHandle->outputCapacity, &curRow, row1, row2, numOfCols, pCheckInfo->tableId, pSchema1, pSchema2, pCfg->update, &lastKeyAppend); if (cur->win.skey == TSKEY_INITIAL_VAL) { @@ -2058,10 +2108,11 @@ static void doMergeTwoLevelData(STsdbReadHandle* pTsdbReadHandle, STableCheckInf cur->mixBlock = true; moveToNextRowInMem(pCheckInfo); - ++curRow; pos += step; + adjustPos = true; } else { + // discard the memory record moveToNextRowInMem(pCheckInfo); } } else if ((key > tsArray[pos] && ascScan) || (key < tsArray[pos] && !ascScan)) { @@ -2369,7 +2420,7 @@ static int32_t createDataBlocksInfo(STsdbReadHandle* pTsdbReadHandle, int32_t nu static int32_t getFirstFileDataBlock(STsdbReadHandle* pTsdbReadHandle, bool* exists); -static int32_t getDataBlockRv(STsdbReadHandle* pTsdbReadHandle, STableBlockInfo* pNext, bool* exists) { +static int32_t getDataBlock(STsdbReadHandle* pTsdbReadHandle, STableBlockInfo* pNext, bool* exists) { int32_t step = ASCENDING_TRAVERSE(pTsdbReadHandle->order) ? 1 : -1; SQueryFilePos* cur = &pTsdbReadHandle->cur; @@ -2478,7 +2529,7 @@ static int32_t getFirstFileDataBlock(STsdbReadHandle* pTsdbReadHandle, bool* exi cur->fid = pTsdbReadHandle->pFileGroup->fid; STableBlockInfo* pBlockInfo = &pTsdbReadHandle->pDataBlockInfo[cur->slot]; - return getDataBlockRv(pTsdbReadHandle, pBlockInfo, exists); + return getDataBlock(pTsdbReadHandle, pBlockInfo, exists); } static bool isEndFileDataBlock(SQueryFilePos* cur, int32_t numOfBlocks, bool ascTrav) { @@ -2643,7 +2694,7 @@ static int32_t getDataBlocksInFiles(STsdbReadHandle* pTsdbReadHandle, bool* exis } else { moveToNextDataBlockInCurrentFile(pTsdbReadHandle); STableBlockInfo* pNext = &pTsdbReadHandle->pDataBlockInfo[cur->slot]; - return getDataBlockRv(pTsdbReadHandle, pNext, exists); + return getDataBlock(pTsdbReadHandle, pNext, exists); } } } @@ -2729,7 +2780,7 @@ static int tsdbReadRowsFromCache(STableCheckInfo* pCheckInfo, TSKEY maxKey, int win->ekey = key; if (rv != TD_ROW_SVER(row)) { - pSchema = metaGetTbTSchema(REPO_META(pTsdbReadHandle->pTsdb), pCheckInfo->tableId, 1); + pSchema = metaGetTbTSchema(REPO_META(pTsdbReadHandle->pTsdb), pCheckInfo->tableId, TD_ROW_SVER(row)); rv = TD_ROW_SVER(row); } numOfRows += mergeTwoRowFromMem(pTsdbReadHandle, maxRowsToRead, &curRows, row, NULL, numOfCols, pCheckInfo->tableId, diff --git a/source/dnode/vnode/src/tsdb/tsdbReadImpl.c b/source/dnode/vnode/src/tsdb/tsdbReadImpl.c index c1a1e7570ecdeba56c03f9b1b6fcb28894089610..f66037b16d76a79743d626010d32ce3820716e70 100644 --- a/source/dnode/vnode/src/tsdb/tsdbReadImpl.c +++ b/source/dnode/vnode/src/tsdb/tsdbReadImpl.c @@ -20,11 +20,11 @@ static void tsdbResetReadTable(SReadH *pReadh); static void tsdbResetReadFile(SReadH *pReadh); static int tsdbLoadBlockOffset(SReadH *pReadh, SBlock *pBlock); -static int tsdbLoadBlockDataImpl(SReadH *pReadh, SBlock *pBlock, SDataCols *pDataCols); +static int tsdbLoadBlockDataImpl(SReadH *pReadh, SBlock *pBlock, SDataCols *pDataCols, int8_t bitmapMode); static int tsdbCheckAndDecodeColumnData(SDataCol *pDataCol, void *content, int32_t len, int32_t bitmapLen, int8_t comp, int numOfRows, int numOfBitmaps, int maxPoints, char *buffer, int bufferSize); static int tsdbLoadBlockDataColsImpl(SReadH *pReadh, SBlock *pBlock, SDataCols *pDataCols, const int16_t *colIds, - int numOfColIds); + int numOfColIds, int8_t bitmapMode); static int tsdbLoadColData(SReadH *pReadh, SDFile *pDFile, SBlock *pBlock, SBlockCol *pBlockCol, SDataCol *pDataCol); int tsdbInitReadH(SReadH *pReadh, STsdb *pRepo) { @@ -252,6 +252,45 @@ static FORCE_INLINE void tsdbSwapDataCols(SDataCols *pDest, SDataCols *pSrc) { pSrc->cols = pCols; } +static void printTsdbLoadBlkData(SReadH *readh, SDataCols *pDCols, SBlock *pBlock, const char *tag, int32_t ln) { + printf("%s:%d:%" PRIi64 " ================\n", tag, ln, taosGetSelfPthreadId()); + if (pBlock) { + SDFile *pHeadf = TSDB_READ_HEAD_FILE(readh); + printf("%s:%d:%" PRIi64 ":%p:%d %s\n", tag, ln, taosGetSelfPthreadId(), pBlock, (int32_t)pBlock->len, + pHeadf->f.aname); + SDFile *pDFile = pBlock->last ? TSDB_READ_LAST_FILE(readh) : TSDB_READ_DATA_FILE(readh); + printf("%s:%d:%" PRIi64 ":%p:%d %s\n", tag, ln, taosGetSelfPthreadId(), pBlock, (int32_t)pBlock->len, + pDFile->f.aname); + } + SDataCol *pDCol = pDCols->cols + 0; + if (TSKEY_MIN == *(int64_t *)pDCol->pData) { + ASSERT(0); + } + + int rows = pDCols->numOfRows; + for (int r = 0; r < rows; ++r) { + if (pBlock) { + printf("%s:%d:%" PRIi64 ":%p:%d rows[%d][%d] ", tag, ln, taosGetSelfPthreadId(), pBlock, (int32_t)pBlock->len, + rows, r); + } else { + printf("%s:%d:%" PRIi64 ":%s rows[%d][%d] ", tag, ln, taosGetSelfPthreadId(), "=== merge === ", rows, r); + } + + int nDataCols = pDCols->numOfCols; + int j = 0; + SCellVal sVal = {0}; + while (j < nDataCols) { + SDataCol *pDataCol = pDCols->cols + j; + tdGetColDataOfRow(&sVal, pDataCol, r, pDCols->bitmapMode); + tdSCellValPrint(&sVal, pDataCol->type); + ++j; + } + printf("\n"); + } + + fflush(stdout); +} + int tsdbLoadBlockData(SReadH *pReadh, SBlock *pBlock, SBlockInfo *pBlkInfo) { ASSERT(pBlock->numOfSubBlocks > 0); STsdbCfg *pCfg = REPO_CFG(pReadh->pRepo); @@ -266,14 +305,23 @@ int tsdbLoadBlockData(SReadH *pReadh, SBlock *pBlock, SBlockInfo *pBlkInfo) { } } - if (tsdbLoadBlockDataImpl(pReadh, iBlock, pReadh->pDCols[0]) < 0) return -1; + if (tsdbLoadBlockDataImpl(pReadh, iBlock, pReadh->pDCols[0], TSDB_BITMODE_ONE_BIT) < 0) return -1; +#ifdef TD_DEBUG_PRINT_TSDB_LOAD_DCOLS + printTsdbLoadBlkData(pReadh, pReadh->pDCols[0], iBlock, __func__, __LINE__); +#endif for (int i = 1; i < pBlock->numOfSubBlocks; i++) { iBlock++; - if (tsdbLoadBlockDataImpl(pReadh, iBlock, pReadh->pDCols[1]) < 0) return -1; + if (tsdbLoadBlockDataImpl(pReadh, iBlock, pReadh->pDCols[1], TSDB_BITMODE_DEFAULT) < 0) return -1; +#ifdef TD_DEBUG_PRINT_TSDB_LOAD_DCOLS + printTsdbLoadBlkData(pReadh, pReadh->pDCols[1], iBlock, __func__, __LINE__); +#endif // TODO: use the real maxVersion to replace the UINT64_MAX to support Multi-Version if (tdMergeDataCols(pReadh->pDCols[0], pReadh->pDCols[1], pReadh->pDCols[1]->numOfRows, NULL, TD_SUPPORT_UPDATE(update), TD_VER_MAX) < 0) return -1; +#ifdef TD_DEBUG_PRINT_TSDB_LOAD_DCOLS + printTsdbLoadBlkData(pReadh, pReadh->pDCols[0], iBlock, " === MERGE === ", __LINE__); +#endif } // if ((pBlock->numOfSubBlocks == 1) && (iBlock->hasDupKey)) { // TODO: use this line if (pBlock->numOfSubBlocks == 1) { @@ -285,6 +333,9 @@ int tsdbLoadBlockData(SReadH *pReadh, SBlock *pBlock, SBlockInfo *pBlkInfo) { } tsdbSwapDataCols(pReadh->pDCols[0], pReadh->pDCols[1]); ASSERT(pReadh->pDCols[0]->bitmapMode != 0); +#ifdef TD_DEBUG_PRINT_TSDB_LOAD_DCOLS + printTsdbLoadBlkData(pReadh, pReadh->pDCols[0], iBlock, " === UPDATE FILTER === ", __LINE__); +#endif } ASSERT(pReadh->pDCols[0]->numOfRows <= pBlock->numOfRows); @@ -294,6 +345,53 @@ int tsdbLoadBlockData(SReadH *pReadh, SBlock *pBlock, SBlockInfo *pBlkInfo) { return 0; } +static void printTsdbLoadBlkDataCols(SReadH *readh, SDataCols *pDCols, SBlock *pBlock, const int16_t *colIds, + int numOfColsIds, const char *tag, int32_t ln) { + printf("%s:%d:%" PRIi64 " ================\n", tag, ln, taosGetSelfPthreadId()); + if (pBlock) { + SDFile *pHeadf = TSDB_READ_HEAD_FILE(readh); + printf("%s:%d:%" PRIi64 ":%p:%d %s\n", tag, ln, taosGetSelfPthreadId(), pBlock, (int32_t)pBlock->len, + pHeadf->f.aname); + SDFile *pDFile = pBlock->last ? TSDB_READ_LAST_FILE(readh) : TSDB_READ_DATA_FILE(readh); + printf("%s:%d:%" PRIi64 ":%p:%d %s\n", tag, ln, taosGetSelfPthreadId(), pBlock, (int32_t)pBlock->len, + pDFile->f.aname); + } + + int rows = pDCols->numOfRows; + for (int r = 0; r < rows; ++r) { + if (pBlock) { + printf("%s:%d:%" PRIi64 ":%p:%d rows[%d][%d] ", tag, ln, taosGetSelfPthreadId(), pBlock, (int32_t)pBlock->len, + rows, r); + } else { + printf("%s:%d:%" PRIi64 ":%s rows[%d][%d] ", tag, ln, taosGetSelfPthreadId(), "=== merge === ", rows, r); + } + + int nDataCols = pDCols->numOfCols; + int j = 0, k = 0; + SCellVal sVal = {0}; + while (j < nDataCols) { + if (k >= numOfColsIds) break; + SDataCol *pDataCol = pDCols->cols + j; + int16_t colId1 = pDataCol->colId; + int16_t colId2 = *(colIds + k); + if (colId1 < colId2) { + ++j; + } else if (colId1 > colId2) { + ++k; // colId2 not exists in SDataCols + printf("NotExists "); + } else { + tdGetColDataOfRow(&sVal, pDataCol, r, pDCols->bitmapMode); + tdSCellValPrint(&sVal, pDataCol->type); + ++j; + ++k; + } + } + printf("\n"); + } + + fflush(stdout); +} + // TODO: filter by Multi-Version int tsdbLoadBlockDataCols(SReadH *pReadh, SBlock *pBlock, SBlockInfo *pBlkInfo, const int16_t *colIds, int numOfColsIds, bool mergeBitmap) { @@ -309,14 +407,25 @@ int tsdbLoadBlockDataCols(SReadH *pReadh, SBlock *pBlock, SBlockInfo *pBlkInfo, } } - if (tsdbLoadBlockDataColsImpl(pReadh, iBlock, pReadh->pDCols[0], colIds, numOfColsIds) < 0) return -1; + if (tsdbLoadBlockDataColsImpl(pReadh, iBlock, pReadh->pDCols[0], colIds, numOfColsIds, TSDB_BITMODE_ONE_BIT) < 0) + return -1; +#ifdef TD_DEBUG_PRINT_TSDB_LOAD_DCOLS + printTsdbLoadBlkDataCols(pReadh, pReadh->pDCols[0], iBlock, colIds, numOfColsIds, __func__, __LINE__); +#endif for (int i = 1; i < pBlock->numOfSubBlocks; i++) { iBlock++; - if (tsdbLoadBlockDataColsImpl(pReadh, iBlock, pReadh->pDCols[1], colIds, numOfColsIds) < 0) return -1; + if (tsdbLoadBlockDataColsImpl(pReadh, iBlock, pReadh->pDCols[1], colIds, numOfColsIds, TSDB_BITMODE_DEFAULT) < 0) + return -1; +#ifdef TD_DEBUG_PRINT_TSDB_LOAD_DCOLS + printTsdbLoadBlkDataCols(pReadh, pReadh->pDCols[1], iBlock, colIds, numOfColsIds, __func__, __LINE__); +#endif // TODO: use the real maxVersion to replace the UINT64_MAX to support Multi-Version if (tdMergeDataCols(pReadh->pDCols[0], pReadh->pDCols[1], pReadh->pDCols[1]->numOfRows, NULL, TD_SUPPORT_UPDATE(update), TD_VER_MAX) < 0) return -1; +#ifdef TD_DEBUG_PRINT_TSDB_LOAD_DCOLS + printTsdbLoadBlkDataCols(pReadh, pReadh->pDCols[0], NULL, colIds, numOfColsIds, __func__, __LINE__); +#endif } // if ((pBlock->numOfSubBlocks == 1) && (iBlock->hasDupKey)) { // TODO: use this line if (pBlock->numOfSubBlocks == 1) { @@ -328,18 +437,23 @@ int tsdbLoadBlockDataCols(SReadH *pReadh, SBlock *pBlock, SBlockInfo *pBlkInfo, } tsdbSwapDataCols(pReadh->pDCols[0], pReadh->pDCols[1]); ASSERT(pReadh->pDCols[0]->bitmapMode != 0); +#ifdef TD_DEBUG_PRINT_TSDB_LOAD_DCOLS + printTsdbLoadBlkDataCols(pReadh, pReadh->pDCols[0], NULL, colIds, numOfColsIds, + " === update filter === ", __LINE__); +#endif } if (mergeBitmap && !tdDataColsIsBitmapI(pReadh->pDCols[0])) { for (int i = 0; i < numOfColsIds; ++i) { SDataCol *pDataCol = pReadh->pDCols[0]->cols + i; if (pDataCol->len > 0 && pDataCol->bitmap) { - ASSERT(pDataCol->colId != PRIMARYKEY_TIMESTAMP_COL_ID); - ASSERT(pDataCol->pBitmap); tdMergeBitmap(pDataCol->pBitmap, pReadh->pDCols[0]->numOfRows, pDataCol->pBitmap); tdDataColsSetBitmapI(pReadh->pDCols[0]); } } +#ifdef TD_DEBUG_PRINT_TSDB_LOAD_DCOLS + printTsdbLoadBlkDataCols(pReadh, pReadh->pDCols[0], NULL, colIds, numOfColsIds, " === merge bitmap === ", __LINE__); +#endif } ASSERT(pReadh->pDCols[0]->numOfRows <= pBlock->numOfRows); @@ -543,16 +657,14 @@ static void tsdbResetReadFile(SReadH *pReadh) { tsdbCloseDFileSet(TSDB_READ_FSET(pReadh)); } -static int tsdbLoadBlockDataImpl(SReadH *pReadh, SBlock *pBlock, SDataCols *pDataCols) { +static int tsdbLoadBlockDataImpl(SReadH *pReadh, SBlock *pBlock, SDataCols *pDataCols, int8_t bitmapMode) { ASSERT(pBlock->numOfSubBlocks == 0 || pBlock->numOfSubBlocks == 1); SDFile *pDFile = (pBlock->last) ? TSDB_READ_LAST_FILE(pReadh) : TSDB_READ_DATA_FILE(pReadh); tdResetDataCols(pDataCols); - if (tsdbIsSupBlock(pBlock)) { - tdDataColsSetBitmapI(pDataCols); - } + pDataCols->bitmapMode = bitmapMode; if (tsdbMakeRoom((void **)(&TSDB_READ_BUF(pReadh)), pBlock->len) < 0) return -1; @@ -730,7 +842,7 @@ static int tsdbCheckAndDecodeColumnData(SDataCol *pDataCol, void *content, int32 } static int tsdbLoadBlockDataColsImpl(SReadH *pReadh, SBlock *pBlock, SDataCols *pDataCols, const int16_t *colIds, - int numOfColIds) { + int numOfColIds, int8_t bitmapMode) { ASSERT(pBlock->numOfSubBlocks == 0 || pBlock->numOfSubBlocks == 1); ASSERT(colIds[0] == PRIMARYKEY_TIMESTAMP_COL_ID); @@ -739,9 +851,7 @@ static int tsdbLoadBlockDataColsImpl(SReadH *pReadh, SBlock *pBlock, SDataCols * tdResetDataCols(pDataCols); - if (tsdbIsSupBlock(pBlock)) { - tdDataColsSetBitmapI(pDataCols); - } + pDataCols->bitmapMode = bitmapMode; // If only load timestamp column, no need to load SBlockData part if (numOfColIds > 1 && tsdbLoadBlockOffset(pReadh, pBlock) < 0) return -1; diff --git a/source/dnode/vnode/src/tsdb/tsdbWrite.c b/source/dnode/vnode/src/tsdb/tsdbWrite.c index a67f413ba7f2016797cbcfcf90b0efe094171904..aab4da26a37119c6e0044849b1494ede2f33e552 100644 --- a/source/dnode/vnode/src/tsdb/tsdbWrite.c +++ b/source/dnode/vnode/src/tsdb/tsdbWrite.c @@ -85,7 +85,7 @@ static FORCE_INLINE int tsdbCheckRowRange(STsdb *pTsdb, tb_uid_t uid, STSRow *ro return 0; } -int tsdbScanAndConvertSubmitMsg(STsdb *pTsdb, const SSubmitReq *pMsg) { +int tsdbScanAndConvertSubmitMsg(STsdb *pTsdb, SSubmitReq *pMsg) { ASSERT(pMsg != NULL); // STsdbMeta * pMeta = pTsdb->tsdbMeta; SSubmitMsgIter msgIter = {0}; @@ -150,7 +150,6 @@ int tsdbScanAndConvertSubmitMsg(STsdb *pTsdb, const SSubmitReq *pMsg) { return -1; } } - } if (terrno != TSDB_CODE_SUCCESS) return -1; diff --git a/source/dnode/vnode/src/vnd/vnodeOpen.c b/source/dnode/vnode/src/vnd/vnodeOpen.c index 9412c39b27e4870d059dc78e2d5b49eddf256b5b..a90bb7afcb6847ba0cb803d7a7e58720159bf10f 100644 --- a/source/dnode/vnode/src/vnd/vnodeOpen.c +++ b/source/dnode/vnode/src/vnd/vnodeOpen.c @@ -77,7 +77,8 @@ SVnode *vnodeOpen(const char *path, STfs *pTfs, SMsgCb msgCb) { pVnode->path = (char *)&pVnode[1]; strcpy(pVnode->path, path); pVnode->config = info.config; - pVnode->state = info.state; + pVnode->state.committed = info.state.committed; + pVnode->state.applied = info.state.committed; pVnode->pTfs = pTfs; pVnode->msgCb = msgCb; @@ -180,8 +181,6 @@ void vnodeClose(SVnode *pVnode) { // start the sync timer after the queue is ready int32_t vnodeStart(SVnode *pVnode) { - vnodeSyncSetQ(pVnode, NULL); - vnodeSyncSetRpc(pVnode, NULL); vnodeSyncStart(pVnode); return 0; } diff --git a/source/dnode/vnode/src/vnd/vnodeSvr.c b/source/dnode/vnode/src/vnd/vnodeSvr.c index 87a7d5e3d4475d0dd26a1bc4063c5300676210a7..5e50a1b79627d02c51da5f040d9559455aaaca79 100644 --- a/source/dnode/vnode/src/vnd/vnodeSvr.c +++ b/source/dnode/vnode/src/vnd/vnodeSvr.c @@ -24,26 +24,66 @@ static int vnodeProcessDropTbReq(SVnode *pVnode, int64_t version, void *pReq, in static int vnodeProcessSubmitReq(SVnode *pVnode, int64_t version, void *pReq, int32_t len, SRpcMsg *pRsp); static int vnodeProcessCreateTSmaReq(SVnode *pVnode, int64_t version, void *pReq, int len, SRpcMsg *pRsp); -int vnodePreprocessWriteReqs(SVnode *pVnode, SArray *pMsgs, int64_t *version) { -#if 0 - SRpcMsg *pMsg; - SRpcMsg *pRpc; - - *version = pVnode->state.processed; - for (int i = 0; i < taosArrayGetSize(pMsgs); i++) { - pMsg = *(SRpcMsg **)taosArrayGet(pMsgs, i); - pRpc = pMsg; - - // set request version - if (walWrite(pVnode->pWal, pVnode->state.processed++, pRpc->msgType, pRpc->pCont, pRpc->contLen) < 0) { - vError("vnode:%d write wal error since %s", TD_VID(pVnode), terrstr()); - return -1; - } - } +int32_t vnodePreprocessReq(SVnode *pVnode, SRpcMsg *pMsg) { + SDecoder dc = {0}; - walFsync(pVnode->pWal, false); + switch (pMsg->msgType) { + case TDMT_VND_CREATE_TABLE: { + int64_t ctime = taosGetTimestampMs(); + int32_t nReqs; + + tDecoderInit(&dc, (uint8_t *)pMsg->pCont + sizeof(SMsgHead), pMsg->contLen - sizeof(SMsgHead)); + tStartDecode(&dc); + + tDecodeI32v(&dc, &nReqs); + for (int32_t iReq = 0; iReq < nReqs; iReq++) { + tb_uid_t uid = tGenIdPI64(); + tStartDecode(&dc); + + tDecodeI32v(&dc, NULL); + *(int64_t *)(dc.data + dc.pos) = uid; + *(int64_t *)(dc.data + dc.pos + 8) = ctime; + + tEndDecode(&dc); + } + + tEndDecode(&dc); + tDecoderClear(&dc); + } break; + case TDMT_VND_SUBMIT: { + SSubmitMsgIter msgIter = {0}; + SSubmitReq *pSubmitReq = (SSubmitReq *)pMsg->pCont; + SSubmitBlk *pBlock = NULL; + int64_t ctime = taosGetTimestampMs(); + tb_uid_t uid; + + tInitSubmitMsgIter(pSubmitReq, &msgIter); + + for (;;) { + tGetSubmitMsgNext(&msgIter, &pBlock); + if (pBlock == NULL) break; + + if (msgIter.schemaLen > 0) { + uid = tGenIdPI64(); + + tDecoderInit(&dc, pBlock->data, msgIter.schemaLen); + tStartDecode(&dc); + + tDecodeI32v(&dc, NULL); + *(int64_t *)(dc.data + dc.pos) = uid; + *(int64_t *)(dc.data + dc.pos + 8) = ctime; + pBlock->uid = htobe64(uid); + + tEndDecode(&dc); + tDecoderClear(&dc); + } + } + + } break; + default: + break; + } -#endif return 0; } @@ -106,11 +146,6 @@ int vnodeProcessWriteReq(SVnode *pVnode, SRpcMsg *pMsg, int64_t version, SRpcMsg pMsg->contLen - sizeof(SMsgHead)) < 0) { } } break; - case TDMT_VND_TASK_WRITE_EXEC: { - if (tqProcessTaskExec(pVnode->pTq, POINTER_SHIFT(pMsg->pCont, sizeof(SMsgHead)), pMsg->contLen - sizeof(SMsgHead), - 0) < 0) { - } - } break; case TDMT_VND_ALTER_VNODE: break; default: @@ -145,9 +180,6 @@ _err: int vnodeProcessQueryMsg(SVnode *pVnode, SRpcMsg *pMsg) { vTrace("message in vnode query queue is processing"); -#if 0 - SReadHandle handle = {.reader = pVnode->pTsdb, .meta = pVnode->pMeta, .config = &pVnode->config, .vnode = pVnode, .pMsgCb = &pVnode->msgCb}; -#endif SReadHandle handle = {.meta = pVnode->pMeta, .config = &pVnode->config, .vnode = pVnode, .pMsgCb = &pVnode->msgCb}; switch (pMsg->msgType) { case TDMT_VND_QUERY: @@ -181,11 +213,21 @@ int vnodeProcessFetchMsg(SVnode *pVnode, SRpcMsg *pMsg, SQueueInfo *pInfo) { return vnodeGetTableMeta(pVnode, pMsg); case TDMT_VND_CONSUME: return tqProcessPollReq(pVnode->pTq, pMsg, pInfo->workerId); - case TDMT_VND_TASK_PIPE_EXEC: - case TDMT_VND_TASK_MERGE_EXEC: - return tqProcessTaskExec(pVnode->pTq, msgstr, msgLen, 0); - case TDMT_VND_STREAM_TRIGGER: - return tqProcessStreamTrigger(pVnode->pTq, pMsg->pCont, pMsg->contLen, 0); + + case TDMT_VND_TASK_RUN: { + int32_t code = tqProcessTaskRunReq(pVnode->pTq, pMsg); + pMsg->pCont = NULL; + return code; + } + case TDMT_VND_TASK_DISPATCH: + return tqProcessTaskDispatchReq(pVnode->pTq, pMsg); + case TDMT_VND_TASK_RECOVER: + return tqProcessTaskRecoverReq(pVnode->pTq, pMsg); + case TDMT_VND_TASK_DISPATCH_RSP: + return tqProcessTaskDispatchRsp(pVnode->pTq, pMsg); + case TDMT_VND_TASK_RECOVER_RSP: + return tqProcessTaskRecoverRsp(pVnode->pTq, pMsg); + case TDMT_VND_QUERY_HEARTBEAT: return qWorkerProcessHbMsg(pVnode, pVnode->pQuery, pMsg); default: @@ -655,7 +697,7 @@ static int vnodeProcessSubmitReq(SVnode *pVnode, int64_t version, void *pReq, in goto _exit; } - for (int i = 0;;) { + for (;;) { tGetSubmitMsgNext(&msgIter, &pBlock); if (pBlock == NULL) break; @@ -682,7 +724,7 @@ static int vnodeProcessSubmitReq(SVnode *pVnode, int64_t version, void *pReq, in submitBlkRsp.uid = createTbReq.uid; submitBlkRsp.tblFName = taosMemoryMalloc(strlen(pVnode->config.dbname) + strlen(createTbReq.name) + 2); - sprintf(submitBlkRsp.tblFName, "%s.%s", pVnode->config.dbname, createTbReq.name); + sprintf(submitBlkRsp.tblFName, "%s.", pVnode->config.dbname); msgIter.uid = createTbReq.uid; if (createTbReq.type == TSDB_CHILD_TABLE) { diff --git a/source/dnode/vnode/src/vnd/vnodeSync.c b/source/dnode/vnode/src/vnd/vnodeSync.c index a93844c5ffeb46db1319cc4dda7b7ea762c59151..8659c418070cdccf4dc9c3164d36f5548199f030 100644 --- a/source/dnode/vnode/src/vnd/vnodeSync.c +++ b/source/dnode/vnode/src/vnd/vnodeSync.c @@ -13,90 +13,62 @@ * along with this program. If not, see . */ +#define _DEFAULT_SOURCE #include "vnd.h" +static int32_t vnodeSyncEqMsg(const SMsgCb *msgcb, SRpcMsg *pMsg); +static int32_t vnodeSyncSendMsg(const SEpSet *pEpSet, SRpcMsg *pMsg); +static SSyncFSM *vnodeSyncMakeFsm(SVnode *pVnode); +static void vnodeSyncCommitMsg(SSyncFSM *pFsm, const SRpcMsg *pMsg, SFsmCbMeta cbMeta); +static void vnodeSyncPreCommitMsg(SSyncFSM *pFsm, const SRpcMsg *pMsg, SFsmCbMeta cbMeta); +static void vnodeSyncRollBackMsg(SSyncFSM *pFsm, const SRpcMsg *pMsg, SFsmCbMeta cbMeta); +static int32_t vnodeSyncGetSnapshot(SSyncFSM *pFsm, SSnapshot *pSnapshot); + int32_t vnodeSyncOpen(SVnode *pVnode, char *path) { - SSyncInfo syncInfo; - syncInfo.vgId = pVnode->config.vgId; - SSyncCfg *pCfg = &(syncInfo.syncCfg); - pCfg->replicaNum = pVnode->config.syncCfg.replicaNum; - pCfg->myIndex = pVnode->config.syncCfg.myIndex; - memcpy(pCfg->nodeInfo, pVnode->config.syncCfg.nodeInfo, sizeof(pCfg->nodeInfo)); - - snprintf(syncInfo.path, sizeof(syncInfo.path), "%s/sync", path); - syncInfo.pWal = pVnode->pWal; - - syncInfo.pFsm = syncVnodeMakeFsm(pVnode); - syncInfo.rpcClient = NULL; - syncInfo.FpSendMsg = vnodeSendMsg; - syncInfo.queue = NULL; - syncInfo.FpEqMsg = vnodeSyncEqMsg; + SSyncInfo syncInfo = { + .vgId = pVnode->config.vgId, + .syncCfg = pVnode->config.syncCfg, + .pWal = pVnode->pWal, + .msgcb = NULL, + .FpSendMsg = vnodeSyncSendMsg, + .FpEqMsg = vnodeSyncEqMsg, + }; + + snprintf(syncInfo.path, sizeof(syncInfo.path), "%s%ssync", path, TD_DIRSEP); + syncInfo.pFsm = vnodeSyncMakeFsm(pVnode); pVnode->sync = syncOpen(&syncInfo); - assert(pVnode->sync > 0); + if (pVnode->sync <= 0) { + vError("vgId:%d, failed to open sync since %s", pVnode->config.vgId, terrstr()); + return -1; + } - // for test setPingTimerMS(pVnode->sync, 3000); setElectTimerMS(pVnode->sync, 500); setHeartbeatTimerMS(pVnode->sync, 100); - return 0; } -int32_t vnodeSyncStart(SVnode *pVnode) { +void vnodeSyncStart(SVnode *pVnode) { + syncSetMsgCb(pVnode->sync, &pVnode->msgCb); syncStart(pVnode->sync); - return 0; -} - -void vnodeSyncClose(SVnode *pVnode) { - // stop by ref id - syncStop(pVnode->sync); -} - -void vnodeSyncSetQ(SVnode *pVnode, void *qHandle) { syncSetQ(pVnode->sync, (void *)(&(pVnode->msgCb))); } - -void vnodeSyncSetRpc(SVnode *pVnode, void *rpcHandle) { syncSetRpc(pVnode->sync, (void *)(&(pVnode->msgCb))); } - -int32_t vnodeSyncEqMsg(void *qHandle, SRpcMsg *pMsg) { - int32_t ret = 0; - SMsgCb *pMsgCb = qHandle; - if (pMsgCb->queueFps[SYNC_QUEUE] != NULL) { - tmsgPutToQueue(qHandle, SYNC_QUEUE, pMsg); - } else { - vError("vnodeSyncEqMsg queue is NULL, SYNC_QUEUE:%d", SYNC_QUEUE); - } - return ret; } -int32_t vnodeSendMsg(void *rpcHandle, const SEpSet *pEpSet, SRpcMsg *pMsg) { - int32_t ret = 0; - SMsgCb *pMsgCb = rpcHandle; - if (pMsgCb->queueFps[SYNC_QUEUE] != NULL) { - pMsg->info.noResp = 1; - tmsgSendReq(pEpSet, pMsg); - } else { - vError("vnodeSendMsg queue is NULL, SYNC_QUEUE:%d", SYNC_QUEUE); - } - return ret; -} +void vnodeSyncClose(SVnode *pVnode) { syncStop(pVnode->sync); } -int32_t vnodeSyncGetSnapshotCb(struct SSyncFSM *pFsm, SSnapshot *pSnapshot) { - SVnode *pVnode = (SVnode *)(pFsm->data); - vnodeGetSnapshot(pVnode, pSnapshot); +int32_t vnodeSyncEqMsg(const SMsgCb *msgcb, SRpcMsg *pMsg) { return tmsgPutToQueue(msgcb, SYNC_QUEUE, pMsg); } - /* - pSnapshot->data = NULL; - pSnapshot->lastApplyIndex = 0; - pSnapshot->lastApplyTerm = 0; - */ +int32_t vnodeSyncSendMsg(const SEpSet *pEpSet, SRpcMsg *pMsg) { return tmsgSendReq(pEpSet, pMsg); } +int32_t vnodeSyncGetSnapshot(SSyncFSM *pFsm, SSnapshot *pSnapshot) { + vnodeGetSnapshot(pFsm->data, pSnapshot); return 0; } -void vnodeSyncCommitCb(struct SSyncFSM *pFsm, const SRpcMsg *pMsg, SFsmCbMeta cbMeta) { +void vnodeSyncCommitMsg(SSyncFSM *pFsm, const SRpcMsg *pMsg, SFsmCbMeta cbMeta) { SyncIndex beginIndex = SYNC_INDEX_INVALID; if (pFsm->FpGetSnapshot != NULL) { - SSnapshot snapshot; + SSnapshot snapshot = {0}; pFsm->FpGetSnapshot(pFsm, &snapshot); beginIndex = snapshot.lastApplyIndex; } @@ -147,7 +119,7 @@ void vnodeSyncCommitCb(struct SSyncFSM *pFsm, const SRpcMsg *pMsg, SFsmCbMeta cb } } -void vnodeSyncPreCommitCb(struct SSyncFSM *pFsm, const SRpcMsg *pMsg, SFsmCbMeta cbMeta) { +void vnodeSyncPreCommitMsg(SSyncFSM *pFsm, const SRpcMsg *pMsg, SFsmCbMeta cbMeta) { char logBuf[256]; snprintf(logBuf, sizeof(logBuf), "==callback== ==PreCommitCb== pFsm:%p, index:%ld, isWeak:%d, code:%d, state:%d %s \n", pFsm, cbMeta.index, @@ -155,19 +127,19 @@ void vnodeSyncPreCommitCb(struct SSyncFSM *pFsm, const SRpcMsg *pMsg, SFsmCbMeta syncRpcMsgLog2(logBuf, (SRpcMsg *)pMsg); } -void vnodeSyncRollBackCb(struct SSyncFSM *pFsm, const SRpcMsg *pMsg, SFsmCbMeta cbMeta) { +void vnodeSyncRollBackMsg(SSyncFSM *pFsm, const SRpcMsg *pMsg, SFsmCbMeta cbMeta) { char logBuf[256]; snprintf(logBuf, sizeof(logBuf), "==callback== ==RollBackCb== pFsm:%p, index:%ld, isWeak:%d, code:%d, state:%d %s \n", pFsm, cbMeta.index, cbMeta.isWeak, cbMeta.code, cbMeta.state, syncUtilState2String(cbMeta.state)); syncRpcMsgLog2(logBuf, (SRpcMsg *)pMsg); } -SSyncFSM *syncVnodeMakeFsm(SVnode *pVnode) { - SSyncFSM *pFsm = (SSyncFSM *)taosMemoryMalloc(sizeof(SSyncFSM)); +SSyncFSM *vnodeSyncMakeFsm(SVnode *pVnode) { + SSyncFSM *pFsm = taosMemoryCalloc(1, sizeof(SSyncFSM)); pFsm->data = pVnode; - pFsm->FpCommitCb = vnodeSyncCommitCb; - pFsm->FpPreCommitCb = vnodeSyncPreCommitCb; - pFsm->FpRollBackCb = vnodeSyncRollBackCb; - pFsm->FpGetSnapshot = vnodeSyncGetSnapshotCb; + pFsm->FpCommitCb = vnodeSyncCommitMsg; + pFsm->FpPreCommitCb = vnodeSyncPreCommitMsg; + pFsm->FpRollBackCb = vnodeSyncRollBackMsg; + pFsm->FpGetSnapshot = vnodeSyncGetSnapshot; return pFsm; -} +} \ No newline at end of file diff --git a/source/dnode/vnode/test/tqMetaTest.cpp b/source/dnode/vnode/test/tqMetaTest.cpp deleted file mode 100644 index 627dbc6f18122e39cde2e243fd564ad622324cd2..0000000000000000000000000000000000000000 --- a/source/dnode/vnode/test/tqMetaTest.cpp +++ /dev/null @@ -1,279 +0,0 @@ -#include -#include -#include -#include - -#include "tqMetaStore.h" - -struct Foo { - int32_t a; -}; - -int FooSerializer(const void* pObj, STqSerializedHead** ppHead) { - Foo* foo = (Foo*)pObj; - if ((*ppHead) == NULL || (*ppHead)->ssize < sizeof(STqSerializedHead) + sizeof(int32_t)) { - *ppHead = (STqSerializedHead*)taosMemoryRealloc(*ppHead, sizeof(STqSerializedHead) + sizeof(int32_t)); - (*ppHead)->ssize = sizeof(STqSerializedHead) + sizeof(int32_t); - } - *(int32_t*)(*ppHead)->content = foo->a; - return (*ppHead)->ssize; -} - -const void* FooDeserializer(const STqSerializedHead* pHead, void** ppObj) { - if (*ppObj == NULL) { - *ppObj = taosMemoryRealloc(*ppObj, sizeof(int32_t)); - } - Foo* pFoo = *(Foo**)ppObj; - pFoo->a = *(int32_t*)pHead->content; - return NULL; -} - -void FooDeleter(void* pObj) { taosMemoryFree(pObj); } - -class TqMetaUpdateAppendTest : public ::testing::Test { - protected: - void SetUp() override { - taosRemoveDir(pathName); - pMeta = tqStoreOpen(pathName, FooSerializer, FooDeserializer, FooDeleter, TQ_UPDATE_APPEND); - ASSERT(pMeta); - } - - void TearDown() override { tqStoreClose(pMeta); } - - STqMetaStore* pMeta; - const char* pathName = "/tmp/tq_test"; -}; - -TEST_F(TqMetaUpdateAppendTest, copyPutTest) { - Foo foo; - foo.a = 3; - tqHandleCopyPut(pMeta, 1, &foo, sizeof(Foo)); - - Foo* pFoo = (Foo*)tqHandleGet(pMeta, 1); - EXPECT_EQ(pFoo == NULL, true); - - tqHandleCommit(pMeta, 1); - pFoo = (Foo*)tqHandleGet(pMeta, 1); - EXPECT_EQ(pFoo->a, 3); -} - -TEST_F(TqMetaUpdateAppendTest, persistTest) { - Foo* pFoo = (Foo*)taosMemoryMalloc(sizeof(Foo)); - pFoo->a = 2; - tqHandleMovePut(pMeta, 1, pFoo); - Foo* pBar = (Foo*)tqHandleGet(pMeta, 1); - EXPECT_EQ(pBar == NULL, true); - tqHandleCommit(pMeta, 1); - pBar = (Foo*)tqHandleGet(pMeta, 1); - EXPECT_EQ(pBar->a, pFoo->a); - pBar = (Foo*)tqHandleGet(pMeta, 2); - EXPECT_EQ(pBar == NULL, true); - - tqStoreClose(pMeta); - pMeta = tqStoreOpen(pathName, FooSerializer, FooDeserializer, FooDeleter, TQ_UPDATE_APPEND); - ASSERT(pMeta); - - pBar = (Foo*)tqHandleGet(pMeta, 1); - ASSERT_EQ(pBar != NULL, true); - EXPECT_EQ(pBar->a, 2); - - pBar = (Foo*)tqHandleGet(pMeta, 2); - EXPECT_EQ(pBar == NULL, true); -} - -TEST_F(TqMetaUpdateAppendTest, uncommittedTest) { - Foo* pFoo = (Foo*)taosMemoryMalloc(sizeof(Foo)); - pFoo->a = 3; - tqHandleMovePut(pMeta, 1, pFoo); - - pFoo = (Foo*)tqHandleGet(pMeta, 1); - EXPECT_EQ(pFoo == NULL, true); -} - -TEST_F(TqMetaUpdateAppendTest, abortTest) { - Foo* pFoo = (Foo*)taosMemoryMalloc(sizeof(Foo)); - pFoo->a = 3; - tqHandleMovePut(pMeta, 1, pFoo); - - pFoo = (Foo*)tqHandleGet(pMeta, 1); - EXPECT_EQ(pFoo == NULL, true); - - tqHandleAbort(pMeta, 1); - pFoo = (Foo*)tqHandleGet(pMeta, 1); - EXPECT_EQ(pFoo == NULL, true); -} - -TEST_F(TqMetaUpdateAppendTest, deleteTest) { - Foo* pFoo = (Foo*)taosMemoryMalloc(sizeof(Foo)); - pFoo->a = 3; - tqHandleMovePut(pMeta, 1, pFoo); - - pFoo = (Foo*)tqHandleGet(pMeta, 1); - EXPECT_EQ(pFoo == NULL, true); - - tqHandleCommit(pMeta, 1); - - pFoo = (Foo*)tqHandleGet(pMeta, 1); - ASSERT_EQ(pFoo != NULL, true); - EXPECT_EQ(pFoo->a, 3); - - tqHandleDel(pMeta, 1); - pFoo = (Foo*)tqHandleGet(pMeta, 1); - ASSERT_EQ(pFoo != NULL, true); - EXPECT_EQ(pFoo->a, 3); - - tqHandleCommit(pMeta, 1); - pFoo = (Foo*)tqHandleGet(pMeta, 1); - EXPECT_EQ(pFoo == NULL, true); - - tqStoreClose(pMeta); - pMeta = tqStoreOpen(pathName, FooSerializer, FooDeserializer, FooDeleter, TQ_UPDATE_APPEND); - ASSERT(pMeta); - - pFoo = (Foo*)tqHandleGet(pMeta, 1); - EXPECT_EQ(pFoo == NULL, true); -} - -TEST_F(TqMetaUpdateAppendTest, intxnPersist) { - Foo* pFoo = (Foo*)taosMemoryMalloc(sizeof(Foo)); - pFoo->a = 3; - tqHandleMovePut(pMeta, 1, pFoo); - tqHandleCommit(pMeta, 1); - - Foo* pBar = (Foo*)taosMemoryMalloc(sizeof(Foo)); - pBar->a = 4; - tqHandleMovePut(pMeta, 1, pBar); - - Foo* pFoo1 = (Foo*)tqHandleGet(pMeta, 1); - EXPECT_EQ(pFoo1->a, 3); - - tqStoreClose(pMeta); - pMeta = tqStoreOpen(pathName, FooSerializer, FooDeserializer, FooDeleter, TQ_UPDATE_APPEND); - ASSERT(pMeta); - - pFoo1 = (Foo*)tqHandleGet(pMeta, 1); - EXPECT_EQ(pFoo1->a, 3); - - tqHandleCommit(pMeta, 1); - - pFoo1 = (Foo*)tqHandleGet(pMeta, 1); - EXPECT_EQ(pFoo1->a, 4); - - tqStoreClose(pMeta); - pMeta = tqStoreOpen(pathName, FooSerializer, FooDeserializer, FooDeleter, TQ_UPDATE_APPEND); - ASSERT(pMeta); - - pFoo1 = (Foo*)tqHandleGet(pMeta, 1); - EXPECT_EQ(pFoo1->a, 4); -} - -TEST_F(TqMetaUpdateAppendTest, multiplePage) { - taosSeedRand(0); - std::vector v; - for (int i = 0; i < 1000; i++) { - v.push_back(taosRand()); - Foo foo; - foo.a = v[i]; - tqHandleCopyPut(pMeta, i, &foo, sizeof(Foo)); - } - for (int i = 0; i < 500; i++) { - tqHandleCommit(pMeta, i); - Foo* pFoo = (Foo*)tqHandleGet(pMeta, i); - ASSERT_EQ(pFoo != NULL, true) << " at idx " << i << "\n"; - EXPECT_EQ(pFoo->a, v[i]); - } - - tqStoreClose(pMeta); - pMeta = tqStoreOpen(pathName, FooSerializer, FooDeserializer, FooDeleter, TQ_UPDATE_APPEND); - ASSERT(pMeta); - - for (int i = 500; i < 1000; i++) { - tqHandleCommit(pMeta, i); - Foo* pFoo = (Foo*)tqHandleGet(pMeta, i); - ASSERT_EQ(pFoo != NULL, true) << " at idx " << i << "\n"; - EXPECT_EQ(pFoo->a, v[i]); - } - - for (int i = 0; i < 1000; i++) { - Foo* pFoo = (Foo*)tqHandleGet(pMeta, i); - ASSERT_EQ(pFoo != NULL, true) << " at idx " << i << "\n"; - EXPECT_EQ(pFoo->a, v[i]); - } -} - -TEST_F(TqMetaUpdateAppendTest, multipleRewrite) { - taosSeedRand(0); - std::vector v; - for (int i = 0; i < 1000; i++) { - v.push_back(taosRand()); - Foo foo; - foo.a = v[i]; - tqHandleCopyPut(pMeta, i, &foo, sizeof(Foo)); - } - - for (int i = 0; i < 500; i++) { - tqHandleCommit(pMeta, i); - v[i] = taosRand(); - Foo foo; - foo.a = v[i]; - tqHandleCopyPut(pMeta, i, &foo, sizeof(Foo)); - } - - for (int i = 500; i < 1000; i++) { - v[i] = taosRand(); - Foo foo; - foo.a = v[i]; - tqHandleCopyPut(pMeta, i, &foo, sizeof(Foo)); - } - - for (int i = 0; i < 1000; i++) { - tqHandleCommit(pMeta, i); - } - - tqStoreClose(pMeta); - pMeta = tqStoreOpen(pathName, FooSerializer, FooDeserializer, FooDeleter, TQ_UPDATE_APPEND); - ASSERT(pMeta); - - for (int i = 500; i < 1000; i++) { - v[i] = taosRand(); - Foo foo; - foo.a = v[i]; - tqHandleCopyPut(pMeta, i, &foo, sizeof(Foo)); - tqHandleCommit(pMeta, i); - } - - for (int i = 0; i < 1000; i++) { - Foo* pFoo = (Foo*)tqHandleGet(pMeta, i); - ASSERT_EQ(pFoo != NULL, true) << " at idx " << i << "\n"; - EXPECT_EQ(pFoo->a, v[i]); - } -} - -TEST_F(TqMetaUpdateAppendTest, dupCommit) { - taosSeedRand(0); - std::vector v; - for (int i = 0; i < 1000; i++) { - v.push_back(taosRand()); - Foo foo; - foo.a = v[i]; - tqHandleCopyPut(pMeta, i, &foo, sizeof(Foo)); - } - - for (int i = 0; i < 1000; i++) { - int ret = tqHandleCommit(pMeta, i); - EXPECT_EQ(ret, 0); - ret = tqHandleCommit(pMeta, i); - EXPECT_EQ(ret, -1); - } - - for (int i = 0; i < 1000; i++) { - int ret = tqHandleCommit(pMeta, i); - EXPECT_EQ(ret, -1); - } - - for (int i = 0; i < 1000; i++) { - Foo* pFoo = (Foo*)tqHandleGet(pMeta, i); - ASSERT_EQ(pFoo != NULL, true) << " at idx " << i << "\n"; - EXPECT_EQ(pFoo->a, v[i]); - } -} diff --git a/source/libs/catalog/inc/catalogInt.h b/source/libs/catalog/inc/catalogInt.h index 857c7088523a919671c05c15eef75debdbffc0de..9f66b6c598de2dcb3c1c32190760c74719ccb414 100644 --- a/source/libs/catalog/inc/catalogInt.h +++ b/source/libs/catalog/inc/catalogInt.h @@ -58,6 +58,17 @@ enum { CTG_ACT_MAX }; +typedef enum { + CTG_TASK_GET_QNODE = 0, + CTG_TASK_GET_DB_VGROUP, + CTG_TASK_GET_DB_CFG, + CTG_TASK_GET_TB_META, + CTG_TASK_GET_TB_HASH, + CTG_TASK_GET_INDEX, + CTG_TASK_GET_UDF, + CTG_TASK_GET_USER, +} CTG_TASK_TYPE; + typedef struct SCtgDebug { bool lockEnable; bool cacheEnable; @@ -66,6 +77,43 @@ typedef struct SCtgDebug { uint32_t showCachePeriodSec; } SCtgDebug; +typedef struct SCtgTbCacheInfo { + bool inCache; + uint64_t dbId; + uint64_t suid; + int32_t tbType; +} SCtgTbCacheInfo; + +typedef struct SCtgTbMetaCtx { + SCtgTbCacheInfo tbInfo; + SName* pName; + int32_t flag; +} SCtgTbMetaCtx; + +typedef struct SCtgDbVgCtx { + char dbFName[TSDB_DB_FNAME_LEN]; +} SCtgDbVgCtx; + +typedef struct SCtgDbCfgCtx { + char dbFName[TSDB_DB_FNAME_LEN]; +} SCtgDbCfgCtx; + +typedef struct SCtgTbHashCtx { + char dbFName[TSDB_DB_FNAME_LEN]; + SName* pName; +} SCtgTbHashCtx; + +typedef struct SCtgIndexCtx { + char indexFName[TSDB_INDEX_FNAME_LEN]; +} SCtgIndexCtx; + +typedef struct SCtgUdfCtx { + char udfName[TSDB_FUNC_NAME_LEN]; +} SCtgUdfCtx; + +typedef struct SCtgUserCtx { + SUserAuthInfo user; +} SCtgUserCtx; typedef struct SCtgTbMetaCache { SRWLatch stbLock; @@ -113,6 +161,55 @@ typedef struct SCatalog { SCtgRentMgmt stbRent; } SCatalog; +typedef struct SCtgJob { + int64_t refId; + SArray* pTasks; + int32_t taskDone; + SMetaData jobRes; + int32_t rspCode; + + uint64_t queryId; + SCatalog* pCtg; + void* pTrans; + const SEpSet* pMgmtEps; + void* userParam; + catalogCallback userFp; + int32_t tbMetaNum; + int32_t tbHashNum; + int32_t dbVgNum; + int32_t udfNum; + int32_t qnodeNum; + int32_t dbCfgNum; + int32_t indexNum; + int32_t userNum; +} SCtgJob; + +typedef struct SCtgMsgCtx { + int32_t reqType; + void* lastOut; + void* out; + char* target; +} SCtgMsgCtx; + +typedef struct SCtgTask { + CTG_TASK_TYPE type; + int32_t taskId; + SCtgJob *pJob; + void* taskCtx; + SCtgMsgCtx msgCtx; + void* res; +} SCtgTask; + +typedef int32_t (*ctgLanchTaskFp)(SCtgTask*); +typedef int32_t (*ctgHandleTaskMsgRspFp)(SCtgTask*, int32_t, const SDataBuf *, int32_t); +typedef int32_t (*ctgDumpTaskResFp)(SCtgTask*); + +typedef struct SCtgAsyncFps { + ctgLanchTaskFp launchFp; + ctgHandleTaskMsgRspFp handleRspFp; + ctgDumpTaskResFp dumpResFp; +} SCtgAsyncFps; + typedef struct SCtgApiStat { #ifdef WINDOWS @@ -214,6 +311,7 @@ typedef struct SCtgQueue { typedef struct SCatalogMgmt { bool exit; + int32_t jobPool; SRWLatch lock; SCtgQueue queue; TdThread updateThread; @@ -327,10 +425,80 @@ typedef struct SCtgAction { #define CTG_API_LEAVE(c) do { int32_t __code = c; CTG_UNLOCK(CTG_READ, &gCtgMgmt.lock); CTG_API_DEBUG("CTG API leave %s", __FUNCTION__); CTG_RET(__code); } while (0) #define CTG_API_ENTER() do { CTG_API_DEBUG("CTG API enter %s", __FUNCTION__); CTG_LOCK(CTG_READ, &gCtgMgmt.lock); if (atomic_load_8((int8_t*)&gCtgMgmt.exit)) { CTG_API_LEAVE(TSDB_CODE_CTG_OUT_OF_SERVICE); } } while (0) - -extern void ctgdShowTableMeta(SCatalog* pCtg, const char *tbName, STableMeta* p); -extern void ctgdShowClusterCache(SCatalog* pCtg); -extern int32_t ctgdShowCacheInfo(void); +#define CTG_PARAMS SCatalog* pCtg, void *pTrans, const SEpSet* pMgmtEps +#define CTG_PARAMS_LIST() pCtg, pTrans, pMgmtEps + +void ctgdShowTableMeta(SCatalog* pCtg, const char *tbName, STableMeta* p); +void ctgdShowClusterCache(SCatalog* pCtg); +int32_t ctgdShowCacheInfo(void); + +int32_t ctgRemoveTbMetaFromCache(SCatalog* pCtg, SName* pTableName, bool syncReq); +int32_t ctgGetTbMetaFromCache(CTG_PARAMS, SCtgTbMetaCtx* ctx, STableMeta** pTableMeta); + +int32_t ctgActUpdateVg(SCtgMetaAction *action); +int32_t ctgActUpdateTb(SCtgMetaAction *action); +int32_t ctgActRemoveDB(SCtgMetaAction *action); +int32_t ctgActRemoveStb(SCtgMetaAction *action); +int32_t ctgActRemoveTb(SCtgMetaAction *action); +int32_t ctgActUpdateUser(SCtgMetaAction *action); +int32_t ctgAcquireVgInfoFromCache(SCatalog* pCtg, const char *dbFName, SCtgDBCache **pCache); +void ctgReleaseDBCache(SCatalog *pCtg, SCtgDBCache *dbCache); +void ctgReleaseVgInfo(SCtgDBCache *dbCache); +int32_t ctgAcquireVgInfoFromCache(SCatalog* pCtg, const char *dbFName, SCtgDBCache **pCache); +int32_t ctgTbMetaExistInCache(SCatalog* pCtg, char *dbFName, char* tbName, int32_t *exist); +int32_t ctgReadTbMetaFromCache(SCatalog* pCtg, SCtgTbMetaCtx* ctx, STableMeta** pTableMeta); +int32_t ctgReadTbSverFromCache(SCatalog *pCtg, const SName *pTableName, int32_t *sver, int32_t *tbType, uint64_t *suid, char *stbName); +int32_t ctgChkAuthFromCache(SCatalog* pCtg, const char* user, const char* dbFName, AUTH_TYPE type, bool *inCache, bool *pass); +int32_t ctgPutRmDBToQueue(SCatalog* pCtg, const char *dbFName, int64_t dbId); +int32_t ctgPutRmStbToQueue(SCatalog* pCtg, const char *dbFName, int64_t dbId, const char *stbName, uint64_t suid, bool syncReq); +int32_t ctgPutRmTbToQueue(SCatalog* pCtg, const char *dbFName, int64_t dbId, const char *tbName, bool syncReq); +int32_t ctgPutUpdateVgToQueue(SCatalog* pCtg, const char *dbFName, int64_t dbId, SDBVgInfo* dbInfo, bool syncReq); +int32_t ctgPutUpdateTbToQueue(SCatalog* pCtg, STableMetaOutput *output, bool syncReq); +int32_t ctgPutUpdateUserToQueue(SCatalog* pCtg, SGetUserAuthRsp *pAuth, bool syncReq); +int32_t ctgMetaRentInit(SCtgRentMgmt *mgmt, uint32_t rentSec, int8_t type); +int32_t ctgMetaRentAdd(SCtgRentMgmt *mgmt, void *meta, int64_t id, int32_t size); +int32_t ctgMetaRentGet(SCtgRentMgmt *mgmt, void **res, uint32_t *num, int32_t size); +int32_t ctgUpdateTbMetaToCache(SCatalog* pCtg, STableMetaOutput* pOut, bool syncReq); +int32_t ctgStartUpdateThread(); +int32_t ctgRelaunchGetTbMetaTask(SCtgTask *pTask); + + + +int32_t ctgProcessRspMsg(void* out, int32_t reqType, char* msg, int32_t msgSize, int32_t rspCode, char* target); +int32_t ctgGetDBVgInfoFromMnode(CTG_PARAMS, SBuildUseDBInput *input, SUseDbOutput *out, SCtgTask* pTask); +int32_t ctgGetQnodeListFromMnode(CTG_PARAMS, SArray *out, SCtgTask* pTask); +int32_t ctgGetDBCfgFromMnode(CTG_PARAMS, const char *dbFName, SDbCfgInfo *out, SCtgTask* pTask); +int32_t ctgGetIndexInfoFromMnode(CTG_PARAMS, const char *indexName, SIndexInfo *out, SCtgTask* pTask); +int32_t ctgGetUdfInfoFromMnode(CTG_PARAMS, const char *funcName, SFuncInfo *out, SCtgTask* pTask); +int32_t ctgGetUserDbAuthFromMnode(CTG_PARAMS, const char *user, SGetUserAuthRsp *out, SCtgTask* pTask); +int32_t ctgGetTbMetaFromMnodeImpl(CTG_PARAMS, char *dbFName, char* tbName, STableMetaOutput* out, SCtgTask* pTask); +int32_t ctgGetTbMetaFromMnode(CTG_PARAMS, const SName* pTableName, STableMetaOutput* out, SCtgTask* pTask); +int32_t ctgGetTbMetaFromVnode(CTG_PARAMS, const SName* pTableName, SVgroupInfo *vgroupInfo, STableMetaOutput* out, SCtgTask* pTask); + +int32_t ctgInitJob(CTG_PARAMS, SCtgJob** job, uint64_t reqId, const SCatalogReq* pReq, catalogCallback fp, void* param); +int32_t ctgLaunchJob(SCtgJob *pJob); +int32_t ctgMakeAsyncRes(SCtgJob *pJob); + +int32_t ctgCloneVgInfo(SDBVgInfo *src, SDBVgInfo **dst); +int32_t ctgCloneMetaOutput(STableMetaOutput *output, STableMetaOutput **pOutput); +int32_t ctgGenerateVgList(SCatalog *pCtg, SHashObj *vgHash, SArray** pList); +void ctgFreeJob(void* job); +void ctgFreeHandle(SCatalog* pCtg); +void ctgFreeVgInfo(SDBVgInfo *vgInfo); +int32_t ctgGetVgInfoFromHashValue(SCatalog *pCtg, SDBVgInfo *dbInfo, const SName *pTableName, SVgroupInfo *pVgroup); +void ctgResetTbMetaTask(SCtgTask* pTask); +void ctgFreeDbCache(SCtgDBCache *dbCache); +int32_t ctgStbVersionSortCompare(const void* key1, const void* key2); +int32_t ctgDbVgVersionSortCompare(const void* key1, const void* key2); +int32_t ctgStbVersionSearchCompare(const void* key1, const void* key2); +int32_t ctgDbVgVersionSearchCompare(const void* key1, const void* key2); +void ctgFreeSTableMetaOutput(STableMetaOutput* pOutput); +int32_t ctgUpdateMsgCtx(SCtgMsgCtx* pCtx, int32_t reqType, void* out, char* target); + + +extern SCatalogMgmt gCtgMgmt; +extern SCtgDebug gCTGDebug; +extern SCtgAsyncFps gCtgAsyncFps[]; #ifdef __cplusplus } diff --git a/source/libs/executor/inc/indexoperator.h b/source/libs/catalog/inc/ctgRemote.h similarity index 66% rename from source/libs/executor/inc/indexoperator.h rename to source/libs/catalog/inc/ctgRemote.h index d033c63ef8c9e6adb2685182103ed918299e1d66..cd88863c1b9b306b264cd05986f0a7e3a6b814d8 100644 --- a/source/libs/executor/inc/indexoperator.h +++ b/source/libs/catalog/inc/ctgRemote.h @@ -13,23 +13,23 @@ * along with this program. If not, see . */ -#ifndef _INDEX_OPERATOR_H -#define _INDEX_OPERATOR_H +#ifndef _TD_CATALOG_REMOTE_H_ +#define _TD_CATALOG_REMOTE_H_ #ifdef __cplusplus extern "C" { #endif -#include "nodes.h" -#include "tglobal.h" -typedef enum { SFLT_NOT_INDEX, SFLT_COARSE_INDEX, SFLT_ACCURATE_INDEX } SIdxFltStatus; +typedef struct SCtgTaskCallbackParam { + uint64_t queryId; + int64_t refId; + uint64_t taskId; + int32_t reqType; +} SCtgTaskCallbackParam; -SIdxFltStatus idxGetFltStatus(SNode *pFilterNode); -// construct tag filter operator later -int32_t doFilterTag(const SNode *pFilterNode, SArray *result); #ifdef __cplusplus } #endif -#endif /*INDEX_OPERATOR_*/ +#endif /*_TD_CATALOG_REMOTE_H_*/ diff --git a/source/libs/catalog/src/catalog.c b/source/libs/catalog/src/catalog.c index 23957d1a6bfdc0cd87dd6f94727d0e1dc76adb57..4afebf9951db2a895ef4b8c728e7b99fe979ce35 100644 --- a/source/libs/catalog/src/catalog.c +++ b/source/libs/catalog/src/catalog.c @@ -18,1788 +18,47 @@ #include "tname.h" #include "catalogInt.h" #include "systable.h" +#include "tref.h" -int32_t ctgActUpdateVg(SCtgMetaAction *action); -int32_t ctgActUpdateTbl(SCtgMetaAction *action); -int32_t ctgActRemoveDB(SCtgMetaAction *action); -int32_t ctgActRemoveStb(SCtgMetaAction *action); -int32_t ctgActRemoveTbl(SCtgMetaAction *action); -int32_t ctgActUpdateUser(SCtgMetaAction *action); - -extern SCtgDebug gCTGDebug; -SCatalogMgmt gCtgMgmt = {0}; -SCtgAction gCtgAction[CTG_ACT_MAX] = {{ - CTG_ACT_UPDATE_VG, - "update vgInfo", - ctgActUpdateVg - }, - { - CTG_ACT_UPDATE_TBL, - "update tbMeta", - ctgActUpdateTbl - }, - { - CTG_ACT_REMOVE_DB, - "remove DB", - ctgActRemoveDB - }, - { - CTG_ACT_REMOVE_STB, - "remove stbMeta", - ctgActRemoveStb - }, - { - CTG_ACT_REMOVE_TBL, - "remove tbMeta", - ctgActRemoveTbl - }, - { - CTG_ACT_UPDATE_USER, - "update user", - ctgActUpdateUser - } -}; - -void ctgFreeMetaRent(SCtgRentMgmt *mgmt) { - if (NULL == mgmt->slots) { - return; - } - - for (int32_t i = 0; i < mgmt->slotNum; ++i) { - SCtgRentSlot *slot = &mgmt->slots[i]; - if (slot->meta) { - taosArrayDestroy(slot->meta); - slot->meta = NULL; - } - } - - taosMemoryFreeClear(mgmt->slots); -} - - -void ctgFreeTableMetaCache(SCtgTbMetaCache *cache) { - CTG_LOCK(CTG_WRITE, &cache->stbLock); - if (cache->stbCache) { - int32_t stblNum = taosHashGetSize(cache->stbCache); - taosHashCleanup(cache->stbCache); - cache->stbCache = NULL; - CTG_CACHE_STAT_SUB(stblNum, stblNum); - } - CTG_UNLOCK(CTG_WRITE, &cache->stbLock); - - CTG_LOCK(CTG_WRITE, &cache->metaLock); - if (cache->metaCache) { - int32_t tblNum = taosHashGetSize(cache->metaCache); - taosHashCleanup(cache->metaCache); - cache->metaCache = NULL; - CTG_CACHE_STAT_SUB(tblNum, tblNum); - } - CTG_UNLOCK(CTG_WRITE, &cache->metaLock); -} - -void ctgFreeVgInfo(SDBVgInfo *vgInfo) { - if (NULL == vgInfo) { - return; - } - - if (vgInfo->vgHash) { - taosHashCleanup(vgInfo->vgHash); - vgInfo->vgHash = NULL; - } - - taosMemoryFreeClear(vgInfo); -} - -void ctgFreeDbCache(SCtgDBCache *dbCache) { - if (NULL == dbCache) { - return; - } - - CTG_LOCK(CTG_WRITE, &dbCache->vgLock); - ctgFreeVgInfo (dbCache->vgInfo); - CTG_UNLOCK(CTG_WRITE, &dbCache->vgLock); - - ctgFreeTableMetaCache(&dbCache->tbCache); -} - -void ctgFreeSCtgUserAuth(SCtgUserAuth *userCache) { - taosHashCleanup(userCache->createdDbs); - taosHashCleanup(userCache->readDbs); - taosHashCleanup(userCache->writeDbs); -} - -void ctgFreeHandle(SCatalog* pCtg) { - ctgFreeMetaRent(&pCtg->dbRent); - ctgFreeMetaRent(&pCtg->stbRent); - - if (pCtg->dbCache) { - int32_t dbNum = taosHashGetSize(pCtg->dbCache); - - void *pIter = taosHashIterate(pCtg->dbCache, NULL); - while (pIter) { - SCtgDBCache *dbCache = pIter; - - atomic_store_8(&dbCache->deleted, 1); - - ctgFreeDbCache(dbCache); - - pIter = taosHashIterate(pCtg->dbCache, pIter); - } - - taosHashCleanup(pCtg->dbCache); - - CTG_CACHE_STAT_SUB(dbNum, dbNum); - } - - if (pCtg->userCache) { - int32_t userNum = taosHashGetSize(pCtg->userCache); - - void *pIter = taosHashIterate(pCtg->userCache, NULL); - while (pIter) { - SCtgUserAuth *userCache = pIter; - - ctgFreeSCtgUserAuth(userCache); - - pIter = taosHashIterate(pCtg->userCache, pIter); - } - - taosHashCleanup(pCtg->userCache); - - CTG_CACHE_STAT_SUB(userNum, userNum); - } - - taosMemoryFree(pCtg); -} - - - -void ctgWaitAction(SCtgMetaAction *action) { - while (true) { - tsem_wait(&gCtgMgmt.queue.rspSem); - - if (atomic_load_8((int8_t*)&gCtgMgmt.exit)) { - tsem_post(&gCtgMgmt.queue.rspSem); - break; - } - - if (gCtgMgmt.queue.seqDone >= action->seqId) { - break; - } - - tsem_post(&gCtgMgmt.queue.rspSem); - sched_yield(); - } -} - -void ctgPopAction(SCtgMetaAction **action) { - SCtgQNode *orig = gCtgMgmt.queue.head; - - SCtgQNode *node = gCtgMgmt.queue.head->next; - gCtgMgmt.queue.head = gCtgMgmt.queue.head->next; - - CTG_QUEUE_SUB(); - - taosMemoryFreeClear(orig); - - *action = &node->action; -} - - -int32_t ctgPushAction(SCatalog* pCtg, SCtgMetaAction *action) { - SCtgQNode *node = taosMemoryCalloc(1, sizeof(SCtgQNode)); - if (NULL == node) { - qError("calloc %d failed", (int32_t)sizeof(SCtgQNode)); - CTG_RET(TSDB_CODE_CTG_MEM_ERROR); - } - - action->seqId = atomic_add_fetch_64(&gCtgMgmt.queue.seqId, 1); - - node->action = *action; - - CTG_LOCK(CTG_WRITE, &gCtgMgmt.queue.qlock); - gCtgMgmt.queue.tail->next = node; - gCtgMgmt.queue.tail = node; - CTG_UNLOCK(CTG_WRITE, &gCtgMgmt.queue.qlock); - - CTG_QUEUE_ADD(); - CTG_RUNTIME_STAT_ADD(qNum, 1); - - tsem_post(&gCtgMgmt.queue.reqSem); - - ctgDebug("action [%s] added into queue", gCtgAction[action->act].name); - - if (action->syncReq) { - ctgWaitAction(action); - } - - return TSDB_CODE_SUCCESS; -} - - -int32_t ctgPushRmDBMsgInQueue(SCatalog* pCtg, const char *dbFName, int64_t dbId) { - int32_t code = 0; - SCtgMetaAction action= {.act = CTG_ACT_REMOVE_DB}; - SCtgRemoveDBMsg *msg = taosMemoryMalloc(sizeof(SCtgRemoveDBMsg)); - if (NULL == msg) { - ctgError("malloc %d failed", (int32_t)sizeof(SCtgRemoveDBMsg)); - CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); - } - - char *p = strchr(dbFName, '.'); - if (p && CTG_IS_SYS_DBNAME(p + 1)) { - dbFName = p + 1; - } - - msg->pCtg = pCtg; - strncpy(msg->dbFName, dbFName, sizeof(msg->dbFName)); - msg->dbId = dbId; - - action.data = msg; - - CTG_ERR_JRET(ctgPushAction(pCtg, &action)); - - return TSDB_CODE_SUCCESS; - -_return: - - taosMemoryFreeClear(action.data); - CTG_RET(code); -} - - -int32_t ctgPushRmStbMsgInQueue(SCatalog* pCtg, const char *dbFName, int64_t dbId, const char *stbName, uint64_t suid, bool syncReq) { - int32_t code = 0; - SCtgMetaAction action= {.act = CTG_ACT_REMOVE_STB, .syncReq = syncReq}; - SCtgRemoveStbMsg *msg = taosMemoryMalloc(sizeof(SCtgRemoveStbMsg)); - if (NULL == msg) { - ctgError("malloc %d failed", (int32_t)sizeof(SCtgRemoveStbMsg)); - CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); - } - - msg->pCtg = pCtg; - strncpy(msg->dbFName, dbFName, sizeof(msg->dbFName)); - strncpy(msg->stbName, stbName, sizeof(msg->stbName)); - msg->dbId = dbId; - msg->suid = suid; - - action.data = msg; - - CTG_ERR_JRET(ctgPushAction(pCtg, &action)); - - return TSDB_CODE_SUCCESS; - -_return: - - taosMemoryFreeClear(action.data); - CTG_RET(code); -} - - - -int32_t ctgPushRmTblMsgInQueue(SCatalog* pCtg, const char *dbFName, int64_t dbId, const char *tbName, bool syncReq) { - int32_t code = 0; - SCtgMetaAction action= {.act = CTG_ACT_REMOVE_TBL, .syncReq = syncReq}; - SCtgRemoveTblMsg *msg = taosMemoryMalloc(sizeof(SCtgRemoveTblMsg)); - if (NULL == msg) { - ctgError("malloc %d failed", (int32_t)sizeof(SCtgRemoveTblMsg)); - CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); - } - - msg->pCtg = pCtg; - strncpy(msg->dbFName, dbFName, sizeof(msg->dbFName)); - strncpy(msg->tbName, tbName, sizeof(msg->tbName)); - msg->dbId = dbId; - - action.data = msg; - - CTG_ERR_JRET(ctgPushAction(pCtg, &action)); - - return TSDB_CODE_SUCCESS; - -_return: - - taosMemoryFreeClear(action.data); - CTG_RET(code); -} - -int32_t ctgPushUpdateVgMsgInQueue(SCatalog* pCtg, const char *dbFName, int64_t dbId, SDBVgInfo* dbInfo, bool syncReq) { - int32_t code = 0; - SCtgMetaAction action= {.act = CTG_ACT_UPDATE_VG, .syncReq = syncReq}; - SCtgUpdateVgMsg *msg = taosMemoryMalloc(sizeof(SCtgUpdateVgMsg)); - if (NULL == msg) { - ctgError("malloc %d failed", (int32_t)sizeof(SCtgUpdateVgMsg)); - ctgFreeVgInfo(dbInfo); - CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); - } - - char *p = strchr(dbFName, '.'); - if (p && CTG_IS_SYS_DBNAME(p + 1)) { - dbFName = p + 1; - } - - strncpy(msg->dbFName, dbFName, sizeof(msg->dbFName)); - msg->pCtg = pCtg; - msg->dbId = dbId; - msg->dbInfo = dbInfo; - - action.data = msg; - - CTG_ERR_JRET(ctgPushAction(pCtg, &action)); - - return TSDB_CODE_SUCCESS; - -_return: - - ctgFreeVgInfo(dbInfo); - taosMemoryFreeClear(action.data); - CTG_RET(code); -} - -int32_t ctgPushUpdateTblMsgInQueue(SCatalog* pCtg, STableMetaOutput *output, bool syncReq) { - int32_t code = 0; - SCtgMetaAction action= {.act = CTG_ACT_UPDATE_TBL, .syncReq = syncReq}; - SCtgUpdateTblMsg *msg = taosMemoryMalloc(sizeof(SCtgUpdateTblMsg)); - if (NULL == msg) { - ctgError("malloc %d failed", (int32_t)sizeof(SCtgUpdateTblMsg)); - CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); - } - - char *p = strchr(output->dbFName, '.'); - if (p && CTG_IS_SYS_DBNAME(p + 1)) { - memmove(output->dbFName, p + 1, strlen(p + 1)); - } - - msg->pCtg = pCtg; - msg->output = output; - - action.data = msg; - - CTG_ERR_JRET(ctgPushAction(pCtg, &action)); - - return TSDB_CODE_SUCCESS; - -_return: - - taosMemoryFreeClear(msg); - - CTG_RET(code); -} - -int32_t ctgPushUpdateUserMsgInQueue(SCatalog* pCtg, SGetUserAuthRsp *pAuth, bool syncReq) { - int32_t code = 0; - SCtgMetaAction action= {.act = CTG_ACT_UPDATE_USER, .syncReq = syncReq}; - SCtgUpdateUserMsg *msg = taosMemoryMalloc(sizeof(SCtgUpdateUserMsg)); - if (NULL == msg) { - ctgError("malloc %d failed", (int32_t)sizeof(SCtgUpdateUserMsg)); - CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); - } - - msg->pCtg = pCtg; - msg->userAuth = *pAuth; - - action.data = msg; - - CTG_ERR_JRET(ctgPushAction(pCtg, &action)); - - return TSDB_CODE_SUCCESS; - -_return: - - tFreeSGetUserAuthRsp(pAuth); - taosMemoryFreeClear(msg); - - CTG_RET(code); -} - -int32_t ctgAcquireVgInfo(SCatalog *pCtg, SCtgDBCache *dbCache, bool *inCache) { - CTG_LOCK(CTG_READ, &dbCache->vgLock); - - if (dbCache->deleted) { - CTG_UNLOCK(CTG_READ, &dbCache->vgLock); - - ctgDebug("db is dropping, dbId:%"PRIx64, dbCache->dbId); - - *inCache = false; - return TSDB_CODE_SUCCESS; - } - - - if (NULL == dbCache->vgInfo) { - CTG_UNLOCK(CTG_READ, &dbCache->vgLock); - - *inCache = false; - ctgDebug("db vgInfo is empty, dbId:%"PRIx64, dbCache->dbId); - return TSDB_CODE_SUCCESS; - } - - *inCache = true; - - return TSDB_CODE_SUCCESS; -} - -int32_t ctgWAcquireVgInfo(SCatalog *pCtg, SCtgDBCache *dbCache) { - CTG_LOCK(CTG_WRITE, &dbCache->vgLock); - - if (dbCache->deleted) { - ctgDebug("db is dropping, dbId:%"PRIx64, dbCache->dbId); - CTG_UNLOCK(CTG_WRITE, &dbCache->vgLock); - CTG_ERR_RET(TSDB_CODE_CTG_DB_DROPPED); - } - - return TSDB_CODE_SUCCESS; -} - -void ctgReleaseDBCache(SCatalog *pCtg, SCtgDBCache *dbCache) { - taosHashRelease(pCtg->dbCache, dbCache); -} - -void ctgReleaseVgInfo(SCtgDBCache *dbCache) { - CTG_UNLOCK(CTG_READ, &dbCache->vgLock); -} - -void ctgWReleaseVgInfo(SCtgDBCache *dbCache) { - CTG_UNLOCK(CTG_WRITE, &dbCache->vgLock); -} - - -int32_t ctgAcquireDBCacheImpl(SCatalog* pCtg, const char *dbFName, SCtgDBCache **pCache, bool acquire) { - char *p = strchr(dbFName, '.'); - if (p && CTG_IS_SYS_DBNAME(p + 1)) { - dbFName = p + 1; - } - - SCtgDBCache *dbCache = NULL; - if (acquire) { - dbCache = (SCtgDBCache *)taosHashAcquire(pCtg->dbCache, dbFName, strlen(dbFName)); - } else { - dbCache = (SCtgDBCache *)taosHashGet(pCtg->dbCache, dbFName, strlen(dbFName)); - } - - if (NULL == dbCache) { - *pCache = NULL; - ctgDebug("db not in cache, dbFName:%s", dbFName); - return TSDB_CODE_SUCCESS; - } - - if (dbCache->deleted) { - if (acquire) { - ctgReleaseDBCache(pCtg, dbCache); - } - - *pCache = NULL; - ctgDebug("db is removing from cache, dbFName:%s", dbFName); - return TSDB_CODE_SUCCESS; - } - - *pCache = dbCache; - - return TSDB_CODE_SUCCESS; -} - -int32_t ctgAcquireDBCache(SCatalog* pCtg, const char *dbFName, SCtgDBCache **pCache) { - CTG_RET(ctgAcquireDBCacheImpl(pCtg, dbFName, pCache, true)); -} - -int32_t ctgGetDBCache(SCatalog* pCtg, const char *dbFName, SCtgDBCache **pCache) { - CTG_RET(ctgAcquireDBCacheImpl(pCtg, dbFName, pCache, false)); -} - - -int32_t ctgAcquireVgInfoFromCache(SCatalog* pCtg, const char *dbFName, SCtgDBCache **pCache, bool *inCache) { - SCtgDBCache *dbCache = NULL; - - if (NULL == pCtg->dbCache) { - ctgDebug("empty db cache, dbFName:%s", dbFName); - goto _return; - } - - ctgAcquireDBCache(pCtg, dbFName, &dbCache); - if (NULL == dbCache) { - ctgDebug("db %s not in cache", dbFName); - goto _return; - } - - ctgAcquireVgInfo(pCtg, dbCache, inCache); - if (!(*inCache)) { - ctgDebug("vgInfo of db %s not in cache", dbFName); - goto _return; - } - - *pCache = dbCache; - *inCache = true; - - CTG_CACHE_STAT_ADD(vgHitNum, 1); - - ctgDebug("Got db vgInfo from cache, dbFName:%s", dbFName); - - return TSDB_CODE_SUCCESS; - -_return: - - if (dbCache) { - ctgReleaseDBCache(pCtg, dbCache); - } - - *pCache = NULL; - *inCache = false; - - CTG_CACHE_STAT_ADD(vgMissNum, 1); - - return TSDB_CODE_SUCCESS; -} - -int32_t ctgGetQnodeListFromMnode(SCatalog* pCtg, void *pRpc, const SEpSet* pMgmtEps, SArray *out) { - char *msg = NULL; - int32_t msgLen = 0; - - ctgDebug("try to get qnode list from mnode, mgmtEpInUse:%d", pMgmtEps->inUse); - - int32_t code = queryBuildMsg[TMSG_INDEX(TDMT_MND_QNODE_LIST)](NULL, &msg, 0, &msgLen); - if (code) { - ctgError("Build qnode list msg failed, error:%s", tstrerror(code)); - CTG_ERR_RET(code); - } - - SRpcMsg rpcMsg = { - .msgType = TDMT_MND_QNODE_LIST, - .pCont = msg, - .contLen = msgLen, - }; - - SRpcMsg rpcRsp = {0}; - - rpcSendRecv(pRpc, (SEpSet*)pMgmtEps, &rpcMsg, &rpcRsp); - if (TSDB_CODE_SUCCESS != rpcRsp.code) { - ctgError("error rsp for qnode list, error:%s", tstrerror(rpcRsp.code)); - CTG_ERR_RET(rpcRsp.code); - } - - code = queryProcessMsgRsp[TMSG_INDEX(TDMT_MND_QNODE_LIST)](out, rpcRsp.pCont, rpcRsp.contLen); - if (code) { - ctgError("Process qnode list rsp failed, error:%s", tstrerror(rpcRsp.code)); - CTG_ERR_RET(code); - } - - ctgDebug("Got qnode list from mnode, listNum:%d", (int32_t)taosArrayGetSize(out)); - - return TSDB_CODE_SUCCESS; -} - - -int32_t ctgGetDBVgInfoFromMnode(SCatalog* pCtg, void *pRpc, const SEpSet* pMgmtEps, SBuildUseDBInput *input, SUseDbOutput *out) { - char *msg = NULL; - int32_t msgLen = 0; - - ctgDebug("try to get db vgInfo from mnode, dbFName:%s", input->db); - - int32_t code = queryBuildMsg[TMSG_INDEX(TDMT_MND_USE_DB)](input, &msg, 0, &msgLen); - if (code) { - ctgError("Build use db msg failed, code:%x, db:%s", code, input->db); - CTG_ERR_RET(code); - } - - SRpcMsg rpcMsg = { - .msgType = TDMT_MND_USE_DB, - .pCont = msg, - .contLen = msgLen, - }; - - SRpcMsg rpcRsp = {0}; - - rpcSendRecv(pRpc, (SEpSet*)pMgmtEps, &rpcMsg, &rpcRsp); - if (TSDB_CODE_SUCCESS != rpcRsp.code) { - ctgError("error rsp for use db, error:%s, db:%s", tstrerror(rpcRsp.code), input->db); - CTG_ERR_RET(rpcRsp.code); - } - - code = queryProcessMsgRsp[TMSG_INDEX(TDMT_MND_USE_DB)](out, rpcRsp.pCont, rpcRsp.contLen); - if (code) { - ctgError("Process use db rsp failed, code:%x, db:%s", code, input->db); - CTG_ERR_RET(code); - } - - ctgDebug("Got db vgInfo from mnode, dbFName:%s", input->db); - - return TSDB_CODE_SUCCESS; -} - -int32_t ctgGetDBCfgFromMnode(SCatalog* pCtg, void *pRpc, const SEpSet* pMgmtEps, const char *dbFName, SDbCfgInfo *out) { - char *msg = NULL; - int32_t msgLen = 0; - - ctgDebug("try to get db cfg from mnode, dbFName:%s", dbFName); - - int32_t code = queryBuildMsg[TMSG_INDEX(TDMT_MND_GET_DB_CFG)]((void *)dbFName, &msg, 0, &msgLen); - if (code) { - ctgError("Build get db cfg msg failed, code:%x, db:%s", code, dbFName); - CTG_ERR_RET(code); - } - - SRpcMsg rpcMsg = { - .msgType = TDMT_MND_GET_DB_CFG, - .pCont = msg, - .contLen = msgLen, - }; - - SRpcMsg rpcRsp = {0}; - - rpcSendRecv(pRpc, (SEpSet*)pMgmtEps, &rpcMsg, &rpcRsp); - if (TSDB_CODE_SUCCESS != rpcRsp.code) { - ctgError("error rsp for get db cfg, error:%s, db:%s", tstrerror(rpcRsp.code), dbFName); - CTG_ERR_RET(rpcRsp.code); - } - - code = queryProcessMsgRsp[TMSG_INDEX(TDMT_MND_GET_DB_CFG)](out, rpcRsp.pCont, rpcRsp.contLen); - if (code) { - ctgError("Process get db cfg rsp failed, code:%x, db:%s", code, dbFName); - CTG_ERR_RET(code); - } - - ctgDebug("Got db cfg from mnode, dbFName:%s", dbFName); - - return TSDB_CODE_SUCCESS; -} - -int32_t ctgGetIndexInfoFromMnode(SCatalog* pCtg, void *pRpc, const SEpSet* pMgmtEps, const char *indexName, SIndexInfo *out) { - char *msg = NULL; - int32_t msgLen = 0; - - ctgDebug("try to get index from mnode, indexName:%s", indexName); - - int32_t code = queryBuildMsg[TMSG_INDEX(TDMT_MND_GET_INDEX)]((void *)indexName, &msg, 0, &msgLen); - if (code) { - ctgError("Build get index msg failed, code:%x, db:%s", code, indexName); - CTG_ERR_RET(code); - } - - SRpcMsg rpcMsg = { - .msgType = TDMT_MND_GET_INDEX, - .pCont = msg, - .contLen = msgLen, - }; - - SRpcMsg rpcRsp = {0}; - - rpcSendRecv(pRpc, (SEpSet*)pMgmtEps, &rpcMsg, &rpcRsp); - if (TSDB_CODE_SUCCESS != rpcRsp.code) { - ctgError("error rsp for get index, error:%s, indexName:%s", tstrerror(rpcRsp.code), indexName); - CTG_ERR_RET(rpcRsp.code); - } - - code = queryProcessMsgRsp[TMSG_INDEX(TDMT_MND_GET_INDEX)](out, rpcRsp.pCont, rpcRsp.contLen); - if (code) { - ctgError("Process get index rsp failed, code:%x, indexName:%s", code, indexName); - CTG_ERR_RET(code); - } - - ctgDebug("Got index from mnode, indexName:%s", indexName); - - return TSDB_CODE_SUCCESS; -} - -int32_t ctgGetUdfInfoFromMnode(SCatalog* pCtg, void *pRpc, const SEpSet* pMgmtEps, const char *funcName, SFuncInfo **out) { - char *msg = NULL; - int32_t msgLen = 0; - - ctgDebug("try to get udf info from mnode, funcName:%s", funcName); - - int32_t code = queryBuildMsg[TMSG_INDEX(TDMT_MND_RETRIEVE_FUNC)]((void *)funcName, &msg, 0, &msgLen); - if (code) { - ctgError("Build get udf msg failed, code:%x, db:%s", code, funcName); - CTG_ERR_RET(code); - } - - SRpcMsg rpcMsg = { - .msgType = TDMT_MND_RETRIEVE_FUNC, - .pCont = msg, - .contLen = msgLen, - }; - - SRpcMsg rpcRsp = {0}; - - rpcSendRecv(pRpc, (SEpSet*)pMgmtEps, &rpcMsg, &rpcRsp); - if (TSDB_CODE_SUCCESS != rpcRsp.code) { - if (TSDB_CODE_MND_FUNC_NOT_EXIST == rpcRsp.code) { - ctgDebug("funcName %s not exist in mnode", funcName); - taosMemoryFreeClear(*out); - CTG_RET(TSDB_CODE_SUCCESS); - } - - ctgError("error rsp for get udf, error:%s, funcName:%s", tstrerror(rpcRsp.code), funcName); - CTG_ERR_RET(rpcRsp.code); - } - - code = queryProcessMsgRsp[TMSG_INDEX(TDMT_MND_RETRIEVE_FUNC)](*out, rpcRsp.pCont, rpcRsp.contLen); - if (code) { - ctgError("Process get udf rsp failed, code:%x, funcName:%s", code, funcName); - CTG_ERR_RET(code); - } - - ctgDebug("Got udf from mnode, funcName:%s", funcName); - - return TSDB_CODE_SUCCESS; -} - -int32_t ctgGetUserDbAuthFromMnode(SCatalog* pCtg, void *pRpc, const SEpSet* pMgmtEps, const char *user, SGetUserAuthRsp *authRsp) { - char *msg = NULL; - int32_t msgLen = 0; - - ctgDebug("try to get user auth from mnode, user:%s", user); - - int32_t code = queryBuildMsg[TMSG_INDEX(TDMT_MND_GET_USER_AUTH)]((void *)user, &msg, 0, &msgLen); - if (code) { - ctgError("Build get user auth msg failed, code:%x, db:%s", code, user); - CTG_ERR_RET(code); - } - - SRpcMsg rpcMsg = { - .msgType = TDMT_MND_GET_USER_AUTH, - .pCont = msg, - .contLen = msgLen, - }; - - SRpcMsg rpcRsp = {0}; - - rpcSendRecv(pRpc, (SEpSet*)pMgmtEps, &rpcMsg, &rpcRsp); - if (TSDB_CODE_SUCCESS != rpcRsp.code) { - ctgError("error rsp for get user auth, error:%s, user:%s", tstrerror(rpcRsp.code), user); - CTG_ERR_RET(rpcRsp.code); - } - - code = queryProcessMsgRsp[TMSG_INDEX(TDMT_MND_GET_USER_AUTH)](authRsp, rpcRsp.pCont, rpcRsp.contLen); - if (code) { - ctgError("Process get user auth rsp failed, code:%x, user:%s", code, user); - CTG_ERR_RET(code); - } - - ctgDebug("Got user auth from mnode, user:%s", user); - - return TSDB_CODE_SUCCESS; -} - - -int32_t ctgIsTableMetaExistInCache(SCatalog* pCtg, char *dbFName, char* tbName, int32_t *exist) { - if (NULL == pCtg->dbCache) { - *exist = 0; - ctgWarn("empty db cache, dbFName:%s, tbName:%s", dbFName, tbName); - return TSDB_CODE_SUCCESS; - } - - SCtgDBCache *dbCache = NULL; - ctgAcquireDBCache(pCtg, dbFName, &dbCache); - if (NULL == dbCache) { - *exist = 0; - return TSDB_CODE_SUCCESS; - } - - size_t sz = 0; - CTG_LOCK(CTG_READ, &dbCache->tbCache.metaLock); - STableMeta *tbMeta = taosHashGet(dbCache->tbCache.metaCache, tbName, strlen(tbName)); - CTG_UNLOCK(CTG_READ, &dbCache->tbCache.metaLock); - - if (NULL == tbMeta) { - ctgReleaseDBCache(pCtg, dbCache); - - *exist = 0; - ctgDebug("tbmeta not in cache, dbFName:%s, tbName:%s", dbFName, tbName); - return TSDB_CODE_SUCCESS; - } - - *exist = 1; - - ctgReleaseDBCache(pCtg, dbCache); - - ctgDebug("tbmeta is in cache, dbFName:%s, tbName:%s", dbFName, tbName); - - return TSDB_CODE_SUCCESS; -} - - -int32_t ctgGetTableMetaFromCache(SCatalog* pCtg, const SName* pTableName, STableMeta** pTableMeta, bool *inCache, int32_t flag, uint64_t *dbId) { - if (NULL == pCtg->dbCache) { - ctgDebug("empty tbmeta cache, tbName:%s", pTableName->tname); - goto _return; - } - - char dbFName[TSDB_DB_FNAME_LEN] = {0}; - if (CTG_FLAG_IS_SYS_DB(flag)) { - strcpy(dbFName, pTableName->dbname); - } else { - tNameGetFullDbName(pTableName, dbFName); - } - - *pTableMeta = NULL; - - SCtgDBCache *dbCache = NULL; - ctgAcquireDBCache(pCtg, dbFName, &dbCache); - if (NULL == dbCache) { - ctgDebug("db %s not in cache", pTableName->tname); - goto _return; - } - - int32_t sz = 0; - CTG_LOCK(CTG_READ, &dbCache->tbCache.metaLock); - int32_t code = taosHashGetDup_m(dbCache->tbCache.metaCache, pTableName->tname, strlen(pTableName->tname), (void **)pTableMeta, &sz); - CTG_UNLOCK(CTG_READ, &dbCache->tbCache.metaLock); - - if (NULL == *pTableMeta) { - ctgReleaseDBCache(pCtg, dbCache); - ctgDebug("tbl not in cache, dbFName:%s, tbName:%s", dbFName, pTableName->tname); - goto _return; - } - - if (dbId) { - *dbId = dbCache->dbId; - } - - STableMeta* tbMeta = *pTableMeta; - - if (tbMeta->tableType != TSDB_CHILD_TABLE) { - ctgReleaseDBCache(pCtg, dbCache); - ctgDebug("Got meta from cache, type:%d, dbFName:%s, tbName:%s", tbMeta->tableType, dbFName, pTableName->tname); - - *inCache = true; - CTG_CACHE_STAT_ADD(tblHitNum, 1); - - return TSDB_CODE_SUCCESS; - } - - ctgDebug("Got subtable meta from cache, type:%d, dbFName:%s, tbName:%s, suid:%" PRIx64, tbMeta->tableType, dbFName, pTableName->tname, tbMeta->suid); - - CTG_LOCK(CTG_READ, &dbCache->tbCache.stbLock); - - STableMeta **stbMeta = taosHashGet(dbCache->tbCache.stbCache, &tbMeta->suid, sizeof(tbMeta->suid)); - if (NULL == stbMeta || NULL == *stbMeta) { - CTG_UNLOCK(CTG_READ, &dbCache->tbCache.stbLock); - ctgReleaseDBCache(pCtg, dbCache); - ctgError("stb not in stbCache, suid:%"PRIx64, tbMeta->suid); - taosMemoryFreeClear(*pTableMeta); - goto _return; - } - - if ((*stbMeta)->suid != tbMeta->suid) { - CTG_UNLOCK(CTG_READ, &dbCache->tbCache.stbLock); - ctgReleaseDBCache(pCtg, dbCache); - taosMemoryFreeClear(*pTableMeta); - ctgError("stable suid in stbCache mis-match, expected suid:%"PRIx64 ",actual suid:%"PRIx64, tbMeta->suid, (*stbMeta)->suid); - CTG_ERR_RET(TSDB_CODE_CTG_INTERNAL_ERROR); - } - - int32_t metaSize = CTG_META_SIZE(*stbMeta); - *pTableMeta = taosMemoryRealloc(*pTableMeta, metaSize); - if (NULL == *pTableMeta) { - CTG_UNLOCK(CTG_READ, &dbCache->tbCache.stbLock); - ctgReleaseDBCache(pCtg, dbCache); - ctgError("realloc size[%d] failed", metaSize); - CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); - } - - memcpy(&(*pTableMeta)->sversion, &(*stbMeta)->sversion, metaSize - sizeof(SCTableMeta)); - - CTG_UNLOCK(CTG_READ, &dbCache->tbCache.stbLock); - - ctgReleaseDBCache(pCtg, dbCache); - - *inCache = true; - CTG_CACHE_STAT_ADD(tblHitNum, 1); - - ctgDebug("Got tbmeta from cache, dbFName:%s, tbName:%s", dbFName, pTableName->tname); - - return TSDB_CODE_SUCCESS; - -_return: - - *inCache = false; - CTG_CACHE_STAT_ADD(tblMissNum, 1); - - return TSDB_CODE_SUCCESS; -} - -int32_t ctgGetTableTypeFromCache(SCatalog* pCtg, const char* dbFName, const char *tableName, int32_t *tbType) { - if (NULL == pCtg->dbCache) { - ctgWarn("empty db cache, dbFName:%s, tbName:%s", dbFName, tableName); - return TSDB_CODE_SUCCESS; - } - - SCtgDBCache *dbCache = NULL; - ctgAcquireDBCache(pCtg, dbFName, &dbCache); - if (NULL == dbCache) { - return TSDB_CODE_SUCCESS; - } - - CTG_LOCK(CTG_READ, &dbCache->tbCache.metaLock); - STableMeta *pTableMeta = (STableMeta *)taosHashAcquire(dbCache->tbCache.metaCache, tableName, strlen(tableName)); - - if (NULL == pTableMeta) { - CTG_UNLOCK(CTG_READ, &dbCache->tbCache.metaLock); - ctgWarn("tbl not in cache, dbFName:%s, tbName:%s", dbFName, tableName); - ctgReleaseDBCache(pCtg, dbCache); - - return TSDB_CODE_SUCCESS; - } - - *tbType = atomic_load_8(&pTableMeta->tableType); - - taosHashRelease(dbCache->tbCache.metaCache, pTableMeta); - - CTG_UNLOCK(CTG_READ, &dbCache->tbCache.metaLock); - - ctgReleaseDBCache(pCtg, dbCache); - - ctgDebug("Got tbtype from cache, dbFName:%s, tbName:%s, type:%d", dbFName, tableName, *tbType); - - return TSDB_CODE_SUCCESS; -} - -int32_t ctgChkAuthFromCache(SCatalog* pCtg, const char* user, const char* dbFName, AUTH_TYPE type, bool *inCache, bool *pass) { - if (NULL == pCtg->userCache) { - ctgDebug("empty user auth cache, user:%s", user); - goto _return; - } - - SCtgUserAuth *pUser = (SCtgUserAuth *)taosHashGet(pCtg->userCache, user, strlen(user)); - if (NULL == pUser) { - ctgDebug("user not in cache, user:%s", user); - goto _return; - } - - *inCache = true; - - ctgDebug("Got user from cache, user:%s", user); - CTG_CACHE_STAT_ADD(userHitNum, 1); - - if (pUser->superUser) { - *pass = true; - return TSDB_CODE_SUCCESS; - } - - CTG_LOCK(CTG_READ, &pUser->lock); - if (pUser->createdDbs && taosHashGet(pUser->createdDbs, dbFName, strlen(dbFName))) { - *pass = true; - CTG_UNLOCK(CTG_READ, &pUser->lock); - return TSDB_CODE_SUCCESS; - } - - if (pUser->readDbs && taosHashGet(pUser->readDbs, dbFName, strlen(dbFName)) && type == AUTH_TYPE_READ) { - *pass = true; - } - - if (pUser->writeDbs && taosHashGet(pUser->writeDbs, dbFName, strlen(dbFName)) && type == AUTH_TYPE_WRITE) { - *pass = true; - } - - CTG_UNLOCK(CTG_READ, &pUser->lock); - - return TSDB_CODE_SUCCESS; - -_return: - - *inCache = false; - CTG_CACHE_STAT_ADD(userMissNum, 1); - - return TSDB_CODE_SUCCESS; -} - -int32_t ctgGetTableMetaFromMnodeImpl(SCatalog* pCtg, void *pTrans, const SEpSet* pMgmtEps, char *dbFName, char* tbName, STableMetaOutput* output) { - SBuildTableMetaInput bInput = {.vgId = 0, .dbFName = dbFName, .tbName = tbName}; - char *msg = NULL; - SEpSet *pVnodeEpSet = NULL; - int32_t msgLen = 0; - - ctgDebug("try to get table meta from mnode, dbFName:%s, tbName:%s", dbFName, tbName); - - int32_t code = queryBuildMsg[TMSG_INDEX(TDMT_MND_TABLE_META)](&bInput, &msg, 0, &msgLen); - if (code) { - ctgError("Build mnode stablemeta msg failed, code:%x", code); - CTG_ERR_RET(code); - } - - SRpcMsg rpcMsg = { - .msgType = TDMT_MND_TABLE_META, - .pCont = msg, - .contLen = msgLen, - }; - - SRpcMsg rpcRsp = {0}; - - rpcSendRecv(pTrans, (SEpSet*)pMgmtEps, &rpcMsg, &rpcRsp); - - if (TSDB_CODE_SUCCESS != rpcRsp.code) { - if (CTG_TABLE_NOT_EXIST(rpcRsp.code)) { - SET_META_TYPE_NULL(output->metaType); - ctgDebug("stablemeta not exist in mnode, dbFName:%s, tbName:%s", dbFName, tbName); - return TSDB_CODE_SUCCESS; - } - - ctgError("error rsp for stablemeta from mnode, code:%s, dbFName:%s, tbName:%s", tstrerror(rpcRsp.code), dbFName, tbName); - CTG_ERR_RET(rpcRsp.code); - } - - code = queryProcessMsgRsp[TMSG_INDEX(TDMT_MND_TABLE_META)](output, rpcRsp.pCont, rpcRsp.contLen); - if (code) { - ctgError("Process mnode stablemeta rsp failed, code:%x, dbFName:%s, tbName:%s", code, dbFName, tbName); - CTG_ERR_RET(code); - } - - ctgDebug("Got table meta from mnode, dbFName:%s, tbName:%s", dbFName, tbName); - - return TSDB_CODE_SUCCESS; -} - -int32_t ctgGetTableMetaFromMnode(SCatalog* pCtg, void *pTrans, const SEpSet* pMgmtEps, const SName* pTableName, STableMetaOutput* output) { - char dbFName[TSDB_DB_FNAME_LEN]; - tNameGetFullDbName(pTableName, dbFName); - - return ctgGetTableMetaFromMnodeImpl(pCtg, pTrans, pMgmtEps, dbFName, (char *)pTableName->tname, output); -} - -int32_t ctgGetTableMetaFromVnodeImpl(SCatalog* pCtg, void *pTrans, const SEpSet* pMgmtEps, const SName* pTableName, SVgroupInfo *vgroupInfo, STableMetaOutput* output) { - if (NULL == pCtg || NULL == pTrans || NULL == pMgmtEps || NULL == pTableName || NULL == vgroupInfo || NULL == output) { - CTG_ERR_RET(TSDB_CODE_CTG_INVALID_INPUT); - } - - char dbFName[TSDB_DB_FNAME_LEN]; - tNameGetFullDbName(pTableName, dbFName); - - ctgDebug("try to get table meta from vnode, dbFName:%s, tbName:%s", dbFName, tNameGetTableName(pTableName)); - - SBuildTableMetaInput bInput = {.vgId = vgroupInfo->vgId, .dbFName = dbFName, .tbName = (char *)tNameGetTableName(pTableName)}; - char *msg = NULL; - int32_t msgLen = 0; - - int32_t code = queryBuildMsg[TMSG_INDEX(TDMT_VND_TABLE_META)](&bInput, &msg, 0, &msgLen); - if (code) { - ctgError("Build vnode tablemeta msg failed, code:%x, dbFName:%s, tbName:%s", code, dbFName, tNameGetTableName(pTableName)); - CTG_ERR_RET(code); - } - - SRpcMsg rpcMsg = { - .msgType = TDMT_VND_TABLE_META, - .pCont = msg, - .contLen = msgLen, - }; - - SRpcMsg rpcRsp = {0}; - rpcSendRecv(pTrans, &vgroupInfo->epSet, &rpcMsg, &rpcRsp); - - if (TSDB_CODE_SUCCESS != rpcRsp.code) { - if (CTG_TABLE_NOT_EXIST(rpcRsp.code)) { - SET_META_TYPE_NULL(output->metaType); - ctgDebug("tablemeta not exist in vnode, dbFName:%s, tbName:%s", dbFName, tNameGetTableName(pTableName)); - return TSDB_CODE_SUCCESS; - } - - ctgError("error rsp for table meta from vnode, code:%s, dbFName:%s, tbName:%s", tstrerror(rpcRsp.code), dbFName, tNameGetTableName(pTableName)); - CTG_ERR_RET(rpcRsp.code); - } - - code = queryProcessMsgRsp[TMSG_INDEX(TDMT_VND_TABLE_META)](output, rpcRsp.pCont, rpcRsp.contLen); - if (code) { - ctgError("Process vnode tablemeta rsp failed, code:%s, dbFName:%s, tbName:%s", tstrerror(code), dbFName, tNameGetTableName(pTableName)); - CTG_ERR_RET(code); - } - - ctgDebug("Got table meta from vnode, dbFName:%s, tbName:%s", dbFName, tNameGetTableName(pTableName)); - return TSDB_CODE_SUCCESS; -} - -int32_t ctgGetTableMetaFromVnode(SCatalog* pCtg, void *pTrans, const SEpSet* pMgmtEps, const SName* pTableName, SVgroupInfo *vgroupInfo, STableMetaOutput* output) { - int32_t code = 0; - int32_t retryNum = 0; - - while (retryNum < CTG_DEFAULT_MAX_RETRY_TIMES) { - code = ctgGetTableMetaFromVnodeImpl(pCtg, pTrans, pMgmtEps, pTableName, vgroupInfo, output); - if (code) { - if (TSDB_CODE_VND_HASH_MISMATCH == code) { - char dbFName[TSDB_DB_FNAME_LEN]; - tNameGetFullDbName(pTableName, dbFName); - - code = catalogRefreshDBVgInfo(pCtg, pTrans, pMgmtEps, dbFName); - if (code != TSDB_CODE_SUCCESS) { - break; - } - - ++retryNum; - continue; - } - } - - break; - } - - CTG_RET(code); -} - -int32_t ctgGetHashFunction(int8_t hashMethod, tableNameHashFp *fp) { - switch (hashMethod) { - default: - *fp = MurmurHash3_32; - break; - } - - return TSDB_CODE_SUCCESS; -} - -int32_t ctgGenerateVgList(SCatalog *pCtg, SHashObj *vgHash, SArray** pList) { - SHashObj *vgroupHash = NULL; - SVgroupInfo *vgInfo = NULL; - SArray *vgList = NULL; - int32_t code = 0; - int32_t vgNum = taosHashGetSize(vgHash); - - vgList = taosArrayInit(vgNum, sizeof(SVgroupInfo)); - if (NULL == vgList) { - ctgError("taosArrayInit failed, num:%d", vgNum); - CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); - } - - void *pIter = taosHashIterate(vgHash, NULL); - while (pIter) { - vgInfo = pIter; - - if (NULL == taosArrayPush(vgList, vgInfo)) { - ctgError("taosArrayPush failed, vgId:%d", vgInfo->vgId); - taosHashCancelIterate(vgHash, pIter); - CTG_ERR_JRET(TSDB_CODE_CTG_MEM_ERROR); - } - - pIter = taosHashIterate(vgHash, pIter); - vgInfo = NULL; - } - - *pList = vgList; - - ctgDebug("Got vgList from cache, vgNum:%d", vgNum); - - return TSDB_CODE_SUCCESS; - -_return: - - if (vgList) { - taosArrayDestroy(vgList); - } - - CTG_RET(code); -} - -int32_t ctgGetVgInfoFromHashValue(SCatalog *pCtg, SDBVgInfo *dbInfo, const SName *pTableName, SVgroupInfo *pVgroup) { - int32_t code = 0; - - int32_t vgNum = taosHashGetSize(dbInfo->vgHash); - char db[TSDB_DB_FNAME_LEN] = {0}; - tNameGetFullDbName(pTableName, db); - - if (vgNum <= 0) { - ctgError("db vgroup cache invalid, db:%s, vgroup number:%d", db, vgNum); - CTG_ERR_RET(TSDB_CODE_TSC_DB_NOT_SELECTED); - } - - tableNameHashFp fp = NULL; - SVgroupInfo *vgInfo = NULL; - - CTG_ERR_RET(ctgGetHashFunction(dbInfo->hashMethod, &fp)); - - char tbFullName[TSDB_TABLE_FNAME_LEN]; - tNameExtractFullName(pTableName, tbFullName); - - uint32_t hashValue = (*fp)(tbFullName, (uint32_t)strlen(tbFullName)); - - void *pIter = taosHashIterate(dbInfo->vgHash, NULL); - while (pIter) { - vgInfo = pIter; - if (hashValue >= vgInfo->hashBegin && hashValue <= vgInfo->hashEnd) { - taosHashCancelIterate(dbInfo->vgHash, pIter); - break; - } - - pIter = taosHashIterate(dbInfo->vgHash, pIter); - vgInfo = NULL; - } - - if (NULL == vgInfo) { - ctgError("no hash range found for hash value [%u], db:%s, numOfVgId:%d", hashValue, db, taosHashGetSize(dbInfo->vgHash)); - CTG_ERR_RET(TSDB_CODE_CTG_INTERNAL_ERROR); - } - - *pVgroup = *vgInfo; - - CTG_RET(code); -} - -int32_t ctgStbVersionSearchCompare(const void* key1, const void* key2) { - if (*(uint64_t *)key1 < ((SSTableMetaVersion*)key2)->suid) { - return -1; - } else if (*(uint64_t *)key1 > ((SSTableMetaVersion*)key2)->suid) { - return 1; - } else { - return 0; - } -} - -int32_t ctgDbVgVersionSearchCompare(const void* key1, const void* key2) { - if (*(int64_t *)key1 < ((SDbVgVersion*)key2)->dbId) { - return -1; - } else if (*(int64_t *)key1 > ((SDbVgVersion*)key2)->dbId) { - return 1; - } else { - return 0; - } -} - -int32_t ctgStbVersionSortCompare(const void* key1, const void* key2) { - if (((SSTableMetaVersion*)key1)->suid < ((SSTableMetaVersion*)key2)->suid) { - return -1; - } else if (((SSTableMetaVersion*)key1)->suid > ((SSTableMetaVersion*)key2)->suid) { - return 1; - } else { - return 0; - } -} - -int32_t ctgDbVgVersionSortCompare(const void* key1, const void* key2) { - if (((SDbVgVersion*)key1)->dbId < ((SDbVgVersion*)key2)->dbId) { - return -1; - } else if (((SDbVgVersion*)key1)->dbId > ((SDbVgVersion*)key2)->dbId) { - return 1; - } else { - return 0; - } -} - - -int32_t ctgMetaRentInit(SCtgRentMgmt *mgmt, uint32_t rentSec, int8_t type) { - mgmt->slotRIdx = 0; - mgmt->slotNum = rentSec / CTG_RENT_SLOT_SECOND; - mgmt->type = type; - - size_t msgSize = sizeof(SCtgRentSlot) * mgmt->slotNum; - - mgmt->slots = taosMemoryCalloc(1, msgSize); - if (NULL == mgmt->slots) { - qError("calloc %d failed", (int32_t)msgSize); - CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); - } - - qDebug("meta rent initialized, type:%d, slotNum:%d", type, mgmt->slotNum); - - return TSDB_CODE_SUCCESS; -} - - -int32_t ctgMetaRentAdd(SCtgRentMgmt *mgmt, void *meta, int64_t id, int32_t size) { - int16_t widx = abs((int)(id % mgmt->slotNum)); - - SCtgRentSlot *slot = &mgmt->slots[widx]; - int32_t code = 0; - - CTG_LOCK(CTG_WRITE, &slot->lock); - if (NULL == slot->meta) { - slot->meta = taosArrayInit(CTG_DEFAULT_RENT_SLOT_SIZE, size); - if (NULL == slot->meta) { - qError("taosArrayInit %d failed, id:%"PRIx64", slot idx:%d, type:%d", CTG_DEFAULT_RENT_SLOT_SIZE, id, widx, mgmt->type); - CTG_ERR_JRET(TSDB_CODE_CTG_MEM_ERROR); - } - } - - if (NULL == taosArrayPush(slot->meta, meta)) { - qError("taosArrayPush meta to rent failed, id:%"PRIx64", slot idx:%d, type:%d", id, widx, mgmt->type); - CTG_ERR_JRET(TSDB_CODE_CTG_MEM_ERROR); - } - - slot->needSort = true; - - qDebug("add meta to rent, id:%"PRIx64", slot idx:%d, type:%d", id, widx, mgmt->type); - -_return: - - CTG_UNLOCK(CTG_WRITE, &slot->lock); - CTG_RET(code); -} - -int32_t ctgMetaRentUpdate(SCtgRentMgmt *mgmt, void *meta, int64_t id, int32_t size, __compar_fn_t sortCompare, __compar_fn_t searchCompare) { - int16_t widx = abs((int)(id % mgmt->slotNum)); - - SCtgRentSlot *slot = &mgmt->slots[widx]; - int32_t code = 0; - - CTG_LOCK(CTG_WRITE, &slot->lock); - if (NULL == slot->meta) { - qError("empty meta slot, id:%"PRIx64", slot idx:%d, type:%d", id, widx, mgmt->type); - CTG_ERR_JRET(TSDB_CODE_CTG_INTERNAL_ERROR); - } - - if (slot->needSort) { - qDebug("meta slot before sorte, slot idx:%d, type:%d, size:%d", widx, mgmt->type, (int32_t)taosArrayGetSize(slot->meta)); - taosArraySort(slot->meta, sortCompare); - slot->needSort = false; - qDebug("meta slot sorted, slot idx:%d, type:%d, size:%d", widx, mgmt->type, (int32_t)taosArrayGetSize(slot->meta)); - } - - void *orig = taosArraySearch(slot->meta, &id, searchCompare, TD_EQ); - if (NULL == orig) { - qError("meta not found in slot, id:%"PRIx64", slot idx:%d, type:%d, size:%d", id, widx, mgmt->type, (int32_t)taosArrayGetSize(slot->meta)); - CTG_ERR_JRET(TSDB_CODE_CTG_INTERNAL_ERROR); - } - - memcpy(orig, meta, size); - - qDebug("meta in rent updated, id:%"PRIx64", slot idx:%d, type:%d", id, widx, mgmt->type); - -_return: - - CTG_UNLOCK(CTG_WRITE, &slot->lock); - - if (code) { - qWarn("meta in rent update failed, will try to add it, code:%x, id:%"PRIx64", slot idx:%d, type:%d", code, id, widx, mgmt->type); - CTG_RET(ctgMetaRentAdd(mgmt, meta, id, size)); - } - - CTG_RET(code); -} - -int32_t ctgMetaRentRemove(SCtgRentMgmt *mgmt, int64_t id, __compar_fn_t sortCompare, __compar_fn_t searchCompare) { - int16_t widx = abs((int)(id % mgmt->slotNum)); - - SCtgRentSlot *slot = &mgmt->slots[widx]; - int32_t code = 0; - - CTG_LOCK(CTG_WRITE, &slot->lock); - if (NULL == slot->meta) { - qError("empty meta slot, id:%"PRIx64", slot idx:%d, type:%d", id, widx, mgmt->type); - CTG_ERR_JRET(TSDB_CODE_CTG_INTERNAL_ERROR); - } - - if (slot->needSort) { - taosArraySort(slot->meta, sortCompare); - slot->needSort = false; - qDebug("meta slot sorted, slot idx:%d, type:%d", widx, mgmt->type); - } - - int32_t idx = taosArraySearchIdx(slot->meta, &id, searchCompare, TD_EQ); - if (idx < 0) { - qError("meta not found in slot, id:%"PRIx64", slot idx:%d, type:%d", id, widx, mgmt->type); - CTG_ERR_JRET(TSDB_CODE_CTG_INTERNAL_ERROR); - } - - taosArrayRemove(slot->meta, idx); - - qDebug("meta in rent removed, id:%"PRIx64", slot idx:%d, type:%d", id, widx, mgmt->type); - -_return: - - CTG_UNLOCK(CTG_WRITE, &slot->lock); - - CTG_RET(code); -} - - -int32_t ctgMetaRentGetImpl(SCtgRentMgmt *mgmt, void **res, uint32_t *num, int32_t size) { - int16_t ridx = atomic_add_fetch_16(&mgmt->slotRIdx, 1); - if (ridx >= mgmt->slotNum) { - ridx %= mgmt->slotNum; - atomic_store_16(&mgmt->slotRIdx, ridx); - } - - SCtgRentSlot *slot = &mgmt->slots[ridx]; - int32_t code = 0; - - CTG_LOCK(CTG_READ, &slot->lock); - if (NULL == slot->meta) { - qDebug("empty meta in slot:%d, type:%d", ridx, mgmt->type); - *num = 0; - goto _return; - } - - size_t metaNum = taosArrayGetSize(slot->meta); - if (metaNum <= 0) { - qDebug("no meta in slot:%d, type:%d", ridx, mgmt->type); - *num = 0; - goto _return; - } - - size_t msize = metaNum * size; - *res = taosMemoryMalloc(msize); - if (NULL == *res) { - qError("malloc %d failed", (int32_t)msize); - CTG_ERR_JRET(TSDB_CODE_CTG_MEM_ERROR); - } - - void *meta = taosArrayGet(slot->meta, 0); - - memcpy(*res, meta, msize); - - *num = (uint32_t)metaNum; - - qDebug("Got %d meta from rent, type:%d", (int32_t)metaNum, mgmt->type); - -_return: - - CTG_UNLOCK(CTG_READ, &slot->lock); - - CTG_RET(code); -} - -int32_t ctgMetaRentGet(SCtgRentMgmt *mgmt, void **res, uint32_t *num, int32_t size) { - while (true) { - int64_t msec = taosGetTimestampMs(); - int64_t lsec = atomic_load_64(&mgmt->lastReadMsec); - if ((msec - lsec) < CTG_RENT_SLOT_SECOND * 1000) { - *res = NULL; - *num = 0; - qDebug("too short time period to get expired meta, type:%d", mgmt->type); - return TSDB_CODE_SUCCESS; - } - - if (lsec != atomic_val_compare_exchange_64(&mgmt->lastReadMsec, lsec, msec)) { - continue; - } - - break; - } - - CTG_ERR_RET(ctgMetaRentGetImpl(mgmt, res, num, size)); - - return TSDB_CODE_SUCCESS; -} - -int32_t ctgAddNewDBCache(SCatalog *pCtg, const char *dbFName, uint64_t dbId) { - int32_t code = 0; - - SCtgDBCache newDBCache = {0}; - newDBCache.dbId = dbId; - - newDBCache.tbCache.metaCache = taosHashInit(gCtgMgmt.cfg.maxTblCacheNum, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY), true, HASH_ENTRY_LOCK); - if (NULL == newDBCache.tbCache.metaCache) { - ctgError("taosHashInit %d metaCache failed", gCtgMgmt.cfg.maxTblCacheNum); - CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); - } - - newDBCache.tbCache.stbCache = taosHashInit(gCtgMgmt.cfg.maxTblCacheNum, taosGetDefaultHashFunction(TSDB_DATA_TYPE_UBIGINT), true, HASH_ENTRY_LOCK); - if (NULL == newDBCache.tbCache.stbCache) { - ctgError("taosHashInit %d stbCache failed", gCtgMgmt.cfg.maxTblCacheNum); - CTG_ERR_JRET(TSDB_CODE_CTG_MEM_ERROR); - } - - code = taosHashPut(pCtg->dbCache, dbFName, strlen(dbFName), &newDBCache, sizeof(SCtgDBCache)); - if (code) { - if (HASH_NODE_EXIST(code)) { - ctgDebug("db already in cache, dbFName:%s", dbFName); - goto _return; - } - - ctgError("taosHashPut db to cache failed, dbFName:%s", dbFName); - CTG_ERR_JRET(TSDB_CODE_CTG_MEM_ERROR); - } - - CTG_CACHE_STAT_ADD(dbNum, 1); - - SDbVgVersion vgVersion = {.dbId = newDBCache.dbId, .vgVersion = -1}; - strncpy(vgVersion.dbFName, dbFName, sizeof(vgVersion.dbFName)); - - ctgDebug("db added to cache, dbFName:%s, dbId:%"PRIx64, dbFName, dbId); - - CTG_ERR_RET(ctgMetaRentAdd(&pCtg->dbRent, &vgVersion, dbId, sizeof(SDbVgVersion))); - - ctgDebug("db added to rent, dbFName:%s, vgVersion:%d, dbId:%"PRIx64, dbFName, vgVersion.vgVersion, dbId); - - return TSDB_CODE_SUCCESS; - -_return: - - ctgFreeDbCache(&newDBCache); - - CTG_RET(code); -} - - -void ctgRemoveStbRent(SCatalog* pCtg, SCtgTbMetaCache *cache) { - CTG_LOCK(CTG_WRITE, &cache->stbLock); - if (cache->stbCache) { - void *pIter = taosHashIterate(cache->stbCache, NULL); - while (pIter) { - uint64_t *suid = NULL; - suid = taosHashGetKey(pIter, NULL); - - if (TSDB_CODE_SUCCESS == ctgMetaRentRemove(&pCtg->stbRent, *suid, ctgStbVersionSortCompare, ctgStbVersionSearchCompare)) { - ctgDebug("stb removed from rent, suid:%"PRIx64, *suid); - } - - pIter = taosHashIterate(cache->stbCache, pIter); - } - } - CTG_UNLOCK(CTG_WRITE, &cache->stbLock); -} - - -int32_t ctgRemoveDB(SCatalog* pCtg, SCtgDBCache *dbCache, const char* dbFName) { - uint64_t dbId = dbCache->dbId; - - ctgInfo("start to remove db from cache, dbFName:%s, dbId:%"PRIx64, dbFName, dbCache->dbId); - - atomic_store_8(&dbCache->deleted, 1); - - ctgRemoveStbRent(pCtg, &dbCache->tbCache); - - ctgFreeDbCache(dbCache); - - CTG_ERR_RET(ctgMetaRentRemove(&pCtg->dbRent, dbCache->dbId, ctgDbVgVersionSortCompare, ctgDbVgVersionSearchCompare)); - - ctgDebug("db removed from rent, dbFName:%s, dbId:%"PRIx64, dbFName, dbCache->dbId); - - if (taosHashRemove(pCtg->dbCache, dbFName, strlen(dbFName))) { - ctgInfo("taosHashRemove from dbCache failed, may be removed, dbFName:%s", dbFName); - CTG_ERR_RET(TSDB_CODE_CTG_DB_DROPPED); - } - - CTG_CACHE_STAT_SUB(dbNum, 1); - - ctgInfo("db removed from cache, dbFName:%s, dbId:%"PRIx64, dbFName, dbId); - - return TSDB_CODE_SUCCESS; -} - - -int32_t ctgGetAddDBCache(SCatalog* pCtg, const char *dbFName, uint64_t dbId, SCtgDBCache **pCache) { - int32_t code = 0; - SCtgDBCache *dbCache = NULL; - ctgGetDBCache(pCtg, dbFName, &dbCache); - - if (dbCache) { - // TODO OPEN IT -#if 0 - if (dbCache->dbId == dbId) { - *pCache = dbCache; - return TSDB_CODE_SUCCESS; - } -#else - if (0 == dbId) { - *pCache = dbCache; - return TSDB_CODE_SUCCESS; - } - - if (dbId && (dbCache->dbId == 0)) { - dbCache->dbId = dbId; - *pCache = dbCache; - return TSDB_CODE_SUCCESS; - } - - if (dbCache->dbId == dbId) { - *pCache = dbCache; - return TSDB_CODE_SUCCESS; - } -#endif - CTG_ERR_RET(ctgRemoveDB(pCtg, dbCache, dbFName)); - } - - CTG_ERR_RET(ctgAddNewDBCache(pCtg, dbFName, dbId)); - - ctgGetDBCache(pCtg, dbFName, &dbCache); - - *pCache = dbCache; - - return TSDB_CODE_SUCCESS; -} - - -int32_t ctgUpdateDBVgInfo(SCatalog* pCtg, const char* dbFName, uint64_t dbId, SDBVgInfo** pDbInfo) { - int32_t code = 0; - SDBVgInfo* dbInfo = *pDbInfo; - - if (NULL == dbInfo->vgHash) { - return TSDB_CODE_SUCCESS; - } - - if (dbInfo->vgVersion < 0 || taosHashGetSize(dbInfo->vgHash) <= 0) { - ctgError("invalid db vgInfo, dbFName:%s, vgHash:%p, vgVersion:%d, vgHashSize:%d", - dbFName, dbInfo->vgHash, dbInfo->vgVersion, taosHashGetSize(dbInfo->vgHash)); - CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); - } - - bool newAdded = false; - SDbVgVersion vgVersion = {.dbId = dbId, .vgVersion = dbInfo->vgVersion, .numOfTable = dbInfo->numOfTable}; - - SCtgDBCache *dbCache = NULL; - CTG_ERR_RET(ctgGetAddDBCache(pCtg, dbFName, dbId, &dbCache)); - if (NULL == dbCache) { - ctgInfo("conflict db update, ignore this update, dbFName:%s, dbId:%"PRIx64, dbFName, dbId); - CTG_ERR_RET(TSDB_CODE_CTG_INTERNAL_ERROR); - } - - SDBVgInfo *vgInfo = NULL; - CTG_ERR_RET(ctgWAcquireVgInfo(pCtg, dbCache)); - - if (dbCache->vgInfo) { - if (dbInfo->vgVersion < dbCache->vgInfo->vgVersion) { - ctgDebug("db vgVersion is old, dbFName:%s, vgVersion:%d, currentVersion:%d", dbFName, dbInfo->vgVersion, dbCache->vgInfo->vgVersion); - ctgWReleaseVgInfo(dbCache); - - return TSDB_CODE_SUCCESS; - } - - if (dbInfo->vgVersion == dbCache->vgInfo->vgVersion && dbInfo->numOfTable == dbCache->vgInfo->numOfTable) { - ctgDebug("no new db vgVersion or numOfTable, dbFName:%s, vgVersion:%d, numOfTable:%d", dbFName, dbInfo->vgVersion, dbInfo->numOfTable); - ctgWReleaseVgInfo(dbCache); - - return TSDB_CODE_SUCCESS; - } - - ctgFreeVgInfo(dbCache->vgInfo); - } - - dbCache->vgInfo = dbInfo; - - *pDbInfo = NULL; - - ctgDebug("db vgInfo updated, dbFName:%s, vgVersion:%d, dbId:%"PRIx64, dbFName, vgVersion.vgVersion, vgVersion.dbId); - - ctgWReleaseVgInfo(dbCache); - - dbCache = NULL; - - strncpy(vgVersion.dbFName, dbFName, sizeof(vgVersion.dbFName)); - CTG_ERR_RET(ctgMetaRentUpdate(&pCtg->dbRent, &vgVersion, vgVersion.dbId, sizeof(SDbVgVersion), ctgDbVgVersionSortCompare, ctgDbVgVersionSearchCompare)); - - CTG_RET(code); -} - - -int32_t ctgUpdateTblMeta(SCatalog *pCtg, SCtgDBCache *dbCache, char *dbFName, uint64_t dbId, char *tbName, STableMeta *meta, int32_t metaSize) { - SCtgTbMetaCache *tbCache = &dbCache->tbCache; - - CTG_LOCK(CTG_READ, &tbCache->metaLock); - if (dbCache->deleted || NULL == tbCache->metaCache || NULL == tbCache->stbCache) { - CTG_UNLOCK(CTG_READ, &tbCache->metaLock); - ctgError("db is dropping, dbId:%"PRIx64, dbCache->dbId); - CTG_ERR_RET(TSDB_CODE_CTG_DB_DROPPED); - } - - int8_t origType = 0; - uint64_t origSuid = 0; - bool isStb = meta->tableType == TSDB_SUPER_TABLE; - STableMeta *orig = taosHashGet(tbCache->metaCache, tbName, strlen(tbName)); - if (orig) { - origType = orig->tableType; - - if (origType == meta->tableType && orig->uid == meta->uid && orig->sversion >= meta->sversion && orig->tversion >= meta->tversion) { - CTG_UNLOCK(CTG_READ, &tbCache->metaLock); - return TSDB_CODE_SUCCESS; - } - - if (origType == TSDB_SUPER_TABLE) { - if ((!isStb) || orig->suid != meta->suid) { - CTG_LOCK(CTG_WRITE, &tbCache->stbLock); - if (taosHashRemove(tbCache->stbCache, &orig->suid, sizeof(orig->suid))) { - ctgError("stb not exist in stbCache, dbFName:%s, stb:%s, suid:%"PRIx64, dbFName, tbName, orig->suid); - } else { - CTG_CACHE_STAT_SUB(stblNum, 1); - } - CTG_UNLOCK(CTG_WRITE, &tbCache->stbLock); - - ctgDebug("stb removed from stbCache, dbFName:%s, stb:%s, suid:%"PRIx64, dbFName, tbName, orig->suid); - - ctgMetaRentRemove(&pCtg->stbRent, orig->suid, ctgStbVersionSortCompare, ctgStbVersionSearchCompare); - } - - origSuid = orig->suid; - } - } - - if (isStb) { - CTG_LOCK(CTG_WRITE, &tbCache->stbLock); - } - - if (taosHashPut(tbCache->metaCache, tbName, strlen(tbName), meta, metaSize) != 0) { - if (isStb) { - CTG_UNLOCK(CTG_WRITE, &tbCache->stbLock); - } - - CTG_UNLOCK(CTG_READ, &tbCache->metaLock); - ctgError("taosHashPut tbmeta to cache failed, dbFName:%s, tbName:%s, tbType:%d", dbFName, tbName, meta->tableType); - CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); - } - - if (NULL == orig) { - CTG_CACHE_STAT_ADD(tblNum, 1); - } - - ctgDebug("tbmeta updated to cache, dbFName:%s, tbName:%s, tbType:%d, suid:%" PRIx64, dbFName, tbName, meta->tableType, meta->suid); - ctgdShowTableMeta(pCtg, tbName, meta); - - if (!isStb) { - CTG_UNLOCK(CTG_READ, &tbCache->metaLock); - return TSDB_CODE_SUCCESS; - } - - STableMeta *tbMeta = taosHashGet(tbCache->metaCache, tbName, strlen(tbName)); - if (taosHashPut(tbCache->stbCache, &meta->suid, sizeof(meta->suid), &tbMeta, POINTER_BYTES) != 0) { - CTG_UNLOCK(CTG_WRITE, &tbCache->stbLock); - CTG_UNLOCK(CTG_READ, &tbCache->metaLock); - ctgError("taosHashPut stable to stable cache failed, suid:%"PRIx64, meta->suid); - CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); - } - - CTG_CACHE_STAT_ADD(stblNum, 1); - - CTG_UNLOCK(CTG_WRITE, &tbCache->stbLock); - - CTG_UNLOCK(CTG_READ, &tbCache->metaLock); +SCatalogMgmt gCtgMgmt = {0}; - ctgDebug("stb updated to stbCache, dbFName:%s, tbName:%s, tbType:%d, suid:%" PRIx64 ",ma:%p", dbFName, tbName, meta->tableType, meta->suid, tbMeta); - SSTableMetaVersion metaRent = {.dbId = dbId, .suid = meta->suid, .sversion = meta->sversion, .tversion = meta->tversion}; - strcpy(metaRent.dbFName, dbFName); - strcpy(metaRent.stbName, tbName); - CTG_ERR_RET(ctgMetaRentAdd(&pCtg->stbRent, &metaRent, metaRent.suid, sizeof(SSTableMetaVersion))); +int32_t ctgRemoveTbMetaFromCache(SCatalog* pCtg, SName* pTableName, bool syncReq) { + int32_t code = 0; + STableMeta *tblMeta = NULL; + SCtgTbMetaCtx tbCtx = {0}; + tbCtx.flag = CTG_FLAG_UNKNOWN_STB; + tbCtx.pName = pTableName; - return TSDB_CODE_SUCCESS; -} + CTG_ERR_JRET(ctgReadTbMetaFromCache(pCtg, &tbCtx, &tblMeta)); -int32_t ctgCloneVgInfo(SDBVgInfo *src, SDBVgInfo **dst) { - *dst = taosMemoryMalloc(sizeof(SDBVgInfo)); - if (NULL == *dst) { - qError("malloc %d failed", (int32_t)sizeof(SDBVgInfo)); - CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); - } - - memcpy(*dst, src, sizeof(SDBVgInfo)); - - size_t hashSize = taosHashGetSize(src->vgHash); - (*dst)->vgHash = taosHashInit(hashSize, taosGetDefaultHashFunction(TSDB_DATA_TYPE_INT), true, HASH_ENTRY_LOCK); - if (NULL == (*dst)->vgHash) { - qError("taosHashInit %d failed", (int32_t)hashSize); - taosMemoryFreeClear(*dst); - CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); + if (NULL == tblMeta) { + ctgDebug("table already not in cache, db:%s, tblName:%s", pTableName->dbname, pTableName->tname); + return TSDB_CODE_SUCCESS; } - int32_t *vgId = NULL; - void *pIter = taosHashIterate(src->vgHash, NULL); - while (pIter) { - vgId = taosHashGetKey(pIter, NULL); - - if (taosHashPut((*dst)->vgHash, (void *)vgId, sizeof(int32_t), pIter, sizeof(SVgroupInfo))) { - qError("taosHashPut failed, hashSize:%d", (int32_t)hashSize); - taosHashCancelIterate(src->vgHash, pIter); - taosHashCleanup((*dst)->vgHash); - taosMemoryFreeClear(*dst); - CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); - } - - pIter = taosHashIterate(src->vgHash, pIter); + char dbFName[TSDB_DB_FNAME_LEN]; + tNameGetFullDbName(pTableName, dbFName); + + if (TSDB_SUPER_TABLE == tblMeta->tableType) { + CTG_ERR_JRET(ctgPutRmStbToQueue(pCtg, dbFName, tbCtx.tbInfo.dbId, pTableName->tname, tblMeta->suid, syncReq)); + } else { + CTG_ERR_JRET(ctgPutRmTbToQueue(pCtg, dbFName, tbCtx.tbInfo.dbId, pTableName->tname, syncReq)); } + +_return: + taosMemoryFreeClear(tblMeta); - return TSDB_CODE_SUCCESS; + CTG_RET(code); } - - -int32_t ctgGetDBVgInfo(SCatalog* pCtg, void *pRpc, const SEpSet* pMgmtEps, const char* dbFName, SCtgDBCache** dbCache, SDBVgInfo **pInfo) { - bool inCache = false; +int32_t ctgGetDBVgInfo(SCatalog* pCtg, void *pTrans, const SEpSet* pMgmtEps, const char* dbFName, SCtgDBCache** dbCache, SDBVgInfo **pInfo) { int32_t code = 0; - CTG_ERR_RET(ctgAcquireVgInfoFromCache(pCtg, dbFName, dbCache, &inCache)); + CTG_ERR_RET(ctgAcquireVgInfoFromCache(pCtg, dbFName, dbCache)); - if (inCache) { + if (*dbCache) { return TSDB_CODE_SUCCESS; } @@ -1809,19 +68,11 @@ int32_t ctgGetDBVgInfo(SCatalog* pCtg, void *pRpc, const SEpSet* pMgmtEps, const tstrncpy(input.db, dbFName, tListLen(input.db)); input.vgVersion = CTG_DEFAULT_INVALID_VERSION; - code = ctgGetDBVgInfoFromMnode(pCtg, pRpc, pMgmtEps, &input, &DbOut); - if (code) { - if (CTG_DB_NOT_EXIST(code) && input.vgVersion > CTG_DEFAULT_INVALID_VERSION) { - ctgDebug("db no longer exist, dbFName:%s, dbId:%" PRIx64, input.db, input.dbId); - ctgPushRmDBMsgInQueue(pCtg, input.db, input.dbId); - } - - CTG_ERR_RET(code); - } + CTG_ERR_RET(ctgGetDBVgInfoFromMnode(pCtg, pTrans, pMgmtEps, &input, &DbOut, NULL)); CTG_ERR_JRET(ctgCloneVgInfo(DbOut.dbVgroup, pInfo)); - CTG_ERR_RET(ctgPushUpdateVgMsgInQueue(pCtg, dbFName, DbOut.dbId, DbOut.dbVgroup, false)); + CTG_ERR_RET(ctgPutUpdateVgToQueue(pCtg, dbFName, DbOut.dbId, DbOut.dbVgroup, false)); return TSDB_CODE_SUCCESS; @@ -1833,18 +84,17 @@ _return: CTG_RET(code); } -int32_t ctgRefreshDBVgInfo(SCatalog* pCtg, void *pRpc, const SEpSet* pMgmtEps, const char* dbFName) { - bool inCache = false; +int32_t ctgRefreshDBVgInfo(SCatalog* pCtg, void *pTrans, const SEpSet* pMgmtEps, const char* dbFName) { int32_t code = 0; SCtgDBCache* dbCache = NULL; - CTG_ERR_RET(ctgAcquireVgInfoFromCache(pCtg, dbFName, &dbCache, &inCache)); + CTG_ERR_RET(ctgAcquireVgInfoFromCache(pCtg, dbFName, &dbCache)); SUseDbOutput DbOut = {0}; SBuildUseDBInput input = {0}; tstrncpy(input.db, dbFName, tListLen(input.db)); - if (inCache) { + if (NULL != dbCache) { input.dbId = dbCache->dbId; ctgReleaseVgInfo(dbCache); @@ -1854,59 +104,29 @@ int32_t ctgRefreshDBVgInfo(SCatalog* pCtg, void *pRpc, const SEpSet* pMgmtEps, c input.vgVersion = CTG_DEFAULT_INVALID_VERSION; input.numOfTable = 0; - code = ctgGetDBVgInfoFromMnode(pCtg, pRpc, pMgmtEps, &input, &DbOut); + code = ctgGetDBVgInfoFromMnode(pCtg, pTrans, pMgmtEps, &input, &DbOut, NULL); if (code) { - if (CTG_DB_NOT_EXIST(code) && inCache) { + if (CTG_DB_NOT_EXIST(code) && (NULL != dbCache)) { ctgDebug("db no longer exist, dbFName:%s, dbId:%" PRIx64, input.db, input.dbId); - ctgPushRmDBMsgInQueue(pCtg, input.db, input.dbId); + ctgPutRmDBToQueue(pCtg, input.db, input.dbId); } CTG_ERR_RET(code); } - CTG_ERR_RET(ctgPushUpdateVgMsgInQueue(pCtg, dbFName, DbOut.dbId, DbOut.dbVgroup, true)); - - return TSDB_CODE_SUCCESS; -} - - - -int32_t ctgCloneMetaOutput(STableMetaOutput *output, STableMetaOutput **pOutput) { - *pOutput = taosMemoryMalloc(sizeof(STableMetaOutput)); - if (NULL == *pOutput) { - qError("malloc %d failed", (int32_t)sizeof(STableMetaOutput)); - CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); - } - - memcpy(*pOutput, output, sizeof(STableMetaOutput)); - - if (output->tbMeta) { - int32_t metaSize = CTG_META_SIZE(output->tbMeta); - (*pOutput)->tbMeta = taosMemoryMalloc(metaSize); - if (NULL == (*pOutput)->tbMeta) { - qError("malloc %d failed", (int32_t)sizeof(STableMetaOutput)); - taosMemoryFreeClear(*pOutput); - CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); - } - - memcpy((*pOutput)->tbMeta, output->tbMeta, metaSize); - } + CTG_ERR_RET(ctgPutUpdateVgToQueue(pCtg, dbFName, DbOut.dbId, DbOut.dbVgroup, true)); return TSDB_CODE_SUCCESS; } -int32_t ctgRefreshTblMeta(SCatalog* pCtg, void *pTrans, const SEpSet* pMgmtEps, const SName* pTableName, int32_t flag, STableMetaOutput **pOutput, bool syncReq) { - if (NULL == pCtg || NULL == pTrans || NULL == pMgmtEps || NULL == pTableName) { - CTG_ERR_RET(TSDB_CODE_CTG_INVALID_INPUT); - } - +int32_t ctgRefreshTbMeta(CTG_PARAMS, SCtgTbMetaCtx* ctx, STableMetaOutput **pOutput, bool syncReq) { SVgroupInfo vgroupInfo = {0}; int32_t code = 0; - if (!CTG_FLAG_IS_SYS_DB(flag)) { - CTG_ERR_RET(catalogGetTableHashVgroup(pCtg, pTrans, pMgmtEps, pTableName, &vgroupInfo)); + if (!CTG_FLAG_IS_SYS_DB(ctx->flag)) { + CTG_ERR_RET(catalogGetTableHashVgroup(CTG_PARAMS_LIST(), ctx->pName, &vgroupInfo)); } STableMetaOutput moutput = {0}; @@ -1916,39 +136,39 @@ int32_t ctgRefreshTblMeta(SCatalog* pCtg, void *pTrans, const SEpSet* pMgmtEps, CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); } - if (CTG_FLAG_IS_SYS_DB(flag)) { - ctgDebug("will refresh tbmeta, supposed in information_schema, tbName:%s", tNameGetTableName(pTableName)); + if (CTG_FLAG_IS_SYS_DB(ctx->flag)) { + ctgDebug("will refresh tbmeta, supposed in information_schema, tbName:%s", tNameGetTableName(ctx->pName)); - CTG_ERR_JRET(ctgGetTableMetaFromMnodeImpl(pCtg, pTrans, pMgmtEps, (char *)pTableName->dbname, (char *)pTableName->tname, output)); - } else if (CTG_FLAG_IS_STB(flag)) { - ctgDebug("will refresh tbmeta, supposed to be stb, tbName:%s", tNameGetTableName(pTableName)); + CTG_ERR_JRET(ctgGetTbMetaFromMnodeImpl(CTG_PARAMS_LIST(), (char *)ctx->pName->dbname, (char *)ctx->pName->tname, output, NULL)); + } else if (CTG_FLAG_IS_STB(ctx->flag)) { + ctgDebug("will refresh tbmeta, supposed to be stb, tbName:%s", tNameGetTableName(ctx->pName)); // if get from mnode failed, will not try vnode - CTG_ERR_JRET(ctgGetTableMetaFromMnode(pCtg, pTrans, pMgmtEps, pTableName, output)); + CTG_ERR_JRET(ctgGetTbMetaFromMnode(CTG_PARAMS_LIST(), ctx->pName, output, NULL)); if (CTG_IS_META_NULL(output->metaType)) { - CTG_ERR_JRET(ctgGetTableMetaFromVnode(pCtg, pTrans, pMgmtEps, pTableName, &vgroupInfo, output)); + CTG_ERR_JRET(ctgGetTbMetaFromVnode(CTG_PARAMS_LIST(), ctx->pName, &vgroupInfo, output, NULL)); } } else { - ctgDebug("will refresh tbmeta, not supposed to be stb, tbName:%s, flag:%d", tNameGetTableName(pTableName), flag); + ctgDebug("will refresh tbmeta, not supposed to be stb, tbName:%s, flag:%d", tNameGetTableName(ctx->pName), ctx->flag); // if get from vnode failed or no table meta, will not try mnode - CTG_ERR_JRET(ctgGetTableMetaFromVnode(pCtg, pTrans, pMgmtEps, pTableName, &vgroupInfo, output)); + CTG_ERR_JRET(ctgGetTbMetaFromVnode(CTG_PARAMS_LIST(), ctx->pName, &vgroupInfo, output, NULL)); if (CTG_IS_META_TABLE(output->metaType) && TSDB_SUPER_TABLE == output->tbMeta->tableType) { - ctgDebug("will continue to refresh tbmeta since got stb, tbName:%s", tNameGetTableName(pTableName)); + ctgDebug("will continue to refresh tbmeta since got stb, tbName:%s", tNameGetTableName(ctx->pName)); taosMemoryFreeClear(output->tbMeta); - CTG_ERR_JRET(ctgGetTableMetaFromMnodeImpl(pCtg, pTrans, pMgmtEps, output->dbFName, output->tbName, output)); + CTG_ERR_JRET(ctgGetTbMetaFromMnodeImpl(CTG_PARAMS_LIST(), output->dbFName, output->tbName, output, NULL)); } else if (CTG_IS_META_BOTH(output->metaType)) { int32_t exist = 0; - if (!CTG_FLAG_IS_FORCE_UPDATE(flag)) { - CTG_ERR_JRET(ctgIsTableMetaExistInCache(pCtg, output->dbFName, output->tbName, &exist)); + if (!CTG_FLAG_IS_FORCE_UPDATE(ctx->flag)) { + CTG_ERR_JRET(ctgTbMetaExistInCache(pCtg, output->dbFName, output->tbName, &exist)); } if (0 == exist) { - CTG_ERR_JRET(ctgGetTableMetaFromMnodeImpl(pCtg, pTrans, pMgmtEps, output->dbFName, output->tbName, &moutput)); + CTG_ERR_JRET(ctgGetTbMetaFromMnodeImpl(CTG_PARAMS_LIST(), output->dbFName, output->tbName, &moutput, NULL)); if (CTG_IS_META_NULL(moutput.metaType)) { SET_META_TYPE_NULL(output->metaType); @@ -1962,486 +182,197 @@ int32_t ctgRefreshTblMeta(SCatalog* pCtg, void *pTrans, const SEpSet* pMgmtEps, SET_META_TYPE_CTABLE(output->metaType); } - } - } - - if (CTG_IS_META_NULL(output->metaType)) { - ctgError("no tbmeta got, tbNmae:%s", tNameGetTableName(pTableName)); - catalogRemoveTableMeta(pCtg, pTableName); - CTG_ERR_JRET(CTG_ERR_CODE_TABLE_NOT_EXIST); - } - - if (CTG_IS_META_TABLE(output->metaType)) { - ctgDebug("tbmeta got, dbFName:%s, tbName:%s, tbType:%d", output->dbFName, output->tbName, output->tbMeta->tableType); - } else { - ctgDebug("tbmeta got, dbFName:%s, tbName:%s, tbType:%d, stbMetaGot:%d", output->dbFName, output->ctbName, output->ctbMeta.tableType, CTG_IS_META_BOTH(output->metaType)); - } - - if (pOutput) { - CTG_ERR_JRET(ctgCloneMetaOutput(output, pOutput)); - } - - CTG_ERR_JRET(ctgPushUpdateTblMsgInQueue(pCtg, output, syncReq)); - - return TSDB_CODE_SUCCESS; - -_return: - - taosMemoryFreeClear(output->tbMeta); - taosMemoryFreeClear(output); - - CTG_RET(code); -} - -int32_t ctgGetTableMeta(SCatalog* pCtg, void *pRpc, const SEpSet* pMgmtEps, const SName* pTableName, STableMeta** pTableMeta, int32_t flag) { - if (NULL == pCtg || NULL == pRpc || NULL == pMgmtEps || NULL == pTableName || NULL == pTableMeta) { - CTG_ERR_RET(TSDB_CODE_CTG_INVALID_INPUT); - } - - bool inCache = false; - int32_t code = 0; - uint64_t dbId = 0; - uint64_t suid = 0; - STableMetaOutput *output = NULL; - - if (CTG_IS_SYS_DBNAME(pTableName->dbname)) { - CTG_FLAG_SET_SYS_DB(flag); - } - - CTG_ERR_RET(ctgGetTableMetaFromCache(pCtg, pTableName, pTableMeta, &inCache, flag, &dbId)); - - int32_t tbType = 0; - - if (inCache) { - if (CTG_FLAG_MATCH_STB(flag, (*pTableMeta)->tableType) && ((!CTG_FLAG_IS_FORCE_UPDATE(flag)) || (CTG_FLAG_IS_SYS_DB(flag)))) { - goto _return; - } - - tbType = (*pTableMeta)->tableType; - suid = (*pTableMeta)->suid; - - taosMemoryFreeClear(*pTableMeta); - } - - if (CTG_FLAG_IS_UNKNOWN_STB(flag)) { - CTG_FLAG_SET_STB(flag, tbType); - } - - - while (true) { - CTG_ERR_JRET(ctgRefreshTblMeta(pCtg, pRpc, pMgmtEps, pTableName, flag, &output, false)); - - if (CTG_IS_META_TABLE(output->metaType)) { - *pTableMeta = output->tbMeta; - goto _return; - } - - if (CTG_IS_META_BOTH(output->metaType)) { - memcpy(output->tbMeta, &output->ctbMeta, sizeof(output->ctbMeta)); - - *pTableMeta = output->tbMeta; - goto _return; - } - - if ((!CTG_IS_META_CTABLE(output->metaType)) || output->tbMeta) { - ctgError("invalid metaType:%d", output->metaType); - taosMemoryFreeClear(output->tbMeta); - CTG_ERR_JRET(TSDB_CODE_CTG_INTERNAL_ERROR); - } - - // HANDLE ONLY CHILD TABLE META - - SName stbName = *pTableName; - strcpy(stbName.tname, output->tbName); - - taosMemoryFreeClear(output->tbMeta); - - CTG_ERR_JRET(ctgGetTableMetaFromCache(pCtg, &stbName, pTableMeta, &inCache, flag, NULL)); - if (!inCache) { - ctgDebug("stb no longer exist, dbFName:%s, tbName:%s", output->dbFName, pTableName->tname); - - continue; - } - - memcpy(*pTableMeta, &output->ctbMeta, sizeof(output->ctbMeta)); - - break; - } - -_return: - - if (CTG_TABLE_NOT_EXIST(code) && inCache) { - char dbFName[TSDB_DB_FNAME_LEN] = {0}; - if (CTG_FLAG_IS_SYS_DB(flag)) { - strcpy(dbFName, pTableName->dbname); - } else { - tNameGetFullDbName(pTableName, dbFName); - } - - if (TSDB_SUPER_TABLE == tbType) { - ctgPushRmStbMsgInQueue(pCtg, dbFName, dbId, pTableName->tname, suid, false); - } else { - ctgPushRmTblMsgInQueue(pCtg, dbFName, dbId, pTableName->tname, false); - } - } - - taosMemoryFreeClear(output); - - if (*pTableMeta) { - ctgDebug("tbmeta returned, tbName:%s, tbType:%d", pTableName->tname, (*pTableMeta)->tableType); - ctgdShowTableMeta(pCtg, pTableName->tname, *pTableMeta); - } - - CTG_RET(code); -} - -int32_t ctgChkAuth(SCatalog* pCtg, void *pRpc, const SEpSet* pMgmtEps, const char* user, const char* dbFName, AUTH_TYPE type, bool *pass) { - bool inCache = false; - int32_t code = 0; - - *pass = false; - - CTG_ERR_RET(ctgChkAuthFromCache(pCtg, user, dbFName, type, &inCache, pass)); - - if (inCache) { - return TSDB_CODE_SUCCESS; - } - - SGetUserAuthRsp authRsp = {0}; - CTG_ERR_RET(ctgGetUserDbAuthFromMnode(pCtg, pRpc, pMgmtEps, user, &authRsp)); - - if (authRsp.superAuth) { - *pass = true; - goto _return; - } - - if (authRsp.createdDbs && taosHashGet(authRsp.createdDbs, dbFName, strlen(dbFName))) { - *pass = true; - goto _return; - } - - if (authRsp.readDbs && taosHashGet(authRsp.readDbs, dbFName, strlen(dbFName)) && type == AUTH_TYPE_READ) { - *pass = true; - } - - if (authRsp.writeDbs && taosHashGet(authRsp.writeDbs, dbFName, strlen(dbFName)) && type == AUTH_TYPE_WRITE) { - *pass = true; - } - -_return: - - ctgPushUpdateUserMsgInQueue(pCtg, &authRsp, false); - - return TSDB_CODE_SUCCESS; -} - - -int32_t ctgActUpdateVg(SCtgMetaAction *action) { - int32_t code = 0; - SCtgUpdateVgMsg *msg = action->data; - - CTG_ERR_JRET(ctgUpdateDBVgInfo(msg->pCtg, msg->dbFName, msg->dbId, &msg->dbInfo)); - -_return: - - ctgFreeVgInfo(msg->dbInfo); - taosMemoryFreeClear(msg); - - CTG_RET(code); -} - -int32_t ctgActRemoveDB(SCtgMetaAction *action) { - int32_t code = 0; - SCtgRemoveDBMsg *msg = action->data; - SCatalog* pCtg = msg->pCtg; - - SCtgDBCache *dbCache = NULL; - ctgGetDBCache(msg->pCtg, msg->dbFName, &dbCache); - if (NULL == dbCache) { - goto _return; - } - - if (dbCache->dbId != msg->dbId) { - ctgInfo("dbId already updated, dbFName:%s, dbId:%"PRIx64 ", targetId:%"PRIx64, msg->dbFName, dbCache->dbId, msg->dbId); - goto _return; - } - - CTG_ERR_JRET(ctgRemoveDB(pCtg, dbCache, msg->dbFName)); - -_return: - - taosMemoryFreeClear(msg); - - CTG_RET(code); -} - - -int32_t ctgActUpdateTbl(SCtgMetaAction *action) { - int32_t code = 0; - SCtgUpdateTblMsg *msg = action->data; - SCatalog* pCtg = msg->pCtg; - STableMetaOutput* output = msg->output; - SCtgDBCache *dbCache = NULL; - - if ((!CTG_IS_META_CTABLE(output->metaType)) && NULL == output->tbMeta) { - ctgError("no valid tbmeta got from meta rsp, dbFName:%s, tbName:%s", output->dbFName, output->tbName); - CTG_ERR_JRET(TSDB_CODE_CTG_INTERNAL_ERROR); - } - - if (CTG_IS_META_BOTH(output->metaType) && TSDB_SUPER_TABLE != output->tbMeta->tableType) { - ctgError("table type error, expected:%d, actual:%d", TSDB_SUPER_TABLE, output->tbMeta->tableType); - CTG_ERR_JRET(TSDB_CODE_CTG_INTERNAL_ERROR); - } - - CTG_ERR_JRET(ctgGetAddDBCache(pCtg, output->dbFName, output->dbId, &dbCache)); - if (NULL == dbCache) { - ctgInfo("conflict db update, ignore this update, dbFName:%s, dbId:%"PRIx64, output->dbFName, output->dbId); - CTG_ERR_JRET(TSDB_CODE_CTG_INTERNAL_ERROR); - } - - if (CTG_IS_META_TABLE(output->metaType) || CTG_IS_META_BOTH(output->metaType)) { - int32_t metaSize = CTG_META_SIZE(output->tbMeta); - - CTG_ERR_JRET(ctgUpdateTblMeta(pCtg, dbCache, output->dbFName, output->dbId, output->tbName, output->tbMeta, metaSize)); - } - - if (CTG_IS_META_CTABLE(output->metaType) || CTG_IS_META_BOTH(output->metaType)) { - CTG_ERR_JRET(ctgUpdateTblMeta(pCtg, dbCache, output->dbFName, output->dbId, output->ctbName, (STableMeta *)&output->ctbMeta, sizeof(output->ctbMeta))); - } - -_return: - - if (output) { - taosMemoryFreeClear(output->tbMeta); - taosMemoryFreeClear(output); - } - - taosMemoryFreeClear(msg); - - CTG_RET(code); -} - - -int32_t ctgActRemoveStb(SCtgMetaAction *action) { - int32_t code = 0; - SCtgRemoveStbMsg *msg = action->data; - SCatalog* pCtg = msg->pCtg; - - SCtgDBCache *dbCache = NULL; - ctgGetDBCache(pCtg, msg->dbFName, &dbCache); - if (NULL == dbCache) { - return TSDB_CODE_SUCCESS; + } } - if (msg->dbId && (dbCache->dbId != msg->dbId)) { - ctgDebug("dbId already modified, dbFName:%s, current:%"PRIx64", dbId:%"PRIx64", stb:%s, suid:%"PRIx64, msg->dbFName, dbCache->dbId, msg->dbId, msg->stbName, msg->suid); - return TSDB_CODE_SUCCESS; + if (CTG_IS_META_NULL(output->metaType)) { + ctgError("no tbmeta got, tbNmae:%s", tNameGetTableName(ctx->pName)); + ctgRemoveTbMetaFromCache(pCtg, ctx->pName, false); + CTG_ERR_JRET(CTG_ERR_CODE_TABLE_NOT_EXIST); } - - CTG_LOCK(CTG_WRITE, &dbCache->tbCache.stbLock); - if (taosHashRemove(dbCache->tbCache.stbCache, &msg->suid, sizeof(msg->suid))) { - ctgDebug("stb not exist in stbCache, may be removed, dbFName:%s, stb:%s, suid:%"PRIx64, msg->dbFName, msg->stbName, msg->suid); + + if (CTG_IS_META_TABLE(output->metaType)) { + ctgDebug("tbmeta got, dbFName:%s, tbName:%s, tbType:%d", output->dbFName, output->tbName, output->tbMeta->tableType); } else { - CTG_CACHE_STAT_SUB(stblNum, 1); + ctgDebug("tbmeta got, dbFName:%s, tbName:%s, tbType:%d, stbMetaGot:%d", output->dbFName, output->ctbName, output->ctbMeta.tableType, CTG_IS_META_BOTH(output->metaType)); } - CTG_LOCK(CTG_READ, &dbCache->tbCache.metaLock); - if (taosHashRemove(dbCache->tbCache.metaCache, msg->stbName, strlen(msg->stbName))) { - ctgError("stb not exist in cache, dbFName:%s, stb:%s, suid:%"PRIx64, msg->dbFName, msg->stbName, msg->suid); - } else { - CTG_CACHE_STAT_SUB(tblNum, 1); + if (pOutput) { + CTG_ERR_JRET(ctgCloneMetaOutput(output, pOutput)); } - CTG_UNLOCK(CTG_READ, &dbCache->tbCache.metaLock); - - CTG_UNLOCK(CTG_WRITE, &dbCache->tbCache.stbLock); - - ctgInfo("stb removed from cache, dbFName:%s, stbName:%s, suid:%"PRIx64, msg->dbFName, msg->stbName, msg->suid); - CTG_ERR_JRET(ctgMetaRentRemove(&msg->pCtg->stbRent, msg->suid, ctgStbVersionSortCompare, ctgStbVersionSearchCompare)); - - ctgDebug("stb removed from rent, dbFName:%s, stbName:%s, suid:%"PRIx64, msg->dbFName, msg->stbName, msg->suid); - + CTG_ERR_JRET(ctgPutUpdateTbToQueue(pCtg, output, syncReq)); + + return TSDB_CODE_SUCCESS; + _return: - taosMemoryFreeClear(msg); + taosMemoryFreeClear(output->tbMeta); + taosMemoryFreeClear(output); CTG_RET(code); } -int32_t ctgActRemoveTbl(SCtgMetaAction *action) { - int32_t code = 0; - SCtgRemoveTblMsg *msg = action->data; - SCatalog* pCtg = msg->pCtg; - - SCtgDBCache *dbCache = NULL; - ctgGetDBCache(pCtg, msg->dbFName, &dbCache); - if (NULL == dbCache) { - return TSDB_CODE_SUCCESS; +int32_t ctgGetTbMetaFromCache(CTG_PARAMS, SCtgTbMetaCtx* ctx, STableMeta** pTableMeta) { + if (CTG_IS_SYS_DBNAME(ctx->pName->dbname)) { + CTG_FLAG_SET_SYS_DB(ctx->flag); } - if (dbCache->dbId != msg->dbId) { - ctgDebug("dbId already modified, dbFName:%s, current:%"PRIx64", dbId:%"PRIx64", tbName:%s", msg->dbFName, dbCache->dbId, msg->dbId, msg->tbName); - return TSDB_CODE_SUCCESS; + CTG_ERR_RET(ctgReadTbMetaFromCache(pCtg, ctx, pTableMeta)); + + if (*pTableMeta) { + if (CTG_FLAG_MATCH_STB(ctx->flag, (*pTableMeta)->tableType) && ((!CTG_FLAG_IS_FORCE_UPDATE(ctx->flag)) || (CTG_FLAG_IS_SYS_DB(ctx->flag)))) { + return TSDB_CODE_SUCCESS; + } + + taosMemoryFreeClear(*pTableMeta); } - CTG_LOCK(CTG_READ, &dbCache->tbCache.metaLock); - if (taosHashRemove(dbCache->tbCache.metaCache, msg->tbName, strlen(msg->tbName))) { - CTG_UNLOCK(CTG_READ, &dbCache->tbCache.metaLock); - ctgError("stb not exist in cache, dbFName:%s, tbName:%s", msg->dbFName, msg->tbName); - CTG_ERR_RET(TSDB_CODE_CTG_INTERNAL_ERROR); - } else { - CTG_CACHE_STAT_SUB(tblNum, 1); + if (CTG_FLAG_IS_UNKNOWN_STB(ctx->flag)) { + CTG_FLAG_SET_STB(ctx->flag, ctx->tbInfo.tbType); } - CTG_UNLOCK(CTG_READ, &dbCache->tbCache.metaLock); + + return TSDB_CODE_SUCCESS; +} - ctgInfo("table removed from cache, dbFName:%s, tbName:%s", msg->dbFName, msg->tbName); -_return: +int32_t ctgGetTbMeta(CTG_PARAMS, SCtgTbMetaCtx* ctx, STableMeta** pTableMeta) { + int32_t code = 0; + STableMetaOutput *output = NULL; - taosMemoryFreeClear(msg); + CTG_ERR_RET(ctgGetTbMetaFromCache(CTG_PARAMS_LIST(), ctx, pTableMeta)); + if (*pTableMeta) { + goto _return; + } - CTG_RET(code); -} + while (true) { + CTG_ERR_JRET(ctgRefreshTbMeta(CTG_PARAMS_LIST(), ctx, &output, false)); -int32_t ctgActUpdateUser(SCtgMetaAction *action) { - int32_t code = 0; - SCtgUpdateUserMsg *msg = action->data; - SCatalog* pCtg = msg->pCtg; - - if (NULL == pCtg->userCache) { - pCtg->userCache = taosHashInit(gCtgMgmt.cfg.maxUserCacheNum, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY), false, HASH_ENTRY_LOCK); - if (NULL == pCtg->userCache) { - ctgError("taosHashInit %d user cache failed", gCtgMgmt.cfg.maxUserCacheNum); - CTG_ERR_JRET(TSDB_CODE_OUT_OF_MEMORY); + if (CTG_IS_META_TABLE(output->metaType)) { + *pTableMeta = output->tbMeta; + goto _return; } - } - - SCtgUserAuth *pUser = (SCtgUserAuth *)taosHashGet(pCtg->userCache, msg->userAuth.user, strlen(msg->userAuth.user)); - if (NULL == pUser) { - SCtgUserAuth userAuth = {0}; - - userAuth.version = msg->userAuth.version; - userAuth.superUser = msg->userAuth.superAuth; - userAuth.createdDbs = msg->userAuth.createdDbs; - userAuth.readDbs = msg->userAuth.readDbs; - userAuth.writeDbs = msg->userAuth.writeDbs; - - if (taosHashPut(pCtg->userCache, msg->userAuth.user, strlen(msg->userAuth.user), &userAuth, sizeof(userAuth))) { - ctgError("taosHashPut user %s to cache failed", msg->userAuth.user); - CTG_ERR_JRET(TSDB_CODE_OUT_OF_MEMORY); + + if (CTG_IS_META_BOTH(output->metaType)) { + memcpy(output->tbMeta, &output->ctbMeta, sizeof(output->ctbMeta)); + + *pTableMeta = output->tbMeta; + goto _return; + } + + if ((!CTG_IS_META_CTABLE(output->metaType)) || output->tbMeta) { + ctgError("invalid metaType:%d", output->metaType); + taosMemoryFreeClear(output->tbMeta); + CTG_ERR_JRET(TSDB_CODE_CTG_INTERNAL_ERROR); } - taosMemoryFreeClear(msg); + // HANDLE ONLY CHILD TABLE META - return TSDB_CODE_SUCCESS; - } + taosMemoryFreeClear(output->tbMeta); - pUser->version = msg->userAuth.version; + SName stbName = *ctx->pName; + strcpy(stbName.tname, output->tbName); + SCtgTbMetaCtx stbCtx = {0}; + stbCtx.flag = ctx->flag; + stbCtx.pName = &stbName; + + CTG_ERR_JRET(ctgReadTbMetaFromCache(pCtg, &stbCtx, pTableMeta)); + if (NULL == *pTableMeta) { + ctgDebug("stb no longer exist, dbFName:%s, tbName:%s", output->dbFName, ctx->pName->tname); + continue; + } - CTG_LOCK(CTG_WRITE, &pUser->lock); + memcpy(*pTableMeta, &output->ctbMeta, sizeof(output->ctbMeta)); - taosHashCleanup(pUser->createdDbs); - pUser->createdDbs = msg->userAuth.createdDbs; - msg->userAuth.createdDbs = NULL; + break; + } - taosHashCleanup(pUser->readDbs); - pUser->readDbs = msg->userAuth.readDbs; - msg->userAuth.readDbs = NULL; +_return: - taosHashCleanup(pUser->writeDbs); - pUser->writeDbs = msg->userAuth.writeDbs; - msg->userAuth.writeDbs = NULL; + if (CTG_TABLE_NOT_EXIST(code) && ctx->tbInfo.inCache) { + char dbFName[TSDB_DB_FNAME_LEN] = {0}; + if (CTG_FLAG_IS_SYS_DB(ctx->flag)) { + strcpy(dbFName, ctx->pName->dbname); + } else { + tNameGetFullDbName(ctx->pName, dbFName); + } - CTG_UNLOCK(CTG_WRITE, &pUser->lock); + if (TSDB_SUPER_TABLE == ctx->tbInfo.tbType) { + ctgPutRmStbToQueue(pCtg, dbFName, ctx->tbInfo.dbId, ctx->pName->tname, ctx->tbInfo.suid, false); + } else { + ctgPutRmTbToQueue(pCtg, dbFName, ctx->tbInfo.dbId, ctx->pName->tname, false); + } + } -_return: + taosMemoryFreeClear(output); + if (*pTableMeta) { + ctgDebug("tbmeta returned, tbName:%s, tbType:%d", ctx->pName->tname, (*pTableMeta)->tableType); + ctgdShowTableMeta(pCtg, ctx->pName->tname, *pTableMeta); + } - taosHashCleanup(msg->userAuth.createdDbs); - taosHashCleanup(msg->userAuth.readDbs); - taosHashCleanup(msg->userAuth.writeDbs); - - taosMemoryFreeClear(msg); - CTG_RET(code); } -void* ctgUpdateThreadFunc(void* param) { - setThreadName("catalog"); - - qInfo("catalog update thread started"); - - CTG_LOCK(CTG_READ, &gCtgMgmt.lock); +int32_t ctgChkAuth(SCatalog* pCtg, void *pTrans, const SEpSet* pMgmtEps, const char* user, const char* dbFName, AUTH_TYPE type, bool *pass) { + bool inCache = false; + int32_t code = 0; - while (true) { - if (tsem_wait(&gCtgMgmt.queue.reqSem)) { - qError("ctg tsem_wait failed, error:%s", tstrerror(TAOS_SYSTEM_ERROR(errno))); - } - - if (atomic_load_8((int8_t*)&gCtgMgmt.exit)) { - tsem_post(&gCtgMgmt.queue.rspSem); - break; - } - - SCtgMetaAction *action = NULL; - ctgPopAction(&action); - SCatalog *pCtg = ((SCtgUpdateMsgHeader *)action->data)->pCtg; - - ctgDebug("process [%s] action", gCtgAction[action->act].name); - - (*gCtgAction[action->act].func)(action); - - gCtgMgmt.queue.seqDone = action->seqId; - - if (action->syncReq) { - tsem_post(&gCtgMgmt.queue.rspSem); - } + *pass = false; + + CTG_ERR_RET(ctgChkAuthFromCache(pCtg, user, dbFName, type, &inCache, pass)); - CTG_RUNTIME_STAT_ADD(qDoneNum, 1); + if (inCache) { + return TSDB_CODE_SUCCESS; + } - ctgdShowClusterCache(pCtg); + SGetUserAuthRsp authRsp = {0}; + CTG_ERR_RET(ctgGetUserDbAuthFromMnode(CTG_PARAMS_LIST(), user, &authRsp, NULL)); + + if (authRsp.superAuth) { + *pass = true; + goto _return; } - CTG_UNLOCK(CTG_READ, &gCtgMgmt.lock); + if (authRsp.createdDbs && taosHashGet(authRsp.createdDbs, dbFName, strlen(dbFName))) { + *pass = true; + goto _return; + } - qInfo("catalog update thread stopped"); - - return NULL; -} + if (type == AUTH_TYPE_READ && authRsp.readDbs && taosHashGet(authRsp.readDbs, dbFName, strlen(dbFName))) { + *pass = true; + } else if (type == AUTH_TYPE_WRITE && authRsp.writeDbs && taosHashGet(authRsp.writeDbs, dbFName, strlen(dbFName))) { + *pass = true; + } +_return: -int32_t ctgStartUpdateThread() { - TdThreadAttr thAttr; - taosThreadAttrInit(&thAttr); - taosThreadAttrSetDetachState(&thAttr, PTHREAD_CREATE_JOINABLE); + ctgPutUpdateUserToQueue(pCtg, &authRsp, false); - if (taosThreadCreate(&gCtgMgmt.updateThread, &thAttr, ctgUpdateThreadFunc, NULL) != 0) { - terrno = TAOS_SYSTEM_ERROR(errno); - CTG_ERR_RET(terrno); - } - - taosThreadAttrDestroy(&thAttr); return TSDB_CODE_SUCCESS; } -int32_t ctgGetTableDistVgInfo(SCatalog* pCtg, void *pRpc, const SEpSet* pMgmtEps, const SName* pTableName, SArray** pVgList) { +int32_t ctgGetTbDistVgInfo(SCatalog* pCtg, void *pTrans, const SEpSet* pMgmtEps, SName* pTableName, SArray** pVgList) { STableMeta *tbMeta = NULL; int32_t code = 0; SVgroupInfo vgroupInfo = {0}; SCtgDBCache* dbCache = NULL; SArray *vgList = NULL; SDBVgInfo *vgInfo = NULL; + SCtgTbMetaCtx ctx = {0}; + ctx.pName = pTableName; + ctx.flag = CTG_FLAG_UNKNOWN_STB; *pVgList = NULL; - CTG_ERR_JRET(ctgGetTableMeta(pCtg, pRpc, pMgmtEps, pTableName, &tbMeta, CTG_FLAG_UNKNOWN_STB)); + CTG_ERR_JRET(ctgGetTbMeta(CTG_PARAMS_LIST(), &ctx, &tbMeta)); char db[TSDB_DB_FNAME_LEN] = {0}; tNameGetFullDbName(pTableName, db); SHashObj *vgHash = NULL; - CTG_ERR_JRET(ctgGetDBVgInfo(pCtg, pRpc, pMgmtEps, db, &dbCache, &vgInfo)); + CTG_ERR_JRET(ctgGetDBVgInfo(pCtg, pTrans, pMgmtEps, db, &dbCache, &vgInfo)); if (dbCache) { vgHash = dbCache->vgInfo->vgHash; @@ -2557,6 +488,12 @@ int32_t catalogInit(SCatalogCfg *cfg) { } gCtgMgmt.queue.tail = gCtgMgmt.queue.head; + gCtgMgmt.jobPool = taosOpenRef(200, ctgFreeJob); + if (gCtgMgmt.jobPool < 0) { + qError("taosOpenRef failed, error:%s", tstrerror(terrno)); + CTG_ERR_RET(terrno); + } + CTG_ERR_RET(ctgStartUpdateThread()); qDebug("catalog initialized, maxDb:%u, maxTbl:%u, dbRentSec:%u, stbRentSec:%u", gCtgMgmt.cfg.maxDBCacheNum, gCtgMgmt.cfg.maxTblCacheNum, gCtgMgmt.cfg.dbRentSec, gCtgMgmt.cfg.stbRentSec); @@ -2603,6 +540,12 @@ int32_t catalogGetHandle(uint64_t clusterId, SCatalog** catalogHandle) { CTG_ERR_JRET(TSDB_CODE_CTG_MEM_ERROR); } + SHashObj *metaCache = taosHashInit(gCtgMgmt.cfg.maxTblCacheNum, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY), true, HASH_ENTRY_LOCK); + if (NULL == metaCache) { + qError("taosHashInit failed, num:%d", gCtgMgmt.cfg.maxTblCacheNum); + CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); + } + code = taosHashPut(gCtgMgmt.pCluster, &clusterId, sizeof(clusterId), &clusterCtg, POINTER_BYTES); if (code) { if (HASH_NODE_EXIST(code)) { @@ -2659,11 +602,10 @@ int32_t catalogGetDBVgVersion(SCatalog* pCtg, const char* dbFName, int32_t* vers } SCtgDBCache *dbCache = NULL; - bool inCache = false; int32_t code = 0; - CTG_ERR_JRET(ctgAcquireVgInfoFromCache(pCtg, dbFName, &dbCache, &inCache)); - if (!inCache) { + CTG_ERR_JRET(ctgAcquireVgInfoFromCache(pCtg, dbFName, &dbCache)); + if (NULL == dbCache) { *version = CTG_DEFAULT_INVALID_VERSION; CTG_API_LEAVE(TSDB_CODE_SUCCESS); } @@ -2684,10 +626,10 @@ _return: CTG_API_LEAVE(code); } -int32_t catalogGetDBVgInfo(SCatalog* pCtg, void *pRpc, const SEpSet* pMgmtEps, const char* dbFName, SArray** vgroupList) { +int32_t catalogGetDBVgInfo(SCatalog* pCtg, void *pTrans, const SEpSet* pMgmtEps, const char* dbFName, SArray** vgroupList) { CTG_API_ENTER(); - if (NULL == pCtg || NULL == dbFName || NULL == pRpc || NULL == pMgmtEps || NULL == vgroupList) { + if (NULL == pCtg || NULL == dbFName || NULL == pTrans || NULL == pMgmtEps || NULL == vgroupList) { CTG_API_LEAVE(TSDB_CODE_CTG_INVALID_INPUT); } @@ -2696,7 +638,7 @@ int32_t catalogGetDBVgInfo(SCatalog* pCtg, void *pRpc, const SEpSet* pMgmtEps, c SArray *vgList = NULL; SHashObj *vgHash = NULL; SDBVgInfo *vgInfo = NULL; - CTG_ERR_JRET(ctgGetDBVgInfo(pCtg, pRpc, pMgmtEps, dbFName, &dbCache, &vgInfo)); + CTG_ERR_JRET(ctgGetDBVgInfo(pCtg, pTrans, pMgmtEps, dbFName, &dbCache, &vgInfo)); if (dbCache) { vgHash = dbCache->vgInfo->vgHash; } else { @@ -2734,7 +676,7 @@ int32_t catalogUpdateDBVgInfo(SCatalog* pCtg, const char* dbFName, uint64_t dbId CTG_ERR_JRET(TSDB_CODE_CTG_INVALID_INPUT); } - code = ctgPushUpdateVgMsgInQueue(pCtg, dbFName, dbId, dbInfo, false); + code = ctgPutUpdateVgToQueue(pCtg, dbFName, dbId, dbInfo, false); _return: @@ -2755,7 +697,7 @@ int32_t catalogRemoveDB(SCatalog* pCtg, const char* dbFName, uint64_t dbId) { CTG_API_LEAVE(TSDB_CODE_SUCCESS); } - CTG_ERR_JRET(ctgPushRmDBMsgInQueue(pCtg, dbFName, dbId)); + CTG_ERR_JRET(ctgPutRmDBToQueue(pCtg, dbFName, dbId)); CTG_API_LEAVE(TSDB_CODE_SUCCESS); @@ -2768,7 +710,7 @@ int32_t catalogUpdateVgEpSet(SCatalog* pCtg, const char* dbFName, int32_t vgId, return 0; } -int32_t catalogRemoveTableMeta(SCatalog* pCtg, const SName* pTableName) { +int32_t catalogRemoveTableMeta(SCatalog* pCtg, SName* pTableName) { CTG_API_ENTER(); int32_t code = 0; @@ -2781,30 +723,10 @@ int32_t catalogRemoveTableMeta(SCatalog* pCtg, const SName* pTableName) { CTG_API_LEAVE(TSDB_CODE_SUCCESS); } - STableMeta *tblMeta = NULL; - bool inCache = false; - uint64_t dbId = 0; - CTG_ERR_JRET(ctgGetTableMetaFromCache(pCtg, pTableName, &tblMeta, &inCache, 0, &dbId)); - - if (!inCache) { - ctgDebug("table already not in cache, db:%s, tblName:%s", pTableName->dbname, pTableName->tname); - goto _return; - } - - char dbFName[TSDB_DB_FNAME_LEN]; - tNameGetFullDbName(pTableName, dbFName); - - if (TSDB_SUPER_TABLE == tblMeta->tableType) { - CTG_ERR_JRET(ctgPushRmStbMsgInQueue(pCtg, dbFName, dbId, pTableName->tname, tblMeta->suid, true)); - } else { - CTG_ERR_JRET(ctgPushRmTblMsgInQueue(pCtg, dbFName, dbId, pTableName->tname, true)); - } + CTG_ERR_JRET(ctgRemoveTbMetaFromCache(pCtg, pTableName, true)); - _return: - - taosMemoryFreeClear(tblMeta); - + CTG_API_LEAVE(code); } @@ -2822,7 +744,7 @@ int32_t catalogRemoveStbMeta(SCatalog* pCtg, const char* dbFName, uint64_t dbId, CTG_API_LEAVE(TSDB_CODE_SUCCESS); } - CTG_ERR_JRET(ctgPushRmStbMsgInQueue(pCtg, dbFName, dbId, stbName, suid, true)); + CTG_ERR_JRET(ctgPutRmStbToQueue(pCtg, dbFName, dbId, stbName, suid, true)); CTG_API_LEAVE(TSDB_CODE_SUCCESS); @@ -2831,20 +753,24 @@ _return: CTG_API_LEAVE(code); } -int32_t catalogGetIndexMeta(SCatalog* pCtg, void *pTrans, const SEpSet* pMgmtEps, const SName* pTableName, const char *pIndexName, SIndexMeta** pIndexMeta) { - return 0; -} - int32_t catalogGetTableMeta(SCatalog* pCtg, void *pTrans, const SEpSet* pMgmtEps, const SName* pTableName, STableMeta** pTableMeta) { CTG_API_ENTER(); - CTG_API_LEAVE(ctgGetTableMeta(pCtg, pTrans, pMgmtEps, pTableName, pTableMeta, CTG_FLAG_UNKNOWN_STB)); + SCtgTbMetaCtx ctx = {0}; + ctx.pName = (SName*)pTableName; + ctx.flag = CTG_FLAG_UNKNOWN_STB; + + CTG_API_LEAVE(ctgGetTbMeta(pCtg, pTrans, pMgmtEps, &ctx, pTableMeta)); } int32_t catalogGetSTableMeta(SCatalog* pCtg, void * pTrans, const SEpSet* pMgmtEps, const SName* pTableName, STableMeta** pTableMeta) { CTG_API_ENTER(); - CTG_API_LEAVE(ctgGetTableMeta(pCtg, pTrans, pMgmtEps, pTableName, pTableMeta, CTG_FLAG_STB)); + SCtgTbMetaCtx ctx = {0}; + ctx.pName = (SName*)pTableName; + ctx.flag = CTG_FLAG_STB; + + CTG_API_LEAVE(ctgGetTbMeta(CTG_PARAMS_LIST(), &ctx, pTableMeta)); } int32_t catalogUpdateSTableMeta(SCatalog* pCtg, STableMetaRsp *rspMsg) { @@ -2871,7 +797,7 @@ int32_t catalogUpdateSTableMeta(SCatalog* pCtg, STableMetaRsp *rspMsg) { CTG_ERR_JRET(queryCreateTableMetaFromMsg(rspMsg, true, &output->tbMeta)); - CTG_ERR_JRET(ctgPushUpdateTblMsgInQueue(pCtg, output, false)); + CTG_ERR_JRET(ctgPutUpdateTbToQueue(pCtg, output, false)); CTG_API_LEAVE(code); @@ -2883,82 +809,6 @@ _return: CTG_API_LEAVE(code); } - - -int32_t ctgGetTbSverFromCache(SCatalog* pCtg, const SName* pTableName, int32_t* sver) { - *sver = -1; - - if (NULL == pCtg->dbCache) { - ctgDebug("empty tbmeta cache, tbName:%s", pTableName->tname); - return TSDB_CODE_SUCCESS; - } - - SCtgDBCache *dbCache = NULL; - char dbFName[TSDB_DB_FNAME_LEN] = {0}; - tNameGetFullDbName(pTableName, dbFName); - - ctgAcquireDBCache(pCtg, dbFName, &dbCache); - if (NULL == dbCache) { - ctgDebug("db %s not in cache", pTableName->tname); - return TSDB_CODE_SUCCESS; - } - - int32_t tbType = 0; - uint64_t suid = 0; - CTG_LOCK(CTG_READ, &dbCache->tbCache.metaLock); - STableMeta* tbMeta = taosHashGet(dbCache->tbCache.metaCache, pTableName->tname, strlen(pTableName->tname)); - if (tbMeta) { - tbType = tbMeta->tableType; - suid = tbMeta->suid; - if (tbType != TSDB_CHILD_TABLE) { - *sver = tbMeta->sversion; - } - } - CTG_UNLOCK(CTG_READ, &dbCache->tbCache.metaLock); - - if (NULL == tbMeta) { - ctgReleaseDBCache(pCtg, dbCache); - return TSDB_CODE_SUCCESS; - } - - if (tbType != TSDB_CHILD_TABLE) { - ctgReleaseDBCache(pCtg, dbCache); - ctgDebug("Got sver %d from cache, type:%d, dbFName:%s, tbName:%s", *sver, tbType, dbFName, pTableName->tname); - - return TSDB_CODE_SUCCESS; - } - - ctgDebug("Got subtable meta from cache, dbFName:%s, tbName:%s, suid:%" PRIx64, dbFName, pTableName->tname, suid); - - CTG_LOCK(CTG_READ, &dbCache->tbCache.stbLock); - - STableMeta **stbMeta = taosHashGet(dbCache->tbCache.stbCache, &suid, sizeof(suid)); - if (NULL == stbMeta || NULL == *stbMeta) { - CTG_UNLOCK(CTG_READ, &dbCache->tbCache.stbLock); - ctgReleaseDBCache(pCtg, dbCache); - ctgDebug("stb not in stbCache, suid:%"PRIx64, suid); - return TSDB_CODE_SUCCESS; - } - - if ((*stbMeta)->suid != suid) { - CTG_UNLOCK(CTG_READ, &dbCache->tbCache.stbLock); - ctgReleaseDBCache(pCtg, dbCache); - ctgError("stable suid in stbCache mis-match, expected suid:%"PRIx64 ",actual suid:%"PRIx64, suid, (*stbMeta)->suid); - CTG_ERR_RET(TSDB_CODE_CTG_INTERNAL_ERROR); - } - - *sver = (*stbMeta)->sversion; - - CTG_UNLOCK(CTG_READ, &dbCache->tbCache.stbLock); - - ctgReleaseDBCache(pCtg, dbCache); - - ctgDebug("Got sver %d from cache, type:%d, dbFName:%s, tbName:%s", *sver, tbType, dbFName, pTableName->tname); - - return TSDB_CODE_SUCCESS; -} - - int32_t catalogChkTbMetaVersion(SCatalog* pCtg, void *pTrans, const SEpSet* pMgmtEps, SArray* pTables) { CTG_API_ENTER(); @@ -2971,15 +821,36 @@ int32_t catalogChkTbMetaVersion(SCatalog* pCtg, void *pTrans, const SEpSet* pMgm int32_t tbNum = taosArrayGetSize(pTables); for (int32_t i = 0; i < tbNum; ++i) { STbSVersion* pTb = (STbSVersion*)taosArrayGet(pTables, i); - tNameFromString(&name, pTb->tbFName, T_NAME_ACCT | T_NAME_DB | T_NAME_TABLE); + if (NULL == pTb->tbFName || 0 == pTb->tbFName[0]) { + continue; + } + tNameFromString(&name, pTb->tbFName, T_NAME_ACCT | T_NAME_DB | T_NAME_TABLE); + if (CTG_IS_SYS_DBNAME(name.dbname)) { continue; } - ctgGetTbSverFromCache(pCtg, &name, &sver); + int32_t tbType = 0; + uint64_t suid = 0; + char stbName[TSDB_TABLE_FNAME_LEN]; + ctgReadTbSverFromCache(pCtg, &name, &sver, &tbType, &suid, stbName); if (sver >= 0 && sver < pTb->sver) { - catalogRemoveTableMeta(pCtg, &name); //TODO REMOVE STB FROM CACHE + switch (tbType) { + case TSDB_CHILD_TABLE: { + SName stb = name; + strcpy(stb.tname, stbName); + catalogRemoveTableMeta(pCtg, &stb); + break; + } + case TSDB_SUPER_TABLE: + case TSDB_NORMAL_TABLE: + catalogRemoveTableMeta(pCtg, &name); + break; + default: + ctgError("ignore table type %d", tbType); + break; + } } } @@ -3004,19 +875,27 @@ int32_t catalogRefreshTableMeta(SCatalog* pCtg, void *pTrans, const SEpSet* pMgm CTG_API_LEAVE(TSDB_CODE_CTG_INVALID_INPUT); } - CTG_API_LEAVE(ctgRefreshTblMeta(pCtg, pTrans, pMgmtEps, pTableName, CTG_FLAG_FORCE_UPDATE | CTG_FLAG_MAKE_STB(isSTable), NULL, true)); + SCtgTbMetaCtx ctx = {0}; + ctx.pName = (SName*)pTableName; + ctx.flag = CTG_FLAG_FORCE_UPDATE | CTG_FLAG_MAKE_STB(isSTable); + + CTG_API_LEAVE(ctgRefreshTbMeta(CTG_PARAMS_LIST(), &ctx, NULL, true)); } int32_t catalogRefreshGetTableMeta(SCatalog* pCtg, void *pTrans, const SEpSet* pMgmtEps, const SName* pTableName, STableMeta** pTableMeta, int32_t isSTable) { CTG_API_ENTER(); - CTG_API_LEAVE(ctgGetTableMeta(pCtg, pTrans, pMgmtEps, pTableName, pTableMeta, CTG_FLAG_FORCE_UPDATE | CTG_FLAG_MAKE_STB(isSTable))); + SCtgTbMetaCtx ctx = {0}; + ctx.pName = (SName*)pTableName; + ctx.flag = CTG_FLAG_FORCE_UPDATE | CTG_FLAG_MAKE_STB(isSTable); + + CTG_API_LEAVE(ctgGetTbMeta(CTG_PARAMS_LIST(), &ctx, pTableMeta)); } -int32_t catalogGetTableDistVgInfo(SCatalog* pCtg, void *pRpc, const SEpSet* pMgmtEps, const SName* pTableName, SArray** pVgList) { +int32_t catalogGetTableDistVgInfo(SCatalog* pCtg, void *pTrans, const SEpSet* pMgmtEps, const SName* pTableName, SArray** pVgList) { CTG_API_ENTER(); - if (NULL == pCtg || NULL == pRpc || NULL == pMgmtEps || NULL == pTableName || NULL == pVgList) { + if (NULL == pCtg || NULL == pTrans || NULL == pMgmtEps || NULL == pTableName || NULL == pVgList) { CTG_API_LEAVE(TSDB_CODE_CTG_INVALID_INPUT); } @@ -3025,28 +904,7 @@ int32_t catalogGetTableDistVgInfo(SCatalog* pCtg, void *pRpc, const SEpSet* pMgm CTG_API_LEAVE(TSDB_CODE_CTG_INVALID_INPUT); } - int32_t code = 0; - - while (true) { - code = ctgGetTableDistVgInfo(pCtg, pRpc, pMgmtEps, pTableName, pVgList); - if (code) { - if (TSDB_CODE_CTG_VG_META_MISMATCH == code) { - CTG_ERR_JRET(ctgRefreshTblMeta(pCtg, pRpc, pMgmtEps, pTableName, CTG_FLAG_FORCE_UPDATE | CTG_FLAG_MAKE_STB(CTG_FLAG_UNKNOWN_STB), NULL, true)); - - char dbFName[TSDB_DB_FNAME_LEN] = {0}; - tNameGetFullDbName(pTableName, dbFName); - CTG_ERR_JRET(ctgRefreshDBVgInfo(pCtg, pRpc, pMgmtEps, dbFName)); - - continue; - } - } - - break; - } - -_return: - - CTG_API_LEAVE(code); + CTG_API_LEAVE(ctgGetTbDistVgInfo(pCtg, pTrans, pMgmtEps, (SName*)pTableName, pVgList)); } @@ -3094,8 +952,8 @@ int32_t catalogGetAllMeta(SCatalog* pCtg, void *pTrans, const SEpSet* pMgmtEps, int32_t code = 0; pRsp->pTableMeta = NULL; - if (pReq->pTableName) { - int32_t tbNum = (int32_t)taosArrayGetSize(pReq->pTableName); + if (pReq->pTableMeta) { + int32_t tbNum = (int32_t)taosArrayGetSize(pReq->pTableMeta); if (tbNum <= 0) { ctgError("empty table name list, tbNum:%d", tbNum); CTG_ERR_JRET(TSDB_CODE_CTG_INVALID_INPUT); @@ -3108,10 +966,13 @@ int32_t catalogGetAllMeta(SCatalog* pCtg, void *pTrans, const SEpSet* pMgmtEps, } for (int32_t i = 0; i < tbNum; ++i) { - SName *name = taosArrayGet(pReq->pTableName, i); + SName *name = taosArrayGet(pReq->pTableMeta, i); STableMeta *pTableMeta = NULL; + SCtgTbMetaCtx ctx = {0}; + ctx.pName = name; + ctx.flag = CTG_FLAG_UNKNOWN_STB; - CTG_ERR_JRET(ctgGetTableMeta(pCtg, pTrans, pMgmtEps, name, &pTableMeta, CTG_FLAG_UNKNOWN_STB)); + CTG_ERR_JRET(ctgGetTbMeta(CTG_PARAMS_LIST(), &ctx, &pTableMeta)); if (NULL == taosArrayPush(pRsp->pTableMeta, &pTableMeta)) { ctgError("taosArrayPush failed, idx:%d", i); @@ -3123,7 +984,7 @@ int32_t catalogGetAllMeta(SCatalog* pCtg, void *pTrans, const SEpSet* pMgmtEps, if (pReq->qNodeRequired) { pRsp->pQnodeList = taosArrayInit(10, sizeof(SQueryNodeAddr)); - CTG_ERR_JRET(ctgGetQnodeListFromMnode(pCtg, pTrans, pMgmtEps, pRsp->pQnodeList)); + CTG_ERR_JRET(ctgGetQnodeListFromMnode(CTG_PARAMS_LIST(), pRsp->pQnodeList, NULL)); } CTG_API_LEAVE(TSDB_CODE_SUCCESS); @@ -3144,15 +1005,43 @@ _return: CTG_API_LEAVE(code); } -int32_t catalogGetQnodeList(SCatalog* pCtg, void *pRpc, const SEpSet* pMgmtEps, SArray* pQnodeList) { +int32_t catalogAsyncGetAllMeta(SCatalog* pCtg, void *pTrans, const SEpSet* pMgmtEps, uint64_t reqId, const SCatalogReq* pReq, catalogCallback fp, void* param, int64_t* jobId) { + CTG_API_ENTER(); + + if (NULL == pCtg || NULL == pTrans || NULL == pMgmtEps || NULL == pReq || NULL == fp || NULL == param) { + CTG_API_LEAVE(TSDB_CODE_CTG_INVALID_INPUT); + } + + int32_t code = 0; + SCtgJob *pJob = NULL; + CTG_ERR_JRET(ctgInitJob(CTG_PARAMS_LIST(), &pJob, reqId, pReq, fp, param)); + + CTG_ERR_JRET(ctgLaunchJob(pJob)); + + *jobId = pJob->refId; + +_return: + + if (pJob) { + taosReleaseRef(gCtgMgmt.jobPool, pJob->refId); + + if (code) { + taosRemoveRef(gCtgMgmt.jobPool, pJob->refId); + } + } + + CTG_API_LEAVE(code); +} + +int32_t catalogGetQnodeList(SCatalog* pCtg, void *pTrans, const SEpSet* pMgmtEps, SArray* pQnodeList) { CTG_API_ENTER(); int32_t code = 0; - if (NULL == pCtg || NULL == pRpc || NULL == pMgmtEps || NULL == pQnodeList) { + if (NULL == pCtg || NULL == pTrans || NULL == pMgmtEps || NULL == pQnodeList) { CTG_API_LEAVE(TSDB_CODE_CTG_INVALID_INPUT); } - CTG_ERR_JRET(ctgGetQnodeListFromMnode(pCtg, pRpc, pMgmtEps, pQnodeList)); + CTG_ERR_JRET(ctgGetQnodeListFromMnode(CTG_PARAMS_LIST(), pQnodeList, NULL)); _return: @@ -3198,9 +1087,12 @@ int32_t catalogGetExpiredUsers(SCatalog* pCtg, SUserAuthVersion **users, uint32_ uint32_t i = 0; SCtgUserAuth *pAuth = taosHashIterate(pCtg->userCache, NULL); while (pAuth != NULL) { - void *key = taosHashGetKey(pAuth, NULL); - strncpy((*users)[i].user, key, sizeof((*users)[i].user)); + size_t len = 0; + void *key = taosHashGetKey(pAuth, &len); + strncpy((*users)[i].user, key, len); + (*users)[i].user[len] = 0; (*users)[i].version = pAuth->version; + ++i; pAuth = taosHashIterate(pCtg->userCache, pAuth); } @@ -3208,59 +1100,50 @@ int32_t catalogGetExpiredUsers(SCatalog* pCtg, SUserAuthVersion **users, uint32_ } -int32_t catalogGetDBCfg(SCatalog* pCtg, void *pRpc, const SEpSet* pMgmtEps, const char* dbFName, SDbCfgInfo* pDbCfg) { +int32_t catalogGetDBCfg(SCatalog* pCtg, void *pTrans, const SEpSet* pMgmtEps, const char* dbFName, SDbCfgInfo* pDbCfg) { CTG_API_ENTER(); - if (NULL == pCtg || NULL == pRpc || NULL == pMgmtEps || NULL == dbFName || NULL == pDbCfg) { + if (NULL == pCtg || NULL == pTrans || NULL == pMgmtEps || NULL == dbFName || NULL == pDbCfg) { CTG_API_LEAVE(TSDB_CODE_CTG_INVALID_INPUT); } - CTG_API_LEAVE(ctgGetDBCfgFromMnode(pCtg, pRpc, pMgmtEps, dbFName, pDbCfg)); + CTG_API_LEAVE(ctgGetDBCfgFromMnode(CTG_PARAMS_LIST(), dbFName, pDbCfg, NULL)); } -int32_t catalogGetIndexInfo(SCatalog* pCtg, void *pRpc, const SEpSet* pMgmtEps, const char* indexName, SIndexInfo* pInfo) { +int32_t catalogGetIndexMeta(SCatalog* pCtg, void *pTrans, const SEpSet* pMgmtEps, const char* indexName, SIndexInfo* pInfo) { CTG_API_ENTER(); - if (NULL == pCtg || NULL == pRpc || NULL == pMgmtEps || NULL == indexName || NULL == pInfo) { + if (NULL == pCtg || NULL == pTrans || NULL == pMgmtEps || NULL == indexName || NULL == pInfo) { CTG_API_LEAVE(TSDB_CODE_CTG_INVALID_INPUT); } - CTG_API_LEAVE(ctgGetIndexInfoFromMnode(pCtg, pRpc, pMgmtEps, indexName, pInfo)); + CTG_API_LEAVE(ctgGetIndexInfoFromMnode(CTG_PARAMS_LIST(), indexName, pInfo, NULL)); } -int32_t catalogGetUdfInfo(SCatalog* pCtg, void *pRpc, const SEpSet* pMgmtEps, const char* funcName, SFuncInfo** pInfo) { +int32_t catalogGetUdfInfo(SCatalog* pCtg, void *pTrans, const SEpSet* pMgmtEps, const char* funcName, SFuncInfo* pInfo) { CTG_API_ENTER(); - if (NULL == pCtg || NULL == pRpc || NULL == pMgmtEps || NULL == funcName || NULL == pInfo) { + if (NULL == pCtg || NULL == pTrans || NULL == pMgmtEps || NULL == funcName || NULL == pInfo) { CTG_API_LEAVE(TSDB_CODE_CTG_INVALID_INPUT); } int32_t code = 0; - *pInfo = taosMemoryMalloc(sizeof(SFuncInfo)); - if (NULL == *pInfo) { - CTG_API_LEAVE(TSDB_CODE_OUT_OF_MEMORY); - } - - CTG_ERR_JRET(ctgGetUdfInfoFromMnode(pCtg, pRpc, pMgmtEps, funcName, pInfo)); + CTG_ERR_JRET(ctgGetUdfInfoFromMnode(CTG_PARAMS_LIST(), funcName, pInfo, NULL)); _return: - - if (code) { - taosMemoryFreeClear(*pInfo); - } CTG_API_LEAVE(code); } -int32_t catalogChkAuth(SCatalog* pCtg, void *pRpc, const SEpSet* pMgmtEps, const char* user, const char* dbFName, AUTH_TYPE type, bool *pass) { +int32_t catalogChkAuth(SCatalog* pCtg, void *pTrans, const SEpSet* pMgmtEps, const char* user, const char* dbFName, AUTH_TYPE type, bool *pass) { CTG_API_ENTER(); - if (NULL == pCtg || NULL == pRpc || NULL == pMgmtEps || NULL == user || NULL == dbFName || NULL == pass) { + if (NULL == pCtg || NULL == pTrans || NULL == pMgmtEps || NULL == user || NULL == dbFName || NULL == pass) { CTG_API_LEAVE(TSDB_CODE_CTG_INVALID_INPUT); } int32_t code = 0; - CTG_ERR_JRET(ctgChkAuth(pCtg, pRpc, pMgmtEps, user, dbFName, type, pass)); + CTG_ERR_JRET(ctgChkAuth(CTG_PARAMS_LIST(), user, dbFName, type, pass)); _return: @@ -3274,7 +1157,7 @@ int32_t catalogUpdateUserAuthInfo(SCatalog* pCtg, SGetUserAuthRsp* pAuth) { CTG_API_LEAVE(TSDB_CODE_CTG_INVALID_INPUT); } - CTG_API_LEAVE(ctgPushUpdateUserMsgInQueue(pCtg, pAuth, false)); + CTG_API_LEAVE(ctgPutUpdateUserToQueue(pCtg, pAuth, false)); } diff --git a/source/libs/catalog/src/ctgAsync.c b/source/libs/catalog/src/ctgAsync.c new file mode 100644 index 0000000000000000000000000000000000000000..4908dc510116a2347796239d6dd2708df29f2cf8 --- /dev/null +++ b/source/libs/catalog/src/ctgAsync.c @@ -0,0 +1,1015 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "trpc.h" +#include "query.h" +#include "tname.h" +#include "catalogInt.h" +#include "systable.h" +#include "tref.h" + +int32_t ctgInitGetTbMetaTask(SCtgJob *pJob, int32_t taskIdx, SName *name) { + SCtgTask *pTask = taosArrayGet(pJob->pTasks, taskIdx); + + pTask->type = CTG_TASK_GET_TB_META; + pTask->taskId = taskIdx; + pTask->pJob = pJob; + + pTask->taskCtx = taosMemoryCalloc(1, sizeof(SCtgTbMetaCtx)); + if (NULL == pTask->taskCtx) { + CTG_ERR_RET(TSDB_CODE_OUT_OF_MEMORY); + } + + SCtgTbMetaCtx* ctx = pTask->taskCtx; + ctx->pName = taosMemoryMalloc(sizeof(*name)); + if (NULL == ctx->pName) { + taosMemoryFree(pTask->taskCtx); + CTG_ERR_RET(TSDB_CODE_OUT_OF_MEMORY); + } + + memcpy(ctx->pName, name, sizeof(*name)); + ctx->flag = CTG_FLAG_UNKNOWN_STB; + + qDebug("QID:%" PRIx64 " task %d type %d initialized, tableName:%s", pJob->queryId, taskIdx, pTask->type, name->tname); + + return TSDB_CODE_SUCCESS; +} + +int32_t ctgInitGetDbVgTask(SCtgJob *pJob, int32_t taskIdx, char *dbFName) { + SCtgTask *pTask = taosArrayGet(pJob->pTasks, taskIdx); + + pTask->type = CTG_TASK_GET_DB_VGROUP; + pTask->taskId = taskIdx; + pTask->pJob = pJob; + + pTask->taskCtx = taosMemoryCalloc(1, sizeof(SCtgDbVgCtx)); + if (NULL == pTask->taskCtx) { + CTG_ERR_RET(TSDB_CODE_OUT_OF_MEMORY); + } + + SCtgDbVgCtx* ctx = pTask->taskCtx; + + memcpy(ctx->dbFName, dbFName, sizeof(ctx->dbFName)); + + qDebug("QID:%" PRIx64 " task %d type %d initialized, dbFName:%s", pJob->queryId, taskIdx, pTask->type, dbFName); + + return TSDB_CODE_SUCCESS; +} + +int32_t ctgInitGetDbCfgTask(SCtgJob *pJob, int32_t taskIdx, char *dbFName) { + SCtgTask *pTask = taosArrayGet(pJob->pTasks, taskIdx); + + pTask->type = CTG_TASK_GET_DB_CFG; + pTask->taskId = taskIdx; + pTask->pJob = pJob; + + pTask->taskCtx = taosMemoryCalloc(1, sizeof(SCtgDbCfgCtx)); + if (NULL == pTask->taskCtx) { + CTG_ERR_RET(TSDB_CODE_OUT_OF_MEMORY); + } + + SCtgDbCfgCtx* ctx = pTask->taskCtx; + + memcpy(ctx->dbFName, dbFName, sizeof(ctx->dbFName)); + + qDebug("QID:%" PRIx64 " task %d type %d initialized, dbFName:%s", pJob->queryId, taskIdx, pTask->type, dbFName); + + return TSDB_CODE_SUCCESS; +} + +int32_t ctgInitGetTbHashTask(SCtgJob *pJob, int32_t taskIdx, SName *name) { + SCtgTask *pTask = taosArrayGet(pJob->pTasks, taskIdx); + + pTask->type = CTG_TASK_GET_TB_HASH; + pTask->taskId = taskIdx; + pTask->pJob = pJob; + + pTask->taskCtx = taosMemoryCalloc(1, sizeof(SCtgTbHashCtx)); + if (NULL == pTask->taskCtx) { + CTG_ERR_RET(TSDB_CODE_OUT_OF_MEMORY); + } + + SCtgTbHashCtx* ctx = pTask->taskCtx; + ctx->pName = taosMemoryMalloc(sizeof(*name)); + if (NULL == ctx->pName) { + taosMemoryFree(pTask->taskCtx); + CTG_ERR_RET(TSDB_CODE_OUT_OF_MEMORY); + } + + memcpy(ctx->pName, name, sizeof(*name)); + tNameGetFullDbName(ctx->pName, ctx->dbFName); + + qDebug("QID:%" PRIx64 " task %d type %d initialized, tableName:%s", pJob->queryId, taskIdx, pTask->type, name->tname); + + return TSDB_CODE_SUCCESS; +} + +int32_t ctgInitGetQnodeTask(SCtgJob *pJob, int32_t taskIdx) { + SCtgTask *pTask = taosArrayGet(pJob->pTasks, taskIdx); + + pTask->type = CTG_TASK_GET_QNODE; + pTask->taskId = taskIdx; + pTask->pJob = pJob; + pTask->taskCtx = NULL; + + qDebug("QID:%" PRIx64 " task %d type %d initialized", pJob->queryId, taskIdx, pTask->type); + + return TSDB_CODE_SUCCESS; +} + +int32_t ctgInitGetIndexTask(SCtgJob *pJob, int32_t taskIdx, char *name) { + SCtgTask *pTask = taosArrayGet(pJob->pTasks, taskIdx); + + pTask->type = CTG_TASK_GET_INDEX; + pTask->taskId = taskIdx; + pTask->pJob = pJob; + + pTask->taskCtx = taosMemoryCalloc(1, sizeof(SCtgIndexCtx)); + if (NULL == pTask->taskCtx) { + CTG_ERR_RET(TSDB_CODE_OUT_OF_MEMORY); + } + + SCtgIndexCtx* ctx = pTask->taskCtx; + + strcpy(ctx->indexFName, name); + + qDebug("QID:%" PRIx64 " task %d type %d initialized, indexFName:%s", pJob->queryId, taskIdx, pTask->type, name); + + return TSDB_CODE_SUCCESS; +} + +int32_t ctgInitGetUdfTask(SCtgJob *pJob, int32_t taskIdx, char *name) { + SCtgTask *pTask = taosArrayGet(pJob->pTasks, taskIdx); + + pTask->type = CTG_TASK_GET_UDF; + pTask->taskId = taskIdx; + pTask->pJob = pJob; + + pTask->taskCtx = taosMemoryCalloc(1, sizeof(SCtgUdfCtx)); + if (NULL == pTask->taskCtx) { + CTG_ERR_RET(TSDB_CODE_OUT_OF_MEMORY); + } + + SCtgUdfCtx* ctx = pTask->taskCtx; + + strcpy(ctx->udfName, name); + + qDebug("QID:%" PRIx64 " task %d type %d initialized, udfName:%s", pJob->queryId, taskIdx, pTask->type, name); + + return TSDB_CODE_SUCCESS; +} + +int32_t ctgInitGetUserTask(SCtgJob *pJob, int32_t taskIdx, SUserAuthInfo *user) { + SCtgTask *pTask = taosArrayGet(pJob->pTasks, taskIdx); + + pTask->type = CTG_TASK_GET_USER; + pTask->taskId = taskIdx; + pTask->pJob = pJob; + + pTask->taskCtx = taosMemoryCalloc(1, sizeof(SCtgUserCtx)); + if (NULL == pTask->taskCtx) { + CTG_ERR_RET(TSDB_CODE_OUT_OF_MEMORY); + } + + SCtgUserCtx* ctx = pTask->taskCtx; + + memcpy(&ctx->user, user, sizeof(*user)); + + qDebug("QID:%" PRIx64 " task %d type %d initialized, user:%s", pJob->queryId, taskIdx, pTask->type, user->user); + + return TSDB_CODE_SUCCESS; +} + + +int32_t ctgInitJob(CTG_PARAMS, SCtgJob** job, uint64_t reqId, const SCatalogReq* pReq, catalogCallback fp, void* param) { + int32_t code = 0; + int32_t tbMetaNum = (int32_t)taosArrayGetSize(pReq->pTableMeta); + int32_t dbVgNum = (int32_t)taosArrayGetSize(pReq->pDbVgroup); + int32_t tbHashNum = (int32_t)taosArrayGetSize(pReq->pTableHash); + int32_t udfNum = (int32_t)taosArrayGetSize(pReq->pUdf); + int32_t qnodeNum = pReq->qNodeRequired ? 1 : 0; + int32_t dbCfgNum = (int32_t)taosArrayGetSize(pReq->pDbCfg); + int32_t indexNum = (int32_t)taosArrayGetSize(pReq->pIndex); + int32_t userNum = (int32_t)taosArrayGetSize(pReq->pUser); + + int32_t taskNum = tbMetaNum + dbVgNum + udfNum + tbHashNum + qnodeNum + dbCfgNum + indexNum + userNum; + if (taskNum <= 0) { + ctgError("empty input for job, taskNum:%d", taskNum); + CTG_ERR_RET(TSDB_CODE_CTG_INVALID_INPUT); + } + + *job = taosMemoryCalloc(1, sizeof(SCtgJob)); + if (NULL == *job) { + ctgError("calloc %d failed", (int32_t)sizeof(SCtgJob)); + CTG_ERR_RET(TSDB_CODE_OUT_OF_MEMORY); + } + + SCtgJob *pJob = *job; + + pJob->queryId = reqId; + pJob->userFp = fp; + pJob->pCtg = pCtg; + pJob->pTrans = pTrans; + pJob->pMgmtEps = pMgmtEps; + pJob->userParam = param; + + pJob->tbMetaNum = tbMetaNum; + pJob->tbHashNum = tbHashNum; + pJob->qnodeNum = qnodeNum; + pJob->dbVgNum = dbVgNum; + pJob->udfNum = udfNum; + pJob->dbCfgNum = dbCfgNum; + pJob->indexNum = indexNum; + pJob->userNum = userNum; + + pJob->pTasks = taosArrayInit(taskNum, sizeof(SCtgTask)); + + if (NULL == pJob->pTasks) { + ctgError("taosArrayInit %d tasks failed", taskNum); + CTG_ERR_JRET(TSDB_CODE_OUT_OF_MEMORY); + } + + int32_t taskIdx = 0; + for (int32_t i = 0; i < dbVgNum; ++i) { + char *dbFName = taosArrayGet(pReq->pDbVgroup, i); + CTG_ERR_JRET(ctgInitGetDbVgTask(pJob, taskIdx++, dbFName)); + } + + for (int32_t i = 0; i < dbCfgNum; ++i) { + char *dbFName = taosArrayGet(pReq->pDbCfg, i); + CTG_ERR_JRET(ctgInitGetDbCfgTask(pJob, taskIdx++, dbFName)); + } + + for (int32_t i = 0; i < tbMetaNum; ++i) { + SName *name = taosArrayGet(pReq->pTableMeta, i); + CTG_ERR_JRET(ctgInitGetTbMetaTask(pJob, taskIdx++, name)); + } + + for (int32_t i = 0; i < tbHashNum; ++i) { + SName *name = taosArrayGet(pReq->pTableHash, i); + CTG_ERR_JRET(ctgInitGetTbHashTask(pJob, taskIdx++, name)); + } + + for (int32_t i = 0; i < indexNum; ++i) { + char *indexName = taosArrayGet(pReq->pIndex, i); + CTG_ERR_JRET(ctgInitGetIndexTask(pJob, taskIdx++, indexName)); + } + + for (int32_t i = 0; i < udfNum; ++i) { + char *udfName = taosArrayGet(pReq->pUdf, i); + CTG_ERR_JRET(ctgInitGetUdfTask(pJob, taskIdx++, udfName)); + } + + for (int32_t i = 0; i < userNum; ++i) { + SUserAuthInfo *user = taosArrayGet(pReq->pUser, i); + CTG_ERR_JRET(ctgInitGetUserTask(pJob, taskIdx++, user)); + } + + if (qnodeNum) { + CTG_ERR_JRET(ctgInitGetQnodeTask(pJob, taskIdx++)); + } + + pJob->refId = taosAddRef(gCtgMgmt.jobPool, pJob); + if (pJob->refId < 0) { + ctgError("add job to ref failed, error: %s", tstrerror(terrno)); + CTG_ERR_JRET(terrno); + } + + taosAcquireRef(gCtgMgmt.jobPool, pJob->refId); + + qDebug("QID:%" PRIx64 ", job %" PRIx64 " initialized, task num %d", pJob->queryId, pJob->refId, taskNum); + + return TSDB_CODE_SUCCESS; + +_return: + + taosMemoryFreeClear(*job); + + CTG_RET(code); +} + +int32_t ctgDumpTbMetaRes(SCtgTask* pTask) { + SCtgJob* pJob = pTask->pJob; + if (NULL == pJob->jobRes.pTableMeta) { + pJob->jobRes.pTableMeta = taosArrayInit(pJob->tbMetaNum, sizeof(STableMeta)); + if (NULL == pJob->jobRes.pTableMeta) { + CTG_ERR_RET(TSDB_CODE_OUT_OF_MEMORY); + } + } + + taosArrayPush(pJob->jobRes.pTableMeta, pTask->res); + + taosMemoryFreeClear(pTask->res); + + return TSDB_CODE_SUCCESS; +} + +int32_t ctgDumpDbVgRes(SCtgTask* pTask) { + SCtgJob* pJob = pTask->pJob; + if (NULL == pJob->jobRes.pDbVgroup) { + pJob->jobRes.pDbVgroup = taosArrayInit(pJob->dbVgNum, POINTER_BYTES); + if (NULL == pJob->jobRes.pDbVgroup) { + CTG_ERR_RET(TSDB_CODE_OUT_OF_MEMORY); + } + } + + taosArrayPush(pJob->jobRes.pDbVgroup, &pTask->res); + pTask->res = NULL; + + return TSDB_CODE_SUCCESS; +} + +int32_t ctgDumpTbHashRes(SCtgTask* pTask) { + SCtgJob* pJob = pTask->pJob; + if (NULL == pJob->jobRes.pTableHash) { + pJob->jobRes.pTableHash = taosArrayInit(pJob->tbHashNum, sizeof(SVgroupInfo)); + if (NULL == pJob->jobRes.pTableHash) { + CTG_ERR_RET(TSDB_CODE_OUT_OF_MEMORY); + } + } + + taosArrayPush(pJob->jobRes.pTableHash, &pTask->res); + + return TSDB_CODE_SUCCESS; +} + +int32_t ctgDumpIndexRes(SCtgTask* pTask) { + SCtgJob* pJob = pTask->pJob; + if (NULL == pJob->jobRes.pIndex) { + pJob->jobRes.pIndex = taosArrayInit(pJob->indexNum, sizeof(SIndexInfo)); + if (NULL == pJob->jobRes.pIndex) { + CTG_ERR_RET(TSDB_CODE_OUT_OF_MEMORY); + } + } + + taosArrayPush(pJob->jobRes.pIndex, pTask->res); + + return TSDB_CODE_SUCCESS; +} + +int32_t ctgDumpQnodeRes(SCtgTask* pTask) { + SCtgJob* pJob = pTask->pJob; + + TSWAP(pJob->jobRes.pQnodeList, pTask->res); + + return TSDB_CODE_SUCCESS; +} + +int32_t ctgDumpDbCfgRes(SCtgTask* pTask) { + SCtgJob* pJob = pTask->pJob; + if (NULL == pJob->jobRes.pDbCfg) { + pJob->jobRes.pDbCfg = taosArrayInit(pJob->dbCfgNum, sizeof(SDbCfgInfo)); + if (NULL == pJob->jobRes.pDbCfg) { + CTG_ERR_RET(TSDB_CODE_OUT_OF_MEMORY); + } + } + + taosArrayPush(pJob->jobRes.pDbCfg, &pTask->res); + + return TSDB_CODE_SUCCESS; +} + +int32_t ctgDumpUdfRes(SCtgTask* pTask) { + SCtgJob* pJob = pTask->pJob; + if (NULL == pJob->jobRes.pUdfList) { + pJob->jobRes.pUdfList = taosArrayInit(pJob->udfNum, sizeof(SFuncInfo)); + if (NULL == pJob->jobRes.pUdfList) { + CTG_ERR_RET(TSDB_CODE_OUT_OF_MEMORY); + } + } + + taosArrayPush(pJob->jobRes.pUdfList, pTask->res); + + return TSDB_CODE_SUCCESS; +} + +int32_t ctgDumpUserRes(SCtgTask* pTask) { + SCtgJob* pJob = pTask->pJob; + if (NULL == pJob->jobRes.pUser) { + pJob->jobRes.pUser = taosArrayInit(pJob->userNum, sizeof(bool)); + if (NULL == pJob->jobRes.pUser) { + CTG_ERR_RET(TSDB_CODE_OUT_OF_MEMORY); + } + } + + taosArrayPush(pJob->jobRes.pUser, pTask->res); + + return TSDB_CODE_SUCCESS; +} + +int32_t ctgHandleTaskEnd(SCtgTask* pTask, int32_t rspCode) { + SCtgJob* pJob = pTask->pJob; + int32_t code = 0; + + qDebug("QID:%" PRIx64 " task %d end with rsp %s", pJob->queryId, pTask->taskId, tstrerror(rspCode)); + + if (rspCode) { + int32_t lastCode = atomic_val_compare_exchange_32(&pJob->rspCode, 0, rspCode); + if (0 == lastCode) { + CTG_ERR_JRET(rspCode); + } + + return TSDB_CODE_SUCCESS; + } + + int32_t taskDone = atomic_add_fetch_32(&pJob->taskDone, 1); + if (taskDone < taosArrayGetSize(pJob->pTasks)) { + qDebug("task done: %d, total: %d", taskDone, (int32_t)taosArrayGetSize(pJob->pTasks)); + return TSDB_CODE_SUCCESS; + } + + CTG_ERR_JRET(ctgMakeAsyncRes(pJob)); + +_return: + + qDebug("QID:%" PRIx64 " user callback with rsp %s", pJob->queryId, tstrerror(code)); + + (*pJob->userFp)(&pJob->jobRes, pJob->userParam, code); + + taosRemoveRef(gCtgMgmt.jobPool, pJob->refId); + + CTG_RET(code); +} + +int32_t ctgHandleGetTbMetaRsp(SCtgTask* pTask, int32_t reqType, const SDataBuf *pMsg, int32_t rspCode) { + int32_t code = 0; + SCtgDBCache *dbCache = NULL; + CTG_ERR_JRET(ctgProcessRspMsg(pTask->msgCtx.out, reqType, pMsg->pData, pMsg->len, rspCode, pTask->msgCtx.target)); + + SCtgTbMetaCtx* ctx = (SCtgTbMetaCtx*)pTask->taskCtx; + SCatalog* pCtg = pTask->pJob->pCtg; + void *pTrans = pTask->pJob->pTrans; + const SEpSet* pMgmtEps = pTask->pJob->pMgmtEps; + + switch (reqType) { + case TDMT_MND_USE_DB: { + SUseDbOutput* pOut = (SUseDbOutput*)pTask->msgCtx.out; + + SVgroupInfo vgInfo = {0}; + CTG_ERR_JRET(ctgGetVgInfoFromHashValue(pCtg, pOut->dbVgroup, ctx->pName, &vgInfo)); + + ctgDebug("will refresh tbmeta, not supposed to be stb, tbName:%s, flag:%d", tNameGetTableName(ctx->pName), ctx->flag); + + CTG_ERR_JRET(ctgGetTbMetaFromVnode(CTG_PARAMS_LIST(), ctx->pName, &vgInfo, NULL, pTask)); + + return TSDB_CODE_SUCCESS; + } + case TDMT_MND_TABLE_META: { + STableMetaOutput* pOut = (STableMetaOutput*)pTask->msgCtx.out; + + if (CTG_IS_META_NULL(pOut->metaType)) { + if (CTG_FLAG_IS_STB(ctx->flag)) { + char dbFName[TSDB_DB_FNAME_LEN] = {0}; + tNameGetFullDbName(ctx->pName, dbFName); + + CTG_ERR_RET(ctgAcquireVgInfoFromCache(pCtg, dbFName, &dbCache)); + if (NULL != dbCache) { + SVgroupInfo vgInfo = {0}; + CTG_ERR_JRET(ctgGetVgInfoFromHashValue(pCtg, dbCache->vgInfo, ctx->pName, &vgInfo)); + + ctgDebug("will refresh tbmeta, not supposed to be stb, tbName:%s, flag:%d", tNameGetTableName(ctx->pName), ctx->flag); + + CTG_ERR_JRET(ctgGetTbMetaFromVnode(CTG_PARAMS_LIST(), ctx->pName, &vgInfo, NULL, pTask)); + + ctgReleaseVgInfo(dbCache); + ctgReleaseDBCache(pCtg, dbCache); + } else { + SBuildUseDBInput input = {0}; + + tstrncpy(input.db, dbFName, tListLen(input.db)); + input.vgVersion = CTG_DEFAULT_INVALID_VERSION; + + CTG_ERR_JRET(ctgGetDBVgInfoFromMnode(pCtg, pTrans, pMgmtEps, &input, NULL, pTask)); + } + + return TSDB_CODE_SUCCESS; + } + + ctgError("no tbmeta got, tbNmae:%s", tNameGetTableName(ctx->pName)); + ctgRemoveTbMetaFromCache(pCtg, ctx->pName, false); + + CTG_ERR_JRET(CTG_ERR_CODE_TABLE_NOT_EXIST); + } + + if (pTask->msgCtx.lastOut) { + TSWAP(pTask->msgCtx.out, pTask->msgCtx.lastOut); + STableMetaOutput* pLastOut = (STableMetaOutput*)pTask->msgCtx.out; + TSWAP(pLastOut->tbMeta, pOut->tbMeta); + } + + break; + } + case TDMT_VND_TABLE_META: { + STableMetaOutput* pOut = (STableMetaOutput*)pTask->msgCtx.out; + + if (CTG_IS_META_NULL(pOut->metaType)) { + ctgError("no tbmeta got, tbNmae:%s", tNameGetTableName(ctx->pName)); + ctgRemoveTbMetaFromCache(pCtg, ctx->pName, false); + CTG_ERR_JRET(CTG_ERR_CODE_TABLE_NOT_EXIST); + } + + if (CTG_FLAG_IS_STB(ctx->flag)) { + break; + } + + if (CTG_IS_META_TABLE(pOut->metaType) && TSDB_SUPER_TABLE == pOut->tbMeta->tableType) { + ctgDebug("will continue to refresh tbmeta since got stb, tbName:%s", tNameGetTableName(ctx->pName)); + + taosMemoryFreeClear(pOut->tbMeta); + + CTG_ERR_JRET(ctgGetTbMetaFromMnode(CTG_PARAMS_LIST(), ctx->pName, NULL, pTask)); + } else if (CTG_IS_META_BOTH(pOut->metaType)) { + int32_t exist = 0; + if (!CTG_FLAG_IS_FORCE_UPDATE(ctx->flag)) { + CTG_ERR_JRET(ctgTbMetaExistInCache(pCtg, pOut->dbFName, pOut->tbName, &exist)); + } + + if (0 == exist) { + TSWAP(pTask->msgCtx.lastOut, pTask->msgCtx.out); + CTG_ERR_JRET(ctgGetTbMetaFromMnodeImpl(CTG_PARAMS_LIST(), pOut->dbFName, pOut->tbName, NULL, pTask)); + } else { + taosMemoryFreeClear(pOut->tbMeta); + + SET_META_TYPE_CTABLE(pOut->metaType); + } + } + break; + } + default: + ctgError("invalid reqType %d", reqType); + CTG_ERR_JRET(TSDB_CODE_INVALID_MSG); + break; + } + + STableMetaOutput* pOut = (STableMetaOutput*)pTask->msgCtx.out; + + ctgUpdateTbMetaToCache(pCtg, pOut, false); + + if (CTG_IS_META_BOTH(pOut->metaType)) { + memcpy(pOut->tbMeta, &pOut->ctbMeta, sizeof(pOut->ctbMeta)); + } else if (CTG_IS_META_CTABLE(pOut->metaType)) { + SName stbName = *ctx->pName; + strcpy(stbName.tname, pOut->tbName); + SCtgTbMetaCtx stbCtx = {0}; + stbCtx.flag = ctx->flag; + stbCtx.pName = &stbName; + + CTG_ERR_JRET(ctgReadTbMetaFromCache(pCtg, &stbCtx, &pOut->tbMeta)); + if (NULL == pOut->tbMeta) { + ctgDebug("stb no longer exist, stbName:%s", stbName.tname); + CTG_ERR_JRET(ctgRelaunchGetTbMetaTask(pTask)); + + return TSDB_CODE_SUCCESS; + } + + memcpy(pOut->tbMeta, &pOut->ctbMeta, sizeof(pOut->ctbMeta)); + } + + TSWAP(pTask->res, pOut->tbMeta); + +_return: + + if (dbCache) { + ctgReleaseVgInfo(dbCache); + ctgReleaseDBCache(pCtg, dbCache); + } + + ctgHandleTaskEnd(pTask, code); + + CTG_RET(code); +} + +int32_t ctgHandleGetDbVgRsp(SCtgTask* pTask, int32_t reqType, const SDataBuf *pMsg, int32_t rspCode) { + int32_t code = 0; + CTG_ERR_JRET(ctgProcessRspMsg(pTask->msgCtx.out, reqType, pMsg->pData, pMsg->len, rspCode, pTask->msgCtx.target)); + + SCtgDbVgCtx* ctx = (SCtgDbVgCtx*)pTask->taskCtx; + SCatalog* pCtg = pTask->pJob->pCtg; + void *pTrans = pTask->pJob->pTrans; + const SEpSet* pMgmtEps = pTask->pJob->pMgmtEps; + + switch (reqType) { + case TDMT_MND_USE_DB: { + SUseDbOutput* pOut = (SUseDbOutput*)pTask->msgCtx.out; + + CTG_ERR_JRET(ctgGenerateVgList(pCtg, pOut->dbVgroup->vgHash, (SArray**)&pTask->res)); + + CTG_ERR_JRET(ctgPutUpdateVgToQueue(pCtg, ctx->dbFName, pOut->dbId, pOut->dbVgroup, false)); + pOut->dbVgroup = NULL; + + break; + } + default: + ctgError("invalid reqType %d", reqType); + CTG_ERR_JRET(TSDB_CODE_INVALID_MSG); + break; + } + + +_return: + + ctgHandleTaskEnd(pTask, code); + + CTG_RET(code); +} + +int32_t ctgHandleGetTbHashRsp(SCtgTask* pTask, int32_t reqType, const SDataBuf *pMsg, int32_t rspCode) { + int32_t code = 0; + CTG_ERR_JRET(ctgProcessRspMsg(pTask->msgCtx.out, reqType, pMsg->pData, pMsg->len, rspCode, pTask->msgCtx.target)); + + SCtgTbHashCtx* ctx = (SCtgTbHashCtx*)pTask->taskCtx; + SCatalog* pCtg = pTask->pJob->pCtg; + void *pTrans = pTask->pJob->pTrans; + const SEpSet* pMgmtEps = pTask->pJob->pMgmtEps; + + switch (reqType) { + case TDMT_MND_USE_DB: { + SUseDbOutput* pOut = (SUseDbOutput*)pTask->msgCtx.out; + + pTask->res = taosMemoryMalloc(sizeof(SVgroupInfo)); + if (NULL == pTask->res) { + CTG_ERR_JRET(TSDB_CODE_OUT_OF_MEMORY); + } + + CTG_ERR_JRET(ctgGetVgInfoFromHashValue(pCtg, pOut->dbVgroup, ctx->pName, (SVgroupInfo*)pTask->res)); + + CTG_ERR_JRET(ctgPutUpdateVgToQueue(pCtg, ctx->dbFName, pOut->dbId, pOut->dbVgroup, false)); + pOut->dbVgroup = NULL; + + break; + } + default: + ctgError("invalid reqType %d", reqType); + CTG_ERR_JRET(TSDB_CODE_INVALID_MSG); + break; + } + + +_return: + + ctgHandleTaskEnd(pTask, code); + + CTG_RET(code); +} + +int32_t ctgHandleGetDbCfgRsp(SCtgTask* pTask, int32_t reqType, const SDataBuf *pMsg, int32_t rspCode) { + int32_t code = 0; + CTG_ERR_JRET(ctgProcessRspMsg(pTask->msgCtx.out, reqType, pMsg->pData, pMsg->len, rspCode, pTask->msgCtx.target)); + + TSWAP(pTask->res, pTask->msgCtx.out); + +_return: + + ctgHandleTaskEnd(pTask, code); + + CTG_RET(code); +} + +int32_t ctgHandleGetQnodeRsp(SCtgTask* pTask, int32_t reqType, const SDataBuf *pMsg, int32_t rspCode) { + int32_t code = 0; + CTG_ERR_JRET(ctgProcessRspMsg(pTask->msgCtx.out, reqType, pMsg->pData, pMsg->len, rspCode, pTask->msgCtx.target)); + + TSWAP(pTask->res, pTask->msgCtx.out); + +_return: + + ctgHandleTaskEnd(pTask, code); + + CTG_RET(code); +} + +int32_t ctgHandleGetIndexRsp(SCtgTask* pTask, int32_t reqType, const SDataBuf *pMsg, int32_t rspCode) { + int32_t code = 0; + CTG_ERR_JRET(ctgProcessRspMsg(pTask->msgCtx.out, reqType, pMsg->pData, pMsg->len, rspCode, pTask->msgCtx.target)); + + TSWAP(pTask->res, pTask->msgCtx.out); + +_return: + + ctgHandleTaskEnd(pTask, code); + + CTG_RET(code); +} + +int32_t ctgHandleGetUdfRsp(SCtgTask* pTask, int32_t reqType, const SDataBuf *pMsg, int32_t rspCode) { + int32_t code = 0; + CTG_ERR_JRET(ctgProcessRspMsg(pTask->msgCtx.out, reqType, pMsg->pData, pMsg->len, rspCode, pTask->msgCtx.target)); + + TSWAP(pTask->res, pTask->msgCtx.out); + +_return: + + ctgHandleTaskEnd(pTask, code); + + CTG_RET(code); +} + +int32_t ctgHandleGetUserRsp(SCtgTask* pTask, int32_t reqType, const SDataBuf *pMsg, int32_t rspCode) { + int32_t code = 0; + SCtgDBCache *dbCache = NULL; + CTG_ERR_JRET(ctgProcessRspMsg(pTask->msgCtx.out, reqType, pMsg->pData, pMsg->len, rspCode, pTask->msgCtx.target)); + + SCtgUserCtx* ctx = (SCtgUserCtx*)pTask->taskCtx; + SCatalog* pCtg = pTask->pJob->pCtg; + void *pTrans = pTask->pJob->pTrans; + const SEpSet* pMgmtEps = pTask->pJob->pMgmtEps; + bool pass = false; + SGetUserAuthRsp* pOut = (SGetUserAuthRsp*)pTask->msgCtx.out; + + if (pOut->superAuth) { + pass = true; + goto _return; + } + + if (pOut->createdDbs && taosHashGet(pOut->createdDbs, ctx->user.dbFName, strlen(ctx->user.dbFName))) { + pass = true; + goto _return; + } + + if (ctx->user.type == AUTH_TYPE_READ && pOut->readDbs && taosHashGet(pOut->readDbs, ctx->user.dbFName, strlen(ctx->user.dbFName))) { + pass = true; + } else if (ctx->user.type == AUTH_TYPE_WRITE && pOut->writeDbs && taosHashGet(pOut->writeDbs, ctx->user.dbFName, strlen(ctx->user.dbFName))) { + pass = true; + } + +_return: + + if (TSDB_CODE_SUCCESS == code) { + pTask->res = taosMemoryCalloc(1, sizeof(bool)); + if (NULL == pTask->res) { + code = TSDB_CODE_OUT_OF_MEMORY; + } else { + *(bool*)pTask->res = pass; + } + } + + ctgPutUpdateUserToQueue(pCtg, pOut, false); + pTask->msgCtx.out = NULL; + + ctgHandleTaskEnd(pTask, code); + + CTG_RET(code); +} + +int32_t ctgAsyncRefreshTbMeta(SCtgTask *pTask) { + SCatalog* pCtg = pTask->pJob->pCtg; + void *pTrans = pTask->pJob->pTrans; + const SEpSet* pMgmtEps = pTask->pJob->pMgmtEps; + int32_t code = 0; + SCtgTbMetaCtx* ctx = (SCtgTbMetaCtx*)pTask->taskCtx; + + if (CTG_FLAG_IS_SYS_DB(ctx->flag)) { + ctgDebug("will refresh sys db tbmeta, tbName:%s", tNameGetTableName(ctx->pName)); + + CTG_RET(ctgGetTbMetaFromMnodeImpl(CTG_PARAMS_LIST(), (char *)ctx->pName->dbname, (char *)ctx->pName->tname, NULL, pTask)); + } + + if (CTG_FLAG_IS_STB(ctx->flag)) { + ctgDebug("will refresh tbmeta, supposed to be stb, tbName:%s", tNameGetTableName(ctx->pName)); + + // if get from mnode failed, will not try vnode + CTG_RET(ctgGetTbMetaFromMnode(CTG_PARAMS_LIST(), ctx->pName, NULL, pTask)); + } + + SCtgDBCache *dbCache = NULL; + char dbFName[TSDB_DB_FNAME_LEN] = {0}; + tNameGetFullDbName(ctx->pName, dbFName); + + CTG_ERR_RET(ctgAcquireVgInfoFromCache(pCtg, dbFName, &dbCache)); + if (NULL == dbCache) { + SVgroupInfo vgInfo = {0}; + CTG_ERR_RET(ctgGetVgInfoFromHashValue(pCtg, dbCache->vgInfo, ctx->pName, &vgInfo)); + + ctgDebug("will refresh tbmeta, not supposed to be stb, tbName:%s, flag:%d", tNameGetTableName(ctx->pName), ctx->flag); + + CTG_ERR_JRET(ctgGetTbMetaFromVnode(CTG_PARAMS_LIST(), ctx->pName, &vgInfo, NULL, pTask)); + } else { + SBuildUseDBInput input = {0}; + + tstrncpy(input.db, dbFName, tListLen(input.db)); + input.vgVersion = CTG_DEFAULT_INVALID_VERSION; + + CTG_ERR_JRET(ctgGetDBVgInfoFromMnode(pCtg, pTrans, pMgmtEps, &input, NULL, pTask)); + } + +_return: + + if (dbCache) { + ctgReleaseVgInfo(dbCache); + ctgReleaseDBCache(pCtg, dbCache); + } + + CTG_RET(code); +} + +int32_t ctgLaunchGetTbMetaTask(SCtgTask *pTask) { + SCatalog* pCtg = pTask->pJob->pCtg; + void *pTrans = pTask->pJob->pTrans; + const SEpSet* pMgmtEps = pTask->pJob->pMgmtEps; + + CTG_ERR_RET(ctgGetTbMetaFromCache(CTG_PARAMS_LIST(), (SCtgTbMetaCtx*)pTask->taskCtx, (STableMeta**)&pTask->res)); + if (pTask->res) { + CTG_ERR_RET(ctgHandleTaskEnd(pTask, 0)); + return TSDB_CODE_SUCCESS; + } + + CTG_ERR_RET(ctgAsyncRefreshTbMeta(pTask)); + + return TSDB_CODE_SUCCESS; +} + +int32_t ctgLaunchGetDbVgTask(SCtgTask *pTask) { + int32_t code = 0; + SCatalog* pCtg = pTask->pJob->pCtg; + void *pTrans = pTask->pJob->pTrans; + const SEpSet* pMgmtEps = pTask->pJob->pMgmtEps; + SCtgDBCache *dbCache = NULL; + SCtgDbVgCtx* pCtx = (SCtgDbVgCtx*)pTask->taskCtx; + + CTG_ERR_RET(ctgAcquireVgInfoFromCache(pCtg, pCtx->dbFName, &dbCache)); + if (NULL != dbCache) { + CTG_ERR_JRET(ctgGenerateVgList(pCtg, dbCache->vgInfo->vgHash, (SArray**)&pTask->res)); + + CTG_ERR_JRET(ctgHandleTaskEnd(pTask, 0)); + } else { + SBuildUseDBInput input = {0}; + + tstrncpy(input.db, pCtx->dbFName, tListLen(input.db)); + input.vgVersion = CTG_DEFAULT_INVALID_VERSION; + + CTG_ERR_RET(ctgGetDBVgInfoFromMnode(pCtg, pTrans, pMgmtEps, &input, NULL, pTask)); + } + +_return: + + if (dbCache) { + ctgReleaseVgInfo(dbCache); + ctgReleaseDBCache(pCtg, dbCache); + } + + CTG_RET(code); +} + +int32_t ctgLaunchGetTbHashTask(SCtgTask *pTask) { + int32_t code = 0; + SCatalog* pCtg = pTask->pJob->pCtg; + void *pTrans = pTask->pJob->pTrans; + const SEpSet* pMgmtEps = pTask->pJob->pMgmtEps; + SCtgDBCache *dbCache = NULL; + SCtgTbHashCtx* pCtx = (SCtgTbHashCtx*)pTask->taskCtx; + + CTG_ERR_RET(ctgAcquireVgInfoFromCache(pCtg, pCtx->dbFName, &dbCache)); + if (NULL != dbCache) { + pTask->res = taosMemoryMalloc(sizeof(SVgroupInfo)); + if (NULL == pTask->res) { + CTG_ERR_JRET(TSDB_CODE_OUT_OF_MEMORY); + } + CTG_ERR_JRET(ctgGetVgInfoFromHashValue(pCtg, dbCache->vgInfo, pCtx->pName, (SVgroupInfo*)pTask->res)); + + CTG_ERR_JRET(ctgHandleTaskEnd(pTask, 0)); + } else { + SBuildUseDBInput input = {0}; + + tstrncpy(input.db, pCtx->dbFName, tListLen(input.db)); + input.vgVersion = CTG_DEFAULT_INVALID_VERSION; + + CTG_ERR_RET(ctgGetDBVgInfoFromMnode(pCtg, pTrans, pMgmtEps, &input, NULL, pTask)); + } + +_return: + + if (dbCache) { + ctgReleaseVgInfo(dbCache); + ctgReleaseDBCache(pCtg, dbCache); + } + + CTG_RET(code); +} + +int32_t ctgLaunchGetQnodeTask(SCtgTask *pTask) { + SCatalog* pCtg = pTask->pJob->pCtg; + void *pTrans = pTask->pJob->pTrans; + const SEpSet* pMgmtEps = pTask->pJob->pMgmtEps; + + CTG_ERR_RET(ctgGetQnodeListFromMnode(CTG_PARAMS_LIST(), NULL, pTask)); + + return TSDB_CODE_SUCCESS; +} + +int32_t ctgLaunchGetDbCfgTask(SCtgTask *pTask) { + SCatalog* pCtg = pTask->pJob->pCtg; + void *pTrans = pTask->pJob->pTrans; + const SEpSet* pMgmtEps = pTask->pJob->pMgmtEps; + SCtgDbCfgCtx* pCtx = (SCtgDbCfgCtx*)pTask->taskCtx; + + CTG_ERR_RET(ctgGetDBCfgFromMnode(CTG_PARAMS_LIST(), pCtx->dbFName, NULL, pTask)); + + return TSDB_CODE_SUCCESS; +} + +int32_t ctgLaunchGetIndexTask(SCtgTask *pTask) { + SCatalog* pCtg = pTask->pJob->pCtg; + void *pTrans = pTask->pJob->pTrans; + const SEpSet* pMgmtEps = pTask->pJob->pMgmtEps; + SCtgIndexCtx* pCtx = (SCtgIndexCtx*)pTask->taskCtx; + + CTG_ERR_RET(ctgGetIndexInfoFromMnode(CTG_PARAMS_LIST(), pCtx->indexFName, NULL, pTask)); + + return TSDB_CODE_SUCCESS; +} + +int32_t ctgLaunchGetUdfTask(SCtgTask *pTask) { + SCatalog* pCtg = pTask->pJob->pCtg; + void *pTrans = pTask->pJob->pTrans; + const SEpSet* pMgmtEps = pTask->pJob->pMgmtEps; + SCtgUdfCtx* pCtx = (SCtgUdfCtx*)pTask->taskCtx; + + CTG_ERR_RET(ctgGetUdfInfoFromMnode(CTG_PARAMS_LIST(), pCtx->udfName, NULL, pTask)); + + return TSDB_CODE_SUCCESS; +} + +int32_t ctgLaunchGetUserTask(SCtgTask *pTask) { + SCatalog* pCtg = pTask->pJob->pCtg; + void *pTrans = pTask->pJob->pTrans; + const SEpSet* pMgmtEps = pTask->pJob->pMgmtEps; + SCtgUserCtx* pCtx = (SCtgUserCtx*)pTask->taskCtx; + bool inCache = false; + bool pass = false; + + CTG_ERR_RET(ctgChkAuthFromCache(pCtg, pCtx->user.user, pCtx->user.dbFName, pCtx->user.type, &inCache, &pass)); + if (inCache) { + pTask->res = taosMemoryCalloc(1, sizeof(bool)); + if (NULL == pTask->res) { + CTG_ERR_RET(TSDB_CODE_OUT_OF_MEMORY); + } + *(bool*)pTask->res = pass; + + CTG_ERR_RET(ctgHandleTaskEnd(pTask, 0)); + return TSDB_CODE_SUCCESS; + } + + CTG_ERR_RET(ctgGetUserDbAuthFromMnode(CTG_PARAMS_LIST(), pCtx->user.user, NULL, pTask)); + + return TSDB_CODE_SUCCESS; +} + +int32_t ctgRelaunchGetTbMetaTask(SCtgTask *pTask) { + ctgResetTbMetaTask(pTask); + + CTG_ERR_RET(ctgLaunchGetTbMetaTask(pTask)); + + return TSDB_CODE_SUCCESS; +} + +SCtgAsyncFps gCtgAsyncFps[] = { + {ctgLaunchGetQnodeTask, ctgHandleGetQnodeRsp, ctgDumpQnodeRes}, + {ctgLaunchGetDbVgTask, ctgHandleGetDbVgRsp, ctgDumpDbVgRes}, + {ctgLaunchGetDbCfgTask, ctgHandleGetDbCfgRsp, ctgDumpDbCfgRes}, + {ctgLaunchGetTbMetaTask, ctgHandleGetTbMetaRsp, ctgDumpTbMetaRes}, + {ctgLaunchGetTbHashTask, ctgHandleGetTbHashRsp, ctgDumpTbHashRes}, + {ctgLaunchGetIndexTask, ctgHandleGetIndexRsp, ctgDumpIndexRes}, + {ctgLaunchGetUdfTask, ctgHandleGetUdfRsp, ctgDumpUdfRes}, + {ctgLaunchGetUserTask, ctgHandleGetUserRsp, ctgDumpUserRes}, +}; + +int32_t ctgMakeAsyncRes(SCtgJob *pJob) { + int32_t code = 0; + int32_t taskNum = taosArrayGetSize(pJob->pTasks); + + for (int32_t i = 0; i < taskNum; ++i) { + SCtgTask *pTask = taosArrayGet(pJob->pTasks, i); + CTG_ERR_RET((*gCtgAsyncFps[pTask->type].dumpResFp)(pTask)); + } + + return TSDB_CODE_SUCCESS; +} + + +int32_t ctgLaunchJob(SCtgJob *pJob) { + int32_t taskNum = taosArrayGetSize(pJob->pTasks); + + for (int32_t i = 0; i < taskNum; ++i) { + SCtgTask *pTask = taosArrayGet(pJob->pTasks, i); + + qDebug("QID:%" PRIx64 " start to launch task %d", pJob->queryId, pTask->taskId); + CTG_ERR_RET((*gCtgAsyncFps[pTask->type].launchFp)(pTask)); + } + + return TSDB_CODE_SUCCESS; +} + + + diff --git a/source/libs/catalog/src/ctgCache.c b/source/libs/catalog/src/ctgCache.c new file mode 100644 index 0000000000000000000000000000000000000000..6335a056b9461877745fb80b893c9235ef7622a5 --- /dev/null +++ b/source/libs/catalog/src/ctgCache.c @@ -0,0 +1,1520 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "trpc.h" +#include "query.h" +#include "tname.h" +#include "catalogInt.h" +#include "systable.h" + +SCtgAction gCtgAction[CTG_ACT_MAX] = { + { + CTG_ACT_UPDATE_VG, + "update vgInfo", + ctgActUpdateVg + }, + { + CTG_ACT_UPDATE_TBL, + "update tbMeta", + ctgActUpdateTb + }, + { + CTG_ACT_REMOVE_DB, + "remove DB", + ctgActRemoveDB + }, + { + CTG_ACT_REMOVE_STB, + "remove stbMeta", + ctgActRemoveStb + }, + { + CTG_ACT_REMOVE_TBL, + "remove tbMeta", + ctgActRemoveTb + }, + { + CTG_ACT_UPDATE_USER, + "update user", + ctgActUpdateUser + } +}; + + + + +int32_t ctgAcquireVgInfo(SCatalog *pCtg, SCtgDBCache *dbCache, bool *inCache) { + CTG_LOCK(CTG_READ, &dbCache->vgLock); + + if (dbCache->deleted) { + CTG_UNLOCK(CTG_READ, &dbCache->vgLock); + + ctgDebug("db is dropping, dbId:%"PRIx64, dbCache->dbId); + + *inCache = false; + return TSDB_CODE_SUCCESS; + } + + + if (NULL == dbCache->vgInfo) { + CTG_UNLOCK(CTG_READ, &dbCache->vgLock); + + *inCache = false; + ctgDebug("db vgInfo is empty, dbId:%"PRIx64, dbCache->dbId); + return TSDB_CODE_SUCCESS; + } + + *inCache = true; + + return TSDB_CODE_SUCCESS; +} + +int32_t ctgWAcquireVgInfo(SCatalog *pCtg, SCtgDBCache *dbCache) { + CTG_LOCK(CTG_WRITE, &dbCache->vgLock); + + if (dbCache->deleted) { + ctgDebug("db is dropping, dbId:%"PRIx64, dbCache->dbId); + CTG_UNLOCK(CTG_WRITE, &dbCache->vgLock); + CTG_ERR_RET(TSDB_CODE_CTG_DB_DROPPED); + } + + return TSDB_CODE_SUCCESS; +} + +void ctgReleaseDBCache(SCatalog *pCtg, SCtgDBCache *dbCache) { + taosHashRelease(pCtg->dbCache, dbCache); +} + +void ctgReleaseVgInfo(SCtgDBCache *dbCache) { + CTG_UNLOCK(CTG_READ, &dbCache->vgLock); +} + +void ctgWReleaseVgInfo(SCtgDBCache *dbCache) { + CTG_UNLOCK(CTG_WRITE, &dbCache->vgLock); +} + + +int32_t ctgAcquireDBCacheImpl(SCatalog* pCtg, const char *dbFName, SCtgDBCache **pCache, bool acquire) { + char *p = strchr(dbFName, '.'); + if (p && CTG_IS_SYS_DBNAME(p + 1)) { + dbFName = p + 1; + } + + SCtgDBCache *dbCache = NULL; + if (acquire) { + dbCache = (SCtgDBCache *)taosHashAcquire(pCtg->dbCache, dbFName, strlen(dbFName)); + } else { + dbCache = (SCtgDBCache *)taosHashGet(pCtg->dbCache, dbFName, strlen(dbFName)); + } + + if (NULL == dbCache) { + *pCache = NULL; + ctgDebug("db not in cache, dbFName:%s", dbFName); + return TSDB_CODE_SUCCESS; + } + + if (dbCache->deleted) { + if (acquire) { + ctgReleaseDBCache(pCtg, dbCache); + } + + *pCache = NULL; + ctgDebug("db is removing from cache, dbFName:%s", dbFName); + return TSDB_CODE_SUCCESS; + } + + *pCache = dbCache; + + return TSDB_CODE_SUCCESS; +} + +int32_t ctgAcquireDBCache(SCatalog* pCtg, const char *dbFName, SCtgDBCache **pCache) { + CTG_RET(ctgAcquireDBCacheImpl(pCtg, dbFName, pCache, true)); +} + +int32_t ctgGetDBCache(SCatalog* pCtg, const char *dbFName, SCtgDBCache **pCache) { + CTG_RET(ctgAcquireDBCacheImpl(pCtg, dbFName, pCache, false)); +} + + +int32_t ctgAcquireVgInfoFromCache(SCatalog* pCtg, const char *dbFName, SCtgDBCache **pCache) { + SCtgDBCache *dbCache = NULL; + + if (NULL == pCtg->dbCache) { + ctgDebug("empty db cache, dbFName:%s", dbFName); + goto _return; + } + + ctgAcquireDBCache(pCtg, dbFName, &dbCache); + if (NULL == dbCache) { + ctgDebug("db %s not in cache", dbFName); + goto _return; + } + + bool inCache = false; + ctgAcquireVgInfo(pCtg, dbCache, &inCache); + if (!inCache) { + ctgDebug("vgInfo of db %s not in cache", dbFName); + goto _return; + } + + *pCache = dbCache; + + CTG_CACHE_STAT_ADD(vgHitNum, 1); + + ctgDebug("Got db vgInfo from cache, dbFName:%s", dbFName); + + return TSDB_CODE_SUCCESS; + +_return: + + if (dbCache) { + ctgReleaseDBCache(pCtg, dbCache); + } + + *pCache = NULL; + + CTG_CACHE_STAT_ADD(vgMissNum, 1); + + return TSDB_CODE_SUCCESS; +} + +int32_t ctgTbMetaExistInCache(SCatalog* pCtg, char *dbFName, char* tbName, int32_t *exist) { + if (NULL == pCtg->dbCache) { + *exist = 0; + ctgWarn("empty db cache, dbFName:%s, tbName:%s", dbFName, tbName); + return TSDB_CODE_SUCCESS; + } + + SCtgDBCache *dbCache = NULL; + ctgAcquireDBCache(pCtg, dbFName, &dbCache); + if (NULL == dbCache) { + *exist = 0; + return TSDB_CODE_SUCCESS; + } + + size_t sz = 0; + CTG_LOCK(CTG_READ, &dbCache->tbCache.metaLock); + STableMeta *tbMeta = taosHashGet(dbCache->tbCache.metaCache, tbName, strlen(tbName)); + CTG_UNLOCK(CTG_READ, &dbCache->tbCache.metaLock); + + if (NULL == tbMeta) { + ctgReleaseDBCache(pCtg, dbCache); + + *exist = 0; + ctgDebug("tbmeta not in cache, dbFName:%s, tbName:%s", dbFName, tbName); + return TSDB_CODE_SUCCESS; + } + + *exist = 1; + + ctgReleaseDBCache(pCtg, dbCache); + + ctgDebug("tbmeta is in cache, dbFName:%s, tbName:%s", dbFName, tbName); + + return TSDB_CODE_SUCCESS; +} + + +int32_t ctgReadTbMetaFromCache(SCatalog* pCtg, SCtgTbMetaCtx* ctx, STableMeta** pTableMeta) { + int32_t code = 0; + SCtgDBCache *dbCache = NULL; + + *pTableMeta = NULL; + + if (NULL == pCtg->dbCache) { + ctgDebug("empty tbmeta cache, tbName:%s", ctx->pName->tname); + return TSDB_CODE_SUCCESS; + } + + char dbFName[TSDB_DB_FNAME_LEN] = {0}; + if (CTG_FLAG_IS_SYS_DB(ctx->flag)) { + strcpy(dbFName, ctx->pName->dbname); + } else { + tNameGetFullDbName(ctx->pName, dbFName); + } + + ctgAcquireDBCache(pCtg, dbFName, &dbCache); + if (NULL == dbCache) { + ctgDebug("db %s not in cache", ctx->pName->tname); + return TSDB_CODE_SUCCESS; + } + + int32_t sz = 0; + CTG_LOCK(CTG_READ, &dbCache->tbCache.metaLock); + taosHashGetDup_m(dbCache->tbCache.metaCache, ctx->pName->tname, strlen(ctx->pName->tname), (void **)pTableMeta, &sz); + CTG_UNLOCK(CTG_READ, &dbCache->tbCache.metaLock); + + if (NULL == *pTableMeta) { + ctgReleaseDBCache(pCtg, dbCache); + ctgDebug("tbl not in cache, dbFName:%s, tbName:%s", dbFName, ctx->pName->tname); + return TSDB_CODE_SUCCESS; + } + + STableMeta* tbMeta = *pTableMeta; + ctx->tbInfo.inCache = true; + ctx->tbInfo.dbId = dbCache->dbId; + ctx->tbInfo.suid = tbMeta->suid; + ctx->tbInfo.tbType = tbMeta->tableType; + + if (tbMeta->tableType != TSDB_CHILD_TABLE) { + ctgReleaseDBCache(pCtg, dbCache); + ctgDebug("Got meta from cache, type:%d, dbFName:%s, tbName:%s", tbMeta->tableType, dbFName, ctx->pName->tname); + + CTG_CACHE_STAT_ADD(tblHitNum, 1); + return TSDB_CODE_SUCCESS; + } + + CTG_LOCK(CTG_READ, &dbCache->tbCache.stbLock); + + STableMeta **stbMeta = taosHashGet(dbCache->tbCache.stbCache, &tbMeta->suid, sizeof(tbMeta->suid)); + if (NULL == stbMeta || NULL == *stbMeta) { + CTG_UNLOCK(CTG_READ, &dbCache->tbCache.stbLock); + ctgError("stb not in stbCache, suid:%"PRIx64, tbMeta->suid); + goto _return; + } + + if ((*stbMeta)->suid != tbMeta->suid) { + CTG_UNLOCK(CTG_READ, &dbCache->tbCache.stbLock); + ctgError("stable suid in stbCache mis-match, expected suid:%"PRIx64 ",actual suid:%"PRIx64, tbMeta->suid, (*stbMeta)->suid); + CTG_ERR_JRET(TSDB_CODE_CTG_INTERNAL_ERROR); + } + + int32_t metaSize = CTG_META_SIZE(*stbMeta); + *pTableMeta = taosMemoryRealloc(*pTableMeta, metaSize); + if (NULL == *pTableMeta) { + CTG_UNLOCK(CTG_READ, &dbCache->tbCache.stbLock); + ctgError("realloc size[%d] failed", metaSize); + CTG_ERR_JRET(TSDB_CODE_CTG_MEM_ERROR); + } + + memcpy(&(*pTableMeta)->sversion, &(*stbMeta)->sversion, metaSize - sizeof(SCTableMeta)); + + CTG_UNLOCK(CTG_READ, &dbCache->tbCache.stbLock); + + ctgReleaseDBCache(pCtg, dbCache); + + CTG_CACHE_STAT_ADD(tblHitNum, 1); + + ctgDebug("Got tbmeta from cache, dbFName:%s, tbName:%s", dbFName, ctx->pName->tname); + + return TSDB_CODE_SUCCESS; + +_return: + + ctgReleaseDBCache(pCtg, dbCache); + taosMemoryFreeClear(*pTableMeta); + + CTG_CACHE_STAT_ADD(tblMissNum, 1); + + CTG_RET(code); +} + +int32_t ctgReadTbSverFromCache(SCatalog *pCtg, const SName *pTableName, int32_t *sver, int32_t *tbType, uint64_t *suid, + char *stbName) { + *sver = -1; + + if (NULL == pCtg->dbCache) { + ctgDebug("empty tbmeta cache, tbName:%s", pTableName->tname); + return TSDB_CODE_SUCCESS; + } + + SCtgDBCache *dbCache = NULL; + char dbFName[TSDB_DB_FNAME_LEN] = {0}; + tNameGetFullDbName(pTableName, dbFName); + + ctgAcquireDBCache(pCtg, dbFName, &dbCache); + if (NULL == dbCache) { + ctgDebug("db %s not in cache", pTableName->tname); + return TSDB_CODE_SUCCESS; + } + + CTG_LOCK(CTG_READ, &dbCache->tbCache.metaLock); + STableMeta *tbMeta = taosHashGet(dbCache->tbCache.metaCache, pTableName->tname, strlen(pTableName->tname)); + if (tbMeta) { + *tbType = tbMeta->tableType; + *suid = tbMeta->suid; + if (*tbType != TSDB_CHILD_TABLE) { + *sver = tbMeta->sversion; + } + } + CTG_UNLOCK(CTG_READ, &dbCache->tbCache.metaLock); + + if (NULL == tbMeta) { + ctgReleaseDBCache(pCtg, dbCache); + return TSDB_CODE_SUCCESS; + } + + if (*tbType != TSDB_CHILD_TABLE) { + ctgReleaseDBCache(pCtg, dbCache); + ctgDebug("Got sver %d from cache, type:%d, dbFName:%s, tbName:%s", *sver, *tbType, dbFName, pTableName->tname); + + return TSDB_CODE_SUCCESS; + } + + ctgDebug("Got subtable meta from cache, dbFName:%s, tbName:%s, suid:%" PRIx64, dbFName, pTableName->tname, *suid); + + CTG_LOCK(CTG_READ, &dbCache->tbCache.stbLock); + + STableMeta **stbMeta = taosHashGet(dbCache->tbCache.stbCache, suid, sizeof(*suid)); + if (NULL == stbMeta || NULL == *stbMeta) { + CTG_UNLOCK(CTG_READ, &dbCache->tbCache.stbLock); + ctgReleaseDBCache(pCtg, dbCache); + ctgDebug("stb not in stbCache, suid:%" PRIx64, *suid); + return TSDB_CODE_SUCCESS; + } + + if ((*stbMeta)->suid != *suid) { + CTG_UNLOCK(CTG_READ, &dbCache->tbCache.stbLock); + ctgReleaseDBCache(pCtg, dbCache); + ctgError("stable suid in stbCache mis-match, expected suid:%" PRIx64 ",actual suid:%" PRIx64, *suid, + (*stbMeta)->suid); + CTG_ERR_RET(TSDB_CODE_CTG_INTERNAL_ERROR); + } + + size_t nameLen = 0; + char *name = taosHashGetKey(*stbMeta, &nameLen); + + strncpy(stbName, name, nameLen); + stbName[nameLen] = 0; + + *sver = (*stbMeta)->sversion; + + CTG_UNLOCK(CTG_READ, &dbCache->tbCache.stbLock); + + ctgReleaseDBCache(pCtg, dbCache); + + ctgDebug("Got sver %d from cache, type:%d, dbFName:%s, tbName:%s", *sver, *tbType, dbFName, pTableName->tname); + + return TSDB_CODE_SUCCESS; +} + + +int32_t ctgGetTbTypeFromCache(SCatalog* pCtg, const char* dbFName, const char *tableName, int32_t *tbType) { + if (NULL == pCtg->dbCache) { + ctgWarn("empty db cache, dbFName:%s, tbName:%s", dbFName, tableName); + return TSDB_CODE_SUCCESS; + } + + SCtgDBCache *dbCache = NULL; + ctgAcquireDBCache(pCtg, dbFName, &dbCache); + if (NULL == dbCache) { + return TSDB_CODE_SUCCESS; + } + + CTG_LOCK(CTG_READ, &dbCache->tbCache.metaLock); + STableMeta *pTableMeta = (STableMeta *)taosHashAcquire(dbCache->tbCache.metaCache, tableName, strlen(tableName)); + + if (NULL == pTableMeta) { + CTG_UNLOCK(CTG_READ, &dbCache->tbCache.metaLock); + ctgWarn("tbl not in cache, dbFName:%s, tbName:%s", dbFName, tableName); + ctgReleaseDBCache(pCtg, dbCache); + + return TSDB_CODE_SUCCESS; + } + + *tbType = atomic_load_8(&pTableMeta->tableType); + + taosHashRelease(dbCache->tbCache.metaCache, pTableMeta); + + CTG_UNLOCK(CTG_READ, &dbCache->tbCache.metaLock); + + ctgReleaseDBCache(pCtg, dbCache); + + ctgDebug("Got tbtype from cache, dbFName:%s, tbName:%s, type:%d", dbFName, tableName, *tbType); + + return TSDB_CODE_SUCCESS; +} + +int32_t ctgChkAuthFromCache(SCatalog* pCtg, const char* user, const char* dbFName, AUTH_TYPE type, bool *inCache, bool *pass) { + if (NULL == pCtg->userCache) { + ctgDebug("empty user auth cache, user:%s", user); + goto _return; + } + + SCtgUserAuth *pUser = (SCtgUserAuth *)taosHashGet(pCtg->userCache, user, strlen(user)); + if (NULL == pUser) { + ctgDebug("user not in cache, user:%s", user); + goto _return; + } + + *inCache = true; + + ctgDebug("Got user from cache, user:%s", user); + CTG_CACHE_STAT_ADD(userHitNum, 1); + + if (pUser->superUser) { + *pass = true; + return TSDB_CODE_SUCCESS; + } + + CTG_LOCK(CTG_READ, &pUser->lock); + if (pUser->createdDbs && taosHashGet(pUser->createdDbs, dbFName, strlen(dbFName))) { + *pass = true; + CTG_UNLOCK(CTG_READ, &pUser->lock); + return TSDB_CODE_SUCCESS; + } + + if (pUser->readDbs && taosHashGet(pUser->readDbs, dbFName, strlen(dbFName)) && type == AUTH_TYPE_READ) { + *pass = true; + } + + if (pUser->writeDbs && taosHashGet(pUser->writeDbs, dbFName, strlen(dbFName)) && type == AUTH_TYPE_WRITE) { + *pass = true; + } + + CTG_UNLOCK(CTG_READ, &pUser->lock); + + return TSDB_CODE_SUCCESS; + +_return: + + *inCache = false; + CTG_CACHE_STAT_ADD(userMissNum, 1); + + return TSDB_CODE_SUCCESS; +} + + +void ctgWaitAction(SCtgMetaAction *action) { + while (true) { + tsem_wait(&gCtgMgmt.queue.rspSem); + + if (atomic_load_8((int8_t*)&gCtgMgmt.exit)) { + tsem_post(&gCtgMgmt.queue.rspSem); + break; + } + + if (gCtgMgmt.queue.seqDone >= action->seqId) { + break; + } + + tsem_post(&gCtgMgmt.queue.rspSem); + sched_yield(); + } +} + +void ctgPopAction(SCtgMetaAction **action) { + SCtgQNode *orig = gCtgMgmt.queue.head; + + SCtgQNode *node = gCtgMgmt.queue.head->next; + gCtgMgmt.queue.head = gCtgMgmt.queue.head->next; + + CTG_QUEUE_SUB(); + + taosMemoryFreeClear(orig); + + *action = &node->action; +} + + +int32_t ctgPushAction(SCatalog* pCtg, SCtgMetaAction *action) { + SCtgQNode *node = taosMemoryCalloc(1, sizeof(SCtgQNode)); + if (NULL == node) { + qError("calloc %d failed", (int32_t)sizeof(SCtgQNode)); + CTG_RET(TSDB_CODE_CTG_MEM_ERROR); + } + + action->seqId = atomic_add_fetch_64(&gCtgMgmt.queue.seqId, 1); + + node->action = *action; + + CTG_LOCK(CTG_WRITE, &gCtgMgmt.queue.qlock); + gCtgMgmt.queue.tail->next = node; + gCtgMgmt.queue.tail = node; + CTG_UNLOCK(CTG_WRITE, &gCtgMgmt.queue.qlock); + + CTG_QUEUE_ADD(); + CTG_RUNTIME_STAT_ADD(qNum, 1); + + tsem_post(&gCtgMgmt.queue.reqSem); + + ctgDebug("action [%s] added into queue", gCtgAction[action->act].name); + + if (action->syncReq) { + ctgWaitAction(action); + } + + return TSDB_CODE_SUCCESS; +} + + +int32_t ctgPutRmDBToQueue(SCatalog* pCtg, const char *dbFName, int64_t dbId) { + int32_t code = 0; + SCtgMetaAction action= {.act = CTG_ACT_REMOVE_DB}; + SCtgRemoveDBMsg *msg = taosMemoryMalloc(sizeof(SCtgRemoveDBMsg)); + if (NULL == msg) { + ctgError("malloc %d failed", (int32_t)sizeof(SCtgRemoveDBMsg)); + CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); + } + + char *p = strchr(dbFName, '.'); + if (p && CTG_IS_SYS_DBNAME(p + 1)) { + dbFName = p + 1; + } + + msg->pCtg = pCtg; + strncpy(msg->dbFName, dbFName, sizeof(msg->dbFName)); + msg->dbId = dbId; + + action.data = msg; + + CTG_ERR_JRET(ctgPushAction(pCtg, &action)); + + return TSDB_CODE_SUCCESS; + +_return: + + taosMemoryFreeClear(action.data); + CTG_RET(code); +} + + +int32_t ctgPutRmStbToQueue(SCatalog* pCtg, const char *dbFName, int64_t dbId, const char *stbName, uint64_t suid, bool syncReq) { + int32_t code = 0; + SCtgMetaAction action= {.act = CTG_ACT_REMOVE_STB, .syncReq = syncReq}; + SCtgRemoveStbMsg *msg = taosMemoryMalloc(sizeof(SCtgRemoveStbMsg)); + if (NULL == msg) { + ctgError("malloc %d failed", (int32_t)sizeof(SCtgRemoveStbMsg)); + CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); + } + + msg->pCtg = pCtg; + strncpy(msg->dbFName, dbFName, sizeof(msg->dbFName)); + strncpy(msg->stbName, stbName, sizeof(msg->stbName)); + msg->dbId = dbId; + msg->suid = suid; + + action.data = msg; + + CTG_ERR_JRET(ctgPushAction(pCtg, &action)); + + return TSDB_CODE_SUCCESS; + +_return: + + taosMemoryFreeClear(action.data); + CTG_RET(code); +} + + + +int32_t ctgPutRmTbToQueue(SCatalog* pCtg, const char *dbFName, int64_t dbId, const char *tbName, bool syncReq) { + int32_t code = 0; + SCtgMetaAction action= {.act = CTG_ACT_REMOVE_TBL, .syncReq = syncReq}; + SCtgRemoveTblMsg *msg = taosMemoryMalloc(sizeof(SCtgRemoveTblMsg)); + if (NULL == msg) { + ctgError("malloc %d failed", (int32_t)sizeof(SCtgRemoveTblMsg)); + CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); + } + + msg->pCtg = pCtg; + strncpy(msg->dbFName, dbFName, sizeof(msg->dbFName)); + strncpy(msg->tbName, tbName, sizeof(msg->tbName)); + msg->dbId = dbId; + + action.data = msg; + + CTG_ERR_JRET(ctgPushAction(pCtg, &action)); + + return TSDB_CODE_SUCCESS; + +_return: + + taosMemoryFreeClear(action.data); + CTG_RET(code); +} + +int32_t ctgPutUpdateVgToQueue(SCatalog* pCtg, const char *dbFName, int64_t dbId, SDBVgInfo* dbInfo, bool syncReq) { + int32_t code = 0; + SCtgMetaAction action= {.act = CTG_ACT_UPDATE_VG, .syncReq = syncReq}; + SCtgUpdateVgMsg *msg = taosMemoryMalloc(sizeof(SCtgUpdateVgMsg)); + if (NULL == msg) { + ctgError("malloc %d failed", (int32_t)sizeof(SCtgUpdateVgMsg)); + ctgFreeVgInfo(dbInfo); + CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); + } + + char *p = strchr(dbFName, '.'); + if (p && CTG_IS_SYS_DBNAME(p + 1)) { + dbFName = p + 1; + } + + strncpy(msg->dbFName, dbFName, sizeof(msg->dbFName)); + msg->pCtg = pCtg; + msg->dbId = dbId; + msg->dbInfo = dbInfo; + + action.data = msg; + + CTG_ERR_JRET(ctgPushAction(pCtg, &action)); + + return TSDB_CODE_SUCCESS; + +_return: + + ctgFreeVgInfo(dbInfo); + taosMemoryFreeClear(action.data); + CTG_RET(code); +} + +int32_t ctgPutUpdateTbToQueue(SCatalog* pCtg, STableMetaOutput *output, bool syncReq) { + int32_t code = 0; + SCtgMetaAction action= {.act = CTG_ACT_UPDATE_TBL, .syncReq = syncReq}; + SCtgUpdateTblMsg *msg = taosMemoryMalloc(sizeof(SCtgUpdateTblMsg)); + if (NULL == msg) { + ctgError("malloc %d failed", (int32_t)sizeof(SCtgUpdateTblMsg)); + CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); + } + + char *p = strchr(output->dbFName, '.'); + if (p && CTG_IS_SYS_DBNAME(p + 1)) { + memmove(output->dbFName, p + 1, strlen(p + 1)); + } + + msg->pCtg = pCtg; + msg->output = output; + + action.data = msg; + + CTG_ERR_JRET(ctgPushAction(pCtg, &action)); + + return TSDB_CODE_SUCCESS; + +_return: + + taosMemoryFreeClear(msg); + + CTG_RET(code); +} + +int32_t ctgPutUpdateUserToQueue(SCatalog* pCtg, SGetUserAuthRsp *pAuth, bool syncReq) { + int32_t code = 0; + SCtgMetaAction action= {.act = CTG_ACT_UPDATE_USER, .syncReq = syncReq}; + SCtgUpdateUserMsg *msg = taosMemoryMalloc(sizeof(SCtgUpdateUserMsg)); + if (NULL == msg) { + ctgError("malloc %d failed", (int32_t)sizeof(SCtgUpdateUserMsg)); + CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); + } + + msg->pCtg = pCtg; + msg->userAuth = *pAuth; + + action.data = msg; + + CTG_ERR_JRET(ctgPushAction(pCtg, &action)); + + return TSDB_CODE_SUCCESS; + +_return: + + tFreeSGetUserAuthRsp(pAuth); + taosMemoryFreeClear(msg); + + CTG_RET(code); +} + +int32_t ctgMetaRentInit(SCtgRentMgmt *mgmt, uint32_t rentSec, int8_t type) { + mgmt->slotRIdx = 0; + mgmt->slotNum = rentSec / CTG_RENT_SLOT_SECOND; + mgmt->type = type; + + size_t msgSize = sizeof(SCtgRentSlot) * mgmt->slotNum; + + mgmt->slots = taosMemoryCalloc(1, msgSize); + if (NULL == mgmt->slots) { + qError("calloc %d failed", (int32_t)msgSize); + CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); + } + + qDebug("meta rent initialized, type:%d, slotNum:%d", type, mgmt->slotNum); + + return TSDB_CODE_SUCCESS; +} + + +int32_t ctgMetaRentAdd(SCtgRentMgmt *mgmt, void *meta, int64_t id, int32_t size) { + int16_t widx = abs((int)(id % mgmt->slotNum)); + + SCtgRentSlot *slot = &mgmt->slots[widx]; + int32_t code = 0; + + CTG_LOCK(CTG_WRITE, &slot->lock); + if (NULL == slot->meta) { + slot->meta = taosArrayInit(CTG_DEFAULT_RENT_SLOT_SIZE, size); + if (NULL == slot->meta) { + qError("taosArrayInit %d failed, id:%"PRIx64", slot idx:%d, type:%d", CTG_DEFAULT_RENT_SLOT_SIZE, id, widx, mgmt->type); + CTG_ERR_JRET(TSDB_CODE_CTG_MEM_ERROR); + } + } + + if (NULL == taosArrayPush(slot->meta, meta)) { + qError("taosArrayPush meta to rent failed, id:%"PRIx64", slot idx:%d, type:%d", id, widx, mgmt->type); + CTG_ERR_JRET(TSDB_CODE_CTG_MEM_ERROR); + } + + slot->needSort = true; + + qDebug("add meta to rent, id:%"PRIx64", slot idx:%d, type:%d", id, widx, mgmt->type); + +_return: + + CTG_UNLOCK(CTG_WRITE, &slot->lock); + CTG_RET(code); +} + +int32_t ctgMetaRentUpdate(SCtgRentMgmt *mgmt, void *meta, int64_t id, int32_t size, __compar_fn_t sortCompare, __compar_fn_t searchCompare) { + int16_t widx = abs((int)(id % mgmt->slotNum)); + + SCtgRentSlot *slot = &mgmt->slots[widx]; + int32_t code = 0; + + CTG_LOCK(CTG_WRITE, &slot->lock); + if (NULL == slot->meta) { + qError("empty meta slot, id:%"PRIx64", slot idx:%d, type:%d", id, widx, mgmt->type); + CTG_ERR_JRET(TSDB_CODE_CTG_INTERNAL_ERROR); + } + + if (slot->needSort) { + qDebug("meta slot before sorte, slot idx:%d, type:%d, size:%d", widx, mgmt->type, (int32_t)taosArrayGetSize(slot->meta)); + taosArraySort(slot->meta, sortCompare); + slot->needSort = false; + qDebug("meta slot sorted, slot idx:%d, type:%d, size:%d", widx, mgmt->type, (int32_t)taosArrayGetSize(slot->meta)); + } + + void *orig = taosArraySearch(slot->meta, &id, searchCompare, TD_EQ); + if (NULL == orig) { + qError("meta not found in slot, id:%"PRIx64", slot idx:%d, type:%d, size:%d", id, widx, mgmt->type, (int32_t)taosArrayGetSize(slot->meta)); + CTG_ERR_JRET(TSDB_CODE_CTG_INTERNAL_ERROR); + } + + memcpy(orig, meta, size); + + qDebug("meta in rent updated, id:%"PRIx64", slot idx:%d, type:%d", id, widx, mgmt->type); + +_return: + + CTG_UNLOCK(CTG_WRITE, &slot->lock); + + if (code) { + qWarn("meta in rent update failed, will try to add it, code:%x, id:%"PRIx64", slot idx:%d, type:%d", code, id, widx, mgmt->type); + CTG_RET(ctgMetaRentAdd(mgmt, meta, id, size)); + } + + CTG_RET(code); +} + +int32_t ctgMetaRentRemove(SCtgRentMgmt *mgmt, int64_t id, __compar_fn_t sortCompare, __compar_fn_t searchCompare) { + int16_t widx = abs((int)(id % mgmt->slotNum)); + + SCtgRentSlot *slot = &mgmt->slots[widx]; + int32_t code = 0; + + CTG_LOCK(CTG_WRITE, &slot->lock); + if (NULL == slot->meta) { + qError("empty meta slot, id:%"PRIx64", slot idx:%d, type:%d", id, widx, mgmt->type); + CTG_ERR_JRET(TSDB_CODE_CTG_INTERNAL_ERROR); + } + + if (slot->needSort) { + taosArraySort(slot->meta, sortCompare); + slot->needSort = false; + qDebug("meta slot sorted, slot idx:%d, type:%d", widx, mgmt->type); + } + + int32_t idx = taosArraySearchIdx(slot->meta, &id, searchCompare, TD_EQ); + if (idx < 0) { + qError("meta not found in slot, id:%"PRIx64", slot idx:%d, type:%d", id, widx, mgmt->type); + CTG_ERR_JRET(TSDB_CODE_CTG_INTERNAL_ERROR); + } + + taosArrayRemove(slot->meta, idx); + + qDebug("meta in rent removed, id:%"PRIx64", slot idx:%d, type:%d", id, widx, mgmt->type); + +_return: + + CTG_UNLOCK(CTG_WRITE, &slot->lock); + + CTG_RET(code); +} + + +int32_t ctgMetaRentGetImpl(SCtgRentMgmt *mgmt, void **res, uint32_t *num, int32_t size) { + int16_t ridx = atomic_add_fetch_16(&mgmt->slotRIdx, 1); + if (ridx >= mgmt->slotNum) { + ridx %= mgmt->slotNum; + atomic_store_16(&mgmt->slotRIdx, ridx); + } + + SCtgRentSlot *slot = &mgmt->slots[ridx]; + int32_t code = 0; + + CTG_LOCK(CTG_READ, &slot->lock); + if (NULL == slot->meta) { + qDebug("empty meta in slot:%d, type:%d", ridx, mgmt->type); + *num = 0; + goto _return; + } + + size_t metaNum = taosArrayGetSize(slot->meta); + if (metaNum <= 0) { + qDebug("no meta in slot:%d, type:%d", ridx, mgmt->type); + *num = 0; + goto _return; + } + + size_t msize = metaNum * size; + *res = taosMemoryMalloc(msize); + if (NULL == *res) { + qError("malloc %d failed", (int32_t)msize); + CTG_ERR_JRET(TSDB_CODE_CTG_MEM_ERROR); + } + + void *meta = taosArrayGet(slot->meta, 0); + + memcpy(*res, meta, msize); + + *num = (uint32_t)metaNum; + + qDebug("Got %d meta from rent, type:%d", (int32_t)metaNum, mgmt->type); + +_return: + + CTG_UNLOCK(CTG_READ, &slot->lock); + + CTG_RET(code); +} + +int32_t ctgMetaRentGet(SCtgRentMgmt *mgmt, void **res, uint32_t *num, int32_t size) { + while (true) { + int64_t msec = taosGetTimestampMs(); + int64_t lsec = atomic_load_64(&mgmt->lastReadMsec); + if ((msec - lsec) < CTG_RENT_SLOT_SECOND * 1000) { + *res = NULL; + *num = 0; + qDebug("too short time period to get expired meta, type:%d", mgmt->type); + return TSDB_CODE_SUCCESS; + } + + if (lsec != atomic_val_compare_exchange_64(&mgmt->lastReadMsec, lsec, msec)) { + continue; + } + + break; + } + + CTG_ERR_RET(ctgMetaRentGetImpl(mgmt, res, num, size)); + + return TSDB_CODE_SUCCESS; +} + +int32_t ctgAddNewDBCache(SCatalog *pCtg, const char *dbFName, uint64_t dbId) { + int32_t code = 0; + + SCtgDBCache newDBCache = {0}; + newDBCache.dbId = dbId; + + newDBCache.tbCache.metaCache = taosHashInit(gCtgMgmt.cfg.maxTblCacheNum, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY), true, HASH_ENTRY_LOCK); + if (NULL == newDBCache.tbCache.metaCache) { + ctgError("taosHashInit %d metaCache failed", gCtgMgmt.cfg.maxTblCacheNum); + CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); + } + + newDBCache.tbCache.stbCache = taosHashInit(gCtgMgmt.cfg.maxTblCacheNum, taosGetDefaultHashFunction(TSDB_DATA_TYPE_UBIGINT), true, HASH_ENTRY_LOCK); + if (NULL == newDBCache.tbCache.stbCache) { + ctgError("taosHashInit %d stbCache failed", gCtgMgmt.cfg.maxTblCacheNum); + CTG_ERR_JRET(TSDB_CODE_CTG_MEM_ERROR); + } + + code = taosHashPut(pCtg->dbCache, dbFName, strlen(dbFName), &newDBCache, sizeof(SCtgDBCache)); + if (code) { + if (HASH_NODE_EXIST(code)) { + ctgDebug("db already in cache, dbFName:%s", dbFName); + goto _return; + } + + ctgError("taosHashPut db to cache failed, dbFName:%s", dbFName); + CTG_ERR_JRET(TSDB_CODE_CTG_MEM_ERROR); + } + + CTG_CACHE_STAT_ADD(dbNum, 1); + + SDbVgVersion vgVersion = {.dbId = newDBCache.dbId, .vgVersion = -1}; + strncpy(vgVersion.dbFName, dbFName, sizeof(vgVersion.dbFName)); + + ctgDebug("db added to cache, dbFName:%s, dbId:%"PRIx64, dbFName, dbId); + + CTG_ERR_RET(ctgMetaRentAdd(&pCtg->dbRent, &vgVersion, dbId, sizeof(SDbVgVersion))); + + ctgDebug("db added to rent, dbFName:%s, vgVersion:%d, dbId:%"PRIx64, dbFName, vgVersion.vgVersion, dbId); + + return TSDB_CODE_SUCCESS; + +_return: + + ctgFreeDbCache(&newDBCache); + + CTG_RET(code); +} + + +void ctgRemoveStbRent(SCatalog* pCtg, SCtgTbMetaCache *cache) { + CTG_LOCK(CTG_WRITE, &cache->stbLock); + if (cache->stbCache) { + void *pIter = taosHashIterate(cache->stbCache, NULL); + while (pIter) { + uint64_t *suid = NULL; + suid = taosHashGetKey(pIter, NULL); + + if (TSDB_CODE_SUCCESS == ctgMetaRentRemove(&pCtg->stbRent, *suid, ctgStbVersionSortCompare, ctgStbVersionSearchCompare)) { + ctgDebug("stb removed from rent, suid:%"PRIx64, *suid); + } + + pIter = taosHashIterate(cache->stbCache, pIter); + } + } + CTG_UNLOCK(CTG_WRITE, &cache->stbLock); +} + + +int32_t ctgRemoveDBFromCache(SCatalog* pCtg, SCtgDBCache *dbCache, const char* dbFName) { + uint64_t dbId = dbCache->dbId; + + ctgInfo("start to remove db from cache, dbFName:%s, dbId:%"PRIx64, dbFName, dbCache->dbId); + + atomic_store_8(&dbCache->deleted, 1); + + ctgRemoveStbRent(pCtg, &dbCache->tbCache); + + ctgFreeDbCache(dbCache); + + CTG_ERR_RET(ctgMetaRentRemove(&pCtg->dbRent, dbCache->dbId, ctgDbVgVersionSortCompare, ctgDbVgVersionSearchCompare)); + + ctgDebug("db removed from rent, dbFName:%s, dbId:%"PRIx64, dbFName, dbCache->dbId); + + if (taosHashRemove(pCtg->dbCache, dbFName, strlen(dbFName))) { + ctgInfo("taosHashRemove from dbCache failed, may be removed, dbFName:%s", dbFName); + CTG_ERR_RET(TSDB_CODE_CTG_DB_DROPPED); + } + + CTG_CACHE_STAT_SUB(dbNum, 1); + + ctgInfo("db removed from cache, dbFName:%s, dbId:%"PRIx64, dbFName, dbId); + + return TSDB_CODE_SUCCESS; +} + + +int32_t ctgGetAddDBCache(SCatalog* pCtg, const char *dbFName, uint64_t dbId, SCtgDBCache **pCache) { + int32_t code = 0; + SCtgDBCache *dbCache = NULL; + ctgGetDBCache(pCtg, dbFName, &dbCache); + + if (dbCache) { + // TODO OPEN IT +#if 0 + if (dbCache->dbId == dbId) { + *pCache = dbCache; + return TSDB_CODE_SUCCESS; + } +#else + if (0 == dbId) { + *pCache = dbCache; + return TSDB_CODE_SUCCESS; + } + + if (dbId && (dbCache->dbId == 0)) { + dbCache->dbId = dbId; + *pCache = dbCache; + return TSDB_CODE_SUCCESS; + } + + if (dbCache->dbId == dbId) { + *pCache = dbCache; + return TSDB_CODE_SUCCESS; + } +#endif + CTG_ERR_RET(ctgRemoveDBFromCache(pCtg, dbCache, dbFName)); + } + + CTG_ERR_RET(ctgAddNewDBCache(pCtg, dbFName, dbId)); + + ctgGetDBCache(pCtg, dbFName, &dbCache); + + *pCache = dbCache; + + return TSDB_CODE_SUCCESS; +} + + +int32_t ctgWriteDBVgInfoToCache(SCatalog* pCtg, const char* dbFName, uint64_t dbId, SDBVgInfo** pDbInfo) { + int32_t code = 0; + SDBVgInfo* dbInfo = *pDbInfo; + + if (NULL == dbInfo->vgHash) { + return TSDB_CODE_SUCCESS; + } + + if (dbInfo->vgVersion < 0 || taosHashGetSize(dbInfo->vgHash) <= 0) { + ctgError("invalid db vgInfo, dbFName:%s, vgHash:%p, vgVersion:%d, vgHashSize:%d", + dbFName, dbInfo->vgHash, dbInfo->vgVersion, taosHashGetSize(dbInfo->vgHash)); + CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); + } + + bool newAdded = false; + SDbVgVersion vgVersion = {.dbId = dbId, .vgVersion = dbInfo->vgVersion, .numOfTable = dbInfo->numOfTable}; + + SCtgDBCache *dbCache = NULL; + CTG_ERR_RET(ctgGetAddDBCache(pCtg, dbFName, dbId, &dbCache)); + if (NULL == dbCache) { + ctgInfo("conflict db update, ignore this update, dbFName:%s, dbId:%"PRIx64, dbFName, dbId); + CTG_ERR_RET(TSDB_CODE_CTG_INTERNAL_ERROR); + } + + SDBVgInfo *vgInfo = NULL; + CTG_ERR_RET(ctgWAcquireVgInfo(pCtg, dbCache)); + + if (dbCache->vgInfo) { + if (dbInfo->vgVersion < dbCache->vgInfo->vgVersion) { + ctgDebug("db vgVersion is old, dbFName:%s, vgVersion:%d, currentVersion:%d", dbFName, dbInfo->vgVersion, dbCache->vgInfo->vgVersion); + ctgWReleaseVgInfo(dbCache); + + return TSDB_CODE_SUCCESS; + } + + if (dbInfo->vgVersion == dbCache->vgInfo->vgVersion && dbInfo->numOfTable == dbCache->vgInfo->numOfTable) { + ctgDebug("no new db vgVersion or numOfTable, dbFName:%s, vgVersion:%d, numOfTable:%d", dbFName, dbInfo->vgVersion, dbInfo->numOfTable); + ctgWReleaseVgInfo(dbCache); + + return TSDB_CODE_SUCCESS; + } + + ctgFreeVgInfo(dbCache->vgInfo); + } + + dbCache->vgInfo = dbInfo; + + *pDbInfo = NULL; + + ctgDebug("db vgInfo updated, dbFName:%s, vgVersion:%d, dbId:%"PRIx64, dbFName, vgVersion.vgVersion, vgVersion.dbId); + + ctgWReleaseVgInfo(dbCache); + + dbCache = NULL; + + strncpy(vgVersion.dbFName, dbFName, sizeof(vgVersion.dbFName)); + CTG_ERR_RET(ctgMetaRentUpdate(&pCtg->dbRent, &vgVersion, vgVersion.dbId, sizeof(SDbVgVersion), ctgDbVgVersionSortCompare, ctgDbVgVersionSearchCompare)); + + CTG_RET(code); +} + + +int32_t ctgWriteTbMetaToCache(SCatalog *pCtg, SCtgDBCache *dbCache, char *dbFName, uint64_t dbId, char *tbName, STableMeta *meta, int32_t metaSize) { + SCtgTbMetaCache *tbCache = &dbCache->tbCache; + + CTG_LOCK(CTG_READ, &tbCache->metaLock); + if (dbCache->deleted || NULL == tbCache->metaCache || NULL == tbCache->stbCache) { + CTG_UNLOCK(CTG_READ, &tbCache->metaLock); + ctgError("db is dropping, dbId:%"PRIx64, dbCache->dbId); + CTG_ERR_RET(TSDB_CODE_CTG_DB_DROPPED); + } + + int8_t origType = 0; + uint64_t origSuid = 0; + bool isStb = meta->tableType == TSDB_SUPER_TABLE; + STableMeta *orig = taosHashGet(tbCache->metaCache, tbName, strlen(tbName)); + if (orig) { + origType = orig->tableType; + + if (origType == meta->tableType && orig->uid == meta->uid && orig->sversion >= meta->sversion && orig->tversion >= meta->tversion) { + CTG_UNLOCK(CTG_READ, &tbCache->metaLock); + return TSDB_CODE_SUCCESS; + } + + if (origType == TSDB_SUPER_TABLE) { + CTG_LOCK(CTG_WRITE, &tbCache->stbLock); + if (taosHashRemove(tbCache->stbCache, &orig->suid, sizeof(orig->suid))) { + ctgError("stb not exist in stbCache, dbFName:%s, stb:%s, suid:%"PRIx64, dbFName, tbName, orig->suid); + } else { + CTG_CACHE_STAT_SUB(stblNum, 1); + } + CTG_UNLOCK(CTG_WRITE, &tbCache->stbLock); + + ctgDebug("stb removed from stbCache, dbFName:%s, stb:%s, suid:%"PRIx64, dbFName, tbName, orig->suid); + + ctgMetaRentRemove(&pCtg->stbRent, orig->suid, ctgStbVersionSortCompare, ctgStbVersionSearchCompare); + + origSuid = orig->suid; + } + } + + if (isStb) { + CTG_LOCK(CTG_WRITE, &tbCache->stbLock); + } + + if (taosHashPut(tbCache->metaCache, tbName, strlen(tbName), meta, metaSize) != 0) { + if (isStb) { + CTG_UNLOCK(CTG_WRITE, &tbCache->stbLock); + } + + CTG_UNLOCK(CTG_READ, &tbCache->metaLock); + ctgError("taosHashPut tbmeta to cache failed, dbFName:%s, tbName:%s, tbType:%d", dbFName, tbName, meta->tableType); + CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); + } + + if (NULL == orig) { + CTG_CACHE_STAT_ADD(tblNum, 1); + } + + ctgDebug("tbmeta updated to cache, dbFName:%s, tbName:%s, tbType:%d", dbFName, tbName, meta->tableType); + ctgdShowTableMeta(pCtg, tbName, meta); + + if (!isStb) { + CTG_UNLOCK(CTG_READ, &tbCache->metaLock); + return TSDB_CODE_SUCCESS; + } + + STableMeta *tbMeta = taosHashGet(tbCache->metaCache, tbName, strlen(tbName)); + if (taosHashPut(tbCache->stbCache, &meta->suid, sizeof(meta->suid), &tbMeta, POINTER_BYTES) != 0) { + CTG_UNLOCK(CTG_WRITE, &tbCache->stbLock); + CTG_UNLOCK(CTG_READ, &tbCache->metaLock); + ctgError("taosHashPut stable to stable cache failed, suid:%"PRIx64, meta->suid); + CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); + } + + CTG_CACHE_STAT_ADD(stblNum, 1); + + CTG_UNLOCK(CTG_WRITE, &tbCache->stbLock); + + CTG_UNLOCK(CTG_READ, &tbCache->metaLock); + + ctgDebug("stb updated to stbCache, dbFName:%s, tbName:%s, tbType:%d", dbFName, tbName, meta->tableType); + + SSTableMetaVersion metaRent = {.dbId = dbId, .suid = meta->suid, .sversion = meta->sversion, .tversion = meta->tversion}; + strcpy(metaRent.dbFName, dbFName); + strcpy(metaRent.stbName, tbName); + CTG_ERR_RET(ctgMetaRentAdd(&pCtg->stbRent, &metaRent, metaRent.suid, sizeof(SSTableMetaVersion))); + + return TSDB_CODE_SUCCESS; +} + +int32_t ctgUpdateTbMetaToCache(SCatalog* pCtg, STableMetaOutput* pOut, bool syncReq) { + STableMetaOutput* pOutput = NULL; + int32_t code = 0; + + CTG_ERR_RET(ctgCloneMetaOutput(pOut, &pOutput)); + CTG_ERR_JRET(ctgPutUpdateTbToQueue(pCtg, pOutput, syncReq)); + + return TSDB_CODE_SUCCESS; + +_return: + + ctgFreeSTableMetaOutput(pOutput); + CTG_RET(code); +} + + +int32_t ctgActUpdateVg(SCtgMetaAction *action) { + int32_t code = 0; + SCtgUpdateVgMsg *msg = action->data; + + CTG_ERR_JRET(ctgWriteDBVgInfoToCache(msg->pCtg, msg->dbFName, msg->dbId, &msg->dbInfo)); + +_return: + + ctgFreeVgInfo(msg->dbInfo); + taosMemoryFreeClear(msg); + + CTG_RET(code); +} + +int32_t ctgActRemoveDB(SCtgMetaAction *action) { + int32_t code = 0; + SCtgRemoveDBMsg *msg = action->data; + SCatalog* pCtg = msg->pCtg; + + SCtgDBCache *dbCache = NULL; + ctgGetDBCache(msg->pCtg, msg->dbFName, &dbCache); + if (NULL == dbCache) { + goto _return; + } + + if (dbCache->dbId != msg->dbId) { + ctgInfo("dbId already updated, dbFName:%s, dbId:%"PRIx64 ", targetId:%"PRIx64, msg->dbFName, dbCache->dbId, msg->dbId); + goto _return; + } + + CTG_ERR_JRET(ctgRemoveDBFromCache(pCtg, dbCache, msg->dbFName)); + +_return: + + taosMemoryFreeClear(msg); + + CTG_RET(code); +} + + +int32_t ctgActUpdateTb(SCtgMetaAction *action) { + int32_t code = 0; + SCtgUpdateTblMsg *msg = action->data; + SCatalog* pCtg = msg->pCtg; + STableMetaOutput* output = msg->output; + SCtgDBCache *dbCache = NULL; + + if ((!CTG_IS_META_CTABLE(output->metaType)) && NULL == output->tbMeta) { + ctgError("no valid tbmeta got from meta rsp, dbFName:%s, tbName:%s", output->dbFName, output->tbName); + CTG_ERR_JRET(TSDB_CODE_CTG_INTERNAL_ERROR); + } + + if (CTG_IS_META_BOTH(output->metaType) && TSDB_SUPER_TABLE != output->tbMeta->tableType) { + ctgError("table type error, expected:%d, actual:%d", TSDB_SUPER_TABLE, output->tbMeta->tableType); + CTG_ERR_JRET(TSDB_CODE_CTG_INTERNAL_ERROR); + } + + CTG_ERR_JRET(ctgGetAddDBCache(pCtg, output->dbFName, output->dbId, &dbCache)); + if (NULL == dbCache) { + ctgInfo("conflict db update, ignore this update, dbFName:%s, dbId:%"PRIx64, output->dbFName, output->dbId); + CTG_ERR_JRET(TSDB_CODE_CTG_INTERNAL_ERROR); + } + + if (CTG_IS_META_TABLE(output->metaType) || CTG_IS_META_BOTH(output->metaType)) { + int32_t metaSize = CTG_META_SIZE(output->tbMeta); + + CTG_ERR_JRET(ctgWriteTbMetaToCache(pCtg, dbCache, output->dbFName, output->dbId, output->tbName, output->tbMeta, metaSize)); + } + + if (CTG_IS_META_CTABLE(output->metaType) || CTG_IS_META_BOTH(output->metaType)) { + CTG_ERR_JRET(ctgWriteTbMetaToCache(pCtg, dbCache, output->dbFName, output->dbId, output->ctbName, (STableMeta *)&output->ctbMeta, sizeof(output->ctbMeta))); + } + +_return: + + if (output) { + taosMemoryFreeClear(output->tbMeta); + taosMemoryFreeClear(output); + } + + taosMemoryFreeClear(msg); + + CTG_RET(code); +} + + +int32_t ctgActRemoveStb(SCtgMetaAction *action) { + int32_t code = 0; + SCtgRemoveStbMsg *msg = action->data; + SCatalog* pCtg = msg->pCtg; + + SCtgDBCache *dbCache = NULL; + ctgGetDBCache(pCtg, msg->dbFName, &dbCache); + if (NULL == dbCache) { + return TSDB_CODE_SUCCESS; + } + + if (msg->dbId && (dbCache->dbId != msg->dbId)) { + ctgDebug("dbId already modified, dbFName:%s, current:%"PRIx64", dbId:%"PRIx64", stb:%s, suid:%"PRIx64, msg->dbFName, dbCache->dbId, msg->dbId, msg->stbName, msg->suid); + return TSDB_CODE_SUCCESS; + } + + CTG_LOCK(CTG_WRITE, &dbCache->tbCache.stbLock); + if (taosHashRemove(dbCache->tbCache.stbCache, &msg->suid, sizeof(msg->suid))) { + ctgDebug("stb not exist in stbCache, may be removed, dbFName:%s, stb:%s, suid:%"PRIx64, msg->dbFName, msg->stbName, msg->suid); + } else { + CTG_CACHE_STAT_SUB(stblNum, 1); + } + + CTG_LOCK(CTG_READ, &dbCache->tbCache.metaLock); + if (taosHashRemove(dbCache->tbCache.metaCache, msg->stbName, strlen(msg->stbName))) { + ctgError("stb not exist in cache, dbFName:%s, stb:%s, suid:%"PRIx64, msg->dbFName, msg->stbName, msg->suid); + } else { + CTG_CACHE_STAT_SUB(tblNum, 1); + } + CTG_UNLOCK(CTG_READ, &dbCache->tbCache.metaLock); + + CTG_UNLOCK(CTG_WRITE, &dbCache->tbCache.stbLock); + + ctgInfo("stb removed from cache, dbFName:%s, stbName:%s, suid:%"PRIx64, msg->dbFName, msg->stbName, msg->suid); + + CTG_ERR_JRET(ctgMetaRentRemove(&msg->pCtg->stbRent, msg->suid, ctgStbVersionSortCompare, ctgStbVersionSearchCompare)); + + ctgDebug("stb removed from rent, dbFName:%s, stbName:%s, suid:%"PRIx64, msg->dbFName, msg->stbName, msg->suid); + +_return: + + taosMemoryFreeClear(msg); + + CTG_RET(code); +} + +int32_t ctgActRemoveTb(SCtgMetaAction *action) { + int32_t code = 0; + SCtgRemoveTblMsg *msg = action->data; + SCatalog* pCtg = msg->pCtg; + + SCtgDBCache *dbCache = NULL; + ctgGetDBCache(pCtg, msg->dbFName, &dbCache); + if (NULL == dbCache) { + return TSDB_CODE_SUCCESS; + } + + if (dbCache->dbId != msg->dbId) { + ctgDebug("dbId already modified, dbFName:%s, current:%"PRIx64", dbId:%"PRIx64", tbName:%s", msg->dbFName, dbCache->dbId, msg->dbId, msg->tbName); + return TSDB_CODE_SUCCESS; + } + + CTG_LOCK(CTG_READ, &dbCache->tbCache.metaLock); + if (taosHashRemove(dbCache->tbCache.metaCache, msg->tbName, strlen(msg->tbName))) { + CTG_UNLOCK(CTG_READ, &dbCache->tbCache.metaLock); + ctgError("stb not exist in cache, dbFName:%s, tbName:%s", msg->dbFName, msg->tbName); + CTG_ERR_RET(TSDB_CODE_CTG_INTERNAL_ERROR); + } else { + CTG_CACHE_STAT_SUB(tblNum, 1); + } + CTG_UNLOCK(CTG_READ, &dbCache->tbCache.metaLock); + + ctgInfo("table removed from cache, dbFName:%s, tbName:%s", msg->dbFName, msg->tbName); + +_return: + + taosMemoryFreeClear(msg); + + CTG_RET(code); +} + +int32_t ctgActUpdateUser(SCtgMetaAction *action) { + int32_t code = 0; + SCtgUpdateUserMsg *msg = action->data; + SCatalog* pCtg = msg->pCtg; + + if (NULL == pCtg->userCache) { + pCtg->userCache = taosHashInit(gCtgMgmt.cfg.maxUserCacheNum, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY), false, HASH_ENTRY_LOCK); + if (NULL == pCtg->userCache) { + ctgError("taosHashInit %d user cache failed", gCtgMgmt.cfg.maxUserCacheNum); + CTG_ERR_JRET(TSDB_CODE_OUT_OF_MEMORY); + } + } + + SCtgUserAuth *pUser = (SCtgUserAuth *)taosHashGet(pCtg->userCache, msg->userAuth.user, strlen(msg->userAuth.user)); + if (NULL == pUser) { + SCtgUserAuth userAuth = {0}; + + userAuth.version = msg->userAuth.version; + userAuth.superUser = msg->userAuth.superAuth; + userAuth.createdDbs = msg->userAuth.createdDbs; + userAuth.readDbs = msg->userAuth.readDbs; + userAuth.writeDbs = msg->userAuth.writeDbs; + + if (taosHashPut(pCtg->userCache, msg->userAuth.user, strlen(msg->userAuth.user), &userAuth, sizeof(userAuth))) { + ctgError("taosHashPut user %s to cache failed", msg->userAuth.user); + CTG_ERR_JRET(TSDB_CODE_OUT_OF_MEMORY); + } + + taosMemoryFreeClear(msg); + + return TSDB_CODE_SUCCESS; + } + + pUser->version = msg->userAuth.version; + + CTG_LOCK(CTG_WRITE, &pUser->lock); + + taosHashCleanup(pUser->createdDbs); + pUser->createdDbs = msg->userAuth.createdDbs; + msg->userAuth.createdDbs = NULL; + + taosHashCleanup(pUser->readDbs); + pUser->readDbs = msg->userAuth.readDbs; + msg->userAuth.readDbs = NULL; + + taosHashCleanup(pUser->writeDbs); + pUser->writeDbs = msg->userAuth.writeDbs; + msg->userAuth.writeDbs = NULL; + + CTG_UNLOCK(CTG_WRITE, &pUser->lock); + +_return: + + + taosHashCleanup(msg->userAuth.createdDbs); + taosHashCleanup(msg->userAuth.readDbs); + taosHashCleanup(msg->userAuth.writeDbs); + + taosMemoryFreeClear(msg); + + CTG_RET(code); +} + + +void* ctgUpdateThreadFunc(void* param) { + setThreadName("catalog"); + + qInfo("catalog update thread started"); + + CTG_LOCK(CTG_READ, &gCtgMgmt.lock); + + while (true) { + if (tsem_wait(&gCtgMgmt.queue.reqSem)) { + qError("ctg tsem_wait failed, error:%s", tstrerror(TAOS_SYSTEM_ERROR(errno))); + } + + if (atomic_load_8((int8_t*)&gCtgMgmt.exit)) { + tsem_post(&gCtgMgmt.queue.rspSem); + break; + } + + SCtgMetaAction *action = NULL; + ctgPopAction(&action); + SCatalog *pCtg = ((SCtgUpdateMsgHeader *)action->data)->pCtg; + + ctgDebug("process [%s] action", gCtgAction[action->act].name); + + (*gCtgAction[action->act].func)(action); + + gCtgMgmt.queue.seqDone = action->seqId; + + if (action->syncReq) { + tsem_post(&gCtgMgmt.queue.rspSem); + } + + CTG_RUNTIME_STAT_ADD(qDoneNum, 1); + + ctgdShowClusterCache(pCtg); + } + + CTG_UNLOCK(CTG_READ, &gCtgMgmt.lock); + + qInfo("catalog update thread stopped"); + + return NULL; +} + + +int32_t ctgStartUpdateThread() { + TdThreadAttr thAttr; + taosThreadAttrInit(&thAttr); + taosThreadAttrSetDetachState(&thAttr, PTHREAD_CREATE_JOINABLE); + + if (taosThreadCreate(&gCtgMgmt.updateThread, &thAttr, ctgUpdateThreadFunc, NULL) != 0) { + terrno = TAOS_SYSTEM_ERROR(errno); + CTG_ERR_RET(terrno); + } + + taosThreadAttrDestroy(&thAttr); + return TSDB_CODE_SUCCESS; +} + + + diff --git a/source/libs/catalog/src/catalogDbg.c b/source/libs/catalog/src/ctgDbg.c similarity index 100% rename from source/libs/catalog/src/catalogDbg.c rename to source/libs/catalog/src/ctgDbg.c diff --git a/source/libs/catalog/src/ctgRemote.c b/source/libs/catalog/src/ctgRemote.c new file mode 100644 index 0000000000000000000000000000000000000000..9e86b863f425fafa4403e0ea03841ae696753a03 --- /dev/null +++ b/source/libs/catalog/src/ctgRemote.c @@ -0,0 +1,577 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "trpc.h" +#include "query.h" +#include "tname.h" +#include "catalogInt.h" +#include "systable.h" +#include "ctgRemote.h" +#include "tref.h" + +int32_t ctgProcessRspMsg(void* out, int32_t reqType, char* msg, int32_t msgSize, int32_t rspCode, char* target) { + int32_t code = 0; + + switch (reqType) { + case TDMT_MND_QNODE_LIST: { + if (TSDB_CODE_SUCCESS != rspCode) { + qError("error rsp for qnode list, error:%s", tstrerror(rspCode)); + CTG_ERR_RET(rspCode); + } + + code = queryProcessMsgRsp[TMSG_INDEX(reqType)](out, msg, msgSize); + if (code) { + qError("Process qnode list rsp failed, error:%s", tstrerror(rspCode)); + CTG_ERR_RET(code); + } + + qDebug("Got qnode list from mnode, listNum:%d", (int32_t)taosArrayGetSize(out)); + break; + } + case TDMT_MND_USE_DB: { + if (TSDB_CODE_SUCCESS != rspCode) { + qError("error rsp for use db, error:%s, dbFName:%s", tstrerror(rspCode), target); + CTG_ERR_RET(rspCode); + } + + code = queryProcessMsgRsp[TMSG_INDEX(reqType)](out, msg, msgSize); + if (code) { + qError("Process use db rsp failed, error:%s, dbFName:%s", tstrerror(code), target); + CTG_ERR_RET(code); + } + + qDebug("Got db vgInfo from mnode, dbFName:%s", target); + break; + } + case TDMT_MND_GET_DB_CFG: { + if (TSDB_CODE_SUCCESS != rspCode) { + qError("error rsp for get db cfg, error:%s, db:%s", tstrerror(rspCode), target); + CTG_ERR_RET(rspCode); + } + + code = queryProcessMsgRsp[TMSG_INDEX(reqType)](out, msg, msgSize); + if (code) { + qError("Process get db cfg rsp failed, error:%s, db:%s", tstrerror(code), target); + CTG_ERR_RET(code); + } + + qDebug("Got db cfg from mnode, dbFName:%s", target); + break; + } + case TDMT_MND_GET_INDEX: { + if (TSDB_CODE_SUCCESS != rspCode) { + qError("error rsp for get index, error:%s, indexName:%s", tstrerror(rspCode), target); + CTG_ERR_RET(rspCode); + } + + code = queryProcessMsgRsp[TMSG_INDEX(reqType)](out, msg, msgSize); + if (code) { + qError("Process get index rsp failed, error:%s, indexName:%s", tstrerror(code), target); + CTG_ERR_RET(code); + } + + qDebug("Got index from mnode, indexName:%s", target); + break; + } + case TDMT_MND_RETRIEVE_FUNC: { + if (TSDB_CODE_SUCCESS != rspCode) { + qError("error rsp for get udf, error:%s, funcName:%s", tstrerror(rspCode), target); + CTG_ERR_RET(rspCode); + } + + code = queryProcessMsgRsp[TMSG_INDEX(reqType)](out, msg, msgSize); + if (code) { + qError("Process get udf rsp failed, error:%s, funcName:%s", tstrerror(code), target); + CTG_ERR_RET(code); + } + + qDebug("Got udf from mnode, funcName:%s", target); + break; + } + case TDMT_MND_GET_USER_AUTH: { + if (TSDB_CODE_SUCCESS != rspCode) { + qError("error rsp for get user auth, error:%s, user:%s", tstrerror(rspCode), target); + CTG_ERR_RET(rspCode); + } + + code = queryProcessMsgRsp[TMSG_INDEX(reqType)](out, msg, msgSize); + if (code) { + qError("Process get user auth rsp failed, error:%s, user:%s", tstrerror(code), target); + CTG_ERR_RET(code); + } + + qDebug("Got user auth from mnode, user:%s", target); + break; + } + case TDMT_MND_TABLE_META: { + if (TSDB_CODE_SUCCESS != rspCode) { + if (CTG_TABLE_NOT_EXIST(rspCode)) { + SET_META_TYPE_NULL(((STableMetaOutput*)out)->metaType); + qDebug("stablemeta not exist in mnode, tbFName:%s", target); + return TSDB_CODE_SUCCESS; + } + + qError("error rsp for stablemeta from mnode, error:%s, tbFName:%s", tstrerror(rspCode), target); + CTG_ERR_RET(rspCode); + } + + code = queryProcessMsgRsp[TMSG_INDEX(reqType)](out, msg, msgSize); + if (code) { + qError("Process mnode stablemeta rsp failed, error:%s, tbFName:%s", tstrerror(code), target); + CTG_ERR_RET(code); + } + + qDebug("Got table meta from mnode, tbFName:%s", target); + break; + } + case TDMT_VND_TABLE_META: { + if (TSDB_CODE_SUCCESS != rspCode) { + if (CTG_TABLE_NOT_EXIST(rspCode)) { + SET_META_TYPE_NULL(((STableMetaOutput*)out)->metaType); + qDebug("tablemeta not exist in vnode, tbFName:%s", target); + return TSDB_CODE_SUCCESS; + } + + qError("error rsp for table meta from vnode, code:%s, tbFName:%s", tstrerror(rspCode), target); + CTG_ERR_RET(rspCode); + } + + code = queryProcessMsgRsp[TMSG_INDEX(reqType)](out, msg, msgSize); + if (code) { + qError("Process vnode tablemeta rsp failed, code:%s, tbFName:%s", tstrerror(code), target); + CTG_ERR_RET(code); + } + + qDebug("Got table meta from vnode, tbFName:%s", target); + break; + } + } + + return TSDB_CODE_SUCCESS; +} + + +int32_t ctgHandleMsgCallback(void *param, const SDataBuf *pMsg, int32_t rspCode) { + SCtgTaskCallbackParam* cbParam = (SCtgTaskCallbackParam*)param; + int32_t code = 0; + + CTG_API_ENTER(); + + SCtgJob* pJob = taosAcquireRef(gCtgMgmt.jobPool, cbParam->refId); + if (NULL == pJob) { + qDebug("job refId %" PRIx64 " already dropped", cbParam->refId); + goto _return; + } + + SCtgTask *pTask = taosArrayGet(pJob->pTasks, cbParam->taskId); + + qDebug("QID:%" PRIx64 " task %d start to handle rsp %s", pJob->queryId, pTask->taskId, TMSG_INFO(cbParam->reqType + 1)); + + CTG_ERR_JRET((*gCtgAsyncFps[pTask->type].handleRspFp)(pTask, cbParam->reqType, pMsg, rspCode)); + +_return: + + if (pJob) { + taosReleaseRef(gCtgMgmt.jobPool, cbParam->refId); + } + + taosMemoryFree(param); + + CTG_API_LEAVE(code); +} + + +int32_t ctgMakeMsgSendInfo(SCtgTask* pTask, int32_t msgType, SMsgSendInfo **pMsgSendInfo) { + int32_t code = 0; + SMsgSendInfo *msgSendInfo = taosMemoryCalloc(1, sizeof(SMsgSendInfo)); + if (NULL == msgSendInfo) { + qError("calloc %d failed", (int32_t)sizeof(SMsgSendInfo)); + CTG_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + SCtgTaskCallbackParam *param = taosMemoryCalloc(1, sizeof(SCtgTaskCallbackParam)); + if (NULL == param) { + qError("calloc %d failed", (int32_t)sizeof(SCtgTaskCallbackParam)); + CTG_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + param->reqType = msgType; + param->queryId = pTask->pJob->queryId; + param->refId = pTask->pJob->refId; + param->taskId = pTask->taskId; + + msgSendInfo->param = param; + msgSendInfo->fp = ctgHandleMsgCallback; + + *pMsgSendInfo = msgSendInfo; + + return TSDB_CODE_SUCCESS; + +_return: + + taosMemoryFree(param); + taosMemoryFree(msgSendInfo); + + CTG_RET(code); +} + +int32_t ctgAsyncSendMsg(CTG_PARAMS, SCtgTask* pTask, int32_t msgType, void *msg, uint32_t msgSize) { + int32_t code = 0; + SMsgSendInfo *pMsgSendInfo = NULL; + CTG_ERR_JRET(ctgMakeMsgSendInfo(pTask, msgType, &pMsgSendInfo)); + + pMsgSendInfo->msgInfo.pData = msg; + pMsgSendInfo->msgInfo.len = msgSize; + pMsgSendInfo->msgInfo.handle = NULL; + pMsgSendInfo->msgType = msgType; + + int64_t transporterId = 0; + code = asyncSendMsgToServer(pTrans, (SEpSet*)pMgmtEps, &transporterId, pMsgSendInfo); + if (code) { + ctgError("asyncSendMsgToSever failed, error: %s", tstrerror(code)); + CTG_ERR_JRET(code); + } + + ctgDebug("req msg sent, reqId:%" PRIx64 ", msg type:%d, %s", pTask->pJob->queryId, msgType, TMSG_INFO(msgType)); + return TSDB_CODE_SUCCESS; + +_return: + + if (pMsgSendInfo) { + taosMemoryFreeClear(pMsgSendInfo->param); + taosMemoryFreeClear(pMsgSendInfo); + } + + CTG_RET(code); +} + + + + +int32_t ctgGetQnodeListFromMnode(CTG_PARAMS, SArray *out, SCtgTask* pTask) { + char *msg = NULL; + int32_t msgLen = 0; + int32_t reqType = TDMT_MND_QNODE_LIST; + + ctgDebug("try to get qnode list from mnode, mgmtEpInUse:%d", pMgmtEps->inUse); + + int32_t code = queryBuildMsg[TMSG_INDEX(reqType)](NULL, &msg, 0, &msgLen); + if (code) { + ctgError("Build qnode list msg failed, error:%s", tstrerror(code)); + CTG_ERR_RET(code); + } + + if (pTask) { + void* pOut = taosArrayInit(4, sizeof(struct SQueryNodeAddr)); + if (NULL == pOut) { + CTG_ERR_RET(TSDB_CODE_OUT_OF_MEMORY); + } + CTG_ERR_RET(ctgUpdateMsgCtx(&pTask->msgCtx, reqType, pOut, NULL)); + CTG_RET(ctgAsyncSendMsg(CTG_PARAMS_LIST(), pTask, reqType, msg, msgLen)); + } + + SRpcMsg rpcMsg = { + .msgType = reqType, + .pCont = msg, + .contLen = msgLen, + }; + + SRpcMsg rpcRsp = {0}; + rpcSendRecv(pTrans, (SEpSet*)pMgmtEps, &rpcMsg, &rpcRsp); + + CTG_ERR_RET(ctgProcessRspMsg(out, reqType, rpcRsp.pCont, rpcRsp.contLen, rpcRsp.code, NULL)); + + return TSDB_CODE_SUCCESS; +} + + +int32_t ctgGetDBVgInfoFromMnode(CTG_PARAMS, SBuildUseDBInput *input, SUseDbOutput *out, SCtgTask* pTask) { + char *msg = NULL; + int32_t msgLen = 0; + int32_t reqType = TDMT_MND_USE_DB; + + ctgDebug("try to get db vgInfo from mnode, dbFName:%s", input->db); + + int32_t code = queryBuildMsg[TMSG_INDEX(reqType)](input, &msg, 0, &msgLen); + if (code) { + ctgError("Build use db msg failed, code:%x, db:%s", code, input->db); + CTG_ERR_RET(code); + } + + if (pTask) { + void* pOut = taosMemoryCalloc(1, sizeof(SUseDbOutput)); + if (NULL == pOut) { + CTG_ERR_RET(TSDB_CODE_OUT_OF_MEMORY); + } + CTG_ERR_RET(ctgUpdateMsgCtx(&pTask->msgCtx, reqType, pOut, input->db)); + + CTG_RET(ctgAsyncSendMsg(CTG_PARAMS_LIST(), pTask, reqType, msg, msgLen)); + } + + SRpcMsg rpcMsg = { + .msgType = reqType, + .pCont = msg, + .contLen = msgLen, + }; + + SRpcMsg rpcRsp = {0}; + rpcSendRecv(pTrans, (SEpSet*)pMgmtEps, &rpcMsg, &rpcRsp); + + CTG_ERR_RET(ctgProcessRspMsg(out, reqType, rpcRsp.pCont, rpcRsp.contLen, rpcRsp.code, input->db)); + + return TSDB_CODE_SUCCESS; +} + +int32_t ctgGetDBCfgFromMnode(CTG_PARAMS, const char *dbFName, SDbCfgInfo *out, SCtgTask* pTask) { + char *msg = NULL; + int32_t msgLen = 0; + int32_t reqType = TDMT_MND_GET_DB_CFG; + + ctgDebug("try to get db cfg from mnode, dbFName:%s", dbFName); + + int32_t code = queryBuildMsg[TMSG_INDEX(reqType)]((void *)dbFName, &msg, 0, &msgLen); + if (code) { + ctgError("Build get db cfg msg failed, code:%x, db:%s", code, dbFName); + CTG_ERR_RET(code); + } + + if (pTask) { + void* pOut = taosMemoryCalloc(1, sizeof(SDbCfgInfo)); + if (NULL == pOut) { + CTG_ERR_RET(TSDB_CODE_OUT_OF_MEMORY); + } + CTG_ERR_RET(ctgUpdateMsgCtx(&pTask->msgCtx, reqType, pOut, (char*)dbFName)); + + CTG_RET(ctgAsyncSendMsg(CTG_PARAMS_LIST(), pTask, reqType, msg, msgLen)); + } + + SRpcMsg rpcMsg = { + .msgType = TDMT_MND_GET_DB_CFG, + .pCont = msg, + .contLen = msgLen, + }; + + SRpcMsg rpcRsp = {0}; + rpcSendRecv(pTrans, (SEpSet*)pMgmtEps, &rpcMsg, &rpcRsp); + + CTG_ERR_RET(ctgProcessRspMsg(out, reqType, rpcRsp.pCont, rpcRsp.contLen, rpcRsp.code, (char*)dbFName)); + + return TSDB_CODE_SUCCESS; +} + +int32_t ctgGetIndexInfoFromMnode(CTG_PARAMS, const char *indexName, SIndexInfo *out, SCtgTask* pTask) { + char *msg = NULL; + int32_t msgLen = 0; + int32_t reqType = TDMT_MND_GET_INDEX; + + ctgDebug("try to get index from mnode, indexName:%s", indexName); + + int32_t code = queryBuildMsg[TMSG_INDEX(reqType)]((void *)indexName, &msg, 0, &msgLen); + if (code) { + ctgError("Build get index msg failed, code:%x, db:%s", code, indexName); + CTG_ERR_RET(code); + } + + if (pTask) { + void* pOut = taosMemoryCalloc(1, sizeof(SIndexInfo)); + if (NULL == pOut) { + CTG_ERR_RET(TSDB_CODE_OUT_OF_MEMORY); + } + CTG_ERR_RET(ctgUpdateMsgCtx(&pTask->msgCtx, reqType, pOut, (char*)indexName)); + + CTG_RET(ctgAsyncSendMsg(CTG_PARAMS_LIST(), pTask, reqType, msg, msgLen)); + } + + SRpcMsg rpcMsg = { + .msgType = reqType, + .pCont = msg, + .contLen = msgLen, + }; + + SRpcMsg rpcRsp = {0}; + rpcSendRecv(pTrans, (SEpSet*)pMgmtEps, &rpcMsg, &rpcRsp); + + CTG_ERR_RET(ctgProcessRspMsg(out, reqType, rpcRsp.pCont, rpcRsp.contLen, rpcRsp.code, (char*)indexName)); + + return TSDB_CODE_SUCCESS; +} + +int32_t ctgGetUdfInfoFromMnode(CTG_PARAMS, const char *funcName, SFuncInfo *out, SCtgTask* pTask) { + char *msg = NULL; + int32_t msgLen = 0; + int32_t reqType = TDMT_MND_RETRIEVE_FUNC; + + ctgDebug("try to get udf info from mnode, funcName:%s", funcName); + + int32_t code = queryBuildMsg[TMSG_INDEX(reqType)]((void *)funcName, &msg, 0, &msgLen); + if (code) { + ctgError("Build get udf msg failed, code:%x, db:%s", code, funcName); + CTG_ERR_RET(code); + } + + if (pTask) { + void* pOut = taosMemoryCalloc(1, sizeof(SFuncInfo)); + if (NULL == pOut) { + CTG_ERR_RET(TSDB_CODE_OUT_OF_MEMORY); + } + CTG_ERR_RET(ctgUpdateMsgCtx(&pTask->msgCtx, reqType, pOut, (char*)funcName)); + + CTG_RET(ctgAsyncSendMsg(CTG_PARAMS_LIST(), pTask, reqType, msg, msgLen)); + } + + SRpcMsg rpcMsg = { + .msgType = reqType, + .pCont = msg, + .contLen = msgLen, + }; + + SRpcMsg rpcRsp = {0}; + rpcSendRecv(pTrans, (SEpSet*)pMgmtEps, &rpcMsg, &rpcRsp); + + CTG_ERR_RET(ctgProcessRspMsg(out, reqType, rpcRsp.pCont, rpcRsp.contLen, rpcRsp.code, (char*)funcName)); + + return TSDB_CODE_SUCCESS; +} + +int32_t ctgGetUserDbAuthFromMnode(CTG_PARAMS, const char *user, SGetUserAuthRsp *out, SCtgTask* pTask) { + char *msg = NULL; + int32_t msgLen = 0; + int32_t reqType = TDMT_MND_GET_USER_AUTH; + + ctgDebug("try to get user auth from mnode, user:%s", user); + + int32_t code = queryBuildMsg[TMSG_INDEX(reqType)]((void *)user, &msg, 0, &msgLen); + if (code) { + ctgError("Build get user auth msg failed, code:%x, db:%s", code, user); + CTG_ERR_RET(code); + } + + if (pTask) { + void* pOut = taosMemoryCalloc(1, sizeof(SGetUserAuthRsp)); + if (NULL == pOut) { + CTG_ERR_RET(TSDB_CODE_OUT_OF_MEMORY); + } + CTG_ERR_RET(ctgUpdateMsgCtx(&pTask->msgCtx, reqType, pOut, (char*)user)); + + CTG_RET(ctgAsyncSendMsg(CTG_PARAMS_LIST(), pTask, reqType, msg, msgLen)); + } + + SRpcMsg rpcMsg = { + .msgType = reqType, + .pCont = msg, + .contLen = msgLen, + }; + + SRpcMsg rpcRsp = {0}; + rpcSendRecv(pTrans, (SEpSet*)pMgmtEps, &rpcMsg, &rpcRsp); + + CTG_ERR_RET(ctgProcessRspMsg(out, reqType, rpcRsp.pCont, rpcRsp.contLen, rpcRsp.code, (char*)user)); + + return TSDB_CODE_SUCCESS; +} + + +int32_t ctgGetTbMetaFromMnodeImpl(CTG_PARAMS, char *dbFName, char* tbName, STableMetaOutput* out, SCtgTask* pTask) { + SBuildTableMetaInput bInput = {.vgId = 0, .dbFName = dbFName, .tbName = tbName}; + char *msg = NULL; + SEpSet *pVnodeEpSet = NULL; + int32_t msgLen = 0; + int32_t reqType = TDMT_MND_TABLE_META; + char tbFName[TSDB_TABLE_FNAME_LEN]; + sprintf(tbFName, "%s.%s", dbFName, tbName); + + ctgDebug("try to get table meta from mnode, tbFName:%s", tbFName); + + int32_t code = queryBuildMsg[TMSG_INDEX(reqType)](&bInput, &msg, 0, &msgLen); + if (code) { + ctgError("Build mnode stablemeta msg failed, code:%x", code); + CTG_ERR_RET(code); + } + + if (pTask) { + void* pOut = taosMemoryCalloc(1, sizeof(STableMetaOutput)); + if (NULL == pOut) { + CTG_ERR_RET(TSDB_CODE_OUT_OF_MEMORY); + } + CTG_ERR_RET(ctgUpdateMsgCtx(&pTask->msgCtx, reqType, pOut, tbFName)); + + CTG_RET(ctgAsyncSendMsg(CTG_PARAMS_LIST(), pTask, reqType, msg, msgLen)); + } + + SRpcMsg rpcMsg = { + .msgType = reqType, + .pCont = msg, + .contLen = msgLen, + }; + + SRpcMsg rpcRsp = {0}; + rpcSendRecv(pTrans, (SEpSet*)pMgmtEps, &rpcMsg, &rpcRsp); + + CTG_ERR_RET(ctgProcessRspMsg(out, reqType, rpcRsp.pCont, rpcRsp.contLen, rpcRsp.code, tbFName)); + + return TSDB_CODE_SUCCESS; +} + +int32_t ctgGetTbMetaFromMnode(CTG_PARAMS, const SName* pTableName, STableMetaOutput* out, SCtgTask* pTask) { + char dbFName[TSDB_DB_FNAME_LEN]; + tNameGetFullDbName(pTableName, dbFName); + + return ctgGetTbMetaFromMnodeImpl(CTG_PARAMS_LIST(), dbFName, (char *)pTableName->tname, out, pTask); +} + +int32_t ctgGetTbMetaFromVnode(CTG_PARAMS, const SName* pTableName, SVgroupInfo *vgroupInfo, STableMetaOutput* out, SCtgTask* pTask) { + char dbFName[TSDB_DB_FNAME_LEN]; + tNameGetFullDbName(pTableName, dbFName); + int32_t reqType = TDMT_VND_TABLE_META; + char tbFName[TSDB_TABLE_FNAME_LEN]; + sprintf(tbFName, "%s.%s", dbFName, pTableName->tname); + + ctgDebug("try to get table meta from vnode, vgId:%d, tbFName:%s", vgroupInfo->vgId, tbFName); + + SBuildTableMetaInput bInput = {.vgId = vgroupInfo->vgId, .dbFName = dbFName, .tbName = (char *)tNameGetTableName(pTableName)}; + char *msg = NULL; + int32_t msgLen = 0; + + int32_t code = queryBuildMsg[TMSG_INDEX(reqType)](&bInput, &msg, 0, &msgLen); + if (code) { + ctgError("Build vnode tablemeta msg failed, code:%x, tbFName:%s", code, tbFName); + CTG_ERR_RET(code); + } + + if (pTask) { + void* pOut = taosMemoryCalloc(1, sizeof(STableMetaOutput)); + if (NULL == pOut) { + CTG_ERR_RET(TSDB_CODE_OUT_OF_MEMORY); + } + CTG_ERR_RET(ctgUpdateMsgCtx(&pTask->msgCtx, reqType, pOut, tbFName)); + + CTG_RET(ctgAsyncSendMsg(CTG_PARAMS_LIST(), pTask, reqType, msg, msgLen)); + } + + SRpcMsg rpcMsg = { + .msgType = reqType, + .pCont = msg, + .contLen = msgLen, + }; + + SRpcMsg rpcRsp = {0}; + rpcSendRecv(pTrans, &vgroupInfo->epSet, &rpcMsg, &rpcRsp); + + CTG_ERR_RET(ctgProcessRspMsg(out, reqType, rpcRsp.pCont, rpcRsp.contLen, rpcRsp.code, tbFName)); + + return TSDB_CODE_SUCCESS; +} + + diff --git a/source/libs/catalog/src/ctgUtil.c b/source/libs/catalog/src/ctgUtil.c new file mode 100644 index 0000000000000000000000000000000000000000..2d7fb8aa97af13b8eaa589f0316882e3223e810e --- /dev/null +++ b/source/libs/catalog/src/ctgUtil.c @@ -0,0 +1,577 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "trpc.h" +#include "query.h" +#include "tname.h" +#include "catalogInt.h" +#include "systable.h" + +void ctgFreeSMetaData(SMetaData* pData) { + taosArrayDestroy(pData->pTableMeta); + pData->pTableMeta = NULL; + + for (int32_t i = 0; i < taosArrayGetSize(pData->pDbVgroup); ++i) { + SArray** pArray = taosArrayGet(pData->pDbVgroup, i); + taosArrayDestroy(*pArray); + } + taosArrayDestroy(pData->pDbVgroup); + pData->pDbVgroup = NULL; + + taosArrayDestroy(pData->pTableHash); + pData->pTableHash = NULL; + + taosArrayDestroy(pData->pUdfList); + pData->pUdfList = NULL; + + for (int32_t i = 0; i < taosArrayGetSize(pData->pDbCfg); ++i) { + SDbCfgInfo* pInfo = taosArrayGet(pData->pDbCfg, i); + taosArrayDestroy(pInfo->pRetensions); + } + taosArrayDestroy(pData->pDbCfg); + pData->pDbCfg = NULL; + + taosArrayDestroy(pData->pIndex); + pData->pIndex = NULL; + + taosArrayDestroy(pData->pUser); + pData->pUser = NULL; + + taosArrayDestroy(pData->pQnodeList); + pData->pQnodeList = NULL; +} + +void ctgFreeSCtgUserAuth(SCtgUserAuth *userCache) { + taosHashCleanup(userCache->createdDbs); + taosHashCleanup(userCache->readDbs); + taosHashCleanup(userCache->writeDbs); +} + +void ctgFreeMetaRent(SCtgRentMgmt *mgmt) { + if (NULL == mgmt->slots) { + return; + } + + for (int32_t i = 0; i < mgmt->slotNum; ++i) { + SCtgRentSlot *slot = &mgmt->slots[i]; + if (slot->meta) { + taosArrayDestroy(slot->meta); + slot->meta = NULL; + } + } + + taosMemoryFreeClear(mgmt->slots); +} + + +void ctgFreeTbMetaCache(SCtgTbMetaCache *cache) { + CTG_LOCK(CTG_WRITE, &cache->stbLock); + if (cache->stbCache) { + int32_t stblNum = taosHashGetSize(cache->stbCache); + taosHashCleanup(cache->stbCache); + cache->stbCache = NULL; + CTG_CACHE_STAT_SUB(stblNum, stblNum); + } + CTG_UNLOCK(CTG_WRITE, &cache->stbLock); + + CTG_LOCK(CTG_WRITE, &cache->metaLock); + if (cache->metaCache) { + int32_t tblNum = taosHashGetSize(cache->metaCache); + taosHashCleanup(cache->metaCache); + cache->metaCache = NULL; + CTG_CACHE_STAT_SUB(tblNum, tblNum); + } + CTG_UNLOCK(CTG_WRITE, &cache->metaLock); +} + +void ctgFreeVgInfo(SDBVgInfo *vgInfo) { + if (NULL == vgInfo) { + return; + } + + if (vgInfo->vgHash) { + taosHashCleanup(vgInfo->vgHash); + vgInfo->vgHash = NULL; + } + + taosMemoryFreeClear(vgInfo); +} + +void ctgFreeDbCache(SCtgDBCache *dbCache) { + if (NULL == dbCache) { + return; + } + + CTG_LOCK(CTG_WRITE, &dbCache->vgLock); + ctgFreeVgInfo (dbCache->vgInfo); + CTG_UNLOCK(CTG_WRITE, &dbCache->vgLock); + + ctgFreeTbMetaCache(&dbCache->tbCache); +} + + +void ctgFreeHandle(SCatalog* pCtg) { + ctgFreeMetaRent(&pCtg->dbRent); + ctgFreeMetaRent(&pCtg->stbRent); + + if (pCtg->dbCache) { + int32_t dbNum = taosHashGetSize(pCtg->dbCache); + + void *pIter = taosHashIterate(pCtg->dbCache, NULL); + while (pIter) { + SCtgDBCache *dbCache = pIter; + + atomic_store_8(&dbCache->deleted, 1); + + ctgFreeDbCache(dbCache); + + pIter = taosHashIterate(pCtg->dbCache, pIter); + } + + taosHashCleanup(pCtg->dbCache); + + CTG_CACHE_STAT_SUB(dbNum, dbNum); + } + + if (pCtg->userCache) { + int32_t userNum = taosHashGetSize(pCtg->userCache); + + void *pIter = taosHashIterate(pCtg->userCache, NULL); + while (pIter) { + SCtgUserAuth *userCache = pIter; + + ctgFreeSCtgUserAuth(userCache); + + pIter = taosHashIterate(pCtg->userCache, pIter); + } + + taosHashCleanup(pCtg->userCache); + + CTG_CACHE_STAT_SUB(userNum, userNum); + } + + taosMemoryFree(pCtg); +} + + +void ctgFreeSUseDbOutput(SUseDbOutput* pOutput) { + if (NULL == pOutput || NULL == pOutput->dbVgroup) { + return; + } + + taosHashCleanup(pOutput->dbVgroup->vgHash); + taosMemoryFreeClear(pOutput->dbVgroup); + taosMemoryFree(pOutput); +} + +void ctgFreeMsgCtx(SCtgMsgCtx* pCtx) { + taosMemoryFreeClear(pCtx->target); + if (NULL == pCtx->out) { + return; + } + + switch (pCtx->reqType) { + case TDMT_MND_GET_DB_CFG: { + SDbCfgInfo* pOut = (SDbCfgInfo*)pCtx->out; + taosArrayDestroy(pOut->pRetensions); + taosMemoryFreeClear(pCtx->out); + break; + } + case TDMT_MND_USE_DB:{ + SUseDbOutput* pOut = (SUseDbOutput*)pCtx->out; + ctgFreeSUseDbOutput(pOut); + pCtx->out = NULL; + break; + } + case TDMT_MND_GET_INDEX: { + SIndexInfo* pOut = (SIndexInfo*)pCtx->out; + taosMemoryFreeClear(pCtx->out); + break; + } + case TDMT_MND_QNODE_LIST: { + SArray* pOut = (SArray*)pCtx->out; + taosArrayDestroy(pOut); + pCtx->out = NULL; + break; + } + case TDMT_VND_TABLE_META: + case TDMT_MND_TABLE_META: { + STableMetaOutput* pOut = (STableMetaOutput*)pCtx->out; + taosMemoryFree(pOut->tbMeta); + taosMemoryFreeClear(pCtx->out); + break; + } + case TDMT_MND_RETRIEVE_FUNC: { + SFuncInfo* pOut = (SFuncInfo*)pCtx->out; + taosMemoryFree(pOut->pCode); + taosMemoryFree(pOut->pComment); + taosMemoryFreeClear(pCtx->out); + break; + } + case TDMT_MND_GET_USER_AUTH: { + SGetUserAuthRsp* pOut = (SGetUserAuthRsp*)pCtx->out; + taosHashCleanup(pOut->createdDbs); + taosHashCleanup(pOut->readDbs); + taosHashCleanup(pOut->writeDbs); + taosMemoryFreeClear(pCtx->out); + break; + } + default: + qError("invalid reqType %d", pCtx->reqType); + break; + } +} + +void ctgFreeSTableMetaOutput(STableMetaOutput* pOutput) { + if (NULL == pOutput) { + return; + } + + taosMemoryFree(pOutput->tbMeta); + taosMemoryFree(pOutput); +} + + +void ctgResetTbMetaTask(SCtgTask* pTask) { + SCtgTbMetaCtx* taskCtx = (SCtgTbMetaCtx*)pTask->taskCtx; + memset(&taskCtx->tbInfo, 0, sizeof(taskCtx->tbInfo)); + taskCtx->flag = CTG_FLAG_UNKNOWN_STB; + + if (pTask->msgCtx.lastOut) { + ctgFreeSTableMetaOutput((STableMetaOutput*)pTask->msgCtx.lastOut); + pTask->msgCtx.lastOut = NULL; + } + if (pTask->msgCtx.out) { + ctgFreeSTableMetaOutput((STableMetaOutput*)pTask->msgCtx.out); + pTask->msgCtx.out = NULL; + } + taosMemoryFreeClear(pTask->msgCtx.target); + taosMemoryFreeClear(pTask->res); +} + +void ctgFreeTask(SCtgTask* pTask) { + ctgFreeMsgCtx(&pTask->msgCtx); + + switch (pTask->type) { + case CTG_TASK_GET_QNODE: { + taosArrayDestroy((SArray*)pTask->res); + pTask->res = NULL; + break; + } + case CTG_TASK_GET_TB_META: { + SCtgTbMetaCtx* taskCtx = (SCtgTbMetaCtx*)pTask->taskCtx; + taosMemoryFreeClear(taskCtx->pName); + if (pTask->msgCtx.lastOut) { + ctgFreeSTableMetaOutput((STableMetaOutput*)pTask->msgCtx.lastOut); + pTask->msgCtx.lastOut = NULL; + } + taosMemoryFreeClear(pTask->res); + break; + } + case CTG_TASK_GET_DB_VGROUP: { + taosArrayDestroy((SArray*)pTask->res); + pTask->res = NULL; + break; + } + case CTG_TASK_GET_DB_CFG: { + if (pTask->res) { + taosArrayDestroy(((SDbCfgInfo*)pTask->res)->pRetensions); + taosMemoryFreeClear(pTask->res); + } + break; + } + case CTG_TASK_GET_TB_HASH: { + SCtgTbHashCtx* taskCtx = (SCtgTbHashCtx*)pTask->taskCtx; + taosMemoryFreeClear(taskCtx->pName); + taosMemoryFreeClear(pTask->res); + break; + } + case CTG_TASK_GET_INDEX: { + taosMemoryFreeClear(pTask->taskCtx); + taosMemoryFreeClear(pTask->res); + break; + } + case CTG_TASK_GET_UDF: { + taosMemoryFreeClear(pTask->taskCtx); + taosMemoryFreeClear(pTask->res); + break; + } + case CTG_TASK_GET_USER: { + taosMemoryFreeClear(pTask->taskCtx); + taosMemoryFreeClear(pTask->res); + break; + } + default: + qError("invalid task type %d", pTask->type); + break; + } +} + +void ctgFreeTasks(SArray* pArray) { + if (NULL == pArray) { + return; + } + + int32_t num = taosArrayGetSize(pArray); + for (int32_t i = 0; i < num; ++i) { + SCtgTask* pTask = taosArrayGet(pArray, i); + ctgFreeTask(pTask); + } + + taosArrayDestroy(pArray); +} + +void ctgFreeJob(void* job) { + if (NULL == job) { + return; + } + + SCtgJob* pJob = (SCtgJob*)job; + + int64_t rid = pJob->refId; + uint64_t qid = pJob->queryId; + + ctgFreeTasks(pJob->pTasks); + + ctgFreeSMetaData(&pJob->jobRes); + + taosMemoryFree(job); + + qDebug("QID:%" PRIx64 ", job %" PRIx64 " freed", qid, rid); +} + +int32_t ctgUpdateMsgCtx(SCtgMsgCtx* pCtx, int32_t reqType, void* out, char* target) { + ctgFreeMsgCtx(pCtx); + + pCtx->reqType = reqType; + pCtx->out = out; + if (target) { + pCtx->target = strdup(target); + if (NULL == pCtx->target) { + CTG_ERR_RET(TSDB_CODE_OUT_OF_MEMORY); + } + } else { + pCtx->target = NULL; + } + + return TSDB_CODE_SUCCESS; +} + + +int32_t ctgGetHashFunction(int8_t hashMethod, tableNameHashFp *fp) { + switch (hashMethod) { + default: + *fp = MurmurHash3_32; + break; + } + + return TSDB_CODE_SUCCESS; +} + +int32_t ctgGenerateVgList(SCatalog *pCtg, SHashObj *vgHash, SArray** pList) { + SHashObj *vgroupHash = NULL; + SVgroupInfo *vgInfo = NULL; + SArray *vgList = NULL; + int32_t code = 0; + int32_t vgNum = taosHashGetSize(vgHash); + + vgList = taosArrayInit(vgNum, sizeof(SVgroupInfo)); + if (NULL == vgList) { + ctgError("taosArrayInit failed, num:%d", vgNum); + CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); + } + + void *pIter = taosHashIterate(vgHash, NULL); + while (pIter) { + vgInfo = pIter; + + if (NULL == taosArrayPush(vgList, vgInfo)) { + ctgError("taosArrayPush failed, vgId:%d", vgInfo->vgId); + taosHashCancelIterate(vgHash, pIter); + CTG_ERR_JRET(TSDB_CODE_CTG_MEM_ERROR); + } + + pIter = taosHashIterate(vgHash, pIter); + vgInfo = NULL; + } + + *pList = vgList; + + ctgDebug("Got vgList from cache, vgNum:%d", vgNum); + + return TSDB_CODE_SUCCESS; + +_return: + + if (vgList) { + taosArrayDestroy(vgList); + } + + CTG_RET(code); +} + + +int32_t ctgGetVgInfoFromHashValue(SCatalog *pCtg, SDBVgInfo *dbInfo, const SName *pTableName, SVgroupInfo *pVgroup) { + int32_t code = 0; + + int32_t vgNum = taosHashGetSize(dbInfo->vgHash); + char db[TSDB_DB_FNAME_LEN] = {0}; + tNameGetFullDbName(pTableName, db); + + if (vgNum <= 0) { + ctgError("db vgroup cache invalid, db:%s, vgroup number:%d", db, vgNum); + CTG_ERR_RET(TSDB_CODE_TSC_DB_NOT_SELECTED); + } + + tableNameHashFp fp = NULL; + SVgroupInfo *vgInfo = NULL; + + CTG_ERR_RET(ctgGetHashFunction(dbInfo->hashMethod, &fp)); + + char tbFullName[TSDB_TABLE_FNAME_LEN]; + tNameExtractFullName(pTableName, tbFullName); + + uint32_t hashValue = (*fp)(tbFullName, (uint32_t)strlen(tbFullName)); + + void *pIter = taosHashIterate(dbInfo->vgHash, NULL); + while (pIter) { + vgInfo = pIter; + if (hashValue >= vgInfo->hashBegin && hashValue <= vgInfo->hashEnd) { + taosHashCancelIterate(dbInfo->vgHash, pIter); + break; + } + + pIter = taosHashIterate(dbInfo->vgHash, pIter); + vgInfo = NULL; + } + + if (NULL == vgInfo) { + ctgError("no hash range found for hash value [%u], db:%s, numOfVgId:%d", hashValue, db, taosHashGetSize(dbInfo->vgHash)); + CTG_ERR_RET(TSDB_CODE_CTG_INTERNAL_ERROR); + } + + *pVgroup = *vgInfo; + + CTG_RET(code); +} + +int32_t ctgStbVersionSearchCompare(const void* key1, const void* key2) { + if (*(uint64_t *)key1 < ((SSTableMetaVersion*)key2)->suid) { + return -1; + } else if (*(uint64_t *)key1 > ((SSTableMetaVersion*)key2)->suid) { + return 1; + } else { + return 0; + } +} + +int32_t ctgDbVgVersionSearchCompare(const void* key1, const void* key2) { + if (*(int64_t *)key1 < ((SDbVgVersion*)key2)->dbId) { + return -1; + } else if (*(int64_t *)key1 > ((SDbVgVersion*)key2)->dbId) { + return 1; + } else { + return 0; + } +} + +int32_t ctgStbVersionSortCompare(const void* key1, const void* key2) { + if (((SSTableMetaVersion*)key1)->suid < ((SSTableMetaVersion*)key2)->suid) { + return -1; + } else if (((SSTableMetaVersion*)key1)->suid > ((SSTableMetaVersion*)key2)->suid) { + return 1; + } else { + return 0; + } +} + +int32_t ctgDbVgVersionSortCompare(const void* key1, const void* key2) { + if (((SDbVgVersion*)key1)->dbId < ((SDbVgVersion*)key2)->dbId) { + return -1; + } else if (((SDbVgVersion*)key1)->dbId > ((SDbVgVersion*)key2)->dbId) { + return 1; + } else { + return 0; + } +} + + + + +int32_t ctgCloneVgInfo(SDBVgInfo *src, SDBVgInfo **dst) { + *dst = taosMemoryMalloc(sizeof(SDBVgInfo)); + if (NULL == *dst) { + qError("malloc %d failed", (int32_t)sizeof(SDBVgInfo)); + CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); + } + + memcpy(*dst, src, sizeof(SDBVgInfo)); + + size_t hashSize = taosHashGetSize(src->vgHash); + (*dst)->vgHash = taosHashInit(hashSize, taosGetDefaultHashFunction(TSDB_DATA_TYPE_INT), true, HASH_ENTRY_LOCK); + if (NULL == (*dst)->vgHash) { + qError("taosHashInit %d failed", (int32_t)hashSize); + taosMemoryFreeClear(*dst); + CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); + } + + int32_t *vgId = NULL; + void *pIter = taosHashIterate(src->vgHash, NULL); + while (pIter) { + vgId = taosHashGetKey(pIter, NULL); + + if (taosHashPut((*dst)->vgHash, (void *)vgId, sizeof(int32_t), pIter, sizeof(SVgroupInfo))) { + qError("taosHashPut failed, hashSize:%d", (int32_t)hashSize); + taosHashCancelIterate(src->vgHash, pIter); + taosHashCleanup((*dst)->vgHash); + taosMemoryFreeClear(*dst); + CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); + } + + pIter = taosHashIterate(src->vgHash, pIter); + } + + + return TSDB_CODE_SUCCESS; +} + + + +int32_t ctgCloneMetaOutput(STableMetaOutput *output, STableMetaOutput **pOutput) { + *pOutput = taosMemoryMalloc(sizeof(STableMetaOutput)); + if (NULL == *pOutput) { + qError("malloc %d failed", (int32_t)sizeof(STableMetaOutput)); + CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); + } + + memcpy(*pOutput, output, sizeof(STableMetaOutput)); + + if (output->tbMeta) { + int32_t metaSize = CTG_META_SIZE(output->tbMeta); + (*pOutput)->tbMeta = taosMemoryMalloc(metaSize); + if (NULL == (*pOutput)->tbMeta) { + qError("malloc %d failed", (int32_t)sizeof(STableMetaOutput)); + taosMemoryFreeClear(*pOutput); + CTG_ERR_RET(TSDB_CODE_CTG_MEM_ERROR); + } + + memcpy((*pOutput)->tbMeta, output->tbMeta, metaSize); + } + + return TSDB_CODE_SUCCESS; +} + + + diff --git a/source/libs/catalog/test/catalogTests.cpp b/source/libs/catalog/test/catalogTests.cpp index cff0087d6ccc7dbab5f690ffe1cecc27ec4813b7..6c7d1ac4ca554e69b92bcde3e4c64f20a46d0dcb 100644 --- a/source/libs/catalog/test/catalogTests.cpp +++ b/source/libs/catalog/test/catalogTests.cpp @@ -40,10 +40,8 @@ namespace { -extern "C" int32_t ctgGetTableMetaFromCache(struct SCatalog *pCatalog, const SName *pTableName, STableMeta **pTableMeta, - bool *inCache, int32_t flag, uint64_t *dbId); extern "C" int32_t ctgdGetClusterCacheNum(struct SCatalog* pCatalog, int32_t type); -extern "C" int32_t ctgActUpdateTbl(SCtgMetaAction *action); +extern "C" int32_t ctgActUpdateTb(SCtgMetaAction *action); extern "C" int32_t ctgdEnableDebug(char *option); extern "C" int32_t ctgdGetStatNum(char *option, void *res); @@ -52,7 +50,7 @@ void ctgTestSetRspCTableMeta(); void ctgTestSetRspSTableMeta(); void ctgTestSetRspMultiSTableMeta(); -extern "C" SCatalogMgmt gCtgMgmt; +//extern "C" SCatalogMgmt gCtgMgmt; enum { CTGT_RSP_VGINFO = 1, @@ -859,8 +857,12 @@ void *ctgTestGetCtableMetaThread(void *param) { strcpy(cn.dbname, "db1"); strcpy(cn.tname, ctgTestCTablename); + SCtgTbMetaCtx ctx = {0}; + ctx.pName = &cn; + ctx.flag = CTG_FLAG_UNKNOWN_STB; + while (!ctgTestStop) { - code = ctgGetTableMetaFromCache(pCtg, &cn, &tbMeta, &inCache, 0, NULL); + code = ctgReadTbMetaFromCache(pCtg, &ctx, &tbMeta); if (code || !inCache) { assert(0); } @@ -899,7 +901,7 @@ void *ctgTestSetCtableMetaThread(void *param) { msg->output = output; action.data = msg; - code = ctgActUpdateTbl(&action); + code = ctgActUpdateTb(&action); if (code) { assert(0); } diff --git a/source/libs/command/src/command.c b/source/libs/command/src/command.c index 621ea7b7fc7483dd6b073cf6301bf373e154e634..3034b4b02a293dd4cce91814bde367682db6ac8d 100644 --- a/source/libs/command/src/command.c +++ b/source/libs/command/src/command.c @@ -21,6 +21,7 @@ static int32_t getSchemaBytes(const SSchema* pSchema) { case TSDB_DATA_TYPE_BINARY: return (pSchema->bytes - VARSTR_HEADER_SIZE); case TSDB_DATA_TYPE_NCHAR: + case TSDB_DATA_TYPE_JSON: return (pSchema->bytes - VARSTR_HEADER_SIZE) / TSDB_NCHAR_SIZE; default: return pSchema->bytes; diff --git a/source/libs/executor/inc/executil.h b/source/libs/executor/inc/executil.h index edc21626f55e70cd0b6fb473451bf276f9589cc6..63c398618f38446260124978a803b2a63c6f0688 100644 --- a/source/libs/executor/inc/executil.h +++ b/source/libs/executor/inc/executil.h @@ -42,11 +42,8 @@ #define GET_TASKID(_t) (((SExecTaskInfo*)(_t))->id.str) typedef struct SGroupResInfo { - int32_t totalGroup; - int32_t currentGroup; int32_t index; - SArray* pRows; // SArray - bool ordered; + SArray* pRows; // SArray int32_t position; } SGroupResInfo; @@ -80,18 +77,12 @@ typedef struct SResultRowInfo { SResultRowPosition cur; } SResultRowInfo; -struct STaskAttr; -struct STaskRuntimeEnv; -struct SUdfInfo; struct SqlFunctionCtx; -int32_t getOutputInterResultBufSize(struct STaskAttr* pQueryAttr); - size_t getResultRowSize(struct SqlFunctionCtx* pCtx, int32_t numOfOutput); int32_t initResultRowInfo(SResultRowInfo* pResultRowInfo, int32_t size); void cleanupResultRowInfo(SResultRowInfo* pResultRowInfo); -void resetResultRowInfo(struct STaskRuntimeEnv* pRuntimeEnv, SResultRowInfo* pResultRowInfo); int32_t numOfClosedResultRows(SResultRowInfo* pResultRowInfo); void closeAllResultRows(SResultRowInfo* pResultRowInfo); @@ -99,9 +90,7 @@ void initResultRow(SResultRow *pResultRow); void closeResultRow(SResultRow* pResultRow); bool isResultRowClosed(SResultRow* pResultRow); -struct SResultRowEntryInfo* getResultCell(const SResultRow* pRow, int32_t index, int32_t* offset); - -int32_t getRowNumForMultioutput(struct STaskAttr* pQueryAttr, bool topBottomQuery, bool stable); +struct SResultRowEntryInfo* getResultCell(const SResultRow* pRow, int32_t index, const int32_t* offset); static FORCE_INLINE SResultRow *getResultRow(SDiskbasedBuf* pBuf, SResultRowInfo *pResultRowInfo, int32_t slot) { ASSERT(pResultRowInfo != NULL && slot >= 0 && slot < pResultRowInfo->size); @@ -118,39 +107,13 @@ static FORCE_INLINE SResultRow *getResultRowByPos(SDiskbasedBuf* pBuf, SResultRo return pRow; } -static FORCE_INLINE char* getPosInResultPage(struct STaskAttr* pQueryAttr, SFilePage* page, int32_t rowOffset, - int32_t offset) { - assert(rowOffset >= 0 && pQueryAttr != NULL); - - // int32_t numOfRows = (int32_t)getRowNumForMultioutput(pQueryAttr, pQueryAttr->topBotQuery, pQueryAttr->stableQuery); - return ((char *)page->data); - -} - -static FORCE_INLINE char* getPosInResultPage_rv(SFilePage* page, int32_t rowOffset, int32_t offset) { - assert(rowOffset >= 0); - - int32_t numOfRows = 1;//(int32_t)getRowNumForMultioutput(pQueryAttr, pQueryAttr->topBotQuery, pQueryAttr->stableQuery); - return (char*) page + rowOffset + offset * numOfRows; -} - -typedef struct { - SArray* pResult; // SArray - int32_t colId; -} SStddevInterResult; - void initGroupedResultInfo(SGroupResInfo* pGroupResInfo, SHashObj* pHashmap, int32_t order); void initMultiResInfoFromArrayList(SGroupResInfo* pGroupResInfo, SArray* pArrayList); void cleanupGroupResInfo(SGroupResInfo* pGroupResInfo); -bool hasRemainDataInCurrentGroup(SGroupResInfo* pGroupResInfo); -bool hasRemainData(SGroupResInfo* pGroupResInfo); +bool hashRemainDataInGroupInfo(SGroupResInfo* pGroupResInfo); bool incNextGroup(SGroupResInfo* pGroupResInfo); int32_t getNumOfTotalRes(SGroupResInfo* pGroupResInfo); -int32_t mergeIntoGroupResult(SGroupResInfo* pGroupResInfo, struct STaskRuntimeEnv *pRuntimeEnv, int32_t* offset); - -//int32_t initUdfInfo(struct SUdfInfo* pUdfInfo); - #endif // TDENGINE_QUERYUTIL_H diff --git a/source/libs/executor/inc/executorimpl.h b/source/libs/executor/inc/executorimpl.h index 78f3266d745046582679222f18137e6ced87fb6c..0dacbba8e590b010405571727775cdcda983629c 100644 --- a/source/libs/executor/inc/executorimpl.h +++ b/source/libs/executor/inc/executorimpl.h @@ -666,7 +666,6 @@ int32_t initAggInfo(SOptrBasicInfo* pBasicInfo, SAggSupporter* pAggSup, SExprInf void initResultSizeInfo(SOperatorInfo* pOperator, int32_t numOfRows); void doBuildResultDatablock(SOperatorInfo* pOperator, SOptrBasicInfo* pbInfo, SGroupResInfo* pGroupResInfo, SDiskbasedBuf* pBuf); -void finalizeMultiTupleQueryResult(int32_t numOfOutput, SDiskbasedBuf* pBuf, SResultRowInfo* pResultRowInfo, int32_t* rowCellInfoOffset); void doApplyFunctions(SExecTaskInfo* taskInfo, SqlFunctionCtx* pCtx, STimeWindow* pWin, SColumnInfoData* pTimeWindowData, int32_t offset, int32_t forwardStep, TSKEY* tsCol, int32_t numOfTotal, int32_t numOfOutput, int32_t order); int32_t setGroupResultOutputBuf(SOptrBasicInfo* binfo, int32_t numOfCols, char* pData, int16_t type, int16_t bytes, diff --git a/source/libs/executor/src/executil.c b/source/libs/executor/src/executil.c index 14cff488ecd183b0567fa20709657d44c84a1e47..5a02547f58aa4cf73c5297dda771ba0900bce141 100644 --- a/source/libs/executor/src/executil.c +++ b/source/libs/executor/src/executil.c @@ -114,12 +114,6 @@ void closeAllResultRows(SResultRowInfo *pResultRowInfo) { assert(pResultRowInfo->size >= 0 && pResultRowInfo->capacity >= pResultRowInfo->size); for (int32_t i = 0; i < pResultRowInfo->size; ++i) { -// SResultRow* pRow = pResultRowInfo->pResult[i]; -// if (pRow->closed) { -// continue; -// } - -// pRow->closed = true; } } @@ -144,11 +138,11 @@ void clearResultRow(STaskRuntimeEnv *pRuntimeEnv, SResultRow *pResultRow) { for (int32_t i = 0; i < pRuntimeEnv->pQueryAttr->numOfOutput; ++i) { struct SResultRowEntryInfo *pEntryInfo = NULL;//pResultRow->pEntryInfo[i]; - int16_t size = pRuntimeEnv->pQueryAttr->pExpr1[i].base.resSchema.bytes; - char * s = getPosInResultPage(pRuntimeEnv->pQueryAttr, page, pResultRow->offset, offset); - memset(s, 0, size); +// int16_t size = pRuntimeEnv->pQueryAttr->pExpr1[i].base.resSchema.bytes; +// char * s = getPosInResultPage(pRuntimeEnv->pQueryAttr, page, pResultRow->offset, offset); +// memset(s, 0, size); - offset += size; +// offset += size; cleanupResultRowEntry(pEntryInfo); } } @@ -161,7 +155,7 @@ void clearResultRow(STaskRuntimeEnv *pRuntimeEnv, SResultRow *pResultRow) { } // TODO refactor: use macro -SResultRowEntryInfo* getResultCell(const SResultRow* pRow, int32_t index, int32_t* offset) { +SResultRowEntryInfo* getResultCell(const SResultRow* pRow, int32_t index, const int32_t* offset) { assert(index >= 0 && offset != NULL); return (SResultRowEntryInfo*)((char*) pRow->pEntryInfo + offset[index]); } @@ -247,7 +241,7 @@ void initMultiResInfoFromArrayList(SGroupResInfo* pGroupResInfo, SArray* pArrayL ASSERT(pGroupResInfo->index <= getNumOfTotalRes(pGroupResInfo)); } -bool hasRemainDataInCurrentGroup(SGroupResInfo* pGroupResInfo) { +bool hashRemainDataInGroupInfo(SGroupResInfo* pGroupResInfo) { if (pGroupResInfo->pRows == NULL) { return false; } @@ -255,18 +249,6 @@ bool hasRemainDataInCurrentGroup(SGroupResInfo* pGroupResInfo) { return pGroupResInfo->index < taosArrayGetSize(pGroupResInfo->pRows); } -bool hasRemainData(SGroupResInfo* pGroupResInfo) { - if (hasRemainDataInCurrentGroup(pGroupResInfo)) { - return true; - } - - return pGroupResInfo->currentGroup < pGroupResInfo->totalGroup; -} - -bool incNextGroup(SGroupResInfo* pGroupResInfo) { - return (++pGroupResInfo->currentGroup) < pGroupResInfo->totalGroup; -} - int32_t getNumOfTotalRes(SGroupResInfo* pGroupResInfo) { assert(pGroupResInfo != NULL); if (pGroupResInfo->pRows == 0) { @@ -387,11 +369,6 @@ void orderTheResultRows(STaskRuntimeEnv* pRuntimeEnv) { } static int32_t mergeIntoGroupResultImplRv(STaskRuntimeEnv *pRuntimeEnv, SGroupResInfo* pGroupResInfo, uint64_t groupId, int32_t* rowCellInfoOffset) { - if (!pGroupResInfo->ordered) { - orderTheResultRows(pRuntimeEnv); - pGroupResInfo->ordered = true; - } - if (pGroupResInfo->pRows == NULL) { pGroupResInfo->pRows = taosArrayInit(100, POINTER_BYTES); } @@ -403,6 +380,7 @@ static int32_t mergeIntoGroupResultImplRv(STaskRuntimeEnv *pRuntimeEnv, SGroupRe break; } + int64_t num = getNumOfResultWindowRes(pRuntimeEnv, &pResultRowCell->pos, rowCellInfoOffset); if (num <= 0) { continue; diff --git a/source/libs/executor/src/executor.c b/source/libs/executor/src/executor.c index fa840e1cd61dee1f796e9569b403e94f8062bee9..2811c8dce84918bc61339597150b15f56690b99d 100644 --- a/source/libs/executor/src/executor.c +++ b/source/libs/executor/src/executor.c @@ -125,6 +125,33 @@ qTaskInfo_t qCreateStreamExecTaskInfo(void* msg, void* streamReadHandle) { return pTaskInfo; } +static SArray* filterQualifiedChildTables(const SStreamBlockScanInfo* pScanInfo, const SArray* tableIdList) { + SArray* qa = taosArrayInit(4, sizeof(tb_uid_t)); + + // let's discard the tables those are not created according to the queried super table. + SMetaReader mr = {0}; + metaReaderInit(&mr, pScanInfo->readHandle.meta, 0); + for (int32_t i = 0; i < taosArrayGetSize(tableIdList); ++i) { + int64_t* id = (int64_t*)taosArrayGet(tableIdList, i); + + int32_t code = metaGetTableEntryByUid(&mr, *id); + if (code != TSDB_CODE_SUCCESS) { + qError("failed to get table meta, uid:%" PRIu64 " code:%s", *id, tstrerror(terrno)); + continue; + } + + ASSERT(mr.me.type == TSDB_CHILD_TABLE); + if (mr.me.ctbEntry.suid != pScanInfo->tableUid) { + continue; + } + + taosArrayPush(qa, id); + } + + metaReaderClear(&mr); + return qa; +} + int32_t qUpdateQualifiedTableId(qTaskInfo_t tinfo, const SArray* tableIdList, bool isAdd) { SExecTaskInfo* pTaskInfo = (SExecTaskInfo*)tinfo; @@ -134,41 +161,20 @@ int32_t qUpdateQualifiedTableId(qTaskInfo_t tinfo, const SArray* tableIdList, bo pInfo = pInfo->pDownstream[0]; } + int32_t code = 0; SStreamBlockScanInfo* pScanInfo = pInfo->info; - if (isAdd) { - SArray* qa = taosArrayInit(4, sizeof(tb_uid_t)); - - SMetaReader mr = {0}; - metaReaderInit(&mr, pScanInfo->readHandle.meta, 0); - for (int32_t i = 0; i < taosArrayGetSize(tableIdList); ++i) { - int64_t* id = (int64_t*)taosArrayGet(tableIdList, i); - - int32_t code = metaGetTableEntryByUid(&mr, *id); - if (code != TSDB_CODE_SUCCESS) { - qError("failed to get table meta, uid:%" PRIu64 " code:%s", *id, tstrerror(terrno)); - continue; - } - - ASSERT(mr.me.type == TSDB_CHILD_TABLE); - if (mr.me.ctbEntry.suid != pScanInfo->tableUid) { - continue; - } - - taosArrayPush(qa, id); - } - - metaReaderClear(&mr); + if (isAdd) { // add new table id + SArray* qa = filterQualifiedChildTables(pScanInfo, tableIdList); qDebug(" %d qualified child tables added into stream scanner", (int32_t)taosArrayGetSize(qa)); - int32_t code = tqReadHandleAddTbUidList(pScanInfo->streamBlockReader, qa); - if (code != TSDB_CODE_SUCCESS) { - return code; - } - } else { - assert(0); + code = tqReadHandleAddTbUidList(pScanInfo->streamBlockReader, qa); + taosArrayDestroy(qa); + } else { // remove the table id in current list + qDebug(" %d remove child tables from the stream scanner", (int32_t)taosArrayGetSize(tableIdList)); + code = tqReadHandleRemoveTbUidList(pScanInfo->streamBlockReader, tableIdList); } - return TSDB_CODE_SUCCESS; + return code; } int32_t qGetQueriedTableSchemaVersion(qTaskInfo_t tinfo, char* dbName, char* tableName, int32_t* sversion, int32_t* tversion) { @@ -177,8 +183,16 @@ int32_t qGetQueriedTableSchemaVersion(qTaskInfo_t tinfo, char* dbName, char* tab *sversion = pTaskInfo->schemaVer.sversion; *tversion = pTaskInfo->schemaVer.tversion; - strcpy(dbName, pTaskInfo->schemaVer.dbname); - strcpy(tableName, pTaskInfo->schemaVer.tablename); + if (pTaskInfo->schemaVer.dbname) { + strcpy(dbName, pTaskInfo->schemaVer.dbname); + } else { + dbName[0] = 0; + } + if (pTaskInfo->schemaVer.tablename) { + strcpy(tableName, pTaskInfo->schemaVer.tablename); + } else { + tableName[0] = 0; + } return 0; } \ No newline at end of file diff --git a/source/libs/executor/src/executorimpl.c b/source/libs/executor/src/executorimpl.c index 168589148ec5b23d17bc1d1110ac93dbb440221b..1da5770c6d7aa1b49a4250b147afd21795d76468 100644 --- a/source/libs/executor/src/executorimpl.c +++ b/source/libs/executor/src/executorimpl.c @@ -13,7 +13,6 @@ * along with this program. If not, see . */ -#include #include "filter.h" #include "function.h" #include "functionMgt.h" @@ -107,7 +106,7 @@ static void destroyTableQueryInfoImpl(STableQueryInfo* pTableQueryInfo); static SColumnInfo* extractColumnFilterInfo(SExprInfo* pExpr, int32_t numOfOutput, int32_t* numOfFilterCols); -static void releaseQueryBuf(size_t numOfTables); +static void releaseQueryBuf(size_t numOfTables); static int32_t getNumOfScanTimes(STaskAttr* pQueryAttr); @@ -155,8 +154,9 @@ SOperatorFpSet createOperatorFpSet(__optr_open_fn_t openFn, __optr_fn_t nextFn, void operatorDummyCloseFn(void* param, int32_t numOfCols) {} -static int32_t doCopyToSDataBlock(SExecTaskInfo* taskInfo, SSDataBlock* pBlock, SExprInfo* pExprInfo, SDiskbasedBuf* pBuf, SGroupResInfo* pGroupResInfo, - int32_t* rowCellOffset, SqlFunctionCtx* pCtx, int32_t numOfExprs); +static int32_t doCopyToSDataBlock(SExecTaskInfo* taskInfo, SSDataBlock* pBlock, SExprInfo* pExprInfo, + SDiskbasedBuf* pBuf, SGroupResInfo* pGroupResInfo, const int32_t* rowCellOffset, + SqlFunctionCtx* pCtx, int32_t numOfExprs); static void initCtxOutputBuffer(SqlFunctionCtx* pCtx, int32_t size); static void setResultBufSize(STaskAttr* pQueryAttr, SResultInfo* pResultInfo); @@ -182,10 +182,11 @@ static int compareRowData(const void* a, const void* b, const void* userData) { SFilePage* page2 = getBufPage(pRuntimeEnv->pResultBuf, pRow2->pageId); int16_t offset = supporter->dataOffset; - char* in1 = getPosInResultPage(pRuntimeEnv->pQueryAttr, page1, pRow1->offset, offset); - char* in2 = getPosInResultPage(pRuntimeEnv->pQueryAttr, page2, pRow2->offset, offset); + return 0; + // char* in1 = getPosInResultPage(pRuntimeEnv->pQueryAttr, page1, pRow1->offset, offset); + // char* in2 = getPosInResultPage(pRuntimeEnv->pQueryAttr, page2, pRow2->offset, offset); - return (in1 != NULL && in2 != NULL) ? supporter->comFunc(in1, in2) : 0; + // return (in1 != NULL && in2 != NULL) ? supporter->comFunc(in1, in2) : 0; } // setup the output buffer for each operator @@ -333,6 +334,8 @@ SResultRow* getNewResultRow_rv(SDiskbasedBuf* pResultBuf, int64_t tableGroupId, return NULL; } + setBufPageDirty(pData, true); + // set the number of rows in current disk page SResultRow* pResultRow = (SResultRow*)((char*)pData + pData->num); pResultRow->pageId = pageId; @@ -364,12 +367,14 @@ SResultRow* doSetResultOutBufByKey(SDiskbasedBuf* pResultBuf, SResultRowInfo* pR if (isIntervalQuery) { if (masterscan && p1 != NULL) { // the *p1 may be NULL in case of sliding+offset exists. pResult = getResultRowByPos(pResultBuf, p1); + ASSERT(pResult->pageId == p1->pageId && pResult->offset == p1->offset); } } else { // In case of group by column query, the required SResultRow object must be existInCurrentResusltRowInfo in the // pResultRowInfo object. if (p1 != NULL) { pResult = getResultRowByPos(pResultBuf, p1); + ASSERT(pResult->pageId == p1->pageId && pResult->offset == p1->offset); } } @@ -578,8 +583,9 @@ void initExecTimeWindowInfo(SColumnInfoData* pColData, STimeWindow* pQueryWindow colDataAppendInt64(pColData, 4, &pQueryWindow->ekey); } -void doApplyFunctions(SExecTaskInfo* taskInfo, SqlFunctionCtx* pCtx, STimeWindow* pWin, SColumnInfoData* pTimeWindowData, int32_t offset, - int32_t forwardStep, TSKEY* tsCol, int32_t numOfTotal, int32_t numOfOutput, int32_t order) { +void doApplyFunctions(SExecTaskInfo* taskInfo, SqlFunctionCtx* pCtx, STimeWindow* pWin, + SColumnInfoData* pTimeWindowData, int32_t offset, int32_t forwardStep, TSKEY* tsCol, + int32_t numOfTotal, int32_t numOfOutput, int32_t order) { for (int32_t k = 0; k < numOfOutput; ++k) { // keep it temporarily bool hasAgg = pCtx[k].input.colDataAggIsSet; @@ -661,8 +667,8 @@ static void doSetInputDataBlockInfo(SOperatorInfo* pOperator, SqlFunctionCtx* pC } } -void setInputDataBlock(SOperatorInfo* pOperator, SqlFunctionCtx* pCtx, SSDataBlock* pBlock, int32_t order, int32_t scanFlag, - bool createDummyCol) { +void setInputDataBlock(SOperatorInfo* pOperator, SqlFunctionCtx* pCtx, SSDataBlock* pBlock, int32_t order, + int32_t scanFlag, bool createDummyCol) { if (pBlock->pBlockAgg != NULL) { doSetInputDataBlockInfo(pOperator, pCtx, pBlock, order); } else { @@ -713,7 +719,7 @@ static int32_t doCreateConstantValColumnInfo(SInputColumnInfoData* pInput, SFunc } static int32_t doSetInputDataBlock(SOperatorInfo* pOperator, SqlFunctionCtx* pCtx, SSDataBlock* pBlock, int32_t order, - int32_t scanFlag, bool createDummyCol) { + int32_t scanFlag, bool createDummyCol) { int32_t code = TSDB_CODE_SUCCESS; for (int32_t i = 0; i < pOperator->numOfExprs; ++i) { @@ -721,7 +727,7 @@ static int32_t doSetInputDataBlock(SOperatorInfo* pOperator, SqlFunctionCtx* pCt pCtx[i].input.numOfRows = pBlock->info.rows; pCtx[i].pSrcBlock = pBlock; - pCtx[i].scanFlag = scanFlag; + pCtx[i].scanFlag = scanFlag; SInputColumnInfoData* pInput = &pCtx[i].input; pInput->uid = pBlock->info.uid; @@ -830,7 +836,7 @@ int32_t projectApplyFunctions(SExprInfo* pExpr, SSDataBlock* pResult, SSDataBloc SColumnInfoData idata = {.info = pResColData->info, .hasNull = true}; SScalarParam dest = {.columnData = &idata}; - int32_t code = scalarCalculate(pExpr[k].pExpr->_optrRoot.pRootNode, pBlockList, &dest); + int32_t code = scalarCalculate(pExpr[k].pExpr->_optrRoot.pRootNode, pBlockList, &dest); if (code != TSDB_CODE_SUCCESS) { taosArrayDestroy(pBlockList); return code; @@ -848,7 +854,7 @@ int32_t projectApplyFunctions(SExprInfo* pExpr, SSDataBlock* pResult, SSDataBloc // _rowts/_c0, not tbname column if (fmIsPseudoColumnFunc(pfCtx->functionId) && (!fmIsScanPseudoColumnFunc(pfCtx->functionId))) { // do nothing - } else if (fmIsNonstandardSQLFunc(pfCtx->functionId)) { + } else if (fmIsIndefiniteRowsFunc(pfCtx->functionId)) { SResultRowEntryInfo* pResInfo = GET_RES_INFO(&pCtx[k]); pfCtx->fpSet.init(&pCtx[k], pResInfo); @@ -946,14 +952,14 @@ static bool functionNeedToExecute(SqlFunctionCtx* pCtx) { return false; } -// if (functionId == FUNCTION_FIRST_DST || functionId == FUNCTION_FIRST) { -// // return QUERY_IS_ASC_QUERY(pQueryAttr); -// } -// -// // denote the order type -// if ((functionId == FUNCTION_LAST_DST || functionId == FUNCTION_LAST)) { -// // return pCtx->param[0].i == pQueryAttr->order.order; -// } + // if (functionId == FUNCTION_FIRST_DST || functionId == FUNCTION_FIRST) { + // // return QUERY_IS_ASC_QUERY(pQueryAttr); + // } + // + // // denote the order type + // if ((functionId == FUNCTION_LAST_DST || functionId == FUNCTION_LAST)) { + // // return pCtx->param[0].i == pQueryAttr->order.order; + // } // in the reverse table scan, only the following functions need to be executed // if (IS_REVERSE_SCAN(pRuntimeEnv) || @@ -1068,19 +1074,19 @@ static int32_t setSelectValueColumnInfo(SqlFunctionCtx* pCtx, int32_t numOfOutpu for (int32_t i = 0; i < numOfOutput; ++i) { if (strcmp(pCtx[i].pExpr->pExpr->_function.functionName, "_select_value") == 0) { pValCtx[num++] = &pCtx[i]; - } else if (fmIsAggFunc(pCtx[i].functionId)) { + } else if (fmIsSelectFunc(pCtx[i].functionId)) { p = &pCtx[i]; } -// if (functionId == FUNCTION_TAG_DUMMY || functionId == FUNCTION_TS_DUMMY) { -// tagLen += pCtx[i].resDataInfo.bytes; -// pTagCtx[num++] = &pCtx[i]; -// } else if (functionId == FUNCTION_TS || functionId == FUNCTION_TAG) { -// // tag function may be the group by tag column -// // ts may be the required primary timestamp column -// continue; -// } else { -// // the column may be the normal column, group by normal_column, the functionId is FUNCTION_PRJ -// } + // if (functionId == FUNCTION_TAG_DUMMY || functionId == FUNCTION_TS_DUMMY) { + // tagLen += pCtx[i].resDataInfo.bytes; + // pTagCtx[num++] = &pCtx[i]; + // } else if (functionId == FUNCTION_TS || functionId == FUNCTION_TAG) { + // // tag function may be the group by tag column + // // ts may be the required primary timestamp column + // continue; + // } else { + // // the column may be the normal column, group by normal_column, the functionId is FUNCTION_PRJ + // } } if (p != NULL) { @@ -1119,7 +1125,7 @@ SqlFunctionCtx* createSqlFunctionCtx(SExprInfo* pExprInfo, int32_t numOfOutput, SFuncExecEnv env = {0}; pCtx->functionId = pExpr->pExpr->_function.pFunctNode->funcId; - if (fmIsAggFunc(pCtx->functionId) || fmIsNonstandardSQLFunc(pCtx->functionId)) { + if (fmIsAggFunc(pCtx->functionId) || fmIsIndefiniteRowsFunc(pCtx->functionId)) { bool isUdaf = fmIsUserDefinedFunc(pCtx->functionId); if (!isUdaf) { fmGetFuncExecFuncs(pCtx->functionId, &pCtx->fpSet); @@ -1417,21 +1423,6 @@ static int32_t updateBlockLoadStatus(STaskAttr* pQuery, int32_t status) { // } //} -static void getIntermediateBufInfo(STaskRuntimeEnv* pRuntimeEnv, int32_t* ps, int32_t* rowsize) { - STaskAttr* pQueryAttr = pRuntimeEnv->pQueryAttr; - int32_t MIN_ROWS_PER_PAGE = 4; - - *rowsize = (int32_t)(pQueryAttr->resultRowSize * - getRowNumForMultioutput(pQueryAttr, pQueryAttr->topBotQuery, pQueryAttr->stableQuery)); - int32_t overhead = sizeof(SFilePage); - - // one page contains at least two rows - *ps = DEFAULT_INTERN_BUF_PAGE_SIZE; - while (((*rowsize) * MIN_ROWS_PER_PAGE) > (*ps) - overhead) { - *ps = ((*ps) << 1u); - } -} - // static FORCE_INLINE bool doFilterByBlockStatistics(STaskRuntimeEnv* pRuntimeEnv, SDataStatis *pDataStatis, // SqlFunctionCtx *pCtx, int32_t numOfRows) { // STaskAttr* pQueryAttr = pRuntimeEnv->pQueryAttr; @@ -1708,36 +1699,6 @@ int32_t loadDataBlockOnDemand(SExecTaskInfo* pTaskInfo, STableScanInfo* pTableSc return TSDB_CODE_SUCCESS; } -void copyToSDataBlock(SSDataBlock* pBlock, int32_t* offset, SGroupResInfo* pGroupResInfo, SDiskbasedBuf* pResBuf) { - pBlock->info.rows = 0; - - int32_t code = TSDB_CODE_SUCCESS; - while (pGroupResInfo->currentGroup < pGroupResInfo->totalGroup) { - // all results in current group have been returned to client, try next group - if ((pGroupResInfo->pRows == NULL) || taosArrayGetSize(pGroupResInfo->pRows) == 0) { - assert(pGroupResInfo->index == 0); - // if ((code = mergeIntoGroupResult(&pGroupResInfo, pRuntimeEnv, offset)) != TSDB_CODE_SUCCESS) { - return; - // } - } - - // doCopyToSDataBlock(pResBuf, pGroupResInfo, TSDB_ORDER_ASC, pBlock, ); - - // current data are all dumped to result buffer, clear it - if (!hasRemainDataInCurrentGroup(pGroupResInfo)) { - cleanupGroupResInfo(pGroupResInfo); - if (!incNextGroup(pGroupResInfo)) { - break; - } - } - - // enough results in data buffer, return - // if (pBlock->info.rows >= threshold) { - // break; - // } - } -} - static void updateTableQueryInfoForReverseScan(STableQueryInfo* pTableQueryInfo) { if (pTableQueryInfo == NULL) { return; @@ -1884,35 +1845,6 @@ void setTaskStatus(SExecTaskInfo* pTaskInfo, int8_t status) { } } -// todo merged with the build group result. -void finalizeMultiTupleQueryResult(int32_t numOfOutput, SDiskbasedBuf* pBuf, SResultRowInfo* pResultRowInfo, - int32_t* rowCellInfoOffset) { - for (int32_t i = 0; i < pResultRowInfo->size; ++i) { - SResultRowPosition* pPos = &pResultRowInfo->pPosition[i]; - - SFilePage* bufPage = getBufPage(pBuf, pPos->pageId); - SResultRow* pRow = (SResultRow*)((char*)bufPage + pPos->offset); - - // TODO ignore the close status anyway. - // if (!isResultRowClosed(pRow)) { - // continue; - // } - - for (int32_t j = 0; j < numOfOutput; ++j) { - struct SResultRowEntryInfo* pResInfo = getResultCell(pRow, j, rowCellInfoOffset); - if (!isRowEntryInitialized(pResInfo)) { - continue; - } - - if (pRow->numOfRows < pResInfo->numOfRes) { - pRow->numOfRows = pResInfo->numOfRes; - } - } - - releaseBufPage(pBuf, bufPage); - } -} - STableQueryInfo* createTableQueryInfo(void* buf, STimeWindow win) { STableQueryInfo* pTableQueryInfo = buf; pTableQueryInfo->lastKey = win.skey; @@ -1952,7 +1884,7 @@ void setResultRowInitCtx(SResultRow* pResult, SqlFunctionCtx* pCtx, int32_t numO } static void extractQualifiedTupleByFilterResult(SSDataBlock* pBlock, const int8_t* rowRes, bool keep); -void doFilter(const SNode* pFilterNode, SSDataBlock* pBlock, SArray* pColMatchInfo) { +void doFilter(const SNode* pFilterNode, SSDataBlock* pBlock, SArray* pColMatchInfo) { if (pFilterNode == NULL) { return; } @@ -1972,7 +1904,7 @@ void doFilter(const SNode* pFilterNode, SSDataBlock* pBlock, SArray* pColMatchIn filterFreeInfo(filter); extractQualifiedTupleByFilterResult(pBlock, rowRes, keep); - blockDataUpdateTsWindow(pBlock); + blockDataUpdateTsWindow(pBlock, 0); } void extractQualifiedTupleByFilterResult(SSDataBlock* pBlock, const int8_t* rowRes, bool keep) { @@ -2062,32 +1994,50 @@ void setExecutionContext(int32_t numOfOutput, uint64_t groupId, SExecTaskInfo* p pAggInfo->groupId = groupId; } -/** - * For interval query of both super table and table, copy the data in ascending order, since the output results are - * ordered in SWindowResutl already. While handling the group by query for both table and super table, - * all group result are completed already. - * - * @param pQInfo - * @param result - */ -int32_t doCopyToSDataBlock(SExecTaskInfo* taskInfo, SSDataBlock* pBlock, SExprInfo* pExprInfo, SDiskbasedBuf* pBuf, SGroupResInfo* pGroupResInfo, - int32_t* rowCellOffset, SqlFunctionCtx* pCtx, int32_t numOfExprs) { +static void doUpdateNumOfRows(SResultRow* pRow, int32_t numOfExprs, const int32_t* rowCellOffset) { + for (int32_t j = 0; j < numOfExprs; ++j) { + struct SResultRowEntryInfo* pResInfo = getResultCell(pRow, j, rowCellOffset); + if (!isRowEntryInitialized(pResInfo)) { + continue; + } + + if (pRow->numOfRows < pResInfo->numOfRes) { + pRow->numOfRows = pResInfo->numOfRes; + } + } +} + +int32_t doCopyToSDataBlock(SExecTaskInfo* pTaskInfo, SSDataBlock* pBlock, SExprInfo* pExprInfo, SDiskbasedBuf* pBuf, + SGroupResInfo* pGroupResInfo, const int32_t* rowCellOffset, SqlFunctionCtx* pCtx, + int32_t numOfExprs) { int32_t numOfRows = getNumOfTotalRes(pGroupResInfo); int32_t start = pGroupResInfo->index; - // qDebug("QInfo:0x%"PRIx64" start to copy data from windowResInfo to output buf", GET_TASKID(pRuntimeEnv)); - for (int32_t i = start; i < numOfRows; i += 1) { SResKeyPos* pPos = taosArrayGetP(pGroupResInfo->pRows, i); SFilePage* page = getBufPage(pBuf, pPos->pos.pageId); SResultRow* pRow = (SResultRow*)((char*)page + pPos->pos.offset); + + doUpdateNumOfRows(pRow, numOfExprs, rowCellOffset); if (pRow->numOfRows == 0) { pGroupResInfo->index += 1; + releaseBufPage(pBuf, page); continue; } + if (pBlock->info.groupId == 0) { + pBlock->info.groupId = pPos->groupId; + } else { + // current value belongs to different group, it can't be packed into one datablock + if (pBlock->info.groupId != pPos->groupId) { + releaseBufPage(pBuf, page); + break; + } + } + if (pBlock->info.rows + pRow->numOfRows > pBlock->info.capacity) { + releaseBufPage(pBuf, page); break; } @@ -2100,20 +2050,19 @@ int32_t doCopyToSDataBlock(SExecTaskInfo* taskInfo, SSDataBlock* pBlock, SExprIn if (pCtx[j].fpSet.finalize) { int32_t code = pCtx[j].fpSet.finalize(&pCtx[j], pBlock); if (TAOS_FAILED(code)) { - qError("%s build result data block error, code %s", GET_TASKID(taskInfo), tstrerror(code)); - taskInfo->code = code; - longjmp(taskInfo->env, code); + qError("%s build result data block error, code %s", GET_TASKID(pTaskInfo), tstrerror(code)); + longjmp(pTaskInfo->env, code); } } else if (strcmp(pCtx[j].pExpr->pExpr->_function.functionName, "_select_value") == 0) { // do nothing, todo refactor } else { // expand the result into multiple rows. E.g., _wstartts, top(k, 20) // the _wstartts needs to copy to 20 following rows, since the results of top-k expands to 20 different rows. - SColumnInfoData* pColInfoData = taosArrayGet(pBlock->pDataBlock, slotId); - char* in = GET_ROWCELL_INTERBUF(pCtx[j].resultInfo); - for(int32_t k = 0; k < pRow->numOfRows; ++k) { - colDataAppend(pColInfoData, pBlock->info.rows + k, in, pCtx[j].resultInfo->isNullRes); - } + SColumnInfoData* pColInfoData = taosArrayGet(pBlock->pDataBlock, slotId); + char* in = GET_ROWCELL_INTERBUF(pCtx[j].resultInfo); + for (int32_t k = 0; k < pRow->numOfRows; ++k) { + colDataAppend(pColInfoData, pBlock->info.rows + k, in, pCtx[j].resultInfo->isNullRes); + } } } @@ -2124,16 +2073,16 @@ int32_t doCopyToSDataBlock(SExecTaskInfo* taskInfo, SSDataBlock* pBlock, SExprIn } } - // qDebug("QInfo:0x%"PRIx64" copy data to query buf completed", GET_TASKID(pRuntimeEnv)); - blockDataUpdateTsWindow(pBlock); + qDebug("%s result generated, rows:%d, groupId:%" PRIu64, GET_TASKID(pTaskInfo), pBlock->info.rows, + pBlock->info.groupId); + blockDataUpdateTsWindow(pBlock, 0); return 0; } -void doBuildResultDatablock(SOperatorInfo* pOperator, SOptrBasicInfo* pbInfo, SGroupResInfo* pGroupResInfo, SDiskbasedBuf* pBuf) { - assert(pGroupResInfo->currentGroup <= pGroupResInfo->totalGroup); - - SExprInfo* pExprInfo = pOperator->pExpr; - int32_t numOfExprs = pOperator->numOfExprs; +void doBuildResultDatablock(SOperatorInfo* pOperator, SOptrBasicInfo* pbInfo, SGroupResInfo* pGroupResInfo, + SDiskbasedBuf* pBuf) { + SExprInfo* pExprInfo = pOperator->pExpr; + int32_t numOfExprs = pOperator->numOfExprs; SExecTaskInfo* pTaskInfo = pOperator->pTaskInfo; int32_t* rowCellOffset = pbInfo->rowCellInfoOffset; @@ -2141,14 +2090,13 @@ void doBuildResultDatablock(SOperatorInfo* pOperator, SOptrBasicInfo* pbInfo, SG SqlFunctionCtx* pCtx = pbInfo->pCtx; blockDataCleanup(pBlock); - if (!hasRemainDataInCurrentGroup(pGroupResInfo)) { + if (!hashRemainDataInGroupInfo(pGroupResInfo)) { return; } + // clear the existed group id + pBlock->info.groupId = 0; doCopyToSDataBlock(pTaskInfo, pBlock, pExprInfo, pBuf, pGroupResInfo, rowCellOffset, pCtx, numOfExprs); - - // add condition (pBlock->info.rows >= 1) just to runtime happy - blockDataUpdateTsWindow(pBlock); } static void updateNumOfRowsInResultRows(SqlFunctionCtx* pCtx, int32_t numOfOutput, SResultRowInfo* pResultRowInfo, @@ -2803,10 +2751,10 @@ int32_t setSDataBlockFromFetchRsp(SSDataBlock* pRes, SLoadRemoteDataInfo* pLoadI for (int32_t i = 0; i < numOfCols; ++i) { SColumnInfoData idata = {0}; - idata.info.type = pSchema[i].type; + idata.info.type = pSchema[i].type; idata.info.bytes = pSchema[i].bytes; idata.info.colId = pSchema[i].colId; - idata.hasNull = true; + idata.hasNull = true; taosArrayPush(pBlock->pDataBlock, &idata); if (IS_VAR_DATA_TYPE(idata.info.type)) { @@ -2853,7 +2801,9 @@ int32_t setSDataBlockFromFetchRsp(SSDataBlock* pRes, SLoadRemoteDataInfo* pLoadI } pRes->info.rows = numOfRows; - blockDataUpdateTsWindow(pRes); + + // todo move this to time window aggregator, since the primary timestamp may not be known by exchange operator. + blockDataUpdateTsWindow(pRes, 0); int64_t el = taosGetTimestampUs() - startTs; @@ -3154,7 +3104,7 @@ static int32_t initDataSource(int32_t numOfSources, SExchangeInfo* pInfo) { return TSDB_CODE_SUCCESS; } -SOperatorInfo* createExchangeOperatorInfo(void *pTransporter, const SNodeList* pSources, SSDataBlock* pBlock, +SOperatorInfo* createExchangeOperatorInfo(void* pTransporter, const SNodeList* pSources, SSDataBlock* pBlock, SExecTaskInfo* pTaskInfo) { SExchangeInfo* pInfo = taosMemoryCalloc(1, sizeof(SExchangeInfo)); SOperatorInfo* pOperator = taosMemoryCalloc(1, sizeof(SOperatorInfo)); @@ -3267,7 +3217,7 @@ static bool needToMerge(SSDataBlock* pBlock, SArray* groupInfo, char** buf, int3 static void doMergeResultImpl(SSortedMergeOperatorInfo* pInfo, SqlFunctionCtx* pCtx, int32_t numOfExpr, int32_t rowIndex) { for (int32_t j = 0; j < numOfExpr; ++j) { // TODO set row index -// pCtx[j].startRow = rowIndex; + // pCtx[j].startRow = rowIndex; } for (int32_t j = 0; j < numOfExpr; ++j) { @@ -3318,7 +3268,7 @@ static void doMergeImpl(SOperatorInfo* pOperator, int32_t numOfExpr, SSDataBlock SqlFunctionCtx* pCtx = pInfo->binfo.pCtx; for (int32_t i = 0; i < pBlock->info.numOfCols; ++i) { -// pCtx[i].size = 1; + // pCtx[i].size = 1; } for (int32_t i = 0; i < pBlock->info.rows; ++i) { @@ -3544,13 +3494,15 @@ _error: return NULL; } -int32_t getTableScanInfo(SOperatorInfo* pOperator, int32_t *order, int32_t* scanFlag) { +int32_t getTableScanInfo(SOperatorInfo* pOperator, int32_t* order, int32_t* scanFlag) { // todo add more information about exchange operation - if (pOperator->operatorType == QUERY_NODE_PHYSICAL_PLAN_EXCHANGE) { + int32_t type = pOperator->operatorType; + if (type == QUERY_NODE_PHYSICAL_PLAN_EXCHANGE || type == QUERY_NODE_PHYSICAL_PLAN_SYSTABLE_SCAN || + type == QUERY_NODE_PHYSICAL_PLAN_STREAM_SCAN) { *order = TSDB_ORDER_ASC; *scanFlag = MAIN_SCAN; return TSDB_CODE_SUCCESS; - } else if (pOperator->operatorType == QUERY_NODE_PHYSICAL_PLAN_TABLE_SCAN) { + } else if (type == QUERY_NODE_PHYSICAL_PLAN_TABLE_SCAN) { STableScanInfo* pTableScanInfo = pOperator->info; *order = pTableScanInfo->cond.order; *scanFlag = pTableScanInfo->scanFlag; @@ -3574,7 +3526,7 @@ static int32_t doOpenAggregateOptr(SOperatorInfo* pOperator) { SAggOperatorInfo* pAggInfo = pOperator->info; SOptrBasicInfo* pInfo = &pAggInfo->binfo; - SOperatorInfo* downstream = pOperator->pDownstream[0]; + SOperatorInfo* downstream = pOperator->pDownstream[0]; int32_t order = TSDB_ORDER_ASC; int32_t scanFlag = MAIN_SCAN; @@ -3627,9 +3579,6 @@ static int32_t doOpenAggregateOptr(SOperatorInfo* pOperator) { } closeAllResultRows(&pAggInfo->binfo.resultRowInfo); - finalizeMultiTupleQueryResult(pOperator->numOfExprs, pAggInfo->aggSup.pResultBuf, &pAggInfo->binfo.resultRowInfo, - pAggInfo->binfo.rowCellInfoOffset); - initGroupedResultInfo(&pAggInfo->groupResInfo, pAggInfo->aggSup.pResultRowHashTable, 0); OPTR_SET_OPENED(pOperator); return TSDB_CODE_SUCCESS; @@ -3651,11 +3600,10 @@ static SSDataBlock* getAggregateResult(SOperatorInfo* pOperator) { blockDataEnsureCapacity(pInfo->pRes, pOperator->resultInfo.capacity); doBuildResultDatablock(pOperator, pInfo, &pAggInfo->groupResInfo, pAggInfo->aggSup.pResultBuf); - if (pInfo->pRes->info.rows == 0 || !hasRemainDataInCurrentGroup(&pAggInfo->groupResInfo)) { + if (pInfo->pRes->info.rows == 0 || !hashRemainDataInGroupInfo(&pAggInfo->groupResInfo)) { doSetOperatorCompleted(pOperator); } - doSetOperatorCompleted(pOperator); return (blockDataGetNumOfRows(pInfo->pRes) != 0) ? pInfo->pRes : NULL; } @@ -3910,11 +3858,15 @@ static SSDataBlock* doProjectOperation(SOperatorInfo* pOperator) { // the pDataBlock are always the same one, no need to call this again int32_t code = getTableScanInfo(pOperator->pDownstream[0], &order, &scanFlag); + if (code != TSDB_CODE_SUCCESS) { + longjmp(pTaskInfo->env, code); + } setInputDataBlock(pOperator, pInfo->pCtx, pBlock, order, scanFlag, false); blockDataEnsureCapacity(pInfo->pRes, pInfo->pRes->info.rows + pBlock->info.rows); - code = projectApplyFunctions(pOperator->pExpr, pInfo->pRes, pBlock, pInfo->pCtx, pOperator->numOfExprs, pProjectInfo->pPseudoColInfo); + code = projectApplyFunctions(pOperator->pExpr, pInfo->pRes, pBlock, pInfo->pCtx, pOperator->numOfExprs, + pProjectInfo->pPseudoColInfo); if (code != TSDB_CODE_SUCCESS) { longjmp(pTaskInfo->env, code); } @@ -4113,7 +4065,7 @@ int32_t doInitAggInfoSup(SAggSupporter* pAggSup, SqlFunctionCtx* pCtx, int32_t n defaultBufsz = defaultPgsz * 4; } - int32_t code = createDiskbasedBuf(&pAggSup->pResultBuf, defaultPgsz, defaultBufsz, pKey, "/tmp/"); + int32_t code = createDiskbasedBuf(&pAggSup->pResultBuf, defaultPgsz, defaultBufsz, pKey, TD_TMP_DIR_PATH); if (code != TSDB_CODE_SUCCESS) { return code; } @@ -4164,7 +4116,7 @@ static STableQueryInfo* initTableQueryInfo(const STableGroupInfo* pTableGroupInf for (int32_t i = 0; i < taosArrayGetSize(pTableGroupInfo->pGroupList); ++i) { SArray* pa = taosArrayGetP(pTableGroupInfo->pGroupList, i); for (int32_t j = 0; j < taosArrayGetSize(pa); ++j) { - STableKeyInfo* pk = taosArrayGet(pa, j); + STableKeyInfo* pk = taosArrayGet(pa, j); STableQueryInfo* pTQueryInfo = &pTableQueryInfo[index++]; pTQueryInfo->lastKey = pk->lastKey; } @@ -4203,7 +4155,7 @@ SOperatorInfo* createAggregateOperatorInfo(SOperatorInfo* downstream, SExprInfo* pInfo->pScalarExprInfo = pScalarExprInfo; pInfo->numOfScalarExpr = numOfScalarExpr; if (pInfo->pScalarExprInfo != NULL) { - pInfo->pScalarCtx = createSqlFunctionCtx(pScalarExprInfo, numOfCols, &pInfo->rowCellInfoOffset); + pInfo->pScalarCtx = createSqlFunctionCtx(pScalarExprInfo, numOfScalarExpr, &pInfo->rowCellInfoOffset); } pOperator->name = "TableAggregate"; @@ -4300,9 +4252,9 @@ SOperatorInfo* createProjectOperatorInfo(SOperatorInfo* downstream, SExprInfo* p goto _error; } - pInfo->limit = *pLimit; - pInfo->slimit = *pSlimit; - pInfo->curOffset = pLimit->offset; + pInfo->limit = *pLimit; + pInfo->slimit = *pSlimit; + pInfo->curOffset = pLimit->offset; pInfo->curSOffset = pSlimit->offset; pInfo->binfo.pRes = pResBlock; @@ -4311,11 +4263,17 @@ SOperatorInfo* createProjectOperatorInfo(SOperatorInfo* downstream, SExprInfo* p int32_t numOfRows = 4096; size_t keyBufSize = sizeof(int64_t) + sizeof(int64_t) + POINTER_BYTES; + // Make sure the size of SSDataBlock will never exceed the size of 2MB. + int32_t TWOMB = 2 * 1024 * 1024; + if (numOfRows * pResBlock->info.rowSize > TWOMB) { + numOfRows = TWOMB / pResBlock->info.rowSize; + } initResultSizeInfo(pOperator, numOfRows); + initAggInfo(&pInfo->binfo, &pInfo->aggSup, pExprInfo, numOfCols, pResBlock, keyBufSize, pTaskInfo->id.str); setFunctionResultOutput(&pInfo->binfo, &pInfo->aggSup, MAIN_SCAN, numOfCols, pTaskInfo); - pInfo->pPseudoColInfo = setRowTsColumnOutputInfo(pInfo->binfo.pCtx, numOfCols); + pInfo->pPseudoColInfo = setRowTsColumnOutputInfo(pInfo->binfo.pCtx, numOfCols); pOperator->name = "ProjectOperator"; pOperator->operatorType = QUERY_NODE_PHYSICAL_PLAN_PROJECT; pOperator->blocking = false; @@ -4323,11 +4281,11 @@ SOperatorInfo* createProjectOperatorInfo(SOperatorInfo* downstream, SExprInfo* p pOperator->info = pInfo; pOperator->pExpr = pExprInfo; pOperator->numOfExprs = num; + pOperator->pTaskInfo = pTaskInfo; pOperator->fpSet = createOperatorFpSet(operatorDummyOpenFn, doProjectOperation, NULL, NULL, destroyProjectOperatorInfo, NULL, NULL, NULL); - pOperator->pTaskInfo = pTaskInfo; int32_t code = appendDownstream(pOperator, &downstream, 1); if (code != TSDB_CODE_SUCCESS) { goto _error; @@ -4442,10 +4400,10 @@ static SColumn* createColumn(int32_t blockId, int32_t slotId, int32_t colId, SDa } pCol->slotId = slotId; - pCol->colId = colId; - pCol->bytes = pType->bytes; - pCol->type = pType->type; - pCol->scale = pType->scale; + pCol->colId = colId; + pCol->bytes = pType->bytes; + pCol->type = pType->type; + pCol->scale = pType->scale; pCol->precision = pType->precision; pCol->dataBlockId = blockId; @@ -4520,10 +4478,10 @@ SExprInfo* createExprInfo(SNodeList* pNodeList, SNodeList* pGroupKeys, int32_t* if (strcmp(pExp->pExpr->_function.functionName, "tbname") == 0) { pFuncNode->pParameterList = nodesMakeList(); ASSERT(LIST_LENGTH(pFuncNode->pParameterList) == 0); - SValueNode *res = (SValueNode *)nodesMakeNode(QUERY_NODE_VALUE); - if (NULL == res) { // todo handle error + SValueNode* res = (SValueNode*)nodesMakeNode(QUERY_NODE_VALUE); + if (NULL == res) { // todo handle error } else { - res->node.resType = (SDataType) {.bytes = sizeof(int64_t), .type = TSDB_DATA_TYPE_BIGINT}; + res->node.resType = (SDataType){.bytes = sizeof(int64_t), .type = TSDB_DATA_TYPE_BIGINT}; nodesListAppend(pFuncNode->pParameterList, res); } } @@ -4566,10 +4524,11 @@ SExprInfo* createExprInfo(SNodeList* pNodeList, SNodeList* pGroupKeys, int32_t* return pExprs; } -static SExecTaskInfo* createExecTaskInfo(uint64_t queryId, uint64_t taskId, EOPTR_EXEC_MODEL model) { +static SExecTaskInfo* createExecTaskInfo(uint64_t queryId, uint64_t taskId, EOPTR_EXEC_MODEL model, char* dbFName) { SExecTaskInfo* pTaskInfo = taosMemoryCalloc(1, sizeof(SExecTaskInfo)); setTaskStatus(pTaskInfo, TASK_NOT_COMPLETED); + pTaskInfo->schemaVer.dbname = strdup(dbFName); pTaskInfo->cost.created = taosGetTimestampMs(); pTaskInfo->id.queryId = queryId; pTaskInfo->execModel = model; @@ -4592,7 +4551,7 @@ static SArray* extractColumnInfo(SNodeList* pNodeList); static SArray* createSortInfo(SNodeList* pNodeList); static SArray* extractPartitionColInfo(SNodeList* pNodeList); -void extractTableSchemaVersion(SReadHandle *pHandle, uint64_t uid, SExecTaskInfo* pTaskInfo) { +void extractTableSchemaVersion(SReadHandle* pHandle, uint64_t uid, SExecTaskInfo* pTaskInfo) { SMetaReader mr = {0}; metaReaderInit(&mr, pHandle->meta, 0); metaGetTableEntryByUid(&mr, uid); @@ -4639,7 +4598,7 @@ SOperatorInfo* createOperatorTree(SPhysiNode* pPhyNode, SExecTaskInfo* pTaskInfo SSDataBlock* pResBlock = createResDataBlock(pExchange->node.pOutputDataBlockDesc); return createExchangeOperatorInfo(pHandle->pMsgCb->clientRpc, pExchange->pSrcEndPoints, pResBlock, pTaskInfo); } else if (QUERY_NODE_PHYSICAL_PLAN_STREAM_SCAN == type) { - SScanPhysiNode* pScanPhyNode = (SScanPhysiNode*)pPhyNode; // simple child table. + SScanPhysiNode* pScanPhyNode = (SScanPhysiNode*)pPhyNode; // simple child table. STableScanPhysiNode* pTableScanNode = (STableScanPhysiNode*)pPhyNode; int32_t numOfCols = 0; @@ -4648,8 +4607,7 @@ SOperatorInfo* createOperatorTree(SPhysiNode* pPhyNode, SExecTaskInfo* pTaskInfo if (pHandle->vnode) { pDataReader = doCreateDataReader(pTableScanNode, pHandle, pTableGroupInfo, (uint64_t)queryId, taskId); } else { - doCreateTableGroup(pHandle->meta, pScanPhyNode->tableType, pScanPhyNode->uid, pTableGroupInfo, - queryId, taskId); + doCreateTableGroup(pHandle->meta, pScanPhyNode->tableType, pScanPhyNode->uid, pTableGroupInfo, queryId, taskId); } if (pDataReader == NULL && terrno != 0) { @@ -4660,15 +4618,17 @@ SOperatorInfo* createOperatorTree(SPhysiNode* pPhyNode, SExecTaskInfo* pTaskInfo } SDataBlockDescNode* pDescNode = pScanPhyNode->node.pOutputDataBlockDesc; - SOperatorInfo* pOperatorDumy = createTableScanOperatorInfo(pTableScanNode, pDataReader, pHandle, pTaskInfo); + SOperatorInfo* pOperatorDumy = createTableScanOperatorInfo(pTableScanNode, pDataReader, pHandle, pTaskInfo); SArray* tableIdList = extractTableIdList(pTableGroupInfo); SSDataBlock* pResBlock = createResDataBlock(pDescNode); - SArray* pCols = extractColMatchInfo(pScanPhyNode->pScanCols, pDescNode, &numOfCols, pTaskInfo, COL_MATCH_FROM_COL_ID); + SArray* pCols = + extractColMatchInfo(pScanPhyNode->pScanCols, pDescNode, &numOfCols, pTaskInfo, COL_MATCH_FROM_COL_ID); - SOperatorInfo* pOperator = createStreamScanOperatorInfo(pHandle->reader, pDataReader, pHandle, pScanPhyNode->uid, pResBlock, pCols, tableIdList, pTaskInfo, - pScanPhyNode->node.pConditions, pOperatorDumy); + SOperatorInfo* pOperator = + createStreamScanOperatorInfo(pHandle->reader, pDataReader, pHandle, pScanPhyNode->uid, pResBlock, pCols, + tableIdList, pTaskInfo, pScanPhyNode->node.pConditions, pOperatorDumy); taosArrayDestroy(tableIdList); return pOperator; } else if (QUERY_NODE_PHYSICAL_PLAN_SYSTABLE_SCAN == type) { @@ -4680,7 +4640,8 @@ SOperatorInfo* createOperatorTree(SPhysiNode* pPhyNode, SExecTaskInfo* pTaskInfo SSDataBlock* pResBlock = createResDataBlock(pDescNode); int32_t numOfOutputCols = 0; - SArray* colList = extractColMatchInfo(pScanNode->pScanCols, pDescNode, &numOfOutputCols, pTaskInfo, COL_MATCH_FROM_COL_ID); + SArray* colList = + extractColMatchInfo(pScanNode->pScanCols, pDescNode, &numOfOutputCols, pTaskInfo, COL_MATCH_FROM_COL_ID); SOperatorInfo* pOperator = createSysTableScanOperatorInfo( pHandle, pResBlock, &pScanNode->tableName, pScanNode->node.pConditions, pSysScanPhyNode->mgmtEpSet, colList, pTaskInfo, pSysScanPhyNode->showRewrite, pSysScanPhyNode->accountId); @@ -4702,8 +4663,8 @@ SOperatorInfo* createOperatorTree(SPhysiNode* pPhyNode, SExecTaskInfo* pTaskInfo SExprInfo* pExprInfo = createExprInfo(pScanPhyNode->pScanPseudoCols, NULL, &num); int32_t numOfOutputCols = 0; - SArray* colList = - extractColMatchInfo(pScanPhyNode->pScanPseudoCols, pDescNode, &numOfOutputCols, pTaskInfo, COL_MATCH_FROM_COL_ID); + SArray* colList = extractColMatchInfo(pScanPhyNode->pScanPseudoCols, pDescNode, &numOfOutputCols, pTaskInfo, + COL_MATCH_FROM_COL_ID); SOperatorInfo* pOperator = createTagScanOperatorInfo(pHandle, pExprInfo, num, pResBlock, colList, pTableGroupInfo, pTaskInfo); @@ -4785,7 +4746,8 @@ SOperatorInfo* createOperatorTree(SPhysiNode* pPhyNode, SExecTaskInfo* pTaskInfo SExprInfo* pExprInfo = createExprInfo(pSortPhyNode->pExprs, NULL, &numOfCols); int32_t numOfOutputCols = 0; - SArray* pColList = extractColMatchInfo(pSortPhyNode->pTargets, pDescNode, &numOfOutputCols, pTaskInfo, COL_MATCH_FROM_SLOT_ID); + SArray* pColList = + extractColMatchInfo(pSortPhyNode->pTargets, pDescNode, &numOfOutputCols, pTaskInfo, COL_MATCH_FROM_SLOT_ID); pOptr = createSortOperatorInfo(ops[0], pResBlock, info, pExprInfo, numOfCols, pColList, pTaskInfo); } else if (QUERY_NODE_PHYSICAL_PLAN_SESSION_WINDOW == type) { @@ -4817,7 +4779,7 @@ SOperatorInfo* createOperatorTree(SPhysiNode* pPhyNode, SExecTaskInfo* pTaskInfo int32_t tsSlotId = ((SColumnNode*)pStateNode->window.pTspk)->slotId; SColumnNode* pColNode = (SColumnNode*)((STargetNode*)pStateNode->pStateKey)->pExpr; - SColumn col = extractColumnFromColumnNode(pColNode); + SColumn col = extractColumnFromColumnNode(pColNode); pOptr = createStatewindowOperatorInfo(ops[0], pExprInfo, num, pResBlock, &as, tsSlotId, &col, pTaskInfo); } else if (QUERY_NODE_PHYSICAL_PLAN_JOIN == type) { SJoinPhysiNode* pJoinNode = (SJoinPhysiNode*)pPhyNode; @@ -4885,11 +4847,11 @@ int32_t initQueryTableDataCond(SQueryTableDataCond* pCond, const STableScanPhysi SColumn extractColumnFromColumnNode(SColumnNode* pColNode) { SColumn c = {0}; - c.slotId = pColNode->slotId; - c.colId = pColNode->colId; - c.type = pColNode->node.resType.type; - c.bytes = pColNode->node.resType.bytes; - c.scale = pColNode->node.resType.scale; + c.slotId = pColNode->slotId; + c.colId = pColNode->colId; + c.type = pColNode->node.resType.type; + c.bytes = pColNode->node.resType.bytes; + c.scale = pColNode->node.resType.scale; c.precision = pColNode->node.resType.precision; return c; } @@ -4984,16 +4946,10 @@ SArray* extractColMatchInfo(SNodeList* pNodeList, SDataBlockDescNode* pOutputNod return NULL; } - const char* tname = pTaskInfo->schemaVer.tablename; for (int32_t i = 0; i < numOfCols; ++i) { STargetNode* pNode = (STargetNode*)nodesListGetNode(pNodeList, i); SColumnNode* pColNode = (SColumnNode*)pNode->pExpr; - if (tname != NULL && (pTaskInfo->schemaVer.dbname == NULL) && - strncmp(pColNode->tableName, tname, tListLen(pColNode->tableName)) == 0) { - pTaskInfo->schemaVer.dbname = strdup(pColNode->dbName); - } - SColMatchInfo c = {0}; c.output = true; c.colId = pColNode->colId; @@ -5089,7 +5045,7 @@ int32_t createExecTaskInfoImpl(SSubplan* pPlan, SExecTaskInfo** pTaskInfo, SRead uint64_t queryId = pPlan->id.queryId; int32_t code = TSDB_CODE_SUCCESS; - *pTaskInfo = createExecTaskInfo(queryId, taskId, model); + *pTaskInfo = createExecTaskInfo(queryId, taskId, model, pPlan->dbFName); if (*pTaskInfo == NULL) { code = TSDB_CODE_QRY_OUT_OF_MEMORY; goto _complete; @@ -5292,15 +5248,15 @@ int32_t getOperatorExplainExecInfo(SOperatorInfo* operatorInfo, SExplainExecInfo return TSDB_CODE_SUCCESS; } -int32_t initCatchSupporter(SCatchSupporter* pCatchSup, size_t rowSize, size_t keyBufSize, - const char* pKey, const char* pDir) { +int32_t initCatchSupporter(SCatchSupporter* pCatchSup, size_t rowSize, size_t keyBufSize, const char* pKey, + const char* pDir) { pCatchSup->keySize = sizeof(int64_t) + sizeof(int64_t) + sizeof(TSKEY); pCatchSup->pKeyBuf = taosMemoryCalloc(1, pCatchSup->keySize); int32_t pageSize = rowSize * 32; int32_t bufSize = pageSize * 4096; createDiskbasedBuf(&pCatchSup->pDataBuf, pageSize, bufSize, pKey, pDir); _hash_fn_t hashFn = taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY); - pCatchSup->pWindowHashTable = taosHashInit(10000, hashFn, true, HASH_NO_LOCK);; + pCatchSup->pWindowHashTable = taosHashInit(10000, hashFn, true, HASH_NO_LOCK); + ; return TSDB_CODE_SUCCESS; } - diff --git a/source/libs/executor/src/groupoperator.c b/source/libs/executor/src/groupoperator.c index b1e19213dd6ef57a6a687348a6443fc0b60704c1..484d439dea47e83e99edcbe02dd543399f17f9de 100644 --- a/source/libs/executor/src/groupoperator.c +++ b/source/libs/executor/src/groupoperator.c @@ -269,7 +269,7 @@ static SSDataBlock* hashGroupbyAggregate(SOperatorInfo* pOperator) { if (pOperator->status == OP_RES_TO_RETURN) { doBuildResultDatablock(pOperator, &pInfo->binfo, &pInfo->groupResInfo, pInfo->aggSup.pResultBuf); - if (pRes->info.rows == 0 || !hasRemainDataInCurrentGroup(&pInfo->groupResInfo)) { + if (pRes->info.rows == 0 || !hashRemainDataInGroupInfo(&pInfo->groupResInfo)) { pOperator->status = OP_EXEC_DONE; } return (pRes->info.rows == 0)? NULL:pRes; @@ -303,9 +303,6 @@ static SSDataBlock* hashGroupbyAggregate(SOperatorInfo* pOperator) { pOperator->status = OP_RES_TO_RETURN; closeAllResultRows(&pInfo->binfo.resultRowInfo); - - finalizeMultiTupleQueryResult(pOperator->numOfExprs, pInfo->aggSup.pResultBuf, &pInfo->binfo.resultRowInfo, - pInfo->binfo.rowCellInfoOffset); // if (!stableQuery) { // finalize include the update of result rows // finalizeQueryResult(pInfo->binfo.pCtx, pOperator->numOfExprs); // } else { @@ -320,7 +317,7 @@ static SSDataBlock* hashGroupbyAggregate(SOperatorInfo* pOperator) { doBuildResultDatablock(pOperator, &pInfo->binfo, &pInfo->groupResInfo, pInfo->aggSup.pResultBuf); doFilter(pInfo->pCondition, pRes, NULL); - bool hasRemain = hasRemainDataInCurrentGroup(&pInfo->groupResInfo); + bool hasRemain = hashRemainDataInGroupInfo(&pInfo->groupResInfo); if (!hasRemain) { pOperator->status = OP_EXEC_DONE; break; @@ -556,7 +553,7 @@ static SSDataBlock* buildPartitionResult(SOperatorInfo* pOperator) { pInfo->pageIndex += 1; - blockDataUpdateTsWindow(pInfo->binfo.pRes); + blockDataUpdateTsWindow(pInfo->binfo.pRes, 0); pInfo->binfo.pRes->info.groupId = pGroupInfo->groupId; return pInfo->binfo.pRes; } @@ -617,7 +614,7 @@ SOperatorInfo* createPartitionOperatorInfo(SOperatorInfo* downstream, SExprInfo* goto _error; } - int32_t code = createDiskbasedBuf(&pInfo->pBuf, 4096, 4096 * 256, pTaskInfo->id.str, "/tmp/"); + int32_t code = createDiskbasedBuf(&pInfo->pBuf, 4096, 4096 * 256, pTaskInfo->id.str, TD_TMP_DIR_PATH); if (code != TSDB_CODE_SUCCESS) { goto _error; } diff --git a/source/libs/executor/src/scanoperator.c b/source/libs/executor/src/scanoperator.c index f5122a26efcd5103a483174ed3576466e44e536c..d0eb0ae8dad4a182413af4818bd0ece474e9d238 100644 --- a/source/libs/executor/src/scanoperator.c +++ b/source/libs/executor/src/scanoperator.c @@ -300,10 +300,26 @@ void addTagPseudoColumnData(STableScanInfo* pTableScanInfo, SSDataBlock* pBlock) if (fmIsScanPseudoColumnFunc(functionId)) { setTbNameColData(pTableScanInfo->readHandle.meta, pBlock, pColInfoData, functionId); } else { // these are tags - const char* p = metaGetTableTagVal(&mr.me, pExpr->base.pParam[0].pCol->colId); + const char* p = NULL; + if(pColInfoData->info.type == TSDB_DATA_TYPE_JSON){ + const uint8_t *tmp = mr.me.ctbEntry.pTags; + char *data = taosMemoryCalloc(kvRowLen(tmp) + 1, 1); + if(data == NULL){ + qError("doTagScan calloc error:%d", kvRowLen(tmp) + 1); + return; + } + *data = TSDB_DATA_TYPE_JSON; + memcpy(data+1, tmp, kvRowLen(tmp)); + p = data; + }else{ + p = metaGetTableTagVal(&mr.me, pExpr->base.pParam[0].pCol->colId); + } for (int32_t i = 0; i < pBlock->info.rows; ++i) { colDataAppend(pColInfoData, i, p, (p == NULL)); } + if(pColInfoData->info.type == TSDB_DATA_TYPE_JSON){ + taosMemoryFree((void*)p); + } } } @@ -673,7 +689,7 @@ static SSDataBlock* getUpdateDataBlock(SStreamBlockScanInfo* pInfo, bool inverti } pDataBlock->info.rows = size; pDataBlock->info.type = STREAM_REPROCESS; - blockDataUpdateTsWindow(pDataBlock); + blockDataUpdateTsWindow(pDataBlock, 0); taosArrayClear(pInfo->tsArray); return pDataBlock; } @@ -883,7 +899,7 @@ static SSDataBlock* doStreamBlockScan(SOperatorInfo* pOperator) { } rows = pBlockInfo->rows; doFilter(pInfo->pCondition, pInfo->pRes, NULL); - blockDataUpdateTsWindow(pInfo->pRes); + blockDataUpdateTsWindow(pInfo->pRes, 0); break; } @@ -956,9 +972,10 @@ SOperatorInfo* createStreamScanOperatorInfo(void* streamReadHandle, void* pDataR } pInfo->primaryTsIndex = 0; // TODO(liuyao) get it from physical plan - pInfo->pUpdateInfo = updateInfoInitP(&pSTInfo->interval, 10000); // TODO(liuyao) get watermark from physical plan - if (pInfo->pUpdateInfo == NULL) { - goto _error; + if (pSTInfo->interval.interval > 0) { + pInfo->pUpdateInfo = updateInfoInitP(&pSTInfo->interval, 10000); // TODO(liuyao) get watermark from physical plan + } else { + pInfo->pUpdateInfo = NULL; } pInfo->readHandle = *pHandle; @@ -973,7 +990,7 @@ SOperatorInfo* createStreamScanOperatorInfo(void* streamReadHandle, void* pDataR size_t childKeyBufSize = sizeof(int64_t) + sizeof(int64_t) + sizeof(TSKEY); initCatchSupporter(&pInfo->childAggSup, 1024, childKeyBufSize, - "StreamFinalInterval", "/tmp/"); // TODO(liuyao) get row size from phy plan + "StreamFinalInterval", TD_TMP_DIR_PATH); // TODO(liuyao) get row size from phy plan pOperator->name = "StreamBlockScanOperator"; pOperator->operatorType = QUERY_NODE_PHYSICAL_PLAN_STREAM_SCAN; @@ -1587,8 +1604,21 @@ static SSDataBlock* doTagScan(SOperatorInfo* pOperator) { STR_TO_VARSTR(str, mr.me.name); colDataAppend(pDst, count, str, false); } else { // it is a tag value - const char* p = metaGetTableTagVal(&mr.me, pExprInfo[j].base.pParam[0].pCol->colId); - colDataAppend(pDst, count, p, (p == NULL)); + if(pDst->info.type == TSDB_DATA_TYPE_JSON){ + const uint8_t *tmp = mr.me.ctbEntry.pTags; + char *data = taosMemoryCalloc(kvRowLen(tmp) + 1, 1); + if(data == NULL){ + qError("doTagScan calloc error:%d", kvRowLen(tmp) + 1); + return NULL; + } + *data = TSDB_DATA_TYPE_JSON; + memcpy(data+1, tmp, kvRowLen(tmp)); + colDataAppend(pDst, count, data, false); + taosMemoryFree(data); + }else{ + const char* p = metaGetTableTagVal(&mr.me, pExprInfo[j].base.pParam[0].pCol->colId); + colDataAppend(pDst, count, p, (p == NULL)); + } } } diff --git a/source/libs/executor/src/timewindowoperator.c b/source/libs/executor/src/timewindowoperator.c index 3be131e550bbe9e3c8f025ef90dee857421bf5f9..9e073ec05bdc31c392b91bfa13d857e3a65465f4 100644 --- a/source/libs/executor/src/timewindowoperator.c +++ b/source/libs/executor/src/timewindowoperator.c @@ -621,7 +621,7 @@ static void saveDataBlockLastRow(char** pRow, SArray* pDataBlock, int32_t rowInd } } -static SArray* hashIntervalAgg(SOperatorInfo* pOperatorInfo, SResultRowInfo* pResultRowInfo, SSDataBlock* pSDataBlock, +static SArray* hashIntervalAgg(SOperatorInfo* pOperatorInfo, SResultRowInfo* pResultRowInfo, SSDataBlock* pBlock, uint64_t tableGroupId) { SIntervalAggOperatorInfo* pInfo = (SIntervalAggOperatorInfo*)pOperatorInfo->info; @@ -639,13 +639,17 @@ static SArray* hashIntervalAgg(SOperatorInfo* pOperatorInfo, SResultRowInfo* pRe // int32_t prevIndex = pResultRowInfo->curPos; TSKEY* tsCols = NULL; - if (pSDataBlock->pDataBlock != NULL) { - SColumnInfoData* pColDataInfo = taosArrayGet(pSDataBlock->pDataBlock, pInfo->primaryTsIndex); + if (pBlock->pDataBlock != NULL) { + SColumnInfoData* pColDataInfo = taosArrayGet(pBlock->pDataBlock, pInfo->primaryTsIndex); tsCols = (int64_t*)pColDataInfo->pData; + + if (tsCols != NULL) { + blockDataUpdateTsWindow(pBlock, pInfo->primaryTsIndex); + } } int32_t startPos = 0; - TSKEY ts = getStartTsKey(&pSDataBlock->info.window, tsCols, pSDataBlock->info.rows, ascScan); + TSKEY ts = getStartTsKey(&pBlock->info.window, tsCols, pBlock->info.rows, ascScan); STimeWindow win = getActiveTimeWindow(pInfo->aggSup.pResultBuf, pResultRowInfo, ts, &pInfo->interval, pInfo->interval.precision, &pInfo->win); @@ -670,7 +674,7 @@ static SArray* hashIntervalAgg(SOperatorInfo* pOperatorInfo, SResultRowInfo* pRe int32_t forwardStep = 0; TSKEY ekey = ascScan? win.ekey:win.skey; forwardStep = - getNumOfRowsInTimeWindow(&pSDataBlock->info, tsCols, startPos, ekey, binarySearchForKey, NULL, pInfo->order); + getNumOfRowsInTimeWindow(&pBlock->info, tsCols, startPos, ekey, binarySearchForKey, NULL, pInfo->order); ASSERT(forwardStep > 0); // prev time window not interpolation yet. @@ -686,7 +690,7 @@ static SArray* hashIntervalAgg(SOperatorInfo* pOperatorInfo, SResultRowInfo* pRe } STimeWindow w = pRes->win; - ret = setTimeWindowOutputBuf(pResultRowInfo, pSDataBlock->info.uid, &w, masterScan, &pResult, tableGroupId, + ret = setTimeWindowOutputBuf(pResultRowInfo, pBlock->info.uid, &w, masterScan, &pResult, tableGroupId, pInfo->binfo.pCtx, numOfOutput, pInfo->binfo.rowCellInfoOffset, &pInfo->aggSup, pTaskInfo); if (ret != TSDB_CODE_SUCCESS) { @@ -694,17 +698,17 @@ static SArray* hashIntervalAgg(SOperatorInfo* pOperatorInfo, SResultRowInfo* pRe } assert(!resultRowInterpolated(pResult, RESULT_ROW_END_INTERP)); - doTimeWindowInterpolation(pOperatorInfo, &pInfo->binfo, pSDataBlock->pDataBlock, *(TSKEY*)pInfo->pRow[0], -1, + doTimeWindowInterpolation(pOperatorInfo, &pInfo->binfo, pBlock->pDataBlock, *(TSKEY*)pInfo->pRow[0], -1, tsCols[startPos], startPos, w.ekey, RESULT_ROW_END_INTERP); setResultRowInterpo(pResult, RESULT_ROW_END_INTERP); setNotInterpoWindowKey(pInfo->binfo.pCtx, pOperatorInfo->numOfExprs, RESULT_ROW_START_INTERP); - doApplyFunctions(pInfo->binfo.pCtx, &w, &pInfo->timeWindowData, startPos, 0, tsCols, pSDataBlock->info.rows, numOfOutput, TSDB_ORDER_ASC); + doApplyFunctions(pInfo->binfo.pCtx, &w, &pInfo->timeWindowData, startPos, 0, tsCols, pBlock->info.rows, numOfOutput, TSDB_ORDER_ASC); } // restore current time window - ret = setTimeWindowOutputBuf(pResultRowInfo, pSDataBlock->info.uid, &win, masterScan, &pResult, tableGroupId, + ret = setTimeWindowOutputBuf(pResultRowInfo, pBlock->info.uid, &win, masterScan, &pResult, tableGroupId, pInfo->binfo.pCtx, numOfOutput, pInfo->binfo.rowCellInfoOffset, &pInfo->aggSup, pTaskInfo); if (ret != TSDB_CODE_SUCCESS) { @@ -714,17 +718,17 @@ static SArray* hashIntervalAgg(SOperatorInfo* pOperatorInfo, SResultRowInfo* pRe #endif // window start key interpolation - doWindowBorderInterpolation(pOperatorInfo, pSDataBlock, pInfo->binfo.pCtx, pResult, &win, startPos, forwardStep, + doWindowBorderInterpolation(pOperatorInfo, pBlock, pInfo->binfo.pCtx, pResult, &win, startPos, forwardStep, pInfo->order, false); updateTimeWindowInfo(&pInfo->twAggSup.timeWindowData, &win, true); doApplyFunctions(pTaskInfo, pInfo->binfo.pCtx, &win, &pInfo->twAggSup.timeWindowData, startPos, forwardStep, tsCols, - pSDataBlock->info.rows, numOfOutput, TSDB_ORDER_ASC); + pBlock->info.rows, numOfOutput, TSDB_ORDER_ASC); STimeWindow nextWin = win; while (1) { int32_t prevEndPos = (forwardStep - 1) * step + startPos; - startPos = getNextQualifiedWindow(&pInfo->interval, &nextWin, &pSDataBlock->info, tsCols, prevEndPos, pInfo->order); + startPos = getNextQualifiedWindow(&pInfo->interval, &nextWin, &pBlock->info, tsCols, prevEndPos, pInfo->order); if (startPos < 0) { break; } @@ -748,20 +752,20 @@ static SArray* hashIntervalAgg(SOperatorInfo* pOperatorInfo, SResultRowInfo* pRe ekey = ascScan? nextWin.ekey:nextWin.skey; forwardStep = - getNumOfRowsInTimeWindow(&pSDataBlock->info, tsCols, startPos, ekey, binarySearchForKey, NULL, pInfo->order); + getNumOfRowsInTimeWindow(&pBlock->info, tsCols, startPos, ekey, binarySearchForKey, NULL, pInfo->order); // window start(end) key interpolation - doWindowBorderInterpolation(pOperatorInfo, pSDataBlock, pInfo->binfo.pCtx, pResult, &nextWin, startPos, forwardStep, + doWindowBorderInterpolation(pOperatorInfo, pBlock, pInfo->binfo.pCtx, pResult, &nextWin, startPos, forwardStep, pInfo->order, false); updateTimeWindowInfo(&pInfo->twAggSup.timeWindowData, &nextWin, true); doApplyFunctions(pTaskInfo, pInfo->binfo.pCtx, &nextWin, &pInfo->twAggSup.timeWindowData, startPos, forwardStep, tsCols, - pSDataBlock->info.rows, numOfOutput, TSDB_ORDER_ASC); + pBlock->info.rows, numOfOutput, TSDB_ORDER_ASC); } if (pInfo->timeWindowInterpo) { - int32_t rowIndex = ascScan ? (pSDataBlock->info.rows - 1) : 0; - saveDataBlockLastRow(pInfo->pRow, pSDataBlock->pDataBlock, rowIndex, pSDataBlock->info.numOfCols); + int32_t rowIndex = ascScan ? (pBlock->info.rows - 1) : 0; + saveDataBlockLastRow(pInfo->pRow, pBlock->pDataBlock, rowIndex, pBlock->info.numOfCols); } return pUpdated; @@ -815,9 +819,6 @@ static int32_t doOpenIntervalAgg(SOperatorInfo* pOperator) { } closeAllResultRows(&pInfo->binfo.resultRowInfo); - finalizeMultiTupleQueryResult(pOperator->numOfExprs, pInfo->aggSup.pResultBuf, &pInfo->binfo.resultRowInfo, - pInfo->binfo.rowCellInfoOffset); - initGroupedResultInfo(&pInfo->groupResInfo, pInfo->aggSup.pResultRowHashTable, pInfo->order); OPTR_SET_OPENED(pOperator); return TSDB_CODE_SUCCESS; @@ -933,7 +934,7 @@ static SSDataBlock* doStateWindowAgg(SOperatorInfo* pOperator) { if (pOperator->status == OP_RES_TO_RETURN) { doBuildResultDatablock(pOperator, pBInfo, &pInfo->groupResInfo, pInfo->aggSup.pResultBuf); - if (pBInfo->pRes->info.rows == 0 || !hasRemainDataInCurrentGroup(&pInfo->groupResInfo)) { + if (pBInfo->pRes->info.rows == 0 || !hashRemainDataInGroupInfo(&pInfo->groupResInfo)) { doSetOperatorCompleted(pOperator); return NULL; } @@ -941,8 +942,7 @@ static SSDataBlock* doStateWindowAgg(SOperatorInfo* pOperator) { return pBInfo->pRes; } - int32_t order = TSDB_ORDER_ASC; - STimeWindow win = pTaskInfo->window; + int32_t order = TSDB_ORDER_ASC; SOperatorInfo* downstream = pOperator->pDownstream[0]; while (1) { @@ -955,18 +955,18 @@ static SSDataBlock* doStateWindowAgg(SOperatorInfo* pOperator) { } setInputDataBlock(pOperator, pBInfo->pCtx, pBlock, order, MAIN_SCAN, true); + blockDataUpdateTsWindow(pBlock, pInfo->tsSlotId); + doStateWindowAggImpl(pOperator, pInfo, pBlock); } pOperator->status = OP_RES_TO_RETURN; closeAllResultRows(&pBInfo->resultRowInfo); - finalizeMultiTupleQueryResult(pOperator->numOfExprs, pInfo->aggSup.pResultBuf, &pBInfo->resultRowInfo, - pBInfo->rowCellInfoOffset); initGroupedResultInfo(&pInfo->groupResInfo, pInfo->aggSup.pResultRowHashTable, TSDB_ORDER_ASC); blockDataEnsureCapacity(pBInfo->pRes, pOperator->resultInfo.capacity); doBuildResultDatablock(pOperator, pBInfo, &pInfo->groupResInfo, pInfo->aggSup.pResultBuf); - if (pBInfo->pRes->info.rows == 0 || !hasRemainDataInCurrentGroup(&pInfo->groupResInfo)) { + if (pBInfo->pRes->info.rows == 0 || !hashRemainDataInGroupInfo(&pInfo->groupResInfo)) { doSetOperatorCompleted(pOperator); } @@ -994,7 +994,7 @@ static SSDataBlock* doBuildIntervalResult(SOperatorInfo* pOperator) { blockDataEnsureCapacity(pBlock, pOperator->resultInfo.capacity); doBuildResultDatablock(pOperator, &pInfo->binfo, &pInfo->groupResInfo, pInfo->aggSup.pResultBuf); - if (pBlock->info.rows == 0 || !hasRemainDataInCurrentGroup(&pInfo->groupResInfo)) { + if (pBlock->info.rows == 0 || !hashRemainDataInGroupInfo(&pInfo->groupResInfo)) { doSetOperatorCompleted(pOperator); } @@ -1082,7 +1082,7 @@ static SSDataBlock* doStreamIntervalAgg(SOperatorInfo* pOperator) { if (pOperator->status == OP_RES_TO_RETURN) { doBuildResultDatablock(pOperator, &pInfo->binfo, &pInfo->groupResInfo, pInfo->aggSup.pResultBuf); - if (pInfo->binfo.pRes->info.rows == 0 || !hasRemainDataInCurrentGroup(&pInfo->groupResInfo)) { + if (pInfo->binfo.pRes->info.rows == 0 || !hashRemainDataInGroupInfo(&pInfo->groupResInfo)) { pOperator->status = OP_EXEC_DONE; } return pInfo->binfo.pRes->info.rows == 0 ? NULL : pInfo->binfo.pRes; @@ -1181,6 +1181,7 @@ SOperatorInfo* createIntervalOperatorInfo(SOperatorInfo* downstream, SExprInfo* initExecTimeWindowInfo(&pInfo->twAggSup.timeWindowData, &pInfo->win); pInfo->invertible = allInvertible(pInfo->binfo.pCtx, numOfCols); + pInfo->invertible = false; // Todo(liuyao): Dependent TSDB API // pInfo->pTableQueryInfo = initTableQueryInfo(pTableGroupInfo); if (code != TSDB_CODE_SUCCESS /* || pInfo->pTableQueryInfo == NULL*/) { @@ -1413,7 +1414,7 @@ static SSDataBlock* doSessionWindowAgg(SOperatorInfo* pOperator) { if (pOperator->status == OP_RES_TO_RETURN) { doBuildResultDatablock(pOperator, pBInfo, &pInfo->groupResInfo, pInfo->aggSup.pResultBuf); - if (pBInfo->pRes->info.rows == 0 || !hasRemainDataInCurrentGroup(&pInfo->groupResInfo)) { + if (pBInfo->pRes->info.rows == 0 || !hashRemainDataInGroupInfo(&pInfo->groupResInfo)) { doSetOperatorCompleted(pOperator); return NULL; } @@ -1434,19 +1435,19 @@ static SSDataBlock* doSessionWindowAgg(SOperatorInfo* pOperator) { // the pDataBlock are always the same one, no need to call this again setInputDataBlock(pOperator, pBInfo->pCtx, pBlock, order, MAIN_SCAN, true); + blockDataUpdateTsWindow(pBlock, pInfo->tsSlotId); + doSessionWindowAggImpl(pOperator, pInfo, pBlock); } // restore the value pOperator->status = OP_RES_TO_RETURN; closeAllResultRows(&pBInfo->resultRowInfo); - finalizeMultiTupleQueryResult(pOperator->numOfExprs, pInfo->aggSup.pResultBuf, &pBInfo->resultRowInfo, - pBInfo->rowCellInfoOffset); initGroupedResultInfo(&pInfo->groupResInfo, pInfo->aggSup.pResultRowHashTable, TSDB_ORDER_ASC); blockDataEnsureCapacity(pBInfo->pRes, pOperator->resultInfo.capacity); doBuildResultDatablock(pOperator, pBInfo, &pInfo->groupResInfo, pInfo->aggSup.pResultBuf); - if (pBInfo->pRes->info.rows == 0 || !hasRemainDataInCurrentGroup(&pInfo->groupResInfo)) { + if (pBInfo->pRes->info.rows == 0 || !hashRemainDataInGroupInfo(&pInfo->groupResInfo)) { doSetOperatorCompleted(pOperator); } @@ -1461,7 +1462,7 @@ static SSDataBlock* doAllIntervalAgg(SOperatorInfo* pOperator) { STimeSliceOperatorInfo* pSliceInfo = pOperator->info; if (pOperator->status == OP_RES_TO_RETURN) { // doBuildResultDatablock(&pRuntimeEnv->groupResInfo, pRuntimeEnv, pIntervalInfo->pRes); - if (pSliceInfo->binfo.pRes->info.rows == 0 || !hasRemainDataInCurrentGroup(&pSliceInfo->groupResInfo)) { + if (pSliceInfo->binfo.pRes->info.rows == 0 || !hashRemainDataInGroupInfo(&pSliceInfo->groupResInfo)) { doSetOperatorCompleted(pOperator); } @@ -1493,7 +1494,7 @@ static SSDataBlock* doAllIntervalAgg(SOperatorInfo* pOperator) { // initGroupedResultInfo(&pSliceInfo->groupResInfo, &pSliceInfo->binfo.resultRowInfo); // doBuildResultDatablock(&pRuntimeEnv->groupResInfo, pRuntimeEnv, pSliceInfo->pRes); - if (pSliceInfo->binfo.pRes->info.rows == 0 || !hasRemainDataInCurrentGroup(&pSliceInfo->groupResInfo)) { + if (pSliceInfo->binfo.pRes->info.rows == 0 || !hashRemainDataInGroupInfo(&pSliceInfo->groupResInfo)) { pOperator->status = OP_EXEC_DONE; } @@ -1695,7 +1696,7 @@ static SSDataBlock* doStreamFinalIntervalAgg(SOperatorInfo* pOperator) { return NULL; } else if (pOperator->status == OP_RES_TO_RETURN) { doBuildResultDatablock(pOperator, &pInfo->binfo, &pInfo->groupResInfo, pInfo->aggSup.pResultBuf); - if (pInfo->binfo.pRes->info.rows == 0 || !hasRemainDataInCurrentGroup(&pInfo->groupResInfo)) { + if (pInfo->binfo.pRes->info.rows == 0 || !hashRemainDataInGroupInfo(&pInfo->groupResInfo)) { pOperator->status = OP_EXEC_DONE; } return pInfo->binfo.pRes->info.rows == 0 ? NULL : pInfo->binfo.pRes; diff --git a/source/libs/executor/src/tlinearhash.c b/source/libs/executor/src/tlinearhash.c index 21fd54b620574272aad02315a3f0d74ae05405a8..00a9f3ae6c8ff087a9031c7c0d70e81bb3c88504 100644 --- a/source/libs/executor/src/tlinearhash.c +++ b/source/libs/executor/src/tlinearhash.c @@ -247,7 +247,7 @@ SLHashObj* tHashInit(int32_t inMemPages, int32_t pageSize, _hash_fn_t fn, int32_ return NULL; } - int32_t code = createDiskbasedBuf(&pHashObj->pBuf, pageSize, inMemPages * pageSize, 0, "/tmp"); + int32_t code = createDiskbasedBuf(&pHashObj->pBuf, pageSize, inMemPages * pageSize, 0, TD_TMP_DIR_PATH); if (code != 0) { terrno = code; return NULL; diff --git a/source/libs/executor/src/tsort.c b/source/libs/executor/src/tsort.c index d585988e5e6c9802ee9864d89b956ecfbb2cdf33..c826cb68bfaa5053d6c6cf09aa409f4518df9dfc 100644 --- a/source/libs/executor/src/tsort.c +++ b/source/libs/executor/src/tsort.c @@ -155,7 +155,7 @@ static int32_t doAddToBuf(SSDataBlock* pDataBlock, SSortHandle* pHandle) { int32_t start = 0; if (pHandle->pBuf == NULL) { - int32_t code = createDiskbasedBuf(&pHandle->pBuf, pHandle->pageSize, pHandle->numOfPages * pHandle->pageSize, "doAddToBuf", "/tmp"); + int32_t code = createDiskbasedBuf(&pHandle->pBuf, pHandle->pageSize, pHandle->numOfPages * pHandle->pageSize, "doAddToBuf", TD_TMP_DIR_PATH); dBufSetPrintInfo(pHandle->pBuf); if (code != TSDB_CODE_SUCCESS) { return code; @@ -217,7 +217,7 @@ static int32_t sortComparInit(SMsortComparParam* cmpParam, SArray* pSources, int } else { // multi-pass internal merge sort is required if (pHandle->pBuf == NULL) { - code = createDiskbasedBuf(&pHandle->pBuf, pHandle->pageSize, pHandle->numOfPages * pHandle->pageSize, "sortComparInit", "/tmp"); + code = createDiskbasedBuf(&pHandle->pBuf, pHandle->pageSize, pHandle->numOfPages * pHandle->pageSize, "sortComparInit", TD_TMP_DIR_PATH); dBufSetPrintInfo(pHandle->pBuf); if (code != TSDB_CODE_SUCCESS) { return code; diff --git a/source/libs/executor/test/executorTests.cpp b/source/libs/executor/test/executorTests.cpp index c7ec0eece209a6d584ce846802abede47a9b8e81..880ac6c78eb8e04c08de7b612cdc58b50025e2c9 100644 --- a/source/libs/executor/test/executorTests.cpp +++ b/source/libs/executor/test/executorTests.cpp @@ -189,7 +189,7 @@ SSDataBlock* get2ColsDummyBlock(SOperatorInfo* pOperator) { pInfo->current += 1; - blockDataUpdateTsWindow(pBlock); + blockDataUpdateTsWindow(pBlock, 0); return pBlock; } diff --git a/source/libs/function/inc/builtinsimpl.h b/source/libs/function/inc/builtinsimpl.h index a20d0e471828c7dc19c508325bed30608c693a13..3e2ccbc6b8fd86926f576eee274efa233a6ed95c 100644 --- a/source/libs/function/inc/builtinsimpl.h +++ b/source/libs/function/inc/builtinsimpl.h @@ -120,7 +120,13 @@ int32_t sampleFunction(SqlFunctionCtx* pCtx); bool getTailFuncEnv(struct SFunctionNode* pFunc, SFuncExecEnv* pEnv); bool tailFunctionSetup(SqlFunctionCtx *pCtx, SResultRowEntryInfo* pResultInfo); int32_t tailFunction(SqlFunctionCtx* pCtx); -int32_t tailFinalize(SqlFunctionCtx* pCtx, SSDataBlock* pBlock); +//int32_t tailFinalize(SqlFunctionCtx* pCtx, SSDataBlock* pBlock); + +bool getUniqueFuncEnv(struct SFunctionNode* pFunc, SFuncExecEnv* pEnv); +bool uniqueFunctionSetup(SqlFunctionCtx *pCtx, SResultRowEntryInfo* pResultInfo); +int32_t uniqueFunction(SqlFunctionCtx *pCtx); +//int32_t uniqueFinalize(SqlFunctionCtx* pCtx, SSDataBlock* pBlock); + bool getSelectivityFuncEnv(SFunctionNode* pFunc, SFuncExecEnv* pEnv); diff --git a/source/libs/function/inc/functionMgtInt.h b/source/libs/function/inc/functionMgtInt.h index 4d45eb91cebced879e75ce23368b7a650d119f2e..21d277665872fc520ecea0fe6157b8338789499b 100644 --- a/source/libs/function/inc/functionMgtInt.h +++ b/source/libs/function/inc/functionMgtInt.h @@ -28,7 +28,7 @@ extern "C" { #define FUNC_MGT_AGG_FUNC FUNC_MGT_FUNC_CLASSIFICATION_MASK(0) #define FUNC_MGT_SCALAR_FUNC FUNC_MGT_FUNC_CLASSIFICATION_MASK(1) -#define FUNC_MGT_NONSTANDARD_SQL_FUNC FUNC_MGT_FUNC_CLASSIFICATION_MASK(2) +#define FUNC_MGT_INDEFINITE_ROWS_FUNC FUNC_MGT_FUNC_CLASSIFICATION_MASK(2) #define FUNC_MGT_STRING_FUNC FUNC_MGT_FUNC_CLASSIFICATION_MASK(3) #define FUNC_MGT_DATETIME_FUNC FUNC_MGT_FUNC_CLASSIFICATION_MASK(4) #define FUNC_MGT_TIMELINE_FUNC FUNC_MGT_FUNC_CLASSIFICATION_MASK(5) diff --git a/source/libs/function/src/builtins.c b/source/libs/function/src/builtins.c index 5358930df000b3d758667ae6879354eefbe41a5d..2cec75c8d3f03270613241ed44973502ff1e72fb 100644 --- a/source/libs/function/src/builtins.c +++ b/source/libs/function/src/builtins.c @@ -14,8 +14,8 @@ */ #include "builtins.h" -#include "querynodes.h" #include "builtinsimpl.h" +#include "querynodes.h" #include "scalar.h" #include "taoserror.h" #include "tdatablock.h" @@ -185,6 +185,19 @@ static int32_t translateApercentile(SFunctionNode* pFunc, char* pErrBuf, int32_t if (!IS_NUMERIC_TYPE(para1Type) || !IS_INTEGER_TYPE(para2Type)) { return invaildFuncParaTypeErrMsg(pErrBuf, len, pFunc->functionName); } + + SNode* pParamNode = nodesListGetNode(pFunc->pParameterList, 1); + if (nodeType(pParamNode) != QUERY_NODE_VALUE) { + return invaildFuncParaTypeErrMsg(pErrBuf, len, pFunc->functionName); + } + + SValueNode* pValue = (SValueNode*)pParamNode; + if (pValue->datum.i < 0 || pValue->datum.i > 100) { + return invaildFuncParaValueErrMsg(pErrBuf, len, pFunc->functionName); + } + + pValue->notReserved = true; + if (3 == paraNum) { SNode* pPara3 = nodesListGetNode(pFunc->pParameterList, 2); if (QUERY_NODE_VALUE != nodeType(pPara3) || !validAperventileAlgo((SValueNode*)pPara3)) { @@ -215,7 +228,7 @@ static int32_t translateTop(SFunctionNode* pFunc, char* pErrBuf, int32_t len) { return invaildFuncParaTypeErrMsg(pErrBuf, len, pFunc->functionName); } - SValueNode* pValue = (SValueNode*) pParamNode; + SValueNode* pValue = (SValueNode*)pParamNode; if (pValue->node.resType.type != TSDB_DATA_TYPE_BIGINT) { return invaildFuncParaTypeErrMsg(pErrBuf, len, pFunc->functionName); } @@ -224,6 +237,8 @@ static int32_t translateTop(SFunctionNode* pFunc, char* pErrBuf, int32_t len) { return invaildFuncParaValueErrMsg(pErrBuf, len, pFunc->functionName); } + pValue->notReserved = true; + SDataType* pType = &((SExprNode*)nodesListGetNode(pFunc->pParameterList, 0))->resType; pFunc->node.resType = (SDataType){.bytes = pType->bytes, .type = pType->type}; return TSDB_CODE_SUCCESS; @@ -336,7 +351,7 @@ static int32_t translateStateCount(SFunctionNode* pFunc, char* pErrBuf, int32_t return invaildFuncParaTypeErrMsg(pErrBuf, len, pFunc->functionName); } - pFunc->node.resType = (SDataType) { .bytes = tDataTypes[TSDB_DATA_TYPE_BIGINT].bytes, .type = TSDB_DATA_TYPE_BIGINT }; + pFunc->node.resType = (SDataType){.bytes = tDataTypes[TSDB_DATA_TYPE_BIGINT].bytes, .type = TSDB_DATA_TYPE_BIGINT}; return TSDB_CODE_SUCCESS; } @@ -361,7 +376,7 @@ static int32_t translateStateDuration(SFunctionNode* pFunc, char* pErrBuf, int32 return invaildFuncParaTypeErrMsg(pErrBuf, len, pFunc->functionName); } - pFunc->node.resType = (SDataType) { .bytes = tDataTypes[TSDB_DATA_TYPE_BIGINT].bytes, .type = TSDB_DATA_TYPE_BIGINT }; + pFunc->node.resType = (SDataType){.bytes = tDataTypes[TSDB_DATA_TYPE_BIGINT].bytes, .type = TSDB_DATA_TYPE_BIGINT}; return TSDB_CODE_SUCCESS; } @@ -392,7 +407,7 @@ static int32_t translateCsum(SFunctionNode* pFunc, char* pErrBuf, int32_t len) { } } - pFunc->node.resType = (SDataType) { .bytes = tDataTypes[resType].bytes, .type = resType}; + pFunc->node.resType = (SDataType){.bytes = tDataTypes[resType].bytes, .type = resType}; return TSDB_CODE_SUCCESS; } @@ -434,7 +449,7 @@ static int32_t translateSample(SFunctionNode* pFunc, char* pErrBuf, int32_t len) } SExprNode* pCol = (SExprNode*)nodesListGetNode(pFunc->pParameterList, 0); - uint8_t colType = pCol->resType.type; + uint8_t colType = pCol->resType.type; if (IS_VAR_DATA_TYPE(colType)) { pFunc->node.resType = (SDataType){.bytes = pCol->resType.bytes, .type = colType}; } else { @@ -463,7 +478,7 @@ static int32_t translateTail(SFunctionNode* pFunc, char* pErrBuf, int32_t len) { } SExprNode* pCol = (SExprNode*)nodesListGetNode(pFunc->pParameterList, 0); - uint8_t colType = pCol->resType.type; + uint8_t colType = pCol->resType.type; if (IS_VAR_DATA_TYPE(colType)) { pFunc->node.resType = (SDataType){.bytes = pCol->resType.bytes, .type = colType}; } else { @@ -493,6 +508,20 @@ static int32_t translateFirstLast(SFunctionNode* pFunc, char* pErrBuf, int32_t l return TSDB_CODE_SUCCESS; } +static int32_t translateUnique(SFunctionNode* pFunc, char* pErrBuf, int32_t len) { + if (1 != LIST_LENGTH(pFunc->pParameterList)) { + return TSDB_CODE_SUCCESS; + } + + SNode* pPara = nodesListGetNode(pFunc->pParameterList, 0); + if (QUERY_NODE_COLUMN != nodeType(pPara)) { + return buildFuncErrMsg(pErrBuf, len, TSDB_CODE_FUNC_FUNTION_ERROR, "The parameters of UNIQUE can only be columns"); + } + + pFunc->node.resType = ((SExprNode*)pPara)->resType; + return TSDB_CODE_SUCCESS; +} + static int32_t translateDiff(SFunctionNode* pFunc, char* pErrBuf, int32_t len) { int32_t paraLen = LIST_LENGTH(pFunc->pParameterList); if (paraLen == 0 || paraLen > 2) { @@ -808,7 +837,7 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = { { .name = "top", .type = FUNCTION_TYPE_TOP, - .classification = FUNC_MGT_AGG_FUNC | FUNC_MGT_SELECT_FUNC, + .classification = FUNC_MGT_SELECT_FUNC | FUNC_MGT_INDEFINITE_ROWS_FUNC, .translateFunc = translateTop, .getEnvFunc = getTopBotFuncEnv, .initFunc = functionSetup, @@ -818,7 +847,7 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = { { .name = "bottom", .type = FUNCTION_TYPE_BOTTOM, - .classification = FUNC_MGT_AGG_FUNC | FUNC_MGT_SELECT_FUNC, + .classification = FUNC_MGT_SELECT_FUNC | FUNC_MGT_INDEFINITE_ROWS_FUNC, .translateFunc = translateBottom, .getEnvFunc = getTopBotFuncEnv, .initFunc = functionSetup, @@ -877,16 +906,6 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = { .processFunc = lastFunction, .finalizeFunc = lastFinalize }, - { - .name = "diff", - .type = FUNCTION_TYPE_DIFF, - .classification = FUNC_MGT_NONSTANDARD_SQL_FUNC | FUNC_MGT_TIMELINE_FUNC, - .translateFunc = translateDiff, - .getEnvFunc = getDiffFuncEnv, - .initFunc = diffFunctionSetup, - .processFunc = diffFunction, - .finalizeFunc = functionFinalize - }, { .name = "histogram", .type = FUNCTION_TYPE_HISTOGRAM, @@ -907,10 +926,20 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = { .processFunc = hllFunction, .finalizeFunc = hllFinalize }, + { + .name = "diff", + .type = FUNCTION_TYPE_DIFF, + .classification = FUNC_MGT_INDEFINITE_ROWS_FUNC | FUNC_MGT_TIMELINE_FUNC, + .translateFunc = translateDiff, + .getEnvFunc = getDiffFuncEnv, + .initFunc = diffFunctionSetup, + .processFunc = diffFunction, + .finalizeFunc = functionFinalize + }, { .name = "state_count", .type = FUNCTION_TYPE_STATE_COUNT, - .classification = FUNC_MGT_NONSTANDARD_SQL_FUNC, + .classification = FUNC_MGT_INDEFINITE_ROWS_FUNC, .translateFunc = translateStateCount, .getEnvFunc = getStateFuncEnv, .initFunc = functionSetup, @@ -920,7 +949,7 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = { { .name = "state_duration", .type = FUNCTION_TYPE_STATE_DURATION, - .classification = FUNC_MGT_NONSTANDARD_SQL_FUNC | FUNC_MGT_TIMELINE_FUNC, + .classification = FUNC_MGT_INDEFINITE_ROWS_FUNC | FUNC_MGT_TIMELINE_FUNC, .translateFunc = translateStateDuration, .getEnvFunc = getStateFuncEnv, .initFunc = functionSetup, @@ -930,7 +959,7 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = { { .name = "csum", .type = FUNCTION_TYPE_CSUM, - .classification = FUNC_MGT_NONSTANDARD_SQL_FUNC | FUNC_MGT_TIMELINE_FUNC, + .classification = FUNC_MGT_INDEFINITE_ROWS_FUNC | FUNC_MGT_TIMELINE_FUNC, .translateFunc = translateCsum, .getEnvFunc = getCsumFuncEnv, .initFunc = functionSetup, @@ -940,7 +969,7 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = { { .name = "mavg", .type = FUNCTION_TYPE_MAVG, - .classification = FUNC_MGT_NONSTANDARD_SQL_FUNC | FUNC_MGT_TIMELINE_FUNC, + .classification = FUNC_MGT_INDEFINITE_ROWS_FUNC | FUNC_MGT_TIMELINE_FUNC, .translateFunc = translateMavg, .getEnvFunc = getMavgFuncEnv, .initFunc = mavgFunctionSetup, @@ -950,7 +979,7 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = { { .name = "sample", .type = FUNCTION_TYPE_SAMPLE, - .classification = FUNC_MGT_NONSTANDARD_SQL_FUNC | FUNC_MGT_TIMELINE_FUNC, + .classification = FUNC_MGT_INDEFINITE_ROWS_FUNC | FUNC_MGT_TIMELINE_FUNC, .translateFunc = translateSample, .getEnvFunc = getSampleFuncEnv, .initFunc = sampleFunctionSetup, @@ -960,12 +989,22 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = { { .name = "tail", .type = FUNCTION_TYPE_TAIL, - .classification = FUNC_MGT_NONSTANDARD_SQL_FUNC | FUNC_MGT_TIMELINE_FUNC, + .classification = FUNC_MGT_INDEFINITE_ROWS_FUNC | FUNC_MGT_TIMELINE_FUNC, .translateFunc = translateTail, .getEnvFunc = getTailFuncEnv, .initFunc = tailFunctionSetup, .processFunc = tailFunction, - .finalizeFunc = tailFinalize + .finalizeFunc = NULL + }, + { + .name = "unique", + .type = FUNCTION_TYPE_UNIQUE, + .classification = FUNC_MGT_INDEFINITE_ROWS_FUNC | FUNC_MGT_TIMELINE_FUNC, + .translateFunc = translateUnique, + .getEnvFunc = getUniqueFuncEnv, + .initFunc = uniqueFunctionSetup, + .processFunc = uniqueFunction, + .finalizeFunc = NULL }, { .name = "abs", diff --git a/source/libs/function/src/builtinsimpl.c b/source/libs/function/src/builtinsimpl.c index d54cc96611787a245b4e0c5b6d9030873f14ff38..ad92d095d5a292d366f127642e835b3dadda10dd 100644 --- a/source/libs/function/src/builtinsimpl.c +++ b/source/libs/function/src/builtinsimpl.c @@ -28,12 +28,15 @@ #define TAIL_MAX_POINTS_NUM 100 #define TAIL_MAX_OFFSET 100 +#define UNIQUE_MAX_RESULT_SIZE (1024*1024*10) + #define HLL_BUCKET_BITS 14 // The bits of the bucket #define HLL_DATA_BITS (64-HLL_BUCKET_BITS) #define HLL_BUCKETS (1<subsidiaries.num; ++_i) { \ + SqlFunctionCtx* __ctx = (ctx)->subsidiaries.pCtx[_i]; \ + if (__ctx->functionId == FUNCTION_TS_DUMMY) { \ + __ctx->tag.i = (ts); \ + __ctx->tag.nType = TSDB_DATA_TYPE_BIGINT; \ + } \ + __ctx->fpSet.process(__ctx); \ + } \ + } while (0) + #define UPDATE_DATA(ctx, left, right, num, sign, _ts) \ do { \ if (((left) < (right)) ^ (sign)) { \ @@ -514,7 +543,7 @@ bool getSumFuncEnv(SFunctionNode* UNUSED_PARAM(pFunc), SFuncExecEnv* pEnv) { } bool getAvgFuncEnv(SFunctionNode* UNUSED_PARAM(pFunc), SFuncExecEnv* pEnv) { - pEnv->calcMemSize = sizeof(double); + pEnv->calcMemSize = sizeof(SAvgRes); return true; } @@ -748,50 +777,6 @@ bool getMinmaxFuncEnv(SFunctionNode* UNUSED_PARAM(pFunc), SFuncExecEnv* pEnv) { return true; } -#define GET_TS_LIST(x) ((TSKEY*)((x)->ptsList)) -#define GET_TS_DATA(x, y) (GET_TS_LIST(x)[(y)]) - -#define DO_UPDATE_TAG_COLUMNS_WITHOUT_TS(ctx) \ - do { \ - for (int32_t _i = 0; _i < (ctx)->tagInfo.numOfTagCols; ++_i) { \ - SqlFunctionCtx* __ctx = (ctx)->tagInfo.pTagCtxList[_i]; \ - __ctx->fpSet.process(__ctx); \ - } \ - } while (0); - -#define DO_UPDATE_SUBSID_RES(ctx, ts) \ - do { \ - for (int32_t _i = 0; _i < (ctx)->subsidiaries.num; ++_i) { \ - SqlFunctionCtx* __ctx = (ctx)->subsidiaries.pCtx[_i]; \ - if (__ctx->functionId == FUNCTION_TS_DUMMY) { \ - __ctx->tag.i = (ts); \ - __ctx->tag.nType = TSDB_DATA_TYPE_BIGINT; \ - } \ - __ctx->fpSet.process(__ctx); \ - } \ - } while (0) - -#define UPDATE_DATA(ctx, left, right, num, sign, _ts) \ - do { \ - if (((left) < (right)) ^ (sign)) { \ - (left) = (right); \ - DO_UPDATE_SUBSID_RES(ctx, _ts); \ - (num) += 1; \ - } \ - } while (0) - -#define LOOPCHECK_N(val, _col, ctx, _t, _nrow, _start, sign, num) \ - do { \ - _t* d = (_t*)((_col)->pData); \ - for (int32_t i = (_start); i < (_nrow) + (_start); ++i) { \ - if (((_col)->hasNull) && colDataIsNull_f((_col)->nullbitmap, i)) { \ - continue; \ - } \ - TSKEY ts = (ctx)->ptsList != NULL ? GET_TS_DATA(ctx, i) : 0; \ - UPDATE_DATA(ctx, val, d[i], num, sign, ts); \ - } \ - } while (0) - static void saveTupleData(SqlFunctionCtx* pCtx, int32_t rowIndex, const SSDataBlock* pSrcBlock, STuplePos* pPos); static void copyTupleData(SqlFunctionCtx* pCtx, int32_t rowIndex, const SSDataBlock* pSrcBlock, STuplePos* pPos); @@ -2106,7 +2091,7 @@ static void doHandleDiff(SDiffInfo* pDiffInfo, int32_t type, const char* pv, SCo default: ASSERT(0); } - } +} int32_t diffFunction(SqlFunctionCtx* pCtx) { SResultRowEntryInfo* pResInfo = GET_RES_INFO(pCtx); @@ -3581,3 +3566,92 @@ int32_t tailFinalize(SqlFunctionCtx* pCtx, SSDataBlock* pBlock) { return pEntryInfo->numOfRes; } + +bool getUniqueFuncEnv(SFunctionNode* pFunc, SFuncExecEnv* pEnv) { + pEnv->calcMemSize = sizeof(SUniqueInfo) + UNIQUE_MAX_RESULT_SIZE; + return true; +} + +bool uniqueFunctionSetup(SqlFunctionCtx* pCtx, SResultRowEntryInfo* pResInfo) { + if (!functionSetup(pCtx, pResInfo)) { + return false; + } + + SUniqueInfo* pInfo = GET_ROWCELL_INTERBUF(pResInfo); + pInfo->numOfPoints = 0; + pInfo->colType = pCtx->resDataInfo.type; + pInfo->colBytes = pCtx->resDataInfo.bytes; + if (pInfo->pHash != NULL) { + taosHashClear(pInfo->pHash); + } else { + pInfo->pHash = taosHashInit(64, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY), true, HASH_NO_LOCK); + } + return true; +} + +static void doUniqueAdd(SUniqueInfo* pInfo, char *data, TSKEY ts, bool isNull) { + int32_t hashKeyBytes = IS_VAR_DATA_TYPE(pInfo->colType) ? varDataTLen(data) : pInfo->colBytes; + + SUniqueItem *pHashItem = taosHashGet(pInfo->pHash, data, hashKeyBytes); + if (pHashItem == NULL) { + int32_t size = sizeof(SUniqueItem) + pInfo->colBytes; + SUniqueItem *pItem = (SUniqueItem *)(pInfo->pItems + pInfo->numOfPoints * size); + pItem->timestamp = ts; + memcpy(pItem->data, data, pInfo->colBytes); + + taosHashPut(pInfo->pHash, data, hashKeyBytes, (char *)pItem, sizeof(SUniqueItem*)); + pInfo->numOfPoints++; + } else if (pHashItem->timestamp > ts) { + pHashItem->timestamp = ts; + } + +} + +int32_t uniqueFunction(SqlFunctionCtx* pCtx) { + SResultRowEntryInfo* pResInfo = GET_RES_INFO(pCtx); + SUniqueInfo* pInfo = GET_ROWCELL_INTERBUF(pResInfo); + + SInputColumnInfoData* pInput = &pCtx->input; + TSKEY* tsList = (int64_t*)pInput->pPTS->pData; + + SColumnInfoData* pInputCol = pInput->pData[0]; + SColumnInfoData* pTsOutput = pCtx->pTsOutput; + SColumnInfoData* pOutput = (SColumnInfoData*)pCtx->pOutput; + + int32_t startOffset = pCtx->offset; + for (int32_t i = pInput->startRowIndex; i < pInput->numOfRows + pInput->startRowIndex; ++i) { + char* data = colDataGetData(pInputCol, i); + doUniqueAdd(pInfo, data, tsList[i], colDataIsNull_s(pInputCol, i)); + + if (sizeof(SUniqueInfo) + pInfo->numOfPoints * (sizeof(SUniqueItem) + pInfo->colBytes) >= UNIQUE_MAX_RESULT_SIZE) { + taosHashCleanup(pInfo->pHash); + return 0; + } + } + + for (int32_t i = 0; i < pInfo->numOfPoints; ++i) { + SUniqueItem *pItem = (SUniqueItem *)(pInfo->pItems + i * (sizeof(SUniqueItem) + pInfo->colBytes)); + colDataAppend(pOutput, i, pItem->data, false); + if (pTsOutput != NULL) { + colDataAppendInt64(pTsOutput, i, &pItem->timestamp); + } + } + + return pInfo->numOfPoints; +} + +int32_t uniqueFinalize(SqlFunctionCtx* pCtx, SSDataBlock* pBlock) { + SResultRowEntryInfo* pResInfo = GET_RES_INFO(pCtx); + SUniqueInfo* pInfo = GET_ROWCELL_INTERBUF(GET_RES_INFO(pCtx)); + int32_t slotId = pCtx->pExpr->base.resSchema.slotId; + SColumnInfoData* pCol = taosArrayGet(pBlock->pDataBlock, slotId); + + for (int32_t i = 0; i < pResInfo->numOfRes; ++i) { + SUniqueItem *pItem = (SUniqueItem *)(pInfo->pItems + i * (sizeof(SUniqueItem) + pInfo->colBytes)); + colDataAppend(pCol, i, pItem->data, false); + //TODO: handle ts output + } + + return pResInfo->numOfRes; +} + diff --git a/source/libs/function/src/functionMgt.c b/source/libs/function/src/functionMgt.c index 3b1e66f2ad5a4818e8d8b3e502b14a825d66c8e8..49b20ebc853e1f97f191b2f5d9d0c5396d241b60 100644 --- a/source/libs/function/src/functionMgt.c +++ b/source/libs/function/src/functionMgt.c @@ -66,22 +66,18 @@ static bool isSpecificClassifyFunc(int32_t funcId, uint64_t classification) { } static int32_t getUdfInfo(SFmGetFuncInfoParam* pParam, SFunctionNode* pFunc) { - SFuncInfo* pInfo = NULL; - int32_t code = catalogGetUdfInfo(pParam->pCtg, pParam->pRpc, pParam->pMgmtEps, pFunc->functionName, &pInfo); + SFuncInfo funcInfo = {0}; + int32_t code = catalogGetUdfInfo(pParam->pCtg, pParam->pRpc, pParam->pMgmtEps, pFunc->functionName, &funcInfo); if (TSDB_CODE_SUCCESS != code) { return code; } - if (NULL == pInfo) { - snprintf(pParam->pErrBuf, pParam->errBufLen, "Invalid function name: %s", pFunc->functionName); - return TSDB_CODE_FUNC_INVALID_FUNTION; - } + pFunc->funcType = FUNCTION_TYPE_UDF; - pFunc->funcId = TSDB_FUNC_TYPE_AGGREGATE == pInfo->funcType ? FUNC_AGGREGATE_UDF_ID : FUNC_SCALAR_UDF_ID; - pFunc->node.resType.type = pInfo->outputType; - pFunc->node.resType.bytes = pInfo->outputLen; - pFunc->udfBufSize = pInfo->bufSize; - tFreeSFuncInfo(pInfo); - taosMemoryFree(pInfo); + pFunc->funcId = TSDB_FUNC_TYPE_AGGREGATE == funcInfo.funcType ? FUNC_AGGREGATE_UDF_ID : FUNC_SCALAR_UDF_ID; + pFunc->node.resType.type = funcInfo.outputType; + pFunc->node.resType.bytes = funcInfo.outputLen; + pFunc->udfBufSize = funcInfo.bufSize; + tFreeSFuncInfo(&funcInfo); return TSDB_CODE_SUCCESS; } @@ -149,6 +145,8 @@ bool fmIsAggFunc(int32_t funcId) { return isSpecificClassifyFunc(funcId, FUNC_MG bool fmIsScalarFunc(int32_t funcId) { return isSpecificClassifyFunc(funcId, FUNC_MGT_SCALAR_FUNC); } +bool fmIsVectorFunc(int32_t funcId) { return !fmIsScalarFunc(funcId); } + bool fmIsSelectFunc(int32_t funcId) { return isSpecificClassifyFunc(funcId, FUNC_MGT_SELECT_FUNC); } bool fmIsTimelineFunc(int32_t funcId) { return isSpecificClassifyFunc(funcId, FUNC_MGT_TIMELINE_FUNC); } @@ -161,7 +159,7 @@ bool fmIsWindowPseudoColumnFunc(int32_t funcId) { return isSpecificClassifyFunc( bool fmIsWindowClauseFunc(int32_t funcId) { return fmIsAggFunc(funcId) || fmIsWindowPseudoColumnFunc(funcId); } -bool fmIsNonstandardSQLFunc(int32_t funcId) { return isSpecificClassifyFunc(funcId, FUNC_MGT_NONSTANDARD_SQL_FUNC); } +bool fmIsIndefiniteRowsFunc(int32_t funcId) { return isSpecificClassifyFunc(funcId, FUNC_MGT_INDEFINITE_ROWS_FUNC); } bool fmIsSpecialDataRequiredFunc(int32_t funcId) { return isSpecificClassifyFunc(funcId, FUNC_MGT_SPECIAL_DATA_REQUIRED); diff --git a/source/libs/function/src/tpercentile.c b/source/libs/function/src/tpercentile.c index dd57024624f09d807996309c34e3990a39fdab46..90d0640f403ef3be0949f52d1313b715b225ced0 100644 --- a/source/libs/function/src/tpercentile.c +++ b/source/libs/function/src/tpercentile.c @@ -255,7 +255,7 @@ tMemBucket *tMemBucketCreate(int16_t nElemSize, int16_t dataType, double minval, resetSlotInfo(pBucket); - int32_t ret = createDiskbasedBuf(&pBucket->pBuffer, pBucket->bufPageSize, pBucket->bufPageSize * 512, "1", "/tmp"); + int32_t ret = createDiskbasedBuf(&pBucket->pBuffer, pBucket->bufPageSize, pBucket->bufPageSize * 512, "1", TD_TMP_DIR_PATH); if (ret != 0) { tMemBucketDestroy(pBucket); return NULL; diff --git a/source/libs/function/src/tudf.c b/source/libs/function/src/tudf.c index 5f20d2e50a50fd0cef4e3b9cbaa21d22fb930464..441648e52b2ef78326d73d1944bcfbfd0009abc6 100644 --- a/source/libs/function/src/tudf.c +++ b/source/libs/function/src/tudf.c @@ -24,7 +24,6 @@ #include "builtinsimpl.h" #include "functionMgt.h" -//TODO: add unit test typedef struct SUdfdData { bool startCalled; bool needCleanUp; @@ -45,7 +44,15 @@ typedef struct SUdfdData { SUdfdData udfdGlobal = {0}; +int32_t udfStartUdfd(int32_t startDnodeId); +int32_t udfStopUdfd(); + static int32_t udfSpawnUdfd(SUdfdData *pData); +void udfUdfdExit(uv_process_t *process, int64_t exitStatus, int termSignal); +static int32_t udfSpawnUdfd(SUdfdData* pData); +static void udfUdfdCloseWalkCb(uv_handle_t* handle, void* arg); +static void udfUdfdStopAsyncCb(uv_async_t *async); +static void udfWatchUdfd(void *args); void udfUdfdExit(uv_process_t *process, int64_t exitStatus, int termSignal) { fnInfo("udfd process exited with status %" PRId64 ", signal %d", exitStatus, termSignal); @@ -65,12 +72,20 @@ static int32_t udfSpawnUdfd(SUdfdData* pData) { char path[PATH_MAX] = {0}; if (tsProcPath == NULL) { path[0] = '.'; + #ifdef WINDOWS + GetModuleFileName(NULL, path, PATH_MAX); + taosDirName(path); + #endif } else { strncpy(path, tsProcPath, strlen(tsProcPath)); taosDirName(path); } #ifdef WINDOWS - strcat(path, "udfd.exe"); + if (strlen(path)==0) { + strcat(path, "udfd.exe"); + } else { + strcat(path, "\\udfd.exe"); + } #else strcat(path, "/udfd"); #endif @@ -413,6 +428,34 @@ enum { UDFC_STATE_STOPPING, // stopping after udfcClose }; +int32_t getUdfdPipeName(char* pipeName, int32_t size); +int32_t encodeUdfSetupRequest(void **buf, const SUdfSetupRequest *setup); +void* decodeUdfSetupRequest(const void* buf, SUdfSetupRequest *request); +int32_t encodeUdfInterBuf(void **buf, const SUdfInterBuf* state); +void* decodeUdfInterBuf(const void* buf, SUdfInterBuf* state); +int32_t encodeUdfCallRequest(void **buf, const SUdfCallRequest *call); +void* decodeUdfCallRequest(const void* buf, SUdfCallRequest* call); +int32_t encodeUdfTeardownRequest(void **buf, const SUdfTeardownRequest *teardown); +void* decodeUdfTeardownRequest(const void* buf, SUdfTeardownRequest *teardown); +int32_t encodeUdfRequest(void** buf, const SUdfRequest* request); +void* decodeUdfRequest(const void* buf, SUdfRequest* request); +int32_t encodeUdfSetupResponse(void **buf, const SUdfSetupResponse *setupRsp); +void* decodeUdfSetupResponse(const void* buf, SUdfSetupResponse* setupRsp); +int32_t encodeUdfCallResponse(void **buf, const SUdfCallResponse *callRsp); +void* decodeUdfCallResponse(const void* buf, SUdfCallResponse* callRsp); +int32_t encodeUdfTeardownResponse(void** buf, const SUdfTeardownResponse* teardownRsp); +void* decodeUdfTeardownResponse(const void* buf, SUdfTeardownResponse* teardownResponse); +int32_t encodeUdfResponse(void** buf, const SUdfResponse* rsp); +void* decodeUdfResponse(const void* buf, SUdfResponse* rsp); +void freeUdfColumnData(SUdfColumnData *data, SUdfColumnMeta *meta); +void freeUdfColumn(SUdfColumn* col); +void freeUdfDataDataBlock(SUdfDataBlock *block); +void freeUdfInterBuf(SUdfInterBuf *buf); +int32_t convertDataBlockToUdfDataBlock(SSDataBlock *block, SUdfDataBlock *udfBlock); +int32_t convertUdfColumnToDataBlock(SUdfColumn *udfCol, SSDataBlock *block); +int32_t convertScalarParamToDataBlock(SScalarParam *input, int32_t numOfCols, SSDataBlock *output); +int32_t convertDataBlockToScalarParm(SSDataBlock *input, SScalarParam *output); + int32_t getUdfdPipeName(char* pipeName, int32_t size) { char dnodeId[8] = {0}; size_t dnodeIdSize = sizeof(dnodeId); @@ -650,7 +693,7 @@ int32_t encodeUdfResponse(void** buf, const SUdfResponse* rsp) { len += encodeUdfTeardownResponse(buf, &rsp->teardownRsp); break; default: - //TODO: log error + fnError("encode udf response, invalid udf response type %d", rsp->type); break; } return len; @@ -676,7 +719,7 @@ void* decodeUdfResponse(const void* buf, SUdfResponse* rsp) { buf = decodeUdfTeardownResponse(buf, &rsp->teardownRsp); break; default: - //TODO: log error + fnError("decode udf response, invalid udf response type %d", rsp->type); break; } return (void*)buf; @@ -817,185 +860,495 @@ int32_t convertDataBlockToScalarParm(SSDataBlock *input, SScalarParam *output) { return 0; } -void onUdfcPipeClose(uv_handle_t *handle) { - SClientUvConn *conn = handle->data; - if (!QUEUE_EMPTY(&conn->taskQueue)) { - QUEUE* h = QUEUE_HEAD(&conn->taskQueue); - SClientUvTaskNode *task = QUEUE_DATA(h, SClientUvTaskNode, connTaskQueue); - task->errCode = 0; - QUEUE_REMOVE(&task->procTaskQueue); - uv_sem_post(&task->taskSem); - } - conn->session->udfUvPipe = NULL; - taosMemoryFree(conn->readBuf.buf); - taosMemoryFree(conn); - taosMemoryFree((uv_pipe_t *) handle); -} - -int32_t udfcGetUdfTaskResultFromUvTask(SClientUdfTask *task, SClientUvTaskNode *uvTask) { - fnDebug("udfc get uv task result. task: %p, uvTask: %p", task, uvTask); - if (uvTask->type == UV_TASK_REQ_RSP) { - if (uvTask->rspBuf.base != NULL) { - SUdfResponse rsp = {0}; - void* buf = decodeUdfResponse(uvTask->rspBuf.base, &rsp); - assert(uvTask->rspBuf.len == POINTER_DISTANCE(buf, uvTask->rspBuf.base)); - task->errCode = rsp.code; +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//memory layout |---SUdfAggRes----|-----final result-----|---inter result----| +typedef struct SUdfAggRes { + int8_t finalResNum; + int8_t interResNum; + char* finalResBuf; + char* interResBuf; +} SUdfAggRes; +void onUdfcPipeClose(uv_handle_t *handle); +int32_t udfcGetUdfTaskResultFromUvTask(SClientUdfTask *task, SClientUvTaskNode *uvTask); +void udfcAllocateBuffer(uv_handle_t *handle, size_t suggestedSize, uv_buf_t *buf); +bool isUdfcUvMsgComplete(SClientConnBuf *connBuf); +void udfcUvHandleRsp(SClientUvConn *conn); +void udfcUvHandleError(SClientUvConn *conn); +void onUdfcPipeRead(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf); +void onUdfcPipetWrite(uv_write_t *write, int status); +void onUdfcPipeConnect(uv_connect_t *connect, int status); +int32_t udfcCreateUvTask(SClientUdfTask *task, int8_t uvTaskType, SClientUvTaskNode **pUvTask); +int32_t udfcQueueUvTask(SClientUvTaskNode *uvTask); +int32_t udfcStartUvTask(SClientUvTaskNode *uvTask); +void udfcAsyncTaskCb(uv_async_t *async); +void cleanUpUvTasks(SUdfcProxy *udfc); +void udfStopAsyncCb(uv_async_t *async); +void constructUdfService(void *argsThread); +int32_t udfcRunUdfUvTask(SClientUdfTask *task, int8_t uvTaskType); +int32_t doSetupUdf(char udfName[], UdfcFuncHandle *funcHandle); +int compareUdfcFuncSub(const void* elem1, const void* elem2); +int32_t doTeardownUdf(UdfcFuncHandle handle); - switch (task->type) { - case UDF_TASK_SETUP: { - //TODO: copy or not - task->_setup.rsp = rsp.setupRsp; - break; - } - case UDF_TASK_CALL: { - task->_call.rsp = rsp.callRsp; - //TODO: copy or not - break; - } - case UDF_TASK_TEARDOWN: { - task->_teardown.rsp = rsp.teardownRsp; - //TODO: copy or not? - break; - } - default: { - break; - } - } +int32_t callUdf(UdfcFuncHandle handle, int8_t callType, SSDataBlock *input, SUdfInterBuf *state, SUdfInterBuf *state2, + SSDataBlock* output, SUdfInterBuf *newState); +int32_t doCallUdfAggInit(UdfcFuncHandle handle, SUdfInterBuf *interBuf); +int32_t doCallUdfAggProcess(UdfcFuncHandle handle, SSDataBlock *block, SUdfInterBuf *state, SUdfInterBuf *newState); +int32_t doCallUdfAggMerge(UdfcFuncHandle handle, SUdfInterBuf *interBuf1, SUdfInterBuf *interBuf2, SUdfInterBuf *resultBuf); +int32_t doCallUdfAggFinalize(UdfcFuncHandle handle, SUdfInterBuf *interBuf, SUdfInterBuf *resultData); +int32_t doCallUdfScalarFunc(UdfcFuncHandle handle, SScalarParam *input, int32_t numOfCols, SScalarParam* output); +int32_t callUdfScalarFunc(char *udfName, SScalarParam *input, int32_t numOfCols, SScalarParam *output); + +int32_t udfcOpen(); +int32_t udfcClose(); + +int32_t acquireUdfFuncHandle(char* udfName, UdfcFuncHandle* pHandle); +void releaseUdfFuncHandle(char* udfName); +int32_t cleanUpUdfs(); + +bool udfAggGetEnv(struct SFunctionNode* pFunc, SFuncExecEnv* pEnv); +bool udfAggInit(struct SqlFunctionCtx *pCtx, struct SResultRowEntryInfo* pResultCellInfo); +int32_t udfAggProcess(struct SqlFunctionCtx *pCtx); +int32_t udfAggFinalize(struct SqlFunctionCtx *pCtx, SSDataBlock* pBlock); - // TODO: the call buffer is setup and freed by udf invocation - taosMemoryFree(uvTask->rspBuf.base); - } else { - task->errCode = uvTask->errCode; - } - } else if (uvTask->type == UV_TASK_CONNECT) { - task->errCode = uvTask->errCode; - } else if (uvTask->type == UV_TASK_DISCONNECT) { - task->errCode = uvTask->errCode; - } - return 0; +int compareUdfcFuncSub(const void* elem1, const void* elem2) { + SUdfcFuncStub *stub1 = (SUdfcFuncStub *)elem1; + SUdfcFuncStub *stub2 = (SUdfcFuncStub *)elem2; + return strcmp(stub1->udfName, stub2->udfName); } -void udfcAllocateBuffer(uv_handle_t *handle, size_t suggestedSize, uv_buf_t *buf) { - SClientUvConn *conn = handle->data; - SClientConnBuf *connBuf = &conn->readBuf; - - int32_t msgHeadSize = sizeof(int32_t) + sizeof(int64_t); - if (connBuf->cap == 0) { - connBuf->buf = taosMemoryMalloc(msgHeadSize); - if (connBuf->buf) { - connBuf->len = 0; - connBuf->cap = msgHeadSize; - connBuf->total = -1; - - buf->base = connBuf->buf; - buf->len = connBuf->cap; +int32_t acquireUdfFuncHandle(char* udfName, UdfcFuncHandle* pHandle) { + int32_t code = 0; + uv_mutex_lock(&gUdfdProxy.udfStubsMutex); + SUdfcFuncStub key = {0}; + strcpy(key.udfName, udfName); + int32_t stubIndex = taosArraySearchIdx(gUdfdProxy.udfStubs, &key, compareUdfcFuncSub, TD_EQ); + if (stubIndex != -1) { + SUdfcFuncStub *foundStub = taosArrayGet(gUdfdProxy.udfStubs, stubIndex); + UdfcFuncHandle handle = foundStub->handle; + if (handle != NULL && ((SUdfcUvSession*)handle)->udfUvPipe != NULL) { + *pHandle = foundStub->handle; + ++foundStub->refCount; + foundStub->lastRefTime = taosGetTimestampUs(); + uv_mutex_unlock(&gUdfdProxy.udfStubsMutex); + return 0; } else { - fnError("udfc allocate buffer failure. size: %d", msgHeadSize); - buf->base = NULL; - buf->len = 0; + fnInfo("invalid handle for %s, refCount: %d, last ref time: %"PRId64". remove it from cache", + udfName, foundStub->refCount, foundStub->lastRefTime); + taosArrayRemove(gUdfdProxy.udfStubs, stubIndex); } + } + *pHandle = NULL; + code = doSetupUdf(udfName, pHandle); + if (code == TSDB_CODE_SUCCESS) { + SUdfcFuncStub stub = {0}; + strcpy(stub.udfName, udfName); + stub.handle = *pHandle; + ++stub.refCount; + stub.lastRefTime = taosGetTimestampUs(); + taosArrayPush(gUdfdProxy.udfStubs, &stub); + taosArraySort(gUdfdProxy.udfStubs, compareUdfcFuncSub); } else { - connBuf->cap = connBuf->total > connBuf->cap ? connBuf->total : connBuf->cap; - void *resultBuf = taosMemoryRealloc(connBuf->buf, connBuf->cap); - if (resultBuf) { - connBuf->buf = resultBuf; - buf->base = connBuf->buf + connBuf->len; - buf->len = connBuf->cap - connBuf->len; - } else { - fnError("udfc re-allocate buffer failure. size: %d", connBuf->cap); - buf->base = NULL; - buf->len = 0; - } + *pHandle = NULL; } - fnTrace("conn buf cap - len - total : %d - %d - %d", connBuf->cap, connBuf->len, connBuf->total); - + uv_mutex_unlock(&gUdfdProxy.udfStubsMutex); + return code; } -bool isUdfcUvMsgComplete(SClientConnBuf *connBuf) { - if (connBuf->total == -1 && connBuf->len >= sizeof(int32_t)) { - connBuf->total = *(int32_t *) (connBuf->buf); +void releaseUdfFuncHandle(char* udfName) { + uv_mutex_lock(&gUdfdProxy.udfStubsMutex); + SUdfcFuncStub key = {0}; + strcpy(key.udfName, udfName); + SUdfcFuncStub *foundStub = taosArraySearch(gUdfdProxy.udfStubs, &key, compareUdfcFuncSub, TD_EQ); + if (!foundStub) { + return; } - if (connBuf->len == connBuf->cap && connBuf->total == connBuf->cap) { - fnTrace("udfc complete message is received, now handle it"); - return true; + if (foundStub->refCount > 0) { + --foundStub->refCount; } - return false; + uv_mutex_unlock(&gUdfdProxy.udfStubsMutex); } -void udfcUvHandleRsp(SClientUvConn *conn) { - SClientConnBuf *connBuf = &conn->readBuf; - int64_t seqNum = *(int64_t *) (connBuf->buf + sizeof(int32_t)); // msglen then seqnum - - if (QUEUE_EMPTY(&conn->taskQueue)) { - fnError("udfc no task waiting for response on connection"); - return; - } - bool found = false; - SClientUvTaskNode *taskFound = NULL; - QUEUE* h = QUEUE_NEXT(&conn->taskQueue); - SClientUvTaskNode *task = QUEUE_DATA(h, SClientUvTaskNode, connTaskQueue); - - while (h != &conn->taskQueue) { - if (task->seqNum == seqNum) { - if (found == false) { - found = true; - taskFound = task; +int32_t cleanUpUdfs() { + uv_mutex_lock(&gUdfdProxy.udfStubsMutex); + int32_t i = 0; + SArray* udfStubs = taosArrayInit(16, sizeof(SUdfcFuncStub)); + while (i < taosArrayGetSize(gUdfdProxy.udfStubs)) { + SUdfcFuncStub *stub = taosArrayGet(gUdfdProxy.udfStubs, i); + if (stub->refCount == 0) { + fnInfo("tear down udf. udf name: %s, handle: %p, ref count: %d", stub->udfName, stub->handle, stub->refCount); + doTeardownUdf(stub->handle); + } else { + fnInfo("udf still in use. udf name: %s, ref count: %d, last ref time: %"PRId64", handle: %p", + stub->udfName, stub->refCount, stub->lastRefTime, stub->handle); + UdfcFuncHandle handle = stub->handle; + if (handle != NULL && ((SUdfcUvSession*)handle)->udfUvPipe != NULL) { + taosArrayPush(udfStubs, stub); } else { - fnError("udfc more than one task waiting for the same response"); - continue; + fnInfo("udf invalid handle for %s, refCount: %d, last ref time: %"PRId64". remove it from cache", + stub->udfName, stub->refCount, stub->lastRefTime); } } - h = QUEUE_NEXT(h); - task = QUEUE_DATA(h, SClientUvTaskNode, connTaskQueue); + ++i; } + taosArrayDestroy(gUdfdProxy.udfStubs); + gUdfdProxy.udfStubs = udfStubs; + uv_mutex_unlock(&gUdfdProxy.udfStubsMutex); + return 0; +} - if (taskFound) { - taskFound->rspBuf = uv_buf_init(connBuf->buf, connBuf->len); - QUEUE_REMOVE(&taskFound->connTaskQueue); - QUEUE_REMOVE(&taskFound->procTaskQueue); - uv_sem_post(&taskFound->taskSem); +int32_t callUdfScalarFunc(char *udfName, SScalarParam *input, int32_t numOfCols, SScalarParam *output) { + UdfcFuncHandle handle = NULL; + int32_t code = acquireUdfFuncHandle(udfName, &handle); + if (code != 0) { + return code; + } + SUdfcUvSession *session = handle; + code = doCallUdfScalarFunc(handle, input, numOfCols, output); + if (output->columnData == NULL) { + fnError("udfc scalar function calculate error. no column data"); + code = TSDB_CODE_UDF_INVALID_OUTPUT_TYPE; } else { - fnError("no task is waiting for the response."); + if (session->outputType != output->columnData->info.type || session->outputLen != output->columnData->info.bytes) { + fnError("udfc scalar function calculate error. type mismatch. session type: %d(%d), output type: %d(%d)", session->outputType, + session->outputLen, output->columnData->info.type, output->columnData->info.bytes); + code = TSDB_CODE_UDF_INVALID_OUTPUT_TYPE; + } } - connBuf->buf = NULL; - connBuf->total = -1; - connBuf->len = 0; - connBuf->cap = 0; + releaseUdfFuncHandle(udfName); + return code; } -void udfcUvHandleError(SClientUvConn *conn) { - while (!QUEUE_EMPTY(&conn->taskQueue)) { - QUEUE* h = QUEUE_HEAD(&conn->taskQueue); - SClientUvTaskNode *task = QUEUE_DATA(h, SClientUvTaskNode, connTaskQueue); - task->errCode = TSDB_CODE_UDF_PIPE_READ_ERR; - QUEUE_REMOVE(&task->connTaskQueue); - QUEUE_REMOVE(&task->procTaskQueue); - uv_sem_post(&task->taskSem); +bool udfAggGetEnv(struct SFunctionNode* pFunc, SFuncExecEnv* pEnv) { + if (fmIsScalarFunc(pFunc->funcId)) { + return false; } - - uv_close((uv_handle_t *) conn->pipe, onUdfcPipeClose); + pEnv->calcMemSize = sizeof(SUdfAggRes) + pFunc->node.resType.bytes + pFunc->udfBufSize; + return true; } -void onUdfcPipeRead(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf) { - fnTrace("udfc client %p, client read from pipe. nread: %zd", client, nread); - if (nread == 0) return; - - SClientUvConn *conn = client->data; - SClientConnBuf *connBuf = &conn->readBuf; - if (nread > 0) { - connBuf->len += nread; - if (isUdfcUvMsgComplete(connBuf)) { - udfcUvHandleRsp(conn); - } - +bool udfAggInit(struct SqlFunctionCtx *pCtx, struct SResultRowEntryInfo* pResultCellInfo) { + if (functionSetup(pCtx, pResultCellInfo) != true) { + return false; } - if (nread < 0) { - fnError("udfc client pipe %p read error: %zd, %s.", client, nread, uv_strerror(nread)); - if (nread == UV_EOF) { - fnError("\tudfc client pipe %p closed", client); - } - udfcUvHandleError(conn); + UdfcFuncHandle handle; + int32_t udfCode = 0; + if ((udfCode = acquireUdfFuncHandle((char *)pCtx->udfName, &handle)) != 0) { + fnError("udfAggInit error. step doSetupUdf. udf code: %d", udfCode); + return false; + } + SUdfcUvSession *session = (SUdfcUvSession *)handle; + SUdfAggRes *udfRes = (SUdfAggRes*)GET_ROWCELL_INTERBUF(pResultCellInfo); + int32_t envSize = sizeof(SUdfAggRes) + session->outputLen + session->bufSize; + memset(udfRes, 0, envSize); + + udfRes->finalResBuf = (char*)udfRes + sizeof(SUdfAggRes); + udfRes->interResBuf = (char*)udfRes + sizeof(SUdfAggRes) + session->outputLen; + + SUdfInterBuf buf = {0}; + if ((udfCode = doCallUdfAggInit(handle, &buf)) != 0) { + fnError("udfAggInit error. step doCallUdfAggInit. udf code: %d", udfCode); + releaseUdfFuncHandle(pCtx->udfName); + return false; + } + udfRes->interResNum = buf.numOfResult; + if (buf.bufLen <= session->bufSize) { + memcpy(udfRes->interResBuf, buf.buf, buf.bufLen); + } else { + fnError("udfc inter buf size %d is greater than function bufSize %d", buf.bufLen, session->bufSize); + releaseUdfFuncHandle(pCtx->udfName); + return false; + } + releaseUdfFuncHandle(pCtx->udfName); + freeUdfInterBuf(&buf); + return true; +} + +int32_t udfAggProcess(struct SqlFunctionCtx *pCtx) { + int32_t udfCode = 0; + UdfcFuncHandle handle = 0; + if ((udfCode = acquireUdfFuncHandle((char *)pCtx->udfName, &handle)) != 0) { + fnError("udfAggProcess error. step acquireUdfFuncHandle. udf code: %d", udfCode); + return udfCode; + } + + SUdfcUvSession *session = handle; + SUdfAggRes* udfRes = (SUdfAggRes *)GET_ROWCELL_INTERBUF(GET_RES_INFO(pCtx)); + udfRes->finalResBuf = (char*)udfRes + sizeof(SUdfAggRes); + udfRes->interResBuf = (char*)udfRes + sizeof(SUdfAggRes) + session->outputLen; + + SInputColumnInfoData* pInput = &pCtx->input; + int32_t numOfCols = pInput->numOfInputCols; + int32_t start = pInput->startRowIndex; + int32_t numOfRows = pInput->numOfRows; + + + SSDataBlock tempBlock = {0}; + tempBlock.info.numOfCols = numOfCols; + tempBlock.info.rows = pInput->totalRows; + tempBlock.info.uid = pInput->uid; + bool hasVarCol = false; + tempBlock.pDataBlock = taosArrayInit(numOfCols, sizeof(SColumnInfoData)); + + for (int32_t i = 0; i < numOfCols; ++i) { + SColumnInfoData *col = pInput->pData[i]; + if (IS_VAR_DATA_TYPE(col->info.type)) { + hasVarCol = true; + } + taosArrayPush(tempBlock.pDataBlock, col); + } + tempBlock.info.hasVarCol = hasVarCol; + + SSDataBlock *inputBlock = blockDataExtractBlock(&tempBlock, start, numOfRows); + + SUdfInterBuf state = {.buf = udfRes->interResBuf, + .bufLen = session->bufSize, + .numOfResult = udfRes->interResNum}; + SUdfInterBuf newState = {0}; + + udfCode = doCallUdfAggProcess(session, inputBlock, &state, &newState); + if (udfCode != 0) { + fnError("udfAggProcess error. code: %d", udfCode); + newState.numOfResult = 0; + } else { + udfRes->interResNum = newState.numOfResult; + if (newState.bufLen <= session->bufSize) { + memcpy(udfRes->interResBuf, newState.buf, newState.bufLen); + } else { + fnError("udfc inter buf size %d is greater than function bufSize %d", newState.bufLen, session->bufSize); + udfCode = TSDB_CODE_UDF_INVALID_BUFSIZE; + } + } + if (newState.numOfResult == 1 || state.numOfResult == 1) { + GET_RES_INFO(pCtx)->numOfRes = 1; + } + + blockDataDestroy(inputBlock); + taosArrayDestroy(tempBlock.pDataBlock); + + releaseUdfFuncHandle(pCtx->udfName); + freeUdfInterBuf(&newState); + return udfCode; +} + +int32_t udfAggFinalize(struct SqlFunctionCtx *pCtx, SSDataBlock* pBlock) { + int32_t udfCode = 0; + UdfcFuncHandle handle = 0; + if ((udfCode = acquireUdfFuncHandle((char *)pCtx->udfName, &handle)) != 0) { + fnError("udfAggProcess error. step acquireUdfFuncHandle. udf code: %d", udfCode); + return udfCode; + } + + SUdfcUvSession *session = handle; + SUdfAggRes* udfRes = (SUdfAggRes *)GET_ROWCELL_INTERBUF(GET_RES_INFO(pCtx)); + udfRes->finalResBuf = (char*)udfRes + sizeof(SUdfAggRes); + udfRes->interResBuf = (char*)udfRes + sizeof(SUdfAggRes) + session->outputLen; + + + SUdfInterBuf resultBuf = {0}; + SUdfInterBuf state = {.buf = udfRes->interResBuf, + .bufLen = session->bufSize, + .numOfResult = udfRes->interResNum}; + int32_t udfCallCode= 0; + udfCallCode= doCallUdfAggFinalize(session, &state, &resultBuf); + if (udfCallCode != 0) { + fnError("udfAggFinalize error. doCallUdfAggFinalize step. udf code:%d", udfCallCode); + GET_RES_INFO(pCtx)->numOfRes = 0; + } else { + if (resultBuf.bufLen <= session->outputLen) { + memcpy(udfRes->finalResBuf, resultBuf.buf, session->outputLen); + udfRes->finalResNum = resultBuf.numOfResult; + GET_RES_INFO(pCtx)->numOfRes = udfRes->finalResNum; + } else { + fnError("udfc inter buf size %d is greater than function output size %d", resultBuf.bufLen, session->outputLen); + GET_RES_INFO(pCtx)->numOfRes = 0; + udfCallCode = TSDB_CODE_UDF_INVALID_OUTPUT_TYPE; + } + } + + freeUdfInterBuf(&resultBuf); + + int32_t numOfResults = functionFinalizeWithResultBuf(pCtx, pBlock, udfRes->finalResBuf); + releaseUdfFuncHandle(pCtx->udfName); + return udfCallCode == 0 ? numOfResults : udfCallCode; +} + +void onUdfcPipeClose(uv_handle_t *handle) { + SClientUvConn *conn = handle->data; + if (!QUEUE_EMPTY(&conn->taskQueue)) { + QUEUE* h = QUEUE_HEAD(&conn->taskQueue); + SClientUvTaskNode *task = QUEUE_DATA(h, SClientUvTaskNode, connTaskQueue); + task->errCode = 0; + QUEUE_REMOVE(&task->procTaskQueue); + uv_sem_post(&task->taskSem); + } + conn->session->udfUvPipe = NULL; + taosMemoryFree(conn->readBuf.buf); + taosMemoryFree(conn); + taosMemoryFree((uv_pipe_t *) handle); +} + +int32_t udfcGetUdfTaskResultFromUvTask(SClientUdfTask *task, SClientUvTaskNode *uvTask) { + fnDebug("udfc get uv task result. task: %p, uvTask: %p", task, uvTask); + if (uvTask->type == UV_TASK_REQ_RSP) { + if (uvTask->rspBuf.base != NULL) { + SUdfResponse rsp = {0}; + void* buf = decodeUdfResponse(uvTask->rspBuf.base, &rsp); + assert(uvTask->rspBuf.len == POINTER_DISTANCE(buf, uvTask->rspBuf.base)); + task->errCode = rsp.code; + + switch (task->type) { + case UDF_TASK_SETUP: { + task->_setup.rsp = rsp.setupRsp; + break; + } + case UDF_TASK_CALL: { + task->_call.rsp = rsp.callRsp; + break; + } + case UDF_TASK_TEARDOWN: { + task->_teardown.rsp = rsp.teardownRsp; + break; + } + default: { + break; + } + } + + // TODO: the call buffer is setup and freed by udf invocation + taosMemoryFree(uvTask->rspBuf.base); + } else { + task->errCode = uvTask->errCode; + } + } else if (uvTask->type == UV_TASK_CONNECT) { + task->errCode = uvTask->errCode; + } else if (uvTask->type == UV_TASK_DISCONNECT) { + task->errCode = uvTask->errCode; + } + return 0; +} + +void udfcAllocateBuffer(uv_handle_t *handle, size_t suggestedSize, uv_buf_t *buf) { + SClientUvConn *conn = handle->data; + SClientConnBuf *connBuf = &conn->readBuf; + + int32_t msgHeadSize = sizeof(int32_t) + sizeof(int64_t); + if (connBuf->cap == 0) { + connBuf->buf = taosMemoryMalloc(msgHeadSize); + if (connBuf->buf) { + connBuf->len = 0; + connBuf->cap = msgHeadSize; + connBuf->total = -1; + + buf->base = connBuf->buf; + buf->len = connBuf->cap; + } else { + fnError("udfc allocate buffer failure. size: %d", msgHeadSize); + buf->base = NULL; + buf->len = 0; + } + } else { + connBuf->cap = connBuf->total > connBuf->cap ? connBuf->total : connBuf->cap; + void *resultBuf = taosMemoryRealloc(connBuf->buf, connBuf->cap); + if (resultBuf) { + connBuf->buf = resultBuf; + buf->base = connBuf->buf + connBuf->len; + buf->len = connBuf->cap - connBuf->len; + } else { + fnError("udfc re-allocate buffer failure. size: %d", connBuf->cap); + buf->base = NULL; + buf->len = 0; + } + } + + fnTrace("conn buf cap - len - total : %d - %d - %d", connBuf->cap, connBuf->len, connBuf->total); + +} + +bool isUdfcUvMsgComplete(SClientConnBuf *connBuf) { + if (connBuf->total == -1 && connBuf->len >= sizeof(int32_t)) { + connBuf->total = *(int32_t *) (connBuf->buf); + } + if (connBuf->len == connBuf->cap && connBuf->total == connBuf->cap) { + fnTrace("udfc complete message is received, now handle it"); + return true; + } + return false; +} + +void udfcUvHandleRsp(SClientUvConn *conn) { + SClientConnBuf *connBuf = &conn->readBuf; + int64_t seqNum = *(int64_t *) (connBuf->buf + sizeof(int32_t)); // msglen then seqnum + + if (QUEUE_EMPTY(&conn->taskQueue)) { + fnError("udfc no task waiting for response on connection"); + return; + } + bool found = false; + SClientUvTaskNode *taskFound = NULL; + QUEUE* h = QUEUE_NEXT(&conn->taskQueue); + SClientUvTaskNode *task = QUEUE_DATA(h, SClientUvTaskNode, connTaskQueue); + + while (h != &conn->taskQueue) { + if (task->seqNum == seqNum) { + if (found == false) { + found = true; + taskFound = task; + } else { + fnError("udfc more than one task waiting for the same response"); + continue; + } + } + h = QUEUE_NEXT(h); + task = QUEUE_DATA(h, SClientUvTaskNode, connTaskQueue); + } + + if (taskFound) { + taskFound->rspBuf = uv_buf_init(connBuf->buf, connBuf->len); + QUEUE_REMOVE(&taskFound->connTaskQueue); + QUEUE_REMOVE(&taskFound->procTaskQueue); + uv_sem_post(&taskFound->taskSem); + } else { + fnError("no task is waiting for the response."); + } + connBuf->buf = NULL; + connBuf->total = -1; + connBuf->len = 0; + connBuf->cap = 0; +} + +void udfcUvHandleError(SClientUvConn *conn) { + while (!QUEUE_EMPTY(&conn->taskQueue)) { + QUEUE* h = QUEUE_HEAD(&conn->taskQueue); + SClientUvTaskNode *task = QUEUE_DATA(h, SClientUvTaskNode, connTaskQueue); + task->errCode = TSDB_CODE_UDF_PIPE_READ_ERR; + QUEUE_REMOVE(&task->connTaskQueue); + QUEUE_REMOVE(&task->procTaskQueue); + uv_sem_post(&task->taskSem); + } + + uv_close((uv_handle_t *) conn->pipe, onUdfcPipeClose); +} + +void onUdfcPipeRead(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf) { + fnTrace("udfc client %p, client read from pipe. nread: %zd", client, nread); + if (nread == 0) return; + + SClientUvConn *conn = client->data; + SClientConnBuf *connBuf = &conn->readBuf; + if (nread > 0) { + connBuf->len += nread; + if (isUdfcUvMsgComplete(connBuf)) { + udfcUvHandleRsp(conn); + } + + } + if (nread < 0) { + fnError("udfc client pipe %p read error: %zd, %s.", client, nread, uv_strerror(nread)); + if (nread == UV_EOF) { + fnError("\tudfc client pipe %p closed", client); + } + udfcUvHandleError(conn); } } @@ -1050,7 +1403,7 @@ int32_t udfcCreateUvTask(SClientUdfTask *task, int8_t uvTaskType, SClientUvTaskN request.teardown = task->_teardown.req; request.type = UDF_TASK_TEARDOWN; } else { - //TODO log and return error + fnError("udfc create uv task, invalid task type : %d", task->type); } int32_t bufLen = encodeUdfRequest(NULL, &request); request.msgLen = bufLen; @@ -1271,134 +1624,47 @@ int32_t udfcRunUdfUvTask(SClientUdfTask *task, int8_t uvTaskType) { SClientUvConn *conn = uvTask->pipe->data; conn->session = task->session; } - taosMemoryFree(uvTask); - uvTask = NULL; - return task->errCode; -} - -int32_t doSetupUdf(char udfName[], UdfcFuncHandle *funcHandle) { - if (gUdfdProxy.udfcState != UDFC_STATE_READY) { - return TSDB_CODE_UDF_INVALID_STATE; - } - SClientUdfTask *task = taosMemoryCalloc(1,sizeof(SClientUdfTask)); - task->errCode = 0; - task->session = taosMemoryCalloc(1, sizeof(SUdfcUvSession)); - task->session->udfc = &gUdfdProxy; - task->type = UDF_TASK_SETUP; - - SUdfSetupRequest *req = &task->_setup.req; - strncpy(req->udfName, udfName, TSDB_FUNC_NAME_LEN); - - int32_t errCode = udfcRunUdfUvTask(task, UV_TASK_CONNECT); - if (errCode != 0) { - fnError("failed to connect to pipe. udfName: %s, pipe: %s", udfName, (&gUdfdProxy)->udfdPipeName); - return TSDB_CODE_UDF_PIPE_CONNECT_ERR; - } - - udfcRunUdfUvTask(task, UV_TASK_REQ_RSP); - - SUdfSetupResponse *rsp = &task->_setup.rsp; - task->session->severHandle = rsp->udfHandle; - task->session->outputType = rsp->outputType; - task->session->outputLen = rsp->outputLen; - task->session->bufSize = rsp->bufSize; - strcpy(task->session->udfName, udfName); - if (task->errCode != 0) { - fnError("failed to setup udf. udfname: %s, err: %d", udfName, task->errCode) - } else { - fnInfo("sucessfully setup udf func handle. udfName: %s, handle: %p", udfName, task->session); - *funcHandle = task->session; - } - int32_t err = task->errCode; - taosMemoryFree(task); - return err; -} - -int compareUdfcFuncSub(const void* elem1, const void* elem2) { - SUdfcFuncStub *stub1 = (SUdfcFuncStub *)elem1; - SUdfcFuncStub *stub2 = (SUdfcFuncStub *)elem2; - return strcmp(stub1->udfName, stub2->udfName); -} - -int32_t acquireUdfFuncHandle(char* udfName, UdfcFuncHandle* pHandle) { - int32_t code = 0; - uv_mutex_lock(&gUdfdProxy.udfStubsMutex); - SUdfcFuncStub key = {0}; - strcpy(key.udfName, udfName); - int32_t stubIndex = taosArraySearchIdx(gUdfdProxy.udfStubs, &key, compareUdfcFuncSub, TD_EQ); - if (stubIndex != -1) { - SUdfcFuncStub *foundStub = taosArrayGet(gUdfdProxy.udfStubs, stubIndex); - UdfcFuncHandle handle = foundStub->handle; - if (handle != NULL && ((SUdfcUvSession*)handle)->udfUvPipe != NULL) { - *pHandle = foundStub->handle; - ++foundStub->refCount; - foundStub->lastRefTime = taosGetTimestampUs(); - uv_mutex_unlock(&gUdfdProxy.udfStubsMutex); - return 0; - } else { - fnInfo("invalid handle for %s, refCount: %d, last ref time: %"PRId64". remove it from cache", - udfName, foundStub->refCount, foundStub->lastRefTime); - taosArrayRemove(gUdfdProxy.udfStubs, stubIndex); - } - } - *pHandle = NULL; - code = doSetupUdf(udfName, pHandle); - if (code == TSDB_CODE_SUCCESS) { - SUdfcFuncStub stub = {0}; - strcpy(stub.udfName, udfName); - stub.handle = *pHandle; - ++stub.refCount; - stub.lastRefTime = taosGetTimestampUs(); - taosArrayPush(gUdfdProxy.udfStubs, &stub); - taosArraySort(gUdfdProxy.udfStubs, compareUdfcFuncSub); - } else { - *pHandle = NULL; - } - - uv_mutex_unlock(&gUdfdProxy.udfStubsMutex); - return code; + taosMemoryFree(uvTask); + uvTask = NULL; + return task->errCode; } -void releaseUdfFuncHandle(char* udfName) { - uv_mutex_lock(&gUdfdProxy.udfStubsMutex); - SUdfcFuncStub key = {0}; - strcpy(key.udfName, udfName); - SUdfcFuncStub *foundStub = taosArraySearch(gUdfdProxy.udfStubs, &key, compareUdfcFuncSub, TD_EQ); - if (!foundStub) { - return; +int32_t doSetupUdf(char udfName[], UdfcFuncHandle *funcHandle) { + if (gUdfdProxy.udfcState != UDFC_STATE_READY) { + return TSDB_CODE_UDF_INVALID_STATE; } - if (foundStub->refCount > 0) { - --foundStub->refCount; + SClientUdfTask *task = taosMemoryCalloc(1,sizeof(SClientUdfTask)); + task->errCode = 0; + task->session = taosMemoryCalloc(1, sizeof(SUdfcUvSession)); + task->session->udfc = &gUdfdProxy; + task->type = UDF_TASK_SETUP; + + SUdfSetupRequest *req = &task->_setup.req; + strncpy(req->udfName, udfName, TSDB_FUNC_NAME_LEN); + + int32_t errCode = udfcRunUdfUvTask(task, UV_TASK_CONNECT); + if (errCode != 0) { + fnError("failed to connect to pipe. udfName: %s, pipe: %s", udfName, (&gUdfdProxy)->udfdPipeName); + return TSDB_CODE_UDF_PIPE_CONNECT_ERR; } - uv_mutex_unlock(&gUdfdProxy.udfStubsMutex); -} -int32_t cleanUpUdfs() { - uv_mutex_lock(&gUdfdProxy.udfStubsMutex); - int32_t i = 0; - SArray* udfStubs = taosArrayInit(16, sizeof(SUdfcFuncStub)); - while (i < taosArrayGetSize(gUdfdProxy.udfStubs)) { - SUdfcFuncStub *stub = taosArrayGet(gUdfdProxy.udfStubs, i); - if (stub->refCount == 0) { - fnInfo("tear down udf. udf name: %s, handle: %p, ref count: %d", stub->udfName, stub->handle, stub->refCount); - doTeardownUdf(stub->handle); - } else { - fnInfo("udf still in use. udf name: %s, ref count: %d, last ref time: %"PRId64", handle: %p", - stub->udfName, stub->refCount, stub->lastRefTime, stub->handle); - UdfcFuncHandle handle = stub->handle; - if (handle != NULL && ((SUdfcUvSession*)handle)->udfUvPipe != NULL) { - taosArrayPush(udfStubs, stub); - } else { - fnInfo("udf invalid handle for %s, refCount: %d, last ref time: %"PRId64". remove it from cache", - stub->udfName, stub->refCount, stub->lastRefTime); - } - } - ++i; + udfcRunUdfUvTask(task, UV_TASK_REQ_RSP); + + SUdfSetupResponse *rsp = &task->_setup.rsp; + task->session->severHandle = rsp->udfHandle; + task->session->outputType = rsp->outputType; + task->session->outputLen = rsp->outputLen; + task->session->bufSize = rsp->bufSize; + strcpy(task->session->udfName, udfName); + if (task->errCode != 0) { + fnError("failed to setup udf. udfname: %s, err: %d", udfName, task->errCode) + } else { + fnInfo("sucessfully setup udf func handle. udfName: %s, handle: %p", udfName, task->session); + *funcHandle = task->session; } - taosArrayDestroy(gUdfdProxy.udfStubs); - gUdfdProxy.udfStubs = udfStubs; - uv_mutex_unlock(&gUdfdProxy.udfStubsMutex); - return 0; + int32_t err = task->errCode; + taosMemoryFree(task); + return err; } int32_t callUdf(UdfcFuncHandle handle, int8_t callType, SSDataBlock *input, SUdfInterBuf *state, SUdfInterBuf *state2, @@ -1524,29 +1790,6 @@ int32_t doCallUdfScalarFunc(UdfcFuncHandle handle, SScalarParam *input, int32_t return err; } - -int32_t callUdfScalarFunc(char *udfName, SScalarParam *input, int32_t numOfCols, SScalarParam *output) { - UdfcFuncHandle handle = NULL; - int32_t code = acquireUdfFuncHandle(udfName, &handle); - if (code != 0) { - return code; - } - SUdfcUvSession *session = handle; - code = doCallUdfScalarFunc(handle, input, numOfCols, output); - if (output->columnData == NULL) { - fnError("udfc scalar function calculate error. no column data"); - code = TSDB_CODE_UDF_INVALID_OUTPUT_TYPE; - } else { - if (session->outputType != output->columnData->info.type || session->outputLen != output->columnData->info.bytes) { - fnError("udfc scalar function calculate error. type mismatch. session type: %d(%d), output type: %d(%d)", session->outputType, - session->outputLen, output->columnData->info.type, output->columnData->info.bytes); - code = TSDB_CODE_UDF_INVALID_OUTPUT_TYPE; - } - } - releaseUdfFuncHandle(udfName); - return code; -} - int32_t doTeardownUdf(UdfcFuncHandle handle) { SUdfcUvSession *session = (SUdfcUvSession *) handle; @@ -1576,165 +1819,3 @@ int32_t doTeardownUdf(UdfcFuncHandle handle) { return err; } - -//memory layout |---SUdfAggRes----|-----final result-----|---inter result----| -typedef struct SUdfAggRes { - int8_t finalResNum; - int8_t interResNum; - char* finalResBuf; - char* interResBuf; -} SUdfAggRes; - -bool udfAggGetEnv(struct SFunctionNode* pFunc, SFuncExecEnv* pEnv) { - if (fmIsScalarFunc(pFunc->funcId)) { - return false; - } - pEnv->calcMemSize = sizeof(SUdfAggRes) + pFunc->node.resType.bytes + pFunc->udfBufSize; - return true; -} - -bool udfAggInit(struct SqlFunctionCtx *pCtx, struct SResultRowEntryInfo* pResultCellInfo) { - if (functionSetup(pCtx, pResultCellInfo) != true) { - return false; - } - UdfcFuncHandle handle; - int32_t udfCode = 0; - if ((udfCode = acquireUdfFuncHandle((char *)pCtx->udfName, &handle)) != 0) { - fnError("udfAggInit error. step doSetupUdf. udf code: %d", udfCode); - return false; - } - SUdfcUvSession *session = (SUdfcUvSession *)handle; - SUdfAggRes *udfRes = (SUdfAggRes*)GET_ROWCELL_INTERBUF(pResultCellInfo); - int32_t envSize = sizeof(SUdfAggRes) + session->outputLen + session->bufSize; - memset(udfRes, 0, envSize); - - udfRes->finalResBuf = (char*)udfRes + sizeof(SUdfAggRes); - udfRes->interResBuf = (char*)udfRes + sizeof(SUdfAggRes) + session->outputLen; - - SUdfInterBuf buf = {0}; - if ((udfCode = doCallUdfAggInit(handle, &buf)) != 0) { - fnError("udfAggInit error. step doCallUdfAggInit. udf code: %d", udfCode); - releaseUdfFuncHandle(pCtx->udfName); - return false; - } - udfRes->interResNum = buf.numOfResult; - if (buf.bufLen <= session->bufSize) { - memcpy(udfRes->interResBuf, buf.buf, buf.bufLen); - } else { - fnError("udfc inter buf size %d is greater than function bufSize %d", buf.bufLen, session->bufSize); - releaseUdfFuncHandle(pCtx->udfName); - return false; - } - releaseUdfFuncHandle(pCtx->udfName); - freeUdfInterBuf(&buf); - return true; -} - -int32_t udfAggProcess(struct SqlFunctionCtx *pCtx) { - int32_t udfCode = 0; - UdfcFuncHandle handle = 0; - if ((udfCode = acquireUdfFuncHandle((char *)pCtx->udfName, &handle)) != 0) { - fnError("udfAggProcess error. step acquireUdfFuncHandle. udf code: %d", udfCode); - return udfCode; - } - - SUdfcUvSession *session = handle; - SUdfAggRes* udfRes = (SUdfAggRes *)GET_ROWCELL_INTERBUF(GET_RES_INFO(pCtx)); - udfRes->finalResBuf = (char*)udfRes + sizeof(SUdfAggRes); - udfRes->interResBuf = (char*)udfRes + sizeof(SUdfAggRes) + session->outputLen; - - SInputColumnInfoData* pInput = &pCtx->input; - int32_t numOfCols = pInput->numOfInputCols; - int32_t start = pInput->startRowIndex; - int32_t numOfRows = pInput->numOfRows; - - - SSDataBlock tempBlock = {0}; - tempBlock.info.numOfCols = numOfCols; - tempBlock.info.rows = pInput->totalRows; - tempBlock.info.uid = pInput->uid; - bool hasVarCol = false; - tempBlock.pDataBlock = taosArrayInit(numOfCols, sizeof(SColumnInfoData)); - - for (int32_t i = 0; i < numOfCols; ++i) { - SColumnInfoData *col = pInput->pData[i]; - if (IS_VAR_DATA_TYPE(col->info.type)) { - hasVarCol = true; - } - taosArrayPush(tempBlock.pDataBlock, col); - } - tempBlock.info.hasVarCol = hasVarCol; - - SSDataBlock *inputBlock = blockDataExtractBlock(&tempBlock, start, numOfRows); - - SUdfInterBuf state = {.buf = udfRes->interResBuf, - .bufLen = session->bufSize, - .numOfResult = udfRes->interResNum}; - SUdfInterBuf newState = {0}; - - udfCode = doCallUdfAggProcess(session, inputBlock, &state, &newState); - if (udfCode != 0) { - fnError("udfAggProcess error. code: %d", udfCode); - newState.numOfResult = 0; - } else { - udfRes->interResNum = newState.numOfResult; - if (newState.bufLen <= session->bufSize) { - memcpy(udfRes->interResBuf, newState.buf, newState.bufLen); - } else { - fnError("udfc inter buf size %d is greater than function bufSize %d", newState.bufLen, session->bufSize); - udfCode = TSDB_CODE_UDF_INVALID_BUFSIZE; - } - } - if (newState.numOfResult == 1 || state.numOfResult == 1) { - GET_RES_INFO(pCtx)->numOfRes = 1; - } - - blockDataDestroy(inputBlock); - taosArrayDestroy(tempBlock.pDataBlock); - - releaseUdfFuncHandle(pCtx->udfName); - freeUdfInterBuf(&newState); - return udfCode; -} - -int32_t udfAggFinalize(struct SqlFunctionCtx *pCtx, SSDataBlock* pBlock) { - int32_t udfCode = 0; - UdfcFuncHandle handle = 0; - if ((udfCode = acquireUdfFuncHandle((char *)pCtx->udfName, &handle)) != 0) { - fnError("udfAggProcess error. step acquireUdfFuncHandle. udf code: %d", udfCode); - return udfCode; - } - - SUdfcUvSession *session = handle; - SUdfAggRes* udfRes = (SUdfAggRes *)GET_ROWCELL_INTERBUF(GET_RES_INFO(pCtx)); - udfRes->finalResBuf = (char*)udfRes + sizeof(SUdfAggRes); - udfRes->interResBuf = (char*)udfRes + sizeof(SUdfAggRes) + session->outputLen; - - - SUdfInterBuf resultBuf = {0}; - SUdfInterBuf state = {.buf = udfRes->interResBuf, - .bufLen = session->bufSize, - .numOfResult = udfRes->interResNum}; - int32_t udfCallCode= 0; - udfCallCode= doCallUdfAggFinalize(session, &state, &resultBuf); - if (udfCallCode != 0) { - fnError("udfAggFinalize error. doCallUdfAggFinalize step. udf code:%d", udfCallCode); - GET_RES_INFO(pCtx)->numOfRes = 0; - } else { - if (resultBuf.bufLen <= session->outputLen) { - memcpy(udfRes->finalResBuf, resultBuf.buf, session->outputLen); - udfRes->finalResNum = resultBuf.numOfResult; - GET_RES_INFO(pCtx)->numOfRes = udfRes->finalResNum; - } else { - fnError("udfc inter buf size %d is greater than function output size %d", resultBuf.bufLen, session->outputLen); - GET_RES_INFO(pCtx)->numOfRes = 0; - udfCallCode = TSDB_CODE_UDF_INVALID_OUTPUT_TYPE; - } - } - - freeUdfInterBuf(&resultBuf); - - int32_t numOfResults = functionFinalizeWithResultBuf(pCtx, pBlock, udfRes->finalResBuf); - releaseUdfFuncHandle(pCtx->udfName); - return udfCallCode == 0 ? numOfResults : udfCallCode; -} diff --git a/source/libs/function/src/udfd.c b/source/libs/function/src/udfd.c index 006914bf65ce9933eeeb49c3cbca16fa093cdeda..83dcb6d7f010936f46e1f9d375be517d8d06432b 100644 --- a/source/libs/function/src/udfd.c +++ b/source/libs/function/src/udfd.c @@ -27,16 +27,16 @@ #include "trpc.h" typedef struct SUdfdContext { - uv_loop_t *loop; + uv_loop_t * loop; uv_pipe_t ctrlPipe; uv_signal_t intrSignal; char listenPipeName[PATH_MAX + UDF_LISTEN_PIPE_NAME_LEN + 2]; uv_pipe_t listeningPipe; - void *clientRpc; + void * clientRpc; SCorEpSet mgmtEp; uv_mutex_t udfsMutex; - SHashObj *udfsHash; + SHashObj * udfsHash; bool printVersion; } SUdfdContext; @@ -45,7 +45,7 @@ SUdfdContext global; typedef struct SUdfdUvConn { uv_stream_t *client; - char *inputBuf; + char * inputBuf; int32_t inputLen; int32_t inputCap; int32_t inputTotal; @@ -65,25 +65,25 @@ typedef struct SUdf { uv_mutex_t lock; uv_cond_t condReady; - char name[TSDB_FUNC_NAME_LEN]; - int8_t funcType; - int8_t scriptType; - int8_t outputType; + char name[TSDB_FUNC_NAME_LEN]; + int8_t funcType; + int8_t scriptType; + int8_t outputType; int32_t outputLen; int32_t bufSize; - char path[PATH_MAX]; + char path[PATH_MAX]; - uv_lib_t lib; + uv_lib_t lib; - TUdfScalarProcFunc scalarProcFunc; + TUdfScalarProcFunc scalarProcFunc; - TUdfAggStartFunc aggStartFunc; - TUdfAggProcessFunc aggProcFunc; - TUdfAggFinishFunc aggFinishFunc; + TUdfAggStartFunc aggStartFunc; + TUdfAggProcessFunc aggProcFunc; + TUdfAggFinishFunc aggFinishFunc; - TUdfInitFunc initFunc; - TUdfDestroyFunc destroyFunc; + TUdfInitFunc initFunc; + TUdfDestroyFunc destroyFunc; } SUdf; // TODO: add private udf structure. @@ -98,11 +98,268 @@ typedef enum EUdfdRpcReqRspType { typedef struct SUdfdRpcSendRecvInfo { EUdfdRpcReqRspType rpcType; - int32_t code; - void* param; - uv_sem_t resultSem; + int32_t code; + void * param; + uv_sem_t resultSem; } SUdfdRpcSendRecvInfo; +static void udfdProcessRpcRsp(void *parent, SRpcMsg *pMsg, SEpSet *pEpSet); +static int32_t udfdFillUdfInfoFromMNode(void *clientRpc, char *udfName, SUdf *udf); +static int32_t udfdConnectToMnode(); +static int32_t udfdLoadUdf(char *udfName, SUdf *udf); +static bool udfdRpcRfp(int32_t code); +static int initEpSetFromCfg(const char *firstEp, const char *secondEp, SCorEpSet *pEpSet); +static int32_t udfdOpenClientRpc(); +static int32_t udfdCloseClientRpc(); + +static void udfdProcessSetupRequest(SUvUdfWork *uvUdf, SUdfRequest *request); +static void udfdProcessCallRequest(SUvUdfWork *uvUdf, SUdfRequest *request); +static void udfdProcessTeardownRequest(SUvUdfWork *uvUdf, SUdfRequest *request); +static void udfdProcessRequest(uv_work_t *req); +static void udfdOnWrite(uv_write_t *req, int status); +static void udfdSendResponse(uv_work_t *work, int status); +static void udfdAllocBuffer(uv_handle_t *handle, size_t suggestedSize, uv_buf_t *buf); +static bool isUdfdUvMsgComplete(SUdfdUvConn *pipe); +static void udfdHandleRequest(SUdfdUvConn *conn); +static void udfdPipeCloseCb(uv_handle_t *pipe); +static void udfdUvHandleError(SUdfdUvConn *conn) { uv_close((uv_handle_t *)conn->client, udfdPipeCloseCb); } +static void udfdPipeRead(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf); +static void udfdOnNewConnection(uv_stream_t *server, int status); + +static void udfdIntrSignalHandler(uv_signal_t *handle, int signum); +static int32_t removeListeningPipe(); + +static void udfdPrintVersion(); +static int32_t udfdParseArgs(int32_t argc, char *argv[]); +static int32_t udfdInitLog(); + +static void udfdCtrlAllocBufCb(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf); +static void udfdCtrlReadCb(uv_stream_t *q, ssize_t nread, const uv_buf_t *buf); +static int32_t udfdUvInit(); +static void udfdCloseWalkCb(uv_handle_t *handle, void *arg); +static int32_t udfdRun(); +static void udfdConnectMnodeThreadFunc(void* args); + +void udfdProcessRequest(uv_work_t *req) { + SUvUdfWork *uvUdf = (SUvUdfWork *)(req->data); + SUdfRequest request = {0}; + decodeUdfRequest(uvUdf->input.base, &request); + + switch (request.type) { + case UDF_TASK_SETUP: { + udfdProcessSetupRequest(uvUdf, &request); + break; + } + + case UDF_TASK_CALL: { + udfdProcessCallRequest(uvUdf, &request); + break; + } + case UDF_TASK_TEARDOWN: { + udfdProcessTeardownRequest(uvUdf, &request); + break; + } + default: { + break; + } + } +} + +void udfdProcessSetupRequest(SUvUdfWork *uvUdf, SUdfRequest *request) { + // TODO: tracable id from client. connect, setup, call, teardown + fnInfo("setup request. seq num: %" PRId64 ", udf name: %s", request->seqNum, request->setup.udfName); + SUdfSetupRequest *setup = &request->setup; + int32_t code = TSDB_CODE_SUCCESS; + SUdf * udf = NULL; + uv_mutex_lock(&global.udfsMutex); + SUdf **udfInHash = taosHashGet(global.udfsHash, request->setup.udfName, strlen(request->setup.udfName)); + if (udfInHash) { + ++(*udfInHash)->refCount; + udf = *udfInHash; + uv_mutex_unlock(&global.udfsMutex); + } else { + SUdf *udfNew = taosMemoryCalloc(1, sizeof(SUdf)); + udfNew->refCount = 1; + udfNew->state = UDF_STATE_INIT; + + uv_mutex_init(&udfNew->lock); + uv_cond_init(&udfNew->condReady); + udf = udfNew; + taosHashPut(global.udfsHash, request->setup.udfName, strlen(request->setup.udfName), &udfNew, sizeof(&udfNew)); + uv_mutex_unlock(&global.udfsMutex); + } + + uv_mutex_lock(&udf->lock); + if (udf->state == UDF_STATE_INIT) { + udf->state = UDF_STATE_LOADING; + code = udfdLoadUdf(setup->udfName, udf); + if (udf->initFunc) { + udf->initFunc(); + } + udf->state = UDF_STATE_READY; + uv_cond_broadcast(&udf->condReady); + uv_mutex_unlock(&udf->lock); + } else { + while (udf->state != UDF_STATE_READY) { + uv_cond_wait(&udf->condReady, &udf->lock); + } + uv_mutex_unlock(&udf->lock); + } + SUdfcFuncHandle *handle = taosMemoryMalloc(sizeof(SUdfcFuncHandle)); + handle->udf = udf; + + SUdfResponse rsp; + rsp.seqNum = request->seqNum; + rsp.type = request->type; + rsp.code = code; + rsp.setupRsp.udfHandle = (int64_t)(handle); + rsp.setupRsp.outputType = udf->outputType; + rsp.setupRsp.outputLen = udf->outputLen; + rsp.setupRsp.bufSize = udf->bufSize; + + int32_t len = encodeUdfResponse(NULL, &rsp); + rsp.msgLen = len; + void *bufBegin = taosMemoryMalloc(len); + void *buf = bufBegin; + encodeUdfResponse(&buf, &rsp); + + uvUdf->output = uv_buf_init(bufBegin, len); + + taosMemoryFree(uvUdf->input.base); + return; +} + +void udfdProcessCallRequest(SUvUdfWork *uvUdf, SUdfRequest *request) { + SUdfCallRequest *call = &request->call; + fnDebug("%" PRId64 "call request. call type %d, handle: %" PRIx64, request->seqNum, call->callType, call->udfHandle); + SUdfcFuncHandle * handle = (SUdfcFuncHandle *)(call->udfHandle); + SUdf * udf = handle->udf; + SUdfResponse response = {0}; + SUdfResponse * rsp = &response; + SUdfCallResponse *subRsp = &rsp->callRsp; + + int32_t code = TSDB_CODE_SUCCESS; + switch (call->callType) { + case TSDB_UDF_CALL_SCALA_PROC: { + SUdfColumn output = {0}; + + SUdfDataBlock input = {0}; + convertDataBlockToUdfDataBlock(&call->block, &input); + code = udf->scalarProcFunc(&input, &output); + freeUdfDataDataBlock(&input); + convertUdfColumnToDataBlock(&output, &response.callRsp.resultData); + freeUdfColumn(&output); + break; + } + case TSDB_UDF_CALL_AGG_INIT: { + SUdfInterBuf outBuf = {.buf = taosMemoryMalloc(udf->bufSize), .bufLen = udf->bufSize, .numOfResult = 0}; + udf->aggStartFunc(&outBuf); + subRsp->resultBuf = outBuf; + break; + } + case TSDB_UDF_CALL_AGG_PROC: { + SUdfDataBlock input = {0}; + convertDataBlockToUdfDataBlock(&call->block, &input); + SUdfInterBuf outBuf = {.buf = taosMemoryMalloc(udf->bufSize), .bufLen = udf->bufSize, .numOfResult = 0}; + code = udf->aggProcFunc(&input, &call->interBuf, &outBuf); + freeUdfInterBuf(&call->interBuf); + freeUdfDataDataBlock(&input); + subRsp->resultBuf = outBuf; + + break; + } + case TSDB_UDF_CALL_AGG_FIN: { + SUdfInterBuf outBuf = {.buf = taosMemoryMalloc(udf->bufSize), .bufLen = udf->bufSize, .numOfResult = 0}; + code = udf->aggFinishFunc(&call->interBuf, &outBuf); + freeUdfInterBuf(&call->interBuf); + subRsp->resultBuf = outBuf; + break; + } + default: + break; + } + + rsp->seqNum = request->seqNum; + rsp->type = request->type; + rsp->code = code; + subRsp->callType = call->callType; + + int32_t len = encodeUdfResponse(NULL, rsp); + rsp->msgLen = len; + void *bufBegin = taosMemoryMalloc(len); + void *buf = bufBegin; + encodeUdfResponse(&buf, rsp); + uvUdf->output = uv_buf_init(bufBegin, len); + + switch (call->callType) { + case TSDB_UDF_CALL_SCALA_PROC: { + tDeleteSSDataBlock(&call->block); + tDeleteSSDataBlock(&subRsp->resultData); + break; + } + case TSDB_UDF_CALL_AGG_INIT: { + freeUdfInterBuf(&subRsp->resultBuf); + break; + } + case TSDB_UDF_CALL_AGG_PROC: { + tDeleteSSDataBlock(&call->block); + freeUdfInterBuf(&subRsp->resultBuf); + break; + } + case TSDB_UDF_CALL_AGG_FIN: { + freeUdfInterBuf(&subRsp->resultBuf); + break; + } + default: + break; + } + + taosMemoryFree(uvUdf->input.base); + return; +} + +void udfdProcessTeardownRequest(SUvUdfWork *uvUdf, SUdfRequest *request) { + SUdfTeardownRequest *teardown = &request->teardown; + fnInfo("teardown. seq number: %" PRId64 ", handle:%" PRIx64, request->seqNum, teardown->udfHandle); + SUdfcFuncHandle *handle = (SUdfcFuncHandle *)(teardown->udfHandle); + SUdf * udf = handle->udf; + bool unloadUdf = false; + int32_t code = TSDB_CODE_SUCCESS; + + uv_mutex_lock(&global.udfsMutex); + udf->refCount--; + if (udf->refCount == 0) { + unloadUdf = true; + taosHashRemove(global.udfsHash, udf->name, strlen(udf->name)); + } + uv_mutex_unlock(&global.udfsMutex); + if (unloadUdf) { + uv_cond_destroy(&udf->condReady); + uv_mutex_destroy(&udf->lock); + if (udf->destroyFunc) { + (udf->destroyFunc)(); + } + uv_dlclose(&udf->lib); + taosMemoryFree(udf); + } + taosMemoryFree(handle); + + SUdfResponse response; + SUdfResponse *rsp = &response; + rsp->seqNum = request->seqNum; + rsp->type = request->type; + rsp->code = code; + int32_t len = encodeUdfResponse(NULL, rsp); + rsp->msgLen = len; + void *bufBegin = taosMemoryMalloc(len); + void *buf = bufBegin; + encodeUdfResponse(&buf, rsp); + uvUdf->output = uv_buf_init(bufBegin, len); + + taosMemoryFree(uvUdf->input.base); + return; +} + void udfdProcessRpcRsp(void *parent, SRpcMsg *pMsg, SEpSet *pEpSet) { SUdfdRpcSendRecvInfo *msgInfo = (SUdfdRpcSendRecvInfo *)pMsg->info.ahandle; ASSERT(pMsg->info.ahandle != NULL); @@ -136,7 +393,7 @@ void udfdProcessRpcRsp(void *parent, SRpcMsg *pMsg, SEpSet *pEpSet) { tDeserializeSRetrieveFuncRsp(pMsg->pCont, pMsg->contLen, &retrieveRsp); SFuncInfo *pFuncInfo = (SFuncInfo *)taosArrayGet(retrieveRsp.pFuncInfos, 0); - SUdf* udf = msgInfo->param; + SUdf * udf = msgInfo->param; udf->funcType = pFuncInfo->funcType; udf->scriptType = pFuncInfo->scriptType; udf->outputType = pFuncInfo->outputType; @@ -144,10 +401,14 @@ void udfdProcessRpcRsp(void *parent, SRpcMsg *pMsg, SEpSet *pEpSet) { udf->bufSize = pFuncInfo->bufSize; char path[PATH_MAX] = {0}; - snprintf(path, sizeof(path), "%s/lib%s.so", "/tmp", pFuncInfo->name); - TdFilePtr file = taosOpenFile(path, TD_FILE_CREATE | TD_FILE_WRITE | TD_FILE_READ | TD_FILE_TRUNC | TD_FILE_AUTO_DEL); - // TODO check for failure of flush to disk - taosWriteFile(file, pFuncInfo->pCode, pFuncInfo->codeSize); + snprintf(path, sizeof(path), "%s/lib%s.so", TD_TMP_DIR_PATH, pFuncInfo->name); + TdFilePtr file = + taosOpenFile(path, TD_FILE_CREATE | TD_FILE_WRITE | TD_FILE_READ | TD_FILE_TRUNC | TD_FILE_AUTO_DEL); + int64_t count = taosWriteFile(file, pFuncInfo->pCode, pFuncInfo->codeSize); + if (count != pFuncInfo->codeSize) { + fnError("udfd write udf shared library failed"); + msgInfo->code = TSDB_CODE_FILE_CORRUPTED; + } taosCloseFile(&file); strncpy(udf->path, path, strlen(path)); tFreeSFuncInfo(pFuncInfo); @@ -168,11 +429,11 @@ int32_t udfdFillUdfInfoFromMNode(void *clientRpc, char *udfName, SUdf *udf) { taosArrayPush(retrieveReq.pFuncNames, udfName); int32_t contLen = tSerializeSRetrieveFuncReq(NULL, 0, &retrieveReq); - void *pReq = rpcMallocCont(contLen); + void * pReq = rpcMallocCont(contLen); tSerializeSRetrieveFuncReq(pReq, contLen, &retrieveReq); taosArrayDestroy(retrieveReq.pFuncNames); - SUdfdRpcSendRecvInfo* msgInfo = taosMemoryCalloc(1, sizeof(SUdfdRpcSendRecvInfo)); + SUdfdRpcSendRecvInfo *msgInfo = taosMemoryCalloc(1, sizeof(SUdfdRpcSendRecvInfo)); msgInfo->rpcType = UDFD_RPC_RETRIVE_FUNC; msgInfo->param = udf; uv_sem_init(&msgInfo->resultSem, 0); @@ -194,7 +455,7 @@ int32_t udfdFillUdfInfoFromMNode(void *clientRpc, char *udfName, SUdf *udf) { int32_t udfdConnectToMnode() { SConnectReq connReq = {0}; connReq.connType = CONN_TYPE__UDFD; - tstrncpy(connReq.app, "udfd",sizeof(connReq.app)); + tstrncpy(connReq.app, "udfd", sizeof(connReq.app)); tstrncpy(connReq.user, TSDB_DEFAULT_USER, sizeof(connReq.user)); char pass[TSDB_PASSWORD_LEN + 1] = {0}; taosEncryptPass_c((uint8_t *)(TSDB_DEFAULT_PASS), strlen(TSDB_DEFAULT_PASS), pass); @@ -203,7 +464,7 @@ int32_t udfdConnectToMnode() { connReq.startTime = htobe64(taosGetTimestampMs()); int32_t contLen = tSerializeSConnectReq(NULL, 0, &connReq); - void* pReq = rpcMallocCont(contLen); + void * pReq = rpcMallocCont(contLen); tSerializeSConnectReq(pReq, contLen, &connReq); SUdfdRpcSendRecvInfo *msgInfo = taosMemoryCalloc(1, sizeof(SUdfdRpcSendRecvInfo)); @@ -240,17 +501,17 @@ int32_t udfdLoadUdf(char *udfName, SUdf *udf) { return TSDB_CODE_UDF_LOAD_UDF_FAILURE; } - char initFuncName[TSDB_FUNC_NAME_LEN+5] = {0}; + char initFuncName[TSDB_FUNC_NAME_LEN + 5] = {0}; char *initSuffix = "_init"; strcpy(initFuncName, udfName); strncat(initFuncName, initSuffix, strlen(initSuffix)); - uv_dlsym(&udf->lib, initFuncName, (void**)(&udf->initFunc)); + uv_dlsym(&udf->lib, initFuncName, (void **)(&udf->initFunc)); - char destroyFuncName[TSDB_FUNC_NAME_LEN+5] = {0}; + char destroyFuncName[TSDB_FUNC_NAME_LEN + 5] = {0}; char *destroySuffix = "_destroy"; strcpy(destroyFuncName, udfName); strncat(destroyFuncName, destroySuffix, strlen(destroySuffix)); - uv_dlsym(&udf->lib, destroyFuncName, (void**)(&udf->destroyFunc)); + uv_dlsym(&udf->lib, destroyFuncName, (void **)(&udf->destroyFunc)); if (udf->funcType == TSDB_FUNC_TYPE_SCALAR) { char processFuncName[TSDB_FUNC_NAME_LEN] = {0}; @@ -270,237 +531,82 @@ int32_t udfdLoadUdf(char *udfName, SUdf *udf) { strncpy(finishFuncName, processFuncName, strlen(processFuncName)); strncat(finishFuncName, finishSuffix, strlen(finishSuffix)); uv_dlsym(&udf->lib, finishFuncName, (void **)(&udf->aggFinishFunc)); - //TODO: merge + // TODO: merge } return 0; } - -void udfdProcessSetupRequest(SUvUdfWork* uvUdf, SUdfRequest* request) { - // TODO: tracable id from client. connect, setup, call, teardown - fnInfo( "setup request. seq num: %" PRId64 ", udf name: %s", request->seqNum, request->setup.udfName); - SUdfSetupRequest *setup = &request->setup; - int32_t code = TSDB_CODE_SUCCESS; - SUdf *udf = NULL; - uv_mutex_lock(&global.udfsMutex); - SUdf **udfInHash = taosHashGet(global.udfsHash, request->setup.udfName, strlen(request->setup.udfName)); - if (udfInHash) { - ++(*udfInHash)->refCount; - udf = *udfInHash; - uv_mutex_unlock(&global.udfsMutex); - } else { - SUdf *udfNew = taosMemoryCalloc(1, sizeof(SUdf)); - udfNew->refCount = 1; - udfNew->state = UDF_STATE_INIT; - - uv_mutex_init(&udfNew->lock); - uv_cond_init(&udfNew->condReady); - udf = udfNew; - taosHashPut(global.udfsHash, request->setup.udfName, strlen(request->setup.udfName), &udfNew, sizeof(&udfNew)); - uv_mutex_unlock(&global.udfsMutex); - } - - uv_mutex_lock(&udf->lock); - if (udf->state == UDF_STATE_INIT) { - udf->state = UDF_STATE_LOADING; - code = udfdLoadUdf(setup->udfName, udf); - if (udf->initFunc) { - udf->initFunc(); - } - udf->state = UDF_STATE_READY; - uv_cond_broadcast(&udf->condReady); - uv_mutex_unlock(&udf->lock); - } else { - while (udf->state != UDF_STATE_READY) { - uv_cond_wait(&udf->condReady, &udf->lock); - } - uv_mutex_unlock(&udf->lock); - } - SUdfcFuncHandle *handle = taosMemoryMalloc(sizeof(SUdfcFuncHandle)); - handle->udf = udf; - - SUdfResponse rsp; - rsp.seqNum = request->seqNum; - rsp.type = request->type; - rsp.code = code; - rsp.setupRsp.udfHandle = (int64_t)(handle); - rsp.setupRsp.outputType = udf->outputType; - rsp.setupRsp.outputLen = udf->outputLen; - rsp.setupRsp.bufSize = udf->bufSize; - - int32_t len = encodeUdfResponse(NULL, &rsp); - rsp.msgLen = len; - void *bufBegin = taosMemoryMalloc(len); - void *buf = bufBegin; - encodeUdfResponse(&buf, &rsp); - - uvUdf->output = uv_buf_init(bufBegin, len); - - taosMemoryFree(uvUdf->input.base); - return; +static bool udfdRpcRfp(int32_t code) { + if (code == TSDB_CODE_RPC_REDIRECT) { + return true; + } else { + return false; + } } -void udfdProcessCallRequest(SUvUdfWork *uvUdf, SUdfRequest *request) { - SUdfCallRequest *call = &request->call; - fnDebug("%" PRId64 "call request. call type %d, handle: %" PRIx64, request->seqNum, call->callType, - call->udfHandle); - SUdfcFuncHandle *handle = (SUdfcFuncHandle *)(call->udfHandle); - SUdf *udf = handle->udf; - SUdfResponse response = {0}; - SUdfResponse *rsp = &response; - SUdfCallResponse *subRsp = &rsp->callRsp; +int initEpSetFromCfg(const char *firstEp, const char *secondEp, SCorEpSet *pEpSet) { + pEpSet->version = 0; - int32_t code = TSDB_CODE_SUCCESS; - switch(call->callType) { - case TSDB_UDF_CALL_SCALA_PROC: { - SUdfColumn output = {0}; + // init mnode ip set + SEpSet *mgmtEpSet = &(pEpSet->epSet); + mgmtEpSet->numOfEps = 0; + mgmtEpSet->inUse = 0; - SUdfDataBlock input = {0}; - convertDataBlockToUdfDataBlock(&call->block, &input); - code = udf->scalarProcFunc(&input, &output); - freeUdfDataDataBlock(&input); - convertUdfColumnToDataBlock(&output, &response.callRsp.resultData); - freeUdfColumn(&output); - break; - } - case TSDB_UDF_CALL_AGG_INIT: { - SUdfInterBuf outBuf = {.buf = taosMemoryMalloc(udf->bufSize), - .bufLen= udf->bufSize, - .numOfResult = 0}; - udf->aggStartFunc(&outBuf); - subRsp->resultBuf = outBuf; - break; + if (firstEp && firstEp[0] != 0) { + if (strlen(firstEp) >= TSDB_EP_LEN) { + terrno = TSDB_CODE_TSC_INVALID_FQDN; + return -1; } - case TSDB_UDF_CALL_AGG_PROC: { - SUdfDataBlock input = {0}; - convertDataBlockToUdfDataBlock(&call->block, &input); - SUdfInterBuf outBuf = {.buf = taosMemoryMalloc(udf->bufSize), - .bufLen= udf->bufSize, - .numOfResult = 0}; - code = udf->aggProcFunc(&input, &call->interBuf, &outBuf); - freeUdfInterBuf(&call->interBuf); - freeUdfDataDataBlock(&input); - subRsp->resultBuf = outBuf; - break; - } - case TSDB_UDF_CALL_AGG_FIN: { - SUdfInterBuf outBuf = {.buf = taosMemoryMalloc(udf->bufSize), - .bufLen= udf->bufSize, - .numOfResult = 0}; - code = udf->aggFinishFunc(&call->interBuf, &outBuf); - freeUdfInterBuf(&call->interBuf); - subRsp->resultBuf = outBuf; - break; + int32_t code = taosGetFqdnPortFromEp(firstEp, &mgmtEpSet->eps[0]); + if (code != TSDB_CODE_SUCCESS) { + terrno = TSDB_CODE_TSC_INVALID_FQDN; + return terrno; } - default: - break; - } - rsp->seqNum = request->seqNum; - rsp->type = request->type; - rsp->code = code; - subRsp->callType = call->callType; - - int32_t len = encodeUdfResponse(NULL, rsp); - rsp->msgLen = len; - void *bufBegin = taosMemoryMalloc(len); - void *buf = bufBegin; - encodeUdfResponse(&buf, rsp); - uvUdf->output = uv_buf_init(bufBegin, len); + mgmtEpSet->numOfEps++; + } - switch (call->callType) { - case TSDB_UDF_CALL_SCALA_PROC: { - tDeleteSSDataBlock(&call->block); - tDeleteSSDataBlock(&subRsp->resultData); - break; - } - case TSDB_UDF_CALL_AGG_INIT: { - freeUdfInterBuf(&subRsp->resultBuf); - break; - } - case TSDB_UDF_CALL_AGG_PROC: { - tDeleteSSDataBlock(&call->block); - freeUdfInterBuf(&subRsp->resultBuf); - break; - } - case TSDB_UDF_CALL_AGG_FIN: { - freeUdfInterBuf(&subRsp->resultBuf); - break; + if (secondEp && secondEp[0] != 0) { + if (strlen(secondEp) >= TSDB_EP_LEN) { + terrno = TSDB_CODE_TSC_INVALID_FQDN; + return -1; } - default: - break; + taosGetFqdnPortFromEp(secondEp, &mgmtEpSet->eps[mgmtEpSet->numOfEps]); + mgmtEpSet->numOfEps++; } - taosMemoryFree(uvUdf->input.base); - return; -} - -void udfdProcessTeardownRequest(SUvUdfWork* uvUdf, SUdfRequest* request) { - SUdfTeardownRequest *teardown = &request->teardown; - fnInfo("teardown. seq number: %" PRId64 ", handle:%" PRIx64, request->seqNum, teardown->udfHandle); - SUdfcFuncHandle *handle = (SUdfcFuncHandle *)(teardown->udfHandle); - SUdf *udf = handle->udf; - bool unloadUdf = false; - int32_t code = TSDB_CODE_SUCCESS; - - uv_mutex_lock(&global.udfsMutex); - udf->refCount--; - if (udf->refCount == 0) { - unloadUdf = true; - taosHashRemove(global.udfsHash, udf->name, strlen(udf->name)); - } - uv_mutex_unlock(&global.udfsMutex); - if (unloadUdf) { - uv_cond_destroy(&udf->condReady); - uv_mutex_destroy(&udf->lock); - if (udf->destroyFunc) { - (udf->destroyFunc)(); - } - uv_dlclose(&udf->lib); - taosMemoryFree(udf); + if (mgmtEpSet->numOfEps == 0) { + terrno = TSDB_CODE_TSC_INVALID_FQDN; + return -1; } - taosMemoryFree(handle); - SUdfResponse response; - SUdfResponse *rsp = &response; - rsp->seqNum = request->seqNum; - rsp->type = request->type; - rsp->code = code; - int32_t len = encodeUdfResponse(NULL, rsp); - rsp->msgLen = len; - void *bufBegin = taosMemoryMalloc(len); - void *buf = bufBegin; - encodeUdfResponse(&buf, rsp); - uvUdf->output = uv_buf_init(bufBegin, len); - - taosMemoryFree(uvUdf->input.base); - return; + return 0; } -void udfdProcessRequest(uv_work_t *req) { - SUvUdfWork *uvUdf = (SUvUdfWork *)(req->data); - SUdfRequest request = {0}; - decodeUdfRequest(uvUdf->input.base, &request); - - switch (request.type) { - case UDF_TASK_SETUP: { - udfdProcessSetupRequest(uvUdf, &request); - break; - } +int32_t udfdOpenClientRpc() { + SRpcInit rpcInit = {0}; + rpcInit.label = "UDFD"; + rpcInit.numOfThreads = 1; + rpcInit.cfp = (RpcCfp)udfdProcessRpcRsp; + rpcInit.sessions = 1024; + rpcInit.connType = TAOS_CONN_CLIENT; + rpcInit.idleTime = tsShellActivityTimer * 1000; + rpcInit.user = TSDB_DEFAULT_USER; + rpcInit.parent = &global; + rpcInit.rfp = udfdRpcRfp; - case UDF_TASK_CALL: { - udfdProcessCallRequest(uvUdf, &request); - break; - } - case UDF_TASK_TEARDOWN: { - udfdProcessTeardownRequest(uvUdf, &request); - break; - } - default: { - break; - } + global.clientRpc = rpcOpen(&rpcInit); + if (global.clientRpc == NULL) { + fnError("failed to init dnode rpc client"); + return -1; } + return 0; +} + +int32_t udfdCloseClientRpc() { + rpcClose(global.clientRpc); + return 0; } void udfdOnWrite(uv_write_t *req, int status) { @@ -536,7 +642,7 @@ void udfdAllocBuffer(uv_handle_t *handle, size_t suggestedSize, uv_buf_t *buf) { buf->base = ctx->inputBuf; buf->len = ctx->inputCap; } else { - // TODO: log error + fnError("udfd can not allocate enough memory") buf->base = NULL; buf->len = 0; } @@ -548,7 +654,7 @@ void udfdAllocBuffer(uv_handle_t *handle, size_t suggestedSize, uv_buf_t *buf) { buf->base = ctx->inputBuf + ctx->inputLen; buf->len = ctx->inputCap - ctx->inputLen; } else { - // TODO: log error + fnError("udfd can not allocate enough memory") buf->base = NULL; buf->len = 0; } @@ -568,7 +674,7 @@ bool isUdfdUvMsgComplete(SUdfdUvConn *pipe) { } void udfdHandleRequest(SUdfdUvConn *conn) { - uv_work_t *work = taosMemoryMalloc(sizeof(uv_work_t)); + uv_work_t * work = taosMemoryMalloc(sizeof(uv_work_t)); SUvUdfWork *udfWork = taosMemoryMalloc(sizeof(SUvUdfWork)); udfWork->client = conn->client; udfWork->input = uv_buf_init(conn->inputBuf, conn->inputLen); @@ -587,8 +693,6 @@ void udfdPipeCloseCb(uv_handle_t *pipe) { taosMemoryFree(conn); } -void udfdUvHandleError(SUdfdUvConn *conn) { uv_close((uv_handle_t *)conn->client, udfdPipeCloseCb); } - void udfdPipeRead(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf) { fnDebug("udf read %zd bytes from client", nread); if (nread == 0) return; @@ -606,7 +710,7 @@ void udfdPipeRead(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf) { } if (nread < 0) { - fnDebug("Receive error %s", uv_err_name(nread)); + fnError("Receive error %s", uv_err_name(nread)); if (nread == UV_EOF) { // TODO check more when close } else { @@ -645,98 +749,6 @@ void udfdIntrSignalHandler(uv_signal_t *handle, int signum) { uv_stop(global.loop); } -static bool udfdRpcRfp(int32_t code) { - if (code == TSDB_CODE_RPC_REDIRECT) { - return true; - } else { - return false; - } -} - -int initEpSetFromCfg(const char* firstEp, const char* secondEp, SCorEpSet* pEpSet) { - pEpSet->version = 0; - - // init mnode ip set - SEpSet* mgmtEpSet = &(pEpSet->epSet); - mgmtEpSet->numOfEps = 0; - mgmtEpSet->inUse = 0; - - if (firstEp && firstEp[0] != 0) { - if (strlen(firstEp) >= TSDB_EP_LEN) { - terrno = TSDB_CODE_TSC_INVALID_FQDN; - return -1; - } - - int32_t code = taosGetFqdnPortFromEp(firstEp, &mgmtEpSet->eps[0]); - if (code != TSDB_CODE_SUCCESS) { - terrno = TSDB_CODE_TSC_INVALID_FQDN; - return terrno; - } - - mgmtEpSet->numOfEps++; - } - - if (secondEp && secondEp[0] != 0) { - if (strlen(secondEp) >= TSDB_EP_LEN) { - terrno = TSDB_CODE_TSC_INVALID_FQDN; - return -1; - } - - taosGetFqdnPortFromEp(secondEp, &mgmtEpSet->eps[mgmtEpSet->numOfEps]); - mgmtEpSet->numOfEps++; - } - - if (mgmtEpSet->numOfEps == 0) { - terrno = TSDB_CODE_TSC_INVALID_FQDN; - return -1; - } - - return 0; -} - - -int32_t udfdOpenClientRpc() { - SRpcInit rpcInit = {0}; - rpcInit.label = "UDFD"; - rpcInit.numOfThreads = 1; - rpcInit.cfp = (RpcCfp)udfdProcessRpcRsp; - rpcInit.sessions = 1024; - rpcInit.connType = TAOS_CONN_CLIENT; - rpcInit.idleTime = tsShellActivityTimer * 1000; - rpcInit.user = TSDB_DEFAULT_USER; - rpcInit.ckey = "key"; - rpcInit.spi = 1; - rpcInit.parent = &global; - rpcInit.rfp = udfdRpcRfp; - - char pass[TSDB_PASSWORD_LEN + 1] = {0}; - taosEncryptPass_c((uint8_t *)(TSDB_DEFAULT_PASS), strlen(TSDB_DEFAULT_PASS), pass); - rpcInit.secret = pass; - - global.clientRpc = rpcOpen(&rpcInit); - if (global.clientRpc == NULL) { - fnError("failed to init dnode rpc client"); - return -1; - } - return 0; -} - -int32_t udfdCloseClientRpc() { - rpcClose(global.clientRpc); - return 0; -} - -static void udfdPrintVersion() { -#ifdef TD_ENTERPRISE - char *releaseName = "enterprise"; -#else - char *releaseName = "community"; -#endif - printf("%s version: %s compatible_version: %s\n", releaseName, version, compatible_version); - printf("gitinfo: %s\n", gitinfo); - printf("buildInfo: %s\n", buildinfo); -} - static int32_t udfdParseArgs(int32_t argc, char *argv[]) { for (int32_t i = 1; i < argc; ++i) { if (strcmp(argv[i], "-c") == 0) { @@ -759,6 +771,17 @@ static int32_t udfdParseArgs(int32_t argc, char *argv[]) { return 0; } +static void udfdPrintVersion() { +#ifdef TD_ENTERPRISE + char *releaseName = "enterprise"; +#else + char *releaseName = "community"; +#endif + printf("%s version: %s compatible_version: %s\n", releaseName, version, compatible_version); + printf("gitinfo: %s\n", gitinfo); + printf("buildInfo: %s\n", buildinfo); +} + static int32_t udfdInitLog() { char logName[12] = {0}; snprintf(logName, sizeof(logName), "%slog", "udfd"); @@ -823,7 +846,7 @@ static int32_t udfdUvInit() { return 0; } -static void udfdCloseWalkCb(uv_handle_t* handle, void* arg) { +static void udfdCloseWalkCb(uv_handle_t *handle, void *arg) { if (!uv_is_closing(handle)) { uv_close(handle, NULL); } @@ -848,6 +871,23 @@ static int32_t udfdRun() { return 0; } +void udfdConnectMnodeThreadFunc(void* args) { + int32_t retryMnodeTimes = 0; + int32_t code = 0; + while (retryMnodeTimes++ <= TSDB_MAX_REPLICA) { + uv_sleep(100 * (1 << retryMnodeTimes)); + code = udfdConnectToMnode(); + if (code == 0) { + break; + } + fnError("udfd can not connect to mnode, code: %s. retry", tstrerror(code)); + } + + if (code != 0) { + fnError("udfd can not connect to mnode"); + } +} + int main(int argc, char *argv[]) { if (!taosCheckSystemIsSmallEnd()) { printf("failed to start since on non-small-end machines\n"); @@ -880,30 +920,19 @@ int main(int argc, char *argv[]) { return -3; } - int32_t retryMnodeTimes = 0; - int32_t code = 0; - while (retryMnodeTimes++ < TSDB_MAX_REPLICA) { - uv_sleep(500 * ( 1 << retryMnodeTimes)); - code = udfdConnectToMnode(); - if (code == 0) { - break; - } - fnError("can not connect to mnode, code: %s. retry", tstrerror(code)); - } - - if (code != 0) { - fnError("failed to start since can not connect to mnode"); - return -4; - } - if (udfdUvInit() != 0) { fnError("uv init failure"); return -5; } + uv_thread_t mnodeConnectThread; + uv_thread_create(&mnodeConnectThread, udfdConnectMnodeThreadFunc, NULL); + udfdRun(); removeListeningPipe(); - + uv_thread_join(&mnodeConnectThread); udfdCloseClientRpc(); + + return 0; } diff --git a/source/libs/function/test/udf2.c b/source/libs/function/test/udf2.c index 101595e86642bad53a0aa51a03dc99191d9fe674..49d681f5eb72b5e1f2ecca3baa45139f3a6a3116 100644 --- a/source/libs/function/test/udf2.c +++ b/source/libs/function/test/udf2.c @@ -26,7 +26,7 @@ int32_t udf2_start(SUdfInterBuf *buf) { int32_t udf2(SUdfDataBlock* block, SUdfInterBuf *interBuf, SUdfInterBuf *newInterBuf) { double sumSquares = *(double*)interBuf->buf; - int8_t numOutput = 0; + int8_t numNotNull = 0; for (int32_t i = 0; i < block->numOfCols; ++i) { SUdfColumn* col = block->udfCols[i]; if (!(col->colMeta.type == TSDB_DATA_TYPE_INT || @@ -56,15 +56,14 @@ int32_t udf2(SUdfDataBlock* block, SUdfInterBuf *interBuf, SUdfInterBuf *newInte default: break; } - numOutput = 1; + ++numNotNull; } } - if (numOutput == 1) { - *(double*)(newInterBuf->buf) = sumSquares; - newInterBuf->bufLen = sizeof(double); - } - if (interBuf->numOfResult == 0 && numOutput == 0) { + *(double*)(newInterBuf->buf) = sumSquares; + newInterBuf->bufLen = sizeof(double); + + if (interBuf->numOfResult == 0 && numNotNull == 0) { newInterBuf->numOfResult = 0; } else { newInterBuf->numOfResult = 1; diff --git a/source/libs/index/CMakeLists.txt b/source/libs/index/CMakeLists.txt index 7dc66e47898eed6e58288d775a0d4c78f79dc4b0..e55b004972d841a2049dc0474dbf3343b1cc300a 100644 --- a/source/libs/index/CMakeLists.txt +++ b/source/libs/index/CMakeLists.txt @@ -12,6 +12,9 @@ target_link_libraries( PUBLIC os PUBLIC util PUBLIC common + PUBLIC nodes + PUBLIC scalar + PUBLIC function ) if (${BUILD_WITH_LUCENE}) diff --git a/source/libs/index/inc/indexCache.h b/source/libs/index/inc/indexCache.h index d474d874090b09a4a9952e25a8a8f84eca77bf10..aff2e0e836c0f2aae9a1fe63dd984cd4f5eb7850 100644 --- a/source/libs/index/inc/indexCache.h +++ b/source/libs/index/inc/indexCache.h @@ -63,7 +63,10 @@ typedef struct CacheTerm { IndexCache* indexCacheCreate(SIndex* idx, uint64_t suid, const char* colName, int8_t type); +void indexCacheForceToMerge(void* cache); void indexCacheDestroy(void* cache); +void indexCacheBroadcast(void* cache); +void indexCacheWait(void* cache); Iterate* indexCacheIteratorCreate(IndexCache* cache); void indexCacheIteratorDestroy(Iterate* iiter); diff --git a/source/libs/index/inc/indexInt.h b/source/libs/index/inc/indexInt.h index 27c380beafb53065f7bfd4e5955be234406a58f6..0bdcb131b69befd518b233e38a2653a17e67bde8 100644 --- a/source/libs/index/inc/indexInt.h +++ b/source/libs/index/inc/indexInt.h @@ -58,6 +58,8 @@ struct SIndex { SIndexStat stat; TdThreadMutex mtx; + tsem_t sem; + bool quit; }; struct SIndexOpts { @@ -69,6 +71,7 @@ struct SIndexOpts { int32_t cacheSize; // MB // add cache module later #endif + int32_t cacheOpt; // MB }; struct SIndexMultiTermQuery { @@ -131,42 +134,14 @@ int32_t indexSerialCacheKey(ICacheKey* key, char* buf); // int32_t indexSerialKey(ICacheKey* key, char* buf); // int32_t indexSerialTermKey(SIndexTerm* itm, char* buf); -#define indexFatal(...) \ - do { \ - if (sDebugFlag & DEBUG_FATAL) { \ - taosPrintLog("index FATAL ", DEBUG_FATAL, 255, __VA_ARGS__); \ - } \ - } while (0) -#define indexError(...) \ - do { \ - if (sDebugFlag & DEBUG_ERROR) { \ - taosPrintLog("index ERROR ", DEBUG_ERROR, 255, __VA_ARGS__); \ - } \ - } while (0) -#define indexWarn(...) \ - do { \ - if (sDebugFlag & DEBUG_WARN) { \ - taosPrintLog("index WARN ", DEBUG_WARN, 255, __VA_ARGS__); \ - } \ - } while (0) -#define indexInfo(...) \ - do { \ - if (sDebugFlag & DEBUG_INFO) { \ - taosPrintLog("index ", DEBUG_INFO, 255, __VA_ARGS__); \ - } \ - } while (0) -#define indexDebug(...) \ - do { \ - if (sDebugFlag & DEBUG_DEBUG) { \ - taosPrintLog("index ", DEBUG_DEBUG, sDebugFlag, __VA_ARGS__); \ - } \ - } while (0) -#define indexTrace(...) \ - do { \ - if (sDebugFlag & DEBUG_TRACE) { \ - taosPrintLog("index ", DEBUG_TRACE, sDebugFlag, __VA_ARGS__); \ - } \ - } while (0) +// clang-format off +#define indexFatal(...) do { if (sDebugFlag & DEBUG_FATAL) { taosPrintLog("INDEX FATAL ", DEBUG_FATAL, 255, __VA_ARGS__); }} while (0) +#define indexError(...) do { if (sDebugFlag & DEBUG_ERROR) { taosPrintLog("INDEX ERROR ", DEBUG_ERROR, 255, __VA_ARGS__); }} while (0) +#define indexWarn(...) do { if (sDebugFlag & DEBUG_WARN) { taosPrintLog("INDEX WARN ", DEBUG_WARN, 255, __VA_ARGS__); }} while (0) +#define indexInfo(...) do { if (sDebugFlag & DEBUG_INFO) { taosPrintLog("INDEX ", DEBUG_INFO, 255, __VA_ARGS__); } } while (0) +#define indexDebug(...) do { if (sDebugFlag & DEBUG_DEBUG) { taosPrintLog("INDEX ", DEBUG_DEBUG, sDebugFlag, __VA_ARGS__);} } while (0) +#define indexTrace(...) do { if (sDebugFlag & DEBUG_TRACE) { taosPrintLog("INDEX ", DEBUG_TRACE, sDebugFlag, __VA_ARGS__);} } while (0) +// clang-format on #define INDEX_TYPE_CONTAIN_EXTERN_TYPE(ty, exTy) (((ty >> 4) & (exTy)) != 0) diff --git a/source/libs/index/inc/indexTfile.h b/source/libs/index/inc/indexTfile.h index 9712e4b30f7ecfdabb4e8d3c824190f60319ec69..85ed397b0ac5d14984a4020b265fbdcf6951c68e 100644 --- a/source/libs/index/inc/indexTfile.h +++ b/source/libs/index/inc/indexTfile.h @@ -40,7 +40,7 @@ typedef struct TFileHeader { } TFileHeader; #pragma pack(pop) -#define TFILE_HEADER_SIZE (sizeof(TFileHeader)) +#define TFILE_HEADER_SIZE (sizeof(TFileHeader)) #define TFILE_HEADER_NO_FST (TFILE_HEADER_SIZE - sizeof(int32_t)) typedef struct TFileValue { diff --git a/source/libs/index/src/index.c b/source/libs/index/src/index.c index 162d64c41c472e88aa019b4b5def7727850c636d..6add788a896f8149f49d9f224538d5b3ab4e5b57 100644 --- a/source/libs/index/src/index.c +++ b/source/libs/index/src/index.c @@ -29,7 +29,7 @@ #include "lucene++/Lucene_c.h" #endif -#define INDEX_NUM_OF_THREADS 4 +#define INDEX_NUM_OF_THREADS 1 #define INDEX_QUEUE_SIZE 200 #define INDEX_DATA_BOOL_NULL 0x02 @@ -90,6 +90,15 @@ static void indexMergeCacheAndTFile(SArray* result, IterateValue* icache, Iterat // static int32_t indexSerialTermKey(SIndexTerm* itm, char* buf); // int32_t indexSerialKey(ICacheKey* key, char* buf); +static void indexPost(void* idx) { + SIndex* pIdx = idx; + tsem_post(&pIdx->sem); +} +static void indexWait(void* idx) { + SIndex* pIdx = idx; + tsem_wait(&pIdx->sem); +} + int indexOpen(SIndexOpts* opts, const char* path, SIndex** index) { taosThreadOnce(&isInit, indexInit); SIndex* sIdx = taosMemoryCalloc(1, sizeof(SIndex)); @@ -107,6 +116,7 @@ int indexOpen(SIndexOpts* opts, const char* path, SIndex** index) { sIdx->cVersion = 1; sIdx->path = tstrdup(path); taosThreadMutexInit(&sIdx->mtx, NULL); + tsem_init(&sIdx->sem, 0, 0); sIdx->refId = indexAddRef(sIdx); indexAcquireRef(sIdx->refId); @@ -124,22 +134,28 @@ END: void indexDestroy(void* handle) { SIndex* sIdx = handle; - void* iter = taosHashIterate(sIdx->colObj, NULL); - while (iter) { - IndexCache** pCache = iter; - if (*pCache) { - indexCacheUnRef(*pCache); - } - iter = taosHashIterate(sIdx->colObj, iter); - } - taosHashCleanup(sIdx->colObj); taosThreadMutexDestroy(&sIdx->mtx); + tsem_destroy(&sIdx->sem); indexTFileDestroy(sIdx->tindex); taosMemoryFree(sIdx->path); taosMemoryFree(sIdx); return; } void indexClose(SIndex* sIdx) { + bool ref = 0; + if (sIdx->colObj != NULL) { + void* iter = taosHashIterate(sIdx->colObj, NULL); + while (iter) { + IndexCache** pCache = iter; + indexCacheForceToMerge((void*)(*pCache)); + indexInfo("%s wait to merge", (*pCache)->colName); + indexWait((void*)(sIdx)); + iter = taosHashIterate(sIdx->colObj, iter); + indexCacheUnRef(*pCache); + } + taosHashCleanup(sIdx->colObj); + sIdx->colObj = NULL; + } indexReleaseRef(sIdx->refId); indexRemoveRef(sIdx->refId); } @@ -451,6 +467,18 @@ int indexFlushCacheToTFile(SIndex* sIdx, void* cache) { } // handle flush Iterate* cacheIter = indexCacheIteratorCreate(pCache); + if (cacheIter == NULL) { + indexError("%p immtable is empty, ignore merge opera", pCache); + indexCacheDestroyImm(pCache); + tfileReaderUnRef(pReader); + if (sIdx->quit) { + indexPost(sIdx); + // indexCacheBroadcast(pCache); + } + indexReleaseRef(sIdx->refId); + return 0; + } + Iterate* tfileIter = tfileIteratorCreate(pReader); if (tfileIter == NULL) { indexWarn("empty tfile reader iterator"); @@ -506,7 +534,11 @@ int indexFlushCacheToTFile(SIndex* sIdx, void* cache) { } else { indexInfo("success to merge , time cost: %" PRId64 "ms", cost / 1000); } + if (sIdx->quit) { + indexPost(sIdx); + } indexReleaseRef(sIdx->refId); + return ret; } void iterateValueDestroy(IterateValue* value, bool destroy) { @@ -521,8 +553,29 @@ void iterateValueDestroy(IterateValue* value, bool destroy) { taosMemoryFree(value->colVal); value->colVal = NULL; } + +static int64_t indexGetAvaialbleVer(SIndex* sIdx, IndexCache* cache) { + ICacheKey key = {.suid = cache->suid, .colName = cache->colName, .nColName = strlen(cache->colName)}; + int64_t ver = CACHE_VERSION(cache); + taosThreadMutexLock(&sIdx->mtx); + TFileReader* trd = tfileCacheGet(((IndexTFile*)sIdx->tindex)->cache, &key); + if (trd != NULL) { + if (ver < trd->header.version) { + ver = trd->header.version + 1; + } else { + ver += 1; + } + indexInfo("header: %d, ver: %" PRId64 "", trd->header.version, ver); + tfileReaderUnRef(trd); + } else { + indexInfo("not found reader base %p", trd); + } + taosThreadMutexUnlock(&sIdx->mtx); + return ver; +} static int indexGenTFile(SIndex* sIdx, IndexCache* cache, SArray* batch) { - int32_t version = CACHE_VERSION(cache); + int64_t version = indexGetAvaialbleVer(sIdx, cache); + indexInfo("file name version: %" PRId64 "", version); uint8_t colType = cache->type; TFileWriter* tw = tfileWriterOpen(sIdx->path, cache->suid, version, cache->colName, colType); @@ -542,6 +595,7 @@ static int indexGenTFile(SIndex* sIdx, IndexCache* cache, SArray* batch) { if (reader == NULL) { return -1; } + indexInfo("success to create tfile, reopen it, %s", reader->ctx->file.buf); TFileHeader* header = &reader->header; ICacheKey key = {.suid = cache->suid, .colName = header->colName, .nColName = strlen(header->colName)}; @@ -563,10 +617,11 @@ int32_t indexSerialCacheKey(ICacheKey* key, char* buf) { bool hasJson = INDEX_TYPE_CONTAIN_EXTERN_TYPE(key->colType, TSDB_DATA_TYPE_JSON); char* p = buf; - SERIALIZE_MEM_TO_BUF(buf, key, suid); + char tbuf[65] = {0}; + indexInt2str((int64_t)key->suid, tbuf, 0); + + SERIALIZE_STR_VAR_TO_BUF(buf, tbuf, strlen(tbuf)); SERIALIZE_VAR_TO_BUF(buf, '_', char); - // SERIALIZE_MEM_TO_BUF(buf, key, colType); - // SERIALIZE_VAR_TO_BUF(buf, '_', char); if (hasJson) { SERIALIZE_STR_VAR_TO_BUF(buf, JSON_COLUMN, strlen(JSON_COLUMN)); } else { diff --git a/source/libs/index/src/indexCache.c b/source/libs/index/src/indexCache.c index 1d7a4a541982ca5a61f66ef9999106306907acc2..d704e3876e4979cdf8c1354e9b3d2ef23bf91132 100644 --- a/source/libs/index/src/indexCache.c +++ b/source/libs/index/src/indexCache.c @@ -22,7 +22,8 @@ #define MAX_INDEX_KEY_LEN 256 // test only, change later #define MEM_TERM_LIMIT 10 * 10000 -#define MEM_THRESHOLD 1024 * 1024 +#define MEM_THRESHOLD 64 * 1024 +#define MEM_SIGNAL_QUIT MEM_THRESHOLD * 20 #define MEM_ESTIMATE_RADIO 1.5 static void indexMemRef(MemTable* tbl); @@ -334,6 +335,9 @@ IndexCache* indexCacheCreate(SIndex* idx, uint64_t suid, const char* colName, in taosThreadCondInit(&cache->finished, NULL); indexCacheRef(cache); + if (idx != NULL) { + indexAcquireRef(idx->refId); + } return cache; } void indexCacheDebug(IndexCache* cache) { @@ -385,7 +389,7 @@ void indexCacheDebug(IndexCache* cache) { void indexCacheDestroySkiplist(SSkipList* slt) { SSkipListIterator* iter = tSkipListCreateIter(slt); - while (tSkipListIterNext(iter)) { + while (iter != NULL && tSkipListIterNext(iter)) { SSkipListNode* node = tSkipListIterGet(iter); CacheTerm* ct = (CacheTerm*)SL_GET_NODE_DATA(node); if (ct != NULL) { @@ -396,17 +400,24 @@ void indexCacheDestroySkiplist(SSkipList* slt) { tSkipListDestroyIter(iter); tSkipListDestroy(slt); } +void indexCacheBroadcast(void* cache) { + IndexCache* pCache = cache; + taosThreadCondBroadcast(&pCache->finished); +} +void indexCacheWait(void* cache) { + IndexCache* pCache = cache; + taosThreadCondWait(&pCache->finished, &pCache->mtx); +} void indexCacheDestroyImm(IndexCache* cache) { if (cache == NULL) { return; } - MemTable* tbl = NULL; taosThreadMutexLock(&cache->mtx); tbl = cache->imm; cache->imm = NULL; // or throw int bg thread - taosThreadCondBroadcast(&cache->finished); + indexCacheBroadcast(cache); taosThreadMutexUnlock(&cache->mtx); @@ -418,22 +429,27 @@ void indexCacheDestroy(void* cache) { if (pCache == NULL) { return; } + indexMemUnRef(pCache->mem); indexMemUnRef(pCache->imm); taosMemoryFree(pCache->colName); taosThreadMutexDestroy(&pCache->mtx); taosThreadCondDestroy(&pCache->finished); - + if (pCache->index != NULL) { + indexReleaseRef(((SIndex*)pCache->index)->refId); + } taosMemoryFree(pCache); } Iterate* indexCacheIteratorCreate(IndexCache* cache) { + if (cache->imm == NULL) { + return NULL; + } Iterate* iiter = taosMemoryCalloc(1, sizeof(Iterate)); if (iiter == NULL) { return NULL; } - taosThreadMutexLock(&cache->mtx); indexMemRef(cache->imm); @@ -458,17 +474,16 @@ void indexCacheIteratorDestroy(Iterate* iter) { taosMemoryFree(iter); } -int indexCacheSchedToMerge(IndexCache* pCache) { +int indexCacheSchedToMerge(IndexCache* pCache, bool notify) { SSchedMsg schedMsg = {0}; schedMsg.fp = doMergeWork; schedMsg.ahandle = pCache; - schedMsg.thandle = NULL; - // schedMsg.thandle = taosMemoryCalloc(1, sizeof(int64_t)); - // memcpy((char*)(schedMsg.thandle), (char*)&(pCache->index->refId), sizeof(int64_t)); + if (notify) { + schedMsg.thandle = taosMemoryMalloc(1); + } schedMsg.msg = NULL; indexAcquireRef(pCache->index->refId); taosScheduleTask(indexQhandle, &schedMsg); - return 0; } @@ -478,8 +493,10 @@ static void indexCacheMakeRoomForWrite(IndexCache* cache) { break; } else if (cache->imm != NULL) { // TODO: wake up by condition variable - taosThreadCondWait(&cache->finished, &cache->mtx); + indexCacheWait(cache); } else { + bool notifyQuit = cache->occupiedMem >= MEM_SIGNAL_QUIT ? true : false; + indexCacheRef(cache); cache->imm = cache->mem; cache->mem = indexInternalCacheCreate(cache->type); @@ -487,7 +504,7 @@ static void indexCacheMakeRoomForWrite(IndexCache* cache) { cache->occupiedMem = 0; // sched to merge // unref cache in bgwork - indexCacheSchedToMerge(cache); + indexCacheSchedToMerge(cache, notifyQuit); } } } @@ -533,6 +550,19 @@ int indexCachePut(void* cache, SIndexTerm* term, uint64_t uid) { return 0; // encode end } +void indexCacheForceToMerge(void* cache) { + IndexCache* pCache = cache; + indexCacheRef(pCache); + taosThreadMutexLock(&pCache->mtx); + + indexInfo("%p is forced to merge into tfile", pCache); + pCache->occupiedMem += MEM_SIGNAL_QUIT; + indexCacheMakeRoomForWrite(pCache); + + taosThreadMutexUnlock(&pCache->mtx); + indexCacheUnRef(pCache); + return; +} int indexCacheDel(void* cache, const char* fieldValue, int32_t fvlen, uint64_t uid, int8_t operType) { IndexCache* pCache = cache; return 0; @@ -691,6 +721,9 @@ static MemTable* indexInternalCacheCreate(int8_t type) { static void doMergeWork(SSchedMsg* msg) { IndexCache* pCache = msg->ahandle; SIndex* sidx = (SIndex*)pCache->index; + + sidx->quit = msg->thandle ? true : false; + taosMemoryFree(msg->thandle); indexFlushCacheToTFile(sidx, pCache); } static bool indexCacheIteratorNext(Iterate* itera) { @@ -709,9 +742,6 @@ static bool indexCacheIteratorNext(Iterate* itera) { iv->type = ct->operaType; iv->ver = ct->version; iv->colVal = tstrdup(ct->colVal); - // printf("col Val: %s\n", iv->colVal); - // iv->colType = cv->colType; - taosArrayPush(iv->val, &ct->uid); } return next; diff --git a/source/libs/index/src/indexComm.c b/source/libs/index/src/indexComm.c index 4c23e4ba4b9a89d124a93a434da27158891816bc..78c7babb681e44629281f0ffd6ea6ba835495b5b 100644 --- a/source/libs/index/src/indexComm.c +++ b/source/libs/index/src/indexComm.c @@ -22,6 +22,29 @@ #include "ttypes.h" #include "tvariant.h" +#define INDEX_DATA_BOOL_NULL 0x02 +#define INDEX_DATA_TINYINT_NULL 0x80 +#define INDEX_DATA_SMALLINT_NULL 0x8000 +#define INDEX_DATA_INT_NULL 0x80000000L +#define INDEX_DATA_BIGINT_NULL 0x8000000000000000L +#define INDEX_DATA_TIMESTAMP_NULL TSDB_DATA_BIGINT_NULL + +#define INDEX_DATA_FLOAT_NULL 0x7FF00000 // it is an NAN +#define INDEX_DATA_DOUBLE_NULL 0x7FFFFF0000000000L // an NAN +#define INDEX_DATA_NCHAR_NULL 0xFFFFFFFF +#define INDEX_DATA_BINARY_NULL 0xFF +#define INDEX_DATA_JSON_NULL 0xFFFFFFFF +#define INDEX_DATA_JSON_null 0xFFFFFFFE +#define INDEX_DATA_JSON_NOT_NULL 0x01 + +#define INDEX_DATA_UTINYINT_NULL 0xFF +#define INDEX_DATA_USMALLINT_NULL 0xFFFF +#define INDEX_DATA_UINT_NULL 0xFFFFFFFF +#define INDEX_DATA_UBIGINT_NULL 0xFFFFFFFFFFFFFFFFL + +#define INDEX_DATA_NULL_STR "NULL" +#define INDEX_DATA_NULL_STR_L "null" + char JSON_COLUMN[] = "JSON"; char JSON_VALUE_DELIM = '&'; @@ -372,7 +395,7 @@ int32_t indexConvertDataToStr(void* src, int8_t type, void** dst) { tlen = taosEncodeBinary(NULL, varDataVal(src), varDataLen(src)); *dst = taosMemoryCalloc(1, tlen + 1); tlen = taosEncodeBinary(dst, varDataVal(src), varDataLen(src)); - *dst = (char*) * dst - tlen; + *dst = (char*)*dst - tlen; break; } case TSDB_DATA_TYPE_VARCHAR: { // TSDB_DATA_TYPE_BINARY diff --git a/source/libs/executor/src/indexoperator.c b/source/libs/index/src/indexFilter.c similarity index 89% rename from source/libs/executor/src/indexoperator.c rename to source/libs/index/src/indexFilter.c index 2c204e93563367b820eabc27321631935beb2489..0273867ccf040f3d3344066270ef3b8aa6a3bae2 100644 --- a/source/libs/executor/src/indexoperator.c +++ b/source/libs/index/src/indexFilter.c @@ -13,44 +13,18 @@ * along with this program. If not, see . */ -#include "indexoperator.h" -#include "executorimpl.h" #include "index.h" +#include "indexInt.h" #include "nodes.h" +#include "querynodes.h" +#include "scalar.h" #include "tdatablock.h" -typedef struct SIFCtx { - int32_t code; - SHashObj *pRes; /* element is SScalarParam */ - bool noExec; // true: just iterate condition tree, and add hint to executor plan - // SIdxFltStatus st; -} SIFCtx; - -#define SIF_ERR_RET(c) \ - do { \ - int32_t _code = c; \ - if (_code != TSDB_CODE_SUCCESS) { \ - terrno = _code; \ - return _code; \ - } \ - } while (0) -#define SIF_RET(c) \ - do { \ - int32_t _code = c; \ - if (_code != TSDB_CODE_SUCCESS) { \ - terrno = _code; \ - } \ - return _code; \ - } while (0) -#define SIF_ERR_JRET(c) \ - do { \ - code = c; \ - if (code != TSDB_CODE_SUCCESS) { \ - terrno = code; \ - goto _return; \ - } \ - } while (0) - +// clang-format off +#define SIF_ERR_RET(c) do { int32_t _code = c; if (_code != TSDB_CODE_SUCCESS) { terrno = _code; return _code; } } while (0) +#define SIF_RET(c) do { int32_t _code = c; if (_code != TSDB_CODE_SUCCESS) { terrno = _code; } return _code; } while (0) +#define SIF_ERR_JRET(c) do { code = c; if (code != TSDB_CODE_SUCCESS) { terrno = code; goto _return; } } while (0) +// clang-format on typedef struct SIFParam { SHashObj *pFilter; @@ -65,6 +39,13 @@ typedef struct SIFParam { char colName[TSDB_COL_NAME_LEN]; } SIFParam; +typedef struct SIFCtx { + int32_t code; + SHashObj *pRes; /* element is SIFParam */ + bool noExec; // true: just iterate condition tree, and add hint to executor plan + // SIdxFltStatus st; +} SIFCtx; + static int32_t sifGetFuncFromSql(EOperatorType src, EIndexQueryType *dst) { if (src == OP_TYPE_GREATER_THAN) { *dst = QUERY_GREATER_THAN; @@ -89,9 +70,9 @@ typedef int32_t (*sif_func_t)(SIFParam *left, SIFParam *rigth, SIFParam *output) static sif_func_t sifNullFunc = NULL; // typedef struct SIFWalkParm // construct tag filter operator later -static void destroyTagFilterOperatorInfo(void *param) { - STagFilterOperatorInfo *pInfo = (STagFilterOperatorInfo *)param; -} +// static void destroyTagFilterOperatorInfo(void *param) { +// STagFilterOperatorInfo *pInfo = (STagFilterOperatorInfo *)param; +//} static void sifFreeParam(SIFParam *param) { if (param == NULL) return; @@ -198,13 +179,13 @@ static int32_t sifInitParam(SNode *node, SIFParam *param, SIFCtx *ctx) { case QUERY_NODE_NODE_LIST: { SNodeListNode *nl = (SNodeListNode *)node; if (LIST_LENGTH(nl->pNodeList) <= 0) { - qError("invalid length for node:%p, length: %d", node, LIST_LENGTH(nl->pNodeList)); + indexError("invalid length for node:%p, length: %d", node, LIST_LENGTH(nl->pNodeList)); SIF_ERR_RET(TSDB_CODE_QRY_INVALID_INPUT); } SIF_ERR_RET(scalarGenerateSetFromList((void **)¶m->pFilter, node, nl->dataType.type)); if (taosHashPut(ctx->pRes, &node, POINTER_BYTES, param, sizeof(*param))) { taosHashCleanup(param->pFilter); - qError("taosHashPut nodeList failed, size:%d", (int32_t)sizeof(*param)); + indexError("taosHashPut nodeList failed, size:%d", (int32_t)sizeof(*param)); SIF_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); } break; @@ -214,7 +195,7 @@ static int32_t sifInitParam(SNode *node, SIFParam *param, SIFCtx *ctx) { case QUERY_NODE_LOGIC_CONDITION: { SIFParam *res = (SIFParam *)taosHashGet(ctx->pRes, &node, POINTER_BYTES); if (NULL == res) { - qError("no result for node, type:%d, node:%p", nodeType(node), node); + indexError("no result for node, type:%d, node:%p", nodeType(node), node); SIF_ERR_RET(TSDB_CODE_QRY_APP_ERROR); } *param = *res; @@ -230,7 +211,7 @@ static int32_t sifInitOperParams(SIFParam **params, SOperatorNode *node, SIFCtx int32_t code = 0; int32_t nParam = sifGetOperParamNum(node->opType); if (NULL == node->pLeft || (nParam == 2 && NULL == node->pRight)) { - qError("invalid operation node, left: %p, rigth: %p", node->pLeft, node->pRight); + indexError("invalid operation node, left: %p, rigth: %p", node->pLeft, node->pRight); SIF_ERR_RET(TSDB_CODE_QRY_INVALID_INPUT); } SIFParam *paramList = taosMemoryCalloc(nParam, sizeof(SIFParam)); @@ -252,7 +233,7 @@ static int32_t sifInitParamList(SIFParam **params, SNodeList *nodeList, SIFCtx * int32_t code = 0; SIFParam *tParams = taosMemoryCalloc(nodeList->length, sizeof(SIFParam)); if (tParams == NULL) { - qError("failed to calloc, nodeList: %p", nodeList); + indexError("failed to calloc, nodeList: %p", nodeList); SIF_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); } @@ -272,7 +253,7 @@ _return: SIF_RET(code); } static int32_t sifExecFunction(SFunctionNode *node, SIFCtx *ctx, SIFParam *output) { - qError("index-filter not support buildin function"); + indexError("index-filter not support buildin function"); return TSDB_CODE_QRY_INVALID_INPUT; } static int32_t sifDoIndex(SIFParam *left, SIFParam *right, int8_t operType, SIFParam *output) { @@ -410,8 +391,8 @@ _return: static int32_t sifExecLogic(SLogicConditionNode *node, SIFCtx *ctx, SIFParam *output) { if (NULL == node->pParameterList || node->pParameterList->length <= 0) { - qError("invalid logic parameter list, list:%p, paramNum:%d", node->pParameterList, - node->pParameterList ? node->pParameterList->length : 0); + indexError("invalid logic parameter list, list:%p, paramNum:%d", node->pParameterList, + node->pParameterList ? node->pParameterList->length : 0); return TSDB_CODE_QRY_INVALID_INPUT; } @@ -505,7 +486,7 @@ EDealRes sifCalcWalker(SNode *node, void *context) { return sifWalkOper(node, ctx); } - qError("invalid node type for index filter calculating, type:%d", nodeType(node)); + indexError("invalid node type for index filter calculating, type:%d", nodeType(node)); ctx->code = TSDB_CODE_QRY_INVALID_INPUT; return DEAL_RES_ERROR; } @@ -529,7 +510,7 @@ static int32_t sifCalculate(SNode *pNode, SIFParam *pDst) { SIFCtx ctx = {.code = 0, .noExec = false}; ctx.pRes = taosHashInit(4, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BIGINT), false, HASH_NO_LOCK); if (NULL == ctx.pRes) { - qError("index-filter failed to taosHashInit"); + indexError("index-filter failed to taosHashInit"); return TSDB_CODE_QRY_OUT_OF_MEMORY; } @@ -539,7 +520,7 @@ static int32_t sifCalculate(SNode *pNode, SIFParam *pDst) { if (pDst) { SIFParam *res = (SIFParam *)taosHashGet(ctx.pRes, (void *)&pNode, POINTER_BYTES); if (res == NULL) { - qError("no valid res in hash, node:(%p), type(%d)", (void *)&pNode, nodeType(pNode)); + indexError("no valid res in hash, node:(%p), type(%d)", (void *)&pNode, nodeType(pNode)); SIF_ERR_RET(TSDB_CODE_QRY_APP_ERROR); } taosArrayAddAll(pDst->result, res->result); @@ -559,7 +540,7 @@ static int32_t sifGetFltHint(SNode *pNode, SIdxFltStatus *status) { SIFCtx ctx = {.code = 0, .noExec = true}; ctx.pRes = taosHashInit(4, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BIGINT), false, HASH_NO_LOCK); if (NULL == ctx.pRes) { - qError("index-filter failed to taosHashInit"); + indexError("index-filter failed to taosHashInit"); return TSDB_CODE_QRY_OUT_OF_MEMORY; } @@ -569,7 +550,7 @@ static int32_t sifGetFltHint(SNode *pNode, SIdxFltStatus *status) { SIFParam *res = (SIFParam *)taosHashGet(ctx.pRes, (void *)&pNode, POINTER_BYTES); if (res == NULL) { - qError("no valid res in hash, node:(%p), type(%d)", (void *)&pNode, nodeType(pNode)); + indexError("no valid res in hash, node:(%p), type(%d)", (void *)&pNode, nodeType(pNode)); SIF_ERR_RET(TSDB_CODE_QRY_APP_ERROR); } *status = res->status; diff --git a/source/libs/index/src/indexFst.c b/source/libs/index/src/indexFst.c index e2975fb7bcc9784b34391215e91bd264ea82efd6..335b0865269604432259847de072a53854286c2c 100644 --- a/source/libs/index/src/indexFst.c +++ b/source/libs/index/src/indexFst.c @@ -99,7 +99,7 @@ void fstUnFinishedNodesAddSuffix(FstUnFinishedNodes* nodes, FstSlice bs, Output if (fstSliceIsEmpty(s)) { return; } - size_t sz = taosArrayGetSize(nodes->stack) - 1; + int32_t sz = taosArrayGetSize(nodes->stack) - 1; FstBuilderNodeUnfinished* un = taosArrayGet(nodes->stack, sz); assert(un->last == NULL); @@ -130,11 +130,11 @@ void fstUnFinishedNodesAddSuffix(FstUnFinishedNodes* nodes, FstSlice bs, Output uint64_t fstUnFinishedNodesFindCommPrefix(FstUnFinishedNodes* node, FstSlice bs) { FstSlice* s = &bs; - size_t ssz = taosArrayGetSize(node->stack); // stack size + int32_t ssz = taosArrayGetSize(node->stack); // stack size uint64_t count = 0; int32_t lsz; // data len uint8_t* data = fstSliceData(s, &lsz); - for (size_t i = 0; i < ssz && i < lsz; i++) { + for (int32_t i = 0; i < ssz && i < lsz; i++) { FstBuilderNodeUnfinished* un = taosArrayGet(node->stack, i); if (un->last->inp == data[i]) { count++; @@ -147,8 +147,8 @@ uint64_t fstUnFinishedNodesFindCommPrefix(FstUnFinishedNodes* node, FstSlice bs) uint64_t fstUnFinishedNodesFindCommPrefixAndSetOutput(FstUnFinishedNodes* node, FstSlice bs, Output in, Output* out) { FstSlice* s = &bs; - size_t lsz = (size_t)(s->end - s->start + 1); // data len - size_t ssz = taosArrayGetSize(node->stack); // stack size + int32_t lsz = (size_t)(s->end - s->start + 1); // data len + int32_t ssz = taosArrayGetSize(node->stack); // stack size *out = in; uint64_t i = 0; for (i = 0; i < lsz && i < ssz; i++) { @@ -245,7 +245,7 @@ void fstStateCompileForOneTrans(FstCountingWriter* w, CompiledAddr addr, FstTran return; } void fstStateCompileForAnyTrans(FstCountingWriter* w, CompiledAddr addr, FstBuilderNode* node) { - size_t sz = taosArrayGetSize(node->trans); + int32_t sz = taosArrayGetSize(node->trans); assert(sz <= 256); uint8_t tSize = 0; @@ -253,7 +253,7 @@ void fstStateCompileForAnyTrans(FstCountingWriter* w, CompiledAddr addr, FstBuil // finalOutput.is_zero() bool anyOuts = (node->finalOutput != 0); - for (size_t i = 0; i < sz; i++) { + for (int32_t i = 0; i < sz; i++) { FstTransition* t = taosArrayGet(node->trans, i); tSize = TMAX(tSize, packDeltaSize(addr, t->addr)); oSize = TMAX(oSize, packSize(t->out)); @@ -301,7 +301,7 @@ void fstStateCompileForAnyTrans(FstCountingWriter* w, CompiledAddr addr, FstBuil /// for (uint8_t i = 0; i < 256; i++) { // index[i] = 255; ///} - for (size_t i = 0; i < sz; i++) { + for (int32_t i = 0; i < sz; i++) { FstTransition* t = taosArrayGet(node->trans, i); index[t->inp] = i; // fstPackDeltaIn(w, addr, t->addr, tSize); @@ -731,7 +731,7 @@ bool fstNodeFindInput(FstNode* node, uint8_t b, uint64_t* res) { } bool fstNodeCompile(FstNode* node, void* w, CompiledAddr lastAddr, CompiledAddr addr, FstBuilderNode* builderNode) { - size_t sz = taosArrayGetSize(builderNode->trans); + int32_t sz = taosArrayGetSize(builderNode->trans); assert(sz < 256); if (sz == 0 && builderNode->isFinal && builderNode->finalOutput == 0) { return true; @@ -959,8 +959,8 @@ void fstBuilderNodeUnfinishedAddOutputPrefix(FstBuilderNodeUnfinished* unNode, O if (FST_BUILDER_NODE_IS_FINAL(unNode->node)) { unNode->node->finalOutput += out; } - size_t sz = taosArrayGetSize(unNode->node->trans); - for (size_t i = 0; i < sz; i++) { + int32_t sz = taosArrayGetSize(unNode->node->trans); + for (int32_t i = 0; i < sz; i++) { FstTransition* trn = taosArrayGet(unNode->node->trans, i); trn->out += out; } @@ -1077,7 +1077,7 @@ bool fstGet(Fst* fst, FstSlice* b, Output* out) { tOut = tOut + FST_NODE_FINAL_OUTPUT(root); } - for (size_t i = 0; i < taosArrayGetSize(nodes); i++) { + for (int32_t i = 0; i < taosArrayGetSize(nodes); i++) { FstNode** node = (FstNode**)taosArrayGet(nodes, i); fstNodeDestroy(*node); } @@ -1352,7 +1352,7 @@ StreamWithStateResult* streamWithStateNextWith(StreamWithState* sws, StreamCallb StreamState s2 = {.node = nextNode, .trans = 0, .out = {.null = false, .out = out}, .autState = nextState}; taosArrayPush(sws->stack, &s2); - size_t isz = taosArrayGetSize(sws->inp); + int32_t isz = taosArrayGetSize(sws->inp); uint8_t* buf = (uint8_t*)taosMemoryMalloc(isz * sizeof(uint8_t)); for (uint32_t i = 0; i < isz; i++) { buf[i] = *(uint8_t*)taosArrayGet(sws->inp, i); diff --git a/source/libs/index/src/indexFstCountingWriter.c b/source/libs/index/src/indexFstCountingWriter.c index 1d4395aff6bb01eade0c27d9b26241aab1b5e005..8ba51736028515f2d671ecab876ed7437c96b8b7 100644 --- a/source/libs/index/src/indexFstCountingWriter.c +++ b/source/libs/index/src/indexFstCountingWriter.c @@ -97,6 +97,7 @@ WriterCtx* writerCtxCreate(WriterType type, const char* path, bool readOnly, int int64_t file_size; taosStatFile(path, &file_size, NULL); ctx->file.size = (int)file_size; + } else { // ctx->file.pFile = open(path, O_RDONLY, S_IRWXU | S_IRWXG | S_IRWXO); ctx->file.pFile = taosOpenFile(path, TD_FILE_READ); diff --git a/source/libs/index/src/indexTfile.c b/source/libs/index/src/indexTfile.c index 163bb53163b1671a6e5ff68bfe986a1b15c292b5..3d85646bd25596e7d3a666b99287d6b5e3d5e902 100644 --- a/source/libs/index/src/indexTfile.c +++ b/source/libs/index/src/indexTfile.c @@ -1,6 +1,5 @@ /* * Copyright (c) 2019 TAOS Data, Inc. -p * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. @@ -116,7 +115,7 @@ TFileCache* tfileCacheCreate(const char* path) { continue; } TFileHeader* header = &reader->header; - ICacheKey key = {.suid = header->suid, .colName = header->colName, .nColName = strlen(header->colName)}; + ICacheKey key = {.suid = header->suid, .colName = header->colName, .nColName = (int32_t)strlen(header->colName)}; char buf[128] = {0}; int32_t sz = indexSerialCacheKey(&key, buf); @@ -141,7 +140,6 @@ void tfileCacheDestroy(TFileCache* tcache) { TFileReader* p = *reader; indexInfo("drop table cache suid: %" PRIu64 ", colName: %s, colType: %d", p->header.suid, p->header.colName, p->header.colType); - tfileReaderUnRef(p); reader = taosHashIterate(tcache->tableCache, reader); } @@ -153,10 +151,13 @@ TFileReader* tfileCacheGet(TFileCache* tcache, ICacheKey* key) { char buf[128] = {0}; int32_t sz = indexSerialCacheKey(key, buf); assert(sz < sizeof(buf)); + indexInfo("Try to get key: %s", buf); TFileReader** reader = taosHashGet(tcache->tableCache, buf, sz); - if (reader == NULL) { + if (reader == NULL || *reader == NULL) { + indexInfo("failed to get key: %s", buf); return NULL; } + indexInfo("Get key: %s file: %s", buf, (*reader)->ctx->file.buf); tfileReaderRef(*reader); return *reader; @@ -166,13 +167,13 @@ void tfileCachePut(TFileCache* tcache, ICacheKey* key, TFileReader* reader) { int32_t sz = indexSerialCacheKey(key, buf); // remove last version index reader TFileReader** p = taosHashGet(tcache->tableCache, buf, sz); - if (p != NULL) { + if (p != NULL && *p != NULL) { TFileReader* oldReader = *p; taosHashRemove(tcache->tableCache, buf, sz); + indexInfo("found %s, remove file %s", buf, oldReader->ctx->file.buf); oldReader->remove = true; tfileReaderUnRef(oldReader); } - taosHashPut(tcache->tableCache, buf, sz, &reader, sizeof(void*)); tfileReaderRef(reader); return; @@ -182,7 +183,6 @@ TFileReader* tfileReaderCreate(WriterCtx* ctx) { if (reader == NULL) { return NULL; } - reader->ctx = ctx; if (0 != tfileReaderVerify(reader)) { @@ -204,6 +204,7 @@ TFileReader* tfileReaderCreate(WriterCtx* ctx) { tfileReaderDestroy(reader); return NULL; } + reader->remove = false; return reader; } @@ -230,7 +231,7 @@ static int32_t tfSearchTerm(void* reader, SIndexTerm* tem, SIdxTempResult* tr) { indexInfo("index: %" PRIu64 ", col: %s, colVal: %s, found table info in tindex, time cost: %" PRIu64 "us", tem->suid, tem->colName, tem->colVal, cost); - ret = tfileReaderLoadTableIds((TFileReader*)reader, offset, tr->total); + ret = tfileReaderLoadTableIds((TFileReader*)reader, (int32_t)offset, tr->total); cost = taosGetTimestampUs() - et; indexInfo("index: %" PRIu64 ", col: %s, colVal: %s, load all table info, time cost: %" PRIu64 "us", tem->suid, tem->colName, tem->colVal, cost); @@ -500,15 +501,15 @@ static int32_t tfSearchCompareFunc_JSON(void* reader, SIndexTerm* tem, SIdxTempR int tfileReaderSearch(TFileReader* reader, SIndexTermQuery* query, SIdxTempResult* tr) { SIndexTerm* term = query->term; EIndexQueryType qtype = query->qType; - + int ret = 0; if (INDEX_TYPE_CONTAIN_EXTERN_TYPE(term->colType, TSDB_DATA_TYPE_JSON)) { - return tfSearch[1][qtype](reader, term, tr); + ret = tfSearch[1][qtype](reader, term, tr); } else { - return tfSearch[0][qtype](reader, term, tr); + ret = tfSearch[0][qtype](reader, term, tr); } tfileReaderUnRef(reader); - return 0; + return ret; } TFileWriter* tfileWriterOpen(char* path, uint64_t suid, int32_t version, const char* colName, uint8_t colType) { @@ -538,7 +539,7 @@ TFileReader* tfileReaderOpen(char* path, uint64_t suid, int32_t version, const c indexError("failed to open readonly file: %s, reason: %s", fullname, terrstr()); return NULL; } - indexInfo("open read file name:%s, file size: %d", wc->file.buf, wc->file.size); + indexTrace("open read file name:%s, file size: %d", wc->file.buf, wc->file.size); TFileReader* reader = tfileReaderCreate(wc); return reader; @@ -890,7 +891,7 @@ static int tfileWriteFooter(TFileWriter* write) { char buf[sizeof(tfileMagicNumber) + 1] = {0}; void* pBuf = (void*)buf; taosEncodeFixedU64((void**)(void*)&pBuf, tfileMagicNumber); - int nwrite = write->ctx->write(write->ctx, buf, strlen(buf)); + int nwrite = write->ctx->write(write->ctx, buf, (int32_t)strlen(buf)); indexInfo("tfile write footer size: %d", write->ctx->size(write->ctx)); assert(nwrite == sizeof(tfileMagicNumber)); diff --git a/source/libs/index/src/indexUtil.c b/source/libs/index/src/indexUtil.c index 7b83cf465db951847ca45fd72327aa24a9ba9890..a618787fd49c96b729e782b4a01a5374c76639be 100644 --- a/source/libs/index/src/indexUtil.c +++ b/source/libs/index/src/indexUtil.c @@ -37,14 +37,14 @@ static int iBinarySearch(SArray *arr, int s, int e, uint64_t k) { } void iIntersection(SArray *inters, SArray *final) { - int32_t sz = taosArrayGetSize(inters); + int32_t sz = (int32_t)taosArrayGetSize(inters); if (sz <= 0) { return; } MergeIndex *mi = taosMemoryCalloc(sz, sizeof(MergeIndex)); for (int i = 0; i < sz; i++) { SArray *t = taosArrayGetP(inters, i); - mi[i].len = taosArrayGetSize(t); + mi[i].len = (int32_t)taosArrayGetSize(t); mi[i].idx = 0; } @@ -70,7 +70,7 @@ void iIntersection(SArray *inters, SArray *final) { taosMemoryFreeClear(mi); } void iUnion(SArray *inters, SArray *final) { - int32_t sz = taosArrayGetSize(inters); + int32_t sz = (int32_t)taosArrayGetSize(inters); if (sz <= 0) { return; } @@ -82,7 +82,7 @@ void iUnion(SArray *inters, SArray *final) { MergeIndex *mi = taosMemoryCalloc(sz, sizeof(MergeIndex)); for (int i = 0; i < sz; i++) { SArray *t = taosArrayGetP(inters, i); - mi[i].len = taosArrayGetSize(t); + mi[i].len = (int32_t)taosArrayGetSize(t); mi[i].idx = 0; } while (1) { @@ -117,8 +117,8 @@ void iUnion(SArray *inters, SArray *final) { } void iExcept(SArray *total, SArray *except) { - int32_t tsz = taosArrayGetSize(total); - int32_t esz = taosArrayGetSize(except); + int32_t tsz = (int32_t)taosArrayGetSize(total); + int32_t esz = (int32_t)taosArrayGetSize(except); if (esz == 0 || tsz == 0) { return; } @@ -141,7 +141,10 @@ int uidCompare(const void *a, const void *b) { // add more version compare uint64_t u1 = *(uint64_t *)a; uint64_t u2 = *(uint64_t *)b; - return u1 - u2; + if (u1 == u2) { + return 0; + } + return u1 < u2 ? -1 : 1; } int verdataCompare(const void *a, const void *b) { SIdxVerdata *va = (SIdxVerdata *)a; diff --git a/source/libs/index/test/CMakeLists.txt b/source/libs/index/test/CMakeLists.txt index a5c02fb9dcf28d6a6c20b951cd23ecff0b2560e2..c0b47e74c6b0561141806dae8ce14ab4d632ec8e 100644 --- a/source/libs/index/test/CMakeLists.txt +++ b/source/libs/index/test/CMakeLists.txt @@ -92,7 +92,19 @@ target_link_libraries (jsonUT index ) -#add_test( -# NAME index_test -# COMMAND indexTest -#) +add_test( + NAME idxtest + COMMAND indexTest +) +add_test( + NAME idxJsonUT + COMMAND jsonUT +) +add_test( + NAME idxUtilUT + COMMAND UtilUT +) +add_test( + NAME idxFstUT + COMMAND fstUT +) diff --git a/source/libs/index/test/fstTest.cc b/source/libs/index/test/fstTest.cc index 0af82c9175635ddb2952a1e41b2634ce31729590..a2d7adf1c7fc6e9ea2c1996206280f953939fd65 100644 --- a/source/libs/index/test/fstTest.cc +++ b/source/libs/index/test/fstTest.cc @@ -15,7 +15,7 @@ #include "tutil.h" void* callback(void* s) { return s; } -static std::string fileName = "/tmp/tindex.tindex"; +static std::string fileName = TD_TMP_DIR_PATH "tindex.tindex"; class FstWriter { public: FstWriter() { @@ -48,7 +48,7 @@ class FstWriter { class FstReadMemory { public: - FstReadMemory(size_t size, const std::string& fileName = "/tmp/tindex.tindex") { + FstReadMemory(int32_t size, const std::string& fileName = TD_TMP_DIR_PATH "tindex.tindex") { _wc = writerCtxCreate(TFile, fileName.c_str(), true, 64 * 1024); _w = fstCountingWriterCreate(_wc); _size = size; @@ -152,7 +152,7 @@ class FstReadMemory { Fst* _fst; FstSlice _s; WriterCtx* _wc; - size_t _size; + int32_t _size; }; #define L 100 diff --git a/source/libs/index/test/fstUT.cc b/source/libs/index/test/fstUT.cc index ab6c1a47045ea4957dcfd57ccda8c9bbcd8d7a90..136c4dafecdceba665d4a8657ed2053fa2486675 100644 --- a/source/libs/index/test/fstUT.cc +++ b/source/libs/index/test/fstUT.cc @@ -17,7 +17,7 @@ #include "tskiplist.h" #include "tutil.h" -static std::string dir = "/tmp/index"; +static std::string dir = TD_TMP_DIR_PATH "index"; static char indexlog[PATH_MAX] = {0}; static char tindex[PATH_MAX] = {0}; diff --git a/source/libs/index/test/indexTests.cc b/source/libs/index/test/indexTests.cc index 896451c686ec720b89a9e5c6edd9e3b2da83790b..f848cee86b4af0376af61640eb01a07eb1c22371 100644 --- a/source/libs/index/test/indexTests.cc +++ b/source/libs/index/test/indexTests.cc @@ -51,7 +51,7 @@ class DebugInfo { class FstWriter { public: FstWriter() { - _wc = writerCtxCreate(TFile, "/tmp/tindex", false, 64 * 1024 * 1024); + _wc = writerCtxCreate(TFile, TD_TMP_DIR_PATH "tindex", false, 64 * 1024 * 1024); _b = fstBuilderCreate(NULL, 0); } bool Put(const std::string& key, uint64_t val) { @@ -75,7 +75,7 @@ class FstWriter { class FstReadMemory { public: FstReadMemory(size_t size) { - _wc = writerCtxCreate(TFile, "/tmp/tindex", true, 64 * 1024); + _wc = writerCtxCreate(TFile, TD_TMP_DIR_PATH "tindex", true, 64 * 1024); _w = fstCountingWriterCreate(_wc); _size = size; memset((void*)&_s, 0, sizeof(_s)); @@ -272,9 +272,26 @@ void validateFst() { } delete m; } +static std::string logDir = "/tmp/log"; + +static void initLog() { + const char* defaultLogFileNamePrefix = "taoslog"; + const int32_t maxLogFileNum = 10; + + tsAsyncLog = 0; + sDebugFlag = 143; + strcpy(tsLogDir, logDir.c_str()); + taosRemoveDir(tsLogDir); + taosMkDir(tsLogDir); + + if (taosInitLog(defaultLogFileNamePrefix, maxLogFileNum) < 0) { + printf("failed to open log file in directory:%s\n", tsLogDir); + } +} class IndexEnv : public ::testing::Test { protected: virtual void SetUp() { + initLog(); taosRemoveDir(path); opts = indexOptsCreate(); int ret = indexOpen(opts, path, &index); @@ -285,7 +302,7 @@ class IndexEnv : public ::testing::Test { indexOptsDestroy(opts); } - const char* path = "/tmp/tindex"; + const char* path = TD_TMP_DIR_PATH "tindex"; SIndexOpts* opts; SIndex* index; }; @@ -342,7 +359,7 @@ class IndexEnv : public ::testing::Test { class TFileObj { public: - TFileObj(const std::string& path = "/tmp/tindex", const std::string& colName = "voltage") + TFileObj(const std::string& path = TD_TMP_DIR_PATH "tindex", const std::string& colName = "voltage") : path_(path), colName_(colName) { colId_ = 10; reader_ = NULL; @@ -437,7 +454,7 @@ class IndexTFileEnv : public ::testing::Test { // tfileWriterDestroy(twrite); } TFileObj* fObj; - std::string dir = "/tmp/tindex"; + std::string dir = TD_TMP_DIR_PATH "tindex"; std::string colName = "voltage"; int coldId = 2; @@ -657,10 +674,13 @@ class IndexObj { // opt numOfWrite = 0; numOfRead = 0; - indexInit(); + // indexInit(); } - int Init(const std::string& dir) { - taosRemoveDir(dir.c_str()); + int Init(const std::string& dir, bool remove = true) { + if (remove) { + taosRemoveDir(dir.c_str()); + taosMkDir(dir.c_str()); + } taosMkDir(dir.c_str()); int ret = indexOpen(&opts, dir.c_str(), &idx); if (ret != 0) { @@ -714,7 +734,7 @@ class IndexObj { return numOfTable; } int ReadMultiMillonData(const std::string& colName, const std::string& colVal = "Hello world", - size_t numOfTable = 100 * 10000) { + size_t numOfTable = 100) { std::string tColVal = colVal; int colValSize = tColVal.size(); @@ -804,7 +824,7 @@ class IndexObj { } ~IndexObj() { - indexCleanUp(); + // indexCleanUp(); indexClose(idx); } @@ -817,12 +837,18 @@ class IndexObj { class IndexEnv2 : public ::testing::Test { protected: - virtual void SetUp() { index = new IndexObj(); } - virtual void TearDown() { delete index; } - IndexObj* index; + virtual void SetUp() { + initLog(); + index = new IndexObj(); + } + virtual void TearDown() { + // taosMsleep(500); + delete index; + } + IndexObj* index; }; TEST_F(IndexEnv2, testIndexOpen) { - std::string path = "/tmp/test"; + std::string path = TD_TMP_DIR_PATH "test"; if (index->Init(path) != 0) { std::cout << "failed to init index" << std::endl; exit(1); @@ -884,19 +910,42 @@ TEST_F(IndexEnv2, testIndexOpen) { SArray* result = (SArray*)taosArrayInit(1, sizeof(uint64_t)); index->Search(mq, result); std::cout << "target size: " << taosArrayGetSize(result) << std::endl; - assert(taosArrayGetSize(result) == 400); + EXPECT_EQ(400, taosArrayGetSize(result)); taosArrayDestroy(result); indexMultiTermQueryDestroy(mq); } } +TEST_F(IndexEnv2, testEmptyIndexOpen) { + std::string path = "/tmp/test"; + if (index->Init(path) != 0) { + std::cout << "failed to init index" << std::endl; + exit(1); + } + + int targetSize = 1; + { + std::string colName("tag1"), colVal("Hello"); + + SIndexTerm* term = indexTermCreate(0, ADD_VALUE, TSDB_DATA_TYPE_BINARY, colName.c_str(), colName.size(), + colVal.c_str(), colVal.size()); + SIndexMultiTerm* terms = indexMultiTermCreate(); + indexMultiTermAdd(terms, term); + for (size_t i = 0; i < targetSize; i++) { + int tableId = i; + int ret = index->Put(terms, tableId); + assert(ret == 0); + } + indexMultiTermDestroy(terms); + } +} TEST_F(IndexEnv2, testIndex_TrigeFlush) { - std::string path = "/tmp/testxxx"; + std::string path = TD_TMP_DIR_PATH "testxxx"; if (index->Init(path) != 0) { // r std::cout << "failed to init" << std::endl; } - int numOfTable = 100 * 10000; + int numOfTable = 100 * 100; index->WriteMillonData("tag1", "Hello Wolrd", numOfTable); int target = index->SearchOne("tag1", "Hello Wolrd"); std::cout << "Get Index: " << target << std::endl; @@ -908,20 +957,22 @@ static void single_write_and_search(IndexObj* idx) { target = idx->SearchOne("tag2", "Test"); } static void multi_write_and_search(IndexObj* idx) { + idx->PutOne("tag1", "Hello"); + idx->PutOne("tag2", "Test"); int target = idx->SearchOne("tag1", "Hello"); target = idx->SearchOne("tag2", "Test"); - idx->WriteMultiMillonData("tag1", "hello world test", 100 * 10000); - idx->WriteMultiMillonData("tag2", "world test nothing", 100 * 10000); + idx->WriteMultiMillonData("tag1", "hello world test", 100 * 100); + idx->WriteMultiMillonData("tag2", "world test nothing", 100 * 10); } TEST_F(IndexEnv2, testIndex_serarch_cache_and_tfile) { - std::string path = "/tmp/cache_and_tfile"; + std::string path = TD_TMP_DIR_PATH "cache_and_tfile"; if (index->Init(path) != 0) { // opt } index->PutOne("tag1", "Hello"); index->PutOne("tag2", "Test"); - index->WriteMultiMillonData("tag1", "Hello", 100 * 10000); - index->WriteMultiMillonData("tag2", "Test", 100 * 10000); + index->WriteMultiMillonData("tag1", "Hello", 100 * 100); + index->WriteMultiMillonData("tag2", "Test", 100 * 100); std::thread threads[NUM_OF_THREAD]; for (int i = 0; i < NUM_OF_THREAD; i++) { @@ -934,7 +985,7 @@ TEST_F(IndexEnv2, testIndex_serarch_cache_and_tfile) { } } TEST_F(IndexEnv2, testIndex_MultiWrite_and_MultiRead) { - std::string path = "/tmp/cache_and_tfile"; + std::string path = TD_TMP_DIR_PATH "cache_and_tfile"; if (index->Init(path) != 0) { } @@ -950,116 +1001,116 @@ TEST_F(IndexEnv2, testIndex_MultiWrite_and_MultiRead) { } TEST_F(IndexEnv2, testIndex_restart) { - std::string path = "/tmp/cache_and_tfile"; - if (index->Init(path) != 0) { - } - index->SearchOneTarget("tag1", "Hello", 10); - index->SearchOneTarget("tag2", "Test", 10); -} -TEST_F(IndexEnv2, testIndex_restart1) { - std::string path = "/tmp/cache_and_tfile"; - if (index->Init(path) != 0) { + std::string path = TD_TMP_DIR_PATH "cache_and_tfile"; + if (index->Init(path, false) != 0) { } - index->ReadMultiMillonData("tag1", "coding"); index->SearchOneTarget("tag1", "Hello", 10); index->SearchOneTarget("tag2", "Test", 10); } +// TEST_F(IndexEnv2, testIndex_restart1) { +// std::string path = TD_TMP_DIR_PATH "cache_and_tfile"; +// if (index->Init(path, false) != 0) { +// } +// index->ReadMultiMillonData("tag1", "coding"); +// index->SearchOneTarget("tag1", "Hello", 10); +// index->SearchOneTarget("tag2", "Test", 10); +//} -TEST_F(IndexEnv2, testIndex_read_performance) { - std::string path = "/tmp/cache_and_tfile"; - if (index->Init(path) != 0) { - } - index->PutOneTarge("tag1", "Hello", 12); - index->PutOneTarge("tag1", "Hello", 15); - index->ReadMultiMillonData("tag1", "Hello"); - std::cout << "reader sz: " << index->SearchOne("tag1", "Hello") << std::endl; - assert(3 == index->SearchOne("tag1", "Hello")); -} +// TEST_F(IndexEnv2, testIndex_read_performance) { +// std::string path = TD_TMP_DIR_PATH "cache_and_tfile"; +// if (index->Init(path) != 0) { +// } +// index->PutOneTarge("tag1", "Hello", 12); +// index->PutOneTarge("tag1", "Hello", 15); +// index->ReadMultiMillonData("tag1", "Hello"); +// std::cout << "reader sz: " << index->SearchOne("tag1", "Hello") << std::endl; +// assert(3 == index->SearchOne("tag1", "Hello")); +//} TEST_F(IndexEnv2, testIndexMultiTag) { - std::string path = "/tmp/multi_tag"; + std::string path = TD_TMP_DIR_PATH "multi_tag"; if (index->Init(path) != 0) { } int64_t st = taosGetTimestampUs(); - int32_t num = 1000 * 10000; + int32_t num = 100 * 100; index->WriteMultiMillonData("tag1", "xxxxxxxxxxxxxxx", num); std::cout << "numOfRow: " << num << "\ttime cost:" << taosGetTimestampUs() - st << std::endl; // index->WriteMultiMillonData("tag2", "xxxxxxxxxxxxxxxxxxxxxxxxx", 100 * 10000); } TEST_F(IndexEnv2, testLongComVal1) { - std::string path = "/tmp/long_colVal"; + std::string path = TD_TMP_DIR_PATH "long_colVal"; if (index->Init(path) != 0) { } // gen colVal by randstr std::string randstr = "xxxxxxxxxxxxxxxxx"; - index->WriteMultiMillonData("tag1", randstr, 100 * 10000); + index->WriteMultiMillonData("tag1", randstr, 100 * 1000); } TEST_F(IndexEnv2, testLongComVal2) { - std::string path = "/tmp/long_colVal"; + std::string path = TD_TMP_DIR_PATH "long_colVal"; if (index->Init(path) != 0) { } // gen colVal by randstr std::string randstr = "abcccc fdadfafdafda"; - index->WriteMultiMillonData("tag1", randstr, 100 * 10000); + index->WriteMultiMillonData("tag1", randstr, 100 * 1000); } TEST_F(IndexEnv2, testLongComVal3) { - std::string path = "/tmp/long_colVal"; + std::string path = TD_TMP_DIR_PATH "long_colVal"; if (index->Init(path) != 0) { } // gen colVal by randstr std::string randstr = "Yes, coding and coding and coding"; - index->WriteMultiMillonData("tag1", randstr, 100 * 10000); + index->WriteMultiMillonData("tag1", randstr, 100 * 1000); } TEST_F(IndexEnv2, testLongComVal4) { - std::string path = "/tmp/long_colVal"; + std::string path = TD_TMP_DIR_PATH "long_colVal"; if (index->Init(path) != 0) { } // gen colVal by randstr std::string randstr = "111111 bac fdadfa"; - index->WriteMultiMillonData("tag1", randstr, 100 * 10000); + index->WriteMultiMillonData("tag1", randstr, 100 * 100); } TEST_F(IndexEnv2, testIndex_read_performance1) { - std::string path = "/tmp/cache_and_tfile"; + std::string path = TD_TMP_DIR_PATH "cache_and_tfile"; if (index->Init(path) != 0) { } index->PutOneTarge("tag1", "Hello", 12); index->PutOneTarge("tag1", "Hello", 15); index->ReadMultiMillonData("tag1", "Hello", 1000); std::cout << "reader sz: " << index->SearchOne("tag1", "Hello") << std::endl; - assert(3 == index->SearchOne("tag1", "Hello")); + EXPECT_EQ(2, index->SearchOne("tag1", "Hello")); } TEST_F(IndexEnv2, testIndex_read_performance2) { - std::string path = "/tmp/cache_and_tfile"; + std::string path = TD_TMP_DIR_PATH "cache_and_tfile"; if (index->Init(path) != 0) { } index->PutOneTarge("tag1", "Hello", 12); index->PutOneTarge("tag1", "Hello", 15); - index->ReadMultiMillonData("tag1", "Hello", 1000 * 10); + index->ReadMultiMillonData("tag1", "Hello", 1000); std::cout << "reader sz: " << index->SearchOne("tag1", "Hello") << std::endl; - assert(3 == index->SearchOne("tag1", "Hello")); + EXPECT_EQ(2, index->SearchOne("tag1", "Hello")); } TEST_F(IndexEnv2, testIndex_read_performance3) { - std::string path = "/tmp/cache_and_tfile"; + std::string path = TD_TMP_DIR_PATH "cache_and_tfile"; if (index->Init(path) != 0) { } index->PutOneTarge("tag1", "Hello", 12); index->PutOneTarge("tag1", "Hello", 15); - index->ReadMultiMillonData("tag1", "Hello", 1000 * 100); + index->ReadMultiMillonData("tag1", "Hello", 1000); std::cout << "reader sz: " << index->SearchOne("tag1", "Hello") << std::endl; - assert(3 == index->SearchOne("tag1", "Hello")); + EXPECT_EQ(2, index->SearchOne("tag1", "Hello")); } TEST_F(IndexEnv2, testIndex_read_performance4) { - std::string path = "/tmp/cache_and_tfile"; + std::string path = TD_TMP_DIR_PATH "cache_and_tfile"; if (index->Init(path) != 0) { } index->PutOneTarge("tag10", "Hello", 12); index->PutOneTarge("tag12", "Hello", 15); - index->ReadMultiMillonData("tag10", "Hello", 1000 * 100); + index->ReadMultiMillonData("tag10", "Hello", 1000); std::cout << "reader sz: " << index->SearchOne("tag1", "Hello") << std::endl; - assert(3 == index->SearchOne("tag10", "Hello")); + EXPECT_EQ(1, index->SearchOne("tag10", "Hello")); } TEST_F(IndexEnv2, testIndex_cache_del) { - std::string path = "/tmp/cache_and_tfile"; + std::string path = TD_TMP_DIR_PATH "cache_and_tfile"; if (index->Init(path) != 0) { } for (int i = 0; i < 100; i++) { @@ -1098,7 +1149,7 @@ TEST_F(IndexEnv2, testIndex_cache_del) { } TEST_F(IndexEnv2, testIndex_del) { - std::string path = "/tmp/cache_and_tfile"; + std::string path = TD_TMP_DIR_PATH "cache_and_tfile"; if (index->Init(path) != 0) { } for (int i = 0; i < 100; i++) { @@ -1108,7 +1159,7 @@ TEST_F(IndexEnv2, testIndex_del) { index->Del("tag10", "Hello", 11); EXPECT_EQ(98, index->SearchOne("tag10", "Hello")); - index->WriteMultiMillonData("tag10", "xxxxxxxxxxxxxx", 100 * 10000); + index->WriteMultiMillonData("tag10", "xxxxxxxxxxxxxx", 100 * 100); index->Del("tag10", "Hello", 17); EXPECT_EQ(97, index->SearchOne("tag10", "Hello")); } diff --git a/source/libs/executor/test/index_executor_tests.cpp b/source/libs/index/test/index_executor_tests.cpp similarity index 99% rename from source/libs/executor/test/index_executor_tests.cpp rename to source/libs/index/test/index_executor_tests.cpp index 2449bd1da1824ec94e4c071a0052950b29aeabe1..b0c2a983d1b5f60b50e4f5734a8c99fb3729d80e 100644 --- a/source/libs/executor/test/index_executor_tests.cpp +++ b/source/libs/index/test/index_executor_tests.cpp @@ -57,7 +57,7 @@ void sifInitLogFile() { tsAsyncLog = 0; qDebugFlag = 159; - strcpy(tsLogDir, "/tmp/sif"); + strcpy(tsLogDir, TD_TMP_DIR_PATH "sif"); taosRemoveDir(tsLogDir); taosMkDir(tsLogDir); diff --git a/source/libs/index/test/jsonUT.cc b/source/libs/index/test/jsonUT.cc index ffb102ef2eee0bc2e74e80616a10e4c0ccbe9cce..8a837c5700da2b8c70d083d5f282933844091673 100644 --- a/source/libs/index/test/jsonUT.cc +++ b/source/libs/index/test/jsonUT.cc @@ -16,8 +16,8 @@ #include "tskiplist.h" #include "tutil.h" -static std::string dir = "/tmp/json"; -static std::string logDir = "/tmp/log"; +static std::string dir = TD_TMP_DIR_PATH "json"; +static std::string logDir = TD_TMP_DIR_PATH "log"; static void initLog() { const char* defaultLogFileNamePrefix = "taoslog"; @@ -154,7 +154,7 @@ TEST_F(JsonEnv, testWriteMillonData) { SIndexMultiTerm* terms = indexMultiTermCreate(); indexMultiTermAdd(terms, term); - for (size_t i = 0; i < 100; i++) { + for (size_t i = 0; i < 10; i++) { tIndexJsonPut(index, terms, i); } indexMultiTermDestroy(terms); @@ -162,14 +162,14 @@ TEST_F(JsonEnv, testWriteMillonData) { { std::string colName("voltagefdadfa"); std::string colVal("abxxxxxxxxxxxx"); - for (int i = 0; i < 1000; i++) { + for (int i = 0; i < 10; i++) { colVal[i % colVal.size()] = '0' + i % 128; SIndexTerm* term = indexTermCreate(1, ADD_VALUE, TSDB_DATA_TYPE_BINARY, colName.c_str(), colName.size(), colVal.c_str(), colVal.size()); SIndexMultiTerm* terms = indexMultiTermCreate(); indexMultiTermAdd(terms, term); - for (size_t i = 0; i < 1000; i++) { + for (size_t i = 0; i < 100; i++) { tIndexJsonPut(index, terms, i); } indexMultiTermDestroy(terms); @@ -199,7 +199,7 @@ TEST_F(JsonEnv, testWriteMillonData) { SArray* result = taosArrayInit(1, sizeof(uint64_t)); indexMultiTermQueryAdd(mq, q, QUERY_TERM); tIndexJsonSearch(index, mq, result); - assert(100 == taosArrayGetSize(result)); + EXPECT_EQ(10, taosArrayGetSize(result)); indexMultiTermQueryDestroy(mq); } { @@ -229,7 +229,7 @@ TEST_F(JsonEnv, testWriteMillonData) { SArray* result = taosArrayInit(1, sizeof(uint64_t)); indexMultiTermQueryAdd(mq, q, QUERY_GREATER_EQUAL); tIndexJsonSearch(index, mq, result); - assert(100 == taosArrayGetSize(result)); + EXPECT_EQ(10, taosArrayGetSize(result)); indexMultiTermQueryDestroy(mq); } } @@ -385,7 +385,7 @@ TEST_F(JsonEnv, testWriteJsonTfileAndCache_INT) { SIndexMultiTerm* terms = indexMultiTermCreate(); indexMultiTermAdd(terms, term); - for (size_t i = 0; i < 100000; i++) { + for (size_t i = 0; i < 1000; i++) { tIndexJsonPut(index, terms, i); } indexMultiTermDestroy(terms); @@ -523,7 +523,7 @@ TEST_F(JsonEnv, testWriteJsonTfileAndCache_INT2) { { int val = 10; std::string colName("test1"); - for (int i = 0; i < 10000; i++) { + for (int i = 0; i < 1000; i++) { val += 1; WriteData(index, colName, TSDB_DATA_TYPE_INT, &val, sizeof(val), i); } @@ -532,7 +532,7 @@ TEST_F(JsonEnv, testWriteJsonTfileAndCache_INT2) { int val = 10; std::string colName("test2xxx"); std::string colVal("xxxxxxxxxxxxxxx"); - for (int i = 0; i < 100000; i++) { + for (int i = 0; i < 1000; i++) { val += 1; WriteData(index, colName, TSDB_DATA_TYPE_BINARY, (void*)(colVal.c_str()), colVal.size(), i); } @@ -542,14 +542,14 @@ TEST_F(JsonEnv, testWriteJsonTfileAndCache_INT2) { std::string colName("test1"); int val = 9; Search(index, colName, TSDB_DATA_TYPE_INT, &val, sizeof(val), QUERY_GREATER_EQUAL, &res); - EXPECT_EQ(10000, taosArrayGetSize(res)); + EXPECT_EQ(1000, taosArrayGetSize(res)); } { SArray* res = NULL; std::string colName("test2xxx"); std::string colVal("xxxxxxxxxxxxxxx"); Search(index, colName, TSDB_DATA_TYPE_BINARY, (void*)(colVal.c_str()), colVal.size(), QUERY_TERM, &res); - EXPECT_EQ(100000, taosArrayGetSize(res)); + EXPECT_EQ(1000, taosArrayGetSize(res)); } } TEST_F(JsonEnv, testWriteJsonTfileAndCache_FLOAT) { diff --git a/source/libs/nodes/src/nodesCloneFuncs.c b/source/libs/nodes/src/nodesCloneFuncs.c index 8019200e7653c72f2993b3b6b2b9303ddef6085b..5774dcaa1d2c7d5006f440e04867ed66f67d90f1 100644 --- a/source/libs/nodes/src/nodesCloneFuncs.c +++ b/source/libs/nodes/src/nodesCloneFuncs.c @@ -19,6 +19,21 @@ #include "taos.h" #include "taoserror.h" +#define COPY_SCALAR_FIELD(fldname) \ + do { \ + (pDst)->fldname = (pSrc)->fldname; \ + } while (0) + +#define COPY_CHAR_ARRAY_FIELD(fldname) \ + do { \ + strcpy((pDst)->fldname, (pSrc)->fldname); \ + } while (0) + +#define COPY_OBJECT_FIELD(fldname, size) \ + do { \ + memcpy(&((pDst)->fldname), &((pSrc)->fldname), size); \ + } while (0) + #define COPY_CHAR_POINT_FIELD(fldname) \ do { \ if (NULL == (pSrc)->fldname) { \ @@ -70,27 +85,61 @@ } \ } while (0) -static void dataTypeCopy(const SDataType* pSrc, SDataType* pDst) {} - static SNode* exprNodeCopy(const SExprNode* pSrc, SExprNode* pDst) { - dataTypeCopy(&pSrc->resType, &pDst->resType); - pDst->pAssociation = NULL; + COPY_OBJECT_FIELD(resType, sizeof(SDataType)); + COPY_CHAR_ARRAY_FIELD(aliasName); + COPY_CHAR_ARRAY_FIELD(userAlias); return (SNode*)pDst; } static SNode* columnNodeCopy(const SColumnNode* pSrc, SColumnNode* pDst) { COPY_BASE_OBJECT_FIELD(node, exprNodeCopy); - pDst->pProjectRef = NULL; + COPY_SCALAR_FIELD(tableId); + COPY_SCALAR_FIELD(tableType); + COPY_SCALAR_FIELD(colId); + COPY_SCALAR_FIELD(colType); + COPY_CHAR_ARRAY_FIELD(dbName); + COPY_CHAR_ARRAY_FIELD(tableName); + COPY_CHAR_ARRAY_FIELD(tableAlias); + COPY_CHAR_ARRAY_FIELD(colName); + COPY_SCALAR_FIELD(dataBlockId); + COPY_SCALAR_FIELD(slotId); return (SNode*)pDst; } static SNode* valueNodeCopy(const SValueNode* pSrc, SValueNode* pDst) { COPY_BASE_OBJECT_FIELD(node, exprNodeCopy); COPY_CHAR_POINT_FIELD(literal); + COPY_SCALAR_FIELD(isDuration); + COPY_SCALAR_FIELD(translate); + COPY_SCALAR_FIELD(notReserved); + COPY_SCALAR_FIELD(placeholderNo); + COPY_SCALAR_FIELD(typeData); + COPY_SCALAR_FIELD(unit); if (!pSrc->translate) { return (SNode*)pDst; } switch (pSrc->node.resType.type) { + case TSDB_DATA_TYPE_BOOL: + COPY_SCALAR_FIELD(datum.b); + break; + case TSDB_DATA_TYPE_TINYINT: + case TSDB_DATA_TYPE_SMALLINT: + case TSDB_DATA_TYPE_INT: + case TSDB_DATA_TYPE_BIGINT: + case TSDB_DATA_TYPE_TIMESTAMP: + COPY_SCALAR_FIELD(datum.i); + break; + case TSDB_DATA_TYPE_FLOAT: + case TSDB_DATA_TYPE_DOUBLE: + COPY_SCALAR_FIELD(datum.d); + break; + case TSDB_DATA_TYPE_UTINYINT: + case TSDB_DATA_TYPE_USMALLINT: + case TSDB_DATA_TYPE_UINT: + case TSDB_DATA_TYPE_UBIGINT: + COPY_SCALAR_FIELD(datum.u); + break; case TSDB_DATA_TYPE_NCHAR: case TSDB_DATA_TYPE_VARCHAR: case TSDB_DATA_TYPE_VARBINARY: @@ -104,7 +153,7 @@ static SNode* valueNodeCopy(const SValueNode* pSrc, SValueNode* pDst) { case TSDB_DATA_TYPE_JSON: case TSDB_DATA_TYPE_DECIMAL: case TSDB_DATA_TYPE_BLOB: - // todo + case TSDB_DATA_TYPE_MEDIUMBLOB: default: break; } @@ -113,6 +162,7 @@ static SNode* valueNodeCopy(const SValueNode* pSrc, SValueNode* pDst) { static SNode* operatorNodeCopy(const SOperatorNode* pSrc, SOperatorNode* pDst) { COPY_BASE_OBJECT_FIELD(node, exprNodeCopy); + COPY_SCALAR_FIELD(opType); CLONE_NODE_FIELD(pLeft); CLONE_NODE_FIELD(pRight); return (SNode*)pDst; @@ -120,18 +170,27 @@ static SNode* operatorNodeCopy(const SOperatorNode* pSrc, SOperatorNode* pDst) { static SNode* logicConditionNodeCopy(const SLogicConditionNode* pSrc, SLogicConditionNode* pDst) { COPY_BASE_OBJECT_FIELD(node, exprNodeCopy); + COPY_SCALAR_FIELD(condType); CLONE_NODE_LIST_FIELD(pParameterList); return (SNode*)pDst; } static SNode* functionNodeCopy(const SFunctionNode* pSrc, SFunctionNode* pDst) { COPY_BASE_OBJECT_FIELD(node, exprNodeCopy); + COPY_CHAR_ARRAY_FIELD(functionName); + COPY_SCALAR_FIELD(funcId); + COPY_SCALAR_FIELD(funcType); CLONE_NODE_LIST_FIELD(pParameterList); + COPY_SCALAR_FIELD(udfBufSize); return (SNode*)pDst; } static SNode* tableNodeCopy(const STableNode* pSrc, STableNode* pDst) { COPY_BASE_OBJECT_FIELD(node, exprNodeCopy); + COPY_CHAR_ARRAY_FIELD(dbName); + COPY_CHAR_ARRAY_FIELD(tableName); + COPY_CHAR_ARRAY_FIELD(tableAlias); + COPY_SCALAR_FIELD(precision); return (SNode*)pDst; } @@ -159,6 +218,8 @@ static SNode* realTableNodeCopy(const SRealTableNode* pSrc, SRealTableNode* pDst COPY_BASE_OBJECT_FIELD(table, tableNodeCopy); CLONE_OBJECT_FIELD(pMeta, tableMetaClone); CLONE_OBJECT_FIELD(pVgroupList, vgroupsInfoClone); + COPY_CHAR_ARRAY_FIELD(qualDbName); + COPY_SCALAR_FIELD(ratio); return (SNode*)pDst; } @@ -170,6 +231,7 @@ static SNode* tempTableNodeCopy(const STempTableNode* pSrc, STempTableNode* pDst static SNode* joinTableNodeCopy(const SJoinTableNode* pSrc, SJoinTableNode* pDst) { COPY_BASE_OBJECT_FIELD(table, tableNodeCopy); + COPY_SCALAR_FIELD(joinType); CLONE_NODE_FIELD(pLeft); CLONE_NODE_FIELD(pRight); CLONE_NODE_FIELD(pOnCond); @@ -177,21 +239,30 @@ static SNode* joinTableNodeCopy(const SJoinTableNode* pSrc, SJoinTableNode* pDst } static SNode* targetNodeCopy(const STargetNode* pSrc, STargetNode* pDst) { + COPY_SCALAR_FIELD(dataBlockId); + COPY_SCALAR_FIELD(slotId); CLONE_NODE_FIELD(pExpr); return (SNode*)pDst; } static SNode* groupingSetNodeCopy(const SGroupingSetNode* pSrc, SGroupingSetNode* pDst) { + COPY_SCALAR_FIELD(groupingSetType); CLONE_NODE_LIST_FIELD(pParameterList); return (SNode*)pDst; } static SNode* orderByExprNodeCopy(const SOrderByExprNode* pSrc, SOrderByExprNode* pDst) { CLONE_NODE_FIELD(pExpr); + COPY_SCALAR_FIELD(order); + COPY_SCALAR_FIELD(nullOrder); return (SNode*)pDst; } -static SNode* limitNodeCopy(const SLimitNode* pSrc, SLimitNode* pDst) { return (SNode*)pDst; } +static SNode* limitNodeCopy(const SLimitNode* pSrc, SLimitNode* pDst) { + COPY_SCALAR_FIELD(limit); + COPY_SCALAR_FIELD(offset); + return (SNode*)pDst; +} static SNode* stateWindowNodeCopy(const SStateWindowNode* pSrc, SStateWindowNode* pDst) { CLONE_NODE_FIELD(pCol); @@ -215,13 +286,16 @@ static SNode* intervalWindowNodeCopy(const SIntervalWindowNode* pSrc, SIntervalW } static SNode* nodeListNodeCopy(const SNodeListNode* pSrc, SNodeListNode* pDst) { + COPY_OBJECT_FIELD(dataType, sizeof(SDataType)); CLONE_NODE_LIST_FIELD(pNodeList); return (SNode*)pDst; } static SNode* fillNodeCopy(const SFillNode* pSrc, SFillNode* pDst) { + COPY_SCALAR_FIELD(mode); CLONE_NODE_FIELD(pValues); CLONE_NODE_FIELD(pWStartTs); + COPY_OBJECT_FIELD(timeRange, sizeof(STimeWindow)); return (SNode*)pDst; } @@ -229,7 +303,7 @@ static SNode* logicNodeCopy(const SLogicNode* pSrc, SLogicNode* pDst) { CLONE_NODE_LIST_FIELD(pTargets); CLONE_NODE_FIELD(pConditions); CLONE_NODE_LIST_FIELD(pChildren); - pDst->pParent = NULL; + COPY_SCALAR_FIELD(optimizedFlag); return (SNode*)pDst; } @@ -239,12 +313,25 @@ static SNode* logicScanCopy(const SScanLogicNode* pSrc, SScanLogicNode* pDst) { CLONE_NODE_LIST_FIELD(pScanPseudoCols); CLONE_OBJECT_FIELD(pMeta, tableMetaClone); CLONE_OBJECT_FIELD(pVgroupList, vgroupsInfoClone); + COPY_SCALAR_FIELD(scanType); + COPY_OBJECT_FIELD(scanSeq[0], sizeof(uint8_t) * 2); + COPY_OBJECT_FIELD(scanRange, sizeof(STimeWindow)); + COPY_OBJECT_FIELD(tableName, sizeof(SName)); + COPY_SCALAR_FIELD(showRewrite); + COPY_SCALAR_FIELD(ratio); CLONE_NODE_LIST_FIELD(pDynamicScanFuncs); + COPY_SCALAR_FIELD(dataRequired); + COPY_SCALAR_FIELD(interval); + COPY_SCALAR_FIELD(offset); + COPY_SCALAR_FIELD(sliding); + COPY_SCALAR_FIELD(intervalUnit); + COPY_SCALAR_FIELD(slidingUnit); return (SNode*)pDst; } static SNode* logicJoinCopy(const SJoinLogicNode* pSrc, SJoinLogicNode* pDst) { COPY_BASE_OBJECT_FIELD(node, logicNodeCopy); + COPY_SCALAR_FIELD(joinType); CLONE_NODE_FIELD(pOnConditions); return (SNode*)pDst; } @@ -259,32 +346,50 @@ static SNode* logicAggCopy(const SAggLogicNode* pSrc, SAggLogicNode* pDst) { static SNode* logicProjectCopy(const SProjectLogicNode* pSrc, SProjectLogicNode* pDst) { COPY_BASE_OBJECT_FIELD(node, logicNodeCopy); CLONE_NODE_LIST_FIELD(pProjections); + COPY_CHAR_ARRAY_FIELD(stmtName); + COPY_SCALAR_FIELD(limit); + COPY_SCALAR_FIELD(offset); + COPY_SCALAR_FIELD(slimit); + COPY_SCALAR_FIELD(soffset); return (SNode*)pDst; } static SNode* logicVnodeModifCopy(const SVnodeModifLogicNode* pSrc, SVnodeModifLogicNode* pDst) { COPY_BASE_OBJECT_FIELD(node, logicNodeCopy); - pDst->pDataBlocks = NULL; - pDst->pVgDataBlocks = NULL; + COPY_SCALAR_FIELD(msgType); return (SNode*)pDst; } static SNode* logicExchangeCopy(const SExchangeLogicNode* pSrc, SExchangeLogicNode* pDst) { COPY_BASE_OBJECT_FIELD(node, logicNodeCopy); + COPY_SCALAR_FIELD(srcGroupId); + COPY_SCALAR_FIELD(precision); return (SNode*)pDst; } static SNode* logicWindowCopy(const SWindowLogicNode* pSrc, SWindowLogicNode* pDst) { COPY_BASE_OBJECT_FIELD(node, logicNodeCopy); + COPY_SCALAR_FIELD(winType); CLONE_NODE_LIST_FIELD(pFuncs); + COPY_SCALAR_FIELD(interval); + COPY_SCALAR_FIELD(offset); + COPY_SCALAR_FIELD(sliding); + COPY_SCALAR_FIELD(intervalUnit); + COPY_SCALAR_FIELD(slidingUnit); + COPY_SCALAR_FIELD(sessionGap); CLONE_NODE_FIELD(pTspk); + CLONE_NODE_FIELD(pStateExpr); + COPY_SCALAR_FIELD(triggerType); + COPY_SCALAR_FIELD(watermark); return (SNode*)pDst; } static SNode* logicFillCopy(const SFillLogicNode* pSrc, SFillLogicNode* pDst) { COPY_BASE_OBJECT_FIELD(node, logicNodeCopy); + COPY_SCALAR_FIELD(mode); CLONE_NODE_FIELD(pWStartTs); CLONE_NODE_FIELD(pValues); + COPY_OBJECT_FIELD(timeRange, sizeof(STimeWindow)); return (SNode*)pDst; } @@ -301,28 +406,41 @@ static SNode* logicPartitionCopy(const SPartitionLogicNode* pSrc, SPartitionLogi } static SNode* logicSubplanCopy(const SLogicSubplan* pSrc, SLogicSubplan* pDst) { + COPY_OBJECT_FIELD(id, sizeof(SSubplanId)); CLONE_NODE_FIELD(pNode); - pDst->pChildren = NULL; - pDst->pParents = NULL; - pDst->pVgroupList = NULL; + COPY_SCALAR_FIELD(subplanType); + COPY_SCALAR_FIELD(level); + COPY_SCALAR_FIELD(splitFlag); return (SNode*)pDst; } static SNode* dataBlockDescCopy(const SDataBlockDescNode* pSrc, SDataBlockDescNode* pDst) { + COPY_SCALAR_FIELD(dataBlockId); CLONE_NODE_LIST_FIELD(pSlots); + COPY_SCALAR_FIELD(totalRowSize); + COPY_SCALAR_FIELD(outputRowSize); + COPY_SCALAR_FIELD(precision); return (SNode*)pDst; } static SNode* slotDescCopy(const SSlotDescNode* pSrc, SSlotDescNode* pDst) { - dataTypeCopy(&pSrc->dataType, &pDst->dataType); + COPY_SCALAR_FIELD(slotId); + COPY_OBJECT_FIELD(dataType, sizeof(SDataType)); + COPY_SCALAR_FIELD(reserve); + COPY_SCALAR_FIELD(output); + COPY_SCALAR_FIELD(tag); return (SNode*)pDst; } static SNode* downstreamSourceCopy(const SDownstreamSourceNode* pSrc, SDownstreamSourceNode* pDst) { + COPY_OBJECT_FIELD(addr, sizeof(SQueryNodeAddr)); + COPY_SCALAR_FIELD(taskId); + COPY_SCALAR_FIELD(schedId); return (SNode*)pDst; } static SNode* selectStmtCopy(const SSelectStmt* pSrc, SSelectStmt* pDst) { + COPY_SCALAR_FIELD(isDistinct); CLONE_NODE_LIST_FIELD(pProjectionList); CLONE_NODE_FIELD(pFromTable); CLONE_NODE_FIELD(pWhere); @@ -333,6 +451,12 @@ static SNode* selectStmtCopy(const SSelectStmt* pSrc, SSelectStmt* pDst) { CLONE_NODE_LIST_FIELD(pOrderByList); CLONE_NODE_FIELD(pLimit); CLONE_NODE_FIELD(pLimit); + COPY_CHAR_ARRAY_FIELD(stmtName); + COPY_SCALAR_FIELD(precision); + COPY_SCALAR_FIELD(isEmptyResult); + COPY_SCALAR_FIELD(isTimeOrderQuery); + COPY_SCALAR_FIELD(hasAggFuncs); + COPY_SCALAR_FIELD(hasRepeatScanFuncs); return (SNode*)pDst; } @@ -345,7 +469,6 @@ SNodeptr nodesCloneNode(const SNodeptr pNode) { terrno = TSDB_CODE_OUT_OF_MEMORY; return NULL; } - memcpy(pDst, pNode, nodesNodeSize(nodeType(pNode))); switch (nodeType(pNode)) { case QUERY_NODE_COLUMN: return columnNodeCopy((const SColumnNode*)pNode, (SColumnNode*)pDst); @@ -387,6 +510,8 @@ SNodeptr nodesCloneNode(const SNodeptr pNode) { return slotDescCopy((const SSlotDescNode*)pNode, (SSlotDescNode*)pDst); case QUERY_NODE_DOWNSTREAM_SOURCE: return downstreamSourceCopy((const SDownstreamSourceNode*)pNode, (SDownstreamSourceNode*)pDst); + case QUERY_NODE_LEFT_VALUE: + return pDst; case QUERY_NODE_SELECT_STMT: return selectStmtCopy((const SSelectStmt*)pNode, (SSelectStmt*)pDst); case QUERY_NODE_LOGIC_PLAN_SCAN: diff --git a/source/libs/nodes/src/nodesCodeFuncs.c b/source/libs/nodes/src/nodesCodeFuncs.c index bc49f36afe121db0c676662c972f088a660f897c..f28885aad560d09e3aee28f524d2835d5b66f2de 100644 --- a/source/libs/nodes/src/nodesCodeFuncs.c +++ b/source/libs/nodes/src/nodesCodeFuncs.c @@ -78,6 +78,8 @@ const char* nodesNodeName(ENodeType type) { return "TableOptions"; case QUERY_NODE_INDEX_OPTIONS: return "IndexOptions"; + case QUERY_NODE_LEFT_VALUE: + return "LeftValue"; case QUERY_NODE_SET_OPERATOR: return "SetOperator"; case QUERY_NODE_SELECT_STMT: @@ -490,6 +492,7 @@ static const char* jkScanLogicPlanScanCols = "ScanCols"; static const char* jkScanLogicPlanScanPseudoCols = "ScanPseudoCols"; static const char* jkScanLogicPlanTableMetaSize = "TableMetaSize"; static const char* jkScanLogicPlanTableMeta = "TableMeta"; +static const char* jkScanLogicPlanTagCond = "TagCond"; static int32_t logicScanNodeToJson(const void* pObj, SJson* pJson) { const SScanLogicNode* pNode = (const SScanLogicNode*)pObj; @@ -507,6 +510,9 @@ static int32_t logicScanNodeToJson(const void* pObj, SJson* pJson) { if (TSDB_CODE_SUCCESS == code) { code = tjsonAddObject(pJson, jkScanLogicPlanTableMeta, tableMetaToJson, pNode->pMeta); } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddObject(pJson, jkScanLogicPlanTagCond, nodeToJson, pNode->pTagCond); + } return code; } @@ -528,6 +534,9 @@ static int32_t jsonToLogicScanNode(const SJson* pJson, void* pObj) { if (TSDB_CODE_SUCCESS == code) { code = tjsonMakeObject(pJson, jkScanLogicPlanTableMeta, jsonToTableMeta, (void**)&pNode->pMeta, objSize); } + if (TSDB_CODE_SUCCESS == code) { + code = jsonToNodeObject(pJson, jkScanLogicPlanTagCond, &pNode->pTagCond); + } return code; } @@ -1771,9 +1780,11 @@ static const char* jkSubplanId = "Id"; static const char* jkSubplanType = "SubplanType"; static const char* jkSubplanMsgType = "MsgType"; static const char* jkSubplanLevel = "Level"; +static const char* jkSubplanDbFName = "DbFName"; static const char* jkSubplanNodeAddr = "NodeAddr"; static const char* jkSubplanRootNode = "RootNode"; static const char* jkSubplanDataSink = "DataSink"; +static const char* jkSubplanTagCond = "TagCond"; static int32_t subplanToJson(const void* pObj, SJson* pJson) { const SSubplan* pNode = (const SSubplan*)pObj; @@ -1788,6 +1799,9 @@ static int32_t subplanToJson(const void* pObj, SJson* pJson) { if (TSDB_CODE_SUCCESS == code) { code = tjsonAddIntegerToObject(pJson, jkSubplanLevel, pNode->level); } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddStringToObject(pJson, jkSubplanDbFName, pNode->dbFName); + } if (TSDB_CODE_SUCCESS == code) { code = tjsonAddObject(pJson, jkSubplanNodeAddr, queryNodeAddrToJson, &pNode->execNode); } @@ -1797,6 +1811,9 @@ static int32_t subplanToJson(const void* pObj, SJson* pJson) { if (TSDB_CODE_SUCCESS == code) { code = tjsonAddObject(pJson, jkSubplanDataSink, nodeToJson, pNode->pDataSink); } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddObject(pJson, jkSubplanTagCond, nodeToJson, pNode->pTagCond); + } return code; } @@ -1815,6 +1832,9 @@ static int32_t jsonToSubplan(const SJson* pJson, void* pObj) { if (TSDB_CODE_SUCCESS == code) { code = tjsonGetIntValue(pJson, jkSubplanLevel, &pNode->level); } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonGetStringValue(pJson, jkSubplanDbFName, pNode->dbFName); + } if (TSDB_CODE_SUCCESS == code) { code = tjsonToObject(pJson, jkSubplanNodeAddr, jsonToQueryNodeAddr, &pNode->execNode); } @@ -1824,6 +1844,9 @@ static int32_t jsonToSubplan(const SJson* pJson, void* pObj) { if (TSDB_CODE_SUCCESS == code) { code = jsonToNodeObject(pJson, jkSubplanDataSink, (SNode**)&pNode->pDataSink); } + if (TSDB_CODE_SUCCESS == code) { + code = jsonToNodeObject(pJson, jkSubplanTagCond, (SNode**)&pNode->pTagCond); + } return code; } @@ -2168,7 +2191,7 @@ static int32_t jsonToDatum(const SJson* pJson, void* pObj) { code = TSDB_CODE_OUT_OF_MEMORY; break; } - varDataSetLen(pNode->datum.p, pNode->node.resType.bytes); + varDataSetLen(pNode->datum.p, pNode->node.resType.bytes - VARSTR_HEADER_SIZE); if (TSDB_DATA_TYPE_NCHAR == pNode->node.resType.type) { char* buf = taosMemoryCalloc(1, pNode->node.resType.bytes * 2 + VARSTR_HEADER_SIZE + 1); if (NULL == buf) { @@ -3012,6 +3035,8 @@ static int32_t specificNodeToJson(const void* pObj, SJson* pJson) { break; case QUERY_NODE_DOWNSTREAM_SOURCE: return downstreamSourceNodeToJson(pObj, pJson); + case QUERY_NODE_LEFT_VALUE: + return TSDB_CODE_SUCCESS; // SLeftValueNode has no fields to serialize. case QUERY_NODE_SET_OPERATOR: return setOperatorToJson(pObj, pJson); case QUERY_NODE_SELECT_STMT: @@ -3123,6 +3148,8 @@ static int32_t jsonToSpecificNode(const SJson* pJson, void* pObj) { return jsonToSlotDescNode(pJson, pObj); case QUERY_NODE_DOWNSTREAM_SOURCE: return jsonToDownstreamSourceNode(pJson, pObj); + case QUERY_NODE_LEFT_VALUE: + return TSDB_CODE_SUCCESS; // SLeftValueNode has no fields to deserialize. case QUERY_NODE_SET_OPERATOR: return jsonToSetOperator(pJson, pObj); case QUERY_NODE_SELECT_STMT: diff --git a/source/libs/nodes/src/nodesUtilFuncs.c b/source/libs/nodes/src/nodesUtilFuncs.c index 476b3b278678a906e0b1c71240ff809b4f4d394e..3f7003dfa3b5a911e35c8823d1d883d9cca5bea7 100644 --- a/source/libs/nodes/src/nodesUtilFuncs.c +++ b/source/libs/nodes/src/nodesUtilFuncs.c @@ -79,6 +79,8 @@ int32_t nodesNodeSize(ENodeType type) { return sizeof(SStreamOptions); case QUERY_NODE_TOPIC_OPTIONS: return sizeof(STopicOptions); + case QUERY_NODE_LEFT_VALUE: + return sizeof(SLeftValueNode); case QUERY_NODE_SET_OPERATOR: return sizeof(SSetOperator); case QUERY_NODE_SELECT_STMT: @@ -1117,6 +1119,7 @@ bool nodesIsComparisonOp(const SOperatorNode* pOp) { bool nodesIsJsonOp(const SOperatorNode* pOp) { switch (pOp->opType) { case OP_TYPE_JSON_GET_VALUE: + case OP_TYPE_JSON_CONTAINS: return true; default: break; @@ -1137,10 +1140,6 @@ bool nodesIsRegularOp(const SOperatorNode* pOp) { return false; } -bool nodesIsTimeorderQuery(const SNode* pQuery) { return false; } - -bool nodesIsTimelineQuery(const SNode* pQuery) { return false; } - typedef struct SCollectColumnsCxt { int32_t errCode; const char* pTableAlias; diff --git a/source/libs/parser/inc/sql.y b/source/libs/parser/inc/sql.y index 10bb47d3ffd9ed59b72aab3d528b19d909bf4e8c..2cba1eb043e0e4063b4a7519a3c116820412c69d 100644 --- a/source/libs/parser/inc/sql.y +++ b/source/libs/parser/inc/sql.y @@ -241,7 +241,7 @@ alter_table_clause(A) ::= alter_table_clause(A) ::= full_table_name(B) RENAME TAG column_name(C) column_name(D). { A = createAlterTableRenameCol(pCxt, B, TSDB_ALTER_TABLE_UPDATE_TAG_NAME, &C, &D); } alter_table_clause(A) ::= - full_table_name(B) SET TAG column_name(C) NK_EQ literal(D). { A = createAlterTableSetTag(pCxt, B, &C, releaseRawExprNode(pCxt, D)); } + full_table_name(B) SET TAG column_name(C) NK_EQ signed_literal(D). { A = createAlterTableSetTag(pCxt, B, &C, D); } %type multi_create_clause { SNodeList* } %destructor multi_create_clause { nodesDestroyList($$); } @@ -448,7 +448,7 @@ agg_func_opt(A) ::= AGGREGATE. %type bufsize_opt { int32_t } %destructor bufsize_opt { } bufsize_opt(A) ::= . { A = 0; } -bufsize_opt(A) ::= BUFSIZE NK_INTEGER(B). { A = strtol(B.z, NULL, 10); } +bufsize_opt(A) ::= BUFSIZE NK_INTEGER(B). { A = taosStr2Int32(B.z, NULL, 10); } /************************************************ create/drop stream **************************************************/ cmd ::= CREATE STREAM not_exists_opt(E) stream_name(A) diff --git a/source/libs/parser/src/parAstCreater.c b/source/libs/parser/src/parAstCreater.c index 80c4593d9bb683788953d0410f654b813d0c126a..f93f0218d4537218e3a3fdc07686995d9bb4935c 100644 --- a/source/libs/parser/src/parAstCreater.c +++ b/source/libs/parser/src/parAstCreater.c @@ -342,25 +342,19 @@ SNode* createLogicConditionNode(SAstCreateContext* pCxt, ELogicConditionType typ CHECK_OUT_OF_MEM(cond); cond->condType = type; cond->pParameterList = nodesMakeList(); - if ((QUERY_NODE_LOGIC_CONDITION == nodeType(pParam1) && type != ((SLogicConditionNode*)pParam1)->condType) || - (QUERY_NODE_LOGIC_CONDITION == nodeType(pParam2) && type != ((SLogicConditionNode*)pParam2)->condType)) { + if (QUERY_NODE_LOGIC_CONDITION == nodeType(pParam1) && type == ((SLogicConditionNode*)pParam1)->condType) { + nodesListAppendList(cond->pParameterList, ((SLogicConditionNode*)pParam1)->pParameterList); + ((SLogicConditionNode*)pParam1)->pParameterList = NULL; + nodesDestroyNode(pParam1); + } else { nodesListAppend(cond->pParameterList, pParam1); - nodesListAppend(cond->pParameterList, pParam2); + } + if (QUERY_NODE_LOGIC_CONDITION == nodeType(pParam2) && type == ((SLogicConditionNode*)pParam2)->condType) { + nodesListAppendList(cond->pParameterList, ((SLogicConditionNode*)pParam2)->pParameterList); + ((SLogicConditionNode*)pParam2)->pParameterList = NULL; + nodesDestroyNode(pParam2); } else { - if (QUERY_NODE_LOGIC_CONDITION == nodeType(pParam1)) { - nodesListAppendList(cond->pParameterList, ((SLogicConditionNode*)pParam1)->pParameterList); - ((SLogicConditionNode*)pParam1)->pParameterList = NULL; - nodesDestroyNode(pParam1); - } else { - nodesListAppend(cond->pParameterList, pParam1); - } - if (QUERY_NODE_LOGIC_CONDITION == nodeType(pParam2)) { - nodesListAppendList(cond->pParameterList, ((SLogicConditionNode*)pParam2)->pParameterList); - ((SLogicConditionNode*)pParam2)->pParameterList = NULL; - nodesDestroyNode(pParam2); - } else { - nodesListAppend(cond->pParameterList, pParam2); - } + nodesListAppend(cond->pParameterList, pParam2); } return (SNode*)cond; } diff --git a/source/libs/parser/src/parInsert.c b/source/libs/parser/src/parInsert.c index 34ae13580b96720fe580de45dc4374eb71aa41d9..239bd21abc53db744fb0d8def841b5d59b2eff11 100644 --- a/source/libs/parser/src/parInsert.c +++ b/source/libs/parser/src/parInsert.c @@ -41,6 +41,13 @@ sToken = tStrGetToken(pSql, &index, false); \ } while (0) +#define NEXT_VALID_TOKEN(pSql, sToken) \ + do { \ + sToken.n = tGetToken(pSql, &sToken.type); \ + sToken.z = pSql; \ + pSql += sToken.n; \ + } while (TK_NK_SPACE == sToken.type) + typedef struct SInsertParseContext { SParseContext* pComCxt; // input char* pSql; // input @@ -482,9 +489,11 @@ static int32_t parseValueToken(char** end, SToken* pToken, SSchema* pSchema, int return buildSyntaxErrMsg(pMsgBuf, "invalid bool data", pToken->z); } } else if (pToken->type == TK_NK_INTEGER) { - return func(pMsgBuf, ((taosStr2Int64(pToken->z, NULL, 10) == 0) ? &FALSE_VALUE : &TRUE_VALUE), pSchema->bytes, param); + return func(pMsgBuf, ((taosStr2Int64(pToken->z, NULL, 10) == 0) ? &FALSE_VALUE : &TRUE_VALUE), pSchema->bytes, + param); } else if (pToken->type == TK_NK_FLOAT) { - return func(pMsgBuf, ((taosStr2Double(pToken->z, NULL) == 0) ? &FALSE_VALUE : &TRUE_VALUE), pSchema->bytes, param); + return func(pMsgBuf, ((taosStr2Double(pToken->z, NULL) == 0) ? &FALSE_VALUE : &TRUE_VALUE), pSchema->bytes, + param); } else { return buildSyntaxErrMsg(pMsgBuf, "invalid bool data", pToken->z); } @@ -638,7 +647,7 @@ static FORCE_INLINE int32_t MemRowAppend(SMsgBuf* pMsgBuf, const void* value, in if (TSDB_DATA_TYPE_BINARY == pa->schema->type) { const char* rowEnd = tdRowEnd(rb->pBuf); STR_WITH_SIZE_TO_VARSTR(rowEnd, value, len); - tdAppendColValToRow(rb, pa->schema->colId, pa->schema->type, TD_VTYPE_NORM, rowEnd, true, pa->toffset, pa->colIdx); + tdAppendColValToRow(rb, pa->schema->colId, pa->schema->type, TD_VTYPE_NORM, rowEnd, false, pa->toffset, pa->colIdx); } else if (TSDB_DATA_TYPE_NCHAR == pa->schema->type) { // if the converted output len is over than pColumnModel->bytes, return error: 'Argument list too long' int32_t output = 0; @@ -685,7 +694,7 @@ static int32_t parseBoundColumns(SInsertParseContext* pCxt, SParsedDataColInfo* isOrdered = false; } if (index < 0) { - return buildSyntaxErrMsg(&pCxt->msg, "invalid column/tag name", sToken.z); + return generateSyntaxErrMsg(&pCxt->msg, TSDB_CODE_PAR_INVALID_COLUMN, sToken.z); } if (pColList->cols[index].valStat == VAL_STAT_HAS) { return buildSyntaxErrMsg(&pCxt->msg, "duplicated column name", sToken.z); @@ -818,7 +827,7 @@ static int32_t parseTagsClause(SInsertParseContext* pCxt, SSchema* pSchema, uint SKVRow row = tdGetKVRowFromBuilder(&pCxt->tagsBuilder); if (NULL == row) { - return buildInvalidOperationMsg(&pCxt->msg, "tag value expected"); + return buildInvalidOperationMsg(&pCxt->msg, "out of memory"); } tdSortKVRowByColIdx(row); @@ -895,8 +904,10 @@ static int32_t parseUsingClause(SInsertParseContext* pCxt, SName* name, char* tb return buildSyntaxErrMsg(&pCxt->msg, "( is expected", sToken.z); } CHECK_CODE(parseTagsClause(pCxt, pCxt->pTableMeta->schema, getTableInfo(pCxt->pTableMeta).precision, name->tname)); - NEXT_TOKEN(pCxt->pSql, sToken); - if (TK_NK_RP != sToken.type) { + NEXT_VALID_TOKEN(pCxt->pSql, sToken); + if (TK_NK_COMMA == sToken.type) { + return generateSyntaxErrMsg(&pCxt->msg, TSDB_CODE_PAR_TAGS_NOT_MATCHED); + } else if (TK_NK_RP != sToken.type) { return buildSyntaxErrMsg(&pCxt->msg, ") is expected", sToken.z); } @@ -996,8 +1007,10 @@ static int32_t parseValues(SInsertParseContext* pCxt, STableDataBlocks* pDataBlo pDataBlock->size += extendedRowSize; // len; } - NEXT_TOKEN(pCxt->pSql, sToken); - if (TK_NK_RP != sToken.type) { + NEXT_VALID_TOKEN(pCxt->pSql, sToken); + if (TK_NK_COMMA == sToken.type) { + return generateSyntaxErrMsg(&pCxt->msg, TSDB_CODE_PAR_INVALID_COLUMNS_NUM); + } else if (TK_NK_RP != sToken.type) { return buildSyntaxErrMsg(&pCxt->msg, ") expected", sToken.z); } @@ -1057,10 +1070,10 @@ static void destroyInsertParseContext(SInsertParseContext* pCxt) { // VALUES (field1_value, ...) [(field1_value2, ...) ...] | FILE csv_file_path // [...]; static int32_t parseInsertBody(SInsertParseContext* pCxt) { - int32_t tbNum = 0; - char tbFName[TSDB_TABLE_FNAME_LEN]; - bool autoCreateTbl = false; - STableMeta *pMeta = NULL; + int32_t tbNum = 0; + char tbFName[TSDB_TABLE_FNAME_LEN]; + bool autoCreateTbl = false; + STableMeta* pMeta = NULL; // for each table while (1) { @@ -1072,6 +1085,10 @@ static int32_t parseInsertBody(SInsertParseContext* pCxt) { // no data in the sql string anymore. if (sToken.n == 0) { + if (sToken.type && pCxt->pSql[0]) { + return buildSyntaxErrMsg(&pCxt->msg, "invalid charactor in SQL", sToken.z); + } + if (0 == pCxt->totalNum && (!TSDB_QUERY_HAS_TYPE(pCxt->pOutput->insertType, TSDB_QUERY_TYPE_STMT_INSERT))) { return buildInvalidOperationMsg(&pCxt->msg, "no data in sql"); } @@ -1110,7 +1127,9 @@ static int32_t parseInsertBody(SInsertParseContext* pCxt) { NEXT_TOKEN(pCxt->pSql, sToken); autoCreateTbl = true; } else { - CHECK_CODE(getTableMeta(pCxt, &name, tbFName)); + char dbFName[TSDB_DB_FNAME_LEN]; + tNameGetFullDbName(&name, dbFName); + CHECK_CODE(getTableMeta(pCxt, &name, dbFName)); } STableDataBlocks* dataBuf = NULL; @@ -1119,7 +1138,7 @@ static int32_t parseInsertBody(SInsertParseContext* pCxt) { &dataBuf, NULL, &pCxt->createTblReq)); pMeta = pCxt->pTableMeta; pCxt->pTableMeta = NULL; - + if (TK_NK_LP == sToken.type) { // pSql -> field1_name, ...) CHECK_CODE(parseBoundColumns(pCxt, &dataBuf->boundColumnInfo, getTableColumnSchema(pMeta))); @@ -1158,7 +1177,8 @@ static int32_t parseInsertBody(SInsertParseContext* pCxt) { return TSDB_CODE_TSC_OUT_OF_MEMORY; } memcpy(tags, &pCxt->tags, sizeof(pCxt->tags)); - (*pCxt->pStmtCb->setInfoFn)(pCxt->pStmtCb->pStmt, pMeta, tags, tbFName, autoCreateTbl, pCxt->pVgroupsHashObj, pCxt->pTableBlockHashObj); + (*pCxt->pStmtCb->setInfoFn)(pCxt->pStmtCb->pStmt, pMeta, tags, tbFName, autoCreateTbl, pCxt->pVgroupsHashObj, + pCxt->pTableBlockHashObj); memset(&pCxt->tags, 0, sizeof(pCxt->tags)); pCxt->pVgroupsHashObj = NULL; @@ -1229,14 +1249,14 @@ int32_t parseInsertSql(SParseContext* pContext, SQuery** pQuery) { return TSDB_CODE_OUT_OF_MEMORY; } } - + context.pOutput->payloadType = PAYLOAD_TYPE_KV; int32_t code = skipInsertInto(&context); if (TSDB_CODE_SUCCESS == code) { code = parseInsertBody(&context); } - if (TSDB_CODE_SUCCESS == code) { + if (TSDB_CODE_SUCCESS == code || NEED_CLIENT_HANDLE_ERROR(code)) { SName* pTable = taosHashIterate(context.pTableNameHashObj, NULL); while (NULL != pTable) { taosArrayPush((*pQuery)->pTableList, pTable); @@ -1331,7 +1351,7 @@ int32_t qBindStmtTagsValue(void* pBlock, void* boundTags, int64_t suid, char* tN SKVRow row = tdGetKVRowFromBuilder(&tagBuilder); if (NULL == row) { tdDestroyKVRowBuilder(&tagBuilder); - return buildInvalidOperationMsg(&pBuf, "tag value expected"); + return buildInvalidOperationMsg(&pBuf, "out of memory"); } tdSortKVRowByColIdx(row); @@ -1577,9 +1597,9 @@ typedef struct SmlExecTableHandle { } SmlExecTableHandle; typedef struct SmlExecHandle { - SHashObj* pBlockHash; - SmlExecTableHandle tableExecHandle; - SQuery *pQuery; + SHashObj* pBlockHash; + SmlExecTableHandle tableExecHandle; + SQuery* pQuery; } SSmlExecHandle; static void smlDestroyTableHandle(void* pHandle) { @@ -1671,28 +1691,28 @@ static int32_t smlBuildTagRow(SArray* cols, SKVRowBuilder* tagsBuilder, SParsedD SSchema* pTagSchema = &pSchema[tags->boundColumns[i] - 1]; // colId starts with 1 param.schema = pTagSchema; SSmlKv* kv = taosArrayGetP(cols, i); - if(IS_VAR_DATA_TYPE(kv->type)){ + if (IS_VAR_DATA_TYPE(kv->type)) { KvRowAppend(msg, kv->value, kv->length, ¶m); - }else{ + } else { KvRowAppend(msg, &(kv->value), kv->length, ¶m); } } *row = tdGetKVRowFromBuilder(tagsBuilder); if (*row == NULL) { - return TSDB_CODE_SML_INVALID_DATA; + return TSDB_CODE_OUT_OF_MEMORY; } tdSortKVRowByColIdx(*row); return TSDB_CODE_SUCCESS; } -int32_t smlBindData(void *handle, SArray *tags, SArray *colsSchema, SArray *cols, bool format, - STableMeta *pTableMeta, char *tableName, char *msgBuf, int16_t msgBufLen) { +int32_t smlBindData(void* handle, SArray* tags, SArray* colsSchema, SArray* cols, bool format, STableMeta* pTableMeta, + char* tableName, char* msgBuf, int16_t msgBufLen) { SMsgBuf pBuf = {.buf = msgBuf, .len = msgBufLen}; SSmlExecHandle* smlHandle = (SSmlExecHandle*)handle; - smlDestroyTableHandle(&smlHandle->tableExecHandle); // free for each table - SSchema* pTagsSchema = getTableTagSchema(pTableMeta); + smlDestroyTableHandle(&smlHandle->tableExecHandle); // free for each table + SSchema* pTagsSchema = getTableTagSchema(pTableMeta); setBoundColumnInfo(&smlHandle->tableExecHandle.tags, pTagsSchema, getNumOfTags(pTableMeta)); int ret = smlBoundColumnData(tags, &smlHandle->tableExecHandle.tags, pTagsSchema); if (ret != TSDB_CODE_SUCCESS) { @@ -1700,7 +1720,8 @@ int32_t smlBindData(void *handle, SArray *tags, SArray *colsSchema, SArray *cols return ret; } SKVRow row = NULL; - ret = smlBuildTagRow(tags, &smlHandle->tableExecHandle.tagsBuilder, &smlHandle->tableExecHandle.tags, pTagsSchema, &row, &pBuf); + ret = smlBuildTagRow(tags, &smlHandle->tableExecHandle.tagsBuilder, &smlHandle->tableExecHandle.tags, pTagsSchema, + &row, &pBuf); if (ret != TSDB_CODE_SUCCESS) { return ret; } @@ -1731,7 +1752,7 @@ int32_t smlBindData(void *handle, SArray *tags, SArray *colsSchema, SArray *cols initRowBuilder(&pDataBlock->rowBuilder, pDataBlock->pTableMeta->sversion, &pDataBlock->boundColumnInfo); int32_t rowNum = taosArrayGetSize(cols); - if(rowNum <= 0) { + if (rowNum <= 0) { return buildInvalidOperationMsg(&pBuf, "cols size <= 0"); } ret = allocateMemForSize(pDataBlock, extendedRowSize * rowNum); @@ -1742,9 +1763,9 @@ int32_t smlBindData(void *handle, SArray *tags, SArray *colsSchema, SArray *cols for (int32_t r = 0; r < rowNum; ++r) { STSRow* row = (STSRow*)(pDataBlock->pData + pDataBlock->size); // skip the SSubmitBlk header tdSRowResetBuf(pBuilder, row); - void *rowData = taosArrayGetP(cols, r); + void* rowData = taosArrayGetP(cols, r); size_t rowDataSize = 0; - if(format){ + if (format) { rowDataSize = taosArrayGetSize(rowData); } @@ -1779,9 +1800,9 @@ int32_t smlBindData(void *handle, SArray *tags, SArray *colsSchema, SArray *cols kv->i = convertTimePrecision(kv->i, TSDB_TIME_PRECISION_NANO, pTableMeta->tableInfo.precision); } - if(IS_VAR_DATA_TYPE(kv->type)){ + if (IS_VAR_DATA_TYPE(kv->type)) { MemRowAppend(&pBuf, kv->value, colLen, ¶m); - }else{ + } else { MemRowAppend(&pBuf, &(kv->value), colLen, ¶m); } } diff --git a/source/libs/parser/src/parTokenizer.c b/source/libs/parser/src/parTokenizer.c index abc7ddb17f4b6ea4cd9a859637fe3886dffa21fc..8fb9780f8a5b52c62822c25eb1b52be40d30c1d9 100644 --- a/source/libs/parser/src/parTokenizer.c +++ b/source/libs/parser/src/parTokenizer.c @@ -704,6 +704,7 @@ SToken tStrGetToken(const char* str, int32_t* i, bool isPrevOptr) { if (t0.type == TK_NK_SEMI) { t0.n = 0; + t0.type = 0; return t0; } diff --git a/source/libs/parser/src/parTranslater.c b/source/libs/parser/src/parTranslater.c index 074a5bc30cd298deb40433eda34868225106472e..e57fc3556455d731dc937e4dcdb9cdabe12c350b 100644 --- a/source/libs/parser/src/parTranslater.c +++ b/source/libs/parser/src/parTranslater.c @@ -53,6 +53,8 @@ static bool afterGroupBy(ESqlClause clause) { return clause > SQL_CLAUSE_GROUP_B static bool beforeHaving(ESqlClause clause) { return clause < SQL_CLAUSE_HAVING; } +static bool afterHaving(ESqlClause clause) { return clause > SQL_CLAUSE_HAVING; } + static int32_t addNamespace(STranslateContext* pCxt, void* pTable) { size_t currTotalLevel = taosArrayGetSize(pCxt->pNsLevel); if (currTotalLevel > pCxt->currLevel) { @@ -118,6 +120,20 @@ static int32_t getTableMeta(STranslateContext* pCxt, const char* pDbName, const return getTableMetaImpl(pCxt, toName(pCxt->pParseCxt->acctId, pDbName, pTableName, &name), pMeta); } +static int32_t refreshGetTableMeta(STranslateContext* pCxt, const char* pDbName, const char* pTableName, + STableMeta** pMeta) { + SParseContext* pParCxt = pCxt->pParseCxt; + SName name; + toName(pCxt->pParseCxt->acctId, pDbName, pTableName, &name); + int32_t code = + catalogRefreshGetTableMeta(pParCxt->pCatalog, pParCxt->pTransporter, &pParCxt->mgmtEpSet, &name, pMeta, false); + if (TSDB_CODE_SUCCESS != code) { + parserError("catalogRefreshGetTableMeta error, code:%s, dbName:%s, tbName:%s", tstrerror(code), pDbName, + pTableName); + } + return code; +} + static int32_t getTableDistVgInfo(STranslateContext* pCxt, const SName* pName, SArray** pVgInfo) { SParseContext* pParCxt = pCxt->pParseCxt; int32_t code = collectUseDatabase(pName, pCxt->pDbs); @@ -276,6 +292,10 @@ static bool isScanPseudoColumnFunc(const SNode* pNode) { return (QUERY_NODE_FUNCTION == nodeType(pNode) && fmIsScanPseudoColumnFunc(((SFunctionNode*)pNode)->funcId)); } +static bool isIndefiniteRowsFunc(const SNode* pNode) { + return (QUERY_NODE_FUNCTION == nodeType(pNode) && fmIsIndefiniteRowsFunc(((SFunctionNode*)pNode)->funcId)); +} + static bool isDistinctOrderBy(STranslateContext* pCxt) { return (SQL_CLAUSE_ORDER_BY == pCxt->currClause && pCxt->pCurrStmt->isDistinct); } @@ -376,6 +396,35 @@ static bool isInternalPrimaryKey(const SColumnNode* pCol) { return PRIMARYKEY_TIMESTAMP_COL_ID == pCol->colId && 0 == strcmp(pCol->colName, PK_TS_COL_INTERNAL_NAME); } +static bool isTimeOrderQuery(SNode* pStmt) { + if (QUERY_NODE_SELECT_STMT == nodeType(pStmt)) { + return ((SSelectStmt*)pStmt)->isTimeOrderQuery; + } else { + return false; + } +} + +static bool isPrimaryKeyImpl(STempTableNode* pTable, SNode* pExpr) { + if (QUERY_NODE_COLUMN == nodeType(pExpr)) { + return (PRIMARYKEY_TIMESTAMP_COL_ID == ((SColumnNode*)pExpr)->colId); + } else if (QUERY_NODE_FUNCTION == nodeType(pExpr)) { + SFunctionNode* pFunc = (SFunctionNode*)pExpr; + if (FUNCTION_TYPE_SELECT_VALUE == pFunc->funcType) { + return isPrimaryKeyImpl(pTable, nodesListGetNode(pFunc->pParameterList, 0)); + } else if (FUNCTION_TYPE_WSTARTTS == pFunc->funcType || FUNCTION_TYPE_WENDTS == pFunc->funcType) { + return true; + } + } + return false; +} + +static bool isPrimaryKey(STempTableNode* pTable, SNode* pExpr) { + if (!isTimeOrderQuery(pTable->pSubquery)) { + return false; + } + return isPrimaryKeyImpl(pTable, pExpr); +} + static bool findAndSetColumn(SColumnNode* pCol, const STableNode* pTable) { bool found = false; if (QUERY_NODE_REAL_TABLE == nodeType(pTable)) { @@ -398,8 +447,7 @@ static bool findAndSetColumn(SColumnNode* pCol, const STableNode* pTable) { FOREACH(pNode, pProjectList) { SExprNode* pExpr = (SExprNode*)pNode; if (0 == strcmp(pCol->colName, pExpr->aliasName) || - ((QUERY_NODE_COLUMN == nodeType(pExpr) && PRIMARYKEY_TIMESTAMP_COL_ID == ((SColumnNode*)pExpr)->colId) && - isInternalPrimaryKey(pCol))) { + (isPrimaryKey((STempTableNode*)pTable, pNode) && isInternalPrimaryKey(pCol))) { setColumnInfoByExpr(pTable, pExpr, pCol); found = true; break; @@ -433,6 +481,7 @@ static EDealRes translateColumnWithoutPrefix(STranslateContext* pCxt, SColumnNod SArray* pTables = taosArrayGetP(pCxt->pNsLevel, pCxt->currLevel); size_t nums = taosArrayGetSize(pTables); bool found = false; + bool isInternalPk = isInternalPrimaryKey(pCol); for (size_t i = 0; i < nums; ++i) { STableNode* pTable = taosArrayGetP(pTables, i); if (findAndSetColumn(pCol, pTable)) { @@ -440,10 +489,16 @@ static EDealRes translateColumnWithoutPrefix(STranslateContext* pCxt, SColumnNod return generateDealNodeErrMsg(pCxt, TSDB_CODE_PAR_AMBIGUOUS_COLUMN, pCol->colName); } found = true; + if (isInternalPk) { + break; + } } } if (!found) { - if (isInternalPrimaryKey(pCol)) { + if (isInternalPk) { + if (NULL != pCxt->pCurrStmt->pWindow) { + return generateDealNodeErrMsg(pCxt, TSDB_CODE_PAR_NOT_ALLOWED_WIN_QUERY); + } return generateDealNodeErrMsg(pCxt, TSDB_CODE_PAR_INVALID_INTERNAL_PK); } else { return generateDealNodeErrMsg(pCxt, TSDB_CODE_PAR_INVALID_COLUMN, pCol->colName); @@ -591,12 +646,13 @@ static EDealRes translateValueImpl(STranslateContext* pCxt, SValueNode* pVal, SD } case TSDB_DATA_TYPE_VARCHAR: case TSDB_DATA_TYPE_VARBINARY: { - pVal->datum.p = taosMemoryCalloc(1, targetDt.bytes + VARSTR_HEADER_SIZE + 1); + pVal->datum.p = taosMemoryCalloc(1, targetDt.bytes + 1); if (NULL == pVal->datum.p) { return generateDealNodeErrMsg(pCxt, TSDB_CODE_OUT_OF_MEMORY); } - varDataSetLen(pVal->datum.p, targetDt.bytes); - strncpy(varDataVal(pVal->datum.p), pVal->literal, targetDt.bytes); + int32_t len = TMIN(targetDt.bytes - VARSTR_HEADER_SIZE, pVal->node.resType.bytes); + varDataSetLen(pVal->datum.p, len); + strncpy(varDataVal(pVal->datum.p), pVal->literal, len); break; } case TSDB_DATA_TYPE_TIMESTAMP: { @@ -607,22 +663,20 @@ static EDealRes translateValueImpl(STranslateContext* pCxt, SValueNode* pVal, SD break; } case TSDB_DATA_TYPE_NCHAR: { - int32_t bytes = targetDt.bytes * TSDB_NCHAR_SIZE; - pVal->datum.p = taosMemoryCalloc(1, bytes + VARSTR_HEADER_SIZE + 1); + pVal->datum.p = taosMemoryCalloc(1, targetDt.bytes + 1); if (NULL == pVal->datum.p) { return generateDealNodeErrMsg(pCxt, TSDB_CODE_OUT_OF_MEMORY); ; } - int32_t output = 0; - if (!taosMbsToUcs4(pVal->literal, pVal->node.resType.bytes, (TdUcs4*)varDataVal(pVal->datum.p), bytes, - &output)) { + int32_t len = 0; + if (!taosMbsToUcs4(pVal->literal, pVal->node.resType.bytes, (TdUcs4*)varDataVal(pVal->datum.p), + targetDt.bytes - VARSTR_HEADER_SIZE, &len)) { return generateDealNodeErrMsg(pCxt, TSDB_CODE_PAR_WRONG_VALUE_TYPE, pVal->literal); } - varDataSetLen(pVal->datum.p, output); + varDataSetLen(pVal->datum.p, len); break; } - case TSDB_DATA_TYPE_JSON: case TSDB_DATA_TYPE_DECIMAL: case TSDB_DATA_TYPE_BLOB: return generateDealNodeErrMsg(pCxt, TSDB_CODE_PAR_WRONG_VALUE_TYPE, pVal->literal); @@ -635,78 +689,136 @@ static EDealRes translateValueImpl(STranslateContext* pCxt, SValueNode* pVal, SD return DEAL_RES_CONTINUE; } +static int32_t calcTypeBytes(SDataType dt) { + if (TSDB_DATA_TYPE_BINARY == dt.type) { + return dt.bytes + VARSTR_HEADER_SIZE; + } else if (TSDB_DATA_TYPE_NCHAR == dt.type) { + return dt.bytes * TSDB_NCHAR_SIZE + VARSTR_HEADER_SIZE; + } else { + return dt.bytes; + } +} + static EDealRes translateValue(STranslateContext* pCxt, SValueNode* pVal) { - return translateValueImpl(pCxt, pVal, pVal->node.resType); + SDataType dt = pVal->node.resType; + dt.bytes = calcTypeBytes(dt); + return translateValueImpl(pCxt, pVal, dt); } -static EDealRes translateOperator(STranslateContext* pCxt, SOperatorNode* pOp) { - if (nodesIsUnaryOp(pOp)) { - if (OP_TYPE_MINUS == pOp->opType) { - if (!IS_MATHABLE_TYPE(((SExprNode*)(pOp->pLeft))->resType.type)) { - return generateDealNodeErrMsg(pCxt, TSDB_CODE_PAR_WRONG_VALUE_TYPE, ((SExprNode*)(pOp->pLeft))->aliasName); - } - pOp->node.resType.type = TSDB_DATA_TYPE_DOUBLE; - pOp->node.resType.bytes = tDataTypes[TSDB_DATA_TYPE_DOUBLE].bytes; - } else { - pOp->node.resType.type = TSDB_DATA_TYPE_BOOL; - pOp->node.resType.bytes = tDataTypes[TSDB_DATA_TYPE_BOOL].bytes; +static bool isMultiResFunc(SNode* pNode) { + if (NULL == pNode) { + return false; + } + if (QUERY_NODE_FUNCTION != nodeType(pNode) || !fmIsMultiResFunc(((SFunctionNode*)pNode)->funcId)) { + return false; + } + SNodeList* pParameterList = ((SFunctionNode*)pNode)->pParameterList; + if (LIST_LENGTH(pParameterList) > 1) { + return true; + } + SNode* pParam = nodesListGetNode(pParameterList, 0); + return (QUERY_NODE_COLUMN == nodeType(pParam) ? 0 == strcmp(((SColumnNode*)pParam)->colName, "*") : false); +} + +static EDealRes translateUnaryOperator(STranslateContext* pCxt, SOperatorNode* pOp) { + if (OP_TYPE_MINUS == pOp->opType) { + if (!IS_MATHABLE_TYPE(((SExprNode*)(pOp->pLeft))->resType.type)) { + return generateDealNodeErrMsg(pCxt, TSDB_CODE_PAR_WRONG_VALUE_TYPE, ((SExprNode*)(pOp->pLeft))->aliasName); } - return DEAL_RES_CONTINUE; + pOp->node.resType.type = TSDB_DATA_TYPE_DOUBLE; + pOp->node.resType.bytes = tDataTypes[TSDB_DATA_TYPE_DOUBLE].bytes; + } else { + pOp->node.resType.type = TSDB_DATA_TYPE_BOOL; + pOp->node.resType.bytes = tDataTypes[TSDB_DATA_TYPE_BOOL].bytes; } + return DEAL_RES_CONTINUE; +} + +static EDealRes translateArithmeticOperator(STranslateContext* pCxt, SOperatorNode* pOp) { SDataType ldt = ((SExprNode*)(pOp->pLeft))->resType; SDataType rdt = ((SExprNode*)(pOp->pRight))->resType; - if (nodesIsArithmeticOp(pOp)) { - if (TSDB_DATA_TYPE_JSON == ldt.type || TSDB_DATA_TYPE_BLOB == ldt.type || TSDB_DATA_TYPE_JSON == rdt.type || - TSDB_DATA_TYPE_BLOB == rdt.type) { - return generateDealNodeErrMsg(pCxt, TSDB_CODE_PAR_WRONG_VALUE_TYPE, ((SExprNode*)(pOp->pRight))->aliasName); + if (TSDB_DATA_TYPE_BLOB == ldt.type || TSDB_DATA_TYPE_BLOB == rdt.type) { + return generateDealNodeErrMsg(pCxt, TSDB_CODE_PAR_WRONG_VALUE_TYPE, ((SExprNode*)(pOp->pRight))->aliasName); + } + if ((TSDB_DATA_TYPE_TIMESTAMP == ldt.type && TSDB_DATA_TYPE_TIMESTAMP == rdt.type) || + (TSDB_DATA_TYPE_TIMESTAMP == ldt.type && (IS_VAR_DATA_TYPE(rdt.type) || IS_FLOAT_TYPE(rdt.type))) || + (TSDB_DATA_TYPE_TIMESTAMP == rdt.type && (IS_VAR_DATA_TYPE(ldt.type) || IS_FLOAT_TYPE(ldt.type)))) { + return generateDealNodeErrMsg(pCxt, TSDB_CODE_PAR_WRONG_VALUE_TYPE, ((SExprNode*)(pOp->pRight))->aliasName); + } + + if ((TSDB_DATA_TYPE_TIMESTAMP == ldt.type && IS_INTEGER_TYPE(rdt.type)) || + (TSDB_DATA_TYPE_TIMESTAMP == rdt.type && IS_INTEGER_TYPE(ldt.type)) || + (TSDB_DATA_TYPE_TIMESTAMP == ldt.type && TSDB_DATA_TYPE_BOOL == rdt.type) || + (TSDB_DATA_TYPE_TIMESTAMP == rdt.type && TSDB_DATA_TYPE_BOOL == ldt.type)) { + pOp->node.resType.type = TSDB_DATA_TYPE_TIMESTAMP; + pOp->node.resType.bytes = tDataTypes[TSDB_DATA_TYPE_TIMESTAMP].bytes; + } else { + pOp->node.resType.type = TSDB_DATA_TYPE_DOUBLE; + pOp->node.resType.bytes = tDataTypes[TSDB_DATA_TYPE_DOUBLE].bytes; + } + return DEAL_RES_CONTINUE; +} + +static EDealRes translateComparisonOperator(STranslateContext* pCxt, SOperatorNode* pOp) { + SDataType ldt = ((SExprNode*)(pOp->pLeft))->resType; + SDataType rdt = ((SExprNode*)(pOp->pRight))->resType; + if (TSDB_DATA_TYPE_BLOB == ldt.type || TSDB_DATA_TYPE_BLOB == rdt.type) { + return generateDealNodeErrMsg(pCxt, TSDB_CODE_PAR_WRONG_VALUE_TYPE, ((SExprNode*)(pOp->pRight))->aliasName); + } + if (OP_TYPE_IN == pOp->opType || OP_TYPE_NOT_IN == pOp->opType) { + ((SExprNode*)pOp->pRight)->resType = ((SExprNode*)pOp->pLeft)->resType; + } + if (nodesIsRegularOp(pOp)) { + if (!IS_VAR_DATA_TYPE(((SExprNode*)(pOp->pLeft))->resType.type)) { + return generateDealNodeErrMsg(pCxt, TSDB_CODE_PAR_WRONG_VALUE_TYPE, ((SExprNode*)(pOp->pLeft))->aliasName); } - if ((TSDB_DATA_TYPE_TIMESTAMP == ldt.type && TSDB_DATA_TYPE_TIMESTAMP == rdt.type) || - (TSDB_DATA_TYPE_TIMESTAMP == ldt.type && (IS_VAR_DATA_TYPE(rdt.type) || IS_FLOAT_TYPE(rdt.type))) || - (TSDB_DATA_TYPE_TIMESTAMP == rdt.type && (IS_VAR_DATA_TYPE(ldt.type) || IS_FLOAT_TYPE(ldt.type)))) { + if (QUERY_NODE_VALUE != nodeType(pOp->pRight) || !IS_STR_DATA_TYPE(((SExprNode*)(pOp->pRight))->resType.type)) { return generateDealNodeErrMsg(pCxt, TSDB_CODE_PAR_WRONG_VALUE_TYPE, ((SExprNode*)(pOp->pRight))->aliasName); } + } + pOp->node.resType.type = TSDB_DATA_TYPE_BOOL; + pOp->node.resType.bytes = tDataTypes[TSDB_DATA_TYPE_BOOL].bytes; + return DEAL_RES_CONTINUE; +} - if ((TSDB_DATA_TYPE_TIMESTAMP == ldt.type && IS_INTEGER_TYPE(rdt.type)) || - (TSDB_DATA_TYPE_TIMESTAMP == rdt.type && IS_INTEGER_TYPE(ldt.type)) || - (TSDB_DATA_TYPE_TIMESTAMP == ldt.type && TSDB_DATA_TYPE_BOOL == rdt.type) || - (TSDB_DATA_TYPE_TIMESTAMP == rdt.type && TSDB_DATA_TYPE_BOOL == ldt.type)) { - pOp->node.resType.type = TSDB_DATA_TYPE_TIMESTAMP; - pOp->node.resType.bytes = tDataTypes[TSDB_DATA_TYPE_TIMESTAMP].bytes; - } else { - pOp->node.resType.type = TSDB_DATA_TYPE_DOUBLE; - pOp->node.resType.bytes = tDataTypes[TSDB_DATA_TYPE_DOUBLE].bytes; - } +static EDealRes translateJsonOperator(STranslateContext* pCxt, SOperatorNode* pOp) { + SDataType ldt = ((SExprNode*)(pOp->pLeft))->resType; + SDataType rdt = ((SExprNode*)(pOp->pRight))->resType; + if (TSDB_DATA_TYPE_JSON != ldt.type || TSDB_DATA_TYPE_BINARY != rdt.type) { + return generateDealNodeErrMsg(pCxt, TSDB_CODE_PAR_WRONG_VALUE_TYPE, ((SExprNode*)(pOp->pRight))->aliasName); + } + pOp->node.resType.type = TSDB_DATA_TYPE_JSON; + pOp->node.resType.bytes = tDataTypes[TSDB_DATA_TYPE_JSON].bytes; + return DEAL_RES_CONTINUE; +} + +static EDealRes translateOperator(STranslateContext* pCxt, SOperatorNode* pOp) { + if (isMultiResFunc(pOp->pLeft)) { + return generateDealNodeErrMsg(pCxt, TSDB_CODE_PAR_WRONG_VALUE_TYPE, ((SExprNode*)(pOp->pLeft))->aliasName); + } + if (isMultiResFunc(pOp->pRight)) { + return generateDealNodeErrMsg(pCxt, TSDB_CODE_PAR_WRONG_VALUE_TYPE, ((SExprNode*)(pOp->pRight))->aliasName); + } + + if (nodesIsUnaryOp(pOp)) { + return translateUnaryOperator(pCxt, pOp); + } else if (nodesIsArithmeticOp(pOp)) { + return translateArithmeticOperator(pCxt, pOp); } else if (nodesIsComparisonOp(pOp)) { - if (TSDB_DATA_TYPE_BLOB == ldt.type || TSDB_DATA_TYPE_JSON == rdt.type || TSDB_DATA_TYPE_BLOB == rdt.type) { - return generateDealNodeErrMsg(pCxt, TSDB_CODE_PAR_WRONG_VALUE_TYPE, ((SExprNode*)(pOp->pRight))->aliasName); - } - if (OP_TYPE_IN == pOp->opType || OP_TYPE_NOT_IN == pOp->opType) { - ((SExprNode*)pOp->pRight)->resType = ((SExprNode*)pOp->pLeft)->resType; - } - if (nodesIsRegularOp(pOp)) { - if (!IS_STR_DATA_TYPE(((SExprNode*)(pOp->pLeft))->resType.type)) { - return generateDealNodeErrMsg(pCxt, TSDB_CODE_PAR_WRONG_VALUE_TYPE, ((SExprNode*)(pOp->pLeft))->aliasName); - } - if (QUERY_NODE_VALUE != nodeType(pOp->pRight) || !IS_STR_DATA_TYPE(((SExprNode*)(pOp->pRight))->resType.type)) { - return generateDealNodeErrMsg(pCxt, TSDB_CODE_PAR_WRONG_VALUE_TYPE, ((SExprNode*)(pOp->pRight))->aliasName); - } - } - pOp->node.resType.type = TSDB_DATA_TYPE_BOOL; - pOp->node.resType.bytes = tDataTypes[TSDB_DATA_TYPE_BOOL].bytes; + return translateComparisonOperator(pCxt, pOp); } else if (nodesIsJsonOp(pOp)) { - if (TSDB_DATA_TYPE_JSON != ldt.type || TSDB_DATA_TYPE_BINARY != rdt.type) { - return generateDealNodeErrMsg(pCxt, TSDB_CODE_PAR_WRONG_VALUE_TYPE, ((SExprNode*)(pOp->pRight))->aliasName); - } - pOp->node.resType.type = TSDB_DATA_TYPE_JSON; - pOp->node.resType.bytes = tDataTypes[TSDB_DATA_TYPE_JSON].bytes; + return translateJsonOperator(pCxt, pOp); } return DEAL_RES_CONTINUE; } -static EDealRes haveAggFunction(SNode* pNode, void* pContext) { +static EDealRes haveAggOrNonstdFunction(SNode* pNode, void* pContext) { if (isAggFunc(pNode)) { *((bool*)pContext) = true; return DEAL_RES_END; + } else if (isIndefiniteRowsFunc(pNode)) { + *((bool*)pContext) = true; + return DEAL_RES_END; } return DEAL_RES_CONTINUE; } @@ -743,25 +855,42 @@ static int32_t rewriteCountStar(STranslateContext* pCxt, SFunctionNode* pCount) return code; } -static EDealRes translateFunction(STranslateContext* pCxt, SFunctionNode* pFunc) { +static bool hasInvalidFuncNesting(SNodeList* pParameterList) { + bool hasInvalidFunc = false; + nodesWalkExprs(pParameterList, haveAggOrNonstdFunction, &hasInvalidFunc); + return hasInvalidFunc; +} + +static int32_t getFuncInfo(STranslateContext* pCxt, SFunctionNode* pFunc) { SFmGetFuncInfoParam param = {.pCtg = pCxt->pParseCxt->pCatalog, .pRpc = pCxt->pParseCxt->pTransporter, .pMgmtEps = &pCxt->pParseCxt->mgmtEpSet, .pErrBuf = pCxt->msgBuf.buf, .errBufLen = pCxt->msgBuf.len}; - pCxt->errCode = fmGetFuncInfo(¶m, pFunc); + return fmGetFuncInfo(¶m, pFunc); +} + +static EDealRes translateFunction(STranslateContext* pCxt, SFunctionNode* pFunc) { + SNode* pParam = NULL; + FOREACH(pParam, pFunc->pParameterList) { + if (isMultiResFunc(pParam)) { + return generateDealNodeErrMsg(pCxt, TSDB_CODE_PAR_WRONG_VALUE_TYPE, ((SExprNode*)pParam)->aliasName); + } + } + + pCxt->errCode = getFuncInfo(pCxt, pFunc); if (TSDB_CODE_SUCCESS == pCxt->errCode && fmIsAggFunc(pFunc->funcId)) { if (beforeHaving(pCxt->currClause)) { return generateDealNodeErrMsg(pCxt, TSDB_CODE_PAR_ILLEGAL_USE_AGG_FUNCTION); } - bool haveAggFunc = false; - nodesWalkExprs(pFunc->pParameterList, haveAggFunction, &haveAggFunc); - if (haveAggFunc) { + if (hasInvalidFuncNesting(pFunc->pParameterList)) { return generateDealNodeErrMsg(pCxt, TSDB_CODE_PAR_AGG_FUNC_NESTING); } + if (pCxt->pCurrStmt->hasIndefiniteRowsFunc) { + return generateDealNodeErrMsg(pCxt, TSDB_CODE_PAR_NOT_ALLOWED_FUNC); + } pCxt->pCurrStmt->hasAggFuncs = true; - pCxt->pCurrStmt->isTimeOrderQuery = false; if (isCountStar(pFunc)) { pCxt->errCode = rewriteCountStar(pCxt, pFunc); } @@ -784,6 +913,16 @@ static EDealRes translateFunction(STranslateContext* pCxt, SFunctionNode* pFunc) } } } + if (TSDB_CODE_SUCCESS == pCxt->errCode && fmIsIndefiniteRowsFunc(pFunc->funcId)) { + if (SQL_CLAUSE_SELECT != pCxt->currClause || pCxt->pCurrStmt->hasIndefiniteRowsFunc || + pCxt->pCurrStmt->hasAggFuncs) { + return generateDealNodeErrMsg(pCxt, TSDB_CODE_PAR_NOT_ALLOWED_FUNC); + } + if (hasInvalidFuncNesting(pFunc->pParameterList)) { + return generateDealNodeErrMsg(pCxt, TSDB_CODE_PAR_AGG_FUNC_NESTING); + } + pCxt->pCurrStmt->hasIndefiniteRowsFunc = true; + } return TSDB_CODE_SUCCESS == pCxt->errCode ? DEAL_RES_CONTINUE : DEAL_RES_ERROR; } @@ -853,9 +992,10 @@ typedef struct SCheckExprForGroupByCxt { STranslateContext* pTranslateCxt; int32_t selectFuncNum; bool hasSelectValFunc; + bool hasOtherAggFunc; } SCheckExprForGroupByCxt; -static EDealRes rewriteColToSelectValFunc(STranslateContext* pCxt, bool* pHasSelectValFunc, SNode** pNode) { +static EDealRes rewriteColToSelectValFunc(STranslateContext* pCxt, SNode** pNode) { SFunctionNode* pFunc = nodesMakeNode(QUERY_NODE_FUNCTION); if (NULL == pFunc) { pCxt->errCode = TSDB_CODE_OUT_OF_MEMORY; @@ -865,13 +1005,10 @@ static EDealRes rewriteColToSelectValFunc(STranslateContext* pCxt, bool* pHasSel strcpy(pFunc->node.aliasName, ((SExprNode*)*pNode)->aliasName); pCxt->errCode = nodesListMakeAppend(&pFunc->pParameterList, *pNode); if (TSDB_CODE_SUCCESS == pCxt->errCode) { - translateFunction(pCxt, pFunc); + pCxt->errCode == getFuncInfo(pCxt, pFunc); } if (TSDB_CODE_SUCCESS == pCxt->errCode) { *pNode = (SNode*)pFunc; - if (NULL != pHasSelectValFunc) { - *pHasSelectValFunc = true; - } } else { nodesDestroyNode(pFunc); } @@ -883,8 +1020,12 @@ static EDealRes doCheckExprForGroupBy(SNode** pNode, void* pContext) { if (!nodesIsExprNode(*pNode) || isAliasColumn(*pNode)) { return DEAL_RES_CONTINUE; } - pCxt->selectFuncNum += isSelectFunc(*pNode) ? 1 : 0; - if (pCxt->selectFuncNum > 1 && pCxt->hasSelectValFunc) { + if (isSelectFunc(*pNode)) { + ++(pCxt->selectFuncNum); + } else if (isAggFunc(*pNode)) { + pCxt->hasOtherAggFunc = true; + } + if ((pCxt->selectFuncNum > 1 && pCxt->hasSelectValFunc) || (pCxt->hasOtherAggFunc && pCxt->hasSelectValFunc)) { return generateDealNodeErrMsg(pCxt->pTranslateCxt, getGroupByErrorCode(pCxt->pTranslateCxt)); } if (isAggFunc(*pNode) && !isDistinctOrderBy(pCxt->pTranslateCxt)) { @@ -897,10 +1038,11 @@ static EDealRes doCheckExprForGroupBy(SNode** pNode, void* pContext) { } } if (isScanPseudoColumnFunc(*pNode) || QUERY_NODE_COLUMN == nodeType(*pNode)) { - if (pCxt->selectFuncNum > 1) { + if (pCxt->selectFuncNum > 1 || pCxt->hasOtherAggFunc) { return generateDealNodeErrMsg(pCxt->pTranslateCxt, getGroupByErrorCode(pCxt->pTranslateCxt)); } else { - return rewriteColToSelectValFunc(pCxt->pTranslateCxt, &pCxt->hasSelectValFunc, pNode); + pCxt->hasSelectValFunc = true; + return rewriteColToSelectValFunc(pCxt->pTranslateCxt, pNode); } } if (isAggFunc(*pNode) && isDistinctOrderBy(pCxt->pTranslateCxt)) { @@ -910,7 +1052,8 @@ static EDealRes doCheckExprForGroupBy(SNode** pNode, void* pContext) { } static int32_t checkExprForGroupBy(STranslateContext* pCxt, SNode** pNode) { - SCheckExprForGroupByCxt cxt = {.pTranslateCxt = pCxt, .selectFuncNum = 0, .hasSelectValFunc = false}; + SCheckExprForGroupByCxt cxt = { + .pTranslateCxt = pCxt, .selectFuncNum = 0, .hasSelectValFunc = false, .hasOtherAggFunc = false}; nodesRewriteExpr(pNode, doCheckExprForGroupBy, &cxt); if (cxt.selectFuncNum != 1 && cxt.hasSelectValFunc) { return generateSyntaxErrMsg(&pCxt->msgBuf, getGroupByErrorCode(pCxt)); @@ -922,7 +1065,8 @@ static int32_t checkExprListForGroupBy(STranslateContext* pCxt, SNodeList* pList if (NULL == getGroupByList(pCxt)) { return TSDB_CODE_SUCCESS; } - SCheckExprForGroupByCxt cxt = {.pTranslateCxt = pCxt, .selectFuncNum = 0, .hasSelectValFunc = false}; + SCheckExprForGroupByCxt cxt = { + .pTranslateCxt = pCxt, .selectFuncNum = 0, .hasSelectValFunc = false, .hasOtherAggFunc = false}; nodesRewriteExprs(pList, doCheckExprForGroupBy, &cxt); if (cxt.selectFuncNum != 1 && cxt.hasSelectValFunc) { return generateSyntaxErrMsg(&pCxt->msgBuf, getGroupByErrorCode(pCxt)); @@ -931,11 +1075,11 @@ static int32_t checkExprListForGroupBy(STranslateContext* pCxt, SNodeList* pList } static EDealRes rewriteColsToSelectValFuncImpl(SNode** pNode, void* pContext) { - if (isAggFunc(*pNode)) { + if (isAggFunc(*pNode) || isIndefiniteRowsFunc(*pNode)) { return DEAL_RES_IGNORE_CHILD; } if (isScanPseudoColumnFunc(*pNode) || QUERY_NODE_COLUMN == nodeType(*pNode)) { - return rewriteColToSelectValFunc((STranslateContext*)pContext, NULL, pNode); + return rewriteColToSelectValFunc((STranslateContext*)pContext, pNode); } return DEAL_RES_CONTINUE; } @@ -952,16 +1096,26 @@ typedef struct CheckAggColCoexistCxt { STranslateContext* pTranslateCxt; bool existAggFunc; bool existCol; + bool existNonstdFunc; int32_t selectFuncNum; + bool existOtherAggFunc; } CheckAggColCoexistCxt; static EDealRes doCheckAggColCoexist(SNode* pNode, void* pContext) { CheckAggColCoexistCxt* pCxt = (CheckAggColCoexistCxt*)pContext; - pCxt->selectFuncNum += isSelectFunc(pNode) ? 1 : 0; + if (isSelectFunc(pNode)) { + ++(pCxt->selectFuncNum); + } else if (isAggFunc(pNode)) { + pCxt->existOtherAggFunc = true; + } if (isAggFunc(pNode)) { pCxt->existAggFunc = true; return DEAL_RES_IGNORE_CHILD; } + if (isIndefiniteRowsFunc(pNode)) { + pCxt->existNonstdFunc = true; + return DEAL_RES_IGNORE_CHILD; + } if (isScanPseudoColumnFunc(pNode) || QUERY_NODE_COLUMN == nodeType(pNode)) { pCxt->existCol = true; } @@ -972,16 +1126,25 @@ static int32_t checkAggColCoexist(STranslateContext* pCxt, SSelectStmt* pSelect) if (NULL != pSelect->pGroupByList) { return TSDB_CODE_SUCCESS; } - CheckAggColCoexistCxt cxt = {.pTranslateCxt = pCxt, .existAggFunc = false, .existCol = false}; + CheckAggColCoexistCxt cxt = {.pTranslateCxt = pCxt, + .existAggFunc = false, + .existCol = false, + .existNonstdFunc = false, + .selectFuncNum = 0, + .existOtherAggFunc = false}; nodesWalkExprs(pSelect->pProjectionList, doCheckAggColCoexist, &cxt); if (!pSelect->isDistinct) { nodesWalkExprs(pSelect->pOrderByList, doCheckAggColCoexist, &cxt); } - if (1 == cxt.selectFuncNum) { + if (1 == cxt.selectFuncNum && !cxt.existOtherAggFunc) { return rewriteColsToSelectValFunc(pCxt, pSelect); - } else if ((cxt.selectFuncNum > 1 || cxt.existAggFunc || NULL != pSelect->pWindow) && cxt.existCol) { + } + if ((cxt.selectFuncNum > 1 || cxt.existAggFunc || NULL != pSelect->pWindow) && cxt.existCol) { return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_NOT_SINGLE_GROUP); } + if (cxt.existNonstdFunc && cxt.existCol) { + return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_NOT_ALLOWED_FUNC); + } return TSDB_CODE_SUCCESS; } @@ -1147,18 +1310,6 @@ static int32_t createAllColumns(STranslateContext* pCxt, SNodeList** pCols) { return TSDB_CODE_SUCCESS; } -static bool isMultiResFunc(SNode* pNode) { - if (QUERY_NODE_FUNCTION != nodeType(pNode) || !fmIsMultiResFunc(((SFunctionNode*)pNode)->funcId)) { - return false; - } - SNodeList* pParameterList = ((SFunctionNode*)pNode)->pParameterList; - if (LIST_LENGTH(pParameterList) > 1) { - return true; - } - SNode* pParam = nodesListGetNode(pParameterList, 0); - return (QUERY_NODE_COLUMN == nodeType(pParam) ? 0 == strcmp(((SColumnNode*)pParam)->colName, "*") : false); -} - static SNode* createMultiResFunc(SFunctionNode* pSrcFunc, SExprNode* pExpr) { SFunctionNode* pFunc = nodesMakeNode(QUERY_NODE_FUNCTION); if (NULL == pFunc) { @@ -1789,7 +1940,7 @@ static SNode* createSetOperProject(const char* pTableAlias, SNode* pNode) { } static bool dataTypeEqual(const SDataType* l, const SDataType* r) { - return (l->type == r->type && l->bytes == l->bytes && l->precision == r->precision && l->scale == l->scale); + return (l->type == r->type && l->bytes == r->bytes && l->precision == r->precision && l->scale == r->scale); } static int32_t createCastFunc(STranslateContext* pCxt, SNode* pExpr, SDataType dt, SNode** pCast) { @@ -1803,7 +1954,7 @@ static int32_t createCastFunc(STranslateContext* pCxt, SNode* pExpr, SDataType d nodesDestroyNode(pFunc); return TSDB_CODE_OUT_OF_MEMORY; } - if (DEAL_RES_ERROR == translateFunction(pCxt, pFunc)) { + if (TSDB_CODE_SUCCESS != getFuncInfo(pCxt, pFunc)) { nodesClearList(pFunc->pParameterList); pFunc->pParameterList = NULL; nodesDestroyNode(pFunc); @@ -2207,16 +2358,6 @@ static int32_t translateAlterDatabase(STranslateContext* pCxt, SAlterDatabaseStm return buildCmdMsg(pCxt, TDMT_MND_ALTER_DB, (FSerializeFunc)tSerializeSAlterDbReq, &alterReq); } -static int32_t calcTypeBytes(SDataType dt) { - if (TSDB_DATA_TYPE_BINARY == dt.type) { - return dt.bytes + VARSTR_HEADER_SIZE; - } else if (TSDB_DATA_TYPE_NCHAR == dt.type) { - return dt.bytes * TSDB_NCHAR_SIZE + VARSTR_HEADER_SIZE; - } else { - return dt.bytes; - } -} - static int32_t columnDefNodeToField(SNodeList* pList, SArray** pArray) { *pArray = taosArrayInit(LIST_LENGTH(pList), sizeof(SField)); SNode* pNode; @@ -2342,6 +2483,9 @@ static int32_t checkTableColsSchema(STranslateContext* pCxt, SHashObj* pHash, SN code = generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_FIRST_COLUMN); } } + if (TSDB_CODE_SUCCESS == code && pCol->dataType.type == TSDB_DATA_TYPE_JSON) { + code = generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_COL_JSON); + } int32_t len = strlen(pCol->colName); if (TSDB_CODE_SUCCESS == code && NULL != taosHashGet(pHash, pCol->colName, len)) { code = generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_DUPLICATED_COLUMN); @@ -2349,7 +2493,7 @@ static int32_t checkTableColsSchema(STranslateContext* pCxt, SHashObj* pHash, SN if (TSDB_CODE_SUCCESS == code) { if ((TSDB_DATA_TYPE_VARCHAR == pCol->dataType.type && calcTypeBytes(pCol->dataType) > TSDB_MAX_BINARY_LEN) || (TSDB_DATA_TYPE_NCHAR == pCol->dataType.type && calcTypeBytes(pCol->dataType) > TSDB_MAX_NCHAR_LEN)) { - code = code = generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_VAR_COLUMN_LEN); + code = generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_VAR_COLUMN_LEN); } } if (TSDB_CODE_SUCCESS == code) { @@ -2643,8 +2787,11 @@ static int32_t buildCreateStbReq(STranslateContext* pCxt, SCreateTableStmt* pStm SName tableName; tNameExtractFullName(toName(pCxt->pParseCxt->acctId, pStmt->dbName, pStmt->tableName, &tableName), pReq->name); - - return buildRollupAst(pCxt, pStmt, pReq); + int32_t code = collectUseTable(&tableName, pCxt->pTables); + if (TSDB_CODE_SUCCESS == code) { + code = buildRollupAst(pCxt, pStmt, pReq); + } + return code; } static int32_t translateCreateSuperTable(STranslateContext* pCxt, SCreateTableStmt* pStmt) { @@ -3132,7 +3279,7 @@ static int32_t translateExplain(STranslateContext* pCxt, SExplainStmt* pStmt) { } static int32_t translateDescribe(STranslateContext* pCxt, SDescribeStmt* pStmt) { - return getTableMeta(pCxt, pStmt->dbName, pStmt->tableName, &pStmt->pMeta); + return refreshGetTableMeta(pCxt, pStmt->dbName, pStmt->tableName, &pStmt->pMeta); } static int32_t translateKillConnection(STranslateContext* pCxt, SKillStmt* pStmt) { @@ -3943,19 +4090,15 @@ static int32_t addValToKVRow(STranslateContext* pCxt, SValueNode* pVal, const SS } static int32_t createValueFromFunction(STranslateContext* pCxt, SFunctionNode* pFunc, SValueNode** pVal) { - if (DEAL_RES_ERROR == translateFunction(pCxt, pFunc)) { - return pCxt->errCode; + int32_t code = getFuncInfo(pCxt, pFunc); + if (TSDB_CODE_SUCCESS == code) { + code = scalarCalculateConstants((SNode*)pFunc, (SNode**)pVal); } - return scalarCalculateConstants((SNode*)pFunc, (SNode**)pVal); + return code; } static SDataType schemaToDataType(SSchema* pSchema) { SDataType dt = {.type = pSchema->type, .bytes = pSchema->bytes, .precision = 0, .scale = 0}; - if (TSDB_DATA_TYPE_VARCHAR == dt.type || TSDB_DATA_TYPE_BINARY == dt.type || TSDB_DATA_TYPE_VARBINARY == dt.type) { - dt.bytes -= VARSTR_HEADER_SIZE; - } else if (TSDB_DATA_TYPE_NCHAR == dt.type) { - dt.bytes = (dt.bytes - VARSTR_HEADER_SIZE) / TSDB_NCHAR_SIZE; - } return dt; } @@ -4309,9 +4452,38 @@ static int32_t buildUpdateTagValReq(STranslateContext* pCxt, SAlterTableStmt* pS } pReq->isNull = (TSDB_DATA_TYPE_NULL == pStmt->pVal->node.resType.type); - pReq->nTagVal = pStmt->pVal->node.resType.bytes; - char* pVal = nodesGetValueFromNode(pStmt->pVal); - pReq->pTagVal = IS_VAR_DATA_TYPE(pStmt->pVal->node.resType.type) ? pVal + VARSTR_HEADER_SIZE : pVal; + if(pStmt->pVal->node.resType.type == TSDB_DATA_TYPE_JSON){ + SKVRowBuilder kvRowBuilder = {0}; + int32_t code = tdInitKVRowBuilder(&kvRowBuilder); + + if (TSDB_CODE_SUCCESS != code) { + return TSDB_CODE_OUT_OF_MEMORY; + } + if (pStmt->pVal->literal && strlen(pStmt->pVal->literal) > (TSDB_MAX_JSON_TAG_LEN - VARSTR_HEADER_SIZE) / TSDB_NCHAR_SIZE) { + return buildSyntaxErrMsg(&pCxt->msgBuf, "json string too long than 4095", pStmt->pVal->literal); + } + + code = parseJsontoTagData(pStmt->pVal->literal, &kvRowBuilder, &pCxt->msgBuf, pSchema->colId); + if (TSDB_CODE_SUCCESS != code) { + return code; + } + + SKVRow row = tdGetKVRowFromBuilder(&kvRowBuilder); + if (NULL == row) { + tdDestroyKVRowBuilder(&kvRowBuilder); + return TSDB_CODE_OUT_OF_MEMORY; + } + pReq->nTagVal = kvRowLen(row); + pReq->pTagVal = row; + pStmt->pVal->datum.p = row; // for free + tdDestroyKVRowBuilder(&kvRowBuilder); + }else{ + pReq->nTagVal = pStmt->pVal->node.resType.bytes; + if (TSDB_DATA_TYPE_NCHAR == pStmt->pVal->node.resType.type) { + pReq->nTagVal = pReq->nTagVal * TSDB_NCHAR_SIZE; + } + pReq->pTagVal = nodesGetValueFromNode(pStmt->pVal); + } return TSDB_CODE_SUCCESS; } @@ -4335,6 +4507,9 @@ static int32_t buildAddColReq(STranslateContext* pCxt, SAlterTableStmt* pStmt, S static int32_t buildDropColReq(STranslateContext* pCxt, SAlterTableStmt* pStmt, STableMeta* pTableMeta, SVAlterTbReq* pReq) { + if (2 == getNumOfColumns(pTableMeta)) { + return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_DROP_COL); + } SSchema* pSchema = getColSchema(pTableMeta, pStmt->colName); if (NULL == pSchema) { return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_COLUMN, pStmt->colName); @@ -4357,7 +4532,8 @@ static int32_t buildUpdateColReq(STranslateContext* pCxt, SAlterTableStmt* pStmt SSchema* pSchema = getColSchema(pTableMeta, pStmt->colName); if (NULL == pSchema) { return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_COLUMN, pStmt->colName); - } else if (!IS_VAR_DATA_TYPE(pSchema->type) || pSchema->bytes >= pReq->colModBytes) { + } else if (!IS_VAR_DATA_TYPE(pSchema->type) || pSchema->type != pStmt->dataType.type || + pSchema->bytes >= pReq->colModBytes) { return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_MODIFY_COL); } @@ -4504,7 +4680,26 @@ static int32_t rewriteAlterTable(STranslateContext* pCxt, SQuery* pQuery) { return code; } + if (pStmt->dataType.type == TSDB_DATA_TYPE_JSON && pStmt->alterType == TSDB_ALTER_TABLE_ADD_TAG) { + return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_ONLY_ONE_JSON_TAG); + } + + if (pStmt->dataType.type == TSDB_DATA_TYPE_JSON && pStmt->alterType == TSDB_ALTER_TABLE_ADD_COLUMN) { + return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_COL_JSON); + } + + if (getNumOfTags(pTableMeta) == 1 && pStmt->alterType == TSDB_ALTER_TABLE_DROP_TAG) { + return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_ALTER_TABLE, "can not drop tag if there is only one tag"); + } + if (TSDB_SUPER_TABLE == pTableMeta->tableType) { + SSchema* pTagsSchema = getTableTagSchema(pTableMeta); + if (getNumOfTags(pTableMeta) == 1 && pTagsSchema->type == TSDB_DATA_TYPE_JSON && + (pStmt->alterType == TSDB_ALTER_TABLE_ADD_TAG || + pStmt->alterType == TSDB_ALTER_TABLE_DROP_TAG || + pStmt->alterType == TSDB_ALTER_TABLE_UPDATE_TAG_BYTES)) { + return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_ONLY_ONE_JSON_TAG); + } return TSDB_CODE_SUCCESS; } else if (TSDB_CHILD_TABLE != pTableMeta->tableType && TSDB_NORMAL_TABLE != pTableMeta->tableType) { return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_ALTER_TABLE); diff --git a/source/libs/parser/src/parUtil.c b/source/libs/parser/src/parUtil.c index 7b9147beab4ebd83430fc569ea78d0effd575631..652ed10ce8b71c71c8eaa640dde145499fe69098 100644 --- a/source/libs/parser/src/parUtil.c +++ b/source/libs/parser/src/parUtil.c @@ -164,6 +164,15 @@ static char* getSyntaxErrFormat(int32_t errCode) { return "Invalid function name"; case TSDB_CODE_PAR_COMMENT_TOO_LONG: return "Comment too long"; + case TSDB_CODE_PAR_NOT_ALLOWED_FUNC: + return "Some functions are allowed only in the SELECT list of a query. " + "And, cannot be mixed with other non scalar functions or columns."; + case TSDB_CODE_PAR_NOT_ALLOWED_WIN_QUERY: + return "Window query not supported, since the result of subquery not include valid timestamp column"; + case TSDB_CODE_PAR_INVALID_DROP_COL: + return "No columns can be dropped"; + case TSDB_CODE_PAR_INVALID_COL_JSON: + return "Only tag can be json type"; case TSDB_CODE_OUT_OF_MEMORY: return "Out of memory"; default: @@ -321,7 +330,7 @@ int parseJsontoTagData(const char* json, SKVRowBuilder* kvRowBuilder, SMsgBuf* p // set json NULL data uint8_t jsonNULL = TSDB_DATA_TYPE_NULL; int jsonIndex = startColId + 1; - if (!json || strcasecmp(json, TSDB_DATA_NULL_STR_L) == 0) { + if (!json || strtrim((char*)json) == 0 ||strcasecmp(json, TSDB_DATA_NULL_STR_L) == 0) { tdAddColToKVRow(kvRowBuilder, jsonIndex, &jsonNULL, CHAR_BYTES); return TSDB_CODE_SUCCESS; } @@ -353,17 +362,17 @@ int parseJsontoTagData(const char* json, SKVRowBuilder* kvRowBuilder, SMsgBuf* p retCode = buildSyntaxErrMsg(pMsgBuf, "json key not validate", jsonKey); goto end; } - // if(strlen(jsonKey) > TSDB_MAX_JSON_KEY_LEN){ - // tscError("json key too long error"); - // retCode = tscSQLSyntaxErrMsg(errMsg, "json key too long, more than 256", NULL); - // goto end; - // } size_t keyLen = strlen(jsonKey); + if(keyLen > TSDB_MAX_JSON_KEY_LEN){ + qError("json key too long error"); + retCode = buildSyntaxErrMsg(pMsgBuf, "json key too long, more than 256", jsonKey); + goto end; + } if (keyLen == 0 || taosHashGet(keyHash, jsonKey, keyLen) != NULL) { continue; } - // key: keyLen + VARSTR_HEADER_SIZE, value type: CHAR_BYTES, value reserved: LONG_BYTES - tagKV = taosMemoryCalloc(keyLen + VARSTR_HEADER_SIZE + CHAR_BYTES + LONG_BYTES, 1); + // key: keyLen + VARSTR_HEADER_SIZE, value type: CHAR_BYTES, value reserved: DOUBLE_BYTES + tagKV = taosMemoryCalloc(keyLen + VARSTR_HEADER_SIZE + CHAR_BYTES + DOUBLE_BYTES, 1); if (!tagKV) { retCode = TSDB_CODE_TSC_OUT_OF_MEMORY; goto end; @@ -408,13 +417,9 @@ int parseJsontoTagData(const char* json, SKVRowBuilder* kvRowBuilder, SMsgBuf* p } char* valueType = POINTER_SHIFT(tagKV, keyLen + VARSTR_HEADER_SIZE); char* valueData = POINTER_SHIFT(tagKV, keyLen + VARSTR_HEADER_SIZE + CHAR_BYTES); - *valueType = - (item->valuedouble - (int64_t)(item->valuedouble) == 0) ? TSDB_DATA_TYPE_BIGINT : TSDB_DATA_TYPE_DOUBLE; - if (*valueType == TSDB_DATA_TYPE_DOUBLE) - *((double*)valueData) = item->valuedouble; - else if (*valueType == TSDB_DATA_TYPE_BIGINT) - *((int64_t*)valueData) = item->valueint; - tdAddColToKVRow(kvRowBuilder, jsonIndex++, tagKV, keyLen + VARSTR_HEADER_SIZE + CHAR_BYTES + LONG_BYTES); + *valueType = TSDB_DATA_TYPE_DOUBLE; + *((double*)valueData) = item->valuedouble; + tdAddColToKVRow(kvRowBuilder, jsonIndex++, tagKV, keyLen + VARSTR_HEADER_SIZE + CHAR_BYTES + DOUBLE_BYTES); } else if (item->type == cJSON_True || item->type == cJSON_False) { char* valueType = POINTER_SHIFT(tagKV, keyLen + VARSTR_HEADER_SIZE); char* valueData = POINTER_SHIFT(tagKV, keyLen + VARSTR_HEADER_SIZE + CHAR_BYTES); diff --git a/source/libs/parser/src/sql.c b/source/libs/parser/src/sql.c index 2d844e18429fbaaed9e6a61cf3234c7dc5da26a3..0854bb83e471151e83efd066192ee576561b28ee 100644 --- a/source/libs/parser/src/sql.c +++ b/source/libs/parser/src/sql.c @@ -211,540 +211,538 @@ typedef union { ** yy_default[] Default action for each state. ** *********** Begin parsing tables **********************************************/ -#define YY_ACTTAB_COUNT (2167) +#define YY_ACTTAB_COUNT (2154) static const YYACTIONTYPE yy_action[] = { - /* 0 */ 383, 76, 384, 1380, 391, 519, 384, 1380, 1631, 28, - /* 10 */ 223, 1467, 35, 33, 113, 1463, 343, 347, 1646, 1777, - /* 20 */ 301, 1470, 1159, 1627, 1635, 1633, 36, 34, 32, 31, - /* 30 */ 30, 1729, 1776, 1478, 94, 522, 1774, 93, 92, 91, - /* 40 */ 90, 89, 88, 87, 86, 85, 1662, 1157, 1523, 292, - /* 50 */ 32, 31, 30, 503, 291, 1726, 1777, 479, 14, 1521, - /* 60 */ 35, 33, 1285, 502, 1165, 518, 1631, 1617, 301, 145, - /* 70 */ 1159, 349, 274, 1774, 398, 36, 34, 32, 31, 30, - /* 80 */ 1, 1627, 1634, 1633, 1675, 112, 24, 132, 1647, 505, - /* 90 */ 1649, 1650, 501, 522, 522, 1157, 36, 34, 32, 31, - /* 100 */ 30, 61, 601, 1247, 271, 483, 14, 519, 35, 33, - /* 110 */ 597, 596, 1165, 1158, 108, 518, 301, 305, 1159, 104, - /* 120 */ 1181, 274, 1473, 110, 939, 128, 419, 130, 2, 1360, - /* 130 */ 54, 484, 1791, 1480, 1777, 1478, 519, 69, 212, 1722, - /* 140 */ 478, 1348, 477, 1157, 1196, 1777, 1777, 146, 104, 417, - /* 150 */ 601, 1774, 1247, 1248, 14, 424, 1160, 1471, 147, 1775, - /* 160 */ 1165, 1158, 1774, 1774, 1478, 103, 102, 101, 100, 99, - /* 170 */ 98, 97, 96, 95, 1253, 38, 2, 554, 1163, 1164, - /* 180 */ 54, 1209, 1210, 1212, 1213, 1214, 1215, 1216, 498, 520, - /* 190 */ 1224, 1225, 1226, 1227, 1228, 1229, 553, 552, 601, 551, - /* 200 */ 550, 549, 1248, 495, 1160, 342, 1309, 341, 148, 1158, - /* 210 */ 27, 299, 1242, 1243, 1244, 1245, 1246, 1250, 1251, 1252, - /* 220 */ 131, 1183, 506, 1253, 1435, 304, 1163, 1164, 1568, 1209, + /* 0 */ 1467, 1777, 1777, 1646, 383, 1634, 384, 1380, 292, 11, + /* 10 */ 10, 343, 35, 33, 1776, 146, 24, 923, 1774, 1774, + /* 20 */ 301, 391, 1159, 384, 1380, 1631, 36, 34, 32, 31, + /* 30 */ 30, 1662, 26, 36, 34, 32, 31, 30, 518, 503, + /* 40 */ 1627, 1633, 36, 34, 32, 31, 30, 1157, 1346, 502, + /* 50 */ 1777, 522, 130, 1617, 1360, 927, 928, 518, 14, 483, + /* 60 */ 35, 33, 1285, 145, 1165, 28, 223, 1774, 301, 1675, + /* 70 */ 1159, 349, 80, 1647, 505, 1649, 1650, 501, 77, 522, + /* 80 */ 1, 62, 1715, 1777, 1181, 519, 273, 1711, 518, 1261, + /* 90 */ 1634, 113, 398, 309, 108, 1157, 1775, 104, 1777, 1470, + /* 100 */ 1774, 1646, 601, 1473, 419, 271, 14, 317, 35, 33, + /* 110 */ 1631, 147, 1165, 1158, 1478, 1774, 301, 38, 1159, 36, + /* 120 */ 34, 32, 31, 30, 388, 1627, 1633, 56, 2, 1662, + /* 130 */ 1181, 36, 34, 32, 31, 30, 522, 503, 36, 34, + /* 140 */ 32, 31, 30, 1157, 55, 1523, 1777, 502, 39, 131, + /* 150 */ 601, 1617, 291, 1435, 14, 1371, 1160, 1521, 1662, 145, + /* 160 */ 1165, 1158, 559, 1774, 1450, 274, 472, 1675, 140, 1341, + /* 170 */ 132, 1647, 505, 1649, 1650, 501, 2, 522, 1163, 1164, + /* 180 */ 1517, 1209, 1210, 1212, 1213, 1214, 1215, 1216, 498, 520, + /* 190 */ 1224, 1225, 1226, 1227, 1228, 1229, 1247, 1410, 601, 519, + /* 200 */ 1299, 471, 1183, 55, 1160, 1617, 1456, 1196, 148, 1158, + /* 210 */ 473, 347, 447, 94, 484, 1791, 93, 92, 91, 90, + /* 220 */ 89, 88, 87, 86, 85, 1729, 1163, 1164, 1478, 1209, /* 230 */ 1210, 1212, 1213, 1214, 1215, 1216, 498, 520, 1224, 1225, - /* 240 */ 1226, 1227, 1228, 1229, 1407, 465, 1307, 1308, 1310, 1311, - /* 250 */ 55, 54, 1160, 36, 34, 32, 31, 30, 148, 27, - /* 260 */ 299, 1242, 1243, 1244, 1245, 1246, 1250, 1251, 1252, 63, - /* 270 */ 289, 479, 1284, 188, 1163, 1164, 1346, 1209, 1210, 1212, + /* 240 */ 1226, 1227, 1228, 1229, 1454, 148, 1248, 479, 1523, 1726, + /* 250 */ 1340, 1777, 1160, 597, 596, 306, 1309, 433, 432, 55, + /* 260 */ 1521, 1523, 431, 398, 145, 109, 428, 1253, 1774, 427, + /* 270 */ 426, 425, 148, 1522, 1163, 1164, 112, 1209, 1210, 1212, /* 280 */ 1213, 1214, 1215, 1216, 498, 520, 1224, 1225, 1226, 1227, - /* 290 */ 1228, 1229, 35, 33, 36, 34, 32, 31, 30, 112, - /* 300 */ 301, 1456, 1159, 148, 577, 576, 575, 316, 148, 574, - /* 310 */ 573, 572, 114, 567, 566, 565, 564, 563, 562, 561, - /* 320 */ 560, 121, 1299, 1239, 1523, 313, 54, 1157, 991, 1662, - /* 330 */ 306, 1646, 1559, 1561, 317, 1521, 472, 110, 140, 311, - /* 340 */ 35, 33, 1230, 473, 1165, 993, 923, 128, 301, 1517, - /* 350 */ 1159, 481, 142, 1722, 1723, 1480, 1727, 26, 398, 1662, - /* 360 */ 8, 36, 34, 32, 31, 30, 503, 36, 34, 32, - /* 370 */ 31, 30, 471, 468, 1777, 1157, 502, 1410, 314, 148, - /* 380 */ 1617, 556, 601, 462, 927, 928, 128, 145, 35, 33, - /* 390 */ 334, 1774, 1165, 1158, 1480, 518, 301, 1675, 1159, 53, - /* 400 */ 268, 1647, 505, 1649, 1650, 501, 382, 522, 9, 386, - /* 410 */ 336, 332, 1029, 545, 544, 543, 1033, 542, 1035, 1036, - /* 420 */ 541, 1038, 538, 1157, 1044, 535, 1046, 1047, 532, 529, - /* 430 */ 601, 36, 34, 32, 31, 30, 1160, 433, 432, 548, - /* 440 */ 1165, 1158, 431, 474, 469, 109, 428, 1454, 519, 427, - /* 450 */ 426, 425, 1185, 1469, 148, 39, 9, 1729, 1163, 1164, - /* 460 */ 348, 1209, 1210, 1212, 1213, 1214, 1215, 1216, 498, 520, - /* 470 */ 1224, 1225, 1226, 1227, 1228, 1229, 1478, 1283, 601, 1292, - /* 480 */ 1235, 1725, 433, 432, 1160, 1183, 1183, 431, 148, 1158, - /* 490 */ 109, 428, 1523, 447, 427, 426, 425, 1341, 312, 1637, - /* 500 */ 1196, 1159, 1617, 1521, 556, 455, 1163, 1164, 309, 1209, + /* 290 */ 1228, 1229, 35, 33, 1349, 465, 1307, 1308, 1310, 1311, + /* 300 */ 301, 556, 1159, 27, 299, 1242, 1243, 1244, 1245, 1246, + /* 310 */ 1250, 1251, 1252, 110, 939, 94, 62, 1469, 93, 92, + /* 320 */ 91, 90, 89, 88, 87, 86, 85, 1157, 143, 1722, + /* 330 */ 1723, 148, 1727, 519, 1184, 519, 479, 1631, 1474, 417, + /* 340 */ 35, 33, 1230, 506, 1165, 348, 304, 104, 301, 1568, + /* 350 */ 1159, 55, 1627, 1633, 424, 36, 34, 32, 31, 30, + /* 360 */ 8, 479, 1478, 522, 1478, 112, 36, 34, 32, 31, + /* 370 */ 30, 1556, 433, 432, 1635, 1157, 154, 431, 156, 1646, + /* 380 */ 109, 428, 601, 519, 427, 426, 425, 148, 35, 33, + /* 390 */ 112, 556, 1165, 1158, 1631, 358, 301, 305, 1159, 1523, + /* 400 */ 60, 274, 110, 59, 1182, 128, 312, 1662, 9, 1627, + /* 410 */ 1633, 1521, 1478, 1185, 1480, 503, 481, 142, 1722, 1723, + /* 420 */ 522, 1727, 468, 1157, 1196, 502, 342, 110, 341, 1617, + /* 430 */ 601, 313, 1247, 64, 289, 1397, 1160, 188, 1559, 1561, + /* 440 */ 1165, 1158, 144, 1722, 1723, 1675, 1727, 548, 263, 1647, + /* 450 */ 505, 1649, 1650, 501, 1370, 522, 9, 434, 1163, 1164, + /* 460 */ 334, 1209, 1210, 1212, 1213, 1214, 1215, 1216, 498, 520, + /* 470 */ 1224, 1225, 1226, 1227, 1228, 1229, 283, 311, 601, 148, + /* 480 */ 336, 332, 1248, 1120, 1160, 128, 475, 455, 148, 1158, + /* 490 */ 373, 1122, 474, 469, 1480, 1463, 519, 36, 34, 32, + /* 500 */ 31, 30, 1369, 1253, 1617, 1465, 1163, 1164, 359, 1209, /* 510 */ 1210, 1212, 1213, 1214, 1215, 1216, 498, 520, 1224, 1225, - /* 520 */ 1226, 1227, 1228, 1229, 154, 1631, 1157, 36, 34, 32, - /* 530 */ 31, 30, 1160, 1777, 390, 1639, 1523, 386, 388, 1371, - /* 540 */ 1627, 1634, 1633, 1165, 1181, 373, 145, 1522, 59, 1370, - /* 550 */ 1774, 58, 522, 456, 1163, 1164, 1646, 1209, 1210, 1212, + /* 520 */ 1226, 1227, 1228, 1229, 284, 1478, 282, 281, 1368, 421, + /* 530 */ 558, 1292, 1160, 423, 158, 157, 456, 1183, 214, 27, + /* 540 */ 299, 1242, 1243, 1244, 1245, 1246, 1250, 1251, 1252, 1646, + /* 550 */ 382, 1121, 1617, 386, 1163, 1164, 422, 1209, 1210, 1212, /* 560 */ 1213, 1214, 1215, 1216, 498, 520, 1224, 1225, 1226, 1227, - /* 570 */ 1228, 1229, 35, 33, 270, 519, 1181, 214, 1340, 61, - /* 580 */ 301, 601, 1159, 366, 1662, 519, 378, 358, 1617, 158, - /* 590 */ 157, 503, 1158, 1777, 128, 1369, 174, 516, 1617, 1729, - /* 600 */ 1474, 502, 1481, 1478, 379, 1617, 145, 1157, 139, 519, - /* 610 */ 1774, 483, 1368, 1478, 415, 411, 407, 403, 173, 283, - /* 620 */ 485, 359, 1675, 1724, 1165, 259, 1647, 505, 1649, 1650, - /* 630 */ 501, 519, 522, 1120, 940, 1160, 939, 1478, 479, 445, - /* 640 */ 2, 1122, 62, 397, 1617, 171, 1367, 1211, 1211, 11, - /* 650 */ 10, 1777, 443, 1366, 430, 429, 7, 1163, 1164, 1478, - /* 660 */ 127, 1617, 601, 941, 147, 1365, 112, 284, 1774, 282, - /* 670 */ 281, 1184, 421, 1158, 377, 1364, 423, 372, 371, 370, + /* 570 */ 1228, 1229, 35, 33, 270, 1777, 1181, 1662, 1617, 519, + /* 580 */ 301, 314, 1159, 366, 1249, 482, 378, 1461, 145, 128, + /* 590 */ 554, 397, 1774, 390, 423, 502, 386, 1235, 1480, 1617, + /* 600 */ 940, 54, 939, 1183, 379, 1254, 70, 1157, 1478, 553, + /* 610 */ 552, 1367, 551, 550, 549, 1675, 1366, 422, 81, 1647, + /* 620 */ 505, 1649, 1650, 501, 1165, 522, 1365, 1471, 1715, 941, + /* 630 */ 127, 1186, 294, 1711, 141, 1407, 32, 31, 30, 191, + /* 640 */ 2, 25, 1029, 545, 544, 543, 1033, 542, 1035, 1036, + /* 650 */ 541, 1038, 538, 1743, 1044, 535, 1046, 1047, 532, 529, + /* 660 */ 497, 1617, 601, 1364, 1363, 1362, 1617, 1359, 1358, 1357, + /* 670 */ 1356, 1355, 1354, 1158, 377, 1353, 1617, 372, 371, 370, /* 680 */ 369, 368, 365, 364, 363, 362, 361, 357, 356, 355, - /* 690 */ 354, 353, 352, 351, 350, 1617, 1183, 519, 519, 422, - /* 700 */ 519, 1121, 1617, 170, 110, 165, 1455, 167, 479, 1475, - /* 710 */ 1597, 1186, 517, 1249, 1617, 1363, 1160, 1362, 1359, 143, - /* 720 */ 1722, 1723, 487, 1727, 1617, 1478, 1478, 163, 1478, 1261, - /* 730 */ 1646, 1358, 1357, 1356, 1254, 490, 112, 1355, 1163, 1164, - /* 740 */ 1182, 1209, 1210, 1212, 1213, 1214, 1215, 1216, 498, 520, - /* 750 */ 1224, 1225, 1226, 1227, 1228, 1229, 519, 246, 1662, 519, - /* 760 */ 1508, 1354, 1353, 1352, 1617, 503, 1617, 1617, 236, 1556, - /* 770 */ 25, 315, 571, 569, 110, 502, 156, 927, 928, 1617, - /* 780 */ 1617, 1617, 1617, 1351, 1478, 483, 1617, 1478, 506, 144, - /* 790 */ 1722, 1723, 1606, 1727, 1569, 559, 1675, 1450, 554, 79, - /* 800 */ 1647, 505, 1649, 1650, 501, 1646, 522, 423, 570, 1715, - /* 810 */ 1617, 1617, 1617, 273, 1711, 1560, 1561, 553, 552, 1397, - /* 820 */ 551, 550, 549, 1734, 1280, 1777, 1280, 558, 179, 1392, - /* 830 */ 422, 177, 1617, 1662, 1144, 1145, 199, 324, 147, 1465, - /* 840 */ 503, 434, 1774, 181, 129, 1390, 180, 1461, 337, 252, - /* 850 */ 502, 436, 183, 185, 1617, 182, 184, 118, 1211, 45, - /* 860 */ 483, 250, 52, 46, 272, 51, 1646, 439, 202, 11, - /* 870 */ 10, 1675, 1343, 1344, 79, 1647, 505, 1649, 1650, 501, - /* 880 */ 438, 522, 159, 37, 1715, 37, 191, 37, 273, 1711, - /* 890 */ 225, 497, 75, 116, 1662, 446, 547, 457, 454, 1306, - /* 900 */ 1777, 482, 71, 1361, 117, 1168, 54, 488, 204, 187, - /* 910 */ 1436, 502, 118, 145, 1167, 1617, 1453, 1774, 218, 1646, - /* 920 */ 45, 441, 491, 1255, 209, 1217, 435, 1115, 466, 1663, - /* 930 */ 227, 186, 1675, 511, 448, 80, 1647, 505, 1649, 1650, - /* 940 */ 501, 527, 522, 78, 233, 1715, 117, 1662, 1381, 294, - /* 950 */ 1711, 141, 1022, 416, 503, 50, 1518, 480, 49, 1745, - /* 960 */ 245, 217, 220, 215, 502, 118, 3, 1181, 1617, 461, - /* 970 */ 1742, 1171, 1646, 119, 57, 56, 346, 222, 117, 153, - /* 980 */ 1170, 1050, 319, 323, 340, 1675, 1054, 964, 263, 1647, - /* 990 */ 505, 1649, 1650, 501, 279, 522, 269, 991, 280, 330, - /* 1000 */ 1662, 326, 322, 150, 965, 1061, 241, 482, 554, 1128, - /* 1010 */ 360, 155, 1558, 1059, 367, 374, 375, 502, 120, 376, - /* 1020 */ 380, 1617, 1187, 381, 389, 1190, 475, 553, 552, 392, - /* 1030 */ 551, 550, 549, 393, 148, 162, 164, 1189, 1675, 1349, - /* 1040 */ 394, 80, 1647, 505, 1649, 1650, 501, 1646, 522, 166, - /* 1050 */ 395, 1715, 1188, 396, 169, 294, 1711, 141, 60, 399, - /* 1060 */ 94, 172, 420, 93, 92, 91, 90, 89, 88, 87, - /* 1070 */ 86, 85, 1468, 418, 288, 1662, 1743, 1165, 176, 1464, - /* 1080 */ 1646, 84, 503, 178, 122, 1601, 123, 242, 189, 1466, - /* 1090 */ 1462, 124, 502, 125, 449, 1186, 1617, 450, 192, 453, - /* 1100 */ 1746, 243, 194, 467, 458, 197, 1756, 509, 1662, 6, - /* 1110 */ 459, 1755, 476, 1675, 1736, 503, 80, 1647, 505, 1649, - /* 1120 */ 1650, 501, 464, 522, 293, 502, 1715, 200, 470, 1617, - /* 1130 */ 294, 1711, 1790, 463, 203, 5, 208, 1280, 135, 210, - /* 1140 */ 111, 1749, 1646, 1185, 40, 492, 1675, 295, 1730, 80, - /* 1150 */ 1647, 505, 1649, 1650, 501, 216, 522, 1567, 489, 1715, - /* 1160 */ 18, 1566, 211, 294, 1711, 1790, 512, 229, 507, 244, - /* 1170 */ 1662, 508, 303, 513, 1772, 514, 70, 503, 231, 1696, - /* 1180 */ 68, 525, 1793, 1479, 247, 1451, 238, 502, 600, 1611, - /* 1190 */ 290, 1617, 47, 1773, 249, 134, 1646, 253, 251, 1610, - /* 1200 */ 486, 219, 260, 318, 221, 1646, 254, 1607, 1675, 493, - /* 1210 */ 320, 80, 1647, 505, 1649, 1650, 501, 321, 522, 1153, - /* 1220 */ 1154, 1715, 151, 325, 1662, 294, 1711, 1790, 1605, 327, - /* 1230 */ 328, 503, 1604, 1662, 329, 331, 1733, 1603, 333, 1602, - /* 1240 */ 503, 502, 335, 1587, 152, 1617, 338, 339, 1131, 1130, - /* 1250 */ 502, 1581, 1580, 344, 1617, 1579, 345, 1578, 1098, 1646, - /* 1260 */ 483, 1551, 1675, 1550, 1549, 132, 1647, 505, 1649, 1650, - /* 1270 */ 501, 1675, 522, 1548, 259, 1647, 505, 1649, 1650, 501, - /* 1280 */ 1547, 522, 1546, 1545, 1544, 1543, 1542, 1662, 1541, 1540, - /* 1290 */ 1539, 1538, 1537, 1536, 503, 1535, 1534, 1533, 115, 1646, - /* 1300 */ 1777, 1532, 1531, 1530, 502, 1529, 1528, 1527, 1617, 1100, - /* 1310 */ 1792, 1526, 1525, 145, 1524, 1409, 1377, 1774, 1646, 160, - /* 1320 */ 1376, 385, 138, 401, 930, 1675, 1595, 1662, 81, 1647, - /* 1330 */ 505, 1649, 1650, 501, 503, 522, 106, 929, 1715, 405, - /* 1340 */ 1402, 107, 1714, 1711, 502, 1589, 1662, 1573, 1617, 1564, - /* 1350 */ 1457, 161, 387, 500, 168, 1408, 1406, 958, 402, 1404, - /* 1360 */ 1400, 406, 410, 502, 414, 1675, 400, 1617, 81, 1647, - /* 1370 */ 505, 1649, 1650, 501, 1646, 522, 404, 408, 1715, 409, - /* 1380 */ 412, 413, 494, 1711, 1675, 1389, 1388, 267, 1647, 505, - /* 1390 */ 1649, 1650, 501, 499, 522, 496, 1687, 1375, 1459, 1065, - /* 1400 */ 1064, 1458, 1662, 604, 990, 989, 568, 83, 988, 503, - /* 1410 */ 987, 570, 1398, 984, 983, 175, 285, 240, 982, 502, - /* 1420 */ 437, 286, 1391, 1617, 1393, 287, 440, 1374, 442, 105, - /* 1430 */ 1373, 444, 1594, 1646, 82, 593, 589, 585, 581, 239, - /* 1440 */ 1675, 1588, 1138, 81, 1647, 505, 1649, 1650, 501, 1572, - /* 1450 */ 522, 451, 1571, 1715, 1563, 64, 196, 4, 1712, 37, - /* 1460 */ 15, 1662, 201, 77, 43, 207, 234, 48, 503, 1305, - /* 1470 */ 133, 205, 22, 41, 1298, 206, 452, 193, 502, 1637, - /* 1480 */ 65, 198, 1617, 23, 16, 298, 1277, 1646, 1276, 213, - /* 1490 */ 136, 308, 307, 126, 42, 1329, 1334, 17, 1323, 1675, - /* 1500 */ 515, 1173, 268, 1647, 505, 1649, 1650, 501, 1328, 522, - /* 1510 */ 296, 1333, 13, 1332, 297, 1662, 10, 1646, 19, 1219, - /* 1520 */ 1240, 1218, 500, 29, 137, 460, 1166, 12, 195, 149, - /* 1530 */ 20, 1204, 502, 504, 1562, 21, 1617, 230, 224, 1303, - /* 1540 */ 226, 71, 228, 1165, 1175, 1662, 1136, 510, 190, 66, - /* 1550 */ 1646, 232, 503, 1675, 67, 1636, 267, 1647, 505, 1649, - /* 1560 */ 1650, 501, 502, 522, 235, 1688, 1617, 1678, 521, 300, - /* 1570 */ 1221, 44, 524, 1051, 526, 310, 528, 530, 1662, 1048, - /* 1580 */ 1646, 523, 531, 1675, 1045, 503, 268, 1647, 505, 1649, - /* 1590 */ 1650, 501, 1169, 522, 533, 502, 1039, 534, 536, 1617, - /* 1600 */ 1037, 537, 302, 539, 540, 1028, 1060, 1058, 1662, 1043, - /* 1610 */ 1646, 1042, 479, 72, 1041, 503, 1675, 1040, 546, 268, - /* 1620 */ 1647, 505, 1649, 1650, 501, 502, 522, 1057, 73, 1617, - /* 1630 */ 74, 1056, 956, 555, 997, 1174, 557, 237, 1662, 978, - /* 1640 */ 112, 977, 976, 973, 975, 503, 1675, 974, 972, 255, - /* 1650 */ 1647, 505, 1649, 1650, 501, 502, 522, 1177, 971, 1617, - /* 1660 */ 483, 1646, 994, 992, 968, 967, 966, 963, 520, 1224, - /* 1670 */ 1225, 962, 961, 1405, 578, 579, 1675, 1646, 110, 262, - /* 1680 */ 1647, 505, 1649, 1650, 501, 580, 522, 1403, 582, 1662, - /* 1690 */ 583, 584, 1401, 212, 1722, 478, 503, 477, 586, 588, - /* 1700 */ 1777, 1399, 587, 591, 592, 1662, 502, 590, 1387, 595, - /* 1710 */ 1617, 594, 503, 145, 1386, 1372, 598, 1774, 599, 1347, - /* 1720 */ 1161, 248, 502, 602, 1646, 603, 1617, 1675, 1347, 1347, - /* 1730 */ 264, 1647, 505, 1649, 1650, 501, 1347, 522, 1347, 1347, - /* 1740 */ 1347, 1347, 1646, 1675, 1347, 1347, 256, 1647, 505, 1649, - /* 1750 */ 1650, 501, 1662, 522, 1347, 1347, 1347, 1646, 1347, 503, - /* 1760 */ 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 502, - /* 1770 */ 1662, 1347, 1347, 1617, 1347, 1347, 1347, 503, 1347, 1347, - /* 1780 */ 1347, 1347, 1347, 1347, 1347, 1662, 1347, 502, 1347, 1347, - /* 1790 */ 1675, 1617, 503, 265, 1647, 505, 1649, 1650, 501, 1347, - /* 1800 */ 522, 1347, 502, 1347, 1646, 1347, 1617, 1347, 1675, 1347, - /* 1810 */ 1347, 257, 1647, 505, 1649, 1650, 501, 1347, 522, 1347, - /* 1820 */ 1646, 1347, 1347, 1675, 1347, 1347, 266, 1647, 505, 1649, - /* 1830 */ 1650, 501, 1662, 522, 1347, 1347, 1347, 1347, 1347, 503, - /* 1840 */ 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1662, 502, - /* 1850 */ 1347, 1347, 1347, 1617, 1347, 503, 1347, 1347, 1347, 1347, - /* 1860 */ 1347, 1347, 1347, 1347, 1347, 502, 1347, 1646, 1347, 1617, - /* 1870 */ 1675, 1347, 1347, 258, 1647, 505, 1649, 1650, 501, 1347, - /* 1880 */ 522, 1347, 1347, 1347, 1347, 1347, 1675, 1347, 1347, 1658, - /* 1890 */ 1647, 505, 1649, 1650, 501, 1662, 522, 1646, 1347, 1347, - /* 1900 */ 1347, 1347, 503, 1347, 1347, 1347, 1347, 1347, 1347, 1347, - /* 1910 */ 1347, 1347, 502, 1347, 1347, 1347, 1617, 1347, 1347, 1347, - /* 1920 */ 1347, 1347, 1347, 1347, 1347, 1662, 1347, 1347, 1347, 1347, - /* 1930 */ 1347, 1347, 503, 1675, 1347, 1347, 1657, 1647, 505, 1649, - /* 1940 */ 1650, 501, 502, 522, 1347, 1347, 1617, 1646, 1347, 1347, - /* 1950 */ 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, - /* 1960 */ 1347, 1347, 1347, 1675, 1347, 1347, 1656, 1647, 505, 1649, - /* 1970 */ 1650, 501, 1347, 522, 1347, 1662, 1347, 1646, 1347, 1347, - /* 1980 */ 1347, 1347, 503, 1347, 1347, 1347, 1347, 1347, 1347, 1347, - /* 1990 */ 1347, 1347, 502, 1347, 1347, 1347, 1617, 1347, 1347, 1347, - /* 2000 */ 1347, 1347, 1347, 1347, 1347, 1662, 1347, 1347, 1347, 1347, - /* 2010 */ 1347, 1347, 503, 1675, 1347, 1347, 277, 1647, 505, 1649, - /* 2020 */ 1650, 501, 502, 522, 1347, 1347, 1617, 1646, 1347, 1347, - /* 2030 */ 1347, 1347, 1347, 1347, 1347, 1347, 1646, 1347, 1347, 1347, - /* 2040 */ 1347, 1347, 1347, 1675, 1347, 1347, 276, 1647, 505, 1649, - /* 2050 */ 1650, 501, 1347, 522, 1347, 1662, 1347, 1347, 1347, 1347, - /* 2060 */ 1347, 1347, 503, 1347, 1662, 1347, 1347, 1347, 1347, 1347, - /* 2070 */ 1347, 503, 502, 1347, 1347, 1347, 1617, 1347, 1347, 1347, - /* 2080 */ 1347, 502, 1347, 1347, 1347, 1617, 1347, 1347, 1347, 1347, - /* 2090 */ 1646, 1347, 1347, 1675, 1347, 1347, 278, 1647, 505, 1649, - /* 2100 */ 1650, 501, 1675, 522, 1347, 275, 1647, 505, 1649, 1650, - /* 2110 */ 501, 1347, 522, 1347, 1347, 1347, 1347, 1347, 1662, 1347, - /* 2120 */ 1347, 1347, 1347, 1347, 1347, 503, 1347, 1347, 1347, 1347, - /* 2130 */ 1347, 1347, 1347, 1347, 1347, 502, 1347, 1347, 1347, 1617, - /* 2140 */ 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, - /* 2150 */ 1347, 1347, 1347, 1347, 1347, 1347, 1675, 1347, 1347, 261, - /* 2160 */ 1647, 505, 1649, 1650, 501, 1347, 522, + /* 690 */ 354, 353, 352, 351, 350, 577, 576, 575, 316, 1211, + /* 700 */ 574, 573, 572, 114, 567, 566, 565, 564, 563, 562, + /* 710 */ 561, 560, 121, 1617, 1617, 1617, 1160, 1617, 1617, 1617, + /* 720 */ 1617, 1617, 1617, 1352, 7, 1617, 128, 1351, 430, 429, + /* 730 */ 1646, 571, 569, 1560, 1561, 1481, 927, 928, 1163, 1164, + /* 740 */ 1729, 1209, 1210, 1212, 1213, 1214, 1215, 1216, 498, 520, + /* 750 */ 1224, 1225, 1226, 1227, 1228, 1229, 199, 129, 1662, 519, + /* 760 */ 1284, 1646, 252, 1729, 1725, 1211, 503, 991, 1159, 519, + /* 770 */ 1183, 1475, 1606, 1617, 250, 53, 502, 1617, 52, 506, + /* 780 */ 1617, 1597, 1734, 1280, 993, 1569, 483, 1724, 1478, 1662, + /* 790 */ 1144, 1145, 487, 1157, 479, 159, 1675, 482, 1478, 80, + /* 800 */ 1647, 505, 1649, 1650, 501, 246, 522, 502, 1508, 1715, + /* 810 */ 1165, 1617, 519, 273, 1711, 570, 454, 324, 179, 55, + /* 820 */ 1168, 177, 485, 112, 516, 1777, 181, 1675, 490, 180, + /* 830 */ 81, 1647, 505, 1649, 1650, 501, 1637, 522, 145, 519, + /* 840 */ 1715, 1478, 1774, 483, 294, 1711, 141, 183, 601, 185, + /* 850 */ 182, 517, 184, 445, 495, 337, 79, 1646, 215, 1158, + /* 860 */ 110, 519, 519, 547, 461, 1742, 443, 964, 1478, 47, + /* 870 */ 272, 118, 1639, 236, 315, 212, 1722, 478, 1392, 477, + /* 880 */ 11, 10, 1777, 1390, 965, 1662, 1171, 58, 57, 346, + /* 890 */ 1478, 1478, 153, 503, 1280, 147, 1361, 340, 46, 1774, + /* 900 */ 436, 1167, 1160, 502, 202, 439, 37, 1617, 1436, 269, + /* 910 */ 37, 457, 330, 37, 326, 322, 150, 225, 1343, 1344, + /* 920 */ 1646, 1455, 116, 1675, 1163, 1164, 81, 1647, 505, 1649, + /* 930 */ 1650, 501, 1211, 522, 218, 117, 1715, 466, 1306, 76, + /* 940 */ 294, 1711, 1790, 448, 204, 118, 1255, 148, 1662, 72, + /* 950 */ 1217, 1749, 1663, 1115, 1381, 46, 503, 227, 527, 209, + /* 960 */ 416, 174, 511, 480, 1518, 1283, 502, 1170, 1745, 217, + /* 970 */ 1617, 117, 1646, 139, 1239, 233, 220, 488, 222, 415, + /* 980 */ 411, 407, 403, 173, 118, 1022, 1675, 119, 117, 81, + /* 990 */ 1647, 505, 1649, 1650, 501, 245, 522, 1453, 1050, 1715, + /* 1000 */ 1662, 3, 1181, 294, 1711, 1790, 319, 63, 503, 323, + /* 1010 */ 171, 1054, 991, 554, 1772, 491, 280, 279, 502, 241, + /* 1020 */ 1128, 155, 1617, 360, 1061, 1558, 367, 1059, 120, 375, + /* 1030 */ 374, 1646, 553, 552, 380, 551, 550, 549, 1675, 1187, + /* 1040 */ 376, 81, 1647, 505, 1649, 1650, 501, 438, 522, 381, + /* 1050 */ 389, 1715, 1190, 162, 392, 294, 1711, 1790, 393, 1662, + /* 1060 */ 1189, 164, 446, 394, 166, 395, 1733, 503, 170, 1188, + /* 1070 */ 165, 396, 167, 399, 169, 61, 187, 502, 418, 172, + /* 1080 */ 1165, 1617, 1646, 420, 1468, 176, 1464, 483, 441, 554, + /* 1090 */ 84, 242, 163, 435, 288, 1601, 178, 1675, 186, 1646, + /* 1100 */ 259, 1647, 505, 1649, 1650, 501, 122, 522, 553, 552, + /* 1110 */ 1662, 551, 550, 549, 123, 1466, 1462, 124, 503, 125, + /* 1120 */ 449, 189, 51, 453, 450, 50, 1777, 1662, 502, 243, + /* 1130 */ 458, 192, 1617, 194, 1186, 503, 197, 459, 483, 147, + /* 1140 */ 1746, 467, 509, 1774, 1756, 502, 6, 200, 1675, 1617, + /* 1150 */ 463, 259, 1647, 505, 1649, 1650, 501, 1646, 522, 464, + /* 1160 */ 476, 5, 1736, 203, 1755, 1675, 293, 210, 82, 1647, + /* 1170 */ 505, 1649, 1650, 501, 1280, 522, 111, 1777, 1715, 470, + /* 1180 */ 208, 1185, 1714, 1711, 1348, 1662, 40, 211, 492, 1646, + /* 1190 */ 145, 1773, 135, 503, 1774, 1730, 295, 489, 18, 1567, + /* 1200 */ 1793, 507, 508, 502, 1566, 512, 303, 1617, 103, 102, + /* 1210 */ 101, 100, 99, 98, 97, 96, 95, 1662, 216, 479, + /* 1220 */ 1696, 513, 229, 1675, 219, 500, 82, 1647, 505, 1649, + /* 1230 */ 1650, 501, 514, 522, 231, 502, 1715, 69, 486, 1617, + /* 1240 */ 494, 1711, 1646, 493, 221, 244, 1479, 71, 112, 525, + /* 1250 */ 1451, 247, 600, 238, 48, 1675, 134, 253, 267, 1647, + /* 1260 */ 505, 1649, 1650, 501, 499, 522, 496, 1687, 483, 290, + /* 1270 */ 1662, 260, 249, 254, 251, 1611, 1610, 318, 503, 1607, + /* 1280 */ 320, 321, 1153, 1154, 151, 110, 325, 1605, 502, 327, + /* 1290 */ 328, 329, 1617, 1604, 331, 1603, 333, 1602, 335, 1646, + /* 1300 */ 212, 1722, 478, 1587, 477, 152, 339, 1777, 1675, 338, + /* 1310 */ 1131, 82, 1647, 505, 1649, 1650, 501, 1581, 522, 1130, + /* 1320 */ 145, 1715, 1580, 344, 1774, 345, 1712, 1662, 604, 1579, + /* 1330 */ 1578, 1551, 1098, 1550, 1549, 503, 1548, 1547, 1546, 1545, + /* 1340 */ 1544, 1543, 240, 1100, 115, 502, 1646, 1542, 1541, 1617, + /* 1350 */ 1540, 1539, 462, 1538, 105, 1537, 1536, 1535, 1534, 1533, + /* 1360 */ 593, 589, 585, 581, 239, 1675, 1532, 1531, 268, 1647, + /* 1370 */ 505, 1649, 1650, 501, 1662, 522, 1530, 1529, 1528, 1527, + /* 1380 */ 1526, 1525, 503, 1524, 1409, 1646, 1377, 138, 78, 160, + /* 1390 */ 1376, 234, 502, 1595, 1589, 1573, 1617, 106, 385, 930, + /* 1400 */ 161, 929, 107, 1564, 1457, 387, 168, 1408, 1406, 401, + /* 1410 */ 400, 958, 1675, 1662, 1404, 132, 1647, 505, 1649, 1650, + /* 1420 */ 501, 503, 522, 1402, 1400, 515, 402, 1389, 406, 404, + /* 1430 */ 1388, 502, 175, 405, 410, 1617, 1646, 414, 298, 408, + /* 1440 */ 1375, 409, 1459, 1458, 413, 412, 1398, 1064, 1065, 990, + /* 1450 */ 460, 1675, 989, 195, 268, 1647, 505, 1649, 1650, 501, + /* 1460 */ 1792, 522, 45, 988, 1662, 1646, 987, 568, 1393, 570, + /* 1470 */ 285, 1136, 500, 190, 286, 1391, 984, 287, 983, 1374, + /* 1480 */ 982, 440, 502, 437, 442, 1373, 1617, 444, 1594, 83, + /* 1490 */ 1588, 1138, 451, 1662, 1572, 1571, 126, 1646, 1563, 4, + /* 1500 */ 65, 503, 1675, 196, 37, 267, 1647, 505, 1649, 1650, + /* 1510 */ 501, 502, 522, 49, 1688, 1617, 452, 193, 300, 15, + /* 1520 */ 201, 43, 1305, 206, 41, 1662, 1298, 133, 207, 205, + /* 1530 */ 22, 1675, 23, 503, 268, 1647, 505, 1649, 1650, 501, + /* 1540 */ 1637, 522, 1277, 502, 66, 213, 198, 1617, 1276, 42, + /* 1550 */ 302, 136, 1334, 1646, 16, 17, 13, 1323, 1329, 10, + /* 1560 */ 1328, 19, 296, 1675, 1333, 1332, 268, 1647, 505, 1649, + /* 1570 */ 1650, 501, 297, 522, 1219, 137, 149, 29, 1204, 510, + /* 1580 */ 1218, 1662, 12, 20, 1646, 21, 226, 1240, 1562, 503, + /* 1590 */ 504, 224, 230, 232, 72, 1636, 235, 1303, 1175, 502, + /* 1600 */ 1221, 228, 67, 1617, 68, 526, 1678, 521, 44, 310, + /* 1610 */ 1051, 1048, 1662, 1646, 524, 528, 530, 531, 533, 1675, + /* 1620 */ 503, 1045, 255, 1647, 505, 1649, 1650, 501, 534, 522, + /* 1630 */ 502, 1039, 536, 537, 1617, 1037, 539, 1043, 1042, 540, + /* 1640 */ 1041, 1662, 1040, 1028, 73, 74, 1060, 75, 1057, 503, + /* 1650 */ 1675, 1056, 546, 262, 1647, 505, 1649, 1650, 501, 502, + /* 1660 */ 522, 956, 1646, 1617, 555, 557, 237, 997, 308, 307, + /* 1670 */ 978, 977, 973, 1646, 1058, 976, 994, 975, 1173, 1675, + /* 1680 */ 974, 972, 264, 1647, 505, 1649, 1650, 501, 971, 522, + /* 1690 */ 1662, 992, 968, 967, 966, 963, 962, 1405, 503, 961, + /* 1700 */ 578, 1662, 579, 1166, 580, 1403, 582, 583, 502, 503, + /* 1710 */ 584, 1401, 1617, 586, 587, 588, 1399, 590, 592, 502, + /* 1720 */ 1165, 591, 1387, 1617, 1646, 1386, 594, 1372, 1675, 595, + /* 1730 */ 598, 256, 1647, 505, 1649, 1650, 501, 1161, 522, 1675, + /* 1740 */ 599, 603, 265, 1647, 505, 1649, 1650, 501, 248, 522, + /* 1750 */ 602, 1347, 1662, 1646, 1347, 1347, 1347, 1347, 523, 1347, + /* 1760 */ 503, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1169, + /* 1770 */ 502, 1347, 1347, 1347, 1617, 1347, 1347, 1347, 1347, 1347, + /* 1780 */ 1347, 1662, 1347, 1347, 1347, 1646, 1347, 1347, 1347, 503, + /* 1790 */ 1675, 1347, 1347, 257, 1647, 505, 1649, 1650, 501, 502, + /* 1800 */ 522, 1347, 1347, 1617, 1347, 1347, 1347, 1347, 1347, 1347, + /* 1810 */ 1347, 1347, 1174, 1662, 1347, 1347, 1347, 1347, 1347, 1675, + /* 1820 */ 1347, 503, 266, 1647, 505, 1649, 1650, 501, 1347, 522, + /* 1830 */ 1347, 502, 1347, 1347, 1177, 1617, 1347, 1347, 1347, 1347, + /* 1840 */ 1347, 1646, 1347, 1347, 1347, 520, 1224, 1225, 1347, 1347, + /* 1850 */ 1347, 1675, 1347, 1347, 258, 1647, 505, 1649, 1650, 501, + /* 1860 */ 1347, 522, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1662, + /* 1870 */ 1347, 1347, 1646, 1347, 1347, 1347, 1347, 503, 1347, 1347, + /* 1880 */ 1347, 1347, 1347, 1347, 1347, 1347, 1347, 502, 1347, 1347, + /* 1890 */ 1347, 1617, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, + /* 1900 */ 1662, 1646, 1347, 1347, 1347, 1347, 1347, 1675, 503, 1347, + /* 1910 */ 1658, 1647, 505, 1649, 1650, 501, 1347, 522, 502, 1347, + /* 1920 */ 1347, 1347, 1617, 1347, 1347, 1347, 1347, 1347, 1347, 1662, + /* 1930 */ 1347, 1347, 1646, 1347, 1347, 1347, 1347, 503, 1675, 1347, + /* 1940 */ 1347, 1657, 1647, 505, 1649, 1650, 501, 502, 522, 1347, + /* 1950 */ 1347, 1617, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, + /* 1960 */ 1662, 1646, 1347, 1347, 1347, 1347, 1347, 1675, 503, 1347, + /* 1970 */ 1656, 1647, 505, 1649, 1650, 501, 1347, 522, 502, 1347, + /* 1980 */ 1347, 1347, 1617, 1347, 1347, 1347, 1347, 1347, 1347, 1662, + /* 1990 */ 1347, 1347, 1347, 1347, 1347, 1347, 1347, 503, 1675, 1347, + /* 2000 */ 1347, 277, 1647, 505, 1649, 1650, 501, 502, 522, 1347, + /* 2010 */ 1347, 1617, 1646, 1347, 1347, 1347, 1347, 1347, 1347, 1347, + /* 2020 */ 1347, 1347, 1347, 1646, 1347, 1347, 1347, 1675, 1347, 1347, + /* 2030 */ 276, 1647, 505, 1649, 1650, 501, 1347, 522, 1347, 1347, + /* 2040 */ 1662, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 503, 1347, + /* 2050 */ 1347, 1662, 1347, 1347, 1347, 1347, 1347, 1347, 502, 503, + /* 2060 */ 1347, 1347, 1617, 1347, 1347, 1347, 1347, 1347, 1347, 502, + /* 2070 */ 1347, 1347, 1347, 1617, 1347, 1347, 1347, 1646, 1675, 1347, + /* 2080 */ 1347, 278, 1647, 505, 1649, 1650, 501, 1347, 522, 1675, + /* 2090 */ 1347, 1347, 275, 1647, 505, 1649, 1650, 501, 1347, 522, + /* 2100 */ 1347, 1347, 1347, 1347, 1347, 1662, 1347, 1347, 1347, 1347, + /* 2110 */ 1347, 1347, 1347, 503, 1347, 1347, 1347, 1347, 1347, 1347, + /* 2120 */ 1347, 1347, 1347, 502, 1347, 1347, 1347, 1617, 1347, 1347, + /* 2130 */ 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, + /* 2140 */ 1347, 1347, 1347, 1675, 1347, 1347, 261, 1647, 505, 1649, + /* 2150 */ 1650, 501, 1347, 522, }; static const YYCODETYPE yy_lookahead[] = { - /* 0 */ 244, 251, 246, 247, 244, 248, 246, 247, 290, 321, - /* 10 */ 322, 270, 12, 13, 264, 270, 296, 260, 241, 336, - /* 20 */ 20, 271, 22, 305, 306, 307, 12, 13, 14, 15, - /* 30 */ 16, 308, 349, 276, 21, 317, 353, 24, 25, 26, - /* 40 */ 27, 28, 29, 30, 31, 32, 269, 47, 269, 273, - /* 50 */ 14, 15, 16, 276, 275, 332, 336, 248, 58, 280, - /* 60 */ 12, 13, 14, 286, 64, 20, 290, 290, 20, 349, - /* 70 */ 22, 248, 58, 353, 57, 12, 13, 14, 15, 16, - /* 80 */ 80, 305, 306, 307, 307, 276, 2, 310, 311, 312, - /* 90 */ 313, 314, 315, 317, 317, 47, 12, 13, 14, 15, - /* 100 */ 16, 253, 102, 89, 281, 296, 58, 248, 12, 13, - /* 110 */ 249, 250, 64, 113, 266, 20, 20, 261, 22, 260, - /* 120 */ 20, 58, 274, 314, 22, 269, 267, 240, 80, 242, - /* 130 */ 80, 354, 355, 277, 336, 276, 248, 251, 329, 330, - /* 140 */ 331, 0, 333, 47, 81, 336, 336, 349, 260, 47, - /* 150 */ 102, 353, 89, 139, 58, 267, 156, 271, 349, 349, - /* 160 */ 64, 113, 353, 353, 276, 24, 25, 26, 27, 28, - /* 170 */ 29, 30, 31, 32, 160, 80, 80, 92, 178, 179, - /* 180 */ 80, 181, 182, 183, 184, 185, 186, 187, 188, 189, - /* 190 */ 190, 191, 192, 193, 194, 195, 111, 112, 102, 114, - /* 200 */ 115, 116, 139, 58, 156, 155, 178, 157, 208, 113, - /* 210 */ 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, - /* 220 */ 254, 20, 286, 160, 258, 289, 178, 179, 292, 181, + /* 0 */ 270, 336, 336, 241, 244, 271, 246, 247, 274, 1, + /* 10 */ 2, 297, 12, 13, 349, 349, 2, 4, 353, 353, + /* 20 */ 20, 244, 22, 246, 247, 291, 12, 13, 14, 15, + /* 30 */ 16, 269, 2, 12, 13, 14, 15, 16, 20, 277, + /* 40 */ 306, 307, 12, 13, 14, 15, 16, 47, 238, 287, + /* 50 */ 336, 317, 240, 291, 242, 42, 43, 20, 58, 297, + /* 60 */ 12, 13, 14, 349, 64, 321, 322, 353, 20, 307, + /* 70 */ 22, 248, 310, 311, 312, 313, 314, 315, 251, 317, + /* 80 */ 80, 253, 320, 336, 20, 248, 324, 325, 20, 81, + /* 90 */ 271, 264, 57, 274, 266, 47, 349, 260, 336, 272, + /* 100 */ 353, 241, 102, 275, 267, 282, 58, 297, 12, 13, + /* 110 */ 291, 349, 64, 113, 277, 353, 20, 80, 22, 12, + /* 120 */ 13, 14, 15, 16, 14, 306, 307, 4, 80, 269, + /* 130 */ 20, 12, 13, 14, 15, 16, 317, 277, 12, 13, + /* 140 */ 14, 15, 16, 47, 80, 269, 336, 287, 80, 254, + /* 150 */ 102, 291, 276, 258, 58, 241, 156, 281, 269, 349, + /* 160 */ 64, 113, 257, 353, 259, 58, 277, 307, 268, 148, + /* 170 */ 310, 311, 312, 313, 314, 315, 80, 317, 178, 179, + /* 180 */ 280, 181, 182, 183, 184, 185, 186, 187, 188, 189, + /* 190 */ 190, 191, 192, 193, 194, 195, 89, 0, 102, 248, + /* 200 */ 81, 312, 20, 80, 156, 291, 0, 81, 208, 113, + /* 210 */ 20, 260, 297, 21, 354, 355, 24, 25, 26, 27, + /* 220 */ 28, 29, 30, 31, 32, 308, 178, 179, 277, 181, /* 230 */ 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, - /* 240 */ 192, 193, 194, 195, 0, 217, 218, 219, 220, 221, - /* 250 */ 4, 80, 156, 12, 13, 14, 15, 16, 208, 196, - /* 260 */ 197, 198, 199, 200, 201, 202, 203, 204, 205, 165, - /* 270 */ 166, 248, 4, 169, 178, 179, 238, 181, 182, 183, + /* 240 */ 192, 193, 194, 195, 0, 208, 139, 248, 269, 332, + /* 250 */ 229, 336, 156, 249, 250, 276, 178, 60, 61, 80, + /* 260 */ 281, 269, 65, 57, 349, 68, 69, 160, 353, 72, + /* 270 */ 73, 74, 208, 281, 178, 179, 277, 181, 182, 183, /* 280 */ 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, - /* 290 */ 194, 195, 12, 13, 12, 13, 14, 15, 16, 276, - /* 300 */ 20, 0, 22, 208, 60, 61, 62, 63, 208, 65, - /* 310 */ 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, - /* 320 */ 76, 77, 81, 178, 269, 278, 80, 47, 47, 269, - /* 330 */ 275, 241, 285, 286, 296, 280, 276, 314, 268, 261, - /* 340 */ 12, 13, 14, 20, 64, 64, 4, 269, 20, 279, - /* 350 */ 22, 328, 329, 330, 331, 277, 333, 2, 57, 269, - /* 360 */ 80, 12, 13, 14, 15, 16, 276, 12, 13, 14, - /* 370 */ 15, 16, 312, 143, 336, 47, 286, 0, 261, 208, - /* 380 */ 290, 57, 102, 293, 42, 43, 269, 349, 12, 13, - /* 390 */ 151, 353, 64, 113, 277, 20, 20, 307, 22, 3, - /* 400 */ 310, 311, 312, 313, 314, 315, 245, 317, 80, 248, - /* 410 */ 171, 172, 93, 94, 95, 96, 97, 98, 99, 100, - /* 420 */ 101, 102, 103, 47, 105, 106, 107, 108, 109, 110, - /* 430 */ 102, 12, 13, 14, 15, 16, 156, 60, 61, 91, - /* 440 */ 64, 113, 65, 213, 214, 68, 69, 0, 248, 72, - /* 450 */ 73, 74, 20, 241, 208, 80, 80, 308, 178, 179, - /* 460 */ 260, 181, 182, 183, 184, 185, 186, 187, 188, 189, - /* 470 */ 190, 191, 192, 193, 194, 195, 276, 209, 102, 14, - /* 480 */ 14, 332, 60, 61, 156, 20, 20, 65, 208, 113, - /* 490 */ 68, 69, 269, 296, 72, 73, 74, 148, 275, 44, - /* 500 */ 81, 22, 290, 280, 57, 248, 178, 179, 273, 181, + /* 290 */ 194, 195, 12, 13, 0, 217, 218, 219, 220, 221, + /* 300 */ 20, 57, 22, 196, 197, 198, 199, 200, 201, 202, + /* 310 */ 203, 204, 205, 314, 22, 21, 253, 271, 24, 25, + /* 320 */ 26, 27, 28, 29, 30, 31, 32, 47, 329, 330, + /* 330 */ 331, 208, 333, 248, 20, 248, 248, 291, 275, 47, + /* 340 */ 12, 13, 14, 287, 64, 260, 290, 260, 20, 293, + /* 350 */ 22, 80, 306, 307, 267, 12, 13, 14, 15, 16, + /* 360 */ 80, 248, 277, 317, 277, 277, 12, 13, 14, 15, + /* 370 */ 16, 277, 60, 61, 271, 47, 55, 65, 284, 241, + /* 380 */ 68, 69, 102, 248, 72, 73, 74, 208, 12, 13, + /* 390 */ 277, 57, 64, 113, 291, 260, 20, 261, 22, 269, + /* 400 */ 79, 58, 314, 82, 20, 269, 276, 269, 80, 306, + /* 410 */ 307, 281, 277, 20, 278, 277, 328, 329, 330, 331, + /* 420 */ 317, 333, 143, 47, 81, 287, 155, 314, 157, 291, + /* 430 */ 102, 279, 89, 165, 166, 0, 156, 169, 286, 287, + /* 440 */ 64, 113, 329, 330, 331, 307, 333, 91, 310, 311, + /* 450 */ 312, 313, 314, 315, 241, 317, 80, 22, 178, 179, + /* 460 */ 151, 181, 182, 183, 184, 185, 186, 187, 188, 189, + /* 470 */ 190, 191, 192, 193, 194, 195, 35, 261, 102, 208, + /* 480 */ 171, 172, 139, 79, 156, 269, 348, 248, 208, 113, + /* 490 */ 75, 87, 213, 214, 278, 270, 248, 12, 13, 14, + /* 500 */ 15, 16, 241, 160, 291, 270, 178, 179, 260, 181, /* 510 */ 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, - /* 520 */ 192, 193, 194, 195, 55, 290, 47, 12, 13, 14, - /* 530 */ 15, 16, 156, 336, 245, 80, 269, 248, 14, 241, - /* 540 */ 305, 306, 307, 64, 20, 75, 349, 280, 79, 241, - /* 550 */ 353, 82, 317, 296, 178, 179, 241, 181, 182, 183, + /* 520 */ 192, 193, 194, 195, 83, 277, 85, 86, 241, 88, + /* 530 */ 64, 14, 156, 92, 119, 120, 297, 20, 145, 196, + /* 540 */ 197, 198, 199, 200, 201, 202, 203, 204, 205, 241, + /* 550 */ 245, 147, 291, 248, 178, 179, 115, 181, 182, 183, /* 560 */ 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, - /* 570 */ 194, 195, 12, 13, 18, 248, 20, 145, 229, 253, - /* 580 */ 20, 102, 22, 27, 269, 248, 30, 260, 290, 119, - /* 590 */ 120, 276, 113, 336, 269, 241, 33, 260, 290, 308, - /* 600 */ 274, 286, 277, 276, 48, 290, 349, 47, 45, 248, - /* 610 */ 353, 296, 241, 276, 51, 52, 53, 54, 55, 35, - /* 620 */ 224, 260, 307, 332, 64, 310, 311, 312, 313, 314, - /* 630 */ 315, 248, 317, 79, 20, 156, 22, 276, 248, 21, - /* 640 */ 80, 87, 79, 260, 290, 82, 241, 182, 182, 1, - /* 650 */ 2, 336, 34, 241, 255, 256, 37, 178, 179, 276, - /* 660 */ 145, 290, 102, 49, 349, 241, 276, 83, 353, 85, - /* 670 */ 86, 20, 88, 113, 118, 241, 92, 121, 122, 123, + /* 570 */ 194, 195, 12, 13, 18, 336, 20, 269, 291, 248, + /* 580 */ 20, 261, 22, 27, 139, 277, 30, 270, 349, 269, + /* 590 */ 92, 260, 353, 245, 92, 287, 248, 14, 278, 291, + /* 600 */ 20, 3, 22, 20, 48, 160, 251, 47, 277, 111, + /* 610 */ 112, 241, 114, 115, 116, 307, 241, 115, 310, 311, + /* 620 */ 312, 313, 314, 315, 64, 317, 241, 272, 320, 49, + /* 630 */ 145, 20, 324, 325, 326, 0, 14, 15, 16, 270, + /* 640 */ 80, 196, 93, 94, 95, 96, 97, 98, 99, 100, + /* 650 */ 101, 102, 103, 345, 105, 106, 107, 108, 109, 110, + /* 660 */ 270, 291, 102, 241, 241, 241, 291, 241, 241, 241, + /* 670 */ 241, 241, 241, 113, 118, 241, 291, 121, 122, 123, /* 680 */ 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, - /* 690 */ 134, 135, 136, 137, 138, 290, 20, 248, 248, 115, - /* 700 */ 248, 147, 290, 140, 314, 142, 0, 144, 248, 260, - /* 710 */ 260, 20, 260, 139, 290, 241, 156, 241, 241, 329, - /* 720 */ 330, 331, 41, 333, 290, 276, 276, 164, 276, 81, - /* 730 */ 241, 241, 241, 241, 160, 41, 276, 241, 178, 179, - /* 740 */ 20, 181, 182, 183, 184, 185, 186, 187, 188, 189, - /* 750 */ 190, 191, 192, 193, 194, 195, 248, 262, 269, 248, - /* 760 */ 265, 241, 241, 241, 290, 276, 290, 290, 260, 276, - /* 770 */ 196, 260, 255, 256, 314, 286, 283, 42, 43, 290, - /* 780 */ 290, 290, 290, 241, 276, 296, 290, 276, 286, 329, - /* 790 */ 330, 331, 0, 333, 292, 257, 307, 259, 92, 310, - /* 800 */ 311, 312, 313, 314, 315, 241, 317, 92, 41, 320, - /* 810 */ 290, 290, 290, 324, 325, 285, 286, 111, 112, 0, - /* 820 */ 114, 115, 116, 206, 207, 336, 207, 64, 84, 0, - /* 830 */ 115, 87, 290, 269, 167, 168, 145, 45, 349, 270, - /* 840 */ 276, 22, 353, 84, 18, 0, 87, 270, 81, 23, - /* 850 */ 286, 22, 84, 84, 290, 87, 87, 41, 182, 41, - /* 860 */ 296, 35, 36, 145, 146, 39, 241, 22, 41, 1, - /* 870 */ 2, 307, 193, 194, 310, 311, 312, 313, 314, 315, - /* 880 */ 4, 317, 56, 41, 320, 41, 270, 41, 324, 325, - /* 890 */ 41, 270, 80, 41, 269, 19, 270, 81, 299, 81, - /* 900 */ 336, 276, 90, 242, 41, 47, 80, 226, 81, 33, - /* 910 */ 258, 286, 41, 349, 47, 290, 0, 353, 356, 241, - /* 920 */ 41, 45, 228, 81, 341, 81, 50, 81, 347, 269, - /* 930 */ 81, 55, 307, 81, 303, 310, 311, 312, 313, 314, - /* 940 */ 315, 41, 317, 117, 81, 320, 41, 269, 247, 324, - /* 950 */ 325, 326, 81, 249, 276, 79, 279, 334, 82, 309, - /* 960 */ 81, 350, 350, 338, 286, 41, 337, 20, 290, 344, - /* 970 */ 345, 113, 241, 41, 148, 149, 150, 350, 41, 153, - /* 980 */ 113, 81, 248, 45, 158, 307, 81, 47, 310, 311, - /* 990 */ 312, 313, 314, 315, 304, 317, 170, 47, 255, 173, - /* 1000 */ 269, 175, 176, 177, 64, 81, 297, 276, 92, 154, - /* 1010 */ 248, 40, 248, 81, 284, 282, 139, 286, 81, 282, - /* 1020 */ 248, 290, 20, 243, 243, 20, 348, 111, 112, 301, - /* 1030 */ 114, 115, 116, 286, 208, 253, 253, 20, 307, 0, - /* 1040 */ 294, 310, 311, 312, 313, 314, 315, 241, 317, 253, - /* 1050 */ 276, 320, 20, 287, 253, 324, 325, 326, 253, 248, - /* 1060 */ 21, 253, 269, 24, 25, 26, 27, 28, 29, 30, - /* 1070 */ 31, 32, 269, 243, 243, 269, 345, 64, 269, 269, - /* 1080 */ 241, 248, 276, 269, 269, 290, 269, 301, 251, 269, - /* 1090 */ 269, 269, 286, 269, 163, 20, 290, 300, 251, 286, - /* 1100 */ 309, 294, 251, 216, 276, 251, 346, 215, 269, 223, - /* 1110 */ 287, 346, 222, 307, 343, 276, 310, 311, 312, 313, - /* 1120 */ 314, 315, 290, 317, 290, 286, 320, 291, 290, 290, - /* 1130 */ 324, 325, 326, 211, 291, 210, 342, 207, 340, 339, - /* 1140 */ 276, 335, 241, 20, 40, 227, 307, 230, 308, 310, - /* 1150 */ 311, 312, 313, 314, 315, 351, 317, 291, 225, 320, - /* 1160 */ 80, 291, 327, 324, 325, 326, 142, 276, 290, 265, - /* 1170 */ 269, 290, 290, 288, 335, 287, 80, 276, 251, 323, - /* 1180 */ 251, 272, 357, 276, 248, 259, 251, 286, 243, 0, - /* 1190 */ 295, 290, 298, 352, 252, 302, 241, 263, 239, 0, - /* 1200 */ 352, 351, 263, 72, 351, 241, 263, 0, 307, 352, - /* 1210 */ 47, 310, 311, 312, 313, 314, 315, 174, 317, 47, - /* 1220 */ 47, 320, 47, 174, 269, 324, 325, 326, 0, 47, - /* 1230 */ 47, 276, 0, 269, 174, 47, 335, 0, 47, 0, - /* 1240 */ 276, 286, 47, 0, 80, 290, 160, 159, 113, 156, - /* 1250 */ 286, 0, 0, 152, 290, 0, 151, 0, 44, 241, - /* 1260 */ 296, 0, 307, 0, 0, 310, 311, 312, 313, 314, - /* 1270 */ 315, 307, 317, 0, 310, 311, 312, 313, 314, 315, - /* 1280 */ 0, 317, 0, 0, 0, 0, 0, 269, 0, 0, - /* 1290 */ 0, 0, 0, 0, 276, 0, 0, 0, 40, 241, - /* 1300 */ 336, 0, 0, 0, 286, 0, 0, 0, 290, 22, - /* 1310 */ 355, 0, 0, 349, 0, 0, 0, 353, 241, 40, - /* 1320 */ 0, 44, 41, 45, 14, 307, 0, 269, 310, 311, - /* 1330 */ 312, 313, 314, 315, 276, 317, 37, 14, 320, 45, - /* 1340 */ 0, 37, 324, 325, 286, 0, 269, 0, 290, 0, - /* 1350 */ 0, 38, 44, 276, 37, 0, 0, 59, 37, 0, - /* 1360 */ 0, 37, 37, 286, 37, 307, 47, 290, 310, 311, - /* 1370 */ 312, 313, 314, 315, 241, 317, 47, 47, 320, 45, - /* 1380 */ 47, 45, 324, 325, 307, 0, 0, 310, 311, 312, - /* 1390 */ 313, 314, 315, 316, 317, 318, 319, 0, 0, 47, - /* 1400 */ 22, 0, 269, 19, 47, 47, 41, 89, 47, 276, - /* 1410 */ 47, 41, 0, 47, 47, 87, 22, 33, 47, 286, - /* 1420 */ 48, 22, 0, 290, 0, 22, 47, 0, 22, 45, - /* 1430 */ 0, 22, 0, 241, 20, 51, 52, 53, 54, 55, - /* 1440 */ 307, 0, 47, 310, 311, 312, 313, 314, 315, 0, - /* 1450 */ 317, 22, 0, 320, 0, 80, 37, 41, 325, 41, - /* 1460 */ 212, 269, 81, 79, 41, 44, 82, 145, 276, 81, - /* 1470 */ 80, 80, 80, 206, 81, 41, 145, 142, 286, 44, - /* 1480 */ 80, 140, 290, 41, 212, 293, 81, 241, 81, 44, - /* 1490 */ 44, 12, 13, 161, 41, 47, 81, 41, 81, 307, - /* 1500 */ 116, 22, 310, 311, 312, 313, 314, 315, 47, 317, - /* 1510 */ 47, 47, 212, 47, 47, 269, 2, 241, 41, 81, - /* 1520 */ 178, 81, 276, 80, 44, 141, 47, 80, 144, 44, - /* 1530 */ 80, 22, 286, 180, 0, 80, 290, 37, 81, 81, - /* 1540 */ 80, 90, 80, 64, 22, 269, 162, 143, 164, 80, - /* 1550 */ 241, 140, 276, 307, 80, 44, 310, 311, 312, 313, - /* 1560 */ 314, 315, 286, 317, 44, 319, 290, 80, 80, 293, - /* 1570 */ 81, 80, 91, 81, 47, 47, 80, 47, 269, 81, - /* 1580 */ 241, 102, 80, 307, 81, 276, 310, 311, 312, 313, - /* 1590 */ 314, 315, 113, 317, 47, 286, 81, 80, 47, 290, - /* 1600 */ 81, 80, 293, 47, 80, 22, 47, 113, 269, 104, - /* 1610 */ 241, 104, 248, 80, 104, 276, 307, 104, 92, 310, - /* 1620 */ 311, 312, 313, 314, 315, 286, 317, 47, 80, 290, - /* 1630 */ 80, 22, 59, 58, 64, 156, 78, 41, 269, 47, - /* 1640 */ 276, 47, 47, 22, 47, 276, 307, 47, 47, 310, - /* 1650 */ 311, 312, 313, 314, 315, 286, 317, 178, 47, 290, - /* 1660 */ 296, 241, 64, 47, 47, 47, 47, 47, 189, 190, - /* 1670 */ 191, 47, 47, 0, 47, 45, 307, 241, 314, 310, - /* 1680 */ 311, 312, 313, 314, 315, 37, 317, 0, 47, 269, - /* 1690 */ 45, 37, 0, 329, 330, 331, 276, 333, 47, 37, - /* 1700 */ 336, 0, 45, 45, 37, 269, 286, 47, 0, 46, - /* 1710 */ 290, 47, 276, 349, 0, 0, 22, 353, 21, 358, - /* 1720 */ 22, 22, 286, 21, 241, 20, 290, 307, 358, 358, - /* 1730 */ 310, 311, 312, 313, 314, 315, 358, 317, 358, 358, - /* 1740 */ 358, 358, 241, 307, 358, 358, 310, 311, 312, 313, - /* 1750 */ 314, 315, 269, 317, 358, 358, 358, 241, 358, 276, - /* 1760 */ 358, 358, 358, 358, 358, 358, 358, 358, 358, 286, - /* 1770 */ 269, 358, 358, 290, 358, 358, 358, 276, 358, 358, - /* 1780 */ 358, 358, 358, 358, 358, 269, 358, 286, 358, 358, - /* 1790 */ 307, 290, 276, 310, 311, 312, 313, 314, 315, 358, - /* 1800 */ 317, 358, 286, 358, 241, 358, 290, 358, 307, 358, - /* 1810 */ 358, 310, 311, 312, 313, 314, 315, 358, 317, 358, - /* 1820 */ 241, 358, 358, 307, 358, 358, 310, 311, 312, 313, - /* 1830 */ 314, 315, 269, 317, 358, 358, 358, 358, 358, 276, - /* 1840 */ 358, 358, 358, 358, 358, 358, 358, 358, 269, 286, - /* 1850 */ 358, 358, 358, 290, 358, 276, 358, 358, 358, 358, - /* 1860 */ 358, 358, 358, 358, 358, 286, 358, 241, 358, 290, - /* 1870 */ 307, 358, 358, 310, 311, 312, 313, 314, 315, 358, - /* 1880 */ 317, 358, 358, 358, 358, 358, 307, 358, 358, 310, - /* 1890 */ 311, 312, 313, 314, 315, 269, 317, 241, 358, 358, - /* 1900 */ 358, 358, 276, 358, 358, 358, 358, 358, 358, 358, - /* 1910 */ 358, 358, 286, 358, 358, 358, 290, 358, 358, 358, - /* 1920 */ 358, 358, 358, 358, 358, 269, 358, 358, 358, 358, - /* 1930 */ 358, 358, 276, 307, 358, 358, 310, 311, 312, 313, - /* 1940 */ 314, 315, 286, 317, 358, 358, 290, 241, 358, 358, - /* 1950 */ 358, 358, 358, 358, 358, 358, 358, 358, 358, 358, - /* 1960 */ 358, 358, 358, 307, 358, 358, 310, 311, 312, 313, - /* 1970 */ 314, 315, 358, 317, 358, 269, 358, 241, 358, 358, - /* 1980 */ 358, 358, 276, 358, 358, 358, 358, 358, 358, 358, - /* 1990 */ 358, 358, 286, 358, 358, 358, 290, 358, 358, 358, - /* 2000 */ 358, 358, 358, 358, 358, 269, 358, 358, 358, 358, - /* 2010 */ 358, 358, 276, 307, 358, 358, 310, 311, 312, 313, - /* 2020 */ 314, 315, 286, 317, 358, 358, 290, 241, 358, 358, - /* 2030 */ 358, 358, 358, 358, 358, 358, 241, 358, 358, 358, - /* 2040 */ 358, 358, 358, 307, 358, 358, 310, 311, 312, 313, - /* 2050 */ 314, 315, 358, 317, 358, 269, 358, 358, 358, 358, - /* 2060 */ 358, 358, 276, 358, 269, 358, 358, 358, 358, 358, - /* 2070 */ 358, 276, 286, 358, 358, 358, 290, 358, 358, 358, - /* 2080 */ 358, 286, 358, 358, 358, 290, 358, 358, 358, 358, - /* 2090 */ 241, 358, 358, 307, 358, 358, 310, 311, 312, 313, - /* 2100 */ 314, 315, 307, 317, 358, 310, 311, 312, 313, 314, - /* 2110 */ 315, 358, 317, 358, 358, 358, 358, 358, 269, 358, - /* 2120 */ 358, 358, 358, 358, 358, 276, 358, 358, 358, 358, - /* 2130 */ 358, 358, 358, 358, 358, 286, 358, 358, 358, 290, - /* 2140 */ 358, 358, 358, 358, 358, 358, 358, 358, 358, 358, - /* 2150 */ 358, 358, 358, 358, 358, 358, 307, 358, 358, 310, - /* 2160 */ 311, 312, 313, 314, 315, 358, 317, + /* 690 */ 134, 135, 136, 137, 138, 60, 61, 62, 63, 182, + /* 700 */ 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + /* 710 */ 75, 76, 77, 291, 291, 291, 156, 291, 291, 291, + /* 720 */ 291, 291, 291, 241, 37, 291, 269, 241, 255, 256, + /* 730 */ 241, 255, 256, 286, 287, 278, 42, 43, 178, 179, + /* 740 */ 308, 181, 182, 183, 184, 185, 186, 187, 188, 189, + /* 750 */ 190, 191, 192, 193, 194, 195, 145, 18, 269, 248, + /* 760 */ 4, 241, 23, 308, 332, 182, 277, 47, 22, 248, + /* 770 */ 20, 260, 0, 291, 35, 36, 287, 291, 39, 287, + /* 780 */ 291, 260, 206, 207, 64, 293, 297, 332, 277, 269, + /* 790 */ 167, 168, 41, 47, 248, 56, 307, 277, 277, 310, + /* 800 */ 311, 312, 313, 314, 315, 262, 317, 287, 265, 320, + /* 810 */ 64, 291, 248, 324, 325, 41, 300, 45, 84, 80, + /* 820 */ 47, 87, 224, 277, 260, 336, 84, 307, 41, 87, + /* 830 */ 310, 311, 312, 313, 314, 315, 44, 317, 349, 248, + /* 840 */ 320, 277, 353, 297, 324, 325, 326, 84, 102, 84, + /* 850 */ 87, 260, 87, 21, 58, 81, 117, 241, 338, 113, + /* 860 */ 314, 248, 248, 270, 344, 345, 34, 47, 277, 145, + /* 870 */ 146, 41, 80, 260, 260, 329, 330, 331, 0, 333, + /* 880 */ 1, 2, 336, 0, 64, 269, 113, 148, 149, 150, + /* 890 */ 277, 277, 153, 277, 207, 349, 242, 158, 41, 353, + /* 900 */ 22, 47, 156, 287, 41, 22, 41, 291, 258, 170, + /* 910 */ 41, 81, 173, 41, 175, 176, 177, 41, 193, 194, + /* 920 */ 241, 0, 41, 307, 178, 179, 310, 311, 312, 313, + /* 930 */ 314, 315, 182, 317, 356, 41, 320, 347, 81, 80, + /* 940 */ 324, 325, 326, 304, 81, 41, 81, 208, 269, 90, + /* 950 */ 81, 335, 269, 81, 247, 41, 277, 81, 41, 341, + /* 960 */ 249, 33, 81, 334, 280, 209, 287, 113, 309, 350, + /* 970 */ 291, 41, 241, 45, 178, 81, 350, 226, 350, 51, + /* 980 */ 52, 53, 54, 55, 41, 81, 307, 41, 41, 310, + /* 990 */ 311, 312, 313, 314, 315, 81, 317, 0, 81, 320, + /* 1000 */ 269, 337, 20, 324, 325, 326, 248, 79, 277, 45, + /* 1010 */ 82, 81, 47, 92, 335, 228, 255, 305, 287, 298, + /* 1020 */ 154, 40, 291, 248, 81, 248, 285, 81, 81, 139, + /* 1030 */ 283, 241, 111, 112, 248, 114, 115, 116, 307, 20, + /* 1040 */ 283, 310, 311, 312, 313, 314, 315, 4, 317, 243, + /* 1050 */ 243, 320, 20, 253, 302, 324, 325, 326, 287, 269, + /* 1060 */ 20, 253, 19, 295, 253, 277, 335, 277, 140, 20, + /* 1070 */ 142, 288, 144, 248, 253, 253, 33, 287, 243, 253, + /* 1080 */ 64, 291, 241, 269, 269, 269, 269, 297, 45, 92, + /* 1090 */ 248, 302, 164, 50, 243, 291, 269, 307, 55, 241, + /* 1100 */ 310, 311, 312, 313, 314, 315, 269, 317, 111, 112, + /* 1110 */ 269, 114, 115, 116, 269, 269, 269, 269, 277, 269, + /* 1120 */ 163, 251, 79, 287, 301, 82, 336, 269, 287, 295, + /* 1130 */ 277, 251, 291, 251, 20, 277, 251, 288, 297, 349, + /* 1140 */ 309, 216, 215, 353, 346, 287, 223, 292, 307, 291, + /* 1150 */ 211, 310, 311, 312, 313, 314, 315, 241, 317, 291, + /* 1160 */ 222, 210, 343, 292, 346, 307, 291, 339, 310, 311, + /* 1170 */ 312, 313, 314, 315, 207, 317, 277, 336, 320, 291, + /* 1180 */ 342, 20, 324, 325, 0, 269, 40, 327, 227, 241, + /* 1190 */ 349, 352, 340, 277, 353, 308, 230, 225, 80, 292, + /* 1200 */ 357, 291, 291, 287, 292, 142, 291, 291, 24, 25, + /* 1210 */ 26, 27, 28, 29, 30, 31, 32, 269, 351, 248, + /* 1220 */ 323, 289, 277, 307, 351, 277, 310, 311, 312, 313, + /* 1230 */ 314, 315, 288, 317, 251, 287, 320, 251, 352, 291, + /* 1240 */ 324, 325, 241, 352, 351, 265, 277, 80, 277, 273, + /* 1250 */ 259, 248, 243, 251, 299, 307, 303, 263, 310, 311, + /* 1260 */ 312, 313, 314, 315, 316, 317, 318, 319, 297, 296, + /* 1270 */ 269, 263, 252, 263, 239, 0, 0, 72, 277, 0, + /* 1280 */ 47, 174, 47, 47, 47, 314, 174, 0, 287, 47, + /* 1290 */ 47, 174, 291, 0, 47, 0, 47, 0, 47, 241, + /* 1300 */ 329, 330, 331, 0, 333, 80, 159, 336, 307, 160, + /* 1310 */ 113, 310, 311, 312, 313, 314, 315, 0, 317, 156, + /* 1320 */ 349, 320, 0, 152, 353, 151, 325, 269, 19, 0, + /* 1330 */ 0, 0, 44, 0, 0, 277, 0, 0, 0, 0, + /* 1340 */ 0, 0, 33, 22, 40, 287, 241, 0, 0, 291, + /* 1350 */ 0, 0, 294, 0, 45, 0, 0, 0, 0, 0, + /* 1360 */ 51, 52, 53, 54, 55, 307, 0, 0, 310, 311, + /* 1370 */ 312, 313, 314, 315, 269, 317, 0, 0, 0, 0, + /* 1380 */ 0, 0, 277, 0, 0, 241, 0, 41, 79, 40, + /* 1390 */ 0, 82, 287, 0, 0, 0, 291, 37, 44, 14, + /* 1400 */ 38, 14, 37, 0, 0, 44, 37, 0, 0, 45, + /* 1410 */ 47, 59, 307, 269, 0, 310, 311, 312, 313, 314, + /* 1420 */ 315, 277, 317, 0, 0, 116, 37, 0, 37, 47, + /* 1430 */ 0, 287, 87, 45, 37, 291, 241, 37, 294, 47, + /* 1440 */ 0, 45, 0, 0, 45, 47, 0, 22, 47, 47, + /* 1450 */ 141, 307, 47, 144, 310, 311, 312, 313, 314, 315, + /* 1460 */ 355, 317, 89, 47, 269, 241, 47, 41, 0, 41, + /* 1470 */ 22, 162, 277, 164, 22, 0, 47, 22, 47, 0, + /* 1480 */ 47, 47, 287, 48, 22, 0, 291, 22, 0, 20, + /* 1490 */ 0, 47, 22, 269, 0, 0, 161, 241, 0, 41, + /* 1500 */ 80, 277, 307, 37, 41, 310, 311, 312, 313, 314, + /* 1510 */ 315, 287, 317, 145, 319, 291, 145, 142, 294, 212, + /* 1520 */ 81, 41, 81, 41, 206, 269, 81, 80, 44, 80, + /* 1530 */ 80, 307, 41, 277, 310, 311, 312, 313, 314, 315, + /* 1540 */ 44, 317, 81, 287, 80, 44, 140, 291, 81, 41, + /* 1550 */ 294, 44, 81, 241, 212, 41, 212, 81, 47, 2, + /* 1560 */ 47, 41, 47, 307, 47, 47, 310, 311, 312, 313, + /* 1570 */ 314, 315, 47, 317, 81, 44, 44, 80, 22, 143, + /* 1580 */ 81, 269, 80, 80, 241, 80, 80, 178, 0, 277, + /* 1590 */ 180, 81, 37, 140, 90, 44, 44, 81, 22, 287, + /* 1600 */ 81, 80, 80, 291, 80, 47, 80, 80, 80, 47, + /* 1610 */ 81, 81, 269, 241, 91, 80, 47, 80, 47, 307, + /* 1620 */ 277, 81, 310, 311, 312, 313, 314, 315, 80, 317, + /* 1630 */ 287, 81, 47, 80, 291, 81, 47, 104, 104, 80, + /* 1640 */ 104, 269, 104, 22, 80, 80, 47, 80, 47, 277, + /* 1650 */ 307, 22, 92, 310, 311, 312, 313, 314, 315, 287, + /* 1660 */ 317, 59, 241, 291, 58, 78, 41, 64, 12, 13, + /* 1670 */ 47, 47, 22, 241, 113, 47, 64, 47, 22, 307, + /* 1680 */ 47, 47, 310, 311, 312, 313, 314, 315, 47, 317, + /* 1690 */ 269, 47, 47, 47, 47, 47, 47, 0, 277, 47, + /* 1700 */ 47, 269, 45, 47, 37, 0, 47, 45, 287, 277, + /* 1710 */ 37, 0, 291, 47, 45, 37, 0, 47, 37, 287, + /* 1720 */ 64, 45, 0, 291, 241, 0, 47, 0, 307, 46, + /* 1730 */ 22, 310, 311, 312, 313, 314, 315, 22, 317, 307, + /* 1740 */ 21, 20, 310, 311, 312, 313, 314, 315, 22, 317, + /* 1750 */ 21, 358, 269, 241, 358, 358, 358, 358, 102, 358, + /* 1760 */ 277, 358, 358, 358, 358, 358, 358, 358, 358, 113, + /* 1770 */ 287, 358, 358, 358, 291, 358, 358, 358, 358, 358, + /* 1780 */ 358, 269, 358, 358, 358, 241, 358, 358, 358, 277, + /* 1790 */ 307, 358, 358, 310, 311, 312, 313, 314, 315, 287, + /* 1800 */ 317, 358, 358, 291, 358, 358, 358, 358, 358, 358, + /* 1810 */ 358, 358, 156, 269, 358, 358, 358, 358, 358, 307, + /* 1820 */ 358, 277, 310, 311, 312, 313, 314, 315, 358, 317, + /* 1830 */ 358, 287, 358, 358, 178, 291, 358, 358, 358, 358, + /* 1840 */ 358, 241, 358, 358, 358, 189, 190, 191, 358, 358, + /* 1850 */ 358, 307, 358, 358, 310, 311, 312, 313, 314, 315, + /* 1860 */ 358, 317, 358, 358, 358, 358, 358, 358, 358, 269, + /* 1870 */ 358, 358, 241, 358, 358, 358, 358, 277, 358, 358, + /* 1880 */ 358, 358, 358, 358, 358, 358, 358, 287, 358, 358, + /* 1890 */ 358, 291, 358, 358, 358, 358, 358, 358, 358, 358, + /* 1900 */ 269, 241, 358, 358, 358, 358, 358, 307, 277, 358, + /* 1910 */ 310, 311, 312, 313, 314, 315, 358, 317, 287, 358, + /* 1920 */ 358, 358, 291, 358, 358, 358, 358, 358, 358, 269, + /* 1930 */ 358, 358, 241, 358, 358, 358, 358, 277, 307, 358, + /* 1940 */ 358, 310, 311, 312, 313, 314, 315, 287, 317, 358, + /* 1950 */ 358, 291, 358, 358, 358, 358, 358, 358, 358, 358, + /* 1960 */ 269, 241, 358, 358, 358, 358, 358, 307, 277, 358, + /* 1970 */ 310, 311, 312, 313, 314, 315, 358, 317, 287, 358, + /* 1980 */ 358, 358, 291, 358, 358, 358, 358, 358, 358, 269, + /* 1990 */ 358, 358, 358, 358, 358, 358, 358, 277, 307, 358, + /* 2000 */ 358, 310, 311, 312, 313, 314, 315, 287, 317, 358, + /* 2010 */ 358, 291, 241, 358, 358, 358, 358, 358, 358, 358, + /* 2020 */ 358, 358, 358, 241, 358, 358, 358, 307, 358, 358, + /* 2030 */ 310, 311, 312, 313, 314, 315, 358, 317, 358, 358, + /* 2040 */ 269, 358, 358, 358, 358, 358, 358, 358, 277, 358, + /* 2050 */ 358, 269, 358, 358, 358, 358, 358, 358, 287, 277, + /* 2060 */ 358, 358, 291, 358, 358, 358, 358, 358, 358, 287, + /* 2070 */ 358, 358, 358, 291, 358, 358, 358, 241, 307, 358, + /* 2080 */ 358, 310, 311, 312, 313, 314, 315, 358, 317, 307, + /* 2090 */ 358, 358, 310, 311, 312, 313, 314, 315, 358, 317, + /* 2100 */ 358, 358, 358, 358, 358, 269, 358, 358, 358, 358, + /* 2110 */ 358, 358, 358, 277, 358, 358, 358, 358, 358, 358, + /* 2120 */ 358, 358, 358, 287, 358, 358, 358, 291, 358, 358, + /* 2130 */ 358, 358, 358, 358, 358, 358, 358, 358, 358, 358, + /* 2140 */ 358, 358, 358, 307, 358, 358, 310, 311, 312, 313, + /* 2150 */ 314, 315, 358, 317, }; #define YY_SHIFT_COUNT (604) #define YY_SHIFT_MIN (0) -#define YY_SHIFT_MAX (1715) +#define YY_SHIFT_MAX (1729) static const unsigned short int yy_shift_ofst[] = { - /* 0 */ 826, 0, 0, 48, 96, 96, 96, 96, 280, 280, + /* 0 */ 739, 0, 0, 48, 96, 96, 96, 96, 280, 280, /* 10 */ 96, 96, 328, 376, 560, 376, 376, 376, 376, 376, /* 20 */ 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, - /* 30 */ 376, 376, 376, 376, 376, 376, 376, 376, 95, 95, - /* 40 */ 375, 375, 375, 1479, 1479, 1479, 100, 50, 171, 45, - /* 50 */ 45, 342, 342, 246, 171, 171, 45, 45, 45, 45, - /* 60 */ 45, 45, 17, 45, 201, 323, 651, 201, 45, 45, - /* 70 */ 201, 45, 201, 201, 651, 201, 45, 324, 556, 63, - /* 80 */ 14, 14, 13, 479, 422, 479, 479, 479, 479, 479, - /* 90 */ 479, 479, 479, 479, 479, 479, 479, 479, 479, 479, - /* 100 */ 479, 479, 479, 479, 584, 614, 524, 524, 301, 281, - /* 110 */ 432, 432, 432, 447, 281, 720, 651, 201, 201, 651, - /* 120 */ 348, 763, 319, 319, 319, 319, 319, 319, 319, 1384, - /* 130 */ 1039, 377, 349, 28, 104, 230, 465, 466, 735, 102, - /* 140 */ 715, 691, 617, 619, 617, 396, 396, 396, 268, 676, - /* 150 */ 947, 938, 950, 855, 947, 947, 971, 877, 877, 947, - /* 160 */ 1002, 1002, 1005, 17, 651, 17, 1017, 17, 720, 1032, - /* 170 */ 17, 17, 947, 17, 1002, 201, 201, 201, 201, 201, - /* 180 */ 201, 201, 201, 201, 201, 201, 947, 1002, 1013, 1005, - /* 190 */ 324, 931, 651, 324, 1017, 324, 720, 1032, 324, 1075, - /* 200 */ 887, 892, 1013, 887, 892, 1013, 1013, 201, 886, 890, - /* 210 */ 922, 925, 930, 720, 1123, 1104, 918, 933, 917, 918, - /* 220 */ 933, 918, 933, 1080, 892, 1013, 1013, 892, 1013, 1024, - /* 230 */ 720, 1032, 324, 348, 324, 720, 1096, 763, 947, 324, - /* 240 */ 1002, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 244, - /* 250 */ 563, 141, 876, 706, 916, 241, 84, 355, 515, 419, - /* 260 */ 85, 282, 282, 282, 282, 282, 282, 282, 282, 239, - /* 270 */ 469, 470, 554, 648, 574, 36, 36, 36, 36, 792, - /* 280 */ 767, 744, 759, 768, 769, 819, 829, 845, 618, 667, - /* 290 */ 718, 816, 818, 827, 868, 679, 681, 694, 842, 145, - /* 300 */ 844, 455, 846, 849, 852, 863, 871, 858, 867, 879, - /* 310 */ 900, 905, 924, 932, 937, 812, 940, 1189, 1199, 1131, - /* 320 */ 1207, 1163, 1043, 1172, 1173, 1175, 1049, 1228, 1182, 1183, - /* 330 */ 1060, 1232, 1188, 1237, 1191, 1239, 1195, 1243, 1164, 1086, - /* 340 */ 1088, 1135, 1093, 1251, 1252, 1101, 1105, 1255, 1257, 1214, - /* 350 */ 1261, 1263, 1264, 1273, 1280, 1282, 1283, 1284, 1285, 1286, - /* 360 */ 1288, 1289, 1290, 1291, 1292, 1293, 1295, 1296, 1258, 1297, - /* 370 */ 1301, 1302, 1303, 1305, 1306, 1287, 1307, 1311, 1312, 1314, - /* 380 */ 1315, 1316, 1279, 1299, 1281, 1310, 1277, 1323, 1308, 1320, - /* 390 */ 1313, 1304, 1326, 1345, 1347, 1349, 1317, 1350, 1298, 1355, - /* 400 */ 1356, 1319, 1278, 1321, 1359, 1329, 1294, 1324, 1340, 1330, - /* 410 */ 1334, 1325, 1360, 1333, 1336, 1327, 1385, 1386, 1397, 1398, - /* 420 */ 1318, 1328, 1352, 1378, 1401, 1357, 1358, 1361, 1363, 1365, - /* 430 */ 1370, 1366, 1367, 1371, 1412, 1394, 1424, 1399, 1372, 1422, - /* 440 */ 1403, 1379, 1427, 1406, 1430, 1409, 1414, 1432, 1322, 1395, - /* 450 */ 1441, 1332, 1429, 1331, 1335, 1449, 1452, 1454, 1375, 1419, - /* 460 */ 1341, 1416, 1418, 1248, 1381, 1423, 1388, 1390, 1391, 1392, - /* 470 */ 1393, 1434, 1421, 1435, 1400, 1442, 1272, 1405, 1407, 1445, - /* 480 */ 1267, 1453, 1446, 1415, 1456, 1300, 1417, 1448, 1461, 1463, - /* 490 */ 1464, 1466, 1467, 1417, 1514, 1342, 1477, 1438, 1443, 1440, - /* 500 */ 1480, 1447, 1450, 1485, 1509, 1353, 1455, 1457, 1458, 1460, - /* 510 */ 1462, 1404, 1469, 1534, 1500, 1411, 1474, 1451, 1511, 1520, - /* 520 */ 1487, 1489, 1488, 1522, 1491, 1481, 1492, 1527, 1528, 1496, - /* 530 */ 1498, 1530, 1502, 1503, 1547, 1517, 1515, 1551, 1521, 1519, - /* 540 */ 1556, 1524, 1505, 1507, 1510, 1513, 1583, 1526, 1533, 1548, - /* 550 */ 1559, 1550, 1494, 1580, 1609, 1573, 1575, 1570, 1558, 1596, - /* 560 */ 1592, 1594, 1595, 1597, 1600, 1621, 1601, 1611, 1598, 1365, - /* 570 */ 1616, 1370, 1617, 1618, 1619, 1620, 1624, 1625, 1673, 1627, - /* 580 */ 1630, 1648, 1687, 1641, 1645, 1654, 1692, 1651, 1657, 1662, - /* 590 */ 1701, 1660, 1658, 1667, 1708, 1664, 1663, 1714, 1715, 1694, - /* 600 */ 1697, 1698, 1699, 1702, 1705, + /* 30 */ 376, 376, 376, 376, 376, 376, 376, 376, 37, 37, + /* 40 */ 68, 68, 68, 1656, 1656, 1656, 1656, 64, 271, 179, + /* 50 */ 18, 18, 13, 13, 123, 179, 179, 18, 18, 18, + /* 60 */ 18, 18, 18, 35, 18, 182, 190, 314, 182, 18, + /* 70 */ 18, 182, 18, 182, 182, 314, 182, 18, 334, 556, + /* 80 */ 343, 107, 107, 192, 312, 746, 746, 746, 746, 746, + /* 90 */ 746, 746, 746, 746, 746, 746, 746, 746, 746, 746, + /* 100 */ 746, 746, 746, 746, 441, 580, 110, 110, 206, 720, + /* 110 */ 393, 393, 393, 244, 720, 384, 314, 182, 182, 314, + /* 120 */ 356, 466, 549, 549, 549, 549, 549, 549, 549, 1309, + /* 130 */ 294, 197, 21, 78, 268, 279, 517, 583, 694, 292, + /* 140 */ 502, 611, 576, 687, 576, 598, 598, 598, 756, 750, + /* 150 */ 982, 964, 965, 866, 982, 982, 981, 890, 890, 982, + /* 160 */ 1019, 1019, 1032, 35, 314, 35, 1040, 35, 384, 1049, + /* 170 */ 35, 35, 982, 35, 1019, 182, 182, 182, 182, 182, + /* 180 */ 182, 182, 182, 182, 182, 182, 982, 1019, 1016, 1032, + /* 190 */ 334, 957, 314, 334, 1040, 334, 384, 1049, 334, 1114, + /* 200 */ 925, 927, 1016, 925, 927, 1016, 1016, 182, 923, 938, + /* 210 */ 939, 951, 967, 384, 1161, 1146, 961, 972, 966, 961, + /* 220 */ 972, 961, 972, 1118, 927, 1016, 1016, 927, 1016, 1063, + /* 230 */ 384, 1049, 334, 356, 334, 384, 1167, 466, 982, 334, + /* 240 */ 1019, 2154, 2154, 2154, 2154, 2154, 2154, 2154, 2154, 635, + /* 250 */ 928, 1184, 1043, 921, 997, 119, 14, 30, 485, 126, + /* 260 */ 498, 354, 354, 354, 354, 354, 354, 354, 354, 309, + /* 270 */ 321, 415, 404, 8, 445, 622, 622, 622, 622, 772, + /* 280 */ 774, 734, 742, 763, 765, 435, 878, 883, 832, 623, + /* 290 */ 724, 830, 857, 863, 879, 725, 751, 787, 865, 796, + /* 300 */ 869, 792, 872, 876, 881, 894, 904, 773, 854, 914, + /* 310 */ 917, 930, 943, 946, 947, 859, 820, 1275, 1276, 1205, + /* 320 */ 1279, 1233, 1107, 1235, 1236, 1237, 1112, 1287, 1242, 1243, + /* 330 */ 1117, 1293, 1247, 1295, 1249, 1297, 1251, 1303, 1225, 1149, + /* 340 */ 1147, 1197, 1163, 1317, 1322, 1171, 1174, 1329, 1330, 1288, + /* 350 */ 1331, 1333, 1334, 1336, 1337, 1338, 1339, 1340, 1341, 1347, + /* 360 */ 1348, 1350, 1351, 1353, 1355, 1356, 1357, 1358, 1304, 1359, + /* 370 */ 1366, 1367, 1376, 1377, 1378, 1321, 1379, 1380, 1381, 1383, + /* 380 */ 1384, 1386, 1349, 1360, 1346, 1385, 1354, 1387, 1361, 1390, + /* 390 */ 1362, 1365, 1393, 1394, 1395, 1403, 1369, 1404, 1352, 1407, + /* 400 */ 1408, 1363, 1364, 1389, 1414, 1382, 1388, 1391, 1423, 1392, + /* 410 */ 1396, 1397, 1424, 1398, 1399, 1400, 1427, 1430, 1440, 1442, + /* 420 */ 1373, 1345, 1401, 1425, 1443, 1402, 1405, 1416, 1419, 1426, + /* 430 */ 1428, 1429, 1431, 1433, 1446, 1448, 1468, 1452, 1435, 1475, + /* 440 */ 1455, 1434, 1479, 1462, 1485, 1465, 1469, 1488, 1368, 1444, + /* 450 */ 1490, 1335, 1470, 1371, 1375, 1494, 1495, 1498, 1420, 1466, + /* 460 */ 1406, 1458, 1463, 1307, 1439, 1480, 1441, 1447, 1449, 1450, + /* 470 */ 1445, 1482, 1484, 1496, 1464, 1491, 1342, 1461, 1467, 1501, + /* 480 */ 1318, 1508, 1507, 1471, 1514, 1344, 1476, 1511, 1513, 1515, + /* 490 */ 1517, 1518, 1525, 1476, 1557, 1409, 1520, 1493, 1497, 1499, + /* 500 */ 1531, 1502, 1503, 1532, 1556, 1410, 1505, 1510, 1516, 1506, + /* 510 */ 1521, 1436, 1522, 1588, 1555, 1453, 1524, 1504, 1551, 1552, + /* 520 */ 1526, 1519, 1527, 1576, 1528, 1523, 1529, 1558, 1562, 1535, + /* 530 */ 1530, 1569, 1537, 1540, 1571, 1548, 1550, 1585, 1553, 1554, + /* 540 */ 1589, 1559, 1533, 1534, 1536, 1538, 1621, 1560, 1564, 1565, + /* 550 */ 1599, 1567, 1561, 1601, 1629, 1602, 1606, 1603, 1587, 1625, + /* 560 */ 1623, 1624, 1628, 1630, 1633, 1650, 1634, 1641, 1612, 1426, + /* 570 */ 1644, 1428, 1645, 1646, 1647, 1648, 1649, 1652, 1697, 1653, + /* 580 */ 1657, 1667, 1705, 1659, 1662, 1673, 1711, 1666, 1669, 1678, + /* 590 */ 1716, 1670, 1676, 1681, 1722, 1679, 1683, 1725, 1727, 1708, + /* 600 */ 1719, 1715, 1726, 1729, 1721, }; #define YY_REDUCE_COUNT (248) -#define YY_REDUCE_MIN (-317) -#define YY_REDUCE_MAX (1849) +#define YY_REDUCE_MIN (-335) +#define YY_REDUCE_MAX (1836) static const short yy_reduce_ofst[] = { - /* 0 */ 38, 489, 564, 625, 731, 806, 839, 901, 315, 964, - /* 10 */ 1018, 1058, 1077, -223, 1133, 90, 678, 955, 1192, 1246, - /* 20 */ 1276, 1309, 1339, 1369, 1420, 1436, 1483, 1501, 1516, 1563, - /* 30 */ 1579, 1626, 1656, 1706, 1736, 1786, 1795, 1849, -191, 1364, - /* 40 */ 23, 390, 460, -224, 235, -282, 257, -280, 197, -141, - /* 50 */ -112, -244, -240, -317, -202, -190, -243, 200, 327, 361, - /* 60 */ 383, 449, -152, 450, -221, 60, -64, -144, 337, 452, - /* 70 */ 55, 508, 78, 223, 47, 117, 511, -250, -177, -312, - /* 80 */ -312, -312, -113, 212, -34, 298, 308, 354, 371, 405, - /* 90 */ 412, 424, 434, 474, 476, 477, 490, 491, 492, 496, - /* 100 */ 520, 521, 522, 542, 70, -139, 161, 289, 326, 399, - /* 110 */ -277, 149, 291, -114, 517, 493, 502, 325, 267, 530, - /* 120 */ 495, 538, -259, -255, 569, 577, 616, 621, 626, 599, - /* 130 */ 661, 652, 562, 581, 631, 583, 660, 660, 701, 704, - /* 140 */ 677, 650, 623, 623, 623, 611, 612, 627, 629, 660, - /* 150 */ 734, 690, 743, 709, 762, 764, 730, 733, 737, 772, - /* 160 */ 780, 781, 728, 782, 747, 783, 746, 796, 774, 766, - /* 170 */ 801, 805, 811, 808, 830, 793, 803, 809, 810, 814, - /* 180 */ 815, 817, 820, 821, 822, 824, 833, 831, 795, 786, - /* 190 */ 837, 797, 813, 847, 807, 851, 828, 823, 854, 791, - /* 200 */ 760, 836, 832, 765, 843, 834, 838, 660, 771, 794, - /* 210 */ 798, 800, 623, 864, 840, 835, 841, 804, 825, 848, - /* 220 */ 850, 857, 853, 856, 866, 878, 881, 870, 882, 885, - /* 230 */ 891, 888, 927, 904, 929, 907, 909, 926, 936, 935, - /* 240 */ 945, 894, 893, 895, 934, 939, 943, 942, 959, + /* 0 */ -190, -238, 489, 520, 308, 616, 679, 731, 790, 841, + /* 10 */ 858, 916, 948, -140, 1001, 1058, 138, 1105, 1144, 1195, + /* 20 */ 1224, 1256, 1312, 1343, 1372, 1421, 1432, 1483, 1512, 1544, + /* 30 */ 1600, 1631, 1660, 1691, 1720, 1771, 1782, 1836, 546, 971, + /* 40 */ 88, -1, 113, -266, -181, 46, 103, 239, -286, -85, + /* 50 */ -163, 87, -240, -223, -335, -334, -253, -49, 85, 135, + /* 60 */ 248, 331, 511, -172, 521, -124, -111, 56, 136, 564, + /* 70 */ 591, -21, 613, 216, 130, 152, 320, 614, -173, -177, + /* 80 */ -256, -256, -256, -188, -105, -86, 213, 261, 287, 370, + /* 90 */ 375, 385, 422, 423, 424, 426, 427, 428, 429, 430, + /* 100 */ 431, 434, 482, 486, -100, 4, 305, 348, 63, 473, + /* 110 */ -83, 432, 455, 355, 476, 94, 492, 457, -8, 447, + /* 120 */ 543, -95, -270, 225, 235, 317, 369, 390, 593, 516, + /* 130 */ 654, 650, 578, 590, 639, 618, 683, 683, 707, 711, + /* 140 */ 684, 659, 629, 629, 629, 619, 626, 628, 664, 683, + /* 150 */ 758, 712, 761, 721, 775, 777, 741, 747, 757, 786, + /* 160 */ 806, 807, 752, 800, 771, 808, 768, 811, 788, 783, + /* 170 */ 821, 822, 825, 826, 835, 814, 815, 816, 817, 827, + /* 180 */ 837, 845, 846, 847, 848, 850, 842, 851, 804, 789, + /* 190 */ 870, 823, 836, 880, 834, 882, 853, 849, 885, 831, + /* 200 */ 798, 855, 868, 818, 871, 875, 888, 683, 819, 838, + /* 210 */ 852, 828, 629, 899, 887, 860, 839, 867, 843, 886, + /* 220 */ 873, 891, 893, 897, 907, 910, 911, 912, 915, 932, + /* 230 */ 945, 944, 983, 980, 986, 969, 976, 991, 1003, 1002, + /* 240 */ 1009, 955, 953, 973, 994, 1008, 1010, 1020, 1035, }; static const YYACTIONTYPE yy_default[] = { /* 0 */ 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, @@ -753,9 +751,9 @@ static const YYACTIONTYPE yy_default[] = { /* 30 */ 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, /* 40 */ 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, /* 50 */ 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, - /* 60 */ 1345, 1345, 1414, 1345, 1345, 1345, 1345, 1345, 1345, 1345, - /* 70 */ 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1412, 1552, 1345, - /* 80 */ 1717, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, + /* 60 */ 1345, 1345, 1345, 1414, 1345, 1345, 1345, 1345, 1345, 1345, + /* 70 */ 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1412, 1552, + /* 80 */ 1345, 1717, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, /* 90 */ 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, /* 100 */ 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1414, 1345, /* 110 */ 1728, 1728, 1728, 1412, 1345, 1345, 1345, 1345, 1345, 1345, @@ -1423,42 +1421,42 @@ static const char *const yyTokenName[] = { /* 268 */ "alter_table_options", /* 269 */ "column_name", /* 270 */ "type_name", - /* 271 */ "create_subtable_clause", - /* 272 */ "specific_tags_opt", - /* 273 */ "literal_list", - /* 274 */ "drop_table_clause", - /* 275 */ "col_name_list", - /* 276 */ "table_name", - /* 277 */ "column_def", - /* 278 */ "func_name_list", - /* 279 */ "alter_table_option", - /* 280 */ "col_name", - /* 281 */ "db_name_cond_opt", - /* 282 */ "like_pattern_opt", - /* 283 */ "table_name_cond", - /* 284 */ "from_db_opt", - /* 285 */ "func_name", - /* 286 */ "function_name", - /* 287 */ "index_name", - /* 288 */ "index_options", - /* 289 */ "func_list", - /* 290 */ "duration_literal", - /* 291 */ "sliding_opt", - /* 292 */ "func", - /* 293 */ "expression_list", - /* 294 */ "topic_name", - /* 295 */ "topic_options", - /* 296 */ "query_expression", - /* 297 */ "analyze_opt", - /* 298 */ "explain_options", - /* 299 */ "agg_func_opt", - /* 300 */ "bufsize_opt", - /* 301 */ "stream_name", - /* 302 */ "stream_options", - /* 303 */ "into_opt", - /* 304 */ "dnode_list", - /* 305 */ "signed", - /* 306 */ "signed_literal", + /* 271 */ "signed_literal", + /* 272 */ "create_subtable_clause", + /* 273 */ "specific_tags_opt", + /* 274 */ "literal_list", + /* 275 */ "drop_table_clause", + /* 276 */ "col_name_list", + /* 277 */ "table_name", + /* 278 */ "column_def", + /* 279 */ "func_name_list", + /* 280 */ "alter_table_option", + /* 281 */ "col_name", + /* 282 */ "db_name_cond_opt", + /* 283 */ "like_pattern_opt", + /* 284 */ "table_name_cond", + /* 285 */ "from_db_opt", + /* 286 */ "func_name", + /* 287 */ "function_name", + /* 288 */ "index_name", + /* 289 */ "index_options", + /* 290 */ "func_list", + /* 291 */ "duration_literal", + /* 292 */ "sliding_opt", + /* 293 */ "func", + /* 294 */ "expression_list", + /* 295 */ "topic_name", + /* 296 */ "topic_options", + /* 297 */ "query_expression", + /* 298 */ "analyze_opt", + /* 299 */ "explain_options", + /* 300 */ "agg_func_opt", + /* 301 */ "bufsize_opt", + /* 302 */ "stream_name", + /* 303 */ "stream_options", + /* 304 */ "into_opt", + /* 305 */ "dnode_list", + /* 306 */ "signed", /* 307 */ "literal_func", /* 308 */ "table_alias", /* 309 */ "column_alias", @@ -1638,7 +1636,7 @@ static const char *const yyRuleName[] = { /* 118 */ "alter_table_clause ::= full_table_name DROP TAG column_name", /* 119 */ "alter_table_clause ::= full_table_name MODIFY TAG column_name type_name", /* 120 */ "alter_table_clause ::= full_table_name RENAME TAG column_name column_name", - /* 121 */ "alter_table_clause ::= full_table_name SET TAG column_name NK_EQ literal", + /* 121 */ "alter_table_clause ::= full_table_name SET TAG column_name NK_EQ signed_literal", /* 122 */ "multi_create_clause ::= create_subtable_clause", /* 123 */ "multi_create_clause ::= multi_create_clause create_subtable_clause", /* 124 */ "create_subtable_clause ::= not_exists_opt full_table_name USING full_table_name specific_tags_opt TAGS NK_LP literal_list NK_RP table_options", @@ -2104,26 +2102,26 @@ static void yy_destructor( case 263: /* table_options */ case 267: /* alter_table_clause */ case 268: /* alter_table_options */ - case 271: /* create_subtable_clause */ - case 274: /* drop_table_clause */ - case 277: /* column_def */ - case 280: /* col_name */ - case 281: /* db_name_cond_opt */ - case 282: /* like_pattern_opt */ - case 283: /* table_name_cond */ - case 284: /* from_db_opt */ - case 285: /* func_name */ - case 288: /* index_options */ - case 290: /* duration_literal */ - case 291: /* sliding_opt */ - case 292: /* func */ - case 295: /* topic_options */ - case 296: /* query_expression */ - case 298: /* explain_options */ - case 302: /* stream_options */ - case 303: /* into_opt */ - case 305: /* signed */ - case 306: /* signed_literal */ + case 271: /* signed_literal */ + case 272: /* create_subtable_clause */ + case 275: /* drop_table_clause */ + case 278: /* column_def */ + case 281: /* col_name */ + case 282: /* db_name_cond_opt */ + case 283: /* like_pattern_opt */ + case 284: /* table_name_cond */ + case 285: /* from_db_opt */ + case 286: /* func_name */ + case 289: /* index_options */ + case 291: /* duration_literal */ + case 292: /* sliding_opt */ + case 293: /* func */ + case 296: /* topic_options */ + case 297: /* query_expression */ + case 299: /* explain_options */ + case 303: /* stream_options */ + case 304: /* into_opt */ + case 306: /* signed */ case 307: /* literal_func */ case 310: /* expression */ case 311: /* pseudo_column */ @@ -2161,7 +2159,7 @@ static void yy_destructor( case 239: /* account_options */ case 240: /* alter_account_options */ case 242: /* alter_account_option */ - case 300: /* bufsize_opt */ + case 301: /* bufsize_opt */ { } @@ -2172,11 +2170,11 @@ static void yy_destructor( case 249: /* dnode_endpoint */ case 250: /* dnode_host_name */ case 269: /* column_name */ - case 276: /* table_name */ - case 286: /* function_name */ - case 287: /* index_name */ - case 294: /* topic_name */ - case 301: /* stream_name */ + case 277: /* table_name */ + case 287: /* function_name */ + case 288: /* index_name */ + case 295: /* topic_name */ + case 302: /* stream_name */ case 308: /* table_alias */ case 309: /* column_alias */ case 315: /* star_func */ @@ -2195,8 +2193,8 @@ static void yy_destructor( break; case 251: /* not_exists_opt */ case 253: /* exists_opt */ - case 297: /* analyze_opt */ - case 299: /* agg_func_opt */ + case 298: /* analyze_opt */ + case 300: /* agg_func_opt */ case 337: /* set_quantifier_opt */ { @@ -2210,13 +2208,13 @@ static void yy_destructor( case 264: /* multi_create_clause */ case 265: /* tags_def */ case 266: /* multi_drop_clause */ - case 272: /* specific_tags_opt */ - case 273: /* literal_list */ - case 275: /* col_name_list */ - case 278: /* func_name_list */ - case 289: /* func_list */ - case 293: /* expression_list */ - case 304: /* dnode_list */ + case 273: /* specific_tags_opt */ + case 274: /* literal_list */ + case 276: /* col_name_list */ + case 279: /* func_name_list */ + case 290: /* func_list */ + case 294: /* expression_list */ + case 305: /* dnode_list */ case 316: /* star_func_para_list */ case 318: /* other_para_list */ case 338: /* select_list */ @@ -2231,7 +2229,7 @@ static void yy_destructor( } break; case 258: /* alter_db_option */ - case 279: /* alter_table_option */ + case 280: /* alter_table_option */ { } @@ -2682,21 +2680,21 @@ static const struct { { 267, -4 }, /* (118) alter_table_clause ::= full_table_name DROP TAG column_name */ { 267, -5 }, /* (119) alter_table_clause ::= full_table_name MODIFY TAG column_name type_name */ { 267, -5 }, /* (120) alter_table_clause ::= full_table_name RENAME TAG column_name column_name */ - { 267, -6 }, /* (121) alter_table_clause ::= full_table_name SET TAG column_name NK_EQ literal */ + { 267, -6 }, /* (121) alter_table_clause ::= full_table_name SET TAG column_name NK_EQ signed_literal */ { 264, -1 }, /* (122) multi_create_clause ::= create_subtable_clause */ { 264, -2 }, /* (123) multi_create_clause ::= multi_create_clause create_subtable_clause */ - { 271, -10 }, /* (124) create_subtable_clause ::= not_exists_opt full_table_name USING full_table_name specific_tags_opt TAGS NK_LP literal_list NK_RP table_options */ + { 272, -10 }, /* (124) create_subtable_clause ::= not_exists_opt full_table_name USING full_table_name specific_tags_opt TAGS NK_LP literal_list NK_RP table_options */ { 266, -1 }, /* (125) multi_drop_clause ::= drop_table_clause */ { 266, -2 }, /* (126) multi_drop_clause ::= multi_drop_clause drop_table_clause */ - { 274, -2 }, /* (127) drop_table_clause ::= exists_opt full_table_name */ - { 272, 0 }, /* (128) specific_tags_opt ::= */ - { 272, -3 }, /* (129) specific_tags_opt ::= NK_LP col_name_list NK_RP */ + { 275, -2 }, /* (127) drop_table_clause ::= exists_opt full_table_name */ + { 273, 0 }, /* (128) specific_tags_opt ::= */ + { 273, -3 }, /* (129) specific_tags_opt ::= NK_LP col_name_list NK_RP */ { 260, -1 }, /* (130) full_table_name ::= table_name */ { 260, -3 }, /* (131) full_table_name ::= db_name NK_DOT table_name */ { 261, -1 }, /* (132) column_def_list ::= column_def */ { 261, -3 }, /* (133) column_def_list ::= column_def_list NK_COMMA column_def */ - { 277, -2 }, /* (134) column_def ::= column_name type_name */ - { 277, -4 }, /* (135) column_def ::= column_name type_name COMMENT NK_STRING */ + { 278, -2 }, /* (134) column_def ::= column_name type_name */ + { 278, -4 }, /* (135) column_def ::= column_name type_name COMMENT NK_STRING */ { 270, -1 }, /* (136) type_name ::= BOOL */ { 270, -1 }, /* (137) type_name ::= TINYINT */ { 270, -1 }, /* (138) type_name ::= SMALLINT */ @@ -2732,11 +2730,11 @@ static const struct { { 263, -5 }, /* (168) table_options ::= table_options SMA NK_LP col_name_list NK_RP */ { 268, -1 }, /* (169) alter_table_options ::= alter_table_option */ { 268, -2 }, /* (170) alter_table_options ::= alter_table_options alter_table_option */ - { 279, -2 }, /* (171) alter_table_option ::= COMMENT NK_STRING */ - { 279, -2 }, /* (172) alter_table_option ::= TTL NK_INTEGER */ - { 275, -1 }, /* (173) col_name_list ::= col_name */ - { 275, -3 }, /* (174) col_name_list ::= col_name_list NK_COMMA col_name */ - { 280, -1 }, /* (175) col_name ::= column_name */ + { 280, -2 }, /* (171) alter_table_option ::= COMMENT NK_STRING */ + { 280, -2 }, /* (172) alter_table_option ::= TTL NK_INTEGER */ + { 276, -1 }, /* (173) col_name_list ::= col_name */ + { 276, -3 }, /* (174) col_name_list ::= col_name_list NK_COMMA col_name */ + { 281, -1 }, /* (175) col_name ::= column_name */ { 238, -2 }, /* (176) cmd ::= SHOW DNODES */ { 238, -2 }, /* (177) cmd ::= SHOW USERS */ { 238, -2 }, /* (178) cmd ::= SHOW DATABASES */ @@ -2765,64 +2763,64 @@ static const struct { { 238, -2 }, /* (201) cmd ::= SHOW SNODES */ { 238, -2 }, /* (202) cmd ::= SHOW CLUSTER */ { 238, -2 }, /* (203) cmd ::= SHOW TRANSACTIONS */ - { 281, 0 }, /* (204) db_name_cond_opt ::= */ - { 281, -2 }, /* (205) db_name_cond_opt ::= db_name NK_DOT */ - { 282, 0 }, /* (206) like_pattern_opt ::= */ - { 282, -2 }, /* (207) like_pattern_opt ::= LIKE NK_STRING */ - { 283, -1 }, /* (208) table_name_cond ::= table_name */ - { 284, 0 }, /* (209) from_db_opt ::= */ - { 284, -2 }, /* (210) from_db_opt ::= FROM db_name */ - { 278, -1 }, /* (211) func_name_list ::= func_name */ - { 278, -3 }, /* (212) func_name_list ::= func_name_list NK_COMMA func_name */ - { 285, -1 }, /* (213) func_name ::= function_name */ + { 282, 0 }, /* (204) db_name_cond_opt ::= */ + { 282, -2 }, /* (205) db_name_cond_opt ::= db_name NK_DOT */ + { 283, 0 }, /* (206) like_pattern_opt ::= */ + { 283, -2 }, /* (207) like_pattern_opt ::= LIKE NK_STRING */ + { 284, -1 }, /* (208) table_name_cond ::= table_name */ + { 285, 0 }, /* (209) from_db_opt ::= */ + { 285, -2 }, /* (210) from_db_opt ::= FROM db_name */ + { 279, -1 }, /* (211) func_name_list ::= func_name */ + { 279, -3 }, /* (212) func_name_list ::= func_name_list NK_COMMA func_name */ + { 286, -1 }, /* (213) func_name ::= function_name */ { 238, -8 }, /* (214) cmd ::= CREATE SMA INDEX not_exists_opt index_name ON table_name index_options */ { 238, -10 }, /* (215) cmd ::= CREATE FULLTEXT INDEX not_exists_opt index_name ON table_name NK_LP col_name_list NK_RP */ { 238, -6 }, /* (216) cmd ::= DROP INDEX exists_opt index_name ON table_name */ - { 288, 0 }, /* (217) index_options ::= */ - { 288, -9 }, /* (218) index_options ::= FUNCTION NK_LP func_list NK_RP INTERVAL NK_LP duration_literal NK_RP sliding_opt */ - { 288, -11 }, /* (219) index_options ::= FUNCTION NK_LP func_list NK_RP INTERVAL NK_LP duration_literal NK_COMMA duration_literal NK_RP sliding_opt */ - { 289, -1 }, /* (220) func_list ::= func */ - { 289, -3 }, /* (221) func_list ::= func_list NK_COMMA func */ - { 292, -4 }, /* (222) func ::= function_name NK_LP expression_list NK_RP */ + { 289, 0 }, /* (217) index_options ::= */ + { 289, -9 }, /* (218) index_options ::= FUNCTION NK_LP func_list NK_RP INTERVAL NK_LP duration_literal NK_RP sliding_opt */ + { 289, -11 }, /* (219) index_options ::= FUNCTION NK_LP func_list NK_RP INTERVAL NK_LP duration_literal NK_COMMA duration_literal NK_RP sliding_opt */ + { 290, -1 }, /* (220) func_list ::= func */ + { 290, -3 }, /* (221) func_list ::= func_list NK_COMMA func */ + { 293, -4 }, /* (222) func ::= function_name NK_LP expression_list NK_RP */ { 238, -7 }, /* (223) cmd ::= CREATE TOPIC not_exists_opt topic_name topic_options AS query_expression */ { 238, -7 }, /* (224) cmd ::= CREATE TOPIC not_exists_opt topic_name topic_options AS db_name */ { 238, -4 }, /* (225) cmd ::= DROP TOPIC exists_opt topic_name */ - { 295, 0 }, /* (226) topic_options ::= */ - { 295, -3 }, /* (227) topic_options ::= topic_options WITH TABLE */ - { 295, -3 }, /* (228) topic_options ::= topic_options WITH SCHEMA */ - { 295, -3 }, /* (229) topic_options ::= topic_options WITH TAG */ + { 296, 0 }, /* (226) topic_options ::= */ + { 296, -3 }, /* (227) topic_options ::= topic_options WITH TABLE */ + { 296, -3 }, /* (228) topic_options ::= topic_options WITH SCHEMA */ + { 296, -3 }, /* (229) topic_options ::= topic_options WITH TAG */ { 238, -2 }, /* (230) cmd ::= DESC full_table_name */ { 238, -2 }, /* (231) cmd ::= DESCRIBE full_table_name */ { 238, -3 }, /* (232) cmd ::= RESET QUERY CACHE */ { 238, -4 }, /* (233) cmd ::= EXPLAIN analyze_opt explain_options query_expression */ - { 297, 0 }, /* (234) analyze_opt ::= */ - { 297, -1 }, /* (235) analyze_opt ::= ANALYZE */ - { 298, 0 }, /* (236) explain_options ::= */ - { 298, -3 }, /* (237) explain_options ::= explain_options VERBOSE NK_BOOL */ - { 298, -3 }, /* (238) explain_options ::= explain_options RATIO NK_FLOAT */ + { 298, 0 }, /* (234) analyze_opt ::= */ + { 298, -1 }, /* (235) analyze_opt ::= ANALYZE */ + { 299, 0 }, /* (236) explain_options ::= */ + { 299, -3 }, /* (237) explain_options ::= explain_options VERBOSE NK_BOOL */ + { 299, -3 }, /* (238) explain_options ::= explain_options RATIO NK_FLOAT */ { 238, -6 }, /* (239) cmd ::= COMPACT VNODES IN NK_LP integer_list NK_RP */ { 238, -10 }, /* (240) cmd ::= CREATE agg_func_opt FUNCTION not_exists_opt function_name AS NK_STRING OUTPUTTYPE type_name bufsize_opt */ { 238, -4 }, /* (241) cmd ::= DROP FUNCTION exists_opt function_name */ - { 299, 0 }, /* (242) agg_func_opt ::= */ - { 299, -1 }, /* (243) agg_func_opt ::= AGGREGATE */ - { 300, 0 }, /* (244) bufsize_opt ::= */ - { 300, -2 }, /* (245) bufsize_opt ::= BUFSIZE NK_INTEGER */ + { 300, 0 }, /* (242) agg_func_opt ::= */ + { 300, -1 }, /* (243) agg_func_opt ::= AGGREGATE */ + { 301, 0 }, /* (244) bufsize_opt ::= */ + { 301, -2 }, /* (245) bufsize_opt ::= BUFSIZE NK_INTEGER */ { 238, -8 }, /* (246) cmd ::= CREATE STREAM not_exists_opt stream_name stream_options into_opt AS query_expression */ { 238, -4 }, /* (247) cmd ::= DROP STREAM exists_opt stream_name */ - { 303, 0 }, /* (248) into_opt ::= */ - { 303, -2 }, /* (249) into_opt ::= INTO full_table_name */ - { 302, 0 }, /* (250) stream_options ::= */ - { 302, -3 }, /* (251) stream_options ::= stream_options TRIGGER AT_ONCE */ - { 302, -3 }, /* (252) stream_options ::= stream_options TRIGGER WINDOW_CLOSE */ - { 302, -3 }, /* (253) stream_options ::= stream_options WATERMARK duration_literal */ + { 304, 0 }, /* (248) into_opt ::= */ + { 304, -2 }, /* (249) into_opt ::= INTO full_table_name */ + { 303, 0 }, /* (250) stream_options ::= */ + { 303, -3 }, /* (251) stream_options ::= stream_options TRIGGER AT_ONCE */ + { 303, -3 }, /* (252) stream_options ::= stream_options TRIGGER WINDOW_CLOSE */ + { 303, -3 }, /* (253) stream_options ::= stream_options WATERMARK duration_literal */ { 238, -3 }, /* (254) cmd ::= KILL CONNECTION NK_INTEGER */ { 238, -3 }, /* (255) cmd ::= KILL QUERY NK_INTEGER */ { 238, -3 }, /* (256) cmd ::= KILL TRANSACTION NK_INTEGER */ { 238, -4 }, /* (257) cmd ::= MERGE VGROUP NK_INTEGER NK_INTEGER */ { 238, -4 }, /* (258) cmd ::= REDISTRIBUTE VGROUP NK_INTEGER dnode_list */ { 238, -3 }, /* (259) cmd ::= SPLIT VGROUP NK_INTEGER */ - { 304, -2 }, /* (260) dnode_list ::= DNODE NK_INTEGER */ - { 304, -3 }, /* (261) dnode_list ::= dnode_list DNODE NK_INTEGER */ + { 305, -2 }, /* (260) dnode_list ::= DNODE NK_INTEGER */ + { 305, -3 }, /* (261) dnode_list ::= dnode_list DNODE NK_INTEGER */ { 238, -3 }, /* (262) cmd ::= SYNCDB db_name REPLICA */ { 238, -1 }, /* (263) cmd ::= query_expression */ { 241, -1 }, /* (264) literal ::= NK_INTEGER */ @@ -2833,32 +2831,32 @@ static const struct { { 241, -1 }, /* (269) literal ::= duration_literal */ { 241, -1 }, /* (270) literal ::= NULL */ { 241, -1 }, /* (271) literal ::= NK_QUESTION */ - { 290, -1 }, /* (272) duration_literal ::= NK_VARIABLE */ - { 305, -1 }, /* (273) signed ::= NK_INTEGER */ - { 305, -2 }, /* (274) signed ::= NK_PLUS NK_INTEGER */ - { 305, -2 }, /* (275) signed ::= NK_MINUS NK_INTEGER */ - { 305, -1 }, /* (276) signed ::= NK_FLOAT */ - { 305, -2 }, /* (277) signed ::= NK_PLUS NK_FLOAT */ - { 305, -2 }, /* (278) signed ::= NK_MINUS NK_FLOAT */ - { 306, -1 }, /* (279) signed_literal ::= signed */ - { 306, -1 }, /* (280) signed_literal ::= NK_STRING */ - { 306, -1 }, /* (281) signed_literal ::= NK_BOOL */ - { 306, -2 }, /* (282) signed_literal ::= TIMESTAMP NK_STRING */ - { 306, -1 }, /* (283) signed_literal ::= duration_literal */ - { 306, -1 }, /* (284) signed_literal ::= NULL */ - { 306, -1 }, /* (285) signed_literal ::= literal_func */ - { 273, -1 }, /* (286) literal_list ::= signed_literal */ - { 273, -3 }, /* (287) literal_list ::= literal_list NK_COMMA signed_literal */ + { 291, -1 }, /* (272) duration_literal ::= NK_VARIABLE */ + { 306, -1 }, /* (273) signed ::= NK_INTEGER */ + { 306, -2 }, /* (274) signed ::= NK_PLUS NK_INTEGER */ + { 306, -2 }, /* (275) signed ::= NK_MINUS NK_INTEGER */ + { 306, -1 }, /* (276) signed ::= NK_FLOAT */ + { 306, -2 }, /* (277) signed ::= NK_PLUS NK_FLOAT */ + { 306, -2 }, /* (278) signed ::= NK_MINUS NK_FLOAT */ + { 271, -1 }, /* (279) signed_literal ::= signed */ + { 271, -1 }, /* (280) signed_literal ::= NK_STRING */ + { 271, -1 }, /* (281) signed_literal ::= NK_BOOL */ + { 271, -2 }, /* (282) signed_literal ::= TIMESTAMP NK_STRING */ + { 271, -1 }, /* (283) signed_literal ::= duration_literal */ + { 271, -1 }, /* (284) signed_literal ::= NULL */ + { 271, -1 }, /* (285) signed_literal ::= literal_func */ + { 274, -1 }, /* (286) literal_list ::= signed_literal */ + { 274, -3 }, /* (287) literal_list ::= literal_list NK_COMMA signed_literal */ { 248, -1 }, /* (288) db_name ::= NK_ID */ - { 276, -1 }, /* (289) table_name ::= NK_ID */ + { 277, -1 }, /* (289) table_name ::= NK_ID */ { 269, -1 }, /* (290) column_name ::= NK_ID */ - { 286, -1 }, /* (291) function_name ::= NK_ID */ + { 287, -1 }, /* (291) function_name ::= NK_ID */ { 308, -1 }, /* (292) table_alias ::= NK_ID */ { 309, -1 }, /* (293) column_alias ::= NK_ID */ { 243, -1 }, /* (294) user_name ::= NK_ID */ - { 287, -1 }, /* (295) index_name ::= NK_ID */ - { 294, -1 }, /* (296) topic_name ::= NK_ID */ - { 301, -1 }, /* (297) stream_name ::= NK_ID */ + { 288, -1 }, /* (295) index_name ::= NK_ID */ + { 295, -1 }, /* (296) topic_name ::= NK_ID */ + { 302, -1 }, /* (297) stream_name ::= NK_ID */ { 310, -1 }, /* (298) expression ::= literal */ { 310, -1 }, /* (299) expression ::= pseudo_column */ { 310, -1 }, /* (300) expression ::= column_reference */ @@ -2873,8 +2871,8 @@ static const struct { { 310, -3 }, /* (309) expression ::= expression NK_SLASH expression */ { 310, -3 }, /* (310) expression ::= expression NK_REM expression */ { 310, -3 }, /* (311) expression ::= column_reference NK_ARROW NK_STRING */ - { 293, -1 }, /* (312) expression_list ::= expression */ - { 293, -3 }, /* (313) expression_list ::= expression_list NK_COMMA expression */ + { 294, -1 }, /* (312) expression_list ::= expression */ + { 294, -3 }, /* (313) expression_list ::= expression_list NK_COMMA expression */ { 312, -1 }, /* (314) column_reference ::= column_name */ { 312, -3 }, /* (315) column_reference ::= table_name NK_DOT column_name */ { 311, -1 }, /* (316) pseudo_column ::= ROWTS */ @@ -2970,8 +2968,8 @@ static const struct { { 341, -4 }, /* (406) twindow_clause_opt ::= STATE_WINDOW NK_LP expression NK_RP */ { 341, -6 }, /* (407) twindow_clause_opt ::= INTERVAL NK_LP duration_literal NK_RP sliding_opt fill_opt */ { 341, -8 }, /* (408) twindow_clause_opt ::= INTERVAL NK_LP duration_literal NK_COMMA duration_literal NK_RP sliding_opt fill_opt */ - { 291, 0 }, /* (409) sliding_opt ::= */ - { 291, -4 }, /* (410) sliding_opt ::= SLIDING NK_LP duration_literal NK_RP */ + { 292, 0 }, /* (409) sliding_opt ::= */ + { 292, -4 }, /* (410) sliding_opt ::= SLIDING NK_LP duration_literal NK_RP */ { 346, 0 }, /* (411) fill_opt ::= */ { 346, -4 }, /* (412) fill_opt ::= FILL NK_LP fill_mode NK_RP */ { 346, -6 }, /* (413) fill_opt ::= FILL NK_LP VALUE NK_COMMA literal_list NK_RP */ @@ -2986,7 +2984,7 @@ static const struct { { 348, -3 }, /* (422) group_by_list ::= group_by_list NK_COMMA expression */ { 343, 0 }, /* (423) having_clause_opt ::= */ { 343, -2 }, /* (424) having_clause_opt ::= HAVING search_condition */ - { 296, -4 }, /* (425) query_expression ::= query_expression_body order_by_clause_opt slimit_clause_opt limit_clause_opt */ + { 297, -4 }, /* (425) query_expression ::= query_expression_body order_by_clause_opt slimit_clause_opt limit_clause_opt */ { 349, -1 }, /* (426) query_expression_body ::= query_primary */ { 349, -4 }, /* (427) query_expression_body ::= query_expression_body UNION ALL query_expression_body */ { 349, -3 }, /* (428) query_expression_body ::= query_expression_body UNION query_expression_body */ @@ -3501,8 +3499,8 @@ static YYACTIONTYPE yy_reduce( { yylhsminor.yy172 = createAlterTableRenameCol(pCxt, yymsp[-4].minor.yy172, TSDB_ALTER_TABLE_UPDATE_TAG_NAME, &yymsp[-1].minor.yy105, &yymsp[0].minor.yy105); } yymsp[-4].minor.yy172 = yylhsminor.yy172; break; - case 121: /* alter_table_clause ::= full_table_name SET TAG column_name NK_EQ literal */ -{ yylhsminor.yy172 = createAlterTableSetTag(pCxt, yymsp[-5].minor.yy172, &yymsp[-2].minor.yy105, releaseRawExprNode(pCxt, yymsp[0].minor.yy172)); } + case 121: /* alter_table_clause ::= full_table_name SET TAG column_name NK_EQ signed_literal */ +{ yylhsminor.yy172 = createAlterTableSetTag(pCxt, yymsp[-5].minor.yy172, &yymsp[-2].minor.yy105, yymsp[0].minor.yy172); } yymsp[-5].minor.yy172 = yylhsminor.yy172; break; case 123: /* multi_create_clause ::= multi_create_clause create_subtable_clause */ diff --git a/source/libs/parser/test/parInitialATest.cpp b/source/libs/parser/test/parInitialATest.cpp index cc0dded5701bbe72c062aa69d339454976e6d1ac..784586dfb2258eab827fb4db40eb4fe9ee70bde9 100644 --- a/source/libs/parser/test/parInitialATest.cpp +++ b/source/libs/parser/test/parInitialATest.cpp @@ -204,7 +204,7 @@ TEST_F(ParserInitialATest, alterTable) { } }; - auto setAlterTagFunc = [&](const char* pTbname, const char* pTagName, const uint8_t* pNewVal, uint32_t bytes) { + auto setAlterTagFunc = [&](const char* pTbname, const char* pTagName, uint8_t* pNewVal, uint32_t bytes) { memset(&expect, 0, sizeof(SVAlterTbReq)); expect.tbName = strdup(pTbname); expect.action = TSDB_ALTER_TABLE_UPDATE_TAG_VAL; @@ -215,7 +215,7 @@ TEST_F(ParserInitialATest, alterTable) { expect.pTagVal = pNewVal; }; - auto setAlterOptionsFunc = [&](const char* pTbname, int32_t ttl, const char* pComment = nullptr) { + auto setAlterOptionsFunc = [&](const char* pTbname, int32_t ttl, char* pComment = nullptr) { memset(&expect, 0, sizeof(SVAlterTbReq)); expect.tbName = strdup(pTbname); expect.action = TSDB_ALTER_TABLE_UPDATE_OPTIONS; @@ -240,7 +240,7 @@ TEST_F(ParserInitialATest, alterTable) { void* pBuf = POINTER_SHIFT(pVgData->pData, sizeof(SMsgHead)); SVAlterTbReq req = {0}; SDecoder coder = {0}; - tDecoderInit(&coder, (const uint8_t*)pBuf, pVgData->size); + tDecoderInit(&coder, (uint8_t*)pBuf, pVgData->size); ASSERT_EQ(tDecodeSVAlterTbReq(&coder, &req), TSDB_CODE_SUCCESS); ASSERT_EQ(std::string(req.tbName), std::string(expect.tbName)); @@ -274,7 +274,7 @@ TEST_F(ParserInitialATest, alterTable) { setAlterOptionsFunc("t1", 10, nullptr); run("ALTER TABLE t1 TTL 10"); - setAlterOptionsFunc("t1", -1, "test"); + setAlterOptionsFunc("t1", -1, (char*)"test"); run("ALTER TABLE t1 COMMENT 'test'"); setAlterColFunc("t1", TSDB_ALTER_TABLE_ADD_COLUMN, "cc1", TSDB_DATA_TYPE_BIGINT); @@ -290,7 +290,7 @@ TEST_F(ParserInitialATest, alterTable) { run("ALTER TABLE t1 RENAME COLUMN c1 cc1"); int32_t val = 10; - setAlterTagFunc("st1s1", "tag1", (const uint8_t*)&val, sizeof(val)); + setAlterTagFunc("st1s1", "tag1", (uint8_t*)&val, sizeof(val)); run("ALTER TABLE st1s1 SET TAG tag1=10"); // todo diff --git a/source/libs/parser/test/parSelectTest.cpp b/source/libs/parser/test/parSelectTest.cpp index 2da50babd6038123ca17ac2596eeb8fe8ba90358..b68ef2c591e0497c6f32a9ce69c9e1f229b5f92f 100644 --- a/source/libs/parser/test/parSelectTest.cpp +++ b/source/libs/parser/test/parSelectTest.cpp @@ -121,10 +121,29 @@ TEST_F(ParserSelectTest, selectFunc) { run("SELECT MAX(c1), c2 FROM t1 STATE_WINDOW(c3)"); } -TEST_F(ParserSelectTest, clause) { +TEST_F(ParserSelectTest, nonstdFunc) { + useDb("root", "test"); + + run("SELECT DIFF(c1) FROM t1"); +} + +TEST_F(ParserSelectTest, nonstdFuncSemanticCheck) { + useDb("root", "test"); + + run("SELECT DIFF(c1), c2 FROM t1", TSDB_CODE_PAR_NOT_ALLOWED_FUNC, PARSER_STAGE_TRANSLATE); + + run("SELECT DIFF(c1), tbname FROM t1", TSDB_CODE_PAR_NOT_ALLOWED_FUNC, PARSER_STAGE_TRANSLATE); + + run("SELECT DIFF(c1), count(*) FROM t1", TSDB_CODE_PAR_NOT_ALLOWED_FUNC, PARSER_STAGE_TRANSLATE); + + run("SELECT DIFF(c1), CSUM(c1) FROM t1", TSDB_CODE_PAR_NOT_ALLOWED_FUNC, PARSER_STAGE_TRANSLATE); + + // run("SELECT DIFF(c1) FROM t1 INTERVAL(10s)"); +} + +TEST_F(ParserSelectTest, groupBy) { useDb("root", "test"); - // GROUP BY clause run("SELECT COUNT(*) cnt FROM t1 WHERE c1 > 0"); run("SELECT COUNT(*), c2 cnt FROM t1 WHERE c1 > 0 GROUP BY c2"); @@ -134,13 +153,19 @@ TEST_F(ParserSelectTest, clause) { run("SELECT COUNT(*), c1, c2 + 10, c1 + c2 cnt FROM t1 WHERE c1 > 0 GROUP BY c2, c1"); run("SELECT COUNT(*), c1 + 10, c2 cnt FROM t1 WHERE c1 > 0 GROUP BY c1 + 10, c2"); +} + +TEST_F(ParserSelectTest, orderBy) { + useDb("root", "test"); - // order by clause run("SELECT COUNT(*) cnt FROM t1 WHERE c1 > 0 GROUP BY c2 order by cnt"); run("SELECT COUNT(*) cnt FROM t1 WHERE c1 > 0 GROUP BY c2 order by 1"); +} + +TEST_F(ParserSelectTest, distinct) { + useDb("root", "test"); - // distinct clause // run("SELECT distinct c1, c2 FROM t1 WHERE c1 > 0 order by c1"); // run("SELECT distinct c1 + 10, c2 FROM t1 WHERE c1 > 0 order by c1 + 10, c2"); @@ -174,6 +199,25 @@ TEST_F(ParserSelectTest, intervalSemanticCheck) { PARSER_STAGE_TRANSLATE); } +TEST_F(ParserSelectTest, subquery) { + useDb("root", "test"); + + run("SELECT SUM(a) FROM (SELECT MAX(c1) a, ts FROM st1s1 INTERVAL(1m)) INTERVAL(1n)"); + + run("SELECT SUM(a) FROM (SELECT MAX(c1) a, _wstartts FROM st1s1 INTERVAL(1m)) INTERVAL(1n)"); + + run("SELECT SUM(a) FROM (SELECT MAX(c1) a, ts FROM st1s1 PARTITION BY TBNAME INTERVAL(1m)) INTERVAL(1n)"); + + run("SELECT SUM(a) FROM (SELECT MAX(c1) a, _wstartts FROM st1s1 PARTITION BY TBNAME INTERVAL(1m)) INTERVAL(1n)"); +} + +TEST_F(ParserSelectTest, subquerySemanticError) { + useDb("root", "test"); + + run("SELECT SUM(a) FROM (SELECT MAX(c1) a FROM st1s1 INTERVAL(1m)) INTERVAL(1n)", TSDB_CODE_PAR_NOT_ALLOWED_WIN_QUERY, + PARSER_STAGE_TRANSLATE); +} + TEST_F(ParserSelectTest, semanticError) { useDb("root", "test"); @@ -195,6 +239,10 @@ TEST_F(ParserSelectTest, semanticError) { // TSDB_CODE_PAR_WRONG_VALUE_TYPE run("SELECT timestamp '2010a' FROM t1", TSDB_CODE_PAR_WRONG_VALUE_TYPE, PARSER_STAGE_TRANSLATE); + run("SELECT LAST(*) + SUM(c1) FROM t1", TSDB_CODE_PAR_WRONG_VALUE_TYPE, PARSER_STAGE_TRANSLATE); + + run("SELECT CEIL(LAST(ts, c1)) FROM t1", TSDB_CODE_PAR_WRONG_VALUE_TYPE, PARSER_STAGE_TRANSLATE); + // TSDB_CODE_PAR_ILLEGAL_USE_AGG_FUNCTION run("SELECT c2 FROM t1 tt1 join t1 tt2 on COUNT(*) > 0", TSDB_CODE_PAR_ILLEGAL_USE_AGG_FUNCTION, PARSER_STAGE_TRANSLATE); diff --git a/source/libs/planner/CMakeLists.txt b/source/libs/planner/CMakeLists.txt index f0bf32bf173b2731e49636dfdca27b8907996844..ad981073ca7fa7ff26129783a5e337155e2e020d 100644 --- a/source/libs/planner/CMakeLists.txt +++ b/source/libs/planner/CMakeLists.txt @@ -8,7 +8,7 @@ target_include_directories( target_link_libraries( planner - PRIVATE os util nodes catalog cjson parser function qcom scalar + PRIVATE os util nodes catalog cjson parser function qcom scalar index PUBLIC transport ) diff --git a/source/libs/planner/src/planLogicCreater.c b/source/libs/planner/src/planLogicCreater.c index 6c567fd4ab90729277532b6e95f9d55ba1e787d2..4e77ae5fba32314fafed0de7538056794616c7b1 100644 --- a/source/libs/planner/src/planLogicCreater.c +++ b/source/libs/planner/src/planLogicCreater.c @@ -418,7 +418,7 @@ static SColumnNode* createColumnByExpr(const char* pStmtName, SExprNode* pExpr) } static int32_t createAggLogicNode(SLogicPlanContext* pCxt, SSelectStmt* pSelect, SLogicNode** pLogicNode) { - if (!pSelect->hasAggFuncs && NULL == pSelect->pGroupByList) { + if (!pSelect->hasAggFuncs && !pSelect->hasIndefiniteRowsFunc && NULL == pSelect->pGroupByList) { return TSDB_CODE_SUCCESS; } @@ -442,8 +442,8 @@ static int32_t createAggLogicNode(SLogicPlanContext* pCxt, SSelectStmt* pSelect, code = rewriteExprForSelect(pAgg->pGroupKeys, pSelect, SQL_CLAUSE_GROUP_BY); } - if (TSDB_CODE_SUCCESS == code && pSelect->hasAggFuncs) { - code = nodesCollectFuncs(pSelect, SQL_CLAUSE_GROUP_BY, fmIsAggFunc, &pAgg->pAggFuncs); + if (TSDB_CODE_SUCCESS == code && (pSelect->hasAggFuncs || pSelect->hasIndefiniteRowsFunc)) { + code = nodesCollectFuncs(pSelect, SQL_CLAUSE_GROUP_BY, fmIsVectorFunc, &pAgg->pAggFuncs); } // rewrite the expression in subsequent clauses diff --git a/source/libs/planner/src/planOptimizer.c b/source/libs/planner/src/planOptimizer.c index 8645225c04bc82e1ffa9c36db0ab482a8dd6b5a3..4d489f68e7c4ff042e6f0d0c82bbd98a6dbbfb2b 100644 --- a/source/libs/planner/src/planOptimizer.c +++ b/source/libs/planner/src/planOptimizer.c @@ -15,6 +15,7 @@ #include "filter.h" #include "functionMgt.h" +#include "index.h" #include "planInt.h" #define OPTIMIZE_FLAG_MASK(n) (1 << n) @@ -313,22 +314,53 @@ static EDealRes cpdIsPrimaryKeyCondImpl(SNode* pNode, void* pContext) { } static bool cpdIsPrimaryKeyCond(SNode* pNode) { + if (QUERY_NODE_LOGIC_CONDITION == nodeType(pNode)) { + return false; + } bool isPrimaryKeyCond = false; nodesWalkExpr(pNode, cpdIsPrimaryKeyCondImpl, &isPrimaryKeyCond); return isPrimaryKeyCond; } -static int32_t cpdPartitionScanLogicCond(SScanLogicNode* pScan, SNode** pPrimaryKeyCond, SNode** pOtherCond) { +static EDealRes cpdIsTagCondImpl(SNode* pNode, void* pContext) { + if (QUERY_NODE_COLUMN == nodeType(pNode)) { + *((bool*)pContext) = ((COLUMN_TYPE_TAG == ((SColumnNode*)pNode)->colType) ? true : false); + return *((bool*)pContext) ? DEAL_RES_CONTINUE : DEAL_RES_END; + } + return DEAL_RES_CONTINUE; +} + +static bool cpdIsTagCond(SNode* pNode) { + if (QUERY_NODE_LOGIC_CONDITION == nodeType(pNode)) { + return false; + } + bool isTagCond = false; + nodesWalkExpr(pNode, cpdIsTagCondImpl, &isTagCond); + return isTagCond; +} + +static int32_t cpdPartitionScanLogicCond(SScanLogicNode* pScan, SNode** pPrimaryKeyCond, SNode** pTagCond, + SNode** pOtherCond) { SLogicConditionNode* pLogicCond = (SLogicConditionNode*)pScan->node.pConditions; + if (LOGIC_COND_TYPE_AND != pLogicCond->condType) { + *pPrimaryKeyCond = NULL; + *pOtherCond = pScan->node.pConditions; + pScan->node.pConditions = NULL; + return TSDB_CODE_SUCCESS; + } + int32_t code = TSDB_CODE_SUCCESS; SNodeList* pPrimaryKeyConds = NULL; + SNodeList* pTagConds = NULL; SNodeList* pOtherConds = NULL; SNode* pCond = NULL; FOREACH(pCond, pLogicCond->pParameterList) { if (cpdIsPrimaryKeyCond(pCond)) { code = nodesListMakeAppend(&pPrimaryKeyConds, nodesCloneNode(pCond)); + } else if (cpdIsTagCond(pScan->node.pConditions)) { + code = nodesListMakeAppend(&pTagConds, nodesCloneNode(pCond)); } else { code = nodesListMakeAppend(&pOtherConds, nodesCloneNode(pCond)); } @@ -338,37 +370,46 @@ static int32_t cpdPartitionScanLogicCond(SScanLogicNode* pScan, SNode** pPrimary } SNode* pTempPrimaryKeyCond = NULL; + SNode* pTempTagCond = NULL; SNode* pTempOtherCond = NULL; if (TSDB_CODE_SUCCESS == code) { code = cpdMergeConds(&pTempPrimaryKeyCond, &pPrimaryKeyConds); } + if (TSDB_CODE_SUCCESS == code) { + code = cpdMergeConds(&pTempTagCond, &pTagConds); + } if (TSDB_CODE_SUCCESS == code) { code = cpdMergeConds(&pTempOtherCond, &pOtherConds); } if (TSDB_CODE_SUCCESS == code) { *pPrimaryKeyCond = pTempPrimaryKeyCond; + *pTagCond = pTempTagCond; *pOtherCond = pTempOtherCond; nodesDestroyNode(pScan->node.pConditions); pScan->node.pConditions = NULL; } else { nodesDestroyList(pPrimaryKeyConds); + nodesDestroyList(pTagConds); nodesDestroyList(pOtherConds); nodesDestroyNode(pTempPrimaryKeyCond); + nodesDestroyNode(pTempTagCond); nodesDestroyNode(pTempOtherCond); } return code; } -static int32_t cpdPartitionScanCond(SScanLogicNode* pScan, SNode** pPrimaryKeyCond, SNode** pOtherCond) { - if (QUERY_NODE_LOGIC_CONDITION == nodeType(pScan->node.pConditions) && - LOGIC_COND_TYPE_AND == ((SLogicConditionNode*)pScan->node.pConditions)->condType) { - return cpdPartitionScanLogicCond(pScan, pPrimaryKeyCond, pOtherCond); +static int32_t cpdPartitionScanCond(SScanLogicNode* pScan, SNode** pPrimaryKeyCond, SNode** pTagCond, + SNode** pOtherCond) { + if (QUERY_NODE_LOGIC_CONDITION == nodeType(pScan->node.pConditions)) { + return cpdPartitionScanLogicCond(pScan, pPrimaryKeyCond, pTagCond, pOtherCond); } if (cpdIsPrimaryKeyCond(pScan->node.pConditions)) { *pPrimaryKeyCond = pScan->node.pConditions; + } else if (cpdIsTagCond(pScan->node.pConditions)) { + *pTagCond = pScan->node.pConditions; } else { *pOtherCond = pScan->node.pConditions; } @@ -391,6 +432,32 @@ static int32_t cpdCalcTimeRange(SScanLogicNode* pScan, SNode** pPrimaryKeyCond, return code; } +static int32_t cpdApplyTagIndex(SScanLogicNode* pScan, SNode** pTagCond, SNode** pOtherCond) { + int32_t code = TSDB_CODE_SUCCESS; + SIdxFltStatus idxStatus = idxGetFltStatus(*pTagCond); + switch (idxStatus) { + case SFLT_NOT_INDEX: + code = cpdCondAppend(pOtherCond, pTagCond); + break; + case SFLT_COARSE_INDEX: + pScan->pTagCond = nodesCloneNode(*pTagCond); + if (NULL == pScan->pTagCond) { + code = TSDB_CODE_OUT_OF_MEMORY; + break; + } + code = cpdCondAppend(pOtherCond, pTagCond); + break; + case SFLT_ACCURATE_INDEX: + pScan->pTagCond = *pTagCond; + *pTagCond = NULL; + break; + default: + code = TSDB_CODE_FAILED; + break; + } + return code; +} + static int32_t cpdOptimizeScanCondition(SOptimizeContext* pCxt, SScanLogicNode* pScan) { if (NULL == pScan->node.pConditions || OPTIMIZE_FLAG_TEST_MASK(pScan->node.optimizedFlag, OPTIMIZE_FLAG_CPD) || TSDB_SYSTEM_TABLE == pScan->pMeta->tableType) { @@ -398,11 +465,15 @@ static int32_t cpdOptimizeScanCondition(SOptimizeContext* pCxt, SScanLogicNode* } SNode* pPrimaryKeyCond = NULL; + SNode* pTagCond = NULL; SNode* pOtherCond = NULL; - int32_t code = cpdPartitionScanCond(pScan, &pPrimaryKeyCond, &pOtherCond); + int32_t code = cpdPartitionScanCond(pScan, &pPrimaryKeyCond, &pTagCond, &pOtherCond); if (TSDB_CODE_SUCCESS == code && NULL != pPrimaryKeyCond) { code = cpdCalcTimeRange(pScan, &pPrimaryKeyCond, &pOtherCond); } + if (TSDB_CODE_SUCCESS == code && NULL != pTagCond) { + code = cpdApplyTagIndex(pScan, &pTagCond, &pOtherCond); + } if (TSDB_CODE_SUCCESS == code) { pScan->node.pConditions = pOtherCond; } @@ -618,30 +689,6 @@ static bool cpdContainPrimaryKeyEqualCond(SJoinLogicNode* pJoin, SNode* pCond) { } } -// static int32_t cpdCheckOpCond(SOptimizeContext* pCxt, SJoinLogicNode* pJoin, SNode* pOnCond) { -// if (!cpdIsPrimaryKeyEqualCond(pJoin, pOnCond)) { -// return generateUsageErrMsg(pCxt->pPlanCxt->pMsg, pCxt->pPlanCxt->msgLen, TSDB_CODE_PLAN_EXPECTED_TS_EQUAL); -// } -// return TSDB_CODE_SUCCESS; -// } - -// static int32_t cpdCheckLogicCond(SOptimizeContext* pCxt, SJoinLogicNode* pJoin, SLogicConditionNode* pOnCond) { -// if (LOGIC_COND_TYPE_AND != pOnCond->condType) { -// return generateUsageErrMsg(pCxt->pPlanCxt->pMsg, pCxt->pPlanCxt->msgLen, TSDB_CODE_PLAN_EXPECTED_TS_EQUAL); -// } -// bool hasPrimaryKeyEqualCond = false; -// SNode* pCond = NULL; -// FOREACH(pCond, pOnCond->pParameterList) { -// if (cpdIsPrimaryKeyEqualCond(pJoin, pCond)) { -// hasPrimaryKeyEqualCond = true; -// } -// } -// if (!hasPrimaryKeyEqualCond) { -// return generateUsageErrMsg(pCxt->pPlanCxt->pMsg, pCxt->pPlanCxt->msgLen, TSDB_CODE_PLAN_EXPECTED_TS_EQUAL); -// } -// return TSDB_CODE_SUCCESS; -// } - static int32_t cpdCheckJoinOnCond(SOptimizeContext* pCxt, SJoinLogicNode* pJoin) { if (NULL == pJoin->pOnConditions) { return generateUsageErrMsg(pCxt->pPlanCxt->pMsg, pCxt->pPlanCxt->msgLen, TSDB_CODE_PLAN_NOT_SUPPORT_CROSS_JOIN); @@ -650,11 +697,6 @@ static int32_t cpdCheckJoinOnCond(SOptimizeContext* pCxt, SJoinLogicNode* pJoin) return generateUsageErrMsg(pCxt->pPlanCxt->pMsg, pCxt->pPlanCxt->msgLen, TSDB_CODE_PLAN_EXPECTED_TS_EQUAL); } return TSDB_CODE_SUCCESS; - // if (QUERY_NODE_LOGIC_CONDITION == nodeType(pJoin->pOnConditions)) { - // return cpdCheckLogicCond(pCxt, pJoin, (SLogicConditionNode*)pJoin->pOnConditions); - // } else { - // return cpdCheckOpCond(pCxt, pJoin, pJoin->pOnConditions); - // } } static int32_t cpdPushJoinCondition(SOptimizeContext* pCxt, SJoinLogicNode* pJoin) { diff --git a/source/libs/planner/src/planPhysiCreater.c b/source/libs/planner/src/planPhysiCreater.c index affe9ef2f61f0afce95ba7f1feec5f65f23a7f1f..fcba2aa2d33a926b1608c03d22489bd86fdded8a 100644 --- a/source/libs/planner/src/planPhysiCreater.c +++ b/source/libs/planner/src/planPhysiCreater.c @@ -411,7 +411,7 @@ static int32_t createScanCols(SPhysiPlanContext* pCxt, SScanPhysiNode* pScanPhys return sortScanCols(pScanPhysiNode->pScanCols); } -static int32_t createScanPhysiNodeFinalize(SPhysiPlanContext* pCxt, SScanLogicNode* pScanLogicNode, +static int32_t createScanPhysiNodeFinalize(SPhysiPlanContext* pCxt, SSubplan* pSubplan, SScanLogicNode* pScanLogicNode, SScanPhysiNode* pScanPhysiNode, SPhysiNode** pPhyNode) { int32_t code = createScanCols(pCxt, pScanPhysiNode, pScanLogicNode->pScanCols); if (TSDB_CODE_SUCCESS == code) { @@ -438,6 +438,12 @@ static int32_t createScanPhysiNodeFinalize(SPhysiPlanContext* pCxt, SScanLogicNo pScanPhysiNode->uid = pScanLogicNode->pMeta->uid; pScanPhysiNode->tableType = pScanLogicNode->pMeta->tableType; memcpy(&pScanPhysiNode->tableName, &pScanLogicNode->tableName, sizeof(SName)); + if (NULL != pScanLogicNode->pTagCond) { + pSubplan->pTagCond = nodesCloneNode(pScanLogicNode->pTagCond); + if (NULL == pSubplan->pTagCond) { + code = TSDB_CODE_OUT_OF_MEMORY; + } + } } if (TSDB_CODE_SUCCESS == code) { @@ -463,7 +469,7 @@ static int32_t createTagScanPhysiNode(SPhysiPlanContext* pCxt, SSubplan* pSubpla } vgroupInfoToNodeAddr(pScanLogicNode->pVgroupList->vgroups, &pSubplan->execNode); taosArrayPush(pCxt->pExecNodeList, &pSubplan->execNode); - return createScanPhysiNodeFinalize(pCxt, pScanLogicNode, (SScanPhysiNode*)pTagScan, pPhyNode); + return createScanPhysiNodeFinalize(pCxt, pSubplan, pScanLogicNode, (SScanPhysiNode*)pTagScan, pPhyNode); } static int32_t createTableScanPhysiNode(SPhysiPlanContext* pCxt, SSubplan* pSubplan, SScanLogicNode* pScanLogicNode, @@ -498,7 +504,7 @@ static int32_t createTableScanPhysiNode(SPhysiPlanContext* pCxt, SSubplan* pSubp pTableScan->intervalUnit = pScanLogicNode->intervalUnit; pTableScan->slidingUnit = pScanLogicNode->slidingUnit; - return createScanPhysiNodeFinalize(pCxt, pScanLogicNode, (SScanPhysiNode*)pTableScan, pPhyNode); + return createScanPhysiNodeFinalize(pCxt, pSubplan, pScanLogicNode, (SScanPhysiNode*)pTableScan, pPhyNode); } static int32_t createSystemTableScanPhysiNode(SPhysiPlanContext* pCxt, SSubplan* pSubplan, @@ -522,7 +528,7 @@ static int32_t createSystemTableScanPhysiNode(SPhysiPlanContext* pCxt, SSubplan* pScan->mgmtEpSet = pCxt->pPlanCxt->mgmtEpSet; tNameGetFullDbName(&pScanLogicNode->tableName, pSubplan->dbFName); - return createScanPhysiNodeFinalize(pCxt, pScanLogicNode, (SScanPhysiNode*)pScan, pPhyNode); + return createScanPhysiNodeFinalize(pCxt, pSubplan, pScanLogicNode, (SScanPhysiNode*)pScan, pPhyNode); } static int32_t createStreamScanPhysiNode(SPhysiPlanContext* pCxt, SSubplan* pSubplan, SScanLogicNode* pScanLogicNode, @@ -599,14 +605,17 @@ typedef struct SRewritePrecalcExprsCxt { static EDealRes collectAndRewrite(SRewritePrecalcExprsCxt* pCxt, SNode** pNode) { SNode* pExpr = nodesCloneNode(*pNode); if (NULL == pExpr) { + pCxt->errCode = TSDB_CODE_OUT_OF_MEMORY; return DEAL_RES_ERROR; } if (nodesListAppend(pCxt->pPrecalcExprs, pExpr)) { + pCxt->errCode = TSDB_CODE_OUT_OF_MEMORY; nodesDestroyNode(pExpr); return DEAL_RES_ERROR; } SColumnNode* pCol = (SColumnNode*)nodesMakeNode(QUERY_NODE_COLUMN); if (NULL == pCol) { + pCxt->errCode = TSDB_CODE_OUT_OF_MEMORY; nodesDestroyNode(pExpr); return DEAL_RES_ERROR; } @@ -624,16 +633,45 @@ static EDealRes collectAndRewrite(SRewritePrecalcExprsCxt* pCxt, SNode** pNode) return DEAL_RES_IGNORE_CHILD; } +static int32_t rewriteValueToOperator(SRewritePrecalcExprsCxt* pCxt, SNode** pNode) { + SOperatorNode* pOper = (SOperatorNode*)nodesMakeNode(QUERY_NODE_OPERATOR); + if (NULL == pOper) { + return TSDB_CODE_OUT_OF_MEMORY; + } + pOper->pLeft = nodesMakeNode(QUERY_NODE_LEFT_VALUE); + if (NULL == pOper->pLeft) { + nodesDestroyNode(pOper); + return TSDB_CODE_OUT_OF_MEMORY; + } + SValueNode* pVal = (SValueNode*)*pNode; + pOper->node.resType = pVal->node.resType; + strcpy(pOper->node.aliasName, pVal->node.aliasName); + pOper->opType = OP_TYPE_ASSIGN; + pOper->pRight = *pNode; + *pNode = (SNode*)pOper; + return TSDB_CODE_SUCCESS; +} + static EDealRes doRewritePrecalcExprs(SNode** pNode, void* pContext) { SRewritePrecalcExprsCxt* pCxt = (SRewritePrecalcExprsCxt*)pContext; switch (nodeType(*pNode)) { + case QUERY_NODE_VALUE: { + if (((SValueNode*)*pNode)->notReserved) { + break; + } + pCxt->errCode = rewriteValueToOperator(pCxt, pNode); + if (TSDB_CODE_SUCCESS != pCxt->errCode) { + return DEAL_RES_ERROR; + } + return collectAndRewrite(pCxt, pNode); + } case QUERY_NODE_OPERATOR: case QUERY_NODE_LOGIC_CONDITION: { - return collectAndRewrite(pContext, pNode); + return collectAndRewrite(pCxt, pNode); } case QUERY_NODE_FUNCTION: { if (fmIsScalarFunc(((SFunctionNode*)(*pNode))->funcId)) { - return collectAndRewrite(pContext, pNode); + return collectAndRewrite(pCxt, pNode); } } default: @@ -677,9 +715,8 @@ static int32_t rewritePrecalcExprs(SPhysiPlanContext* pCxt, SNodeList* pList, SN } SRewritePrecalcExprsCxt cxt = {.errCode = TSDB_CODE_SUCCESS, .pPrecalcExprs = *pPrecalcExprs}; nodesRewriteExprs(*pRewrittenList, doRewritePrecalcExprs, &cxt); - if (0 == LIST_LENGTH(cxt.pPrecalcExprs)) { - nodesDestroyList(cxt.pPrecalcExprs); - *pPrecalcExprs = NULL; + if (0 == LIST_LENGTH(cxt.pPrecalcExprs) || TSDB_CODE_SUCCESS != cxt.errCode) { + DESTORY_LIST(*pPrecalcExprs); } return cxt.errCode; } diff --git a/source/libs/planner/src/planSpliter.c b/source/libs/planner/src/planSpliter.c index fb716af3e5563a6adf9f6c239c9c946c9b5d9be9..1a97d9ab1b898b9b4a5dae1d4bade0e9b6d87bb8 100644 --- a/source/libs/planner/src/planSpliter.c +++ b/source/libs/planner/src/planSpliter.c @@ -24,9 +24,9 @@ #define SPLIT_FLAG_TEST_MASK(val, mask) (((val) & (mask)) != 0) typedef struct SSplitContext { - int32_t queryId; - int32_t groupId; - bool split; + uint64_t queryId; + int32_t groupId; + bool split; } SSplitContext; typedef int32_t (*FSplit)(SSplitContext* pCxt, SLogicSubplan* pSubplan); @@ -248,6 +248,7 @@ static SLogicSubplan* unionCreateSubplan(SSplitContext* pCxt, SLogicNode* pNode) pSubplan->id.groupId = pCxt->groupId; pSubplan->subplanType = SUBPLAN_TYPE_SCAN; pSubplan->pNode = pNode; + pNode->pParent = NULL; return pSubplan; } @@ -408,17 +409,30 @@ static const SSplitRule splitRuleSet[] = {{.pName = "SuperTableScan", .splitFunc static const int32_t splitRuleNum = (sizeof(splitRuleSet) / sizeof(SSplitRule)); +static void dumpLogicSubplan(const char* pRuleName, SLogicSubplan* pSubplan) { + char* pStr = NULL; + nodesNodeToString(pSubplan, false, &pStr, NULL); + qDebugL("apply %s rule: %s", pRuleName, pStr); + taosMemoryFree(pStr); +} + static int32_t applySplitRule(SLogicSubplan* pSubplan) { SSplitContext cxt = {.queryId = pSubplan->id.queryId, .groupId = pSubplan->id.groupId + 1, .split = false}; + bool split = false; do { - cxt.split = false; + split = false; for (int32_t i = 0; i < splitRuleNum; ++i) { + cxt.split = false; int32_t code = splitRuleSet[i].splitFunc(&cxt, pSubplan); if (TSDB_CODE_SUCCESS != code) { return code; } + if (cxt.split) { + split = true; + dumpLogicSubplan(splitRuleSet[i].pName, pSubplan); + } } - } while (cxt.split); + } while (split); return TSDB_CODE_SUCCESS; } diff --git a/source/libs/planner/src/planner.c b/source/libs/planner/src/planner.c index 8e6c04bb337b900b1ebc8b33c73b29d111231ab3..af62c52a89baa90aaf857fa6606267a437275f87 100644 --- a/source/libs/planner/src/planner.c +++ b/source/libs/planner/src/planner.c @@ -18,6 +18,13 @@ #include "planInt.h" #include "scalar.h" +static void dumpQueryPlan(SQueryPlan* pPlan) { + char* pStr = NULL; + nodesNodeToString(pPlan, false, &pStr, NULL); + planDebugL("Query Plan: %s", pStr); + taosMemoryFree(pStr); +} + int32_t qCreateQueryPlan(SPlanContext* pCxt, SQueryPlan** pPlan, SArray* pExecNodeList) { SLogicNode* pLogicNode = NULL; SLogicSubplan* pLogicSubplan = NULL; @@ -36,6 +43,9 @@ int32_t qCreateQueryPlan(SPlanContext* pCxt, SQueryPlan** pPlan, SArray* pExecNo if (TSDB_CODE_SUCCESS == code) { code = createPhysiPlan(pCxt, pLogicPlan, pPlan, pExecNodeList); } + if (TSDB_CODE_SUCCESS == code) { + dumpQueryPlan(*pPlan); + } nodesDestroyNode(pLogicNode); nodesDestroyNode(pLogicSubplan); diff --git a/source/libs/planner/test/planBasicTest.cpp b/source/libs/planner/test/planBasicTest.cpp index a17d8cd850d51ed38877b2db1550865408879666..4b84079f7bd417b538a789ff964675208d9ea0bf 100644 --- a/source/libs/planner/test/planBasicTest.cpp +++ b/source/libs/planner/test/planBasicTest.cpp @@ -50,4 +50,6 @@ TEST_F(PlanBasicTest, func) { run("SELECT DIFF(c1) FROM t1"); run("SELECT PERCENTILE(c1, 60) FROM t1"); + + run("SELECT TOP(c1, 60) FROM t1"); } diff --git a/source/libs/planner/test/planGroupByTest.cpp b/source/libs/planner/test/planGroupByTest.cpp index 9ca1001f4cb24224d9dd223e366c9eed83db4fbe..cf516034707e53a230d4e2e7af6fb81c3b8aaecf 100644 --- a/source/libs/planner/test/planGroupByTest.cpp +++ b/source/libs/planner/test/planGroupByTest.cpp @@ -49,6 +49,8 @@ TEST_F(PlanGroupByTest, aggFunc) { run("SELECT LAST(*), FIRST(*) FROM t1"); run("SELECT LAST(*), FIRST(*) FROM t1 GROUP BY c1"); + + run("SELECT SUM(10), COUNT(c1) FROM t1 GROUP BY c2"); } TEST_F(PlanGroupByTest, selectFunc) { diff --git a/source/libs/planner/test/planOptimizeTest.cpp b/source/libs/planner/test/planOptimizeTest.cpp index 77f9b5846c1edbe879c21d90c978a01232c2444e..4234a1320a433da1184cdca6d5f567fa3a2005c6 100644 --- a/source/libs/planner/test/planOptimizeTest.cpp +++ b/source/libs/planner/test/planOptimizeTest.cpp @@ -32,6 +32,12 @@ TEST_F(PlanOptimizeTest, optimizeScanData) { run("SELECT PERCENTILE(c1, 40), COUNT(*) FROM t1"); } +TEST_F(PlanOptimizeTest, ConditionPushDown) { + useDb("root", "test"); + + run("SELECT ts, c1 FROM st1 WHERE tag1 > 4"); +} + TEST_F(PlanOptimizeTest, orderByPrimaryKey) { useDb("root", "test"); diff --git a/source/libs/planner/test/planSetOpTest.cpp b/source/libs/planner/test/planSetOpTest.cpp index 717384aae69fe26973216c996aef199954225e23..5568d3cfbb994b83c3531957a33fc7ae1ff20eb3 100644 --- a/source/libs/planner/test/planSetOpTest.cpp +++ b/source/libs/planner/test/planSetOpTest.cpp @@ -23,7 +23,12 @@ class PlanSetOpTest : public PlannerTestBase {}; TEST_F(PlanSetOpTest, unionAll) { useDb("root", "test"); + // sql 1: single UNION ALL operator run("SELECT c1, c2 FROM t1 WHERE c1 > 10 UNION ALL SELECT c1, c2 FROM t1 WHERE c1 > 20"); + // sql 2: multi UNION ALL operator + run("SELECT c1, c2 FROM t1 WHERE c1 > 10 " + "UNION ALL SELECT c1, c2 FROM t1 WHERE c1 > 20 " + "UNION ALL SELECT c1, c2 FROM t1 WHERE c1 > 30"); } TEST_F(PlanSetOpTest, unionAllSubquery) { @@ -44,7 +49,12 @@ TEST_F(PlanSetOpTest, unionAllWithSubquery) { TEST_F(PlanSetOpTest, union) { useDb("root", "test"); + // single UNION operator run("SELECT c1, c2 FROM t1 WHERE c1 > 10 UNION SELECT c1, c2 FROM t1 WHERE c1 > 20"); + // multi UNION operator + run("SELECT c1, c2 FROM t1 WHERE c1 > 10 " + "UNION SELECT c1, c2 FROM t1 WHERE c1 > 20 " + "UNION SELECT c1, c2 FROM t1 WHERE c1 > 30"); } TEST_F(PlanSetOpTest, unionContainJoin) { @@ -62,3 +72,12 @@ TEST_F(PlanSetOpTest, unionSubquery) { run("SELECT * FROM (SELECT c1, c2 FROM t1 UNION SELECT c1, c2 FROM t1)"); } + +TEST_F(PlanSetOpTest, bug001) { + useDb("root", "test"); + + run("SELECT c2 FROM t1 WHERE c1 IS NOT NULL GROUP BY c2 " + "UNION " + "SELECT 'abcdefghijklmnopqrstuvwxyz' FROM t1 " + "WHERE 'abcdefghijklmnopqrstuvwxyz' IS NOT NULL GROUP BY 'abcdefghijklmnopqrstuvwxyz'"); +} diff --git a/source/libs/planner/test/planSubqueryTest.cpp b/source/libs/planner/test/planSubqueryTest.cpp index 6a7cb91bb931fc5d0d22e7cefd5c2156fcf4c74a..2d559c6f3b5322e4bd27bd571fc5e6829ccf262c 100644 --- a/source/libs/planner/test/planSubqueryTest.cpp +++ b/source/libs/planner/test/planSubqueryTest.cpp @@ -23,9 +23,7 @@ class PlanSubqeuryTest : public PlannerTestBase {}; TEST_F(PlanSubqeuryTest, basic) { useDb("root", "test"); - if (0 == g_skipSql) { - run("SELECT * FROM (SELECT * FROM t1)"); - } + run("SELECT * FROM (SELECT * FROM t1)"); run("SELECT LAST(c1) FROM (SELECT * FROM t1)"); } diff --git a/source/libs/planner/test/planTestMain.cpp b/source/libs/planner/test/planTestMain.cpp index 36f66ddff689ed7f29acc571a34d2a3a7c1e6163..42c8558239b4389e0a0c469fdaa5842b53dc2398 100644 --- a/source/libs/planner/test/planTestMain.cpp +++ b/source/libs/planner/test/planTestMain.cpp @@ -25,7 +25,7 @@ class PlannerEnv : public testing::Environment { virtual void SetUp() { initMetaDataEnv(); generateMetaData(); - initLog("/tmp/td"); + initLog(TD_TMP_DIR_PATH "td"); } virtual void TearDown() { destroyMetaDataEnv(); } @@ -35,18 +35,19 @@ class PlannerEnv : public testing::Environment { private: void initLog(const char* path) { - dDebugFlag = 143; - vDebugFlag = 0; - mDebugFlag = 143; - cDebugFlag = 0; - jniDebugFlag = 0; - tmrDebugFlag = 135; - uDebugFlag = 135; - rpcDebugFlag = 143; - qDebugFlag = 143; - wDebugFlag = 0; - sDebugFlag = 0; - tsdbDebugFlag = 0; + int32_t logLevel = getLogLevel(); + dDebugFlag = logLevel; + vDebugFlag = logLevel; + mDebugFlag = logLevel; + cDebugFlag = logLevel; + jniDebugFlag = logLevel; + tmrDebugFlag = logLevel; + uDebugFlag = logLevel; + rpcDebugFlag = logLevel; + qDebugFlag = logLevel; + wDebugFlag = logLevel; + sDebugFlag = logLevel; + tsdbDebugFlag = logLevel; tsLogEmbedded = 1; tsAsyncLog = 0; @@ -60,17 +61,26 @@ class PlannerEnv : public testing::Environment { }; static void parseArg(int argc, char* argv[]) { - int opt = 0; - const char* optstring = ""; + int opt = 0; + const char* optstring = ""; + // clang-format off static struct option long_options[] = { - {"dump", optional_argument, NULL, 'd'}, {"skipSql", optional_argument, NULL, 's'}, {0, 0, 0, 0}}; + {"dump", optional_argument, NULL, 'd'}, + {"skipSql", required_argument, NULL, 's'}, + {"log", required_argument, NULL, 'l'}, + {0, 0, 0, 0} + }; + // clang-format on while ((opt = getopt_long(argc, argv, optstring, long_options, NULL)) != -1) { switch (opt) { case 'd': setDumpModule(optarg); break; case 's': - g_skipSql = 1; + setSkipSqlNum(optarg); + break; + case 'l': + setLogLevel(optarg); break; default: break; diff --git a/source/libs/planner/test/planTestUtil.cpp b/source/libs/planner/test/planTestUtil.cpp index 94a28f46a82de78fd038f05682f0521796b716fe..084762088823edee627b4ea3bad2286208d570ac 100644 --- a/source/libs/planner/test/planTestUtil.cpp +++ b/source/libs/planner/test/planTestUtil.cpp @@ -14,6 +14,7 @@ */ #include "planTestUtil.h" +#include #include #include @@ -48,6 +49,7 @@ enum DumpModule { DumpModule g_dumpModule = DUMP_MODULE_NOTHING; int32_t g_skipSql = 0; +int32_t g_logLevel = 131; void setDumpModule(const char* pModule) { if (NULL == pModule) { @@ -71,14 +73,26 @@ void setDumpModule(const char* pModule) { } } +void setSkipSqlNum(const char* pNum) { g_skipSql = stoi(optarg); } + +void setLogLevel(const char* pLogLevel) { g_logLevel = stoi(pLogLevel); } + +int32_t getLogLevel() { return g_logLevel; } + class PlannerTestBaseImpl { public: void useDb(const string& acctId, const string& db) { caseEnv_.acctId_ = acctId; caseEnv_.db_ = db; + caseEnv_.nsql_ = g_skipSql; } void run(const string& sql) { + if (caseEnv_.nsql_ > 0) { + --(caseEnv_.nsql_); + return; + } + reset(); try { SQuery* pQuery = nullptr; @@ -109,6 +123,10 @@ class PlannerTestBaseImpl { } void prepare(const string& sql) { + if (caseEnv_.nsql_ > 0) { + return; + } + reset(); try { doParseSql(sql, &stmtEnv_.pQuery_, true); @@ -119,6 +137,10 @@ class PlannerTestBaseImpl { } void bindParams(TAOS_MULTI_BIND* pParams, int32_t colIdx) { + if (caseEnv_.nsql_ > 0) { + return; + } + try { doBindParams(stmtEnv_.pQuery_, pParams, colIdx); } catch (...) { @@ -128,6 +150,11 @@ class PlannerTestBaseImpl { } void exec() { + if (caseEnv_.nsql_ > 0) { + --(caseEnv_.nsql_); + return; + } + try { doParseBoundSql(stmtEnv_.pQuery_); @@ -157,8 +184,9 @@ class PlannerTestBaseImpl { private: struct caseEnv { - string acctId_; - string db_; + string acctId_; + string db_; + int32_t nsql_; }; struct stmtEnv { @@ -205,45 +233,45 @@ class PlannerTestBaseImpl { if (DUMP_MODULE_ALL == module || DUMP_MODULE_PARSER == module) { if (res_.prepareAst_.empty()) { - cout << "syntax tree : " << endl; + cout << "+++++++++++++++++++++syntax tree : " << endl; cout << res_.ast_ << endl; } else { - cout << "prepare syntax tree : " << endl; + cout << "+++++++++++++++++++++prepare syntax tree : " << endl; cout << res_.prepareAst_ << endl; - cout << "bound syntax tree : " << endl; + cout << "+++++++++++++++++++++bound syntax tree : " << endl; cout << res_.boundAst_ << endl; - cout << "syntax tree : " << endl; + cout << "+++++++++++++++++++++syntax tree : " << endl; cout << res_.ast_ << endl; } } if (DUMP_MODULE_ALL == module || DUMP_MODULE_LOGIC == module) { - cout << "raw logic plan : " << endl; + cout << "+++++++++++++++++++++raw logic plan : " << endl; cout << res_.rawLogicPlan_ << endl; } if (DUMP_MODULE_ALL == module || DUMP_MODULE_OPTIMIZED == module) { - cout << "optimized logic plan : " << endl; + cout << "+++++++++++++++++++++optimized logic plan : " << endl; cout << res_.optimizedLogicPlan_ << endl; } if (DUMP_MODULE_ALL == module || DUMP_MODULE_SPLIT == module) { - cout << "split logic plan : " << endl; + cout << "+++++++++++++++++++++split logic plan : " << endl; cout << res_.splitLogicPlan_ << endl; } if (DUMP_MODULE_ALL == module || DUMP_MODULE_SCALED == module) { - cout << "scaled logic plan : " << endl; + cout << "+++++++++++++++++++++scaled logic plan : " << endl; cout << res_.scaledLogicPlan_ << endl; } if (DUMP_MODULE_ALL == module || DUMP_MODULE_PHYSICAL == module) { - cout << "physical plan : " << endl; + cout << "+++++++++++++++++++++physical plan : " << endl; cout << res_.physiPlan_ << endl; } if (DUMP_MODULE_ALL == module || DUMP_MODULE_SUBPLAN == module) { - cout << "physical subplan : " << endl; + cout << "+++++++++++++++++++++physical subplan : " << endl; for (const auto& subplan : res_.physiSubplans_) { cout << subplan << endl; } diff --git a/source/libs/planner/test/planTestUtil.h b/source/libs/planner/test/planTestUtil.h index 5a2050a45e91d262751333e2f8dd12b02690af2e..6b75573fb3f86a91446ef3ad492a51703af4f00f 100644 --- a/source/libs/planner/test/planTestUtil.h +++ b/source/libs/planner/test/planTestUtil.h @@ -37,8 +37,9 @@ class PlannerTestBase : public testing::Test { std::unique_ptr impl_; }; -extern int32_t g_skipSql; - -extern void setDumpModule(const char* pModule); +extern void setDumpModule(const char* pModule); +extern void setSkipSqlNum(const char* pNum); +extern void setLogLevel(const char* pLogLevel); +extern int32_t getLogLevel(); #endif // PLAN_TEST_UTIL_H diff --git a/source/libs/qcom/src/queryUtil.c b/source/libs/qcom/src/queryUtil.c index 58886d706af0e1091ddb9ac805deb50510d85a37..f4ba2fca8146b37a3e25bc7173488dd85c8e48d0 100644 --- a/source/libs/qcom/src/queryUtil.c +++ b/source/libs/qcom/src/queryUtil.c @@ -58,7 +58,7 @@ static bool doValidateSchema(SSchema* pSchema, int32_t numOfCols, int32_t maxLen // 3. valid column names for (int32_t j = i + 1; j < numOfCols; ++j) { - if (strncasecmp(pSchema[i].name, pSchema[j].name, sizeof(pSchema[i].name) - 1) == 0) { + if (strncmp(pSchema[i].name, pSchema[j].name, sizeof(pSchema[i].name) - 1) == 0) { return false; } } diff --git a/source/libs/qworker/inc/qworkerInt.h b/source/libs/qworker/inc/qworkerInt.h index bcc17a9ae9c0bffe95e1df75746c36021f2d9a6e..511327658f14a58e25460f979a4ebb197c8d4b8c 100644 --- a/source/libs/qworker/inc/qworkerInt.h +++ b/source/libs/qworker/inc/qworkerInt.h @@ -131,6 +131,7 @@ typedef struct SQWTaskCtx { void *taskHandle; void *sinkHandle; SSubplan *plan; + STbVerInfo tbInfo; } SQWTaskCtx; typedef struct SQWSchStatus { diff --git a/source/libs/qworker/inc/qworkerMsg.h b/source/libs/qworker/inc/qworkerMsg.h index 93994c8287c9a5866a6dbc0969977e91f8997f15..6453cff70095b246f0ede7034da07536b1075f2f 100644 --- a/source/libs/qworker/inc/qworkerMsg.h +++ b/source/libs/qworker/inc/qworkerMsg.h @@ -36,7 +36,7 @@ int32_t qwBuildAndSendFetchRsp(SRpcHandleInfo *pConn, SRetrieveTableRsp *pRsp, i int32_t code); void qwBuildFetchRsp(void *msg, SOutputData *input, int32_t len, bool qComplete); int32_t qwBuildAndSendCQueryMsg(QW_FPARAMS_DEF, SRpcHandleInfo *pConn); -int32_t qwBuildAndSendReadyRsp(SRpcHandleInfo *pConn, int32_t code); +int32_t qwBuildAndSendReadyRsp(SRpcHandleInfo *pConn, int32_t code, STbVerInfo* tbInfo); int32_t qwBuildAndSendQueryRsp(SRpcHandleInfo *pConn, int32_t code); int32_t qwBuildAndSendExplainRsp(SRpcHandleInfo *pConn, SExplainExecInfo *execInfo, int32_t num); void qwFreeFetchRsp(void *msg); diff --git a/source/libs/qworker/src/qworker.c b/source/libs/qworker/src/qworker.c index db63c71d1123cec32e810a5583deb0a936688070..e7a680de3c0287be37dc5084715c05e4d1283088 100644 --- a/source/libs/qworker/src/qworker.c +++ b/source/libs/qworker/src/qworker.c @@ -528,20 +528,18 @@ int32_t qwDropTask(QW_FPARAMS_DEF) { } int32_t qwHandleTaskComplete(QW_FPARAMS_DEF, SQWTaskCtx *ctx) { - qTaskInfo_t *taskHandle = &ctx->taskHandle; + qTaskInfo_t taskHandle = ctx->taskHandle; - if (TASK_TYPE_TEMP == ctx->taskType) { + if (TASK_TYPE_TEMP == ctx->taskType && taskHandle) { if (ctx->explain) { SExplainExecInfo *execInfo = NULL; int32_t resNum = 0; - QW_ERR_RET(qGetExplainExecInfo(ctx->taskHandle, &resNum, &execInfo)); + QW_ERR_RET(qGetExplainExecInfo(taskHandle, &resNum, &execInfo)); SRpcHandleInfo connInfo = ctx->ctrlConnInfo; connInfo.ahandle = NULL; QW_ERR_RET(qwBuildAndSendExplainRsp(&connInfo, execInfo, resNum)); } - - qwFreeTaskHandle(QW_FPARAMS(), taskHandle); } return TSDB_CODE_SUCCESS; @@ -554,16 +552,21 @@ int32_t qwExecTask(QW_FPARAMS_DEF, SQWTaskCtx *ctx, bool *queryEnd) { uint64_t useconds = 0; int32_t i = 0; int32_t execNum = 0; - qTaskInfo_t *taskHandle = &ctx->taskHandle; + qTaskInfo_t taskHandle = ctx->taskHandle; DataSinkHandle sinkHandle = ctx->sinkHandle; while (true) { QW_TASK_DLOG("start to execTask, loopIdx:%d", i++); - code = qExecTask(*taskHandle, &pRes, &useconds); - if (code) { - QW_TASK_ELOG("qExecTask failed, code:%x - %s", code, tstrerror(code)); - QW_ERR_RET(code); + pRes = NULL; + + // if *taskHandle is NULL, it's killed right now + if (taskHandle) { + code = qExecTask(taskHandle, &pRes, &useconds); + if (code) { + QW_TASK_ELOG("qExecTask failed, code:%x - %s", code, tstrerror(code)); + QW_ERR_RET(code); + } } ++execNum; @@ -718,6 +721,20 @@ int32_t qwGetResFromSink(QW_FPARAMS_DEF, SQWTaskCtx *ctx, int32_t *dataLen, void return TSDB_CODE_SUCCESS; } + +void qwSaveTbVersionInfo(qTaskInfo_t pTaskInfo, SQWTaskCtx *ctx) { + char dbFName[TSDB_DB_FNAME_LEN]; + char tbName[TSDB_TABLE_NAME_LEN]; + + qGetQueriedTableSchemaVersion(pTaskInfo, dbFName, tbName, &ctx->tbInfo.sversion, &ctx->tbInfo.tversion); + + if (dbFName[0] && tbName[0]) { + sprintf(ctx->tbInfo.tbFName, "%s.%s", dbFName, tbName); + } else { + ctx->tbInfo.tbFName[0] = 0; + } +} + int32_t qwHandlePrePhaseEvents(QW_FPARAMS_DEF, int8_t phase, SQWPhaseInput *input, SQWPhaseOutput *output) { int32_t code = 0; SQWTaskCtx *ctx = NULL; @@ -899,6 +916,11 @@ _return: qwUpdateTaskStatus(QW_FPARAMS(), JOB_TASK_STATUS_PARTIAL_SUCCEED); } + if (readyConnection) { + qwBuildAndSendReadyRsp(readyConnection, code, ctx ? &ctx->tbInfo : NULL); + QW_TASK_DLOG("ready msg rsped, handle:%p, code:%x - %s", readyConnection->handle, code, tstrerror(code)); + } + if (ctx) { QW_UPDATE_RSP_CODE(ctx, code); @@ -910,11 +932,6 @@ _return: qwReleaseTaskCtx(mgmt, ctx); } - if (readyConnection) { - qwBuildAndSendReadyRsp(readyConnection, code); - QW_TASK_DLOG("ready msg rsped, handle:%p, code:%x - %s", readyConnection->handle, code, tstrerror(code)); - } - if (code) { qwUpdateTaskStatus(QW_FPARAMS(), JOB_TASK_STATUS_FAILED); } @@ -975,6 +992,7 @@ int32_t qwProcessQuery(QW_FPARAMS_DEF, SQWMsg *qwMsg, int8_t taskType, int8_t ex atomic_store_ptr(&ctx->sinkHandle, sinkHandle); if (pTaskInfo && sinkHandle) { + qwSaveTbVersionInfo(pTaskInfo, ctx); QW_ERR_JRET(qwExecTask(QW_FPARAMS(), ctx, NULL)); } @@ -1047,7 +1065,7 @@ _return: } if (needRsp) { - qwBuildAndSendReadyRsp(&qwMsg->connInfo, code); + qwBuildAndSendReadyRsp(&qwMsg->connInfo, code, NULL); QW_TASK_DLOG("ready msg rsped, handle:%p, code:%x - %s", qwMsg->connInfo.handle, code, tstrerror(code)); } @@ -1150,8 +1168,7 @@ int32_t qwProcessFetch(QW_FPARAMS_DEF, SQWMsg *qwMsg) { QW_ERR_JRET(qwGetResFromSink(QW_FPARAMS(), ctx, &dataLen, &rsp, &sOutput)); if (NULL == rsp) { - atomic_store_ptr(&ctx->dataConnInfo.handle, qwMsg->connInfo.handle); - atomic_store_ptr(&ctx->dataConnInfo.ahandle, qwMsg->connInfo.ahandle); + ctx->dataConnInfo = qwMsg->connInfo; QW_SET_EVENT_RECEIVED(ctx, QW_EVENT_FETCH); } else { diff --git a/source/libs/qworker/src/qworkerMsg.c b/source/libs/qworker/src/qworkerMsg.c index d502d952f31671679728c62180fddb8b29c40488..0a192eb795b689285831f366aff30af4a3743b27 100644 --- a/source/libs/qworker/src/qworkerMsg.c +++ b/source/libs/qworker/src/qworkerMsg.c @@ -47,7 +47,7 @@ int32_t qwBuildAndSendQueryRsp(SRpcHandleInfo *pConn, int32_t code) { SQueryTableRsp rsp = {.code = code}; int32_t contLen = tSerializeSQueryTableRsp(NULL, 0, &rsp); - void *msg = rpcMallocCont(contLen); + void * msg = rpcMallocCont(contLen); tSerializeSQueryTableRsp(msg, contLen, &rsp); SRpcMsg rpcRsp = { @@ -63,9 +63,14 @@ int32_t qwBuildAndSendQueryRsp(SRpcHandleInfo *pConn, int32_t code) { return TSDB_CODE_SUCCESS; } -int32_t qwBuildAndSendReadyRsp(SRpcHandleInfo *pConn, int32_t code) { +int32_t qwBuildAndSendReadyRsp(SRpcHandleInfo *pConn, int32_t code, STbVerInfo* tbInfo) { SResReadyRsp *pRsp = (SResReadyRsp *)rpcMallocCont(sizeof(SResReadyRsp)); pRsp->code = code; + if (tbInfo) { + strcpy(pRsp->tbFName, tbInfo->tbFName); + pRsp->sversion = tbInfo->sversion; + pRsp->tversion = tbInfo->tversion; + } SRpcMsg rpcRsp = { .msgType = TDMT_VND_RES_READY_RSP, @@ -85,7 +90,7 @@ int32_t qwBuildAndSendExplainRsp(SRpcHandleInfo *pConn, SExplainExecInfo *execIn SExplainRsp rsp = {.numOfPlans = num, .subplanInfo = execInfo}; int32_t contLen = tSerializeSExplainRsp(NULL, 0, &rsp); - void *pRsp = rpcMallocCont(contLen); + void * pRsp = rpcMallocCont(contLen); tSerializeSExplainRsp(pRsp, contLen, &rsp); SRpcMsg rpcRsp = { @@ -104,7 +109,7 @@ int32_t qwBuildAndSendExplainRsp(SRpcHandleInfo *pConn, SExplainExecInfo *execIn int32_t qwBuildAndSendHbRsp(SRpcHandleInfo *pConn, SSchedulerHbRsp *pStatus, int32_t code) { int32_t contLen = tSerializeSSchedulerHbRsp(NULL, 0, pStatus); - void *pRsp = rpcMallocCont(contLen); + void * pRsp = rpcMallocCont(contLen); tSerializeSSchedulerHbRsp(pRsp, contLen, pStatus); SRpcMsg rpcRsp = { @@ -212,7 +217,7 @@ int32_t qwBuildAndSendShowRsp(SRpcMsg *pMsg, int32_t code) { showRsp.tableMeta.numOfColumns = cols; int32_t bufLen = tSerializeSShowRsp(NULL, 0, &showRsp); - void *pBuf = rpcMallocCont(bufLen); + void * pBuf = rpcMallocCont(bufLen); tSerializeSShowRsp(pBuf, bufLen, &showRsp); SRpcMsg rpcMsg = { @@ -287,7 +292,7 @@ int32_t qwRegisterQueryBrokenLinkArg(QW_FPARAMS_DEF, SRpcHandleInfo *pConn) { req->taskId = htobe64(tId); req->refId = htobe64(rId); - SRpcMsg pMsg = { + SRpcMsg brokenMsg = { .msgType = TDMT_VND_DROP_TASK, .pCont = req, .contLen = sizeof(STaskDropReq), @@ -295,7 +300,7 @@ int32_t qwRegisterQueryBrokenLinkArg(QW_FPARAMS_DEF, SRpcHandleInfo *pConn) { .info = *pConn, }; - tmsgRegisterBrokenLinkArg(&pMsg); + tmsgRegisterBrokenLinkArg(&brokenMsg); return TSDB_CODE_SUCCESS; } @@ -321,7 +326,7 @@ int32_t qwRegisterHbBrokenLinkArg(SQWorker *mgmt, uint64_t sId, SRpcHandleInfo * QW_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); } - SRpcMsg pMsg = { + SRpcMsg brokenMsg = { .msgType = TDMT_VND_QUERY_HEARTBEAT, .pCont = msg, .contLen = msgSize, @@ -329,7 +334,7 @@ int32_t qwRegisterHbBrokenLinkArg(SQWorker *mgmt, uint64_t sId, SRpcHandleInfo * .info = *pConn, }; - tmsgRegisterBrokenLinkArg(&pMsg); + tmsgRegisterBrokenLinkArg(&brokenMsg); return TSDB_CODE_SUCCESS; } @@ -341,7 +346,7 @@ int32_t qWorkerProcessQueryMsg(void *node, void *qWorkerMgmt, SRpcMsg *pMsg) { int32_t code = 0; SSubQueryMsg *msg = pMsg->pCont; - SQWorker *mgmt = (SQWorker *)qWorkerMgmt; + SQWorker * mgmt = (SQWorker *)qWorkerMgmt; if (NULL == msg || pMsg->contLen <= sizeof(*msg)) { QW_ELOG("invalid query msg, msg:%p, msgLen:%d", msg, pMsg->contLen); @@ -361,7 +366,7 @@ int32_t qWorkerProcessQueryMsg(void *node, void *qWorkerMgmt, SRpcMsg *pMsg) { int64_t rId = msg->refId; SQWMsg qwMsg = {.node = node, .msg = msg->msg + msg->sqlLen, .msgLen = msg->phyLen, .connInfo = pMsg->info}; - char *sql = strndup(msg->msg, msg->sqlLen); + char * sql = strndup(msg->msg, msg->sqlLen); QW_SCH_TASK_DLOG("processQuery start, node:%p, handle:%p, sql:%s", node, pMsg->info.handle, sql); taosMemoryFreeClear(sql); @@ -378,8 +383,8 @@ int32_t qWorkerProcessCQueryMsg(void *node, void *qWorkerMgmt, SRpcMsg *pMsg) { bool queryDone = false; SQueryContinueReq *msg = (SQueryContinueReq *)pMsg->pCont; bool needStop = false; - SQWTaskCtx *handles = NULL; - SQWorker *mgmt = (SQWorker *)qWorkerMgmt; + SQWTaskCtx * handles = NULL; + SQWorker * mgmt = (SQWorker *)qWorkerMgmt; if (NULL == msg || pMsg->contLen < sizeof(*msg)) { QW_ELOG("invalid cquery msg, msg:%p, msgLen:%d", msg, pMsg->contLen); @@ -407,7 +412,7 @@ int32_t qWorkerProcessReadyMsg(void *node, void *qWorkerMgmt, SRpcMsg *pMsg) { return TSDB_CODE_QRY_INVALID_INPUT; } - SQWorker *mgmt = (SQWorker *)qWorkerMgmt; + SQWorker * mgmt = (SQWorker *)qWorkerMgmt; SResReadyReq *msg = pMsg->pCont; if (NULL == msg || pMsg->contLen < sizeof(*msg)) { QW_ELOG("invalid task ready msg, msg:%p, msgLen:%d", msg, pMsg->contLen); @@ -467,7 +472,7 @@ int32_t qWorkerProcessFetchMsg(void *node, void *qWorkerMgmt, SRpcMsg *pMsg) { } SResFetchReq *msg = pMsg->pCont; - SQWorker *mgmt = (SQWorker *)qWorkerMgmt; + SQWorker * mgmt = (SQWorker *)qWorkerMgmt; if (NULL == msg || pMsg->contLen < sizeof(*msg)) { QW_ELOG("invalid fetch msg, msg:%p, msgLen:%d", msg, pMsg->contLen); @@ -505,7 +510,7 @@ int32_t qWorkerProcessCancelMsg(void *node, void *qWorkerMgmt, SRpcMsg *pMsg) { return TSDB_CODE_QRY_INVALID_INPUT; } - SQWorker *mgmt = (SQWorker *)qWorkerMgmt; + SQWorker * mgmt = (SQWorker *)qWorkerMgmt; int32_t code = 0; STaskCancelReq *msg = pMsg->pCont; if (NULL == msg || pMsg->contLen < sizeof(*msg)) { @@ -542,7 +547,7 @@ int32_t qWorkerProcessDropMsg(void *node, void *qWorkerMgmt, SRpcMsg *pMsg) { int32_t code = 0; STaskDropReq *msg = pMsg->pCont; - SQWorker *mgmt = (SQWorker *)qWorkerMgmt; + SQWorker * mgmt = (SQWorker *)qWorkerMgmt; if (NULL == msg || pMsg->contLen < sizeof(*msg)) { QW_ELOG("invalid task drop msg, msg:%p, msgLen:%d", msg, pMsg->contLen); @@ -581,7 +586,7 @@ int32_t qWorkerProcessHbMsg(void *node, void *qWorkerMgmt, SRpcMsg *pMsg) { int32_t code = 0; SSchedulerHbReq req = {0}; - SQWorker *mgmt = (SQWorker *)qWorkerMgmt; + SQWorker * mgmt = (SQWorker *)qWorkerMgmt; if (NULL == pMsg->pCont) { QW_ELOG("invalid hb msg, msg:%p, msgLen:%d", pMsg->pCont, pMsg->contLen); diff --git a/source/libs/scalar/src/scalar.c b/source/libs/scalar/src/scalar.c index 7e3dbaf7d02595b04ea6a744660579e223ba29fc..fb03eaefa4fe79034d731b74de6bd166fa0db83e 100644 --- a/source/libs/scalar/src/scalar.c +++ b/source/libs/scalar/src/scalar.c @@ -182,6 +182,11 @@ int32_t sclCopyValueNodeValue(SValueNode *pNode, void **res) { int32_t sclInitParam(SNode* node, SScalarParam *param, SScalarCtx *ctx, int32_t *rowNum) { switch (nodeType(node)) { + case QUERY_NODE_LEFT_VALUE: { + SSDataBlock* pb = taosArrayGetP(ctx->pBlockList, 0); + param->numOfRows = pb->info.rows; + break; + } case QUERY_NODE_VALUE: { SValueNode *valueNode = (SValueNode *)node; @@ -845,7 +850,7 @@ EDealRes sclWalkTarget(SNode* pNode, SScalarCtx *ctx) { } EDealRes sclCalcWalker(SNode* pNode, void* pContext) { - if (QUERY_NODE_VALUE == nodeType(pNode) || QUERY_NODE_NODE_LIST == nodeType(pNode) || QUERY_NODE_COLUMN == nodeType(pNode)) { + if (QUERY_NODE_VALUE == nodeType(pNode) || QUERY_NODE_NODE_LIST == nodeType(pNode) || QUERY_NODE_COLUMN == nodeType(pNode)|| QUERY_NODE_LEFT_VALUE == nodeType(pNode)) { return DEAL_RES_CONTINUE; } @@ -899,7 +904,7 @@ int32_t scalarCalculate(SNode *pNode, SArray *pBlockList, SScalarParam *pDst) { } int32_t code = 0; - SScalarCtx ctx = {.code = 0, .pBlockList = pBlockList, .param = pDst->param}; + SScalarCtx ctx = {.code = 0, .pBlockList = pBlockList, .param = pDst ? pDst->param : NULL}; // TODO: OPT performance ctx.pRes = taosHashInit(SCL_DEFAULT_OP_NUM, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BIGINT), false, HASH_NO_LOCK); diff --git a/source/libs/scalar/src/sclfunc.c b/source/libs/scalar/src/sclfunc.c index 45742189d5e0585d68730a1c2f9843ecf58688b6..12496eec551c3793cede8b6e065b31d4e554052e 100644 --- a/source/libs/scalar/src/sclfunc.c +++ b/source/libs/scalar/src/sclfunc.c @@ -824,7 +824,7 @@ int32_t castFunction(SScalarParam *pInput, int32_t inputNum, SScalarParam *pOutp } //for constant conversion, need to set proper length of pOutput description if (len < outputLen) { - pOutput->columnData->info.bytes = len; + pOutput->columnData->info.bytes = len + VARSTR_HEADER_SIZE; } break; } diff --git a/source/libs/scalar/src/sclvector.c b/source/libs/scalar/src/sclvector.c index 19453bf7600128de3abd8a5f096ebf9c023b62ec..0fb3712c30bb349a406a92f14346370f80112ae6 100644 --- a/source/libs/scalar/src/sclvector.c +++ b/source/libs/scalar/src/sclvector.c @@ -922,7 +922,8 @@ static void doReleaseVec(SColumnInfoData* pCol, int32_t type) { } } -char *getJsonValue(char *json, char *key){ //todo +char *getJsonValue(char *json, char *key){ //todo + json++; // jump type int16_t cols = kvRowNCols(json); for (int i = 0; i < cols; ++i) { SColIdx *pColIdx = kvRowColIdxAt(json, i); @@ -1332,6 +1333,22 @@ void vectorMathMinus(SScalarParam* pLeft, SScalarParam* pRight, SScalarParam *pO doReleaseVec(pLeftCol, leftConvert); } +void vectorAssign(SScalarParam* pLeft, SScalarParam* pRight, SScalarParam *pOut, int32_t _ord) { + SColumnInfoData *pOutputCol = pOut->columnData; + + pOut->numOfRows = pLeft->numOfRows; + + if (colDataIsNull_s(pRight->columnData, 0)) { + for (int32_t i = 0; i < pOut->numOfRows; ++i) { + colDataAppend(pOutputCol, i, NULL, true); + } + } else { + for (int32_t i = 0; i < pOut->numOfRows; ++i) { + colDataAppend(pOutputCol, i, colDataGetData(pRight->columnData, 0), false); + } + } +} + void vectorConcat(SScalarParam* pLeft, SScalarParam* pRight, void *out, int32_t _ord) { #if 0 int32_t len = pLeft->bytes + pRight->bytes; @@ -1690,6 +1707,8 @@ _bin_scalar_fn_t getBinScalarOperatorFn(int32_t binFunctionId) { return vectorMathRemainder; case OP_TYPE_MINUS: return vectorMathMinus; + case OP_TYPE_ASSIGN: + return vectorAssign; case OP_TYPE_GREATER_THAN: return vectorGreater; case OP_TYPE_GREATER_EQUAL: diff --git a/source/libs/scalar/test/scalar/CMakeLists.txt b/source/libs/scalar/test/scalar/CMakeLists.txt index 866297b017abcfa253638a46bd51952a84c02410..15d1c2cb4424fded0b04d1c82504768d57b21807 100644 --- a/source/libs/scalar/test/scalar/CMakeLists.txt +++ b/source/libs/scalar/test/scalar/CMakeLists.txt @@ -17,7 +17,7 @@ TARGET_INCLUDE_DIRECTORIES( PUBLIC "${TD_SOURCE_DIR}/source/libs/parser/inc" PRIVATE "${TD_SOURCE_DIR}/source/libs/scalar/inc" ) -#add_test( -# NAME scalarTest -# COMMAND scalarTest -#) +add_test( + NAME scalarTest + COMMAND scalarTest +) diff --git a/source/libs/scalar/test/scalar/scalarTests.cpp b/source/libs/scalar/test/scalar/scalarTests.cpp index 49a5f5b9a46a7bdc87c093969f9612dfa5ac1d81..3fafc83b18365d490003a792748606c8d4fce804 100644 --- a/source/libs/scalar/test/scalar/scalarTests.cpp +++ b/source/libs/scalar/test/scalar/scalarTests.cpp @@ -1035,7 +1035,7 @@ void makeJsonArrow(SSDataBlock **src, SNode **opNode, void *json, char *key){ SNode *pLeft = NULL, *pRight = NULL; scltMakeValueNode(&pRight, TSDB_DATA_TYPE_BINARY, keyVar); - scltMakeColumnNode(&pLeft, src, TSDB_DATA_TYPE_JSON, varDataLen(json), 1, json); + scltMakeColumnNode(&pLeft, src, TSDB_DATA_TYPE_JSON, kvRowLen(json), 1, json); scltMakeOpNode(opNode, OP_TYPE_JSON_GET_VALUE, TSDB_DATA_TYPE_JSON, pLeft, pRight); } @@ -1088,18 +1088,17 @@ void makeCalculate(void *json, void *key, int32_t rightType, void *rightData, do }else if(opType == OP_TYPE_ADD || opType == OP_TYPE_SUB || opType == OP_TYPE_MULTI || opType == OP_TYPE_DIV || opType == OP_TYPE_MOD || opType == OP_TYPE_MINUS){ - double tmp = *((double *)colDataGetData(column, 0)); - ASSERT_TRUE(tmp == exceptValue); - printf("result:%lf\n", tmp); + printf("1result:%f,except:%f\n", *((double *)colDataGetData(column, 0)), exceptValue); + ASSERT_TRUE(fabs(*((double *)colDataGetData(column, 0)) - exceptValue) < 0.0001); }else if(opType == OP_TYPE_BIT_AND || opType == OP_TYPE_BIT_OR){ + printf("2result:%ld,except:%f\n", *((int64_t *)colDataGetData(column, 0)), exceptValue); ASSERT_EQ(*((int64_t *)colDataGetData(column, 0)), exceptValue); - printf("result:%ld\n", *((int64_t *)colDataGetData(column, 0))); }else if(opType == OP_TYPE_GREATER_THAN || opType == OP_TYPE_GREATER_EQUAL || opType == OP_TYPE_LOWER_THAN || opType == OP_TYPE_LOWER_EQUAL || opType == OP_TYPE_EQUAL || opType == OP_TYPE_NOT_EQUAL || opType == OP_TYPE_IS_NULL || opType == OP_TYPE_IS_NOT_NULL || opType == OP_TYPE_IS_TRUE || opType == OP_TYPE_LIKE || opType == OP_TYPE_NOT_LIKE || opType == OP_TYPE_MATCH || opType == OP_TYPE_NMATCH){ + printf("3result:%d,except:%f\n", *((bool *)colDataGetData(column, 0)), exceptValue); ASSERT_EQ(*((bool *)colDataGetData(column, 0)), exceptValue); - printf("result:%d\n", *((bool *)colDataGetData(column, 0))); } taosArrayDestroyEx(blockList, scltFreeDataBlock); @@ -1108,12 +1107,21 @@ void makeCalculate(void *json, void *key, int32_t rightType, void *rightData, do TEST(columnTest, json_column_arith_op) { scltInitLogFile(); - char *rightv= "{\"k1\":4,\"k2\":\"hello\",\"k3\":null,\"k4\":true,\"k5\":5.44}"; + char *rightvTmp= "{\"k1\":4,\"k2\":\"hello\",\"k3\":null,\"k4\":true,\"k5\":5.44}"; + char rightv[256] = {0}; + memcpy(rightv, rightvTmp, strlen(rightvTmp)); SKVRowBuilder kvRowBuilder; tdInitKVRowBuilder(&kvRowBuilder); parseJsontoTagData(rightv, &kvRowBuilder, NULL, 0); SKVRow row = tdGetKVRowFromBuilder(&kvRowBuilder); + char *tmp = (char *)taosMemoryRealloc(row, kvRowLen(row)+1); + if(tmp == NULL){ + ASSERT_TRUE(0); + } + memmove(tmp+1, tmp, kvRowLen(tmp)); + *tmp = TSDB_DATA_TYPE_JSON; + row = tmp; const int32_t len = 8; EOperatorType op[len] = {OP_TYPE_ADD, OP_TYPE_SUB, OP_TYPE_MULTI, OP_TYPE_DIV, @@ -1166,6 +1174,9 @@ TEST(columnTest, json_column_arith_op) { for(int i = 0; i < len; i++){ makeCalculate(row, key, TSDB_DATA_TYPE_INT, &input[i], eRes5[i], op[i]); } + + tdDestroyKVRowBuilder(&kvRowBuilder); + taosMemoryFree(row); } void *prepareNchar(char* rightData){ @@ -1180,12 +1191,21 @@ void *prepareNchar(char* rightData){ TEST(columnTest, json_column_logic_op) { scltInitLogFile(); - char *rightv= "{\"k1\":4,\"k2\":\"hello\",\"k3\":null,\"k4\":true,\"k5\":5.44,\"k6\":\"6.6hello\"}"; + char *rightvTmp= "{\"k1\":4,\"k2\":\"hello\",\"k3\":null,\"k4\":true,\"k5\":5.44,\"k6\":\"6.6hello\"}"; + char rightv[256] = {0}; + memcpy(rightv, rightvTmp, strlen(rightvTmp)); SKVRowBuilder kvRowBuilder; tdInitKVRowBuilder(&kvRowBuilder); parseJsontoTagData(rightv, &kvRowBuilder, NULL, 0); SKVRow row = tdGetKVRowFromBuilder(&kvRowBuilder); + char *tmp = (char *)taosMemoryRealloc(row, kvRowLen(row)+1); + if(tmp == NULL){ + ASSERT_TRUE(0); + } + memmove(tmp+1, tmp, kvRowLen(tmp)); + *tmp = TSDB_DATA_TYPE_JSON; + row = tmp; const int32_t len = 9; const int32_t len1 = 4; @@ -1222,8 +1242,8 @@ TEST(columnTest, json_column_logic_op) { printf("--------------------json null---------------------\n"); - key = "k3"; - double eRes2[len+len1] = {DBL_MAX, DBL_MAX, DBL_MAX, DBL_MAX, DBL_MAX, DBL_MAX, true, false, DBL_MAX, DBL_MAX, DBL_MAX, DBL_MAX, DBL_MAX}; + key = "k3"; // (null is true) return NULL, so use DBL_MAX represent NULL + double eRes2[len+len1] = {false, false, false, false, false, false, true, false, DBL_MAX, false, false, false, false}; for(int i = 0; i < len; i++){ makeCalculate(row, key, TSDB_DATA_TYPE_INT, &input[i], eRes2[i], op[i]); } @@ -1262,7 +1282,7 @@ TEST(columnTest, json_column_logic_op) { printf("--------------------json double---------------------\n"); key = "k6"; - bool eRes5[len+len1] = {true, false, false, false, false, true, false, true, true, false, false, false, true}; + bool eRes5[len+len1] = {true, false, false, false, false, true, false, true, true, false, true, false, true}; for(int i = 0; i < len; i++){ makeCalculate(row, key, TSDB_DATA_TYPE_INT, &input[i], eRes5[i], op[i]); } @@ -1274,8 +1294,8 @@ TEST(columnTest, json_column_logic_op) { printf("---------------------json not exist--------------------\n"); - key = "k10"; - double eRes10[len+len1] = {DBL_MAX, DBL_MAX, DBL_MAX, DBL_MAX, DBL_MAX, DBL_MAX, true, false, DBL_MAX, DBL_MAX, DBL_MAX, DBL_MAX, DBL_MAX}; + key = "k10"; // (NULL is true) return NULL, so use DBL_MAX represent NULL + double eRes10[len+len1] = {false, false, false, false, false, false, true, false, DBL_MAX, false, false, false, false}; for(int i = 0; i < len; i++){ makeCalculate(row, key, TSDB_DATA_TYPE_INT, &input[i], eRes10[i], op[i]); } @@ -1284,6 +1304,9 @@ TEST(columnTest, json_column_logic_op) { makeCalculate(row, key, TSDB_DATA_TYPE_NCHAR, rightData, eRes10[i], op[i]); taosMemoryFree(rightData); } + + tdDestroyKVRowBuilder(&kvRowBuilder); + taosMemoryFree(row); } TEST(columnTest, smallint_value_add_int_column) { @@ -3437,7 +3460,7 @@ TEST(ScalarFunctionTest, powFunction_column) { //TINYINT AND FLOAT int8_t param0[] = {2, 3, 4}; - float param1[] = {3.0, 3.0, 2.0}; + float param1[] = {3.0, 3.0, 3.0}; scltMakeDataBlock(&input[0], TSDB_DATA_TYPE_TINYINT, 0, rowNum, false); pInput[0] = *input[0]; for (int32_t i = 0; i < rowNum; ++i) { diff --git a/source/libs/scheduler/inc/schedulerInt.h b/source/libs/scheduler/inc/schedulerInt.h index 5a6fcee759d86ce85c9d67eb28cb5950401b9857..ffac0f856dbebf7b04b44c0cdfad8df75021e650 100644 --- a/source/libs/scheduler/inc/schedulerInt.h +++ b/source/libs/scheduler/inc/schedulerInt.h @@ -39,12 +39,6 @@ enum { SCH_WRITE, }; -typedef enum { - SCH_RES_TYPE_QUERY, - SCH_RES_TYPE_FETCH, -} SCH_RES_TYPE; - - typedef struct SSchTrans { void *transInst; void *transHandle; @@ -138,7 +132,7 @@ typedef struct SSchLevel { int32_t taskSucceed; int32_t taskNum; int32_t taskLaunchedNum; - SHashObj *flowCtrl; // key is ep, element is SSchFlowControl + int32_t taskDoneNum; SArray *subTasks; // Element is SQueryTask } SSchLevel; @@ -181,11 +175,13 @@ typedef struct SSchJob { SArray *levels; // starting from 0. SArray SNodeList *subPlans; // subplan pointer copied from DAG, no need to free it in scheduler + SArray *dataSrcTasks; // SArray int32_t levelIdx; SEpSet dataSrcEps; SHashObj *execTasks; // executing tasks, key:taskid, value:SQueryTask* SHashObj *succTasks; // succeed tasks, key:taskid, value:SQueryTask* SHashObj *failTasks; // failed tasks, key:taskid, value:SQueryTask* + SHashObj *flowCtrl; // key is ep, element is SSchFlowControl SExplainCtx *explainCtx; int8_t status; @@ -197,7 +193,7 @@ typedef struct SSchJob { int32_t errCode; SArray *errList; // SArray SRWLatch resLock; - SCH_RES_TYPE resType; + void *queryRes; void *resData; //TODO free it or not int32_t resNumOfRows; const char *sql; @@ -206,7 +202,7 @@ typedef struct SSchJob { extern SSchedulerMgmt schMgmt; -#define SCH_TASK_READY_TO_LUNCH(readyNum, task) ((readyNum) >= taosArrayGetSize((task)->children)) +#define SCH_TASK_READY_FOR_LAUNCH(readyNum, task) ((readyNum) >= taosArrayGetSize((task)->children)) #define SCH_TASK_ID(_task) ((_task) ? (_task)->taskId : -1) #define SCH_SET_TASK_LASTMSG_TYPE(_task, _type) do { if(_task) { atomic_store_32(&(_task)->lastMsgType, _type); } } while (0) @@ -229,7 +225,7 @@ extern SSchedulerMgmt schMgmt; #define SCH_SET_JOB_NEED_FLOW_CTRL(_job) (_job)->attr.needFlowCtrl = true #define SCH_JOB_NEED_FLOW_CTRL(_job) ((_job)->attr.needFlowCtrl) -#define SCH_TASK_NEED_FLOW_CTRL(_job, _task) (SCH_IS_DATA_SRC_QRY_TASK(_task) && SCH_JOB_NEED_FLOW_CTRL(_job) && SCH_IS_LEAF_TASK(_job, _task) && SCH_IS_LEVEL_UNFINISHED((_task)->level)) +#define SCH_TASK_NEED_FLOW_CTRL(_job, _task) (SCH_IS_DATA_SRC_QRY_TASK(_task) && SCH_JOB_NEED_FLOW_CTRL(_job) && SCH_IS_LEVEL_UNFINISHED((_task)->level)) #define SCH_SET_JOB_TYPE(_job, type) (_job)->attr.queryJob = ((type) != SUBPLAN_TYPE_MODIFY) #define SCH_IS_QUERY_JOB(_job) ((_job)->attr.queryJob) @@ -267,8 +263,8 @@ int32_t schLaunchTask(SSchJob *job, SSchTask *task); int32_t schBuildAndSendMsg(SSchJob *job, SSchTask *task, SQueryNodeAddr *addr, int32_t msgType); SSchJob *schAcquireJob(int64_t refId); int32_t schReleaseJob(int64_t refId); -void schFreeFlowCtrl(SSchLevel *pLevel); -int32_t schCheckJobNeedFlowCtrl(SSchJob *pJob, SSchLevel *pLevel); +void schFreeFlowCtrl(SSchJob *pJob); +int32_t schChkJobNeedFlowCtrl(SSchJob *pJob, SSchLevel *pLevel); int32_t schDecTaskFlowQuota(SSchJob *pJob, SSchTask *pTask); int32_t schCheckIncTaskFlowQuota(SSchJob *pJob, SSchTask *pTask, bool *enough); int32_t schLaunchTasksInFlowCtrlList(SSchJob *pJob, SSchTask *pTask); @@ -279,6 +275,32 @@ int32_t schBuildAndSendHbMsg(SQueryNodeEpId *nodeEpId); int32_t schCloneSMsgSendInfo(void *src, void **dst); int32_t schValidateAndBuildJob(SQueryPlan *pDag, SSchJob *pJob); void schFreeJobImpl(void *job); +int32_t schMakeHbCallbackParam(SSchJob *pJob, SSchTask *pTask, void **pParam); +int32_t schMakeHbRpcCtx(SSchJob *pJob, SSchTask *pTask, SRpcCtx *pCtx); +int32_t schEnsureHbConnection(SSchJob *pJob, SSchTask *pTask); +int32_t schUpdateHbConnection(SQueryNodeEpId *epId, SSchTrans *trans); +int32_t schHandleHbCallback(void *param, const SDataBuf *pMsg, int32_t code); +void schFreeRpcCtx(SRpcCtx *pCtx); +int32_t schGetCallbackFp(int32_t msgType, __async_send_cb_fn_t *fp); +bool schJobNeedToStop(SSchJob *pJob, int8_t *pStatus); +int32_t schProcessOnTaskSuccess(SSchJob *pJob, SSchTask *pTask); +int32_t schSaveJobQueryRes(SSchJob *pJob, SResReadyRsp *rsp); +int32_t schProcessOnExplainDone(SSchJob *pJob, SSchTask *pTask, SRetrieveTableRsp *pRsp); +void schProcessOnDataFetched(SSchJob *job); +int32_t schGetTaskFromTaskList(SHashObj *pTaskList, uint64_t taskId, SSchTask **pTask); +int32_t schUpdateTaskExecNodeHandle(SSchTask *pTask, void *handle, int32_t rspCode); +void schFreeRpcCtxVal(const void *arg); +int32_t schMakeBrokenLinkVal(SSchJob *pJob, SSchTask *pTask, SRpcBrokenlinkVal *brokenVal, bool isHb); +int32_t schRecordTaskExecNode(SSchJob *pJob, SSchTask *pTask, SQueryNodeAddr *addr, void *handle); +int32_t schExecStaticExplain(void *transport, SArray *pNodeList, SQueryPlan *pDag, int64_t *job, const char *sql, + bool syncSchedule); +int32_t schExecJobImpl(void *transport, SArray *pNodeList, SQueryPlan *pDag, int64_t *job, const char *sql, + int64_t startTs, bool sync); +int32_t schChkUpdateJobStatus(SSchJob *pJob, int8_t newStatus); +int32_t schCancelJob(SSchJob *pJob); +int32_t schProcessOnJobDropped(SSchJob *pJob, int32_t errCode); +uint64_t schGenTaskId(void); +void schCloseJobRef(void); #ifdef __cplusplus diff --git a/source/libs/scheduler/src/schFlowCtrl.c b/source/libs/scheduler/src/schFlowCtrl.c index 993521da8722afd2363e47d65cc48b51526968e6..85d205f5f2eba00a2f6bd741b891b26f30488d05 100644 --- a/source/libs/scheduler/src/schFlowCtrl.c +++ b/source/libs/scheduler/src/schFlowCtrl.c @@ -19,13 +19,13 @@ #include "catalog.h" #include "tref.h" -void schFreeFlowCtrl(SSchLevel *pLevel) { - if (NULL == pLevel->flowCtrl) { +void schFreeFlowCtrl(SSchJob *pJob) { + if (NULL == pJob->flowCtrl) { return; } SSchFlowControl *ctrl = NULL; - void *pIter = taosHashIterate(pLevel->flowCtrl, NULL); + void *pIter = taosHashIterate(pJob->flowCtrl, NULL); while (pIter) { ctrl = (SSchFlowControl *)pIter; @@ -33,23 +33,23 @@ void schFreeFlowCtrl(SSchLevel *pLevel) { taosArrayDestroy(ctrl->taskList); } - pIter = taosHashIterate(pLevel->flowCtrl, pIter); + pIter = taosHashIterate(pJob->flowCtrl, pIter); } - taosHashCleanup(pLevel->flowCtrl); - pLevel->flowCtrl = NULL; + taosHashCleanup(pJob->flowCtrl); + pJob->flowCtrl = NULL; } -int32_t schCheckJobNeedFlowCtrl(SSchJob *pJob, SSchLevel *pLevel) { +int32_t schChkJobNeedFlowCtrl(SSchJob *pJob, SSchLevel *pLevel) { if (!SCH_IS_QUERY_JOB(pJob)) { SCH_JOB_DLOG("job no need flow ctrl, queryJob:%d", SCH_IS_QUERY_JOB(pJob)); return TSDB_CODE_SUCCESS; } int32_t sum = 0; - - for (int32_t i = 0; i < pLevel->taskNum; ++i) { - SSchTask *pTask = taosArrayGet(pLevel->subTasks, i); + int32_t taskNum = taosArrayGetSize(pJob->dataSrcTasks); + for (int32_t i = 0; i < taskNum; ++i) { + SSchTask *pTask = *(SSchTask **)taosArrayGet(pJob->dataSrcTasks, i); sum += pTask->plan->execNodeStat.tableNum; } @@ -59,9 +59,9 @@ int32_t schCheckJobNeedFlowCtrl(SSchJob *pJob, SSchLevel *pLevel) { return TSDB_CODE_SUCCESS; } - pLevel->flowCtrl = taosHashInit(pLevel->taskNum, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY), false, HASH_ENTRY_LOCK); - if (NULL == pLevel->flowCtrl) { - SCH_JOB_ELOG("taosHashInit %d flowCtrl failed", pLevel->taskNum); + pJob->flowCtrl = taosHashInit(pJob->taskNum, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY), false, HASH_ENTRY_LOCK); + if (NULL == pJob->flowCtrl) { + SCH_JOB_ELOG("taosHashInit %d flowCtrl failed", pJob->taskNum); SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); } @@ -78,7 +78,7 @@ int32_t schDecTaskFlowQuota(SSchJob *pJob, SSchTask *pTask) { int32_t code = 0; SEp *ep = SCH_GET_CUR_EP(&pTask->plan->execNode); - ctrl = (SSchFlowControl *)taosHashGet(pLevel->flowCtrl, ep, sizeof(SEp)); + ctrl = (SSchFlowControl *)taosHashGet(pJob->flowCtrl, ep, sizeof(SEp)); if (NULL == ctrl) { SCH_TASK_ELOG("taosHashGet node from flowCtrl failed, fqdn:%s, port:%d", ep->fqdn, ep->port); SCH_ERR_RET(TSDB_CODE_SCH_INTERNAL_ERROR); @@ -110,11 +110,11 @@ int32_t schCheckIncTaskFlowQuota(SSchJob *pJob, SSchTask *pTask, bool *enough) { SEp *ep = SCH_GET_CUR_EP(&pTask->plan->execNode); do { - ctrl = (SSchFlowControl *)taosHashGet(pLevel->flowCtrl, ep, sizeof(SEp)); + ctrl = (SSchFlowControl *)taosHashGet(pJob->flowCtrl, ep, sizeof(SEp)); if (NULL == ctrl) { SSchFlowControl nctrl = {.tableNumSum = pTask->plan->execNodeStat.tableNum, .execTaskNum = 1}; - code = taosHashPut(pLevel->flowCtrl, ep, sizeof(SEp), &nctrl, sizeof(nctrl)); + code = taosHashPut(pJob->flowCtrl, ep, sizeof(SEp), &nctrl, sizeof(nctrl)); if (code) { if (HASH_NODE_EXIST(code)) { continue; @@ -273,10 +273,9 @@ int32_t schLaunchTasksInFlowCtrlList(SSchJob *pJob, SSchTask *pTask) { SCH_ERR_RET(schDecTaskFlowQuota(pJob, pTask)); - SSchLevel *pLevel = pTask->level; SEp *ep = SCH_GET_CUR_EP(&pTask->plan->execNode); - SSchFlowControl *ctrl = (SSchFlowControl *)taosHashGet(pLevel->flowCtrl, ep, sizeof(SEp)); + SSchFlowControl *ctrl = (SSchFlowControl *)taosHashGet(pJob->flowCtrl, ep, sizeof(SEp)); if (NULL == ctrl) { SCH_TASK_ELOG("taosHashGet node from flowCtrl failed, fqdn:%s, port:%d", ep->fqdn, ep->port); SCH_ERR_RET(TSDB_CODE_SCH_INTERNAL_ERROR); diff --git a/source/libs/scheduler/src/schJob.c b/source/libs/scheduler/src/schJob.c new file mode 100644 index 0000000000000000000000000000000000000000..14f46463971fd103b1cf79a1c6bddd9ee0efa85c --- /dev/null +++ b/source/libs/scheduler/src/schJob.c @@ -0,0 +1,1312 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "catalog.h" +#include "command.h" +#include "query.h" +#include "schedulerInt.h" +#include "tmsg.h" +#include "tref.h" +#include "trpc.h" + +FORCE_INLINE SSchJob *schAcquireJob(int64_t refId) { return (SSchJob *)taosAcquireRef(schMgmt.jobRef, refId); } + +FORCE_INLINE int32_t schReleaseJob(int64_t refId) { return taosReleaseRef(schMgmt.jobRef, refId); } + +int32_t schInitTask(SSchJob *pJob, SSchTask *pTask, SSubplan *pPlan, SSchLevel *pLevel) { + pTask->plan = pPlan; + pTask->level = pLevel; + SCH_SET_TASK_STATUS(pTask, JOB_TASK_STATUS_NOT_START); + pTask->taskId = schGenTaskId(); + pTask->execNodes = taosArrayInit(SCH_MAX_CANDIDATE_EP_NUM, sizeof(SSchNodeInfo)); + if (NULL == pTask->execNodes) { + SCH_TASK_ELOG("taosArrayInit %d execNodes failed", SCH_MAX_CANDIDATE_EP_NUM); + SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + return TSDB_CODE_SUCCESS; +} + +int32_t schInitJob(SSchJob **pSchJob, SQueryPlan *pDag, void *transport, SArray *pNodeList, const char *sql, + int64_t startTs, bool syncSchedule) { + int32_t code = 0; + int64_t refId = -1; + SSchJob *pJob = taosMemoryCalloc(1, sizeof(SSchJob)); + if (NULL == pJob) { + qError("QID:%" PRIx64 " calloc %d failed", pDag->queryId, (int32_t)sizeof(SSchJob)); + SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + pJob->attr.explainMode = pDag->explainInfo.mode; + pJob->attr.syncSchedule = syncSchedule; + pJob->transport = transport; + pJob->sql = sql; + + if (pNodeList != NULL) { + pJob->nodeList = taosArrayDup(pNodeList); + } + + SCH_ERR_JRET(schValidateAndBuildJob(pDag, pJob)); + + if (SCH_IS_EXPLAIN_JOB(pJob)) { + SCH_ERR_JRET(qExecExplainBegin(pDag, &pJob->explainCtx, startTs)); + } + + pJob->execTasks = + taosHashInit(pDag->numOfSubplans, taosGetDefaultHashFunction(TSDB_DATA_TYPE_UBIGINT), false, HASH_ENTRY_LOCK); + if (NULL == pJob->execTasks) { + SCH_JOB_ELOG("taosHashInit %d execTasks failed", pDag->numOfSubplans); + SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + pJob->succTasks = + taosHashInit(pDag->numOfSubplans, taosGetDefaultHashFunction(TSDB_DATA_TYPE_UBIGINT), false, HASH_ENTRY_LOCK); + if (NULL == pJob->succTasks) { + SCH_JOB_ELOG("taosHashInit %d succTasks failed", pDag->numOfSubplans); + SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + pJob->failTasks = + taosHashInit(pDag->numOfSubplans, taosGetDefaultHashFunction(TSDB_DATA_TYPE_UBIGINT), false, HASH_ENTRY_LOCK); + if (NULL == pJob->failTasks) { + SCH_JOB_ELOG("taosHashInit %d failTasks failed", pDag->numOfSubplans); + SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + tsem_init(&pJob->rspSem, 0, 0); + + refId = taosAddRef(schMgmt.jobRef, pJob); + if (refId < 0) { + SCH_JOB_ELOG("taosAddRef job failed, error:%s", tstrerror(terrno)); + SCH_ERR_JRET(terrno); + } + + atomic_add_fetch_32(&schMgmt.jobNum, 1); + + if (NULL == schAcquireJob(refId)) { + SCH_JOB_ELOG("schAcquireJob job failed, refId:%" PRIx64, refId); + SCH_ERR_JRET(TSDB_CODE_SCH_STATUS_ERROR); + } + + pJob->refId = refId; + + SCH_JOB_DLOG("job refId:%" PRIx64, pJob->refId); + + pJob->status = JOB_TASK_STATUS_NOT_START; + + *pSchJob = pJob; + + return TSDB_CODE_SUCCESS; + +_return: + + if (refId < 0) { + schFreeJobImpl(pJob); + } else { + taosRemoveRef(schMgmt.jobRef, refId); + } + SCH_RET(code); +} + +void schFreeTask(SSchTask *pTask) { + if (pTask->candidateAddrs) { + taosArrayDestroy(pTask->candidateAddrs); + } + + taosMemoryFreeClear(pTask->msg); + + if (pTask->children) { + taosArrayDestroy(pTask->children); + } + + if (pTask->parents) { + taosArrayDestroy(pTask->parents); + } + + if (pTask->execNodes) { + taosArrayDestroy(pTask->execNodes); + } +} + +FORCE_INLINE bool schJobNeedToStop(SSchJob *pJob, int8_t *pStatus) { + int8_t status = SCH_GET_JOB_STATUS(pJob); + if (pStatus) { + *pStatus = status; + } + + return (status == JOB_TASK_STATUS_FAILED || status == JOB_TASK_STATUS_CANCELLED || + status == JOB_TASK_STATUS_CANCELLING || status == JOB_TASK_STATUS_DROPPING || + status == JOB_TASK_STATUS_SUCCEED); +} + +int32_t schChkUpdateJobStatus(SSchJob *pJob, int8_t newStatus) { + int32_t code = 0; + + int8_t oriStatus = 0; + + while (true) { + oriStatus = SCH_GET_JOB_STATUS(pJob); + + if (oriStatus == newStatus) { + SCH_ERR_JRET(TSDB_CODE_QRY_APP_ERROR); + } + + switch (oriStatus) { + case JOB_TASK_STATUS_NULL: + if (newStatus != JOB_TASK_STATUS_NOT_START) { + SCH_ERR_JRET(TSDB_CODE_QRY_APP_ERROR); + } + + break; + case JOB_TASK_STATUS_NOT_START: + if (newStatus != JOB_TASK_STATUS_EXECUTING) { + SCH_ERR_JRET(TSDB_CODE_QRY_APP_ERROR); + } + + break; + case JOB_TASK_STATUS_EXECUTING: + if (newStatus != JOB_TASK_STATUS_PARTIAL_SUCCEED && newStatus != JOB_TASK_STATUS_FAILED && + newStatus != JOB_TASK_STATUS_CANCELLING && newStatus != JOB_TASK_STATUS_CANCELLED && + newStatus != JOB_TASK_STATUS_DROPPING) { + SCH_ERR_JRET(TSDB_CODE_QRY_APP_ERROR); + } + + break; + case JOB_TASK_STATUS_PARTIAL_SUCCEED: + if (newStatus != JOB_TASK_STATUS_FAILED && newStatus != JOB_TASK_STATUS_SUCCEED && + newStatus != JOB_TASK_STATUS_DROPPING) { + SCH_ERR_JRET(TSDB_CODE_QRY_APP_ERROR); + } + + break; + case JOB_TASK_STATUS_SUCCEED: + case JOB_TASK_STATUS_FAILED: + case JOB_TASK_STATUS_CANCELLING: + if (newStatus != JOB_TASK_STATUS_DROPPING) { + SCH_ERR_JRET(TSDB_CODE_QRY_APP_ERROR); + } + + break; + case JOB_TASK_STATUS_CANCELLED: + case JOB_TASK_STATUS_DROPPING: + SCH_ERR_JRET(TSDB_CODE_QRY_JOB_FREED); + break; + + default: + SCH_JOB_ELOG("invalid job status:%s", jobTaskStatusStr(oriStatus)); + SCH_ERR_JRET(TSDB_CODE_QRY_APP_ERROR); + } + + if (oriStatus != atomic_val_compare_exchange_8(&pJob->status, oriStatus, newStatus)) { + continue; + } + + SCH_JOB_DLOG("job status updated from %s to %s", jobTaskStatusStr(oriStatus), jobTaskStatusStr(newStatus)); + + break; + } + + return TSDB_CODE_SUCCESS; + +_return: + + SCH_JOB_ELOG("invalid job status update, from %s to %s", jobTaskStatusStr(oriStatus), jobTaskStatusStr(newStatus)); + SCH_ERR_RET(code); + return TSDB_CODE_SUCCESS; +} + +int32_t schBuildTaskRalation(SSchJob *pJob, SHashObj *planToTask) { + for (int32_t i = 0; i < pJob->levelNum; ++i) { + SSchLevel *pLevel = taosArrayGet(pJob->levels, i); + + for (int32_t m = 0; m < pLevel->taskNum; ++m) { + SSchTask *pTask = taosArrayGet(pLevel->subTasks, m); + SSubplan *pPlan = pTask->plan; + int32_t childNum = pPlan->pChildren ? (int32_t)LIST_LENGTH(pPlan->pChildren) : 0; + int32_t parentNum = pPlan->pParents ? (int32_t)LIST_LENGTH(pPlan->pParents) : 0; + + if (childNum > 0) { + if (pJob->levelIdx == pLevel->level) { + SCH_JOB_ELOG("invalid query plan, lowest level, childNum:%d", childNum); + SCH_ERR_RET(TSDB_CODE_SCH_INTERNAL_ERROR); + } + + pTask->children = taosArrayInit(childNum, POINTER_BYTES); + if (NULL == pTask->children) { + SCH_TASK_ELOG("taosArrayInit %d children failed", childNum); + SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + } + + for (int32_t n = 0; n < childNum; ++n) { + SSubplan *child = (SSubplan *)nodesListGetNode(pPlan->pChildren, n); + SSchTask **childTask = taosHashGet(planToTask, &child, POINTER_BYTES); + if (NULL == childTask || NULL == *childTask) { + SCH_TASK_ELOG("subplan children relationship error, level:%d, taskIdx:%d, childIdx:%d", i, m, n); + SCH_ERR_RET(TSDB_CODE_SCH_INTERNAL_ERROR); + } + + if (NULL == taosArrayPush(pTask->children, childTask)) { + SCH_TASK_ELOG("taosArrayPush childTask failed, level:%d, taskIdx:%d, childIdx:%d", i, m, n); + SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + SCH_TASK_DLOG("children info, the %d child TID %" PRIx64, n, (*childTask)->taskId); + } + + if (parentNum > 0) { + if (0 == pLevel->level) { + SCH_TASK_ELOG("invalid task info, level:0, parentNum:%d", parentNum); + SCH_ERR_RET(TSDB_CODE_SCH_INTERNAL_ERROR); + } + + pTask->parents = taosArrayInit(parentNum, POINTER_BYTES); + if (NULL == pTask->parents) { + SCH_TASK_ELOG("taosArrayInit %d parents failed", parentNum); + SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + } else { + if (0 != pLevel->level) { + SCH_TASK_ELOG("invalid task info, level:%d, parentNum:%d", pLevel->level, parentNum); + SCH_ERR_RET(TSDB_CODE_SCH_INTERNAL_ERROR); + } + } + + for (int32_t n = 0; n < parentNum; ++n) { + SSubplan *parent = (SSubplan *)nodesListGetNode(pPlan->pParents, n); + SSchTask **parentTask = taosHashGet(planToTask, &parent, POINTER_BYTES); + if (NULL == parentTask || NULL == *parentTask) { + SCH_TASK_ELOG("subplan parent relationship error, level:%d, taskIdx:%d, childIdx:%d", i, m, n); + SCH_ERR_RET(TSDB_CODE_SCH_INTERNAL_ERROR); + } + + if (NULL == taosArrayPush(pTask->parents, parentTask)) { + SCH_TASK_ELOG("taosArrayPush parentTask failed, level:%d, taskIdx:%d, childIdx:%d", i, m, n); + SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + SCH_TASK_DLOG("parents info, the %d parent TID %" PRIx64, n, (*parentTask)->taskId); + } + + SCH_TASK_DLOG("level:%d, parentNum:%d, childNum:%d", i, parentNum, childNum); + } + } + + SSchLevel *pLevel = taosArrayGet(pJob->levels, 0); + if (SCH_IS_QUERY_JOB(pJob) && pLevel->taskNum > 1) { + SCH_JOB_ELOG("invalid query plan, level:0, taskNum:%d", pLevel->taskNum); + SCH_ERR_RET(TSDB_CODE_SCH_INTERNAL_ERROR); + } + + return TSDB_CODE_SUCCESS; +} + +int32_t schRecordTaskSucceedNode(SSchJob *pJob, SSchTask *pTask) { + SQueryNodeAddr *addr = taosArrayGet(pTask->candidateAddrs, pTask->candidateIdx); + if (NULL == addr) { + SCH_TASK_ELOG("taosArrayGet candidate addr failed, idx:%d, size:%d", pTask->candidateIdx, + (int32_t)taosArrayGetSize(pTask->candidateAddrs)); + SCH_ERR_RET(TSDB_CODE_SCH_INTERNAL_ERROR); + } + + pTask->succeedAddr = *addr; + + return TSDB_CODE_SUCCESS; +} + +int32_t schRecordTaskExecNode(SSchJob *pJob, SSchTask *pTask, SQueryNodeAddr *addr, void *handle) { + SSchNodeInfo nodeInfo = {.addr = *addr, .handle = handle}; + + if (NULL == taosArrayPush(pTask->execNodes, &nodeInfo)) { + SCH_TASK_ELOG("taosArrayPush nodeInfo to execNodes list failed, errno:%d", errno); + SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + SCH_TASK_DLOG("task execNode recorded, handle:%p", handle); + + return TSDB_CODE_SUCCESS; +} + +int32_t schRecordQueryDataSrc(SSchJob *pJob, SSchTask *pTask) { + if (!SCH_IS_DATA_SRC_QRY_TASK(pTask)) { + return TSDB_CODE_SUCCESS; + } + + taosArrayPush(pJob->dataSrcTasks, &pTask); + + return TSDB_CODE_SUCCESS; +} + + +int32_t schValidateAndBuildJob(SQueryPlan *pDag, SSchJob *pJob) { + int32_t code = 0; + pJob->queryId = pDag->queryId; + + if (pDag->numOfSubplans <= 0) { + SCH_JOB_ELOG("invalid subplan num:%d", pDag->numOfSubplans); + SCH_ERR_RET(TSDB_CODE_QRY_INVALID_INPUT); + } + + pJob->dataSrcTasks = taosArrayInit(pDag->numOfSubplans, POINTER_BYTES); + if (NULL == pJob->dataSrcTasks) { + SCH_ERR_RET(TSDB_CODE_OUT_OF_MEMORY); + } + + int32_t levelNum = (int32_t)LIST_LENGTH(pDag->pSubplans); + if (levelNum <= 0) { + SCH_JOB_ELOG("invalid level num:%d", levelNum); + SCH_ERR_RET(TSDB_CODE_QRY_INVALID_INPUT); + } + + SHashObj *planToTask = taosHashInit( + SCHEDULE_DEFAULT_MAX_TASK_NUM, + taosGetDefaultHashFunction(POINTER_BYTES == sizeof(int64_t) ? TSDB_DATA_TYPE_BIGINT : TSDB_DATA_TYPE_INT), false, + HASH_NO_LOCK); + if (NULL == planToTask) { + SCH_JOB_ELOG("taosHashInit %d failed", SCHEDULE_DEFAULT_MAX_TASK_NUM); + SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + pJob->levels = taosArrayInit(levelNum, sizeof(SSchLevel)); + if (NULL == pJob->levels) { + SCH_JOB_ELOG("taosArrayInit %d failed", levelNum); + SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + pJob->levelNum = levelNum; + pJob->levelIdx = levelNum - 1; + + pJob->subPlans = pDag->pSubplans; + + SSchLevel level = {0}; + SNodeListNode *plans = NULL; + int32_t taskNum = 0; + SSchLevel *pLevel = NULL; + + level.status = JOB_TASK_STATUS_NOT_START; + + for (int32_t i = 0; i < levelNum; ++i) { + if (NULL == taosArrayPush(pJob->levels, &level)) { + SCH_JOB_ELOG("taosArrayPush level failed, level:%d", i); + SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + pLevel = taosArrayGet(pJob->levels, i); + pLevel->level = i; + + plans = (SNodeListNode *)nodesListGetNode(pDag->pSubplans, i); + if (NULL == plans) { + SCH_JOB_ELOG("empty level plan, level:%d", i); + SCH_ERR_JRET(TSDB_CODE_QRY_INVALID_INPUT); + } + + taskNum = (int32_t)LIST_LENGTH(plans->pNodeList); + if (taskNum <= 0) { + SCH_JOB_ELOG("invalid level plan number:%d, level:%d", taskNum, i); + SCH_ERR_JRET(TSDB_CODE_QRY_INVALID_INPUT); + } + + pLevel->taskNum = taskNum; + + pLevel->subTasks = taosArrayInit(taskNum, sizeof(SSchTask)); + if (NULL == pLevel->subTasks) { + SCH_JOB_ELOG("taosArrayInit %d failed", taskNum); + SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + for (int32_t n = 0; n < taskNum; ++n) { + SSubplan *plan = (SSubplan *)nodesListGetNode(plans->pNodeList, n); + + SCH_SET_JOB_TYPE(pJob, plan->subplanType); + + SSchTask task = {0}; + SSchTask *pTask = &task; + + SCH_ERR_JRET(schInitTask(pJob, &task, plan, pLevel)); + + void *p = taosArrayPush(pLevel->subTasks, &task); + if (NULL == p) { + SCH_TASK_ELOG("taosArrayPush task to level failed, level:%d, taskIdx:%d", pLevel->level, n); + SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + SCH_ERR_JRET(schRecordQueryDataSrc(pJob, p)); + + if (0 != taosHashPut(planToTask, &plan, POINTER_BYTES, &p, POINTER_BYTES)) { + SCH_TASK_ELOG("taosHashPut to planToTaks failed, taskIdx:%d", n); + SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + ++pJob->taskNum; + } + + SCH_JOB_DLOG("level initialized, taskNum:%d", taskNum); + } + + SCH_ERR_JRET(schBuildTaskRalation(pJob, planToTask)); + +_return: + if (planToTask) { + taosHashCleanup(planToTask); + } + + SCH_RET(code); +} + +int32_t schSetTaskCandidateAddrs(SSchJob *pJob, SSchTask *pTask) { + if (NULL != pTask->candidateAddrs) { + return TSDB_CODE_SUCCESS; + } + + pTask->candidateIdx = 0; + pTask->candidateAddrs = taosArrayInit(SCH_MAX_CANDIDATE_EP_NUM, sizeof(SQueryNodeAddr)); + if (NULL == pTask->candidateAddrs) { + SCH_TASK_ELOG("taosArrayInit %d condidate addrs failed", SCH_MAX_CANDIDATE_EP_NUM); + SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + if (pTask->plan->execNode.epSet.numOfEps > 0) { + if (NULL == taosArrayPush(pTask->candidateAddrs, &pTask->plan->execNode)) { + SCH_TASK_ELOG("taosArrayPush execNode to candidate addrs failed, errno:%d", errno); + SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + SCH_TASK_DLOG("use execNode from plan as candidate addr, numOfEps:%d", pTask->plan->execNode.epSet.numOfEps); + + return TSDB_CODE_SUCCESS; + } + + int32_t addNum = 0; + int32_t nodeNum = 0; + if (pJob->nodeList) { + nodeNum = taosArrayGetSize(pJob->nodeList); + + for (int32_t i = 0; i < nodeNum && addNum < SCH_MAX_CANDIDATE_EP_NUM; ++i) { + SQueryNodeAddr *naddr = taosArrayGet(pJob->nodeList, i); + + if (NULL == taosArrayPush(pTask->candidateAddrs, naddr)) { + SCH_TASK_ELOG("taosArrayPush execNode to candidate addrs failed, addNum:%d, errno:%d", addNum, errno); + SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + ++addNum; + } + } + + if (addNum <= 0) { + SCH_TASK_ELOG("no available execNode as candidates, nodeNum:%d", nodeNum); + SCH_ERR_RET(TSDB_CODE_QRY_INVALID_INPUT); + } + + /* + for (int32_t i = 0; i < job->dataSrcEps.numOfEps && addNum < SCH_MAX_CANDIDATE_EP_NUM; ++i) { + strncpy(epSet->fqdn[epSet->numOfEps], job->dataSrcEps.fqdn[i], sizeof(job->dataSrcEps.fqdn[i])); + epSet->port[epSet->numOfEps] = job->dataSrcEps.port[i]; + + ++epSet->numOfEps; + } + */ + + return TSDB_CODE_SUCCESS; +} + +int32_t schRemoveTaskFromExecList(SSchJob *pJob, SSchTask *pTask) { + int32_t code = taosHashRemove(pJob->execTasks, &pTask->taskId, sizeof(pTask->taskId)); + if (code) { + SCH_TASK_ELOG("task failed to rm from execTask list, code:%x", code); + SCH_ERR_RET(TSDB_CODE_SCH_INTERNAL_ERROR); + } + + return TSDB_CODE_SUCCESS; +} + + +int32_t schPushTaskToExecList(SSchJob *pJob, SSchTask *pTask) { + int32_t code = taosHashPut(pJob->execTasks, &pTask->taskId, sizeof(pTask->taskId), &pTask, POINTER_BYTES); + if (0 != code) { + if (HASH_NODE_EXIST(code)) { + SCH_TASK_ELOG("task already in execTask list, code:%x", code); + SCH_ERR_RET(TSDB_CODE_SCH_INTERNAL_ERROR); + } + + SCH_TASK_ELOG("taosHashPut task to execTask list failed, errno:%d", errno); + SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + SCH_TASK_DLOG("task added to execTask list, numOfTasks:%d", taosHashGetSize(pJob->execTasks)); + + return TSDB_CODE_SUCCESS; +} + +int32_t schMoveTaskToSuccList(SSchJob *pJob, SSchTask *pTask, bool *moved) { + if (0 != taosHashRemove(pJob->execTasks, &pTask->taskId, sizeof(pTask->taskId))) { + SCH_TASK_WLOG("remove task from execTask list failed, may not exist, status:%s", SCH_GET_TASK_STATUS_STR(pTask)); + } else { + SCH_TASK_DLOG("task removed from execTask list, numOfTasks:%d", taosHashGetSize(pJob->execTasks)); + } + + int32_t code = taosHashPut(pJob->succTasks, &pTask->taskId, sizeof(pTask->taskId), &pTask, POINTER_BYTES); + if (0 != code) { + if (HASH_NODE_EXIST(code)) { + *moved = true; + SCH_TASK_ELOG("task already in succTask list, status:%s", SCH_GET_TASK_STATUS_STR(pTask)); + SCH_ERR_RET(TSDB_CODE_SCH_STATUS_ERROR); + } + + SCH_TASK_ELOG("taosHashPut task to succTask list failed, errno:%d", errno); + SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + *moved = true; + + SCH_TASK_DLOG("task moved to succTask list, numOfTasks:%d", taosHashGetSize(pJob->succTasks)); + + return TSDB_CODE_SUCCESS; +} + +int32_t schMoveTaskToFailList(SSchJob *pJob, SSchTask *pTask, bool *moved) { + *moved = false; + + if (0 != taosHashRemove(pJob->execTasks, &pTask->taskId, sizeof(pTask->taskId))) { + SCH_TASK_WLOG("remove task from execTask list failed, may not exist, status:%s", SCH_GET_TASK_STATUS_STR(pTask)); + } + + int32_t code = taosHashPut(pJob->failTasks, &pTask->taskId, sizeof(pTask->taskId), &pTask, POINTER_BYTES); + if (0 != code) { + if (HASH_NODE_EXIST(code)) { + *moved = true; + + SCH_TASK_WLOG("task already in failTask list, status:%s", SCH_GET_TASK_STATUS_STR(pTask)); + SCH_ERR_RET(TSDB_CODE_SCH_STATUS_ERROR); + } + + SCH_TASK_ELOG("taosHashPut task to failTask list failed, errno:%d", errno); + SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + *moved = true; + + SCH_TASK_DLOG("task moved to failTask list, numOfTasks:%d", taosHashGetSize(pJob->failTasks)); + + return TSDB_CODE_SUCCESS; +} + +int32_t schMoveTaskToExecList(SSchJob *pJob, SSchTask *pTask, bool *moved) { + if (0 != taosHashRemove(pJob->succTasks, &pTask->taskId, sizeof(pTask->taskId))) { + SCH_TASK_WLOG("remove task from succTask list failed, may not exist, status:%s", SCH_GET_TASK_STATUS_STR(pTask)); + } + + int32_t code = taosHashPut(pJob->execTasks, &pTask->taskId, sizeof(pTask->taskId), &pTask, POINTER_BYTES); + if (0 != code) { + if (HASH_NODE_EXIST(code)) { + *moved = true; + + SCH_TASK_ELOG("task already in execTask list, status:%s", SCH_GET_TASK_STATUS_STR(pTask)); + SCH_ERR_RET(TSDB_CODE_SCH_STATUS_ERROR); + } + + SCH_TASK_ELOG("taosHashPut task to execTask list failed, errno:%d", errno); + SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + *moved = true; + + SCH_TASK_DLOG("task moved to execTask list, numOfTasks:%d", taosHashGetSize(pJob->execTasks)); + + return TSDB_CODE_SUCCESS; +} + +int32_t schTaskCheckSetRetry(SSchJob *pJob, SSchTask *pTask, int32_t errCode, bool *needRetry) { + int8_t status = 0; + ++pTask->tryTimes; + + if (schJobNeedToStop(pJob, &status)) { + *needRetry = false; + SCH_TASK_DLOG("task no more retry cause of job status, job status:%s", jobTaskStatusStr(status)); + return TSDB_CODE_SUCCESS; + } + + if (pTask->tryTimes >= REQUEST_MAX_TRY_TIMES) { + *needRetry = false; + SCH_TASK_DLOG("task no more retry since reach max try times, tryTimes:%d", pTask->tryTimes); + return TSDB_CODE_SUCCESS; + } + + if (!NEED_SCHEDULER_RETRY_ERROR(errCode)) { + *needRetry = false; + SCH_TASK_DLOG("task no more retry cause of errCode, errCode:%x - %s", errCode, tstrerror(errCode)); + return TSDB_CODE_SUCCESS; + } + + // TODO CHECK epList/condidateList + if (SCH_IS_DATA_SRC_TASK(pTask)) { + if (pTask->tryTimes >= SCH_TASK_NUM_OF_EPS(&pTask->plan->execNode)) { + *needRetry = false; + SCH_TASK_DLOG("task no more retry since all ep tried, tryTimes:%d, epNum:%d", pTask->tryTimes, + SCH_TASK_NUM_OF_EPS(&pTask->plan->execNode)); + return TSDB_CODE_SUCCESS; + } + } else { + int32_t candidateNum = taosArrayGetSize(pTask->candidateAddrs); + + if ((pTask->candidateIdx + 1) >= candidateNum) { + *needRetry = false; + SCH_TASK_DLOG("task no more retry since all candiates tried, candidateIdx:%d, candidateNum:%d", + pTask->candidateIdx, candidateNum); + return TSDB_CODE_SUCCESS; + } + } + + *needRetry = true; + SCH_TASK_DLOG("task need the %dth retry, errCode:%x - %s", pTask->tryTimes, errCode, tstrerror(errCode)); + + return TSDB_CODE_SUCCESS; +} + +int32_t schHandleTaskRetry(SSchJob *pJob, SSchTask *pTask) { + atomic_sub_fetch_32(&pTask->level->taskLaunchedNum, 1); + + SCH_ERR_RET(schRemoveTaskFromExecList(pJob, pTask)); + SCH_SET_TASK_STATUS(pTask, JOB_TASK_STATUS_NOT_START); + + if (SCH_TASK_NEED_FLOW_CTRL(pJob, pTask)) { + SCH_ERR_RET(schDecTaskFlowQuota(pJob, pTask)); + SCH_ERR_RET(schLaunchTasksInFlowCtrlList(pJob, pTask)); + } + + if (SCH_IS_DATA_SRC_TASK(pTask)) { + SCH_SWITCH_EPSET(&pTask->plan->execNode); + } else { + ++pTask->candidateIdx; + } + + SCH_ERR_RET(schLaunchTask(pJob, pTask)); + + return TSDB_CODE_SUCCESS; +} + +void schUpdateJobErrCode(SSchJob *pJob, int32_t errCode) { + if (TSDB_CODE_SUCCESS == errCode) { + return; + } + + int32_t origCode = atomic_load_32(&pJob->errCode); + if (TSDB_CODE_SUCCESS == origCode) { + if (origCode == atomic_val_compare_exchange_32(&pJob->errCode, origCode, errCode)) { + goto _return; + } + + origCode = atomic_load_32(&pJob->errCode); + } + + if (NEED_CLIENT_HANDLE_ERROR(origCode)) { + return; + } + + if (NEED_CLIENT_HANDLE_ERROR(errCode)) { + atomic_store_32(&pJob->errCode, errCode); + goto _return; + } + + return; + +_return: + + SCH_JOB_DLOG("job errCode updated to %x - %s", errCode, tstrerror(errCode)); +} + +int32_t schProcessOnJobFailureImpl(SSchJob *pJob, int32_t status, int32_t errCode) { + // if already FAILED, no more processing + SCH_ERR_RET(schChkUpdateJobStatus(pJob, status)); + + schUpdateJobErrCode(pJob, errCode); + + if (atomic_load_8(&pJob->userFetch) || pJob->attr.syncSchedule) { + tsem_post(&pJob->rspSem); + } + + int32_t code = atomic_load_32(&pJob->errCode); + + SCH_JOB_DLOG("job failed with error: %s", tstrerror(code)); + + SCH_RET(code); +} + +// Note: no more task error processing, handled in function internal +int32_t schProcessOnJobFailure(SSchJob *pJob, int32_t errCode) { + SCH_RET(schProcessOnJobFailureImpl(pJob, JOB_TASK_STATUS_FAILED, errCode)); +} + +// Note: no more error processing, handled in function internal +int32_t schProcessOnJobDropped(SSchJob *pJob, int32_t errCode) { + SCH_RET(schProcessOnJobFailureImpl(pJob, JOB_TASK_STATUS_DROPPING, errCode)); +} + +// Note: no more task error processing, handled in function internal +int32_t schProcessOnJobPartialSuccess(SSchJob *pJob) { + int32_t code = 0; + + SCH_ERR_RET(schChkUpdateJobStatus(pJob, JOB_TASK_STATUS_PARTIAL_SUCCEED)); + + if (pJob->attr.syncSchedule) { + tsem_post(&pJob->rspSem); + } + + if (atomic_load_8(&pJob->userFetch)) { + SCH_ERR_JRET(schFetchFromRemote(pJob)); + } + + return TSDB_CODE_SUCCESS; + +_return: + + SCH_RET(schProcessOnJobFailure(pJob, code)); +} + +void schProcessOnDataFetched(SSchJob *job) { + atomic_val_compare_exchange_32(&job->remoteFetch, 1, 0); + tsem_post(&job->rspSem); +} + +// Note: no more task error processing, handled in function internal +int32_t schProcessOnTaskFailure(SSchJob *pJob, SSchTask *pTask, int32_t errCode) { + int8_t status = 0; + + if (schJobNeedToStop(pJob, &status)) { + SCH_TASK_DLOG("task failed not processed cause of job status, job status:%s", jobTaskStatusStr(status)); + SCH_RET(atomic_load_32(&pJob->errCode)); + } + + bool needRetry = false; + bool moved = false; + int32_t taskDone = 0; + int32_t code = 0; + + SCH_TASK_DLOG("taskOnFailure, code:%s", tstrerror(errCode)); + + SCH_ERR_JRET(schTaskCheckSetRetry(pJob, pTask, errCode, &needRetry)); + + if (!needRetry) { + SCH_TASK_ELOG("task failed and no more retry, code:%s", tstrerror(errCode)); + + if (SCH_GET_TASK_STATUS(pTask) == JOB_TASK_STATUS_EXECUTING) { + SCH_ERR_JRET(schMoveTaskToFailList(pJob, pTask, &moved)); + } else { + SCH_TASK_ELOG("task not in executing list, status:%s", SCH_GET_TASK_STATUS_STR(pTask)); + SCH_ERR_JRET(TSDB_CODE_SCH_STATUS_ERROR); + } + + SCH_SET_TASK_STATUS(pTask, JOB_TASK_STATUS_FAILED); + + if (SCH_IS_WAIT_ALL_JOB(pJob)) { + SCH_LOCK(SCH_WRITE, &pTask->level->lock); + pTask->level->taskFailed++; + taskDone = pTask->level->taskSucceed + pTask->level->taskFailed; + SCH_UNLOCK(SCH_WRITE, &pTask->level->lock); + + schUpdateJobErrCode(pJob, errCode); + + if (taskDone < pTask->level->taskNum) { + SCH_TASK_DLOG("need to wait other tasks, doneNum:%d, allNum:%d", taskDone, pTask->level->taskNum); + SCH_RET(errCode); + } + } + } else { + SCH_ERR_JRET(schHandleTaskRetry(pJob, pTask)); + + return TSDB_CODE_SUCCESS; + } + +_return: + + SCH_RET(schProcessOnJobFailure(pJob, errCode)); +} + +int32_t schLaunchNextLevelTasks(SSchJob *pJob, SSchTask *pTask) { + if (!SCH_IS_QUERY_JOB(pJob)) { + return TSDB_CODE_SUCCESS; + } + + SSchLevel *pLevel = pTask->level; + int32_t doneNum = atomic_add_fetch_32(&pLevel->taskDoneNum, 1); + if (doneNum == pLevel->taskNum) { + pJob->levelIdx--; + + pLevel = taosArrayGet(pJob->levels, pJob->levelIdx); + for (int32_t i = 0; i < pLevel->taskNum; ++i) { + SSchTask *pTask = taosArrayGet(pLevel->subTasks, i); + + if (pTask->children && taosArrayGetSize(pTask->children) > 0) { + continue; + } + + SCH_ERR_RET(schLaunchTask(pJob, pTask)); + } + } + + return TSDB_CODE_SUCCESS; +} + + +// Note: no more task error processing, handled in function internal +int32_t schProcessOnTaskSuccess(SSchJob *pJob, SSchTask *pTask) { + bool moved = false; + int32_t code = 0; + + SCH_TASK_DLOG("taskOnSuccess, status:%s", SCH_GET_TASK_STATUS_STR(pTask)); + + SCH_ERR_JRET(schMoveTaskToSuccList(pJob, pTask, &moved)); + + SCH_SET_TASK_STATUS(pTask, JOB_TASK_STATUS_PARTIAL_SUCCEED); + + SCH_ERR_JRET(schRecordTaskSucceedNode(pJob, pTask)); + + SCH_ERR_JRET(schLaunchTasksInFlowCtrlList(pJob, pTask)); + + int32_t parentNum = pTask->parents ? (int32_t)taosArrayGetSize(pTask->parents) : 0; + if (parentNum == 0) { + int32_t taskDone = 0; + if (SCH_IS_WAIT_ALL_JOB(pJob)) { + SCH_LOCK(SCH_WRITE, &pTask->level->lock); + pTask->level->taskSucceed++; + taskDone = pTask->level->taskSucceed + pTask->level->taskFailed; + SCH_UNLOCK(SCH_WRITE, &pTask->level->lock); + + if (taskDone < pTask->level->taskNum) { + SCH_TASK_DLOG("wait all tasks, done:%d, all:%d", taskDone, pTask->level->taskNum); + return TSDB_CODE_SUCCESS; + } else if (taskDone > pTask->level->taskNum) { + SCH_TASK_ELOG("taskDone number invalid, done:%d, total:%d", taskDone, pTask->level->taskNum); + } + + if (pTask->level->taskFailed > 0) { + SCH_RET(schProcessOnJobFailure(pJob, 0)); + } else { + SCH_RET(schProcessOnJobPartialSuccess(pJob)); + } + } else { + pJob->resNode = pTask->succeedAddr; + } + + pJob->fetchTask = pTask; + + SCH_ERR_JRET(schMoveTaskToExecList(pJob, pTask, &moved)); + + SCH_RET(schProcessOnJobPartialSuccess(pJob)); + } + + /* + if (SCH_IS_DATA_SRC_TASK(task) && job->dataSrcEps.numOfEps < SCH_MAX_CANDIDATE_EP_NUM) { + strncpy(job->dataSrcEps.fqdn[job->dataSrcEps.numOfEps], task->execAddr.fqdn, sizeof(task->execAddr.fqdn)); + job->dataSrcEps.port[job->dataSrcEps.numOfEps] = task->execAddr.port; + + ++job->dataSrcEps.numOfEps; + } + */ + + for (int32_t i = 0; i < parentNum; ++i) { + SSchTask *par = *(SSchTask **)taosArrayGet(pTask->parents, i); + int32_t readyNum = atomic_add_fetch_32(&par->childReady, 1); + + SCH_LOCK(SCH_WRITE, &par->lock); + SDownstreamSourceNode source = {.type = QUERY_NODE_DOWNSTREAM_SOURCE, + .taskId = pTask->taskId, + .schedId = schMgmt.sId, + .addr = pTask->succeedAddr}; + qSetSubplanExecutionNode(par->plan, pTask->plan->id.groupId, &source); + SCH_UNLOCK(SCH_WRITE, &par->lock); + + if (SCH_TASK_READY_FOR_LAUNCH(readyNum, par)) { + SCH_ERR_RET(schLaunchTask(pJob, par)); + } + } + + SCH_ERR_RET(schLaunchNextLevelTasks(pJob, pTask)); + + return TSDB_CODE_SUCCESS; + +_return: + + SCH_RET(schProcessOnJobFailure(pJob, code)); +} + +// Note: no more error processing, handled in function internal +int32_t schFetchFromRemote(SSchJob *pJob) { + int32_t code = 0; + + if (atomic_val_compare_exchange_32(&pJob->remoteFetch, 0, 1) != 0) { + SCH_JOB_ELOG("prior fetching not finished, remoteFetch:%d", atomic_load_32(&pJob->remoteFetch)); + return TSDB_CODE_SUCCESS; + } + + void *resData = atomic_load_ptr(&pJob->resData); + if (resData) { + atomic_val_compare_exchange_32(&pJob->remoteFetch, 1, 0); + + SCH_JOB_DLOG("res already fetched, res:%p", resData); + return TSDB_CODE_SUCCESS; + } + + SCH_ERR_JRET(schBuildAndSendMsg(pJob, pJob->fetchTask, &pJob->resNode, TDMT_VND_FETCH)); + + return TSDB_CODE_SUCCESS; + +_return: + + atomic_val_compare_exchange_32(&pJob->remoteFetch, 1, 0); + + SCH_RET(schProcessOnTaskFailure(pJob, pJob->fetchTask, code)); +} + +int32_t schProcessOnExplainDone(SSchJob *pJob, SSchTask *pTask, SRetrieveTableRsp *pRsp) { + SCH_TASK_DLOG("got explain rsp, rows:%d, complete:%d", htonl(pRsp->numOfRows), pRsp->completed); + + atomic_store_32(&pJob->resNumOfRows, htonl(pRsp->numOfRows)); + atomic_store_ptr(&pJob->resData, pRsp); + + SCH_SET_TASK_STATUS(pTask, JOB_TASK_STATUS_SUCCEED); + + schProcessOnDataFetched(pJob); + + return TSDB_CODE_SUCCESS; +} + +int32_t schSaveJobQueryRes(SSchJob *pJob, SResReadyRsp *rsp) { + if (rsp->tbFName[0]) { + if (NULL == pJob->queryRes) { + pJob->queryRes = taosArrayInit(pJob->taskNum, sizeof(STbVerInfo)); + if (NULL == pJob->queryRes) { + SCH_ERR_RET(TSDB_CODE_OUT_OF_MEMORY); + } + } + + STbVerInfo tbInfo; + strcpy(tbInfo.tbFName, rsp->tbFName); + tbInfo.sversion = rsp->sversion; + tbInfo.tversion = rsp->tversion; + + taosArrayPush((SArray *)pJob->queryRes, &tbInfo); + } + + return TSDB_CODE_SUCCESS; +} + +int32_t schGetTaskFromTaskList(SHashObj *pTaskList, uint64_t taskId, SSchTask **pTask) { + int32_t s = taosHashGetSize(pTaskList); + if (s <= 0) { + return TSDB_CODE_SUCCESS; + } + + SSchTask **task = taosHashGet(pTaskList, &taskId, sizeof(taskId)); + if (NULL == task || NULL == (*task)) { + return TSDB_CODE_SUCCESS; + } + + *pTask = *task; + + return TSDB_CODE_SUCCESS; +} + +int32_t schUpdateTaskExecNodeHandle(SSchTask *pTask, void *handle, int32_t rspCode) { + if (rspCode || NULL == pTask->execNodes || taosArrayGetSize(pTask->execNodes) > 1 || + taosArrayGetSize(pTask->execNodes) <= 0) { + return TSDB_CODE_SUCCESS; + } + + SSchNodeInfo *nodeInfo = taosArrayGet(pTask->execNodes, 0); + nodeInfo->handle = handle; + + return TSDB_CODE_SUCCESS; +} + +int32_t schLaunchTaskImpl(SSchJob *pJob, SSchTask *pTask) { + int8_t status = 0; + int32_t code = 0; + + atomic_add_fetch_32(&pTask->level->taskLaunchedNum, 1); + + if (schJobNeedToStop(pJob, &status)) { + SCH_TASK_DLOG("no need to launch task cause of job status, job status:%s", jobTaskStatusStr(status)); + + SCH_RET(atomic_load_32(&pJob->errCode)); + } + + // NOTE: race condition: the task should be put into the hash table before send msg to server + if (SCH_GET_TASK_STATUS(pTask) != JOB_TASK_STATUS_EXECUTING) { + SCH_ERR_RET(schPushTaskToExecList(pJob, pTask)); + SCH_SET_TASK_STATUS(pTask, JOB_TASK_STATUS_EXECUTING); + } + + SSubplan *plan = pTask->plan; + + if (NULL == pTask->msg) { // TODO add more detailed reason for failure + code = qSubPlanToString(plan, &pTask->msg, &pTask->msgLen); + if (TSDB_CODE_SUCCESS != code) { + SCH_TASK_ELOG("failed to create physical plan, code:%s, msg:%p, len:%d", tstrerror(code), pTask->msg, + pTask->msgLen); + SCH_ERR_RET(code); + } else { + SCH_TASK_DLOGL("physical plan len:%d, %s", pTask->msgLen, pTask->msg); + } + } + + SCH_ERR_RET(schSetTaskCandidateAddrs(pJob, pTask)); + + if (SCH_IS_QUERY_JOB(pJob)) { + SCH_ERR_RET(schEnsureHbConnection(pJob, pTask)); + } + + SCH_ERR_RET(schBuildAndSendMsg(pJob, pTask, NULL, plan->msgType)); + + return TSDB_CODE_SUCCESS; +} + +// Note: no more error processing, handled in function internal +int32_t schLaunchTask(SSchJob *pJob, SSchTask *pTask) { + bool enough = false; + int32_t code = 0; + + SCH_SET_TASK_HANDLE(pTask, NULL); + + if (SCH_TASK_NEED_FLOW_CTRL(pJob, pTask)) { + SCH_ERR_JRET(schCheckIncTaskFlowQuota(pJob, pTask, &enough)); + + if (enough) { + SCH_ERR_JRET(schLaunchTaskImpl(pJob, pTask)); + } + } else { + SCH_ERR_JRET(schLaunchTaskImpl(pJob, pTask)); + } + + return TSDB_CODE_SUCCESS; + +_return: + + SCH_RET(schProcessOnTaskFailure(pJob, pTask, code)); +} + +int32_t schLaunchLevelTasks(SSchJob *pJob, SSchLevel *level) { + for (int32_t i = 0; i < level->taskNum; ++i) { + SSchTask *pTask = taosArrayGet(level->subTasks, i); + + SCH_ERR_RET(schLaunchTask(pJob, pTask)); + } + + return TSDB_CODE_SUCCESS; +} + +int32_t schLaunchJob(SSchJob *pJob) { + SSchLevel *level = taosArrayGet(pJob->levels, pJob->levelIdx); + + SCH_ERR_RET(schChkUpdateJobStatus(pJob, JOB_TASK_STATUS_EXECUTING)); + + SCH_ERR_RET(schChkJobNeedFlowCtrl(pJob, level)); + + SCH_ERR_RET(schLaunchLevelTasks(pJob, level)); + + return TSDB_CODE_SUCCESS; +} + +void schDropTaskOnExecNode(SSchJob *pJob, SSchTask *pTask) { + if (NULL == pTask->execNodes) { + SCH_TASK_DLOG("no exec address, status:%s", SCH_GET_TASK_STATUS_STR(pTask)); + return; + } + + int32_t size = (int32_t)taosArrayGetSize(pTask->execNodes); + + if (size <= 0) { + SCH_TASK_DLOG("task has no execNodes, no need to drop it, status:%s", SCH_GET_TASK_STATUS_STR(pTask)); + return; + } + + SSchNodeInfo *nodeInfo = NULL; + for (int32_t i = 0; i < size; ++i) { + nodeInfo = (SSchNodeInfo *)taosArrayGet(pTask->execNodes, i); + SCH_SET_TASK_HANDLE(pTask, nodeInfo->handle); + + schBuildAndSendMsg(pJob, pTask, &nodeInfo->addr, TDMT_VND_DROP_TASK); + } + + SCH_TASK_DLOG("task has %d exec address", size); +} + +void schDropTaskInHashList(SSchJob *pJob, SHashObj *list) { + if (!SCH_IS_NEED_DROP_JOB(pJob)) { + return; + } + + void *pIter = taosHashIterate(list, NULL); + while (pIter) { + SSchTask *pTask = *(SSchTask **)pIter; + + schDropTaskOnExecNode(pJob, pTask); + + pIter = taosHashIterate(list, pIter); + } +} + +void schDropJobAllTasks(SSchJob *pJob) { + schDropTaskInHashList(pJob, pJob->execTasks); + schDropTaskInHashList(pJob, pJob->succTasks); + schDropTaskInHashList(pJob, pJob->failTasks); +} + +int32_t schCancelJob(SSchJob *pJob) { + // TODO + return TSDB_CODE_SUCCESS; + // TODO MOVE ALL TASKS FROM EXEC LIST TO FAIL LIST +} + +void schFreeJobImpl(void *job) { + if (NULL == job) { + return; + } + + SSchJob *pJob = job; + uint64_t queryId = pJob->queryId; + int64_t refId = pJob->refId; + + if (pJob->status == JOB_TASK_STATUS_EXECUTING) { + schCancelJob(pJob); + } + + schDropJobAllTasks(pJob); + + pJob->subPlans = NULL; // it is a reference to pDag->pSubplans + + int32_t numOfLevels = taosArrayGetSize(pJob->levels); + for (int32_t i = 0; i < numOfLevels; ++i) { + SSchLevel *pLevel = taosArrayGet(pJob->levels, i); + + int32_t numOfTasks = taosArrayGetSize(pLevel->subTasks); + for (int32_t j = 0; j < numOfTasks; ++j) { + SSchTask *pTask = taosArrayGet(pLevel->subTasks, j); + schFreeTask(pTask); + } + + taosArrayDestroy(pLevel->subTasks); + } + + schFreeFlowCtrl(pJob); + + taosHashCleanup(pJob->execTasks); + taosHashCleanup(pJob->failTasks); + taosHashCleanup(pJob->succTasks); + + taosArrayDestroy(pJob->levels); + taosArrayDestroy(pJob->nodeList); + taosArrayDestroy(pJob->dataSrcTasks); + + qExplainFreeCtx(pJob->explainCtx); + + if (SCH_IS_QUERY_JOB(pJob)) { + taosArrayDestroy((SArray *)pJob->queryRes); + } else { + tFreeSSubmitRsp((SSubmitRsp*)pJob->queryRes); + } + + taosMemoryFreeClear(pJob->resData); + taosMemoryFreeClear(pJob); + + qDebug("QID:0x%" PRIx64 " job freed, refId:%" PRIx64 ", pointer:%p", queryId, refId, pJob); + + atomic_sub_fetch_32(&schMgmt.jobNum, 1); + + schCloseJobRef(); +} + +int32_t schExecJobImpl(void *transport, SArray *pNodeList, SQueryPlan *pDag, int64_t *job, const char *sql, + int64_t startTs, bool sync) { + qDebug("QID:0x%" PRIx64 " job started", pDag->queryId); + + if (pNodeList == NULL || taosArrayGetSize(pNodeList) <= 0) { + qDebug("QID:0x%" PRIx64 " input exec nodeList is empty", pDag->queryId); + } + + int32_t code = 0; + SSchJob *pJob = NULL; + SCH_ERR_JRET(schInitJob(&pJob, pDag, transport, pNodeList, sql, startTs, sync)); + + SCH_ERR_JRET(schLaunchJob(pJob)); + + *job = pJob->refId; + + if (sync) { + SCH_JOB_DLOG("will wait for rsp now, job status:%s", SCH_GET_JOB_STATUS_STR(pJob)); + tsem_wait(&pJob->rspSem); + } + + SCH_JOB_DLOG("job exec done, job status:%s", SCH_GET_JOB_STATUS_STR(pJob)); + + schReleaseJob(pJob->refId); + + return TSDB_CODE_SUCCESS; + +_return: + + schFreeJobImpl(pJob); + SCH_RET(code); +} + +int32_t schExecStaticExplain(void *transport, SArray *pNodeList, SQueryPlan *pDag, int64_t *job, const char *sql, + bool syncSchedule) { + qDebug("QID:0x%" PRIx64 " job started", pDag->queryId); + + int32_t code = 0; + SSchJob *pJob = taosMemoryCalloc(1, sizeof(SSchJob)); + if (NULL == pJob) { + qError("QID:%" PRIx64 " calloc %d failed", pDag->queryId, (int32_t)sizeof(SSchJob)); + SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + pJob->sql = sql; + pJob->attr.queryJob = true; + pJob->attr.explainMode = pDag->explainInfo.mode; + pJob->queryId = pDag->queryId; + pJob->subPlans = pDag->pSubplans; + + SCH_ERR_JRET(qExecStaticExplain(pDag, (SRetrieveTableRsp **)&pJob->resData)); + + int64_t refId = taosAddRef(schMgmt.jobRef, pJob); + if (refId < 0) { + SCH_JOB_ELOG("taosAddRef job failed, error:%s", tstrerror(terrno)); + SCH_ERR_JRET(terrno); + } + + if (NULL == schAcquireJob(refId)) { + SCH_JOB_ELOG("schAcquireJob job failed, refId:%" PRIx64, refId); + SCH_RET(TSDB_CODE_SCH_STATUS_ERROR); + } + + pJob->refId = refId; + + SCH_JOB_DLOG("job refId:%" PRIx64, pJob->refId); + + pJob->status = JOB_TASK_STATUS_PARTIAL_SUCCEED; + *job = pJob->refId; + SCH_JOB_DLOG("job exec done, job status:%s", SCH_GET_JOB_STATUS_STR(pJob)); + + schReleaseJob(pJob->refId); + + return TSDB_CODE_SUCCESS; + +_return: + + schFreeJobImpl(pJob); + SCH_RET(code); +} + + diff --git a/source/libs/scheduler/src/schRemote.c b/source/libs/scheduler/src/schRemote.c new file mode 100644 index 0000000000000000000000000000000000000000..6d9f6b435f5314f4a2f084b2f3e8bdf5a6f8e903 --- /dev/null +++ b/source/libs/scheduler/src/schRemote.c @@ -0,0 +1,1231 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "catalog.h" +#include "command.h" +#include "query.h" +#include "schedulerInt.h" +#include "tmsg.h" +#include "tref.h" +#include "trpc.h" + + +int32_t schValidateReceivedMsgType(SSchJob *pJob, SSchTask *pTask, int32_t msgType) { + int32_t lastMsgType = SCH_GET_TASK_LASTMSG_TYPE(pTask); + int32_t taskStatus = SCH_GET_TASK_STATUS(pTask); + int32_t reqMsgType = msgType - 1; + switch (msgType) { + case TDMT_SCH_LINK_BROKEN: + case TDMT_VND_EXPLAIN_RSP: + return TSDB_CODE_SUCCESS; + case TDMT_VND_QUERY_RSP: // query_rsp may be processed later than ready_rsp + if (lastMsgType != reqMsgType && -1 != lastMsgType && TDMT_VND_FETCH != lastMsgType) { + SCH_TASK_DLOG("rsp msg type mis-match, last sent msgType:%s, rspType:%s", TMSG_INFO(lastMsgType), + TMSG_INFO(msgType)); + } + + if (taskStatus != JOB_TASK_STATUS_EXECUTING && taskStatus != JOB_TASK_STATUS_PARTIAL_SUCCEED) { + SCH_TASK_DLOG("rsp msg conflicted with task status, status:%s, rspType:%s", jobTaskStatusStr(taskStatus), + TMSG_INFO(msgType)); + } + + SCH_SET_TASK_LASTMSG_TYPE(pTask, -1); + return TSDB_CODE_SUCCESS; + case TDMT_VND_RES_READY_RSP: + reqMsgType = TDMT_VND_QUERY; + if (lastMsgType != reqMsgType && -1 != lastMsgType) { + SCH_TASK_ELOG("rsp msg type mis-match, last sent msgType:%s, rspType:%s", + (lastMsgType > 0 ? TMSG_INFO(lastMsgType) : "null"), TMSG_INFO(msgType)); + SCH_ERR_RET(TSDB_CODE_SCH_STATUS_ERROR); + } + + if (taskStatus != JOB_TASK_STATUS_EXECUTING && taskStatus != JOB_TASK_STATUS_PARTIAL_SUCCEED) { + SCH_TASK_ELOG("rsp msg conflicted with task status, status:%s, rspType:%s", jobTaskStatusStr(taskStatus), + TMSG_INFO(msgType)); + SCH_ERR_RET(TSDB_CODE_SCH_STATUS_ERROR); + } + + SCH_SET_TASK_LASTMSG_TYPE(pTask, -1); + return TSDB_CODE_SUCCESS; + case TDMT_VND_FETCH_RSP: + if (lastMsgType != reqMsgType && -1 != lastMsgType) { + SCH_TASK_ELOG("rsp msg type mis-match, last sent msgType:%s, rspType:%s", TMSG_INFO(lastMsgType), + TMSG_INFO(msgType)); + SCH_ERR_RET(TSDB_CODE_SCH_STATUS_ERROR); + } + + if (taskStatus != JOB_TASK_STATUS_EXECUTING && taskStatus != JOB_TASK_STATUS_PARTIAL_SUCCEED) { + SCH_TASK_ELOG("rsp msg conflicted with task status, status:%s, rspType:%s", jobTaskStatusStr(taskStatus), + TMSG_INFO(msgType)); + SCH_ERR_RET(TSDB_CODE_SCH_STATUS_ERROR); + } + + SCH_SET_TASK_LASTMSG_TYPE(pTask, -1); + return TSDB_CODE_SUCCESS; + case TDMT_VND_CREATE_TABLE_RSP: + case TDMT_VND_DROP_TABLE_RSP: + case TDMT_VND_ALTER_TABLE_RSP: + case TDMT_VND_SUBMIT_RSP: + break; + default: + SCH_TASK_ELOG("unknown rsp msg, type:%s, status:%s", TMSG_INFO(msgType), jobTaskStatusStr(taskStatus)); + SCH_ERR_RET(TSDB_CODE_QRY_INVALID_INPUT); + } + + if (lastMsgType != reqMsgType) { + SCH_TASK_ELOG("rsp msg type mis-match, last sent msgType:%s, rspType:%s", TMSG_INFO(lastMsgType), + TMSG_INFO(msgType)); + SCH_ERR_RET(TSDB_CODE_SCH_STATUS_ERROR); + } + + if (taskStatus != JOB_TASK_STATUS_EXECUTING && taskStatus != JOB_TASK_STATUS_PARTIAL_SUCCEED) { + SCH_TASK_ELOG("rsp msg conflicted with task status, status:%s, rspType:%s", jobTaskStatusStr(taskStatus), + TMSG_INFO(msgType)); + SCH_ERR_RET(TSDB_CODE_SCH_STATUS_ERROR); + } + + SCH_SET_TASK_LASTMSG_TYPE(pTask, -1); + + return TSDB_CODE_SUCCESS; +} + +// Note: no more task error processing, handled in function internal +int32_t schHandleResponseMsg(SSchJob *pJob, SSchTask *pTask, int32_t msgType, char *msg, int32_t msgSize, + int32_t rspCode) { + int32_t code = 0; + int8_t status = 0; + + if (schJobNeedToStop(pJob, &status)) { + SCH_TASK_ELOG("rsp not processed cause of job status, job status:%s, rspCode:0x%x", jobTaskStatusStr(status), + rspCode); + SCH_RET(atomic_load_32(&pJob->errCode)); + } + + SCH_ERR_JRET(schValidateReceivedMsgType(pJob, pTask, msgType)); + + switch (msgType) { + case TDMT_VND_CREATE_TABLE_RSP: { + SVCreateTbBatchRsp batchRsp = {0}; + if (msg) { + SDecoder coder = {0}; + tDecoderInit(&coder, msg, msgSize); + code = tDecodeSVCreateTbBatchRsp(&coder, &batchRsp); + if (TSDB_CODE_SUCCESS == code && batchRsp.nRsps > 0) { + for (int32_t i = 0; i < batchRsp.nRsps; ++i) { + SVCreateTbRsp *rsp = batchRsp.pRsps + i; + if (TSDB_CODE_SUCCESS != rsp->code) { + code = rsp->code; + tDecoderClear(&coder); + SCH_ERR_JRET(code); + } + } + } + tDecoderClear(&coder); + SCH_ERR_JRET(code); + } + + SCH_ERR_JRET(rspCode); + SCH_ERR_RET(schProcessOnTaskSuccess(pJob, pTask)); + break; + } + case TDMT_VND_DROP_TABLE_RSP: { + SVDropTbBatchRsp batchRsp = {0}; + if (msg) { + SDecoder coder = {0}; + tDecoderInit(&coder, msg, msgSize); + code = tDecodeSVDropTbBatchRsp(&coder, &batchRsp); + if (TSDB_CODE_SUCCESS == code && batchRsp.nRsps > 0) { + for (int32_t i = 0; i < batchRsp.nRsps; ++i) { + SVDropTbRsp *rsp = batchRsp.pRsps + i; + if (TSDB_CODE_SUCCESS != rsp->code) { + code = rsp->code; + tDecoderClear(&coder); + SCH_ERR_JRET(code); + } + } + } + tDecoderClear(&coder); + SCH_ERR_JRET(code); + } + + SCH_ERR_JRET(rspCode); + SCH_ERR_RET(schProcessOnTaskSuccess(pJob, pTask)); + break; + } + case TDMT_VND_ALTER_TABLE_RSP: { + SVAlterTbRsp rsp = {0}; + if (msg) { + SDecoder coder = {0}; + tDecoderInit(&coder, msg, msgSize); + code = tDecodeSVAlterTbRsp(&coder, &rsp); + tDecoderClear(&coder); + SCH_ERR_JRET(code); + SCH_ERR_JRET(rsp.code); + } + + SCH_ERR_JRET(rspCode); + + if (NULL == msg) { + SCH_ERR_JRET(TSDB_CODE_QRY_INVALID_INPUT); + } + SCH_ERR_RET(schProcessOnTaskSuccess(pJob, pTask)); + break; + } + case TDMT_VND_SUBMIT_RSP: { + SCH_ERR_JRET(rspCode); + + if (msg) { + SDecoder coder = {0}; + SSubmitRsp *rsp = taosMemoryMalloc(sizeof(*rsp)); + tDecoderInit(&coder, msg, msgSize); + code = tDecodeSSubmitRsp(&coder, rsp); + if (code) { + SCH_TASK_ELOG("decode submitRsp failed, code:%d", code); + tFreeSSubmitRsp(rsp); + SCH_ERR_JRET(code); + } + + if (rsp->nBlocks > 0) { + for (int32_t i = 0; i < rsp->nBlocks; ++i) { + SSubmitBlkRsp *blk = rsp->pBlocks + i; + if (TSDB_CODE_SUCCESS != blk->code) { + code = blk->code; + tFreeSSubmitRsp(rsp); + SCH_ERR_JRET(code); + } + } + } + + atomic_add_fetch_32(&pJob->resNumOfRows, rsp->affectedRows); + SCH_TASK_DLOG("submit succeed, affectedRows:%d", rsp->affectedRows); + + SCH_LOCK(SCH_WRITE, &pJob->resLock); + if (pJob->queryRes) { + SSubmitRsp *sum = pJob->queryRes; + sum->affectedRows += rsp->affectedRows; + sum->nBlocks += rsp->nBlocks; + sum->pBlocks = taosMemoryRealloc(sum->pBlocks, sum->nBlocks * sizeof(*sum->pBlocks)); + memcpy(sum->pBlocks + sum->nBlocks - rsp->nBlocks, rsp->pBlocks, rsp->nBlocks * sizeof(*sum->pBlocks)); + taosMemoryFree(rsp->pBlocks); + taosMemoryFree(rsp); + } else { + pJob->queryRes = rsp; + } + SCH_UNLOCK(SCH_WRITE, &pJob->resLock); + } + + SCH_ERR_RET(schProcessOnTaskSuccess(pJob, pTask)); + + break; + } + case TDMT_VND_QUERY_RSP: { + SQueryTableRsp rsp = {0}; + if (msg) { + SCH_ERR_JRET(tDeserializeSQueryTableRsp(msg, msgSize, &rsp)); + SCH_ERR_JRET(rsp.code); + } + + SCH_ERR_JRET(rspCode); + + if (NULL == msg) { + SCH_ERR_JRET(TSDB_CODE_QRY_INVALID_INPUT); + } + + // SCH_ERR_JRET(schBuildAndSendMsg(pJob, pTask, NULL, TDMT_VND_RES_READY)); + + break; + } + case TDMT_VND_RES_READY_RSP: { + SResReadyRsp *rsp = (SResReadyRsp *)msg; + + SCH_ERR_JRET(rspCode); + if (NULL == msg) { + SCH_ERR_JRET(TSDB_CODE_QRY_INVALID_INPUT); + } + SCH_ERR_JRET(rsp->code); + + SCH_ERR_JRET(schSaveJobQueryRes(pJob, rsp)); + + SCH_ERR_RET(schProcessOnTaskSuccess(pJob, pTask)); + + break; + } + case TDMT_VND_EXPLAIN_RSP: { + SCH_ERR_JRET(rspCode); + if (NULL == msg) { + SCH_ERR_JRET(TSDB_CODE_QRY_INVALID_INPUT); + } + + if (!SCH_IS_EXPLAIN_JOB(pJob)) { + SCH_TASK_ELOG("invalid msg received for none explain query, msg type:%s", TMSG_INFO(msgType)); + SCH_ERR_JRET(TSDB_CODE_QRY_INVALID_INPUT); + } + + if (pJob->resData) { + SCH_TASK_ELOG("explain result is already generated, res:%p", pJob->resData); + SCH_ERR_JRET(TSDB_CODE_SCH_STATUS_ERROR); + } + + SExplainRsp rsp = {0}; + if (tDeserializeSExplainRsp(msg, msgSize, &rsp)) { + taosMemoryFree(rsp.subplanInfo); + SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + SRetrieveTableRsp *pRsp = NULL; + SCH_ERR_JRET(qExplainUpdateExecInfo(pJob->explainCtx, &rsp, pTask->plan->id.groupId, &pRsp)); + + if (pRsp) { + SCH_ERR_JRET(schProcessOnExplainDone(pJob, pTask, pRsp)); + } + break; + } + case TDMT_VND_FETCH_RSP: { + SRetrieveTableRsp *rsp = (SRetrieveTableRsp *)msg; + + SCH_ERR_JRET(rspCode); + if (NULL == msg) { + SCH_ERR_JRET(TSDB_CODE_QRY_INVALID_INPUT); + } + + if (SCH_IS_EXPLAIN_JOB(pJob)) { + if (rsp->completed) { + SRetrieveTableRsp *pRsp = NULL; + SCH_ERR_JRET(qExecExplainEnd(pJob->explainCtx, &pRsp)); + if (pRsp) { + SCH_ERR_JRET(schProcessOnExplainDone(pJob, pTask, pRsp)); + } + + return TSDB_CODE_SUCCESS; + } + + atomic_val_compare_exchange_32(&pJob->remoteFetch, 1, 0); + + SCH_ERR_JRET(schFetchFromRemote(pJob)); + + return TSDB_CODE_SUCCESS; + } + + if (pJob->resData) { + SCH_TASK_ELOG("got fetch rsp while res already exists, res:%p", pJob->resData); + taosMemoryFreeClear(rsp); + SCH_ERR_JRET(TSDB_CODE_SCH_STATUS_ERROR); + } + + atomic_store_ptr(&pJob->resData, rsp); + atomic_add_fetch_32(&pJob->resNumOfRows, htonl(rsp->numOfRows)); + + if (rsp->completed) { + SCH_SET_TASK_STATUS(pTask, JOB_TASK_STATUS_SUCCEED); + } + + SCH_TASK_DLOG("got fetch rsp, rows:%d, complete:%d", htonl(rsp->numOfRows), rsp->completed); + + schProcessOnDataFetched(pJob); + break; + } + case TDMT_VND_DROP_TASK_RSP: { + // SHOULD NEVER REACH HERE + SCH_TASK_ELOG("invalid status to handle drop task rsp, refId:%" PRIx64, pJob->refId); + SCH_ERR_JRET(TSDB_CODE_SCH_INTERNAL_ERROR); + break; + } + case TDMT_SCH_LINK_BROKEN: + SCH_TASK_ELOG("link broken received, error:%x - %s", rspCode, tstrerror(rspCode)); + SCH_ERR_JRET(rspCode); + break; + default: + SCH_TASK_ELOG("unknown rsp msg, type:%d, status:%s", msgType, SCH_GET_TASK_STATUS_STR(pTask)); + SCH_ERR_JRET(TSDB_CODE_QRY_INVALID_INPUT); + } + + return TSDB_CODE_SUCCESS; + +_return: + + SCH_RET(schProcessOnTaskFailure(pJob, pTask, code)); +} + + +int32_t schHandleCallback(void *param, const SDataBuf *pMsg, int32_t msgType, int32_t rspCode) { + int32_t code = 0; + SSchTaskCallbackParam *pParam = (SSchTaskCallbackParam *)param; + SSchTask *pTask = NULL; + + SSchJob *pJob = schAcquireJob(pParam->refId); + if (NULL == pJob) { + qWarn("QID:0x%" PRIx64 ",TID:0x%" PRIx64 "taosAcquireRef job failed, may be dropped, refId:%" PRIx64, + pParam->queryId, pParam->taskId, pParam->refId); + SCH_ERR_JRET(TSDB_CODE_QRY_JOB_FREED); + } + + schGetTaskFromTaskList(pJob->execTasks, pParam->taskId, &pTask); + if (NULL == pTask) { + if (TDMT_VND_EXPLAIN_RSP == msgType) { + schGetTaskFromTaskList(pJob->succTasks, pParam->taskId, &pTask); + } else { + SCH_JOB_ELOG("task not found in execTask list, refId:%" PRIx64 ", taskId:%" PRIx64, pParam->refId, + pParam->taskId); + SCH_ERR_JRET(TSDB_CODE_SCH_INTERNAL_ERROR); + } + } + + if (NULL == pTask) { + SCH_JOB_ELOG("task not found in execList & succList, refId:%" PRIx64 ", taskId:%" PRIx64, pParam->refId, + pParam->taskId); + SCH_ERR_JRET(TSDB_CODE_SCH_INTERNAL_ERROR); + } + + SCH_TASK_DLOG("rsp msg received, type:%s, handle:%p, code:%s", TMSG_INFO(msgType), pMsg->handle, tstrerror(rspCode)); + + SCH_SET_TASK_HANDLE(pTask, pMsg->handle); + schUpdateTaskExecNodeHandle(pTask, pMsg->handle, rspCode); + + SCH_ERR_JRET(schHandleResponseMsg(pJob, pTask, msgType, pMsg->pData, pMsg->len, rspCode)); + +_return: + if (pJob) { + schReleaseJob(pParam->refId); + } + + taosMemoryFreeClear(param); + SCH_RET(code); +} + +int32_t schHandleSubmitCallback(void *param, const SDataBuf *pMsg, int32_t code) { + return schHandleCallback(param, pMsg, TDMT_VND_SUBMIT_RSP, code); +} + +int32_t schHandleCreateTbCallback(void *param, const SDataBuf *pMsg, int32_t code) { + return schHandleCallback(param, pMsg, TDMT_VND_CREATE_TABLE_RSP, code); +} + +int32_t schHandleDropTbCallback(void *param, const SDataBuf *pMsg, int32_t code) { + return schHandleCallback(param, pMsg, TDMT_VND_DROP_TABLE_RSP, code); +} + +int32_t schHandleAlterTbCallback(void *param, const SDataBuf *pMsg, int32_t code) { + return schHandleCallback(param, pMsg, TDMT_VND_ALTER_TABLE_RSP, code); +} + +int32_t schHandleQueryCallback(void *param, const SDataBuf *pMsg, int32_t code) { + return schHandleCallback(param, pMsg, TDMT_VND_QUERY_RSP, code); +} + +int32_t schHandleFetchCallback(void *param, const SDataBuf *pMsg, int32_t code) { + return schHandleCallback(param, pMsg, TDMT_VND_FETCH_RSP, code); +} + +int32_t schHandleReadyCallback(void *param, const SDataBuf *pMsg, int32_t code) { + return schHandleCallback(param, pMsg, TDMT_VND_RES_READY_RSP, code); +} + +int32_t schHandleExplainCallback(void *param, const SDataBuf *pMsg, int32_t code) { + return schHandleCallback(param, pMsg, TDMT_VND_EXPLAIN_RSP, code); +} + +int32_t schHandleDropCallback(void *param, const SDataBuf *pMsg, int32_t code) { + SSchTaskCallbackParam *pParam = (SSchTaskCallbackParam *)param; + qDebug("QID:%" PRIx64 ",TID:%" PRIx64 " drop task rsp received, code:%x", pParam->queryId, pParam->taskId, code); + return TSDB_CODE_SUCCESS; +} + +int32_t schHandleLinkBrokenCallback(void *param, const SDataBuf *pMsg, int32_t code) { + SSchCallbackParamHeader *head = (SSchCallbackParamHeader *)param; + rpcReleaseHandle(pMsg->handle, TAOS_CONN_CLIENT); + + qDebug("handle %p is broken", pMsg->handle); + + if (head->isHbParam) { + SSchHbCallbackParam *hbParam = (SSchHbCallbackParam *)param; + SSchTrans trans = {.transInst = hbParam->transport, .transHandle = NULL}; + SCH_ERR_RET(schUpdateHbConnection(&hbParam->nodeEpId, &trans)); + + SCH_ERR_RET(schBuildAndSendHbMsg(&hbParam->nodeEpId)); + } else { + SCH_ERR_RET(schHandleCallback(param, pMsg, TDMT_SCH_LINK_BROKEN, code)); + } + + return TSDB_CODE_SUCCESS; +} + +int32_t schGenerateCallBackInfo(SSchJob *pJob, SSchTask *pTask, int32_t msgType, SMsgSendInfo **pMsgSendInfo) { + int32_t code = 0; + SMsgSendInfo *msgSendInfo = taosMemoryCalloc(1, sizeof(SMsgSendInfo)); + if (NULL == msgSendInfo) { + SCH_TASK_ELOG("calloc %d failed", (int32_t)sizeof(SMsgSendInfo)); + SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + SSchTaskCallbackParam *param = taosMemoryCalloc(1, sizeof(SSchTaskCallbackParam)); + if (NULL == param) { + SCH_TASK_ELOG("calloc %d failed", (int32_t)sizeof(SSchTaskCallbackParam)); + SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + __async_send_cb_fn_t fp = NULL; + SCH_ERR_JRET(schGetCallbackFp(msgType, &fp)); + + param->queryId = pJob->queryId; + param->refId = pJob->refId; + param->taskId = SCH_TASK_ID(pTask); + param->transport = pJob->transport; + + msgSendInfo->param = param; + msgSendInfo->fp = fp; + + *pMsgSendInfo = msgSendInfo; + + return TSDB_CODE_SUCCESS; + +_return: + + taosMemoryFree(param); + taosMemoryFree(msgSendInfo); + + SCH_RET(code); +} + + +int32_t schGetCallbackFp(int32_t msgType, __async_send_cb_fn_t *fp) { + switch (msgType) { + case TDMT_VND_CREATE_TABLE: + *fp = schHandleCreateTbCallback; + break; + case TDMT_VND_DROP_TABLE: + *fp = schHandleDropTbCallback; + break; + case TDMT_VND_ALTER_TABLE: + *fp = schHandleAlterTbCallback; + break; + case TDMT_VND_SUBMIT: + *fp = schHandleSubmitCallback; + break; + case TDMT_VND_QUERY: + *fp = schHandleQueryCallback; + break; + case TDMT_VND_RES_READY: + *fp = schHandleReadyCallback; + break; + case TDMT_VND_EXPLAIN: + *fp = schHandleExplainCallback; + break; + case TDMT_VND_FETCH: + *fp = schHandleFetchCallback; + break; + case TDMT_VND_DROP_TASK: + *fp = schHandleDropCallback; + break; + case TDMT_VND_QUERY_HEARTBEAT: + *fp = schHandleHbCallback; + break; + case TDMT_SCH_LINK_BROKEN: + *fp = schHandleLinkBrokenCallback; + break; + default: + qError("unknown msg type for callback, msgType:%d", msgType); + SCH_ERR_RET(TSDB_CODE_QRY_APP_ERROR); + } + + return TSDB_CODE_SUCCESS; +} + + +int32_t schMakeHbCallbackParam(SSchJob *pJob, SSchTask *pTask, void **pParam) { + SSchHbCallbackParam *param = taosMemoryCalloc(1, sizeof(SSchHbCallbackParam)); + if (NULL == param) { + SCH_TASK_ELOG("calloc %d failed", (int32_t)sizeof(SSchHbCallbackParam)); + SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + param->head.isHbParam = true; + + SQueryNodeAddr *addr = taosArrayGet(pTask->candidateAddrs, pTask->candidateIdx); + + param->nodeEpId.nodeId = addr->nodeId; + memcpy(¶m->nodeEpId.ep, SCH_GET_CUR_EP(addr), sizeof(SEp)); + param->transport = pJob->transport; + + *pParam = param; + + return TSDB_CODE_SUCCESS; +} + +int32_t schCloneHbRpcCtx(SRpcCtx *pSrc, SRpcCtx *pDst) { + int32_t code = 0; + memcpy(&pDst->brokenVal, &pSrc->brokenVal, sizeof(pSrc->brokenVal)); + pDst->brokenVal.val = NULL; + + SCH_ERR_RET(schCloneSMsgSendInfo(pSrc->brokenVal.val, &pDst->brokenVal.val)); + + pDst->args = taosHashInit(1, taosGetDefaultHashFunction(TSDB_DATA_TYPE_INT), false, HASH_ENTRY_LOCK); + if (NULL == pDst->args) { + qError("taosHashInit %d RpcCtx failed", 1); + SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + SRpcCtxVal dst = {0}; + void *pIter = taosHashIterate(pSrc->args, NULL); + while (pIter) { + SRpcCtxVal *pVal = (SRpcCtxVal *)pIter; + int32_t *msgType = taosHashGetKey(pIter, NULL); + + dst = *pVal; + dst.val = NULL; + + SCH_ERR_JRET(schCloneSMsgSendInfo(pVal->val, &dst.val)); + + if (taosHashPut(pDst->args, msgType, sizeof(*msgType), &dst, sizeof(dst))) { + qError("taosHashPut msg %d to rpcCtx failed", *msgType); + (*dst.freeFunc)(dst.val); + SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + pIter = taosHashIterate(pSrc->args, pIter); + } + + return TSDB_CODE_SUCCESS; + +_return: + + schFreeRpcCtx(pDst); + SCH_RET(code); +} + + +int32_t schMakeHbRpcCtx(SSchJob *pJob, SSchTask *pTask, SRpcCtx *pCtx) { + int32_t code = 0; + SSchHbCallbackParam *param = NULL; + SMsgSendInfo *pMsgSendInfo = NULL; + SQueryNodeAddr *addr = taosArrayGet(pTask->candidateAddrs, pTask->candidateIdx); + SQueryNodeEpId epId = {0}; + + epId.nodeId = addr->nodeId; + memcpy(&epId.ep, SCH_GET_CUR_EP(addr), sizeof(SEp)); + + pCtx->args = taosHashInit(1, taosGetDefaultHashFunction(TSDB_DATA_TYPE_INT), false, HASH_ENTRY_LOCK); + if (NULL == pCtx->args) { + SCH_TASK_ELOG("taosHashInit %d RpcCtx failed", 1); + SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + pMsgSendInfo = taosMemoryCalloc(1, sizeof(SMsgSendInfo)); + if (NULL == pMsgSendInfo) { + SCH_TASK_ELOG("calloc %d failed", (int32_t)sizeof(SMsgSendInfo)); + SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + param = taosMemoryCalloc(1, sizeof(SSchHbCallbackParam)); + if (NULL == param) { + SCH_TASK_ELOG("calloc %d failed", (int32_t)sizeof(SSchHbCallbackParam)); + SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + int32_t msgType = TDMT_VND_QUERY_HEARTBEAT_RSP; + __async_send_cb_fn_t fp = NULL; + SCH_ERR_JRET(schGetCallbackFp(TDMT_VND_QUERY_HEARTBEAT, &fp)); + + param->nodeEpId = epId; + param->transport = pJob->transport; + + pMsgSendInfo->param = param; + pMsgSendInfo->fp = fp; + + SRpcCtxVal ctxVal = {.val = pMsgSendInfo, .clone = schCloneSMsgSendInfo, .freeFunc = schFreeRpcCtxVal}; + if (taosHashPut(pCtx->args, &msgType, sizeof(msgType), &ctxVal, sizeof(ctxVal))) { + SCH_TASK_ELOG("taosHashPut msg %d to rpcCtx failed", msgType); + SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + SCH_ERR_JRET(schMakeBrokenLinkVal(pJob, pTask, &pCtx->brokenVal, true)); + + return TSDB_CODE_SUCCESS; + +_return: + + taosHashCleanup(pCtx->args); + taosMemoryFreeClear(param); + taosMemoryFreeClear(pMsgSendInfo); + + SCH_RET(code); +} + +int32_t schRegisterHbConnection(SSchJob *pJob, SSchTask *pTask, SQueryNodeEpId *epId, bool *exist) { + int32_t code = 0; + SSchHbTrans hb = {0}; + + hb.trans.transInst = pJob->transport; + + SCH_ERR_RET(schMakeHbRpcCtx(pJob, pTask, &hb.rpcCtx)); + + code = taosHashPut(schMgmt.hbConnections, epId, sizeof(SQueryNodeEpId), &hb, sizeof(SSchHbTrans)); + if (code) { + schFreeRpcCtx(&hb.rpcCtx); + + if (HASH_NODE_EXIST(code)) { + *exist = true; + return TSDB_CODE_SUCCESS; + } + + qError("taosHashPut hb trans failed, nodeId:%d, fqdn:%s, port:%d", epId->nodeId, epId->ep.fqdn, epId->ep.port); + SCH_ERR_RET(code); + } + + return TSDB_CODE_SUCCESS; +} + + +int32_t schBuildAndSendHbMsg(SQueryNodeEpId *nodeEpId) { + SSchedulerHbReq req = {0}; + int32_t code = 0; + SRpcCtx rpcCtx = {0}; + SSchTrans trans = {0}; + int32_t msgType = TDMT_VND_QUERY_HEARTBEAT; + + req.header.vgId = nodeEpId->nodeId; + req.sId = schMgmt.sId; + memcpy(&req.epId, nodeEpId, sizeof(SQueryNodeEpId)); + + SSchHbTrans *hb = taosHashGet(schMgmt.hbConnections, nodeEpId, sizeof(SQueryNodeEpId)); + if (NULL == hb) { + qError("taosHashGet hb connection failed, nodeId:%d, fqdn:%s, port:%d", nodeEpId->nodeId, nodeEpId->ep.fqdn, + nodeEpId->ep.port); + SCH_ERR_RET(code); + } + + SCH_LOCK(SCH_WRITE, &hb->lock); + code = schCloneHbRpcCtx(&hb->rpcCtx, &rpcCtx); + memcpy(&trans, &hb->trans, sizeof(trans)); + SCH_UNLOCK(SCH_WRITE, &hb->lock); + + SCH_ERR_RET(code); + + int32_t msgSize = tSerializeSSchedulerHbReq(NULL, 0, &req); + if (msgSize < 0) { + qError("tSerializeSSchedulerHbReq hbReq failed, size:%d", msgSize); + SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + void *msg = taosMemoryCalloc(1, msgSize); + if (NULL == msg) { + qError("calloc hb req %d failed", msgSize); + SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + if (tSerializeSSchedulerHbReq(msg, msgSize, &req) < 0) { + qError("tSerializeSSchedulerHbReq hbReq failed, size:%d", msgSize); + SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + SMsgSendInfo *pMsgSendInfo = taosMemoryCalloc(1, sizeof(SMsgSendInfo)); + if (NULL == pMsgSendInfo) { + qError("calloc SMsgSendInfo failed"); + SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + SSchTaskCallbackParam *param = taosMemoryCalloc(1, sizeof(SSchTaskCallbackParam)); + if (NULL == param) { + qError("calloc SSchTaskCallbackParam failed"); + SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + __async_send_cb_fn_t fp = NULL; + SCH_ERR_JRET(schGetCallbackFp(msgType, &fp)); + + param->transport = trans.transInst; + + pMsgSendInfo->param = param; + pMsgSendInfo->msgInfo.pData = msg; + pMsgSendInfo->msgInfo.len = msgSize; + pMsgSendInfo->msgInfo.handle = trans.transHandle; + pMsgSendInfo->msgType = msgType; + pMsgSendInfo->fp = fp; + + int64_t transporterId = 0; + SEpSet epSet = {.inUse = 0, .numOfEps = 1}; + memcpy(&epSet.eps[0], &nodeEpId->ep, sizeof(nodeEpId->ep)); + + qDebug("start to send hb msg, instance:%p, handle:%p, fqdn:%s, port:%d", trans.transInst, trans.transHandle, + nodeEpId->ep.fqdn, nodeEpId->ep.port); + + code = asyncSendMsgToServerExt(trans.transInst, &epSet, &transporterId, pMsgSendInfo, true, &rpcCtx); + if (code) { + qError("fail to send hb msg, instance:%p, handle:%p, fqdn:%s, port:%d, error:%x - %s", trans.transInst, + trans.transHandle, nodeEpId->ep.fqdn, nodeEpId->ep.port, code, tstrerror(code)); + SCH_ERR_JRET(code); + } + + qDebug("hb msg sent"); + return TSDB_CODE_SUCCESS; + +_return: + + taosMemoryFreeClear(msg); + taosMemoryFreeClear(param); + taosMemoryFreeClear(pMsgSendInfo); + schFreeRpcCtx(&rpcCtx); + SCH_RET(code); +} + + +int32_t schEnsureHbConnection(SSchJob *pJob, SSchTask *pTask) { + SQueryNodeAddr *addr = taosArrayGet(pTask->candidateAddrs, pTask->candidateIdx); + SQueryNodeEpId epId = {0}; + + epId.nodeId = addr->nodeId; + memcpy(&epId.ep, SCH_GET_CUR_EP(addr), sizeof(SEp)); + + SSchHbTrans *hb = taosHashGet(schMgmt.hbConnections, &epId, sizeof(SQueryNodeEpId)); + if (NULL == hb) { + bool exist = false; + SCH_ERR_RET(schRegisterHbConnection(pJob, pTask, &epId, &exist)); + if (!exist) { + SCH_ERR_RET(schBuildAndSendHbMsg(&epId)); + } + } + + return TSDB_CODE_SUCCESS; +} + +int32_t schUpdateHbConnection(SQueryNodeEpId *epId, SSchTrans *trans) { + int32_t code = 0; + SSchHbTrans *hb = NULL; + + hb = taosHashGet(schMgmt.hbConnections, epId, sizeof(SQueryNodeEpId)); + if (NULL == hb) { + qError("taosHashGet hb connection failed, nodeId:%d, fqdn:%s, port:%d", epId->nodeId, epId->ep.fqdn, epId->ep.port); + SCH_ERR_RET(TSDB_CODE_QRY_APP_ERROR); + } + + SCH_LOCK(SCH_WRITE, &hb->lock); + memcpy(&hb->trans, trans, sizeof(*trans)); + SCH_UNLOCK(SCH_WRITE, &hb->lock); + + qDebug("hb connection updated, sId:%" PRIx64 ", nodeId:%d, fqdn:%s, port:%d, instance:%p, handle:%p", schMgmt.sId, + epId->nodeId, epId->ep.fqdn, epId->ep.port, trans->transInst, trans->transHandle); + + return TSDB_CODE_SUCCESS; +} + +int32_t schHandleHbCallback(void *param, const SDataBuf *pMsg, int32_t code) { + SSchedulerHbRsp rsp = {0}; + SSchTaskCallbackParam *pParam = (SSchTaskCallbackParam *)param; + + if (code) { + qError("hb rsp error:%s", tstrerror(code)); + SCH_ERR_JRET(code); + } + + if (tDeserializeSSchedulerHbRsp(pMsg->pData, pMsg->len, &rsp)) { + qError("invalid hb rsp msg, size:%d", pMsg->len); + SCH_ERR_JRET(TSDB_CODE_QRY_INVALID_INPUT); + } + + SSchTrans trans = {0}; + trans.transInst = pParam->transport; + trans.transHandle = pMsg->handle; + + SCH_ERR_JRET(schUpdateHbConnection(&rsp.epId, &trans)); + + int32_t taskNum = (int32_t)taosArrayGetSize(rsp.taskStatus); + qDebug("%d task status in hb rsp, nodeId:%d, fqdn:%s, port:%d", taskNum, rsp.epId.nodeId, rsp.epId.ep.fqdn, + rsp.epId.ep.port); + + for (int32_t i = 0; i < taskNum; ++i) { + STaskStatus *taskStatus = taosArrayGet(rsp.taskStatus, i); + + SSchJob *pJob = schAcquireJob(taskStatus->refId); + if (NULL == pJob) { + qWarn("job not found, refId:0x%" PRIx64 ",QID:0x%" PRIx64 ",TID:0x%" PRIx64, taskStatus->refId, + taskStatus->queryId, taskStatus->taskId); + // TODO DROP TASK FROM SERVER!!!! + continue; + } + + // TODO + + SCH_JOB_DLOG("TID:0x%" PRIx64 " task status in server: %s", taskStatus->taskId, + jobTaskStatusStr(taskStatus->status)); + + schReleaseJob(taskStatus->refId); + } + +_return: + + tFreeSSchedulerHbRsp(&rsp); + taosMemoryFree(param); + + SCH_RET(code); +} + +int32_t schMakeCallbackParam(SSchJob *pJob, SSchTask *pTask, void **pParam) { + SSchTaskCallbackParam *param = taosMemoryCalloc(1, sizeof(SSchTaskCallbackParam)); + if (NULL == param) { + SCH_TASK_ELOG("calloc %d failed", (int32_t)sizeof(SSchTaskCallbackParam)); + SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + param->queryId = pJob->queryId; + param->refId = pJob->refId; + param->taskId = SCH_TASK_ID(pTask); + param->transport = pJob->transport; + + *pParam = param; + + return TSDB_CODE_SUCCESS; +} + +int32_t schMakeBrokenLinkVal(SSchJob *pJob, SSchTask *pTask, SRpcBrokenlinkVal *brokenVal, bool isHb) { + int32_t code = 0; + SMsgSendInfo *pMsgSendInfo = NULL; + + pMsgSendInfo = taosMemoryCalloc(1, sizeof(SMsgSendInfo)); + if (NULL == pMsgSendInfo) { + SCH_TASK_ELOG("calloc %d failed", (int32_t)sizeof(SMsgSendInfo)); + SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + if (isHb) { + SCH_ERR_JRET(schMakeHbCallbackParam(pJob, pTask, &pMsgSendInfo->param)); + } else { + SCH_ERR_JRET(schMakeCallbackParam(pJob, pTask, &pMsgSendInfo->param)); + } + + int32_t msgType = TDMT_SCH_LINK_BROKEN; + __async_send_cb_fn_t fp = NULL; + SCH_ERR_JRET(schGetCallbackFp(msgType, &fp)); + + pMsgSendInfo->fp = fp; + + brokenVal->msgType = msgType; + brokenVal->val = pMsgSendInfo; + brokenVal->clone = schCloneSMsgSendInfo; + brokenVal->freeFunc = schFreeRpcCtxVal; + + return TSDB_CODE_SUCCESS; + +_return: + + taosMemoryFreeClear(pMsgSendInfo->param); + taosMemoryFreeClear(pMsgSendInfo); + + SCH_RET(code); +} + +int32_t schMakeQueryRpcCtx(SSchJob *pJob, SSchTask *pTask, SRpcCtx *pCtx) { + int32_t code = 0; + SMsgSendInfo *pReadyMsgSendInfo = NULL; + SMsgSendInfo *pExplainMsgSendInfo = NULL; + + pCtx->args = taosHashInit(1, taosGetDefaultHashFunction(TSDB_DATA_TYPE_INT), false, HASH_ENTRY_LOCK); + if (NULL == pCtx->args) { + SCH_TASK_ELOG("taosHashInit %d RpcCtx failed", 1); + SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + SCH_ERR_JRET(schGenerateCallBackInfo(pJob, pTask, TDMT_VND_RES_READY, &pReadyMsgSendInfo)); + SCH_ERR_JRET(schGenerateCallBackInfo(pJob, pTask, TDMT_VND_EXPLAIN, &pExplainMsgSendInfo)); + + int32_t msgType = TDMT_VND_RES_READY_RSP; + SRpcCtxVal ctxVal = {.val = pReadyMsgSendInfo, .clone = schCloneSMsgSendInfo, .freeFunc = schFreeRpcCtxVal}; + if (taosHashPut(pCtx->args, &msgType, sizeof(msgType), &ctxVal, sizeof(ctxVal))) { + SCH_TASK_ELOG("taosHashPut msg %d to rpcCtx failed", msgType); + SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + msgType = TDMT_VND_EXPLAIN_RSP; + ctxVal.val = pExplainMsgSendInfo; + if (taosHashPut(pCtx->args, &msgType, sizeof(msgType), &ctxVal, sizeof(ctxVal))) { + SCH_TASK_ELOG("taosHashPut msg %d to rpcCtx failed", msgType); + SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + SCH_ERR_JRET(schMakeBrokenLinkVal(pJob, pTask, &pCtx->brokenVal, false)); + + return TSDB_CODE_SUCCESS; + +_return: + + taosHashCleanup(pCtx->args); + + if (pReadyMsgSendInfo) { + taosMemoryFreeClear(pReadyMsgSendInfo->param); + taosMemoryFreeClear(pReadyMsgSendInfo); + } + + if (pExplainMsgSendInfo) { + taosMemoryFreeClear(pExplainMsgSendInfo->param); + taosMemoryFreeClear(pExplainMsgSendInfo); + } + + SCH_RET(code); +} + +int32_t schCloneCallbackParam(SSchCallbackParamHeader *pSrc, SSchCallbackParamHeader **pDst) { + if (pSrc->isHbParam) { + SSchHbCallbackParam *dst = taosMemoryMalloc(sizeof(SSchHbCallbackParam)); + if (NULL == dst) { + qError("malloc SSchHbCallbackParam failed"); + SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + memcpy(dst, pSrc, sizeof(*dst)); + *pDst = (SSchCallbackParamHeader *)dst; + + return TSDB_CODE_SUCCESS; + } + + SSchTaskCallbackParam *dst = taosMemoryMalloc(sizeof(SSchTaskCallbackParam)); + if (NULL == dst) { + qError("malloc SSchTaskCallbackParam failed"); + SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + memcpy(dst, pSrc, sizeof(*dst)); + *pDst = (SSchCallbackParamHeader *)dst; + + return TSDB_CODE_SUCCESS; +} + +int32_t schCloneSMsgSendInfo(void *src, void **dst) { + SMsgSendInfo *pSrc = src; + int32_t code = 0; + SMsgSendInfo *pDst = taosMemoryMalloc(sizeof(*pSrc)); + if (NULL == pDst) { + qError("malloc SMsgSendInfo for rpcCtx failed, len:%d", (int32_t)sizeof(*pSrc)); + SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + memcpy(pDst, pSrc, sizeof(*pSrc)); + pDst->param = NULL; + + SCH_ERR_JRET(schCloneCallbackParam(pSrc->param, (SSchCallbackParamHeader **)&pDst->param)); + + *dst = pDst; + + return TSDB_CODE_SUCCESS; + +_return: + + taosMemoryFreeClear(pDst); + SCH_RET(code); +} + + +int32_t schAsyncSendMsg(SSchJob *pJob, SSchTask *pTask, void *transport, SEpSet *epSet, int32_t msgType, void *msg, + uint32_t msgSize, bool persistHandle, SRpcCtx *ctx) { + int32_t code = 0; + + SSchTrans *trans = (SSchTrans *)transport; + + SMsgSendInfo *pMsgSendInfo = NULL; + SCH_ERR_JRET(schGenerateCallBackInfo(pJob, pTask, msgType, &pMsgSendInfo)); + + pMsgSendInfo->msgInfo.pData = msg; + pMsgSendInfo->msgInfo.len = msgSize; + pMsgSendInfo->msgInfo.handle = trans->transHandle; + pMsgSendInfo->msgType = msgType; + + qDebug("start to send %s msg to node[%d,%s,%d], refId:%" PRIx64 "instance:%p, handle:%p", TMSG_INFO(msgType), + ntohl(((SMsgHead *)msg)->vgId), epSet->eps[epSet->inUse].fqdn, epSet->eps[epSet->inUse].port, pJob->refId, + trans->transInst, trans->transHandle); + + int64_t transporterId = 0; + code = asyncSendMsgToServerExt(trans->transInst, epSet, &transporterId, pMsgSendInfo, persistHandle, ctx); + if (code) { + SCH_ERR_JRET(code); + } + + SCH_TASK_DLOG("req msg sent, refId:%" PRIx64 ", type:%d, %s", pJob->refId, msgType, TMSG_INFO(msgType)); + return TSDB_CODE_SUCCESS; + +_return: + + if (pMsgSendInfo) { + taosMemoryFreeClear(pMsgSendInfo->param); + taosMemoryFreeClear(pMsgSendInfo); + } + + SCH_RET(code); +} + +int32_t schBuildAndSendMsg(SSchJob *pJob, SSchTask *pTask, SQueryNodeAddr *addr, int32_t msgType) { + uint32_t msgSize = 0; + void *msg = NULL; + int32_t code = 0; + bool isCandidateAddr = false; + bool persistHandle = false; + SRpcCtx rpcCtx = {0}; + + if (NULL == addr) { + addr = taosArrayGet(pTask->candidateAddrs, pTask->candidateIdx); + isCandidateAddr = true; + } + + SEpSet epSet = addr->epSet; + + switch (msgType) { + case TDMT_VND_CREATE_TABLE: + case TDMT_VND_DROP_TABLE: + case TDMT_VND_ALTER_TABLE: + case TDMT_VND_SUBMIT: { + msgSize = pTask->msgLen; + msg = taosMemoryCalloc(1, msgSize); + if (NULL == msg) { + SCH_TASK_ELOG("calloc %d failed", msgSize); + SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + memcpy(msg, pTask->msg, msgSize); + break; + } + + case TDMT_VND_QUERY: { + SCH_ERR_RET(schMakeQueryRpcCtx(pJob, pTask, &rpcCtx)); + + uint32_t len = strlen(pJob->sql); + msgSize = sizeof(SSubQueryMsg) + pTask->msgLen + len; + msg = taosMemoryCalloc(1, msgSize); + if (NULL == msg) { + SCH_TASK_ELOG("calloc %d failed", msgSize); + SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + SSubQueryMsg *pMsg = msg; + pMsg->header.vgId = htonl(addr->nodeId); + pMsg->sId = htobe64(schMgmt.sId); + pMsg->queryId = htobe64(pJob->queryId); + pMsg->taskId = htobe64(pTask->taskId); + pMsg->refId = htobe64(pJob->refId); + pMsg->taskType = TASK_TYPE_TEMP; + pMsg->explain = SCH_IS_EXPLAIN_JOB(pJob); + pMsg->phyLen = htonl(pTask->msgLen); + pMsg->sqlLen = htonl(len); + + memcpy(pMsg->msg, pJob->sql, len); + memcpy(pMsg->msg + len, pTask->msg, pTask->msgLen); + + persistHandle = true; + break; + } + + case TDMT_VND_RES_READY: { + msgSize = sizeof(SResReadyReq); + msg = taosMemoryCalloc(1, msgSize); + if (NULL == msg) { + SCH_TASK_ELOG("calloc %d failed", msgSize); + SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + SResReadyReq *pMsg = msg; + + pMsg->header.vgId = htonl(addr->nodeId); + + pMsg->sId = htobe64(schMgmt.sId); + pMsg->queryId = htobe64(pJob->queryId); + pMsg->taskId = htobe64(pTask->taskId); + break; + } + case TDMT_VND_FETCH: { + msgSize = sizeof(SResFetchReq); + msg = taosMemoryCalloc(1, msgSize); + if (NULL == msg) { + SCH_TASK_ELOG("calloc %d failed", msgSize); + SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + SResFetchReq *pMsg = msg; + + pMsg->header.vgId = htonl(addr->nodeId); + + pMsg->sId = htobe64(schMgmt.sId); + pMsg->queryId = htobe64(pJob->queryId); + pMsg->taskId = htobe64(pTask->taskId); + + break; + } + case TDMT_VND_DROP_TASK: { + msgSize = sizeof(STaskDropReq); + msg = taosMemoryCalloc(1, msgSize); + if (NULL == msg) { + SCH_TASK_ELOG("calloc %d failed", msgSize); + SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + STaskDropReq *pMsg = msg; + + pMsg->header.vgId = htonl(addr->nodeId); + + pMsg->sId = htobe64(schMgmt.sId); + pMsg->queryId = htobe64(pJob->queryId); + pMsg->taskId = htobe64(pTask->taskId); + pMsg->refId = htobe64(pJob->refId); + break; + } + case TDMT_VND_QUERY_HEARTBEAT: { + SCH_ERR_RET(schMakeHbRpcCtx(pJob, pTask, &rpcCtx)); + + SSchedulerHbReq req = {0}; + req.sId = schMgmt.sId; + req.header.vgId = addr->nodeId; + req.epId.nodeId = addr->nodeId; + memcpy(&req.epId.ep, SCH_GET_CUR_EP(addr), sizeof(SEp)); + + msgSize = tSerializeSSchedulerHbReq(NULL, 0, &req); + if (msgSize < 0) { + SCH_JOB_ELOG("tSerializeSSchedulerHbReq hbReq failed, size:%d", msgSize); + SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + msg = taosMemoryCalloc(1, msgSize); + if (NULL == msg) { + SCH_JOB_ELOG("calloc %d failed", msgSize); + SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + if (tSerializeSSchedulerHbReq(msg, msgSize, &req) < 0) { + SCH_JOB_ELOG("tSerializeSSchedulerHbReq hbReq failed, size:%d", msgSize); + SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); + } + + persistHandle = true; + break; + } + default: + SCH_TASK_ELOG("unknown msg type to send, msgType:%d", msgType); + SCH_ERR_RET(TSDB_CODE_SCH_INTERNAL_ERROR); + break; + } + + SCH_SET_TASK_LASTMSG_TYPE(pTask, msgType); + + SSchTrans trans = {.transInst = pJob->transport, .transHandle = SCH_GET_TASK_HANDLE(pTask)}; + SCH_ERR_JRET(schAsyncSendMsg(pJob, pTask, &trans, &epSet, msgType, msg, msgSize, persistHandle, + (rpcCtx.args ? &rpcCtx : NULL))); + + if (msgType == TDMT_VND_QUERY) { + SCH_ERR_RET(schRecordTaskExecNode(pJob, pTask, addr, trans.transHandle)); + } + + return TSDB_CODE_SUCCESS; + +_return: + + SCH_SET_TASK_LASTMSG_TYPE(pTask, -1); + schFreeRpcCtx(&rpcCtx); + + taosMemoryFreeClear(msg); + SCH_RET(code); +} + + + diff --git a/source/libs/scheduler/src/schUtil.c b/source/libs/scheduler/src/schUtil.c new file mode 100644 index 0000000000000000000000000000000000000000..57a86ba125b5cfcc2df39e8c42cff91b6f189509 --- /dev/null +++ b/source/libs/scheduler/src/schUtil.c @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "catalog.h" +#include "command.h" +#include "query.h" +#include "schedulerInt.h" +#include "tmsg.h" +#include "tref.h" +#include "trpc.h" + +void schCloseJobRef(void) { + if (!atomic_load_8((int8_t *)&schMgmt.exit)) { + return; + } + + SCH_LOCK(SCH_WRITE, &schMgmt.lock); + if (atomic_load_32(&schMgmt.jobNum) <= 0 && schMgmt.jobRef >= 0) { + taosCloseRef(schMgmt.jobRef); + schMgmt.jobRef = -1; + } + SCH_UNLOCK(SCH_WRITE, &schMgmt.lock); +} + +uint64_t schGenTaskId(void) { return atomic_add_fetch_64(&schMgmt.taskId, 1); } + +uint64_t schGenUUID(void) { + static uint64_t hashId = 0; + static int32_t requestSerialId = 0; + + if (hashId == 0) { + char uid[64]; + int32_t code = taosGetSystemUUID(uid, tListLen(uid)); + if (code != TSDB_CODE_SUCCESS) { + qError("Failed to get the system uid, reason:%s", tstrerror(TAOS_SYSTEM_ERROR(errno))); + } else { + hashId = MurmurHash3_32(uid, strlen(uid)); + } + } + + int64_t ts = taosGetTimestampMs(); + uint64_t pid = taosGetPId(); + int32_t val = atomic_add_fetch_32(&requestSerialId, 1); + + uint64_t id = ((hashId & 0x0FFF) << 52) | ((pid & 0x0FFF) << 40) | ((ts & 0xFFFFFF) << 16) | (val & 0xFFFF); + return id; +} + + +void schFreeRpcCtxVal(const void *arg) { + if (NULL == arg) { + return; + } + + SMsgSendInfo *pMsgSendInfo = (SMsgSendInfo *)arg; + taosMemoryFreeClear(pMsgSendInfo->param); + taosMemoryFreeClear(pMsgSendInfo); +} + +void schFreeRpcCtx(SRpcCtx *pCtx) { + if (NULL == pCtx) { + return; + } + void *pIter = taosHashIterate(pCtx->args, NULL); + while (pIter) { + SRpcCtxVal *ctxVal = (SRpcCtxVal *)pIter; + + (*ctxVal->freeFunc)(ctxVal->val); + + pIter = taosHashIterate(pCtx->args, pIter); + } + + taosHashCleanup(pCtx->args); + + if (pCtx->brokenVal.freeFunc) { + (*pCtx->brokenVal.freeFunc)(pCtx->brokenVal.val); + } +} + + diff --git a/source/libs/scheduler/src/scheduler.c b/source/libs/scheduler/src/scheduler.c index 9354c1a875d030c1839484db967ad091d7fdf8a9..bd2c7e5b4926d1f2fe17252713b812ebff8c4b49 100644 --- a/source/libs/scheduler/src/scheduler.c +++ b/source/libs/scheduler/src/scheduler.c @@ -25,2473 +25,6 @@ SSchedulerMgmt schMgmt = { .jobRef = -1, }; -FORCE_INLINE SSchJob *schAcquireJob(int64_t refId) { return (SSchJob *)taosAcquireRef(schMgmt.jobRef, refId); } - -FORCE_INLINE int32_t schReleaseJob(int64_t refId) { return taosReleaseRef(schMgmt.jobRef, refId); } - -uint64_t schGenTaskId(void) { return atomic_add_fetch_64(&schMgmt.taskId, 1); } - -#if 0 -uint64_t schGenUUID(void) { - static uint64_t hashId = 0; - static int32_t requestSerialId = 0; - - if (hashId == 0) { - char uid[64]; - int32_t code = taosGetSystemUUID(uid, tListLen(uid)); - if (code != TSDB_CODE_SUCCESS) { - qError("Failed to get the system uid, reason:%s", tstrerror(TAOS_SYSTEM_ERROR(errno))); - } else { - hashId = MurmurHash3_32(uid, strlen(uid)); - } - } - - int64_t ts = taosGetTimestampMs(); - uint64_t pid = taosGetPId(); - int32_t val = atomic_add_fetch_32(&requestSerialId, 1); - - uint64_t id = ((hashId & 0x0FFF) << 52) | ((pid & 0x0FFF) << 40) | ((ts & 0xFFFFFF) << 16) | (val & 0xFFFF); - return id; -} -#endif - -int32_t schInitTask(SSchJob *pJob, SSchTask *pTask, SSubplan *pPlan, SSchLevel *pLevel) { - pTask->plan = pPlan; - pTask->level = pLevel; - SCH_SET_TASK_STATUS(pTask, JOB_TASK_STATUS_NOT_START); - pTask->taskId = schGenTaskId(); - pTask->execNodes = taosArrayInit(SCH_MAX_CANDIDATE_EP_NUM, sizeof(SSchNodeInfo)); - if (NULL == pTask->execNodes) { - SCH_TASK_ELOG("taosArrayInit %d execNodes failed", SCH_MAX_CANDIDATE_EP_NUM); - SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - return TSDB_CODE_SUCCESS; -} - -int32_t schInitJob(SSchJob **pSchJob, SQueryPlan *pDag, void *transport, SArray *pNodeList, const char *sql, - int64_t startTs, bool syncSchedule) { - int32_t code = 0; - int64_t refId = -1; - SSchJob *pJob = taosMemoryCalloc(1, sizeof(SSchJob)); - if (NULL == pJob) { - qError("QID:%" PRIx64 " calloc %d failed", pDag->queryId, (int32_t)sizeof(SSchJob)); - SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - pJob->attr.explainMode = pDag->explainInfo.mode; - pJob->attr.syncSchedule = syncSchedule; - pJob->transport = transport; - pJob->sql = sql; - - if (pNodeList != NULL) { - pJob->nodeList = taosArrayDup(pNodeList); - } - - SCH_ERR_JRET(schValidateAndBuildJob(pDag, pJob)); - - if (SCH_IS_EXPLAIN_JOB(pJob)) { - SCH_ERR_JRET(qExecExplainBegin(pDag, &pJob->explainCtx, startTs)); - } - - pJob->execTasks = - taosHashInit(pDag->numOfSubplans, taosGetDefaultHashFunction(TSDB_DATA_TYPE_UBIGINT), false, HASH_ENTRY_LOCK); - if (NULL == pJob->execTasks) { - SCH_JOB_ELOG("taosHashInit %d execTasks failed", pDag->numOfSubplans); - SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - pJob->succTasks = - taosHashInit(pDag->numOfSubplans, taosGetDefaultHashFunction(TSDB_DATA_TYPE_UBIGINT), false, HASH_ENTRY_LOCK); - if (NULL == pJob->succTasks) { - SCH_JOB_ELOG("taosHashInit %d succTasks failed", pDag->numOfSubplans); - SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - pJob->failTasks = - taosHashInit(pDag->numOfSubplans, taosGetDefaultHashFunction(TSDB_DATA_TYPE_UBIGINT), false, HASH_ENTRY_LOCK); - if (NULL == pJob->failTasks) { - SCH_JOB_ELOG("taosHashInit %d failTasks failed", pDag->numOfSubplans); - SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - tsem_init(&pJob->rspSem, 0, 0); - - refId = taosAddRef(schMgmt.jobRef, pJob); - if (refId < 0) { - SCH_JOB_ELOG("taosAddRef job failed, error:%s", tstrerror(terrno)); - SCH_ERR_JRET(terrno); - } - - atomic_add_fetch_32(&schMgmt.jobNum, 1); - - if (NULL == schAcquireJob(refId)) { - SCH_JOB_ELOG("schAcquireJob job failed, refId:%" PRIx64, refId); - SCH_ERR_JRET(TSDB_CODE_SCH_STATUS_ERROR); - } - - pJob->refId = refId; - - SCH_JOB_DLOG("job refId:%" PRIx64, pJob->refId); - - pJob->status = JOB_TASK_STATUS_NOT_START; - - *pSchJob = pJob; - - return TSDB_CODE_SUCCESS; - -_return: - - if (refId < 0) { - schFreeJobImpl(pJob); - } else { - taosRemoveRef(schMgmt.jobRef, refId); - } - SCH_RET(code); -} - -void schFreeRpcCtx(SRpcCtx *pCtx) { - if (NULL == pCtx) { - return; - } - void *pIter = taosHashIterate(pCtx->args, NULL); - while (pIter) { - SRpcCtxVal *ctxVal = (SRpcCtxVal *)pIter; - - (*ctxVal->freeFunc)(ctxVal->val); - - pIter = taosHashIterate(pCtx->args, pIter); - } - - taosHashCleanup(pCtx->args); - - if (pCtx->brokenVal.freeFunc) { - (*pCtx->brokenVal.freeFunc)(pCtx->brokenVal.val); - } -} - -void schFreeTask(SSchTask *pTask) { - if (pTask->candidateAddrs) { - taosArrayDestroy(pTask->candidateAddrs); - } - - taosMemoryFreeClear(pTask->msg); - - if (pTask->children) { - taosArrayDestroy(pTask->children); - } - - if (pTask->parents) { - taosArrayDestroy(pTask->parents); - } - - if (pTask->execNodes) { - taosArrayDestroy(pTask->execNodes); - } -} - -static FORCE_INLINE bool schJobNeedToStop(SSchJob *pJob, int8_t *pStatus) { - int8_t status = SCH_GET_JOB_STATUS(pJob); - if (pStatus) { - *pStatus = status; - } - - return (status == JOB_TASK_STATUS_FAILED || status == JOB_TASK_STATUS_CANCELLED || - status == JOB_TASK_STATUS_CANCELLING || status == JOB_TASK_STATUS_DROPPING || - status == JOB_TASK_STATUS_SUCCEED); -} - -int32_t schValidateTaskReceivedMsgType(SSchJob *pJob, SSchTask *pTask, int32_t msgType) { - int32_t lastMsgType = SCH_GET_TASK_LASTMSG_TYPE(pTask); - int32_t taskStatus = SCH_GET_TASK_STATUS(pTask); - int32_t reqMsgType = msgType - 1; - switch (msgType) { - case TDMT_SCH_LINK_BROKEN: - case TDMT_VND_EXPLAIN_RSP: - return TSDB_CODE_SUCCESS; - case TDMT_VND_QUERY_RSP: // query_rsp may be processed later than ready_rsp - if (lastMsgType != reqMsgType && -1 != lastMsgType && TDMT_VND_FETCH != lastMsgType) { - SCH_TASK_DLOG("rsp msg type mis-match, last sent msgType:%s, rspType:%s", TMSG_INFO(lastMsgType), - TMSG_INFO(msgType)); - } - - if (taskStatus != JOB_TASK_STATUS_EXECUTING && taskStatus != JOB_TASK_STATUS_PARTIAL_SUCCEED) { - SCH_TASK_DLOG("rsp msg conflicted with task status, status:%s, rspType:%s", jobTaskStatusStr(taskStatus), - TMSG_INFO(msgType)); - } - - SCH_SET_TASK_LASTMSG_TYPE(pTask, -1); - return TSDB_CODE_SUCCESS; - case TDMT_VND_RES_READY_RSP: - reqMsgType = TDMT_VND_QUERY; - if (lastMsgType != reqMsgType && -1 != lastMsgType) { - SCH_TASK_ELOG("rsp msg type mis-match, last sent msgType:%s, rspType:%s", - (lastMsgType > 0 ? TMSG_INFO(lastMsgType) : "null"), TMSG_INFO(msgType)); - SCH_ERR_RET(TSDB_CODE_SCH_STATUS_ERROR); - } - - if (taskStatus != JOB_TASK_STATUS_EXECUTING && taskStatus != JOB_TASK_STATUS_PARTIAL_SUCCEED) { - SCH_TASK_ELOG("rsp msg conflicted with task status, status:%s, rspType:%s", jobTaskStatusStr(taskStatus), - TMSG_INFO(msgType)); - SCH_ERR_RET(TSDB_CODE_SCH_STATUS_ERROR); - } - - SCH_SET_TASK_LASTMSG_TYPE(pTask, -1); - return TSDB_CODE_SUCCESS; - case TDMT_VND_FETCH_RSP: - if (lastMsgType != reqMsgType && -1 != lastMsgType) { - SCH_TASK_ELOG("rsp msg type mis-match, last sent msgType:%s, rspType:%s", TMSG_INFO(lastMsgType), - TMSG_INFO(msgType)); - SCH_ERR_RET(TSDB_CODE_SCH_STATUS_ERROR); - } - - if (taskStatus != JOB_TASK_STATUS_EXECUTING && taskStatus != JOB_TASK_STATUS_PARTIAL_SUCCEED) { - SCH_TASK_ELOG("rsp msg conflicted with task status, status:%s, rspType:%s", jobTaskStatusStr(taskStatus), - TMSG_INFO(msgType)); - SCH_ERR_RET(TSDB_CODE_SCH_STATUS_ERROR); - } - - SCH_SET_TASK_LASTMSG_TYPE(pTask, -1); - return TSDB_CODE_SUCCESS; - case TDMT_VND_CREATE_TABLE_RSP: - case TDMT_VND_DROP_TABLE_RSP: - case TDMT_VND_ALTER_TABLE_RSP: - case TDMT_VND_SUBMIT_RSP: - break; - default: - SCH_TASK_ELOG("unknown rsp msg, type:%s, status:%s", TMSG_INFO(msgType), jobTaskStatusStr(taskStatus)); - SCH_ERR_RET(TSDB_CODE_QRY_INVALID_INPUT); - } - - if (lastMsgType != reqMsgType) { - SCH_TASK_ELOG("rsp msg type mis-match, last sent msgType:%s, rspType:%s", TMSG_INFO(lastMsgType), - TMSG_INFO(msgType)); - SCH_ERR_RET(TSDB_CODE_SCH_STATUS_ERROR); - } - - if (taskStatus != JOB_TASK_STATUS_EXECUTING && taskStatus != JOB_TASK_STATUS_PARTIAL_SUCCEED) { - SCH_TASK_ELOG("rsp msg conflicted with task status, status:%s, rspType:%s", jobTaskStatusStr(taskStatus), - TMSG_INFO(msgType)); - SCH_ERR_RET(TSDB_CODE_SCH_STATUS_ERROR); - } - - SCH_SET_TASK_LASTMSG_TYPE(pTask, -1); - - return TSDB_CODE_SUCCESS; -} - -int32_t schCheckAndUpdateJobStatus(SSchJob *pJob, int8_t newStatus) { - int32_t code = 0; - - int8_t oriStatus = 0; - - while (true) { - oriStatus = SCH_GET_JOB_STATUS(pJob); - - if (oriStatus == newStatus) { - SCH_ERR_JRET(TSDB_CODE_QRY_APP_ERROR); - } - - switch (oriStatus) { - case JOB_TASK_STATUS_NULL: - if (newStatus != JOB_TASK_STATUS_NOT_START) { - SCH_ERR_JRET(TSDB_CODE_QRY_APP_ERROR); - } - - break; - case JOB_TASK_STATUS_NOT_START: - if (newStatus != JOB_TASK_STATUS_EXECUTING) { - SCH_ERR_JRET(TSDB_CODE_QRY_APP_ERROR); - } - - break; - case JOB_TASK_STATUS_EXECUTING: - if (newStatus != JOB_TASK_STATUS_PARTIAL_SUCCEED && newStatus != JOB_TASK_STATUS_FAILED && - newStatus != JOB_TASK_STATUS_CANCELLING && newStatus != JOB_TASK_STATUS_CANCELLED && - newStatus != JOB_TASK_STATUS_DROPPING) { - SCH_ERR_JRET(TSDB_CODE_QRY_APP_ERROR); - } - - break; - case JOB_TASK_STATUS_PARTIAL_SUCCEED: - if (newStatus != JOB_TASK_STATUS_FAILED && newStatus != JOB_TASK_STATUS_SUCCEED && - newStatus != JOB_TASK_STATUS_DROPPING) { - SCH_ERR_JRET(TSDB_CODE_QRY_APP_ERROR); - } - - break; - case JOB_TASK_STATUS_SUCCEED: - case JOB_TASK_STATUS_FAILED: - case JOB_TASK_STATUS_CANCELLING: - if (newStatus != JOB_TASK_STATUS_DROPPING) { - SCH_ERR_JRET(TSDB_CODE_QRY_APP_ERROR); - } - - break; - case JOB_TASK_STATUS_CANCELLED: - case JOB_TASK_STATUS_DROPPING: - SCH_ERR_JRET(TSDB_CODE_QRY_JOB_FREED); - break; - - default: - SCH_JOB_ELOG("invalid job status:%s", jobTaskStatusStr(oriStatus)); - SCH_ERR_JRET(TSDB_CODE_QRY_APP_ERROR); - } - - if (oriStatus != atomic_val_compare_exchange_8(&pJob->status, oriStatus, newStatus)) { - continue; - } - - SCH_JOB_DLOG("job status updated from %s to %s", jobTaskStatusStr(oriStatus), jobTaskStatusStr(newStatus)); - - break; - } - - return TSDB_CODE_SUCCESS; - -_return: - - SCH_JOB_ELOG("invalid job status update, from %s to %s", jobTaskStatusStr(oriStatus), jobTaskStatusStr(newStatus)); - SCH_ERR_RET(code); - return TSDB_CODE_SUCCESS; -} - -int32_t schBuildTaskRalation(SSchJob *pJob, SHashObj *planToTask) { - for (int32_t i = 0; i < pJob->levelNum; ++i) { - SSchLevel *pLevel = taosArrayGet(pJob->levels, i); - - for (int32_t m = 0; m < pLevel->taskNum; ++m) { - SSchTask *pTask = taosArrayGet(pLevel->subTasks, m); - SSubplan *pPlan = pTask->plan; - int32_t childNum = pPlan->pChildren ? (int32_t)LIST_LENGTH(pPlan->pChildren) : 0; - int32_t parentNum = pPlan->pParents ? (int32_t)LIST_LENGTH(pPlan->pParents) : 0; - - if (childNum > 0) { - if (pJob->levelIdx == pLevel->level) { - SCH_JOB_ELOG("invalid query plan, lowest level, childNum:%d", childNum); - SCH_ERR_RET(TSDB_CODE_SCH_INTERNAL_ERROR); - } - - pTask->children = taosArrayInit(childNum, POINTER_BYTES); - if (NULL == pTask->children) { - SCH_TASK_ELOG("taosArrayInit %d children failed", childNum); - SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - } - - for (int32_t n = 0; n < childNum; ++n) { - SSubplan *child = (SSubplan *)nodesListGetNode(pPlan->pChildren, n); - SSchTask **childTask = taosHashGet(planToTask, &child, POINTER_BYTES); - if (NULL == childTask || NULL == *childTask) { - SCH_TASK_ELOG("subplan children relationship error, level:%d, taskIdx:%d, childIdx:%d", i, m, n); - SCH_ERR_RET(TSDB_CODE_SCH_INTERNAL_ERROR); - } - - if (NULL == taosArrayPush(pTask->children, childTask)) { - SCH_TASK_ELOG("taosArrayPush childTask failed, level:%d, taskIdx:%d, childIdx:%d", i, m, n); - SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - } - - if (parentNum > 0) { - if (0 == pLevel->level) { - SCH_TASK_ELOG("invalid task info, level:0, parentNum:%d", parentNum); - SCH_ERR_RET(TSDB_CODE_SCH_INTERNAL_ERROR); - } - - pTask->parents = taosArrayInit(parentNum, POINTER_BYTES); - if (NULL == pTask->parents) { - SCH_TASK_ELOG("taosArrayInit %d parents failed", parentNum); - SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - } else { - if (0 != pLevel->level) { - SCH_TASK_ELOG("invalid task info, level:%d, parentNum:%d", pLevel->level, parentNum); - SCH_ERR_RET(TSDB_CODE_SCH_INTERNAL_ERROR); - } - } - - for (int32_t n = 0; n < parentNum; ++n) { - SSubplan *parent = (SSubplan *)nodesListGetNode(pPlan->pParents, n); - SSchTask **parentTask = taosHashGet(planToTask, &parent, POINTER_BYTES); - if (NULL == parentTask || NULL == *parentTask) { - SCH_TASK_ELOG("subplan parent relationship error, level:%d, taskIdx:%d, childIdx:%d", i, m, n); - SCH_ERR_RET(TSDB_CODE_SCH_INTERNAL_ERROR); - } - - if (NULL == taosArrayPush(pTask->parents, parentTask)) { - SCH_TASK_ELOG("taosArrayPush parentTask failed, level:%d, taskIdx:%d, childIdx:%d", i, m, n); - SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - } - - SCH_TASK_DLOG("level:%d, parentNum:%d, childNum:%d", i, parentNum, childNum); - } - } - - SSchLevel *pLevel = taosArrayGet(pJob->levels, 0); - if (SCH_IS_QUERY_JOB(pJob) && pLevel->taskNum > 1) { - SCH_JOB_ELOG("invalid query plan, level:0, taskNum:%d", pLevel->taskNum); - SCH_ERR_RET(TSDB_CODE_SCH_INTERNAL_ERROR); - } - - return TSDB_CODE_SUCCESS; -} - -int32_t schRecordTaskSucceedNode(SSchJob *pJob, SSchTask *pTask) { - SQueryNodeAddr *addr = taosArrayGet(pTask->candidateAddrs, pTask->candidateIdx); - if (NULL == addr) { - SCH_TASK_ELOG("taosArrayGet candidate addr failed, idx:%d, size:%d", pTask->candidateIdx, - (int32_t)taosArrayGetSize(pTask->candidateAddrs)); - SCH_ERR_RET(TSDB_CODE_SCH_INTERNAL_ERROR); - } - - pTask->succeedAddr = *addr; - - return TSDB_CODE_SUCCESS; -} - -int32_t schRecordTaskExecNode(SSchJob *pJob, SSchTask *pTask, SQueryNodeAddr *addr, void *handle) { - SSchNodeInfo nodeInfo = {.addr = *addr, .handle = handle}; - - if (NULL == taosArrayPush(pTask->execNodes, &nodeInfo)) { - SCH_TASK_ELOG("taosArrayPush nodeInfo to execNodes list failed, errno:%d", errno); - SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - SCH_TASK_DLOG("task execNode recorded, handle:%p", handle); - - return TSDB_CODE_SUCCESS; -} - -int32_t schValidateAndBuildJob(SQueryPlan *pDag, SSchJob *pJob) { - int32_t code = 0; - pJob->queryId = pDag->queryId; - - if (pDag->numOfSubplans <= 0) { - SCH_JOB_ELOG("invalid subplan num:%d", pDag->numOfSubplans); - SCH_ERR_RET(TSDB_CODE_QRY_INVALID_INPUT); - } - - int32_t levelNum = (int32_t)LIST_LENGTH(pDag->pSubplans); - if (levelNum <= 0) { - SCH_JOB_ELOG("invalid level num:%d", levelNum); - SCH_ERR_RET(TSDB_CODE_QRY_INVALID_INPUT); - } - - SHashObj *planToTask = taosHashInit( - SCHEDULE_DEFAULT_MAX_TASK_NUM, - taosGetDefaultHashFunction(POINTER_BYTES == sizeof(int64_t) ? TSDB_DATA_TYPE_BIGINT : TSDB_DATA_TYPE_INT), false, - HASH_NO_LOCK); - if (NULL == planToTask) { - SCH_JOB_ELOG("taosHashInit %d failed", SCHEDULE_DEFAULT_MAX_TASK_NUM); - SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - pJob->levels = taosArrayInit(levelNum, sizeof(SSchLevel)); - if (NULL == pJob->levels) { - SCH_JOB_ELOG("taosArrayInit %d failed", levelNum); - SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - pJob->levelNum = levelNum; - pJob->levelIdx = levelNum - 1; - - pJob->subPlans = pDag->pSubplans; - - SSchLevel level = {0}; - SNodeListNode *plans = NULL; - int32_t taskNum = 0; - SSchLevel *pLevel = NULL; - - level.status = JOB_TASK_STATUS_NOT_START; - - for (int32_t i = 0; i < levelNum; ++i) { - if (NULL == taosArrayPush(pJob->levels, &level)) { - SCH_JOB_ELOG("taosArrayPush level failed, level:%d", i); - SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - pLevel = taosArrayGet(pJob->levels, i); - pLevel->level = i; - - plans = (SNodeListNode *)nodesListGetNode(pDag->pSubplans, i); - if (NULL == plans) { - SCH_JOB_ELOG("empty level plan, level:%d", i); - SCH_ERR_JRET(TSDB_CODE_QRY_INVALID_INPUT); - } - - taskNum = (int32_t)LIST_LENGTH(plans->pNodeList); - if (taskNum <= 0) { - SCH_JOB_ELOG("invalid level plan number:%d, level:%d", taskNum, i); - SCH_ERR_JRET(TSDB_CODE_QRY_INVALID_INPUT); - } - - pLevel->taskNum = taskNum; - - pLevel->subTasks = taosArrayInit(taskNum, sizeof(SSchTask)); - if (NULL == pLevel->subTasks) { - SCH_JOB_ELOG("taosArrayInit %d failed", taskNum); - SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - for (int32_t n = 0; n < taskNum; ++n) { - SSubplan *plan = (SSubplan *)nodesListGetNode(plans->pNodeList, n); - - SCH_SET_JOB_TYPE(pJob, plan->subplanType); - - SSchTask task = {0}; - SSchTask *pTask = &task; - - SCH_ERR_JRET(schInitTask(pJob, &task, plan, pLevel)); - - void *p = taosArrayPush(pLevel->subTasks, &task); - if (NULL == p) { - SCH_TASK_ELOG("taosArrayPush task to level failed, level:%d, taskIdx:%d", pLevel->level, n); - SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - if (0 != taosHashPut(planToTask, &plan, POINTER_BYTES, &p, POINTER_BYTES)) { - SCH_TASK_ELOG("taosHashPut to planToTaks failed, taskIdx:%d", n); - SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - ++pJob->taskNum; - } - - SCH_JOB_DLOG("level initialized, taskNum:%d", taskNum); - } - - SCH_ERR_JRET(schBuildTaskRalation(pJob, planToTask)); - -_return: - if (planToTask) { - taosHashCleanup(planToTask); - } - - SCH_RET(code); -} - -int32_t schSetTaskCandidateAddrs(SSchJob *pJob, SSchTask *pTask) { - if (NULL != pTask->candidateAddrs) { - return TSDB_CODE_SUCCESS; - } - - pTask->candidateIdx = 0; - pTask->candidateAddrs = taosArrayInit(SCH_MAX_CANDIDATE_EP_NUM, sizeof(SQueryNodeAddr)); - if (NULL == pTask->candidateAddrs) { - SCH_TASK_ELOG("taosArrayInit %d condidate addrs failed", SCH_MAX_CANDIDATE_EP_NUM); - SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - if (pTask->plan->execNode.epSet.numOfEps > 0) { - if (NULL == taosArrayPush(pTask->candidateAddrs, &pTask->plan->execNode)) { - SCH_TASK_ELOG("taosArrayPush execNode to candidate addrs failed, errno:%d", errno); - SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - SCH_TASK_DLOG("use execNode from plan as candidate addr, numOfEps:%d", pTask->plan->execNode.epSet.numOfEps); - - return TSDB_CODE_SUCCESS; - } - - int32_t addNum = 0; - int32_t nodeNum = 0; - if (pJob->nodeList) { - nodeNum = taosArrayGetSize(pJob->nodeList); - - for (int32_t i = 0; i < nodeNum && addNum < SCH_MAX_CANDIDATE_EP_NUM; ++i) { - SQueryNodeAddr *naddr = taosArrayGet(pJob->nodeList, i); - - if (NULL == taosArrayPush(pTask->candidateAddrs, naddr)) { - SCH_TASK_ELOG("taosArrayPush execNode to candidate addrs failed, addNum:%d, errno:%d", addNum, errno); - SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - ++addNum; - } - } - - if (addNum <= 0) { - SCH_TASK_ELOG("no available execNode as candidates, nodeNum:%d", nodeNum); - SCH_ERR_RET(TSDB_CODE_QRY_INVALID_INPUT); - } - - /* - for (int32_t i = 0; i < job->dataSrcEps.numOfEps && addNum < SCH_MAX_CANDIDATE_EP_NUM; ++i) { - strncpy(epSet->fqdn[epSet->numOfEps], job->dataSrcEps.fqdn[i], sizeof(job->dataSrcEps.fqdn[i])); - epSet->port[epSet->numOfEps] = job->dataSrcEps.port[i]; - - ++epSet->numOfEps; - } - */ - - return TSDB_CODE_SUCCESS; -} - -int32_t schPushTaskToExecList(SSchJob *pJob, SSchTask *pTask) { - int32_t code = taosHashPut(pJob->execTasks, &pTask->taskId, sizeof(pTask->taskId), &pTask, POINTER_BYTES); - if (0 != code) { - if (HASH_NODE_EXIST(code)) { - SCH_TASK_ELOG("task already in execTask list, code:%x", code); - SCH_ERR_RET(TSDB_CODE_SCH_INTERNAL_ERROR); - } - - SCH_TASK_ELOG("taosHashPut task to execTask list failed, errno:%d", errno); - SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - SCH_TASK_DLOG("task added to execTask list, numOfTasks:%d", taosHashGetSize(pJob->execTasks)); - - return TSDB_CODE_SUCCESS; -} - -int32_t schMoveTaskToSuccList(SSchJob *pJob, SSchTask *pTask, bool *moved) { - if (0 != taosHashRemove(pJob->execTasks, &pTask->taskId, sizeof(pTask->taskId))) { - SCH_TASK_WLOG("remove task from execTask list failed, may not exist, status:%s", SCH_GET_TASK_STATUS_STR(pTask)); - } else { - SCH_TASK_DLOG("task removed from execTask list, numOfTasks:%d", taosHashGetSize(pJob->execTasks)); - } - - int32_t code = taosHashPut(pJob->succTasks, &pTask->taskId, sizeof(pTask->taskId), &pTask, POINTER_BYTES); - if (0 != code) { - if (HASH_NODE_EXIST(code)) { - *moved = true; - SCH_TASK_ELOG("task already in succTask list, status:%s", SCH_GET_TASK_STATUS_STR(pTask)); - SCH_ERR_RET(TSDB_CODE_SCH_STATUS_ERROR); - } - - SCH_TASK_ELOG("taosHashPut task to succTask list failed, errno:%d", errno); - SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - *moved = true; - - SCH_TASK_DLOG("task moved to succTask list, numOfTasks:%d", taosHashGetSize(pJob->succTasks)); - - return TSDB_CODE_SUCCESS; -} - -int32_t schMoveTaskToFailList(SSchJob *pJob, SSchTask *pTask, bool *moved) { - *moved = false; - - if (0 != taosHashRemove(pJob->execTasks, &pTask->taskId, sizeof(pTask->taskId))) { - SCH_TASK_WLOG("remove task from execTask list failed, may not exist, status:%s", SCH_GET_TASK_STATUS_STR(pTask)); - } - - int32_t code = taosHashPut(pJob->failTasks, &pTask->taskId, sizeof(pTask->taskId), &pTask, POINTER_BYTES); - if (0 != code) { - if (HASH_NODE_EXIST(code)) { - *moved = true; - - SCH_TASK_WLOG("task already in failTask list, status:%s", SCH_GET_TASK_STATUS_STR(pTask)); - SCH_ERR_RET(TSDB_CODE_SCH_STATUS_ERROR); - } - - SCH_TASK_ELOG("taosHashPut task to failTask list failed, errno:%d", errno); - SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - *moved = true; - - SCH_TASK_DLOG("task moved to failTask list, numOfTasks:%d", taosHashGetSize(pJob->failTasks)); - - return TSDB_CODE_SUCCESS; -} - -int32_t schMoveTaskToExecList(SSchJob *pJob, SSchTask *pTask, bool *moved) { - if (0 != taosHashRemove(pJob->succTasks, &pTask->taskId, sizeof(pTask->taskId))) { - SCH_TASK_WLOG("remove task from succTask list failed, may not exist, status:%s", SCH_GET_TASK_STATUS_STR(pTask)); - } - - int32_t code = taosHashPut(pJob->execTasks, &pTask->taskId, sizeof(pTask->taskId), &pTask, POINTER_BYTES); - if (0 != code) { - if (HASH_NODE_EXIST(code)) { - *moved = true; - - SCH_TASK_ELOG("task already in execTask list, status:%s", SCH_GET_TASK_STATUS_STR(pTask)); - SCH_ERR_RET(TSDB_CODE_SCH_STATUS_ERROR); - } - - SCH_TASK_ELOG("taosHashPut task to execTask list failed, errno:%d", errno); - SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - *moved = true; - - SCH_TASK_DLOG("task moved to execTask list, numOfTasks:%d", taosHashGetSize(pJob->execTasks)); - - return TSDB_CODE_SUCCESS; -} - -int32_t schTaskCheckSetRetry(SSchJob *pJob, SSchTask *pTask, int32_t errCode, bool *needRetry) { - int8_t status = 0; - ++pTask->tryTimes; - - if (schJobNeedToStop(pJob, &status)) { - *needRetry = false; - SCH_TASK_DLOG("task no more retry cause of job status, job status:%s", jobTaskStatusStr(status)); - return TSDB_CODE_SUCCESS; - } - - if (pTask->tryTimes >= REQUEST_MAX_TRY_TIMES) { - *needRetry = false; - SCH_TASK_DLOG("task no more retry since reach max try times, tryTimes:%d", pTask->tryTimes); - return TSDB_CODE_SUCCESS; - } - - if (!NEED_SCHEDULER_RETRY_ERROR(errCode)) { - *needRetry = false; - SCH_TASK_DLOG("task no more retry cause of errCode, errCode:%x - %s", errCode, tstrerror(errCode)); - return TSDB_CODE_SUCCESS; - } - - // TODO CHECK epList/condidateList - if (SCH_IS_DATA_SRC_TASK(pTask)) { - if (pTask->tryTimes >= SCH_TASK_NUM_OF_EPS(&pTask->plan->execNode)) { - *needRetry = false; - SCH_TASK_DLOG("task no more retry since all ep tried, tryTimes:%d, epNum:%d", pTask->tryTimes, - SCH_TASK_NUM_OF_EPS(&pTask->plan->execNode)); - return TSDB_CODE_SUCCESS; - } - } else { - int32_t candidateNum = taosArrayGetSize(pTask->candidateAddrs); - - if ((pTask->candidateIdx + 1) >= candidateNum) { - *needRetry = false; - SCH_TASK_DLOG("task no more retry since all candiates tried, candidateIdx:%d, candidateNum:%d", - pTask->candidateIdx, candidateNum); - return TSDB_CODE_SUCCESS; - } - } - - *needRetry = true; - SCH_TASK_DLOG("task need the %dth retry, errCode:%x - %s", pTask->tryTimes, errCode, tstrerror(errCode)); - - return TSDB_CODE_SUCCESS; -} - -int32_t schHandleTaskRetry(SSchJob *pJob, SSchTask *pTask) { - atomic_sub_fetch_32(&pTask->level->taskLaunchedNum, 1); - - if (SCH_TASK_NEED_FLOW_CTRL(pJob, pTask)) { - SCH_ERR_RET(schDecTaskFlowQuota(pJob, pTask)); - SCH_ERR_RET(schLaunchTasksInFlowCtrlList(pJob, pTask)); - } - - if (SCH_IS_DATA_SRC_TASK(pTask)) { - SCH_SWITCH_EPSET(&pTask->plan->execNode); - } else { - ++pTask->candidateIdx; - } - - SCH_ERR_RET(schLaunchTask(pJob, pTask)); - - return TSDB_CODE_SUCCESS; -} - -int32_t schUpdateHbConnection(SQueryNodeEpId *epId, SSchTrans *trans) { - int32_t code = 0; - SSchHbTrans *hb = NULL; - - hb = taosHashGet(schMgmt.hbConnections, epId, sizeof(SQueryNodeEpId)); - if (NULL == hb) { - qError("taosHashGet hb connection failed, nodeId:%d, fqdn:%s, port:%d", epId->nodeId, epId->ep.fqdn, epId->ep.port); - SCH_ERR_RET(TSDB_CODE_QRY_APP_ERROR); - } - - SCH_LOCK(SCH_WRITE, &hb->lock); - memcpy(&hb->trans, trans, sizeof(*trans)); - SCH_UNLOCK(SCH_WRITE, &hb->lock); - - qDebug("hb connection updated, sId:%" PRIx64 ", nodeId:%d, fqdn:%s, port:%d, instance:%p, handle:%p", schMgmt.sId, - epId->nodeId, epId->ep.fqdn, epId->ep.port, trans->transInst, trans->transHandle); - - return TSDB_CODE_SUCCESS; -} - -void schUpdateJobErrCode(SSchJob *pJob, int32_t errCode) { - if (TSDB_CODE_SUCCESS == errCode) { - return; - } - - int32_t origCode = atomic_load_32(&pJob->errCode); - if (TSDB_CODE_SUCCESS == origCode) { - if (origCode == atomic_val_compare_exchange_32(&pJob->errCode, origCode, errCode)) { - goto _return; - } - - origCode = atomic_load_32(&pJob->errCode); - } - - if (NEED_CLIENT_HANDLE_ERROR(origCode)) { - return; - } - - if (NEED_CLIENT_HANDLE_ERROR(errCode)) { - atomic_store_32(&pJob->errCode, errCode); - goto _return; - } - - return; - -_return: - - SCH_JOB_DLOG("job errCode updated to %x - %s", errCode, tstrerror(errCode)); -} - -int32_t schProcessOnJobFailureImpl(SSchJob *pJob, int32_t status, int32_t errCode) { - // if already FAILED, no more processing - SCH_ERR_RET(schCheckAndUpdateJobStatus(pJob, status)); - - schUpdateJobErrCode(pJob, errCode); - - if (atomic_load_8(&pJob->userFetch) || pJob->attr.syncSchedule) { - tsem_post(&pJob->rspSem); - } - - int32_t code = atomic_load_32(&pJob->errCode); - - SCH_JOB_DLOG("job failed with error: %s", tstrerror(code)); - - SCH_RET(code); -} - -// Note: no more task error processing, handled in function internal -int32_t schProcessOnJobFailure(SSchJob *pJob, int32_t errCode) { - SCH_RET(schProcessOnJobFailureImpl(pJob, JOB_TASK_STATUS_FAILED, errCode)); -} - -// Note: no more error processing, handled in function internal -int32_t schProcessOnJobDropped(SSchJob *pJob, int32_t errCode) { - SCH_RET(schProcessOnJobFailureImpl(pJob, JOB_TASK_STATUS_DROPPING, errCode)); -} - -// Note: no more task error processing, handled in function internal -int32_t schProcessOnJobPartialSuccess(SSchJob *pJob) { - int32_t code = 0; - - SCH_ERR_RET(schCheckAndUpdateJobStatus(pJob, JOB_TASK_STATUS_PARTIAL_SUCCEED)); - - if (pJob->attr.syncSchedule) { - tsem_post(&pJob->rspSem); - } - - if (atomic_load_8(&pJob->userFetch)) { - SCH_ERR_JRET(schFetchFromRemote(pJob)); - } - - return TSDB_CODE_SUCCESS; - -_return: - - SCH_RET(schProcessOnJobFailure(pJob, code)); -} - -void schProcessOnDataFetched(SSchJob *job) { - atomic_val_compare_exchange_32(&job->remoteFetch, 1, 0); - tsem_post(&job->rspSem); -} - -// Note: no more task error processing, handled in function internal -int32_t schProcessOnTaskFailure(SSchJob *pJob, SSchTask *pTask, int32_t errCode) { - int8_t status = 0; - - if (schJobNeedToStop(pJob, &status)) { - SCH_TASK_DLOG("task failed not processed cause of job status, job status:%s", jobTaskStatusStr(status)); - SCH_RET(atomic_load_32(&pJob->errCode)); - } - - bool needRetry = false; - bool moved = false; - int32_t taskDone = 0; - int32_t code = 0; - - SCH_TASK_DLOG("taskOnFailure, code:%s", tstrerror(errCode)); - - SCH_ERR_JRET(schTaskCheckSetRetry(pJob, pTask, errCode, &needRetry)); - - if (!needRetry) { - SCH_TASK_ELOG("task failed and no more retry, code:%s", tstrerror(errCode)); - - if (SCH_GET_TASK_STATUS(pTask) == JOB_TASK_STATUS_EXECUTING) { - SCH_ERR_JRET(schMoveTaskToFailList(pJob, pTask, &moved)); - } else { - SCH_TASK_ELOG("task not in executing list, status:%s", SCH_GET_TASK_STATUS_STR(pTask)); - SCH_ERR_JRET(TSDB_CODE_SCH_STATUS_ERROR); - } - - SCH_SET_TASK_STATUS(pTask, JOB_TASK_STATUS_FAILED); - - if (SCH_IS_WAIT_ALL_JOB(pJob)) { - SCH_LOCK(SCH_WRITE, &pTask->level->lock); - pTask->level->taskFailed++; - taskDone = pTask->level->taskSucceed + pTask->level->taskFailed; - SCH_UNLOCK(SCH_WRITE, &pTask->level->lock); - - schUpdateJobErrCode(pJob, errCode); - - if (taskDone < pTask->level->taskNum) { - SCH_TASK_DLOG("need to wait other tasks, doneNum:%d, allNum:%d", taskDone, pTask->level->taskNum); - SCH_RET(errCode); - } - } - } else { - SCH_ERR_JRET(schHandleTaskRetry(pJob, pTask)); - - return TSDB_CODE_SUCCESS; - } - -_return: - - SCH_RET(schProcessOnJobFailure(pJob, errCode)); -} - -// Note: no more task error processing, handled in function internal -int32_t schProcessOnTaskSuccess(SSchJob *pJob, SSchTask *pTask) { - bool moved = false; - int32_t code = 0; - - SCH_TASK_DLOG("taskOnSuccess, status:%s", SCH_GET_TASK_STATUS_STR(pTask)); - - SCH_ERR_JRET(schMoveTaskToSuccList(pJob, pTask, &moved)); - - SCH_SET_TASK_STATUS(pTask, JOB_TASK_STATUS_PARTIAL_SUCCEED); - - SCH_ERR_JRET(schRecordTaskSucceedNode(pJob, pTask)); - - SCH_ERR_JRET(schLaunchTasksInFlowCtrlList(pJob, pTask)); - - int32_t parentNum = pTask->parents ? (int32_t)taosArrayGetSize(pTask->parents) : 0; - if (parentNum == 0) { - int32_t taskDone = 0; - if (SCH_IS_WAIT_ALL_JOB(pJob)) { - SCH_LOCK(SCH_WRITE, &pTask->level->lock); - pTask->level->taskSucceed++; - taskDone = pTask->level->taskSucceed + pTask->level->taskFailed; - SCH_UNLOCK(SCH_WRITE, &pTask->level->lock); - - if (taskDone < pTask->level->taskNum) { - SCH_TASK_DLOG("wait all tasks, done:%d, all:%d", taskDone, pTask->level->taskNum); - return TSDB_CODE_SUCCESS; - } else if (taskDone > pTask->level->taskNum) { - SCH_TASK_ELOG("taskDone number invalid, done:%d, total:%d", taskDone, pTask->level->taskNum); - } - - if (pTask->level->taskFailed > 0) { - SCH_RET(schProcessOnJobFailure(pJob, 0)); - } else { - SCH_RET(schProcessOnJobPartialSuccess(pJob)); - } - } else { - pJob->resNode = pTask->succeedAddr; - } - - pJob->fetchTask = pTask; - - SCH_ERR_JRET(schMoveTaskToExecList(pJob, pTask, &moved)); - - SCH_RET(schProcessOnJobPartialSuccess(pJob)); - } - - /* - if (SCH_IS_DATA_SRC_TASK(task) && job->dataSrcEps.numOfEps < SCH_MAX_CANDIDATE_EP_NUM) { - strncpy(job->dataSrcEps.fqdn[job->dataSrcEps.numOfEps], task->execAddr.fqdn, sizeof(task->execAddr.fqdn)); - job->dataSrcEps.port[job->dataSrcEps.numOfEps] = task->execAddr.port; - - ++job->dataSrcEps.numOfEps; - } - */ - - for (int32_t i = 0; i < parentNum; ++i) { - SSchTask *par = *(SSchTask **)taosArrayGet(pTask->parents, i); - int32_t readyNum = atomic_add_fetch_32(&par->childReady, 1); - - SCH_LOCK(SCH_WRITE, &par->lock); - SDownstreamSourceNode source = {.type = QUERY_NODE_DOWNSTREAM_SOURCE, - .taskId = pTask->taskId, - .schedId = schMgmt.sId, - .addr = pTask->succeedAddr}; - qSetSubplanExecutionNode(par->plan, pTask->plan->id.groupId, &source); - SCH_UNLOCK(SCH_WRITE, &par->lock); - - if (SCH_TASK_READY_TO_LUNCH(readyNum, par)) { - SCH_ERR_RET(schLaunchTaskImpl(pJob, par)); - } - } - - return TSDB_CODE_SUCCESS; - -_return: - - SCH_RET(schProcessOnJobFailure(pJob, code)); -} - -// Note: no more error processing, handled in function internal -int32_t schFetchFromRemote(SSchJob *pJob) { - int32_t code = 0; - - if (atomic_val_compare_exchange_32(&pJob->remoteFetch, 0, 1) != 0) { - SCH_JOB_ELOG("prior fetching not finished, remoteFetch:%d", atomic_load_32(&pJob->remoteFetch)); - return TSDB_CODE_SUCCESS; - } - - void *resData = atomic_load_ptr(&pJob->resData); - if (resData) { - atomic_val_compare_exchange_32(&pJob->remoteFetch, 1, 0); - - SCH_JOB_DLOG("res already fetched, res:%p", resData); - return TSDB_CODE_SUCCESS; - } - - SCH_ERR_JRET(schBuildAndSendMsg(pJob, pJob->fetchTask, &pJob->resNode, TDMT_VND_FETCH)); - - return TSDB_CODE_SUCCESS; - -_return: - - atomic_val_compare_exchange_32(&pJob->remoteFetch, 1, 0); - - SCH_RET(schProcessOnTaskFailure(pJob, pJob->fetchTask, code)); -} - -int32_t schProcessOnExplainDone(SSchJob *pJob, SSchTask *pTask, SRetrieveTableRsp *pRsp) { - SCH_TASK_DLOG("got explain rsp, rows:%d, complete:%d", htonl(pRsp->numOfRows), pRsp->completed); - - pJob->resType = SCH_RES_TYPE_FETCH; - - atomic_store_32(&pJob->resNumOfRows, htonl(pRsp->numOfRows)); - atomic_store_ptr(&pJob->resData, pRsp); - - SCH_SET_TASK_STATUS(pTask, JOB_TASK_STATUS_SUCCEED); - - schProcessOnDataFetched(pJob); - - return TSDB_CODE_SUCCESS; -} - -// Note: no more task error processing, handled in function internal -int32_t schHandleResponseMsg(SSchJob *pJob, SSchTask *pTask, int32_t msgType, char *msg, int32_t msgSize, - int32_t rspCode) { - int32_t code = 0; - int8_t status = 0; - - if (schJobNeedToStop(pJob, &status)) { - SCH_TASK_ELOG("rsp not processed cause of job status, job status:%s, rspCode:0x%x", jobTaskStatusStr(status), - rspCode); - SCH_RET(atomic_load_32(&pJob->errCode)); - } - - SCH_ERR_JRET(schValidateTaskReceivedMsgType(pJob, pTask, msgType)); - - switch (msgType) { - case TDMT_VND_CREATE_TABLE_RSP: { - SVCreateTbBatchRsp batchRsp = {0}; - if (msg) { - SDecoder coder = {0}; - tDecoderInit(&coder, msg, msgSize); - code = tDecodeSVCreateTbBatchRsp(&coder, &batchRsp); - if (TSDB_CODE_SUCCESS == code && batchRsp.nRsps > 0) { - for (int32_t i = 0; i < batchRsp.nRsps; ++i) { - SVCreateTbRsp *rsp = batchRsp.pRsps + i; - if (TSDB_CODE_SUCCESS != rsp->code) { - code = rsp->code; - tDecoderClear(&coder); - SCH_ERR_JRET(code); - } - } - } - tDecoderClear(&coder); - SCH_ERR_JRET(code); - } - - SCH_ERR_JRET(rspCode); - SCH_ERR_RET(schProcessOnTaskSuccess(pJob, pTask)); - break; - } - case TDMT_VND_DROP_TABLE_RSP: { - SVDropTbBatchRsp batchRsp = {0}; - if (msg) { - SDecoder coder = {0}; - tDecoderInit(&coder, msg, msgSize); - code = tDecodeSVDropTbBatchRsp(&coder, &batchRsp); - if (TSDB_CODE_SUCCESS == code && batchRsp.nRsps > 0) { - for (int32_t i = 0; i < batchRsp.nRsps; ++i) { - SVDropTbRsp *rsp = batchRsp.pRsps + i; - if (TSDB_CODE_SUCCESS != rsp->code) { - code = rsp->code; - tDecoderClear(&coder); - SCH_ERR_JRET(code); - } - } - } - tDecoderClear(&coder); - SCH_ERR_JRET(code); - } - - SCH_ERR_JRET(rspCode); - SCH_ERR_RET(schProcessOnTaskSuccess(pJob, pTask)); - break; - } - case TDMT_VND_ALTER_TABLE_RSP: { - SVAlterTbRsp rsp = {0}; - if (msg) { - SDecoder coder = {0}; - tDecoderInit(&coder, msg, msgSize); - code = tDecodeSVAlterTbRsp(&coder, &rsp); - tDecoderClear(&coder); - SCH_ERR_JRET(code); - SCH_ERR_JRET(rsp.code); - } - - SCH_ERR_JRET(rspCode); - - if (NULL == msg) { - SCH_ERR_JRET(TSDB_CODE_QRY_INVALID_INPUT); - } - SCH_ERR_RET(schProcessOnTaskSuccess(pJob, pTask)); - break; - } - case TDMT_VND_SUBMIT_RSP: { - SCH_ERR_JRET(rspCode); - - if (msg) { - SDecoder coder = {0}; - SSubmitRsp *rsp = taosMemoryMalloc(sizeof(*rsp)); - tDecoderInit(&coder, msg, msgSize); - code = tDecodeSSubmitRsp(&coder, rsp); - if (code) { - SCH_TASK_ELOG("decode submitRsp failed, code:%d", code); - tFreeSSubmitRsp(rsp); - SCH_ERR_JRET(code); - } - - if (rsp->nBlocks > 0) { - for (int32_t i = 0; i < rsp->nBlocks; ++i) { - SSubmitBlkRsp *blk = rsp->pBlocks + i; - if (TSDB_CODE_SUCCESS != blk->code) { - code = blk->code; - tFreeSSubmitRsp(rsp); - SCH_ERR_JRET(code); - } - } - } - - atomic_add_fetch_32(&pJob->resNumOfRows, rsp->affectedRows); - SCH_TASK_DLOG("submit succeed, affectedRows:%d", rsp->affectedRows); - - pJob->resType = SCH_RES_TYPE_QUERY; - SCH_LOCK(SCH_WRITE, &pJob->resLock); - if (pJob->resData) { - SSubmitRsp *sum = pJob->resData; - sum->affectedRows += rsp->affectedRows; - sum->nBlocks += rsp->nBlocks; - sum->pBlocks = taosMemoryRealloc(sum->pBlocks, sum->nBlocks * sizeof(*sum->pBlocks)); - memcpy(sum->pBlocks + sum->nBlocks - rsp->nBlocks, rsp->pBlocks, rsp->nBlocks * sizeof(*sum->pBlocks)); - taosMemoryFree(rsp->pBlocks); - taosMemoryFree(rsp); - } else { - pJob->resData = rsp; - } - SCH_UNLOCK(SCH_WRITE, &pJob->resLock); - } - - SCH_ERR_RET(schProcessOnTaskSuccess(pJob, pTask)); - - break; - } - case TDMT_VND_QUERY_RSP: { - SQueryTableRsp rsp = {0}; - if (msg) { - SCH_ERR_JRET(tDeserializeSQueryTableRsp(msg, msgSize, &rsp)); - SCH_ERR_JRET(rsp.code); - } - - SCH_ERR_JRET(rspCode); - - if (NULL == msg) { - SCH_ERR_JRET(TSDB_CODE_QRY_INVALID_INPUT); - } - - // SCH_ERR_JRET(schBuildAndSendMsg(pJob, pTask, NULL, TDMT_VND_RES_READY)); - - break; - } - case TDMT_VND_RES_READY_RSP: { - SResReadyRsp *rsp = (SResReadyRsp *)msg; - - SCH_ERR_JRET(rspCode); - if (NULL == msg) { - SCH_ERR_JRET(TSDB_CODE_QRY_INVALID_INPUT); - } - SCH_ERR_JRET(rsp->code); - SCH_ERR_RET(schProcessOnTaskSuccess(pJob, pTask)); - - break; - } - case TDMT_VND_EXPLAIN_RSP: { - SCH_ERR_JRET(rspCode); - if (NULL == msg) { - SCH_ERR_JRET(TSDB_CODE_QRY_INVALID_INPUT); - } - - if (!SCH_IS_EXPLAIN_JOB(pJob)) { - SCH_TASK_ELOG("invalid msg received for none explain query, msg type:%s", TMSG_INFO(msgType)); - SCH_ERR_JRET(TSDB_CODE_QRY_INVALID_INPUT); - } - - if (pJob->resData) { - SCH_TASK_ELOG("explain result is already generated, res:%p", pJob->resData); - SCH_ERR_JRET(TSDB_CODE_SCH_STATUS_ERROR); - } - - SExplainRsp rsp = {0}; - if (tDeserializeSExplainRsp(msg, msgSize, &rsp)) { - taosMemoryFree(rsp.subplanInfo); - SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - SRetrieveTableRsp *pRsp = NULL; - SCH_ERR_JRET(qExplainUpdateExecInfo(pJob->explainCtx, &rsp, pTask->plan->id.groupId, &pRsp)); - - if (pRsp) { - SCH_ERR_JRET(schProcessOnExplainDone(pJob, pTask, pRsp)); - } - break; - } - case TDMT_VND_FETCH_RSP: { - SRetrieveTableRsp *rsp = (SRetrieveTableRsp *)msg; - - SCH_ERR_JRET(rspCode); - if (NULL == msg) { - SCH_ERR_JRET(TSDB_CODE_QRY_INVALID_INPUT); - } - - if (SCH_IS_EXPLAIN_JOB(pJob)) { - if (rsp->completed) { - SRetrieveTableRsp *pRsp = NULL; - SCH_ERR_JRET(qExecExplainEnd(pJob->explainCtx, &pRsp)); - if (pRsp) { - SCH_ERR_JRET(schProcessOnExplainDone(pJob, pTask, pRsp)); - } - - return TSDB_CODE_SUCCESS; - } - - atomic_val_compare_exchange_32(&pJob->remoteFetch, 1, 0); - - SCH_ERR_JRET(schFetchFromRemote(pJob)); - - return TSDB_CODE_SUCCESS; - } - - if (pJob->resData) { - SCH_TASK_ELOG("got fetch rsp while res already exists, res:%p", pJob->resData); - taosMemoryFreeClear(rsp); - SCH_ERR_JRET(TSDB_CODE_SCH_STATUS_ERROR); - } - - atomic_store_ptr(&pJob->resData, rsp); - atomic_add_fetch_32(&pJob->resNumOfRows, htonl(rsp->numOfRows)); - - if (rsp->completed) { - SCH_SET_TASK_STATUS(pTask, JOB_TASK_STATUS_SUCCEED); - } - - SCH_TASK_DLOG("got fetch rsp, rows:%d, complete:%d", htonl(rsp->numOfRows), rsp->completed); - - schProcessOnDataFetched(pJob); - break; - } - case TDMT_VND_DROP_TASK_RSP: { - // SHOULD NEVER REACH HERE - SCH_TASK_ELOG("invalid status to handle drop task rsp, refId:%" PRIx64, pJob->refId); - SCH_ERR_JRET(TSDB_CODE_SCH_INTERNAL_ERROR); - break; - } - case TDMT_SCH_LINK_BROKEN: - SCH_TASK_ELOG("link broken received, error:%x - %s", rspCode, tstrerror(rspCode)); - SCH_ERR_JRET(rspCode); - break; - default: - SCH_TASK_ELOG("unknown rsp msg, type:%d, status:%s", msgType, SCH_GET_TASK_STATUS_STR(pTask)); - SCH_ERR_JRET(TSDB_CODE_QRY_INVALID_INPUT); - } - - return TSDB_CODE_SUCCESS; - -_return: - - SCH_RET(schProcessOnTaskFailure(pJob, pTask, code)); -} - -int32_t schGetTaskFromTaskList(SHashObj *pTaskList, uint64_t taskId, SSchTask **pTask) { - int32_t s = taosHashGetSize(pTaskList); - if (s <= 0) { - return TSDB_CODE_SUCCESS; - } - - SSchTask **task = taosHashGet(pTaskList, &taskId, sizeof(taskId)); - if (NULL == task || NULL == (*task)) { - return TSDB_CODE_SUCCESS; - } - - *pTask = *task; - - return TSDB_CODE_SUCCESS; -} - -int32_t schUpdateTaskExecNodeHandle(SSchTask *pTask, void *handle, int32_t rspCode) { - if (rspCode || NULL == pTask->execNodes || taosArrayGetSize(pTask->execNodes) > 1 || - taosArrayGetSize(pTask->execNodes) <= 0) { - return TSDB_CODE_SUCCESS; - } - - SSchNodeInfo *nodeInfo = taosArrayGet(pTask->execNodes, 0); - nodeInfo->handle = handle; - - return TSDB_CODE_SUCCESS; -} - -int32_t schHandleCallback(void *param, const SDataBuf *pMsg, int32_t msgType, int32_t rspCode) { - int32_t code = 0; - SSchTaskCallbackParam *pParam = (SSchTaskCallbackParam *)param; - SSchTask *pTask = NULL; - - SSchJob *pJob = schAcquireJob(pParam->refId); - if (NULL == pJob) { - qWarn("QID:0x%" PRIx64 ",TID:0x%" PRIx64 "taosAcquireRef job failed, may be dropped, refId:%" PRIx64, - pParam->queryId, pParam->taskId, pParam->refId); - SCH_ERR_JRET(TSDB_CODE_QRY_JOB_FREED); - } - - schGetTaskFromTaskList(pJob->execTasks, pParam->taskId, &pTask); - if (NULL == pTask) { - if (TDMT_VND_EXPLAIN_RSP == msgType) { - schGetTaskFromTaskList(pJob->succTasks, pParam->taskId, &pTask); - } else { - SCH_JOB_ELOG("task not found in execTask list, refId:%" PRIx64 ", taskId:%" PRIx64, pParam->refId, - pParam->taskId); - SCH_ERR_JRET(TSDB_CODE_SCH_INTERNAL_ERROR); - } - } - - if (NULL == pTask) { - SCH_JOB_ELOG("task not found in execList & succList, refId:%" PRIx64 ", taskId:%" PRIx64, pParam->refId, - pParam->taskId); - SCH_ERR_JRET(TSDB_CODE_SCH_INTERNAL_ERROR); - } - - SCH_TASK_DLOG("rsp msg received, type:%s, handle:%p, code:%s", TMSG_INFO(msgType), pMsg->handle, tstrerror(rspCode)); - - SCH_SET_TASK_HANDLE(pTask, pMsg->handle); - schUpdateTaskExecNodeHandle(pTask, pMsg->handle, rspCode); - SCH_ERR_JRET(schHandleResponseMsg(pJob, pTask, msgType, pMsg->pData, pMsg->len, rspCode)); - -_return: - if (pJob) { - schReleaseJob(pParam->refId); - } - - taosMemoryFreeClear(param); - SCH_RET(code); -} - -int32_t schHandleSubmitCallback(void *param, const SDataBuf *pMsg, int32_t code) { - return schHandleCallback(param, pMsg, TDMT_VND_SUBMIT_RSP, code); -} - -int32_t schHandleCreateTableCallback(void *param, const SDataBuf *pMsg, int32_t code) { - return schHandleCallback(param, pMsg, TDMT_VND_CREATE_TABLE_RSP, code); -} - -int32_t schHandleDropTableCallback(void *param, const SDataBuf *pMsg, int32_t code) { - return schHandleCallback(param, pMsg, TDMT_VND_DROP_TABLE_RSP, code); -} - -int32_t schHandleAlterTableCallback(void *param, const SDataBuf *pMsg, int32_t code) { - return schHandleCallback(param, pMsg, TDMT_VND_ALTER_TABLE_RSP, code); -} - -int32_t schHandleQueryCallback(void *param, const SDataBuf *pMsg, int32_t code) { - return schHandleCallback(param, pMsg, TDMT_VND_QUERY_RSP, code); -} - -int32_t schHandleFetchCallback(void *param, const SDataBuf *pMsg, int32_t code) { - return schHandleCallback(param, pMsg, TDMT_VND_FETCH_RSP, code); -} - -int32_t schHandleReadyCallback(void *param, const SDataBuf *pMsg, int32_t code) { - return schHandleCallback(param, pMsg, TDMT_VND_RES_READY_RSP, code); -} - -int32_t schHandleExplainCallback(void *param, const SDataBuf *pMsg, int32_t code) { - return schHandleCallback(param, pMsg, TDMT_VND_EXPLAIN_RSP, code); -} - -int32_t schHandleDropCallback(void *param, const SDataBuf *pMsg, int32_t code) { - SSchTaskCallbackParam *pParam = (SSchTaskCallbackParam *)param; - qDebug("QID:%" PRIx64 ",TID:%" PRIx64 " drop task rsp received, code:%x", pParam->queryId, pParam->taskId, code); - return TSDB_CODE_SUCCESS; -} - -int32_t schHandleHbCallback(void *param, const SDataBuf *pMsg, int32_t code) { - SSchedulerHbRsp rsp = {0}; - SSchTaskCallbackParam *pParam = (SSchTaskCallbackParam *)param; - - if (code) { - qError("hb rsp error:%s", tstrerror(code)); - SCH_ERR_JRET(code); - } - - if (tDeserializeSSchedulerHbRsp(pMsg->pData, pMsg->len, &rsp)) { - qError("invalid hb rsp msg, size:%d", pMsg->len); - SCH_ERR_JRET(TSDB_CODE_QRY_INVALID_INPUT); - } - - SSchTrans trans = {0}; - trans.transInst = pParam->transport; - trans.transHandle = pMsg->handle; - - SCH_ERR_JRET(schUpdateHbConnection(&rsp.epId, &trans)); - - int32_t taskNum = (int32_t)taosArrayGetSize(rsp.taskStatus); - qDebug("%d task status in hb rsp, nodeId:%d, fqdn:%s, port:%d", taskNum, rsp.epId.nodeId, rsp.epId.ep.fqdn, - rsp.epId.ep.port); - - for (int32_t i = 0; i < taskNum; ++i) { - STaskStatus *taskStatus = taosArrayGet(rsp.taskStatus, i); - - SSchJob *pJob = schAcquireJob(taskStatus->refId); - if (NULL == pJob) { - qWarn("job not found, refId:0x%" PRIx64 ",QID:0x%" PRIx64 ",TID:0x%" PRIx64, taskStatus->refId, - taskStatus->queryId, taskStatus->taskId); - // TODO DROP TASK FROM SERVER!!!! - continue; - } - - // TODO - - SCH_JOB_DLOG("TID:0x%" PRIx64 " task status in server: %s", taskStatus->taskId, - jobTaskStatusStr(taskStatus->status)); - - schReleaseJob(taskStatus->refId); - } - -_return: - - tFreeSSchedulerHbRsp(&rsp); - taosMemoryFree(param); - - SCH_RET(code); -} - -int32_t schHandleLinkBrokenCallback(void *param, const SDataBuf *pMsg, int32_t code) { - SSchCallbackParamHeader *head = (SSchCallbackParamHeader *)param; - rpcReleaseHandle(pMsg->handle, TAOS_CONN_CLIENT); - - qDebug("handle %p is broken", pMsg->handle); - - if (head->isHbParam) { - SSchHbCallbackParam *hbParam = (SSchHbCallbackParam *)param; - SSchTrans trans = {.transInst = hbParam->transport, .transHandle = NULL}; - SCH_ERR_RET(schUpdateHbConnection(&hbParam->nodeEpId, &trans)); - - SCH_ERR_RET(schBuildAndSendHbMsg(&hbParam->nodeEpId)); - } else { - SCH_ERR_RET(schHandleCallback(param, pMsg, TDMT_SCH_LINK_BROKEN, code)); - } - - return TSDB_CODE_SUCCESS; -} - -int32_t schGetCallbackFp(int32_t msgType, __async_send_cb_fn_t *fp) { - switch (msgType) { - case TDMT_VND_CREATE_TABLE: - *fp = schHandleCreateTableCallback; - break; - case TDMT_VND_DROP_TABLE: - *fp = schHandleDropTableCallback; - break; - case TDMT_VND_ALTER_TABLE: - *fp = schHandleAlterTableCallback; - break; - case TDMT_VND_SUBMIT: - *fp = schHandleSubmitCallback; - break; - case TDMT_VND_QUERY: - *fp = schHandleQueryCallback; - break; - case TDMT_VND_RES_READY: - *fp = schHandleReadyCallback; - break; - case TDMT_VND_EXPLAIN: - *fp = schHandleExplainCallback; - break; - case TDMT_VND_FETCH: - *fp = schHandleFetchCallback; - break; - case TDMT_VND_DROP_TASK: - *fp = schHandleDropCallback; - break; - case TDMT_VND_QUERY_HEARTBEAT: - *fp = schHandleHbCallback; - break; - case TDMT_SCH_LINK_BROKEN: - *fp = schHandleLinkBrokenCallback; - break; - default: - qError("unknown msg type for callback, msgType:%d", msgType); - SCH_ERR_RET(TSDB_CODE_QRY_APP_ERROR); - } - - return TSDB_CODE_SUCCESS; -} - -int32_t schGenerateTaskCallBackAHandle(SSchJob *pJob, SSchTask *pTask, int32_t msgType, SMsgSendInfo **pMsgSendInfo) { - int32_t code = 0; - SMsgSendInfo *msgSendInfo = taosMemoryCalloc(1, sizeof(SMsgSendInfo)); - if (NULL == msgSendInfo) { - SCH_TASK_ELOG("calloc %d failed", (int32_t)sizeof(SMsgSendInfo)); - SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - SSchTaskCallbackParam *param = taosMemoryCalloc(1, sizeof(SSchTaskCallbackParam)); - if (NULL == param) { - SCH_TASK_ELOG("calloc %d failed", (int32_t)sizeof(SSchTaskCallbackParam)); - SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - __async_send_cb_fn_t fp = NULL; - SCH_ERR_JRET(schGetCallbackFp(msgType, &fp)); - - param->queryId = pJob->queryId; - param->refId = pJob->refId; - param->taskId = SCH_TASK_ID(pTask); - param->transport = pJob->transport; - - msgSendInfo->param = param; - msgSendInfo->fp = fp; - - *pMsgSendInfo = msgSendInfo; - - return TSDB_CODE_SUCCESS; - -_return: - - taosMemoryFree(param); - taosMemoryFree(msgSendInfo); - - SCH_RET(code); -} - -void schFreeRpcCtxVal(const void *arg) { - if (NULL == arg) { - return; - } - - SMsgSendInfo *pMsgSendInfo = (SMsgSendInfo *)arg; - taosMemoryFreeClear(pMsgSendInfo->param); - taosMemoryFreeClear(pMsgSendInfo); -} - -int32_t schMakeTaskCallbackParam(SSchJob *pJob, SSchTask *pTask, void **pParam) { - SSchTaskCallbackParam *param = taosMemoryCalloc(1, sizeof(SSchTaskCallbackParam)); - if (NULL == param) { - SCH_TASK_ELOG("calloc %d failed", (int32_t)sizeof(SSchTaskCallbackParam)); - SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - param->queryId = pJob->queryId; - param->refId = pJob->refId; - param->taskId = SCH_TASK_ID(pTask); - param->transport = pJob->transport; - - *pParam = param; - - return TSDB_CODE_SUCCESS; -} - -int32_t schMakeHbCallbackParam(SSchJob *pJob, SSchTask *pTask, void **pParam) { - SSchHbCallbackParam *param = taosMemoryCalloc(1, sizeof(SSchHbCallbackParam)); - if (NULL == param) { - SCH_TASK_ELOG("calloc %d failed", (int32_t)sizeof(SSchHbCallbackParam)); - SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - param->head.isHbParam = true; - - SQueryNodeAddr *addr = taosArrayGet(pTask->candidateAddrs, pTask->candidateIdx); - - param->nodeEpId.nodeId = addr->nodeId; - memcpy(¶m->nodeEpId.ep, SCH_GET_CUR_EP(addr), sizeof(SEp)); - param->transport = pJob->transport; - - *pParam = param; - - return TSDB_CODE_SUCCESS; -} - -int32_t schMakeBrokenLinkVal(SSchJob *pJob, SSchTask *pTask, SRpcBrokenlinkVal *brokenVal, bool isHb) { - int32_t code = 0; - SMsgSendInfo *pMsgSendInfo = NULL; - - pMsgSendInfo = taosMemoryCalloc(1, sizeof(SMsgSendInfo)); - if (NULL == pMsgSendInfo) { - SCH_TASK_ELOG("calloc %d failed", (int32_t)sizeof(SMsgSendInfo)); - SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - if (isHb) { - SCH_ERR_JRET(schMakeHbCallbackParam(pJob, pTask, &pMsgSendInfo->param)); - } else { - SCH_ERR_JRET(schMakeTaskCallbackParam(pJob, pTask, &pMsgSendInfo->param)); - } - - int32_t msgType = TDMT_SCH_LINK_BROKEN; - __async_send_cb_fn_t fp = NULL; - SCH_ERR_JRET(schGetCallbackFp(msgType, &fp)); - - pMsgSendInfo->fp = fp; - - brokenVal->msgType = msgType; - brokenVal->val = pMsgSendInfo; - brokenVal->clone = schCloneSMsgSendInfo; - brokenVal->freeFunc = schFreeRpcCtxVal; - - return TSDB_CODE_SUCCESS; - -_return: - - taosMemoryFreeClear(pMsgSendInfo->param); - taosMemoryFreeClear(pMsgSendInfo); - - SCH_RET(code); -} - -int32_t schMakeQueryRpcCtx(SSchJob *pJob, SSchTask *pTask, SRpcCtx *pCtx) { - int32_t code = 0; - SMsgSendInfo *pReadyMsgSendInfo = NULL; - SMsgSendInfo *pExplainMsgSendInfo = NULL; - - pCtx->args = taosHashInit(1, taosGetDefaultHashFunction(TSDB_DATA_TYPE_INT), false, HASH_ENTRY_LOCK); - if (NULL == pCtx->args) { - SCH_TASK_ELOG("taosHashInit %d RpcCtx failed", 1); - SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - SCH_ERR_JRET(schGenerateTaskCallBackAHandle(pJob, pTask, TDMT_VND_RES_READY, &pReadyMsgSendInfo)); - SCH_ERR_JRET(schGenerateTaskCallBackAHandle(pJob, pTask, TDMT_VND_EXPLAIN, &pExplainMsgSendInfo)); - - int32_t msgType = TDMT_VND_RES_READY_RSP; - SRpcCtxVal ctxVal = {.val = pReadyMsgSendInfo, .clone = schCloneSMsgSendInfo, .freeFunc = schFreeRpcCtxVal}; - if (taosHashPut(pCtx->args, &msgType, sizeof(msgType), &ctxVal, sizeof(ctxVal))) { - SCH_TASK_ELOG("taosHashPut msg %d to rpcCtx failed", msgType); - SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - msgType = TDMT_VND_EXPLAIN_RSP; - ctxVal.val = pExplainMsgSendInfo; - if (taosHashPut(pCtx->args, &msgType, sizeof(msgType), &ctxVal, sizeof(ctxVal))) { - SCH_TASK_ELOG("taosHashPut msg %d to rpcCtx failed", msgType); - SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - SCH_ERR_JRET(schMakeBrokenLinkVal(pJob, pTask, &pCtx->brokenVal, false)); - - return TSDB_CODE_SUCCESS; - -_return: - - taosHashCleanup(pCtx->args); - - if (pReadyMsgSendInfo) { - taosMemoryFreeClear(pReadyMsgSendInfo->param); - taosMemoryFreeClear(pReadyMsgSendInfo); - } - - if (pExplainMsgSendInfo) { - taosMemoryFreeClear(pExplainMsgSendInfo->param); - taosMemoryFreeClear(pExplainMsgSendInfo); - } - - SCH_RET(code); -} - -int32_t schMakeHbRpcCtx(SSchJob *pJob, SSchTask *pTask, SRpcCtx *pCtx) { - int32_t code = 0; - SSchHbCallbackParam *param = NULL; - SMsgSendInfo *pMsgSendInfo = NULL; - SQueryNodeAddr *addr = taosArrayGet(pTask->candidateAddrs, pTask->candidateIdx); - SQueryNodeEpId epId = {0}; - - epId.nodeId = addr->nodeId; - memcpy(&epId.ep, SCH_GET_CUR_EP(addr), sizeof(SEp)); - - pCtx->args = taosHashInit(1, taosGetDefaultHashFunction(TSDB_DATA_TYPE_INT), false, HASH_ENTRY_LOCK); - if (NULL == pCtx->args) { - SCH_TASK_ELOG("taosHashInit %d RpcCtx failed", 1); - SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - pMsgSendInfo = taosMemoryCalloc(1, sizeof(SMsgSendInfo)); - if (NULL == pMsgSendInfo) { - SCH_TASK_ELOG("calloc %d failed", (int32_t)sizeof(SMsgSendInfo)); - SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - param = taosMemoryCalloc(1, sizeof(SSchHbCallbackParam)); - if (NULL == param) { - SCH_TASK_ELOG("calloc %d failed", (int32_t)sizeof(SSchHbCallbackParam)); - SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - int32_t msgType = TDMT_VND_QUERY_HEARTBEAT_RSP; - __async_send_cb_fn_t fp = NULL; - SCH_ERR_JRET(schGetCallbackFp(TDMT_VND_QUERY_HEARTBEAT, &fp)); - - param->nodeEpId = epId; - param->transport = pJob->transport; - - pMsgSendInfo->param = param; - pMsgSendInfo->fp = fp; - - SRpcCtxVal ctxVal = {.val = pMsgSendInfo, .clone = schCloneSMsgSendInfo, .freeFunc = schFreeRpcCtxVal}; - if (taosHashPut(pCtx->args, &msgType, sizeof(msgType), &ctxVal, sizeof(ctxVal))) { - SCH_TASK_ELOG("taosHashPut msg %d to rpcCtx failed", msgType); - SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - SCH_ERR_JRET(schMakeBrokenLinkVal(pJob, pTask, &pCtx->brokenVal, true)); - - return TSDB_CODE_SUCCESS; - -_return: - - taosHashCleanup(pCtx->args); - taosMemoryFreeClear(param); - taosMemoryFreeClear(pMsgSendInfo); - - SCH_RET(code); -} - -int32_t schRegisterHbConnection(SSchJob *pJob, SSchTask *pTask, SQueryNodeEpId *epId, bool *exist) { - int32_t code = 0; - SSchHbTrans hb = {0}; - - hb.trans.transInst = pJob->transport; - - SCH_ERR_RET(schMakeHbRpcCtx(pJob, pTask, &hb.rpcCtx)); - - code = taosHashPut(schMgmt.hbConnections, epId, sizeof(SQueryNodeEpId), &hb, sizeof(SSchHbTrans)); - if (code) { - schFreeRpcCtx(&hb.rpcCtx); - - if (HASH_NODE_EXIST(code)) { - *exist = true; - return TSDB_CODE_SUCCESS; - } - - qError("taosHashPut hb trans failed, nodeId:%d, fqdn:%s, port:%d", epId->nodeId, epId->ep.fqdn, epId->ep.port); - SCH_ERR_RET(code); - } - - return TSDB_CODE_SUCCESS; -} - -int32_t schCloneCallbackParam(SSchCallbackParamHeader *pSrc, SSchCallbackParamHeader **pDst) { - if (pSrc->isHbParam) { - SSchHbCallbackParam *dst = taosMemoryMalloc(sizeof(SSchHbCallbackParam)); - if (NULL == dst) { - qError("malloc SSchHbCallbackParam failed"); - SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - memcpy(dst, pSrc, sizeof(*dst)); - *pDst = (SSchCallbackParamHeader *)dst; - - return TSDB_CODE_SUCCESS; - } - - SSchTaskCallbackParam *dst = taosMemoryMalloc(sizeof(SSchTaskCallbackParam)); - if (NULL == dst) { - qError("malloc SSchTaskCallbackParam failed"); - SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - memcpy(dst, pSrc, sizeof(*dst)); - *pDst = (SSchCallbackParamHeader *)dst; - - return TSDB_CODE_SUCCESS; -} - -int32_t schCloneSMsgSendInfo(void *src, void **dst) { - SMsgSendInfo *pSrc = src; - int32_t code = 0; - SMsgSendInfo *pDst = taosMemoryMalloc(sizeof(*pSrc)); - if (NULL == pDst) { - qError("malloc SMsgSendInfo for rpcCtx failed, len:%d", (int32_t)sizeof(*pSrc)); - SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - memcpy(pDst, pSrc, sizeof(*pSrc)); - pDst->param = NULL; - - SCH_ERR_JRET(schCloneCallbackParam(pSrc->param, (SSchCallbackParamHeader **)&pDst->param)); - - *dst = pDst; - - return TSDB_CODE_SUCCESS; - -_return: - - taosMemoryFreeClear(pDst); - SCH_RET(code); -} - -int32_t schCloneHbRpcCtx(SRpcCtx *pSrc, SRpcCtx *pDst) { - int32_t code = 0; - memcpy(&pDst->brokenVal, &pSrc->brokenVal, sizeof(pSrc->brokenVal)); - pDst->brokenVal.val = NULL; - - SCH_ERR_RET(schCloneSMsgSendInfo(pSrc->brokenVal.val, &pDst->brokenVal.val)); - - pDst->args = taosHashInit(1, taosGetDefaultHashFunction(TSDB_DATA_TYPE_INT), false, HASH_ENTRY_LOCK); - if (NULL == pDst->args) { - qError("taosHashInit %d RpcCtx failed", 1); - SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - SRpcCtxVal dst = {0}; - void *pIter = taosHashIterate(pSrc->args, NULL); - while (pIter) { - SRpcCtxVal *pVal = (SRpcCtxVal *)pIter; - int32_t *msgType = taosHashGetKey(pIter, NULL); - - dst = *pVal; - dst.val = NULL; - - SCH_ERR_JRET(schCloneSMsgSendInfo(pVal->val, &dst.val)); - - if (taosHashPut(pDst->args, msgType, sizeof(*msgType), &dst, sizeof(dst))) { - qError("taosHashPut msg %d to rpcCtx failed", *msgType); - (*dst.freeFunc)(dst.val); - SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - pIter = taosHashIterate(pSrc->args, pIter); - } - - return TSDB_CODE_SUCCESS; - -_return: - - schFreeRpcCtx(pDst); - SCH_RET(code); -} - -int32_t schAsyncSendMsg(SSchJob *pJob, SSchTask *pTask, void *transport, SEpSet *epSet, int32_t msgType, void *msg, - uint32_t msgSize, bool persistHandle, SRpcCtx *ctx) { - int32_t code = 0; - - SSchTrans *trans = (SSchTrans *)transport; - - SMsgSendInfo *pMsgSendInfo = NULL; - SCH_ERR_JRET(schGenerateTaskCallBackAHandle(pJob, pTask, msgType, &pMsgSendInfo)); - - pMsgSendInfo->msgInfo.pData = msg; - pMsgSendInfo->msgInfo.len = msgSize; - pMsgSendInfo->msgInfo.handle = trans->transHandle; - pMsgSendInfo->msgType = msgType; - - qDebug("start to send %s msg to node[%d,%s,%d], refId:%" PRIx64 "instance:%p, handle:%p", TMSG_INFO(msgType), - ntohl(((SMsgHead *)msg)->vgId), epSet->eps[epSet->inUse].fqdn, epSet->eps[epSet->inUse].port, pJob->refId, - trans->transInst, trans->transHandle); - - int64_t transporterId = 0; - code = asyncSendMsgToServerExt(trans->transInst, epSet, &transporterId, pMsgSendInfo, persistHandle, ctx); - if (code) { - SCH_ERR_JRET(code); - } - - SCH_TASK_DLOG("req msg sent, refId:%" PRIx64 ", type:%d, %s", pJob->refId, msgType, TMSG_INFO(msgType)); - return TSDB_CODE_SUCCESS; - -_return: - - if (pMsgSendInfo) { - taosMemoryFreeClear(pMsgSendInfo->param); - taosMemoryFreeClear(pMsgSendInfo); - } - - SCH_RET(code); -} - -int32_t schBuildAndSendHbMsg(SQueryNodeEpId *nodeEpId) { - SSchedulerHbReq req = {0}; - int32_t code = 0; - SRpcCtx rpcCtx = {0}; - SSchTrans trans = {0}; - int32_t msgType = TDMT_VND_QUERY_HEARTBEAT; - - req.header.vgId = nodeEpId->nodeId; - req.sId = schMgmt.sId; - memcpy(&req.epId, nodeEpId, sizeof(SQueryNodeEpId)); - - SSchHbTrans *hb = taosHashGet(schMgmt.hbConnections, nodeEpId, sizeof(SQueryNodeEpId)); - if (NULL == hb) { - qError("taosHashGet hb connection failed, nodeId:%d, fqdn:%s, port:%d", nodeEpId->nodeId, nodeEpId->ep.fqdn, - nodeEpId->ep.port); - SCH_ERR_RET(code); - } - - SCH_LOCK(SCH_WRITE, &hb->lock); - code = schCloneHbRpcCtx(&hb->rpcCtx, &rpcCtx); - memcpy(&trans, &hb->trans, sizeof(trans)); - SCH_UNLOCK(SCH_WRITE, &hb->lock); - - SCH_ERR_RET(code); - - int32_t msgSize = tSerializeSSchedulerHbReq(NULL, 0, &req); - if (msgSize < 0) { - qError("tSerializeSSchedulerHbReq hbReq failed, size:%d", msgSize); - SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - void *msg = taosMemoryCalloc(1, msgSize); - if (NULL == msg) { - qError("calloc hb req %d failed", msgSize); - SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - if (tSerializeSSchedulerHbReq(msg, msgSize, &req) < 0) { - qError("tSerializeSSchedulerHbReq hbReq failed, size:%d", msgSize); - SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - SMsgSendInfo *pMsgSendInfo = taosMemoryCalloc(1, sizeof(SMsgSendInfo)); - if (NULL == pMsgSendInfo) { - qError("calloc SMsgSendInfo failed"); - SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - SSchTaskCallbackParam *param = taosMemoryCalloc(1, sizeof(SSchTaskCallbackParam)); - if (NULL == param) { - qError("calloc SSchTaskCallbackParam failed"); - SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - __async_send_cb_fn_t fp = NULL; - SCH_ERR_JRET(schGetCallbackFp(msgType, &fp)); - - param->transport = trans.transInst; - - pMsgSendInfo->param = param; - pMsgSendInfo->msgInfo.pData = msg; - pMsgSendInfo->msgInfo.len = msgSize; - pMsgSendInfo->msgInfo.handle = trans.transHandle; - pMsgSendInfo->msgType = msgType; - pMsgSendInfo->fp = fp; - - int64_t transporterId = 0; - SEpSet epSet = {.inUse = 0, .numOfEps = 1}; - memcpy(&epSet.eps[0], &nodeEpId->ep, sizeof(nodeEpId->ep)); - - qDebug("start to send hb msg, instance:%p, handle:%p, fqdn:%s, port:%d", trans.transInst, trans.transHandle, - nodeEpId->ep.fqdn, nodeEpId->ep.port); - - code = asyncSendMsgToServerExt(trans.transInst, &epSet, &transporterId, pMsgSendInfo, true, &rpcCtx); - if (code) { - qError("fail to send hb msg, instance:%p, handle:%p, fqdn:%s, port:%d, error:%x - %s", trans.transInst, - trans.transHandle, nodeEpId->ep.fqdn, nodeEpId->ep.port, code, tstrerror(code)); - SCH_ERR_JRET(code); - } - - qDebug("hb msg sent"); - return TSDB_CODE_SUCCESS; - -_return: - - taosMemoryFreeClear(msg); - taosMemoryFreeClear(param); - taosMemoryFreeClear(pMsgSendInfo); - schFreeRpcCtx(&rpcCtx); - SCH_RET(code); -} - -int32_t schBuildAndSendMsg(SSchJob *pJob, SSchTask *pTask, SQueryNodeAddr *addr, int32_t msgType) { - uint32_t msgSize = 0; - void *msg = NULL; - int32_t code = 0; - bool isCandidateAddr = false; - bool persistHandle = false; - SRpcCtx rpcCtx = {0}; - - if (NULL == addr) { - addr = taosArrayGet(pTask->candidateAddrs, pTask->candidateIdx); - isCandidateAddr = true; - } - - SEpSet epSet = addr->epSet; - - switch (msgType) { - case TDMT_VND_CREATE_TABLE: - case TDMT_VND_DROP_TABLE: - case TDMT_VND_ALTER_TABLE: - case TDMT_VND_SUBMIT: { - msgSize = pTask->msgLen; - msg = taosMemoryCalloc(1, msgSize); - if (NULL == msg) { - SCH_TASK_ELOG("calloc %d failed", msgSize); - SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - memcpy(msg, pTask->msg, msgSize); - break; - } - - case TDMT_VND_QUERY: { - SCH_ERR_RET(schMakeQueryRpcCtx(pJob, pTask, &rpcCtx)); - - uint32_t len = strlen(pJob->sql); - msgSize = sizeof(SSubQueryMsg) + pTask->msgLen + len; - msg = taosMemoryCalloc(1, msgSize); - if (NULL == msg) { - SCH_TASK_ELOG("calloc %d failed", msgSize); - SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - SSubQueryMsg *pMsg = msg; - pMsg->header.vgId = htonl(addr->nodeId); - pMsg->sId = htobe64(schMgmt.sId); - pMsg->queryId = htobe64(pJob->queryId); - pMsg->taskId = htobe64(pTask->taskId); - pMsg->refId = htobe64(pJob->refId); - pMsg->taskType = TASK_TYPE_TEMP; - pMsg->explain = SCH_IS_EXPLAIN_JOB(pJob); - pMsg->phyLen = htonl(pTask->msgLen); - pMsg->sqlLen = htonl(len); - - memcpy(pMsg->msg, pJob->sql, len); - memcpy(pMsg->msg + len, pTask->msg, pTask->msgLen); - - persistHandle = true; - break; - } - - case TDMT_VND_RES_READY: { - msgSize = sizeof(SResReadyReq); - msg = taosMemoryCalloc(1, msgSize); - if (NULL == msg) { - SCH_TASK_ELOG("calloc %d failed", msgSize); - SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - SResReadyReq *pMsg = msg; - - pMsg->header.vgId = htonl(addr->nodeId); - - pMsg->sId = htobe64(schMgmt.sId); - pMsg->queryId = htobe64(pJob->queryId); - pMsg->taskId = htobe64(pTask->taskId); - break; - } - case TDMT_VND_FETCH: { - msgSize = sizeof(SResFetchReq); - msg = taosMemoryCalloc(1, msgSize); - if (NULL == msg) { - SCH_TASK_ELOG("calloc %d failed", msgSize); - SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - SResFetchReq *pMsg = msg; - - pMsg->header.vgId = htonl(addr->nodeId); - - pMsg->sId = htobe64(schMgmt.sId); - pMsg->queryId = htobe64(pJob->queryId); - pMsg->taskId = htobe64(pTask->taskId); - - break; - } - case TDMT_VND_DROP_TASK: { - msgSize = sizeof(STaskDropReq); - msg = taosMemoryCalloc(1, msgSize); - if (NULL == msg) { - SCH_TASK_ELOG("calloc %d failed", msgSize); - SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - STaskDropReq *pMsg = msg; - - pMsg->header.vgId = htonl(addr->nodeId); - - pMsg->sId = htobe64(schMgmt.sId); - pMsg->queryId = htobe64(pJob->queryId); - pMsg->taskId = htobe64(pTask->taskId); - pMsg->refId = htobe64(pJob->refId); - break; - } - case TDMT_VND_QUERY_HEARTBEAT: { - SCH_ERR_RET(schMakeHbRpcCtx(pJob, pTask, &rpcCtx)); - - SSchedulerHbReq req = {0}; - req.sId = schMgmt.sId; - req.header.vgId = addr->nodeId; - req.epId.nodeId = addr->nodeId; - memcpy(&req.epId.ep, SCH_GET_CUR_EP(addr), sizeof(SEp)); - - msgSize = tSerializeSSchedulerHbReq(NULL, 0, &req); - if (msgSize < 0) { - SCH_JOB_ELOG("tSerializeSSchedulerHbReq hbReq failed, size:%d", msgSize); - SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - msg = taosMemoryCalloc(1, msgSize); - if (NULL == msg) { - SCH_JOB_ELOG("calloc %d failed", msgSize); - SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - if (tSerializeSSchedulerHbReq(msg, msgSize, &req) < 0) { - SCH_JOB_ELOG("tSerializeSSchedulerHbReq hbReq failed, size:%d", msgSize); - SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - persistHandle = true; - break; - } - default: - SCH_TASK_ELOG("unknown msg type to send, msgType:%d", msgType); - SCH_ERR_RET(TSDB_CODE_SCH_INTERNAL_ERROR); - break; - } - - SCH_SET_TASK_LASTMSG_TYPE(pTask, msgType); - - SSchTrans trans = {.transInst = pJob->transport, .transHandle = SCH_GET_TASK_HANDLE(pTask)}; - SCH_ERR_JRET(schAsyncSendMsg(pJob, pTask, &trans, &epSet, msgType, msg, msgSize, persistHandle, - (rpcCtx.args ? &rpcCtx : NULL))); - - if (msgType == TDMT_VND_QUERY) { - SCH_ERR_RET(schRecordTaskExecNode(pJob, pTask, addr, trans.transHandle)); - } - - return TSDB_CODE_SUCCESS; - -_return: - - SCH_SET_TASK_LASTMSG_TYPE(pTask, -1); - schFreeRpcCtx(&rpcCtx); - - taosMemoryFreeClear(msg); - SCH_RET(code); -} - -int32_t schEnsureHbConnection(SSchJob *pJob, SSchTask *pTask) { - SQueryNodeAddr *addr = taosArrayGet(pTask->candidateAddrs, pTask->candidateIdx); - SQueryNodeEpId epId = {0}; - - epId.nodeId = addr->nodeId; - memcpy(&epId.ep, SCH_GET_CUR_EP(addr), sizeof(SEp)); - -#if 1 - SSchHbTrans *hb = taosHashGet(schMgmt.hbConnections, &epId, sizeof(SQueryNodeEpId)); - if (NULL == hb) { - bool exist = false; - SCH_ERR_RET(schRegisterHbConnection(pJob, pTask, &epId, &exist)); - if (!exist) { - SCH_ERR_RET(schBuildAndSendHbMsg(&epId)); - } - } -#endif - - return TSDB_CODE_SUCCESS; -} - -int32_t schLaunchTaskImpl(SSchJob *pJob, SSchTask *pTask) { - int8_t status = 0; - int32_t code = 0; - - atomic_add_fetch_32(&pTask->level->taskLaunchedNum, 1); - - if (schJobNeedToStop(pJob, &status)) { - SCH_TASK_DLOG("no need to launch task cause of job status, job status:%s", jobTaskStatusStr(status)); - - SCH_RET(atomic_load_32(&pJob->errCode)); - } - - // NOTE: race condition: the task should be put into the hash table before send msg to server - if (SCH_GET_TASK_STATUS(pTask) != JOB_TASK_STATUS_EXECUTING) { - SCH_ERR_RET(schPushTaskToExecList(pJob, pTask)); - SCH_SET_TASK_STATUS(pTask, JOB_TASK_STATUS_EXECUTING); - } - - SSubplan *plan = pTask->plan; - - if (NULL == pTask->msg) { // TODO add more detailed reason for failure - code = qSubPlanToString(plan, &pTask->msg, &pTask->msgLen); - if (TSDB_CODE_SUCCESS != code) { - SCH_TASK_ELOG("failed to create physical plan, code:%s, msg:%p, len:%d", tstrerror(code), pTask->msg, - pTask->msgLen); - SCH_ERR_RET(code); - } else { - SCH_TASK_DLOGL("physical plan len:%d, %s", pTask->msgLen, pTask->msg); - } - } - - SCH_ERR_RET(schSetTaskCandidateAddrs(pJob, pTask)); - - if (SCH_IS_QUERY_JOB(pJob)) { - SCH_ERR_RET(schEnsureHbConnection(pJob, pTask)); - } - - SCH_ERR_RET(schBuildAndSendMsg(pJob, pTask, NULL, plan->msgType)); - - return TSDB_CODE_SUCCESS; -} - -// Note: no more error processing, handled in function internal -int32_t schLaunchTask(SSchJob *pJob, SSchTask *pTask) { - bool enough = false; - int32_t code = 0; - - SCH_SET_TASK_HANDLE(pTask, NULL); - - if (SCH_TASK_NEED_FLOW_CTRL(pJob, pTask)) { - SCH_ERR_JRET(schCheckIncTaskFlowQuota(pJob, pTask, &enough)); - - if (enough) { - SCH_ERR_JRET(schLaunchTaskImpl(pJob, pTask)); - } - } else { - SCH_ERR_JRET(schLaunchTaskImpl(pJob, pTask)); - } - - return TSDB_CODE_SUCCESS; - -_return: - - SCH_RET(schProcessOnTaskFailure(pJob, pTask, code)); -} - -int32_t schLaunchLevelTasks(SSchJob *pJob, SSchLevel *level) { - for (int32_t i = 0; i < level->taskNum; ++i) { - SSchTask *pTask = taosArrayGet(level->subTasks, i); - - SCH_ERR_RET(schLaunchTask(pJob, pTask)); - } - - return TSDB_CODE_SUCCESS; -} - -int32_t schLaunchJob(SSchJob *pJob) { - SSchLevel *level = taosArrayGet(pJob->levels, pJob->levelIdx); - - SCH_ERR_RET(schCheckAndUpdateJobStatus(pJob, JOB_TASK_STATUS_EXECUTING)); - - SCH_ERR_RET(schCheckJobNeedFlowCtrl(pJob, level)); - - SCH_ERR_RET(schLaunchLevelTasks(pJob, level)); - - return TSDB_CODE_SUCCESS; -} - -void schDropTaskOnExecutedNode(SSchJob *pJob, SSchTask *pTask) { - if (NULL == pTask->execNodes) { - SCH_TASK_DLOG("no exec address, status:%s", SCH_GET_TASK_STATUS_STR(pTask)); - return; - } - - int32_t size = (int32_t)taosArrayGetSize(pTask->execNodes); - - if (size <= 0) { - SCH_TASK_DLOG("task has no execNodes, no need to drop it, status:%s", SCH_GET_TASK_STATUS_STR(pTask)); - return; - } - - SSchNodeInfo *nodeInfo = NULL; - for (int32_t i = 0; i < size; ++i) { - nodeInfo = (SSchNodeInfo *)taosArrayGet(pTask->execNodes, i); - SCH_SET_TASK_HANDLE(pTask, nodeInfo->handle); - - schBuildAndSendMsg(pJob, pTask, &nodeInfo->addr, TDMT_VND_DROP_TASK); - } - - SCH_TASK_DLOG("task has %d exec address", size); -} - -void schDropTaskInHashList(SSchJob *pJob, SHashObj *list) { - if (!SCH_IS_NEED_DROP_JOB(pJob)) { - return; - } - - void *pIter = taosHashIterate(list, NULL); - while (pIter) { - SSchTask *pTask = *(SSchTask **)pIter; - - schDropTaskOnExecutedNode(pJob, pTask); - - pIter = taosHashIterate(list, pIter); - } -} - -void schDropJobAllTasks(SSchJob *pJob) { - schDropTaskInHashList(pJob, pJob->execTasks); - schDropTaskInHashList(pJob, pJob->succTasks); - schDropTaskInHashList(pJob, pJob->failTasks); -} - -int32_t schCancelJob(SSchJob *pJob) { - // TODO - return TSDB_CODE_SUCCESS; - // TODO MOVE ALL TASKS FROM EXEC LIST TO FAIL LIST -} - -void schCloseJobRef(void) { - if (!atomic_load_8((int8_t *)&schMgmt.exit)) { - return; - } - - SCH_LOCK(SCH_WRITE, &schMgmt.lock); - if (atomic_load_32(&schMgmt.jobNum) <= 0 && schMgmt.jobRef >= 0) { - taosCloseRef(schMgmt.jobRef); - schMgmt.jobRef = -1; - } - SCH_UNLOCK(SCH_WRITE, &schMgmt.lock); -} - -void schFreeJobImpl(void *job) { - if (NULL == job) { - return; - } - - SSchJob *pJob = job; - uint64_t queryId = pJob->queryId; - int64_t refId = pJob->refId; - - if (pJob->status == JOB_TASK_STATUS_EXECUTING) { - schCancelJob(pJob); - } - - schDropJobAllTasks(pJob); - - pJob->subPlans = NULL; // it is a reference to pDag->pSubplans - - int32_t numOfLevels = taosArrayGetSize(pJob->levels); - for (int32_t i = 0; i < numOfLevels; ++i) { - SSchLevel *pLevel = taosArrayGet(pJob->levels, i); - - schFreeFlowCtrl(pLevel); - - int32_t numOfTasks = taosArrayGetSize(pLevel->subTasks); - for (int32_t j = 0; j < numOfTasks; ++j) { - SSchTask *pTask = taosArrayGet(pLevel->subTasks, j); - schFreeTask(pTask); - } - - taosArrayDestroy(pLevel->subTasks); - } - - taosHashCleanup(pJob->execTasks); - taosHashCleanup(pJob->failTasks); - taosHashCleanup(pJob->succTasks); - - taosArrayDestroy(pJob->levels); - taosArrayDestroy(pJob->nodeList); - - qExplainFreeCtx(pJob->explainCtx); - - taosMemoryFreeClear(pJob->resData); - taosMemoryFreeClear(pJob); - - qDebug("QID:0x%" PRIx64 " job freed, refId:%" PRIx64 ", pointer:%p", queryId, refId, pJob); - - atomic_sub_fetch_32(&schMgmt.jobNum, 1); - - schCloseJobRef(); -} - -static int32_t schExecJobImpl(void *transport, SArray *pNodeList, SQueryPlan *pDag, int64_t *job, const char *sql, - int64_t startTs, bool syncSchedule) { - qDebug("QID:0x%" PRIx64 " job started", pDag->queryId); - - if (pNodeList == NULL || taosArrayGetSize(pNodeList) <= 0) { - qDebug("QID:0x%" PRIx64 " input exec nodeList is empty", pDag->queryId); - } - - int32_t code = 0; - SSchJob *pJob = NULL; - SCH_ERR_JRET(schInitJob(&pJob, pDag, transport, pNodeList, sql, startTs, syncSchedule)); - - SCH_ERR_JRET(schLaunchJob(pJob)); - - *job = pJob->refId; - - if (syncSchedule) { - SCH_JOB_DLOG("will wait for rsp now, job status:%s", SCH_GET_JOB_STATUS_STR(pJob)); - tsem_wait(&pJob->rspSem); - } - - SCH_JOB_DLOG("job exec done, job status:%s", SCH_GET_JOB_STATUS_STR(pJob)); - - schReleaseJob(pJob->refId); - - return TSDB_CODE_SUCCESS; - -_return: - - schFreeJobImpl(pJob); - SCH_RET(code); -} - -int32_t schExecStaticExplain(void *transport, SArray *pNodeList, SQueryPlan *pDag, int64_t *job, const char *sql, - bool syncSchedule) { - qDebug("QID:0x%" PRIx64 " job started", pDag->queryId); - - int32_t code = 0; - SSchJob *pJob = taosMemoryCalloc(1, sizeof(SSchJob)); - if (NULL == pJob) { - qError("QID:%" PRIx64 " calloc %d failed", pDag->queryId, (int32_t)sizeof(SSchJob)); - SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - pJob->sql = sql; - pJob->attr.queryJob = true; - pJob->attr.explainMode = pDag->explainInfo.mode; - pJob->queryId = pDag->queryId; - pJob->subPlans = pDag->pSubplans; - - SCH_ERR_JRET(qExecStaticExplain(pDag, (SRetrieveTableRsp **)&pJob->resData)); - - pJob->resType = SCH_RES_TYPE_FETCH; - - int64_t refId = taosAddRef(schMgmt.jobRef, pJob); - if (refId < 0) { - SCH_JOB_ELOG("taosAddRef job failed, error:%s", tstrerror(terrno)); - SCH_ERR_JRET(terrno); - } - - if (NULL == schAcquireJob(refId)) { - SCH_JOB_ELOG("schAcquireJob job failed, refId:%" PRIx64, refId); - SCH_RET(TSDB_CODE_SCH_STATUS_ERROR); - } - - pJob->refId = refId; - - SCH_JOB_DLOG("job refId:%" PRIx64, pJob->refId); - - pJob->status = JOB_TASK_STATUS_PARTIAL_SUCCEED; - *job = pJob->refId; - SCH_JOB_DLOG("job exec done, job status:%s", SCH_GET_JOB_STATUS_STR(pJob)); - - schReleaseJob(pJob->refId); - - return TSDB_CODE_SUCCESS; - -_return: - - schFreeJobImpl(pJob); - SCH_RET(code); -} - int32_t schedulerInit(SSchedulerCfg *cfg) { if (schMgmt.jobRef >= 0) { qError("scheduler already initialized"); @@ -2540,24 +73,30 @@ int32_t schedulerExecJob(void *transport, SArray *nodeList, SQueryPlan *pDag, in SCH_ERR_RET(TSDB_CODE_QRY_INVALID_INPUT); } + int32_t code = 0; + + *pJob = 0; + if (EXPLAIN_MODE_STATIC == pDag->explainInfo.mode) { SCH_ERR_RET(schExecStaticExplain(transport, nodeList, pDag, pJob, sql, true)); } else { - SCH_ERR_RET(schExecJobImpl(transport, nodeList, pDag, pJob, sql, startTs, true)); + SCH_ERR_JRET(schExecJobImpl(transport, nodeList, pDag, pJob, sql, startTs, true)); } - SSchJob *job = schAcquireJob(*pJob); +_return: + + if (*pJob) { + SSchJob *job = schAcquireJob(*pJob); - pRes->code = atomic_load_32(&job->errCode); - pRes->numOfRows = job->resNumOfRows; - if (SCH_RES_TYPE_QUERY == job->resType) { - pRes->res = job->resData; - job->resData = NULL; - } + pRes->code = atomic_load_32(&job->errCode); + pRes->numOfRows = job->resNumOfRows; + pRes->res = job->queryRes; + job->queryRes = NULL; - schReleaseJob(*pJob); + schReleaseJob(*pJob); + } - return TSDB_CODE_SUCCESS; + return code; } int32_t schedulerAsyncExecJob(void *transport, SArray *pNodeList, SQueryPlan *pDag, const char *sql, int64_t *pJob) { @@ -2574,129 +113,6 @@ int32_t schedulerAsyncExecJob(void *transport, SArray *pNodeList, SQueryPlan *pD return TSDB_CODE_SUCCESS; } -#if 0 -int32_t schedulerConvertDagToTaskList(SQueryPlan* pDag, SArray **pTasks) { - if (NULL == pDag || pDag->numOfSubplans <= 0 || LIST_LENGTH(pDag->pSubplans) == 0) { - SCH_ERR_RET(TSDB_CODE_QRY_INVALID_INPUT); - } - - int32_t levelNum = LIST_LENGTH(pDag->pSubplans); - if (1 != levelNum) { - qError("invalid level num: %d", levelNum); - SCH_ERR_RET(TSDB_CODE_QRY_INVALID_INPUT); - } - - SNodeListNode *plans = (SNodeListNode*)nodesListGetNode(pDag->pSubplans, 0); - int32_t taskNum = LIST_LENGTH(plans->pNodeList); - if (taskNum <= 0) { - qError("invalid task num: %d", taskNum); - SCH_ERR_RET(TSDB_CODE_QRY_INVALID_INPUT); - } - - SArray *info = taosArrayInit(taskNum, sizeof(STaskInfo)); - if (NULL == info) { - qError("taosArrayInit %d taskInfo failed", taskNum); - SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - STaskInfo tInfo = {0}; - char *msg = NULL; - int32_t msgLen = 0; - int32_t code = 0; - - for (int32_t i = 0; i < taskNum; ++i) { - SSubplan *plan = (SSubplan*)nodesListGetNode(plans->pNodeList, i); - tInfo.addr = plan->execNode; - - code = qSubPlanToString(plan, &msg, &msgLen); - if (TSDB_CODE_SUCCESS != code) { - qError("subplanToString error, code:%x, msg:%p, len:%d", code, msg, msgLen); - SCH_ERR_JRET(code); - } - - int32_t msgSize = sizeof(SSubQueryMsg) + msgLen; - if (NULL == msg) { - qError("calloc %d failed", msgSize); - SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - SSubQueryMsg* pMsg = taosMemoryCalloc(1, msgSize); - - pMsg->header.vgId = tInfo.addr.nodeId; - - pMsg->sId = schMgmt.sId; - pMsg->queryId = plan->id.queryId; - pMsg->taskId = schGenUUID(); - pMsg->taskType = TASK_TYPE_PERSISTENT; - pMsg->phyLen = msgLen; - pMsg->sqlLen = 0; - memcpy(pMsg->msg, msg, msgLen); - /*memcpy(pMsg->msg, ((SSubQueryMsg*)msg)->msg, msgLen);*/ - - tInfo.msg = pMsg; - - if (NULL == taosArrayPush(info, &tInfo)) { - qError("taosArrayPush failed, idx:%d", i); - taosMemoryFree(msg); - SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - } - - *pTasks = info; - info = NULL; - -_return: - schedulerFreeTaskList(info); - SCH_RET(code); -} - -int32_t schedulerCopyTask(STaskInfo *src, SArray **dst, int32_t copyNum) { - if (NULL == src || NULL == dst || copyNum <= 0) { - SCH_ERR_RET(TSDB_CODE_QRY_INVALID_INPUT); - } - - int32_t code = 0; - - *dst = taosArrayInit(copyNum, sizeof(STaskInfo)); - if (NULL == *dst) { - qError("taosArrayInit %d taskInfo failed", copyNum); - SCH_ERR_RET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - int32_t msgSize = src->msg->phyLen + sizeof(*src->msg); - STaskInfo info = {0}; - - info.addr = src->addr; - - for (int32_t i = 0; i < copyNum; ++i) { - info.msg = taosMemoryMalloc(msgSize); - if (NULL == info.msg) { - qError("malloc %d failed", msgSize); - SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - - memcpy(info.msg, src->msg, msgSize); - - info.msg->taskId = schGenUUID(); - - if (NULL == taosArrayPush(*dst, &info)) { - qError("taosArrayPush failed, idx:%d", i); - taosMemoryFree(info.msg); - SCH_ERR_JRET(TSDB_CODE_QRY_OUT_OF_MEMORY); - } - } - - return TSDB_CODE_SUCCESS; - -_return: - - schedulerFreeTaskList(*dst); - *dst = NULL; - - SCH_RET(code); -} -#endif - int32_t schedulerFetchRows(int64_t job, void **pData) { if (NULL == pData) { SCH_ERR_RET(TSDB_CODE_QRY_INVALID_INPUT); @@ -2752,7 +168,7 @@ int32_t schedulerFetchRows(int64_t job, void **pData) { } if (pJob->resData && ((SRetrieveTableRsp *)pJob->resData)->completed) { - SCH_ERR_JRET(schCheckAndUpdateJobStatus(pJob, JOB_TASK_STATUS_SUCCEED)); + SCH_ERR_JRET(schChkUpdateJobStatus(pJob, JOB_TASK_STATUS_SUCCEED)); } while (true) { @@ -2846,20 +262,6 @@ void schedulerFreeJob(int64_t job) { schReleaseJob(job); } -void schedulerFreeTaskList(SArray *taskList) { - if (NULL == taskList) { - return; - } - - int32_t taskNum = taosArrayGetSize(taskList); - for (int32_t i = 0; i < taskNum; ++i) { - STaskInfo *info = taosArrayGet(taskList, i); - taosMemoryFreeClear(info->msg); - } - - taosArrayDestroy(taskList); -} - void schedulerDestroy(void) { atomic_store_8((int8_t *)&schMgmt.exit, 1); diff --git a/source/libs/stream/src/tstream.c b/source/libs/stream/src/tstream.c index 70860be7e1e8e961ad42315cbbbdd4c059c61a16..0acec0e4e6102e0a5622abd166bd4c4025d36f69 100644 --- a/source/libs/stream/src/tstream.c +++ b/source/libs/stream/src/tstream.c @@ -68,7 +68,7 @@ static int32_t streamBuildExecMsg(SStreamTask* pTask, SArray* data, SRpcMsg* pMs // get groupId, compute hash value uint32_t hashValue = MurmurHash3_32(ctbName, strlen(ctbName)); - // + // get node // TODO: optimize search process SArray* vgInfo = pTask->shuffleDispatcher.dbInfo.pVgroupInfos; @@ -134,7 +134,7 @@ int32_t streamEnqueueDataBlk(SStreamTask* pTask, SStreamDataBlock* input) { } static int32_t streamTaskExecImpl(SStreamTask* pTask, void* data, SArray* pRes) { - void* exec = pTask->exec.runners[0].executor; + void* exec = pTask->exec.executor; // set input if (pTask->inputType == STREAM_INPUT__DATA_SUBMIT) { @@ -152,13 +152,13 @@ static int32_t streamTaskExecImpl(SStreamTask* pTask, void* data, SArray* pRes) // exec while (1) { - SSDataBlock* output; + SSDataBlock* output = NULL; uint64_t ts = 0; if (qExecTask(exec, &output, &ts) < 0) { ASSERT(false); } if (output == NULL) break; - taosArrayPush(pRes, &output); + taosArrayPush(pRes, output); } // destroy @@ -171,12 +171,12 @@ static int32_t streamTaskExecImpl(SStreamTask* pTask, void* data, SArray* pRes) } // TODO: handle version -int32_t streamTaskExec2(SStreamTask* pTask, SMsgCb* pMsgCb) { +int32_t streamExec(SStreamTask* pTask, SMsgCb* pMsgCb) { SArray* pRes = taosArrayInit(0, sizeof(SSDataBlock)); if (pRes == NULL) return -1; while (1) { int8_t execStatus = atomic_val_compare_exchange_8(&pTask->status, TASK_STATUS__IDLE, TASK_STATUS__EXECUTING); - void* exec = pTask->exec.runners[0].executor; + void* exec = pTask->exec.executor; if (execStatus == TASK_STATUS__IDLE) { // first run, from qall, handle failure from last exec while (1) { @@ -189,7 +189,7 @@ int32_t streamTaskExec2(SStreamTask* pTask, SMsgCb* pMsgCb) { taosFreeQitem(data); if (taosArrayGetSize(pRes) != 0) { - SStreamDataBlock* resQ = taosAllocateQitem(sizeof(void**), DEF_QITEM); + SStreamDataBlock* resQ = taosAllocateQitem(sizeof(SStreamDataBlock), DEF_QITEM); resQ->type = STREAM_INPUT__DATA_BLOCK; resQ->blocks = pRes; taosWriteQitem(pTask->outputQ, resQ); @@ -209,7 +209,7 @@ int32_t streamTaskExec2(SStreamTask* pTask, SMsgCb* pMsgCb) { taosFreeQitem(data); if (taosArrayGetSize(pRes) != 0) { - SStreamDataBlock* resQ = taosAllocateQitem(sizeof(void**), DEF_QITEM); + SStreamDataBlock* resQ = taosAllocateQitem(sizeof(SStreamDataBlock), DEF_QITEM); resQ->type = STREAM_INPUT__DATA_BLOCK; resQ->blocks = pRes; taosWriteQitem(pTask->outputQ, resQ); @@ -231,7 +231,7 @@ int32_t streamTaskExec2(SStreamTask* pTask, SMsgCb* pMsgCb) { taosFreeQitem(data); if (taosArrayGetSize(pRes) != 0) { - SStreamDataBlock* resQ = taosAllocateQitem(sizeof(void**), DEF_QITEM); + SStreamDataBlock* resQ = taosAllocateQitem(sizeof(SStreamDataBlock), DEF_QITEM); resQ->type = STREAM_INPUT__DATA_BLOCK; resQ->blocks = pRes; taosWriteQitem(pTask->outputQ, resQ); @@ -247,6 +247,19 @@ int32_t streamTaskExec2(SStreamTask* pTask, SMsgCb* pMsgCb) { void* data = NULL; taosGetQitem(pTask->inputQAll, &data); if (data == NULL) break; + + streamTaskExecImpl(pTask, data, pRes); + + taosFreeQitem(data); + + if (taosArrayGetSize(pRes) != 0) { + SStreamDataBlock* resQ = taosAllocateQitem(sizeof(SStreamDataBlock), DEF_QITEM); + resQ->type = STREAM_INPUT__DATA_BLOCK; + resQ->blocks = pRes; + taosWriteQitem(pTask->outputQ, resQ); + pRes = taosArrayInit(0, sizeof(SSDataBlock)); + if (pRes == NULL) goto FAIL; + } } atomic_store_8(&pTask->status, TASK_STATUS__IDLE); @@ -265,7 +278,7 @@ FAIL: return -1; } -int32_t streamTaskSink(SStreamTask* pTask, SMsgCb* pMsgCb) { +int32_t streamSink(SStreamTask* pTask, SMsgCb* pMsgCb) { bool firstRun = 1; while (1) { SStreamDataBlock* pBlock = NULL; @@ -298,62 +311,66 @@ int32_t streamTaskSink(SStreamTask* pTask, SMsgCb* pMsgCb) { } // dispatch - if (pTask->dispatchType == TASK_DISPATCH__INPLACE) { - SRpcMsg dispatchMsg = {0}; - if (streamBuildExecMsg(pTask, pRes, &dispatchMsg, NULL) < 0) { - ASSERT(0); - return -1; - } + // TODO dispatch guard + int8_t outputStatus = atomic_load_8(&pTask->outputStatus); + if (outputStatus == TASK_OUTPUT_STATUS__NORMAL) { + if (pTask->dispatchType == TASK_DISPATCH__INPLACE) { + SRpcMsg dispatchMsg = {0}; + if (streamBuildExecMsg(pTask, pRes, &dispatchMsg, NULL) < 0) { + ASSERT(0); + return -1; + } - int32_t qType; - if (pTask->dispatchMsgType == TDMT_VND_TASK_PIPE_EXEC || pTask->dispatchMsgType == TDMT_SND_TASK_PIPE_EXEC) { - qType = FETCH_QUEUE; - } else if (pTask->dispatchMsgType == TDMT_VND_TASK_MERGE_EXEC || - pTask->dispatchMsgType == TDMT_SND_TASK_MERGE_EXEC) { - qType = MERGE_QUEUE; - } else if (pTask->dispatchMsgType == TDMT_VND_TASK_WRITE_EXEC) { - qType = WRITE_QUEUE; - } else { - ASSERT(0); - } - tmsgPutToQueue(pMsgCb, qType, &dispatchMsg); - - } else if (pTask->dispatchType == TASK_DISPATCH__FIXED) { - SRpcMsg dispatchMsg = {0}; - SEpSet* pEpSet = NULL; - if (streamBuildExecMsg(pTask, pRes, &dispatchMsg, &pEpSet) < 0) { - ASSERT(0); - return -1; - } + int32_t qType; + if (pTask->dispatchMsgType == TDMT_VND_TASK_PIPE_EXEC || pTask->dispatchMsgType == TDMT_SND_TASK_PIPE_EXEC) { + qType = FETCH_QUEUE; + } else if (pTask->dispatchMsgType == TDMT_VND_TASK_MERGE_EXEC || + pTask->dispatchMsgType == TDMT_SND_TASK_MERGE_EXEC) { + qType = MERGE_QUEUE; + } else if (pTask->dispatchMsgType == TDMT_VND_TASK_WRITE_EXEC) { + qType = WRITE_QUEUE; + } else { + ASSERT(0); + } + tmsgPutToQueue(pMsgCb, qType, &dispatchMsg); - tmsgSendReq(pEpSet, &dispatchMsg); + } else if (pTask->dispatchType == TASK_DISPATCH__FIXED) { + SRpcMsg dispatchMsg = {0}; + SEpSet* pEpSet = NULL; + if (streamBuildExecMsg(pTask, pRes, &dispatchMsg, &pEpSet) < 0) { + ASSERT(0); + return -1; + } - } else if (pTask->dispatchType == TASK_DISPATCH__SHUFFLE) { - SHashObj* pShuffleRes = taosHashInit(64, taosGetDefaultHashFunction(TSDB_DATA_TYPE_INT), false, HASH_NO_LOCK); - if (pShuffleRes == NULL) { - return -1; - } + tmsgSendReq(pEpSet, &dispatchMsg); + + } else if (pTask->dispatchType == TASK_DISPATCH__SHUFFLE) { + SHashObj* pShuffleRes = taosHashInit(64, taosGetDefaultHashFunction(TSDB_DATA_TYPE_INT), false, HASH_NO_LOCK); + if (pShuffleRes == NULL) { + return -1; + } - int32_t sz = taosArrayGetSize(pRes); - for (int32_t i = 0; i < sz; i++) { - SSDataBlock* pDataBlock = taosArrayGet(pRes, i); - SArray* pArray = taosHashGet(pShuffleRes, &pDataBlock->info.groupId, sizeof(int64_t)); - if (pArray == NULL) { - pArray = taosArrayInit(0, sizeof(SSDataBlock)); + int32_t sz = taosArrayGetSize(pRes); + for (int32_t i = 0; i < sz; i++) { + SSDataBlock* pDataBlock = taosArrayGet(pRes, i); + SArray* pArray = taosHashGet(pShuffleRes, &pDataBlock->info.groupId, sizeof(int64_t)); if (pArray == NULL) { - return -1; + pArray = taosArrayInit(0, sizeof(SSDataBlock)); + if (pArray == NULL) { + return -1; + } + taosHashPut(pShuffleRes, &pDataBlock->info.groupId, sizeof(int64_t), &pArray, sizeof(void*)); } - taosHashPut(pShuffleRes, &pDataBlock->info.groupId, sizeof(int64_t), &pArray, sizeof(void*)); + taosArrayPush(pArray, pDataBlock); } - taosArrayPush(pArray, pDataBlock); - } - if (streamShuffleDispatch(pTask, pMsgCb, pShuffleRes) < 0) { - return -1; - } + if (streamShuffleDispatch(pTask, pMsgCb, pShuffleRes) < 0) { + return -1; + } - } else { - ASSERT(pTask->dispatchType == TASK_DISPATCH__NONE); + } else { + ASSERT(pTask->dispatchType == TASK_DISPATCH__NONE); + } } } return 0; @@ -375,12 +392,14 @@ int32_t streamTaskEnqueue(SStreamTask* pTask, SStreamDispatchReq* pReq, SRpcMsg* // 1.2 enqueue pBlock->type = STREAM_DATA_TYPE_SSDATA_BLOCK; pBlock->sourceVg = pReq->sourceVg; - pBlock->sourceVer = pReq->sourceVer; + /*pBlock->sourceVer = pReq->sourceVer;*/ taosWriteQitem(pTask->inputQ, pBlock); // 1.3 rsp by input status SStreamDispatchRsp* pCont = rpcMallocCont(sizeof(SStreamDispatchRsp)); pCont->inputStatus = status; + pCont->streamId = pReq->streamId; + pCont->taskId = pReq->sourceTaskId; pRsp->pCont = pCont; pRsp->contLen = sizeof(SStreamDispatchRsp); tmsgSendRsp(pRsp); @@ -388,7 +407,7 @@ int32_t streamTaskEnqueue(SStreamTask* pTask, SStreamDispatchReq* pReq, SRpcMsg* return 0; } -int32_t streamTaskProcessDispatchReq(SStreamTask* pTask, SMsgCb* pMsgCb, SStreamDispatchReq* pReq, SRpcMsg* pRsp) { +int32_t streamProcessDispatchReq(SStreamTask* pTask, SMsgCb* pMsgCb, SStreamDispatchReq* pReq, SRpcMsg* pRsp) { // 1. handle input streamTaskEnqueue(pTask, pReq, pRsp); @@ -396,148 +415,39 @@ int32_t streamTaskProcessDispatchReq(SStreamTask* pTask, SMsgCb* pMsgCb, SStream // 2.1. idle: exec // 2.2. executing: return // 2.3. closing: keep trying - streamTaskExec2(pTask, pMsgCb); + streamExec(pTask, pMsgCb); // 3. handle output // 3.1 check and set status // 3.2 dispatch / sink - streamTaskSink(pTask, pMsgCb); + streamSink(pTask, pMsgCb); return 0; } -int32_t streamTaskProcessRecoverReq(SStreamTask* pTask, char* msg) { - // +int32_t streamProcessDispatchRsp(SStreamTask* pTask, SMsgCb* pMsgCb, SStreamDispatchRsp* pRsp) { + atomic_store_8(&pTask->inputStatus, pRsp->inputStatus); + if (pRsp->inputStatus == TASK_INPUT_STATUS__BLOCKED) { + // TODO: init recover timer + } + // continue dispatch + streamSink(pTask, pMsgCb); return 0; } -int32_t streamExecTask(SStreamTask* pTask, SMsgCb* pMsgCb, const void* input, int32_t inputType, int32_t workId) { - SArray* pRes = NULL; - // source - if (inputType == STREAM_DATA_TYPE_SUBMIT_BLOCK && pTask->sourceType != TASK_SOURCE__SCAN) return 0; - - // exec - if (pTask->execType != TASK_EXEC__NONE) { - ASSERT(workId < pTask->exec.numOfRunners); - void* exec = pTask->exec.runners[workId].executor; - pRes = taosArrayInit(0, sizeof(SSDataBlock)); - if (pRes == NULL) { - return -1; - } - if (inputType == STREAM_DATA_TYPE_SUBMIT_BLOCK) { - qSetStreamInput(exec, input, inputType); - while (1) { - SSDataBlock* output; - uint64_t ts; - if (qExecTask(exec, &output, &ts) < 0) { - ASSERT(false); - } - if (output == NULL) { - break; - } - taosArrayPush(pRes, output); - } - } else if (inputType == STREAM_DATA_TYPE_SSDATA_BLOCK) { - const SArray* blocks = (const SArray*)input; - /*int32_t sz = taosArrayGetSize(blocks);*/ - /*for (int32_t i = 0; i < sz; i++) {*/ - /*SSDataBlock* pBlock = taosArrayGet(blocks, i);*/ - /*qSetStreamInput(exec, pBlock, inputType);*/ - qSetMultiStreamInput(exec, blocks->pData, blocks->size, STREAM_DATA_TYPE_SSDATA_BLOCK); - while (1) { - SSDataBlock* output; - uint64_t ts; - if (qExecTask(exec, &output, &ts) < 0) { - ASSERT(false); - } - if (output == NULL) { - break; - } - taosArrayPush(pRes, output); - } - /*}*/ - } else { - ASSERT(0); - } - } else { - ASSERT(inputType == STREAM_DATA_TYPE_SSDATA_BLOCK); - pRes = (SArray*)input; - } - - if (pRes == NULL || taosArrayGetSize(pRes) == 0) return 0; - - // sink - if (pTask->sinkType == TASK_SINK__TABLE) { - // blockDebugShowData(pRes); - pTask->tbSink.tbSinkFunc(pTask, pTask->tbSink.vnode, 0, pRes); - } else if (pTask->sinkType == TASK_SINK__SMA) { - pTask->smaSink.smaSink(pTask->ahandle, pTask->smaSink.smaId, pRes); - // - } else if (pTask->sinkType == TASK_SINK__FETCH) { - // - } else { - ASSERT(pTask->sinkType == TASK_SINK__NONE); - } - - // dispatch - - if (pTask->dispatchType == TASK_DISPATCH__INPLACE) { - SRpcMsg dispatchMsg = {0}; - if (streamBuildExecMsg(pTask, pRes, &dispatchMsg, NULL) < 0) { - ASSERT(0); - return -1; - } - - int32_t qType; - if (pTask->dispatchMsgType == TDMT_VND_TASK_PIPE_EXEC || pTask->dispatchMsgType == TDMT_SND_TASK_PIPE_EXEC) { - qType = FETCH_QUEUE; - } else if (pTask->dispatchMsgType == TDMT_VND_TASK_MERGE_EXEC || - pTask->dispatchMsgType == TDMT_SND_TASK_MERGE_EXEC) { - qType = MERGE_QUEUE; - } else if (pTask->dispatchMsgType == TDMT_VND_TASK_WRITE_EXEC) { - qType = WRITE_QUEUE; - } else { - ASSERT(0); - } - tmsgPutToQueue(pMsgCb, qType, &dispatchMsg); - - } else if (pTask->dispatchType == TASK_DISPATCH__FIXED) { - SRpcMsg dispatchMsg = {0}; - SEpSet* pEpSet = NULL; - if (streamBuildExecMsg(pTask, pRes, &dispatchMsg, &pEpSet) < 0) { - ASSERT(0); - return -1; - } - - tmsgSendReq(pEpSet, &dispatchMsg); - - } else if (pTask->dispatchType == TASK_DISPATCH__SHUFFLE) { - SHashObj* pShuffleRes = taosHashInit(64, taosGetDefaultHashFunction(TSDB_DATA_TYPE_INT), false, HASH_NO_LOCK); - if (pShuffleRes == NULL) { - return -1; - } - - int32_t sz = taosArrayGetSize(pRes); - for (int32_t i = 0; i < sz; i++) { - SSDataBlock* pDataBlock = taosArrayGet(pRes, i); - SArray* pArray = taosHashGet(pShuffleRes, &pDataBlock->info.groupId, sizeof(int64_t)); - if (pArray == NULL) { - pArray = taosArrayInit(0, sizeof(SSDataBlock)); - if (pArray == NULL) { - return -1; - } - taosHashPut(pShuffleRes, &pDataBlock->info.groupId, sizeof(int64_t), &pArray, sizeof(void*)); - } - taosArrayPush(pArray, pDataBlock); - } +int32_t streamTaskProcessRunReq(SStreamTask* pTask, SMsgCb* pMsgCb) { + streamExec(pTask, pMsgCb); + streamSink(pTask, pMsgCb); + return 0; +} - if (streamShuffleDispatch(pTask, pMsgCb, pShuffleRes) < 0) { - return -1; - } +int32_t streamProcessRecoverReq(SStreamTask* pTask, SMsgCb* pMsgCb, SStreamTaskRecoverReq* pReq, SRpcMsg* pMsg) { + // + return 0; +} - } else { - ASSERT(pTask->dispatchType == TASK_DISPATCH__NONE); - } +int32_t streamProcessRecoverRsp(SStreamTask* pTask, SStreamTaskRecoverRsp* pRsp) { + // return 0; } @@ -567,20 +477,7 @@ SStreamTask* tNewSStreamTask(int64_t streamId) { pTask->streamId = streamId; pTask->status = TASK_STATUS__IDLE; - pTask->inputQ = taosOpenQueue(); - pTask->outputQ = taosOpenQueue(); - pTask->inputQAll = taosAllocateQall(); - pTask->outputQAll = taosAllocateQall(); - if (pTask->inputQ == NULL || pTask->outputQ == NULL || pTask->inputQAll == NULL || pTask->outputQAll == NULL) - goto FAIL; return pTask; -FAIL: - if (pTask->inputQ) taosCloseQueue(pTask->inputQ); - if (pTask->outputQ) taosCloseQueue(pTask->outputQ); - if (pTask->inputQAll) taosFreeQall(pTask->inputQAll); - if (pTask->outputQAll) taosFreeQall(pTask->outputQAll); - if (pTask) taosMemoryFree(pTask); - return NULL; } int32_t tEncodeSStreamTask(SEncoder* pEncoder, const SStreamTask* pTask) { @@ -682,11 +579,7 @@ void tFreeSStreamTask(SStreamTask* pTask) { taosCloseQueue(pTask->outputQ); // TODO if (pTask->exec.qmsg) taosMemoryFree(pTask->exec.qmsg); - for (int32_t i = 0; i < pTask->exec.numOfRunners; i++) { - qDestroyTask(pTask->exec.runners[i].executor); - } - taosMemoryFree(pTask->exec.runners); - /*taosMemoryFree(pTask->executor);*/ + qDestroyTask(pTask->exec.executor); taosMemoryFree(pTask); } diff --git a/source/libs/stream/src/tstreamUpdate.c b/source/libs/stream/src/tstreamUpdate.c index 1197b6100a72c8438886addd3c2207f0b7961a6e..d21dadfe559d3dca81b34e3f6ade817ab1278a3b 100644 --- a/source/libs/stream/src/tstreamUpdate.c +++ b/source/libs/stream/src/tstreamUpdate.c @@ -17,21 +17,26 @@ #include "ttime.h" #define DEFAULT_FALSE_POSITIVE 0.01 -#define DEFAULT_BUCKET_SIZE 1024 -#define ROWS_PER_MILLISECOND 1 -#define MAX_NUM_SCALABLE_BF 120 -#define MIN_NUM_SCALABLE_BF 10 -#define DEFAULT_PREADD_BUCKET 1 -#define MAX_INTERVAL MILLISECOND_PER_MINUTE -#define MIN_INTERVAL (MILLISECOND_PER_SECOND * 10) +#define DEFAULT_BUCKET_SIZE 1024 +#define ROWS_PER_MILLISECOND 1 +#define MAX_NUM_SCALABLE_BF 100000 +#define MIN_NUM_SCALABLE_BF 10 +#define DEFAULT_PREADD_BUCKET 1 +#define MAX_INTERVAL MILLISECOND_PER_MINUTE +#define MIN_INTERVAL (MILLISECOND_PER_SECOND * 10) +#define DEFAULT_EXPECTED_ENTRIES 10000 + +static int64_t adjustExpEntries(int64_t entries) { + return TMIN(DEFAULT_EXPECTED_ENTRIES, entries); +} static void windowSBfAdd(SUpdateInfo *pInfo, uint64_t count) { - if (pInfo->numSBFs < count ) { + if (pInfo->numSBFs < count) { count = pInfo->numSBFs; } for (uint64_t i = 0; i < count; ++i) { - SScalableBf *tsSBF = tScalableBfInit(pInfo->interval * ROWS_PER_MILLISECOND, - DEFAULT_FALSE_POSITIVE); + int64_t rows = adjustExpEntries(pInfo->interval * ROWS_PER_MILLISECOND); + SScalableBf *tsSBF = tScalableBfInit(rows, DEFAULT_FALSE_POSITIVE); taosArrayPush(pInfo->pTsSBFs, &tsSBF); } } @@ -39,9 +44,9 @@ static void windowSBfAdd(SUpdateInfo *pInfo, uint64_t count) { static void windowSBfDelete(SUpdateInfo *pInfo, uint64_t count) { if (count < pInfo->numSBFs - 1) { for (uint64_t i = 0; i < count; ++i) { - SScalableBf *pTsSBFs = taosArrayGetP(pInfo->pTsSBFs, i); + SScalableBf *pTsSBFs = taosArrayGetP(pInfo->pTsSBFs, 0); tScalableBfDestroy(pTsSBFs); - taosArrayRemove(pInfo->pTsSBFs, i); + taosArrayRemove(pInfo->pTsSBFs, 0); } } else { taosArrayClearP(pInfo->pTsSBFs, (FDelete)tScalableBfDestroy); @@ -67,7 +72,7 @@ static int64_t adjustInterval(int64_t interval, int32_t precision) { return val; } -static int64_t adjustWatermark(int64_t interval, int32_t watermark) { +static int64_t adjustWatermark(int64_t interval, int64_t watermark) { if (watermark <= 0 || watermark > MAX_NUM_SCALABLE_BF * interval) { watermark = MAX_NUM_SCALABLE_BF * interval; } else if (watermark < MIN_NUM_SCALABLE_BF * interval) { @@ -76,7 +81,7 @@ static int64_t adjustWatermark(int64_t interval, int32_t watermark) { return watermark; } -SUpdateInfo *updateInfoInitP(SInterval* pInterval, int64_t watermark) { +SUpdateInfo *updateInfoInitP(SInterval *pInterval, int64_t watermark) { return updateInfoInit(pInterval->interval, pInterval->precision, watermark); } @@ -93,7 +98,7 @@ SUpdateInfo *updateInfoInit(int64_t interval, int32_t precision, int64_t waterma uint64_t bfSize = (uint64_t)(pInfo->watermark / pInfo->interval); - pInfo->pTsSBFs = taosArrayInit(bfSize, sizeof(SScalableBf)); + pInfo->pTsSBFs = taosArrayInit(bfSize, sizeof(void *)); if (pInfo->pTsSBFs == NULL) { updateInfoDestroy(pInfo); return NULL; @@ -108,14 +113,14 @@ SUpdateInfo *updateInfoInit(int64_t interval, int32_t precision, int64_t waterma } TSKEY dumy = 0; - for(uint64_t i=0; i < DEFAULT_BUCKET_SIZE; ++i) { + for (uint64_t i = 0; i < DEFAULT_BUCKET_SIZE; ++i) { taosArrayPush(pInfo->pTsBuckets, &dumy); } pInfo->numBuckets = DEFAULT_BUCKET_SIZE; return pInfo; } -static SScalableBf* getSBf(SUpdateInfo *pInfo, TSKEY ts) { +static SScalableBf *getSBf(SUpdateInfo *pInfo, TSKEY ts) { if (ts <= 0) { return NULL; } @@ -131,24 +136,24 @@ static SScalableBf* getSBf(SUpdateInfo *pInfo, TSKEY ts) { } SScalableBf *res = taosArrayGetP(pInfo->pTsSBFs, index); if (res == NULL) { - res = tScalableBfInit(pInfo->interval * ROWS_PER_MILLISECOND, - DEFAULT_FALSE_POSITIVE); + int64_t rows = adjustExpEntries(pInfo->interval * ROWS_PER_MILLISECOND); + res = tScalableBfInit(rows, DEFAULT_FALSE_POSITIVE); taosArrayPush(pInfo->pTsSBFs, &res); } return res; } bool updateInfoIsUpdated(SUpdateInfo *pInfo, tb_uid_t tableId, TSKEY ts) { - int32_t res = TSDB_CODE_FAILED; - uint64_t index = ((uint64_t)tableId) % pInfo->numBuckets; - SScalableBf* pSBf = getSBf(pInfo, ts); + int32_t res = TSDB_CODE_FAILED; + uint64_t index = ((uint64_t)tableId) % pInfo->numBuckets; + SScalableBf *pSBf = getSBf(pInfo, ts); // pSBf may be a null pointer if (pSBf) { res = tScalableBfPut(pSBf, &ts, sizeof(TSKEY)); } TSKEY maxTs = *(TSKEY *)taosArrayGet(pInfo->pTsBuckets, index); - if (maxTs < ts ) { + if (maxTs < ts) { taosArraySet(pInfo->pTsBuckets, index, &ts); return false; } @@ -159,7 +164,7 @@ bool updateInfoIsUpdated(SUpdateInfo *pInfo, tb_uid_t tableId, TSKEY ts) { return false; } - //check from tsdb api + // check from tsdb api return true; } @@ -174,7 +179,7 @@ void updateInfoDestroy(SUpdateInfo *pInfo) { SScalableBf *pSBF = taosArrayGetP(pInfo->pTsSBFs, i); tScalableBfDestroy(pSBF); } - + taosArrayDestroy(pInfo->pTsSBFs); taosMemoryFree(pInfo); -} \ No newline at end of file +} diff --git a/source/libs/stream/test/tstreamUpdateTest.cpp b/source/libs/stream/test/tstreamUpdateTest.cpp index c1e4e2bec1e68c6072e3393140b8a806a8504e9a..93e114db020591dc703b2876cbecc25e1c363011 100644 --- a/source/libs/stream/test/tstreamUpdateTest.cpp +++ b/source/libs/stream/test/tstreamUpdateTest.cpp @@ -4,6 +4,7 @@ #include "ttime.h" using namespace std; +#define MAX_NUM_SCALABLE_BF 100000 TEST(TD_STREAM_UPDATE_TEST, update) { int64_t interval = 20 * 1000; @@ -91,11 +92,11 @@ TEST(TD_STREAM_UPDATE_TEST, update) { } SUpdateInfo *pSU4 = updateInfoInit(-1, TSDB_TIME_PRECISION_MILLI, -1); - GTEST_ASSERT_EQ(pSU4->watermark, 120 * pSU4->interval); + GTEST_ASSERT_EQ(pSU4->watermark, MAX_NUM_SCALABLE_BF * pSU4->interval); GTEST_ASSERT_EQ(pSU4->interval, MILLISECOND_PER_MINUTE); SUpdateInfo *pSU5 = updateInfoInit(0, TSDB_TIME_PRECISION_MILLI, 0); - GTEST_ASSERT_EQ(pSU5->watermark, 120 * pSU4->interval); + GTEST_ASSERT_EQ(pSU5->watermark, MAX_NUM_SCALABLE_BF * pSU4->interval); GTEST_ASSERT_EQ(pSU5->interval, MILLISECOND_PER_MINUTE); diff --git a/source/libs/sync/inc/syncIO.h b/source/libs/sync/inc/syncIO.h index 99f9deb99e8da9e484d07e3566995dd1f3e00daf..f65a31769420d6cf584d2079f1b147e510f3bdb6 100644 --- a/source/libs/sync/inc/syncIO.h +++ b/source/libs/sync/inc/syncIO.h @@ -36,10 +36,10 @@ typedef struct SSyncIO { STaosQueue *pMsgQ; STaosQset * pQset; TdThread consumerTid; - - void * serverRpc; - void * clientRpc; - SEpSet myAddr; + void *serverRpc; + void *clientRpc; + SEpSet myAddr; + SMsgCb msgcb; tmr_h qTimer; int32_t qTimerMS; @@ -65,8 +65,8 @@ extern SSyncIO *gSyncIO; int32_t syncIOStart(char *host, uint16_t port); int32_t syncIOStop(); -int32_t syncIOSendMsg(void *clientRpc, const SEpSet *pEpSet, SRpcMsg *pMsg); -int32_t syncIOEqMsg(void *queue, SRpcMsg *pMsg); +int32_t syncIOSendMsg(const SEpSet *pEpSet, SRpcMsg *pMsg); +int32_t syncIOEqMsg(const SMsgCb *msgcb, SRpcMsg *pMsg); int32_t syncIOQTimerStart(); int32_t syncIOQTimerStop(); diff --git a/source/libs/sync/inc/syncInt.h b/source/libs/sync/inc/syncInt.h index 9b655fb0fae7f699aeccca4bc4047f9ce8008e56..768e1c1cf1b55486dea6c98dae7e6df9ed2f891a 100644 --- a/source/libs/sync/inc/syncInt.h +++ b/source/libs/sync/inc/syncInt.h @@ -20,135 +20,41 @@ extern "C" { #endif -#include -#include -#include -#include "cJSON.h" #include "sync.h" #include "syncTools.h" -#include "taosdef.h" -#include "tglobal.h" #include "tlog.h" #include "ttimer.h" -#define sFatal(...) \ - { \ - if (sDebugFlag & DEBUG_FATAL) { \ - taosPrintLog("SYN FATAL ", DEBUG_FATAL, 255, __VA_ARGS__); \ - } \ - } -#define sError(...) \ - { \ - if (sDebugFlag & DEBUG_ERROR) { \ - taosPrintLog("SYN ERROR ", DEBUG_ERROR, 255, __VA_ARGS__); \ - } \ - } -#define sWarn(...) \ - { \ - if (sDebugFlag & DEBUG_WARN) { \ - taosPrintLog("SYN WARN ", DEBUG_WARN, 255, __VA_ARGS__); \ - } \ - } -#define sInfo(...) \ - { \ - if (sDebugFlag & DEBUG_INFO) { \ - taosPrintLog("SYN INFO ", DEBUG_INFO, 255, __VA_ARGS__); \ - } \ - } -#define sDebug(...) \ - { \ - if (sDebugFlag & DEBUG_DEBUG) { \ - taosPrintLog("SYN DEBUG ", DEBUG_DEBUG, sDebugFlag, __VA_ARGS__); \ - } \ - } -#define sTrace(...) \ - { \ - if (sDebugFlag & DEBUG_TRACE) { \ - taosPrintLog("SYN TRACE ", DEBUG_TRACE, sDebugFlag, __VA_ARGS__); \ - } \ - } - -#define sFatalLong(...) \ - { \ - if (sDebugFlag & DEBUG_FATAL) { \ - taosPrintLongString("SYN FATAL ", DEBUG_FATAL, 255, __VA_ARGS__); \ - } \ - } -#define sErrorLong(...) \ - { \ - if (sDebugFlag & DEBUG_ERROR) { \ - taosPrintLongString("SYN ERROR ", DEBUG_ERROR, 255, __VA_ARGS__); \ - } \ - } -#define sWarnLong(...) \ - { \ - if (sDebugFlag & DEBUG_WARN) { \ - taosPrintLongString("SYN WARN ", DEBUG_WARN, 255, __VA_ARGS__); \ - } \ - } -#define sInfoLong(...) \ - { \ - if (sDebugFlag & DEBUG_INFO) { \ - taosPrintLongString("SYN INFO ", DEBUG_INFO, 255, __VA_ARGS__); \ - } \ - } -#define sDebugLong(...) \ - { \ - if (sDebugFlag & DEBUG_DEBUG) { \ - taosPrintLongString("SYN DEBUG ", DEBUG_DEBUG, sDebugFlag, __VA_ARGS__); \ - } \ - } -#define sTraceLong(...) \ - { \ - if (sDebugFlag & DEBUG_TRACE) { \ - taosPrintLongString("SYN TRACE ", DEBUG_TRACE, sDebugFlag, __VA_ARGS__); \ - } \ - } - -struct SyncTimeout; -typedef struct SyncTimeout SyncTimeout; - -struct SyncClientRequest; -typedef struct SyncClientRequest SyncClientRequest; - -struct SyncPing; -typedef struct SyncPing SyncPing; - -struct SyncPingReply; -typedef struct SyncPingReply SyncPingReply; - -struct SyncRequestVote; -typedef struct SyncRequestVote SyncRequestVote; - -struct SyncRequestVoteReply; -typedef struct SyncRequestVoteReply SyncRequestVoteReply; - -struct SyncAppendEntries; -typedef struct SyncAppendEntries SyncAppendEntries; - -struct SyncAppendEntriesReply; +// clang-format off +#define sFatal(...) do { if (sDebugFlag & DEBUG_FATAL) { taosPrintLog("SYN FATAL ", DEBUG_FATAL, 255, __VA_ARGS__); }} while(0) +#define sError(...) do { if (sDebugFlag & DEBUG_ERROR) { taosPrintLog("SYN ERROR ", DEBUG_ERROR, 255, __VA_ARGS__); }} while(0) +#define sWarn(...) do { if (sDebugFlag & DEBUG_WARN) { taosPrintLog("SYN WARN ", DEBUG_WARN, 255, __VA_ARGS__); }} while(0) +#define sInfo(...) do { if (sDebugFlag & DEBUG_INFO) { taosPrintLog("SYN ", DEBUG_INFO, 255, __VA_ARGS__); }} while(0) +#define sDebug(...) do { if (sDebugFlag & DEBUG_DEBUG) { taosPrintLog("SYN ", DEBUG_DEBUG, sDebugFlag, __VA_ARGS__); }} while(0) +#define sTrace(...) do { if (sDebugFlag & DEBUG_TRACE) { taosPrintLog("SYN ", DEBUG_TRACE, sDebugFlag, __VA_ARGS__); }} while(0) +#define sFatalLong(...) do { if (sDebugFlag & DEBUG_FATAL) { taosPrintLongString("SYN FATAL ", DEBUG_FATAL, 255, __VA_ARGS__); }} while(0) +#define sErrorLong(...) do { if (sDebugFlag & DEBUG_ERROR) { taosPrintLongString("SYN ERROR ", DEBUG_ERROR, 255, __VA_ARGS__); }} while(0) +#define sWarnLong(...) do { if (sDebugFlag & DEBUG_WARN) { taosPrintLongString("SYN WARN ", DEBUG_WARN, 255, __VA_ARGS__); }} while(0) +#define sInfoLong(...) do { if (sDebugFlag & DEBUG_INFO) { taosPrintLongString("SYN ", DEBUG_INFO, 255, __VA_ARGS__); }} while(0) +#define sDebugLong(...) do { if (sDebugFlag & DEBUG_DEBUG) { taosPrintLongString("SYN ", DEBUG_DEBUG, sDebugFlag, __VA_ARGS__); }} while(0) +#define sTraceLong(...) do { if (sDebugFlag & DEBUG_TRACE) { taosPrintLongString("SYN ", DEBUG_TRACE, sDebugFlag, __VA_ARGS__); }} while(0) +// clang-format on + +typedef struct SyncTimeout SyncTimeout; +typedef struct SyncClientRequest SyncClientRequest; +typedef struct SyncPing SyncPing; +typedef struct SyncPingReply SyncPingReply; +typedef struct SyncRequestVote SyncRequestVote; +typedef struct SyncRequestVoteReply SyncRequestVoteReply; +typedef struct SyncAppendEntries SyncAppendEntries; typedef struct SyncAppendEntriesReply SyncAppendEntriesReply; - -struct SSyncEnv; -typedef struct SSyncEnv SSyncEnv; - -struct SRaftStore; -typedef struct SRaftStore SRaftStore; - -struct SVotesGranted; -typedef struct SVotesGranted SVotesGranted; - -struct SVotesRespond; -typedef struct SVotesRespond SVotesRespond; - -struct SSyncIndexMgr; -typedef struct SSyncIndexMgr SSyncIndexMgr; - -struct SRaftCfg; -typedef struct SRaftCfg SRaftCfg; - -struct SSyncRespMgr; -typedef struct SSyncRespMgr SSyncRespMgr; +typedef struct SSyncEnv SSyncEnv; +typedef struct SRaftStore SRaftStore; +typedef struct SVotesGranted SVotesGranted; +typedef struct SVotesRespond SVotesRespond; +typedef struct SSyncIndexMgr SSyncIndexMgr; +typedef struct SRaftCfg SRaftCfg; +typedef struct SSyncRespMgr SSyncRespMgr; typedef struct SSyncNode { // init by SSyncInfo @@ -159,11 +65,10 @@ typedef struct SSyncNode { char configPath[TSDB_FILENAME_LEN * 2]; // sync io - SWal* pWal; - void* rpcClient; - int32_t (*FpSendMsg)(void* rpcClient, const SEpSet* pEpSet, SRpcMsg* pMsg); - void* queue; - int32_t (*FpEqMsg)(void* queue, SRpcMsg* pMsg); + SWal* pWal; + const SMsgCb* msgcb; + int32_t (*FpSendMsg)(const SEpSet* pEpSet, SRpcMsg* pMsg); + int32_t (*FpEqMsg)(const SMsgCb* msgcb, SRpcMsg* pMsg); // init internal SNodeInfo myNodeInfo; diff --git a/source/libs/sync/src/syncIO.c b/source/libs/sync/src/syncIO.c index 203a8a1e625176f2d037915dea945f26999f218f..39760c32e83eddc060aeb9669fb252eaca816e54 100644 --- a/source/libs/sync/src/syncIO.c +++ b/source/libs/sync/src/syncIO.c @@ -66,7 +66,7 @@ int32_t syncIOStop() { return ret; } -int32_t syncIOSendMsg(void *clientRpc, const SEpSet *pEpSet, SRpcMsg *pMsg) { +int32_t syncIOSendMsg(const SEpSet *pEpSet, SRpcMsg *pMsg) { assert(pEpSet->inUse == 0); assert(pEpSet->numOfEps == 1); @@ -83,11 +83,11 @@ int32_t syncIOSendMsg(void *clientRpc, const SEpSet *pEpSet, SRpcMsg *pMsg) { pMsg->info.handle = NULL; pMsg->info.noResp = 1; - rpcSendRequest(clientRpc, pEpSet, pMsg, NULL); + rpcSendRequest(gSyncIO->clientRpc, pEpSet, pMsg, NULL); return ret; } -int32_t syncIOEqMsg(void *queue, SRpcMsg *pMsg) { +int32_t syncIOEqMsg(const SMsgCb *msgcb, SRpcMsg *pMsg) { int32_t ret = 0; char logBuf[128]; syncRpcMsgLog2((char *)"==syncIOEqMsg==", pMsg); @@ -96,7 +96,7 @@ int32_t syncIOEqMsg(void *queue, SRpcMsg *pMsg) { pTemp = taosAllocateQitem(sizeof(SRpcMsg), DEF_QITEM); memcpy(pTemp, pMsg, sizeof(SRpcMsg)); - STaosQueue *pMsgQ = queue; + STaosQueue *pMsgQ = gSyncIO->pMsgQ; taosWriteQitem(pMsgQ, pTemp); return ret; @@ -183,9 +183,6 @@ static int32_t syncIOStartInternal(SSyncIO *io) { rpcInit.sessions = 100; rpcInit.idleTime = 100; rpcInit.user = "sync-io"; - rpcInit.secret = "sync-io"; - rpcInit.ckey = "key"; - rpcInit.spi = 0; rpcInit.connType = TAOS_CONN_CLIENT; io->clientRpc = rpcOpen(&rpcInit); @@ -206,7 +203,6 @@ static int32_t syncIOStartInternal(SSyncIO *io) { rpcInit.cfp = syncIOProcessRequest; rpcInit.sessions = 1000; rpcInit.idleTime = 2 * 1500; - rpcInit.afp = syncIOAuth; rpcInit.parent = io; rpcInit.connType = TAOS_CONN_SERVER; diff --git a/source/libs/sync/src/syncMain.c b/source/libs/sync/src/syncMain.c index 562694bbbcec668e6ebaeda8124299d5e33ec7cd..d9ff60bbe22b573db34331e5aabbd04b06ff5616 100644 --- a/source/libs/sync/src/syncMain.c +++ b/source/libs/sync/src/syncMain.c @@ -240,26 +240,14 @@ int32_t syncGetAndDelRespRpc(int64_t rid, uint64_t index, SRpcMsg* msg) { return ret; } -void syncSetQ(int64_t rid, void* queue) { +void syncSetMsgCb(int64_t rid, const SMsgCb *msgcb) { SSyncNode* pSyncNode = (SSyncNode*)taosAcquireRef(tsNodeRefId, rid); if (pSyncNode == NULL) { sTrace("syncSetQ get pSyncNode is NULL, rid:%ld", rid); return; } assert(rid == pSyncNode->rid); - pSyncNode->queue = queue; - - taosReleaseRef(tsNodeRefId, pSyncNode->rid); -} - -void syncSetRpc(int64_t rid, void* rpcHandle) { - SSyncNode* pSyncNode = (SSyncNode*)taosAcquireRef(tsNodeRefId, rid); - if (pSyncNode == NULL) { - sTrace("syncSetRpc get pSyncNode is NULL, rid:%ld", rid); - return; - } - assert(rid == pSyncNode->rid); - pSyncNode->rpcClient = rpcHandle; + pSyncNode->msgcb = msgcb; taosReleaseRef(tsNodeRefId, pSyncNode->rid); } @@ -332,7 +320,7 @@ int32_t syncPropose(int64_t rid, const SRpcMsg* pMsg, bool isWeak) { SRpcMsg rpcMsg; syncClientRequest2RpcMsg(pSyncMsg, &rpcMsg); if (pSyncNode->FpEqMsg != NULL) { - pSyncNode->FpEqMsg(pSyncNode->queue, &rpcMsg); + pSyncNode->FpEqMsg(pSyncNode->msgcb, &rpcMsg); } else { sTrace("syncPropose pSyncNode->FpEqMsg is NULL"); } @@ -375,9 +363,8 @@ SSyncNode* syncNodeOpen(const SSyncInfo* pSyncInfo) { snprintf(pSyncNode->configPath, sizeof(pSyncNode->configPath), "%s/raft_config.json", pSyncInfo->path); pSyncNode->pWal = pSyncInfo->pWal; - pSyncNode->rpcClient = pSyncInfo->rpcClient; + pSyncNode->msgcb = pSyncInfo->msgcb; pSyncNode->FpSendMsg = pSyncInfo->FpSendMsg; - pSyncNode->queue = pSyncInfo->queue; pSyncNode->FpEqMsg = pSyncInfo->FpEqMsg; // init raft config @@ -687,11 +674,11 @@ int32_t syncNodeSendMsgById(const SRaftId* destRaftId, SSyncNode* pSyncNode, SRp SEpSet epSet; syncUtilraftId2EpSet(destRaftId, &epSet); if (pSyncNode->FpSendMsg != NULL) { - pMsg->info.noResp = 1; // htonl syncUtilMsgHtoN(pMsg->pCont); - pSyncNode->FpSendMsg(pSyncNode->rpcClient, &epSet, pMsg); + pMsg->info.noResp = 1; + pSyncNode->FpSendMsg(&epSet, pMsg); } else { sTrace("syncNodeSendMsgById pSyncNode->FpSendMsg is NULL"); } @@ -702,11 +689,11 @@ int32_t syncNodeSendMsgByInfo(const SNodeInfo* nodeInfo, SSyncNode* pSyncNode, S SEpSet epSet; syncUtilnodeInfo2EpSet(nodeInfo, &epSet); if (pSyncNode->FpSendMsg != NULL) { - pMsg->info.noResp = 1; // htonl syncUtilMsgHtoN(pMsg->pCont); - pSyncNode->FpSendMsg(pSyncNode->rpcClient, &epSet, pMsg); + pMsg->info.noResp = 1; + pSyncNode->FpSendMsg(&epSet, pMsg); } else { sTrace("syncNodeSendMsgByInfo pSyncNode->FpSendMsg is NULL"); } @@ -728,12 +715,12 @@ cJSON* syncNode2Json(const SSyncNode* pSyncNode) { snprintf(u64buf, sizeof(u64buf), "%p", pSyncNode->pWal); cJSON_AddStringToObject(pRoot, "pWal", u64buf); - snprintf(u64buf, sizeof(u64buf), "%p", pSyncNode->rpcClient); + snprintf(u64buf, sizeof(u64buf), "%p", pSyncNode->msgcb); cJSON_AddStringToObject(pRoot, "rpcClient", u64buf); snprintf(u64buf, sizeof(u64buf), "%p", pSyncNode->FpSendMsg); cJSON_AddStringToObject(pRoot, "FpSendMsg", u64buf); - snprintf(u64buf, sizeof(u64buf), "%p", pSyncNode->queue); + snprintf(u64buf, sizeof(u64buf), "%p", pSyncNode->msgcb); cJSON_AddStringToObject(pRoot, "queue", u64buf); snprintf(u64buf, sizeof(u64buf), "%p", pSyncNode->FpEqMsg); cJSON_AddStringToObject(pRoot, "FpEqMsg", u64buf); @@ -1095,7 +1082,7 @@ static void syncNodeEqPingTimer(void* param, void* tmrId) { syncTimeout2RpcMsg(pSyncMsg, &rpcMsg); syncRpcMsgLog2((char*)"==syncNodeEqPingTimer==", &rpcMsg); if (pSyncNode->FpEqMsg != NULL) { - pSyncNode->FpEqMsg(pSyncNode->queue, &rpcMsg); + pSyncNode->FpEqMsg(pSyncNode->msgcb, &rpcMsg); } else { sTrace("syncNodeEqPingTimer pSyncNode->FpEqMsg is NULL"); } @@ -1118,7 +1105,7 @@ static void syncNodeEqElectTimer(void* param, void* tmrId) { syncTimeout2RpcMsg(pSyncMsg, &rpcMsg); syncRpcMsgLog2((char*)"==syncNodeEqElectTimer==", &rpcMsg); if (pSyncNode->FpEqMsg != NULL) { - pSyncNode->FpEqMsg(pSyncNode->queue, &rpcMsg); + pSyncNode->FpEqMsg(pSyncNode->msgcb, &rpcMsg); } else { sTrace("syncNodeEqElectTimer pSyncNode->FpEqMsg is NULL"); } @@ -1145,7 +1132,7 @@ static void syncNodeEqHeartbeatTimer(void* param, void* tmrId) { syncTimeout2RpcMsg(pSyncMsg, &rpcMsg); syncRpcMsgLog2((char*)"==syncNodeEqHeartbeatTimer==", &rpcMsg); if (pSyncNode->FpEqMsg != NULL) { - pSyncNode->FpEqMsg(pSyncNode->queue, &rpcMsg); + pSyncNode->FpEqMsg(pSyncNode->msgcb, &rpcMsg); } else { sTrace("syncNodeEqHeartbeatTimer pSyncNode->FpEqMsg is NULL"); } @@ -1175,10 +1162,10 @@ static int32_t syncNodeEqNoop(SSyncNode* ths) { assert(pSyncMsg->dataLen == entryLen); memcpy(pSyncMsg->data, serialized, entryLen); - SRpcMsg rpcMsg; + SRpcMsg rpcMsg = {0}; syncClientRequest2RpcMsg(pSyncMsg, &rpcMsg); if (ths->FpEqMsg != NULL) { - ths->FpEqMsg(ths->queue, &rpcMsg); + ths->FpEqMsg(ths->msgcb, &rpcMsg); } else { sTrace("syncNodeEqNoop pSyncNode->FpEqMsg is NULL"); } diff --git a/source/libs/sync/src/syncMessage.c b/source/libs/sync/src/syncMessage.c index efefcbb3e7b217f7dd9781eac32659f2183bf5c1..04a989439a678dbd95b80a3a3a7ef1d65897b636 100644 --- a/source/libs/sync/src/syncMessage.c +++ b/source/libs/sync/src/syncMessage.c @@ -411,7 +411,7 @@ SyncPing* syncPingDeserialize3(void* buf, int32_t bufLen) { } uint32_t len; char* data = NULL; - if (tDecodeBinary(&decoder, (const uint8_t**)(&data), &len) < 0) { + if (tDecodeBinary(&decoder, (uint8_t**)(&data), &len) < 0) { return NULL; } assert(len = pMsg->dataLen); @@ -670,7 +670,7 @@ SyncPingReply* syncPingReplyDeserialize3(void* buf, int32_t bufLen) { } uint32_t len; char* data = NULL; - if (tDecodeBinary(&decoder, (const uint8_t**)(&data), &len) < 0) { + if (tDecodeBinary(&decoder, (uint8_t**)(&data), &len) < 0) { return NULL; } assert(len = pMsg->dataLen); diff --git a/source/libs/sync/src/syncRespMgr.c b/source/libs/sync/src/syncRespMgr.c index 47c73745fd06697969d6440e61aab048522e7836..642e57243467819f2d7ef5e322a377c14d84f393 100644 --- a/source/libs/sync/src/syncRespMgr.c +++ b/source/libs/sync/src/syncRespMgr.c @@ -76,8 +76,8 @@ int32_t syncRespMgrGetAndDel(SSyncRespMgr *pObj, uint64_t index, SRespStub *pStu void *pTmp = taosHashGet(pObj->pRespHash, &index, sizeof(index)); if (pTmp != NULL) { memcpy(pStub, pTmp, sizeof(SRespStub)); - taosThreadMutexUnlock(&(pObj->mutex)); taosHashRemove(pObj->pRespHash, &index, sizeof(index)); + taosThreadMutexUnlock(&(pObj->mutex)); return 1; // get one object } taosThreadMutexUnlock(&(pObj->mutex)); @@ -90,4 +90,4 @@ void syncRespClean(SSyncRespMgr *pObj) { taosThreadMutexUnlock(&(pObj->mutex)); } -void syncRespCleanByTTL(SSyncRespMgr *pObj, int64_t ttl) {} \ No newline at end of file +void syncRespCleanByTTL(SSyncRespMgr *pObj, int64_t ttl) {} diff --git a/source/libs/sync/test/syncConfigChangeTest.cpp b/source/libs/sync/test/syncConfigChangeTest.cpp index 9a2d9a6b3461f46c6af744b2fa503ca9ce46a6b8..cff692239a756081cf35191cf0787be5bd878326 100644 --- a/source/libs/sync/test/syncConfigChangeTest.cpp +++ b/source/libs/sync/test/syncConfigChangeTest.cpp @@ -100,9 +100,8 @@ SWal* createWal(char* path, int32_t vgId) { int64_t createSyncNode(int32_t replicaNum, int32_t myIndex, int32_t vgId, SWal* pWal, char* path, bool isStandBy) { SSyncInfo syncInfo; syncInfo.vgId = vgId; - syncInfo.rpcClient = gSyncIO->clientRpc; + syncInfo.msgcb = &gSyncIO->msgcb; syncInfo.FpSendMsg = syncIOSendMsg; - syncInfo.queue = gSyncIO->pMsgQ; syncInfo.FpEqMsg = syncIOEqMsg; syncInfo.pFsm = createFsm(); snprintf(syncInfo.path, sizeof(syncInfo.path), "%s_sync_replica%d_index%d", path, replicaNum, myIndex); diff --git a/source/libs/sync/test/syncElectTest.cpp b/source/libs/sync/test/syncElectTest.cpp index f58b6b670bba73476b177fca179445d3872aa2ec..862f7bd0baebed56d2e103c56ae1a88e9074c8ea 100644 --- a/source/libs/sync/test/syncElectTest.cpp +++ b/source/libs/sync/test/syncElectTest.cpp @@ -44,9 +44,8 @@ SWal* createWal(char* path, int32_t vgId) { SSyncNode* createSyncNode(int32_t replicaNum, int32_t myIndex, int32_t vgId, SWal* pWal, char* path) { SSyncInfo syncInfo; syncInfo.vgId = vgId; - syncInfo.rpcClient = gSyncIO->clientRpc; + syncInfo.msgcb = &gSyncIO->msgcb; syncInfo.FpSendMsg = syncIOSendMsg; - syncInfo.queue = gSyncIO->pMsgQ; syncInfo.FpEqMsg = syncIOEqMsg; syncInfo.pFsm = NULL; snprintf(syncInfo.path, sizeof(syncInfo.path), "%s_sync_replica%d_index%d", path, replicaNum, myIndex); diff --git a/source/libs/sync/test/syncEncodeTest.cpp b/source/libs/sync/test/syncEncodeTest.cpp index 09d20156f476a93b1de432ac9b87972fb691ad1f..454c823c6a501ac0cb2532892159957056d7e1fa 100644 --- a/source/libs/sync/test/syncEncodeTest.cpp +++ b/source/libs/sync/test/syncEncodeTest.cpp @@ -31,9 +31,8 @@ SSyncNode *pSyncNode; SSyncNode *syncNodeInit() { syncInfo.vgId = 1234; - syncInfo.rpcClient = gSyncIO->clientRpc; + syncInfo.msgcb = &gSyncIO->msgcb; syncInfo.FpSendMsg = syncIOSendMsg; - syncInfo.queue = gSyncIO->pMsgQ; syncInfo.FpEqMsg = syncIOEqMsg; syncInfo.pFsm = pFsm; snprintf(syncInfo.path, sizeof(syncInfo.path), "%s", "./"); diff --git a/source/libs/sync/test/syncEnqTest.cpp b/source/libs/sync/test/syncEnqTest.cpp index 6f83ede5a0feec8ddfe930e36c311100ca5a66be..8461bfe9b7403c70d45a79f59474ad5d0ca43b2a 100644 --- a/source/libs/sync/test/syncEnqTest.cpp +++ b/source/libs/sync/test/syncEnqTest.cpp @@ -25,9 +25,7 @@ SSyncFSM* pFsm; SSyncNode* syncNodeInit() { syncInfo.vgId = 1234; - syncInfo.rpcClient = gSyncIO->clientRpc; syncInfo.FpSendMsg = syncIOSendMsg; - syncInfo.queue = gSyncIO->pMsgQ; syncInfo.FpEqMsg = syncIOEqMsg; syncInfo.pFsm = pFsm; snprintf(syncInfo.path, sizeof(syncInfo.path), "%s", "./"); @@ -99,7 +97,7 @@ int main(int argc, char** argv) { SyncPingReply* pSyncMsg = syncPingReplyBuild2(&pSyncNode->myRaftId, &pSyncNode->myRaftId, 1000, "syncEnqTest"); SRpcMsg rpcMsg; syncPingReply2RpcMsg(pSyncMsg, &rpcMsg); - pSyncNode->FpEqMsg(pSyncNode->queue, &rpcMsg); + pSyncNode->FpEqMsg(pSyncNode->msgcb, &rpcMsg); taosMsleep(1000); } diff --git a/source/libs/sync/test/syncIOClientTest.cpp b/source/libs/sync/test/syncIOClientTest.cpp index 492b2e4349cc5a19473f6af936b1bfd45bbb553c..bd0221114ad618fb030ee5ec476a0a01634bcdd7 100644 --- a/source/libs/sync/test/syncIOClientTest.cpp +++ b/source/libs/sync/test/syncIOClientTest.cpp @@ -43,7 +43,7 @@ int main() { SRpcMsg rpcMsg; syncPingReply2RpcMsg(pSyncMsg, &rpcMsg); - syncIOSendMsg(gSyncIO->clientRpc, &epSet, &rpcMsg); + syncIOSendMsg(&epSet, &rpcMsg); taosSsleep(1); } diff --git a/source/libs/sync/test/syncIOSendMsgTest.cpp b/source/libs/sync/test/syncIOSendMsgTest.cpp index 03d308ea285f97c59427efcc92266eb248ee3168..630d96054bef043134b76233683551abe682bd6c 100644 --- a/source/libs/sync/test/syncIOSendMsgTest.cpp +++ b/source/libs/sync/test/syncIOSendMsgTest.cpp @@ -25,9 +25,8 @@ SSyncFSM* pFsm; SSyncNode* syncNodeInit() { syncInfo.vgId = 1234; - syncInfo.rpcClient = gSyncIO->clientRpc; + syncInfo.msgcb = &gSyncIO->msgcb; syncInfo.FpSendMsg = syncIOSendMsg; - syncInfo.queue = gSyncIO->pMsgQ; syncInfo.FpEqMsg = syncIOEqMsg; syncInfo.pFsm = pFsm; snprintf(syncInfo.path, sizeof(syncInfo.path), "%s", "./"); @@ -98,12 +97,13 @@ int main(int argc, char** argv) { for (int i = 0; i < 10; ++i) { SyncPingReply* pSyncMsg = syncPingReplyBuild2(&pSyncNode->myRaftId, &pSyncNode->myRaftId, 1000, "syncIOSendMsgTest"); - SRpcMsg rpcMsg; + SRpcMsg rpcMsg = {0}; syncPingReply2RpcMsg(pSyncMsg, &rpcMsg); SEpSet epSet; syncUtilnodeInfo2EpSet(&pSyncNode->myNodeInfo, &epSet); - pSyncNode->FpSendMsg(pSyncNode->rpcClient, &epSet, &rpcMsg); + rpcMsg.info.noResp = 1; + pSyncNode->FpSendMsg(&epSet, &rpcMsg); taosMsleep(1000); } diff --git a/source/libs/sync/test/syncIndexMgrTest.cpp b/source/libs/sync/test/syncIndexMgrTest.cpp index ea5d5f6b6fff0539d329bfb52ce86202ff65c13b..7fcce2bc4f06b404cc4565ca2e94ea884d9603aa 100644 --- a/source/libs/sync/test/syncIndexMgrTest.cpp +++ b/source/libs/sync/test/syncIndexMgrTest.cpp @@ -28,9 +28,8 @@ SSyncNode* pSyncNode; SSyncNode* syncNodeInit() { syncInfo.vgId = 1234; - syncInfo.rpcClient = gSyncIO->clientRpc; + syncInfo.msgcb = &gSyncIO->msgcb; syncInfo.FpSendMsg = syncIOSendMsg; - syncInfo.queue = gSyncIO->pMsgQ; syncInfo.FpEqMsg = syncIOEqMsg; syncInfo.pFsm = pFsm; snprintf(syncInfo.path, sizeof(syncInfo.path), "%s", "./"); diff --git a/source/libs/sync/test/syncInitTest.cpp b/source/libs/sync/test/syncInitTest.cpp index ca0657c74def18c8c8ed30495f5ca976ee55c402..d0843151f4b3b04c2c67d44b4cdce3a48e78d6d8 100644 --- a/source/libs/sync/test/syncInitTest.cpp +++ b/source/libs/sync/test/syncInitTest.cpp @@ -25,9 +25,8 @@ SSyncFSM* pFsm; SSyncNode* syncNodeInit() { syncInfo.vgId = 1234; - syncInfo.rpcClient = gSyncIO->clientRpc; + syncInfo.msgcb = &gSyncIO->msgcb; syncInfo.FpSendMsg = syncIOSendMsg; - syncInfo.queue = gSyncIO->pMsgQ; syncInfo.FpEqMsg = syncIOEqMsg; syncInfo.pFsm = pFsm; snprintf(syncInfo.path, sizeof(syncInfo.path), "%s", "./sync_init_test"); diff --git a/source/libs/sync/test/syncPingSelfTest.cpp b/source/libs/sync/test/syncPingSelfTest.cpp index 641ff059be3843928cc5e68074b7c131472ffcc2..99287bf7b0fd23c350e7d5f8503b17efee641b7e 100644 --- a/source/libs/sync/test/syncPingSelfTest.cpp +++ b/source/libs/sync/test/syncPingSelfTest.cpp @@ -25,9 +25,8 @@ SSyncFSM* pFsm; SSyncNode* syncNodeInit() { syncInfo.vgId = 1234; - syncInfo.rpcClient = gSyncIO->clientRpc; + syncInfo.msgcb = &gSyncIO->msgcb; syncInfo.FpSendMsg = syncIOSendMsg; - syncInfo.queue = gSyncIO->pMsgQ; syncInfo.FpEqMsg = syncIOEqMsg; syncInfo.pFsm = pFsm; snprintf(syncInfo.path, sizeof(syncInfo.path), "%s", "./"); diff --git a/source/libs/sync/test/syncPingTimerTest.cpp b/source/libs/sync/test/syncPingTimerTest.cpp index 29e99435bef6f82e20ffaf07de52631e00cfe20b..cd9440e3e280b71820ca5db8ed40bcc8b2c37d26 100644 --- a/source/libs/sync/test/syncPingTimerTest.cpp +++ b/source/libs/sync/test/syncPingTimerTest.cpp @@ -25,9 +25,8 @@ SSyncFSM* pFsm; SSyncNode* syncNodeInit() { syncInfo.vgId = 1234; - syncInfo.rpcClient = gSyncIO->clientRpc; + syncInfo.msgcb = &gSyncIO->msgcb; syncInfo.FpSendMsg = syncIOSendMsg; - syncInfo.queue = gSyncIO->pMsgQ; syncInfo.FpEqMsg = syncIOEqMsg; syncInfo.pFsm = pFsm; snprintf(syncInfo.path, sizeof(syncInfo.path), "%s", "./"); diff --git a/source/libs/sync/test/syncPingTimerTest2.cpp b/source/libs/sync/test/syncPingTimerTest2.cpp index 285828125d61216362b24281e3ce446826f906c6..fa09d04368178e7b7ab6c94cd53c7672174bdf7b 100644 --- a/source/libs/sync/test/syncPingTimerTest2.cpp +++ b/source/libs/sync/test/syncPingTimerTest2.cpp @@ -25,9 +25,8 @@ SSyncFSM* pFsm; SSyncNode* syncNodeInit() { syncInfo.vgId = 1234; - syncInfo.rpcClient = gSyncIO->clientRpc; + syncInfo.msgcb = &gSyncIO->msgcb; syncInfo.FpSendMsg = syncIOSendMsg; - syncInfo.queue = gSyncIO->pMsgQ; syncInfo.FpEqMsg = syncIOEqMsg; syncInfo.pFsm = pFsm; snprintf(syncInfo.path, sizeof(syncInfo.path), "%s", "./"); diff --git a/source/libs/sync/test/syncReplicateTest.cpp b/source/libs/sync/test/syncReplicateTest.cpp index 0e94498a382bb61928e4023e84f9e2c97d7acfda..bf9e34fffbc52121001ac0d9010d7a0b18869f88 100644 --- a/source/libs/sync/test/syncReplicateTest.cpp +++ b/source/libs/sync/test/syncReplicateTest.cpp @@ -97,9 +97,8 @@ SWal* createWal(char* path, int32_t vgId) { int64_t createSyncNode(int32_t replicaNum, int32_t myIndex, int32_t vgId, SWal* pWal, char* path) { SSyncInfo syncInfo; syncInfo.vgId = vgId; - syncInfo.rpcClient = gSyncIO->clientRpc; + syncInfo.msgcb = &gSyncIO->msgcb; syncInfo.FpSendMsg = syncIOSendMsg; - syncInfo.queue = gSyncIO->pMsgQ; syncInfo.FpEqMsg = syncIOEqMsg; syncInfo.pFsm = createFsm(); snprintf(syncInfo.path, sizeof(syncInfo.path), "%s_sync_replica%d_index%d", path, replicaNum, myIndex); diff --git a/source/libs/sync/test/syncSnapshotTest.cpp b/source/libs/sync/test/syncSnapshotTest.cpp index 5dd9ea9fcff94be6cb4083a8bf62bbc789e7b340..62bda5b22ec8633f1cb6ba2ff2cfbe224ead8c94 100644 --- a/source/libs/sync/test/syncSnapshotTest.cpp +++ b/source/libs/sync/test/syncSnapshotTest.cpp @@ -83,9 +83,8 @@ void initFsm() { SSyncNode *syncNodeInit() { syncInfo.vgId = 1234; - syncInfo.rpcClient = gSyncIO->clientRpc; + syncInfo.msgcb = &gSyncIO->msgcb; syncInfo.FpSendMsg = syncIOSendMsg; - syncInfo.queue = gSyncIO->pMsgQ; syncInfo.FpEqMsg = syncIOEqMsg; syncInfo.pFsm = pFsm; snprintf(syncInfo.path, sizeof(syncInfo.path), "%s", pDir); @@ -200,7 +199,7 @@ int main(int argc, char **argv) { SyncClientRequest *pSyncClientRequest = pMsg1; SRpcMsg rpcMsg; syncClientRequest2RpcMsg(pSyncClientRequest, &rpcMsg); - gSyncNode->FpEqMsg(gSyncNode->queue, &rpcMsg); + gSyncNode->FpEqMsg(gSyncNode->msgcb, &rpcMsg); taosMsleep(1000); } diff --git a/source/libs/sync/test/syncVotesGrantedTest.cpp b/source/libs/sync/test/syncVotesGrantedTest.cpp index 02a35b3d00fd730da651d02c660abe18cbe801cb..d4885d0316b3a099f1b1e1c0bca45b4f1f471849 100644 --- a/source/libs/sync/test/syncVotesGrantedTest.cpp +++ b/source/libs/sync/test/syncVotesGrantedTest.cpp @@ -27,9 +27,8 @@ SSyncNode* pSyncNode; SSyncNode* syncNodeInit() { syncInfo.vgId = 1234; - syncInfo.rpcClient = gSyncIO->clientRpc; + syncInfo.msgcb = &gSyncIO->msgcb; syncInfo.FpSendMsg = syncIOSendMsg; - syncInfo.queue = gSyncIO->pMsgQ; syncInfo.FpEqMsg = syncIOEqMsg; syncInfo.pFsm = pFsm; snprintf(syncInfo.path, sizeof(syncInfo.path), "%s", "./"); diff --git a/source/libs/sync/test/syncVotesRespondTest.cpp b/source/libs/sync/test/syncVotesRespondTest.cpp index f276d34745b1b52cda42b9dee4385b871c32b2f0..77262dfc65e0f134af75e8a11be674c40a1437ab 100644 --- a/source/libs/sync/test/syncVotesRespondTest.cpp +++ b/source/libs/sync/test/syncVotesRespondTest.cpp @@ -27,9 +27,8 @@ SSyncNode* pSyncNode; SSyncNode* syncNodeInit() { syncInfo.vgId = 1234; - syncInfo.rpcClient = gSyncIO->clientRpc; + syncInfo.msgcb = &gSyncIO->msgcb; syncInfo.FpSendMsg = syncIOSendMsg; - syncInfo.queue = gSyncIO->pMsgQ; syncInfo.FpEqMsg = syncIOEqMsg; syncInfo.pFsm = pFsm; snprintf(syncInfo.path, sizeof(syncInfo.path), "%s", "./"); diff --git a/source/libs/sync/test/syncWriteTest.cpp b/source/libs/sync/test/syncWriteTest.cpp index ef09d2a0a4e66b52f942ae6525a0dd9814a4da03..34c8eb0f56c5036977a6d22878f508ef6418c508 100644 --- a/source/libs/sync/test/syncWriteTest.cpp +++ b/source/libs/sync/test/syncWriteTest.cpp @@ -62,9 +62,8 @@ void initFsm() { SSyncNode *syncNodeInit() { syncInfo.vgId = 1234; - syncInfo.rpcClient = gSyncIO->clientRpc; + syncInfo.msgcb = &gSyncIO->msgcb; syncInfo.FpSendMsg = syncIOSendMsg; - syncInfo.queue = gSyncIO->pMsgQ; syncInfo.FpEqMsg = syncIOEqMsg; syncInfo.pFsm = pFsm; snprintf(syncInfo.path, sizeof(syncInfo.path), "%s", pDir); @@ -178,7 +177,7 @@ int main(int argc, char **argv) { SyncClientRequest *pSyncClientRequest = pMsg1; SRpcMsg rpcMsg; syncClientRequest2RpcMsg(pSyncClientRequest, &rpcMsg); - gSyncNode->FpEqMsg(gSyncNode->queue, &rpcMsg); + gSyncNode->FpEqMsg(gSyncNode->msgcb, &rpcMsg); taosMsleep(1000); } diff --git a/source/libs/tdb/src/db/tdbPCache.c b/source/libs/tdb/src/db/tdbPCache.c index 22d7e8e5a40987b176907d0f66b5eec70f270fe4..cdae73bfb949a4fa95471abbfdec2fa8098a9943 100644 --- a/source/libs/tdb/src/db/tdbPCache.c +++ b/source/libs/tdb/src/db/tdbPCache.c @@ -14,6 +14,9 @@ */ #include "tdbInt.h" +// #include +// #include + struct SPCache { int szPage; int nPages; @@ -32,7 +35,6 @@ static inline uint32_t tdbPCachePageHash(const SPgid *pPgid) { uint32_t *t = (uint32_t *)((pPgid)->fileid); return (uint32_t)(t[0] + t[1] + t[2] + t[3] + t[4] + t[5] + (pPgid)->pgno); } -#define PAGE_IS_PINNED(pPage) ((pPage)->pLruNext == NULL) static int tdbPCacheOpenImpl(SPCache *pCache); static SPage *tdbPCacheFetchImpl(SPCache *pCache, const SPgid *pPgid, TXN *pTxn); @@ -80,16 +82,22 @@ int tdbPCacheClose(SPCache *pCache) { SPage *tdbPCacheFetch(SPCache *pCache, const SPgid *pPgid, TXN *pTxn) { SPage *pPage; + i32 nRef; tdbPCacheLock(pCache); pPage = tdbPCacheFetchImpl(pCache, pPgid, pTxn); if (pPage) { - tdbRefPage(pPage); + nRef = tdbRefPage(pPage); } + ASSERT(pPage); + tdbPCacheUnlock(pCache); + // printf("thread %" PRId64 " fetch page %d pgno %d pPage %p nRef %d\n", taosGetSelfPthreadId(), pPage->id, + // TDB_PAGE_PGNO(pPage), pPage, nRef); + return pPage; } @@ -98,30 +106,31 @@ void tdbPCacheRelease(SPCache *pCache, SPage *pPage, TXN *pTxn) { ASSERT(pTxn); - nRef = tdbUnrefPage(pPage); - ASSERT(nRef >= 0); + // nRef = tdbUnrefPage(pPage); + // ASSERT(nRef >= 0); + tdbPCacheLock(pCache); + nRef = tdbUnrefPage(pPage); if (nRef == 0) { - tdbPCacheLock(pCache); - // test the nRef again to make sure // it is safe th handle the page - nRef = tdbGetPageRef(pPage); - if (nRef == 0) { - if (pPage->isLocal) { - tdbPCacheUnpinPage(pCache, pPage); - } else { - if (TDB_TXN_IS_WRITE(pTxn)) { - // remove from hash - tdbPCacheRemovePageFromHash(pCache, pPage); - } - - tdbPageDestroy(pPage, pTxn->xFree, pTxn->xArg); + // nRef = tdbGetPageRef(pPage); + // if (nRef == 0) { + if (pPage->isLocal) { + tdbPCacheUnpinPage(pCache, pPage); + } else { + if (TDB_TXN_IS_WRITE(pTxn)) { + // remove from hash + tdbPCacheRemovePageFromHash(pCache, pPage); } - } - tdbPCacheUnlock(pCache); + tdbPageDestroy(pPage, pTxn->xFree, pTxn->xArg); + } + // } } + tdbPCacheUnlock(pCache); + // printf("thread %" PRId64 " relas page %d pgno %d pPage %p nRef %d\n", taosGetSelfPthreadId(), pPage->id, + // TDB_PAGE_PGNO(pPage), pPage, nRef); } int tdbPCacheGetPageSize(SPCache *pCache) { return pCache->szPage; } @@ -223,6 +232,7 @@ static void tdbPCachePinPage(SPCache *pCache, SPage *pPage) { pCache->nRecyclable--; + // printf("pin page %d pgno %d pPage %p\n", pPage->id, TDB_PAGE_PGNO(pPage), pPage); tdbTrace("pin page %d", pPage->id); } } @@ -243,6 +253,7 @@ static void tdbPCacheUnpinPage(SPCache *pCache, SPage *pPage) { pCache->nRecyclable++; + // printf("unpin page %d pgno %d pPage %p\n", pPage->id, TDB_PAGE_PGNO(pPage), pPage); tdbTrace("unpin page %d", pPage->id); } @@ -253,10 +264,12 @@ static void tdbPCacheRemovePageFromHash(SPCache *pCache, SPage *pPage) { h = tdbPCachePageHash(&(pPage->pgid)); for (ppPage = &(pCache->pgHash[h % pCache->nHash]); (*ppPage) && *ppPage != pPage; ppPage = &((*ppPage)->pHashNext)) ; - ASSERT(*ppPage == pPage); - *ppPage = pPage->pHashNext; - pCache->nPage--; + if (*ppPage) { + *ppPage = pPage->pHashNext; + pCache->nPage--; + // printf("rmv page %d to hash, pgno %d, pPage %p\n", pPage->id, TDB_PAGE_PGNO(pPage), pPage); + } tdbTrace("remove page %d to hash", pPage->id); } @@ -271,6 +284,7 @@ static void tdbPCacheAddPageToHash(SPCache *pCache, SPage *pPage) { pCache->nPage++; + // printf("add page %d to hash, pgno %d, pPage %p\n", pPage->id, TDB_PAGE_PGNO(pPage), pPage); tdbTrace("add page %d to hash", pPage->id); } diff --git a/source/libs/tdb/src/db/tdbPager.c b/source/libs/tdb/src/db/tdbPager.c index 4024cfe745265441f96d4525f61f2c33ebf274a2..a74bb54883fe3e8d4ab344e80d3cd4b01f2cb525 100644 --- a/source/libs/tdb/src/db/tdbPager.c +++ b/source/libs/tdb/src/db/tdbPager.c @@ -265,6 +265,7 @@ int tdbPagerFetchPage(SPager *pPager, SPgno *ppgno, SPage **ppPage, int (*initPa pgid.pgno = pgno; pPage = tdbPCacheFetch(pPager->pCache, &pgid, pTxn); if (pPage == NULL) { + ASSERT(0); return -1; } @@ -272,10 +273,14 @@ int tdbPagerFetchPage(SPager *pPager, SPgno *ppgno, SPage **ppPage, int (*initPa if (!TDB_PAGE_INITIALIZED(pPage)) { ret = tdbPagerInitPage(pPager, pPage, initPage, arg, loadPage); if (ret < 0) { + ASSERT(0); return -1; } } + // printf("thread %" PRId64 " pager fetch page %d pgno %d ppage %p\n", taosGetSelfPthreadId(), pPage->id, + // TDB_PAGE_PGNO(pPage), pPage); + ASSERT(TDB_PAGE_INITIALIZED(pPage)); ASSERT(pPage->pPager == pPager); @@ -284,7 +289,11 @@ int tdbPagerFetchPage(SPager *pPager, SPgno *ppgno, SPage **ppPage, int (*initPa return 0; } -void tdbPagerReturnPage(SPager *pPager, SPage *pPage, TXN *pTxn) { tdbPCacheRelease(pPager->pCache, pPage, pTxn); } +void tdbPagerReturnPage(SPager *pPager, SPage *pPage, TXN *pTxn) { + tdbPCacheRelease(pPager->pCache, pPage, pTxn); + // printf("thread %" PRId64 " pager retun page %d pgno %d ppage %p\n", taosGetSelfPthreadId(), pPage->id, + // TDB_PAGE_PGNO(pPage), pPage); +} static int tdbPagerAllocFreePage(SPager *pPager, SPgno *ppgno) { // TODO: Allocate a page from the free list @@ -352,6 +361,7 @@ static int tdbPagerInitPage(SPager *pPager, SPage *pPage, int (*initPage)(SPage ret = (*initPage)(pPage, arg, init); if (ret < 0) { + ASSERT(0); TDB_UNLOCK_PAGE(pPage); return -1; } @@ -370,6 +380,7 @@ static int tdbPagerInitPage(SPager *pPager, SPage *pPage, int (*initPage)(SPage } } } else { + ASSERT(0); return -1; } diff --git a/source/libs/tdb/src/inc/tdbInt.h b/source/libs/tdb/src/inc/tdbInt.h index 2c79f39bc05ee8c99d81770774122bb374cea2b4..9f0267da93fca6db1b35844e77fdf8877eb33847 100644 --- a/source/libs/tdb/src/inc/tdbInt.h +++ b/source/libs/tdb/src/inc/tdbInt.h @@ -275,15 +275,15 @@ static inline i32 tdbUnrefPage(SPage *pPage) { #define P_LOCK_FAIL -1 static inline int tdbTryLockPage(tdb_spinlock_t *pLock) { - int ret; - if (tdbSpinlockTrylock(pLock) == 0) { - ret = P_LOCK_SUCC; - } else if (errno == EBUSY) { - ret = P_LOCK_BUSY; + int ret = tdbSpinlockTrylock(pLock); + if (ret == 0) { + return P_LOCK_SUCC; + } else if (ret == EBUSY) { + return P_LOCK_BUSY; } else { - ret = P_LOCK_FAIL; + ASSERT(0); + return P_LOCK_FAIL; } - return ret; } #define TDB_INIT_PAGE_LOCK(pPage) tdbSpinlockInit(&((pPage)->lock), 0) diff --git a/source/libs/tdb/test/tdbTest.cpp b/source/libs/tdb/test/tdbTest.cpp index fee3447884c08f9208be98615318609e344c6cd1..6070052127f8d2efde0fb42a6697935d257675e1 100644 --- a/source/libs/tdb/test/tdbTest.cpp +++ b/source/libs/tdb/test/tdbTest.cpp @@ -4,6 +4,7 @@ #include "os.h" #include "tdb.h" +#include #include #include #include @@ -118,7 +119,7 @@ static int tDefaultKeyCmpr(const void *pKey1, int keyLen1, const void *pKey2, in return cret; } -TEST(tdb_test, simple_insert1) { +TEST(tdb_test, DISABLED_simple_insert1) { int ret; TDB *pEnv; TTB *pDb; @@ -238,7 +239,7 @@ TEST(tdb_test, simple_insert1) { GTEST_ASSERT_EQ(ret, 0); } -TEST(tdb_test, simple_insert2) { +TEST(tdb_test, DISABLED_simple_insert2) { int ret; TDB *pEnv; TTB *pDb; @@ -325,7 +326,7 @@ TEST(tdb_test, simple_insert2) { GTEST_ASSERT_EQ(ret, 0); } -TEST(tdb_test, simple_delete1) { +TEST(tdb_test, DISABLED_simple_delete1) { int ret; TTB *pDb; char key[128]; @@ -420,7 +421,7 @@ TEST(tdb_test, simple_delete1) { tdbClose(pEnv); } -TEST(tdb_test, simple_upsert1) { +TEST(tdb_test, DISABLED_simple_upsert1) { int ret; TDB *pEnv; TTB *pDb; @@ -490,13 +491,13 @@ TEST(tdb_test, multi_thread_query) { TDB *pEnv; TTB *pDb; tdb_cmpr_fn_t compFunc; - int nData = 20000; + int nData = 1000000; TXN txn; taosRemoveDir("tdb"); // Open Env - ret = tdbOpen("tdb", 512, 1, &pEnv); + ret = tdbOpen("tdb", 4096, 10, &pEnv); GTEST_ASSERT_EQ(ret, 0); // Create a database @@ -506,7 +507,7 @@ TEST(tdb_test, multi_thread_query) { char key[64]; char val[64]; - int64_t poolLimit = 4096; // 1M pool limit + int64_t poolLimit = 4096 * 20; // 1M pool limit int64_t txnid = 0; SPoolMem *pPool; @@ -597,4 +598,132 @@ TEST(tdb_test, multi_thread_query) { // Close Env ret = tdbClose(pEnv); GTEST_ASSERT_EQ(ret, 0); +} + +TEST(tdb_test, DISABLED_multi_thread1) { +#if 0 + int ret; + TDB *pDb; + TTB *pTb; + tdb_cmpr_fn_t compFunc; + int nData = 10000000; + TXN txn; + + std::shared_timed_mutex mutex; + + taosRemoveDir("tdb"); + + // Open Env + ret = tdbOpen("tdb", 512, 1, &pDb); + GTEST_ASSERT_EQ(ret, 0); + + ret = tdbTbOpen("db.db", -1, -1, NULL, pDb, &pTb); + GTEST_ASSERT_EQ(ret, 0); + + auto insert = [](TDB *pDb, TTB *pTb, int nData, int *stop, std::shared_timed_mutex *mu) { + TXN txn = {0}; + char key[128]; + char val[128]; + SPoolMem *pPool = openPool(); + + txn.flags = TDB_TXN_WRITE | TDB_TXN_READ_UNCOMMITTED; + txn.txnId = -1; + txn.xMalloc = poolMalloc; + txn.xFree = poolFree; + txn.xArg = pPool; + tdbBegin(pDb, &txn); + for (int iData = 1; iData <= nData; iData++) { + sprintf(key, "key%d", iData); + sprintf(val, "value%d", iData); + { + std::lock_guard wmutex(*mu); + + int ret = tdbTbInsert(pTb, key, strlen(key), val, strlen(val), &txn); + + GTEST_ASSERT_EQ(ret, 0); + } + + if (pPool->size > 1024 * 1024) { + tdbCommit(pDb, &txn); + + clearPool(pPool); + tdbBegin(pDb, &txn); + } + } + + tdbCommit(pDb, &txn); + closePool(pPool); + + *stop = 1; + }; + + auto query = [](TTB *pTb, int *stop, std::shared_timed_mutex *mu) { + TBC *pDBC; + void *pKey = NULL; + void *pVal = NULL; + int vLen, kLen; + int ret; + TXN txn; + + SPoolMem *pPool = openPool(); + txn.flags = 0; + txn.txnId = 0; + txn.xMalloc = poolMalloc; + txn.xFree = poolFree; + txn.xArg = pPool; + + for (;;) { + if (*stop) break; + + clearPool(pPool); + int count = 0; + { + std::shared_lock rMutex(*mu); + + ret = tdbTbcOpen(pTb, &pDBC, &txn); + GTEST_ASSERT_EQ(ret, 0); + + tdbTbcMoveToFirst(pDBC); + + for (;;) { + ret = tdbTbcNext(pDBC, &pKey, &kLen, &pVal, &vLen); + if (ret < 0) break; + count++; + } + + std::cout << count << std::endl; + + tdbTbcClose(pDBC); + } + + usleep(500000); + } + + closePool(pPool); + tdbFree(pKey); + tdbFree(pVal); + }; + + std::vector threads; + int nThreads = 10; + int stop = 0; + for (int i = 0; i < nThreads; i++) { + if (i == 0) { + threads.push_back(std::thread(insert, pDb, pTb, nData, &stop, &mutex)); + } else { + threads.push_back(std::thread(query, pTb, &stop, &mutex)); + } + } + + for (auto &th : threads) { + th.join(); + } + + // Close a database + tdbTbClose(pTb); + + // Close Env + ret = tdbClose(pDb); + GTEST_ASSERT_EQ(ret, 0); +#endif } \ No newline at end of file diff --git a/source/libs/tfs/test/tfsTest.cpp b/source/libs/tfs/test/tfsTest.cpp index 58c3a83aff60bf1852534d73ebea8292264a15f1..d53c4a49ba068dd96fc8629efbbab3f0cedc36d9 100644 --- a/source/libs/tfs/test/tfsTest.cpp +++ b/source/libs/tfs/test/tfsTest.cpp @@ -16,7 +16,7 @@ class TfsTest : public ::testing::Test { protected: - static void SetUpTestSuite() { root = "/tmp/tfsTest"; } + static void SetUpTestSuite() { root = TD_TMP_DIR_PATH "tfsTest"; } static void TearDownTestSuite() {} public: @@ -299,15 +299,15 @@ TEST_F(TfsTest, 04_File) { TEST_F(TfsTest, 05_MultiDisk) { int32_t code = 0; - const char *root00 = "/tmp/tfsTest00"; - const char *root01 = "/tmp/tfsTest01"; - const char *root10 = "/tmp/tfsTest10"; - const char *root11 = "/tmp/tfsTest11"; - const char *root12 = "/tmp/tfsTest12"; - const char *root20 = "/tmp/tfsTest20"; - const char *root21 = "/tmp/tfsTest21"; - const char *root22 = "/tmp/tfsTest22"; - const char *root23 = "/tmp/tfsTest23"; + const char *root00 = TD_TMP_DIR_PATH "tfsTest00"; + const char *root01 = TD_TMP_DIR_PATH "tfsTest01"; + const char *root10 = TD_TMP_DIR_PATH "tfsTest10"; + const char *root11 = TD_TMP_DIR_PATH "tfsTest11"; + const char *root12 = TD_TMP_DIR_PATH "tfsTest12"; + const char *root20 = TD_TMP_DIR_PATH "tfsTest20"; + const char *root21 = TD_TMP_DIR_PATH "tfsTest21"; + const char *root22 = TD_TMP_DIR_PATH "tfsTest22"; + const char *root23 = TD_TMP_DIR_PATH "tfsTest23"; SDiskCfg dCfg[9] = {0}; tstrncpy(dCfg[0].dir, root01, TSDB_FILENAME_LEN); diff --git a/source/libs/transport/inc/rpcCache.h b/source/libs/transport/inc/rpcCache.h deleted file mode 100644 index 5148f5e23c928026c32f623e67fb7e576abd5604..0000000000000000000000000000000000000000 --- a/source/libs/transport/inc/rpcCache.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2019 TAOS Data, Inc. - * - * This program is free software: you can use, redistribute, and/or modify - * it under the terms of the GNU Affero General Public License, version 3 - * or later ("AGPL"), as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#ifndef TDENGINE_RPC_CACHE_H -#define TDENGINE_RPC_CACHE_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -void *rpcOpenConnCache(int maxSessions, void (*cleanFp)(void *), void *tmrCtrl, int64_t keepTimer); -void rpcCloseConnCache(void *handle); -void rpcAddConnIntoCache(void *handle, void *data, char *fqdn, uint16_t port, int8_t connType); -void *rpcGetConnFromCache(void *handle, char *fqdn, uint16_t port, int8_t connType); - -#ifdef __cplusplus -} -#endif - -#endif // TDENGINE_RPC_CACHE_H diff --git a/source/libs/transport/inc/rpcLog.h b/source/libs/transport/inc/rpcLog.h deleted file mode 100644 index a5db866932c5228741de88b3a165def88950f238..0000000000000000000000000000000000000000 --- a/source/libs/transport/inc/rpcLog.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2019 TAOS Data, Inc. - * - * This program is free software: you can use, redistribute, and/or modify - * it under the terms of the GNU Affero General Public License, version 3 - * or later ("AGPL"), as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#ifndef TDENGINE_RPC_LOG_H -#define TDENGINE_RPC_LOG_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "tlog.h" - -#define tFatal(...) \ - { \ - if (rpcDebugFlag & DEBUG_FATAL) { \ - taosPrintLog("RPC FATAL ", DEBUG_FATAL, rpcDebugFlag, __VA_ARGS__); \ - } \ - } -#define tError(...) \ - { \ - if (rpcDebugFlag & DEBUG_ERROR) { \ - taosPrintLog("RPC ERROR ", DEBUG_ERROR, rpcDebugFlag, __VA_ARGS__); \ - } \ - } -#define tWarn(...) \ - { \ - if (rpcDebugFlag & DEBUG_WARN) { \ - taosPrintLog("RPC WARN ", DEBUG_WARN, rpcDebugFlag, __VA_ARGS__); \ - } \ - } -#define tInfo(...) \ - { \ - if (rpcDebugFlag & DEBUG_INFO) { \ - taosPrintLog("RPC ", DEBUG_INFO, rpcDebugFlag, __VA_ARGS__); \ - } \ - } -#define tDebug(...) \ - { \ - if (rpcDebugFlag & DEBUG_DEBUG) { \ - taosPrintLog("RPC ", DEBUG_DEBUG, rpcDebugFlag, __VA_ARGS__); \ - } \ - } -#define tTrace(...) \ - { \ - if (rpcDebugFlag & DEBUG_TRACE) { \ - taosPrintLog("RPC ", DEBUG_TRACE, rpcDebugFlag, __VA_ARGS__); \ - } \ - } -#define tDump(x, y) \ - { \ - if (rpcDebugFlag & DEBUG_DUMP) { \ - taosDumpData((unsigned char *)x, y); \ - } \ - } - -#ifdef __cplusplus -} -#endif - -#endif // TDENGINE_RPC_LOG_H diff --git a/source/libs/transport/inc/rpcTcp.h b/source/libs/transport/inc/rpcTcp.h deleted file mode 100644 index ad42307516462521055575d11bef38273b125fe1..0000000000000000000000000000000000000000 --- a/source/libs/transport/inc/rpcTcp.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2019 TAOS Data, Inc. - * - * This program is free software: you can use, redistribute, and/or modify - * it under the terms of the GNU Affero General Public License, version 3 - * or later ("AGPL"), as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#ifndef _rpc_tcp_header_ -#define _rpc_tcp_header_ -#include - -#ifdef __cplusplus -extern "C" { -#endif - -void *taosInitTcpServer(uint32_t ip, uint16_t port, char *label, int numOfThreads, void *fp, void *shandle); -void taosStopTcpServer(void *param); -void taosCleanUpTcpServer(void *param); - -void *taosInitTcpClient(uint32_t ip, uint16_t port, char *label, int num, void *fp, void *shandle); -void taosStopTcpClient(void *chandle); -void taosCleanUpTcpClient(void *chandle); -void *taosOpenTcpClientConnection(void *shandle, void *thandle, uint32_t ip, uint16_t port); - -void taosCloseTcpConnection(void *chandle); -int taosSendTcpData(uint32_t ip, uint16_t port, void *data, int len, void *chandle); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/source/libs/transport/inc/rpcUdp.h b/source/libs/transport/inc/rpcUdp.h deleted file mode 100644 index 0c651d07ed4662305e107c09a7312f372aa98339..0000000000000000000000000000000000000000 --- a/source/libs/transport/inc/rpcUdp.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2019 TAOS Data, Inc. - * - * This program is free software: you can use, redistribute, and/or modify - * it under the terms of the GNU Affero General Public License, version 3 - * or later ("AGPL"), as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#ifndef _rpc_udp_header_ -#define _rpc_udp_header_ - -#ifdef __cplusplus -extern "C" { -#endif - -#include "taosdef.h" - -void *taosInitUdpConnection(uint32_t ip, uint16_t port, char *label, int, void *fp, void *shandle); -void taosStopUdpConnection(void *handle); -void taosCleanUpUdpConnection(void *handle); -int taosSendUdpData(uint32_t ip, uint16_t port, void *data, int dataLen, void *chandle); -void *taosOpenUdpConnection(void *shandle, void *thandle, uint32_t ip, uint16_t port); - -void taosFreeMsgHdr(void *hdr); -int taosMsgHdrSize(void *hdr); -void taosSendMsgHdr(void *hdr, TdFilePtr pFile); -void taosInitMsgHdr(void **hdr, void *dest, int maxPkts); -void taosSetMsgHdrData(void *hdr, char *data, int dataLen); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/source/libs/transport/inc/transComm.h b/source/libs/transport/inc/transComm.h index e4e79ccf476ce9a1253db536f623431810541ad5..e71e19edceedc32fa196fbdba37fb1d38ac62525 100644 --- a/source/libs/transport/inc/transComm.h +++ b/source/libs/transport/inc/transComm.h @@ -12,31 +12,20 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -#ifdef USE_UV +#ifndef _TD_TRANSPORT_COMM_H +#define _TD_TRANSPORT_COMM_H #ifdef __cplusplus extern "C" { #endif #include -#include "lz4.h" #include "os.h" -#include "osSocket.h" -#include "rpcCache.h" -#include "rpcHead.h" -#include "rpcLog.h" #include "taoserror.h" -#include "tglobal.h" -#include "thash.h" #include "theap.h" -#include "tidpool.h" -#include "tmd5.h" -#include "tmempool.h" -#include "tmsg.h" +#include "transLog.h" #include "transportInt.h" -#include "tref.h" #include "trpc.h" -#include "ttimer.h" #include "tutil.h" typedef void* queue[2]; @@ -109,27 +98,6 @@ typedef void* queue[2]; #define TRANS_RETRY_INTERVAL 15 // ms retry interval #define TRANS_CONN_TIMEOUT 3 // connect timeout -typedef struct { - SRpcInfo* pRpc; // associated SRpcInfo - SEpSet epSet; // ip list provided by app - void* ahandle; // handle provided by app - // struct SRpcConn* pConn; // pConn allocated - tmsg_t msgType; // message type - uint8_t* pCont; // content provided by app - int32_t contLen; // content length - // int32_t code; // error code - // int16_t numOfTry; // number of try for different servers - // int8_t oldInUse; // server EP inUse passed by app - // int8_t redirect; // flag to indicate redirect - int8_t connType; // connection type - int64_t rid; // refId returned by taosAddRef - SRpcMsg* pRsp; // for synchronous API - tsem_t* pSem; // for synchronous API - char* ip; - uint32_t port; - // SEpSet* pSet; // for synchronous API -} SRpcReqContext; - typedef SRpcMsg STransMsg; typedef SRpcCtx STransCtx; typedef SRpcCtxVal STransCtxVal; @@ -193,12 +161,7 @@ typedef enum { ConnNormal, ConnAcquire, ConnRelease, ConnBroken, ConnInPool } Co #define container_of(ptr, type, member) ((type*)((char*)(ptr)-offsetof(type, member))) #define RPC_RESERVE_SIZE (sizeof(STranConnCtx)) -#define RPC_MSG_OVERHEAD (sizeof(SRpcHead) + sizeof(SRpcDigest)) -#define rpcHeadFromCont(cont) ((SRpcHead*)((char*)cont - sizeof(SRpcHead))) -#define rpcContFromHead(msg) (msg + sizeof(SRpcHead)) -#define rpcMsgLenFromCont(contLen) (contLen + sizeof(SRpcHead)) -#define rpcContLenFromMsg(msgLen) (msgLen - sizeof(SRpcHead)) -#define rpcIsReq(type) (type & 1U) +#define rpcIsReq(type) (type & 1U) #define TRANS_RESERVE_SIZE (sizeof(STranConnCtx)) @@ -209,15 +172,14 @@ typedef enum { ConnNormal, ConnAcquire, ConnRelease, ConnBroken, ConnInPool } Co #define transContLenFromMsg(msgLen) (msgLen - sizeof(STransMsgHead)); #define transIsReq(type) (type & 1U) -int rpcAuthenticateMsg(void* pMsg, int msgLen, void* pAuth, void* pKey); -void rpcBuildAuthHead(void* pMsg, int msgLen, void* pAuth, void* pKey); -int32_t rpcCompressRpcMsg(char* pCont, int32_t contLen); -SRpcHead* rpcDecompressRpcMsg(SRpcHead* pHead); - -int transAuthenticateMsg(void* pMsg, int msgLen, void* pAuth, void* pKey); -void transBuildAuthHead(void* pMsg, int msgLen, void* pAuth, void* pKey); -bool transCompressMsg(char* msg, int32_t len, int32_t* flen); -bool transDecompressMsg(char* msg, int32_t len, int32_t* flen); +// int rpcAuthenticateMsg(void* pMsg, int msgLen, void* pAuth, void* pKey); +// void rpcBuildAuthHead(void* pMsg, int msgLen, void* pAuth, void* pKey); +//// int32_t rpcCompressRpcMsg(char* pCont, int32_t contLen); +// +// int transAuthenticateMsg(void* pMsg, int msgLen, void* pAuth, void* pKey); +// void transBuildAuthHead(void* pMsg, int msgLen, void* pAuth, void* pKey); +// bool transCompressMsg(char* msg, int32_t len, int32_t* flen); +// bool transDecompressMsg(char* msg, int32_t len, int32_t* flen); void transFreeMsg(void* msg); @@ -365,4 +327,4 @@ void transThreadOnce(); } #endif -#endif +#endif // _TD_TRANSPORT_COMM_H diff --git a/source/libs/transport/inc/transLog.h b/source/libs/transport/inc/transLog.h new file mode 100644 index 0000000000000000000000000000000000000000..ebf231cfcff11aea08158264da5b71e77a429692 --- /dev/null +++ b/source/libs/transport/inc/transLog.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef _TD_TRANSPORT_LOG_H +#define _TD_TRANSPORT_LOG_H + +#ifdef __cplusplus +extern "C" { +#endif + +// clang-format off +#include "tlog.h" + +#define tFatal(...) do {if (rpcDebugFlag & DEBUG_FATAL){ taosPrintLog("RPC FATAL ", DEBUG_FATAL, rpcDebugFlag, __VA_ARGS__); }} while (0) +#define tError(...)do { if (rpcDebugFlag & DEBUG_ERROR){ taosPrintLog("RPC ERROR ", DEBUG_ERROR, rpcDebugFlag, __VA_ARGS__); } } while(0) +#define tWarn(...) do { if (rpcDebugFlag & DEBUG_WARN) { taosPrintLog("RPC WARN ", DEBUG_WARN, rpcDebugFlag, __VA_ARGS__); }} while(0) +#define tInfo(...) do { if (rpcDebugFlag & DEBUG_INFO) { taosPrintLog("RPC ", DEBUG_INFO, rpcDebugFlag, __VA_ARGS__); }} while(0) +#define tDebug(...) do {if (rpcDebugFlag & DEBUG_DEBUG){ taosPrintLog("RPC ", DEBUG_DEBUG, rpcDebugFlag, __VA_ARGS__); }} while(0) +#define tTrace(...) do {if (rpcDebugFlag & DEBUG_TRACE){ taosPrintLog("RPC ", DEBUG_TRACE, rpcDebugFlag, __VA_ARGS__); }} while(0) +#define tDump(x, y) do {if (rpcDebugFlag & DEBUG_DUMP) { taosDumpData((unsigned char *)x, y); } } while(0) +// clang-format on +#ifdef __cplusplus +} +#endif + +#endif // __TRANS_LOG_H diff --git a/source/libs/transport/inc/transportInt.h b/source/libs/transport/inc/transportInt.h index 56f38a7a553cbc24e85d11d2f8b7fc50e93d6e63..8aeae1b5ade26a1a320dae37cbfe67f676f66eeb 100644 --- a/source/libs/transport/inc/transportInt.h +++ b/source/libs/transport/inc/transportInt.h @@ -21,18 +21,14 @@ #endif #include "lz4.h" #include "os.h" -#include "rpcCache.h" -#include "rpcHead.h" -#include "rpcLog.h" #include "taoserror.h" -#include "tglobal.h" #include "thash.h" -#include "tidpool.h" -#include "tmsg.h" #include "tref.h" +#include "tmsg.h" +#include "transLog.h" #include "trpc.h" -#include "ttimer.h" #include "tutil.h" +#include "tglobal.h" #ifdef __cplusplus extern "C" { @@ -52,23 +48,15 @@ typedef struct { int idleTime; // milliseconds; uint16_t localPort; int8_t connType; - int64_t index; char label[TSDB_LABEL_LEN]; - - char user[TSDB_UNI_LEN]; // meter ID - char spi; // security parameter index - char encrypt; // encrypt algorithm - char secret[TSDB_PASSWORD_LEN]; // secret for the link - char ckey[TSDB_PASSWORD_LEN]; // ciphering key + char user[TSDB_UNI_LEN]; // meter ID void (*cfp)(void* parent, SRpcMsg*, SEpSet*); bool (*retry)(int32_t code); + int index; int32_t refCount; void* parent; - void* idPool; // handle to ID pool - void* tmrCtrl; // handle to timer - SHashObj* hash; // handle returned by hash utility void* tcphandle; // returned handle from TCP initialization TdThreadMutex mutex; } SRpcInfo; diff --git a/source/libs/transport/src/rpcCache.c b/source/libs/transport/src/rpcCache.c deleted file mode 100644 index e02de2d8b96f628d8eb07355690b968840378863..0000000000000000000000000000000000000000 --- a/source/libs/transport/src/rpcCache.c +++ /dev/null @@ -1,294 +0,0 @@ -/* - * Copyright (c) 2019 TAOS Data, Inc. - * - * This program is free software: you can use, redistribute, and/or modify - * it under the terms of the GNU Affero General Public License, version 3 - * or later ("AGPL"), as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include "rpcCache.h" -#include "os.h" -#include "rpcLog.h" -#include "taosdef.h" -#include "tglobal.h" -#include "tmempool.h" -#include "ttimer.h" -#include "tutil.h" - -typedef struct SConnHash { - char fqdn[TSDB_FQDN_LEN]; - uint16_t port; - char connType; - struct SConnHash *prev; - struct SConnHash *next; - void * data; - uint64_t time; -} SConnHash; - -typedef struct { - SConnHash ** connHashList; - mpool_h connHashMemPool; - int maxSessions; - int total; - int * count; - int64_t keepTimer; - TdThreadMutex mutex; - void (*cleanFp)(void *); - void * tmrCtrl; - void * pTimer; - int64_t *lockedBy; -} SConnCache; - -static int rpcHashConn(void *handle, char *fqdn, uint16_t port, int8_t connType); -static void rpcLockCache(int64_t *lockedBy); -static void rpcUnlockCache(int64_t *lockedBy); -static void rpcCleanConnCache(void *handle, void *tmrId); -static void rpcRemoveExpiredNodes(SConnCache *pCache, SConnHash *pNode, int hash, uint64_t time); - -void *rpcOpenConnCache(int maxSessions, void (*cleanFp)(void *), void *tmrCtrl, int64_t keepTimer) { - SConnHash **connHashList; - mpool_h connHashMemPool; - SConnCache *pCache; - - connHashMemPool = taosMemPoolInit(maxSessions, sizeof(SConnHash)); - if (connHashMemPool == 0) return NULL; - - connHashList = taosMemoryCalloc(sizeof(SConnHash *), maxSessions); - if (connHashList == 0) { - taosMemPoolCleanUp(connHashMemPool); - return NULL; - } - - pCache = taosMemoryMalloc(sizeof(SConnCache)); - if (pCache == NULL) { - taosMemPoolCleanUp(connHashMemPool); - taosMemoryFree(connHashList); - return NULL; - } - memset(pCache, 0, sizeof(SConnCache)); - - pCache->count = taosMemoryCalloc(sizeof(int), maxSessions); - pCache->total = 0; - pCache->keepTimer = keepTimer; - pCache->maxSessions = maxSessions; - pCache->connHashMemPool = connHashMemPool; - pCache->connHashList = connHashList; - pCache->cleanFp = cleanFp; - pCache->tmrCtrl = tmrCtrl; - pCache->lockedBy = taosMemoryCalloc(sizeof(int64_t), maxSessions); - taosTmrReset(rpcCleanConnCache, (int32_t)(pCache->keepTimer * 2), pCache, pCache->tmrCtrl, &pCache->pTimer); - - taosThreadMutexInit(&pCache->mutex, NULL); - - return pCache; -} - -void rpcCloseConnCache(void *handle) { - SConnCache *pCache; - - pCache = (SConnCache *)handle; - if (pCache == NULL || pCache->maxSessions == 0) return; - - taosThreadMutexLock(&pCache->mutex); - - taosTmrStopA(&(pCache->pTimer)); - - if (pCache->connHashMemPool) taosMemPoolCleanUp(pCache->connHashMemPool); - - taosMemoryFreeClear(pCache->connHashList); - taosMemoryFreeClear(pCache->count); - taosMemoryFreeClear(pCache->lockedBy); - - taosThreadMutexUnlock(&pCache->mutex); - - taosThreadMutexDestroy(&pCache->mutex); - - memset(pCache, 0, sizeof(SConnCache)); - taosMemoryFree(pCache); -} - -void rpcAddConnIntoCache(void *handle, void *data, char *fqdn, uint16_t port, int8_t connType) { - int hash; - SConnHash * pNode; - SConnCache *pCache; - - uint64_t time = taosGetTimestampMs(); - - pCache = (SConnCache *)handle; - assert(pCache); - assert(data); - - hash = rpcHashConn(pCache, fqdn, port, connType); - pNode = (SConnHash *)taosMemPoolMalloc(pCache->connHashMemPool); - tstrncpy(pNode->fqdn, fqdn, sizeof(pNode->fqdn)); - pNode->port = port; - pNode->connType = connType; - pNode->data = data; - pNode->prev = NULL; - pNode->time = time; - - rpcLockCache(pCache->lockedBy + hash); - - pNode->next = pCache->connHashList[hash]; - if (pCache->connHashList[hash] != NULL) (pCache->connHashList[hash])->prev = pNode; - pCache->connHashList[hash] = pNode; - - pCache->count[hash]++; - rpcRemoveExpiredNodes(pCache, pNode->next, hash, time); - - rpcUnlockCache(pCache->lockedBy + hash); - - pCache->total++; - // tTrace("%p %s:%hu:%d:%d:%p added into cache, connections:%d", data, fqdn, port, connType, hash, pNode, - // pCache->count[hash]); - - return; -} - -void *rpcGetConnFromCache(void *handle, char *fqdn, uint16_t port, int8_t connType) { - int hash; - SConnHash * pNode; - SConnCache *pCache; - void * pData = NULL; - - pCache = (SConnCache *)handle; - assert(pCache); - - uint64_t time = taosGetTimestampMs(); - - hash = rpcHashConn(pCache, fqdn, port, connType); - rpcLockCache(pCache->lockedBy + hash); - - pNode = pCache->connHashList[hash]; - while (pNode) { - if (time >= pCache->keepTimer + pNode->time) { - rpcRemoveExpiredNodes(pCache, pNode, hash, time); - pNode = NULL; - break; - } - - if (strcmp(pNode->fqdn, fqdn) == 0 && pNode->port == port && pNode->connType == connType) break; - - pNode = pNode->next; - } - - if (pNode) { - rpcRemoveExpiredNodes(pCache, pNode->next, hash, time); - - if (pNode->prev) { - pNode->prev->next = pNode->next; - } else { - pCache->connHashList[hash] = pNode->next; - } - - if (pNode->next) { - pNode->next->prev = pNode->prev; - } - - pData = pNode->data; - taosMemPoolFree(pCache->connHashMemPool, (char *)pNode); - pCache->total--; - pCache->count[hash]--; - } - - rpcUnlockCache(pCache->lockedBy + hash); - - if (pData) { - // tTrace("%p %s:%hu:%d:%d:%p retrieved from cache, connections:%d", pData, fqdn, port, connType, hash, pNode, - // pCache->count[hash]); - } else { - // tTrace("%s:%hu:%d:%d failed to retrieve conn from cache, connections:%d", fqdn, port, connType, hash, - // pCache->count[hash]); - } - - return pData; -} - -static void rpcCleanConnCache(void *handle, void *tmrId) { - int hash; - SConnHash * pNode; - SConnCache *pCache; - - pCache = (SConnCache *)handle; - if (pCache == NULL || pCache->maxSessions == 0) return; - if (pCache->pTimer != tmrId) return; - - taosThreadMutexLock(&pCache->mutex); - uint64_t time = taosGetTimestampMs(); - - for (hash = 0; hash < pCache->maxSessions; ++hash) { - rpcLockCache(pCache->lockedBy + hash); - pNode = pCache->connHashList[hash]; - rpcRemoveExpiredNodes(pCache, pNode, hash, time); - rpcUnlockCache(pCache->lockedBy + hash); - } - - // tTrace("timer, total connections in cache:%d", pCache->total); - taosTmrReset(rpcCleanConnCache, (int32_t)(pCache->keepTimer * 2), pCache, pCache->tmrCtrl, &pCache->pTimer); - taosThreadMutexUnlock(&pCache->mutex); -} - -static void rpcRemoveExpiredNodes(SConnCache *pCache, SConnHash *pNode, int hash, uint64_t time) { - if (pNode == NULL || (time < pCache->keepTimer + pNode->time)) return; - - SConnHash *pPrev = pNode->prev, *pNext; - - while (pNode) { - (*pCache->cleanFp)(pNode->data); - pNext = pNode->next; - pCache->total--; - pCache->count[hash]--; - // tTrace("%p %s:%hu:%d:%d:%p removed from cache, connections:%d", pNode->data, pNode->fqdn, pNode->port, - // pNode->connType, hash, pNode, - // pCache->count[hash]); - taosMemPoolFree(pCache->connHashMemPool, (char *)pNode); - pNode = pNext; - } - - if (pPrev) - pPrev->next = NULL; - else - pCache->connHashList[hash] = NULL; -} - -static int rpcHashConn(void *handle, char *fqdn, uint16_t port, int8_t connType) { - SConnCache *pCache = (SConnCache *)handle; - int hash = 0; - char * temp = fqdn; - - while (*temp) { - hash += *temp; - ++temp; - } - - hash += port; - hash += connType; - - hash = hash % pCache->maxSessions; - - return hash; -} - -static void rpcLockCache(int64_t *lockedBy) { - int64_t tid = taosGetSelfPthreadId(); - int i = 0; - while (atomic_val_compare_exchange_64(lockedBy, 0, tid) != 0) { - if (++i % 100 == 0) { - sched_yield(); - } - } -} - -static void rpcUnlockCache(int64_t *lockedBy) { - int64_t tid = taosGetSelfPthreadId(); - if (atomic_val_compare_exchange_64(lockedBy, tid, 0) != tid) { - assert(false); - } -} diff --git a/source/libs/transport/src/rpcMain.c b/source/libs/transport/src/rpcMain.c deleted file mode 100644 index 0e425970da7a600a2ac85431de4d4d4621f80a2d..0000000000000000000000000000000000000000 --- a/source/libs/transport/src/rpcMain.c +++ /dev/null @@ -1,1682 +0,0 @@ -/* - * Copyright (c) 2019 TAOS Data, Inc. - * - * This program is free software: you can use, redistribute, and/or modify - * it under the terms of the GNU Affero General Public License, version 3 - * or later ("AGPL"), as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include "lz4.h" -#include "transportInt.h" -#include "os.h" -#include "rpcCache.h" -#include "rpcHead.h" -#include "rpcLog.h" -#include "rpcTcp.h" -#include "rpcUdp.h" -#include "taoserror.h" -#include "tglobal.h" -#include "thash.h" -#include "tidpool.h" -#include "tmd5.h" -#include "tmempool.h" -#include "tmsg.h" -#include "tref.h" -#include "trpc.h" -#include "ttimer.h" -#include "tutil.h" - -static TdThreadOnce tsRpcInitOnce = PTHREAD_ONCE_INIT; - -int tsRpcMaxUdpSize = 15000; // bytes -int tsProgressTimer = 100; -// not configurable -int tsRpcMaxRetry; -int tsRpcHeadSize; -int tsRpcOverhead; - -SHashObj *tsFqdnHash; - -#ifndef USE_UV - -typedef struct { - int sessions; // number of sessions allowed - int numOfThreads; // number of threads to process incoming messages - int idleTime; // milliseconds; - uint16_t localPort; - int8_t connType; - int index; // for UDP server only, round robin for multiple threads - char label[TSDB_LABEL_LEN]; - - char user[TSDB_UNI_LEN]; // meter ID - char spi; // security parameter index - char encrypt; // encrypt algorithm - char secret[TSDB_PASSWORD_LEN]; // secret for the link - char ckey[TSDB_PASSWORD_LEN]; // ciphering key - - void (*cfp)(void *parent, SRpcMsg *, SEpSet *); - int (*afp)(void *parent, char *user, char *spi, char *encrypt, char *secret, char *ckey); - - int32_t refCount; - void * parent; - void * idPool; // handle to ID pool - void * tmrCtrl; // handle to timer - SHashObj * hash; // handle returned by hash utility - void * tcphandle; // returned handle from TCP initialization - void * udphandle; // returned handle from UDP initialization - void * pCache; // connection cache - TdThreadMutex mutex; - struct SRpcConn *connList; // connection list -} SRpcInfo; - -typedef struct { - SRpcInfo * pRpc; // associated SRpcInfo - SEpSet epSet; // ip list provided by app - void * ahandle; // handle provided by app - struct SRpcConn *pConn; // pConn allocated - tmsg_t msgType; // message type - uint8_t * pCont; // content provided by app - int32_t contLen; // content length - int32_t code; // error code - int16_t numOfTry; // number of try for different servers - int8_t oldInUse; // server EP inUse passed by app - int8_t redirect; // flag to indicate redirect - int8_t connType; // connection type - int64_t rid; // refId returned by taosAddRef - SRpcMsg * pRsp; // for synchronous API - tsem_t * pSem; // for synchronous API - SEpSet * pSet; // for synchronous API - char msg[0]; // RpcHead starts from here -} SRpcReqContext; - -#define RPC_MSG_OVERHEAD (sizeof(SRpcReqContext) + sizeof(SRpcHead) + sizeof(SRpcDigest)) -#define rpcHeadFromCont(cont) ((SRpcHead *)((char *)cont - sizeof(SRpcHead))) -#define rpcContFromHead(msg) (msg + sizeof(SRpcHead)) -#define rpcMsgLenFromCont(contLen) (contLen + sizeof(SRpcHead)) -#define rpcContLenFromMsg(msgLen) (msgLen - sizeof(SRpcHead)) -#define rpcIsReq(type) (type & 1U) - -typedef struct SRpcConn { - char info[48]; // debug info: label + pConn + ahandle - int sid; // session ID - uint32_t ownId; // own link ID - uint32_t peerId; // peer link ID - char user[TSDB_UNI_LEN]; // user ID for the link - char spi; // security parameter index - char encrypt; // encryption, 0:1 - char secret[TSDB_PASSWORD_LEN]; // secret for the link - char ckey[TSDB_PASSWORD_LEN]; // ciphering key - char secured; // if set to 1, no authentication - uint16_t localPort; // for UDP only - uint32_t linkUid; // connection unique ID assigned by client - uint32_t peerIp; // peer IP - uint16_t peerPort; // peer port - char peerFqdn[TSDB_FQDN_LEN]; // peer FQDN or ip string - uint16_t tranId; // outgoing transcation ID, for build message - uint16_t outTranId; // outgoing transcation ID - uint16_t inTranId; // transcation ID for incoming msg - tmsg_t outType; // message type for outgoing request - tmsg_t inType; // message type for incoming request - void * chandle; // handle passed by TCP/UDP connection layer - void * ahandle; // handle provided by upper app layter - int retry; // number of retry for sending request - int tretry; // total retry - void * pTimer; // retry timer to monitor the response - void * pIdleTimer; // idle timer - char * pRspMsg; // response message including header - int rspMsgLen; // response messag length - char * pReqMsg; // request message including header - int reqMsgLen; // request message length - SRpcInfo * pRpc; // the associated SRpcInfo - int8_t connType; // connection type - int64_t lockedBy; // lock for connection - SRpcReqContext *pContext; // request context -} SRpcConn; - -static int tsRpcRefId = -1; -static int32_t tsRpcNum = 0; - -// static TdThreadOnce tsRpcInit = PTHREAD_ONCE_INIT; - -// server:0 client:1 tcp:2 udp:0 -#define RPC_CONN_UDPS 0 -#define RPC_CONN_UDPC 1 -#define RPC_CONN_TCPS 2 -#define RPC_CONN_TCPC 3 - -void *(*taosInitConn[])(uint32_t ip, uint16_t port, char *label, int threads, void *fp, void *shandle) = { - taosInitUdpConnection, taosInitUdpConnection, taosInitTcpServer, taosInitTcpClient}; - -void (*taosCleanUpConn[])(void *thandle) = {taosCleanUpUdpConnection, taosCleanUpUdpConnection, taosCleanUpTcpServer, - taosCleanUpTcpClient}; - -void (*taosStopConn[])(void *thandle) = { - taosStopUdpConnection, - taosStopUdpConnection, - taosStopTcpServer, - taosStopTcpClient, -}; - -int (*taosSendData[])(uint32_t ip, uint16_t port, void *data, int len, void *chandle) = { - taosSendUdpData, taosSendUdpData, taosSendTcpData, taosSendTcpData}; - -void *(*taosOpenConn[])(void *shandle, void *thandle, uint32_t ip, uint16_t port) = { - taosOpenUdpConnection, - taosOpenUdpConnection, - NULL, - taosOpenTcpClientConnection, -}; - -void (*taosCloseConn[])(void *chandle) = {NULL, NULL, taosCloseTcpConnection, taosCloseTcpConnection}; - -static SRpcConn *rpcOpenConn(SRpcInfo *pRpc, char *peerFqdn, uint16_t peerPort, int8_t connType); -static void rpcCloseConn(void *thandle); -static SRpcConn *rpcSetupConnToServer(SRpcReqContext *pContext); -static SRpcConn *rpcAllocateClientConn(SRpcInfo *pRpc); -static SRpcConn *rpcAllocateServerConn(SRpcInfo *pRpc, SRecvInfo *pRecv); -static SRpcConn *rpcGetConnObj(SRpcInfo *pRpc, int sid, SRecvInfo *pRecv); - -static void rpcSendReqToServer(SRpcInfo *pRpc, SRpcReqContext *pContext); -static void rpcSendQuickRsp(SRpcConn *pConn, int32_t code); -static void rpcSendErrorMsgToPeer(SRecvInfo *pRecv, int32_t code); -static void rpcSendMsgToPeer(SRpcConn *pConn, void *data, int dataLen); -static void rpcSendReqHead(SRpcConn *pConn); - -static void *rpcProcessMsgFromPeer(SRecvInfo *pRecv); -static void rpcProcessIncomingMsg(SRpcConn *pConn, SRpcHead *pHead, SRpcReqContext *pContext); -static void rpcProcessConnError(void *param, void *id); -static void rpcProcessRetryTimer(void *, void *); -static void rpcProcessIdleTimer(void *param, void *tmrId); -static void rpcProcessProgressTimer(void *param, void *tmrId); - -static void rpcFreeMsg(void *msg); -static int32_t rpcCompressRpcMsg(char *pCont, int32_t contLen); -static SRpcHead *rpcDecompressRpcMsg(SRpcHead *pHead); -static int rpcAddAuthPart(SRpcConn *pConn, char *msg, int msgLen); -static int rpcCheckAuthentication(SRpcConn *pConn, char *msg, int msgLen); -static void rpcLockConn(SRpcConn *pConn); -static void rpcUnlockConn(SRpcConn *pConn); -static void rpcAddRef(SRpcInfo *pRpc); -static void rpcDecRef(SRpcInfo *pRpc); - -static void rpcFree(void *p) { - tTrace("free mem: %p", p); - taosMemoryFree(p); -} - -static void rpcInitImp(void) { - tsProgressTimer = tsRpcTimer / 2; - tsRpcMaxRetry = tsRpcMaxTime * 1000 / tsProgressTimer; - tsRpcHeadSize = RPC_MSG_OVERHEAD; - tsRpcOverhead = sizeof(SRpcReqContext); - - tsRpcRefId = taosOpenRef(200, rpcFree); - - tsFqdnHash = taosHashInit(64, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY), false, HASH_ENTRY_LOCK); -} - -int32_t rpcInit() { - taosThreadOnce(&tsRpcInitOnce, rpcInitImp); - return 0; -} - -void rpcCleanup(void) { - taosCloseRef(tsRpcRefId); - taosHashClear(tsFqdnHash); - taosHashCleanup(tsFqdnHash); - tsFqdnHash = NULL; - tsRpcRefId = -1; -} - -void *rpcOpen(const SRpcInit *pInit) { - SRpcInfo *pRpc; - - // taosThreadOnce(&tsRpcInit, rpcInit); - - pRpc = (SRpcInfo *)taosMemoryCalloc(1, sizeof(SRpcInfo)); - if (pRpc == NULL) return NULL; - - if (pInit->label) tstrncpy(pRpc->label, pInit->label, tListLen(pInit->label)); - - pRpc->connType = pInit->connType; - if (pRpc->connType == TAOS_CONN_CLIENT) { - pRpc->numOfThreads = pInit->numOfThreads; - if (pRpc->numOfThreads >= 10) { - pRpc->numOfThreads = 10; - } - } else { - pRpc->numOfThreads = pInit->numOfThreads > TSDB_MAX_RPC_THREADS ? TSDB_MAX_RPC_THREADS : pInit->numOfThreads; - } - pRpc->idleTime = pInit->idleTime; - pRpc->localPort = pInit->localPort; - pRpc->afp = pInit->afp; - pRpc->sessions = pInit->sessions + 1; - if (pInit->user) tstrncpy(pRpc->user, pInit->user, sizeof(pRpc->user)); - if (pInit->secret) memcpy(pRpc->secret, pInit->secret, sizeof(pRpc->secret)); - if (pInit->ckey) tstrncpy(pRpc->ckey, pInit->ckey, sizeof(pRpc->ckey)); - pRpc->spi = pInit->spi; - pRpc->cfp = pInit->cfp; - pRpc->afp = pInit->afp; - pRpc->parent = pInit->parent; - pRpc->refCount = 1; - - atomic_add_fetch_32(&tsRpcNum, 1); - - size_t size = sizeof(SRpcConn) * pRpc->sessions; - pRpc->connList = (SRpcConn *)taosMemoryCalloc(1, size); - if (pRpc->connList == NULL) { - tError("%s failed to allocate memory for taos connections, size:%" PRId64, pRpc->label, (int64_t)size); - rpcClose(pRpc); - return NULL; - } - - pRpc->idPool = taosInitIdPool(pRpc->sessions - 1); - if (pRpc->idPool == NULL) { - tError("%s failed to init ID pool", pRpc->label); - rpcClose(pRpc); - return NULL; - } - - pRpc->tmrCtrl = taosTmrInit(pRpc->sessions * 2 + 1, 50, 10000, pRpc->label); - if (pRpc->tmrCtrl == NULL) { - tError("%s failed to init timers", pRpc->label); - rpcClose(pRpc); - return NULL; - } - - if (pRpc->connType == TAOS_CONN_SERVER) { - pRpc->hash = taosHashInit(pRpc->sessions, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY), true, true); - if (pRpc->hash == NULL) { - tError("%s failed to init string hash", pRpc->label); - rpcClose(pRpc); - return NULL; - } - } else { - pRpc->pCache = rpcOpenConnCache(pRpc->sessions, rpcCloseConn, pRpc->tmrCtrl, pRpc->idleTime * 20); - if (pRpc->pCache == NULL) { - tError("%s failed to init connection cache", pRpc->label); - rpcClose(pRpc); - return NULL; - } - } - - taosThreadMutexInit(&pRpc->mutex, NULL); - - pRpc->tcphandle = (*taosInitConn[pRpc->connType | RPC_CONN_TCP])(0, pRpc->localPort, pRpc->label, pRpc->numOfThreads, - rpcProcessMsgFromPeer, pRpc); - pRpc->udphandle = - (*taosInitConn[pRpc->connType])(0, pRpc->localPort, pRpc->label, pRpc->numOfThreads, rpcProcessMsgFromPeer, pRpc); - - if (pRpc->tcphandle == NULL || pRpc->udphandle == NULL) { - tError("%s failed to init network, port:%d", pRpc->label, pRpc->localPort); - rpcClose(pRpc); - return NULL; - } - - tDebug("%s rpc is opened, threads:%d sessions:%d", pRpc->label, pRpc->numOfThreads, pInit->sessions); - - return pRpc; -} - -void rpcClose(void *param) { - SRpcInfo *pRpc = (SRpcInfo *)param; - - // stop connection to outside first - (*taosStopConn[pRpc->connType | RPC_CONN_TCP])(pRpc->tcphandle); - (*taosStopConn[pRpc->connType])(pRpc->udphandle); - - // close all connections - for (int i = 0; i < pRpc->sessions; ++i) { - if (pRpc->connList && pRpc->connList[i].user[0]) { - rpcCloseConn((void *)(pRpc->connList + i)); - } - } - - // clean up - (*taosCleanUpConn[pRpc->connType | RPC_CONN_TCP])(pRpc->tcphandle); - (*taosCleanUpConn[pRpc->connType])(pRpc->udphandle); - - tDebug("%s rpc is closed", pRpc->label); - rpcDecRef(pRpc); -} - -void *rpcMallocCont(int contLen) { - int size = contLen + RPC_MSG_OVERHEAD; - - char *start = (char *)taosMemoryCalloc(1, (size_t)size); - if (start == NULL) { - tError("failed to malloc msg, size:%d", size); - return NULL; - } else { - tTrace("malloc mem:%p size:%d", start, size); - } - - return start + sizeof(SRpcReqContext) + sizeof(SRpcHead); -} - -void rpcFreeCont(void *cont) { - if (cont) { - char *temp = ((char *)cont) - sizeof(SRpcHead) - sizeof(SRpcReqContext); - taosMemoryFree(temp); - tTrace("free mem: %p", temp); - } -} - -void *rpcReallocCont(void *ptr, int contLen) { - if (ptr == NULL) return rpcMallocCont(contLen); - - char *start = ((char *)ptr) - sizeof(SRpcReqContext) - sizeof(SRpcHead); - if (contLen == 0) { - taosMemoryFree(start); - return NULL; - } - - int size = contLen + RPC_MSG_OVERHEAD; - start = taosMemoryRealloc(start, size); - if (start == NULL) { - tError("failed to realloc cont, size:%d", size); - return NULL; - } - - return start + sizeof(SRpcReqContext) + sizeof(SRpcHead); -} - -void rpcSendRequest(void *shandle, const SEpSet *pEpSet, SRpcMsg *pMsg, int64_t *pRid) { - SRpcInfo * pRpc = (SRpcInfo *)shandle; - SRpcReqContext *pContext; - - int contLen = rpcCompressRpcMsg(pMsg->pCont, pMsg->contLen); - pContext = (SRpcReqContext *)((char *)pMsg->pCont - sizeof(SRpcHead) - sizeof(SRpcReqContext)); - pContext->ahandle = pMsg->ahandle; - pContext->pRpc = (SRpcInfo *)shandle; - pContext->epSet = *pEpSet; - pContext->contLen = contLen; - pContext->pCont = pMsg->pCont; - pContext->msgType = pMsg->msgType; - pContext->oldInUse = pEpSet->inUse; - - pContext->connType = RPC_CONN_UDPC; - if (contLen > tsRpcMaxUdpSize || tsRpcForceTcp) pContext->connType = RPC_CONN_TCPC; - - // connection type is application specific. - // for TDengine, all the query, show commands shall have TCP connection - tmsg_t type = pMsg->msgType; - if (type == TDMT_VND_QUERY || type == TDMT_MND_SHOW_RETRIEVE || type == TDMT_VND_FETCH || - type == TDMT_MND_VGROUP_LIST || type == TDMT_VND_TABLES_META || type == TDMT_VND_TABLE_META || - type == TDMT_MND_SHOW || type == TDMT_MND_STATUS || type == TDMT_VND_ALTER_TABLE) - pContext->connType = RPC_CONN_TCPC; - - pContext->rid = taosAddRef(tsRpcRefId, pContext); - if (pRid) *pRid = pContext->rid; - - rpcSendReqToServer(pRpc, pContext); -} - -void rpcSendResponse(const SRpcMsg *pRsp) { - ASSERT(pRsp->handle != NULL); - - int msgLen = 0; - SRpcConn *pConn = (SRpcConn *)pRsp->handle; - SRpcMsg rpcMsg = *pRsp; - SRpcMsg * pMsg = &rpcMsg; - SRpcInfo *pRpc = pConn->pRpc; - - if (pMsg->pCont == NULL) { - pMsg->pCont = rpcMallocCont(0); - pMsg->contLen = 0; - } - - SRpcHead *pHead = rpcHeadFromCont(pMsg->pCont); - char * msg = (char *)pHead; - - pMsg->contLen = rpcCompressRpcMsg(pMsg->pCont, pMsg->contLen); - msgLen = rpcMsgLenFromCont(pMsg->contLen); - - rpcLockConn(pConn); - - if (pConn->inType == 0 || pConn->user[0] == 0) { - tError("%s, connection is already released, rsp wont be sent", pConn->info); - rpcUnlockConn(pConn); - rpcFreeCont(pMsg->pCont); - rpcDecRef(pRpc); - return; - } - - // set msg header - pHead->version = 1; - pHead->msgType = pConn->inType + 1; - pHead->spi = pConn->spi; - pHead->encrypt = pConn->encrypt; - pHead->tranId = pConn->inTranId; - pHead->sourceId = pConn->ownId; - pHead->destId = pConn->peerId; - pHead->linkUid = pConn->linkUid; - pHead->port = htons(pConn->localPort); - pHead->code = htonl(pMsg->code); - pHead->ahandle = (uint64_t)pConn->ahandle; - - // set pConn parameters - pConn->inType = 0; - - // response message is released until new response is sent - rpcFreeMsg(pConn->pRspMsg); - pConn->pRspMsg = msg; - pConn->rspMsgLen = msgLen; - if (pMsg->code == TSDB_CODE_RPC_ACTION_IN_PROGRESS) pConn->inTranId--; - - // stop the progress timer - taosTmrStopA(&pConn->pTimer); - - // set the idle timer to monitor the activity - taosTmrReset(rpcProcessIdleTimer, pRpc->idleTime * 30, pConn, pRpc->tmrCtrl, &pConn->pIdleTimer); - rpcSendMsgToPeer(pConn, msg, msgLen); - - // if not set to secured, set it expcet NOT_READY case, since client wont treat it as secured - if (pConn->secured == 0 && pMsg->code != TSDB_CODE_RPC_NOT_READY) pConn->secured = 1; // connection shall be secured - - if (pConn->pReqMsg) rpcFreeCont(pConn->pReqMsg); - pConn->pReqMsg = NULL; - pConn->reqMsgLen = 0; - - rpcUnlockConn(pConn); - rpcDecRef(pRpc); // decrease the referene count - - return; -} - -void rpcSendRedirectRsp(void *thandle, const SEpSet *pEpSet) { - SRpcMsg rpcMsg; - memset(&rpcMsg, 0, sizeof(rpcMsg)); - - rpcMsg.contLen = sizeof(SEpSet); - rpcMsg.pCont = rpcMallocCont(rpcMsg.contLen); - if (rpcMsg.pCont == NULL) return; - - memcpy(rpcMsg.pCont, pEpSet, sizeof(SEpSet)); - - rpcMsg.code = TSDB_CODE_RPC_REDIRECT; - rpcMsg.handle = thandle; - - rpcSendResponse(&rpcMsg); - - return; -} - -int rpcGetConnInfo(void *thandle, SRpcConnInfo *pInfo) { - SRpcConn *pConn = (SRpcConn *)thandle; - if (pConn->user[0] == 0) return -1; - - pInfo->clientIp = pConn->peerIp; - pInfo->clientPort = pConn->peerPort; - // pInfo->serverIp = pConn->destIp; - - tstrncpy(pInfo->user, pConn->user, sizeof(pInfo->user)); - return 0; -} - -void rpcSendRecv(void *shandle, SEpSet *pEpSet, SRpcMsg *pMsg, SRpcMsg *pRsp) { - SRpcReqContext *pContext; - pContext = (SRpcReqContext *)((char *)pMsg->pCont - sizeof(SRpcHead) - sizeof(SRpcReqContext)); - - memset(pRsp, 0, sizeof(SRpcMsg)); - - tsem_t sem; - tsem_init(&sem, 0, 0); - pContext->pSem = &sem; - pContext->pRsp = pRsp; - pContext->pSet = pEpSet; - - rpcSendRequest(shandle, pEpSet, pMsg, NULL); - - tsem_wait(&sem); - tsem_destroy(&sem); - - return; -} - -// this API is used by server app to keep an APP context in case connection is broken -int rpcReportProgress(void *handle, char *pCont, int contLen) { - SRpcConn *pConn = (SRpcConn *)handle; - int code = 0; - - rpcLockConn(pConn); - - if (pConn->user[0]) { - // pReqMsg and reqMsgLen is re-used to store the context from app server - pConn->pReqMsg = pCont; - pConn->reqMsgLen = contLen; - } else { - tDebug("%s, rpc connection is already released", pConn->info); - rpcFreeCont(pCont); - code = -1; - } - - rpcUnlockConn(pConn); - return code; -} - -void rpcCancelRequest(int64_t rid) { - SRpcReqContext *pContext = taosAcquireRef(tsRpcRefId, rid); - if (pContext == NULL) return; - - rpcCloseConn(pContext->pConn); - - taosReleaseRef(tsRpcRefId, rid); -} - -static void rpcFreeMsg(void *msg) { - if (msg) { - char *temp = (char *)msg - sizeof(SRpcReqContext); - taosMemoryFree(temp); - tTrace("free mem: %p", temp); - } -} - -static SRpcConn *rpcOpenConn(SRpcInfo *pRpc, char *peerFqdn, uint16_t peerPort, int8_t connType) { - SRpcConn *pConn; - - uint32_t peerIp = 0; - uint32_t *pPeerIp = taosHashGet(tsFqdnHash, peerFqdn, strlen(peerFqdn) + 1); - if (pPeerIp != NULL) { - peerIp = *pPeerIp; - } else { - peerIp = taosGetIpv4FromFqdn(peerFqdn); - if (peerIp != 0xFFFFFFFF) { - taosHashPut(tsFqdnHash, peerFqdn, strlen(peerFqdn) + 1, &peerIp, sizeof(peerIp)); - } - } - - if (peerIp == 0xFFFFFFFF) { - tError("%s, failed to resolve FQDN:%s", pRpc->label, peerFqdn); - terrno = TSDB_CODE_RPC_FQDN_ERROR; - return NULL; - } - - pConn = rpcAllocateClientConn(pRpc); - - if (pConn) { - tstrncpy(pConn->peerFqdn, peerFqdn, sizeof(pConn->peerFqdn)); - pConn->peerIp = peerIp; - pConn->peerPort = peerPort; - tstrncpy(pConn->user, pRpc->user, sizeof(pConn->user)); - pConn->connType = connType; - - if (taosOpenConn[connType]) { - void *shandle = (connType & RPC_CONN_TCP) ? pRpc->tcphandle : pRpc->udphandle; - pConn->chandle = (*taosOpenConn[connType])(shandle, pConn, pConn->peerIp, pConn->peerPort); - if (pConn->chandle == NULL) { - tError("failed to connect to:%s:%d", taosIpStr(pConn->peerIp), pConn->peerPort); - - terrno = TSDB_CODE_RPC_NETWORK_UNAVAIL; - rpcCloseConn(pConn); - pConn = NULL; - } - } - } - - return pConn; -} - -static void rpcReleaseConn(SRpcConn *pConn) { - SRpcInfo *pRpc = pConn->pRpc; - if (pConn->user[0] == 0) return; - - pConn->user[0] = 0; - if (taosCloseConn[pConn->connType]) (*taosCloseConn[pConn->connType])(pConn->chandle); - - taosTmrStopA(&pConn->pTimer); - taosTmrStopA(&pConn->pIdleTimer); - - if (pRpc->connType == TAOS_CONN_SERVER) { - char hashstr[40] = {0}; - size_t size = snprintf(hashstr, sizeof(hashstr), "%x:%x:%x:%d", pConn->peerIp, pConn->linkUid, pConn->peerId, - pConn->connType); - taosHashRemove(pRpc->hash, hashstr, size); - rpcFreeMsg(pConn->pRspMsg); // it may have a response msg saved, but not request msg - pConn->pRspMsg = NULL; - - // if server has ever reported progress, free content - if (pConn->pReqMsg) rpcFreeCont(pConn->pReqMsg); // do not use rpcFreeMsg - } else { - // if there is an outgoing message, free it - if (pConn->outType && pConn->pReqMsg) { - SRpcReqContext *pContext = pConn->pContext; - if (pContext) { - if (pContext->pRsp) { - // for synchronous API, post semaphore to unblock app - pContext->pRsp->code = TSDB_CODE_RPC_APP_ERROR; - pContext->pRsp->pCont = NULL; - pContext->pRsp->contLen = 0; - tsem_post(pContext->pSem); - } - pContext->pConn = NULL; - taosRemoveRef(tsRpcRefId, pContext->rid); - } else { - assert(0); - } - } - } - - // memset could not be used, since lockeBy can not be reset - pConn->inType = 0; - pConn->outType = 0; - pConn->inTranId = 0; - pConn->outTranId = 0; - pConn->secured = 0; - pConn->peerId = 0; - pConn->peerIp = 0; - pConn->peerPort = 0; - pConn->pReqMsg = NULL; - pConn->reqMsgLen = 0; - pConn->pContext = NULL; - pConn->chandle = NULL; - - taosFreeId(pRpc->idPool, pConn->sid); - tDebug("%s, rpc connection is released", pConn->info); -} - -static void rpcCloseConn(void *thandle) { - SRpcConn *pConn = (SRpcConn *)thandle; - if (pConn == NULL) return; - - rpcLockConn(pConn); - - if (pConn->user[0]) rpcReleaseConn(pConn); - - rpcUnlockConn(pConn); -} - -static SRpcConn *rpcAllocateClientConn(SRpcInfo *pRpc) { - SRpcConn *pConn = NULL; - - int sid = taosAllocateId(pRpc->idPool); - if (sid <= 0) { - tError("%s maximum number of sessions:%d is reached", pRpc->label, pRpc->sessions); - terrno = TSDB_CODE_RPC_MAX_SESSIONS; - } else { - pConn = pRpc->connList + sid; - - pConn->pRpc = pRpc; - pConn->sid = sid; - pConn->tranId = (uint16_t)(taosRand() & 0xFFFF); - pConn->ownId = htonl(pConn->sid); - pConn->linkUid = (uint32_t)((int64_t)pConn + taosGetPId() + (int64_t)pConn->tranId); - pConn->spi = pRpc->spi; - pConn->encrypt = pRpc->encrypt; - if (pConn->spi) memcpy(pConn->secret, pRpc->secret, TSDB_PASSWORD_LEN); - tDebug("%s %p client connection is allocated, uid:0x%x", pRpc->label, pConn, pConn->linkUid); - } - - return pConn; -} - -static SRpcConn *rpcAllocateServerConn(SRpcInfo *pRpc, SRecvInfo *pRecv) { - SRpcConn *pConn = NULL; - char hashstr[40] = {0}; - SRpcHead *pHead = (SRpcHead *)pRecv->msg; - - size_t size = - snprintf(hashstr, sizeof(hashstr), "%x:%x:%x:%d", pRecv->ip, pHead->linkUid, pHead->sourceId, pRecv->connType); - - // check if it is already allocated - SRpcConn **ppConn = (SRpcConn **)(taosHashGet(pRpc->hash, hashstr, size)); - if (ppConn) pConn = *ppConn; - if (pConn) { - pConn->secured = 0; - return pConn; - } - - // if code is not 0, it means it is simple reqhead, just ignore - if (pHead->code != 0) { - terrno = TSDB_CODE_RPC_ALREADY_PROCESSED; - return NULL; - } - - int sid = taosAllocateId(pRpc->idPool); - if (sid <= 0) { - tError("%s maximum number of sessions:%d is reached", pRpc->label, pRpc->sessions); - terrno = TSDB_CODE_RPC_MAX_SESSIONS; - } else { - pConn = pRpc->connList + sid; - memcpy(pConn->user, pHead->user, tListLen(pConn->user)); - pConn->pRpc = pRpc; - pConn->sid = sid; - pConn->tranId = (uint16_t)(taosRand() & 0xFFFF); - pConn->ownId = htonl(pConn->sid); - pConn->linkUid = pHead->linkUid; - if (pRpc->afp) { - if (pConn->user[0] == 0) { - terrno = TSDB_CODE_RPC_AUTH_REQUIRED; - } else { - terrno = (*pRpc->afp)(pRpc->parent, pConn->user, &pConn->spi, &pConn->encrypt, pConn->secret, pConn->ckey); - } - - if (terrno != 0) { - taosFreeId(pRpc->idPool, sid); // sid shall be released - pConn = NULL; - } - } - } - - if (pConn) { - if (pRecv->connType == RPC_CONN_UDPS && pRpc->numOfThreads > 1) { - // UDP server, assign to new connection - pRpc->index = (pRpc->index + 1) % pRpc->numOfThreads; - pConn->localPort = (pRpc->localPort + pRpc->index); - } - - taosHashPut(pRpc->hash, hashstr, size, (char *)&pConn, POINTER_BYTES); - tDebug("%s %p server connection is allocated, uid:0x%x sid:%d key:%s spi:%d", pRpc->label, pConn, pConn->linkUid, - sid, hashstr, pConn->spi); - } - - return pConn; -} - -static SRpcConn *rpcGetConnObj(SRpcInfo *pRpc, int sid, SRecvInfo *pRecv) { - SRpcConn *pConn = NULL; - SRpcHead *pHead = (SRpcHead *)pRecv->msg; - - if (sid) { - pConn = pRpc->connList + sid; - if (pConn->user[0] == 0) pConn = NULL; - } - - if (pConn == NULL) { - if (pRpc->connType == TAOS_CONN_SERVER) { - pConn = rpcAllocateServerConn(pRpc, pRecv); - } else { - terrno = TSDB_CODE_RPC_UNEXPECTED_RESPONSE; - } - } - - if (pConn) { - if (pConn->linkUid != pHead->linkUid) { - terrno = TSDB_CODE_RPC_MISMATCHED_LINK_ID; - tDebug("%s %p %p, linkUid:0x%x is not matched with received:0x%x", pRpc->label, pConn, (void *)pHead->ahandle, - pConn->linkUid, pHead->linkUid); - pConn = NULL; - } - } - - return pConn; -} - -static SRpcConn *rpcSetupConnToServer(SRpcReqContext *pContext) { - SRpcConn *pConn; - SRpcInfo *pRpc = pContext->pRpc; - SEpSet * pEpSet = &pContext->epSet; - - pConn = rpcGetConnFromCache(pRpc->pCache, pEpSet->eps[pEpSet->inUse].fqdn, pEpSet->eps[pEpSet->inUse].port, - pContext->connType); - if (pConn == NULL || pConn->user[0] == 0) { - pConn = rpcOpenConn(pRpc, pEpSet->eps[pEpSet->inUse].fqdn, pEpSet->eps[pEpSet->inUse].port, pContext->connType); - } - - if (pConn) { - pConn->tretry = 0; - pConn->ahandle = pContext->ahandle; - snprintf(pConn->info, sizeof(pConn->info), "%s %p %p", pRpc->label, pConn, pConn->ahandle); - pConn->tretry = 0; - } else { - tError("%s %p, failed to set up connection(%s)", pRpc->label, pContext->ahandle, tstrerror(terrno)); - } - - return pConn; -} - -static int rpcProcessReqHead(SRpcConn *pConn, SRpcHead *pHead) { - if (pConn->peerId == 0) { - pConn->peerId = pHead->sourceId; - } else { - if (pConn->peerId != pHead->sourceId) { - tDebug("%s, source Id is changed, old:0x%08x new:0x%08x", pConn->info, pConn->peerId, pHead->sourceId); - return TSDB_CODE_RPC_INVALID_VALUE; - } - } - - if (pConn->inTranId == pHead->tranId) { - if (pConn->inType == pHead->msgType) { - if (pHead->code == 0) { - tDebug("%s, %s is retransmitted", pConn->info, TMSG_INFO(pHead->msgType)); - rpcSendQuickRsp(pConn, TSDB_CODE_RPC_ACTION_IN_PROGRESS); - } else { - // do nothing, it is heart beat from client - } - } else if (pConn->inType == 0) { - tDebug("%s, %s is already processed, tranId:%d", pConn->info, TMSG_INFO(pHead->msgType), pConn->inTranId); - rpcSendMsgToPeer(pConn, pConn->pRspMsg, pConn->rspMsgLen); // resend the response - } else { - tDebug("%s, mismatched message %s and tranId", pConn->info, TMSG_INFO(pHead->msgType)); - } - - // do not reply any message - return TSDB_CODE_RPC_ALREADY_PROCESSED; - } - - if (pConn->inType != 0) { - tDebug("%s, last session is not finished, inTranId:%d tranId:%d", pConn->info, pConn->inTranId, pHead->tranId); - return TSDB_CODE_RPC_LAST_SESSION_NOT_FINISHED; - } - - if (rpcContLenFromMsg(pHead->msgLen) <= 0) { - tDebug("%s, message body is empty, ignore", pConn->info); - return TSDB_CODE_RPC_APP_ERROR; - } - - pConn->inTranId = pHead->tranId; - pConn->inType = pHead->msgType; - - // start the progress timer to monitor the response from server app - if (pConn->connType != RPC_CONN_TCPS) - pConn->pTimer = taosTmrStart(rpcProcessProgressTimer, tsProgressTimer, pConn, pConn->pRpc->tmrCtrl); - - return 0; -} - -static int rpcProcessRspHead(SRpcConn *pConn, SRpcHead *pHead) { - SRpcInfo *pRpc = pConn->pRpc; - pConn->peerId = pHead->sourceId; - - if (pConn->outType == 0 || pConn->pContext == NULL) { - return TSDB_CODE_RPC_UNEXPECTED_RESPONSE; - } - - if (pHead->tranId != pConn->outTranId) { - return TSDB_CODE_RPC_INVALID_TRAN_ID; - } - - if (pHead->msgType != pConn->outType + 1) { - return TSDB_CODE_RPC_INVALID_RESPONSE_TYPE; - } - - taosTmrStopA(&pConn->pTimer); - pConn->retry = 0; - - if (pHead->code == TSDB_CODE_RPC_AUTH_REQUIRED && pRpc->spi) { - tDebug("%s, authentication shall be restarted", pConn->info); - pConn->secured = 0; - rpcSendMsgToPeer(pConn, pConn->pReqMsg, pConn->reqMsgLen); - if (pConn->connType != RPC_CONN_TCPC) - pConn->pTimer = taosTmrStart(rpcProcessRetryTimer, tsRpcTimer, pConn, pRpc->tmrCtrl); - return TSDB_CODE_RPC_ALREADY_PROCESSED; - } - - if (pHead->code == TSDB_CODE_RPC_MISMATCHED_LINK_ID) { - tDebug("%s, mismatched linkUid, link shall be restarted", pConn->info); - pConn->secured = 0; - ((SRpcHead *)pConn->pReqMsg)->destId = 0; - rpcSendMsgToPeer(pConn, pConn->pReqMsg, pConn->reqMsgLen); - if (pConn->connType != RPC_CONN_TCPC) - pConn->pTimer = taosTmrStart(rpcProcessRetryTimer, tsRpcTimer, pConn, pRpc->tmrCtrl); - return TSDB_CODE_RPC_ALREADY_PROCESSED; - } - - if (pHead->code == TSDB_CODE_RPC_ACTION_IN_PROGRESS) { - if (pConn->tretry <= tsRpcMaxRetry) { - tDebug("%s, peer is still processing the transaction, retry:%d", pConn->info, pConn->tretry); - pConn->tretry++; - rpcSendReqHead(pConn); - if (pConn->connType != RPC_CONN_TCPC) - pConn->pTimer = taosTmrStart(rpcProcessRetryTimer, tsRpcTimer, pConn, pRpc->tmrCtrl); - return TSDB_CODE_RPC_ALREADY_PROCESSED; - } else { - // peer still in processing, give up - tDebug("%s, server processing takes too long time, give up", pConn->info); - pHead->code = TSDB_CODE_RPC_TOO_SLOW; - } - } - - pConn->outType = 0; - pConn->pReqMsg = NULL; - pConn->reqMsgLen = 0; - SRpcReqContext *pContext = pConn->pContext; - - if (pHead->code == TSDB_CODE_RPC_REDIRECT) { - if (rpcContLenFromMsg(pHead->msgLen) < sizeof(SEpSet)) { - // if EpSet is not included in the msg, treat it as NOT_READY - pHead->code = TSDB_CODE_RPC_NOT_READY; - } else { - pContext->redirect++; - if (pContext->redirect > TSDB_MAX_REPLICA) { - pHead->code = TSDB_CODE_RPC_NETWORK_UNAVAIL; - tWarn("%s, too many redirects, quit", pConn->info); - } - } - } - - return TSDB_CODE_SUCCESS; -} - -static SRpcConn *rpcProcessMsgHead(SRpcInfo *pRpc, SRecvInfo *pRecv, SRpcReqContext **ppContext) { - int32_t sid; - SRpcConn *pConn = NULL; - - SRpcHead *pHead = (SRpcHead *)pRecv->msg; - - sid = htonl(pHead->destId); - *ppContext = NULL; - - if (TMSG_INDEX(pHead->msgType) >= TDMT_MAX || TMSG_INDEX(pHead->msgType) <= 0) { - tDebug("%s sid:%d, invalid message type:%d", pRpc->label, sid, pHead->msgType); - terrno = TSDB_CODE_RPC_INVALID_MSG_TYPE; - return NULL; - } - - if (sid < 0 || sid >= pRpc->sessions) { - tDebug("%s sid:%d, sid is out of range, max sid:%d, %s discarded", pRpc->label, sid, pRpc->sessions, - TMSG_INFO(pHead->msgType)); - terrno = TSDB_CODE_RPC_INVALID_SESSION_ID; - return NULL; - } - - if (rpcIsReq(pHead->msgType) && htonl(pHead->msgVer) != tsVersion >> 8) { - tDebug("%s sid:%d, invalid client version:%x/%x %s", pRpc->label, sid, htonl(pHead->msgVer), tsVersion, - TMSG_INFO(pHead->msgType)); - terrno = TSDB_CODE_RPC_INVALID_VERSION; - return NULL; - } - - pConn = rpcGetConnObj(pRpc, sid, pRecv); - if (pConn == NULL) { - tDebug("%s %p, failed to get connection obj(%s)", pRpc->label, (void *)pHead->ahandle, tstrerror(terrno)); - return NULL; - } - - rpcLockConn(pConn); - - if (rpcIsReq(pHead->msgType)) { - pConn->ahandle = (void *)pHead->ahandle; - snprintf(pConn->info, sizeof(pConn->info), "%s %p %p", pRpc->label, pConn, pConn->ahandle); - } - - sid = pConn->sid; - if (pConn->chandle == NULL) pConn->chandle = pRecv->chandle; - pConn->peerIp = pRecv->ip; - pConn->peerPort = pRecv->port; - if (pHead->port) pConn->peerPort = htons(pHead->port); - - terrno = rpcCheckAuthentication(pConn, (char *)pHead, pRecv->msgLen); - - // code can be transformed only after authentication - pHead->code = htonl(pHead->code); - - if (terrno == 0) { - if (pHead->encrypt) { - // decrypt here - } - - if (rpcIsReq(pHead->msgType)) { - pConn->connType = pRecv->connType; - terrno = rpcProcessReqHead(pConn, pHead); - - // stop idle timer - taosTmrStopA(&pConn->pIdleTimer); - - // client shall send the request within tsRpcTime again for UDP, double it - if (pConn->connType != RPC_CONN_TCPS) - pConn->pIdleTimer = taosTmrStart(rpcProcessIdleTimer, tsRpcTimer * 2, pConn, pRpc->tmrCtrl); - } else { - terrno = rpcProcessRspHead(pConn, pHead); - *ppContext = pConn->pContext; - } - } - - rpcUnlockConn(pConn); - - return pConn; -} - -static void doRpcReportBrokenLinkToServer(void *param, void *id) { - SRpcMsg * pRpcMsg = (SRpcMsg *)(param); - SRpcConn *pConn = (SRpcConn *)(pRpcMsg->handle); - SRpcInfo *pRpc = pConn->pRpc; - (*(pRpc->cfp))(pRpc->parent, pRpcMsg, NULL); - taosMemoryFree(pRpcMsg); -} -static void rpcReportBrokenLinkToServer(SRpcConn *pConn) { - SRpcInfo *pRpc = pConn->pRpc; - if (pConn->pReqMsg == NULL) return; - - // if there are pending request, notify the app - rpcAddRef(pRpc); - tDebug("%s, notify the server app, connection is gone", pConn->info); - - SRpcMsg *rpcMsg = taosMemoryMalloc(sizeof(SRpcMsg)); - rpcMsg->pCont = pConn->pReqMsg; // pReqMsg is re-used to store the APP context from server - rpcMsg->contLen = pConn->reqMsgLen; // reqMsgLen is re-used to store the APP context length - rpcMsg->ahandle = pConn->ahandle; - rpcMsg->handle = pConn; - rpcMsg->msgType = pConn->inType; - rpcMsg->code = TSDB_CODE_RPC_NETWORK_UNAVAIL; - pConn->pReqMsg = NULL; - pConn->reqMsgLen = 0; - if (pRpc->cfp) { - taosTmrStart(doRpcReportBrokenLinkToServer, 0, rpcMsg, pRpc->tmrCtrl); - } else { - taosMemoryFree(rpcMsg); - } -} - -static void rpcProcessBrokenLink(SRpcConn *pConn) { - if (pConn == NULL) return; - SRpcInfo *pRpc = pConn->pRpc; - tDebug("%s, link is broken", pConn->info); - - rpcLockConn(pConn); - - if (pConn->outType) { - SRpcReqContext *pContext = pConn->pContext; - pContext->code = TSDB_CODE_RPC_NETWORK_UNAVAIL; - pContext->pConn = NULL; - pConn->pReqMsg = NULL; - taosTmrStart(rpcProcessConnError, 0, pContext, pRpc->tmrCtrl); - } - - if (pConn->inType) rpcReportBrokenLinkToServer(pConn); - - rpcReleaseConn(pConn); - rpcUnlockConn(pConn); -} - -static void *rpcProcessMsgFromPeer(SRecvInfo *pRecv) { - SRpcHead *pHead = (SRpcHead *)pRecv->msg; - SRpcInfo *pRpc = (SRpcInfo *)pRecv->shandle; - SRpcConn *pConn = (SRpcConn *)pRecv->thandle; - - tDump(pRecv->msg, pRecv->msgLen); - - // underlying UDP layer does not know it is server or client - pRecv->connType = pRecv->connType | pRpc->connType; - - if (pRecv->msg == NULL) { - rpcProcessBrokenLink(pConn); - return NULL; - } - - terrno = 0; - SRpcReqContext *pContext; - pConn = rpcProcessMsgHead(pRpc, pRecv, &pContext); - - char ipstr[24] = {0}; - taosIpPort2String(pRecv->ip, pRecv->port, ipstr); - - if (TMSG_INDEX(pHead->msgType) >= 1 && TMSG_INDEX(pHead->msgType) < TDMT_MAX) { - tDebug("%s %p %p, %s received from %s, parse code:0x%x len:%d sig:0x%08x:0x%08x:%d code:0x%x", pRpc->label, pConn, - (void *)pHead->ahandle, TMSG_INFO(pHead->msgType), ipstr, terrno, pRecv->msgLen, pHead->sourceId, - pHead->destId, pHead->tranId, pHead->code); - } else { - tDebug("%s %p %p, %d received from %s, parse code:0x%x len:%d sig:0x%08x:0x%08x:%d code:0x%x", pRpc->label, pConn, - (void *)pHead->ahandle, pHead->msgType, ipstr, terrno, pRecv->msgLen, pHead->sourceId, pHead->destId, - pHead->tranId, pHead->code); - } - - int32_t code = terrno; - if (code != TSDB_CODE_RPC_ALREADY_PROCESSED) { - if (code != 0) { // parsing error - if (rpcIsReq(pHead->msgType)) { - rpcSendErrorMsgToPeer(pRecv, code); - if (code == TSDB_CODE_RPC_INVALID_TIME_STAMP || code == TSDB_CODE_RPC_AUTH_FAILURE) { - rpcCloseConn(pConn); - } - if (TMSG_INDEX(pHead->msgType) + 1 > 1 && TMSG_INDEX(pHead->msgType) + 1 < TDMT_MAX) { - tDebug("%s %p %p, %s is sent with error code:0x%x", pRpc->label, pConn, (void *)pHead->ahandle, - TMSG_INFO(pHead->msgType + 1), code); - } else { - tError("%s %p %p, %s is sent with error code:0x%x", pRpc->label, pConn, (void *)pHead->ahandle, - TMSG_INFO(pHead->msgType), code); - } - } - } else { // msg is passed to app only parsing is ok - rpcProcessIncomingMsg(pConn, pHead, pContext); - } - } - - if (code) rpcFreeMsg(pRecv->msg); // parsing failed, msg shall be freed - return pConn; -} - -static void rpcNotifyClient(SRpcReqContext *pContext, SRpcMsg *pMsg) { - SRpcInfo *pRpc = pContext->pRpc; - - pContext->pConn = NULL; - if (pContext->pRsp) { - // for synchronous API - memcpy(pContext->pSet, &pContext->epSet, sizeof(SEpSet)); - memcpy(pContext->pRsp, pMsg, sizeof(SRpcMsg)); - tsem_post(pContext->pSem); - } else { - // for asynchronous API - SEpSet *pEpSet = NULL; - if (pContext->epSet.inUse != pContext->oldInUse || pContext->redirect) pEpSet = &pContext->epSet; - - (*pRpc->cfp)(pRpc->parent, pMsg, pEpSet); - } - - // free the request message - taosRemoveRef(tsRpcRefId, pContext->rid); -} - -static void rpcProcessIncomingMsg(SRpcConn *pConn, SRpcHead *pHead, SRpcReqContext *pContext) { - SRpcInfo *pRpc = pConn->pRpc; - SRpcMsg rpcMsg; - - pHead = rpcDecompressRpcMsg(pHead); - rpcMsg.contLen = rpcContLenFromMsg(pHead->msgLen); - rpcMsg.pCont = pHead->content; - rpcMsg.msgType = pHead->msgType; - rpcMsg.code = pHead->code; - - if (rpcIsReq(pHead->msgType)) { - rpcMsg.ahandle = pConn->ahandle; - rpcMsg.handle = pConn; - rpcAddRef(pRpc); // add the refCount for requests - - // notify the server app - (*(pRpc->cfp))(pRpc->parent, &rpcMsg, NULL); - } else { - // it's a response - rpcMsg.handle = pContext; - rpcMsg.ahandle = pContext->ahandle; - pContext->pConn = NULL; - - // for UDP, port may be changed by server, the port in epSet shall be used for cache - if (pHead->code != TSDB_CODE_RPC_TOO_SLOW) { - rpcAddConnIntoCache(pRpc->pCache, pConn, pConn->peerFqdn, pContext->epSet.eps[pContext->epSet.inUse].port, - pConn->connType); - } else { - rpcCloseConn(pConn); - } - - if (pHead->code == TSDB_CODE_RPC_REDIRECT) { - pContext->numOfTry = 0; - SEpSet *pEpSet = (SEpSet *)pHead->content; - if (pEpSet->numOfEps > 0) { - memcpy(&pContext->epSet, pHead->content, sizeof(pContext->epSet)); - tDebug("%s, redirect is received, numOfEps:%d inUse:%d", pConn->info, pContext->epSet.numOfEps, - pContext->epSet.inUse); - for (int i = 0; i < pContext->epSet.numOfEps; ++i) { - pContext->epSet.eps[i].port = htons(pContext->epSet.eps[i].port); - tDebug("%s, redirect is received, index:%d ep:%s:%u", pConn->info, i, pContext->epSet.eps[i].fqdn, - pContext->epSet.eps[i].port); - } - } - rpcSendReqToServer(pRpc, pContext); - rpcFreeCont(rpcMsg.pCont); - } else if (pHead->code == TSDB_CODE_RPC_NOT_READY || pHead->code == TSDB_CODE_APP_NOT_READY || - pHead->code == TSDB_CODE_NODE_OFFLINE) { - pContext->code = pHead->code; - rpcProcessConnError(pContext, NULL); - rpcFreeCont(rpcMsg.pCont); - } else { - rpcNotifyClient(pContext, &rpcMsg); - } - } -} - -static void rpcSendQuickRsp(SRpcConn *pConn, int32_t code) { - char msg[RPC_MSG_OVERHEAD]; - SRpcHead *pHead; - - // set msg header - memset(msg, 0, sizeof(SRpcHead)); - pHead = (SRpcHead *)msg; - pHead->version = 1; - pHead->msgType = pConn->inType + 1; - pHead->spi = pConn->spi; - pHead->encrypt = 0; - pHead->tranId = pConn->inTranId; - pHead->sourceId = pConn->ownId; - pHead->destId = pConn->peerId; - pHead->linkUid = pConn->linkUid; - pHead->ahandle = (uint64_t)pConn->ahandle; - memcpy(pHead->user, pConn->user, tListLen(pHead->user)); - pHead->code = htonl(code); - - rpcSendMsgToPeer(pConn, msg, sizeof(SRpcHead)); - pConn->secured = 1; // connection shall be secured -} - -static void rpcSendReqHead(SRpcConn *pConn) { - char msg[RPC_MSG_OVERHEAD]; - SRpcHead *pHead; - - // set msg header - memset(msg, 0, sizeof(SRpcHead)); - pHead = (SRpcHead *)msg; - pHead->version = 1; - pHead->msgType = pConn->outType; - pHead->msgVer = htonl(tsVersion >> 8); - pHead->spi = pConn->spi; - pHead->encrypt = 0; - pHead->tranId = pConn->outTranId; - pHead->sourceId = pConn->ownId; - pHead->destId = pConn->peerId; - pHead->linkUid = pConn->linkUid; - pHead->ahandle = (uint64_t)pConn->ahandle; - memcpy(pHead->user, pConn->user, tListLen(pHead->user)); - pHead->code = 1; - - rpcSendMsgToPeer(pConn, msg, sizeof(SRpcHead)); -} - -static void rpcSendErrorMsgToPeer(SRecvInfo *pRecv, int32_t code) { - SRpcHead *pRecvHead, *pReplyHead; - char msg[sizeof(SRpcHead) + sizeof(SRpcDigest) + sizeof(uint32_t)]; - uint32_t timeStamp; - int msgLen; - - pRecvHead = (SRpcHead *)pRecv->msg; - pReplyHead = (SRpcHead *)msg; - - memset(msg, 0, sizeof(SRpcHead)); - pReplyHead->version = pRecvHead->version; - pReplyHead->msgType = (tmsg_t)(pRecvHead->msgType + 1); - pReplyHead->spi = 0; - pReplyHead->encrypt = pRecvHead->encrypt; - pReplyHead->tranId = pRecvHead->tranId; - pReplyHead->sourceId = pRecvHead->destId; - pReplyHead->destId = pRecvHead->sourceId; - pReplyHead->linkUid = pRecvHead->linkUid; - pReplyHead->ahandle = pRecvHead->ahandle; - - pReplyHead->code = htonl(code); - msgLen = sizeof(SRpcHead); - - if (code == TSDB_CODE_RPC_INVALID_TIME_STAMP) { - // include a time stamp if client's time is not synchronized well - uint8_t *pContent = pReplyHead->content; - timeStamp = htonl(taosGetTimestampSec()); - memcpy(pContent, &timeStamp, sizeof(timeStamp)); - msgLen += sizeof(timeStamp); - } - - pReplyHead->msgLen = (int32_t)htonl((uint32_t)msgLen); - (*taosSendData[pRecv->connType])(pRecv->ip, pRecv->port, msg, msgLen, pRecv->chandle); - - return; -} - -static void rpcSendReqToServer(SRpcInfo *pRpc, SRpcReqContext *pContext) { - SRpcHead *pHead = rpcHeadFromCont(pContext->pCont); - char * msg = (char *)pHead; - int msgLen = rpcMsgLenFromCont(pContext->contLen); - tmsg_t msgType = pContext->msgType; - - pContext->numOfTry++; - SRpcConn *pConn = rpcSetupConnToServer(pContext); - if (pConn == NULL) { - pContext->code = terrno; - taosTmrStart(rpcProcessConnError, 1, pContext, pRpc->tmrCtrl); - return; - } - - pContext->pConn = pConn; - pConn->ahandle = pContext->ahandle; - rpcLockConn(pConn); - - // set the message header - pHead->version = 1; - pHead->msgVer = htonl(tsVersion >> 8); - pHead->msgType = msgType; - pHead->encrypt = 0; - pConn->tranId++; - if (pConn->tranId == 0) pConn->tranId++; - pHead->tranId = pConn->tranId; - pHead->sourceId = pConn->ownId; - pHead->destId = pConn->peerId; - pHead->port = 0; - pHead->linkUid = pConn->linkUid; - pHead->ahandle = (uint64_t)pConn->ahandle; - memcpy(pHead->user, pConn->user, tListLen(pHead->user)); - - // set the connection parameters - pConn->outType = msgType; - pConn->outTranId = pHead->tranId; - pConn->pReqMsg = msg; - pConn->reqMsgLen = msgLen; - pConn->pContext = pContext; - - rpcSendMsgToPeer(pConn, msg, msgLen); - if (pConn->connType != RPC_CONN_TCPC) - taosTmrReset(rpcProcessRetryTimer, tsRpcTimer, pConn, pRpc->tmrCtrl, &pConn->pTimer); - - rpcUnlockConn(pConn); -} - -static void rpcSendMsgToPeer(SRpcConn *pConn, void *msg, int msgLen) { - int writtenLen = 0; - SRpcHead *pHead = (SRpcHead *)msg; - - msgLen = rpcAddAuthPart(pConn, msg, msgLen); - - if (rpcIsReq(pHead->msgType)) { - tDebug("%s, %s is sent to %s:%hu, len:%d sig:0x%08x:0x%08x:%d", pConn->info, TMSG_INFO(pHead->msgType), - pConn->peerFqdn, pConn->peerPort, msgLen, pHead->sourceId, pHead->destId, pHead->tranId); - } else { - if (pHead->code == 0) { - pConn->secured = 1; // for success response, set link as secured - } - - char ipport[40] = {0}; - taosIpPort2String(pConn->peerIp, pConn->peerPort, ipport); - tDebug("%s, %s is sent to %s, code:0x%x len:%d sig:0x%08x:0x%08x:%d", pConn->info, TMSG_INFO(pHead->msgType), - ipport, htonl(pHead->code), msgLen, pHead->sourceId, pHead->destId, pHead->tranId); - } - - // tTrace("connection type is: %d", pConn->connType); - writtenLen = (*taosSendData[pConn->connType])(pConn->peerIp, pConn->peerPort, pHead, msgLen, pConn->chandle); - - if (writtenLen != msgLen) { - tError("%s, failed to send, msgLen:%d written:%d, reason:%s", pConn->info, msgLen, writtenLen, strerror(errno)); - } - - tDump(msg, msgLen); -} - -static void rpcProcessConnError(void *param, void *id) { - SRpcReqContext *pContext = (SRpcReqContext *)param; - SRpcInfo * pRpc = pContext->pRpc; - SRpcMsg rpcMsg = {0}; - - if (pRpc == NULL) { - return; - } - - tDebug("%s %p, connection error happens", pRpc->label, pContext->ahandle); - - if (pContext->numOfTry >= pContext->epSet.numOfEps || pContext->msgType == TDMT_VND_FETCH) { - rpcMsg.msgType = pContext->msgType + 1; - rpcMsg.ahandle = pContext->ahandle; - rpcMsg.code = pContext->code; - rpcMsg.pCont = NULL; - rpcMsg.contLen = 0; - - rpcNotifyClient(pContext, &rpcMsg); - } else { - // move to next IP - pContext->epSet.inUse++; - pContext->epSet.inUse = pContext->epSet.inUse % pContext->epSet.numOfEps; - rpcSendReqToServer(pRpc, pContext); - } -} - -static void rpcProcessRetryTimer(void *param, void *tmrId) { - SRpcConn *pConn = (SRpcConn *)param; - SRpcInfo *pRpc = pConn->pRpc; - - rpcLockConn(pConn); - - if (pConn->outType && pConn->user[0]) { - tDebug("%s, expected %s is not received", pConn->info, TMSG_INFO((int)pConn->outType + 1)); - pConn->pTimer = NULL; - pConn->retry++; - - if (pConn->retry < 4) { - tDebug("%s, re-send msg:%s to %s:%hu", pConn->info, TMSG_INFO(pConn->outType), pConn->peerFqdn, pConn->peerPort); - rpcSendMsgToPeer(pConn, pConn->pReqMsg, pConn->reqMsgLen); - pConn->pTimer = taosTmrStart(rpcProcessRetryTimer, tsRpcTimer, pConn, pRpc->tmrCtrl); - } else { - // close the connection - tDebug("%s, failed to send msg:%s to %s:%hu", pConn->info, TMSG_INFO(pConn->outType), pConn->peerFqdn, - pConn->peerPort); - if (pConn->pContext) { - pConn->pContext->code = TSDB_CODE_RPC_NETWORK_UNAVAIL; - pConn->pContext->pConn = NULL; - pConn->pReqMsg = NULL; - taosTmrStart(rpcProcessConnError, 1, pConn->pContext, pRpc->tmrCtrl); - rpcReleaseConn(pConn); - } - } - } else { - tDebug("%s, retry timer not processed", pConn->info); - } - - rpcUnlockConn(pConn); -} - -static void rpcProcessIdleTimer(void *param, void *tmrId) { - SRpcConn *pConn = (SRpcConn *)param; - - rpcLockConn(pConn); - - if (pConn->user[0]) { - tDebug("%s, close the connection since no activity", pConn->info); - if (pConn->inType) rpcReportBrokenLinkToServer(pConn); - rpcReleaseConn(pConn); - } else { - tDebug("%s, idle timer:%p not processed", pConn->info, tmrId); - } - - rpcUnlockConn(pConn); -} - -static void rpcProcessProgressTimer(void *param, void *tmrId) { - SRpcConn *pConn = (SRpcConn *)param; - SRpcInfo *pRpc = pConn->pRpc; - - rpcLockConn(pConn); - - if (pConn->inType && pConn->user[0]) { - tDebug("%s, progress timer expired, send progress", pConn->info); - rpcSendQuickRsp(pConn, TSDB_CODE_RPC_ACTION_IN_PROGRESS); - pConn->pTimer = taosTmrStart(rpcProcessProgressTimer, tsProgressTimer, pConn, pRpc->tmrCtrl); - } else { - tDebug("%s, progress timer:%p not processed", pConn->info, tmrId); - } - - rpcUnlockConn(pConn); -} - -static int32_t rpcCompressRpcMsg(char *pCont, int32_t contLen) { - SRpcHead *pHead = rpcHeadFromCont(pCont); - int32_t finalLen = 0; - int overhead = sizeof(SRpcComp); - - if (!NEEDTO_COMPRESSS_MSG(contLen)) { - return contLen; - } - - char *buf = taosMemoryMalloc(contLen + overhead + 8); // 8 extra bytes - if (buf == NULL) { - tError("failed to allocate memory for rpc msg compression, contLen:%d", contLen); - return contLen; - } - - int32_t compLen = LZ4_compress_default(pCont, buf, contLen, contLen + overhead); - tDebug("compress rpc msg, before:%d, after:%d, overhead:%d", contLen, compLen, overhead); - - /* - * only the compressed size is less than the value of contLen - overhead, the compression is applied - * The first four bytes is set to 0, the second four bytes are utilized to keep the original length of message - */ - if (compLen > 0 && compLen < contLen - overhead) { - SRpcComp *pComp = (SRpcComp *)pCont; - pComp->reserved = 0; - pComp->contLen = htonl(contLen); - memcpy(pCont + overhead, buf, compLen); - - pHead->comp = 1; - tDebug("compress rpc msg, before:%d, after:%d", contLen, compLen); - finalLen = compLen + overhead; - } else { - finalLen = contLen; - } - - taosMemoryFree(buf); - return finalLen; -} - -static SRpcHead *rpcDecompressRpcMsg(SRpcHead *pHead) { - int overhead = sizeof(SRpcComp); - SRpcHead *pNewHead = NULL; - uint8_t * pCont = pHead->content; - SRpcComp *pComp = (SRpcComp *)pHead->content; - - if (pHead->comp) { - // decompress the content - assert(pComp->reserved == 0); - int contLen = htonl(pComp->contLen); - - // prepare the temporary buffer to decompress message - char *temp = (char *)taosMemoryMalloc(contLen + RPC_MSG_OVERHEAD); - pNewHead = (SRpcHead *)(temp + sizeof(SRpcReqContext)); // reserve SRpcReqContext - - if (pNewHead) { - int compLen = rpcContLenFromMsg(pHead->msgLen) - overhead; - int origLen = LZ4_decompress_safe((char *)(pCont + overhead), (char *)pNewHead->content, compLen, contLen); - assert(origLen == contLen); - - memcpy(pNewHead, pHead, sizeof(SRpcHead)); - pNewHead->msgLen = rpcMsgLenFromCont(origLen); - rpcFreeMsg(pHead); // free the compressed message buffer - pHead = pNewHead; - tTrace("decomp malloc mem:%p", temp); - } else { - tError("failed to allocate memory to decompress msg, contLen:%d", contLen); - } - } - - return pHead; -} - -static int rpcAuthenticateMsg(void *pMsg, int msgLen, void *pAuth, void *pKey) { - T_MD5_CTX context; - int ret = -1; - - tMD5Init(&context); - tMD5Update(&context, (uint8_t *)pKey, TSDB_PASSWORD_LEN); - tMD5Update(&context, (uint8_t *)pMsg, msgLen); - tMD5Update(&context, (uint8_t *)pKey, TSDB_PASSWORD_LEN); - tMD5Final(&context); - - if (memcmp(context.digest, pAuth, sizeof(context.digest)) == 0) ret = 0; - - return ret; -} - -static void rpcBuildAuthHead(void *pMsg, int msgLen, void *pAuth, void *pKey) { - T_MD5_CTX context; - - tMD5Init(&context); - tMD5Update(&context, (uint8_t *)pKey, TSDB_PASSWORD_LEN); - tMD5Update(&context, (uint8_t *)pMsg, msgLen); - tMD5Update(&context, (uint8_t *)pKey, TSDB_PASSWORD_LEN); - tMD5Final(&context); - - memcpy(pAuth, context.digest, sizeof(context.digest)); -} - -static int rpcAddAuthPart(SRpcConn *pConn, char *msg, int msgLen) { - SRpcHead *pHead = (SRpcHead *)msg; - - if (pConn->spi && pConn->secured == 0) { - // add auth part - pHead->spi = pConn->spi; - SRpcDigest *pDigest = (SRpcDigest *)(msg + msgLen); - pDigest->timeStamp = htonl(taosGetTimestampSec()); - msgLen += sizeof(SRpcDigest); - pHead->msgLen = (int32_t)htonl((uint32_t)msgLen); - rpcBuildAuthHead(pHead, msgLen - TSDB_AUTH_LEN, pDigest->auth, pConn->secret); - } else { - pHead->spi = 0; - pHead->msgLen = (int32_t)htonl((uint32_t)msgLen); - } - - return msgLen; -} - -static int rpcCheckAuthentication(SRpcConn *pConn, char *msg, int msgLen) { - SRpcHead *pHead = (SRpcHead *)msg; - int code = 0; - - if ((pConn->secured && pHead->spi == 0) || (pHead->spi == 0 && pConn->spi == 0)) { - // secured link, or no authentication - pHead->msgLen = (int32_t)htonl((uint32_t)pHead->msgLen); - // tTrace("%s, secured link, no auth is required", pConn->info); - return 0; - } - - if (!rpcIsReq(pHead->msgType)) { - // for response, if code is auth failure, it shall bypass the auth process - code = htonl(pHead->code); - if (code == TSDB_CODE_RPC_INVALID_TIME_STAMP || code == TSDB_CODE_RPC_AUTH_FAILURE || - code == TSDB_CODE_RPC_INVALID_VERSION || code == TSDB_CODE_RPC_AUTH_REQUIRED || - code == TSDB_CODE_MND_USER_NOT_EXIST || code == TSDB_CODE_RPC_NOT_READY) { - pHead->msgLen = (int32_t)htonl((uint32_t)pHead->msgLen); - // tTrace("%s, dont check authentication since code is:0x%x", pConn->info, code); - return 0; - } - } - - code = 0; - if (pHead->spi == pConn->spi) { - // authentication - SRpcDigest *pDigest = (SRpcDigest *)((char *)pHead + msgLen - sizeof(SRpcDigest)); - - int32_t delta; - delta = (int32_t)htonl(pDigest->timeStamp); - delta -= (int32_t)taosGetTimestampSec(); - if (abs(delta) > 900) { - tWarn("%s, time diff:%d is too big, msg discarded", pConn->info, delta); - code = TSDB_CODE_RPC_INVALID_TIME_STAMP; - } else { - if (rpcAuthenticateMsg(pHead, msgLen - TSDB_AUTH_LEN, pDigest->auth, pConn->secret) < 0) { - tDebug("%s, authentication failed, msg discarded", pConn->info); - code = TSDB_CODE_RPC_AUTH_FAILURE; - } else { - pHead->msgLen = (int32_t)htonl((uint32_t)pHead->msgLen) - sizeof(SRpcDigest); - if (!rpcIsReq(pHead->msgType)) pConn->secured = 1; // link is secured for client - // tTrace("%s, message is authenticated", pConn->info); - } - } - } else { - tError("%s, auth spi:%d not matched with received:%d %p", pConn->info, pConn->spi, pHead->spi, pConn); - code = pHead->spi ? TSDB_CODE_RPC_AUTH_FAILURE : TSDB_CODE_RPC_AUTH_REQUIRED; - } - - return code; -} - -static void rpcLockConn(SRpcConn *pConn) { - int64_t tid = taosGetSelfPthreadId(); - int i = 0; - while (atomic_val_compare_exchange_64(&(pConn->lockedBy), 0, tid) != 0) { - if (++i % 1000 == 0) { - sched_yield(); - } - } -} - -static void rpcUnlockConn(SRpcConn *pConn) { - int64_t tid = taosGetSelfPthreadId(); - if (atomic_val_compare_exchange_64(&(pConn->lockedBy), tid, 0) != tid) { - assert(false); - } -} - -static void rpcAddRef(SRpcInfo *pRpc) { atomic_add_fetch_32(&pRpc->refCount, 1); } - -static void rpcDecRef(SRpcInfo *pRpc) { - if (atomic_sub_fetch_32(&pRpc->refCount, 1) == 0) { - rpcCloseConnCache(pRpc->pCache); - taosHashCleanup(pRpc->hash); - taosTmrCleanUp(pRpc->tmrCtrl); - taosIdPoolCleanUp(pRpc->idPool); - - taosMemoryFreeClear(pRpc->connList); - taosThreadMutexDestroy(&pRpc->mutex); - tDebug("%s rpc resources are released", pRpc->label); - taosMemoryFreeClear(pRpc); - - atomic_sub_fetch_32(&tsRpcNum, 1); - } -} -#endif diff --git a/source/libs/transport/src/rpcTcp.c b/source/libs/transport/src/rpcTcp.c deleted file mode 100644 index 52c5ddcf631e9a0cc3a307fab00122c5139fa5dd..0000000000000000000000000000000000000000 --- a/source/libs/transport/src/rpcTcp.c +++ /dev/null @@ -1,657 +0,0 @@ -/* - * Copyright (c) 2019 TAOS Data, Inc. - * - * This program is free software: you can use, redistribute, and/or modify - * it under the terms of the GNU Affero General Public License, version 3 - * or later ("AGPL"), as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include "rpcTcp.h" -#include "os.h" -#include "rpcHead.h" -#include "rpcLog.h" -#include "taosdef.h" -#include "taoserror.h" -#include "tutil.h" - -#ifndef USE_UV -typedef struct SFdObj { - void * signature; - TdSocketPtr pSocket; // TCP socket FD - void * thandle; // handle from upper layer, like TAOS - uint32_t ip; - uint16_t port; - int16_t closedByApp; // 1: already closed by App - struct SThreadObj *pThreadObj; - struct SFdObj * prev; - struct SFdObj * next; -} SFdObj; - -typedef struct SThreadObj { - TdThread thread; - SFdObj * pHead; - TdThreadMutex mutex; - uint32_t ip; - bool stop; - TdEpollPtr pEpoll; - int numOfFds; - int threadId; - char label[TSDB_LABEL_LEN]; - void * shandle; // handle passed by upper layer during server initialization - void *(*processData)(SRecvInfo *pPacket); -} SThreadObj; - -typedef struct { - char label[TSDB_LABEL_LEN]; - int32_t index; - int numOfThreads; - SThreadObj **pThreadObj; -} SClientObj; - -typedef struct { - TdSocketServerPtr pSocketServer; - uint32_t ip; - uint16_t port; - int8_t stop; - int8_t reserve; - char label[TSDB_LABEL_LEN]; - int numOfThreads; - void * shandle; - SThreadObj **pThreadObj; - TdThread thread; -} SServerObj; - -static void * taosProcessTcpData(void *param); -static SFdObj *taosMallocFdObj(SThreadObj *pThreadObj, TdSocketPtr pSocket); -static void taosFreeFdObj(SFdObj *pFdObj); -static void taosReportBrokenLink(SFdObj *pFdObj); -static void * taosAcceptTcpConnection(void *arg); - -void *taosInitTcpServer(uint32_t ip, uint16_t port, char *label, int numOfThreads, void *fp, void *shandle) { - SServerObj *pServerObj; - SThreadObj *pThreadObj; - - pServerObj = (SServerObj *)taosMemoryCalloc(sizeof(SServerObj), 1); - if (pServerObj == NULL) { - tError("TCP:%s no enough memory", label); - terrno = TAOS_SYSTEM_ERROR(errno); - return NULL; - } - - pServerObj->pSocketServer = NULL; - taosResetPthread(&pServerObj->thread); - pServerObj->ip = ip; - pServerObj->port = port; - tstrncpy(pServerObj->label, label, sizeof(pServerObj->label)); - pServerObj->numOfThreads = numOfThreads; - - pServerObj->pThreadObj = (SThreadObj **)taosMemoryCalloc(sizeof(SThreadObj *), numOfThreads); - if (pServerObj->pThreadObj == NULL) { - tError("TCP:%s no enough memory", label); - terrno = TAOS_SYSTEM_ERROR(errno); - taosMemoryFree(pServerObj); - return NULL; - } - - int code = 0; - TdThreadAttr thattr; - taosThreadAttrInit(&thattr); - taosThreadAttrSetDetachState(&thattr, PTHREAD_CREATE_JOINABLE); - - // initialize parameters in case it may encounter error later - for (int i = 0; i < numOfThreads; ++i) { - pThreadObj = (SThreadObj *)taosMemoryCalloc(sizeof(SThreadObj), 1); - if (pThreadObj == NULL) { - tError("TCP:%s no enough memory", label); - terrno = TAOS_SYSTEM_ERROR(errno); - for (int j = 0; j < i; ++j) taosMemoryFree(pServerObj->pThreadObj[j]); - taosMemoryFree(pServerObj->pThreadObj); - taosMemoryFree(pServerObj); - return NULL; - } - - pServerObj->pThreadObj[i] = pThreadObj; - pThreadObj->pEpoll = NULL; - taosResetPthread(&pThreadObj->thread); - pThreadObj->processData = fp; - tstrncpy(pThreadObj->label, label, sizeof(pThreadObj->label)); - pThreadObj->shandle = shandle; - pThreadObj->stop = false; - } - - // initialize mutex, thread, fd which may fail - for (int i = 0; i < numOfThreads; ++i) { - pThreadObj = pServerObj->pThreadObj[i]; - code = taosThreadMutexInit(&(pThreadObj->mutex), NULL); - if (code < 0) { - tError("%s failed to init TCP process data mutex(%s)", label, strerror(errno)); - break; - } - - pThreadObj->pEpoll = taosCreateEpoll(10); // size does not matter - if (pThreadObj->pEpoll == NULL) { - tError("%s failed to create TCP epoll", label); - code = -1; - break; - } - - code = taosThreadCreate(&(pThreadObj->thread), &thattr, taosProcessTcpData, (void *)(pThreadObj)); - if (code != 0) { - tError("%s failed to create TCP process data thread(%s)", label, strerror(errno)); - break; - } - - pThreadObj->threadId = i; - } - - pServerObj->pSocketServer = taosOpenTcpServerSocket(pServerObj->ip, pServerObj->port); - if (pServerObj->pSocketServer == NULL) code = -1; - - if (code == 0) { - code = taosThreadCreate(&pServerObj->thread, &thattr, taosAcceptTcpConnection, (void *)pServerObj); - if (code != 0) { - tError("%s failed to create TCP accept thread(%s)", label, strerror(code)); - } - } - - if (code != 0) { - terrno = TAOS_SYSTEM_ERROR(errno); - taosCleanUpTcpServer(pServerObj); - pServerObj = NULL; - } else { - tDebug("%s TCP server is initialized, ip:0x%x port:%hu numOfThreads:%d", label, ip, port, numOfThreads); - } - - taosThreadAttrDestroy(&thattr); - return (void *)pServerObj; -} - -static void taosStopTcpThread(SThreadObj *pThreadObj) { - if (pThreadObj == NULL) { - return; - } - // save thread into local variable and signal thread to stop - TdThread thread = pThreadObj->thread; - if (!taosCheckPthreadValid(thread)) { - return; - } - pThreadObj->stop = true; - if (taosComparePthread(thread, taosThreadSelf())) { - pthread_detach(taosThreadSelf()); - return; - } - taosThreadJoin(thread, NULL); -} - -void taosStopTcpServer(void *handle) { - SServerObj *pServerObj = handle; - - if (pServerObj == NULL) return; - pServerObj->stop = 1; - - if (pServerObj->pSocketServer != NULL) { - taosShutDownSocketServerRD(pServerObj->pSocketServer); - } - if (taosCheckPthreadValid(pServerObj->thread)) { - if (taosComparePthread(pServerObj->thread, taosThreadSelf())) { - pthread_detach(taosThreadSelf()); - } else { - taosThreadJoin(pServerObj->thread, NULL); - } - } - - tDebug("%s TCP server is stopped", pServerObj->label); -} - -void taosCleanUpTcpServer(void *handle) { - SServerObj *pServerObj = handle; - SThreadObj *pThreadObj; - if (pServerObj == NULL) return; - - for (int i = 0; i < pServerObj->numOfThreads; ++i) { - pThreadObj = pServerObj->pThreadObj[i]; - taosStopTcpThread(pThreadObj); - } - - tDebug("%s TCP server is cleaned up", pServerObj->label); - - taosMemoryFreeClear(pServerObj->pThreadObj); - taosMemoryFreeClear(pServerObj); -} - -static void *taosAcceptTcpConnection(void *arg) { - TdSocketPtr pSocket = NULL; - struct sockaddr_in caddr; - int threadId = 0; - SThreadObj * pThreadObj; - SServerObj * pServerObj; - - pServerObj = (SServerObj *)arg; - tDebug("%s TCP server is ready, ip:0x%x:%hu", pServerObj->label, pServerObj->ip, pServerObj->port); - setThreadName("acceptTcpConn"); - - while (1) { - socklen_t addrlen = sizeof(caddr); - pSocket = taosAcceptTcpConnectSocket(pServerObj->pSocketServer, (struct sockaddr *)&caddr, &addrlen); - if (pServerObj->stop) { - tDebug("%s TCP server stop accepting new connections", pServerObj->label); - break; - } - - if (pSocket == NULL) { - if (errno == EINVAL) { - tDebug("%s TCP server stop accepting new connections, exiting", pServerObj->label); - break; - } - - tError("%s TCP accept failure(%s)", pServerObj->label, strerror(errno)); - continue; - } - - taosKeepTcpAlive(pSocket); - struct timeval to = {5, 0}; - int32_t ret = taosSetSockOpt(pSocket, SOL_SOCKET, SO_RCVTIMEO, &to, sizeof(to)); - if (ret != 0) { - taosCloseSocket(&pSocket); - tError("%s failed to set recv timeout fd(%s)for connection from:%s:%hu", pServerObj->label, strerror(errno), - taosInetNtoa(caddr.sin_addr), htons(caddr.sin_port)); - continue; - } - - // pick up the thread to handle this connection - pThreadObj = pServerObj->pThreadObj[threadId]; - - SFdObj *pFdObj = taosMallocFdObj(pThreadObj, pSocket); - if (pFdObj) { - pFdObj->ip = caddr.sin_addr.s_addr; - pFdObj->port = htons(caddr.sin_port); - tDebug("%s new TCP connection from %s:%hu, FD:%p numOfFds:%d", pServerObj->label, - taosInetNtoa(caddr.sin_addr), pFdObj->port, pFdObj, pThreadObj->numOfFds); - } else { - taosCloseSocket(&pSocket); - tError("%s failed to malloc FdObj(%s) for connection from:%s:%hu", pServerObj->label, strerror(errno), - taosInetNtoa(caddr.sin_addr), htons(caddr.sin_port)); - } - - // pick up next thread for next connection - threadId++; - threadId = threadId % pServerObj->numOfThreads; - } - - taosCloseSocketServer(&pServerObj->pSocketServer); - return NULL; -} - -void *taosInitTcpClient(uint32_t ip, uint16_t port, char *label, int numOfThreads, void *fp, void *shandle) { - SClientObj *pClientObj = (SClientObj *)taosMemoryCalloc(1, sizeof(SClientObj)); - if (pClientObj == NULL) { - tError("TCP:%s no enough memory", label); - terrno = TAOS_SYSTEM_ERROR(errno); - return NULL; - } - - tstrncpy(pClientObj->label, label, sizeof(pClientObj->label)); - pClientObj->numOfThreads = numOfThreads; - pClientObj->pThreadObj = (SThreadObj **)taosMemoryCalloc(numOfThreads, sizeof(SThreadObj *)); - if (pClientObj->pThreadObj == NULL) { - tError("TCP:%s no enough memory", label); - taosMemoryFreeClear(pClientObj); - terrno = TAOS_SYSTEM_ERROR(errno); - } - - int code = 0; - TdThreadAttr thattr; - taosThreadAttrInit(&thattr); - taosThreadAttrSetDetachState(&thattr, PTHREAD_CREATE_JOINABLE); - - for (int i = 0; i < numOfThreads; ++i) { - SThreadObj *pThreadObj = (SThreadObj *)taosMemoryCalloc(1, sizeof(SThreadObj)); - if (pThreadObj == NULL) { - tError("TCP:%s no enough memory", label); - terrno = TAOS_SYSTEM_ERROR(errno); - for (int j = 0; j < i; ++j) taosMemoryFree(pClientObj->pThreadObj[j]); - taosMemoryFree(pClientObj); - taosThreadAttrDestroy(&thattr); - return NULL; - } - pClientObj->pThreadObj[i] = pThreadObj; - taosResetPthread(&pThreadObj->thread); - pThreadObj->ip = ip; - pThreadObj->stop = false; - tstrncpy(pThreadObj->label, label, sizeof(pThreadObj->label)); - pThreadObj->shandle = shandle; - pThreadObj->processData = fp; - } - - // initialize mutex, thread, fd which may fail - for (int i = 0; i < numOfThreads; ++i) { - SThreadObj *pThreadObj = pClientObj->pThreadObj[i]; - code = taosThreadMutexInit(&(pThreadObj->mutex), NULL); - if (code < 0) { - tError("%s failed to init TCP process data mutex(%s)", label, strerror(errno)); - break; - } - - pThreadObj->pEpoll = taosCreateEpoll(10); // size does not matter - if (pThreadObj->pEpoll == NULL) { - tError("%s failed to create TCP epoll", label); - code = -1; - break; - } - - code = taosThreadCreate(&(pThreadObj->thread), &thattr, taosProcessTcpData, (void *)(pThreadObj)); - if (code != 0) { - tError("%s failed to create TCP process data thread(%s)", label, strerror(errno)); - break; - } - pThreadObj->threadId = i; - } - if (code != 0) { - terrno = TAOS_SYSTEM_ERROR(errno); - taosCleanUpTcpClient(pClientObj); - pClientObj = NULL; - } - return pClientObj; -} - -void taosStopTcpClient(void *chandle) { - SClientObj *pClientObj = chandle; - - if (pClientObj == NULL) return; - - tDebug("%s TCP client is stopped", pClientObj->label); -} - -void taosCleanUpTcpClient(void *chandle) { - SClientObj *pClientObj = chandle; - if (pClientObj == NULL) return; - for (int i = 0; i < pClientObj->numOfThreads; ++i) { - SThreadObj *pThreadObj = pClientObj->pThreadObj[i]; - taosStopTcpThread(pThreadObj); - } - - tDebug("%s TCP client is cleaned up", pClientObj->label); - taosMemoryFreeClear(pClientObj->pThreadObj); - taosMemoryFreeClear(pClientObj); -} - -void *taosOpenTcpClientConnection(void *shandle, void *thandle, uint32_t ip, uint16_t port) { - SClientObj *pClientObj = shandle; - int32_t index = atomic_load_32(&pClientObj->index) % pClientObj->numOfThreads; - atomic_store_32(&pClientObj->index, index + 1); - SThreadObj *pThreadObj = pClientObj->pThreadObj[index]; - - TdSocketPtr pSocket = taosOpenTcpClientSocket(ip, port, pThreadObj->ip); - if (pSocket == NULL) return NULL; - - struct sockaddr_in sin; - uint16_t localPort = 0; - unsigned int addrlen = sizeof(sin); - if (taosGetSocketName(pSocket, (struct sockaddr *)&sin, &addrlen) == 0 && sin.sin_family == AF_INET && addrlen == sizeof(sin)) { - localPort = (uint16_t)ntohs(sin.sin_port); - } - - SFdObj *pFdObj = taosMallocFdObj(pThreadObj, pSocket); - - if (pFdObj) { - pFdObj->thandle = thandle; - pFdObj->port = port; - pFdObj->ip = ip; - - char ipport[40] = {0}; - taosIpPort2String(ip, port, ipport); - tDebug("%s %p TCP connection to %s is created, localPort:%hu FD:%p numOfFds:%d", pThreadObj->label, thandle, - ipport, localPort, pFdObj, pThreadObj->numOfFds); - } else { - tError("%s failed to malloc client FdObj(%s)", pThreadObj->label, strerror(errno)); - taosCloseSocket(&pSocket); - } - - return pFdObj; -} - -void taosCloseTcpConnection(void *chandle) { - SFdObj *pFdObj = chandle; - if (pFdObj == NULL || pFdObj->signature != pFdObj) return; - - SThreadObj *pThreadObj = pFdObj->pThreadObj; - tDebug("%s %p TCP connection will be closed, FD:%p", pThreadObj->label, pFdObj->thandle, pFdObj); - - // pFdObj->thandle = NULL; - pFdObj->closedByApp = 1; - taosShutDownSocketWR(pFdObj->pSocket); -} - -int taosSendTcpData(uint32_t ip, uint16_t port, void *data, int len, void *chandle) { - SFdObj *pFdObj = chandle; - if (pFdObj == NULL || pFdObj->signature != pFdObj) return -1; - SThreadObj *pThreadObj = pFdObj->pThreadObj; - - int ret = taosWriteMsg(pFdObj->pSocket, data, len); - tTrace("%s %p TCP data is sent, FD:%p bytes:%d", pThreadObj->label, pFdObj->thandle, pFdObj, ret); - - return ret; -} - -static void taosReportBrokenLink(SFdObj *pFdObj) { - SThreadObj *pThreadObj = pFdObj->pThreadObj; - - // notify the upper layer, so it will clean the associated context - if (pFdObj->closedByApp == 0) { - taosShutDownSocketWR(pFdObj->pSocket); - - SRecvInfo recvInfo; - recvInfo.msg = NULL; - recvInfo.msgLen = 0; - recvInfo.ip = 0; - recvInfo.port = 0; - recvInfo.shandle = pThreadObj->shandle; - recvInfo.thandle = pFdObj->thandle; - recvInfo.chandle = NULL; - recvInfo.connType = RPC_CONN_TCP; - (*(pThreadObj->processData))(&recvInfo); - } - - taosFreeFdObj(pFdObj); -} - -static int taosReadTcpData(SFdObj *pFdObj, SRecvInfo *pInfo) { - SRpcHead rpcHead; - int32_t msgLen, leftLen, retLen, headLen; - char * buffer, *msg; - - SThreadObj *pThreadObj = pFdObj->pThreadObj; - - headLen = taosReadMsg(pFdObj->pSocket, &rpcHead, sizeof(SRpcHead)); - if (headLen != sizeof(SRpcHead)) { - tDebug("%s %p read error, FD:%p headLen:%d", pThreadObj->label, pFdObj->thandle, pFdObj, headLen); - return -1; - } - - msgLen = (int32_t)htonl((uint32_t)rpcHead.msgLen); - int32_t size = msgLen + tsRpcOverhead; - buffer = taosMemoryMalloc(size); - if (NULL == buffer) { - tError("%s %p TCP taosMemoryMalloc(size:%d) fail", pThreadObj->label, pFdObj->thandle, msgLen); - return -1; - } else { - tTrace("%s %p read data, FD:%p TCP malloc mem:%p", pThreadObj->label, pFdObj->thandle, pFdObj, buffer); - } - - msg = buffer + tsRpcOverhead; - leftLen = msgLen - headLen; - retLen = taosReadMsg(pFdObj->pSocket, msg + headLen, leftLen); - - if (leftLen != retLen) { - tError("%s %p read error, leftLen:%d retLen:%d FD:%p", pThreadObj->label, pFdObj->thandle, leftLen, retLen, pFdObj); - taosMemoryFree(buffer); - return -1; - } - - memcpy(msg, &rpcHead, sizeof(SRpcHead)); - - pInfo->msg = msg; - pInfo->msgLen = msgLen; - pInfo->ip = pFdObj->ip; - pInfo->port = pFdObj->port; - pInfo->shandle = pThreadObj->shandle; - pInfo->thandle = pFdObj->thandle; - pInfo->chandle = pFdObj; - pInfo->connType = RPC_CONN_TCP; - - if (pFdObj->closedByApp) { - taosMemoryFree(buffer); - return -1; - } - - return 0; -} - -#define maxEvents 10 - -static void *taosProcessTcpData(void *param) { - SThreadObj * pThreadObj = param; - SFdObj * pFdObj; - struct epoll_event events[maxEvents]; - SRecvInfo recvInfo; - - char name[16] = {0}; - snprintf(name, tListLen(name), "%s-tcp", pThreadObj->label); - setThreadName(name); - - while (1) { - int fdNum = taosWaitEpoll(pThreadObj->pEpoll, events, maxEvents, TAOS_EPOLL_WAIT_TIME); - if (pThreadObj->stop) { - tDebug("%s TCP thread get stop event, exiting...", pThreadObj->label); - break; - } - if (fdNum < 0) continue; - - for (int i = 0; i < fdNum; ++i) { - pFdObj = events[i].data.ptr; - - if (events[i].events & EPOLLERR) { - tDebug("%s %p FD:%p epoll errors", pThreadObj->label, pFdObj->thandle, pFdObj); - taosReportBrokenLink(pFdObj); - continue; - } - - if (events[i].events & EPOLLRDHUP) { - tDebug("%s %p FD:%p RD hang up", pThreadObj->label, pFdObj->thandle, pFdObj); - taosReportBrokenLink(pFdObj); - continue; - } - - if (events[i].events & EPOLLHUP) { - tDebug("%s %p FD:%p hang up", pThreadObj->label, pFdObj->thandle, pFdObj); - taosReportBrokenLink(pFdObj); - continue; - } - - if (taosReadTcpData(pFdObj, &recvInfo) < 0) { - taosShutDownSocketWR(pFdObj->pSocket); - continue; - } - - pFdObj->thandle = (*(pThreadObj->processData))(&recvInfo); - if (pFdObj->thandle == NULL) taosFreeFdObj(pFdObj); - } - - if (pThreadObj->stop) break; - } - - if (pThreadObj->pEpoll != NULL) { - taosCloseEpoll(&pThreadObj->pEpoll); - pThreadObj->pEpoll = NULL; - } - - while (pThreadObj->pHead) { - pFdObj = pThreadObj->pHead; - pThreadObj->pHead = pFdObj->next; - taosReportBrokenLink(pFdObj); - } - - taosThreadMutexDestroy(&(pThreadObj->mutex)); - tDebug("%s TCP thread exits ...", pThreadObj->label); - taosMemoryFreeClear(pThreadObj); - - return NULL; -} - -static SFdObj *taosMallocFdObj(SThreadObj *pThreadObj, TdSocketPtr pSocket) { - struct epoll_event event; - - SFdObj *pFdObj = (SFdObj *)taosMemoryCalloc(sizeof(SFdObj), 1); - if (pFdObj == NULL) { - return NULL; - } - - pFdObj->closedByApp = 0; - pFdObj->pSocket = pSocket; - pFdObj->pThreadObj = pThreadObj; - pFdObj->signature = pFdObj; - - event.events = EPOLLIN | EPOLLRDHUP; - event.data.ptr = pFdObj; - if (taosCtlEpoll(pThreadObj->pEpoll, EPOLL_CTL_ADD, pSocket, &event) < 0) { - taosMemoryFreeClear(pFdObj); - terrno = TAOS_SYSTEM_ERROR(errno); - return NULL; - } - - // notify the data process, add into the FdObj list - taosThreadMutexLock(&(pThreadObj->mutex)); - pFdObj->next = pThreadObj->pHead; - if (pThreadObj->pHead) (pThreadObj->pHead)->prev = pFdObj; - pThreadObj->pHead = pFdObj; - pThreadObj->numOfFds++; - taosThreadMutexUnlock(&(pThreadObj->mutex)); - - return pFdObj; -} - -static void taosFreeFdObj(SFdObj *pFdObj) { - if (pFdObj == NULL) return; - if (pFdObj->signature != pFdObj) return; - - SThreadObj *pThreadObj = pFdObj->pThreadObj; - taosThreadMutexLock(&pThreadObj->mutex); - - if (pFdObj->signature == NULL) { - taosThreadMutexUnlock(&pThreadObj->mutex); - return; - } - - pFdObj->signature = NULL; - taosCtlEpoll(pThreadObj->pEpoll, EPOLL_CTL_DEL, pFdObj->pSocket, NULL); - taosCloseSocket(&pFdObj->pSocket); - - pThreadObj->numOfFds--; - if (pThreadObj->numOfFds < 0) - tError("%s %p TCP thread:%d, number of FDs is negative!!!", pThreadObj->label, pFdObj->thandle, - pThreadObj->threadId); - - if (pFdObj->prev) { - (pFdObj->prev)->next = pFdObj->next; - } else { - pThreadObj->pHead = pFdObj->next; - } - - if (pFdObj->next) { - (pFdObj->next)->prev = pFdObj->prev; - } - - taosThreadMutexUnlock(&pThreadObj->mutex); - - tDebug("%s %p TCP connection is closed, FD:%p numOfFds:%d", pThreadObj->label, pFdObj->thandle, pFdObj, pThreadObj->numOfFds); - - taosMemoryFreeClear(pFdObj); -} -#endif \ No newline at end of file diff --git a/source/libs/transport/src/rpcUdp.c b/source/libs/transport/src/rpcUdp.c deleted file mode 100644 index 359a94011dc493493aa9c63660806787b3be4d11..0000000000000000000000000000000000000000 --- a/source/libs/transport/src/rpcUdp.c +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Copyright (c) 2019 TAOS Data, Inc. - * - * This program is free software: you can use, redistribute, and/or modify - * it under the terms of the GNU Affero General Public License, version 3 - * or later ("AGPL"), as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include "rpcUdp.h" -#include "os.h" -#include "rpcHead.h" -#include "rpcLog.h" -#include "taosdef.h" -#include "taoserror.h" -#include "ttimer.h" -#include "tutil.h" - -#ifndef USE_UV - -#define RPC_MAX_UDP_CONNS 256 -#define RPC_MAX_UDP_PKTS 1000 -#define RPC_UDP_BUF_TIME 5 // mseconds -#define RPC_MAX_UDP_SIZE 65480 - -typedef struct { - int index; - TdSocketPtr pSocket; - uint16_t port; // peer port - uint16_t localPort; // local port - char label[TSDB_LABEL_LEN]; // copy from udpConnSet; - TdThread thread; - void *hash; - void *shandle; // handle passed by upper layer during server initialization - void *pSet; - void *(*processData)(SRecvInfo *pRecv); - char *buffer; // buffer to receive data -} SUdpConn; - -typedef struct { - int index; - int server; - uint32_t ip; // local IP - uint16_t port; // local Port - void * shandle; // handle passed by upper layer during server initialization - int threads; - char label[TSDB_LABEL_LEN]; - void *(*fp)(SRecvInfo *pPacket); - SUdpConn udpConn[]; -} SUdpConnSet; - -static void *taosRecvUdpData(void *param); - -void *taosInitUdpConnection(uint32_t ip, uint16_t port, char *label, int threads, void *fp, void *shandle) { - SUdpConn * pConn; - SUdpConnSet *pSet; - - int size = (int)sizeof(SUdpConnSet) + threads * (int)sizeof(SUdpConn); - pSet = (SUdpConnSet *)taosMemoryMalloc((size_t)size); - if (pSet == NULL) { - tError("%s failed to allocate UdpConn", label); - terrno = TAOS_SYSTEM_ERROR(errno); - return NULL; - } - - memset(pSet, 0, (size_t)size); - pSet->ip = ip; - pSet->port = port; - pSet->shandle = shandle; - pSet->fp = fp; - pSet->threads = threads; - tstrncpy(pSet->label, label, sizeof(pSet->label)); - - TdThreadAttr thAttr; - taosThreadAttrInit(&thAttr); - taosThreadAttrSetDetachState(&thAttr, PTHREAD_CREATE_JOINABLE); - - int i; - uint16_t ownPort; - for (i = 0; i < threads; ++i) { - pConn = pSet->udpConn + i; - ownPort = (port ? port + i : 0); - pConn->pSocket = taosOpenUdpSocket(ip, ownPort); - if (pConn->pSocket == NULL) { - tError("%s failed to open UDP socket %x:%hu", label, ip, port); - break; - } - - pConn->buffer = taosMemoryMalloc(RPC_MAX_UDP_SIZE); - if (NULL == pConn->buffer) { - tError("%s failed to malloc recv buffer", label); - break; - } - - struct sockaddr_in sin; - unsigned int addrlen = sizeof(sin); - if (taosGetSocketName(pConn->pSocket, (struct sockaddr *)&sin, &addrlen) == 0 && sin.sin_family == AF_INET && - addrlen == sizeof(sin)) { - pConn->localPort = (uint16_t)ntohs(sin.sin_port); - } - - tstrncpy(pConn->label, label, sizeof(pConn->label)); - pConn->shandle = shandle; - pConn->processData = fp; - pConn->index = i; - pConn->pSet = pSet; - - int code = taosThreadCreate(&pConn->thread, &thAttr, taosRecvUdpData, pConn); - if (code != 0) { - tError("%s failed to create thread to process UDP data(%s)", label, strerror(errno)); - break; - } - } - - taosThreadAttrDestroy(&thAttr); - - if (i != threads) { - terrno = TAOS_SYSTEM_ERROR(errno); - taosCleanUpUdpConnection(pSet); - return NULL; - } - - tDebug("%s UDP connection is initialized, ip:%x:%hu threads:%d", label, ip, port, threads); - return pSet; -} - -void taosStopUdpConnection(void *handle) { - SUdpConnSet *pSet = (SUdpConnSet *)handle; - SUdpConn * pConn; - - if (pSet == NULL) return; - - for (int i = 0; i < pSet->threads; ++i) { - pConn = pSet->udpConn + i; - if (pConn->pSocket != NULL) taosShutDownSocketRDWR(pConn->pSocket); - if (pConn->pSocket != NULL) taosCloseSocket(&pConn->pSocket); - pConn->pSocket = NULL; - } - - for (int i = 0; i < pSet->threads; ++i) { - pConn = pSet->udpConn + i; - if (taosCheckPthreadValid(pConn->thread)) { - taosThreadJoin(pConn->thread, NULL); - } - taosMemoryFreeClear(pConn->buffer); - // tTrace("%s UDP thread is closed, index:%d", pConn->label, i); - } - - tDebug("%s UDP is stopped", pSet->label); -} - -void taosCleanUpUdpConnection(void *handle) { - SUdpConnSet *pSet = (SUdpConnSet *)handle; - SUdpConn * pConn; - - if (pSet == NULL) return; - - for (int i = 0; i < pSet->threads; ++i) { - pConn = pSet->udpConn + i; - if (pConn->pSocket != NULL) taosCloseSocket(&pConn->pSocket); - } - - tDebug("%s UDP is cleaned up", pSet->label); - taosMemoryFreeClear(pSet); -} - -void *taosOpenUdpConnection(void *shandle, void *thandle, uint32_t ip, uint16_t port) { - SUdpConnSet *pSet = (SUdpConnSet *)shandle; - - pSet->index = (pSet->index + 1) % pSet->threads; - - SUdpConn *pConn = pSet->udpConn + pSet->index; - pConn->port = port; - - tDebug("%s UDP connection is setup, ip:%x:%hu localPort:%hu", pConn->label, ip, port, pConn->localPort); - - return pConn; -} - -static void *taosRecvUdpData(void *param) { - SUdpConn * pConn = param; - struct sockaddr_in sourceAdd; - ssize_t dataLen; - unsigned int addLen; - uint16_t port; - SRecvInfo recvInfo; - - memset(&sourceAdd, 0, sizeof(sourceAdd)); - addLen = sizeof(sourceAdd); - tDebug("%s UDP thread is created, index:%d", pConn->label, pConn->index); - char *msg = pConn->buffer; - - setThreadName("recvUdpData"); - - while (1) { - dataLen = taosReadFromSocket(pConn->pSocket, pConn->buffer, RPC_MAX_UDP_SIZE, 0, (struct sockaddr *)&sourceAdd, &addLen); - if (dataLen <= 0) { - tDebug("%s UDP socket was closed, exiting(%s), dataLen:%d", pConn->label, strerror(errno), (int32_t)dataLen); - - // for windows usage, remote shutdown also returns - 1 in windows client - if (pConn->pSocket == NULL) { - break; - } else { - continue; - } - } - - port = ntohs(sourceAdd.sin_port); - - if (dataLen < sizeof(SRpcHead)) { - tError("%s recvfrom failed(%s)", pConn->label, strerror(errno)); - continue; - } - - int32_t size = dataLen + tsRpcOverhead; - char * tmsg = taosMemoryMalloc(size); - if (NULL == tmsg) { - tError("%s failed to allocate memory, size:%" PRId64, pConn->label, (int64_t)dataLen); - continue; - } else { - tTrace("UDP malloc mem:%p size:%d", tmsg, size); - } - - tmsg += tsRpcOverhead; // overhead for SRpcReqContext - memcpy(tmsg, msg, dataLen); - recvInfo.msg = tmsg; - recvInfo.msgLen = dataLen; - recvInfo.ip = sourceAdd.sin_addr.s_addr; - recvInfo.port = port; - recvInfo.shandle = pConn->shandle; - recvInfo.thandle = NULL; - recvInfo.chandle = pConn; - recvInfo.connType = 0; - (*(pConn->processData))(&recvInfo); - } - - return NULL; -} - -int taosSendUdpData(uint32_t ip, uint16_t port, void *data, int dataLen, void *chandle) { - SUdpConn *pConn = (SUdpConn *)chandle; - - if (pConn == NULL) return -1; - - struct sockaddr_in destAdd; - memset(&destAdd, 0, sizeof(destAdd)); - destAdd.sin_family = AF_INET; - destAdd.sin_addr.s_addr = ip; - destAdd.sin_port = htons(port); - - int ret = taosSendto(pConn->pSocket, data, (size_t)dataLen, 0, (struct sockaddr *)&destAdd, sizeof(destAdd)); - - return ret; -} -#endif \ No newline at end of file diff --git a/source/libs/transport/src/trans.c b/source/libs/transport/src/trans.c index 846cf6f967664091b6b1610a81e21bd8a22bbccf..5627dbfbf54be3eeed4b4d132b19e2c6b9b1d030 100644 --- a/source/libs/transport/src/trans.c +++ b/source/libs/transport/src/trans.c @@ -69,9 +69,6 @@ void* rpcOpen(const SRpcInit* pInit) { if (pInit->user) { memcpy(pRpc->user, pInit->user, strlen(pInit->user)); } - if (pInit->secret) { - memcpy(pRpc->secret, pInit->secret, strlen(pInit->secret)); - } return pRpc; } void rpcClose(void* arg) { @@ -97,7 +94,9 @@ void rpcFreeCont(void* cont) { if (cont == NULL) { return; } + taosMemoryFree((char*)cont - TRANS_MSG_OVERHEAD); + tTrace("free mem: %p", (char*)cont - TRANS_MSG_OVERHEAD); } void* rpcReallocCont(void* ptr, int contLen) { if (ptr == NULL) { diff --git a/source/libs/transport/src/transComm.c b/source/libs/transport/src/transComm.c index 98e9e67edeb532d3ac992863ac54b3fd166182b3..7014cc481f6f3908793ea2f6fc074a04fbe7472b 100644 --- a/source/libs/transport/src/transComm.c +++ b/source/libs/transport/src/transComm.c @@ -133,6 +133,7 @@ int transAllocBuffer(SConnBuffer* connBuf, uv_buf_t* uvBuf) { } else { p->cap = p->total; p->buf = taosMemoryRealloc(p->buf, p->cap); + tTrace("internal malloc mem: %p, size: %d", p->buf, p->cap); uvBuf->base = p->buf + p->len; uvBuf->len = p->cap - p->len; diff --git a/source/libs/transport/src/transSrv.c b/source/libs/transport/src/transSrv.c index 84adeb4bf69686091c16a7d12df9855b9ba1fbac..36f5cf98150e5636b43eb35b819d5bcd9288fe6a 100644 --- a/source/libs/transport/src/transSrv.c +++ b/source/libs/transport/src/transSrv.c @@ -295,14 +295,14 @@ static void uvHandleReq(SSrvConn* pConn) { // no ref here } - // if pHead->noResp = 1, + // pHead->noResp = 1, // 1. server application should not send resp on handle // 2. once send out data, cli conn released to conn pool immediately // 3. not mixed with persist transMsg.info.handle = (void*)uvAcquireExHandle(pConn->refId); - tTrace("server handle %p conn: %p translated to app, refId: %" PRIu64 "", transMsg.info.handle, pConn, pConn->refId); transMsg.info.refId = pConn->refId; + tTrace("server handle %p conn: %p translated to app, refId: %" PRIu64 "", transMsg.info.handle, pConn, pConn->refId); assert(transMsg.info.handle != NULL); if (pHead->noResp == 1) { transMsg.info.refId = -1; @@ -469,6 +469,8 @@ static void uvStartSendResp(SSrvMsg* smsg) { if (pConn->broken == true) { // persist by + transFreeMsg(smsg->msg.pCont); + taosMemoryFree(smsg); transUnrefSrvHandle(pConn); return; } diff --git a/source/libs/transport/test/CMakeLists.txt b/source/libs/transport/test/CMakeLists.txt index 02ada328fc702a2ae36a450e623159b39ceb786a..98a252e008d85b27206fa58055f757dd02d64a78 100644 --- a/source/libs/transport/test/CMakeLists.txt +++ b/source/libs/transport/test/CMakeLists.txt @@ -110,3 +110,13 @@ target_link_libraries (pushServer transport ) + +add_test( + NAME transUT + COMMAND transUT +) +add_test( + NAME transUtilUt + COMMAND transportTest +) + diff --git a/source/libs/transport/test/pushServer.c b/source/libs/transport/test/pushServer.c index 61f3431b772aa53417703ae2cfa9910bb04594f5..8b1dcd46cfc0143c7d52d744317592b70eb5ec19 100644 --- a/source/libs/transport/test/pushServer.c +++ b/source/libs/transport/test/pushServer.c @@ -15,9 +15,9 @@ //#define _DEFAULT_SOURCE #include "os.h" -#include "rpcLog.h" #include "tglobal.h" #include "tqueue.h" +#include "transLog.h" #include "trpc.h" int msgSize = 128; @@ -134,7 +134,6 @@ int main(int argc, char *argv[]) { rpcInit.cfp = processRequestMsg; rpcInit.sessions = 1000; rpcInit.idleTime = 2 * 1500; - rpcInit.afp = retrieveAuthInfo; for (int i = 1; i < argc; ++i) { if (strcmp(argv[i], "-p") == 0 && i < argc - 1) { diff --git a/source/libs/transport/test/rclient.c b/source/libs/transport/test/rclient.c index 78964e5324520940e90389449b854c792601142b..55e6dd000a1270889894fe28f0a024164e5255eb 100644 --- a/source/libs/transport/test/rclient.c +++ b/source/libs/transport/test/rclient.c @@ -14,9 +14,9 @@ */ #include #include "os.h" -#include "rpcLog.h" #include "taoserror.h" #include "tglobal.h" +#include "transLog.h" #include "trpc.h" #include "tutil.h" @@ -118,9 +118,6 @@ int main(int argc, char *argv[]) { rpcInit.sessions = 100; rpcInit.idleTime = 100; rpcInit.user = "michael"; - rpcInit.secret = secret; - rpcInit.ckey = "key"; - rpcInit.spi = 1; rpcInit.connType = TAOS_CONN_CLIENT; rpcDebugFlag = 131; @@ -144,9 +141,7 @@ int main(int argc, char *argv[]) { } else if (strcmp(argv[i], "-u") == 0 && i < argc - 1) { rpcInit.user = argv[++i]; } else if (strcmp(argv[i], "-k") == 0 && i < argc - 1) { - rpcInit.secret = argv[++i]; } else if (strcmp(argv[i], "-spi") == 0 && i < argc - 1) { - rpcInit.spi = atoi(argv[++i]); } else if (strcmp(argv[i], "-d") == 0 && i < argc - 1) { rpcDebugFlag = atoi(argv[++i]); } else { @@ -160,15 +155,13 @@ int main(int argc, char *argv[]) { printf(" [-n requests]: number of requests per thread, default is:%d\n", numOfReqs); printf(" [-o compSize]: compression message size, default is:%d\n", tsCompressMsgSize); printf(" [-u user]: user name for the connection, default is:%s\n", rpcInit.user); - printf(" [-k secret]: password for the connection, default is:%s\n", rpcInit.secret); - printf(" [-spi SPI]: security parameter index, default is:%d\n", rpcInit.spi); printf(" [-d debugFlag]: debug flag, default:%d\n", rpcDebugFlag); printf(" [-h help]: print out this help\n\n"); exit(0); } } - const char *path = "/tmp/transport/client"; + const char *path = TD_TMP_DIR_PATH "transport/client"; taosRemoveDir(path); taosMkDir(path); tstrncpy(tsLogDir, path, PATH_MAX); diff --git a/source/libs/transport/test/rserver.c b/source/libs/transport/test/rserver.c index e852b1e6e21770bee85d66fcfe9f4a4e1d74dbb6..1fd78be77de7575d47e3d2ce70bf6e0908dfec8e 100644 --- a/source/libs/transport/test/rserver.c +++ b/source/libs/transport/test/rserver.c @@ -15,9 +15,9 @@ //#define _DEFAULT_SOURCE #include "os.h" -#include "rpcLog.h" #include "tglobal.h" #include "tqueue.h" +#include "transLog.h" #include "trpc.h" int msgSize = 128; @@ -123,7 +123,6 @@ int main(int argc, char *argv[]) { rpcInit.cfp = processRequestMsg; rpcInit.sessions = 1000; rpcInit.idleTime = 2 * 1500; - rpcInit.afp = retrieveAuthInfo; rpcDebugFlag = 131; @@ -161,7 +160,7 @@ int main(int argc, char *argv[]) { tsAsyncLog = 0; rpcInit.connType = TAOS_CONN_SERVER; - const char *path = "/tmp/transport/server"; + const char *path = TD_TMP_DIR_PATH "transport/server"; taosRemoveDir(path); taosMkDir(path); tstrncpy(tsLogDir, path, PATH_MAX); diff --git a/source/libs/transport/test/syncClient.c b/source/libs/transport/test/syncClient.c index 801aa0fd74bd1bbb61020be51efe2f0170ac1d24..bc6461eaed1f5d2c7ca9d82f9ae47dbebf4fc02b 100644 --- a/source/libs/transport/test/syncClient.c +++ b/source/libs/transport/test/syncClient.c @@ -14,22 +14,22 @@ */ #include #include "os.h" -#include "rpcLog.h" #include "taoserror.h" #include "tglobal.h" +#include "transLog.h" #include "trpc.h" #include "tutil.h" typedef struct { - int index; - SEpSet epSet; - int num; - int numOfReqs; - int msgSize; - tsem_t rspSem; - tsem_t * pOverSem; + int index; + SEpSet epSet; + int num; + int numOfReqs; + int msgSize; + tsem_t rspSem; + tsem_t * pOverSem; TdThread thread; - void * pRpc; + void * pRpc; } SInfo; static void processResponse(void *pParent, SRpcMsg *pMsg, SEpSet *pEpSet) { SInfo *pInfo = (SInfo *)pMsg->info.ahandle; @@ -103,7 +103,7 @@ int main(int argc, char *argv[]) { char secret[20] = "mypassword"; struct timeval systemTime; int64_t startTime, endTime; - TdThreadAttr thattr; + TdThreadAttr thattr; // server info epSet.inUse = 0; @@ -119,9 +119,6 @@ int main(int argc, char *argv[]) { rpcInit.sessions = 100; rpcInit.idleTime = 100; rpcInit.user = "michael"; - rpcInit.secret = secret; - rpcInit.ckey = "key"; - rpcInit.spi = 1; rpcInit.connType = TAOS_CONN_CLIENT; for (int i = 1; i < argc; ++i) { @@ -144,9 +141,7 @@ int main(int argc, char *argv[]) { } else if (strcmp(argv[i], "-u") == 0 && i < argc - 1) { rpcInit.user = argv[++i]; } else if (strcmp(argv[i], "-k") == 0 && i < argc - 1) { - rpcInit.secret = argv[++i]; } else if (strcmp(argv[i], "-spi") == 0 && i < argc - 1) { - rpcInit.spi = atoi(argv[++i]); } else if (strcmp(argv[i], "-d") == 0 && i < argc - 1) { rpcDebugFlag = atoi(argv[++i]); } else { @@ -160,8 +155,6 @@ int main(int argc, char *argv[]) { printf(" [-n requests]: number of requests per thread, default is:%d\n", numOfReqs); printf(" [-o compSize]: compression message size, default is:%d\n", tsCompressMsgSize); printf(" [-u user]: user name for the connection, default is:%s\n", rpcInit.user); - printf(" [-k secret]: password for the connection, default is:%s\n", rpcInit.secret); - printf(" [-spi SPI]: security parameter index, default is:%d\n", rpcInit.spi); printf(" [-d debugFlag]: debug flag, default:%d\n", rpcDebugFlag); printf(" [-h help]: print out this help\n\n"); exit(0); diff --git a/source/libs/transport/test/transUT.cpp b/source/libs/transport/test/transUT.cpp index 51a02993742e1207c6ddb6e987592688231c8530..25b04e769cfe248046fc8e080d1775c331ddcdcd 100644 --- a/source/libs/transport/test/transUT.cpp +++ b/source/libs/transport/test/transUT.cpp @@ -15,10 +15,10 @@ #include #include #include -#include "rpcLog.h" #include "tdatablock.h" #include "tglobal.h" #include "tlog.h" +#include "transLog.h" #include "trpc.h" using namespace std; @@ -43,15 +43,13 @@ static void processResp(void *parent, SRpcMsg *pMsg, SEpSet *pEpSet); class Client { public: void Init(int nThread) { + memcpy(tsTempDir, TD_TMP_DIR_PATH, strlen(TD_TMP_DIR_PATH)); memset(&rpcInit_, 0, sizeof(rpcInit_)); rpcInit_.localPort = 0; rpcInit_.label = (char *)label; rpcInit_.numOfThreads = nThread; rpcInit_.cfp = processResp; rpcInit_.user = (char *)user; - rpcInit_.secret = (char *)secret; - rpcInit_.ckey = (char *)ckey; - rpcInit_.spi = 1; rpcInit_.parent = this; rpcInit_.connType = TAOS_CONN_CLIENT; this->transCli = rpcOpen(&rpcInit_); @@ -107,15 +105,15 @@ class Client { class Server { public: Server() { + memcpy(tsTempDir, TD_TMP_DIR_PATH, strlen(TD_TMP_DIR_PATH)); memset(&rpcInit_, 0, sizeof(rpcInit_)); + + memcpy(rpcInit_.localFqdn, "localhost", strlen("localhost")); rpcInit_.localPort = port; rpcInit_.label = (char *)label; rpcInit_.numOfThreads = 5; rpcInit_.cfp = processReq; rpcInit_.user = (char *)user; - rpcInit_.secret = (char *)secret; - rpcInit_.ckey = (char *)ckey; - rpcInit_.spi = 1; rpcInit_.connType = TAOS_CONN_SERVER; } void Start() { @@ -221,7 +219,7 @@ static void initEnv() { tsLogEmbedded = 1; tsAsyncLog = 0; - std::string path = "/tmp/transport"; + std::string path = TD_TMP_DIR_PATH "transport"; // taosRemoveDir(path.c_str()); taosMkDir(path.c_str()); @@ -300,12 +298,14 @@ TEST_F(TransEnv, 02StopServer) { for (int i = 0; i < 1; i++) { SRpcMsg req = {0}, resp = {0}; req.msgType = 0; + req.info.ahandle = (void *)0x35; req.pCont = rpcMallocCont(10); req.contLen = 10; tr->cliSendAndRecv(&req, &resp); assert(resp.code == 0); } SRpcMsg req = {0}, resp = {0}; + req.info.ahandle = (void *)0x35; req.msgType = 1; req.pCont = rpcMallocCont(10); req.contLen = 10; @@ -388,6 +388,7 @@ TEST_F(TransEnv, cliReleaseHandleExcept) { memset(&req, 0, sizeof(req)); req.info = resp.info; req.info.persistHandle = 1; + req.info.ahandle = (void *)1234; req.msgType = 1; req.pCont = rpcMallocCont(10); req.contLen = 10; @@ -406,12 +407,12 @@ TEST_F(TransEnv, srvContinueSend) { tr->SetSrvContinueSend(processContinueSend); SRpcMsg req = {0}, resp = {0}; for (int i = 0; i < 10; i++) { - memset(&req, 0, sizeof(req)); - memset(&resp, 0, sizeof(resp)); - req.msgType = 1; - req.pCont = rpcMallocCont(10); - req.contLen = 10; - tr->cliSendAndRecv(&req, &resp); + // memset(&req, 0, sizeof(req)); + // memset(&resp, 0, sizeof(resp)); + // req.msgType = 1; + // req.pCont = rpcMallocCont(10); + // req.contLen = 10; + // tr->cliSendAndRecv(&req, &resp); } taosMsleep(1000); } @@ -422,16 +423,16 @@ TEST_F(TransEnv, srvPersistHandleExcept) { SRpcMsg resp = {0}; SRpcMsg req = {0}; for (int i = 0; i < 5; i++) { - memset(&req, 0, sizeof(req)); - req.info = resp.info; - req.msgType = 1; - req.pCont = rpcMallocCont(10); - req.contLen = 10; - tr->cliSendAndRecv(&req, &resp); - if (i > 2) { - tr->StopCli(); - break; - } + // memset(&req, 0, sizeof(req)); + // req.info = resp.info; + // req.msgType = 1; + // req.pCont = rpcMallocCont(10); + // req.contLen = 10; + // tr->cliSendAndRecv(&req, &resp); + // if (i > 2) { + // tr->StopCli(); + // break; + //} } taosMsleep(2000); // conn broken @@ -442,16 +443,16 @@ TEST_F(TransEnv, cliPersistHandleExcept) { SRpcMsg resp = {0}; SRpcMsg req = {0}; for (int i = 0; i < 5; i++) { - memset(&req, 0, sizeof(req)); - req.info = resp.info; - req.msgType = 1; - req.pCont = rpcMallocCont(10); - req.contLen = 10; - tr->cliSendAndRecv(&req, &resp); - if (i > 2) { - tr->StopSrv(); - break; - } + // memset(&req, 0, sizeof(req)); + // req.info = resp.info; + // req.msgType = 1; + // req.pCont = rpcMallocCont(10); + // req.contLen = 10; + // tr->cliSendAndRecv(&req, &resp); + // if (i > 2) { + // tr->StopSrv(); + // break; + //} } taosMsleep(2000); // conn broken @@ -465,34 +466,34 @@ TEST_F(TransEnv, queryExcept) { tr->SetSrvContinueSend(processRegisterFailure); SRpcMsg resp = {0}; SRpcMsg req = {0}; - for (int i = 0; i < 5; i++) { - memset(&req, 0, sizeof(req)); - req.info = resp.info; - req.info.persistHandle = 1; - req.msgType = 1; - req.pCont = rpcMallocCont(10); - req.contLen = 10; - tr->cliSendAndRecv(&req, &resp); - if (i == 2) { - rpcReleaseHandle(resp.info.handle, TAOS_CONN_CLIENT); - tr->StopCli(); - break; - } - } + // for (int i = 0; i < 5; i++) { + // memset(&req, 0, sizeof(req)); + // req.info = resp.info; + // req.info.persistHandle = 1; + // req.msgType = 1; + // req.pCont = rpcMallocCont(10); + // req.contLen = 10; + // tr->cliSendAndRecv(&req, &resp); + // if (i == 2) { + // rpcReleaseHandle(resp.info.handle, TAOS_CONN_CLIENT); + // tr->StopCli(); + // break; + // } + //} taosMsleep(4 * 1000); } TEST_F(TransEnv, noResp) { SRpcMsg resp = {0}; SRpcMsg req = {0}; - for (int i = 0; i < 5; i++) { - memset(&req, 0, sizeof(req)); - req.info.noResp = 1; - req.msgType = 1; - req.pCont = rpcMallocCont(10); - req.contLen = 10; - tr->cliSendAndRecv(&req, &resp); - } - taosMsleep(2000); + // for (int i = 0; i < 5; i++) { + // memset(&req, 0, sizeof(req)); + // req.info.noResp = 1; + // req.msgType = 1; + // req.pCont = rpcMallocCont(10); + // req.contLen = 10; + // tr->cliSendAndRecv(&req, &resp); + //} + // taosMsleep(2000); // no resp } diff --git a/source/libs/transport/test/transportTests.cpp b/source/libs/transport/test/transportTests.cpp index 35009c7686dec2495f69eb3a363f50406fa98a9c..a84bd94a00000b9a412b030e223e574a7a5b9794 100644 --- a/source/libs/transport/test/transportTests.cpp +++ b/source/libs/transport/test/transportTests.cpp @@ -150,20 +150,26 @@ class TransCtxEnv : public ::testing::Test { STransCtx *ctx; }; +int32_t cloneVal(void *src, void **dst) { + int sz = (int)strlen((char *)src); + *dst = taosMemoryCalloc(1, sz + 1); + memcpy(*dst, src, sz); + return 0; +} TEST_F(TransCtxEnv, mergeTest) { int key = 1; { STransCtx *src = (STransCtx *)taosMemoryCalloc(1, sizeof(STransCtx)); transCtxInit(src); { - STransCtxVal val1 = { NULL, NULL, (void (*)(const void*))taosMemoryFree}; + STransCtxVal val1 = {NULL, NULL, (void (*)(const void *))taosMemoryFree}; val1.val = taosMemoryMalloc(12); taosHashPut(src->args, &key, sizeof(key), &val1, sizeof(val1)); key++; } { - STransCtxVal val1 = { NULL, NULL, (void (*)(const void*))taosMemoryFree}; + STransCtxVal val1 = {NULL, NULL, (void (*)(const void *))taosMemoryFree}; val1.val = taosMemoryMalloc(12); taosHashPut(src->args, &key, sizeof(key), &val1, sizeof(val1)); key++; @@ -176,14 +182,14 @@ TEST_F(TransCtxEnv, mergeTest) { STransCtx *src = (STransCtx *)taosMemoryCalloc(1, sizeof(STransCtx)); transCtxInit(src); { - STransCtxVal val1 = { NULL, NULL, (void (*)(const void*))taosMemoryFree}; + STransCtxVal val1 = {NULL, NULL, (void (*)(const void *))taosMemoryFree}; val1.val = taosMemoryMalloc(12); taosHashPut(src->args, &key, sizeof(key), &val1, sizeof(val1)); key++; } { - STransCtxVal val1 = { NULL, NULL, (void (*)(const void*))taosMemoryFree}; + STransCtxVal val1 = {NULL, NULL, (void (*)(const void *))taosMemoryFree}; val1.val = taosMemoryMalloc(12); taosHashPut(src->args, &key, sizeof(key), &val1, sizeof(val1)); key++; @@ -198,16 +204,18 @@ TEST_F(TransCtxEnv, mergeTest) { STransCtx *src = (STransCtx *)taosMemoryCalloc(1, sizeof(STransCtx)); transCtxInit(src); { - STransCtxVal val1 = { NULL, NULL, (void (*)(const void*))taosMemoryFree}; + STransCtxVal val1 = {NULL, NULL, (void (*)(const void *))taosMemoryFree}; val1.val = taosMemoryCalloc(1, 11); + val1.clone = cloneVal; memcpy(val1.val, val.c_str(), val.size()); taosHashPut(src->args, &key, sizeof(key), &val1, sizeof(val1)); key++; } { - STransCtxVal val1 = { NULL, NULL, (void (*)(const void*))taosMemoryFree}; + STransCtxVal val1 = {NULL, NULL, (void (*)(const void *))taosMemoryFree}; val1.val = taosMemoryCalloc(1, 11); + val1.clone = cloneVal; memcpy(val1.val, val.c_str(), val.size()); taosHashPut(src->args, &key, sizeof(key), &val1, sizeof(val1)); key++; diff --git a/source/libs/transport/test/uv.c b/source/libs/transport/test/uv.c index fb026ef1a61fc190eb1b560520a83809191023d5..1d99bf8fef2a2882d6d59bee4ce04af0a3b3b556 100644 --- a/source/libs/transport/test/uv.c +++ b/source/libs/transport/test/uv.c @@ -1,36 +1,36 @@ #include -#include #include #include #include +#include #include "task.h" #define NUM_OF_THREAD 1 -#define TIMEOUT 10000 +#define TIMEOUT 10000 typedef struct SThreadObj { - TdThread thread; - uv_pipe_t *pipe; - uv_loop_t *loop; - uv_async_t *workerAsync; // - int fd; + TdThread thread; + uv_pipe_t * pipe; + uv_loop_t * loop; + uv_async_t *workerAsync; // + int fd; } SThreadObj; typedef struct SServerObj { - uv_tcp_t server; - uv_loop_t *loop; - int workerIdx; - int numOfThread; + uv_tcp_t server; + uv_loop_t * loop; + int workerIdx; + int numOfThread; SThreadObj **pThreadObj; - uv_pipe_t **pipe; + uv_pipe_t ** pipe; } SServerObj; typedef struct SConnCtx { - uv_tcp_t *pClient; + uv_tcp_t * pClient; uv_timer_t *pTimer; uv_async_t *pWorkerAsync; - int ref; + int ref; } SConnCtx; void echo_write(uv_write_t *req, int status) { @@ -42,7 +42,6 @@ void echo_write(uv_write_t *req, int status) { } void echo_read(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf) { - SConnCtx *pConn = container_of(client, SConnCtx, pClient); pConn->ref += 1; printf("read data %d\n", nread, buf->base, buf->len); @@ -59,8 +58,7 @@ void echo_read(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf) { } if (nread < 0) { - if (nread != UV_EOF) - fprintf(stderr, "Read error %s\n", uv_err_name(nread)); + if (nread != UV_EOF) fprintf(stderr, "Read error %s\n", uv_err_name(nread)); uv_close((uv_handle_t *)client, NULL); } taosMemoryFree(buf->base); @@ -83,21 +81,19 @@ void on_new_connection(uv_stream_t *s, int status) { uv_tcp_init(pObj->loop, client); if (uv_accept(s, (uv_stream_t *)client) == 0) { uv_write_t *write_req = (uv_write_t *)taosMemoryMalloc(sizeof(uv_write_t)); - uv_buf_t dummy_buf = uv_buf_init("a", 1); + uv_buf_t dummy_buf = uv_buf_init("a", 1); // despatch to worker thread pObj->workerIdx = (pObj->workerIdx + 1) % pObj->numOfThread; - uv_write2(write_req, (uv_stream_t *)&(pObj->pipe[pObj->workerIdx][0]), - &dummy_buf, 1, (uv_stream_t *)client, echo_write); + uv_write2(write_req, (uv_stream_t *)&(pObj->pipe[pObj->workerIdx][0]), &dummy_buf, 1, (uv_stream_t *)client, + echo_write); } else { uv_close((uv_handle_t *)client, NULL); } } -void child_on_new_connection(uv_stream_t *q, ssize_t nread, - const uv_buf_t *buf) { +void child_on_new_connection(uv_stream_t *q, ssize_t nread, const uv_buf_t *buf) { printf("x child_on_new_connection \n"); if (nread < 0) { - if (nread != UV_EOF) - fprintf(stderr, "Read error %s\n", uv_err_name(nread)); + if (nread != UV_EOF) fprintf(stderr, "Read error %s\n", uv_err_name(nread)); uv_close((uv_handle_t *)q, NULL); return; } @@ -119,7 +115,7 @@ void child_on_new_connection(uv_stream_t *q, ssize_t nread, uv_timer_init(pObj->loop, pConn->pTimer); pConn->pClient = (uv_tcp_t *)taosMemoryMalloc(sizeof(uv_tcp_t)); - pConn->pWorkerAsync = pObj->workerAsync; // thread safty + pConn->pWorkerAsync = pObj->workerAsync; // thread safty uv_tcp_init(pObj->loop, pConn->pClient); if (uv_accept(q, (uv_stream_t *)(pConn->pClient)) == 0) { @@ -143,7 +139,7 @@ static void workerAsyncCallback(uv_async_t *handle) { } void *worker_thread(void *arg) { SThreadObj *pObj = (SThreadObj *)arg; - int fd = pObj->fd; + int fd = pObj->fd; pObj->loop = (uv_loop_t *)taosMemoryMalloc(sizeof(uv_loop_t)); uv_loop_init(pObj->loop); @@ -152,19 +148,16 @@ void *worker_thread(void *arg) { pObj->workerAsync = taosMemoryMalloc(sizeof(uv_async_t)); uv_async_init(pObj->loop, pObj->workerAsync, workerAsyncCallback); - uv_read_start((uv_stream_t *)pObj->pipe, alloc_buffer, - child_on_new_connection); + uv_read_start((uv_stream_t *)pObj->pipe, alloc_buffer, child_on_new_connection); uv_run(pObj->loop, UV_RUN_DEFAULT); } int main() { - SServerObj *server = taosMemoryCalloc(1, sizeof(SServerObj)); server->loop = (uv_loop_t *)taosMemoryMalloc(sizeof(uv_loop_t)); server->numOfThread = NUM_OF_THREAD; server->workerIdx = 0; - server->pThreadObj = - (SThreadObj **)taosMemoryCalloc(server->numOfThread, sizeof(SThreadObj *)); + server->pThreadObj = (SThreadObj **)taosMemoryCalloc(server->numOfThread, sizeof(SThreadObj *)); server->pipe = (uv_pipe_t **)taosMemoryCalloc(server->numOfThread, sizeof(uv_pipe_t *)); uv_loop_init(server->loop); @@ -173,17 +166,15 @@ int main() { server->pThreadObj[i] = (SThreadObj *)taosMemoryCalloc(1, sizeof(SThreadObj)); server->pipe[i] = (uv_pipe_t *)taosMemoryCalloc(2, sizeof(uv_pipe_t)); int fds[2]; - if (uv_socketpair(AF_UNIX, SOCK_STREAM, fds, UV_NONBLOCK_PIPE, - UV_NONBLOCK_PIPE) != 0) { + if (uv_socketpair(AF_UNIX, SOCK_STREAM, fds, UV_NONBLOCK_PIPE, UV_NONBLOCK_PIPE) != 0) { return -1; } uv_pipe_init(server->loop, &(server->pipe[i][0]), 1); - uv_pipe_open(&(server->pipe[i][0]), fds[1]); // init write + uv_pipe_open(&(server->pipe[i][0]), fds[1]); // init write server->pThreadObj[i]->fd = fds[0]; - server->pThreadObj[i]->pipe = &(server->pipe[i][1]); // init read - int err = taosThreadCreate(&(server->pThreadObj[i]->thread), NULL, - worker_thread, (void *)(server->pThreadObj[i])); + server->pThreadObj[i]->pipe = &(server->pipe[i][1]); // init read + int err = taosThreadCreate(&(server->pThreadObj[i]->thread), NULL, worker_thread, (void *)(server->pThreadObj[i])); if (err == 0) { printf("thread %d create\n", i); } else { @@ -195,8 +186,7 @@ int main() { uv_ip4_addr("0.0.0.0", 7000, &bind_addr); uv_tcp_bind(&server->server, (const struct sockaddr *)&bind_addr, 0); int err = 0; - if ((err = uv_listen((uv_stream_t *)&server->server, 128, - on_new_connection)) != 0) { + if ((err = uv_listen((uv_stream_t *)&server->server, 128, on_new_connection)) != 0) { fprintf(stderr, "Listen error %s\n", uv_err_name(err)); return 2; } diff --git a/source/libs/wal/src/walMgmt.c b/source/libs/wal/src/walMgmt.c index ada1f599f231a9a9e2092fbc68637d13e33aa8ff..71cd6de73f005cbc710caf0a22805d60495f55ee 100644 --- a/source/libs/wal/src/walMgmt.c +++ b/source/libs/wal/src/walMgmt.c @@ -14,17 +14,17 @@ */ #define _DEFAULT_SOURCE -#include "tcompare.h" #include "os.h" #include "taoserror.h" +#include "tcompare.h" #include "tref.h" #include "walInt.h" typedef struct { - int8_t stop; - int8_t inited; - uint32_t seq; - int32_t refSetId; + int8_t stop; + int8_t inited; + uint32_t seq; + int32_t refSetId; TdThread thread; } SWalMgmt; @@ -36,30 +36,42 @@ static void walFreeObj(void *pWal); int64_t walGetSeq() { return (int64_t)atomic_load_32(&tsWal.seq); } int32_t walInit() { - int8_t old = atomic_val_compare_exchange_8(&tsWal.inited, 0, 1); - if (old == 1) return 0; + int8_t old; + while (1) { + old = atomic_val_compare_exchange_8(&tsWal.inited, 0, 2); + if (old != 2) break; + } - tsWal.refSetId = taosOpenRef(TSDB_MIN_VNODES, walFreeObj); + if (old == 0) { + tsWal.refSetId = taosOpenRef(TSDB_MIN_VNODES, walFreeObj); - int32_t code = walCreateThread(); - if (code != 0) { - wError("failed to init wal module since %s", tstrerror(code)); - atomic_store_8(&tsWal.inited, 0); - return code; + int32_t code = walCreateThread(); + if (code != 0) { + wError("failed to init wal module since %s", tstrerror(code)); + atomic_store_8(&tsWal.inited, 0); + return code; + } + + wInfo("wal module is initialized, rsetId:%d", tsWal.refSetId); + atomic_store_8(&tsWal.inited, 1); } - wInfo("wal module is initialized, rsetId:%d", tsWal.refSetId); return 0; } void walCleanUp() { - int8_t old = atomic_val_compare_exchange_8(&tsWal.inited, 1, 0); - if (old == 0) { - return; + int8_t old; + while (1) { + old = atomic_val_compare_exchange_8(&tsWal.inited, 1, 2); + if (old != 2) break; + } + + if (old == 1) { + walStopThread(); + taosCloseRef(tsWal.refSetId); + wInfo("wal module is cleaned up"); + atomic_store_8(&tsWal.inited, 0); } - walStopThread(); - taosCloseRef(tsWal.refSetId); - wInfo("wal module is cleaned up"); } SWal *walOpen(const char *path, SWalCfg *pCfg) { @@ -126,7 +138,6 @@ SWal *walOpen(const char *path, SWalCfg *pCfg) { } if (walCheckAndRepairIdx(pWal) < 0) { - } wDebug("vgId:%d, wal:%p is opened, level:%d fsyncPeriod:%d", pWal->cfg.vgId, pWal, pWal->cfg.level, diff --git a/source/libs/wal/src/walWrite.c b/source/libs/wal/src/walWrite.c index 2ddcb27835b5276026d46ff2d46150b7fc9995a7..d2a43c410708249983295dca44ca06f6f75a2b70 100644 --- a/source/libs/wal/src/walWrite.c +++ b/source/libs/wal/src/walWrite.c @@ -121,6 +121,8 @@ int32_t walRollback(SWal *pWal, int64_t ver) { pWal->vers.lastVer = ver - 1; ((SWalFileInfo *)taosArrayGetLast(pWal->fileInfoSet))->lastVer = ver - 1; ((SWalFileInfo *)taosArrayGetLast(pWal->fileInfoSet))->fileSize = entry.offset; + taosCloseFile(&pIdxTFile); + taosCloseFile(&pLogTFile); // unlock taosThreadMutexUnlock(&pWal->mutex); diff --git a/source/libs/wal/test/walMetaTest.cpp b/source/libs/wal/test/walMetaTest.cpp index 18345699b2f5fa55d8356cab7808a09837f61a2e..b1c673e87bfe702f2111c48713fd809199f2520e 100644 --- a/source/libs/wal/test/walMetaTest.cpp +++ b/source/libs/wal/test/walMetaTest.cpp @@ -37,7 +37,7 @@ class WalCleanEnv : public ::testing::Test { } SWal* pWal = NULL; - const char* pathName = "/tmp/wal_test"; + const char* pathName = TD_TMP_DIR_PATH "wal_test"; }; class WalCleanDeleteEnv : public ::testing::Test { @@ -67,7 +67,7 @@ class WalCleanDeleteEnv : public ::testing::Test { } SWal* pWal = NULL; - const char* pathName = "/tmp/wal_test"; + const char* pathName = TD_TMP_DIR_PATH "wal_test"; }; class WalKeepEnv : public ::testing::Test { @@ -104,7 +104,7 @@ class WalKeepEnv : public ::testing::Test { } SWal* pWal = NULL; - const char* pathName = "/tmp/wal_test"; + const char* pathName = TD_TMP_DIR_PATH "wal_test"; }; class WalRetentionEnv : public ::testing::Test { @@ -141,7 +141,7 @@ class WalRetentionEnv : public ::testing::Test { } SWal* pWal = NULL; - const char* pathName = "/tmp/wal_test"; + const char* pathName = TD_TMP_DIR_PATH "wal_test"; }; TEST_F(WalCleanEnv, createNew) { @@ -325,6 +325,7 @@ TEST_F(WalKeepEnv, readHandleRead) { EXPECT_EQ(newStr[j], pRead->pHead->head.body[j]); } } + walCloseReadHandle(pRead); } TEST_F(WalRetentionEnv, repairMeta1) { diff --git a/source/os/CMakeLists.txt b/source/os/CMakeLists.txt index 90b8e9dd8aca8d3ceaee32dc358225d60cf029b3..b6e131d4ccc670f0d3b35e00483f33f072a314e2 100644 --- a/source/os/CMakeLists.txt +++ b/source/os/CMakeLists.txt @@ -18,14 +18,16 @@ if(USE_TD_MEMORY) add_definitions(-DUSE_TD_MEMORY) endif () if(BUILD_ADDR2LINE) - target_include_directories( - os - PUBLIC "${TD_SOURCE_DIR}/contrib/libdwarf/src/lib/libdwarf" - ) + if(NOT TD_WINDOWS) + target_include_directories( + os + PUBLIC "${TD_SOURCE_DIR}/contrib/libdwarf/src/lib/libdwarf" + ) + target_link_libraries( + os PUBLIC addr2line dl z + ) + endif() add_definitions(-DUSE_ADDR2LINE) - target_link_libraries( - os PUBLIC addr2line dl z - ) endif () if(CHECK_STR2INT_ERROR) add_definitions(-DTD_CHECK_STR_TO_INT_ERROR) diff --git a/source/os/src/osDir.c b/source/os/src/osDir.c index 72654d008443a7e42c41a7381d0ad936a41aee2f..c4b7c9386e93fb5fd87d148ff1d3e369d9871de2 100644 --- a/source/os/src/osDir.c +++ b/source/os/src/osDir.c @@ -91,7 +91,12 @@ void taosRemoveDir(const char *dirname) { bool taosDirExist(const char *dirname) { return taosCheckExistFile(dirname); } int32_t taosMkDir(const char *dirname) { + if (taosDirExist(dirname)) return 0; +#ifdef WINDOWS + int32_t code = _mkdir(dirname, 0755); +#else int32_t code = mkdir(dirname, 0755); +#endif if (code < 0 && errno == EEXIST) { return 0; } @@ -101,36 +106,48 @@ int32_t taosMkDir(const char *dirname) { int32_t taosMulMkDir(const char *dirname) { if (dirname == NULL) return -1; - char * temp = strdup(dirname); + char temp[1024]; +#ifdef WINDOWS + taosRealPath(dirname, temp, sizeof(temp)); +#else + strcpy(temp, dirname); +#endif char * pos = temp; int32_t code = 0; - if (strncmp(temp, "/", 1) == 0) { + if (taosDirExist(temp)) return code; + + if (strncmp(temp, TD_DIRSEP, 1) == 0) { pos += 1; - } else if (strncmp(temp, "./", 2) == 0) { + } else if (strncmp(temp, "." TD_DIRSEP, 2) == 0) { pos += 2; } for (; *pos != '\0'; pos++) { - if (*pos == '/') { + if (*pos == TD_DIRSEP[0]) { *pos = '\0'; + #ifdef WINDOWS + code = _mkdir(temp, 0755); + #else code = mkdir(temp, 0755); + #endif if (code < 0 && errno != EEXIST) { - free(temp); return code; } - *pos = '/'; + *pos = TD_DIRSEP[0]; } } - if (*(pos - 1) != '/') { + if (*(pos - 1) != TD_DIRSEP[0]) { + #ifdef WINDOWS + code = _mkdir(temp, 0755); + #else code = mkdir(temp, 0755); + #endif if (code < 0 && errno != EEXIST) { - free(temp); return code; } } - free(temp); // int32_t code = mkdir(dirname, 0755); if (code < 0 && errno == EEXIST) { @@ -233,7 +250,13 @@ char *taosDirName(char *name) { _splitpath(name, Drive1, Dir1, NULL, NULL); size_t dirNameLen = strlen(Drive1) + strlen(Dir1); if (dirNameLen > 0) { - name[dirNameLen] = 0; + if (name[dirNameLen - 1] == '/' || name[dirNameLen - 1] == '\\') { + name[dirNameLen - 1] = 0; + } else { + name[dirNameLen] = 0; + } + } else { + name[0] = 0; } return name; #else diff --git a/source/os/src/osFile.c b/source/os/src/osFile.c index aa64e656382b7b9d90794785ccb0d3a4d791feee..e08b6681630e68d3985df19f18994890cc5abf83 100644 --- a/source/os/src/osFile.c +++ b/source/os/src/osFile.c @@ -109,8 +109,11 @@ void taosGetTmpfilePath(const char *inputTmpDir, const char *fileNamePrefix, cha int64_t taosCopyFile(const char *from, const char *to) { #ifdef WINDOWS - assert(0); - return -1; + if (CopyFile(from, to, 0)) { + return 1; + } else { + return -1; + } #else char buffer[4096]; int64_t size = 0; @@ -236,7 +239,7 @@ int32_t taosDevInoFile(TdFilePtr pFile, int64_t *stDev, int64_t *stIno) { void autoDelFileListAdd(const char *path) { return; } -TdFilePtr taosOpenFile(const char *path, int32_t tdFileOptions) { +TdFilePtr taosOpenFile(const char *path, int32_t tdFileOptions) { int fd = -1; FILE *fp = NULL; if (tdFileOptions & TD_FILE_STREAM) { @@ -343,7 +346,11 @@ int64_t taosReadFile(TdFilePtr pFile, void *buf, int64_t count) { char *tbuf = (char *)buf; while (leftbytes > 0) { + #ifdef WINDOWS + readbytes = _read(pFile->fd, (void *)tbuf, (uint32_t)leftbytes); + #else readbytes = read(pFile->fd, (void *)tbuf, (uint32_t)leftbytes); + #endif if (readbytes < 0) { if (errno == EINTR) { continue; @@ -379,10 +386,10 @@ int64_t taosPReadFile(TdFilePtr pFile, void *buf, int64_t count, int64_t offset) #endif assert(pFile->fd >= 0); // Please check if you have closed the file. #ifdef WINDOWS - size_t pos = lseek(pFile->fd, 0, SEEK_CUR); - lseek(pFile->fd, offset, SEEK_SET); - int64_t ret = read(pFile->fd, buf, count); - lseek(pFile->fd, pos, SEEK_SET); + size_t pos = _lseek(pFile->fd, 0, SEEK_CUR); + _lseek(pFile->fd, offset, SEEK_SET); + int64_t ret = _read(pFile->fd, buf, count); + _lseek(pFile->fd, pos, SEEK_SET); #else int64_t ret = pread(pFile->fd, buf, count, offset); #endif @@ -428,7 +435,11 @@ int64_t taosLSeekFile(TdFilePtr pFile, int64_t offset, int32_t whence) { taosThreadRwlockRdlock(&(pFile->rwlock)); #endif assert(pFile->fd >= 0); // Please check if you have closed the file. +#ifdef WINDOWS + int64_t ret = _lseek(pFile->fd, offset, whence); +#else int64_t ret = lseek(pFile->fd, offset, whence); +#endif #if FILE_WITH_LOCK taosThreadRwlockUnlock(&(pFile->rwlock)); #endif @@ -567,12 +578,12 @@ int64_t taosFSendFile(TdFilePtr pFileOut, TdFilePtr pFileIn, int64_t *offset, in #ifdef WINDOWS - lseek(pFileIn->fd, (int32_t)(*offset), 0); + _lseek(pFileIn->fd, (int32_t)(*offset), 0); int64_t writeLen = 0; uint8_t buffer[_SEND_FILE_STEP_] = {0}; for (int64_t len = 0; len < (size - _SEND_FILE_STEP_); len += _SEND_FILE_STEP_) { - size_t rlen = read(pFileIn->fd, (void *)buffer, _SEND_FILE_STEP_); + size_t rlen = _read(pFileIn->fd, (void *)buffer, _SEND_FILE_STEP_); if (rlen <= 0) { return writeLen; } else if (rlen < _SEND_FILE_STEP_) { @@ -586,7 +597,7 @@ int64_t taosFSendFile(TdFilePtr pFileOut, TdFilePtr pFileIn, int64_t *offset, in int64_t remain = size - writeLen; if (remain > 0) { - size_t rlen = read(pFileIn->fd, (void *)buffer, (size_t)remain); + size_t rlen = _read(pFileIn->fd, (void *)buffer, (size_t)remain); if (rlen <= 0) { return writeLen; } else { diff --git a/source/os/src/osMemory.c b/source/os/src/osMemory.c index e3791af618d341d1cd08a5fc46c9d62055be2e13..24bc9d0b4c1c8b73f4b70d2dd3c78d4fa178d06b 100644 --- a/source/os/src/osMemory.c +++ b/source/os/src/osMemory.c @@ -37,6 +37,49 @@ typedef struct TdMemoryInfo { #ifdef WINDOWS #define tstrdup(str) _strdup(str) + +int32_t taosBackTrace(void **buffer, int32_t size) { + int32_t frame = 0; + return frame; +} + +#ifdef USE_ADDR2LINE +#include +#pragma comment(lib, "dbghelp.lib") + +void taosPrintBackTrace() { + #define MAX_STACK_FRAMES 20 + + void *pStack[MAX_STACK_FRAMES]; + + HANDLE process = GetCurrentProcess(); + SymInitialize(process, NULL, TRUE); + WORD frames = CaptureStackBackTrace(1, MAX_STACK_FRAMES, pStack, NULL); + + char buf_tmp[1024]; + for (WORD i = 0; i < frames; ++i) { + DWORD64 address = (DWORD64)(pStack[i]); + + DWORD64 displacementSym = 0; + char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; + PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer; + pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); + pSymbol->MaxNameLen = MAX_SYM_NAME; + + DWORD displacementLine = 0; + IMAGEHLP_LINE64 line; + //SymSetOptions(SYMOPT_LOAD_LINES); + line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); + + if (SymFromAddr(process, address, &displacementSym, pSymbol) && SymGetLineFromAddr64(process, address, &displacementLine, &line)) { + snprintf(buf_tmp,sizeof(buf_tmp),"BackTrace %08" PRId64 " %s:%d %s\n", taosGetSelfPthreadId(), line.FileName, line.LineNumber, pSymbol->Name); + } else { + snprintf(buf_tmp,sizeof(buf_tmp),"BackTrace error: %d\n",GetLastError()); + } + write(1,buf_tmp,strlen(buf_tmp)); + } +} +#endif #else #define tstrdup(str) strdup(str) diff --git a/source/os/src/osSemaphore.c b/source/os/src/osSemaphore.c index 7df4c26afd8b5e08aede623522bf079605445ae6..d4cfe4fc39a83586a10e7b70b06c22f8e9066bb7 100644 --- a/source/os/src/osSemaphore.c +++ b/source/os/src/osSemaphore.c @@ -68,9 +68,32 @@ int32_t tsem_wait(tsem_t* sem) { } int32_t tsem_timewait(tsem_t* sem, int64_t nanosecs) { - int ret = 0; - - return ret; + struct timespec ts, rel; + FILETIME ft_before, ft_after; + int rc; + + rel.tv_sec = 0; + rel.tv_nsec = nanosecs; + + GetSystemTimeAsFileTime(&ft_before); + errno = 0; + rc = sem_timedwait(&sem, pthread_win32_getabstime_np(&ts, &rel)); + + /* This should have timed out */ + assert(errno == ETIMEDOUT); + assert(rc != 0); + GetSystemTimeAsFileTime(&ft_after); + // We specified a non-zero wait. Time must advance. + if (ft_before.dwLowDateTime == ft_after.dwLowDateTime && ft_before.dwHighDateTime == ft_after.dwHighDateTime) + { + printf("nanoseconds: %d, rc: %d, errno: %d. before filetime: %d, %d; after filetime: %d, %d\n", + nanosecs, rc, errno, + (int)ft_before.dwLowDateTime, (int)ft_before.dwHighDateTime, + (int)ft_after.dwLowDateTime, (int)ft_after.dwHighDateTime); + printf("time must advance during sem_timedwait."); + return 1; + } + return 0; } #elif defined(_TD_DARWIN_64) diff --git a/source/os/src/osSocket.c b/source/os/src/osSocket.c index 105acb188a4236b6889cfb1c26d08479ea32f387..572e2db6fdf8c9045a95ef4b7c9fbcf014f9784b 100644 --- a/source/os/src/osSocket.c +++ b/source/os/src/osSocket.c @@ -718,7 +718,11 @@ bool taosValidIpAndPort(uint32_t ip, uint16_t port) { bzero((char *)&serverAdd, sizeof(serverAdd)); serverAdd.sin_family = AF_INET; +#ifdef WINDOWS + serverAdd.sin_addr.s_addr = INADDR_ANY; +#else serverAdd.sin_addr.s_addr = ip; +#endif serverAdd.sin_port = (uint16_t)htons(port); if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) <= 2) { @@ -882,6 +886,16 @@ void taosBlockSIGPIPE() { } uint32_t taosGetIpv4FromFqdn(const char *fqdn) { +#ifdef WINDOWS + // Initialize Winsock + WSADATA wsaData; + int iResult; + iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (iResult != 0) { + printf("WSAStartup failed: %d\n", iResult); + return 1; + } +#endif struct addrinfo hints = {0}; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; @@ -899,12 +913,12 @@ uint32_t taosGetIpv4FromFqdn(const char *fqdn) { } else { #ifdef EAI_SYSTEM if (ret == EAI_SYSTEM) { - // printf("failed to get the ip address, fqdn:%s, since:%s", fqdn, strerror(errno)); + printf("failed to get the ip address, fqdn:%s, errno:%d, since:%s", fqdn, errno, strerror(errno)); } else { - // printf("failed to get the ip address, fqdn:%s, since:%s", fqdn, gai_strerror(ret)); + printf("failed to get the ip address, fqdn:%s, ret:%d, since:%s", fqdn, ret, gai_strerror(ret)); } #else - // printf("failed to get the ip address, fqdn:%s, since:%s", fqdn, gai_strerror(ret)); + printf("failed to get the ip address, fqdn:%s, ret:%d, since:%s", fqdn, ret, gai_strerror(ret)); #endif return 0xFFFFFFFF; } diff --git a/source/util/src/tencode.c b/source/util/src/tencode.c index fd898984ed7e411c13d55231c717c03ed7efb9ae..185daf9e45e70c86729b467563625677c32a5e07 100644 --- a/source/util/src/tencode.c +++ b/source/util/src/tencode.c @@ -29,10 +29,10 @@ struct SEncoderNode { }; struct SDecoderNode { - SDecoderNode* pNext; - const uint8_t* data; - uint32_t size; - uint32_t pos; + SDecoderNode* pNext; + uint8_t* data; + uint32_t size; + uint32_t pos; }; void tEncoderInit(SEncoder* pEncoder, uint8_t* data, uint32_t size) { @@ -52,7 +52,7 @@ void tEncoderClear(SEncoder* pCoder) { memset(pCoder, 0, sizeof(*pCoder)); } -void tDecoderInit(SDecoder* pDecoder, const uint8_t* data, uint32_t size) { +void tDecoderInit(SDecoder* pDecoder, uint8_t* data, uint32_t size) { pDecoder->data = data; pDecoder->size = size; pDecoder->pos = 0; diff --git a/source/util/src/terror.c b/source/util/src/terror.c index 11851ca5d8928e58e3080499a64a80ede53976e1..7c4f0fa2dd5d170f60f583aa87723a73f72be146 100644 --- a/source/util/src/terror.c +++ b/source/util/src/terror.c @@ -45,32 +45,10 @@ STaosError errors[] = { {.val = 0, .str = "success"}, #endif -// rpc -TAOS_DEFINE_ERROR(TSDB_CODE_RPC_ACTION_IN_PROGRESS, "Action in progress") -TAOS_DEFINE_ERROR(TSDB_CODE_RPC_AUTH_REQUIRED, "Authentication required") -TAOS_DEFINE_ERROR(TSDB_CODE_RPC_AUTH_FAILURE, "Authentication failure") -TAOS_DEFINE_ERROR(TSDB_CODE_RPC_REDIRECT, "Redirect") -TAOS_DEFINE_ERROR(TSDB_CODE_RPC_NOT_READY, "System not ready") // peer is not ready to process data -TAOS_DEFINE_ERROR(TSDB_CODE_RPC_ALREADY_PROCESSED, "Message already processed") -TAOS_DEFINE_ERROR(TSDB_CODE_RPC_LAST_SESSION_NOT_FINISHED, "Last session not finished") -TAOS_DEFINE_ERROR(TSDB_CODE_RPC_MISMATCHED_LINK_ID, "Mismatched meter id") -TAOS_DEFINE_ERROR(TSDB_CODE_RPC_TOO_SLOW, "Processing of request timed out") -TAOS_DEFINE_ERROR(TSDB_CODE_RPC_MAX_SESSIONS, "Number of sessions reached limit") // too many sessions -TAOS_DEFINE_ERROR(TSDB_CODE_RPC_NETWORK_UNAVAIL, "Unable to establish connection") -TAOS_DEFINE_ERROR(TSDB_CODE_RPC_APP_ERROR, "Unexpected generic error in RPC") -TAOS_DEFINE_ERROR(TSDB_CODE_RPC_UNEXPECTED_RESPONSE, "Unexpected response") -TAOS_DEFINE_ERROR(TSDB_CODE_RPC_INVALID_VALUE, "Invalid value") -TAOS_DEFINE_ERROR(TSDB_CODE_RPC_INVALID_TRAN_ID, "Invalid transaction id") -TAOS_DEFINE_ERROR(TSDB_CODE_RPC_INVALID_SESSION_ID, "Invalid session id") -TAOS_DEFINE_ERROR(TSDB_CODE_RPC_INVALID_MSG_TYPE, "Invalid message type") -TAOS_DEFINE_ERROR(TSDB_CODE_RPC_INVALID_RESPONSE_TYPE, "Invalid response type") -TAOS_DEFINE_ERROR(TSDB_CODE_RPC_INVALID_TIME_STAMP, "Client and server's time is not synchronized") -TAOS_DEFINE_ERROR(TSDB_CODE_APP_NOT_READY, "Database not ready") -TAOS_DEFINE_ERROR(TSDB_CODE_RPC_FQDN_ERROR, "Unable to resolve FQDN") -TAOS_DEFINE_ERROR(TSDB_CODE_RPC_INVALID_VERSION, "Invalid app version") -TAOS_DEFINE_ERROR(TSDB_CODE_RPC_PORT_EADDRINUSE, "port already in use") - //common & util +TAOS_DEFINE_ERROR(TSDB_CODE_ACTION_IN_PROGRESS, "Action in progress") +TAOS_DEFINE_ERROR(TSDB_CODE_APP_ERROR, "Unexpected generic error") +TAOS_DEFINE_ERROR(TSDB_CODE_APP_NOT_READY, "Database not ready") TAOS_DEFINE_ERROR(TSDB_CODE_OUT_OF_MEMORY, "Out of Memory") TAOS_DEFINE_ERROR(TSDB_CODE_OUT_OF_RANGE, "Out of range") TAOS_DEFINE_ERROR(TSDB_CODE_OUT_OF_SHM_MEM, "Out of Shared memory") @@ -104,6 +82,13 @@ TAOS_DEFINE_ERROR(TSDB_CODE_REF_INVALID_ID, "Invalid Ref ID") TAOS_DEFINE_ERROR(TSDB_CODE_REF_ALREADY_EXIST, "Ref is already there") TAOS_DEFINE_ERROR(TSDB_CODE_REF_NOT_EXIST, "Ref is not there") +// rpc +TAOS_DEFINE_ERROR(TSDB_CODE_RPC_REDIRECT, "Redirect") +TAOS_DEFINE_ERROR(TSDB_CODE_RPC_AUTH_FAILURE, "Authentication failure") +TAOS_DEFINE_ERROR(TSDB_CODE_RPC_NETWORK_UNAVAIL, "Unable to establish connection") +TAOS_DEFINE_ERROR(TSDB_CODE_RPC_FQDN_ERROR, "Unable to resolve FQDN") +TAOS_DEFINE_ERROR(TSDB_CODE_RPC_PORT_EADDRINUSE, "Port already in use") + //client TAOS_DEFINE_ERROR(TSDB_CODE_TSC_INVALID_OPERATION, "Invalid operation") TAOS_DEFINE_ERROR(TSDB_CODE_TSC_INVALID_QHANDLE, "Invalid qhandle") @@ -149,7 +134,6 @@ TAOS_DEFINE_ERROR(TSDB_CODE_TSC_STMT_CLAUSE_ERROR, "not supported stmt cl // mnode-common TAOS_DEFINE_ERROR(TSDB_CODE_MND_APP_ERROR, "Mnode internal error") TAOS_DEFINE_ERROR(TSDB_CODE_MND_NOT_READY, "Mnode not ready") -TAOS_DEFINE_ERROR(TSDB_CODE_MND_ACTION_IN_PROGRESS, "Message is progressing") TAOS_DEFINE_ERROR(TSDB_CODE_MND_NO_RIGHTS, "Insufficient privilege for operation") TAOS_DEFINE_ERROR(TSDB_CODE_MND_INVALID_CONNECTION, "Invalid message connection") @@ -275,6 +259,7 @@ TAOS_DEFINE_ERROR(TSDB_CODE_MND_TRANS_NOT_EXIST, "Transaction not exist TAOS_DEFINE_ERROR(TSDB_CODE_MND_TRANS_INVALID_STAGE, "Invalid stage to kill") TAOS_DEFINE_ERROR(TSDB_CODE_MND_TRANS_CONFLICT, "Conflict transaction not completed") TAOS_DEFINE_ERROR(TSDB_CODE_MND_TRANS_UNKNOW_ERROR, "Unknown transaction error") +TAOS_DEFINE_ERROR(TSDB_CODE_MND_TRANS_CLOG_IS_NULL, "Transaction commitlog is null") // mnode-mq TAOS_DEFINE_ERROR(TSDB_CODE_MND_TOPIC_ALREADY_EXIST, "Topic already exists") diff --git a/source/util/src/thash.c b/source/util/src/thash.c index 4bbf6ccf450ae4c11685d5012d4acdf2348b551e..551c3b67c8642b8bceab70c9cae75aca78f73769 100644 --- a/source/util/src/thash.c +++ b/source/util/src/thash.c @@ -15,8 +15,8 @@ #define _DEFAULT_SOURCE #include "thash.h" -#include "taoserror.h" #include "os.h" +#include "taoserror.h" #include "tlog.h" // the add ref count operation may trigger the warning if the reference count is greater than the MAX_WARNING_REF_COUNT @@ -27,36 +27,36 @@ #define HASH_NEED_RESIZE(_h) ((_h)->size >= (_h)->capacity * HASH_DEFAULT_LOAD_FACTOR) -#define GET_HASH_NODE_KEY(_n) ((char*)(_n) + sizeof(SHashNode) + (_n)->dataLen) -#define GET_HASH_NODE_DATA(_n) ((char*)(_n) + sizeof(SHashNode)) -#define GET_HASH_PNODE(_n) ((SHashNode *)((char*)(_n) - sizeof(SHashNode))) +#define GET_HASH_NODE_KEY(_n) ((char *)(_n) + sizeof(SHashNode) + (_n)->dataLen) +#define GET_HASH_NODE_DATA(_n) ((char *)(_n) + sizeof(SHashNode)) +#define GET_HASH_PNODE(_n) ((SHashNode *)((char *)(_n) - sizeof(SHashNode))) #define FREE_HASH_NODE(_fp, _n) \ do { \ -/* if (_fp != NULL) { \ - (_fp)(_n); \ - }*/ \ + /* if (_fp != NULL) { \ + (_fp)(_n); \ + }*/ \ taosMemoryFreeClear(_n); \ } while (0); struct SHashNode { - SHashNode *next; - uint32_t hashVal; // the hash value of key - uint32_t dataLen; // length of data - uint32_t keyLen; // length of the key - uint16_t refCount; // reference count - int8_t removed; // flag to indicate removed - char data[]; + SHashNode *next; + uint32_t hashVal; // the hash value of key + uint32_t dataLen; // length of data + uint32_t keyLen; // length of the key + uint16_t refCount; // reference count + int8_t removed; // flag to indicate removed + char data[]; }; typedef struct SHashEntry { - int32_t num; // number of elements in current entry - SRWLatch latch; // entry latch - SHashNode *next; + int32_t num; // number of elements in current entry + SRWLatch latch; // entry latch + SHashNode *next; } SHashEntry; struct SHashObj { - SHashEntry ** hashList; + SHashEntry **hashList; size_t capacity; // number of slots int64_t size; // number of elements in hash table _hash_fn_t hashFp; // hash function @@ -65,7 +65,7 @@ struct SHashObj { SRWLatch lock; // read-write spin lock SHashLockTypeE type; // lock type bool enableUpdate; // enable update - SArray * pMemBlock; // memory block allocated for SHashEntry + SArray *pMemBlock; // memory block allocated for SHashEntry _hash_before_fn_t callbackFp; // function invoked before return the value to caller }; @@ -103,14 +103,14 @@ static FORCE_INLINE void taosHashRUnlock(SHashObj *pHashObj) { taosRUnLockLatch(&pHashObj->lock); } -static FORCE_INLINE void taosHashEntryWLock(const SHashObj *pHashObj, SHashEntry* pe) { +static FORCE_INLINE void taosHashEntryWLock(const SHashObj *pHashObj, SHashEntry *pe) { if (pHashObj->type == HASH_NO_LOCK) { return; } taosWLockLatch(&pe->latch); } -static FORCE_INLINE void taosHashEntryWUnlock(const SHashObj *pHashObj, SHashEntry* pe) { +static FORCE_INLINE void taosHashEntryWUnlock(const SHashObj *pHashObj, SHashEntry *pe) { if (pHashObj->type == HASH_NO_LOCK) { return; } @@ -118,7 +118,7 @@ static FORCE_INLINE void taosHashEntryWUnlock(const SHashObj *pHashObj, SHashEnt taosWUnLockLatch(&pe->latch); } -static FORCE_INLINE void taosHashEntryRLock(const SHashObj *pHashObj, SHashEntry* pe) { +static FORCE_INLINE void taosHashEntryRLock(const SHashObj *pHashObj, SHashEntry *pe) { if (pHashObj->type == HASH_NO_LOCK) { return; } @@ -126,7 +126,7 @@ static FORCE_INLINE void taosHashEntryRLock(const SHashObj *pHashObj, SHashEntry taosRLockLatch(&pe->latch); } -static FORCE_INLINE void taosHashEntryRUnlock(const SHashObj *pHashObj, SHashEntry* pe) { +static FORCE_INLINE void taosHashEntryRUnlock(const SHashObj *pHashObj, SHashEntry *pe) { if (pHashObj->type == HASH_NO_LOCK) { return; } @@ -142,12 +142,11 @@ static FORCE_INLINE int32_t taosHashCapacity(int32_t length) { return i; } -static FORCE_INLINE SHashNode * -doSearchInEntryList(SHashObj *pHashObj, SHashEntry *pe, const void *key, size_t keyLen, uint32_t hashVal) { +static FORCE_INLINE SHashNode *doSearchInEntryList(SHashObj *pHashObj, SHashEntry *pe, const void *key, size_t keyLen, + uint32_t hashVal) { SHashNode *pNode = pe->next; while (pNode) { - if ((pNode->keyLen == keyLen) && - ((*(pHashObj->equalFp))(GET_HASH_NODE_KEY(pNode), key, keyLen) == 0) && + if ((pNode->keyLen == keyLen) && ((*(pHashObj->equalFp))(GET_HASH_NODE_KEY(pNode), key, keyLen) == 0) && pNode->removed == 0) { assert(pNode->hashVal == hashVal); break; @@ -186,18 +185,22 @@ static SHashNode *doCreateHashNode(const void *key, size_t keyLen, const void *p * @param pNode the old node with requested key * @param pNewNode the new node with requested key */ -static FORCE_INLINE void doUpdateHashNode(SHashObj *pHashObj, SHashEntry* pe, SHashNode* prev, SHashNode *pNode, SHashNode *pNewNode) { +static FORCE_INLINE void doUpdateHashNode(SHashObj *pHashObj, SHashEntry *pe, SHashNode *prev, SHashNode *pNode, + SHashNode *pNewNode) { assert(pNode->keyLen == pNewNode->keyLen); atomic_sub_fetch_16(&pNode->refCount, 1); if (prev != NULL) { prev->next = pNewNode; + ASSERT(prev->next != prev); } else { pe->next = pNewNode; } if (pNode->refCount <= 0) { pNewNode->next = pNode->next; + ASSERT(pNewNode->next != pNewNode); + FREE_HASH_NODE(pHashObj->freeFp, pNode); } else { pNewNode->next = pNode; @@ -227,9 +230,7 @@ static FORCE_INLINE bool taosHashTableEmpty(const SHashObj *pHashObj); * @param pHashObj * @return */ -static FORCE_INLINE bool taosHashTableEmpty(const SHashObj *pHashObj) { - return taosHashGetSize(pHashObj) == 0; -} +static FORCE_INLINE bool taosHashTableEmpty(const SHashObj *pHashObj) { return taosHashGetSize(pHashObj) == 0; } SHashObj *taosHashInit(size_t capacity, _hash_fn_t fn, bool update, SHashLockTypeE type) { if (fn == NULL) { @@ -251,7 +252,7 @@ SHashObj *taosHashInit(size_t capacity, _hash_fn_t fn, bool update, SHashLockTyp pHashObj->capacity = taosHashCapacity((int32_t)capacity); pHashObj->equalFp = memcmp; - pHashObj->hashFp = fn; + pHashObj->hashFp = fn; pHashObj->type = type; pHashObj->enableUpdate = update; @@ -305,7 +306,7 @@ int32_t taosHashGetSize(const SHashObj *pHashObj) { if (pHashObj == NULL) { return 0; } - return (int32_t)atomic_load_64((int64_t*)&pHashObj->size); + return (int32_t)atomic_load_64((int64_t *)&pHashObj->size); } int32_t taosHashPut(SHashObj *pHashObj, const void *key, size_t keyLen, const void *data, size_t size) { @@ -340,10 +341,9 @@ int32_t taosHashPut(SHashObj *pHashObj, const void *key, size_t keyLen, const vo } #endif - SHashNode* prev = NULL; + SHashNode *prev = NULL; while (pNode) { - if ((pNode->keyLen == keyLen) && - (*(pHashObj->equalFp))(GET_HASH_NODE_KEY(pNode), key, keyLen) == 0 && + if ((pNode->keyLen == keyLen) && (*(pHashObj->equalFp))(GET_HASH_NODE_KEY(pNode), key, keyLen) == 0 && pNode->removed == 0) { assert(pNode->hashVal == hashVal); break; @@ -391,27 +391,27 @@ int32_t taosHashPut(SHashObj *pHashObj, const void *key, size_t keyLen, const vo } } -static void* taosHashGetImpl(SHashObj *pHashObj, const void *key, size_t keyLen, void** d, int32_t* size, bool addRef); +static void *taosHashGetImpl(SHashObj *pHashObj, const void *key, size_t keyLen, void **d, int32_t *size, bool addRef); void *taosHashGet(SHashObj *pHashObj, const void *key, size_t keyLen) { - void* p = NULL; + void *p = NULL; return taosHashGetImpl(pHashObj, key, keyLen, &p, 0, false); } int32_t taosHashGetDup(SHashObj *pHashObj, const void *key, size_t keyLen, void *destBuf) { terrno = 0; - /*char* p = */taosHashGetImpl(pHashObj, key, keyLen, &destBuf, 0, false); + /*char* p = */ taosHashGetImpl(pHashObj, key, keyLen, &destBuf, 0, false); return terrno; } -int32_t taosHashGetDup_m(SHashObj *pHashObj, const void *key, size_t keyLen, void **destBuf, int32_t* size) { +int32_t taosHashGetDup_m(SHashObj *pHashObj, const void *key, size_t keyLen, void **destBuf, int32_t *size) { terrno = 0; - /*char* p = */taosHashGetImpl(pHashObj, key, keyLen, destBuf, size, false); + /*char* p = */ taosHashGetImpl(pHashObj, key, keyLen, destBuf, size, false); return terrno; } -void* taosHashGetImpl(SHashObj *pHashObj, const void *key, size_t keyLen, void** d, int32_t* size, bool addRef) { +void *taosHashGetImpl(SHashObj *pHashObj, const void *key, size_t keyLen, void **d, int32_t *size, bool addRef) { if (pHashObj == NULL || taosHashTableEmpty(pHashObj) || keyLen == 0 || key == NULL) { return NULL; } @@ -449,15 +449,15 @@ void* taosHashGetImpl(SHashObj *pHashObj, const void *key, size_t keyLen, void** if (size != NULL) { if (*d == NULL) { - *size = pNode->dataLen; + *size = pNode->dataLen; *d = taosMemoryCalloc(1, *size); if (*d == NULL) { terrno = TSDB_CODE_OUT_OF_MEMORY; return NULL; } } else if (*size < pNode->dataLen) { - *size = pNode->dataLen; - char* tmp = taosMemoryRealloc(*d, *size); + *size = pNode->dataLen; + char *tmp = taosMemoryRealloc(*d, *size); if (tmp == NULL) { terrno = TSDB_CODE_OUT_OF_MEMORY; return NULL; @@ -508,13 +508,12 @@ int32_t taosHashRemove(SHashObj *pHashObj, const void *key, size_t keyLen) { return -1; } - int code = -1; + int code = -1; SHashNode *pNode = pe->next; SHashNode *prevNode = NULL; while (pNode) { - if ((pNode->keyLen == keyLen) && - ((*(pHashObj->equalFp))(GET_HASH_NODE_KEY(pNode), key, keyLen) == 0) && + if ((pNode->keyLen == keyLen) && ((*(pHashObj->equalFp))(GET_HASH_NODE_KEY(pNode), key, keyLen) == 0) && pNode->removed == 0) { code = 0; // it is found @@ -525,6 +524,7 @@ int32_t taosHashRemove(SHashObj *pHashObj, const void *key, size_t keyLen) { pe->next = pNode->next; } else { prevNode->next = pNode->next; + ASSERT(prevNode->next != prevNode); } pe->num--; @@ -598,14 +598,14 @@ void taosHashCleanup(SHashObj *pHashObj) { } // for profile only -int32_t taosHashGetMaxOverflowLinkLength(const SHashObj *pHashObj){ +int32_t taosHashGetMaxOverflowLinkLength(const SHashObj *pHashObj) { if (pHashObj == NULL || taosHashTableEmpty(pHashObj)) { return 0; } int32_t num = 0; - taosHashRLock((SHashObj*) pHashObj); + taosHashRLock((SHashObj *)pHashObj); for (int32_t i = 0; i < pHashObj->size; ++i) { SHashEntry *pEntry = pHashObj->hashList[i]; @@ -616,7 +616,7 @@ int32_t taosHashGetMaxOverflowLinkLength(const SHashObj *pHashObj){ } } - taosHashRUnlock((SHashObj*) pHashObj); + taosHashRUnlock((SHashObj *)pHashObj); return num; } @@ -627,22 +627,22 @@ void taosHashTableResize(SHashObj *pHashObj) { int32_t newCapacity = (int32_t)(pHashObj->capacity << 1u); if (newCapacity > HASH_MAX_CAPACITY) { -// uDebug("current capacity:%zu, maximum capacity:%d, no resize applied due to limitation is reached", -// pHashObj->capacity, HASH_MAX_CAPACITY); + // uDebug("current capacity:%zu, maximum capacity:%d, no resize applied due to limitation is reached", + // pHashObj->capacity, HASH_MAX_CAPACITY); return; } int64_t st = taosGetTimestampUs(); - void *pNewEntryList = taosMemoryRealloc(pHashObj->hashList, sizeof(void *) * newCapacity); + void *pNewEntryList = taosMemoryRealloc(pHashObj->hashList, sizeof(void *) * newCapacity); if (pNewEntryList == NULL) { -// uDebug("cache resize failed due to out of memory, capacity remain:%zu", pHashObj->capacity); + // uDebug("cache resize failed due to out of memory, capacity remain:%zu", pHashObj->capacity); return; } pHashObj->hashList = pNewEntryList; size_t inc = newCapacity - pHashObj->capacity; - void * p = taosMemoryCalloc(inc, sizeof(SHashEntry)); + void *p = taosMemoryCalloc(inc, sizeof(SHashEntry)); for (int32_t i = 0; i < inc; ++i) { pHashObj->hashList[i + pHashObj->capacity] = (void *)((char *)p + i * sizeof(SHashEntry)); @@ -653,9 +653,9 @@ void taosHashTableResize(SHashObj *pHashObj) { pHashObj->capacity = newCapacity; for (int32_t idx = 0; idx < pHashObj->capacity; ++idx) { SHashEntry *pe = pHashObj->hashList[idx]; - SHashNode *pNode; - SHashNode *pNext; - SHashNode *pPrev = NULL; + SHashNode *pNode; + SHashNode *pNext; + SHashNode *pPrev = NULL; if (pe->num == 0) { assert(pe->next == NULL); @@ -688,24 +688,25 @@ void taosHashTableResize(SHashObj *pHashObj) { int64_t et = taosGetTimestampUs(); -// uDebug("hash table resize completed, new capacity:%d, load factor:%f, elapsed time:%fms", (int32_t)pHashObj->capacity, -// ((double)pHashObj->size) / pHashObj->capacity, (et - st) / 1000.0); + // uDebug("hash table resize completed, new capacity:%d, load factor:%f, elapsed time:%fms", + // (int32_t)pHashObj->capacity, + // ((double)pHashObj->size) / pHashObj->capacity, (et - st) / 1000.0); } SHashNode *doCreateHashNode(const void *key, size_t keyLen, const void *pData, size_t dsize, uint32_t hashVal) { - SHashNode *pNewNode = taosMemoryMalloc(sizeof(SHashNode) + keyLen + dsize); + SHashNode *pNewNode = taosMemoryMalloc(sizeof(SHashNode) + keyLen + dsize + 1); if (pNewNode == NULL) { terrno = TSDB_CODE_OUT_OF_MEMORY; return NULL; } - pNewNode->keyLen = (uint32_t)keyLen; + pNewNode->keyLen = (uint32_t)keyLen; pNewNode->hashVal = hashVal; pNewNode->dataLen = (uint32_t)dsize; - pNewNode->refCount= 1; + pNewNode->refCount = 1; pNewNode->removed = 0; - pNewNode->next = NULL; + pNewNode->next = NULL; memcpy(GET_HASH_NODE_DATA(pNewNode), pData, dsize); memcpy(GET_HASH_NODE_KEY(pNewNode), key, keyLen); @@ -719,6 +720,7 @@ void pushfrontNodeInEntryList(SHashEntry *pEntry, SHashNode *pNode) { pNode->next = pEntry->next; pEntry->next = pNode; + ASSERT(pNode->next != pNode); pEntry->num += 1; } @@ -727,11 +729,12 @@ size_t taosHashGetMemSize(const SHashObj *pHashObj) { return 0; } - return (pHashObj->capacity * (sizeof(SHashEntry) + sizeof(void*))) + sizeof(SHashNode) * taosHashGetSize(pHashObj) + sizeof(SHashObj); + return (pHashObj->capacity * (sizeof(SHashEntry) + sizeof(void *))) + sizeof(SHashNode) * taosHashGetSize(pHashObj) + + sizeof(SHashObj); } -void *taosHashGetKey(void *data, size_t* keyLen) { - SHashNode * node = GET_HASH_PNODE(data); +void *taosHashGetKey(void *data, size_t *keyLen) { + SHashNode *node = GET_HASH_PNODE(data); if (keyLen != NULL) { *keyLen = node->keyLen; } @@ -751,8 +754,7 @@ static void *taosHashReleaseNode(SHashObj *pHashObj, void *p, int *slot) { SHashNode *pNode = pe->next; while (pNode) { - if (pNode == pOld) - break; + if (pNode == pOld) break; prevNode = pNode; pNode = pNode->next; @@ -766,11 +768,16 @@ static void *taosHashReleaseNode(SHashObj *pHashObj, void *p, int *slot) { } atomic_sub_fetch_16(&pOld->refCount, 1); - if (pOld->refCount <=0) { + if (pOld->refCount <= 0) { if (prevNode) { prevNode->next = pOld->next; + ASSERT(prevNode->next != prevNode); } else { pe->next = pOld->next; + SHashNode* x = pe->next; + if (x != NULL) { + ASSERT(x->next != x); + } } pe->num--; @@ -778,7 +785,7 @@ static void *taosHashReleaseNode(SHashObj *pHashObj, void *p, int *slot) { FREE_HASH_NODE(pHashObj->freeFp, pOld); } } else { -// uError("pNode:%p data:%p is not there!!!", pNode, p); + // uError("pNode:%p data:%p is not there!!!", pNode, p); } return pNode; @@ -787,7 +794,7 @@ static void *taosHashReleaseNode(SHashObj *pHashObj, void *p, int *slot) { void *taosHashIterate(SHashObj *pHashObj, void *p) { if (pHashObj == NULL) return NULL; - int slot = 0; + int slot = 0; char *data = NULL; // only add the read lock to disable the resize process @@ -865,9 +872,9 @@ void taosHashCancelIterate(SHashObj *pHashObj, void *p) { taosHashRUnlock(pHashObj); } -//TODO remove it +// TODO remove it void *taosHashAcquire(SHashObj *pHashObj, const void *key, size_t keyLen) { - void* p = NULL; + void *p = NULL; return taosHashGetImpl(pHashObj, key, keyLen, &p, 0, true); } diff --git a/source/util/src/tpagedbuf.c b/source/util/src/tpagedbuf.c index 1ad12ef43a0154aa1df6ab748701542cba34aaba..00f123370747fcc29eddbb9ad053514134d3bc8f 100644 --- a/source/util/src/tpagedbuf.c +++ b/source/util/src/tpagedbuf.c @@ -187,9 +187,6 @@ static char* doFlushPageToDisk(SDiskbasedBuf* pBuf, SPageInfo* pg) { } } else { // NOTE: the size may be -1, the this recycle page has not been flushed to disk yet. size = pg->length; - if (size == -1) { - printf("----\n"); - } } ASSERT(size > 0 || (pg->offset == -1 && pg->length == -1)); diff --git a/source/util/src/tqueue.c b/source/util/src/tqueue.c index 5e206f3e6e0a55895089312c5172790713fee42a..6a10794ea154306f3c26b9666482a7c3a5b61958 100644 --- a/source/util/src/tqueue.c +++ b/source/util/src/tqueue.c @@ -162,7 +162,7 @@ void *taosAllocateQitem(int32_t size, EQItype itype) { uTrace("item:%p, node:%p is allocated", pNode->item, pNode); } - return (void *)pNode->item; + return pNode->item; } void taosFreeQitem(void *pItem) { diff --git a/source/util/src/tutil.c b/source/util/src/tutil.c index adb6a37ba737fbf1ccafe35231853f8a2fc4cfc8..0534eb3462f92a6ae4252efed389b0d309412c01 100644 --- a/source/util/src/tutil.c +++ b/source/util/src/tutil.c @@ -52,7 +52,7 @@ size_t strtrim(char *z) { int32_t j = 0; int32_t delta = 0; - while (z[j] == ' ') { + while (isspace(z[j])) { ++j; } @@ -65,9 +65,9 @@ size_t strtrim(char *z) { int32_t stop = 0; while (z[j] != 0) { - if (z[j] == ' ' && stop == 0) { + if (isspace(z[j]) && stop == 0) { stop = j; - } else if (z[j] != ' ' && stop != 0) { + } else if (!isspace(z[j]) && stop != 0) { stop = 0; } diff --git a/source/util/test/cfgTest.cpp b/source/util/test/cfgTest.cpp index cd13ebe8ae3a07db3cd75ec39df3f5795566d1dc..c5d9e830d2ece7229af3d18bd8f4e5613c63854e 100644 --- a/source/util/test/cfgTest.cpp +++ b/source/util/test/cfgTest.cpp @@ -59,7 +59,7 @@ TEST_F(CfgTest, 02_Basic) { EXPECT_EQ(cfgAddInt64(pConfig, "test_int64", 2, 0, 16, 0), 0); EXPECT_EQ(cfgAddFloat(pConfig, "test_float", 3, 0, 16, 0), 0); EXPECT_EQ(cfgAddString(pConfig, "test_string", "4", 0), 0); - EXPECT_EQ(cfgAddDir(pConfig, "test_dir", "/tmp", 0), 0); + EXPECT_EQ(cfgAddDir(pConfig, "test_dir", TD_TMP_DIR_PATH, 0), 0); EXPECT_EQ(cfgGetSize(pConfig), 6); @@ -126,7 +126,7 @@ TEST_F(CfgTest, 02_Basic) { EXPECT_EQ(pItem->stype, CFG_STYPE_DEFAULT); EXPECT_EQ(pItem->dtype, CFG_DTYPE_DIR); EXPECT_STREQ(pItem->name, "test_dir"); - EXPECT_STREQ(pItem->str, "/tmp"); + EXPECT_STREQ(pItem->str, TD_TMP_DIR_PATH); cfgCleanup(pConfig); } diff --git a/source/util/test/pageBufferTest.cpp b/source/util/test/pageBufferTest.cpp index 5ad3cb42aa1bfd9061a594c3ba412508b6219767..eaf198a483aa5e3e90595d2417516aa53f754331 100644 --- a/source/util/test/pageBufferTest.cpp +++ b/source/util/test/pageBufferTest.cpp @@ -13,7 +13,7 @@ namespace { // simple test void simpleTest() { SDiskbasedBuf* pBuf = NULL; - int32_t ret = createDiskbasedBuf(&pBuf, 1024, 4096, "", "/tmp/"); + int32_t ret = createDiskbasedBuf(&pBuf, 1024, 4096, "", TD_TMP_DIR_PATH); int32_t pageId = 0; int32_t groupId = 0; @@ -57,7 +57,7 @@ void simpleTest() { void writeDownTest() { SDiskbasedBuf* pBuf = NULL; - int32_t ret = createDiskbasedBuf(&pBuf, 1024, 4*1024, "1", "/tmp/"); + int32_t ret = createDiskbasedBuf(&pBuf, 1024, 4*1024, "1", TD_TMP_DIR_PATH); int32_t pageId = 0; int32_t writePageId = 0; @@ -106,7 +106,7 @@ void writeDownTest() { void recyclePageTest() { SDiskbasedBuf* pBuf = NULL; - int32_t ret = createDiskbasedBuf(&pBuf, 1024, 4*1024, "1", "/tmp/"); + int32_t ret = createDiskbasedBuf(&pBuf, 1024, 4*1024, "1", TD_TMP_DIR_PATH); int32_t pageId = 0; int32_t writePageId = 0; diff --git a/source/util/test/procTest.cpp b/source/util/test/procTest.cpp index af53ddcea5e705b9e8ae50908ac777bdcce6da47..53d3fa2c4bc118d57d9a8d08ecc4810f83aa5eda 100644 --- a/source/util/test/procTest.cpp +++ b/source/util/test/procTest.cpp @@ -38,9 +38,9 @@ class UtilTesProc : public ::testing::Test { head.noResp = 3; head.persistHandle = 4; - taosRemoveDir("/tmp/td"); - taosMkDir("/tmp/td"); - tstrncpy(tsLogDir, "/tmp/td", PATH_MAX); + taosRemoveDir(TD_TMP_DIR_PATH "td"); + taosMkDir(TD_TMP_DIR_PATH "td"); + tstrncpy(tsLogDir, TD_TMP_DIR_PATH "td", PATH_MAX); if (taosInitLog("taosdlog", 1) != 0) { printf("failed to init log file\n"); } diff --git a/tests/pytest/util/dnodes.py b/tests/pytest/util/dnodes.py index c78efb4e8dbcc95c71f5a863fcd93413fcdf1736..9190943dfd25169e9989ce0112242fd046d6e285 100644 --- a/tests/pytest/util/dnodes.py +++ b/tests/pytest/util/dnodes.py @@ -264,7 +264,7 @@ class TDDnode: cmd = "nohup %s -c %s > /dev/null 2>&1 & " % ( binPath, self.cfgDir) else: - valgrindCmdline = "valgrind --tool=memcheck --leak-check=full --show-reachable=no --track-origins=yes --show-leak-kinds=all -v --workaround-gcc296-bugs=yes" + valgrindCmdline = "valgrind --log-file=\"%s/../log/valgrind.log\" --tool=memcheck --leak-check=full --show-reachable=no --track-origins=yes --show-leak-kinds=all -v --workaround-gcc296-bugs=yes"%self.cfgDir cmd = "nohup %s %s -c %s 2>&1 & " % ( valgrindCmdline, binPath, self.cfgDir) @@ -325,7 +325,7 @@ class TDDnode: cmd = "nohup %s -c %s > /dev/null 2>&1 & " % ( binPath, self.cfgDir) else: - valgrindCmdline = "valgrind --tool=memcheck --leak-check=full --show-reachable=no --track-origins=yes --show-leak-kinds=all -v --workaround-gcc296-bugs=yes" + valgrindCmdline = "valgrind --log-file=\"%s/../log/valgrind.log\" --tool=memcheck --leak-check=full --show-reachable=no --track-origins=yes --show-leak-kinds=all -v --workaround-gcc296-bugs=yes"%self.cfgDir cmd = "nohup %s %s -c %s 2>&1 & " % ( valgrindCmdline, binPath, self.cfgDir) diff --git a/tests/script/api/batchprepare.c b/tests/script/api/batchprepare.c index ba1cd00fcb102bf6e0fbcb29c7655a6cc0d7357a..2ded58a979ad16e06f03ab8d4f828f1c10731df3 100644 --- a/tests/script/api/batchprepare.c +++ b/tests/script/api/batchprepare.c @@ -1825,10 +1825,12 @@ int queryColumnTest(TAOS_STMT *stmt, TAOS *taos) { if (bpBindParam(stmt, data.pBind + n * gCurCase->bindColNum)) { exit(1); } - - if (taos_stmt_add_batch(stmt)) { - printf("!!!taos_stmt_add_batch error:%s\n", taos_stmt_errstr(stmt)); - exit(1); + + if (rand() % 2 == 0) { + if (taos_stmt_add_batch(stmt)) { + printf("!!!taos_stmt_add_batch error:%s\n", taos_stmt_errstr(stmt)); + exit(1); + } } if (taos_stmt_execute(stmt) != 0) { @@ -1871,10 +1873,12 @@ int queryMiscTest(TAOS_STMT *stmt, TAOS *taos) { if (bpBindParam(stmt, data.pBind + n * gCurCase->bindColNum)) { exit(1); } - - if (taos_stmt_add_batch(stmt)) { - printf("!!!taos_stmt_add_batch error:%s\n", taos_stmt_errstr(stmt)); - exit(1); + + if (rand() % 2 == 0) { + if (taos_stmt_add_batch(stmt)) { + printf("!!!taos_stmt_add_batch error:%s\n", taos_stmt_errstr(stmt)); + exit(1); + } } if (taos_stmt_execute(stmt) != 0) { diff --git a/tests/script/jenkins/basic.txt b/tests/script/jenkins/basic.txt index 18fe3b9afe9776b1d9d0e15767f307265e229ef6..45fa037458b4c415563bb45c62b7a163495b582a 100644 --- a/tests/script/jenkins/basic.txt +++ b/tests/script/jenkins/basic.txt @@ -28,6 +28,8 @@ ./test.sh -f tsim/insert/basic1.sim ./test.sh -f tsim/insert/backquote.sim ./test.sh -f tsim/insert/null.sim +./test.sh -f tsim/insert/update0.sim +./test.sh -f tsim/insert/commit-merge0.sim # ---- parser ./test.sh -f tsim/parser/groupby-basic.sim @@ -61,9 +63,9 @@ # ---- table ./test.sh -f tsim/table/basic1.sim -# ---- tstream -./test.sh -f tsim/tstream/basic0.sim -./test.sh -f tsim/tstream/basic1.sim +# ---- stream +./test.sh -f tsim/stream/basic0.sim +./test.sh -f tsim/stream/basic1.sim # ---- transaction ./test.sh -f tsim/trans/create_db.sim diff --git a/tests/script/tsim/insert/commit-merge0.sim b/tests/script/tsim/insert/commit-merge0.sim new file mode 100644 index 0000000000000000000000000000000000000000..adbd1904b2071ccc68f978ea1c2da40e208adc93 --- /dev/null +++ b/tests/script/tsim/insert/commit-merge0.sim @@ -0,0 +1,262 @@ +system sh/stop_dnodes.sh +system sh/deploy.sh -n dnode1 -i 1 +system sh/exec.sh -n dnode1 -s start +sleep 50 +sql connect + +print =============== create database +sql create database db days 300 keep 365000d,365000d,365000d +sql show databases +if $rows != 3 then + return -1 +endi + +print $data00 $data01 $data02 + +sql use db +sql create table stb1(ts timestamp, c6 double) tags (t1 int); +sql create table ct1 using stb1 tags ( 1 ); +sql create table ct2 using stb1 tags ( 2 ); +sql create table ct3 using stb1 tags ( 3 ); +sql create table ct4 using stb1 tags ( 4 ); +sql insert into ct1 values ('2022-05-01 18:30:27.001', 0.0); +sql insert into ct4 values ('2022-04-28 18:30:27.002', 0.0); +sql insert into ct1 values ('2022-05-01 18:30:17.003', 11.11); +sql insert into ct4 values ('2022-02-01 18:30:27.004', 11.11); +sql insert into ct1 values ('2022-05-01 18:30:07.005', 22.22); +sql insert into ct4 values ('2021-11-01 18:30:27.006', 22.22); +sql insert into ct1 values ('2022-05-01 18:29:27.007', 33.33); +sql insert into ct4 values ('2022-08-01 18:30:27.008', 33.33); +sql insert into ct1 values ('2022-05-01 18:20:27.009', 44.44); +sql insert into ct4 values ('2021-05-01 18:30:27.010', 44.44); +sql insert into ct1 values ('2022-05-01 18:21:27.011', 55.55); +sql insert into ct4 values ('2021-01-01 18:30:27.012', 55.55); +sql insert into ct1 values ('2022-05-01 18:22:27.013', 66.66); +sql insert into ct4 values ('2020-06-01 18:30:27.014', 66.66); +sql insert into ct1 values ('2022-05-01 18:28:37.015', 77.77); +sql insert into ct4 values ('2020-05-01 18:30:27.016', 77.77); +sql insert into ct1 values ('2022-05-01 18:29:17.017', 88.88); +sql insert into ct4 values ('2019-05-01 18:30:27.018', 88.88); +sql insert into ct1 values ('2022-05-01 18:30:20.019', 0); +sql insert into ct1 values ('2022-05-01 18:30:47.020', -99.99); +sql insert into ct1 values ('2022-05-01 18:30:49.021', NULL); +sql insert into ct1 values ('2022-05-01 18:30:51.022', -99.99); +sql insert into ct4 values ('2018-05-01 18:30:27.023', NULL) ; +sql insert into ct4 values ('2021-03-01 18:30:27.024', NULL) ; +sql insert into ct4 values ('2022-08-01 18:30:27.025', NULL) ; + +print =============== select * from ct1 - memory +sql select * from stb1; +if $rows != 25 then + print rows = $rows != 25 + return -1 +endi + + +print =============== stop and restart taosd + +$reboot_max = 10; + +$reboot_cnt = 0 + +reboot_and_check: + +system sh/exec.sh -n dnode1 -s stop -x SIGINT +system sh/exec.sh -n dnode1 -s start + +$loop_cnt = 0 +check_dnode_ready: + $loop_cnt = $loop_cnt + 1 + sleep 200 + if $loop_cnt == 10 then + print ====> dnode not ready! + return -1 + endi +sql show dnodes +print ===> $rows $data00 $data01 $data02 $data03 $data04 $data05 +if $data00 != 1 then + return -1 +endi +if $data04 != ready then + goto check_dnode_ready +endi + +print =============== insert duplicated records to memory - loop $reboot_max - $reboot_cnt +sql use db +sql insert into ct1 values ('2022-05-01 18:30:27.001', 0.0); +sql insert into ct4 values ('2022-04-28 18:30:27.002', 0.0); +sql insert into ct1 values ('2022-05-01 18:30:17.003', 11.11); +sql insert into ct4 values ('2022-02-01 18:30:27.004', 11.11); +sql insert into ct1 values ('2022-05-01 18:30:07.005', 22.22); +sql insert into ct4 values ('2021-11-01 18:30:27.006', 22.22); +sql insert into ct1 values ('2022-05-01 18:29:27.007', 33.33); +sql insert into ct4 values ('2022-08-01 18:30:27.008', 33.33); +sql insert into ct1 values ('2022-05-01 18:20:27.009', 44.44); +sql insert into ct4 values ('2021-05-01 18:30:27.010', 44.44); +sql insert into ct1 values ('2022-05-01 18:21:27.011', 55.55); +sql insert into ct4 values ('2021-01-01 18:30:27.012', 55.55); +sql insert into ct1 values ('2022-05-01 18:22:27.013', 66.66); +sql insert into ct4 values ('2020-06-01 18:30:27.014', 66.66); +sql insert into ct1 values ('2022-05-01 18:28:37.015', 77.77); +sql insert into ct4 values ('2020-05-01 18:30:27.016', 77.77); +sql insert into ct1 values ('2022-05-01 18:29:17.017', 88.88); +sql insert into ct4 values ('2019-05-01 18:30:27.018', 88.88); +sql insert into ct1 values ('2022-05-01 18:30:20.019', 0); +sql insert into ct1 values ('2022-05-01 18:30:47.020', -99.99); +sql insert into ct1 values ('2022-05-01 18:30:49.021', NULL); +sql insert into ct1 values ('2022-05-01 18:30:51.022', -99.99); +sql insert into ct4 values ('2018-05-01 18:30:27.023', NULL) ; +sql insert into ct4 values ('2021-03-01 18:30:27.024', NULL) ; +sql insert into ct4 values ('2022-08-01 18:30:27.025', NULL) ; + +print =============== select * from ct1 - merge memory and file - loop $reboot_max - $reboot_cnt +sql select * from ct1; +if $rows != 13 then + print rows = $rows != 13 + return -1 +endi +print $data[0][0] $data[0][1] +print $data[1][0] $data[1][1] +print $data[2][0] $data[2][1] +print $data[3][0] $data[3][1] +print $data[4][0] $data[4][1] +print $data[5][0] $data[5][1] +print $data[6][0] $data[6][1] +print $data[7][0] $data[7][1] +print $data[8][0] $data[8][1] +print $data[9][0] $data[9][1] +print $data[10][0] $data[10][1] +print $data[11][0] $data[11][1] +print $data[12][0] $data[12][1] + +if $data[0][1] != 44.440000000 then + print $data[0][1] != 44.440000000 + return -1 +endi +if $data[1][1] != 55.550000000 then + print $data[1][1] != 55.550000000 + return -1 +endi +if $data[2][1] != 66.660000000 then + print $data[2][1] != 66.660000000 + return -1 +endi +if $data[3][1] != 77.770000000 then + print $data[3][1] != 77.770000000 + return -1 +endi +if $data[4][1] != 88.880000000 then + print $data[4][1] != 88.880000000 + return -1 +endi +if $data[5][1] != 33.330000000 then + print $data[5][1] != 33.330000000 + return -1 +endi +if $data[6][1] != 22.220000000 then + print $data[6][1] != 22.220000000 + return -1 +endi +if $data[7][1] != 11.110000000 then + print $data[7][1] != 11.110000000 + return -1 +endi +if $data[8][1] != 0.000000000 then + print $data[8][1] != 0.000000000 + return -1 +endi +if $data[9][1] != 0.000000000 then + print $data[9][1] != 0.000000000 + return -1 +endi +if $data[10][1] != -99.990000000 then + print $data[10][1] != -99.990000000 + return -1 +endi +if $data[11][1] != NULL then + print $data[11][1] != NULL + return -1 +endi +if $data[12][1] != -99.990000000 then + print $data[12][1] != -99.990000000 + return -1 +endi + +print =============== select * from ct4 - merge memory and file - loop $reboot_max - $reboot_cnt +sql select * from ct4; +if $rows != 12 then + print rows = $rows != 12 + return -1 +endi + +print $data[0][0] $data[0][1] +print $data[1][0] $data[1][1] +print $data[2][0] $data[2][1] +print $data[3][0] $data[3][1] +print $data[4][0] $data[4][1] +print $data[5][0] $data[5][1] +print $data[6][0] $data[6][1] +print $data[7][0] $data[7][1] +print $data[8][0] $data[8][1] +print $data[9][0] $data[9][1] +print $data[10][0] $data[10][1] +print $data[11][0] $data[11][1] + +if $data[0][1] != NULL then + print $data[0][1] != NULL + return -1 +endi +if $data[1][1] != 88.880000000 then + print $data[1][1] != 88.880000000 + return -1 +endi +if $data[2][1] != 77.770000000 then + print $data[2][1] != 77.770000000 + return -1 +endi +if $data[3][1] != 66.660000000 then + print $data[3][1] != 66.660000000 + return -1 +endi +if $data[4][1] != 55.550000000 then + print $data[4][1] != 55.550000000 + return -1 +endi +if $data[5][1] != NULL then + print $data[5][1] != NULL + return -1 +endi +if $data[6][1] != 44.440000000 then + print $data[6][1] != 44.440000000 + return -1 +endi +if $data[7][1] != 22.220000000 then + print $data[7][1] != 22.220000000 + return -1 +endi +if $data[8][1] != 11.110000000 then + print $data[8][1] != 11.110000000 + return -1 +endi +if $data[9][1] != 0.000000000 then + print $data[9][1] != 0.000000000 + return -1 +endi +if $data[10][1] != 33.330000000 then + print $data[10][1] != 33.330000000 + return -1 +endi +if $data[11][1] != NULL then + print $data[11][1] != NULL + return -1 +endi + + +if $reboot_cnt > $reboot_max then + print reboot_cnt $reboot_cnt > reboot_max $reboot_max + return 0 +else + print reboot_cnt $reboot_cnt <= reboot_max $reboot_max + $reboot_cnt = $reboot_cnt + 1 + goto reboot_and_check +endi diff --git a/tests/script/tsim/insert/update0.sim b/tests/script/tsim/insert/update0.sim new file mode 100644 index 0000000000000000000000000000000000000000..3cb5e4008e3a57e3178721b7e3f5458ef07be52b --- /dev/null +++ b/tests/script/tsim/insert/update0.sim @@ -0,0 +1,247 @@ +system sh/stop_dnodes.sh +system sh/deploy.sh -n dnode1 -i 1 +system sh/exec.sh -n dnode1 -s start +sleep 50 +sql connect + +print =============== create database +sql create database d0 keep 365000d,365000d,365000d +sql use d0 + +print =============== create super table and register rsma +sql create table if not exists stb (ts timestamp, c1 int) tags (city binary(20),district binary(20)) rollup(min) file_factor 0.1 delay 2; + +sql show stables +if $rows != 1 then + return -1 +endi + +print =============== create child table +sql create table ct1 using stb tags("BeiJing", "ChaoYang") +sql create table ct2 using stb tags("BeiJing", "HaiDian") + +sql show tables +if $rows != 2 then + return -1 +endi + +print =============== step3-1 insert records into ct1 +sql insert into ct1 values('2022-05-03 16:59:00.010', 10); +sql insert into ct1 values('2022-05-03 16:59:00.011', 11); +sql insert into ct1 values('2022-05-03 16:59:00.016', 16); +sql insert into ct1 values('2022-05-03 16:59:00.016', 17); +sql insert into ct1 values('2022-05-03 16:59:00.020', 20); +sql insert into ct1 values('2022-05-03 16:59:00.016', 18); +sql insert into ct1 values('2022-05-03 16:59:00.021', 21); +sql insert into ct1 values('2022-05-03 16:59:00.022', 22); + +print =============== step3-1 query records of ct1 from memory +sql select * from ct1; +print $data00 $data01 +print $data10 $data11 +print $data20 $data21 +print $data30 $data31 +print $data40 $data41 +print $data50 $data51 + +if $rows != 6 then + print rows $rows != 6 + return -1 +endi + +if $data01 != 10 then + print data01 $data01 != 10 + return -1 +endi + +if $data21 != 18 then + print data21 $data21 != 18 + return -1 +endi + +if $data51 != 22 then + print data51 $data51 != 22 + return -1 +endi + +print =============== step3-1 insert records into ct2 +sql insert into ct2 values('2022-03-02 16:59:00.010', 1),('2022-03-02 16:59:00.010',11),('2022-04-01 16:59:00.011',2),('2022-04-01 16:59:00.011',5),('2022-03-06 16:59:00.013',7); +sql insert into ct2 values('2022-03-02 16:59:00.010', 3),('2022-03-02 16:59:00.010',33),('2022-04-01 16:59:00.011',4),('2022-04-01 16:59:00.011',6),('2022-03-06 16:59:00.013',8); +sql insert into ct2 values('2022-03-02 16:59:00.010', 103),('2022-03-02 16:59:00.010',303),('2022-04-01 16:59:00.011',40),('2022-04-01 16:59:00.011',60),('2022-03-06 16:59:00.013',80); + +print =============== step3-1 query records of ct2 from memory +sql select * from ct2; +print $data00 $data01 +print $data10 $data11 +print $data20 $data21 + +if $rows != 3 then + print rows $rows != 3 + return -1 +endi + +if $data01 != 103 then + print data01 $data01 != 103 + return -1 +endi + +if $data11 != 80 then + print data11 $data11 != 80 + return -1 +endi + +if $data21 != 40 then + print data21 $data21 != 40 + return -1 +endi + +#==================== reboot to trigger commit data to file +system sh/exec.sh -n dnode1 -s stop -x SIGINT +system sh/exec.sh -n dnode1 -s start + +$loop_cnt = 0 +check_dnode_ready: + $loop_cnt = $loop_cnt + 1 + sleep 200 + if $loop_cnt == 10 then + print ====> dnode not ready! + return -1 + endi +sql show dnodes +print ===> $rows $data00 $data01 $data02 $data03 $data04 $data05 +if $data00 != 1 then + return -1 +endi +if $data04 != ready then + goto check_dnode_ready +endi + +print =============== step3-2 query records of ct1 from file +sql select * from ct1; +print $data00 $data01 +print $data10 $data11 +print $data20 $data21 +print $data30 $data31 +print $data40 $data41 +print $data50 $data51 + +if $rows != 6 then + print rows $rows != 6 + return -1 +endi + +if $data01 != 10 then + print data01 $data01 != 10 + return -1 +endi + +if $data21 != 18 then + print data21 $data21 != 18 + return -1 +endi + +if $data51 != 22 then + print data51 $data51 != 22 + return -1 +endi + +print =============== step3-2 query records of ct2 from file +sql select * from ct2; +print $data00 $data01 +print $data10 $data11 +print $data20 $data21 + +if $rows != 3 then + print rows $rows != 3 + return -1 +endi + +if $data01 != 103 then + print data01 $data01 != 103 + return -1 +endi + +if $data11 != 80 then + print data11 $data11 != 80 + return -1 +endi + +if $data21 != 40 then + print data21 $data21 != 40 + return -1 +endi + +print =============== step3-3 query records of ct1 from memory and file(merge) +sql insert into ct1 values('2022-05-03 16:59:00.010', 100); +sql insert into ct1 values('2022-05-03 16:59:00.022', 200); +sql insert into ct1 values('2022-05-03 16:59:00.016', 160); + +sql select * from ct1; +print $data00 $data01 +print $data10 $data11 +print $data20 $data21 +print $data30 $data31 +print $data40 $data41 +print $data50 $data51 + +if $rows != 6 then + print rows $rows != 6 + return -1 +endi + +if $data01 != 100 then + print data01 $data01 != 100 + return -1 +endi + +if $data21 != 160 then + print data21 $data21 != 160 + return -1 +endi + +if $data51 != 200 then + print data51 $data51 != 200 + return -1 +endi + +print =============== step3-3 query records of ct2 from memory and file(merge) +sql insert into ct2(ts) values('2022-04-02 16:59:00.016'); +sql insert into ct2 values('2022-03-06 16:59:00.013', NULL); +sql insert into ct2 values('2022-03-01 16:59:00.016', 10); +sql insert into ct2(ts) values('2022-04-01 16:59:00.011'); +sql select * from ct2; +print $data00 $data01 +print $data10 $data11 +print $data20 $data21 +print $data30 $data31 +print $data40 $data41 + +if $rows != 5 then + print rows $rows != 5 + return -1 +endi + +if $data01 != 10 then + print data01 $data01 != 10 + return -1 +endi + +if $data11 != 103 then + print data11 $data11 != 103 + return -1 +endi + +if $data21 != NULL then + print data21 $data21 != NULL + return -1 +endi + +if $data31 != 40 then + print data31 $data31 != 40 + return -1 +endi + +if $data41 != NULL then + print data41 $data41 != NULL + return -1 +endi \ No newline at end of file diff --git a/tests/script/tsim/stable/add_column.sim b/tests/script/tsim/stable/add_column.sim new file mode 100644 index 0000000000000000000000000000000000000000..0b2df509f9f4c32f60fd073076517911d1f84f3e --- /dev/null +++ b/tests/script/tsim/stable/add_column.sim @@ -0,0 +1,141 @@ +system sh/stop_dnodes.sh +system sh/deploy.sh -n dnode1 -i 1 +system sh/exec.sh -n dnode1 -s start +sql connect + +print ========== prepare stb and ctb +sql create database db vgroups 1 +sql create table db.stb (ts timestamp, c1 int, c2 binary(4)) tags(t1 int, t2 float, t3 binary(16)) comment "abd" +sql create table db.ctb using db.stb tags(101, 102, "103") +sql insert into db.ctb values(now, 1, "2") + +sql show db.stables +if $rows != 1 then + return -1 +endi +if $data[0][0] != stb then + return -1 +endi +if $data[0][1] != db then + return -1 +endi +if $data[0][3] != 3 then + return -1 +endi +if $data[0][4] != 3 then + return -1 +endi +if $data[0][6] != abd then + return -1 +endi + +sql show db.tables +if $rows != 1 then + return -1 +endi +if $data[0][0] != ctb then + return -1 +endi +if $data[0][1] != db then + return -1 +endi +if $data[0][3] != 3 then + return -1 +endi +if $data[0][4] != stb then + return -1 +endi +if $data[0][6] != 2 then + return -1 +endi +if $data[0][9] != CHILD_TABLE then + return -1 +endi + +sql select * from db.stb +if $rows != 1 then + return -1 +endi +if $data[0][1] != 1 then + return -1 +endi +if $data[0][2] != 2 then + return -1 +endi +if $data[0][3] != 101 then + return -1 +endi + +print ========== add column c3 +sql alter table db.stb add column c3 int +sql show db.stables +if $data[0][3] != 4 then + return -1 +endi + +sql show db.tables +if $data[0][3] != 4 then + return -1 +endi + +sql select * from db.stb +sql select * from db.stb +print $data[0][0] $data[0][1] $data[0][2] $data[0][3] $data[0][4] $data[0][5] $data[0][6] +if $rows != 1 then + return -1 +endi +if $data[0][1] != 1 then + return -1 +endi +if $data[0][2] != 2 then + return -1 +endi +if $data[0][3] != NULL then + return -1 +endi +if $data[0][4] != 101 then + return -1 +endi + +sql insert into db.ctb values(now+1s, 1, 2, 3) +sql select * from db.stb +print $data[0][0] $data[0][1] $data[0][2] $data[0][3] $data[0][4] $data[0][5] $data[0][6] +print $data[1][0] $data[1][1] $data[1][2] $data[1][3] $data[1][4] $data[1][5] $data[1][6] + +if $rows != 2 then + return -1 +endi +if $data[0][1] != 1 then + return -1 +endi +if $data[0][2] != 2 then + return -1 +endi +if $data[0][3] != NULL then + return -1 +endi +if $data[0][4] != 101 then + return -1 +endi +if $data[1][1] != 1 then + return -1 +endi +if $data[2][2] != 2 then + return -1 +endi +if $data[1][3] != 3 then + return -1 +endi +if $data[1][4] != 101 then + return -1 +endi + +print ========== add column c4 +sql alter table db.stb add column c4 bigint +sql insert into db.ctb values(now+2s, 1, 2, 3, 4) +sql select * from db.stb +sql select * from db.stb +print $data[0][0] $data[0][1] $data[0][2] $data[0][3] $data[0][4] $data[0][5] $data[0][6] +print $data[1][0] $data[1][1] $data[1][2] $data[1][3] $data[1][4] $data[1][5] $data[1][6] +print $data[1][0] $data[1][1] $data[1][2] $data[1][3] $data[1][4] $data[1][5] $data[1][6] + diff --git a/tests/script/tsim/stable/metrics.sim b/tests/script/tsim/stable/metrics.sim index c49de0e8038bc5583f65d6f2e696b5eb9f1a5db5..e68d95511cfd3c4ea556e34ffed5111f05064405 100644 --- a/tests/script/tsim/stable/metrics.sim +++ b/tests/script/tsim/stable/metrics.sim @@ -28,15 +28,17 @@ if $rows != 1 then return -1 endi -print =============== step2 -sql drop table $mt -sql show stables -if $rows != 0 then - return -1 -endi +#TODO OPEN THIS WHEN STABLE DELETE WORKS +#print =============== step2 +#sql drop table $mt +#sql show stables +#if $rows != 0 then +# return -1 +#endi -print =============== step3 -sql create table $mt (ts timestamp, speed int) TAGS(sp int) +#print =============== step3 +#sql create table $mt (ts timestamp, speed int) TAGS(sp int) +#TODO OPEN THIS WHEN STABLE DELETE WORKS sql show stables if $rows != 1 then @@ -134,4 +136,4 @@ if $rows != 2 then return -1 endi -system sh/exec.sh -n dnode1 -s stop -x SIGINT \ No newline at end of file +system sh/exec.sh -n dnode1 -s stop -x SIGINT diff --git a/tests/script/tsim/tstream/basic0.sim b/tests/script/tsim/stream/basic0.sim similarity index 100% rename from tests/script/tsim/tstream/basic0.sim rename to tests/script/tsim/stream/basic0.sim diff --git a/tests/script/tsim/tstream/basic1.sim b/tests/script/tsim/stream/basic1.sim similarity index 100% rename from tests/script/tsim/tstream/basic1.sim rename to tests/script/tsim/stream/basic1.sim diff --git a/tests/script/tsim/stream/basic2.sim b/tests/script/tsim/stream/basic2.sim new file mode 100644 index 0000000000000000000000000000000000000000..247d8f62eeae7cda38829db5c2c4fd6b2843c787 --- /dev/null +++ b/tests/script/tsim/stream/basic2.sim @@ -0,0 +1,112 @@ +system sh/stop_dnodes.sh +system sh/deploy.sh -n dnode1 -i 1 +system sh/exec.sh -n dnode1 -s start +sleep 50 +sql connect + +print =============== create database +sql create database d0 vgroups 1 +sql show databases +if $rows != 3 then + return -1 +endi + +print $data00 $data01 $data02 + +sql use d0 + +print =============== create super table, include column type for count/sum/min/max/first +sql create table if not exists stb (ts timestamp, k int) tags (a int) + +sql show stables +if $rows != 1 then + return -1 +endi + +print =============== create child table +sql create table ct1 using stb tags(1000) +sql create table ct2 using stb tags(2000) +sql create table ct3 using stb tags(3000) + +sql show tables +if $rows != 3 then + return -1 +endi + +sql create stream s1 trigger at_once into outstb as select _wstartts, min(k), max(k), sum(k) as sum_alias from ct1 interval(10m) + +sql show stables +if $rows != 2 then + return -1 +endi + +print =============== insert data + +sql insert into ct1 values('2022-05-08 03:42:00.000', 234) +sleep 100 + +#=================================================================== +print =============== query data from child table + +sql select `_wstartts`,`min(k)`,`max(k)`,sum_alias from outstb +print rows: $rows +print $data00 $data01 $data02 $data03 +if $rows != 1 then + return -1 +endi + +if $data01 != 234 then + return -1 +endi + +if $data02 != 234 then + return -1 +endi + +if $data03 != 234 then + return -1 +endi + +#=================================================================== +print =============== insert data + +sql insert into ct1 values('2022-05-08 03:57:00.000', -111) +sleep 100 + + +#=================================================================== +print =============== query data from child table + +sql select `_wstartts`,`min(k)`,`max(k)`,sum_alias from outstb +print rows: $rows +print $data00 $data01 $data02 $data03 +print $data10 $data11 $data12 $data13 +if $rows != 2 then + return -1 +endi + +if $data01 != 234 then + return -1 +endi + +if $data02 != 234 then + return -1 +endi + +if $data03 != 234 then + return -1 +endi + +if $data11 != -111 then + return -1 +endi + +if $data12 != -111 then + return -1 +endi + +if $data13 != -111 then + return -1 +endi + +system sh/exec.sh -n dnode1 -s stop -x SIGINT diff --git a/tests/script/tsim/sync/threeReplica1VgElectWihtInsert.sim b/tests/script/tsim/sync/threeReplica1VgElectWihtInsert.sim index f6996f1291f1a408145aaa143b32630a8023ed69..f568008a820c880628af0128bb848297d63d5ffe 100644 --- a/tests/script/tsim/sync/threeReplica1VgElectWihtInsert.sim +++ b/tests/script/tsim/sync/threeReplica1VgElectWihtInsert.sim @@ -71,7 +71,7 @@ sql create database db replica $replica vgroups $vgroups $loop_cnt = 0 check_db_ready: $loop_cnt = $loop_cnt + 1 -sleep 200 +sleep 20 if $loop_cnt == 10 then print ====> db not ready! return -1 @@ -83,7 +83,7 @@ print $data(db)[13] $data(db)[14] $data(db)[15] $data(db)[16] $data(db)[17] $dat if $rows != 3 then return -1 endi -if $data(db)[19] != nostrict then +if $data(db)[19] != ready then goto check_db_ready endi @@ -93,7 +93,7 @@ $loop_cnt = 0 check_vg_ready: $loop_cnt = $loop_cnt + 1 sleep 200 -if $loop_cnt == 40 then +if $loop_cnt == 300 then print ====> vgroups not ready! return -1 endi @@ -175,6 +175,7 @@ while $i < $tbNum endw $totalTblNum = $tbNum * 2 +print ====>totalTblNum:$totalTblNum sql show tables if $rows != $totalTblNum then return -1 @@ -226,7 +227,7 @@ $loop_cnt = 0 check_vg_ready_2: $loop_cnt = $loop_cnt + 1 sleep 200 -if $loop_cnt == 10 then +if $loop_cnt == 300 then print ====> vgroups switch fail!!! return -1 endi diff --git a/tests/script/tsim/testsuit.sim b/tests/script/tsim/testsuit.sim new file mode 100644 index 0000000000000000000000000000000000000000..e32abe4b7ff8850f9818113bed5f006c2182392e --- /dev/null +++ b/tests/script/tsim/testsuit.sim @@ -0,0 +1,79 @@ +#run user/pass_alter.sim +#run user/basic1.sim +#run user/privilege2.sim +#run user/user_len.sim +#run user/privilege1.sim +#run user/pass_len.sim +#run tstream/basic1.sim +#run tstream/basic0.sim +#run table/basic1.sim +#run trans/create_db.sim +#run stable/alter1.sim +#run stable/vnode3.sim +#run stable/metrics.sim +#run stable/show.sim +#run stable/values.sim +#run stable/dnode3.sim +#run stable/refcount.sim +#run stable/disk.sim +#run db/basic1.sim +#run db/basic3.sim +#run db/basic7.sim +#run db/basic6.sim +#run db/create_all_options.sim +#run db/basic2.sim +#run db/error1.sim +#run db/taosdlog.sim +#run db/alter_option.sim +#run mnode/basic1.sim +#run parser/fourArithmetic-basic.sim +#run parser/groupby-basic.sim +#run snode/basic1.sim +#run query/time_process.sim +#run query/stddev.sim +#run query/interval-offset.sim +#run query/charScalarFunction.sim +#run query/complex_select.sim +#run query/explain.sim +#run query/crash_sql.sim +#run query/diff.sim +#run query/complex_limit.sim +#run query/complex_having.sim +#run query/udf.sim +#run query/complex_group.sim +#run query/interval.sim +#run query/session.sim + +print ========> dead lock failed when 2 rows in outputCapacity +run query/scalarFunction.sim +run query/scalarNull.sim +run query/complex_where.sim +run tmq/basic1.sim +run tmq/basic4.sim +run tmq/basic1Of2Cons.sim +run tmq/prepareBasicEnv-1vgrp.sim +run tmq/topic.sim +run tmq/basic4Of2Cons.sim +run tmq/prepareBasicEnv-4vgrp.sim +run tmq/basic3.sim +run tmq/basic2Of2Cons.sim +run tmq/basic2.sim +run tmq/basic3Of2Cons.sim +run tmq/basic2Of2ConsOverlap.sim +run tmq/clearConsume.sim +run qnode/basic1.sim +run dnode/basic1.sim +run show/basic.sim +run insert/basic1.sim +run insert/basic0.sim +run insert/backquote.sim +run insert/null.sim +run sync/oneReplica1VgElectWithInsert.sim +run sync/threeReplica1VgElect.sim +run sync/oneReplica1VgElect.sim +run sync/insertDataByRunBack.sim +run sync/threeReplica1VgElectWihtInsert.sim +run sma/tsmaCreateInsertData.sim +run sma/rsmaCreateInsertQuery.sim +run valgrind/checkError.sim +run bnode/basic1.sim diff --git a/tests/script/tsim/valgrind/basic.sim b/tests/script/tsim/valgrind/basic.sim new file mode 100644 index 0000000000000000000000000000000000000000..0f11ae03134dbc97a2a1ae2464ee9b0aa6e953ea --- /dev/null +++ b/tests/script/tsim/valgrind/basic.sim @@ -0,0 +1,8 @@ +system sh/stop_dnodes.sh +system sh/deploy.sh -n dnode1 -i 1 +system sh/exec.sh -n dnode1 -s start +sql connect + +sql create database d0 vgroups 1; + +system sh/exec.sh -n dnode1 -s stop -x SIGINT diff --git a/tests/system-test/0-others/fsync.py b/tests/system-test/0-others/fsync.py new file mode 100644 index 0000000000000000000000000000000000000000..964550cdbc8eb5e9a6ce6b1c01884078b68263b8 --- /dev/null +++ b/tests/system-test/0-others/fsync.py @@ -0,0 +1,301 @@ +import datetime + +from util.log import * +from util.sql import * +from util.cases import * +from util.dnodes import * + +PRIMARY_COL = "ts" + +INT_COL = "c1" +BINT_COL = "c2" +SINT_COL = "c3" +TINT_COL = "c4" +FLOAT_COL = "c5" +DOUBLE_COL = "c6" +BOOL_COL = "c7" + +BINARY_COL = "c8" +NCHAR_COL = "c9" +TS_COL = "c10" + +NUM_COL = [ INT_COL, BINT_COL, SINT_COL, TINT_COL, FLOAT_COL, DOUBLE_COL, ] +CHAR_COL = [ BINARY_COL, NCHAR_COL, ] +BOOLEAN_COL = [ BOOL_COL, ] +TS_TYPE_COL = [ TS_COL, ] + +class TDTestCase: + + def init(self, conn, logSql): + tdLog.debug(f"start to excute {__file__}") + tdSql.init(conn.cursor()) + + def __kill_process(self, process_name): + killCmd = f"ps -ef|grep -w {process_name}| grep -v grep | awk '{{print $2}}' | xargs kill -TERM > /dev/null 2>&1" + + psCmd = f"ps -ef|grep -w {process_name}| grep -v grep | awk '{{print $2}}'" + while processID := subprocess.check_output(psCmd, shell=True): + os.system(killCmd) + time.sleep(1) + + def test_fsync_current(self): + wal_index = 0 + fsync_index = 0 + tdSql.query("show databases") + for i in range(tdSql.queryCols): + if tdSql.cursor.description[i][0] == "wal": + wal_index = i + if tdSql.cursor.description[i][0] == "fsync": + fsync_index = i + + tdSql.execute("drop database if exists db1") + tdSql.execute("create database db1 wal 1") + tdSql.query("show databases") + for i in range(tdSql.queryRows): + if tdSql.queryResult[i][0] == "db1": + tdSql.checkData(i, wal_index, 1) + + tdSql.execute("drop database if exists db1") + tdSql.execute("create database db1 wal 2") + tdSql.query("show databases") + for i in range(tdSql.queryRows): + if tdSql.queryResult[i][0] == "db1": + tdSql.checkData(i, wal_index, 2) + + tdSql.execute("drop database if exists db1") + tdSql.execute("create database db1 fsync 0") + tdSql.query("show databases") + for i in range(tdSql.queryRows): + if tdSql.queryResult[i][0] == "db1": + tdSql.checkData(i, fsync_index, 0) + + tdSql.execute("drop database if exists db1") + tdSql.execute("create database db1 fsync 3000") + tdSql.query("show databases") + for i in range(tdSql.queryRows): + if tdSql.queryResult[i][0] == "db1": + tdSql.checkData(i, fsync_index, 3000) + + tdSql.execute("drop database if exists db1") + tdSql.execute("create database db1 fsync 180000") + tdSql.query("show databases") + for i in range(tdSql.queryRows): + if tdSql.queryResult[i][0] == "db1": + tdSql.checkData(i, fsync_index, 180000) + + + tdSql.execute("drop database if exists db1") + tdSql.execute("create database db1 wal 1 fsync 6000") + tdSql.query("show databases") + for i in range(tdSql.queryRows): + if tdSql.queryResult[i][0] == "db1": + tdSql.checkData(i, fsync_index, 6000) + tdSql.checkData(i, wal_index, 1) + + tdSql.execute("drop database if exists db1") + tdSql.execute("create database db1 wal 2 fsync 3000") + tdSql.query("show databases") + for i in range(tdSql.queryRows): + if tdSql.queryResult[i][0] == "db1": + tdSql.checkData(i, fsync_index, 3000) + tdSql.checkData(i, wal_index, 2) + + tdSql.execute("alter database db1 wal 1") + tdSql.query("show databases") + for i in range(tdSql.queryRows): + if tdSql.queryResult[i][0] == "db1": + tdSql.checkData(i, fsync_index, 3000) + tdSql.checkData(i, wal_index, 1) + + tdSql.execute("alter database db1 wal 2") + tdSql.query("show databases") + for i in range(tdSql.queryRows): + if tdSql.queryResult[i][0] == "db1": + tdSql.checkData(i, fsync_index, 3000) + tdSql.checkData(i, wal_index, 2) + + tdSql.execute("alter database db1 fsync 0") + tdSql.query("show databases") + for i in range(tdSql.queryRows): + if tdSql.queryResult[i][0] == "db1": + tdSql.checkData(i, fsync_index, 0) + tdSql.checkData(i, wal_index, 2) + + tdSql.execute("alter database db1 fsync 3000") + tdSql.query("show databases") + for i in range(tdSql.queryRows): + if tdSql.queryResult[i][0] == "db1": + tdSql.checkData(i, fsync_index, 3000) + tdSql.checkData(i, wal_index, 2) + + tdSql.execute("alter database db1 fsync 18000") + tdSql.query("show databases") + for i in range(tdSql.queryRows): + if tdSql.queryResult[i][0] == "db1": + tdSql.checkData(i, fsync_index, 18000) + tdSql.checkData(i, wal_index, 2) + + tdSql.execute("alter database db1 wal 1 fsync 3000") + tdSql.query("show databases") + for i in range(tdSql.queryRows): + if tdSql.queryResult[i][0] == "db1": + tdSql.checkData(i, fsync_index, 3000) + tdSql.checkData(i, wal_index, 1) + + tdSql.execute("drop database db1 ") + + @property + def fsync_create_err(self): + return [ + "create database db1 wal 0", + "create database db1 wal 3", + "create database db1 wal null", + "create database db1 wal true", + "create database db1 wal 1.1", + "create database db1 fsync -1", + "create database db1 fsync 180001", + "create database db1 fsync 10.111", + "create database db1 fsync true", + ] + + @property + def fsync_alter_err(self): + return [ + "alter database db1 wal 0", + "alter database db1 wal 3", + "alter database db1 wal null", + "alter database db1 wal true", + "alter database db1 wal 1.1", + "alter database db1 fsync -1", + "alter database db1 fsync 180001", + "alter database db1 fsync 10.111", + "alter database db1 fsync true", + ] + + def test_fsync_err(self): + for sql in self.fsync_create_err: + tdSql.error(sql) + tdSql.query("create database db1") + for sql in self.fsync_alter_err: + tdSql.error(sql) + tdSql.query("drop database db1") + + def all_test(self): + self.test_fsync_err() + self.test_fsync_current() + + # def __create_tb(self): + + # tdLog.printNoPrefix("==========step1:create table") + # create_stb_sql = f'''create table stb1( + # ts timestamp, {INT_COL} int, {BINT_COL} bigint, {SINT_COL} smallint, {TINT_COL} tinyint, + # {FLOAT_COL} float, {DOUBLE_COL} double, {BOOL_COL} bool, + # {BINARY_COL} binary(16), {NCHAR_COL} nchar(32), {TS_COL} timestamp + # ) tags (t1 int) + # ''' + # create_ntb_sql = f'''create table t1( + # ts timestamp, {INT_COL} int, {BINT_COL} bigint, {SINT_COL} smallint, {TINT_COL} tinyint, + # {FLOAT_COL} float, {DOUBLE_COL} double, {BOOL_COL} bool, + # {BINARY_COL} binary(16), {NCHAR_COL} nchar(32), {TS_COL} timestamp + # ) + # ''' + # tdSql.execute(create_stb_sql) + # tdSql.execute(create_ntb_sql) + + # for i in range(4): + # tdSql.execute(f'create table ct{i+1} using stb1 tags ( {i+1} )') + # { i % 32767 }, { i % 127}, { i * 1.11111 }, { i * 1000.1111 }, { i % 2} + + # def __insert_data(self, rows): + # now_time = int(datetime.datetime.timestamp(datetime.datetime.now()) * 1000) + # for i in range(rows): + # tdSql.execute( + # f"insert into ct1 values ( { now_time - i * 1000 }, {i}, {11111 * i}, {111 * i % 32767 }, {11 * i % 127}, {1.11*i}, {1100.0011*i}, {i%2}, 'binary{i}', 'nchar_测试_{i}', { now_time + 1 * i } )" + # ) + # tdSql.execute( + # f"insert into ct4 values ( { now_time - i * 7776000000 }, {i}, {11111 * i}, {111 * i % 32767 }, {11 * i % 127}, {1.11*i}, {1100.0011*i}, {i%2}, 'binary{i}', 'nchar_测试_{i}', { now_time + 1 * i } )" + # ) + # tdSql.execute( + # f"insert into ct2 values ( { now_time - i * 7776000000 }, {-i}, {-11111 * i}, {-111 * i % 32767 }, {-11 * i % 127}, {-1.11*i}, {-1100.0011*i}, {i%2}, 'binary{i}', 'nchar_测试_{i}', { now_time + 1 * i } )" + # ) + # tdSql.execute( + # f'''insert into ct1 values + # ( { now_time - rows * 5 }, 0, 0, 0, 0, 0, 0, 0, 'binary0', 'nchar_测试_0', { now_time + 8 } ) + # ( { now_time + 10000 }, { rows }, -99999, -999, -99, -9.99, -99.99, 1, 'binary9', 'nchar_测试_9', { now_time + 9 } ) + # ''' + # ) + + # tdSql.execute( + # f'''insert into ct4 values + # ( { now_time - rows * 7776000000 }, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL ) + # ( { now_time - rows * 3888000000 + 10800000 }, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL ) + # ( { now_time + 7776000000 }, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL ) + # ( + # { now_time + 5184000000}, {pow(2,31)-pow(2,15)}, {pow(2,63)-pow(2,30)}, 32767, 127, + # { 3.3 * pow(10,38) }, { 1.3 * pow(10,308) }, { rows % 2 }, "binary_limit-1", "nchar_测试_limit-1", { now_time - 86400000} + # ) + # ( + # { now_time + 2592000000 }, {pow(2,31)-pow(2,16)}, {pow(2,63)-pow(2,31)}, 32766, 126, + # { 3.2 * pow(10,38) }, { 1.2 * pow(10,308) }, { (rows-1) % 2 }, "binary_limit-2", "nchar_测试_limit-2", { now_time - 172800000} + # ) + # ''' + # ) + + # tdSql.execute( + # f'''insert into ct2 values + # ( { now_time - rows * 7776000000 }, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL ) + # ( { now_time - rows * 3888000000 + 10800000 }, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL ) + # ( { now_time + 7776000000 }, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL ) + # ( + # { now_time + 5184000000 }, { -1 * pow(2,31) + pow(2,15) }, { -1 * pow(2,63) + pow(2,30) }, -32766, -126, + # { -1 * 3.2 * pow(10,38) }, { -1.2 * pow(10,308) }, { rows % 2 }, "binary_limit-1", "nchar_测试_limit-1", { now_time - 86400000 } + # ) + # ( + # { now_time + 2592000000 }, { -1 * pow(2,31) + pow(2,16) }, { -1 * pow(2,63) + pow(2,31) }, -32767, -127, + # { - 3.3 * pow(10,38) }, { -1.3 * pow(10,308) }, { (rows-1) % 2 }, "binary_limit-2", "nchar_测试_limit-2", { now_time - 172800000 } + # ) + # ''' + # ) + + # for i in range(rows): + # insert_data = f'''insert into t1 values + # ( { now_time - i * 3600000 }, {i}, {i * 11111}, { i % 32767 }, { i % 127}, { i * 1.11111 }, { i * 1000.1111 }, { i % 2}, + # "binary_{i}", "nchar_测试_{i}", { now_time - 1000 * i } ) + # ''' + # tdSql.execute(insert_data) + # tdSql.execute( + # f'''insert into t1 values + # ( { now_time + 10800000 }, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL ) + # ( { now_time - (( rows // 2 ) * 60 + 30) * 60000 }, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL ) + # ( { now_time - rows * 3600000 }, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL ) + # ( { now_time + 7200000 }, { pow(2,31) - pow(2,15) }, { pow(2,63) - pow(2,30) }, 32767, 127, + # { 3.3 * pow(10,38) }, { 1.3 * pow(10,308) }, { rows % 2 }, + # "binary_limit-1", "nchar_测试_limit-1", { now_time - 86400000 } + # ) + # ( + # { now_time + 3600000 } , { pow(2,31) - pow(2,16) }, { pow(2,63) - pow(2,31) }, 32766, 126, + # { 3.2 * pow(10,38) }, { 1.2 * pow(10,308) }, { (rows-1) % 2 }, + # "binary_limit-2", "nchar_测试_limit-2", { now_time - 172800000 } + # ) + # ''' + # ) + + def run(self): + tdSql.prepare() + + self.all_test() + + tdDnodes.stop(1) + tdDnodes.start(1) + + # tdSql.execute("use db") + + tdLog.printNoPrefix("==========step4:after wal, all check again ") + self.all_test() + + def stop(self): + tdSql.close() + tdLog.success(f"{__file__} successfully executed") + +tdCases.addLinux(__file__, TDTestCase()) +tdCases.addWindows(__file__, TDTestCase()) diff --git a/tests/system-test/0-others/udfTest.py b/tests/system-test/0-others/udfTest.py index 3aeee69b4196d85f67e8c7eb4815fc7a1d590f8c..679b41509891d1efe92507a81f7add51b9f76253 100644 --- a/tests/system-test/0-others/udfTest.py +++ b/tests/system-test/0-others/udfTest.py @@ -14,7 +14,7 @@ class TDTestCase: def init(self, conn, logSql): tdLog.debug(f"start to excute {__file__}") - tdSql.init(conn.cursor()) + tdSql.init(conn.cursor(), logSql) def getBuildPath(self): selfPath = os.path.dirname(os.path.realpath(__file__)) @@ -44,12 +44,14 @@ class TDTestCase: libudf1 = subprocess.Popen('find %s -name "libudf1.so"|grep lib|head -n1'%projPath , shell=True, stdout=subprocess.PIPE,stderr=subprocess.STDOUT).stdout.read().decode("utf-8") libudf2 = subprocess.Popen('find %s -name "libudf2.so"|grep lib|head -n1'%projPath , shell=True, stdout=subprocess.PIPE,stderr=subprocess.STDOUT).stdout.read().decode("utf-8") os.system("mkdir /tmp/udf/") - os.system("sudo cp %s /tmp/udf/ "%libudf1.replace("\n" ,"")) - os.system("sudo cp %s /tmp/udf/ "%libudf2.replace("\n" ,"")) + os.system("cp %s /tmp/udf/ "%libudf1.replace("\n" ,"")) + os.system("cp %s /tmp/udf/ "%libudf2.replace("\n" ,"")) def prepare_data(self): + tdSql.execute("drop database if exists db ") + tdSql.execute("create database if not exists db days 300") tdSql.execute("use db") tdSql.execute( '''create table stb1 @@ -118,6 +120,17 @@ class TDTestCase: ''' ) + # udf functions with join + ts_start = 1652517451000 + tdSql.execute("create stable st (ts timestamp , c1 int , c2 int ,c3 double ,c4 double ) tags(ind int)") + tdSql.execute("create table sub1 using st tags(1)") + tdSql.execute("create table sub2 using st tags(2)") + + for i in range(10): + ts = ts_start + i *1000 + tdSql.execute(" insert into sub1 values({} , {},{},{},{})".format(ts,i ,i*10,i*100.0,i*1000.0)) + tdSql.execute(" insert into sub2 values({} , {},{},{},{})".format(ts,i ,i*10,i*100.0,i*1000.0)) + def create_udf_function(self): @@ -330,14 +343,14 @@ class TDTestCase: # # bug need fix - tdSql.query("select udf1(num1) , csum(num1) from tb;") - tdSql.checkRows(9) - tdSql.query("select ceil(num1) , csum(num1) from tb;") - tdSql.checkRows(9) - tdSql.query("select udf1(c1) , csum(c1) from stb1;") - tdSql.checkRows(22) - tdSql.query("select floor(c1) , csum(c1) from stb1;") - tdSql.checkRows(22) + #tdSql.query("select udf1(num1) , csum(num1) from tb;") + #tdSql.checkRows(9) + #tdSql.query("select ceil(num1) , csum(num1) from tb;") + #tdSql.checkRows(9) + #tdSql.query("select udf1(c1) , csum(c1) from stb1;") + #tdSql.checkRows(22) + #tdSql.query("select floor(c1) , csum(c1) from stb1;") + #tdSql.checkRows(22) # stable with compute functions tdSql.query("select udf1(c1) , abs(c1) from stb1;") @@ -379,17 +392,6 @@ class TDTestCase: tdSql.checkData(0,1,88) tdSql.checkData(0,2,-99.990000000) tdSql.checkData(0,3,88) - - # udf functions with join - ts_start = 1652517451000 - tdSql.execute("create stable st (ts timestamp , c1 int , c2 int ,c3 double ,c4 double ) tags(ind int)") - tdSql.execute("create table sub1 using st tags(1)") - tdSql.execute("create table sub2 using st tags(2)") - - for i in range(10): - ts = ts_start + i *1000 - tdSql.execute(" insert into sub1 values({} , {},{},{},{})".format(ts,i ,i*10,i*100.0,i*1000.0)) - tdSql.execute(" insert into sub2 values({} , {},{},{},{})".format(ts,i ,i*10,i*100.0,i*1000.0)) tdSql.query("select sub1.c1, sub2.c2 from sub1, sub2 where sub1.ts=sub2.ts and sub1.c1 is not null") tdSql.checkData(0,0,0) @@ -470,51 +472,43 @@ class TDTestCase: tdSql.checkData(0,1,169.661427555) def try_query_sql(self): - sql_lists = [ + udf1_sqls = [ "select num1 , udf1(num1) ,num2 ,udf1(num2),num3 ,udf1(num3),num4 ,udf1(num4) from tb" , "select c1 , udf1(c1) ,c2 ,udf1(c2), c3 ,udf1(c3), c4 ,udf1(c4) from stb1 order by c1" , - "select udf2(num1) ,udf2(num2), udf2(num3) from tb" , - "select udf2(num1)+100 ,udf2(num2)-100, udf2(num3)*100 ,udf2(num3)/100 from tb" , - "select udf2(c1) ,udf2(c6) from stb1 " , - "select udf2(c1)+100 ,udf2(c6)-100 ,udf2(c1)*100 ,udf2(c6)/100 from stb1 " , - "select udf2(c1+100) ,udf2(c6-100) ,udf2(c1*100) ,udf2(c6/100) from ct1" , - "select udf2(c1+100) ,udf2(c6-100) ,udf2(c1*100) ,udf2(c6/100) from stb1 " , "select udf1(num1) , max(num1) from tb;" , - "select floor(num1) , max(num1) from tb;" , "select udf1(num1) , min(num1) from tb;" , - "select ceil(num1) , min(num1) from tb;" , - "select udf1(num1) , top(num1,1) from tb;" , - "select udf1(num1) , bottom(num1,1) from tb;" , + #"select udf1(num1) , top(num1,1) from tb;" , + #"select udf1(num1) , bottom(num1,1) from tb;" , "select udf1(c1) , max(c1) from stb1;" , - "select abs(c1) , max(c1) from stb1;" , "select udf1(c1) , min(c1) from stb1;" , - "select floor(c1) , min(c1) from stb1;" , - "select udf1(c1) , top(c1 ,1) from stb1;" , - "select abs(c1) , top(c1 ,1) from stb1;" , - "select udf1(c1) , bottom(c1,1) from stb1;" , - "select ceil(c1) , bottom(c1,1) from stb1;" , + #"select udf1(c1) , top(c1 ,1) from stb1;" , + #"select udf1(c1) , bottom(c1,1) from stb1;" , "select udf1(num1) , abs(num1) from tb;" , - "select floor(num1) , abs(num1) from tb;" , - "select udf1(num1) , csum(num1) from tb;" , - "select ceil(num1) , csum(num1) from tb;" , - "select udf1(c1) , csum(c1) from stb1;" , - "select floor(c1) , csum(c1) from stb1;" , + #"select udf1(num1) , csum(num1) from tb;" , + #"select udf1(c1) , csum(c1) from stb1;" , "select udf1(c1) , abs(c1) from stb1;" , - "select abs(c1) , ceil(c1) from stb1;" , "select abs(udf1(c1)) , abs(ceil(c1)) from stb1 order by ts;" , "select abs(udf1(c1)) , abs(ceil(c1)) from ct1 order by ts;" , - "select udf2(c1) from stb1 group by 1-udf1(c1)" , - "select abs(udf1(c1)) , abs(ceil(c1)) from stb1 where c1 is null order by ts;" , "select c1 ,udf1(c1) , c6 ,udf1(c6) from stb1 where c1 > 8 order by ts" , - "select sub1.c1, sub2.c2 from sub1, sub2 where sub1.ts=sub2.ts and sub1.c1 is not null" , "select udf1(sub1.c1), udf1(sub2.c2) from sub1, sub2 where sub1.ts=sub2.ts and sub1.c1 is not null" , "select sub1.c1 , udf1(sub1.c1), sub2.c2 ,udf1(sub2.c2) from sub1, sub2 where sub1.ts=sub2.ts and sub1.c1 is not null" , - "select udf2(sub1.c1), udf2(sub2.c2) from sub1, sub2 where sub1.ts=sub2.ts and sub1.c1 is not null" , "select udf1(c1) from ct1 group by c1" , "select udf1(c1) from stb1 group by c1" , "select c1,c2, udf1(c1,c2) from ct1 group by c1,c2" , "select c1,c2, udf1(c1,c2) from stb1 group by c1,c2" , + "select num1,num2,num3,udf1(num1,num2,num3) from tb" , + "select c1,c6,udf1(c1,c6) from stb1 order by ts" , + "select abs(udf1(c1,c6,c1,c6)) , abs(ceil(c1)) from stb1 where c1 is not null order by ts;" + ] + udf2_sqls = ["select udf2(sub1.c1), udf2(sub2.c2) from sub1, sub2 where sub1.ts=sub2.ts and sub1.c1 is not null" , + "select udf2(c1) from stb1 group by 1-udf1(c1)" , + "select udf2(num1) ,udf2(num2), udf2(num3) from tb" , + "select udf2(num1)+100 ,udf2(num2)-100, udf2(num3)*100 ,udf2(num3)/100 from tb" , + "select udf2(c1) ,udf2(c6) from stb1 " , + "select udf2(c1)+100 ,udf2(c6)-100 ,udf2(c1)*100 ,udf2(c6)/100 from stb1 " , + "select udf2(c1+100) ,udf2(c6-100) ,udf2(c1*100) ,udf2(c6/100) from ct1" , + "select udf2(c1+100) ,udf2(c6-100) ,udf2(c1*100) ,udf2(c6/100) from stb1 " , "select udf2(c1) from ct1 group by c1" , "select udf2(c1) from stb1 group by c1" , "select c1,c2, udf2(c1,c6) from ct1 group by c1,c2" , @@ -522,23 +516,13 @@ class TDTestCase: "select udf2(c1) from stb1 group by udf1(c1)" , "select udf2(c1) from stb1 group by floor(c1)" , "select udf2(c1) from stb1 group by floor(c1) order by udf2(c1)" , - "select num1,num2,num3,udf1(num1,num2,num3) from tb" , - "select c1,c6,udf1(c1,c6) from stb1 order by ts" , - "select abs(udf1(c1,c6,c1,c6)) , abs(ceil(c1)) from stb1 where c1 is not null order by ts;" , + "select udf2(sub1.c1 ,sub1.c2), udf2(sub2.c2 ,sub2.c1) from sub1, sub2 where sub1.ts=sub2.ts and sub1.c1 is not null" , - "drop function udf1 " , - "drop function udf2 " , "select udf2(sub1.c1 ,sub1.c2), udf2(sub2.c2 ,sub2.c1) from sub1, sub2 where sub1.ts=sub2.ts and sub1.c1 is not null" , "select udf2(sub1.c1 ,sub1.c2), udf2(sub2.c2 ,sub2.c1) from sub1, sub2 where sub1.ts=sub2.ts and sub1.c1 is not null" , - "select count(*) from stb1" , "select udf2(sub1.c1 ,sub1.c2), udf2(sub2.c2 ,sub2.c1) from sub1, sub2 where sub1.ts=sub2.ts and sub1.c1 is not null"] - tdSql.execute("use db") - for sql in sql_lists: - try: - tdSql.execute(sql) - except: - pass + return udf1_sqls ,udf2_sqls @@ -551,7 +535,12 @@ class TDTestCase: # create function without buffer tdSql.execute("create function udf1 as '/tmp/udf/libudf1.so' outputtype int") tdSql.execute("create aggregate function udf2 as '/tmp/udf/libudf2.so' outputtype double") - # self.try_query_sql() + udf1_sqls ,udf2_sqls = self.try_query_sql() + + for scalar_sql in udf1_sqls: + tdSql.query(scalar_sql) + for aggregate_sql in udf2_sqls: + tdSql.error(aggregate_sql) # create function without aggregate @@ -562,7 +551,21 @@ class TDTestCase: # create function without buffer tdSql.execute("create aggregate function udf1 as '/tmp/udf/libudf1.so' outputtype int bufSize 8 ") tdSql.execute("create function udf2 as '/tmp/udf/libudf2.so' outputtype double bufSize 8") - # self.try_query_sql() + udf1_sqls ,udf2_sqls = self.try_query_sql() + + for scalar_sql in udf1_sqls: + tdSql.error(scalar_sql) + for aggregate_sql in udf2_sqls: + tdSql.error(aggregate_sql) + + tdSql.execute(" create function db as '/tmp/udf/libudf1.so' outputtype int bufSize 8 ") + tdSql.execute(" create aggregate function test as '/tmp/udf/libudf1.so' outputtype int bufSize 8 ") + tdSql.error(" select db(c1) from stb1 ") + tdSql.error(" select db(c1,c6), db(c6) from stb1 ") + tdSql.error(" select db(num1,num2), db(num1) from tb ") + tdSql.error(" select test(c1) from stb1 ") + tdSql.error(" select test(c1,c6), test(c6) from stb1 ") + tdSql.error(" select test(num1,num2), test(num1) from tb ") @@ -577,7 +580,7 @@ class TDTestCase: cfgPath = buildPath + "/../sim/dnode1/cfg" udfdPath = buildPath +'/build/bin/udfd' - for i in range(5): + for i in range(3): tdLog.info(" loop restart udfd %d_th" % i) @@ -585,7 +588,7 @@ class TDTestCase: tdSql.checkData(0,0,169.661427555) tdSql.checkData(0,1,169.661427555) # stop udfd cmds - get_processID = "ps -ef | grep -w udfd | grep 'root' | grep -v grep| grep -v defunct | awk '{print $2}'" + get_processID = "ps -ef | grep -w udfd | grep -v grep| grep -v defunct | awk '{print $2}'" processID = subprocess.check_output(get_processID, shell=True).decode("utf-8") stop_udfd = " kill -9 %s" % processID os.system(stop_udfd) @@ -601,6 +604,7 @@ class TDTestCase: # tdLog.info("start udfd : %s " % start_udfd) def test_function_name(self): + tdLog.info(" create function name is not build_in functions ") tdSql.execute(" drop function udf1 ") tdSql.execute(" drop function udf2 ") tdSql.error("create function max as '/tmp/udf/libudf1.so' outputtype int bufSize 8") @@ -617,8 +621,9 @@ class TDTestCase: def restart_taosd_query_udf(self): + self.create_udf_function() + for i in range(5): - time.sleep(5) tdLog.info(" this is %d_th restart taosd " %i) tdSql.execute("use db ") tdSql.query("select count(*) from stb1") @@ -627,23 +632,29 @@ class TDTestCase: tdSql.checkData(0,0,169.661427555) tdSql.checkData(0,1,169.661427555) tdDnodes.stop(1) - time.sleep(2) tdDnodes.start(1) - time.sleep(5) - + time.sleep(2) def run(self): # sourcery skip: extract-duplicate-method, remove-redundant-fstring - tdSql.prepare() + print(" env is ok for all ") self.prepare_udf_so() self.prepare_data() self.create_udf_function() self.basic_udf_query() self.loop_kill_udfd() + + self.unexpected_create() + tdSql.execute(" drop function udf1 ") + tdSql.execute(" drop function udf2 ") + self.create_udf_function() + time.sleep(2) + self.basic_udf_query() + self.test_function_name() self.restart_taosd_query_udf() - # self.unexpected_create() - # self.test_function_name() + + def stop(self): tdSql.close() diff --git a/tests/system-test/0-others/udf_cluster.py b/tests/system-test/0-others/udf_cluster.py new file mode 100644 index 0000000000000000000000000000000000000000..de998e9087c2bc8ef1ff3b1aab09695cf57fd8f4 --- /dev/null +++ b/tests/system-test/0-others/udf_cluster.py @@ -0,0 +1,338 @@ +import taos +import sys +import time +import os + +from util.log import * +from util.sql import * +from util.cases import * +from util.dnodes import TDDnodes +from util.dnodes import TDDnode +import socket +import subprocess + +class MyDnodes(TDDnodes): + def __init__(self ,dnodes_lists): + super(MyDnodes,self).__init__() + self.dnodes = dnodes_lists # dnode must be TDDnode instance + self.simDeployed = False + +class TDTestCase: + + def init(self,conn ,logSql): + tdLog.debug(f"start to excute {__file__}") + self.TDDnodes = None + self.depoly_cluster(3) + self.master_dnode = self.TDDnodes.dnodes[0] + conn1 = taos.connect(self.master_dnode.cfgDict["fqdn"] , config=self.master_dnode.cfgDir) + tdSql.init(conn1.cursor()) + + + def getBuildPath(self): + selfPath = os.path.dirname(os.path.realpath(__file__)) + + if ("community" in selfPath): + projPath = selfPath[:selfPath.find("community")] + else: + projPath = selfPath[:selfPath.find("tests")] + + for root, dirs, files in os.walk(projPath): + if ("taosd" in files): + rootRealPath = os.path.dirname(os.path.realpath(root)) + if ("packaging" not in rootRealPath): + buildPath = root[:len(root) - len("/build/bin")] + break + return buildPath + + def prepare_udf_so(self): + selfPath = os.path.dirname(os.path.realpath(__file__)) + + if ("community" in selfPath): + projPath = selfPath[:selfPath.find("community")] + else: + projPath = selfPath[:selfPath.find("tests")] + print(projPath) + + libudf1 = subprocess.Popen('find %s -name "libudf1.so"|grep lib|head -n1'%projPath , shell=True, stdout=subprocess.PIPE,stderr=subprocess.STDOUT).stdout.read().decode("utf-8") + libudf2 = subprocess.Popen('find %s -name "libudf2.so"|grep lib|head -n1'%projPath , shell=True, stdout=subprocess.PIPE,stderr=subprocess.STDOUT).stdout.read().decode("utf-8") + os.system("mkdir /tmp/udf/") + os.system("sudo cp %s /tmp/udf/ "%libudf1.replace("\n" ,"")) + os.system("sudo cp %s /tmp/udf/ "%libudf2.replace("\n" ,"")) + + + def prepare_data(self): + + tdSql.execute("drop database if exists db") + tdSql.execute("create database if not exists db replica 1 days 300") + tdSql.execute("use db") + tdSql.execute( + '''create table stb1 + (ts timestamp, c1 int, c2 bigint, c3 smallint, c4 tinyint, c5 float, c6 double, c7 bool, c8 binary(16),c9 nchar(32), c10 timestamp) + tags (t1 int) + ''' + ) + + tdSql.execute( + ''' + create table t1 + (ts timestamp, c1 int, c2 bigint, c3 smallint, c4 tinyint, c5 float, c6 double, c7 bool, c8 binary(16),c9 nchar(32), c10 timestamp) + ''' + ) + for i in range(4): + tdSql.execute(f'create table ct{i+1} using stb1 tags ( {i+1} )') + + for i in range(9): + tdSql.execute( + f"insert into ct1 values ( now()-{i*10}s, {1*i}, {11111*i}, {111*i}, {11*i}, {1.11*i}, {11.11*i}, {i%2}, 'binary{i}', 'nchar{i}', now()+{1*i}a )" + ) + tdSql.execute( + f"insert into ct4 values ( now()-{i*90}d, {1*i}, {11111*i}, {111*i}, {11*i}, {1.11*i}, {11.11*i}, {i%2}, 'binary{i}', 'nchar{i}', now()+{1*i}a )" + ) + tdSql.execute("insert into ct1 values (now()-45s, 0, 0, 0, 0, 0, 0, 0, 'binary0', 'nchar0', now()+8a )") + tdSql.execute("insert into ct1 values (now()+10s, 9, -99999, -999, -99, -9.99, -99.99, 1, 'binary9', 'nchar9', now()+9a )") + tdSql.execute("insert into ct1 values (now()+15s, 9, -99999, -999, -99, -9.99, NULL, 1, 'binary9', 'nchar9', now()+9a )") + tdSql.execute("insert into ct1 values (now()+20s, 9, -99999, -999, NULL, -9.99, -99.99, 1, 'binary9', 'nchar9', now()+9a )") + + tdSql.execute("insert into ct4 values (now()-810d, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL ) ") + tdSql.execute("insert into ct4 values (now()-400d, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL ) ") + tdSql.execute("insert into ct4 values (now()+9d, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL ) ") + + tdSql.execute( + f'''insert into t1 values + ( '2020-04-21 01:01:01.000', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL ) + ( '2020-10-21 01:01:01.000', 1, 11111, 111, 11, 1.11, 11.11, 1, "binary1", "nchar1", now()+1a ) + ( '2020-12-31 01:01:01.000', 2, 22222, 222, 22, 2.22, 22.22, 0, "binary2", "nchar2", now()+2a ) + ( '2021-01-01 01:01:06.000', 3, 33333, 333, 33, 3.33, 33.33, 0, "binary3", "nchar3", now()+3a ) + ( '2021-05-07 01:01:10.000', 4, 44444, 444, 44, 4.44, 44.44, 1, "binary4", "nchar4", now()+4a ) + ( '2021-07-21 01:01:01.000', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL ) + ( '2021-09-30 01:01:16.000', 5, 55555, 555, 55, 5.55, 55.55, 0, "binary5", "nchar5", now()+5a ) + ( '2022-02-01 01:01:20.000', 6, 66666, 666, 66, 6.66, 66.66, 1, "binary6", "nchar6", now()+6a ) + ( '2022-10-28 01:01:26.000', 7, 00000, 000, 00, 0.00, 00.00, 1, "binary7", "nchar7", "1970-01-01 08:00:00.000" ) + ( '2022-12-01 01:01:30.000', 8, -88888, -888, -88, -8.88, -88.88, 0, "binary8", "nchar8", "1969-01-01 01:00:00.000" ) + ( '2022-12-31 01:01:36.000', 9, -99999999999999999, -999, -99, -9.99, -999999999999999999999.99, 1, "binary9", "nchar9", "1900-01-01 00:00:00.000" ) + ( '2023-02-21 01:01:01.000', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL ) + ''' + ) + + tdSql.execute("create table tb (ts timestamp , num1 int , num2 int, num3 double , num4 binary(30))") + tdSql.execute( + f'''insert into tb values + ( '2020-04-21 01:01:01.000', NULL, 1, 1, "binary1" ) + ( '2020-10-21 01:01:01.000', 1, 1, 1.11, "binary1" ) + ( '2020-12-31 01:01:01.000', 2, 22222, 22, "binary1" ) + ( '2021-01-01 01:01:06.000', 3, 33333, 33, "binary1" ) + ( '2021-05-07 01:01:10.000', 4, 44444, 44, "binary1" ) + ( '2021-07-21 01:01:01.000', NULL, NULL, NULL, "binary1" ) + ( '2021-09-30 01:01:16.000', 5, 55555, 55, "binary1" ) + ( '2022-02-01 01:01:20.000', 6, 66666, 66, "binary1" ) + ( '2022-10-28 01:01:26.000', 0, 00000, 00, "binary1" ) + ( '2022-12-01 01:01:30.000', 8, -88888, -88, "binary1" ) + ( '2022-12-31 01:01:36.000', 9, -9999999, -99, "binary1" ) + ( '2023-02-21 01:01:01.000', NULL, NULL, NULL, "binary1" ) + ''' + ) + + + def create_udf_function(self ): + + for i in range(10): + # create scalar functions + tdSql.execute("create function udf1 as '/tmp/udf/libudf1.so' outputtype int bufSize 8;") + + # create aggregate functions + + tdSql.execute("create aggregate function udf2 as '/tmp/udf/libudf2.so' outputtype double bufSize 8;") + + # functions = tdSql.getResult("show functions") + # function_nums = len(functions) + # if function_nums == 2: + # tdLog.info("create two udf functions success ") + + # drop functions + + tdSql.execute("drop function udf1") + tdSql.execute("drop function udf2") + + functions = tdSql.getResult("show functions") + for function in functions: + if "udf1" in function[0] or "udf2" in function[0]: + tdLog.info("drop udf functions failed ") + tdLog.exit("drop udf functions failed") + + tdLog.info("drop two udf functions success ") + + # create scalar functions + tdSql.execute("create function udf1 as '/tmp/udf/libudf1.so' outputtype int bufSize 8;") + + # create aggregate functions + + tdSql.execute("create aggregate function udf2 as '/tmp/udf/libudf2.so' outputtype double bufSize 8;") + + functions = tdSql.getResult("show functions") + function_nums = len(functions) + if function_nums == 2: + tdLog.info("create two udf functions success ") + + def basic_udf_query(self , dnode): + + mytdSql = self.getConnection(dnode) + # scalar functions + + mytdSql.execute("use db ") + + result = mytdSql.query("select num1 , udf1(num1) ,num2 ,udf1(num2),num3 ,udf1(num3),num4 ,udf1(num4) from tb") + data = result.fetch_all() + print(data) + if data == [(None, None, 1, 88, 1.0, 88, 'binary1', 88), (1, 88, 1, 88, 1.11, 88, 'binary1', 88), (2, 88, 22222, 88, 22.0, 88, 'binary1', 88), (3, 88, 33333, 88, 33.0, 88, 'binary1', 88), (4, 88, 44444, 88, 44.0, 88, 'binary1', 88), (None, None, None, None, None, None, 'binary1', 88), (5, 88, 55555, 88, 55.0, 88, 'binary1', 88), (6, 88, 66666, 88, 66.0, 88, 'binary1', 88), (0, 88, 0, 88, 0.0, 88, 'binary1', 88), (8, 88, -88888, 88, -88.0, 88, 'binary1', 88), (9, 88, -9999999, 88, -99.0, 88, 'binary1', 88), (None, None, None, None, None, None, 'binary1', 88)]: + tdLog.info(" UDF query check ok at :dnode_index %s" %dnode.index) + else: + tdLog.info(" UDF query check failed at :dnode_index %s" %dnode.index) + tdLog.exit("query check failed at :dnode_index %s" %dnode.index ) + + result = mytdSql.query("select udf1(c1,c6), udf1(c1) ,udf1(c6) from stb1 order by ts") + data = result.fetch_all() + print(data) + if data == [(None, None, None), (88, 88, 88), (88, 88, 88), (88, 88, 88), (88, 88, 88), (None, None, None), (88, 88, 88), (88, 88, 88), (88, 88, 88), (88, 88, 88), (88, 88, 88), (88, 88, 88), (88, 88, 88), (88, 88, 88), (88, 88, 88), (88, 88, 88), (88, 88, 88), (88, 88, 88), (88, 88, 88), (88, 88, 88), (88, 88, 88), (88, 88, 88), (None, 88, None), (88, 88, 88), (None, None, None)]: + tdLog.info(" UDF query check ok at :dnode_index %s" %dnode.index) + else: + tdLog.info(" UDF query check failed at :dnode_index %s" %dnode.index) + tdLog.exit("query check failed at :dnode_index %s" %dnode.index ) + + result = mytdSql.query("select udf2(c1,c6), udf2(c1) ,udf2(c6) from stb1 ") + data = result.fetch_all() + print(data) + expect_data = [(266.47194411419747, 25.514701644346147, 265.247614503882)] + status = True + for index in range(len(expect_data[0])): + if abs(expect_data[0][index] - data[0][index]) >0.0001: + status = False + break + + if status : + tdLog.info(" UDF query check ok at :dnode_index %s" %dnode.index) + else: + tdLog.info(" UDF query check failed at :dnode_index %s" %dnode.index) + tdLog.exit("query check failed at :dnode_index %s" %dnode.index ) + + result = mytdSql.query("select udf2(num1,num2,num3), udf2(num1) ,udf2(num2) from tb ") + data = result.fetch_all() + print(data) + expect_data = [(10000949.554622812, 15.362291495737216, 10000949.553189287)] + status = True + for index in range(len(expect_data[0])): + if abs(expect_data[0][index] - data[0][index]) >0.0001: + status = False + break + + if status : + tdLog.info(" UDF query check ok at :dnode_index %s" %dnode.index) + else: + tdLog.info(" UDF query check failed at :dnode_index %s" %dnode.index) + tdLog.exit("query check failed at :dnode_index %s" %dnode.index ) + + + def check_UDF_query(self): + + for i in range(20): + for dnode in self.TDDnodes.dnodes: + self.basic_udf_query(dnode) + + + def depoly_cluster(self ,dnodes_nums): + + testCluster = False + valgrind = 0 + hostname = socket.gethostname() + dnodes = [] + start_port = 6030 + for num in range(1, dnodes_nums+1): + dnode = TDDnode(num) + dnode.addExtraCfg("firstEp", f"{hostname}:{start_port}") + dnode.addExtraCfg("fqdn", f"{hostname}") + dnode.addExtraCfg("serverPort", f"{start_port + (num-1)*100}") + dnode.addExtraCfg("monitorFqdn", hostname) + dnode.addExtraCfg("monitorPort", 7043) + dnodes.append(dnode) + + self.TDDnodes = MyDnodes(dnodes) + self.TDDnodes.init("") + self.TDDnodes.setTestCluster(testCluster) + self.TDDnodes.setValgrind(valgrind) + self.TDDnodes.stopAll() + for dnode in self.TDDnodes.dnodes: + self.TDDnodes.deploy(dnode.index,{}) + + for dnode in self.TDDnodes.dnodes: + self.TDDnodes.start(dnode.index) + + # create cluster + + for dnode in self.TDDnodes.dnodes: + print(dnode.cfgDict) + dnode_id = dnode.cfgDict["fqdn"] + ":" +dnode.cfgDict["serverPort"] + dnode_first_host = dnode.cfgDict["firstEp"].split(":")[0] + dnode_first_port = dnode.cfgDict["firstEp"].split(":")[-1] + cmd = f" taos -h {dnode_first_host} -P {dnode_first_port} -s ' create dnode \"{dnode_id} \" ' ;" + print(cmd) + os.system(cmd) + + time.sleep(2) + tdLog.info(" create cluster done! ") + + + + def getConnection(self, dnode): + host = dnode.cfgDict["fqdn"] + port = dnode.cfgDict["serverPort"] + config_dir = dnode.cfgDir + return taos.connect(host=host, port=int(port), config=config_dir) + + def restart_udfd(self, dnode): + + buildPath = self.getBuildPath() + + if (buildPath == ""): + tdLog.exit("taosd not found!") + else: + tdLog.info("taosd found in %s" % buildPath) + + cfgPath = dnode.cfgDir + + udfdPath = buildPath +'/build/bin/udfd' + + for i in range(5): + + tdLog.info(" loop restart udfd %d_th at dnode_index : %s" % (i ,dnode.index)) + self.basic_udf_query(dnode) + # stop udfd cmds + get_processID = "ps -ef | grep -w udfd | grep %s | grep 'root' | grep -v grep| grep -v defunct | awk '{print $2}'"%cfgPath + processID = subprocess.check_output(get_processID, shell=True).decode("utf-8") + stop_udfd = " kill -9 %s" % processID + os.system(stop_udfd) + self.basic_udf_query(dnode) + + def test_restart_udfd_All_dnodes(self): + + for dnode in self.TDDnodes.dnodes: + tdLog.info(" start restart udfd for dnode_index :%s" %dnode.index ) + self.restart_udfd(dnode) + + + def run(self): # sourcery skip: extract-duplicate-method, remove-redundant-fstring + print(self.master_dnode.cfgDict) + self.prepare_data() + self.prepare_udf_so() + self.create_udf_function() + self.basic_udf_query(self.master_dnode) + # self.check_UDF_query() + self.restart_udfd(self.master_dnode) + # self.test_restart_udfd_All_dnodes() + + + + def stop(self): + tdSql.close() + tdLog.success(f"{__file__} successfully executed") + +tdCases.addLinux(__file__, TDTestCase()) +tdCases.addWindows(__file__, TDTestCase()) \ No newline at end of file diff --git a/tests/system-test/0-others/user_control.py b/tests/system-test/0-others/user_control.py index 78aefd5e9ef2366af1c7b4113918ffa40f844bb8..3adc31cc39c182fcc2ba5a6083eb4d2f4c7aaeb9 100644 --- a/tests/system-test/0-others/user_control.py +++ b/tests/system-test/0-others/user_control.py @@ -1,18 +1,37 @@ +from tabnanny import check import taos -import sys +import time import inspect import traceback +from dataclasses import dataclass from util.log import * from util.sql import * from util.cases import * from util.dnodes import * - PRIVILEGES_ALL = "ALL" PRIVILEGES_READ = "READ" PRIVILEGES_WRITE = "WRITE" +WEIGHT_ALL = 5 +WEIGHT_READ = 2 +WEIGHT_WRITE = 3 + +PRIMARY_COL = "ts" + +INT_COL = "c1" +BINT_COL = "c2" +SINT_COL = "c3" +TINT_COL = "c4" +FLOAT_COL = "c5" +DOUBLE_COL = "c6" +BOOL_COL = "c7" + +BINARY_COL = "c8" +NCHAR_COL = "c9" +TS_COL = "c10" + class TDconnect: def __init__(self, host = None, @@ -81,6 +100,7 @@ class TDconnect: self.cursor.close() self._conn.close() + def taos_connect( host = "127.0.0.1", port = 6030, @@ -98,6 +118,15 @@ def taos_connect( config=config ) + +@dataclass +class User: + name : str = None + passwd : str = None + db_set : set = None + priv : str = None + priv_weight : int = 0 + class TDTestCase: def init(self, conn, logSql): @@ -108,6 +137,22 @@ class TDTestCase: def __user_list(self): return [f"user_test{i}" for i in range(self.users_count) ] + def __users(self): + self.users = [] + self.root_user = User() + self.root_user.name = "root" + self.root_user.passwd = "taosdata" + self.root_user.db_set = set("*") + self.root_user.priv = PRIVILEGES_ALL + self.root_user.priv_weight = WEIGHT_ALL + for i in range(self.users_count): + user = User() + user.name = f"user_test{i}" + user.passwd = f"taosdata{i}" + user.db_set = set() + self.users.append(user) + return self.users + @property def __passwd_list(self): return [f"taosdata{i}" for i in range(self.users_count) ] @@ -189,10 +234,211 @@ class TDTestCase: for sql in sqls: tdSql.error(sql) - - def grant_user_privileges(self, privilege, dbname=None, user_name="root"): + def __grant_user_privileges(self, privilege, dbname=None, user_name="root"): return f"GRANT {privilege} ON {self.__priv_level(dbname)} TO {user_name} " + def __revoke_user_privileges(self, privilege, dbname=None, user_name="root"): + return f"REVOKE {privilege} ON {self.__priv_level(dbname)} FROM {user_name} " + + def __user_check(self, user:User=None, check_priv=PRIVILEGES_ALL): + if user is None: + user = self.root_user + with taos_connect(user=user.name, passwd=user.passwd) as use: + time.sleep(2) + use.query("use db") + use.query("show tables") + if check_priv == PRIVILEGES_ALL: + use.query("select * from ct1") + use.query("insert into t1 (ts) values (now())") + elif check_priv == PRIVILEGES_READ: + use.query("select * from ct1") + use.error("insert into t1 (ts) values (now())") + elif check_priv == PRIVILEGES_WRITE: + use.error("select * from ct1") + use.query("insert into t1 (ts) values (now())") + elif check_priv is None: + use.error("select * from ct1") + use.error("insert into t1 (ts) values (now())") + + def __change_user_priv(self, user: User, pre_priv, invoke=False): + if user.priv == pre_priv and invoke : + return + if user.name == "root": + return + + if pre_priv.upper() == PRIVILEGES_ALL: + pre_weight = -5 if invoke else 5 + elif pre_priv.upper() == PRIVILEGES_READ: + pre_weight = -2 if invoke else 2 + elif pre_priv.upper() == PRIVILEGES_WRITE: + pre_weight = -3 if invoke else 3 + else: + return + pre_weight += user.priv_weight + + if pre_weight >= 5: + user.priv = PRIVILEGES_ALL + user.priv_weight = 5 + elif pre_weight == 3: + user.priv = PRIVILEGES_WRITE + user.priv_weight = pre_weight + elif pre_weight == 2: + user.priv_weight = pre_weight + user.priv = PRIVILEGES_READ + elif pre_weight in [1, -1]: + return + elif pre_weight <= 0: + user.priv_weight = 0 + user.priv = "" + + return user + + def grant_user(self, user: User = None, priv=PRIVILEGES_ALL, dbname=None): + if not user: + user = self.root_user + sql = self.__grant_user_privileges(privilege=priv, dbname=dbname, user_name=user.name) + tdLog.info(sql) + if (user not in self.users and user.name != "root") or priv not in (PRIVILEGES_ALL, PRIVILEGES_READ, PRIVILEGES_WRITE): + tdSql.error(sql) + tdSql.query(sql) + self.__change_user_priv(user=user, pre_priv=priv) + user.db_set.add(dbname) + time.sleep(1) + + def revoke_user(self, user: User = None, priv=PRIVILEGES_ALL, dbname=None): + sql = self.__revoke_user_privileges(privilege=priv, dbname=dbname, user_name=user.name) + tdLog.info(sql) + if user is None or priv not in (PRIVILEGES_ALL, PRIVILEGES_READ, PRIVILEGES_WRITE): + tdSql.error(sql) + tdSql.query(sql) + self.__change_user_priv(user=user, pre_priv=priv, invoke=True) + if user.name != "root": + user.db_set.discard(dbname) if dbname else user.db_set.clear() + time.sleep(1) + + def test_priv_change_current(self): + tdLog.printNoPrefix("==========step 1.0: if do not grant, can not read/write") + self.__user_check(user=self.root_user) + self.__user_check(user=self.users[0], check_priv=None) + + tdLog.printNoPrefix("==========step 1.1: grant read, can read, can not write") + self.grant_user(user=self.users[0], priv=PRIVILEGES_READ) + self.__user_check(user=self.users[0], check_priv=PRIVILEGES_READ) + + tdLog.printNoPrefix("==========step 1.2: grant write, can write") + self.grant_user(user=self.users[1], priv=PRIVILEGES_WRITE) + self.__user_check(user=self.users[1], check_priv=PRIVILEGES_WRITE) + + tdLog.printNoPrefix("==========step 1.3: grant all, can write and read") + self.grant_user(user=self.users[2]) + self.__user_check(user=self.users[2], check_priv=PRIVILEGES_ALL) + + tdLog.printNoPrefix("==========step 1.4: grant read to write = all ") + self.grant_user(user=self.users[0], priv=PRIVILEGES_WRITE) + self.__user_check(user=self.users[0], check_priv=PRIVILEGES_ALL) + + tdLog.printNoPrefix("==========step 1.5: revoke write from all = read ") + self.revoke_user(user=self.users[0], priv=PRIVILEGES_WRITE) + self.__user_check(user=self.users[0], check_priv=PRIVILEGES_READ) + + tdLog.printNoPrefix("==========step 1.6: grant write to read = all") + self.grant_user(user=self.users[1], priv=PRIVILEGES_READ) + self.__user_check(user=self.users[1], check_priv=PRIVILEGES_ALL) + + tdLog.printNoPrefix("==========step 1.7: revoke read from all = write ") + self.revoke_user(user=self.users[1], priv=PRIVILEGES_READ) + self.__user_check(user=self.users[1], check_priv=PRIVILEGES_WRITE) + + tdLog.printNoPrefix("==========step 1.8: grant read to all = all") + self.grant_user(user=self.users[0], priv=PRIVILEGES_ALL) + self.__user_check(user=self.users[0], check_priv=PRIVILEGES_ALL) + + tdLog.printNoPrefix("==========step 1.9: grant write to all = all") + self.grant_user(user=self.users[1], priv=PRIVILEGES_ALL) + self.__user_check(user=self.users[1], check_priv=PRIVILEGES_ALL) + + tdLog.printNoPrefix("==========step 1.10: grant all to read = all") + self.grant_user(user=self.users[0], priv=PRIVILEGES_READ) + self.__user_check(user=self.users[0], check_priv=PRIVILEGES_ALL) + + tdLog.printNoPrefix("==========step 1.11: grant all to write = all") + self.grant_user(user=self.users[1], priv=PRIVILEGES_WRITE) + self.__user_check(user=self.users[1], check_priv=PRIVILEGES_ALL) + + ### init user + self.revoke_user(user=self.users[0], priv=PRIVILEGES_WRITE) + self.revoke_user(user=self.users[1], priv=PRIVILEGES_READ) + + tdLog.printNoPrefix("==========step 1.12: revoke read from write = no change") + self.revoke_user(user=self.users[1], priv=PRIVILEGES_READ) + self.__user_check(user=self.users[1], check_priv=PRIVILEGES_WRITE) + + tdLog.printNoPrefix("==========step 1.13: revoke write from read = no change") + self.revoke_user(user=self.users[0], priv=PRIVILEGES_WRITE) + self.__user_check(user=self.users[0], check_priv=PRIVILEGES_READ) + + tdLog.printNoPrefix("==========step 1.14: revoke read from read = nothing") + self.revoke_user(user=self.users[0], priv=PRIVILEGES_READ) + self.__user_check(user=self.users[0], check_priv=None) + + tdLog.printNoPrefix("==========step 1.15: revoke write from write = nothing") + self.revoke_user(user=self.users[1], priv=PRIVILEGES_WRITE) + self.__user_check(user=self.users[1], check_priv=None) + + ### init user + self.grant_user(user=self.users[0], priv=PRIVILEGES_READ) + self.revoke_user(user=self.users[1], priv=PRIVILEGES_WRITE) + + tdLog.printNoPrefix("==========step 1.16: revoke all from write = nothing") + self.revoke_user(user=self.users[1], priv=PRIVILEGES_ALL) + self.__user_check(user=self.users[1], check_priv=None) + + tdLog.printNoPrefix("==========step 1.17: revoke all from read = nothing") + self.revoke_user(user=self.users[0], priv=PRIVILEGES_ALL) + self.__user_check(user=self.users[0], check_priv=None) + + tdLog.printNoPrefix("==========step 1.18: revoke all from all = nothing") + self.revoke_user(user=self.users[2], priv=PRIVILEGES_ALL) + time.sleep(3) + self.__user_check(user=self.users[2], check_priv=None) + + def __grant_err(self): + return [ + self.__grant_user_privileges(privilege=self.__privilege[0], user_name="") , + self.__grant_user_privileges(privilege=self.__privilege[0], user_name="*") , + self.__grant_user_privileges(privilege=self.__privilege[1], dbname="not_exist_db", user_name=self.__user_list[0]), + self.__grant_user_privileges(privilege="any_priv", user_name=self.__user_list[0]), + self.__grant_user_privileges(privilege="", dbname="db", user_name=self.__user_list[0]) , + self.__grant_user_privileges(privilege=" ".join(self.__privilege), user_name=self.__user_list[0]) , + f"GRANT {self.__privilege[0]} ON * TO {self.__user_list[0]}" , + f"GRANT {self.__privilege[0]} ON db.t1 TO {self.__user_list[0]}" , + ] + + def __revoke_err(self): + return [ + self.__revoke_user_privileges(privilege=self.__privilege[0], user_name="") , + self.__revoke_user_privileges(privilege=self.__privilege[0], user_name="*") , + self.__revoke_user_privileges(privilege=self.__privilege[1], dbname="not_exist_db", user_name=self.__user_list[0]), + self.__revoke_user_privileges(privilege="any_priv", user_name=self.__user_list[0]), + self.__revoke_user_privileges(privilege="", dbname="db", user_name=self.__user_list[0]) , + self.__revoke_user_privileges(privilege=" ".join(self.__privilege), user_name=self.__user_list[0]) , + f"REVOKE {self.__privilege[0]} ON * FROM {self.__user_list[0]}" , + f"REVOKE {self.__privilege[0]} ON db.t1 FROM {self.__user_list[0]}" , + ] + + def test_grant_err(self): + for sql in self.__grant_err(): + tdSql.error(sql) + + def test_revoke_err(self): + for sql in self.__revoke_err(): + tdSql.error(sql) + + def test_change_priv(self): + self.test_grant_err() + self.test_revoke_err() + self.test_priv_change_current() + def test_user_create(self): self.create_user_current() self.create_user_err() @@ -218,7 +464,6 @@ class TDTestCase: else: tdLog.info("connect successfully, user and pass matched!") - def login_err(self, user, passwd): login_except, _ = self.user_login(user, passwd) if login_except: @@ -253,7 +498,112 @@ class TDTestCase: self.drop_user_error() self.drop_user_current() + def __create_tb(self): + + tdLog.printNoPrefix("==========step1:create table") + create_stb_sql = f'''create table stb1( + ts timestamp, {INT_COL} int, {BINT_COL} bigint, {SINT_COL} smallint, {TINT_COL} tinyint, + {FLOAT_COL} float, {DOUBLE_COL} double, {BOOL_COL} bool, + {BINARY_COL} binary(16), {NCHAR_COL} nchar(32), {TS_COL} timestamp + ) tags (t1 int) + ''' + create_ntb_sql = f'''create table t1( + ts timestamp, {INT_COL} int, {BINT_COL} bigint, {SINT_COL} smallint, {TINT_COL} tinyint, + {FLOAT_COL} float, {DOUBLE_COL} double, {BOOL_COL} bool, + {BINARY_COL} binary(16), {NCHAR_COL} nchar(32), {TS_COL} timestamp + ) + ''' + tdSql.execute(create_stb_sql) + tdSql.execute(create_ntb_sql) + + for i in range(4): + tdSql.execute(f'create table ct{i+1} using stb1 tags ( {i+1} )') + { i % 32767 }, { i % 127}, { i * 1.11111 }, { i * 1000.1111 }, { i % 2} + + def __insert_data(self, rows): + now_time = int(datetime.datetime.timestamp(datetime.datetime.now()) * 1000) + for i in range(rows): + tdSql.execute( + f"insert into ct1 values ( { now_time - i * 1000 }, {i}, {11111 * i}, {111 * i % 32767 }, {11 * i % 127}, {1.11*i}, {1100.0011*i}, {i%2}, 'binary{i}', 'nchar_测试_{i}', { now_time + 1 * i } )" + ) + tdSql.execute( + f"insert into ct4 values ( { now_time - i * 7776000000 }, {i}, {11111 * i}, {111 * i % 32767 }, {11 * i % 127}, {1.11*i}, {1100.0011*i}, {i%2}, 'binary{i}', 'nchar_测试_{i}', { now_time + 1 * i } )" + ) + tdSql.execute( + f"insert into ct2 values ( { now_time - i * 7776000000 }, {-i}, {-11111 * i}, {-111 * i % 32767 }, {-11 * i % 127}, {-1.11*i}, {-1100.0011*i}, {i%2}, 'binary{i}', 'nchar_测试_{i}', { now_time + 1 * i } )" + ) + tdSql.execute( + f'''insert into ct1 values + ( { now_time - rows * 5 }, 0, 0, 0, 0, 0, 0, 0, 'binary0', 'nchar_测试_0', { now_time + 8 } ) + ( { now_time + 10000 }, { rows }, -99999, -999, -99, -9.99, -99.99, 1, 'binary9', 'nchar_测试_9', { now_time + 9 } ) + ''' + ) + + tdSql.execute( + f'''insert into ct4 values + ( { now_time - rows * 7776000000 }, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL ) + ( { now_time - rows * 3888000000 + 10800000 }, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL ) + ( { now_time + 7776000000 }, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL ) + ( + { now_time + 5184000000}, {pow(2,31)-pow(2,15)}, {pow(2,63)-pow(2,30)}, 32767, 127, + { 3.3 * pow(10,38) }, { 1.3 * pow(10,308) }, { rows % 2 }, "binary_limit-1", "nchar_测试_limit-1", { now_time - 86400000} + ) + ( + { now_time + 2592000000 }, {pow(2,31)-pow(2,16)}, {pow(2,63)-pow(2,31)}, 32766, 126, + { 3.2 * pow(10,38) }, { 1.2 * pow(10,308) }, { (rows-1) % 2 }, "binary_limit-2", "nchar_测试_limit-2", { now_time - 172800000} + ) + ''' + ) + + tdSql.execute( + f'''insert into ct2 values + ( { now_time - rows * 7776000000 }, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL ) + ( { now_time - rows * 3888000000 + 10800000 }, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL ) + ( { now_time + 7776000000 }, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL ) + ( + { now_time + 5184000000 }, { -1 * pow(2,31) + pow(2,15) }, { -1 * pow(2,63) + pow(2,30) }, -32766, -126, + { -1 * 3.2 * pow(10,38) }, { -1.2 * pow(10,308) }, { rows % 2 }, "binary_limit-1", "nchar_测试_limit-1", { now_time - 86400000 } + ) + ( + { now_time + 2592000000 }, { -1 * pow(2,31) + pow(2,16) }, { -1 * pow(2,63) + pow(2,31) }, -32767, -127, + { - 3.3 * pow(10,38) }, { -1.3 * pow(10,308) }, { (rows-1) % 2 }, "binary_limit-2", "nchar_测试_limit-2", { now_time - 172800000 } + ) + ''' + ) + + for i in range(rows): + insert_data = f'''insert into t1 values + ( { now_time - i * 3600000 }, {i}, {i * 11111}, { i % 32767 }, { i % 127}, { i * 1.11111 }, { i * 1000.1111 }, { i % 2}, + "binary_{i}", "nchar_测试_{i}", { now_time - 1000 * i } ) + ''' + tdSql.execute(insert_data) + tdSql.execute( + f'''insert into t1 values + ( { now_time + 10800000 }, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL ) + ( { now_time - (( rows // 2 ) * 60 + 30) * 60000 }, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL ) + ( { now_time - rows * 3600000 }, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL ) + ( { now_time + 7200000 }, { pow(2,31) - pow(2,15) }, { pow(2,63) - pow(2,30) }, 32767, 127, + { 3.3 * pow(10,38) }, { 1.3 * pow(10,308) }, { rows % 2 }, + "binary_limit-1", "nchar_测试_limit-1", { now_time - 86400000 } + ) + ( + { now_time + 3600000 } , { pow(2,31) - pow(2,16) }, { pow(2,63) - pow(2,31) }, 32766, 126, + { 3.2 * pow(10,38) }, { 1.2 * pow(10,308) }, { (rows-1) % 2 }, + "binary_limit-2", "nchar_测试_limit-2", { now_time - 172800000 } + ) + ''' + ) + def run(self): + tdSql.prepare() + self.__create_tb() + self.rows = 10 + self.users_count = 5 + self.__insert_data(self.rows) + self.users = self.__users() + + tdDnodes.stop(1) + tdDnodes.start(1) # 默认只有 root 用户 tdLog.printNoPrefix("==========step0: init, user list only has root account") @@ -264,7 +614,6 @@ class TDTestCase: # root用户权限 # 创建用户测试 tdLog.printNoPrefix("==========step1: create user test") - self.users_count = 5 self.test_user_create() # 查看用户 @@ -276,6 +625,9 @@ class TDTestCase: self.login_currrent(self.__user_list[0], self.__passwd_list[0]) self.login_err(self.__user_list[0], f"new{self.__passwd_list[0]}") + # 用户权限设置 + self.test_change_priv() + # 修改密码 tdLog.printNoPrefix("==========step3: alter user pass test") self.test_alter_pass() diff --git a/tests/system-test/1-insert/insertWithMoreVgroup.py b/tests/system-test/1-insert/insertWithMoreVgroup.py index d8720e8045dece96b1205d1dbeaf38f10b827d44..f0f35831dbd5a276c98e2eede114ea14b7bcc5b2 100644 --- a/tests/system-test/1-insert/insertWithMoreVgroup.py +++ b/tests/system-test/1-insert/insertWithMoreVgroup.py @@ -30,9 +30,12 @@ class TDTestCase: # # --------------- main frame ------------------- # - clientCfgDict = {'queryproxy': '1'} + clientCfgDict = {'queryproxy': '1','debugFlag': 135} clientCfgDict["queryproxy"] = '2' + clientCfgDict["debugFlag"] = 143 + updatecfgDict = {'clientCfg': {}} + updatecfgDict = {'debugFlag': 143} updatecfgDict["clientCfg"] = clientCfgDict def caseDescription(self): ''' @@ -116,7 +119,7 @@ class TDTestCase: # tdLog.debug("spent %.2fs to create 1 stable and %d table, create speed is %.2f table/s... [OK]"% (spendTime,count,speedCreate)) return - def mutiThread_create_tables(self,host,dbname,stbname,vgroups,threadNumbers,count): + def mutiThread_create_tables(self,host,dbname,stbname,vgroups,threadNumbers,childrowcount): buildPath = self.getBuildPath() config = buildPath+ "../sim/dnode1/cfg/" @@ -125,7 +128,7 @@ class TDTestCase: tsql.execute("drop database if exists %s"%dbname) tsql.execute("create database %s vgroups %d"%(dbname,vgroups)) tsql.execute("use %s" %dbname) - count=int(count) + count=int(childrowcount) threads = [] for i in range(threadNumbers): tsql.execute("create stable %s%d(ts timestamp, c1 int, c2 binary(10)) tags(t1 int)"%(stbname,i)) @@ -137,7 +140,7 @@ class TDTestCase: tr.join() end_time = time.time() spendTime=end_time-start_time - speedCreate=count/spendTime + speedCreate=threadNumbers*count/spendTime tdLog.debug("spent %.2fs to create %d stable and %d table, create speed is %.2f table/s... [OK]"% (spendTime,threadNumbers,threadNumbers*count,speedCreate)) return @@ -146,41 +149,39 @@ class TDTestCase: # insert data - def insert_data(self, host, dbname, stbname, ts_start,rowCount): + def insert_data(self, host, dbname, stbname, chilCount, ts_start, rowCount): buildPath = self.getBuildPath() config = buildPath+ "../sim/dnode1/cfg/" - tsql=self.newcur(host,config) tdLog.debug("ready to inser data") - tsql.execute("use %s" %dbname) pre_insert = "insert into " sql = pre_insert - tcount=int(tcount) - allRows=tcount*rowCount + chilCount=int(chilCount) + allRows=chilCount*rowCount tdLog.debug("doing insert data into stable-index:%s rows:%d ..."%(stbname, allRows)) exeStartTime=time.time() - for i in range(0,tcount): + for i in range(0,chilCount): sql += " %s_%d values "%(stbname,i) for j in range(rowCount): sql += "(%d, %d, 'taos_%d') "%(ts_start + j*1000, j, j) - if j >0 and j%5000 == 0: + if j >0 and j%4000 == 0: # print(sql) - tdSql.execute(sql) + tsql.execute(sql) sql = "insert into %s_%d values " %(stbname,i) # end sql if sql != pre_insert: # print(sql) - tdSql.execute(sql) + print(len(sql)) + tsql.execute(sql) exeEndTime=time.time() spendTime=exeEndTime-exeStartTime speedInsert=allRows/spendTime - # tdLog.debug("spent %.2fs to INSERT %d rows , insert rate is %.2f rows/s... [OK]"% (spendTime,allRows,speedInsert)) - - tdLog.debug("INSERT TABLE DATA ............ [OK]") + tdLog.debug("spent %.2fs to INSERT %d rows into %s , insert rate is %.2f rows/s... [OK]"% (spendTime,allRows,stbname,speedInsert)) + # tdLog.debug("INSERT TABLE DATA ............ [OK]") return - def mutiThread_insert_data(self, host, dbname, stbname, threadNumbers, ts_start, tcountStart,tcountStop,rowCount): + def mutiThread_insert_data(self, host, dbname, stbname, threadNumbers, chilCount, ts_start, childrowcount): buildPath = self.getBuildPath() config = buildPath+ "../sim/dnode1/cfg/" @@ -188,42 +189,11 @@ class TDTestCase: tdLog.debug("ready to inser data") tsql.execute("use %s" %dbname) - pre_insert = "insert into " - sql = pre_insert - tcount=tcountStop-tcountStart - allRows=tcount*rowCount - tdLog.debug("doing insert data into stable:%s rows:%d ..."%(stbname, allRows)) - exeStartTime=time.time() - for i in range(tcountStart,tcountStop): - sql += " %s_%d values "%(stbname,i) - for j in range(rowCount): - sql += "(%d, %d, 'taos_%d') "%(ts_start + j*1000, j, j) - if j >0 and j%5000 == 0: - # print(sql) - tdSql.execute(sql) - sql = "insert into %s_%d values " %(stbname,i) - # end sql - if sql != pre_insert: - # print(sql) - tdSql.execute(sql) - exeEndTime=time.time() - spendTime=exeEndTime-exeStartTime - speedInsert=allRows/spendTime - # tdLog.debug("spent %.2fs to INSERT %d rows , insert rate is %.2f rows/s... [OK]"% (spendTime,allRows,speedInsert)) - - tdLog.debug("INSERT TABLE DATA ............ [OK]") - - - buildPath = self.getBuildPath() - config = buildPath+ "../sim/dnode1/cfg/" - - tsql=self.newcur(host,config) - tsql.execute("use %s" %dbname) - count=int(count) + chilCount=int(chilCount) threads = [] for i in range(threadNumbers): - tsql.execute("create stable %s%d(ts timestamp, c1 int, c2 binary(10)) tags(t1 int)"%(stbname,i)) - threads.append(thd.Thread(target=self.create_tables, args=(host, dbname, stbname+"%d"%i, count,))) + # tsql.execute("create stable %s%d(ts timestamp, c1 int, c2 binary(10)) tags(t1 int)"%(stbname,i)) + threads.append(thd.Thread(target=self.insert_data, args=(host, dbname, stbname+"%d"%i, chilCount, ts_start, childrowcount,))) start_time = time.time() for tr in threads: tr.start() @@ -231,8 +201,18 @@ class TDTestCase: tr.join() end_time = time.time() spendTime=end_time-start_time - speedCreate=count/spendTime - tdLog.debug("spent %.2fs to create %d stable and %d table, create speed is %.2f table/s... [OK]"% (spendTime,threadNumbers,threadNumbers*count,speedCreate)) + tableCounts=threadNumbers*chilCount + stableRows=chilCount*childrowcount + allRows=stableRows*threadNumbers + speedInsert=allRows/spendTime + + for i in range(threadNumbers): + tdSql.execute("use %s" %dbname) + tdSql.query("select count(*) from %s%d"%(stbname,i)) + tdSql.checkData(0,0,stableRows) + tdLog.debug("spent %.2fs to insert %d rows into %d stable and %d table, speed is %.2f table/s... [OK]"% (spendTime,allRows,threadNumbers,tableCounts,speedInsert)) + tdLog.debug("INSERT TABLE DATA ............ [OK]") + return @@ -288,7 +268,10 @@ class TDTestCase: def test_case1(self): tdLog.debug("-----create database and muti-thread create tables test------- ") #host,dbname,stbname,vgroups,threadNumbers,tcountStart,tcountStop - self.mutiThread_create_tables(host="localhost",dbname="db2",stbname="stb2", vgroups=1, threadNumbers=5, count=10000) + #host, dbname, stbname, threadNumbers, chilCount, ts_start, childrowcount + self.mutiThread_create_tables(host="localhost",dbname="db",stbname="stb", vgroups=1, threadNumbers=5, childrowcount=50) + self.mutiThread_insert_data(host="localhost",dbname="db",stbname="stb", threadNumbers=5,chilCount=50,ts_start=self.ts,childrowcount=10) + return # test case2 base:insert data @@ -366,17 +349,17 @@ class TDTestCase: # run case def run(self): - # # test base case - # self.test_case1() - # tdLog.debug(" LIMIT test_case1 ............ [OK]") + # create database and tables。 + self.test_case1() + tdLog.debug(" LIMIT test_case1 ............ [OK]") - # test case - # self.test_case2() - # tdLog.debug(" LIMIT test_case2 ............ [OK]") + # # taosBenchmark : create database and table + # self.test_case2() + # tdLog.debug(" LIMIT test_case2 ............ [OK]") - # test case - self.test_case3() - tdLog.debug(" LIMIT test_case3 ............ [OK]") + # # taosBenchmark:create database/table and insert data + # self.test_case3() + # tdLog.debug(" LIMIT test_case3 ............ [OK]") # # test qnode diff --git a/tests/system-test/2-query/json_tag.py b/tests/system-test/2-query/json_tag.py new file mode 100644 index 0000000000000000000000000000000000000000..d8ef8fa363fe1f2911f4ea3fde9d43a82f7a8a88 --- /dev/null +++ b/tests/system-test/2-query/json_tag.py @@ -0,0 +1,565 @@ +################################################################### +# Copyright (c) 2016 by TAOS Technologies, Inc. +# All rights reserved. +# +# This file is proprietary and confidential to TAOS Technologies. +# No part of this file may be reproduced, db_test.stored, transmitted, +# disclosed or used in any form or by any means other than as +# expressly provided by the written permission from Jianhui Tao +# +################################################################### + +# -*- coding: utf-8 -*- + +import sys +import taos +from util.log import tdLog +from util.cases import tdCases +from util.sql import tdSql +import json + + +class TDTestCase: + def caseDescription(self): + ''' + Json tag test case, include create table with json tag, select json tag and query with json tag in where condition, besides, include json tag in group by/order by/join/subquery. + case1: [TD-12452] fix error if json tag is NULL + case2: [TD-12389] describe child table, tag length error if the tag is json tag + ''' + return + + def init(self, conn, logSql): + tdLog.debug("start to execute %s" % __file__) + tdSql.init(conn.cursor(), logSql) + + def run(self): + tdSql.prepare() + print("============== STEP 1 ===== prepare data & validate json string") + tdSql.error("create table if not exists jsons1(ts timestamp, dataInt int, dataBool bool, dataStr nchar(50), dataStrBin binary(150)) tags(jtag json, tagint int)") + tdSql.error("create table if not exists jsons1(ts timestamp, data json) tags(tagint int)") + tdSql.execute("create table if not exists jsons1(ts timestamp, dataInt int, dataBool bool, dataStr nchar(50), dataStrBin binary(150)) tags(jtag json)") + tdSql.execute("insert into jsons1_1 using jsons1 tags('{\"tag1\":\"fff\",\"tag2\":5, \"tag3\":true}') values(1591060618000, 1, false, 'json1', '你是') (1591060608000, 23, true, '等等', 'json')") + tdSql.execute("insert into jsons1_2 using jsons1 tags('{\"tag1\":5,\"tag2\":\"beijing\"}') values (1591060628000, 2, true, 'json2', 'sss')") + tdSql.execute("insert into jsons1_3 using jsons1 tags('{\"tag1\":false,\"tag2\":\"beijing\"}') values (1591060668000, 3, false, 'json3', 'efwe')") + tdSql.execute("insert into jsons1_4 using jsons1 tags('{\"tag1\":null,\"tag2\":\"shanghai\",\"tag3\":\"hello\"}') values (1591060728000, 4, true, 'json4', '323sd')") + tdSql.execute("insert into jsons1_5 using jsons1 tags('{\"tag1\":1.232, \"tag2\":null}') values(1591060928000, 1, false, '你就会', 'ewe')") + tdSql.execute("insert into jsons1_6 using jsons1 tags('{\"tag1\":11,\"tag2\":\"\",\"tag2\":null}') values(1591061628000, 11, false, '你就会','')") + tdSql.execute("insert into jsons1_7 using jsons1 tags('{\"tag1\":\"收到货\",\"tag2\":\"\",\"tag3\":null}') values(1591062628000, 2, NULL, '你就会', 'dws')") + + # test duplicate key using the first one. elimate empty key + tdSql.execute("CREATE TABLE if not exists jsons1_8 using jsons1 tags('{\"tag1\":null, \"tag1\":true, \"tag1\":45, \"1tag$\":2, \" \":90, \"\":32}')") + #tdSql.query("select jtag from jsons1_8") + #tdSql.checkData(0, 0, '{"tag1":null,"1tag$":2," ":90}') + + # test empty json string, save as jtag is NULL + tdSql.execute("insert into jsons1_9 using jsons1 tags('\t') values (1591062328000, 24, NULL, '你就会', '2sdw')") + tdSql.execute("CREATE TABLE if not exists jsons1_10 using jsons1 tags('')") + tdSql.execute("CREATE TABLE if not exists jsons1_11 using jsons1 tags(' ')") + tdSql.execute("CREATE TABLE if not exists jsons1_12 using jsons1 tags('{}')") + tdSql.execute("CREATE TABLE if not exists jsons1_13 using jsons1 tags('null')") + + # test invalidate json + tdSql.error("CREATE TABLE if not exists jsons1_14 using jsons1 tags('\"efwewf\"')") + tdSql.error("CREATE TABLE if not exists jsons1_14 using jsons1 tags('3333')") + tdSql.error("CREATE TABLE if not exists jsons1_14 using jsons1 tags('33.33')") + tdSql.error("CREATE TABLE if not exists jsons1_14 using jsons1 tags('false')") + tdSql.error("CREATE TABLE if not exists jsons1_14 using jsons1 tags('[1,true]')") + tdSql.error("CREATE TABLE if not exists jsons1_14 using jsons1 tags('{222}')") + tdSql.error("CREATE TABLE if not exists jsons1_14 using jsons1 tags('{\"fe\"}')") + # + # test invalidate json key, key must can be printed assic char + tdSql.error("CREATE TABLE if not exists jsons1_14 using jsons1 tags('{\"tag1\":[1,true]}')") + tdSql.error("CREATE TABLE if not exists jsons1_14 using jsons1 tags('{\"tag1\":{}}')") + tdSql.error("CREATE TABLE if not exists jsons1_14 using jsons1 tags('{\"。loc\":\"fff\"}')") + tdSql.error("CREATE TABLE if not exists jsons1_14 using jsons1 tags('{\"\t\":\"fff\"}')") + tdSql.error("CREATE TABLE if not exists jsons1_14 using jsons1 tags('{\"试试\":\"fff\"}')") + + # test invalidate json value, value number can not be inf,nan TD-12166 + tdSql.error("CREATE TABLE if not exists jsons1_14 using jsons1 tags('{\"k\":1.8e308}')") + tdSql.error("CREATE TABLE if not exists jsons1_14 using jsons1 tags('{\"k\":-1.8e308}')") + # + #test length limit + char1= ''.join(['abcd']*64) + char3= ''.join(['abcd']*1021) + print(len(char3)) # 4084 + tdSql.error("CREATE TABLE if not exists jsons1_15 using jsons1 tags('{\"%s1\":5}')" % char1) # len(key)=257 + tdSql.execute("CREATE TABLE if not exists jsons1_15 using jsons1 tags('{\"%s\":5}')" % char1) # len(key)=256 + tdSql.error("CREATE TABLE if not exists jsons1_16 using jsons1 tags('{\"TSSSS\":\"%s\"}')" % char3) # len(object)=4096 + #tdSql.execute("CREATE TABLE if not exists jsons1_16 using jsons1 tags('{\"TSSS\":\"%s\"}')" % char3) # len(object)=4095 + tdSql.execute("drop table if exists jsons1_15") + tdSql.execute("drop table if exists jsons1_16") + # + print("============== STEP 2 ===== alter table json tag") + tdSql.error("ALTER STABLE jsons1 add tag tag2 nchar(20)") + tdSql.error("ALTER STABLE jsons1 drop tag jtag") + tdSql.error("ALTER TABLE jsons1 MODIFY TAG jtag nchar(128)") + # + tdSql.execute("ALTER TABLE jsons1_1 SET TAG jtag='{\"tag1\":\"femail\",\"tag2\":35,\"tag3\":true}'") + # tdSql.query("select jtag from jsons1_1") + # tdSql.checkData(0, 0, '{"tag1":"femail","tag2":35,"tag3":true}') + tdSql.execute("ALTER TABLE jsons1 rename TAG jtag jtag_new") + tdSql.execute("ALTER TABLE jsons1 rename TAG jtag_new jtag") + + tdSql.execute("create table st(ts timestamp, i int) tags(t int)") + tdSql.error("ALTER STABLE st add tag jtag json") + tdSql.error("ALTER STABLE st add column jtag json") + # + # print("============== STEP 3 ===== query table") + # # test error syntax + # tdSql.error("select * from jsons1 where jtag->tag1='beijing'") + # tdSql.error("select * from jsons1 where jtag->'location'") + # tdSql.error("select * from jsons1 where jtag->''") + # tdSql.error("select * from jsons1 where jtag->''=9") + # tdSql.error("select -> from jsons1") + # tdSql.error("select * from jsons1 where contains") + # tdSql.error("select * from jsons1 where jtag->") + # tdSql.error("select jtag->location from jsons1") + # tdSql.error("select jtag contains location from jsons1") + # tdSql.error("select * from jsons1 where jtag contains location") + # tdSql.error("select * from jsons1 where jtag contains''") + # tdSql.error("select * from jsons1 where jtag contains 'location'='beijing'") + # + # # test function error + # tdSql.error("select avg(jtag->'tag1') from jsons1") + # tdSql.error("select avg(jtag) from jsons1") + # tdSql.error("select min(jtag->'tag1') from jsons1") + # tdSql.error("select min(jtag) from jsons1") + # tdSql.error("select ceil(jtag->'tag1') from jsons1") + # tdSql.error("select ceil(jtag) from jsons1") + # + # # test select normal column + # tdSql.query("select dataint from jsons1") + # tdSql.checkRows(9) + # tdSql.checkData(1, 0, 1) + + # test select json tag + # tdSql.query("select * from jsons1") + # tdSql.checkRows(8) + # tdSql.query("select jtag from jsons1") + # tdSql.checkRows(7) + # tdSql.query("select jtag from jsons1 where jtag is null") + # tdSql.checkRows(5) + # tdSql.query("select jtag from jsons1 where jtag is not null") + # tdSql.checkRows(8) + + # test jtag is NULL + #tdSql.query("select jtag from jsons1_9") + #tdSql.checkData(0, 0, None) + + # # test select json tag->'key', value is string + # tdSql.query("select jtag->'tag1' from jsons1_1") + # tdSql.checkData(0, 0, '"femail"') + # tdSql.query("select jtag->'tag2' from jsons1_6") + # tdSql.checkData(0, 0, '""') + # # test select json tag->'key', value is int + # tdSql.query("select jtag->'tag2' from jsons1_1") + # tdSql.checkData(0, 0, 35) + # # test select json tag->'key', value is bool + # tdSql.query("select jtag->'tag3' from jsons1_1") + # tdSql.checkData(0, 0, "true") + # # test select json tag->'key', value is null + # tdSql.query("select jtag->'tag1' from jsons1_4") + # tdSql.checkData(0, 0, "null") + # # test select json tag->'key', value is double + # tdSql.query("select jtag->'tag1' from jsons1_5") + # tdSql.checkData(0, 0, "1.232000000") + # # test select json tag->'key', key is not exist + # tdSql.query("select jtag->'tag10' from jsons1_4") + # tdSql.checkData(0, 0, None) + # + # tdSql.query("select jtag->'tag1' from jsons1") + # tdSql.checkRows(13) + # test header name + res = tdSql.getColNameList("select jtag->'tag1' from jsons1") + cname_list = [] + cname_list.append("jtag->'tag1'") + tdSql.checkColNameList(res, cname_list) + + + + # # test where with json tag + # tdSql.error("select * from jsons1_1 where jtag is not null") + # tdSql.error("select * from jsons1 where jtag='{\"tag1\":11,\"tag2\":\"\"}'") + # tdSql.error("select * from jsons1 where jtag->'tag1'={}") + # + # # where json value is string + # tdSql.query("select * from jsons1 where jtag->'tag2'='beijing'") + # tdSql.checkRows(2) + # tdSql.query("select dataint,tbname,jtag->'tag1',jtag from jsons1 where jtag->'tag2'='beijing'") + # tdSql.checkData(0, 0, 2) + # tdSql.checkData(0, 1, 'jsons1_2') + # tdSql.checkData(0, 2, 5) + # tdSql.checkData(0, 3, '{"tag1":5,"tag2":"beijing"}') + # tdSql.checkData(1, 0, 3) + # tdSql.checkData(1, 1, 'jsons1_3') + # tdSql.checkData(1, 2, 'false') + # tdSql.query("select * from jsons1 where jtag->'tag1'='beijing'") + # tdSql.checkRows(0) + # tdSql.query("select * from jsons1 where jtag->'tag1'='收到货'") + # tdSql.checkRows(1) + # tdSql.query("select * from jsons1 where jtag->'tag2'>'beijing'") + # tdSql.checkRows(1) + # tdSql.query("select * from jsons1 where jtag->'tag2'>='beijing'") + # tdSql.checkRows(3) + # tdSql.query("select * from jsons1 where jtag->'tag2'<'beijing'") + # tdSql.checkRows(2) + # tdSql.query("select * from jsons1 where jtag->'tag2'<='beijing'") + # tdSql.checkRows(4) + # tdSql.query("select * from jsons1 where jtag->'tag2'!='beijing'") + # tdSql.checkRows(3) + # tdSql.query("select * from jsons1 where jtag->'tag2'=''") + # tdSql.checkRows(2) + # + # # where json value is int + # tdSql.query("select * from jsons1 where jtag->'tag1'=5") + # tdSql.checkRows(1) + # tdSql.checkData(0, 1, 2) + # tdSql.query("select * from jsons1 where jtag->'tag1'=10") + # tdSql.checkRows(0) + # tdSql.query("select * from jsons1 where jtag->'tag1'<54") + # tdSql.checkRows(3) + # tdSql.query("select * from jsons1 where jtag->'tag1'<=11") + # tdSql.checkRows(3) + # tdSql.query("select * from jsons1 where jtag->'tag1'>4") + # tdSql.checkRows(2) + # tdSql.query("select * from jsons1 where jtag->'tag1'>=5") + # tdSql.checkRows(2) + # tdSql.query("select * from jsons1 where jtag->'tag1'!=5") + # tdSql.checkRows(2) + # tdSql.query("select * from jsons1 where jtag->'tag1'!=55") + # tdSql.checkRows(3) + # + # # where json value is double + # tdSql.query("select * from jsons1 where jtag->'tag1'=1.232") + # tdSql.checkRows(1) + # tdSql.query("select * from jsons1 where jtag->'tag1'<1.232") + # tdSql.checkRows(0) + # tdSql.query("select * from jsons1 where jtag->'tag1'<=1.232") + # tdSql.checkRows(1) + # tdSql.query("select * from jsons1 where jtag->'tag1'>1.23") + # tdSql.checkRows(3) + # tdSql.query("select * from jsons1 where jtag->'tag1'>=1.232") + # tdSql.checkRows(3) + # tdSql.query("select * from jsons1 where jtag->'tag1'!=1.232") + # tdSql.checkRows(2) + # tdSql.query("select * from jsons1 where jtag->'tag1'!=3.232") + # tdSql.checkRows(3) + # tdSql.error("select * from jsons1 where jtag->'tag1'/0=3") + # tdSql.error("select * from jsons1 where jtag->'tag1'/5=1") + # + # # where json value is bool + # tdSql.query("select * from jsons1 where jtag->'tag1'=true") + # tdSql.checkRows(0) + # tdSql.query("select * from jsons1 where jtag->'tag1'=false") + # tdSql.checkRows(1) + # tdSql.query("select * from jsons1 where jtag->'tag1'!=false") + # tdSql.checkRows(0) + # tdSql.error("select * from jsons1 where jtag->'tag1'>false") + # + # # where json value is null + # tdSql.query("select * from jsons1 where jtag->'tag1'=null") # only json suport =null. This synatx will change later. + # tdSql.checkRows(1) + # + # # where json is null + # tdSql.query("select * from jsons1 where jtag is null") + # tdSql.checkRows(1) + # tdSql.query("select * from jsons1 where jtag is not null") + # tdSql.checkRows(8) + # + # # where json key is null + # tdSql.query("select * from jsons1 where jtag->'tag_no_exist'=3") + # tdSql.checkRows(0) + # + # # where json value is not exist + # tdSql.query("select * from jsons1 where jtag->'tag1' is null") + # tdSql.checkData(0, 0, 'jsons1_9') + # tdSql.checkRows(1) + # tdSql.query("select * from jsons1 where jtag->'tag4' is null") + # tdSql.checkRows(9) + # tdSql.query("select * from jsons1 where jtag->'tag3' is not null") + # tdSql.checkRows(4) + # + # # test contains + # tdSql.query("select * from jsons1 where jtag contains 'tag1'") + # tdSql.checkRows(8) + # tdSql.query("select * from jsons1 where jtag contains 'tag3'") + # tdSql.checkRows(4) + # tdSql.query("select * from jsons1 where jtag contains 'tag_no_exist'") + # tdSql.checkRows(0) + # + # # test json tag in where condition with and/or + # tdSql.query("select * from jsons1 where jtag->'tag1'=false and jtag->'tag2'='beijing'") + # tdSql.checkRows(1) + # tdSql.query("select * from jsons1 where jtag->'tag1'=false or jtag->'tag2'='beijing'") + # tdSql.checkRows(2) + # tdSql.query("select * from jsons1 where jtag->'tag1'=false and jtag->'tag2'='shanghai'") + # tdSql.checkRows(0) + # tdSql.query("select * from jsons1 where jtag->'tag1'=false and jtag->'tag2'='shanghai'") + # tdSql.checkRows(0) + # tdSql.query("select * from jsons1 where jtag->'tag1'=13 or jtag->'tag2'>35") + # tdSql.checkRows(0) + # tdSql.query("select * from jsons1 where jtag->'tag1'=13 or jtag->'tag2'>35") + # tdSql.checkRows(0) + # tdSql.query("select * from jsons1 where jtag->'tag1' is not null and jtag contains 'tag3'") + # tdSql.checkRows(4) + # tdSql.query("select * from jsons1 where jtag->'tag1'='femail' and jtag contains 'tag3'") + # tdSql.checkRows(2) + # + # + # # test with between and + # tdSql.query("select * from jsons1 where jtag->'tag1' between 1 and 30") + # tdSql.checkRows(3) + # tdSql.query("select * from jsons1 where jtag->'tag1' between 'femail' and 'beijing'") + # tdSql.checkRows(2) + # + # # test with tbname/normal column + # tdSql.query("select * from jsons1 where tbname = 'jsons1_1'") + # tdSql.checkRows(2) + # tdSql.query("select * from jsons1 where tbname = 'jsons1_1' and jtag contains 'tag3'") + # tdSql.checkRows(2) + # tdSql.query("select * from jsons1 where tbname = 'jsons1_1' and jtag contains 'tag3' and dataint=3") + # tdSql.checkRows(0) + # tdSql.query("select * from jsons1 where tbname = 'jsons1_1' and jtag contains 'tag3' and dataint=23") + # tdSql.checkRows(1) + # + # + # # test where condition like + # tdSql.query("select *,tbname from jsons1 where jtag->'tag2' like 'bei%'") + # tdSql.checkRows(2) + # tdSql.query("select *,tbname from jsons1 where jtag->'tag1' like 'fe%' and jtag->'tag2' is not null") + # tdSql.checkRows(2) + # + # # test where condition in no support in + # tdSql.error("select * from jsons1 where jtag->'tag1' in ('beijing')") + # + # # test where condition match/nmath + # tdSql.query("select * from jsons1 where jtag->'tag1' match 'ma'") + # tdSql.checkRows(2) + # tdSql.query("select * from jsons1 where jtag->'tag1' match 'ma$'") + # tdSql.checkRows(0) + # tdSql.query("select * from jsons1 where jtag->'tag2' match 'jing$'") + # tdSql.checkRows(2) + # tdSql.query("select * from jsons1 where jtag->'tag1' match '收到'") + # tdSql.checkRows(1) + # tdSql.query("select * from jsons1 where jtag->'tag1' nmatch 'ma'") + # tdSql.checkRows(1) + # + # # test distinct + # tdSql.execute("insert into jsons1_14 using jsons1 tags('{\"tag1\":\"收到货\",\"tag2\":\"\",\"tag3\":null}') values(1591062628000, 2, NULL, '你就会', 'dws')") + # tdSql.query("select distinct jtag->'tag1' from jsons1") + # tdSql.checkRows(8) + # tdSql.query("select distinct jtag from jsons1") + # tdSql.checkRows(9) + # + # #test dumplicate key with normal colomn + # tdSql.execute("INSERT INTO jsons1_15 using jsons1 tags('{\"tbname\":\"tt\",\"databool\":true,\"datastr\":\"是是是\"}') values(1591060828000, 4, false, 'jjsf', \"你就会\")") + # tdSql.query("select *,tbname,jtag from jsons1 where jtag->'datastr' match '是' and datastr match 'js'") + # tdSql.checkRows(1) + # tdSql.query("select tbname,jtag->'tbname' from jsons1 where jtag->'tbname'='tt' and tbname='jsons1_14'") + # tdSql.checkRows(0) + # + # # test join + # tdSql.execute("create table if not exists jsons2(ts timestamp, dataInt int, dataBool bool, dataStr nchar(50), dataStrBin binary(150)) tags(jtag json)") + # tdSql.execute("insert into jsons2_1 using jsons2 tags('{\"tag1\":\"fff\",\"tag2\":5, \"tag3\":true}') values(1591060618000, 2, false, 'json2', '你是2')") + # tdSql.execute("insert into jsons2_2 using jsons2 tags('{\"tag1\":5,\"tag2\":null}') values (1591060628000, 2, true, 'json2', 'sss')") + # + # tdSql.execute("create table if not exists jsons3(ts timestamp, dataInt int, dataBool bool, dataStr nchar(50), dataStrBin binary(150)) tags(jtag json)") + # tdSql.execute("insert into jsons3_1 using jsons3 tags('{\"tag1\":\"fff\",\"tag2\":5, \"tag3\":true}') values(1591060618000, 3, false, 'json3', '你是3')") + # tdSql.execute("insert into jsons3_2 using jsons3 tags('{\"tag1\":5,\"tag2\":\"beijing\"}') values (1591060638000, 2, true, 'json3', 'sss')") + # tdSql.query("select 'sss',33,a.jtag->'tag3' from jsons2 a,jsons3 b where a.ts=b.ts and a.jtag->'tag1'=b.jtag->'tag1'") + # tdSql.checkData(0, 0, "sss") + # tdSql.checkData(0, 2, "true") + # + # res = tdSql.getColNameList("select 'sss',33,a.jtag->'tag3' from jsons2 a,jsons3 b where a.ts=b.ts and a.jtag->'tag1'=b.jtag->'tag1'") + # cname_list = [] + # cname_list.append("sss") + # cname_list.append("33") + # cname_list.append("a.jtag->'tag3'") + # tdSql.checkColNameList(res, cname_list) + # + # # test group by & order by json tag + # tdSql.error("select count(*) from jsons1 group by jtag->'tag1' order by jtag->'tag2'") + # tdSql.error("select count(*) from jsons1 group by jtag->'tag1' order by jtag") + # tdSql.query("select count(*) from jsons1 group by jtag->'tag1' order by jtag->'tag1' desc") + # tdSql.checkRows(8) + # tdSql.checkData(1, 0, 2) + # tdSql.checkData(1, 1, '"femail"') + # tdSql.checkData(2, 0, 1) + # tdSql.checkData(2, 1, 11) + # tdSql.checkData(5, 0, 1) + # tdSql.checkData(5, 1, "false") + # tdSql.checkData(6, 0, 1) + # tdSql.checkData(6, 1, "null") + # tdSql.checkData(7, 0, 2) + # tdSql.checkData(7, 1, None) + # + # tdSql.query("select count(*) from jsons1 group by jtag->'tag1' order by jtag->'tag1' asc") + # tdSql.checkRows(8) + # tdSql.checkData(0, 0, 2) + # tdSql.checkData(0, 1, None) + # tdSql.checkData(2, 0, 1) + # tdSql.checkData(2, 1, "false") + # tdSql.checkData(5, 0, 1) + # tdSql.checkData(5, 1, 11) + # tdSql.checkData(6, 0, 2) + # tdSql.checkData(6, 1, '"femail"') + # + # # test stddev with group by json tag + # tdSql.query("select stddev(dataint) from jsons1 group by jtag->'tag1'") + # tdSql.checkData(0, 0, 10) + # tdSql.checkData(0, 1, None) + # tdSql.checkData(1, 0, 0) + # tdSql.checkData(1, 1, "null") + # tdSql.checkData(6, 0, 11) + # tdSql.checkData(6, 1, '"femail"') + # + # res = tdSql.getColNameList("select stddev(dataint) from jsons1 group by jsons1.jtag->'tag1'") + # cname_list = [] + # cname_list.append("stddev(dataint)") + # cname_list.append("jsons1.jtag->'tag1'") + # tdSql.checkColNameList(res, cname_list) + # + # # test top/bottom with group by json tag + # tdSql.query("select top(dataint,100) from jsons1 group by jtag->'tag1'") + # tdSql.checkRows(11) + # tdSql.checkData(0, 1, 4) + # tdSql.checkData(1, 1, 24) + # tdSql.checkData(1, 2, None) + # tdSql.checkData(8, 1, 1) + # tdSql.checkData(8, 2, '"femail"') + # + # # test having + # tdSql.query("select stddev(dataint) from jsons1 group by jtag->'tag1' having stddev(dataint) > 0") + # tdSql.checkRows(2) + # + # # subquery with json tag + # tdSql.query("select * from (select jtag, dataint from jsons1)") + # tdSql.checkRows(11) + # tdSql.checkData(1, 1, 1) + # tdSql.checkData(2, 0, '{"tag1":5,"tag2":"beijing"}') + # + # tdSql.query("select jtag->'tag1' from (select jtag->'tag1', dataint from jsons1)") + # tdSql.checkRows(11) + # tdSql.checkData(1, 0, '"femail"') + # tdSql.checkData(2, 0, 5) + # + # res = tdSql.getColNameList("select jtag->'tag1' from (select jtag->'tag1', dataint from jsons1)") + # cname_list = [] + # cname_list.append("jtag->'tag1'") + # tdSql.checkColNameList(res, cname_list) + # + # tdSql.query("select ts,tbname,jtag->'tag1' from (select jtag->'tag1',tbname,ts from jsons1 order by ts)") + # tdSql.checkRows(11) + # tdSql.checkData(1, 1, "jsons1_1") + # tdSql.checkData(1, 2, '"femail"') + # + # # union all + # tdSql.error("select jtag->'tag1' from jsons1 union all select jtag->'tag2' from jsons2") + # tdSql.error("select jtag->'tag1' from jsons1_1 union all select jtag->'tag2' from jsons2_1") + # + # tdSql.query("select jtag->'tag1' from jsons1_1 union all select jtag->'tag1' from jsons2_1") + # tdSql.checkRows(2) + # tdSql.query("select dataint,jtag->'tag1',tbname from jsons1 union all select dataint,jtag->'tag1',tbname from jsons2") + # tdSql.checkRows(13) + # tdSql.query("select dataint,jtag,tbname from jsons1 union all select dataint,jtag,tbname from jsons2") + # tdSql.checkRows(13) + # + # #show create table + # tdSql.query("show create table jsons1") + # tdSql.checkData(0, 1, 'CREATE TABLE `jsons1` (`ts` TIMESTAMP,`dataint` INT,`databool` BOOL,`datastr` NCHAR(50),`datastrbin` BINARY(150)) TAGS (`jtag` JSON)') + # + # #test aggregate function:count/avg/twa/irate/sum/stddev/leastsquares + # tdSql.query("select count(*) from jsons1 where jtag is not null") + # tdSql.checkData(0, 0, 10) + # tdSql.query("select avg(dataint) from jsons1 where jtag is not null") + # tdSql.checkData(0, 0, 5.3) + # tdSql.error("select twa(dataint) from jsons1 where jtag is not null") + # tdSql.error("select irate(dataint) from jsons1 where jtag is not null") + # tdSql.query("select sum(dataint) from jsons1 where jtag->'tag1' is not null") + # tdSql.checkData(0, 0, 49) + # tdSql.query("select stddev(dataint) from jsons1 where jtag->'tag1'>1") + # tdSql.checkData(0, 0, 4.496912521) + # tdSql.error("SELECT LEASTSQUARES(dataint, 1, 1) from jsons1 where jtag is not null") + # + # #test selection function:min/max/first/last/top/bottom/percentile/apercentile/last_row/interp + # tdSql.query("select min(dataint) from jsons1 where jtag->'tag1'>1") + # tdSql.checkData(0, 0, 1) + # tdSql.query("select max(dataint) from jsons1 where jtag->'tag1'>1") + # tdSql.checkData(0, 0, 11) + # tdSql.query("select first(dataint) from jsons1 where jtag->'tag1'>1") + # tdSql.checkData(0, 0, 2) + # tdSql.query("select last(dataint) from jsons1 where jtag->'tag1'>1") + # tdSql.checkData(0, 0, 11) + # tdSql.query("select top(dataint,100) from jsons1 where jtag->'tag1'>1") + # tdSql.checkRows(3) + # tdSql.query("select bottom(dataint,100) from jsons1 where jtag->'tag1'>1") + # tdSql.checkRows(3) + # tdSql.error("select percentile(dataint,20) from jsons1 where jtag->'tag1'>1") + # tdSql.query("select apercentile(dataint, 50) from jsons1 where jtag->'tag1'>1") + # tdSql.checkData(0, 0, 1.5) + # tdSql.query("select last_row(dataint) from jsons1 where jtag->'tag1'>1") + # tdSql.checkData(0, 0, 11) + # tdSql.error("select interp(dataint) from jsons1 where ts = '2020-06-02 09:17:08.000' and jtag->'tag1'>1") + # + # #test calculation function:diff/derivative/spread/ceil/floor/round/ + # tdSql.error("select diff(dataint) from jsons1 where jtag->'tag1'>1") + # tdSql.error("select derivative(dataint, 10m, 0) from jsons1 where jtag->'tag1'>1") + # tdSql.query("select spread(dataint) from jsons1 where jtag->'tag1'>1") + # tdSql.checkData(0, 0, 10) + # tdSql.query("select ceil(dataint) from jsons1 where jtag->'tag1'>1") + # tdSql.checkRows(3) + # tdSql.query("select floor(dataint) from jsons1 where jtag->'tag1'>1") + # tdSql.checkRows(3) + # tdSql.query("select round(dataint) from jsons1 where jtag->'tag1'>1") + # tdSql.checkRows(3) + # + # #test TD-12077 + # tdSql.execute("insert into jsons1_16 using jsons1 tags('{\"tag1\":\"收到货\",\"tag2\":\"\",\"tag3\":-2.111}') values(1591062628000, 2, NULL, '你就会', 'dws')") + # tdSql.query("select jtag->'tag3' from jsons1_16") + # tdSql.checkData(0, 0, '-2.111000000') + # + # # test TD-12452 + # tdSql.execute("ALTER TABLE jsons1_1 SET TAG jtag=NULL") + # tdSql.query("select jtag from jsons1_1") + # tdSql.checkData(0, 0, None) + # tdSql.execute("CREATE TABLE if not exists jsons1_20 using jsons1 tags(NULL)") + # tdSql.query("select jtag from jsons1_20") + # tdSql.checkData(0, 0, None) + # tdSql.execute("insert into jsons1_21 using jsons1 tags(NULL) values(1591061628000, 11, false, '你就会','')") + # tdSql.query("select jtag from jsons1_21") + # tdSql.checkData(0, 0, None) + # + # #test TD-12389 + tdSql.query("describe jsons1") + tdSql.checkData(5, 2, 4095) + tdSql.query("describe jsons1_1") + tdSql.checkData(5, 2, 4095) + # + # #test TD-13918 + # tdSql.execute("drop table if exists jsons_13918_1") + # tdSql.execute("drop table if exists jsons_13918_2") + # tdSql.execute("drop table if exists jsons_13918_3") + # tdSql.execute("drop table if exists jsons_13918_4") + # tdSql.execute("drop table if exists jsons_stb") + # tdSql.execute("create table jsons_stb (ts timestamp, dataInt int) tags (jtag json)") + # tdSql.error("create table jsons_13918_1 using jsons_stb tags ('nullx')") + # tdSql.error("create table jsons_13918_2 using jsons_stb tags (nullx)") + # tdSql.error("insert into jsons_13918_3 using jsons_stb tags('NULLx') values(1591061628001, 11)") + # tdSql.error("insert into jsons_13918_4 using jsons_stb tags(NULLx) values(1591061628002, 11)") + # tdSql.execute("create table jsons_13918_1 using jsons_stb tags ('null')") + # tdSql.execute("create table jsons_13918_2 using jsons_stb tags (null)") + # tdSql.execute("insert into jsons_13918_1 values(1591061628003, 11)") + # tdSql.execute("insert into jsons_13918_2 values(1591061628004, 11)") + # tdSql.execute("insert into jsons_13918_3 using jsons_stb tags('NULL') values(1591061628005, 11)") + # tdSql.execute("insert into jsons_13918_4 using jsons_stb tags(\"NULL\") values(1591061628006, 11)") + # tdSql.query("select * from jsons_stb") + # tdSql.checkRows(4) + + def stop(self): + tdSql.close() + tdLog.success("%s successfully executed" % __file__) + + +tdCases.addWindows(__file__, TDTestCase()) +tdCases.addLinux(__file__, TDTestCase()) + diff --git a/tests/system-test/2-query/nestedQuery.py b/tests/system-test/2-query/nestedQuery.py index 1f1766f8e5531f542ebd6a04dbf3bb9fb3040529..871054de3a95cf5eda0d1fc977350624b6a77667 100755 --- a/tests/system-test/2-query/nestedQuery.py +++ b/tests/system-test/2-query/nestedQuery.py @@ -52,6 +52,60 @@ class TDTestCase: # return(conn1,cur1) + def data_matrix_equal(self, sql1,row1_s,row1_e,col1_s,col1_e, sql2,row2_s,row2_e,col2_s,col2_e): + # ----row1_start----col1_start---- + # - - - - 是一个矩阵内的数据相等- - - + # - - - - - - - - - - - - - - - - + # ----row1_end------col1_end------ + self.sql1 = sql1 + list1 =[] + tdSql.query(sql1) + for i1 in range(row1_s-1,row1_e): + #print("iiii=%d"%i1) + for j1 in range(col1_s-1,col1_e): + #print("jjjj=%d"%j1) + #print("data=%s" %(tdSql.getData(i1,j1))) + list1.append(tdSql.getData(i1,j1)) + print("=====list1-------list1---=%s" %set(list1)) + + tdSql.execute("reset query cache;") + self.sql2 = sql2 + list2 =[] + tdSql.query(sql2) + for i2 in range(row2_s-1,row2_e): + #print("iiii222=%d"%i2) + for j2 in range(col2_s-1,col2_e): + #print("jjjj222=%d"%j2) + #print("data=%s" %(tdSql.getData(i2,j2))) + list2.append(tdSql.getData(i2,j2)) + print("=====list2-------list2---=%s" %set(list2)) + + if (list1 == list2) and len(list2)>0: + # print(("=====matrix===sql1.list1:'%s',sql2.list2:'%s'") %(list1,list2)) + tdLog.info(("===matrix===sql1:'%s' matrix_result = sql2:'%s' matrix_result") %(sql1,sql2)) + elif (set(list2)).issubset(set(list1)): + # 解决不同子表排列结果乱序 + # print(("=====list_issubset==matrix2in1-true===sql1.list1:'%s',sql2.list2:'%s'") %(list1,list2)) + tdLog.info(("===matrix_issubset===sql1:'%s' matrix_set_result = sql2:'%s' matrix_set_result") %(sql1,sql2)) + #elif abs(float(str(list1).replace("]","").replace("[","").replace("e+","")) - float(str(list2).replace("]","").replace("[","").replace("e+",""))) <= 0.0001: + elif abs(float(str(list1).replace("datetime.datetime","").replace("]","").replace("[","").replace("e+","").replace(", ","").replace("(","").replace(")","").replace("-","")) - float(str(list2).replace("datetime.datetime","").replace("]","").replace("[","").replace("e+","").replace(", ","").replace("(","").replace(")","").replace("-",""))) <= 0.0001: + print(("=====matrix_abs+e+===sql1.list1:'%s',sql2.list2:'%s'") %(list1,list2)) + print(("=====matrix_abs+e+replace_after===sql1.list1:'%s',sql2.list2:'%s'") %(float(str(list1).replace("datetime.datetime","").replace("]","").replace("[","").replace("e+","").replace(", ","").replace("(","").replace(")","").replace("-","")),float(str(list2).replace("datetime.datetime","").replace("]","").replace("[","").replace("e+","").replace(", ","").replace("(","").replace(")","").replace("-","")))) + tdLog.info(("===matrix_abs+e+===sql1:'%s' matrix_result = sql2:'%s' matrix_result") %(sql1,sql2)) + elif abs(float(str(list1).replace("datetime.datetime","").replace("]","").replace("[","").replace(", ","").replace("(","").replace(")","").replace("-","")) - float(str(list2).replace("datetime.datetime","").replace("]","").replace("[","").replace(", ","").replace("(","").replace(")","").replace("-",""))) <= 0.1: + #{datetime.datetime(2021, 8, 27, 1, 46, 40), -441.46841430664057}replace + print(("=====matrix_abs+replace===sql1.list1:'%s',sql2.list2:'%s'") %(list1,list2)) + print(("=====matrix_abs+replace_after===sql1.list1:'%s',sql2.list2:'%s'") %(float(str(list1).replace("datetime.datetime","").replace("]","").replace("[","").replace(", ","").replace("(","").replace(")","").replace("-","")),float(str(list1).replace("datetime.datetime","").replace("]","").replace("[","").replace(", ","").replace("(","").replace(")","").replace("-","")))) + tdLog.info(("===matrix_abs+replace===sql1:'%s' matrix_result = sql2:'%s' matrix_result") %(sql1,sql2)) + elif abs(float(str(list1).replace("datetime.datetime","").replace("]","").replace("[","").replace(", ","").replace("(","").replace(")","").replace("-","")) - float(str(list2).replace("datetime.datetime","").replace("]","").replace("[","").replace(", ","").replace("(","").replace(")","").replace("-",""))) <= 0.5: + print(("=====matrix_abs===sql1.list1:'%s',sql2.list2:'%s'") %(list1,list2)) + print(("=====matrix_abs===sql1.list1:'%s',sql2.list2:'%s'") %(float(str(list1).replace("datetime.datetime","").replace("]","").replace("[","").replace(", ","").replace("(","").replace(")","").replace("-","")),float(str(list2).replace("datetime.datetime","").replace("]","").replace("[","").replace(", ","").replace("(","").replace(")","").replace("-","")))) + tdLog.info(("===matrix_abs======sql1:'%s' matrix_result = sql2:'%s' matrix_result") %(sql1,sql2)) + else: + print(("=====matrix_error===sql1.list1:'%s',sql2.list2:'%s'") %(list1,list2)) + tdLog.info(("sql1:'%s' matrix_result != sql2:'%s' matrix_result") %(sql1,sql2)) + return tdSql.checkEqual(list1,list2) + def restartDnodes(self): pass # tdDnodes.stop(1) @@ -244,7 +298,7 @@ class TDTestCase: q_u_or_where = ['(t1.q_binary like \'binary%\' or t1.q_binary = \'0\' or t2.q_binary like \'binary%\' or t2.q_binary = \'0\' )' , '(t1.q_nchar like \'nchar%\' or t1.q_nchar = \'0\' or t2.q_nchar like \'nchar%\' or t2.q_nchar = \'0\' )' , '(t1.q_bool = true or t1.q_bool = false or t2.q_bool = true or t2.q_bool = false)' , - '(t1.q_bool in (0 , 1) or t2.q_bool in (0 , 1)' , 't1.q_bool in ( true , false) or t2.q_bool in ( true , false))' , '(t1.q_bool = 0 or t1.q_bool = 1 or t2.q_bool = 0 or t2.q_bool = 1)' , + '(t1.q_bool in (0 , 1) or t2.q_bool in (0 , 1))' , '(t1.q_bool in ( true , false) or t2.q_bool in ( true , false))' , '(t1.q_bool = 0 or t1.q_bool = 1 or t2.q_bool = 0 or t2.q_bool = 1)' , '(t1.q_bigint between -9223372036854775807 and 9223372036854775807 or t2.q_bigint between -9223372036854775807 and 9223372036854775807)', '(t1.q_int between -2147483647 and 2147483647 or t2.q_int between -2147483647 and 2147483647)', '(t1.q_smallint between -32767 and 32767 or t2.q_smallint between -32767 and 32767)', @@ -315,7 +369,63 @@ class TDTestCase: group_where = ['group by tbname , loc' , 'group by tbname', 'group by tbname, t_bigint', 'group by tbname,t_int', 'group by tbname, t_smallint', 'group by tbname,t_tinyint', 'group by tbname,t_float', 'group by tbname,t_double' , 'group by tbname,t_binary', 'group by tbname,t_nchar', 'group by tbname,t_bool' ,'group by tbname ,loc ,t_bigint', - 'group by tbname,t_binary ,t_nchar ,t_bool' , 'group by tbname,t_int ,t_smallint ,t_tinyint' , 'group by tbname,t_float ,t_double ' ] + 'group by tbname,t_binary ,t_nchar ,t_bool' , 'group by tbname,t_int ,t_smallint ,t_tinyint' , 'group by tbname,t_float ,t_double ' , + 'PARTITION BY tbname , loc' , 'PARTITION BY tbname', 'PARTITION BY tbname, t_bigint', 'PARTITION BY tbname,t_int', 'PARTITION BY tbname, t_smallint', 'PARTITION BY tbname,t_tinyint', + 'PARTITION BY tbname,t_float', 'PARTITION BY tbname,t_double' , 'PARTITION BY tbname,t_binary', 'PARTITION BY tbname,t_nchar', 'PARTITION BY tbname,t_bool' ,'PARTITION BY tbname ,loc ,t_bigint', + 'PARTITION BY tbname,t_binary ,t_nchar ,t_bool' , 'PARTITION BY tbname,t_int ,t_smallint ,t_tinyint' , 'PARTITION BY tbname,t_float ,t_double '] + group_where_j = ['group by t1.loc' , 'group by t1.t_bigint', 'group by t1.t_int', 'group by t1.t_smallint', 'group by t1.t_tinyint', + 'group by t1.t_float', 'group by t1.t_double' , 'group by t1.t_binary', 'group by t1.t_nchar', 'group by t1.t_bool' ,'group by t1.loc ,t1.t_bigint', + 'group by t1.t_binary ,t1.t_nchar ,t1.t_bool' , 'group by t1.t_int ,t1.t_smallint ,t1.t_tinyint' , 'group by t1.t_float ,t1.t_double ' , + 'PARTITION BY t1.loc' , 'PARTITION by t1.t_bigint', 'PARTITION by t1.t_int', 'PARTITION by t1.t_smallint', 'PARTITION by t1.t_tinyint', + 'PARTITION by t1.t_float', 'PARTITION by t1.t_double' , 'PARTITION by t1.t_binary', 'PARTITION by t1.t_nchar', 'PARTITION by t1.t_bool' ,'PARTITION BY t1.loc ,t1.t_bigint', + 'PARTITION by t1.t_binary ,t1.t_nchar ,t1.t_bool' , 'PARTITION by t1.t_int ,t1.t_smallint ,t1.t_tinyint' , 'PARTITION by t1.t_float ,t1.t_double ', + 'group by t2.loc' , 'group by t2.t_bigint', 'group by t2.t_int', 'group by t2.t_smallint', 'group by t2.t_tinyint', + 'group by t2.t_float', 'group by t2.t_double' , 'group by t2.t_binary', 'group by t2.t_nchar', 'group by t2.t_bool' ,'group by t2.loc ,t2.t_bigint', + 'group by t2.t_binary ,t2.t_nchar ,t2.t_bool' , 'group by t2.t_int ,t2.t_smallint ,t2.t_tinyint' , 'group by t2.t_float ,t2.t_double ' , + 'PARTITION BY t2.loc' , 'PARTITION by t2.t_bigint', 'PARTITION by t2.t_int', 'PARTITION by t2.t_smallint', 'PARTITION by t2.t_tinyint', + 'PARTITION by t2.t_float', 'PARTITION by t2.t_double' , 'PARTITION by t2.t_binary', 'PARTITION by t2.t_nchar', 'PARTITION by t2.t_bool' ,'PARTITION BY t2.loc ,t2.t_bigint', + 'PARTITION by t2.t_binary ,t2.t_nchar ,t2.t_bool' , 'PARTITION by t2.t_int ,t2.t_smallint ,t2.t_tinyint' , 'PARTITION by t2.t_float ,t2.t_double '] + + partiton_where = ['PARTITION BY tbname , loc' , 'PARTITION BY tbname', 'PARTITION BY tbname, t_bigint', 'PARTITION BY tbname,t_int', 'PARTITION BY tbname, t_smallint', 'PARTITION BY tbname,t_tinyint', + 'PARTITION BY tbname,t_float', 'PARTITION BY tbname,t_double' , 'PARTITION BY tbname,t_binary', 'PARTITION BY tbname,t_nchar', 'PARTITION BY tbname,t_bool' ,'PARTITION BY tbname ,loc ,t_bigint', + 'PARTITION BY tbname,t_binary ,t_nchar ,t_bool' , 'PARTITION BY tbname,t_int ,t_smallint ,t_tinyint' , 'PARTITION BY tbname,t_float ,t_double '] + partiton_where_j = ['PARTITION BY t1.loc' , 'PARTITION by t1.t_bigint', 'PARTITION by t1.t_int', 'PARTITION by t1.t_smallint', 'PARTITION by t1.t_tinyint', + 'PARTITION by t1.t_float', 'PARTITION by t1.t_double' , 'PARTITION by t1.t_binary', 'PARTITION by t1.t_nchar', 'PARTITION by t1.t_bool' ,'PARTITION BY t1.loc ,t1.t_bigint', + 'PARTITION by t1.t_binary ,t1.t_nchar ,t1.t_bool' , 'PARTITION by t1.t_int ,t1.t_smallint ,t1.t_tinyint' , 'PARTITION by t1.t_float ,t1.t_double ', + 'PARTITION BY t2.loc' , 'PARTITION by t2.t_bigint', 'PARTITION by t2.t_int', 'PARTITION by t2.t_smallint', 'PARTITION by t2.t_tinyint', + 'PARTITION by t2.t_float', 'PARTITION by t2.t_double' , 'PARTITION by t2.t_binary', 'PARTITION by t2.t_nchar', 'PARTITION by t2.t_bool' ,'PARTITION BY t2.loc ,t2.t_bigint', + 'PARTITION by t2.t_binary ,t2.t_nchar ,t2.t_bool' , 'PARTITION by t2.t_int ,t2.t_smallint ,t2.t_tinyint' , 'PARTITION by t2.t_float ,t2.t_double '] + + + group_where_regular = ['group by tbname ' , 'group by tbname', 'group by tbname, q_bigint', 'group by tbname,q_int', 'group by tbname, q_smallint', 'group by tbname,q_tinyint', + 'group by tbname,q_float', 'group by tbname,q_double' , 'group by tbname,q_binary', 'group by tbname,q_nchar', 'group by tbname,q_bool' ,'group by tbname ,q_bigint', + 'group by tbname,q_binary ,q_nchar ,q_bool' , 'group by tbname,q_int ,q_smallint ,q_tinyint' , 'group by tbname,q_float ,q_double ' , + 'PARTITION BY tbname ' , 'PARTITION BY tbname', 'PARTITION BY tbname, q_bigint', 'PARTITION BY tbname,q_int', 'PARTITION BY tbname, q_smallint', 'PARTITION BY tbname,q_tinyint', + 'PARTITION BY tbname,q_float', 'PARTITION BY tbname,q_double' , 'PARTITION BY tbname,q_binary', 'PARTITION BY tbname,q_nchar', 'PARTITION BY tbname,q_bool' ,'PARTITION BY tbname ,q_bigint', + 'PARTITION BY tbname,q_binary ,q_nchar ,q_bool' , 'PARTITION BY tbname,q_int ,q_smallint ,q_tinyint' , 'PARTITION BY tbname,q_float ,q_double '] + group_where_regular_j = ['group by t1.q_bigint', 'group by t1.q_int', 'group by t1.q_smallint', 'group by t1.q_tinyint', + 'group by t1.q_float', 'group by t1.q_double' , 'group by t1.q_binary', 'group by t1.q_nchar', 'group by t1.q_bool' ,'group by t1.q_bigint', + 'group by t1.q_binary ,t1.q_nchar ,t1.q_bool' , 'group by t1.q_int ,t1.q_smallint ,t1.q_tinyint' , 'group by t1.q_float ,t1.q_double ' , + 'PARTITION by t1.q_bigint', 'PARTITION by t1.q_int', 'PARTITION by t1.q_smallint', 'PARTITION by t1.q_tinyint', + 'PARTITION by t1.q_float', 'PARTITION by t1.q_double' , 'PARTITION by t1.q_binary', 'PARTITION by t1.q_nchar', 'PARTITION by t1.q_bool' ,'PARTITION BY t1.q_bigint', + 'PARTITION by t1.q_binary ,t1.q_nchar ,t1.q_bool' , 'PARTITION by t1.q_int ,t1.q_smallint ,t1.q_tinyint' , 'PARTITION by t1.q_float ,t1.q_double ', + 'group by t2.q_bigint', 'group by t2.q_int', 'group by t2.q_smallint', 'group by t2.q_tinyint', + 'group by t2.q_float', 'group by t2.q_double' , 'group by t2.q_binary', 'group by t2.q_nchar', 'group by t2.q_bool' ,'group by t2.q_bigint', + 'group by t2.q_binary ,t2.q_nchar ,t2.q_bool' , 'group by t2.q_int ,t2.q_smallint ,t2.q_tinyint' , 'group by t2.q_float ,t2.q_double ' , + 'PARTITION by t2.q_bigint', 'PARTITION by t2.q_int', 'PARTITION by t2.q_smallint', 'PARTITION by t2.q_tinyint', + 'PARTITION by t2.q_float', 'PARTITION by t2.q_double' , 'PARTITION by t2.q_binary', 'PARTITION by t2.q_nchar', 'PARTITION by t2.q_bool' ,'PARTITION BY t2.q_bigint', + 'PARTITION by t2.q_binary ,t2.q_nchar ,t2.q_bool' , 'PARTITION by t2.q_int ,t2.q_smallint ,t2.q_tinyint' , 'PARTITION by t2.q_float ,t2.q_double '] + + partiton_where_regular = ['PARTITION BY tbname ' , 'PARTITION BY tbname', 'PARTITION BY tbname, q_bigint', 'PARTITION BY tbname,q_int', 'PARTITION BY tbname, q_smallint', 'PARTITION BY tbname,q_tinyint', + 'PARTITION BY tbname,q_float', 'PARTITION BY tbname,q_double' , 'PARTITION BY tbname,q_binary', 'PARTITION BY tbname,q_nchar', 'PARTITION BY tbname,q_bool' ,'PARTITION BY tbname ,q_bigint', + 'PARTITION BY tbname,q_binary ,q_nchar ,q_bool' , 'PARTITION BY tbname,q_int ,q_smallint ,q_tinyint' , 'PARTITION BY tbname,q_float ,q_double '] + partiton_where_regular_j = ['PARTITION by t1.q_bigint', 'PARTITION by t1.q_int', 'PARTITION by t1.q_smallint', 'PARTITION by t1.q_tinyint', + 'PARTITION by t1.q_float', 'PARTITION by t1.q_double' , 'PARTITION by t1.q_binary', 'PARTITION by t1.q_nchar', 'PARTITION by t1.q_bool' ,'PARTITION BY t1.q_bigint', + 'PARTITION by t1.q_binary ,t1.q_nchar ,t1.q_bool' , 'PARTITION by t1.q_int ,t1.q_smallint ,t1.q_tinyint' , 'PARTITION by t1.q_float ,t1.q_double ', + 'PARTITION by t2.q_bigint', 'PARTITION by t2.q_int', 'PARTITION by t2.q_smallint', 'PARTITION by t2.q_tinyint', + 'PARTITION by t2.q_float', 'PARTITION by t2.q_double' , 'PARTITION by t2.q_binary', 'PARTITION by t2.q_nchar', 'PARTITION by t2.q_bool' ,'PARTITION BY t2.q_bigint', + 'PARTITION by t2.q_binary ,t2.q_nchar ,t2.q_bool' , 'PARTITION by t2.q_int ,t2.q_smallint ,t2.q_tinyint' , 'PARTITION by t2.q_float ,t2.q_double '] + having_support = ['having count(q_int) > 0','having count(q_bigint) > 0','having count(q_smallint) > 0','having count(q_tinyint) > 0','having count(q_float) > 0','having count(q_double) > 0','having count(q_bool) > 0', 'having avg(q_int) > 0','having avg(q_bigint) > 0','having avg(q_smallint) > 0','having avg(q_tinyint) > 0','having avg(q_float) > 0','having avg(q_double) > 0', 'having sum(q_int) > 0','having sum(q_bigint) > 0','having sum(q_smallint) > 0','having sum(q_tinyint) > 0','having sum(q_float) > 0','having sum(q_double) > 0', @@ -334,6 +444,18 @@ class TDTestCase: 'having PERCENTILE(q_int,10) > 0','having PERCENTILE(q_bigint,10) > 0','having PERCENTILE(q_smallint,10) > 0','having PERCENTILE(q_tinyint,10) > 0','having PERCENTILE(q_float,10) > 0','having PERCENTILE(q_double,10) > 0'] having_tagnot_support = ['having LAST_ROW(q_int) > 0','having LAST_ROW(q_bigint) > 0','having LAST_ROW(q_smallint) > 0','having LAST_ROW(q_tinyint) > 0','having LAST_ROW(q_float) > 0','having LAST_ROW(q_double) > 0'] + having_support_j = ['having count(t1.q_int) > 0','having count(t1.q_bigint) > 0','having count(t1.q_smallint) > 0','having count(t1.q_tinyint) > 0','having count(t1.q_float) > 0','having count(t1.q_double) > 0','having count(t1.q_bool) > 0', + 'having avg(t1.q_int) > 0','having avg(t1.q_bigint) > 0','having avg(t1.q_smallint) > 0','having avg(t1.q_tinyint) > 0','having avg(t1.q_float) > 0','having avg(t1.q_double) > 0', + 'having sum(t1.q_int) > 0','having sum(t1.q_bigint) > 0','having sum(t1.q_smallint) > 0','having sum(t1.q_tinyint) > 0','having sum(t1.q_float) > 0','having sum(t1.q_double) > 0', + 'having STDDEV(t1.q_int) > 0','having STDDEV(t1.q_bigint) > 0','having STDDEV(t1.q_smallint) > 0','having STDDEV(t1.q_tinyint) > 0','having STDDEV(t1.q_float) > 0','having STDDEV(t1.q_double) > 0', + 'having TWA(t1.q_int) > 0','having TWA(t1.q_bigint) > 0','having TWA(t1.q_smallint) > 0','having TWA(t1.q_tinyint) > 0','having TWA(t1.q_float) > 0','having TWA(t1.q_double) > 0', + 'having IRATE(t1.q_int) > 0','having IRATE(t1.q_bigint) > 0','having IRATE(t1.q_smallint) > 0','having IRATE(t1.q_tinyint) > 0','having IRATE(t1.q_float) > 0','having IRATE(t1.q_double) > 0', + 'having MIN(t1.q_int) > 0','having MIN(t1.q_bigint) > 0','having MIN(t1.q_smallint) > 0','having MIN(t1.q_tinyint) > 0','having MIN(t1.q_float) > 0','having MIN(t1.q_double) > 0', + 'having MAX(t1.q_int) > 0','having MAX(t1.q_bigint) > 0','having MAX(t1.q_smallint) > 0','having MAX(t1.q_tinyint) > 0','having MAX(t1.q_float) > 0','having MAX(t1.q_double) > 0', + 'having FIRST(t1.q_int) > 0','having FIRST(t1.q_bigint) > 0','having FIRST(t1.q_smallint) > 0','having FIRST(t1.q_tinyint) > 0','having FIRST(t1.q_float) > 0','having FIRST(t1.q_double) > 0', + 'having LAST(t1.q_int) > 0','having LAST(t1.q_bigint) > 0','having LAST(t1.q_smallint) > 0','having LAST(t1.q_tinyint) > 0','having LAST(t1.q_float) > 0','having LAST(t1.q_double) > 0', + 'having APERCENTILE(t1.q_int,10) > 0','having APERCENTILE(t1.q_bigint,10) > 0','having APERCENTILE(t1.q_smallint,10) > 0','having APERCENTILE(t1.q_tinyint,10) > 0','having APERCENTILE(t1.q_float,10) > 0','having APERCENTILE(t1.q_double,10) > 0'] + # limit offset where limit_where = ['limit 1 offset 1' , 'limit 1' , 'limit 2 offset 1' , 'limit 2', 'limit 12 offset 1' , 'limit 20', 'limit 20 offset 10' , 'limit 200'] limit1_where = ['limit 1 offset 1' , 'limit 1' ] @@ -375,7 +497,17 @@ class TDTestCase: 'last_row(q_int)' , 'last_row(q_bigint)' , 'last_row(q_smallint)' , 'last_row(q_tinyint)' , 'last_row(q_float)' , 'last_row(q_double)' , 'last_row(q_bool)' ,'last_row(q_binary)' ,'last_row(q_nchar)' ,'last_row(q_ts)'] - + calc_select_not_support_ts = ['first(q_int)' , 'first(q_bigint)' , 'first(q_smallint)' , 'first(q_tinyint)' , 'first(q_float)' ,'first(q_double)' ,'first(q_binary)' ,'first(q_nchar)' ,'first(q_bool)' ,'first(q_ts)' , + 'last(q_int)' , 'last(q_bigint)' , 'last(q_smallint)' , 'last(q_tinyint)' , 'last(q_float)' ,'last(q_double)' , 'last(q_binary)' ,'last(q_nchar)' ,'last(q_bool)' ,'last(q_ts)' , + 'last_row(q_int)' , 'last_row(q_bigint)' , 'last_row(q_smallint)' , 'last_row(q_tinyint)' , 'last_row(q_float)' , + 'last_row(q_double)' , 'last_row(q_bool)' ,'last_row(q_binary)' ,'last_row(q_nchar)' ,'last_row(q_ts)', + 'apercentile(q_int,20)' , 'apercentile(q_bigint,20)' ,'apercentile(q_smallint,20)' ,'apercentile(q_tinyint,20)' ,'apercentile(q_float,20)' ,'apercentile(q_double,20)'] + + calc_select_support_ts = ['bottom(q_int,20)' , 'bottom(q_bigint,20)' , 'bottom(q_smallint,20)' , 'bottom(q_tinyint,20)' ,'bottom(q_float,20)' , 'bottom(q_double,20)' , + 'top(q_int,20)' , 'top(q_bigint,20)' , 'top(q_smallint,20)' ,'top(q_tinyint,20)' ,'top(q_float,20)' ,'top(q_double,20)' , + 'min(q_int)' , 'min(q_bigint)' , 'min(q_smallint)' , 'min(q_tinyint)' , 'min(q_float)' ,'min(q_double)' , + 'max(q_int)' , 'max(q_bigint)' , 'max(q_smallint)' , 'max(q_tinyint)' ,'max(q_float)' ,'max(q_double)' ] + calc_select_regular = [ 'PERCENTILE(q_int,10)' ,'PERCENTILE(q_bigint,20)' , 'PERCENTILE(q_smallint,30)' ,'PERCENTILE(q_tinyint,40)' ,'PERCENTILE(q_float,50)' ,'PERCENTILE(q_double,60)'] @@ -392,6 +524,23 @@ class TDTestCase: 'first(t2.q_int)' , 'first(t2.q_bigint)' , 'first(t2.q_smallint)' , 'first(t2.q_tinyint)' , 'first(t2.q_float)' ,'first(t2.q_double)' ,'first(t2.q_binary)' ,'first(t2.q_nchar)' ,'first(t2.q_bool)' ,'first(t2.q_ts)' , 'last(t2.q_int)' , 'last(t2.q_bigint)' , 'last(t2.q_smallint)' , 'last(t2.q_tinyint)' , 'last(t2.q_float)' ,'last(t2.q_double)' , 'last(t2.q_binary)' ,'last(t2.q_nchar)' ,'last(t2.q_bool)' ,'last(t2.q_ts)'] + calc_select_in_support_ts_j = ['bottom(t1.q_int,20)' , 'bottom(t1.q_bigint,20)' , 'bottom(t1.q_smallint,20)' , 'bottom(t1.q_tinyint,20)' ,'bottom(t1.q_float,20)' , 'bottom(t1.q_double,20)' , + 'top(t1.q_int,20)' , 'top(t1.q_bigint,20)' , 'top(t1.q_smallint,20)' ,'top(t1.q_tinyint,20)' ,'top(t1.q_float,20)' ,'top(t1.q_double,20)' , + 'min(t1.q_int)' , 'min(t1.q_bigint)' , 'min(t1.q_smallint)' , 'min(t1.q_tinyint)' , 'min(t1.q_float)' ,'min(t1.q_double)' , + 'max(t1.q_int)' , 'max(t1.q_bigint)' , 'max(t1.q_smallint)' , 'max(t1.q_tinyint)' ,'max(t1.q_float)' ,'max(t1.q_double)' , + 'bottom(t2.q_int,20)' , 'bottom(t2.q_bigint,20)' , 'bottom(t2.q_smallint,20)' , 'bottom(t2.q_tinyint,20)' ,'bottom(t2.q_float,20)' , 'bottom(t2.q_double,20)' , + 'top(t2.q_int,20)' , 'top(t2.q_bigint,20)' , 'top(t2.q_smallint,20)' ,'top(t2.q_tinyint,20)' ,'top(t2.q_float,20)' ,'top(t2.q_double,20)' , + 'min(t2.q_int)' , 'min(t2.q_bigint)' , 'min(t2.q_smallint)' , 'min(t2.q_tinyint)' , 'min(t2.q_float)' ,'min(t2.q_double)' , + 'max(t2.q_int)' , 'max(t2.q_bigint)' , 'max(t2.q_smallint)' , 'max(t2.q_tinyint)' ,'max(t2.q_float)' ,'max(t2.q_double)' , + ] + + calc_select_in_not_support_ts_j = ['apercentile(t1.q_int,20)' , 'apercentile(t1.q_bigint,20)' ,'apercentile(t1.q_smallint,20)' ,'apercentile(t1.q_tinyint,20)' ,'apercentile(t1.q_float,20)' ,'apercentile(t1.q_double,20)' , + 'last_row(t1.q_int)' , 'last_row(t1.q_bigint)' , 'last_row(t1.q_smallint)' , 'last_row(t1.q_tinyint)' , 'last_row(t1.q_float)' , + 'last_row(t1.q_double)' , 'last_row(t1.q_bool)' ,'last_row(t1.q_binary)' ,'last_row(t1.q_nchar)' ,'last_row(t1.q_ts)' , + 'apercentile(t2.q_int,20)' , 'apercentile(t2.q_bigint,20)' ,'apercentile(t2.q_smallint,20)' ,'apercentile(t2.q_tinyint,20)' ,'apercentile(t2.q_float,20)' ,'apercentile(t2.q_double,20)' , + 'last_row(t2.q_int)' , 'last_row(t2.q_bigint)' , 'last_row(t2.q_smallint)' , 'last_row(t2.q_tinyint)' , 'last_row(t2.q_float)' , + 'last_row(t2.q_double)' , 'last_row(t2.q_bool)' ,'last_row(t2.q_binary)' ,'last_row(t2.q_nchar)' ,'last_row(t2.q_ts)'] + calc_select_in_j = ['min(t1.q_int)' , 'min(t1.q_bigint)' , 'min(t1.q_smallint)' , 'min(t1.q_tinyint)' , 'min(t1.q_float)' ,'min(t1.q_double)' , 'max(t1.q_int)' , 'max(t1.q_bigint)' , 'max(t1.q_smallint)' , 'max(t1.q_tinyint)' ,'max(t1.q_float)' ,'max(t1.q_double)' , 'apercentile(t1.q_int,20)' , 'apercentile(t1.q_bigint,20)' ,'apercentile(t1.q_smallint,20)' ,'apercentile(t1.q_tinyint,20)' ,'apercentile(t1.q_float,20)' ,'apercentile(t1.q_double,20)' , @@ -401,8 +550,7 @@ class TDTestCase: 'max(t2.q_int)' , 'max(t2.q_bigint)' , 'max(t2.q_smallint)' , 'max(t2.q_tinyint)' ,'max(t2.q_float)' ,'max(t2.q_double)' , 'apercentile(t2.q_int,20)' , 'apercentile(t2.q_bigint,20)' ,'apercentile(t2.q_smallint,20)' ,'apercentile(t2.q_tinyint,20)' ,'apercentile(t2.q_float,20)' ,'apercentile(t2.q_double,20)' , 'last_row(t2.q_int)' , 'last_row(t2.q_bigint)' , 'last_row(t2.q_smallint)' , 'last_row(t2.q_tinyint)' , 'last_row(t2.q_float)' , - 'last_row(t2.q_double)' , 'last_row(t2.q_bool)' ,'last_row(t2.q_binary)' ,'last_row(t2.q_nchar)' ,'last_row(t2.q_ts)'] - + 'last_row(t2.q_double)' , 'last_row(t2.q_bool)' ,'last_row(t2.q_binary)' ,'last_row(t2.q_nchar)' ,'last_row(t2.q_ts)'] calc_select_all_j = calc_select_in_ts_j + calc_select_in_j calc_select_regular_j = [ 'PERCENTILE(t1.q_int,10)' ,'PERCENTILE(t1.q_bigint,20)' , 'PERCENTILE(t1.q_smallint,30)' ,'PERCENTILE(t1.q_tinyint,40)' ,'PERCENTILE(t1.q_float,50)' ,'PERCENTILE(t1.q_double,60)' , @@ -465,6 +613,8 @@ class TDTestCase: calc_calculate_all = ['SPREAD(ts)' , 'SPREAD(q_ts)' , 'SPREAD(q_int)' ,'SPREAD(q_bigint)' , 'SPREAD(q_smallint)' ,'SPREAD(q_tinyint)' ,'SPREAD(q_float)' ,'SPREAD(q_double)' , '(SPREAD(q_int) + SPREAD(q_bigint))' , '(SPREAD(q_smallint) - SPREAD(q_float))', '(SPREAD(q_double) * SPREAD(q_tinyint))' , '(SPREAD(q_double) / SPREAD(q_float))'] calc_calculate_regular = ['DIFF(q_int)' ,'DIFF(q_bigint)' , 'DIFF(q_smallint)' ,'DIFF(q_tinyint)' ,'DIFF(q_float)' ,'DIFF(q_double)' , + 'DIFF(q_int,0)' ,'DIFF(q_bigint,0)' , 'DIFF(q_smallint,0)' ,'DIFF(q_tinyint,0)' ,'DIFF(q_float,0)' ,'DIFF(q_double,0)' , + 'DIFF(q_int,1)' ,'DIFF(q_bigint,1)' , 'DIFF(q_smallint,1)' ,'DIFF(q_tinyint,1)' ,'DIFF(q_float,1)' ,'DIFF(q_double,1)' , 'DERIVATIVE(q_int,15s,0)' , 'DERIVATIVE(q_bigint,10s,1)' , 'DERIVATIVE(q_smallint,20s,0)' ,'DERIVATIVE(q_tinyint,10s,1)' ,'DERIVATIVE(q_float,6s,0)' ,'DERIVATIVE(q_double,3s,1)' ] calc_calculate_groupbytbname = calc_calculate_regular @@ -475,8 +625,12 @@ class TDTestCase: '(SPREAD(t2.q_int) + SPREAD(t2.q_bigint))' , '(SPREAD(t2.q_smallint) - SPREAD(t2.q_float))', '(SPREAD(t2.q_double) * SPREAD(t2.q_tinyint))' , '(SPREAD(t2.q_double) / SPREAD(t2.q_tinyint))', '(SPREAD(t1.q_int) + SPREAD(t1.q_smallint))' , '(SPREAD(t2.q_smallint) - SPREAD(t2.q_float))', '(SPREAD(t1.q_double) * SPREAD(t1.q_tinyint))' , '(SPREAD(t1.q_double) / SPREAD(t1.q_float))'] calc_calculate_regular_j = ['DIFF(t1.q_int)' ,'DIFF(t1.q_bigint)' , 'DIFF(t1.q_smallint)' ,'DIFF(t1.q_tinyint)' ,'DIFF(t1.q_float)' ,'DIFF(t1.q_double)' , + 'DIFF(t1.q_int,0)' ,'DIFF(t1.q_bigint,0)' , 'DIFF(t1.q_smallint,0)' ,'DIFF(t1.q_tinyint,0)' ,'DIFF(t1.q_float,0)' ,'DIFF(t1.q_double,0)' , + 'DIFF(t1.q_int,1)' ,'DIFF(t1.q_bigint,1)' , 'DIFF(t1.q_smallint,1)' ,'DIFF(t1.q_tinyint,1)' ,'DIFF(t1.q_float,1)' ,'DIFF(t1.q_double,1)' , 'DERIVATIVE(t1.q_int,15s,0)' , 'DERIVATIVE(t1.q_bigint,10s,1)' , 'DERIVATIVE(t1.q_smallint,20s,0)' ,'DERIVATIVE(t1.q_tinyint,10s,1)' ,'DERIVATIVE(t1.q_float,6s,0)' ,'DERIVATIVE(t1.q_double,3s,1)' , 'DIFF(t2.q_int)' ,'DIFF(t2.q_bigint)' , 'DIFF(t2.q_smallint)' ,'DIFF(t2.q_tinyint)' ,'DIFF(t2.q_float)' ,'DIFF(t2.q_double)' , + 'DIFF(t2.q_int,0)' ,'DIFF(t2.q_bigint,0)' , 'DIFF(t2.q_smallint,0)' ,'DIFF(t2.q_tinyint,0)' ,'DIFF(t2.q_float,0)' ,'DIFF(t2.q_double,0)' , + 'DIFF(t2.q_int,1)' ,'DIFF(t2.q_bigint,1)' , 'DIFF(t2.q_smallint,1)' ,'DIFF(t2.q_tinyint,1)' ,'DIFF(t2.q_float,1)' ,'DIFF(t2.q_double,1)' , 'DERIVATIVE(t2.q_int,15s,0)' , 'DERIVATIVE(t2.q_bigint,10s,1)' , 'DERIVATIVE(t2.q_smallint,20s,0)' ,'DERIVATIVE(t2.q_tinyint,10s,1)' ,'DERIVATIVE(t2.q_float,6s,0)' ,'DERIVATIVE(t2.q_double,3s,1)' ] calc_calculate_groupbytbname_j = calc_calculate_regular_j @@ -503,7 +657,7 @@ class TDTestCase: tdSql.checkRows(100) #1 outer union not support - self.dropandcreateDB_random("%s" %db, 1) + #self.dropandcreateDB_random("%s" %db, 1) tdSql.query("select 1-2 from stable_1;") for i in range(self.fornum): #sql = "select ts , * from ( select " @@ -528,7 +682,7 @@ class TDTestCase: tdSql.query(sql) tdSql.checkRows(100) - self.dropandcreateDB_random("%s" %db, 1) + #self.dropandcreateDB_random("%s" %db, 1) tdSql.query("select 1-2 from stable_1;") for i in range(self.fornum): #sql = "select ts , * from ( select " @@ -725,8 +879,27 @@ class TDTestCase: sql += "ts from stable_1 where " sql += "%s " % random.choice(q_where) sql += "%s " % random.choice(order_where) + sql += ") union " + sql += "select ts from ( select " + sql += "%s, " % random.choice(s_r_select) + sql += "%s, " % random.choice(q_select) + sql += "ts from stable_2 where " + sql += "%s " % random.choice(q_where) + sql += "%s " % random.choice(order_where) sql += ")" - sql += " %s " % random.choice(unionall_or_union) + tdLog.info(sql) + tdLog.info(len(sql)) + tdSql.query(sql) + tdSql.checkRows(200) + for i in range(self.fornum): + #sql = "select ts , * from ( select " + sql = "select ts from ( select " + sql += "%s, " % random.choice(s_r_select) + sql += "%s, " % random.choice(q_select) + sql += "ts from stable_1 where " + sql += "%s " % random.choice(q_where) + sql += "%s " % random.choice(order_where) + sql += ") union all " sql += "select ts from ( select " sql += "%s, " % random.choice(s_r_select) sql += "%s, " % random.choice(q_select) @@ -736,10 +909,9 @@ class TDTestCase: sql += ")" tdLog.info(sql) tdLog.info(len(sql)) - tdSql.error(sql) - #TD-15610 tdSql.query(sql) - # tdSql.checkRows(100) - + tdSql.query(sql) + tdSql.checkRows(400) + #3 inter union not support tdSql.query("select 3-4 from stable_1;") for i in range(self.fornum): @@ -761,7 +933,7 @@ class TDTestCase: tdLog.info(len(sql)) tdSql.error(sql) - #join:TD-6020\TD-6155 select * from (select column form stable1,stable2 where t1.ts=t2.ts and <\>\in\and\or order by) + #join:select * from (select column form stable1,stable2 where t1.ts=t2.ts and <\>\in\and\or order by) tdSql.query("select 3-5 from stable_1;") for i in range(self.fornum): sql = "select * from ( select t1.ts ," @@ -874,7 +1046,20 @@ class TDTestCase: sql += ") ;" tdLog.info(sql) tdLog.info(len(sql)) - tdSql.error(sql) + tdSql.error(sql) #distinct 和 order by 不能混合使用 + tdSql.query("select 7-1 from stable_1;") + for i in range(self.fornum): + sql = "select * from ( select " + sql += "%s " % random.choice(dq_select) + sql += " from stable_1 where " + sql += "%s " % random.choice(qt_where) + #sql += "%s " % random.choice(order_desc_where) + sql += "%s " % random.choice([limit_where[0] , limit_where[1]] ) + sql += ") ;" + tdLog.info(sql) + tdLog.info(len(sql)) + tdSql.query(sql) + tdSql.checkRows(1) #calc_select,TWA/Diff/Derivative/Irate are not allowed to apply to super table directly #8 select * from (select ts,calc form ragular_table where <\>\in\and\or order by ) @@ -883,7 +1068,7 @@ class TDTestCase: tdSql.query("select 8-1 from stable_1;") for i in range(self.fornum): sql = "select * from ( select ts ," - sql += "%s " % random.choice(calc_select_in_ts) + sql += "%s " % random.choice(calc_select_support_ts) sql += "from regular_table_1 where " sql += "%s " % random.choice(q_where) sql += "%s " % random.choice(order_where) @@ -891,12 +1076,36 @@ class TDTestCase: sql += ") ;" tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.query(sql) + tdSql.query(sql) # 聚合函数不在可以和ts一起使用了 DB error: Not a single-group group function + tdSql.query("select 8-1 from stable_1;") + for i in range(self.fornum): + sql = "select * from ( select " + sql += "%s " % random.choice(calc_select_not_support_ts) + sql += "from regular_table_1 where " + sql += "%s " % random.choice(q_where) + sql += "%s " % random.choice(limit1_where) + sql += ") ;" + tdLog.info(sql) + tdLog.info(len(sql)) + #TD-15651 tdSql.query(sql) # 聚合函数不在可以和ts一起使用了 DB error: Not a single-group group function + + for i in range(self.fornum): + sql = "select * from ( select " + sql += "%s " % random.choice(calc_select_in_ts) + sql += "from regular_table_1 where " + sql += "%s " % random.choice(q_where) + #sql += "%s " % random.choice(order_where) + sql += "%s " % random.choice(limit1_where) + sql += ") ;" + tdLog.info(sql) + tdLog.info(len(sql)) + tdSql.query(sql) + ##top返回结果有问题nest.sql tdSql.checkRows(1) tdSql.query("select 8-2 from stable_1;") for i in range(self.fornum): - sql = "select * from ( select " - sql += "%s " % random.choice(calc_select_in_ts_j) + sql = "select * from ( select t1.ts, " + sql += "%s " % random.choice(calc_select_in_support_ts_j) sql += "from regular_table_1 t1, regular_table_2 t2 where t1.ts = t2.ts and " sql += "%s " % random.choice(q_u_where) sql += "%s " % random.choice(order_u_where) @@ -904,28 +1113,36 @@ class TDTestCase: sql += ") ;" tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.query(sql) - - tdSql.query("select 8-3 from stable_1;") + tdSql.query(sql)# 聚合函数不在可以和ts一起使用了 DB error: Not a single-group group function for i in range(self.fornum): sql = "select * from ( select " - sql += "%s " % random.choice(calc_select_in_ts_j) + sql += "%s " % random.choice(calc_select_in_not_support_ts_j) sql += "from regular_table_1 t1, regular_table_2 t2 where t1.ts = t2.ts and " - sql += "%s " % random.choice(q_u_or_where) - sql += "%s " % random.choice(order_u_where) + sql += "%s " % random.choice(q_u_where) sql += "%s " % random.choice(limit1_where) sql += ") ;" tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.error(sql) - #tdSql.query(sql) + #TD-15651 tdSql.query(sql) + ##top返回结果有问题 tdSql.checkRows(1) #9 select * from (select ts,calc form stable where <\>\in\and\or order by ) # self.dropandcreateDB_random("%s" %db, 1) tdSql.query("select 9-1 from stable_1;") + for i in range(self.fornum): + sql = "select * from ( select " + sql += "%s " % random.choice(calc_select_not_support_ts) + sql += "from stable_1 where " + sql += "%s " % random.choice(qt_where) + sql += "%s " % random.choice(limit1_where) + sql += ") ;" + tdLog.info(sql) + tdLog.info(len(sql)) + #TD-15651 tdSql.query(sql) + tdSql.query("select 9-2 from stable_1;") for i in range(self.fornum): sql = "select * from ( select ts ," - sql += "%s " % random.choice(calc_select_in_ts) + sql += "%s " % random.choice(calc_select_support_ts) sql += "from stable_1 where " sql += "%s " % random.choice(qt_where) sql += "%s " % random.choice(order_where) @@ -933,35 +1150,33 @@ class TDTestCase: sql += ") ;" tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.query(sql) - - tdSql.query("select 9-2 from stable_1;") + tdSql.query(sql) + + tdSql.query("select 9-3 from stable_1;") for i in range(self.fornum): sql = "select * from ( select " - sql += "%s " % random.choice(calc_select_in_ts_j) + sql += "%s " % random.choice(calc_select_in_not_support_ts_j) sql += "from stable_1 t1, stable_2 t2 where t1.ts = t2.ts and " sql += "%s " % random.choice(t_join_where) - sql += "%s " % random.choice(order_u_where) + sql += " and %s " % random.choice(qt_u_or_where) sql += "%s " % random.choice(limit1_where) sql += ") ;" tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.query(sql) - - tdSql.query("select 9-3 from stable_1;") + #TD-15651 tdSql.query(sql) + tdSql.query("select 9-4 from stable_1;") for i in range(self.fornum): - sql = "select * from ( select " - sql += "%s " % random.choice(calc_select_in_ts_j) + sql = "select * from ( select t1.ts," + sql += "%s " % random.choice(calc_select_in_support_ts_j) sql += "from stable_1 t1, stable_2 t2 where t1.ts = t2.ts and " - sql += "%s " % random.choice(qt_u_or_where) + sql += "%s " % random.choice(t_join_where) + sql += " and %s " % random.choice(qt_u_or_where) sql += "%s " % random.choice(order_u_where) sql += "%s " % random.choice(limit1_where) sql += ") ;" tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.error(sql) - - #functions or others can not be mixed up ,calc out select not use with ts + tdSql.query(sql) #10 select calc from (select * form regualr_table where <\>\in\and\or order by ) tdSql.query("select 10-1 from stable_1;") @@ -975,8 +1190,8 @@ class TDTestCase: sql += ") ;" tdLog.info(sql) tdLog.info(len(sql)) - # TD-15503 tdSql.query(sql) - # tdSql.checkRows(1) + tdSql.query(sql) + tdSql.checkRows(1) #10-1 select calc from (select * form regualr_table where <\>\in\and\or order by ) # rsDn = self.restartDnodes() @@ -986,44 +1201,47 @@ class TDTestCase: for i in range(self.fornum): sql = "select " sql += "%s " % random.choice(calc_select_all) - sql += "as calc10_1 from ( select * from regular_table_1 where " + sql += "as calc10_2 from ( select * from regular_table_1 where " sql += "%s " % random.choice(q_where) sql += "%s " % random.choice(order_desc_where) sql += "%s " % random.choice(limit1_where) sql += ") ;" tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.query(sql) + #TD-15651 tdSql.query(sql) # tdSql.checkRows(1) #10-2 select calc from (select * form regualr_tables where <\>\in\and\or order by ) tdSql.query("select 10-3 from stable_1;") for i in range(self.fornum): sql = "select " - sql += "%s as calc10_1 " % random.choice(calc_select_all) + sql += "%s as calc10_3 " % random.choice(calc_select_all) sql += " from ( select * from regular_table_1 t1, regular_table_2 t2 where t1.ts = t2.ts and " sql += "%s " % random.choice(q_u_where) + sql += " and %s " % random.choice(q_u_or_where) sql += "%s " % random.choice(order_u_where) sql += "%s " % random.choice(limit_u_where) sql += ") " - sql += "%s ;" % random.choice(limit_u_where) + sql += "%s ;" % random.choice(limit1_where) tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.query(sql) + #TD-15651 tdSql.query(sql) tdSql.query("select 10-4 from stable_1;") for i in range(self.fornum): sql = "select " - sql += "%s as calc10_1 " % random.choice(calc_select_all) + sql += "%s as calc10_4 " % random.choice(calc_select_all) sql += " from ( select * from regular_table_1 t1, regular_table_2 t2 where t1.ts = t2.ts and " sql += "%s " % random.choice(q_u_or_where) + sql += " and %s " % random.choice(q_u_or_where) sql += "%s " % random.choice(order_u_where) sql += "%s " % random.choice(limit_u_where) sql += ") " sql += "%s ;" % random.choice(limit_u_where) tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.error(sql) + #TD-15651 tdSql.query(sql) + # tdSql.checkRows(1) #11 select calc from (select * form stable where <\>\in\and\or order by limit ) tdSql.query("select 11-1 from stable_1;") @@ -1037,8 +1255,8 @@ class TDTestCase: sql += ") ;" tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.query(sql) - # tdSql.checkRows(1) + tdSql.query(sql) + tdSql.checkRows(1) #11-1 select calc from (select * form stable where <\>\in\and\or order by limit ) tdSql.query("select 11-2 from stable_1;") @@ -1052,8 +1270,8 @@ class TDTestCase: sql += ") ;" tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.query(sql) - # tdSql.checkRows(1) + #TD-15651 tdSql.query(sql) + #不好计算结果 tdSql.checkRows(1) #11-2 select calc from (select * form stables where <\>\in\and\or order by limit ) tdSql.query("select 11-3 from stable_1;") @@ -1068,7 +1286,7 @@ class TDTestCase: sql += "%s ;" % random.choice(limit_u_where) tdLog.info(sql) tdLog.info(len(sql)) - #TD-15493 tdSql.query(sql) + #TD-15651 tdSql.query(sql) tdSql.query("select 11-4 from stable_1;") for i in range(self.fornum): @@ -1082,7 +1300,8 @@ class TDTestCase: sql += "%s ;" % random.choice(limit_u_where) tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.error(sql) + tdLog.info(len(sql)) + #TD-15651 tdSql.query(sql) #12 select calc-diff from (select * form regualr_table where <\>\in\and\or order by limit ) ##self.dropandcreateDB_random("%s" %db, 1) @@ -1097,7 +1316,7 @@ class TDTestCase: sql += ") ;" tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.query(sql) + ##目前derivative不支持 tdSql.query(sql) # tdSql.checkRows(1) tdSql.query("select 12-2 from stable_1;") @@ -1111,8 +1330,8 @@ class TDTestCase: sql += ") ;" tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.query(sql) - #tdSql.checkRows(1) + #目前derivative不支持 tdSql.query(sql) + # tdSql.checkRows(1) tdSql.query("select 12-2.2 from stable_1;") for i in range(self.fornum): @@ -1125,7 +1344,7 @@ class TDTestCase: sql += ") ;" tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.error(sql) + #目前derivative不支持 tdSql.query(sql) #12-1 select calc-diff from (select * form stable where <\>\in\and\or order by limit ) tdSql.query("select 12-3 from stable_1;") @@ -1142,7 +1361,7 @@ class TDTestCase: sql += " ;" tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.query(sql) + #目前derivative不支持 tdSql.query(sql) tdSql.query("select 12-4 from stable_1;") #join query does not support group by @@ -1151,14 +1370,14 @@ class TDTestCase: sql += "%s " % random.choice(calc_calculate_regular_j) sql += " from stable_1 t1, stable_2 t2 where t1.ts = t2.ts and " sql += "%s " % random.choice(t_join_where) - sql += "%s " % random.choice(group_where) + sql += "%s " % random.choice(group_where_j) sql += ") " - sql += "%s " % random.choice(order_desc_where) + #sql += "%s " % random.choice(order_desc_where) sql += "%s " % random.choice([limit_where[2] , limit_where[3]] ) sql += " ;" tdLog.info(sql) tdLog.info(len(sql)) - tdSql.error(sql) + #tdSql.query(sql) 目前de函数不支持,另外看看需要不需要将group by和pari by分开 tdSql.query("select 12-5 from stable_1;") #join query does not support group by @@ -1167,14 +1386,14 @@ class TDTestCase: sql += "%s " % random.choice(calc_calculate_regular_j) sql += " from stable_1 t1, stable_2 t2 where t1.ts = t2.ts and " sql += "%s " % random.choice(qt_u_or_where) - sql += "%s " % random.choice(group_where) + sql += "%s " % random.choice(group_where_j) sql += ") " sql += "%s " % random.choice(order_desc_where) sql += "%s " % random.choice([limit_where[2] , limit_where[3]] ) sql += " ;" tdLog.info(sql) tdLog.info(len(sql)) - tdSql.error(sql) + #derivative not support tdSql.query(sql) #13 select calc-diff as diffns from (select * form stable where <\>\in\and\or order by limit ) @@ -1189,7 +1408,7 @@ class TDTestCase: sql += ") ;" tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.error(sql) + #derivative not support tdSql.query(sql) #14 select * from (select calc_aggregate_alls as agg from stable where <\>\in\and\or group by order by slimit soffset ) # TD-5955 select * from ( select count (q_double) from stable_1 where t_bool = true or t_bool = false group by loc order by ts asc slimit 1 ) ; @@ -1207,8 +1426,8 @@ class TDTestCase: sql += ") ;" tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.query(sql) - #tdSql.checkRows(1) + #TD-15678 tdSql.query(sql) + # tdSql.checkRows(1) # error group by in out query tdSql.query("select 14-2 from stable_1;") @@ -1226,10 +1445,11 @@ class TDTestCase: sql += ") " sql += "%s " % random.choice(group_where) tdLog.info(sql) - tdLog.info(len(sql)) - # tdSql.error(sql) + tdLog.info(len(sql)) + #TD-15678 tdSql.query(sql) + # tdSql.checkRows(1) - #14-2 TD-6426 select * from (select calc_aggregate_all_js as agg from stables where <\>\in\and\or group by order by slimit soffset ) + #14-2 select * from (select calc_aggregate_all_js as agg from stables where <\>\in\and\or group by order by slimit soffset ) tdSql.query("select 14-3 from stable_1;") for i in range(self.fornum): sql = "select * from ( select " @@ -1238,13 +1458,13 @@ class TDTestCase: sql += "%s " % random.choice(calc_aggregate_all_j) sql += " as calc14_3 from stable_1 t1, stable_2 t2 where t1.ts = t2.ts and " sql += "%s " % random.choice(t_join_where) - sql += "%s " % random.choice(order_u_where) + sql += "%s " % random.choice(partiton_where_j) sql += "%s " % random.choice(slimit1_where) sql += ") " sql += "%s ;" % random.choice(limit_u_where) tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.query(sql) + tdSql.query(sql) tdSql.query("select 14-4 from stable_1;") for i in range(self.fornum): @@ -1254,13 +1474,13 @@ class TDTestCase: sql += "%s " % random.choice(calc_aggregate_all_j) sql += " as calc14_3 from stable_1 t1, stable_2 t2 where t1.ts = t2.ts and " sql += "%s " % random.choice(qt_u_or_where) - sql += "%s " % random.choice(order_u_where) + sql += "%s " % random.choice(partiton_where_j) sql += "%s " % random.choice(slimit1_where) sql += ") " sql += "%s ;" % random.choice(limit_u_where) tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.error(sql) + tdSql.query(sql) #15 TD-6320 select * from (select calc_aggregate_regulars as agg from regular_table where <\>\in\and\or order by slimit soffset ) tdSql.query("select 15-1 from stable_1;") @@ -1271,11 +1491,11 @@ class TDTestCase: sql += "%s " % random.choice(calc_aggregate_regular) sql += " as calc15_3 from regular_table_1 where " sql += "%s " % random.choice(q_where) - sql += "%s " % random.choice(order_desc_where) + sql += "%s " % random.choice(group_where_regular) sql += ") ;" tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.query(sql) + #tdSql.query(sql) #Invalid function name: twa' # tdSql.checkRows(1) tdSql.query("select 15-2 from stable_1;") @@ -1286,13 +1506,13 @@ class TDTestCase: sql += "%s " % random.choice(calc_aggregate_regular_j) sql += " as calc15_3 from regular_table_1 t1, regular_table_2 t2 where t1.ts = t2.ts and " sql += "%s " % random.choice(q_u_where) - sql += "%s " % random.choice(order_u_where) + sql += "%s " % random.choice(group_where_regular_j) sql += "%s " % random.choice(limit_u_where) sql += ") " sql += "%s ;" % random.choice(limit_u_where) tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.query(sql) + #tdSql.query(sql) #Invalid function name: twa' tdSql.query("select 15-2.2 from stable_1;") for i in range(self.fornum): @@ -1302,13 +1522,13 @@ class TDTestCase: sql += "%s " % random.choice(calc_aggregate_regular_j) sql += " as calc15_3 from regular_table_1 t1, regular_table_2 t2 where t1.ts = t2.ts and " sql += "%s " % random.choice(q_u_or_where) - sql += "%s " % random.choice(order_u_where) + sql += "%s " % random.choice(group_where_regular_j) sql += "%s " % random.choice(limit_u_where) sql += ") " sql += "%s ;" % random.choice(limit_u_where) tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.error(sql) + #tdSql.query(sql) #Invalid function name: twa' rsDn = self.restartDnodes() tdSql.query("select 15-3 from stable_1;") @@ -1327,7 +1547,7 @@ class TDTestCase: sql += "%s " % random.choice(limit_where) tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.query(sql) + #tdSql.query(sql) #Invalid function name: twa',可能还的去掉order by tdSql.query("select 15-4 from stable_1;") for i in range(self.fornum): @@ -1337,15 +1557,15 @@ class TDTestCase: sql += "%s " % random.choice(calc_aggregate_groupbytbname_j) sql += " as calc15_3 from stable_1 t1, stable_2 t2 where t1.ts = t2.ts and " sql += "%s " % random.choice(t_join_where) - sql += "%s " % random.choice(group_where) - sql += "%s " % random.choice(having_support) - sql += "%s " % random.choice(orders_desc_where) + sql += "%s " % random.choice(group_where_j) + sql += "%s " % random.choice(having_support_j) + #sql += "%s " % random.choice(orders_desc_where) sql += ") " sql += "order by calc15_1 " sql += "%s " % random.choice(limit_u_where) tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.error(sql) + #tdSql.query(sql) #'Invalid function name: irate' tdSql.query("select 15-4.2 from stable_1;") for i in range(self.fornum): @@ -1355,15 +1575,15 @@ class TDTestCase: sql += "%s " % random.choice(calc_aggregate_groupbytbname_j) sql += " as calc15_3 from stable_1 t1, stable_2 t2 where t1.ts = t2.ts and " sql += "%s " % random.choice(qt_u_or_where) - sql += "%s " % random.choice(group_where) - sql += "%s " % random.choice(having_support) + sql += "%s " % random.choice(group_where_j) + sql += "%s " % random.choice(having_support_j) sql += "%s " % random.choice(orders_desc_where) sql += ") " sql += "order by calc15_1 " sql += "%s " % random.choice(limit_u_where) tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.error(sql) + #TD-15678 #tdSql.query(sql) tdSql.query("select 15-5 from stable_1;") for i in range(self.fornum): @@ -1373,13 +1593,13 @@ class TDTestCase: sql += "%s " % random.choice(calc_aggregate_groupbytbname) sql += " as calc15_3 from stable_1 where " sql += "%s " % random.choice(q_where) - sql += "%s " % random.choice(orders_desc_where) + sql += "%s " % random.choice(group_where) sql += ") " sql += "order by calc15_1 " sql += "%s " % random.choice(limit_where) tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.error(sql) + #tdSql.query(sql) #'Invalid function name: irate' #16 select * from (select calc_aggregate_regulars as agg from regular_table where <\>\in\and\or order by limit offset ) #self.dropandcreateDB_random("%s" %db, 1) @@ -1392,14 +1612,13 @@ class TDTestCase: sql += " from stable_1 where " sql += "%s " % random.choice(q_where) sql += "%s " % random.choice(group_where) - sql += "%s " % random.choice(having_support) + #sql += "%s " % random.choice(having_support)having和 partition不能混合使用 sql += ") " sql += "order by calc16_0 " sql += "%s " % random.choice(limit1_where) tdLog.info(sql) tdLog.info(len(sql)) - # TD-15497 tdSql.query(sql) - # tdSql.checkRows(1) + #TD-15651 tdSql.query(sql) tdSql.query("select 16-2 from stable_1;") for i in range(self.fornum): @@ -1414,7 +1633,7 @@ class TDTestCase: sql += "%s " % random.choice(limit1_where) tdLog.info(sql) tdLog.info(len(sql)) - #TD-15493 tdSql.query(sql) + tdSql.query(sql) tdSql.query("select 16-2.2 from stable_1;") for i in range(self.fornum): @@ -1428,7 +1647,7 @@ class TDTestCase: sql += "%s " % random.choice(limit1_where) tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.error(sql) + tdSql.query(sql) tdSql.query("select 16-3 from stable_1;") for i in range(self.fornum): @@ -1440,8 +1659,7 @@ class TDTestCase: sql += "%s " % random.choice(limit1_where) tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.query(sql) - # tdSql.checkRows(1) + #tdSql.query(sql)#Invalid function name: derivative' tdSql.query("select 16-4 from stable_1;") for i in range(self.fornum): @@ -1453,8 +1671,7 @@ class TDTestCase: sql += "%s " % random.choice(limit1_where) tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.query(sql) - # tdSql.checkRows(1) + #tdSql.query(sql)#Invalid function name: derivative' tdSql.query("select 16-4.2 from stable_1;") for i in range(self.fornum): @@ -1466,7 +1683,7 @@ class TDTestCase: sql += "%s " % random.choice(limit1_where) tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.error(sql) + #tdSql.query(sql)#Invalid function name: derivative' tdSql.query("select 16-5 from stable_1;") for i in range(self.fornum): @@ -1477,13 +1694,13 @@ class TDTestCase: sql += " from stable_1 where " sql += "%s " % random.choice(q_where) sql += "%s " % random.choice(group_where) - sql += "%s " % random.choice(having_support) + #sql += "%s " % random.choice(having_support) sql += ") " sql += "order by calc16_1 " sql += "%s " % random.choice(limit1_where) tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.error(sql) + # tdSql.query(sql) tdSql.query("select 16-6 from stable_1;") for i in range(self.fornum): @@ -1496,8 +1713,7 @@ class TDTestCase: sql += "%s " % random.choice(limit1_where) tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.query(sql) - # tdSql.checkRows(1) + #Invalid function name: derivative' tdSql.query(sql) tdSql.query("select 16-7 from stable_1;") for i in range(self.fornum): @@ -1509,7 +1725,7 @@ class TDTestCase: sql += "%s " % random.choice(limit1_where) tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.error(sql) + #Invalid function name: derivative' tdSql.query(sql) tdSql.query("select 16-8 from stable_1;") for i in range(self.fornum): @@ -1521,7 +1737,7 @@ class TDTestCase: sql += "%s " % random.choice(limit1_where) tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.error(sql) + #Invalid function name: derivative' tdSql.query(sql) #17 select apercentile from (select calc_aggregate_alls form regualr_table or stable where <\>\in\and\or interval_sliding group by having order by limit offset )interval_sliding #self.dropandcreateDB_random("%s" %db, 1) @@ -1534,16 +1750,16 @@ class TDTestCase: sql += "%s as cal17_2 " % random.choice(calc_aggregate_all) sql += " from stable_1 where " sql += "%s " % random.choice(qt_where) + sql += "%s " % random.choice(partiton_where) sql += "%s " % random.choice(interval_sliding) - sql += "%s " % random.choice(group_where) - sql += "%s " % random.choice(having_support) - sql += "%s " % random.choice(order_where) + #sql += "%s " % random.choice(having_support) + #sql += "%s " % random.choice(order_where) sql += "%s " % random.choice(limit1_where) sql += ") " - sql += "%s " % random.choice(interval_sliding) + #sql += "%s " % random.choice(interval_sliding) tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.query(sql) + #TD-15719 tdSql.query(sql) tdSql.query("select 17-2 from stable_1;") for i in range(self.fornum): @@ -1555,13 +1771,13 @@ class TDTestCase: sql += " from stable_1 t1, stable_2 t2 where t1.ts = t2.ts and " sql += "%s " % random.choice(t_join_where) sql += "%s " % random.choice(interval_sliding) - sql += "%s " % random.choice(order_u_where) + #sql += "%s " % random.choice(order_u_where) sql += "%s " % random.choice(limit_u_where) sql += ") " - sql += "%s " % random.choice(interval_sliding) + #sql += "%s " % random.choice(interval_sliding) tdLog.info(sql) tdLog.info(len(sql)) - #Column ambiguously defined: ts tdSql.query(sql) + tdSql.query(sql) tdSql.query("select 17-2.2 from stable_1;") for i in range(self.fornum): @@ -1573,15 +1789,15 @@ class TDTestCase: sql += " from stable_1 t1, stable_2 t2 where t1.ts = t2.ts and " sql += "%s " % random.choice(qt_u_or_where) sql += "%s " % random.choice(interval_sliding) - sql += "%s " % random.choice(order_u_where) + #sql += "%s " % random.choice(order_u_where) sql += "%s " % random.choice(limit_u_where) sql += ") " - sql += "%s " % random.choice(interval_sliding) + #sql += "%s " % random.choice(interval_sliding) tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.error(sql) + tdSql.query(sql) - rsDn = self.restartDnodes() + self.restartDnodes() tdSql.query("select 17-3 from stable_1;") for i in range(self.fornum): #this is having_tagnot_support , because tag-select cannot mix with last_row... @@ -1590,16 +1806,16 @@ class TDTestCase: sql += "%s as cal17_2 " % random.choice(calc_aggregate_all) sql += " from stable_1 where " sql += "%s " % random.choice(q_where) + sql += "%s " % random.choice(partiton_where) sql += "%s " % random.choice(interval_sliding) - sql += "%s " % random.choice(group_where) - sql += "%s " % random.choice(having_tagnot_support) - sql += "%s " % random.choice(order_where) + #sql += "%s " % random.choice(having_tagnot_support) + #sql += "%s " % random.choice(order_where) sql += "%s " % random.choice(limit1_where) sql += ") " - sql += "%s " % random.choice(interval_sliding) + #sql += "%s " % random.choice(interval_sliding) tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.query(sql) + #TD-15770 tdSql.query(sql) tdSql.query("select 17-4 from stable_1;") for i in range(self.fornum): @@ -1610,13 +1826,13 @@ class TDTestCase: sql += " from stable_1 t1, stable_2 t2 where t1.ts = t2.ts and " sql += "%s " % random.choice(t_join_where) sql += "%s " % random.choice(interval_sliding) - sql += "%s " % random.choice(order_u_where) + #sql += "%s " % random.choice(order_u_where) sql += "%s " % random.choice(limit_u_where) sql += ") " - sql += "%s " % random.choice(interval_sliding) + #sql += "%s " % random.choice(interval_sliding) tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.query(sql) + tdSql.query(sql) tdSql.query("select 17-4.2 from stable_1;") for i in range(self.fornum): @@ -1627,13 +1843,13 @@ class TDTestCase: sql += " from stable_1 t1, stable_2 t2 where t1.ts = t2.ts and " sql += "%s " % random.choice(qt_u_or_where) sql += "%s " % random.choice(interval_sliding) - sql += "%s " % random.choice(order_u_where) + #sql += "%s " % random.choice(order_u_where) sql += "%s " % random.choice(limit_u_where) sql += ") " - sql += "%s " % random.choice(interval_sliding) + #sql += "%s " % random.choice(interval_sliding) tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.error(sql) + tdSql.query(sql) tdSql.query("select 17-5 from stable_1;") for i in range(self.fornum): @@ -1643,16 +1859,16 @@ class TDTestCase: sql += "%s as cal17_2 " % random.choice(calc_aggregate_all) sql += " from stable_1 where " sql += "%s " % random.choice(qt_where) + sql += "%s " % random.choice(partiton_where) sql += "%s " % random.choice(interval_sliding) - sql += "%s " % random.choice(group_where) - sql += "%s " % random.choice(having_not_support) - sql += "%s " % random.choice(order_where) + # sql += "%s " % random.choice(having_not_support) + # sql += "%s " % random.choice(order_where) sql += "%s " % random.choice(limit1_where) sql += ") " - sql += "%s " % random.choice(interval_sliding) + #sql += "%s " % random.choice(interval_sliding) tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.error(sql) + #TD-15719 tdSql.query(sql) tdSql.query("select 17-6 from stable_1;") for i in range(self.fornum): @@ -1662,13 +1878,13 @@ class TDTestCase: sql += " from stable_1 where " sql += "%s " % random.choice(q_where) sql += "%s " % random.choice(interval_sliding) - sql += "%s " % random.choice(order_where) + #sql += "%s " % random.choice(order_where) sql += "%s " % random.choice(limit1_where) sql += ") " - sql += "%s " % random.choice(interval_sliding) + #sql += "%s " % random.choice(interval_sliding) tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.query(sql) + #TD-15770 tdSql.query(sql) tdSql.query("select 17-7 from stable_1;") for i in range(self.fornum): @@ -1678,13 +1894,13 @@ class TDTestCase: sql += " from stable_1_1 t1, stable_1_2 t2 where t1.ts = t2.ts and " sql += "%s " % random.choice(q_u_where) sql += "%s " % random.choice(interval_sliding) - sql += "%s " % random.choice(order_u_where) + #sql += "%s " % random.choice(order_u_where) sql += "%s " % random.choice(limit1_where) sql += ") " - sql += "%s " % random.choice(interval_sliding) + #sql += "%s " % random.choice(interval_sliding) tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.query(sql) + tdSql.query(sql) tdSql.query("select 17-7.2 from stable_1;") for i in range(self.fornum): @@ -1694,13 +1910,13 @@ class TDTestCase: sql += " from stable_1_1 t1, stable_1_2 t2 where t1.ts = t2.ts and " sql += "%s " % random.choice(q_u_or_where) sql += "%s " % random.choice(interval_sliding) - sql += "%s " % random.choice(order_u_where) + #sql += "%s " % random.choice(order_u_where) sql += "%s " % random.choice(limit1_where) sql += ") " - sql += "%s " % random.choice(interval_sliding) + #sql += "%s " % random.choice(interval_sliding) tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.error(sql) + tdSql.query(sql) self.restartDnodes() tdSql.query("select 17-8 from stable_1;") @@ -1711,13 +1927,13 @@ class TDTestCase: sql += " from regular_table_1 where " sql += "%s " % random.choice(q_where) sql += "%s " % random.choice(interval_sliding) - sql += "%s " % random.choice(order_where) + #sql += "%s " % random.choice(order_where) sql += "%s " % random.choice(limit1_where) sql += ") " - sql += "%s " % random.choice(interval_sliding) + #sql += "%s " % random.choice(interval_sliding) tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.query(sql) + tdSql.query(sql) tdSql.query("select 17-9 from stable_1;") for i in range(self.fornum): @@ -1727,13 +1943,13 @@ class TDTestCase: sql += " from regular_table_1 t1, regular_table_2 t2 where t1.ts = t2.ts and " sql += "%s " % random.choice(q_u_where) sql += "%s " % random.choice(interval_sliding) - sql += "%s " % random.choice(order_u_where) + #sql += "%s " % random.choice(order_u_where) sql += "%s " % random.choice(limit_u_where) sql += ") " - sql += "%s " % random.choice(interval_sliding) + #sql += "%s " % random.choice(interval_sliding) tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.query(sql) + tdSql.query(sql) tdSql.query("select 17-10 from stable_1;") for i in range(self.fornum): @@ -1743,13 +1959,13 @@ class TDTestCase: sql += " from regular_table_1 t1, regular_table_2 t2 where t1.ts = t2.ts and " sql += "%s " % random.choice(q_u_or_where) sql += "%s " % random.choice(interval_sliding) - sql += "%s " % random.choice(order_u_where) + #sql += "%s " % random.choice(order_u_where) sql += "%s " % random.choice(limit_u_where) sql += ") " - sql += "%s " % random.choice(interval_sliding) + #sql += "%s " % random.choice(interval_sliding) tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.error(sql) + tdSql.query(sql) #18 select apercentile from (select calc_aggregate_alls form regualr_table or stable where <\>\in\and\or session order by limit )interval_sliding tdSql.query("select 18-1 from stable_1;") @@ -1760,14 +1976,14 @@ class TDTestCase: sql += " from regular_table_1 where " sql += "%s " % random.choice(q_where) sql += "%s " % random.choice(session_where) - sql += "%s " % random.choice(fill_where) - sql += "%s " % random.choice(order_where) + #sql += "%s " % random.choice(fill_where) + #sql += "%s " % random.choice(order_where) sql += "%s " % random.choice(limit1_where) sql += ") " - sql += "%s " % random.choice(interval_sliding) + #sql += "%s " % random.choice(interval_sliding) tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.query(sql) + tdSql.query(sql) tdSql.query("select 18-2 from stable_1;") for i in range(self.fornum): @@ -1777,13 +1993,13 @@ class TDTestCase: sql += " from regular_table_1 t1, regular_table_2 t2 where t1.ts = t2.ts and " sql += "%s " % random.choice(q_u_where) sql += "%s " % random.choice(session_u_where) - sql += "%s " % random.choice(order_u_where) + #sql += "%s " % random.choice(order_u_where) sql += "%s " % random.choice(limit_u_where) sql += ") " - sql += "%s " % random.choice(interval_sliding) + #sql += "%s " % random.choice(interval_sliding) tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.query(sql) + tdSql.query(sql) tdSql.query("select 18-2.2 from stable_1;") for i in range(self.fornum): @@ -1793,13 +2009,13 @@ class TDTestCase: sql += " from regular_table_1 t1, regular_table_2 t2 where t1.ts = t2.ts and " sql += "%s " % random.choice(q_u_or_where) sql += "%s " % random.choice(session_u_where) - sql += "%s " % random.choice(order_u_where) + #sql += "%s " % random.choice(order_u_where) sql += "%s " % random.choice(limit_u_where) sql += ") " - sql += "%s " % random.choice(interval_sliding) + #sql += "%s " % random.choice(interval_sliding) tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.error(sql) + tdSql.query(sql) self.restartDnodes() tdSql.query("select 18-3 from stable_1;") @@ -1810,14 +2026,14 @@ class TDTestCase: sql += " from stable_1_1 where " sql += "%s " % random.choice(q_where) sql += "%s " % random.choice(session_where) - sql += "%s " % random.choice(fill_where) - sql += "%s " % random.choice(order_where) + #sql += "%s " % random.choice(fill_where) + #sql += "%s " % random.choice(order_where) sql += "%s " % random.choice(limit1_where) sql += ") " - sql += "%s " % random.choice(interval_sliding) + #sql += "%s " % random.choice(interval_sliding) tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.query(sql) + tdSql.query(sql) tdSql.query("select 18-4 from stable_1;") for i in range(self.fornum): @@ -1827,13 +2043,13 @@ class TDTestCase: sql += " from stable_1_1 t1, regular_table_2 t2 where t1.ts = t2.ts and " sql += "%s " % random.choice(q_u_where) sql += "%s " % random.choice(session_u_where) - sql += "%s " % random.choice(order_u_where) + #sql += "%s " % random.choice(order_u_where) sql += "%s " % random.choice(limit_u_where) sql += ") " - sql += "%s " % random.choice(interval_sliding) + #sql += "%s " % random.choice(interval_sliding) tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.query(sql) + tdSql.query(sql) tdSql.query("select 18-4.2 from stable_1;") for i in range(self.fornum): @@ -1843,13 +2059,13 @@ class TDTestCase: sql += " from stable_1_1 t1, regular_table_2 t2 where t1.ts = t2.ts and " sql += "%s " % random.choice(q_u_or_where) sql += "%s " % random.choice(session_u_where) - sql += "%s " % random.choice(order_u_where) + #sql += "%s " % random.choice(order_u_where) sql += "%s " % random.choice(limit_u_where) sql += ") " - sql += "%s " % random.choice(interval_sliding) + #sql += "%s " % random.choice(interval_sliding) tdLog.info(sql) tdLog.info(len(sql)) - tdSql.error(sql) + tdSql.query(sql) tdSql.query("select 18-5 from stable_1;") for i in range(self.fornum): @@ -1859,14 +2075,14 @@ class TDTestCase: sql += " from stable_1 where " sql += "%s " % random.choice(q_where) sql += "%s " % random.choice(session_where) - sql += "%s " % random.choice(fill_where) - sql += "%s " % random.choice(order_where) + #sql += "%s " % random.choice(fill_where) + #sql += "%s " % random.choice(order_where) sql += "%s " % random.choice(limit1_where) sql += ") " - sql += "%s " % random.choice(interval_sliding) + #sql += "%s " % random.choice(interval_sliding) tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.error(sql) + #TD-15770 tdSql.query(sql) tdSql.query("select 18-6 from stable_1;") for i in range(self.fornum): @@ -1876,13 +2092,13 @@ class TDTestCase: sql += " from stable_1 t1, stable_2 t2 where t1.ts = t2.ts and " sql += "%s " % random.choice(t_join_where) sql += "%s " % random.choice(session_u_where) - sql += "%s " % random.choice(order_u_where) + #sql += "%s " % random.choice(order_u_where) sql += "%s " % random.choice(limit_u_where) sql += ") " - sql += "%s " % random.choice(interval_sliding) + #sql += "%s " % random.choice(interval_sliding) tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.error(sql) + tdSql.query(sql) tdSql.query("select 18-7 from stable_1;") for i in range(self.fornum): @@ -1892,13 +2108,13 @@ class TDTestCase: sql += " from stable_1 t1, stable_2 t2 where t1.ts = t2.ts and " sql += "%s " % random.choice(qt_u_or_where) sql += "%s " % random.choice(session_u_where) - sql += "%s " % random.choice(order_u_where) + #sql += "%s " % random.choice(order_u_where) sql += "%s " % random.choice(limit_u_where) sql += ") " - sql += "%s " % random.choice(interval_sliding) + #sql += "%s " % random.choice(interval_sliding) tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.error(sql) + tdSql.query(sql) #19 select apercentile from (select calc_aggregate_alls form regualr_table or stable where <\>\in\and\or session order by limit )interval_sliding #self.dropandcreateDB_random("%s" %db, 1) @@ -1910,15 +2126,14 @@ class TDTestCase: sql += " from regular_table_1 where " sql += "%s " % random.choice(q_where) sql += "%s " % random.choice(state_window) - sql += "%s " % random.choice(order_where) + #sql += "%s " % random.choice(order_where) sql += "%s " % random.choice(limit1_where) sql += ") " tdLog.info(sql) tdLog.info(len(sql)) - #TD-15545 tdSql.query(sql) + tdSql.query(sql) tdSql.query("select 19-2 from stable_1;") - #TD-6435 state_window not support join for i in range(self.fornum): sql = "select apercentile(cal19_1, %d)/1000 ,apercentile(cal19_2, %d)*10+%d from ( select " %(random.randint(0,100) , random.randint(0,100) ,random.randint(-1000,1000)) sql += "%s as cal19_1 ," % random.choice(calc_aggregate_all_j) @@ -1926,12 +2141,12 @@ class TDTestCase: sql += " from regular_table_1 t1, regular_table_2 t2 where t1.ts = t2.ts and " sql += "%s " % random.choice(q_u_where) sql += "%s " % random.choice(state_u_window) - sql += "%s " % random.choice(order_u_where) + #sql += "%s " % random.choice(order_u_where) sql += "%s " % random.choice(limit_u_where) sql += ") " tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.error(sql) + tdSql.query(sql) tdSql.query("select 19-2.2 from stable_1;") for i in range(self.fornum): @@ -1941,12 +2156,12 @@ class TDTestCase: sql += " from regular_table_1 t1, regular_table_2 t2 where t1.ts = t2.ts and " sql += "%s " % random.choice(q_u_or_where) sql += "%s " % random.choice(state_u_window) - sql += "%s " % random.choice(order_u_where) + #sql += "%s " % random.choice(order_u_where) sql += "%s " % random.choice(limit_u_where) sql += ") " tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.error(sql) + tdSql.query(sql) tdSql.query("select 19-3 from stable_1;") for i in range(self.fornum): @@ -1956,12 +2171,12 @@ class TDTestCase: sql += " from stable_1_1 where " sql += "%s " % random.choice(q_where) sql += "%s " % random.choice(state_window) - sql += "%s " % random.choice(order_where) + #sql += "%s " % random.choice(order_where) sql += "%s " % random.choice(limit1_where) sql += ") " tdLog.info(sql) tdLog.info(len(sql)) - #不支持order by tdSql.query(sql) + tdSql.query(sql) tdSql.query("select 19-4 from stable_1;") for i in range(self.fornum): @@ -1971,12 +2186,12 @@ class TDTestCase: sql += " from stable_1_1 t1, stable_1_2 t2 where t1.ts = t2.ts and " sql += "%s " % random.choice(q_u_where) #sql += "%s " % random.choice(state_window) - sql += "%s " % random.choice(order_u_where) + #sql += "%s " % random.choice(order_u_where) sql += "%s " % random.choice(limit_u_where) sql += ") " tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.query(sql) + tdSql.query(sql) tdSql.query("select 19-4.2 from stable_1;") for i in range(self.fornum): @@ -1985,12 +2200,12 @@ class TDTestCase: sql += "%s as cal19_2 " % random.choice(calc_aggregate_all_j) sql += " from stable_1_1 t1, stable_1_2 t2 where t1.ts = t2.ts and " sql += "%s " % random.choice(q_u_or_where) - sql += "%s " % random.choice(order_u_where) + #sql += "%s " % random.choice(order_u_where) sql += "%s " % random.choice(limit_u_where) sql += ") " tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.error(sql) + tdSql.query(sql) tdSql.query("select 19-5 from stable_1;") for i in range(self.fornum): @@ -2006,7 +2221,7 @@ class TDTestCase: sql += "%s " % random.choice(interval_sliding) tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.error(sql) + tdSql.error(sql) #'STATE_WINDOW not support for super table query' tdSql.query("select 19-6 from stable_1;") for i in range(self.fornum): @@ -2016,13 +2231,13 @@ class TDTestCase: sql += " from stable_1 t1 , stable_2 t2 where t1.ts = t2.ts and " sql += "%s " % random.choice(q_u_where) #sql += "%s " % random.choice(state_window) - sql += "%s " % random.choice(order_u_where) + #sql += "%s " % random.choice(order_u_where) sql += "%s " % random.choice(limit_u_where) sql += ") " - sql += "%s " % random.choice(interval_sliding) + #sql += "%s " % random.choice(interval_sliding) tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.error(sql) + tdSql.query(sql) tdSql.query("select 19-7 from stable_1;") for i in range(self.fornum): @@ -2031,13 +2246,13 @@ class TDTestCase: sql += "%s as cal19_2 " % random.choice(calc_aggregate_all_j) sql += " from stable_1 t1 , stable_2 t2 where t1.ts = t2.ts and " sql += "%s " % random.choice(qt_u_or_where) - sql += "%s " % random.choice(order_u_where) + #sql += "%s " % random.choice(order_u_where) sql += "%s " % random.choice(limit_u_where) sql += ") " - sql += "%s " % random.choice(interval_sliding) + #sql += "%s " % random.choice(interval_sliding) tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.error(sql) + tdSql.query(sql) #20 select * from (select calc_select_fills form regualr_table or stable where <\>\in\and\or fill_where group by order by limit offset ) #self.dropandcreateDB_random("%s" %db, 1) @@ -2056,11 +2271,10 @@ class TDTestCase: sql += ") " tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.query(sql) + #interp不支持 tdSql.query(sql) rsDn = self.restartDnodes() tdSql.query("select 20-2 from stable_1;") - #TD-6438 for i in range(self.fornum): sql = "select * from ( select " sql += "%s , " % random.choice(calc_select_fill_j) @@ -2074,7 +2288,7 @@ class TDTestCase: sql += ") " tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.query(sql) + #interp不支持 tdSql.query(sql) tdSql.query("select 20-2.2 from stable_1;") for i in range(self.fornum): @@ -2090,7 +2304,7 @@ class TDTestCase: sql += ") " tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.error(sql) + #interp不支持 tdSql.query(sql) tdSql.query("select 20-3 from stable_1;") for i in range(self.fornum): @@ -2122,7 +2336,7 @@ class TDTestCase: sql += ") " tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.query(sql) + #interp不支持 tdSql.query(sql) tdSql.query("select 20-4.2 from stable_1;") for i in range(self.fornum): @@ -2139,7 +2353,7 @@ class TDTestCase: sql += ") " tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.error(sql) + ##interp不支持 tdSql.error(sql) tdSql.query("select 20-5 from stable_1;") for i in range(self.fornum): @@ -2155,7 +2369,7 @@ class TDTestCase: sql += ") " tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.query(sql) + ##interp不支持 tdSql.query(sql) tdSql.query("select 20-6 from stable_1;") for i in range(self.fornum): @@ -2171,40 +2385,124 @@ class TDTestCase: sql += ") " tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.query(sql) + ##interp不支持 tdSql.query(sql) - # error #1 select * from (select * from (select * form regular_table where <\>\in\and\or order by limit )) tdSql.query("select 1-1 from stable_1;") + for i in range(self.fornum): + # sql_start = "select * from ( " + # sql_end = ")" + for_num = random.randint(1, 15); + sql = "select * from (" * for_num + sql += "select * from ( select * from ( select " + sql += "%s, " % random.choice(s_r_select) + sql += "%s, " % random.choice(q_select) + sql += "ts from regular_table_1 where " + sql += "%s " % random.choice(q_where) + sql += ")) " + sql += ")" * for_num + tdLog.info(sql) + tdLog.info(len(sql)) + tdSql.query(sql) + + sql2 = "select * from ( select * from ( select " + sql2 += "%s, " % random.choice(s_r_select) + sql2 += "%s, " % random.choice(q_select) + sql2 += "ts from regular_table_1 where " + sql2 += "%s " % random.choice(q_where) + sql2 += ")) " + tdLog.info(sql2) + tdLog.info(len(sql2)) + + self.data_matrix_equal('%s' %sql ,1,10,1,1,'%s' %sql2 ,1,10,1,1) + self.data_matrix_equal('%s' %sql ,1,10,1,1,'%s' %sql ,1,10,3,3) + self.data_matrix_equal('%s' %sql ,1,10,3,3,'%s' %sql2 ,1,10,3,3) + for i in range(self.fornum): - sql = "select * , ts from ( select * from ( select " + for_num = random.randint(1, 15); + sql = "select ts from (" * for_num + sql += "select * from ( select * from ( select " sql += "%s, " % random.choice(s_r_select) sql += "%s, " % random.choice(q_select) sql += "ts from regular_table_1 where " sql += "%s " % random.choice(q_where) - sql += ")) ;" + sql += ")) " + sql += ")" * for_num tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.error(sql) - + tdSql.query(sql) + + sql2 = "select * from ( select * from ( select " + sql2 += "%s, " % random.choice(s_r_select) + sql2 += "%s, " % random.choice(q_select) + sql2 += "ts from regular_table_1 where " + sql2 += "%s " % random.choice(q_where) + sql2 += ")) " + tdLog.info(sql) + tdLog.info(len(sql)) + tdSql.query(sql) + + self.data_matrix_equal('%s' %sql ,1,10,1,1,'%s' %sql2 ,1,10,1,1) + #2 select * from (select * from (select * form stable where <\>\in\and\or order by limit )) tdSql.query("select 2-1 from stable_1;") for i in range(self.fornum): - sql = "select * , ts from ( select * from ( select " + for_num = random.randint(1, 15); + sql = "select * from (" * for_num + sql += "select * from ( select * from ( select " + sql += "%s, " % random.choice(s_s_select) + sql += "%s, " % random.choice(qt_select) + sql += "ts from stable_1 where " + sql += "%s " % random.choice(q_where) + sql += ")) " + sql += ")" * for_num + tdLog.info(sql) + tdLog.info(len(sql)) + tdSql.query(sql) + + sql2 = "select * from ( select * from ( select " + sql2 += "%s, " % random.choice(s_s_select) + sql2 += "%s, " % random.choice(qt_select) + sql2 += "ts from stable_1 where " + sql2 += "%s " % random.choice(q_where) + sql2 += ")) " + tdLog.info(sql) + tdLog.info(len(sql)) + tdSql.query(sql) + + self.data_matrix_equal('%s' %sql ,1,10,3,3,'%s' %sql2 ,1,10,3,3) + + for i in range(self.fornum): + for_num = random.randint(1, 15); + sql = "select ts from (" * for_num + sql += "select * from ( select * from ( select " sql += "%s, " % random.choice(s_s_select) sql += "%s, " % random.choice(qt_select) sql += "ts from stable_1 where " sql += "%s " % random.choice(q_where) - sql += ")) ;" + sql += ")) " + sql += ")" * for_num tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.error(sql) - + tdSql.query(sql) + + sql2 = "select ts from ( select * from ( select " + sql2 += "%s, " % random.choice(s_s_select) + sql2 += "%s, " % random.choice(qt_select) + sql2 += "ts from stable_1 where " + sql2 += "%s " % random.choice(q_where) + sql2 += ")) " + tdLog.info(sql) + tdLog.info(len(sql)) + tdSql.query(sql) + + self.data_matrix_equal('%s' %sql ,1,10,1,1,'%s' %sql2 ,1,10,1,1) + #3 select ts ,calc from (select * form stable where <\>\in\and\or order by limit ) #self.dropandcreateDB_random("%s" %db, 1) tdSql.query("select 3-1 from stable_1;") for i in range(self.fornum): - sql = "select ts , " + sql = "select " sql += "%s " % random.choice(calc_calculate_regular) sql += " from ( select * from stable_1 where " sql += "%s " % random.choice(qt_where) @@ -2213,7 +2511,7 @@ class TDTestCase: sql += ") ;" tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.error(sql) + #'Invalid function name: derivative' tdSql.query(sql) #4 select * from (select calc form stable where <\>\in\and\or order by limit ) tdSql.query("select 4-1 from stable_1;") @@ -2222,12 +2520,12 @@ class TDTestCase: sql += "%s " % random.choice(calc_select_in_ts) sql += "from stable_1 where " sql += "%s " % random.choice(qt_where) - sql += "%s " % random.choice(order_desc_where) + #sql += "%s " % random.choice(order_desc_where) sql += "%s " % random.choice(limit_where) sql += ") ;" tdLog.info(sql) tdLog.info(len(sql)) - #tdSql.query(sql) + tdSql.query(sql) #5 select ts ,tbname from (select * form stable where <\>\in\and\or order by limit ) tdSql.query("select 5-1 from stable_1;") @@ -2243,7 +2541,7 @@ class TDTestCase: sql += ") ;" tdLog.info(sql) tdLog.info(len(sql)) - # tdSql.error(sql) + tdSql.error(sql) #special sql tdSql.query("select 6-1 from stable_1;") diff --git a/tests/system-test/7-tmq/subscribeStb.py b/tests/system-test/7-tmq/subscribeStb.py new file mode 100644 index 0000000000000000000000000000000000000000..d0463be710e77e7bf4f9f3d35e66a2acddee42a3 --- /dev/null +++ b/tests/system-test/7-tmq/subscribeStb.py @@ -0,0 +1,1399 @@ + +import taos +import sys +import time +import socket +import os +import threading +from enum import Enum + +from util.log import * +from util.sql import * +from util.cases import * +from util.dnodes import * + +class actionType(Enum): + CREATE_DATABASE = 0 + CREATE_STABLE = 1 + CREATE_CTABLE = 2 + INSERT_DATA = 3 + +class TDTestCase: + hostname = socket.gethostname() + #rpcDebugFlagVal = '143' + #clientCfgDict = {'serverPort': '', 'firstEp': '', 'secondEp':'', 'rpcDebugFlag':'135', 'fqdn':''} + #clientCfgDict["rpcDebugFlag"] = rpcDebugFlagVal + #updatecfgDict = {'clientCfg': {}, 'serverPort': '', 'firstEp': '', 'secondEp':'', 'rpcDebugFlag':'135', 'fqdn':''} + #updatecfgDict["rpcDebugFlag"] = rpcDebugFlagVal + #print ("===================: ", updatecfgDict) + + def init(self, conn, logSql): + tdLog.debug(f"start to excute {__file__}") + #tdSql.init(conn.cursor()) + tdSql.init(conn.cursor(), logSql) # output sql.txt file + + def getBuildPath(self): + selfPath = os.path.dirname(os.path.realpath(__file__)) + + if ("community" in selfPath): + projPath = selfPath[:selfPath.find("community")] + else: + projPath = selfPath[:selfPath.find("tests")] + + for root, dirs, files in os.walk(projPath): + if ("taosd" in files): + rootRealPath = os.path.dirname(os.path.realpath(root)) + if ("packaging" not in rootRealPath): + buildPath = root[:len(root) - len("/build/bin")] + break + return buildPath + + def newcur(self,cfg,host,port): + user = "root" + password = "taosdata" + con=taos.connect(host=host, user=user, password=password, config=cfg ,port=port) + cur=con.cursor() + print(cur) + return cur + + def initConsumerTable(self,cdbName='cdb'): + tdLog.info("create consume database, and consume info table, and consume result table") + tdSql.query("create database if not exists %s vgroups 1"%(cdbName)) + tdSql.query("drop table if exists %s.consumeinfo "%(cdbName)) + tdSql.query("drop table if exists %s.consumeresult "%(cdbName)) + + tdSql.query("create table %s.consumeinfo (ts timestamp, consumerid int, topiclist binary(1024), keylist binary(1024), expectmsgcnt bigint, ifcheckdata int, ifmanualcommit int)"%cdbName) + tdSql.query("create table %s.consumeresult (ts timestamp, consumerid int, consummsgcnt bigint, consumrowcnt bigint, checkresult int)"%cdbName) + + def initConsumerInfoTable(self,cdbName='cdb'): + tdLog.info("drop consumeinfo table") + tdSql.query("drop table if exists %s.consumeinfo "%(cdbName)) + tdSql.query("create table %s.consumeinfo (ts timestamp, consumerid int, topiclist binary(1024), keylist binary(1024), expectmsgcnt bigint, ifcheckdata int, ifmanualcommit int)"%cdbName) + + def insertConsumerInfo(self,consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifmanualcommit,cdbName='cdb'): + sql = "insert into %s.consumeinfo values "%cdbName + sql += "(now, %d, '%s', '%s', %d, %d, %d)"%(consumerId, topicList, keyList, expectrowcnt, ifcheckdata, ifmanualcommit) + tdLog.info("consume info sql: %s"%sql) + tdSql.query(sql) + + def selectConsumeResult(self,expectRows,cdbName='cdb'): + resultList=[] + while 1: + tdSql.query("select * from %s.consumeresult"%cdbName) + #tdLog.info("row: %d, %l64d, %l64d"%(tdSql.getData(0, 1),tdSql.getData(0, 2),tdSql.getData(0, 3)) + if tdSql.getRows() == expectRows: + break + else: + time.sleep(5) + + for i in range(expectRows): + tdLog.info ("consume id: %d, consume msgs: %d, consume rows: %d"%(tdSql.getData(i , 1), tdSql.getData(i , 2), tdSql.getData(i , 3))) + resultList.append(tdSql.getData(i , 3)) + + return resultList + + def startTmqSimProcess(self,buildPath,cfgPath,pollDelay,dbName,showMsg=1,showRow=1,cdbName='cdb',valgrind=0): + shellCmd = 'nohup ' + if valgrind == 1: + logFile = cfgPath + '/../log/valgrind-tmq.log' + shellCmd = 'nohup valgrind --log-file=' + logFile + shellCmd += '--tool=memcheck --leak-check=full --show-reachable=no --track-origins=yes --show-leak-kinds=all --num-callers=20 -v --workaround-gcc296-bugs=yes ' + + shellCmd += buildPath + '/build/bin/tmq_sim -c ' + cfgPath + shellCmd += " -y %d -d %s -g %d -r %d -w %s "%(pollDelay, dbName, showMsg, showRow, cdbName) + shellCmd += "> /dev/null 2>&1 &" + tdLog.info(shellCmd) + os.system(shellCmd) + + def create_database(self,tsql, dbName,dropFlag=1,vgroups=4,replica=1): + if dropFlag == 1: + tsql.execute("drop database if exists %s"%(dbName)) + + tsql.execute("create database if not exists %s vgroups %d replica %d"%(dbName, vgroups, replica)) + tdLog.debug("complete to create database %s"%(dbName)) + return + + def create_stable(self,tsql, dbName,stbName): + tsql.execute("create table if not exists %s.%s (ts timestamp, c1 bigint, c2 binary(16)) tags(t1 int)"%(dbName, stbName)) + tdLog.debug("complete to create %s.%s" %(dbName, stbName)) + return + + def create_ctables(self,tsql, dbName,stbName,ctbNum): + tsql.execute("use %s" %dbName) + pre_create = "create table" + sql = pre_create + #tdLog.debug("doing create one stable %s and %d child table in %s ..." %(stbname, count ,dbname)) + for i in range(ctbNum): + sql += " %s_%d using %s tags(%d)"%(stbName,i,stbName,i+1) + if (i > 0) and (i%100 == 0): + tsql.execute(sql) + sql = pre_create + if sql != pre_create: + tsql.execute(sql) + + tdLog.debug("complete to create %d child tables in %s.%s" %(ctbNum, dbName, stbName)) + return + + def insert_data(self,tsql,dbName,stbName,ctbNum,rowsPerTbl,batchNum,startTs=0): + tdLog.debug("start to insert data ............") + tsql.execute("use %s" %dbName) + pre_insert = "insert into " + sql = pre_insert + + if startTs == 0: + t = time.time() + startTs = int(round(t * 1000)) + + #tdLog.debug("doing insert data into stable:%s rows:%d ..."%(stbName, allRows)) + rowsOfSql = 0 + for i in range(ctbNum): + sql += " %s_%d values "%(stbName,i) + for j in range(rowsPerTbl): + sql += "(%d, %d, 'tmqrow_%d') "%(startTs + j, j, j) + rowsOfSql += 1 + if (j > 0) and ((rowsOfSql == batchNum) or (j == rowsPerTbl - 1)): + tsql.execute(sql) + rowsOfSql = 0 + if j < rowsPerTbl - 1: + sql = "insert into %s_%d values " %(stbName,i) + else: + sql = "insert into " + #end sql + if sql != pre_insert: + #print("insert sql:%s"%sql) + tsql.execute(sql) + tdLog.debug("insert data ............ [OK]") + return + + def prepareEnv(self, **parameterDict): + # create new connector for my thread + tsql=self.newcur(parameterDict['cfg'], 'localhost', 6030) + + if parameterDict["actionType"] == actionType.CREATE_DATABASE: + self.create_database(tsql, parameterDict["dbName"]) + elif parameterDict["actionType"] == actionType.CREATE_STABLE: + self.create_stable(tsql, parameterDict["dbName"], parameterDict["stbName"]) + elif parameterDict["actionType"] == actionType.CREATE_CTABLE: + self.create_ctables(tsql, parameterDict["dbName"], parameterDict["stbName"], parameterDict["ctbNum"]) + elif parameterDict["actionType"] == actionType.INSERT_DATA: + self.insert_data(tsql, parameterDict["dbName"], parameterDict["stbName"], parameterDict["ctbNum"],\ + parameterDict["rowsPerTbl"],parameterDict["batchNum"]) + else: + tdLog.exit("not support's action: ", parameterDict["actionType"]) + + return + + def tmqCase1(self, cfgPath, buildPath): + tdLog.printNoPrefix("======== test case 1: ") + + self.initConsumerTable() + + # create and start thread + parameterDict = {'cfg': '', \ + 'actionType': 0, \ + 'dbName': 'db1', \ + 'dropFlag': 1, \ + 'vgroups': 4, \ + 'replica': 1, \ + 'stbName': 'stb1', \ + 'ctbNum': 10, \ + 'rowsPerTbl': 10000, \ + 'batchNum': 100, \ + 'startTs': 1640966400000} # 2022-01-01 00:00:00.000 + parameterDict['cfg'] = cfgPath + + self.create_database(tdSql, parameterDict["dbName"]) + self.create_stable(tdSql, parameterDict["dbName"], parameterDict["stbName"]) + + tdLog.info("create topics from stb1") + topicFromStb1 = 'topic_stb1' + + tdSql.execute("create topic %s as select ts, c1, c2 from %s.%s" %(topicFromStb1, parameterDict['dbName'], parameterDict['stbName'])) + consumerId = 0 + expectrowcnt = parameterDict["rowsPerTbl"] * parameterDict["ctbNum"] + topicList = topicFromStb1 + ifcheckdata = 0 + ifManualCommit = 0 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:earliest' + self.insertConsumerInfo(consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("start consume processor") + pollDelay = 100 + showMsg = 1 + showRow = 1 + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + time.sleep(5) + self.create_ctables(tdSql, parameterDict["dbName"], parameterDict["stbName"], parameterDict["ctbNum"]) + self.insert_data(tdSql,\ + parameterDict["dbName"],\ + parameterDict["stbName"],\ + parameterDict["ctbNum"],\ + parameterDict["rowsPerTbl"],\ + parameterDict["batchNum"]) + + tdLog.info("insert process end, and start to check consume result") + expectRows = 1 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt)) + tdLog.exit("tmq consume rows error!") + + tdSql.query("drop topic %s"%topicFromStb1) + + tdLog.printNoPrefix("======== test case 1 end ...... ") + + def tmqCase2(self, cfgPath, buildPath): + tdLog.printNoPrefix("======== test case 2: ") + + self.initConsumerTable() + + # create and start thread + parameterDict = {'cfg': '', \ + 'actionType': 0, \ + 'dbName': 'db2', \ + 'dropFlag': 1, \ + 'vgroups': 4, \ + 'replica': 1, \ + 'stbName': 'stb1', \ + 'ctbNum': 10, \ + 'rowsPerTbl': 10000, \ + 'batchNum': 100, \ + 'startTs': 1640966400000} # 2022-01-01 00:00:00.000 + parameterDict['cfg'] = cfgPath + + parameterDict2 = {'cfg': '', \ + 'actionType': 0, \ + 'dbName': 'db2', \ + 'dropFlag': 1, \ + 'vgroups': 4, \ + 'replica': 1, \ + 'stbName': 'stb2', \ + 'ctbNum': 10, \ + 'rowsPerTbl': 10000, \ + 'batchNum': 100, \ + 'startTs': 1640966400000} # 2022-01-01 00:00:00.000 + parameterDict2['cfg'] = cfgPath + + self.create_database(tdSql, parameterDict["dbName"]) + self.create_stable(tdSql, parameterDict["dbName"], parameterDict["stbName"]) + self.create_stable(tdSql, parameterDict2["dbName"], parameterDict2["stbName"]) + + tdLog.info("create topics from stb1") + topicFromStb1 = 'topic_stb1' + + tdSql.execute("create topic %s as select ts, c1, c2 from %s.%s" %(topicFromStb1, parameterDict['dbName'], parameterDict['stbName'])) + consumerId = 0 + expectrowcnt = parameterDict["rowsPerTbl"] * parameterDict["ctbNum"] + topicList = topicFromStb1 + ifcheckdata = 0 + ifManualCommit = 0 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:earliest' + self.insertConsumerInfo(consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("start consume processor") + pollDelay = 100 + showMsg = 1 + showRow = 1 + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("start create child tables of stb1 and stb2") + parameterDict['actionType'] = actionType.CREATE_CTABLE + parameterDict2['actionType'] = actionType.CREATE_CTABLE + + prepareEnvThread = threading.Thread(target=self.prepareEnv, kwargs=parameterDict) + prepareEnvThread.start() + prepareEnvThread2 = threading.Thread(target=self.prepareEnv, kwargs=parameterDict2) + prepareEnvThread2.start() + + prepareEnvThread.join() + prepareEnvThread2.join() + + tdLog.info("start insert data into child tables of stb1 and stb2") + parameterDict['actionType'] = actionType.INSERT_DATA + parameterDict2['actionType'] = actionType.INSERT_DATA + + prepareEnvThread = threading.Thread(target=self.prepareEnv, kwargs=parameterDict) + prepareEnvThread.start() + prepareEnvThread2 = threading.Thread(target=self.prepareEnv, kwargs=parameterDict2) + prepareEnvThread2.start() + + prepareEnvThread.join() + prepareEnvThread2.join() + + tdLog.info("insert process end, and start to check consume result") + expectRows = 1 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt)) + tdLog.exit("tmq consume rows error!") + + tdSql.query("drop topic %s"%topicFromStb1) + + tdLog.printNoPrefix("======== test case 2 end ...... ") + + def tmqCase3(self, cfgPath, buildPath): + tdLog.printNoPrefix("======== test case 3: ") + + self.initConsumerTable() + + # create and start thread + parameterDict = {'cfg': '', \ + 'actionType': 0, \ + 'dbName': 'db3', \ + 'dropFlag': 1, \ + 'vgroups': 4, \ + 'replica': 1, \ + 'stbName': 'stb1', \ + 'ctbNum': 10, \ + 'rowsPerTbl': 10000, \ + 'batchNum': 13, \ + 'startTs': 1640966400000} # 2022-01-01 00:00:00.000 + parameterDict['cfg'] = cfgPath + + self.create_database(tdSql, parameterDict["dbName"]) + self.create_stable(tdSql, parameterDict["dbName"], parameterDict["stbName"]) + self.create_ctables(tdSql, parameterDict["dbName"], parameterDict["stbName"], parameterDict["ctbNum"]) + self.insert_data(tdSql,parameterDict["dbName"],parameterDict["stbName"],parameterDict["ctbNum"],parameterDict["rowsPerTbl"],parameterDict["batchNum"]) + + tdLog.info("create topics from stb1") + topicFromStb1 = 'topic_stb1' + + tdSql.execute("create topic %s as select ts, c1, c2 from %s.%s" %(topicFromStb1, parameterDict['dbName'], parameterDict['stbName'])) + consumerId = 0 + expectrowcnt = parameterDict["rowsPerTbl"] * parameterDict["ctbNum"] + topicList = topicFromStb1 + ifcheckdata = 0 + ifManualCommit = 0 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:earliest' + self.insertConsumerInfo(consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("start consume processor") + pollDelay = 5 + showMsg = 1 + showRow = 1 + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + time.sleep(2) + tdLog.info("drop som child table of stb1") + dropTblNum = 4 + tdSql.query("drop table if exists %s.%s_9"%(parameterDict["dbName"], parameterDict["stbName"])) + tdSql.query("drop table if exists %s.%s_8"%(parameterDict["dbName"], parameterDict["stbName"])) + tdSql.query("drop table if exists %s.%s_7"%(parameterDict["dbName"], parameterDict["stbName"])) + tdSql.query("drop table if exists %s.%s_3"%(parameterDict["dbName"], parameterDict["stbName"])) + + tdLog.info("drop some child tables, then start to check consume result") + expectRows = 1 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + remaindrowcnt = parameterDict["rowsPerTbl"] * (parameterDict["ctbNum"] - dropTblNum) + + if not (totalConsumeRows < expectrowcnt and totalConsumeRows > remaindrowcnt): + tdLog.info("act consume rows: %d, expect consume rows: between %d and %d"%(totalConsumeRows, remaindrowcnt, expectrowcnt)) + tdLog.exit("tmq consume rows error!") + + tdSql.query("drop topic %s"%topicFromStb1) + + tdLog.printNoPrefix("======== test case 3 end ...... ") + + def tmqCase4(self, cfgPath, buildPath): + tdLog.printNoPrefix("======== test case 4: ") + + self.initConsumerTable() + + # create and start thread + parameterDict = {'cfg': '', \ + 'actionType': 0, \ + 'dbName': 'db4', \ + 'dropFlag': 1, \ + 'vgroups': 4, \ + 'replica': 1, \ + 'stbName': 'stb1', \ + 'ctbNum': 10, \ + 'rowsPerTbl': 10000, \ + 'batchNum': 100, \ + 'startTs': 1640966400000} # 2022-01-01 00:00:00.000 + parameterDict['cfg'] = cfgPath + + self.create_database(tdSql, parameterDict["dbName"]) + self.create_stable(tdSql, parameterDict["dbName"], parameterDict["stbName"]) + self.create_ctables(tdSql, parameterDict["dbName"], parameterDict["stbName"], parameterDict["ctbNum"]) + self.insert_data(tdSql,\ + parameterDict["dbName"],\ + parameterDict["stbName"],\ + parameterDict["ctbNum"],\ + parameterDict["rowsPerTbl"],\ + parameterDict["batchNum"]) + + tdLog.info("create topics from stb1") + topicFromStb1 = 'topic_stb1' + + tdSql.execute("create topic %s as select ts, c1, c2 from %s.%s" %(topicFromStb1, parameterDict['dbName'], parameterDict['stbName'])) + consumerId = 0 + expectrowcnt = parameterDict["rowsPerTbl"] * parameterDict["ctbNum"] + topicList = topicFromStb1 + ifcheckdata = 0 + ifManualCommit = 1 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:earliest' + self.insertConsumerInfo(consumerId, expectrowcnt/4,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("start consume processor") + pollDelay = 5 + showMsg = 1 + showRow = 1 + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("start to check consume result") + expectRows = 1 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt/4: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt/4)) + tdLog.exit("tmq consume rows error!") + + self.initConsumerInfoTable() + consumerId = 1 + self.insertConsumerInfo(consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("again start consume processor") + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("again check consume result") + expectRows = 2 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt)) + tdLog.exit("tmq consume rows error!") + + tdSql.query("drop topic %s"%topicFromStb1) + + tdLog.printNoPrefix("======== test case 4 end ...... ") + + def tmqCase5(self, cfgPath, buildPath): + tdLog.printNoPrefix("======== test case 5: ") + + self.initConsumerTable() + + # create and start thread + parameterDict = {'cfg': '', \ + 'actionType': 0, \ + 'dbName': 'db5', \ + 'dropFlag': 1, \ + 'vgroups': 4, \ + 'replica': 1, \ + 'stbName': 'stb1', \ + 'ctbNum': 10, \ + 'rowsPerTbl': 10000, \ + 'batchNum': 100, \ + 'startTs': 1640966400000} # 2022-01-01 00:00:00.000 + parameterDict['cfg'] = cfgPath + + self.create_database(tdSql, parameterDict["dbName"]) + self.create_stable(tdSql, parameterDict["dbName"], parameterDict["stbName"]) + self.create_ctables(tdSql, parameterDict["dbName"], parameterDict["stbName"], parameterDict["ctbNum"]) + self.insert_data(tdSql,\ + parameterDict["dbName"],\ + parameterDict["stbName"],\ + parameterDict["ctbNum"],\ + parameterDict["rowsPerTbl"],\ + parameterDict["batchNum"]) + + tdLog.info("create topics from stb1") + topicFromStb1 = 'topic_stb1' + + tdSql.execute("create topic %s as select ts, c1, c2 from %s.%s" %(topicFromStb1, parameterDict['dbName'], parameterDict['stbName'])) + consumerId = 0 + expectrowcnt = parameterDict["rowsPerTbl"] * parameterDict["ctbNum"] + topicList = topicFromStb1 + ifcheckdata = 0 + ifManualCommit = 0 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:earliest' + self.insertConsumerInfo(consumerId, expectrowcnt/4,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("start consume processor") + pollDelay = 5 + showMsg = 1 + showRow = 1 + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("start to check consume result") + expectRows = 1 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt/4: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt/4)) + tdLog.exit("tmq consume rows error!") + + self.initConsumerInfoTable() + consumerId = 1 + self.insertConsumerInfo(consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("again start consume processor") + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("again check consume result") + expectRows = 2 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != (expectrowcnt * (1 + 1/4)): + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt)) + tdLog.exit("tmq consume rows error!") + + tdSql.query("drop topic %s"%topicFromStb1) + + tdLog.printNoPrefix("======== test case 5 end ...... ") + + def tmqCase6(self, cfgPath, buildPath): + tdLog.printNoPrefix("======== test case 6: ") + + self.initConsumerTable() + + # create and start thread + parameterDict = {'cfg': '', \ + 'actionType': 0, \ + 'dbName': 'db6', \ + 'dropFlag': 1, \ + 'vgroups': 4, \ + 'replica': 1, \ + 'stbName': 'stb1', \ + 'ctbNum': 10, \ + 'rowsPerTbl': 10000, \ + 'batchNum': 100, \ + 'startTs': 1640966400000} # 2022-01-01 00:00:00.000 + parameterDict['cfg'] = cfgPath + + self.create_database(tdSql, parameterDict["dbName"]) + self.create_stable(tdSql, parameterDict["dbName"], parameterDict["stbName"]) + self.create_ctables(tdSql, parameterDict["dbName"], parameterDict["stbName"], parameterDict["ctbNum"]) + self.insert_data(tdSql,\ + parameterDict["dbName"],\ + parameterDict["stbName"],\ + parameterDict["ctbNum"],\ + parameterDict["rowsPerTbl"],\ + parameterDict["batchNum"]) + + tdLog.info("create topics from stb1") + topicFromStb1 = 'topic_stb1' + + tdSql.execute("create topic %s as select ts, c1, c2 from %s.%s" %(topicFromStb1, parameterDict['dbName'], parameterDict['stbName'])) + consumerId = 0 + expectrowcnt = parameterDict["rowsPerTbl"] * parameterDict["ctbNum"] + topicList = topicFromStb1 + ifcheckdata = 0 + ifManualCommit = 1 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:earliest' + self.insertConsumerInfo(consumerId, expectrowcnt/4,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("start consume processor") + pollDelay = 5 + showMsg = 1 + showRow = 1 + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("start to check consume result") + expectRows = 1 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt/4: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt/4)) + tdLog.exit("tmq consume rows error!") + + self.initConsumerInfoTable() + consumerId = 1 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:latest' + self.insertConsumerInfo(consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("again start consume processor") + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("again check consume result") + expectRows = 2 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt)) + tdLog.exit("tmq consume rows error!") + + tdSql.query("drop topic %s"%topicFromStb1) + + tdLog.printNoPrefix("======== test case 6 end ...... ") + + def tmqCase7(self, cfgPath, buildPath): + tdLog.printNoPrefix("======== test case 7: ") + + self.initConsumerTable() + + # create and start thread + parameterDict = {'cfg': '', \ + 'actionType': 0, \ + 'dbName': 'db7', \ + 'dropFlag': 1, \ + 'vgroups': 4, \ + 'replica': 1, \ + 'stbName': 'stb1', \ + 'ctbNum': 10, \ + 'rowsPerTbl': 10000, \ + 'batchNum': 100, \ + 'startTs': 1640966400000} # 2022-01-01 00:00:00.000 + parameterDict['cfg'] = cfgPath + + self.create_database(tdSql, parameterDict["dbName"]) + self.create_stable(tdSql, parameterDict["dbName"], parameterDict["stbName"]) + self.create_ctables(tdSql, parameterDict["dbName"], parameterDict["stbName"], parameterDict["ctbNum"]) + self.insert_data(tdSql,\ + parameterDict["dbName"],\ + parameterDict["stbName"],\ + parameterDict["ctbNum"],\ + parameterDict["rowsPerTbl"],\ + parameterDict["batchNum"]) + + tdLog.info("create topics from stb1") + topicFromStb1 = 'topic_stb1' + + tdSql.execute("create topic %s as select ts, c1, c2 from %s.%s" %(topicFromStb1, parameterDict['dbName'], parameterDict['stbName'])) + consumerId = 0 + expectrowcnt = parameterDict["rowsPerTbl"] * parameterDict["ctbNum"] + topicList = topicFromStb1 + ifcheckdata = 0 + ifManualCommit = 1 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:latest' + self.insertConsumerInfo(consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("start consume processor") + pollDelay = 5 + showMsg = 1 + showRow = 1 + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("start to check consume result") + expectRows = 1 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != 0: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, 0)) + tdLog.exit("tmq consume rows error!") + + self.initConsumerInfoTable() + consumerId = 1 + self.insertConsumerInfo(consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("again start consume processor") + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("again check consume result") + expectRows = 2 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != 0: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, 0)) + tdLog.exit("tmq consume rows error!") + + tdSql.query("drop topic %s"%topicFromStb1) + + tdLog.printNoPrefix("======== test case 7 end ...... ") + + def tmqCase8(self, cfgPath, buildPath): + tdLog.printNoPrefix("======== test case 8: ") + + self.initConsumerTable() + + # create and start thread + parameterDict = {'cfg': '', \ + 'actionType': 0, \ + 'dbName': 'db8', \ + 'dropFlag': 1, \ + 'vgroups': 4, \ + 'replica': 1, \ + 'stbName': 'stb1', \ + 'ctbNum': 10, \ + 'rowsPerTbl': 10000, \ + 'batchNum': 100, \ + 'startTs': 1640966400000} # 2022-01-01 00:00:00.000 + parameterDict['cfg'] = cfgPath + + self.create_database(tdSql, parameterDict["dbName"]) + self.create_stable(tdSql, parameterDict["dbName"], parameterDict["stbName"]) + self.create_ctables(tdSql, parameterDict["dbName"], parameterDict["stbName"], parameterDict["ctbNum"]) + self.insert_data(tdSql,\ + parameterDict["dbName"],\ + parameterDict["stbName"],\ + parameterDict["ctbNum"],\ + parameterDict["rowsPerTbl"],\ + parameterDict["batchNum"]) + + tdLog.info("create topics from stb1") + topicFromStb1 = 'topic_stb1' + + tdSql.execute("create topic %s as select ts, c1, c2 from %s.%s" %(topicFromStb1, parameterDict['dbName'], parameterDict['stbName'])) + consumerId = 0 + expectrowcnt = parameterDict["rowsPerTbl"] * parameterDict["ctbNum"] + topicList = topicFromStb1 + ifcheckdata = 0 + ifManualCommit = 1 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:latest' + self.insertConsumerInfo(consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("start consume 0 processor") + pollDelay = 10 + showMsg = 1 + showRow = 1 + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("start to check consume 0 result") + expectRows = 1 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != 0: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, 0)) + tdLog.exit("tmq consume rows error!") + + tdLog.info("start consume 1 processor") + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("start one new thread to insert data") + parameterDict['actionType'] = actionType.INSERT_DATA + prepareEnvThread = threading.Thread(target=self.prepareEnv, kwargs=parameterDict) + prepareEnvThread.start() + prepareEnvThread.join() + + tdLog.info("start to check consume 0 and 1 result") + expectRows = 2 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt)) + tdLog.exit("tmq consume rows error!") + + tdLog.info("start consume 2 processor") + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("start one new thread to insert data") + parameterDict['actionType'] = actionType.INSERT_DATA + prepareEnvThread = threading.Thread(target=self.prepareEnv, kwargs=parameterDict) + prepareEnvThread.start() + prepareEnvThread.join() + + tdLog.info("start to check consume 0 and 1 and 2 result") + expectRows = 3 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt*2: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt*2)) + tdLog.exit("tmq consume rows error!") + + tdSql.query("drop topic %s"%topicFromStb1) + + tdLog.printNoPrefix("======== test case 8 end ...... ") + + def tmqCase9(self, cfgPath, buildPath): + tdLog.printNoPrefix("======== test case 9: ") + + self.initConsumerTable() + + # create and start thread + parameterDict = {'cfg': '', \ + 'actionType': 0, \ + 'dbName': 'db9', \ + 'dropFlag': 1, \ + 'vgroups': 4, \ + 'replica': 1, \ + 'stbName': 'stb1', \ + 'ctbNum': 10, \ + 'rowsPerTbl': 10000, \ + 'batchNum': 100, \ + 'startTs': 1640966400000} # 2022-01-01 00:00:00.000 + parameterDict['cfg'] = cfgPath + + self.create_database(tdSql, parameterDict["dbName"]) + self.create_stable(tdSql, parameterDict["dbName"], parameterDict["stbName"]) + self.create_ctables(tdSql, parameterDict["dbName"], parameterDict["stbName"], parameterDict["ctbNum"]) + self.insert_data(tdSql,\ + parameterDict["dbName"],\ + parameterDict["stbName"],\ + parameterDict["ctbNum"],\ + parameterDict["rowsPerTbl"],\ + parameterDict["batchNum"]) + + tdLog.info("create topics from stb1") + topicFromStb1 = 'topic_stb1' + + tdSql.execute("create topic %s as select ts, c1, c2 from %s.%s" %(topicFromStb1, parameterDict['dbName'], parameterDict['stbName'])) + consumerId = 0 + expectrowcnt = parameterDict["rowsPerTbl"] * parameterDict["ctbNum"] + topicList = topicFromStb1 + ifcheckdata = 0 + ifManualCommit = 1 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:latest' + self.insertConsumerInfo(consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("start consume 0 processor") + pollDelay = 10 + showMsg = 1 + showRow = 1 + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("start to check consume 0 result") + expectRows = 1 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != 0: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, 0)) + tdLog.exit("tmq consume rows error!") + + tdLog.info("start consume 1 processor") + self.initConsumerInfoTable() + consumerId = 1 + ifManualCommit = 0 + self.insertConsumerInfo(consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifManualCommit) + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("start one new thread to insert data") + parameterDict['actionType'] = actionType.INSERT_DATA + prepareEnvThread = threading.Thread(target=self.prepareEnv, kwargs=parameterDict) + prepareEnvThread.start() + prepareEnvThread.join() + + tdLog.info("start to check consume 0 and 1 result") + expectRows = 2 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt)) + tdLog.exit("tmq consume rows error!") + + tdLog.info("start consume 2 processor") + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("start one new thread to insert data") + parameterDict['actionType'] = actionType.INSERT_DATA + prepareEnvThread = threading.Thread(target=self.prepareEnv, kwargs=parameterDict) + prepareEnvThread.start() + prepareEnvThread.join() + + tdLog.info("start to check consume 0 and 1 and 2 result") + expectRows = 3 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt*2: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt*2)) + tdLog.exit("tmq consume rows error!") + + tdSql.query("drop topic %s"%topicFromStb1) + + tdLog.printNoPrefix("======== test case 9 end ...... ") + + def tmqCase10(self, cfgPath, buildPath): + tdLog.printNoPrefix("======== test case 10: ") + + self.initConsumerTable() + + # create and start thread + parameterDict = {'cfg': '', \ + 'actionType': 0, \ + 'dbName': 'db10', \ + 'dropFlag': 1, \ + 'vgroups': 4, \ + 'replica': 1, \ + 'stbName': 'stb1', \ + 'ctbNum': 10, \ + 'rowsPerTbl': 10000, \ + 'batchNum': 100, \ + 'startTs': 1640966400000} # 2022-01-01 00:00:00.000 + parameterDict['cfg'] = cfgPath + + self.create_database(tdSql, parameterDict["dbName"]) + self.create_stable(tdSql, parameterDict["dbName"], parameterDict["stbName"]) + self.create_ctables(tdSql, parameterDict["dbName"], parameterDict["stbName"], parameterDict["ctbNum"]) + self.insert_data(tdSql,\ + parameterDict["dbName"],\ + parameterDict["stbName"],\ + parameterDict["ctbNum"],\ + parameterDict["rowsPerTbl"],\ + parameterDict["batchNum"]) + + tdLog.info("create topics from stb1") + topicFromStb1 = 'topic_stb1' + + tdSql.execute("create topic %s as select ts, c1, c2 from %s.%s" %(topicFromStb1, parameterDict['dbName'], parameterDict['stbName'])) + consumerId = 0 + expectrowcnt = parameterDict["rowsPerTbl"] * parameterDict["ctbNum"] + topicList = topicFromStb1 + ifcheckdata = 0 + ifManualCommit = 1 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:latest' + self.insertConsumerInfo(consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("start consume 0 processor") + pollDelay = 10 + showMsg = 1 + showRow = 1 + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("start to check consume 0 result") + expectRows = 1 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != 0: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, 0)) + tdLog.exit("tmq consume rows error!") + + tdLog.info("start consume 1 processor") + self.initConsumerInfoTable() + consumerId = 1 + ifManualCommit = 1 + self.insertConsumerInfo(consumerId, expectrowcnt-10000,topicList,keyList,ifcheckdata,ifManualCommit) + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("start one new thread to insert data") + parameterDict['actionType'] = actionType.INSERT_DATA + prepareEnvThread = threading.Thread(target=self.prepareEnv, kwargs=parameterDict) + prepareEnvThread.start() + prepareEnvThread.join() + + tdLog.info("start to check consume 0 and 1 result") + expectRows = 2 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt-10000: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt-10000)) + tdLog.exit("tmq consume rows error!") + + tdLog.info("start consume 2 processor") + self.initConsumerInfoTable() + consumerId = 2 + ifManualCommit = 1 + self.insertConsumerInfo(consumerId, expectrowcnt+10000,topicList,keyList,ifcheckdata,ifManualCommit) + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("start one new thread to insert data") + parameterDict['actionType'] = actionType.INSERT_DATA + prepareEnvThread = threading.Thread(target=self.prepareEnv, kwargs=parameterDict) + prepareEnvThread.start() + prepareEnvThread.join() + + tdLog.info("start to check consume 0 and 1 and 2 result") + expectRows = 3 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt*2: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt*2)) + tdLog.exit("tmq consume rows error!") + + tdSql.query("drop topic %s"%topicFromStb1) + + tdLog.printNoPrefix("======== test case 10 end ...... ") + + def tmqCase11(self, cfgPath, buildPath): + tdLog.printNoPrefix("======== test case 11: ") + + self.initConsumerTable() + + # create and start thread + parameterDict = {'cfg': '', \ + 'actionType': 0, \ + 'dbName': 'db11', \ + 'dropFlag': 1, \ + 'vgroups': 4, \ + 'replica': 1, \ + 'stbName': 'stb1', \ + 'ctbNum': 10, \ + 'rowsPerTbl': 10000, \ + 'batchNum': 100, \ + 'startTs': 1640966400000} # 2022-01-01 00:00:00.000 + parameterDict['cfg'] = cfgPath + + self.create_database(tdSql, parameterDict["dbName"]) + self.create_stable(tdSql, parameterDict["dbName"], parameterDict["stbName"]) + self.create_ctables(tdSql, parameterDict["dbName"], parameterDict["stbName"], parameterDict["ctbNum"]) + self.insert_data(tdSql,\ + parameterDict["dbName"],\ + parameterDict["stbName"],\ + parameterDict["ctbNum"],\ + parameterDict["rowsPerTbl"],\ + parameterDict["batchNum"]) + + tdLog.info("create topics from stb1") + topicFromStb1 = 'topic_stb1' + + tdSql.execute("create topic %s as select ts, c1, c2 from %s.%s" %(topicFromStb1, parameterDict['dbName'], parameterDict['stbName'])) + consumerId = 0 + expectrowcnt = parameterDict["rowsPerTbl"] * parameterDict["ctbNum"] + topicList = topicFromStb1 + ifcheckdata = 0 + ifManualCommit = 1 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:none' + self.insertConsumerInfo(consumerId, expectrowcnt/4,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("start consume processor") + pollDelay = 5 + showMsg = 1 + showRow = 1 + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("start to check consume result") + expectRows = 1 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != 0: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, 0)) + tdLog.exit("tmq consume rows error!") + + self.initConsumerInfoTable() + consumerId = 1 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:none' + self.insertConsumerInfo(consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("again start consume processor") + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("again check consume result") + expectRows = 2 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != 0: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, 0)) + tdLog.exit("tmq consume rows error!") + + tdSql.query("drop topic %s"%topicFromStb1) + + tdLog.printNoPrefix("======== test case 11 end ...... ") + + def tmqCase12(self, cfgPath, buildPath): + tdLog.printNoPrefix("======== test case 12: ") + + self.initConsumerTable() + + # create and start thread + parameterDict = {'cfg': '', \ + 'actionType': 0, \ + 'dbName': 'db12', \ + 'dropFlag': 1, \ + 'vgroups': 4, \ + 'replica': 1, \ + 'stbName': 'stb1', \ + 'ctbNum': 10, \ + 'rowsPerTbl': 10000, \ + 'batchNum': 100, \ + 'startTs': 1640966400000} # 2022-01-01 00:00:00.000 + parameterDict['cfg'] = cfgPath + + self.create_database(tdSql, parameterDict["dbName"]) + self.create_stable(tdSql, parameterDict["dbName"], parameterDict["stbName"]) + self.create_ctables(tdSql, parameterDict["dbName"], parameterDict["stbName"], parameterDict["ctbNum"]) + self.insert_data(tdSql,\ + parameterDict["dbName"],\ + parameterDict["stbName"],\ + parameterDict["ctbNum"],\ + parameterDict["rowsPerTbl"],\ + parameterDict["batchNum"]) + + tdLog.info("create topics from stb1") + topicFromStb1 = 'topic_stb1' + + tdSql.execute("create topic %s as select ts, c1, c2 from %s.%s" %(topicFromStb1, parameterDict['dbName'], parameterDict['stbName'])) + consumerId = 0 + expectrowcnt = parameterDict["rowsPerTbl"] * parameterDict["ctbNum"] + topicList = topicFromStb1 + ifcheckdata = 0 + ifManualCommit = 0 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:earliest' + self.insertConsumerInfo(consumerId, expectrowcnt/4,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("start consume processor") + pollDelay = 5 + showMsg = 1 + showRow = 1 + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("start to check consume result") + expectRows = 1 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt/4: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt/4)) + tdLog.exit("tmq consume rows error!") + + self.initConsumerInfoTable() + consumerId = 1 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:none' + self.insertConsumerInfo(consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("again start consume processor") + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("again check consume result") + expectRows = 2 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt/4: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt/4)) + tdLog.exit("tmq consume rows error!") + + tdSql.query("drop topic %s"%topicFromStb1) + + tdLog.printNoPrefix("======== test case 12 end ...... ") + + def tmqCase13(self, cfgPath, buildPath): + tdLog.printNoPrefix("======== test case 13: ") + + self.initConsumerTable() + + # create and start thread + parameterDict = {'cfg': '', \ + 'actionType': 0, \ + 'dbName': 'db13', \ + 'dropFlag': 1, \ + 'vgroups': 4, \ + 'replica': 1, \ + 'stbName': 'stb1', \ + 'ctbNum': 10, \ + 'rowsPerTbl': 10000, \ + 'batchNum': 100, \ + 'startTs': 1640966400000} # 2022-01-01 00:00:00.000 + parameterDict['cfg'] = cfgPath + + self.create_database(tdSql, parameterDict["dbName"]) + self.create_stable(tdSql, parameterDict["dbName"], parameterDict["stbName"]) + self.create_ctables(tdSql, parameterDict["dbName"], parameterDict["stbName"], parameterDict["ctbNum"]) + self.insert_data(tdSql,\ + parameterDict["dbName"],\ + parameterDict["stbName"],\ + parameterDict["ctbNum"],\ + parameterDict["rowsPerTbl"],\ + parameterDict["batchNum"]) + + tdLog.info("create topics from stb1") + topicFromStb1 = 'topic_stb1' + + tdSql.execute("create topic %s as select ts, c1, c2 from %s.%s" %(topicFromStb1, parameterDict['dbName'], parameterDict['stbName'])) + consumerId = 0 + expectrowcnt = parameterDict["rowsPerTbl"] * parameterDict["ctbNum"] + topicList = topicFromStb1 + ifcheckdata = 0 + ifManualCommit = 1 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:earliest' + self.insertConsumerInfo(consumerId, expectrowcnt/4,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("start consume processor") + pollDelay = 5 + showMsg = 1 + showRow = 1 + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("start to check consume result") + expectRows = 1 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt/4: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt/4)) + tdLog.exit("tmq consume rows error!") + + self.initConsumerInfoTable() + consumerId = 1 + ifManualCommit = 1 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:none' + self.insertConsumerInfo(consumerId, expectrowcnt/2,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("again start consume processor") + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("again check consume result") + expectRows = 2 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt*(1/2+1/4): + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt*(1/2+1/4))) + tdLog.exit("tmq consume rows error!") + + self.initConsumerInfoTable() + consumerId = 2 + ifManualCommit = 1 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:none' + self.insertConsumerInfo(consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("again start consume processor") + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("again check consume result") + expectRows = 3 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt)) + tdLog.exit("tmq consume rows error!") + + tdSql.query("drop topic %s"%topicFromStb1) + + tdLog.printNoPrefix("======== test case 13 end ...... ") + + def run(self): + tdSql.prepare() + + buildPath = self.getBuildPath() + if (buildPath == ""): + tdLog.exit("taosd not found!") + else: + tdLog.info("taosd found in %s" % buildPath) + cfgPath = buildPath + "/../sim/psim/cfg" + tdLog.info("cfgPath: %s" % cfgPath) + + self.tmqCase1(cfgPath, buildPath) + self.tmqCase2(cfgPath, buildPath) + self.tmqCase3(cfgPath, buildPath) + self.tmqCase4(cfgPath, buildPath) + self.tmqCase5(cfgPath, buildPath) + #self.tmqCase6(cfgPath, buildPath) + #self.tmqCase7(cfgPath, buildPath) + #self.tmqCase8(cfgPath, buildPath) + #self.tmqCase9(cfgPath, buildPath) + #self.tmqCase10(cfgPath, buildPath) + #self.tmqCase11(cfgPath, buildPath) + #self.tmqCase12(cfgPath, buildPath) + #self.tmqCase13(cfgPath, buildPath) + + def stop(self): + tdSql.close() + tdLog.success(f"{__file__} successfully executed") + +event = threading.Event() + +tdCases.addLinux(__file__, TDTestCase()) +tdCases.addWindows(__file__, TDTestCase()) diff --git a/tests/system-test/7-tmq/subscribeStb1.py b/tests/system-test/7-tmq/subscribeStb1.py new file mode 100644 index 0000000000000000000000000000000000000000..049b297d2df0f54e7d681c6236d942340da2d19f --- /dev/null +++ b/tests/system-test/7-tmq/subscribeStb1.py @@ -0,0 +1,1399 @@ + +import taos +import sys +import time +import socket +import os +import threading +from enum import Enum + +from util.log import * +from util.sql import * +from util.cases import * +from util.dnodes import * + +class actionType(Enum): + CREATE_DATABASE = 0 + CREATE_STABLE = 1 + CREATE_CTABLE = 2 + INSERT_DATA = 3 + +class TDTestCase: + hostname = socket.gethostname() + #rpcDebugFlagVal = '143' + #clientCfgDict = {'serverPort': '', 'firstEp': '', 'secondEp':'', 'rpcDebugFlag':'135', 'fqdn':''} + #clientCfgDict["rpcDebugFlag"] = rpcDebugFlagVal + #updatecfgDict = {'clientCfg': {}, 'serverPort': '', 'firstEp': '', 'secondEp':'', 'rpcDebugFlag':'135', 'fqdn':''} + #updatecfgDict["rpcDebugFlag"] = rpcDebugFlagVal + #print ("===================: ", updatecfgDict) + + def init(self, conn, logSql): + tdLog.debug(f"start to excute {__file__}") + #tdSql.init(conn.cursor()) + tdSql.init(conn.cursor(), logSql) # output sql.txt file + + def getBuildPath(self): + selfPath = os.path.dirname(os.path.realpath(__file__)) + + if ("community" in selfPath): + projPath = selfPath[:selfPath.find("community")] + else: + projPath = selfPath[:selfPath.find("tests")] + + for root, dirs, files in os.walk(projPath): + if ("taosd" in files): + rootRealPath = os.path.dirname(os.path.realpath(root)) + if ("packaging" not in rootRealPath): + buildPath = root[:len(root) - len("/build/bin")] + break + return buildPath + + def newcur(self,cfg,host,port): + user = "root" + password = "taosdata" + con=taos.connect(host=host, user=user, password=password, config=cfg ,port=port) + cur=con.cursor() + print(cur) + return cur + + def initConsumerTable(self,cdbName='cdb'): + tdLog.info("create consume database, and consume info table, and consume result table") + tdSql.query("create database if not exists %s vgroups 1"%(cdbName)) + tdSql.query("drop table if exists %s.consumeinfo "%(cdbName)) + tdSql.query("drop table if exists %s.consumeresult "%(cdbName)) + + tdSql.query("create table %s.consumeinfo (ts timestamp, consumerid int, topiclist binary(1024), keylist binary(1024), expectmsgcnt bigint, ifcheckdata int, ifmanualcommit int)"%cdbName) + tdSql.query("create table %s.consumeresult (ts timestamp, consumerid int, consummsgcnt bigint, consumrowcnt bigint, checkresult int)"%cdbName) + + def initConsumerInfoTable(self,cdbName='cdb'): + tdLog.info("drop consumeinfo table") + tdSql.query("drop table if exists %s.consumeinfo "%(cdbName)) + tdSql.query("create table %s.consumeinfo (ts timestamp, consumerid int, topiclist binary(1024), keylist binary(1024), expectmsgcnt bigint, ifcheckdata int, ifmanualcommit int)"%cdbName) + + def insertConsumerInfo(self,consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifmanualcommit,cdbName='cdb'): + sql = "insert into %s.consumeinfo values "%cdbName + sql += "(now, %d, '%s', '%s', %d, %d, %d)"%(consumerId, topicList, keyList, expectrowcnt, ifcheckdata, ifmanualcommit) + tdLog.info("consume info sql: %s"%sql) + tdSql.query(sql) + + def selectConsumeResult(self,expectRows,cdbName='cdb'): + resultList=[] + while 1: + tdSql.query("select * from %s.consumeresult"%cdbName) + #tdLog.info("row: %d, %l64d, %l64d"%(tdSql.getData(0, 1),tdSql.getData(0, 2),tdSql.getData(0, 3)) + if tdSql.getRows() == expectRows: + break + else: + time.sleep(5) + + for i in range(expectRows): + tdLog.info ("consume id: %d, consume msgs: %d, consume rows: %d"%(tdSql.getData(i , 1), tdSql.getData(i , 2), tdSql.getData(i , 3))) + resultList.append(tdSql.getData(i , 3)) + + return resultList + + def startTmqSimProcess(self,buildPath,cfgPath,pollDelay,dbName,showMsg=1,showRow=1,cdbName='cdb',valgrind=0): + shellCmd = 'nohup ' + if valgrind == 1: + logFile = cfgPath + '/../log/valgrind-tmq.log' + shellCmd = 'nohup valgrind --log-file=' + logFile + shellCmd += '--tool=memcheck --leak-check=full --show-reachable=no --track-origins=yes --show-leak-kinds=all --num-callers=20 -v --workaround-gcc296-bugs=yes ' + + shellCmd += buildPath + '/build/bin/tmq_sim -c ' + cfgPath + shellCmd += " -y %d -d %s -g %d -r %d -w %s "%(pollDelay, dbName, showMsg, showRow, cdbName) + shellCmd += "> /dev/null 2>&1 &" + tdLog.info(shellCmd) + os.system(shellCmd) + + def create_database(self,tsql, dbName,dropFlag=1,vgroups=4,replica=1): + if dropFlag == 1: + tsql.execute("drop database if exists %s"%(dbName)) + + tsql.execute("create database if not exists %s vgroups %d replica %d"%(dbName, vgroups, replica)) + tdLog.debug("complete to create database %s"%(dbName)) + return + + def create_stable(self,tsql, dbName,stbName): + tsql.execute("create table if not exists %s.%s (ts timestamp, c1 bigint, c2 binary(16)) tags(t1 int)"%(dbName, stbName)) + tdLog.debug("complete to create %s.%s" %(dbName, stbName)) + return + + def create_ctables(self,tsql, dbName,stbName,ctbNum): + tsql.execute("use %s" %dbName) + pre_create = "create table" + sql = pre_create + #tdLog.debug("doing create one stable %s and %d child table in %s ..." %(stbname, count ,dbname)) + for i in range(ctbNum): + sql += " %s_%d using %s tags(%d)"%(stbName,i,stbName,i+1) + if (i > 0) and (i%100 == 0): + tsql.execute(sql) + sql = pre_create + if sql != pre_create: + tsql.execute(sql) + + tdLog.debug("complete to create %d child tables in %s.%s" %(ctbNum, dbName, stbName)) + return + + def insert_data(self,tsql,dbName,stbName,ctbNum,rowsPerTbl,batchNum,startTs=0): + tdLog.debug("start to insert data ............") + tsql.execute("use %s" %dbName) + pre_insert = "insert into " + sql = pre_insert + + if startTs == 0: + t = time.time() + startTs = int(round(t * 1000)) + + #tdLog.debug("doing insert data into stable:%s rows:%d ..."%(stbName, allRows)) + rowsOfSql = 0 + for i in range(ctbNum): + sql += " %s_%d values "%(stbName,i) + for j in range(rowsPerTbl): + sql += "(%d, %d, 'tmqrow_%d') "%(startTs + j, j, j) + rowsOfSql += 1 + if (j > 0) and ((rowsOfSql == batchNum) or (j == rowsPerTbl - 1)): + tsql.execute(sql) + rowsOfSql = 0 + if j < rowsPerTbl - 1: + sql = "insert into %s_%d values " %(stbName,i) + else: + sql = "insert into " + #end sql + if sql != pre_insert: + #print("insert sql:%s"%sql) + tsql.execute(sql) + tdLog.debug("insert data ............ [OK]") + return + + def prepareEnv(self, **parameterDict): + # create new connector for my thread + tsql=self.newcur(parameterDict['cfg'], 'localhost', 6030) + + if parameterDict["actionType"] == actionType.CREATE_DATABASE: + self.create_database(tsql, parameterDict["dbName"]) + elif parameterDict["actionType"] == actionType.CREATE_STABLE: + self.create_stable(tsql, parameterDict["dbName"], parameterDict["stbName"]) + elif parameterDict["actionType"] == actionType.CREATE_CTABLE: + self.create_ctables(tsql, parameterDict["dbName"], parameterDict["stbName"], parameterDict["ctbNum"]) + elif parameterDict["actionType"] == actionType.INSERT_DATA: + self.insert_data(tsql, parameterDict["dbName"], parameterDict["stbName"], parameterDict["ctbNum"],\ + parameterDict["rowsPerTbl"],parameterDict["batchNum"]) + else: + tdLog.exit("not support's action: ", parameterDict["actionType"]) + + return + + def tmqCase1(self, cfgPath, buildPath): + tdLog.printNoPrefix("======== test case 1: ") + + self.initConsumerTable() + + # create and start thread + parameterDict = {'cfg': '', \ + 'actionType': 0, \ + 'dbName': 'db1', \ + 'dropFlag': 1, \ + 'vgroups': 4, \ + 'replica': 1, \ + 'stbName': 'stb1', \ + 'ctbNum': 10, \ + 'rowsPerTbl': 10000, \ + 'batchNum': 100, \ + 'startTs': 1640966400000} # 2022-01-01 00:00:00.000 + parameterDict['cfg'] = cfgPath + + self.create_database(tdSql, parameterDict["dbName"]) + self.create_stable(tdSql, parameterDict["dbName"], parameterDict["stbName"]) + + tdLog.info("create topics from stb1") + topicFromStb1 = 'topic_stb1' + + tdSql.execute("create topic %s as select ts, c1, c2 from %s.%s" %(topicFromStb1, parameterDict['dbName'], parameterDict['stbName'])) + consumerId = 0 + expectrowcnt = parameterDict["rowsPerTbl"] * parameterDict["ctbNum"] + topicList = topicFromStb1 + ifcheckdata = 0 + ifManualCommit = 0 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:earliest' + self.insertConsumerInfo(consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("start consume processor") + pollDelay = 100 + showMsg = 1 + showRow = 1 + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + time.sleep(5) + self.create_ctables(tdSql, parameterDict["dbName"], parameterDict["stbName"], parameterDict["ctbNum"]) + self.insert_data(tdSql,\ + parameterDict["dbName"],\ + parameterDict["stbName"],\ + parameterDict["ctbNum"],\ + parameterDict["rowsPerTbl"],\ + parameterDict["batchNum"]) + + tdLog.info("insert process end, and start to check consume result") + expectRows = 1 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt)) + tdLog.exit("tmq consume rows error!") + + tdSql.query("drop topic %s"%topicFromStb1) + + tdLog.printNoPrefix("======== test case 1 end ...... ") + + def tmqCase2(self, cfgPath, buildPath): + tdLog.printNoPrefix("======== test case 2: ") + + self.initConsumerTable() + + # create and start thread + parameterDict = {'cfg': '', \ + 'actionType': 0, \ + 'dbName': 'db2', \ + 'dropFlag': 1, \ + 'vgroups': 4, \ + 'replica': 1, \ + 'stbName': 'stb1', \ + 'ctbNum': 10, \ + 'rowsPerTbl': 10000, \ + 'batchNum': 100, \ + 'startTs': 1640966400000} # 2022-01-01 00:00:00.000 + parameterDict['cfg'] = cfgPath + + parameterDict2 = {'cfg': '', \ + 'actionType': 0, \ + 'dbName': 'db2', \ + 'dropFlag': 1, \ + 'vgroups': 4, \ + 'replica': 1, \ + 'stbName': 'stb2', \ + 'ctbNum': 10, \ + 'rowsPerTbl': 10000, \ + 'batchNum': 100, \ + 'startTs': 1640966400000} # 2022-01-01 00:00:00.000 + parameterDict2['cfg'] = cfgPath + + self.create_database(tdSql, parameterDict["dbName"]) + self.create_stable(tdSql, parameterDict["dbName"], parameterDict["stbName"]) + self.create_stable(tdSql, parameterDict2["dbName"], parameterDict2["stbName"]) + + tdLog.info("create topics from stb1") + topicFromStb1 = 'topic_stb1' + + tdSql.execute("create topic %s as select ts, c1, c2 from %s.%s" %(topicFromStb1, parameterDict['dbName'], parameterDict['stbName'])) + consumerId = 0 + expectrowcnt = parameterDict["rowsPerTbl"] * parameterDict["ctbNum"] + topicList = topicFromStb1 + ifcheckdata = 0 + ifManualCommit = 0 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:earliest' + self.insertConsumerInfo(consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("start consume processor") + pollDelay = 100 + showMsg = 1 + showRow = 1 + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("start create child tables of stb1 and stb2") + parameterDict['actionType'] = actionType.CREATE_CTABLE + parameterDict2['actionType'] = actionType.CREATE_CTABLE + + prepareEnvThread = threading.Thread(target=self.prepareEnv, kwargs=parameterDict) + prepareEnvThread.start() + prepareEnvThread2 = threading.Thread(target=self.prepareEnv, kwargs=parameterDict2) + prepareEnvThread2.start() + + prepareEnvThread.join() + prepareEnvThread2.join() + + tdLog.info("start insert data into child tables of stb1 and stb2") + parameterDict['actionType'] = actionType.INSERT_DATA + parameterDict2['actionType'] = actionType.INSERT_DATA + + prepareEnvThread = threading.Thread(target=self.prepareEnv, kwargs=parameterDict) + prepareEnvThread.start() + prepareEnvThread2 = threading.Thread(target=self.prepareEnv, kwargs=parameterDict2) + prepareEnvThread2.start() + + prepareEnvThread.join() + prepareEnvThread2.join() + + tdLog.info("insert process end, and start to check consume result") + expectRows = 1 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt)) + tdLog.exit("tmq consume rows error!") + + tdSql.query("drop topic %s"%topicFromStb1) + + tdLog.printNoPrefix("======== test case 2 end ...... ") + + def tmqCase3(self, cfgPath, buildPath): + tdLog.printNoPrefix("======== test case 3: ") + + self.initConsumerTable() + + # create and start thread + parameterDict = {'cfg': '', \ + 'actionType': 0, \ + 'dbName': 'db3', \ + 'dropFlag': 1, \ + 'vgroups': 4, \ + 'replica': 1, \ + 'stbName': 'stb1', \ + 'ctbNum': 10, \ + 'rowsPerTbl': 10000, \ + 'batchNum': 13, \ + 'startTs': 1640966400000} # 2022-01-01 00:00:00.000 + parameterDict['cfg'] = cfgPath + + self.create_database(tdSql, parameterDict["dbName"]) + self.create_stable(tdSql, parameterDict["dbName"], parameterDict["stbName"]) + self.create_ctables(tdSql, parameterDict["dbName"], parameterDict["stbName"], parameterDict["ctbNum"]) + self.insert_data(tdSql,parameterDict["dbName"],parameterDict["stbName"],parameterDict["ctbNum"],parameterDict["rowsPerTbl"],parameterDict["batchNum"]) + + tdLog.info("create topics from stb1") + topicFromStb1 = 'topic_stb1' + + tdSql.execute("create topic %s as select ts, c1, c2 from %s.%s" %(topicFromStb1, parameterDict['dbName'], parameterDict['stbName'])) + consumerId = 0 + expectrowcnt = parameterDict["rowsPerTbl"] * parameterDict["ctbNum"] + topicList = topicFromStb1 + ifcheckdata = 0 + ifManualCommit = 0 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:earliest' + self.insertConsumerInfo(consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("start consume processor") + pollDelay = 5 + showMsg = 1 + showRow = 1 + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + time.sleep(2) + tdLog.info("drop som child table of stb1") + dropTblNum = 4 + tdSql.query("drop table if exists %s.%s_9"%(parameterDict["dbName"], parameterDict["stbName"])) + tdSql.query("drop table if exists %s.%s_8"%(parameterDict["dbName"], parameterDict["stbName"])) + tdSql.query("drop table if exists %s.%s_7"%(parameterDict["dbName"], parameterDict["stbName"])) + tdSql.query("drop table if exists %s.%s_3"%(parameterDict["dbName"], parameterDict["stbName"])) + + tdLog.info("drop some child tables, then start to check consume result") + expectRows = 1 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + remaindrowcnt = parameterDict["rowsPerTbl"] * (parameterDict["ctbNum"] - dropTblNum) + + if not (totalConsumeRows < expectrowcnt and totalConsumeRows > remaindrowcnt): + tdLog.info("act consume rows: %d, expect consume rows: between %d and %d"%(totalConsumeRows, remaindrowcnt, expectrowcnt)) + tdLog.exit("tmq consume rows error!") + + tdSql.query("drop topic %s"%topicFromStb1) + + tdLog.printNoPrefix("======== test case 3 end ...... ") + + def tmqCase4(self, cfgPath, buildPath): + tdLog.printNoPrefix("======== test case 4: ") + + self.initConsumerTable() + + # create and start thread + parameterDict = {'cfg': '', \ + 'actionType': 0, \ + 'dbName': 'db4', \ + 'dropFlag': 1, \ + 'vgroups': 4, \ + 'replica': 1, \ + 'stbName': 'stb1', \ + 'ctbNum': 10, \ + 'rowsPerTbl': 10000, \ + 'batchNum': 100, \ + 'startTs': 1640966400000} # 2022-01-01 00:00:00.000 + parameterDict['cfg'] = cfgPath + + self.create_database(tdSql, parameterDict["dbName"]) + self.create_stable(tdSql, parameterDict["dbName"], parameterDict["stbName"]) + self.create_ctables(tdSql, parameterDict["dbName"], parameterDict["stbName"], parameterDict["ctbNum"]) + self.insert_data(tdSql,\ + parameterDict["dbName"],\ + parameterDict["stbName"],\ + parameterDict["ctbNum"],\ + parameterDict["rowsPerTbl"],\ + parameterDict["batchNum"]) + + tdLog.info("create topics from stb1") + topicFromStb1 = 'topic_stb1' + + tdSql.execute("create topic %s as select ts, c1, c2 from %s.%s" %(topicFromStb1, parameterDict['dbName'], parameterDict['stbName'])) + consumerId = 0 + expectrowcnt = parameterDict["rowsPerTbl"] * parameterDict["ctbNum"] + topicList = topicFromStb1 + ifcheckdata = 0 + ifManualCommit = 1 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:earliest' + self.insertConsumerInfo(consumerId, expectrowcnt/4,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("start consume processor") + pollDelay = 5 + showMsg = 1 + showRow = 1 + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("start to check consume result") + expectRows = 1 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt/4: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt/4)) + tdLog.exit("tmq consume rows error!") + + self.initConsumerInfoTable() + consumerId = 1 + self.insertConsumerInfo(consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("again start consume processor") + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("again check consume result") + expectRows = 2 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt)) + tdLog.exit("tmq consume rows error!") + + tdSql.query("drop topic %s"%topicFromStb1) + + tdLog.printNoPrefix("======== test case 4 end ...... ") + + def tmqCase5(self, cfgPath, buildPath): + tdLog.printNoPrefix("======== test case 5: ") + + self.initConsumerTable() + + # create and start thread + parameterDict = {'cfg': '', \ + 'actionType': 0, \ + 'dbName': 'db5', \ + 'dropFlag': 1, \ + 'vgroups': 4, \ + 'replica': 1, \ + 'stbName': 'stb1', \ + 'ctbNum': 10, \ + 'rowsPerTbl': 10000, \ + 'batchNum': 100, \ + 'startTs': 1640966400000} # 2022-01-01 00:00:00.000 + parameterDict['cfg'] = cfgPath + + self.create_database(tdSql, parameterDict["dbName"]) + self.create_stable(tdSql, parameterDict["dbName"], parameterDict["stbName"]) + self.create_ctables(tdSql, parameterDict["dbName"], parameterDict["stbName"], parameterDict["ctbNum"]) + self.insert_data(tdSql,\ + parameterDict["dbName"],\ + parameterDict["stbName"],\ + parameterDict["ctbNum"],\ + parameterDict["rowsPerTbl"],\ + parameterDict["batchNum"]) + + tdLog.info("create topics from stb1") + topicFromStb1 = 'topic_stb1' + + tdSql.execute("create topic %s as select ts, c1, c2 from %s.%s" %(topicFromStb1, parameterDict['dbName'], parameterDict['stbName'])) + consumerId = 0 + expectrowcnt = parameterDict["rowsPerTbl"] * parameterDict["ctbNum"] + topicList = topicFromStb1 + ifcheckdata = 0 + ifManualCommit = 0 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:earliest' + self.insertConsumerInfo(consumerId, expectrowcnt/4,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("start consume processor") + pollDelay = 5 + showMsg = 1 + showRow = 1 + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("start to check consume result") + expectRows = 1 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt/4: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt/4)) + tdLog.exit("tmq consume rows error!") + + self.initConsumerInfoTable() + consumerId = 1 + self.insertConsumerInfo(consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("again start consume processor") + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("again check consume result") + expectRows = 2 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != (expectrowcnt * (1 + 1/4)): + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt)) + tdLog.exit("tmq consume rows error!") + + tdSql.query("drop topic %s"%topicFromStb1) + + tdLog.printNoPrefix("======== test case 5 end ...... ") + + def tmqCase6(self, cfgPath, buildPath): + tdLog.printNoPrefix("======== test case 6: ") + + self.initConsumerTable() + + # create and start thread + parameterDict = {'cfg': '', \ + 'actionType': 0, \ + 'dbName': 'db6', \ + 'dropFlag': 1, \ + 'vgroups': 4, \ + 'replica': 1, \ + 'stbName': 'stb1', \ + 'ctbNum': 10, \ + 'rowsPerTbl': 10000, \ + 'batchNum': 100, \ + 'startTs': 1640966400000} # 2022-01-01 00:00:00.000 + parameterDict['cfg'] = cfgPath + + self.create_database(tdSql, parameterDict["dbName"]) + self.create_stable(tdSql, parameterDict["dbName"], parameterDict["stbName"]) + self.create_ctables(tdSql, parameterDict["dbName"], parameterDict["stbName"], parameterDict["ctbNum"]) + self.insert_data(tdSql,\ + parameterDict["dbName"],\ + parameterDict["stbName"],\ + parameterDict["ctbNum"],\ + parameterDict["rowsPerTbl"],\ + parameterDict["batchNum"]) + + tdLog.info("create topics from stb1") + topicFromStb1 = 'topic_stb1' + + tdSql.execute("create topic %s as select ts, c1, c2 from %s.%s" %(topicFromStb1, parameterDict['dbName'], parameterDict['stbName'])) + consumerId = 0 + expectrowcnt = parameterDict["rowsPerTbl"] * parameterDict["ctbNum"] + topicList = topicFromStb1 + ifcheckdata = 0 + ifManualCommit = 1 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:earliest' + self.insertConsumerInfo(consumerId, expectrowcnt/4,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("start consume processor") + pollDelay = 5 + showMsg = 1 + showRow = 1 + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("start to check consume result") + expectRows = 1 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt/4: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt/4)) + tdLog.exit("tmq consume rows error!") + + self.initConsumerInfoTable() + consumerId = 1 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:latest' + self.insertConsumerInfo(consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("again start consume processor") + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("again check consume result") + expectRows = 2 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt)) + tdLog.exit("tmq consume rows error!") + + tdSql.query("drop topic %s"%topicFromStb1) + + tdLog.printNoPrefix("======== test case 6 end ...... ") + + def tmqCase7(self, cfgPath, buildPath): + tdLog.printNoPrefix("======== test case 7: ") + + self.initConsumerTable() + + # create and start thread + parameterDict = {'cfg': '', \ + 'actionType': 0, \ + 'dbName': 'db7', \ + 'dropFlag': 1, \ + 'vgroups': 4, \ + 'replica': 1, \ + 'stbName': 'stb1', \ + 'ctbNum': 10, \ + 'rowsPerTbl': 10000, \ + 'batchNum': 100, \ + 'startTs': 1640966400000} # 2022-01-01 00:00:00.000 + parameterDict['cfg'] = cfgPath + + self.create_database(tdSql, parameterDict["dbName"]) + self.create_stable(tdSql, parameterDict["dbName"], parameterDict["stbName"]) + self.create_ctables(tdSql, parameterDict["dbName"], parameterDict["stbName"], parameterDict["ctbNum"]) + self.insert_data(tdSql,\ + parameterDict["dbName"],\ + parameterDict["stbName"],\ + parameterDict["ctbNum"],\ + parameterDict["rowsPerTbl"],\ + parameterDict["batchNum"]) + + tdLog.info("create topics from stb1") + topicFromStb1 = 'topic_stb1' + + tdSql.execute("create topic %s as select ts, c1, c2 from %s.%s" %(topicFromStb1, parameterDict['dbName'], parameterDict['stbName'])) + consumerId = 0 + expectrowcnt = parameterDict["rowsPerTbl"] * parameterDict["ctbNum"] + topicList = topicFromStb1 + ifcheckdata = 0 + ifManualCommit = 1 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:latest' + self.insertConsumerInfo(consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("start consume processor") + pollDelay = 5 + showMsg = 1 + showRow = 1 + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("start to check consume result") + expectRows = 1 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != 0: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, 0)) + tdLog.exit("tmq consume rows error!") + + self.initConsumerInfoTable() + consumerId = 1 + self.insertConsumerInfo(consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("again start consume processor") + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("again check consume result") + expectRows = 2 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != 0: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, 0)) + tdLog.exit("tmq consume rows error!") + + tdSql.query("drop topic %s"%topicFromStb1) + + tdLog.printNoPrefix("======== test case 7 end ...... ") + + def tmqCase8(self, cfgPath, buildPath): + tdLog.printNoPrefix("======== test case 8: ") + + self.initConsumerTable() + + # create and start thread + parameterDict = {'cfg': '', \ + 'actionType': 0, \ + 'dbName': 'db8', \ + 'dropFlag': 1, \ + 'vgroups': 4, \ + 'replica': 1, \ + 'stbName': 'stb1', \ + 'ctbNum': 10, \ + 'rowsPerTbl': 10000, \ + 'batchNum': 100, \ + 'startTs': 1640966400000} # 2022-01-01 00:00:00.000 + parameterDict['cfg'] = cfgPath + + self.create_database(tdSql, parameterDict["dbName"]) + self.create_stable(tdSql, parameterDict["dbName"], parameterDict["stbName"]) + self.create_ctables(tdSql, parameterDict["dbName"], parameterDict["stbName"], parameterDict["ctbNum"]) + self.insert_data(tdSql,\ + parameterDict["dbName"],\ + parameterDict["stbName"],\ + parameterDict["ctbNum"],\ + parameterDict["rowsPerTbl"],\ + parameterDict["batchNum"]) + + tdLog.info("create topics from stb1") + topicFromStb1 = 'topic_stb1' + + tdSql.execute("create topic %s as select ts, c1, c2 from %s.%s" %(topicFromStb1, parameterDict['dbName'], parameterDict['stbName'])) + consumerId = 0 + expectrowcnt = parameterDict["rowsPerTbl"] * parameterDict["ctbNum"] + topicList = topicFromStb1 + ifcheckdata = 0 + ifManualCommit = 1 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:latest' + self.insertConsumerInfo(consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("start consume 0 processor") + pollDelay = 10 + showMsg = 1 + showRow = 1 + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("start to check consume 0 result") + expectRows = 1 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != 0: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, 0)) + tdLog.exit("tmq consume rows error!") + + tdLog.info("start consume 1 processor") + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("start one new thread to insert data") + parameterDict['actionType'] = actionType.INSERT_DATA + prepareEnvThread = threading.Thread(target=self.prepareEnv, kwargs=parameterDict) + prepareEnvThread.start() + prepareEnvThread.join() + + tdLog.info("start to check consume 0 and 1 result") + expectRows = 2 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt)) + tdLog.exit("tmq consume rows error!") + + tdLog.info("start consume 2 processor") + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("start one new thread to insert data") + parameterDict['actionType'] = actionType.INSERT_DATA + prepareEnvThread = threading.Thread(target=self.prepareEnv, kwargs=parameterDict) + prepareEnvThread.start() + prepareEnvThread.join() + + tdLog.info("start to check consume 0 and 1 and 2 result") + expectRows = 3 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt*2: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt*2)) + tdLog.exit("tmq consume rows error!") + + tdSql.query("drop topic %s"%topicFromStb1) + + tdLog.printNoPrefix("======== test case 8 end ...... ") + + def tmqCase9(self, cfgPath, buildPath): + tdLog.printNoPrefix("======== test case 9: ") + + self.initConsumerTable() + + # create and start thread + parameterDict = {'cfg': '', \ + 'actionType': 0, \ + 'dbName': 'db9', \ + 'dropFlag': 1, \ + 'vgroups': 4, \ + 'replica': 1, \ + 'stbName': 'stb1', \ + 'ctbNum': 10, \ + 'rowsPerTbl': 10000, \ + 'batchNum': 100, \ + 'startTs': 1640966400000} # 2022-01-01 00:00:00.000 + parameterDict['cfg'] = cfgPath + + self.create_database(tdSql, parameterDict["dbName"]) + self.create_stable(tdSql, parameterDict["dbName"], parameterDict["stbName"]) + self.create_ctables(tdSql, parameterDict["dbName"], parameterDict["stbName"], parameterDict["ctbNum"]) + self.insert_data(tdSql,\ + parameterDict["dbName"],\ + parameterDict["stbName"],\ + parameterDict["ctbNum"],\ + parameterDict["rowsPerTbl"],\ + parameterDict["batchNum"]) + + tdLog.info("create topics from stb1") + topicFromStb1 = 'topic_stb1' + + tdSql.execute("create topic %s as select ts, c1, c2 from %s.%s" %(topicFromStb1, parameterDict['dbName'], parameterDict['stbName'])) + consumerId = 0 + expectrowcnt = parameterDict["rowsPerTbl"] * parameterDict["ctbNum"] + topicList = topicFromStb1 + ifcheckdata = 0 + ifManualCommit = 1 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:latest' + self.insertConsumerInfo(consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("start consume 0 processor") + pollDelay = 10 + showMsg = 1 + showRow = 1 + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("start to check consume 0 result") + expectRows = 1 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != 0: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, 0)) + tdLog.exit("tmq consume rows error!") + + tdLog.info("start consume 1 processor") + self.initConsumerInfoTable() + consumerId = 1 + ifManualCommit = 0 + self.insertConsumerInfo(consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifManualCommit) + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("start one new thread to insert data") + parameterDict['actionType'] = actionType.INSERT_DATA + prepareEnvThread = threading.Thread(target=self.prepareEnv, kwargs=parameterDict) + prepareEnvThread.start() + prepareEnvThread.join() + + tdLog.info("start to check consume 0 and 1 result") + expectRows = 2 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt)) + tdLog.exit("tmq consume rows error!") + + tdLog.info("start consume 2 processor") + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("start one new thread to insert data") + parameterDict['actionType'] = actionType.INSERT_DATA + prepareEnvThread = threading.Thread(target=self.prepareEnv, kwargs=parameterDict) + prepareEnvThread.start() + prepareEnvThread.join() + + tdLog.info("start to check consume 0 and 1 and 2 result") + expectRows = 3 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt*2: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt*2)) + tdLog.exit("tmq consume rows error!") + + tdSql.query("drop topic %s"%topicFromStb1) + + tdLog.printNoPrefix("======== test case 9 end ...... ") + + def tmqCase10(self, cfgPath, buildPath): + tdLog.printNoPrefix("======== test case 10: ") + + self.initConsumerTable() + + # create and start thread + parameterDict = {'cfg': '', \ + 'actionType': 0, \ + 'dbName': 'db10', \ + 'dropFlag': 1, \ + 'vgroups': 4, \ + 'replica': 1, \ + 'stbName': 'stb1', \ + 'ctbNum': 10, \ + 'rowsPerTbl': 10000, \ + 'batchNum': 100, \ + 'startTs': 1640966400000} # 2022-01-01 00:00:00.000 + parameterDict['cfg'] = cfgPath + + self.create_database(tdSql, parameterDict["dbName"]) + self.create_stable(tdSql, parameterDict["dbName"], parameterDict["stbName"]) + self.create_ctables(tdSql, parameterDict["dbName"], parameterDict["stbName"], parameterDict["ctbNum"]) + self.insert_data(tdSql,\ + parameterDict["dbName"],\ + parameterDict["stbName"],\ + parameterDict["ctbNum"],\ + parameterDict["rowsPerTbl"],\ + parameterDict["batchNum"]) + + tdLog.info("create topics from stb1") + topicFromStb1 = 'topic_stb1' + + tdSql.execute("create topic %s as select ts, c1, c2 from %s.%s" %(topicFromStb1, parameterDict['dbName'], parameterDict['stbName'])) + consumerId = 0 + expectrowcnt = parameterDict["rowsPerTbl"] * parameterDict["ctbNum"] + topicList = topicFromStb1 + ifcheckdata = 0 + ifManualCommit = 1 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:latest' + self.insertConsumerInfo(consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("start consume 0 processor") + pollDelay = 10 + showMsg = 1 + showRow = 1 + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("start to check consume 0 result") + expectRows = 1 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != 0: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, 0)) + tdLog.exit("tmq consume rows error!") + + tdLog.info("start consume 1 processor") + self.initConsumerInfoTable() + consumerId = 1 + ifManualCommit = 1 + self.insertConsumerInfo(consumerId, expectrowcnt-10000,topicList,keyList,ifcheckdata,ifManualCommit) + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("start one new thread to insert data") + parameterDict['actionType'] = actionType.INSERT_DATA + prepareEnvThread = threading.Thread(target=self.prepareEnv, kwargs=parameterDict) + prepareEnvThread.start() + prepareEnvThread.join() + + tdLog.info("start to check consume 0 and 1 result") + expectRows = 2 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt-10000: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt-10000)) + tdLog.exit("tmq consume rows error!") + + tdLog.info("start consume 2 processor") + self.initConsumerInfoTable() + consumerId = 2 + ifManualCommit = 1 + self.insertConsumerInfo(consumerId, expectrowcnt+10000,topicList,keyList,ifcheckdata,ifManualCommit) + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("start one new thread to insert data") + parameterDict['actionType'] = actionType.INSERT_DATA + prepareEnvThread = threading.Thread(target=self.prepareEnv, kwargs=parameterDict) + prepareEnvThread.start() + prepareEnvThread.join() + + tdLog.info("start to check consume 0 and 1 and 2 result") + expectRows = 3 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt*2: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt*2)) + tdLog.exit("tmq consume rows error!") + + tdSql.query("drop topic %s"%topicFromStb1) + + tdLog.printNoPrefix("======== test case 10 end ...... ") + + def tmqCase11(self, cfgPath, buildPath): + tdLog.printNoPrefix("======== test case 11: ") + + self.initConsumerTable() + + # create and start thread + parameterDict = {'cfg': '', \ + 'actionType': 0, \ + 'dbName': 'db11', \ + 'dropFlag': 1, \ + 'vgroups': 4, \ + 'replica': 1, \ + 'stbName': 'stb1', \ + 'ctbNum': 10, \ + 'rowsPerTbl': 10000, \ + 'batchNum': 100, \ + 'startTs': 1640966400000} # 2022-01-01 00:00:00.000 + parameterDict['cfg'] = cfgPath + + self.create_database(tdSql, parameterDict["dbName"]) + self.create_stable(tdSql, parameterDict["dbName"], parameterDict["stbName"]) + self.create_ctables(tdSql, parameterDict["dbName"], parameterDict["stbName"], parameterDict["ctbNum"]) + self.insert_data(tdSql,\ + parameterDict["dbName"],\ + parameterDict["stbName"],\ + parameterDict["ctbNum"],\ + parameterDict["rowsPerTbl"],\ + parameterDict["batchNum"]) + + tdLog.info("create topics from stb1") + topicFromStb1 = 'topic_stb1' + + tdSql.execute("create topic %s as select ts, c1, c2 from %s.%s" %(topicFromStb1, parameterDict['dbName'], parameterDict['stbName'])) + consumerId = 0 + expectrowcnt = parameterDict["rowsPerTbl"] * parameterDict["ctbNum"] + topicList = topicFromStb1 + ifcheckdata = 0 + ifManualCommit = 1 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:none' + self.insertConsumerInfo(consumerId, expectrowcnt/4,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("start consume processor") + pollDelay = 5 + showMsg = 1 + showRow = 1 + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("start to check consume result") + expectRows = 1 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != 0: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, 0)) + tdLog.exit("tmq consume rows error!") + + self.initConsumerInfoTable() + consumerId = 1 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:none' + self.insertConsumerInfo(consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("again start consume processor") + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("again check consume result") + expectRows = 2 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != 0: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, 0)) + tdLog.exit("tmq consume rows error!") + + tdSql.query("drop topic %s"%topicFromStb1) + + tdLog.printNoPrefix("======== test case 11 end ...... ") + + def tmqCase12(self, cfgPath, buildPath): + tdLog.printNoPrefix("======== test case 12: ") + + self.initConsumerTable() + + # create and start thread + parameterDict = {'cfg': '', \ + 'actionType': 0, \ + 'dbName': 'db12', \ + 'dropFlag': 1, \ + 'vgroups': 4, \ + 'replica': 1, \ + 'stbName': 'stb1', \ + 'ctbNum': 10, \ + 'rowsPerTbl': 10000, \ + 'batchNum': 100, \ + 'startTs': 1640966400000} # 2022-01-01 00:00:00.000 + parameterDict['cfg'] = cfgPath + + self.create_database(tdSql, parameterDict["dbName"]) + self.create_stable(tdSql, parameterDict["dbName"], parameterDict["stbName"]) + self.create_ctables(tdSql, parameterDict["dbName"], parameterDict["stbName"], parameterDict["ctbNum"]) + self.insert_data(tdSql,\ + parameterDict["dbName"],\ + parameterDict["stbName"],\ + parameterDict["ctbNum"],\ + parameterDict["rowsPerTbl"],\ + parameterDict["batchNum"]) + + tdLog.info("create topics from stb1") + topicFromStb1 = 'topic_stb1' + + tdSql.execute("create topic %s as select ts, c1, c2 from %s.%s" %(topicFromStb1, parameterDict['dbName'], parameterDict['stbName'])) + consumerId = 0 + expectrowcnt = parameterDict["rowsPerTbl"] * parameterDict["ctbNum"] + topicList = topicFromStb1 + ifcheckdata = 0 + ifManualCommit = 0 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:earliest' + self.insertConsumerInfo(consumerId, expectrowcnt/4,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("start consume processor") + pollDelay = 5 + showMsg = 1 + showRow = 1 + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("start to check consume result") + expectRows = 1 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt/4: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt/4)) + tdLog.exit("tmq consume rows error!") + + self.initConsumerInfoTable() + consumerId = 1 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:none' + self.insertConsumerInfo(consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("again start consume processor") + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("again check consume result") + expectRows = 2 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt/4: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt/4)) + tdLog.exit("tmq consume rows error!") + + tdSql.query("drop topic %s"%topicFromStb1) + + tdLog.printNoPrefix("======== test case 12 end ...... ") + + def tmqCase13(self, cfgPath, buildPath): + tdLog.printNoPrefix("======== test case 13: ") + + self.initConsumerTable() + + # create and start thread + parameterDict = {'cfg': '', \ + 'actionType': 0, \ + 'dbName': 'db13', \ + 'dropFlag': 1, \ + 'vgroups': 4, \ + 'replica': 1, \ + 'stbName': 'stb1', \ + 'ctbNum': 10, \ + 'rowsPerTbl': 10000, \ + 'batchNum': 100, \ + 'startTs': 1640966400000} # 2022-01-01 00:00:00.000 + parameterDict['cfg'] = cfgPath + + self.create_database(tdSql, parameterDict["dbName"]) + self.create_stable(tdSql, parameterDict["dbName"], parameterDict["stbName"]) + self.create_ctables(tdSql, parameterDict["dbName"], parameterDict["stbName"], parameterDict["ctbNum"]) + self.insert_data(tdSql,\ + parameterDict["dbName"],\ + parameterDict["stbName"],\ + parameterDict["ctbNum"],\ + parameterDict["rowsPerTbl"],\ + parameterDict["batchNum"]) + + tdLog.info("create topics from stb1") + topicFromStb1 = 'topic_stb1' + + tdSql.execute("create topic %s as select ts, c1, c2 from %s.%s" %(topicFromStb1, parameterDict['dbName'], parameterDict['stbName'])) + consumerId = 0 + expectrowcnt = parameterDict["rowsPerTbl"] * parameterDict["ctbNum"] + topicList = topicFromStb1 + ifcheckdata = 0 + ifManualCommit = 1 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:earliest' + self.insertConsumerInfo(consumerId, expectrowcnt/4,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("start consume processor") + pollDelay = 5 + showMsg = 1 + showRow = 1 + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("start to check consume result") + expectRows = 1 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt/4: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt/4)) + tdLog.exit("tmq consume rows error!") + + self.initConsumerInfoTable() + consumerId = 1 + ifManualCommit = 1 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:none' + self.insertConsumerInfo(consumerId, expectrowcnt/2,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("again start consume processor") + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("again check consume result") + expectRows = 2 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt*(1/2+1/4): + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt*(1/2+1/4))) + tdLog.exit("tmq consume rows error!") + + self.initConsumerInfoTable() + consumerId = 2 + ifManualCommit = 1 + keyList = 'group.id:cgrp1,\ + enable.auto.commit:false,\ + auto.commit.interval.ms:6000,\ + auto.offset.reset:none' + self.insertConsumerInfo(consumerId, expectrowcnt,topicList,keyList,ifcheckdata,ifManualCommit) + + tdLog.info("again start consume processor") + self.startTmqSimProcess(buildPath,cfgPath,pollDelay,parameterDict["dbName"],showMsg, showRow) + + tdLog.info("again check consume result") + expectRows = 3 + resultList = self.selectConsumeResult(expectRows) + totalConsumeRows = 0 + for i in range(expectRows): + totalConsumeRows += resultList[i] + + if totalConsumeRows != expectrowcnt: + tdLog.info("act consume rows: %d, expect consume rows: %d"%(totalConsumeRows, expectrowcnt)) + tdLog.exit("tmq consume rows error!") + + tdSql.query("drop topic %s"%topicFromStb1) + + tdLog.printNoPrefix("======== test case 13 end ...... ") + + def run(self): + tdSql.prepare() + + buildPath = self.getBuildPath() + if (buildPath == ""): + tdLog.exit("taosd not found!") + else: + tdLog.info("taosd found in %s" % buildPath) + cfgPath = buildPath + "/../sim/psim/cfg" + tdLog.info("cfgPath: %s" % cfgPath) + + #self.tmqCase1(cfgPath, buildPath) + #self.tmqCase2(cfgPath, buildPath) + #self.tmqCase3(cfgPath, buildPath) + #self.tmqCase4(cfgPath, buildPath) + #self.tmqCase5(cfgPath, buildPath) + self.tmqCase6(cfgPath, buildPath) + self.tmqCase7(cfgPath, buildPath) + self.tmqCase8(cfgPath, buildPath) + self.tmqCase9(cfgPath, buildPath) + self.tmqCase10(cfgPath, buildPath) + self.tmqCase11(cfgPath, buildPath) + self.tmqCase12(cfgPath, buildPath) + self.tmqCase13(cfgPath, buildPath) + + def stop(self): + tdSql.close() + tdLog.success(f"{__file__} successfully executed") + +event = threading.Event() + +tdCases.addLinux(__file__, TDTestCase()) +tdCases.addWindows(__file__, TDTestCase()) diff --git a/tests/system-test/fulltest.sh b/tests/system-test/fulltest.sh index 5363ec227f25483b8829f17be3c969368dec6176..a6dfd5129fd9beed3ef7e649713105854470cb5e 100755 --- a/tests/system-test/fulltest.sh +++ b/tests/system-test/fulltest.sh @@ -10,6 +10,7 @@ python3 ./test.py -f 0-others/taosdMonitor.py python3 ./test.py -f 0-others/udfTest.py python3 ./test.py -f 0-others/user_control.py +python3 ./test.py -f 0-others/fsync.py #python3 ./test.py -f 2-query/between.py python3 ./test.py -f 2-query/distinct.py @@ -63,3 +64,5 @@ python3 ./test.py -f 2-query/nestedQuery.py python3 ./test.py -f 7-tmq/basic5.py python3 ./test.py -f 7-tmq/subscribeDb.py python3 ./test.py -f 7-tmq/subscribeDb1.py +python3 ./test.py -f 7-tmq/subscribeStb.py +python3 ./test.py -f 7-tmq/subscribeStb1.py diff --git a/tests/test/c/CMakeLists.txt b/tests/test/c/CMakeLists.txt index 41e93823797bf66853491430b81317349b1b46a6..964f9fee4abbdc244b83f50390c2b660be6b476c 100644 --- a/tests/test/c/CMakeLists.txt +++ b/tests/test/c/CMakeLists.txt @@ -1,6 +1,6 @@ -add_executable(create_table create_table.c) add_executable(tmq_demo tmqDemo.c) add_executable(tmq_sim tmqSim.c) +add_executable(create_table createTable.c) target_link_libraries( create_table PUBLIC taos_static @@ -22,3 +22,21 @@ target_link_libraries( PUBLIC common PUBLIC os ) + +if(NOT TD_WINDOWS) + add_executable(sdbDump sdbDump.c) + target_link_libraries( + sdbDump + PUBLIC dnode + PUBLIC mnode + PUBLIC sdb + PUBLIC os + ) + target_include_directories( + sdbDump + PUBLIC "${TD_SOURCE_DIR}/include/dnode/mnode" + PRIVATE "${TD_SOURCE_DIR}/source/dnode/mnode/impl/inc" + PRIVATE "${TD_SOURCE_DIR}/source/dnode/mnode/sdb/inc" + PRIVATE "${TD_SOURCE_DIR}/source/dnode/mgmt/node_mgmt/inc" + ) +ENDIF () \ No newline at end of file diff --git a/tests/test/c/create_table.c b/tests/test/c/createTable.c similarity index 99% rename from tests/test/c/create_table.c rename to tests/test/c/createTable.c index c53ae0136cb2da84fd35634d0011a18a36d57c67..6a0f8e244ebcce79ac60a353df5d5343d58b4b34 100644 --- a/tests/test/c/create_table.c +++ b/tests/test/c/createTable.c @@ -261,7 +261,7 @@ void *threadFunc(void *param) { int64_t startTs = taosGetTimestampUs(); TAOS_RES *pRes = taos_query(con, qstr); code = taos_errno(pRes); - if ((code != 0) && (code != TSDB_CODE_RPC_AUTH_REQUIRED)) { + if (code != 0) { pError("failed to insert %s_t%" PRId64 ", reason:%s", stbName, t, tstrerror(code)); } taos_free_result(pRes); diff --git a/tests/test/c/sdbDump.c b/tests/test/c/sdbDump.c new file mode 100644 index 0000000000000000000000000000000000000000..2bc60f777ca99460afc4306d3fa8b13cb25e9003 --- /dev/null +++ b/tests/test/c/sdbDump.c @@ -0,0 +1,434 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#define _DEFAULT_SOURCE +#include "dmMgmt.h" +#include "mndInt.h" +#include "sdbInt.h" +#include "tconfig.h" +#include "tjson.h" + +#define TMP_SDB_DATA_DIR "/tmp/dumpsdb" +#define TMP_SDB_MNODE_DIR "/tmp/dumpsdb/mnode" +#define TMP_SDB_FILE "/tmp/dumpsdb/mnode/data/sdb.data" +#define TMP_SDB_PATH "/tmp/dumpsdb/mnode/data" + +void reportStartup(const char *name, const char *desc) {} + +void sendRsp(SRpcMsg *pMsg) { rpcFreeCont(pMsg->pCont); } + +int32_t sendReq(const SEpSet *pEpSet, SRpcMsg *pMsg) { + terrno = TSDB_CODE_INVALID_PTR; + return -1; +} + +char *i642str(int64_t val) { + static char str[24] = {0}; + snprintf(str, sizeof(str), "%" PRId64, val); + return str; +} + +void dumpFunc(SSdb *pSdb, SJson *json) {} + +void dumpDb(SSdb *pSdb, SJson *json) { + void *pIter = NULL; + SJson *items = tjsonCreateObject(); + tjsonAddItemToObject(json, "dbs", items); + + while (1) { + SDbObj *pObj = NULL; + pIter = sdbFetch(pSdb, SDB_DB, pIter, (void **)&pObj); + if (pIter == NULL) break; + + SJson *item = tjsonCreateObject(); + tjsonAddItemToObject(items, "db", item); + + tjsonAddStringToObject(item, "name", pObj->name); + tjsonAddStringToObject(item, "acct", pObj->acct); + tjsonAddStringToObject(item, "createUser", pObj->createUser); + tjsonAddStringToObject(item, "createdTime", i642str(pObj->createdTime)); + tjsonAddStringToObject(item, "updateTime", i642str(pObj->updateTime)); + tjsonAddStringToObject(item, "uid", i642str(pObj->uid)); + tjsonAddIntegerToObject(item, "cfgVersion", pObj->cfgVersion); + tjsonAddIntegerToObject(item, "vgVersion", pObj->vgVersion); + tjsonAddIntegerToObject(item, "numOfVgroups", pObj->cfg.numOfVgroups); + tjsonAddIntegerToObject(item, "numOfStables", pObj->cfg.numOfStables); + tjsonAddIntegerToObject(item, "buffer", pObj->cfg.buffer); + tjsonAddIntegerToObject(item, "pageSize", pObj->cfg.pageSize); + tjsonAddIntegerToObject(item, "pages", pObj->cfg.pages); + tjsonAddIntegerToObject(item, "daysPerFile", pObj->cfg.daysPerFile); + tjsonAddIntegerToObject(item, "daysToKeep0", pObj->cfg.daysToKeep0); + tjsonAddIntegerToObject(item, "daysToKeep1", pObj->cfg.daysToKeep1); + tjsonAddIntegerToObject(item, "daysToKeep2", pObj->cfg.daysToKeep2); + tjsonAddIntegerToObject(item, "minRows", pObj->cfg.minRows); + tjsonAddIntegerToObject(item, "maxRows", pObj->cfg.maxRows); + tjsonAddIntegerToObject(item, "fsyncPeriod", pObj->cfg.fsyncPeriod); + tjsonAddIntegerToObject(item, "walLevel", pObj->cfg.walLevel); + tjsonAddIntegerToObject(item, "precision", pObj->cfg.precision); + tjsonAddIntegerToObject(item, "compression", pObj->cfg.compression); + tjsonAddIntegerToObject(item, "replications", pObj->cfg.replications); + tjsonAddIntegerToObject(item, "strict", pObj->cfg.strict); + tjsonAddIntegerToObject(item, "cacheLastRow", pObj->cfg.cacheLastRow); + tjsonAddIntegerToObject(item, "hashMethod", pObj->cfg.hashMethod); + tjsonAddIntegerToObject(item, "numOfRetensions", pObj->cfg.numOfRetensions); + + sdbRelease(pSdb, pObj); + } +} + +void dumpStb(SSdb *pSdb, SJson *json) { + void *pIter = NULL; + SJson *items = tjsonCreateObject(); + tjsonAddItemToObject(json, "stbs", items); + + while (1) { + SStbObj *pObj = NULL; + pIter = sdbFetch(pSdb, SDB_STB, pIter, (void **)&pObj); + if (pIter == NULL) break; + + SJson *item = tjsonCreateObject(); + tjsonAddItemToObject(items, "stb", item); + + tjsonAddStringToObject(item, "name", pObj->name); + tjsonAddStringToObject(item, "db", pObj->db); + tjsonAddStringToObject(item, "createdTime", i642str(pObj->createdTime)); + tjsonAddStringToObject(item, "updateTime", i642str(pObj->updateTime)); + tjsonAddStringToObject(item, "uid", i642str(pObj->uid)); + tjsonAddStringToObject(item, "dbUid", i642str(pObj->dbUid)); + tjsonAddIntegerToObject(item, "version", pObj->version); + tjsonAddIntegerToObject(item, "tagVer", pObj->tagVer); + tjsonAddIntegerToObject(item, "colVer", pObj->colVer); + tjsonAddIntegerToObject(item, "nextColId", pObj->nextColId); + tjsonAddIntegerToObject(item, "xFilesFactor", pObj->xFilesFactor * 10000); + tjsonAddIntegerToObject(item, "delay", pObj->delay); + tjsonAddIntegerToObject(item, "ttl", pObj->ttl); + tjsonAddIntegerToObject(item, "numOfColumns", pObj->numOfColumns); + tjsonAddIntegerToObject(item, "numOfTags", pObj->numOfTags); + tjsonAddIntegerToObject(item, "commentLen", pObj->commentLen); + tjsonAddIntegerToObject(item, "ast1Len", pObj->ast1Len); + tjsonAddIntegerToObject(item, "ast2Len", pObj->ast2Len); + + sdbRelease(pSdb, pObj); + } +} + +void dumpSma(SSdb *pSdb, SJson *json) {} + +void dumpVgroup(SSdb *pSdb, SJson *json) {} + +void dumpTopic(SSdb *pSdb, SJson *json) {} + +void dumpConsumber(SSdb *pSdb, SJson *json) {} + +void dumpSubscribe(SSdb *pSdb, SJson *json) {} + +void dumpOffset(SSdb *pSdb, SJson *json) {} + +void dumpStream(SSdb *pSdb, SJson *json) {} + +void dumpAcct(SSdb *pSdb, SJson *json) { + void *pIter = NULL; + SJson *items = tjsonCreateObject(); + tjsonAddItemToObject(json, "accts", items); + + while (1) { + SAcctObj *pObj = NULL; + pIter = sdbFetch(pSdb, SDB_ACCT, pIter, (void **)&pObj); + if (pIter == NULL) break; + + SJson *item = tjsonCreateObject(); + tjsonAddItemToObject(items, "acct", item); + + tjsonAddStringToObject(item, "acct", pObj->acct); + tjsonAddStringToObject(item, "createdTime", i642str(pObj->createdTime)); + tjsonAddStringToObject(item, "updateTime", i642str(pObj->updateTime)); + tjsonAddIntegerToObject(item, "acctId", pObj->acctId); + + sdbRelease(pSdb, pObj); + } +} + +void dumpAuth(SSdb *pSdb, SJson *json) {} + +void dumpUser(SSdb *pSdb, SJson *json) { + void *pIter = NULL; + SJson *items = tjsonCreateObject(); + tjsonAddItemToObject(json, "users", items); + + while (1) { + SUserObj *pObj = NULL; + pIter = sdbFetch(pSdb, SDB_USER, pIter, (void **)&pObj); + if (pIter == NULL) break; + + SJson *item = tjsonCreateObject(); + tjsonAddItemToObject(items, "user", item); + + tjsonAddStringToObject(item, "name", pObj->user); + tjsonAddStringToObject(item, "pass", pObj->pass); + tjsonAddStringToObject(item, "acct", pObj->acct); + tjsonAddStringToObject(item, "createdTime", i642str(pObj->createdTime)); + tjsonAddStringToObject(item, "updateTime", i642str(pObj->updateTime)); + tjsonAddIntegerToObject(item, "superUser", pObj->superUser); + tjsonAddIntegerToObject(item, "authVersion", pObj->authVersion); + tjsonAddIntegerToObject(item, "numOfReadDbs", taosHashGetSize(pObj->readDbs)); + tjsonAddIntegerToObject(item, "numOfWriteDbs", taosHashGetSize(pObj->writeDbs)); + + sdbRelease(pSdb, pObj); + } +} + +void dumpDnode(SSdb *pSdb, SJson *json) { + void *pIter = NULL; + SJson *items = tjsonCreateObject(); + tjsonAddItemToObject(json, "dnodes", items); + + while (1) { + SDnodeObj *pObj = NULL; + pIter = sdbFetch(pSdb, SDB_DNODE, pIter, (void **)&pObj); + if (pIter == NULL) break; + + SJson *item = tjsonCreateObject(); + tjsonAddItemToObject(items, "dnode", item); + + tjsonAddIntegerToObject(item, "id", pObj->id); + tjsonAddStringToObject(item, "createdTime", i642str(pObj->createdTime)); + tjsonAddStringToObject(item, "updateTime", i642str(pObj->updateTime)); + tjsonAddIntegerToObject(item, "port", pObj->port); + tjsonAddStringToObject(item, "fqdn", pObj->fqdn); + + sdbRelease(pSdb, pObj); + } +} + +void dumpBnode(SSdb *pSdb, SJson *json) {} + +void dumpSnode(SSdb *pSdb, SJson *json) {} + +void dumpQnode(SSdb *pSdb, SJson *json) {} + +void dumpMnode(SSdb *pSdb, SJson *json) { + void *pIter = NULL; + SJson *items = tjsonCreateObject(); + tjsonAddItemToObject(json, "mnodes", items); + + while (1) { + SMnodeObj *pObj = NULL; + pIter = sdbFetch(pSdb, SDB_MNODE, pIter, (void **)&pObj); + if (pIter == NULL) break; + + SJson *item = tjsonCreateObject(); + tjsonAddItemToObject(items, "mnode", item); + + tjsonAddIntegerToObject(item, "id", pObj->id); + tjsonAddStringToObject(item, "createdTime", i642str(pObj->createdTime)); + tjsonAddStringToObject(item, "updateTime", i642str(pObj->updateTime)); + + sdbRelease(pSdb, pObj); + } +} + +void dumpCluster(SSdb *pSdb, SJson *json) { + void *pIter = NULL; + SJson *items = tjsonCreateObject(); + tjsonAddItemToObject(json, "clusters", items); + + while (1) { + SClusterObj *pObj = NULL; + pIter = sdbFetch(pSdb, SDB_CLUSTER, pIter, (void **)&pObj); + if (pIter == NULL) break; + + SJson *item = tjsonCreateObject(); + tjsonAddItemToObject(items, "cluster", item); + + tjsonAddStringToObject(item, "id", i642str(pObj->id)); + tjsonAddStringToObject(item, "createdTime", i642str(pObj->createdTime)); + tjsonAddStringToObject(item, "updateTime", i642str(pObj->updateTime)); + tjsonAddStringToObject(item, "name", pObj->name); + + sdbRelease(pSdb, pObj); + } +} + +void dumpTrans(SSdb *pSdb, SJson *json) { + void *pIter = NULL; + SJson *items = tjsonCreateObject(); + tjsonAddItemToObject(json, "transactions", items); + + while (1) { + STrans *pObj = NULL; + pIter = sdbFetch(pSdb, SDB_TRANS, pIter, (void **)&pObj); + if (pIter == NULL) break; + + SJson *item = tjsonCreateObject(); + tjsonAddItemToObject(items, "trans", item); + + tjsonAddIntegerToObject(item, "id", pObj->id); + tjsonAddIntegerToObject(item, "stage", pObj->stage); + tjsonAddIntegerToObject(item, "policy", pObj->policy); + tjsonAddIntegerToObject(item, "type", pObj->type); + tjsonAddStringToObject(item, "createdTime", i642str(pObj->createdTime)); + tjsonAddStringToObject(item, "dbUid", i642str(pObj->dbUid)); + tjsonAddStringToObject(item, "dbname", pObj->dbname); + tjsonAddIntegerToObject(item, "redoLogNum", taosArrayGetSize(pObj->redoLogs)); + tjsonAddIntegerToObject(item, "undoLogNum", taosArrayGetSize(pObj->undoLogs)); + tjsonAddIntegerToObject(item, "commitLogNum", taosArrayGetSize(pObj->commitLogs)); + tjsonAddIntegerToObject(item, "redoActionNum", taosArrayGetSize(pObj->redoActions)); + tjsonAddIntegerToObject(item, "undoActionNum", taosArrayGetSize(pObj->undoActions)); + + sdbRelease(pSdb, pObj); + } +} + +void dumpHeader(SSdb *pSdb, SJson *json) { + tjsonAddIntegerToObject(json, "sver", 1); + tjsonAddStringToObject(json, "curVer", i642str(pSdb->curVer)); + + SJson *maxIdsJson = tjsonCreateObject(); + tjsonAddItemToObject(json, "maxIds", maxIdsJson); + for (int32_t i = 0; i < SDB_MAX; ++i) { + int64_t maxId = 0; + if (i < SDB_MAX) { + maxId = pSdb->maxId[i]; + } + tjsonAddStringToObject(maxIdsJson, sdbTableName(i), i642str(maxId)); + } + + SJson *tableVersJson = tjsonCreateObject(); + tjsonAddItemToObject(json, "tableVers", tableVersJson); + for (int32_t i = 0; i < SDB_MAX; ++i) { + int64_t tableVer = 0; + if (i < SDB_MAX) { + tableVer = pSdb->tableVer[i]; + } + tjsonAddStringToObject(tableVersJson, sdbTableName(i), i642str(tableVer)); + } +} + +int32_t dumpSdb() { + SMsgCb msgCb = {0}; + msgCb.reportStartupFp = reportStartup; + msgCb.sendReqFp = sendReq; + msgCb.sendRspFp = sendRsp; + msgCb.mgmt = (SMgmtWrapper *)(&msgCb); // hack + tmsgSetDefault(&msgCb); + walInit(); + + SMnodeOpt opt = {.msgCb = msgCb}; + SMnode *pMnode = mndOpen(TMP_SDB_MNODE_DIR, &opt); + if (pMnode == NULL) return -1; + + SSdb *pSdb = pMnode->pSdb; + SJson *json = tjsonCreateObject(); + dumpHeader(pSdb, json); + dumpFunc(pSdb, json); + dumpDb(pSdb, json); + dumpStb(pSdb, json); + dumpSma(pSdb, json); + dumpVgroup(pSdb, json); + dumpTopic(pSdb, json); + dumpConsumber(pSdb, json); + dumpSubscribe(pSdb, json); + dumpOffset(pSdb, json); + dumpStream(pSdb, json); + dumpAcct(pSdb, json); + dumpAuth(pSdb, json); + dumpUser(pSdb, json); + dumpDnode(pSdb, json); + dumpBnode(pSdb, json); + dumpSnode(pSdb, json); + dumpQnode(pSdb, json); + dumpMnode(pSdb, json); + dumpCluster(pSdb, json); + dumpTrans(pSdb, json); + + char *pCont = tjsonToString(json); + int32_t contLen = strlen(pCont); + char file[] = "sdb.json"; + TdFilePtr pFile = taosOpenFile(file, TD_FILE_CREATE | TD_FILE_WRITE | TD_FILE_TRUNC); + if (pFile == NULL) { + terrno = TAOS_SYSTEM_ERROR(errno); + dError("failed to write %s since %s", file, terrstr()); + return -1; + } + taosWriteFile(pFile, pCont, contLen); + taosWriteFile(pFile, "\n", 1); + taosFsyncFile(pFile); + taosCloseFile(&pFile); + tjsonDelete(json); + taosMemoryFree(pCont); + taosRemoveDir(TMP_SDB_DATA_DIR); + return 0; +} + +int32_t parseArgs(int32_t argc, char *argv[]) { + char file[PATH_MAX] = {0}; + + for (int32_t i = 1; i < argc; ++i) { + if (strcmp(argv[i], "-c") == 0) { + if (i < argc - 1) { + if (strlen(argv[++i]) >= PATH_MAX) { + printf("config file path overflow"); + return -1; + } + tstrncpy(configDir, argv[i], PATH_MAX); + } else { + printf("'-c' requires a parameter, default is %s\n", configDir); + return -1; + } + } else if (strcmp(argv[i], "-f") == 0) { + if (i < argc - 1) { + if (strlen(argv[++i]) >= PATH_MAX) { + printf("file path overflow"); + return -1; + } + tstrncpy(file, argv[i], PATH_MAX); + } else { + printf("'-f' requires a parameter, default is %s\n", configDir); + return -1; + } + } else { + printf("-c Configuration directory. \n"); + printf("-f Input sdb.data file. \n"); + return -1; + } + } + + if (taosCreateLog("dumplog", 1, configDir, NULL, NULL, NULL, NULL, 1) != 0) { + printf("failed to dump since init log error\n"); + return -1; + } + + if (taosInitCfg(configDir, NULL, NULL, NULL, NULL, 0) != 0) { + printf("failed to dump since read config error\n"); + return -1; + } + + if (file[0] == 0) { + snprintf(file, PATH_MAX, "%s/mnode/data/sdb.data", tsDataDir); + } + + strcpy(tsDataDir, TMP_SDB_DATA_DIR); + taosMulMkDir(TMP_SDB_PATH); + taosCopyFile(file, TMP_SDB_FILE); + return 0; +} + +int32_t main(int32_t argc, char *argv[]) { + if (parseArgs(argc, argv) != 0) { + return -1; + } + + return dumpSdb(); +} diff --git a/tests/test/c/tmqDemo.c b/tests/test/c/tmqDemo.c index 96d7741897d2dd3d888585a4ed5a12be53f5c72f..5a972e071a5e28708cfb561ef1015d72ba830219 100644 --- a/tests/test/c/tmqDemo.c +++ b/tests/test/c/tmqDemo.c @@ -233,7 +233,6 @@ int64_t getDirectorySize(char* dir) { int queryDB(TAOS* taos, char* command) { TAOS_RES* pRes = taos_query(taos, command); int code = taos_errno(pRes); - // if ((code != 0) && (code != TSDB_CODE_RPC_AUTH_REQUIRED)) { if (code != 0) { pError("failed to reason:%s, sql: %s", tstrerror(code), command); taos_free_result(pRes); diff --git a/tests/test/c/tmqSim.c b/tests/test/c/tmqSim.c index 8322c1a60ce2a8d400a164aa36c87877172c4f43..e0f58d052f612fca7ad0a257d8e137d8d4a5a1f6 100644 --- a/tests/test/c/tmqSim.c +++ b/tests/test/c/tmqSim.c @@ -98,16 +98,28 @@ static void printHelp() { exit(EXIT_SUCCESS); } +char* getCurrentTimeString(char* timeString) { + time_t tTime = taosGetTimestampSec(); + struct tm tm = *taosLocalTime(&tTime, NULL); + sprintf(timeString, "%d-%02d-%02d %02d:%02d:%02d", + tm.tm_year + 1900, + tm.tm_mon + 1, + tm.tm_mday, + tm.tm_hour, + tm.tm_min, + tm.tm_sec); + + return timeString; +} + + void initLogFile() { - time_t now; - struct tm curTime; - char filename[256]; - - now = taosTime(NULL); - taosLocalTime(&now, &curTime); - sprintf(filename, "%s/../log/tmqlog_%04d-%02d-%02d %02d-%02d-%02d.txt", configDir, curTime.tm_year + 1900, - curTime.tm_mon + 1, curTime.tm_mday, curTime.tm_hour, curTime.tm_min, curTime.tm_sec); - // sprintf(filename, "%s/../log/tmqlog.txt", configDir); + char filename[256]; + char tmpString[128]; + + sprintf(filename,"%s/../log/tmqlog_%s.txt", configDir, getCurrentTimeString(tmpString)); + //sprintf(filename, "%s/../log/tmqlog.txt", configDir); + TdFilePtr pFile = taosOpenFile(filename, TD_FILE_TEXT | TD_FILE_WRITE | TD_FILE_TRUNC | TD_FILE_STREAM); if (NULL == pFile) { fprintf(stderr, "Failed to open %s for save result\n", filename); @@ -117,9 +129,6 @@ void initLogFile() { } void saveConfigToLogFile() { - time_t tTime = taosGetTimestampSec(); - struct tm tm = *taosLocalTime(&tTime, NULL); - taosFprintfFile(g_fp, "###################################################################\n"); taosFprintfFile(g_fp, "# configDir: %s\n", configDir); taosFprintfFile(g_fp, "# dbName: %s\n", g_stConfInfo.dbName); @@ -144,10 +153,11 @@ void saveConfigToLogFile() { taosFprintfFile(g_fp, "%s:%s, ", g_stConfInfo.stThreads[i].key[k], g_stConfInfo.stThreads[i].value[k]); } taosFprintfFile(g_fp, "\n"); + taosFprintfFile(g_fp, " expect rows: %d\n", g_stConfInfo.stThreads[i].expectMsgCnt); } - taosFprintfFile(g_fp, "# Test time: %d-%02d-%02d %02d:%02d:%02d\n", tm.tm_year + 1900, tm.tm_mon + 1, - tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); + char tmpString[128]; + taosFprintfFile(g_fp, "# Test time: %s\n", getCurrentTimeString(tmpString)); taosFprintfFile(g_fp, "###################################################################\n"); } @@ -248,7 +258,6 @@ static int32_t msg_process(TAOS_RES* msg, int64_t msgIndex, int32_t threadLable) int queryDB(TAOS* taos, char* command) { TAOS_RES* pRes = taos_query(taos, command); int code = taos_errno(pRes); - // if ((code != 0) && (code != TSDB_CODE_RPC_AUTH_REQUIRED)) { if (code != 0) { pError("failed to reason:%s, sql: %s", tstrerror(code), command); taos_free_result(pRes); @@ -316,10 +325,8 @@ int32_t saveConsumeResult(SThreadInfo* pInfo) { sprintf(sqlStr, "insert into %s.consumeresult values (now, %d, %" PRId64 ", %" PRId64 ", %d)", g_stConfInfo.cdbName, pInfo->consumerId, pInfo->consumeMsgCnt, pInfo->consumeRowCnt, pInfo->checkresult); - time_t tTime = taosGetTimestampSec(); - struct tm tm = *taosLocalTime(&tTime, NULL); - taosFprintfFile(g_fp, "# save result: %d-%02d-%02d %02d:%02d:%02d, sql: %s\n", tm.tm_year + 1900, tm.tm_mon + 1, - tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, sqlStr); + char tmpString[128]; + taosFprintfFile(g_fp, "%s, consume id %d result: %s\n", getCurrentTimeString(tmpString), pInfo->consumerId ,sqlStr); TAOS_RES* pRes = taos_query(pConn, sqlStr); if (taos_errno(pRes) != 0) { @@ -339,6 +346,9 @@ void loop_consume(SThreadInfo* pInfo) { int64_t totalMsgs = 0; int64_t totalRows = 0; + char tmpString[128]; + taosFprintfFile(g_fp, "%s consumer id %d start to loop pull msg\n", getCurrentTimeString(tmpString), pInfo->consumerId); + while (running) { TAOS_RES* tmqMsg = tmq_consumer_poll(pInfo->tmq, g_stConfInfo.consumeDelay * 1000); if (tmqMsg) { @@ -351,11 +361,13 @@ void loop_consume(SThreadInfo* pInfo) { totalMsgs++; if (totalRows >= pInfo->expectMsgCnt) { - taosFprintfFile(g_fp, "==== totalRows >= pInfo->expectMsgCnt, so break\n"); + char tmpString[128]; + taosFprintfFile(g_fp, "%s over than expect rows, so break consume\n", getCurrentTimeString(tmpString)); break; } } else { - taosFprintfFile(g_fp, "==== delay over time, so break\n"); + char tmpString[128]; + taosFprintfFile(g_fp, "%s no poll more msg when time over, break consume\n", getCurrentTimeString(tmpString)); break; } } diff --git a/tests/tsim/src/simSystem.c b/tests/tsim/src/simSystem.c index 969332ba5f032efcc2098c3bcc215456b8e6a509..1c751f290a2a23c1496cdbe01f90a3cb9985c860 100644 --- a/tests/tsim/src/simSystem.c +++ b/tests/tsim/src/simSystem.c @@ -98,6 +98,8 @@ SScript *simProcessCallOver(SScript *script) { return NULL; } + if (simScriptPos == -1) return NULL; + return simScriptList[simScriptPos]; } else { simDebug("script:%s, is stopped", script->fileName); diff --git a/tools/shell/src/shellEngine.c b/tools/shell/src/shellEngine.c index 21474c316f48a5cb8b06b9a3e8a24916133ce06b..a866488d3ad2d239c47b3279f506e755737b88bf 100644 --- a/tools/shell/src/shellEngine.c +++ b/tools/shell/src/shellEngine.c @@ -315,6 +315,7 @@ void shellDumpFieldToFile(TdFilePtr pFile, const char *val, TAOS_FIELD *field, i break; case TSDB_DATA_TYPE_BINARY: case TSDB_DATA_TYPE_NCHAR: + case TSDB_DATA_TYPE_JSON: memcpy(buf, val, length); buf[length] = 0; taosFprintfFile(pFile, "\'%s\'", buf); @@ -384,19 +385,25 @@ void shellPrintNChar(const char *str, int32_t length, int32_t width) { while (pos < length) { TdWchar wc; int32_t bytes = taosMbToWchar(&wc, str + pos, MB_CUR_MAX); - if (bytes == 0) { + if (bytes <= 0) { break; } - pos += bytes; - if (pos > length) { + + if (pos + bytes > length) { break; } - + int w = 0; #ifdef WINDOWS - int32_t w = bytes; + w = bytes; #else - int32_t w = taosWcharWidth(wc); + if(*(str + pos) == '\t' || *(str + pos) == '\n' || *(str + pos) == '\r'){ + w = bytes; + }else{ + w = taosWcharWidth(wc); + } #endif + pos += bytes; + if (w <= 0) { continue; } @@ -496,6 +503,7 @@ void shellPrintField(const char *val, TAOS_FIELD *field, int32_t width, int32_t break; case TSDB_DATA_TYPE_BINARY: case TSDB_DATA_TYPE_NCHAR: + case TSDB_DATA_TYPE_JSON: shellPrintNChar(val, length, width); break; case TSDB_DATA_TYPE_TIMESTAMP: @@ -604,7 +612,6 @@ int32_t shellCalcColWidth(TAOS_FIELD *field, int32_t precision) { case TSDB_DATA_TYPE_DOUBLE: return TMAX(25, width); - case TSDB_DATA_TYPE_JSON: case TSDB_DATA_TYPE_BINARY: if (field->bytes > shell.args.displayWidth) { return TMAX(shell.args.displayWidth, width); @@ -612,7 +619,8 @@ int32_t shellCalcColWidth(TAOS_FIELD *field, int32_t precision) { return TMAX(field->bytes, width); } - case TSDB_DATA_TYPE_NCHAR: { + case TSDB_DATA_TYPE_NCHAR: + case TSDB_DATA_TYPE_JSON: { int16_t bytes = field->bytes * TSDB_NCHAR_SIZE; if (bytes > shell.args.displayWidth) { return TMAX(shell.args.displayWidth, width); @@ -734,6 +742,7 @@ void shellReadHistory() { int32_t read_size = 0; while ((read_size = taosGetLineFile(pFile, &line)) != -1) { line[read_size - 1] = '\0'; + taosMemoryFree(pHistory->hist[pHistory->hend]); pHistory->hist[pHistory->hend] = strdup(line); pHistory->hend = (pHistory->hend + 1) % SHELL_MAX_HISTORY_SIZE; @@ -755,7 +764,8 @@ void shellWriteHistory() { for (int32_t i = pHistory->hstart; i != pHistory->hend;) { if (pHistory->hist[i] != NULL) { taosFprintfFile(pFile, "%s\n", pHistory->hist[i]); - taosMemoryFreeClear(pHistory->hist[i]); + taosMemoryFree(pHistory->hist[i]); + pHistory->hist[i] = NULL; } i = (i + 1) % SHELL_MAX_HISTORY_SIZE; } @@ -763,6 +773,16 @@ void shellWriteHistory() { taosCloseFile(&pFile); } +void shellCleanupHistory() { + SShellHistory *pHistory = &shell.history; + for (int32_t i = 0; i < SHELL_MAX_HISTORY_SIZE; ++i) { + if (pHistory->hist[i] != NULL) { + taosMemoryFree(pHistory->hist[i]); + pHistory->hist[i] = NULL; + } + } +} + void shellPrintError(TAOS_RES *tres, int64_t st) { int64_t et = taosGetTimestampUs(); fprintf(stderr, "\nDB error: %s (%.6fs)\n", taos_errstr(tres), (et - st) / 1E6); @@ -963,6 +983,7 @@ int32_t shellExecute() { taos_close(shell.conn); shellWriteHistory(); + shellCleanupHistory(); return 0; } @@ -988,5 +1009,6 @@ int32_t shellExecute() { taosThreadClear(&shell.pid); } + shellCleanupHistory(); return 0; } diff --git a/tools/shell/src/shellNettest.c b/tools/shell/src/shellNettest.c index 566846de1a6d304045bbbe1aea8ce2afb790ab3e..d25d07d83139558b5d5426422884b8cbf3adb4cc 100644 --- a/tools/shell/src/shellNettest.c +++ b/tools/shell/src/shellNettest.c @@ -31,9 +31,6 @@ static void shellWorkAsClient() { rpcInit.connType = TAOS_CONN_CLIENT; rpcInit.idleTime = tsShellActivityTimer * 1000; rpcInit.user = "_dnd"; - rpcInit.ckey = "_key"; - rpcInit.spi = 1; - rpcInit.secret = pass; clientRpc = rpcOpen(&rpcInit); if (clientRpc == NULL) { diff --git a/tools/taos-tools b/tools/taos-tools index 0aad27d725f4ee6b18daf1db0c07d933aed16eea..2c4a1c83322b983881aea93ec2b51e7df826125a 160000 --- a/tools/taos-tools +++ b/tools/taos-tools @@ -1 +1 @@ -Subproject commit 0aad27d725f4ee6b18daf1db0c07d933aed16eea +Subproject commit 2c4a1c83322b983881aea93ec2b51e7df826125a