From 309d9860284fcee8c4f4119d08771505defdc878 Mon Sep 17 00:00:00 2001 From: danielclow <106956386+danielclow@users.noreply.github.com> Date: Mon, 22 Aug 2022 12:44:24 +0800 Subject: [PATCH 01/76] doc: english version of config --- docs/en/14-reference/12-config/index.md | 1321 +++++++++-------------- 1 file changed, 510 insertions(+), 811 deletions(-) diff --git a/docs/en/14-reference/12-config/index.md b/docs/en/14-reference/12-config/index.md index b6b535429b..532e16de5c 100644 --- a/docs/en/14-reference/12-config/index.md +++ b/docs/en/14-reference/12-config/index.md @@ -1,16 +1,13 @@ --- -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 ``` @@ -24,8 +21,6 @@ taosd -C 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 ``` @@ -36,6 +31,11 @@ taos --dump-config # Configuration Parameters +:::note +The parameters described in this document by the effect that they have on the system. + +::: + :::note `taosd` needs to be restarted for the parameters changed in the configuration file to take effect. @@ -45,19 +45,19 @@ taos --dump-config ### firstEp -| Attribute | Description | -| ------------- | ---------------------------------------------------------------------------------------------------- | -| Applicable | Server and Client | +| 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 | +| Default | localhost:6030 | ### secondEp -| Attribute | Description | -| ------------- | ---------------------------------------------------------------------------------------------------------------------- | -| Applicable | Server and Client | +| 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 | +| Default | None | ### fqdn @@ -65,36 +65,28 @@ taos --dump-config | ------------- | ------------------------------------------------------------------------ | | 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 host | -| Note | It should be within 96 bytes | +| Default Value | The first hostname configured for the host | +| Note | It should be within 96 bytes | | ### serverPort -| Attribute | Description | -| ------------- | ------------------------------------------------------------------------------------------------------------------------------- | -| Applicable | Server Only | +| 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 13 continuous ports, both TCP and UDP, starting with the port specified by `serverPort`. You should ensure, in your firewall rules, that these ports are kept open. Below table describes the ports used by TDengine in details. - +- Ensure that your firewall rules do not block TCP port 6042 on any host in the cluster. 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 | 6030 | Communication between client and server. In a multi-node cluster, communication between nodes. serverPort | | 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 | +| TCP | 6043 | Service Port of TaosKeeper | The parameter of TaosKeeper | +| TCP | 6044 | Data access port for StatsD | Configurable through taosAdapter parameters. +| UDP | 6045 | Data access for statsd | Configurable through taosAdapter parameters. +| TCP | 6060 | Port of Monitoring Service in Enterprise version | | ### maxShellConns @@ -105,104 +97,109 @@ TDengine uses 13 continuous ports, both TCP and UDP, starting with the port spec | 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 | 1 | +| Attribute | Description | +| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Applicable | Server only | +| Meaning | The switch for monitoring inside server. The main object of monitoring is to collect information about load on physical nodes, including CPU usage, memory usage, disk usage, and network bandwidth. Monitoring information is sent over HTTP to the taosKeeper service specified by `monitorFqdn` and `monitorProt`. +| Value Range | 0: monitoring disabled, 1: monitoring enabled | +| Default | 1 | + +### monitorFqdn + +| Attribute | Description | +| -------- | -------------------------- | +| Applicable | Server Only | +| Meaning | FQDN of taosKeeper monitoring service | +| Default | None | + +### monitorPort + +| Attribute | Description | +| -------- | --------------------------- | +| Applicable | Server Only | +| Meaning | Port of taosKeeper monitoring service | +| Default Value | 6043 | ### monitorInterval -| Attribute | Description | -| ------------- | ------------------------------------------ | -| Applicable | Server Only | +| Attribute | Description | +| -------- | -------------------------------------------- | +| Applicable | Server Only | | Meaning | The interval of collecting system workload | | Unit | second | -| Value Range | 1-600 | -| Default Value | 30 | +| Value Range | 1-200000 | +| Default Value | 30 | ### telemetryReporting -| Attribute | Description | -| ------------- | ---------------------------------------------------------------------------- | -| Applicable | Server Only | +| 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 | +| Default Value | 1 | ## Query Parameters -### queryBufferSize +### queryPolicy -| 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 | +| Attribute | Description | +| -------- | ----------------------------- | +| Applicable | Client only | +| Meaning | Execution policy for query statements | +| Unit | None | +| Default | 1 | +| Notes | 1: Run queries on vnodes and not on qnodes | -### ratioOfQueryCores +2: Run subtasks without scan operators on qnodes and subtasks with scan operators on vnodes. + +3: Only run scan operators on vnodes; run all other operators on qnodes. + +### querySmaOptimize + +| Attribute | Description | +| -------- | -------------------- | +| Applicable | Client only | +| 含义 | SMA index optimization policy | +| Unit | None | +| Default Value | 0 | +| Notes | + +0: Disable SMA indexing and perform all queries on non-indexed data. + +1: Enable SMA indexing and perform queries from suitable statements on precomputation results.| -| 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 | +| 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 | +| 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 in 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. +To handle the data insertion and data query from multiple timezones, Unix Timestamp is used and stored in TDengine. The timestamp generated from any timezones at same time is same in Unix timestamp. Note that Unix timestamps are converted and recorded on the client side. 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. +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. For example: ``` -timezone UTC-7 +timezone UTC-8 timezone GMT-8 timezone Asia/Shanghai ``` @@ -240,11 +237,11 @@ To avoid the problems of using time strings, Unix timestamp can be used directly | Default Value | Locale configured in host | :::info -A specific type "nchar" is provided in TDengine to store non-ASCII characters such as Chinese, Japanese, and 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. +A specific type "nchar" is provided in TDengine to store non-ASCII characters such as Chinese, Japanese, and Korean. The characters to be stored in nchar type are firstly encoded in UCS4-LE before sending to server side. Note that the correct encoding is determined by the user. 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 and Mac 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. +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. The charset indicates how to display the characters. On Linux and Mac 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. ::: @@ -257,864 +254,566 @@ The locale definition standard on Linux is: \_., f | 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: +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 ``` -On a Linux system, if the charset contained in `locale` is not consistent with that set by `charset`, the later setting in the configuration file takes precedence. - -```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 ``` +Refer to the documentation for your operating system before changing the charset. + +On a Linux system, if the charset contained in `locale` is not consistent with that set by `charset`, the later setting in the configuration file takes precedence. + +``` +locale zh_CN.UTF-8 +charset GBK +``` + +The charset that takes effect is GBK. + +``` +charset GBK +locale zh_CN.UTF-8 +``` + +The charset that takes effect is UTF-8. + ::: ## Storage Parameters ### dataDir -| Attribute | Description | -| ------------- | ------------------------------------------- | +| 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 | +| 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 | +| 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 | +| Attribute | Description | +| -------- | ------------------------------------------------ | +| Applicable | Server Only | +| Meaning | When the available disk space in dataDir is below this threshold, writing to dataDir is suspended | +| Unit | GB | +| Default Value | 2.0 | ## Cluster Parameters -### numOfMnodes +### supportVnodes -| 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 | +| Attribute | Description | +| -------- | --------------------------- | +| Applicable | Server Only | +| Meaning | Maximum number of vnodes per dnode | +| Value Range | 0-4096 | +| Default Value | 256 | ## 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 | +| Attribute | Description | +| -------- | --------------------------- | +| Applicable | Server Only | | Meaning | the interval of dnode reporting status to mnode | | Unit | second | -| Value Range | 1-10 | -| Default Value | 1 | +| Value Range | 1-10 | +| Default Value | 1 | ### shellActivityTimer -| Attribute | Description | -| ------------- | ------------------------------------------------------ | -| Applicable | Server and Client | +| 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) | +| Unit | second | +| Value Range | 1-120 | +| Default Value | 3 | ## 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 | +| Attribute | Description | +| -------- | ---------------------- | +| Applicable | Server Only | | Meaning | Maximum of threads for committing to disk | -| Default Value | | +| 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.. | +| Attribute | Description | +| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Applicable | Server Only | +| Meaning | The threshold for message size to compress the message. | Set the value to 64330 bytes for good message compression. | | Unit | bytes | | Value Range | 0: already compress; >0: compress when message exceeds it; -1: always uncompress | -| Default Value | -1 | +| Default Value | -1 | ### compressColData -| Attribute | Description | -| ------------- | ------------------------------------------------------------------------------------------------------------------- | -| Applicable | Server Only | -| Meaning | The threshold for size of column data to trigger compression for the query result | +| 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 service was provided by `taosd` prior to version 2.4.0.0 and 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 | +| Default Value | -1 | ## Log Parameters ### logDir -| Attribute | Description | -| ------------- | ----------------------------------- | -| Applicable | Server and Client | +| 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 | +| 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 | +| Unit | GB | +| Default Value | 1.0 | ### numOfLogLines -| Attribute | Description | -| ------------- | ------------------------------------------ | -| Applicable | Server and Client | +| Attribute | Description | +| -------- | ---------------------------- | +| Applicable | Server and Client | | Meaning | Maximum number of lines in single log file | -| Default Value | 10,000,000 | +| Default Value | 10000000 | ### asyncLog -| Attribute | Description | -| ------------- | ---------------------------- | -| Applicable | Server and Client | +| Attribute | Description | +| -------- | -------------------- | +| Applicable | Server and Client | | Meaning | The mode of writing log file | | Value Range | 0: sync way; 1: async way | -| Default Value | 1 | +| Default Value | 1 | ### logKeepDays -| Attribute | Description | -| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | -| Applicable | Server and Client | +| Attribute | Description | +| -------- | ----------------------------------------------------------------------------------- | +| Applicable | Server and Client | | Meaning | The number of days for log files to be kept | -| Unit | day | -| Default Value | 0 | +| 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 | +| 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 | -| ------------- | ------------------------- | +| 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 | | +| Value Range | same as debugFlag | +| Default Value | | ### uDebugFlag -| Attribute | Description | -| ------------- | -------------------------- | -| Applicable | Server and Client | +| Attribute | Description | +| -------- | ---------------------- | +| Applicable | Server and Client | | Meaning | Log level of common module | -| Value Range | Same as debugFlag | -| Default Value | | +| Value Range | same as debugFlag | +| Default Value | | -### httpDebugFlag +### rpcDebugFlag -| Attribute | Description | -| ------------- | ------------------------------------------- | -| Applicable | Server Only | -| Meaning | Log level of http module (prior to 2.4.0.0) | -| Value Range | Same as debugFlag | -| Default Value | | +| Attribute | Description | +| -------- | -------------------- | +| Applicable | Server and Client | +| Meaning | Log level of rpc module | +| Value Range | same as debugFlag | +| Default Value | | -### mqttDebugFlag +### jniDebugFlag -| 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 | | +| Attribute | Description | +| -------- | ------------------ | +| Applicable | Client Only | +| Meaning | Log level of jni module | +| Value Range | same as debugFlag | +| Default Value | | ### qDebugFlag -| Attribute | Description | -| ------------- | ------------------------- | +| Attribute | Description | +| -------- | -------------------- | | Applicable | Server and Client | -| Meaning | Log level of query module | -| Value Range | Same as debugFlag | -| Default Value | | +| Meaning | Log level of query 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 | | + +### dDebugFlag + +| Attribute | Description | +| -------- | -------------------- | +| Applicable | Server Only | +| Meaning | Log level of dnode | +| Value Range | same as debugFlag | +| Default Value | 135 | ### vDebugFlag -| Attribute | Description | -| ------------- | ------------------ | -| Applicable | Server and Client | +| Attribute | Description | +| -------- | -------------------- | +| Applicable | Server Only | | Meaning | Log level of vnode | -| Value Range | Same as debugFlag | -| Default Value | | +| Value Range | same as debugFlag | +| Default Value | | + +### mDebugFlag + +| Attribute | Description | +| -------- | -------------------- | +| Applicable | Server Only | +| Meaning | Log level of mnode module | +| Value Range | same as debugFlag | +| Default Value | 135 | + +### wDebugFlag + +| Attribute | Description | +| -------- | ------------------ | +| Applicable | Server Only | +| Meaning | Log level of WAL module | +| 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 | ### tsdbDebugFlag -| Attribute | Description | -| ------------- | ------------------------ | -| Applicable | Server Only | -| Meaning | Log level of TSDB module | -| Value Range | Same as debugFlag | -| Default Value | | +| Attribute | Description | +| -------- | ------------------- | +| Applicable | Server Only | +| Meaning | Log level of TSDB module | +| Value Range | same as debugFlag | +| Default Value | | -### cqDebugFlag +### tqDebugFlag | Attribute | Description | -| ------------- | ------------------------------------ | -| Applicable | Server and Client | -| Meaning | Log level of continuous query module | -| Value Range | Same as debugFlag | -| Default Value | | +| -------- | ----------------- | +| Applicable | Server only | +| Meaning | Log level of TQ module | +| Value Range | same as debugFlag | +| Default Value | | -## Client Only +### fsDebugFlag -### maxSQLLength +| Attribute | Description | +| -------- | ----------------- | +| Applicable | Server only | +| Meaning | Log level of FS module | +| Value Range | same as debugFlag | +| Default Value | | + +### udfDebugFlag | Attribute | Description | -| ------------- | -------------------------------------- | -| Applicable | Client Only | -| Meaning | Maximum length of single SQL statement | -| Unit | bytes | -| Value Range | 65480-1048576 | -| Default Value | 1048576 | +| -------- | ------------------ | +| Applicable | Server Only | +| Meaning | Log level of UDF module | +| Value Range | same as debugFlag | +| Default Value | | -### tscEnableRecordSql +### smaDebugFlag -| 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 | +| Attribute | Description | +| -------- | ------------------ | +| Applicable | Server Only | +| Meaning | Log level of SMA module | +| Value Range | same as debugFlag | +| Default Value | | -### maxBinaryDisplayWidth +### idxDebugFlag -| 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 | +| Attribute | Description | +| -------- | -------------------- | +| Applicable | Server Only | +| Meaning | Log level of index module | +| Value Range | same as debugFlag | +| Default Value | | -:::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`. +### tdbDebugFlag -::: +| Attribute | Description | +| -------- | ------------------ | +| Applicable | Server Only | +| Meaning | Log level of TDB module | +| Value Range | same as debugFlag | +| Default Value | | -### maxWildCardsLength +## Schemaless Parameters -| 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 | +### smlChildTableName -### clientMerge +| Attribute | Description | +| -------- | ------------------------- | +| Applicable | Client only | +| Meaning | Custom subtable name for schemaless writes | +| Type | String | +| Default Value | None | -| 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 | +### smlTagName -### maxRegexStringLen +| Attribute | Description | +| -------- | ------------------------------------ | +| Applicable | Client only | +| Meaning | Default tag for schemaless writes without tag value specified | +| Type | String | +| Default Value | _tag_null | + +### smlDataFormat | Attribute | Description | -| ------------- | ------------------------------------ | -| Meaning | Maximum length of regular expression | -| Value Range | [128, 16384] | -| Default Value | 128 | -| Note | From version 2.3.0.0 | +| -------- | ----------------------------- | +| Applicable | Client only | +| Meaning | Whether schemaless columns are consistently ordered | +| Value Range | 0: not consistent; 1: consistent. | +| Default | 1 | ## Other Parameters ### enableCoreFile -| Attribute | Description | -| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 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. | + +### udf + +| Attribute | Description | +| -------- | ------------------ | +| Applicable | Server Only | +| Meaning | Whether the UDF service is enabled | +| Value Range | 0: disable UDF; 1: enabled UDF | +| Default Value | 1 | + +## Parameter Comparison of TDengine 2.x and 3.0 +| # | **Parameter** | **In 2.x** | **In 3.0** | +| --- | :-----------------: | --------------- | --------------- | +| 1 | firstEp | Yes | Yes | +| 2 | secondEp | Yes | Yes | +| 3 | fqdn | Yes | Yes | +| 4 | serverPort | Yes | Yes | +| 5 | maxShellConns | Yes | Yes | +| 6 | monitor | Yes | Yes | +| 7 | monitorFqdn | No | Yes | +| 8 | monitorPort | No | Yes | +| 9 | monitorInterval | Yes | Yes | +| 10 | monitorMaxLogs | No | Yes | +| 11 | monitorComp | No | Yes | +| 12 | telemetryReporting | Yes | Yes | +| 13 | telemetryInterval | No | Yes | +| 14 | telemetryServer | No | Yes | +| 15 | telemetryPort | No | Yes | +| 16 | queryPolicy | No | Yes | +| 17 | querySmaOptimize | No | Yes | +| 18 | queryBufferSize | Yes | Yes | +| 19 | maxNumOfDistinctRes | Yes | Yes | +| 20 | minSlidingTime | Yes | Yes | +| 21 | minIntervalTime | Yes | Yes | +| 22 | countAlwaysReturnValue | Yes | Yes | +| 23 | dataDir | Yes | Yes | +| 24 | minimalDataDirGB | Yes | Yes | +| 25 | supportVnodes | No | Yes | +| 26 | tempDir | Yes | Yes | +| 27 | minimalTmpDirGB | Yes | Yes | +| 28 | compressMsgSize | Yes | Yes | +| 29 | compressColData | Yes | Yes | +| 30 | smlChildTableName | Yes | Yes | +| 31 | smlTagName | Yes | Yes | +| 32 | smlDataFormat | No | Yes | +| 33 | statusInterval | Yes | Yes | +| 34 | shellActivityTimer | Yes | Yes | +| 35 | transPullupInterval | No | Yes | +| 36 | mqRebalanceInterval | No | Yes | +| 37 | ttlUnit | No | Yes | +| 38 | ttlPushInterval | No | Yes | +| 39 | numOfTaskQueueThreads | No | Yes | +| 40 | numOfRpcThreads | No | Yes | +| 41 | numOfCommitThreads | Yes | Yes | +| 42 | numOfMnodeReadThreads | No | Yes | +| 43 | numOfVnodeQueryThreads | No | Yes | +| 44 | numOfVnodeStreamThreads | No | Yes | +| 45 | numOfVnodeFetchThreads | No | Yes | +| 46 | numOfVnodeWriteThreads | No | Yes | +| 47 | numOfVnodeSyncThreads | No | Yes | +| 48 | numOfQnodeQueryThreads | No | Yes | +| 49 | numOfQnodeFetchThreads | No | Yes | +| 50 | numOfSnodeSharedThreads | No | Yes | +| 51 | numOfSnodeUniqueThreads | No | Yes | +| 52 | rpcQueueMemoryAllowed | No | Yes | +| 53 | logDir | Yes | Yes | +| 54 | minimalLogDirGB | Yes | Yes | +| 55 | numOfLogLines | Yes | Yes | +| 56 | asyncLog | Yes | Yes | +| 57 | logKeepDays | Yes | Yes | +| 58 | debugFlag | Yes | Yes | +| 59 | tmrDebugFlag | Yes | Yes | +| 60 | uDebugFlag | Yes | Yes | +| 61 | rpcDebugFlag | Yes | Yes | +| 62 | jniDebugFlag | Yes | Yes | +| 63 | qDebugFlag | Yes | Yes | +| 64 | cDebugFlag | Yes | Yes | +| 65 | dDebugFlag | Yes | Yes | +| 66 | vDebugFlag | Yes | Yes | +| 67 | mDebugFlag | Yes | Yes | +| 68 | wDebugFlag | Yes | Yes | +| 69 | sDebugFlag | Yes | Yes | +| 70 | tsdbDebugFlag | Yes | Yes | +| 71 | tqDebugFlag | No | Yes | +| 72 | fsDebugFlag | Yes | Yes | +| 73 | udfDebugFlag | No | Yes | +| 74 | smaDebugFlag | No | Yes | +| 75 | idxDebugFlag | No | Yes | +| 76 | tdbDebugFlag | No | Yes | +| 77 | metaDebugFlag | No | Yes | +| 78 | timezone | Yes | Yes | +| 79 | locale | Yes | Yes | +| 80 | charset | Yes | Yes | +| 81 | udf | Yes | Yes | +| 82 | enableCoreFile | Yes | Yes | +| 83 | arbitrator | Yes | No | +| 84 | numOfThreadsPerCore | Yes | No | +| 85 | numOfMnodes | Yes | No | +| 86 | vnodeBak | Yes | No | +| 87 | balance | Yes | No | +| 88 | balanceInterval | Yes | No | +| 89 | offlineThreshold | Yes | No | +| 90 | role | Yes | No | +| 91 | dnodeNopLoop | Yes | No | +| 92 | keepTimeOffset | Yes | No | +| 93 | rpcTimer | Yes | No | +| 94 | rpcMaxTime | Yes | No | +| 95 | rpcForceTcp | Yes | No | +| 96 | tcpConnTimeout | Yes | No | +| 97 | syncCheckInterval | Yes | No | +| 98 | maxTmrCtrl | Yes | No | +| 99 | monitorReplica | Yes | No | +| 100 | smlTagNullName | Yes | No | +| 101 | keepColumnName | Yes | No | +| 102 | ratioOfQueryCores | Yes | No | +| 103 | maxStreamCompDelay | Yes | No | +| 104 | maxFirstStreamCompDelay | Yes | No | +| 105 | retryStreamCompDelay | Yes | No | +| 106 | streamCompDelayRatio | Yes | No | +| 107 | maxVgroupsPerDb | Yes | No | +| 108 | maxTablesPerVnode | Yes | No | +| 109 | minTablesPerVnode | Yes | No | +| 110 | tableIncStepPerVnode | Yes | No | +| 111 | cache | Yes | No | +| 112 | blocks | Yes | No | +| 113 | days | Yes | No | +| 114 | keep | Yes | No | +| 115 | minRows | Yes | No | +| 116 | maxRows | Yes | No | +| 117 | quorum | Yes | No | +| 118 | comp | Yes | No | +| 119 | walLevel | Yes | No | +| 120 | fsync | Yes | No | +| 121 | replica | Yes | No | +| 122 | partitions | Yes | No | +| 123 | quorum | Yes | No | +| 124 | update | Yes | No | +| 125 | cachelast | Yes | No | +| 126 | maxSQLLength | Yes | No | +| 127 | maxWildCardsLength | Yes | No | +| 128 | maxRegexStringLen | Yes | No | +| 129 | maxNumOfOrderedRes | Yes | No | +| 130 | maxConnections | Yes | No | +| 131 | mnodeEqualVnodeNum | Yes | No | +| 132 | http | Yes | No | +| 133 | httpEnableRecordSql | Yes | No | +| 134 | httpMaxThreads | Yes | No | +| 135 | restfulRowLimit | Yes | No | +| 136 | httpDbNameMandatory | Yes | No | +| 137 | httpKeepAlive | Yes | No | +| 138 | enableRecordSql | Yes | No | +| 139 | maxBinaryDisplayWidth | Yes | No | +| 140 | stream | Yes | No | +| 141 | retrieveBlockingModel | Yes | No | +| 142 | tsdbMetaCompactRatio | Yes | No | +| 143 | defaultJSONStrType | Yes | No | +| 144 | walFlushSize | Yes | No | +| 145 | keepTimeOffset | Yes | No | +| 146 | flowctrl | Yes | No | +| 147 | slaveQuery | Yes | No | +| 148 | adjustMaster | Yes | No | +| 149 | topicBinaryLen | Yes | No | +| 150 | telegrafUseFieldNum | Yes | No | +| 151 | deadLockKillQuery | Yes | No | +| 152 | clientMerge | Yes | No | +| 153 | sdbDebugFlag | Yes | No | +| 154 | odbcDebugFlag | Yes | No | +| 155 | httpDebugFlag | Yes | No | +| 156 | monDebugFlag | Yes | No | +| 157 | cqDebugFlag | Yes | No | +| 158 | shortcutFlag | Yes | No | +| 159 | probeSeconds | Yes | No | +| 160 | probeKillSeconds | Yes | No | +| 161 | probeInterval | Yes | No | +| 162 | lossyColumns | Yes | No | +| 163 | fPrecision | Yes | No | +| 164 | dPrecision | Yes | No | +| 165 | maxRange | Yes | No | +| 166 | range | Yes | No | From 3fae4ba6f0e9ed7b8934520530785e2cd5322951 Mon Sep 17 00:00:00 2001 From: danielclow <106956386+danielclow@users.noreply.github.com> Date: Mon, 22 Aug 2022 14:10:16 +0800 Subject: [PATCH 02/76] doc: english version of schemaless --- .../13-schemaless/13-schemaless.md | 114 +++++++++--------- 1 file changed, 60 insertions(+), 54 deletions(-) diff --git a/docs/en/14-reference/13-schemaless/13-schemaless.md b/docs/en/14-reference/13-schemaless/13-schemaless.md index 8b6a26ae52..816ebe0047 100644 --- a/docs/en/14-reference/13-schemaless/13-schemaless.md +++ b/docs/en/14-reference/13-schemaless/13-schemaless.md @@ -3,7 +3,8 @@ 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, data is collected for many purposes such as intelligent control, business analysis, device monitoring and so on. Due to changes in business or functional requirements or changes in device hardware, the application logic and even the data collected may change. To provide the flexibility needed in such cases and in a rapidly changing IoT landscape, TDengine provides a series of interfaces for the schemaless writing method. These interfaces eliminate the need to create super tables and subtables in advance by automatically creating the storage structure corresponding to the data as the data is written to the interface. When necessary, schemaless writing will automatically add the required columns to ensure that the data written by the user is stored correctly. +In IoT applications, data is collected for many purposes such as intelligent control, business analysis, device monitoring and so on. Due to changes in business or functional requirements or changes in device hardware, the application logic and even the data collected may change. Schemaless writing automatically creates storage structures for your data as it is being written to TDengine, so that you do not need to create supertables in advance. 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 subtables. These are completely indistinguishable from the super tables and subtables 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 they lack readability. @@ -19,12 +20,12 @@ With the following formatting conventions, schemaless writing uses a single stri measurement,tag_set field_set timestamp ``` -where : +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 a space. -- The timestamp is the primary key corresponding to the data in this row. +- `tag_set` will be used as tags, with format like `=,=` Enter a space between `tag_set` and `field_set`. +- `field_set`will be used as data columns, with format like `=,=` Enter a space between `field_set` and `timestamp`. +- `timestamp` is the primary key timestamp corresponding to this row of data All data in tag_set is automatically converted to the NCHAR data type and does not require double quotes ("). @@ -37,16 +38,18 @@ In the schemaless writing data line protocol, each data item in the field_set ne | **Serial number** | **Postfix** | **Mapping type** | **Size (bytes)** | | -------- | -------- | ------------ | -------------- | -| 1 | none or f64 | double | 8 | -| 2 | f32 | float | 4 | -| 3 | i8/u8 | TinyInt/UTinyInt | 1 | -| 4 | i16/u16 | SmallInt/USmallInt | 2 | -| 5 | i32/u32 | Int/UInt | 4 | -| 6 | i64/i/u64/u | Bigint/Bigint/UBigint/UBigint | 8 | +| 1 | None or f64 | double | 8 | +| 2 | f32 | float | 4 | +| 3 | i8/u8 | TinyInt/UTinyInt | 1 | +| 4 | i16/u16 | SmallInt/USmallInt | 2 | +| 5 | i32/u32 | Int/UInt | 4 | +| 6 | i64/i/u64/u | BigInt/BigInt/UBigInt/UBigInt | 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. +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 @@ -65,18 +68,22 @@ Schemaless writes process row data according to the following principles. ``` 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. +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. +You can configure smlChildTableName to specify table names, for example, `smlChildTableName=tname`. You can insert `st,tname=cpul,t1=4 c1=3 1626006833639000000` and the cpu1 table will be automatically created. Note that if multiple rows have the same tname but different tag_set values, the tag_set of the first row is used to create the table and the others are ignored. 2. If the super table obtained by parsing the line protocol does not exist, this super table is created. -If the subtable obtained by the parse line protocol does not exist, Schemaless creates the sub-table according to the subtable name determined in steps 1 or 2. +3. If the subtable obtained by the parse line protocol does not exist, Schemaless creates the sub-table according to the subtable name determined in steps 1 or 2. 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. +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. Errors encountered throughout the processing will interrupt the writing process and return an error code. -8. In order to improve the efficiency of writing, it is assumed by default that the order of the fields in the same Super is the same (the first data contains all fields, and the following data is in this order). If the order is different, the parameter smlDataFormat needs to be configured to be false. Otherwise, the data is written in the same order, and the data in the library will be abnormal. +8. It is assumed that the order of field_set in a supertable is consistent, meaning that the first record contains all fields and subsequent records store fields in the same order. If the order is not consistent, set smlDataFormat to false. Otherwise, data will be written out of order and a database error will occur. :::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. +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 +16KB. See [TAOS SQL Boundary Limits](/taos-sql/limit) for specific constraints in this area. + ::: ## Time resolution recognition @@ -85,75 +92,74 @@ Three specified modes are supported in the schemaless writing process, as follow | **Serial** | **Value** | **Description** | | -------- | ------------------- | ------------------------------- | -| 1 | SML_LINE_PROTOCOL | InfluxDB Line Protocol | -| 2 | SML_TELNET_PROTOCOL | OpenTSDB Text Line Protocol | -| 3 | SML_JSON_PROTOCOL | JSON protocol format | +| 1 | SML_LINE_PROTOCOL | InfluxDB Line Protocol | +| 2 | SML_TELNET_PROTOCOL | OpenTSDB file protocol | +| 3 | SML_JSON_PROTOCOL | OpenTSDB JSON protocol | -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. +In InfluxDB line protocol mode, you must specify the precision of the input timestamp. Valid precisions are described in the following table. -| **Serial Number** | **Time Resolution Definition** | **Meaning** | +| **No.** | **Precision** | **Description** | | -------- | --------------------------------- | -------------- | -| 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 | +| 1 | TSDB_SML_TIMESTAMP_NOT_CONFIGURED | Not defined (invalid) | +| 2 | TSDB_SML_TIMESTAMP_HOURS | Hours | +| 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. +In OpenTSDB file and JSON protocol modes, the precision of the timestamp is determined from its length in the standard OpenTSDB manner. User input is ignored. -## Data schema mapping rules +## Data Model Mapping -This section describes how data for line protocols are mapped to data with a schema. The data measurement in each line protocol is mapped as follows: -- The tag name in tag_set is the name of the tag in the data schema -- The name in field_set is the column's name. - -The following data is used as an example to illustrate the mapping rules. +This section describes how data in line protocol is mapped to a schema. The data measurement in each line is mapped to a +supertable name. The tag name in tag_set is the tag name in the schema, and the name in field_set is the column name in the schema. The following example shows how data is mapped: ```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. +This row is mapped to a supertable: `st` contains three NCHAR tags: t1, t2, and t3. Five columns are created: ts (timestamp), c1 (bigint), c3 (binary), c2 (bool), and c4 (bigint). The following SQL statement is generated: ```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 +## Processing Schema Changes -This section describes the impact on the data schema for different line protocol data writing cases. +This section describes the impact on the schema caused by different data being written. -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 +If you use line protocol to write to a specific tag field and then later change the field type, a schema error will ocur. This triggers an error on the write API. This is shown as follows: ```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 +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. +The first row defines c4 as a double. However, in the second row, the suffix indicates that the value of c4 is a bigint. This causes schemaless writing to throw an error. -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. +An error also occurs if data input into a binary column exceeds the defined length of the column. ```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 +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 parse column c5 as a BINARY column. But in the second line, c5's width is 6 so you need to increase the width of the BINARY field to be able to accommodate the new string. +The first row defines c5 as a binary(4). but the second row writes 6 bytes to it. This means that the length of the binary column must be expanded to contain the data. ```json -st,t1=3,t2=4,t3=t3 c1=3i64 1626006833639000000 -st,t1=3,t2=4,t3=t3 c1=3i64,c6="passit" 1626006833640000000 +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. +The preceding data includes a new entry, c6, with type binary(6). When this occurs, a new column c6 with type binary(6) is added automatically. -## Write integrity +## 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. +TDengine guarantees the idempotency of data writes. This means that you can repeatedly call the API to perform write operations with bad data. However, TDengine does not guarantee the atomicity of multi-row writes. In a multi-row write, some data may be written successfully and other data unsuccessfully. -## Error code +##: Error Codes -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. +The TSDB_CODE_TSC_LINE_SYNTAX_ERROR indicates an error in the schemaless writing component. +This error occurs when writing text. For other errors, schemaless writing uses the standard TDengine error codes +found in taos_errstr. From c6288fad5a6c10f6e81c8322dc04b335ac7a0c01 Mon Sep 17 00:00:00 2001 From: danielclow <106956386+danielclow@users.noreply.github.com> Date: Mon, 22 Aug 2022 15:14:42 +0800 Subject: [PATCH 03/76] doc: english version of operation --- docs/en/13-operation/01-pkg-install.md | 251 ++++++------------------- docs/en/13-operation/02-planning.mdx | 50 +++-- docs/en/13-operation/03-tolerance.md | 20 +- docs/en/13-operation/17-diagnose.md | 109 +++-------- 4 files changed, 112 insertions(+), 318 deletions(-) diff --git a/docs/en/13-operation/01-pkg-install.md b/docs/en/13-operation/01-pkg-install.md index c098002962..30060fb085 100644 --- a/docs/en/13-operation/01-pkg-install.md +++ b/docs/en/13-operation/01-pkg-install.md @@ -1,152 +1,63 @@ --- -title: Install & Uninstall +title: Install and Uninstall description: Install, Uninstall, Start, Stop and Upgrade --- import Tabs from "@theme/Tabs"; import TabItem from "@theme/TabItem"; -TDengine community version provides deb and rpm packages for users to choose from, based on their system environment. The deb package supports Debian, Ubuntu and derivative systems. The rpm package supports CentOS, RHEL, SUSE and derivative systems. Furthermore, a tar.gz package is provided for TDengine Enterprise customers. +This document gives more information about installing, uninstalling, and upgrading TDengine. ## 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 the command below - -```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 +See [Quick Install](../../get-started/package) for preliminary steps. -TDengine is installed successfully! -``` - +## Installation Directory - - -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 the command below +TDengine is installed at /usr/local/taos if successful. ``` -$ 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, first 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/ -... - +$ cd /usr/local/taos $ 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` +$ 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/ ``` -:::info -Users will be prompted to enter some configuration information when install.sh is executing. The interactive mode can be disabled by executing `./install.sh -e no`. `./install.sh -h` can show all parameters with detailed explanation. - -::: - - - - -:::note -When installing on the first node in the cluster, at the "Enter FQDN:" prompt, nothing needs to be provided. When installing on subsequent nodes, at the "Enter FQDN:" prompt, you must enter the end point of the first dnode in the cluster if it is already up. You can also just ignore it and configure it later after installation is finished. - -::: +- 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 +- 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 ## Uninstall + + +TBD + + 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) ... +(Reading database ... 120119 files and directories currently installed.) +Removing tdengine (3.0.0.10002) ... TDengine is removed successfully! ``` @@ -170,115 +81,59 @@ 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! ``` + + +Run C:\TDengine\unins000.exe to uninstall TDengine on a Windows system. -:::note +:::info -- We strongly recommend not to use multiple kinds of installation packages on a single host TDengine. -- After deb package is installed, if the installation directory is removed manually, uninstall or reinstall will not work. This issue can be resolved by using the command below which cleans up TDengine package information. You can then reinstall if needed. +- We strongly recommend not to use multiple kinds of installation packages on a single host TDengine. The packages may affect each other and cause errors. -```bash - $ sudo rm -f /var/lib/dpkg/info/tdengine* -``` +- After deb package is installed, if the installation directory is removed manually, uninstall or reinstall will not work. This issue can be resolved by using the command below which cleans up TDengine package information. -- After rpm package is installed, if the installation directory is removed manually, uninstall or reinstall will not work. This issue can be resolved by using the command below which cleans up TDengine package information. You can then reinstall if needed. + ``` + $ sudo rm -f /var/lib/dpkg/info/tdengine* + ``` -```bash - $ sudo rpm -e --noscripts tdengine -``` +You can then reinstall if needed. + +- After rpm package is installed, if the installation directory is removed manually, uninstall or reinstall will not work. This issue can be resolved by using the command below which cleans up TDengine package information. + + ``` + $ sudo rpm -e --noscripts tdengine + ``` + +You can then reinstall if needed. ::: -## 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 -- 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 +Uninstalling and Modifying Files - 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. Please follow data integrity, security, backup or relevant SOPs before deleting any data. + - 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` are 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 operators 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 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. To upgrade a package, follow the steps mentioned previously to first uninstall the old version then install the new version. Upgrading a running server is much more complex. First please check the version number of the old version and the new version. The version number of TDengine consists of 4 sections, only if the first 3 sections 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 is persisted to disk -- Make some simple queries (Such as total rows in stables, tables and so on. Note down the values. Follow best practices and relevant SOPs.) - Stop the cluster of TDengine - Uninstall old version and install new version - Start the cluster of TDengine -- Execute simple queries, such as the ones executed prior to installing the new package, to make sure there is no data loss +- Execute simple queries, such as the ones executed prior to installing the new package, to make sure there is no data loss - Run some simple data insertion statements to make sure the cluster works well - Restore business services :::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 index c1baf92dbf..2dffa7bb87 100644 --- a/docs/en/13-operation/02-planning.mdx +++ b/docs/en/13-operation/02-planning.mdx @@ -1,40 +1,32 @@ --- +sidebar_label: Resource Planning title: Resource Planning --- It is important to plan computing and storage resources if using TDengine to build an IoT, time-series or Big Data platform. How to plan the CPU, memory and disk resources required, will be described in this chapter. -## Memory Requirement of Server Side +## Server Memory Requirements -By default, the number of vgroups created for each database is the same as the number of CPU cores. This can be configured by the parameter `maxVgroupsPerDb`. Each vnode in a vgroup stores one replica. Each vnode consumes a fixed amount of memory, i.e. `blocks` \* `cache`. In addition, 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 the formula below: +Each database creates a fixed number of vgroups. This number is 2 by default and can be configured with the `vgroups` parameter. The number of replicas can be controlled with the `replica` parameter. Each replica requires one vnode per vgroup. Altogether, the memory required by each database depends on the following configuration options: + +- vgroups +- replica +- buffer +- pages +- pagesize +- cachesize + +For more information, see [Database](../../taos-sql/database). + +The memory required by a database is therefore greater than or equal to: ``` -Database Memory Size = maxVgroupsPerDb * replica * (blocks * cache + 10MB) + numOfTables * (tagSizePerTable + 0.5KB) +vgroups * replica * (buffer + pages * pagesize + cachesize) ``` -For example, assuming the default value of `maxVgroupPerDB` is 64, the default value of `cache` is 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. +However, note that this requirement is spread over all dnodes in the cluster, not on a single physical machine. The physical servers that run dnodes meet the requirement together. If a cluster has multiple databases, the memory required increases accordingly. In complex environments where dnodes were added after initial deployment in response to increasing resource requirements, load may not be balanced among the original dnodes and newer dnodes. In this situation, the actual status of your dnodes is more important than theoretical calculations. -In the 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 for Database Memory Size, mentioned above, then dividing by number of dnodes and multiplying the number of replicas. - -``` - vnode_memory = (sum(Database Memory Size) / 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 note 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 reserve 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 +## Client Memory Requirements For the client programs using TDengine client driver `taosc` to connect to the server side there is a memory requirement as well. @@ -56,10 +48,10 @@ So, at least 3GB needs to be reserved for such a client. 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 difference in computing resource consumed, between inserting 1 row at a time, and inserting 10 rows at a time is very small. So, the more the number of rows that can be inserted one time, the higher the efficiency. Inserting in batch also imposes requirements on the client side which needs to cache rows to insert in batch once the number of cached rows reaches a threshold. +- **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 difference in computing resource consumed, between inserting 1 row at a time, and inserting 10 rows at a time is very small. So, the more the number of rows that can be inserted one time, the higher the efficiency. If each insert request contains more than 200 records, a single core can process more than 1 million records per second. Inserting in batch also imposes requirements on the client side which needs to cache rows to insert in batch once the number of 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, and other requirements provided by users. -In short, 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. +In short, the CPU resource required for data insertion can be estimated but it's hard to do so for query use cases. If possible, ensure that CPU usage remains 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 @@ -77,6 +69,6 @@ To increase performance, multiple disks can be setup for parallel data reading o ## 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 formulae mentioned previously. Then, according to the system resources that a single host can provide, assuming all hosts have the same resources, the number of hosts can be derived easily. +A host can be either physical or virtual. The total memory, total CPU, total disk required can be estimated according to the formulae mentioned previously. If the number of data replicas is not 1, the required resources are multiplied by the number of replicas. -**Quick Estimation for CPU, Memory and Disk** Please refer to [Resource Estimate](https://www.taosdata.com/config/config.html). +Then, according to the system resources that a single host can provide, assuming all hosts have the same resources, the number of hosts can be derived easily. diff --git a/docs/en/13-operation/03-tolerance.md b/docs/en/13-operation/03-tolerance.md index d4d48d7fcd..ba9d5d75e3 100644 --- a/docs/en/13-operation/03-tolerance.md +++ b/docs/en/13-operation/03-tolerance.md @@ -1,6 +1,5 @@ --- -sidebar_label: Fault Tolerance -title: Fault Tolerance & Disaster Recovery +title: Fault Tolerance and Disaster Recovery --- ## Fault Tolerance @@ -11,22 +10,21 @@ When a data block is received by TDengine, the original data block is first writ 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:This parameter is only valid when walLevel is set to 2. It specifies the interval, in milliseconds, of invoking fsync. If set to 0, it means fsync is invoked immediately once WAL is written. +- wal_level: Specifies the WAL level. 1 indicates that WAL is enabled but fsync is disabled. 2 indicates that WAL and fsync are both enabled. The default value is 1. +- wal_fsync_period: This parameter is only valid when wal_level is set to 2. It specifies the interval, in milliseconds, of invoking fsync. If set to 0, it means fsync is invoked immediately once WAL is written. -To achieve absolutely no data loss, walLevel should be set to 2 and fsync should be set to 1. There is a performance penalty to the data ingestion rate. However, if the concurrent data insertion threads on the client side can reach a big enough number, for example 50, the data ingestion performance will be still good enough. Our verification shows that the drop is only 30% when fsync is set to 3,000 milliseconds. +To achieve absolutely no data loss, set wal_level to 2 and wal_fsync_period to 0. There is a performance penalty to the data ingestion rate. However, if the concurrent data insertion threads on the client side can reach a big enough number, for example 50, the data ingestion performance will be still good enough. Our verification shows that the drop is only 30% when wal_fsync_period is set to 3000 milliseconds. ## Disaster Recovery -TDengine uses replication to provide high availability and disaster recovery capability. +TDengine uses replication to provide high availability. -A TDengine cluster is managed by mnode. To ensure the high availability of mnode, multiple replicas can be configured by the system parameter `numOfMnodes`. The data replication between mnode replicas is performed in a synchronous way to guarantee metadata consistency. +A TDengine cluster is managed by mnodes. You can configure up to three mnodes to ensure high availability. The data replication between mnode replicas is performed in a synchronous way to guarantee metadata consistency. -The number of replicas for time series data in TDengine is associated with each database. There can be many databases in a cluster and 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 replicas for time series data in TDengine is associated with each database. There can be many databases in a cluster and each database can be configured with a different number of replicas. When creating a database, the parameter `replica` is used to specify the number of replicas. To achieve high availability, set `replica` to 3. 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 a table. As long as the dnodes of a TDengine cluster are deployed on different physical machines and the replica number is higher than 1, high availability can be achieved without any other assistance. For disaster recovery, dnodes of a TDengine cluster should be deployed in geographically different data centers. + +Alternatively, you can use taosX to synchronize the data from one TDengine cluster to another cluster in a remote location. For more information, see [taosX](../../reference/taosX). diff --git a/docs/en/13-operation/17-diagnose.md b/docs/en/13-operation/17-diagnose.md index 2b474fddba..d01d12e831 100644 --- a/docs/en/13-operation/17-diagnose.md +++ b/docs/en/13-operation/17-diagnose.md @@ -13,110 +13,59 @@ Diagnostic steps: 1. If the port range to be diagnosed is being occupied by a `taosd` server process, please first 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 a 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 note that the package length must be same in the above 2 commands executed on server side and client side respectively. + +-l : The size of the testing package, in bytes. The value range is [11, 64,000] and default value is 1,000. +Please note 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 for the example is below: ```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 +# taos -n server -P 6030 -l 1000 +network test server is initialized, port:6030 +request is received, size:1000 +request is received, size:1000 ... ... ... -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 +request is received, size:1000 +request is received, size:1000 ``` Output of the client side for the example is below: ```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 +taos -n client -h v3s2 -P 6030 -l 1000 +network test client is initialized, the server is v3s2:6030 +request is sent, size:1000 +response is received, size:1000 +request is sent, size:1000 +response is received, size:1000 +... +... +... +request is sent, size:1000 +response is received, size:1000 +request is sent, size:1000 +response is received, size: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 +total succ: 100/100 cost: 16.23 ms speed: 5.87 MB/s ``` The output needs to be checked carefully for the system operator to find the root cause and resolve 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 common task which should be performed by a system operator, especially in the case of a cluster, to determine whether `taosd` has been started successfully. - -`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 is working abnormally, this command can be used to initiate a rpc communication with the specified fqdn to determine whether it's a network problem or whether `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 in a Linux shell to check whether the port for sync is working well and whether the sync module on the server side is working well. Additionally, `-P 6042` is used to check whether the arbitrator is configured properly and is working well. - -## Network Speed Diagnostic - -`taos -n speed -h -P 6030 -N 10 -l 10000000 -S TCP` - -From version 2.2.0.0 onwards, the above command can be executed in a Linux shell to test network speed. The command sends uncompressed packages 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 TCP. - -## FQDN Resolution Diagnostic - -`taos -n fqdn -h ` - -From version 2.2.0.0 onward, the above command can be executed in a 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 the `taosd` server process. The default value is 131. For debugging and tracing, it needs to be set to either 135 or 143 respectively. +The parameter `debugFlag` is used to control the log level of the `taosd` server process. The default value is 131. For debugging and tracing, it needs to be set to either 135 or 143 respectively. -Once this parameter is set to 135 or 143, the log file grows very quickly especially when there is a huge volume of data insertion and data query requests. If all the logs are stored together, some important information may be missed very easily and so on the server side, important information is stored in a 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` +Once this parameter is set to 135 or 143, the log file grows very quickly especially when there is a huge volume of data insertion and data query requests. Ensure that the disk drive on which logs are stored has sufficient space. ## 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 logs at level of INFO/ERROR/WARNING are recorded. As stated above, for debugging and tracing, it needs to be changed to 135 or 143 respectively, so that logs at DEBUG or TRACE level can be recorded. +An independent log file, named as "taoslog+" is generated for each client program, i.e. a client process. The parameter `debugFlag` is used to control the log level. The default value is 131. For debugging and tracing, it needs to be set to either 135 or 143 respectively. + +The default value of `debugFlag` is also 131 and only logs at level of INFO/ERROR/WARNING are recorded. As stated above, for debugging and tracing, it needs to be changed to 135 or 143 respectively, so that logs at DEBUG or TRACE level can be recorded. 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 files are written in an async way to minimize the workload on disk, but the trade off for performance is that a few log lines may be lost in some extreme conditions. +Log files are written in an async way to minimize the workload on disk, but the trade off for performance is that a few log lines may be lost in some extreme conditions. You can configure asynclog to 0 when needed for troubleshooting purposes to ensure that no log information is lost. From 77de88a768de345124318dd5d7b7c1573d7f43f3 Mon Sep 17 00:00:00 2001 From: danielclow <106956386+danielclow@users.noreply.github.com> Date: Mon, 22 Aug 2022 16:08:46 +0800 Subject: [PATCH 04/76] doc: english version of third-party --- docs/en/20-third-party/01-grafana.mdx | 17 +++++++++-------- docs/en/20-third-party/06-statsd.md | 9 +++------ docs/en/20-third-party/10-hive-mq-broker.md | 4 ++-- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/docs/en/20-third-party/01-grafana.mdx b/docs/en/20-third-party/01-grafana.mdx index 5dbeb31a23..e0fbefd5a8 100644 --- a/docs/en/20-third-party/01-grafana.mdx +++ b/docs/en/20-third-party/01-grafana.mdx @@ -6,9 +6,7 @@ title: Grafana import Tabs from "@theme/Tabs"; import TabItem from "@theme/TabItem"; -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). +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 @@ -65,7 +63,6 @@ Restart Grafana service and open Grafana in web-browser, usually - Follow the installation steps in [Grafana](https://grafana.com/grafana/plugins/tdengine-datasource/?tab=installation) with the [``grafana-cli`` command-line tool](https://grafana.com/docs/grafana/latest/administration/cli/) for plugin installation. @@ -76,7 +73,7 @@ grafana-cli plugins install tdengine-datasource sudo -u grafana grafana-cli plugins install tdengine-datasource ``` -Alternatively, you can manually download the .zip file from [GitHub](https://github.com/taosdata/grafanaplugin/releases/tag/latest) or [Grafana](https://grafana.com/grafana/plugins/tdengine-datasource/?tab=installation) and unpack it into your grafana plugins directory. +You can also download zip files from [GitHub](https://github.com/taosdata/grafanaplugin/releases/tag/latest) or [Grafana](https://grafana.com/grafana/plugins/tdengine-datasource/?tab=installation) and install manually. The commands are as follows: ```bash GF_VERSION=3.2.2 @@ -131,7 +128,7 @@ docker run -d \ grafana/grafana ``` -You can setup a zero-configuration stack for TDengine + Grafana by [docker-compose](https://docs.docker.com/compose/) and [Grafana provisioning](https://grafana.com/docs/grafana/latest/administration/provisioning/) file: +You can setup a zero-configuration stack for TDengine + Grafana by [docker-compose](https://docs.docker.com/compose/) and [Grafana provisioning](https://grafana.com/docs/grafana/latest/administration/provisioning/) file: 1. Save the provisioning configuration file to `tdengine.yml`. @@ -196,7 +193,7 @@ Go back to the main interface to create a dashboard and click Add Query to enter 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. +- INPUT SQL: Enter the desired query (the results being two columns and multiple rows), such as `select _wstart, avg(mem_system) from log.dnodes_info where ts >= $from and ts < $to interval($interval)`. In this statement, $from, $to, and $interval are variables that Grafana replaces with the query time range and interval. 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. @@ -208,7 +205,11 @@ Follow the default prompt to query the average system memory usage for the speci ### Importing the Dashboard -You can install TDinsight dashboard in data source configuration page (like `http://localhost:3000/datasources/edit/1/dashboards`) as a monitoring visualization tool for TDengine cluster. The dashboard is published in Grafana as [Dashboard 15167 - TDinsight](https://grafana.com/grafana/dashboards/15167). Check the [TDinsight User Manual](/reference/tdinsight/) for the details. +You can install TDinsight dashboard in data source configuration page (like `http://localhost:3000/datasources/edit/1/dashboards`) as a monitoring visualization tool for TDengine cluster. Ensure that you use TDinsight for 3.x. + +![TDengine Database Grafana plugine import dashboard](./import_dashboard.webp) + +A dashboard for TDengine 2.x has been published on Grafana: [Dashboard 15167 - TDinsight](https://grafana.com/grafana/dashboards/15167)) 。 Check the [TDinsight User Manual](/reference/tdinsight/) for the details. For more dashboards using TDengine data source, [search here in Grafana](https://grafana.com/grafana/dashboards/?dataSource=tdengine-datasource). Here is a sub list: diff --git a/docs/en/20-third-party/06-statsd.md b/docs/en/20-third-party/06-statsd.md index 40e927b9fd..32b1bbb97a 100644 --- a/docs/en/20-third-party/06-statsd.md +++ b/docs/en/20-third-party/06-statsd.md @@ -1,6 +1,6 @@ --- sidebar_label: StatsD -title: StatsD writing +title: StatsD Writing --- import StatsD from "../14-reference/_statsd.mdx" @@ -12,8 +12,8 @@ You can write StatsD data to TDengine by simply modifying the configuration file ## 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. +1. The TDengine cluster is deployed and functioning properly +2. taosAdapter is installed and running properly. Please refer to the taosAdapter manual for details. - StatsD has been installed. To install StatsD, please refer to [official documentation](https://github.com/statsd/statsd) ## Configuration steps @@ -39,9 +39,6 @@ $ 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 | ==================================================================================================================================================================================================================================================================================== diff --git a/docs/en/20-third-party/10-hive-mq-broker.md b/docs/en/20-third-party/10-hive-mq-broker.md index 333e00fa0e..828a62ac5b 100644 --- a/docs/en/20-third-party/10-hive-mq-broker.md +++ b/docs/en/20-third-party/10-hive-mq-broker.md @@ -1,6 +1,6 @@ --- sidebar_label: HiveMQ Broker -title: HiveMQ Broker writing +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 +[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. For more information, see [HiveMQ TDengine Extension](https://github.com/huskar-t/hivemq-tdengine-extension/blob/b62a26ecc164a310104df57691691b237e091c89/README_EN.md). From e51e7d9410198d4a117b472a4a41de9ae0dac08d Mon Sep 17 00:00:00 2001 From: danielclow <106956386+danielclow@users.noreply.github.com> Date: Thu, 25 Aug 2022 18:12:26 +0800 Subject: [PATCH 05/76] doc: english faq for tdengine 3.0 --- docs/en/27-train-faq/01-faq.md | 185 +++++++++++++++++++++------------ 1 file changed, 117 insertions(+), 68 deletions(-) diff --git a/docs/en/27-train-faq/01-faq.md b/docs/en/27-train-faq/01-faq.md index c10bca1d05..733b418474 100644 --- a/docs/en/27-train-faq/01-faq.md +++ b/docs/en/27-train-faq/01-faq.md @@ -1,114 +1,163 @@ --- -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. In your description please include the TDengine version, hardware and OS information, the steps to reproduce the problem and any other relevant information. It would be very helpful if you can package the contents in `/var/log/taos` and `/etc/taos` and upload. These two are the default directories used by TDengine. If you have changed the default directories in your configuration, please package the files in your configured directories. We recommended setting `debugFlag` to 135 in `taos.cfg`, restarting `taosd`, then reproducing the problem and collecting the 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. +If your issue could not be resolved by reviewing this documentation, you can submit your issue on GitHub and receive support from the TDengine Team. When you submit an issue, attach the following directories from your TDengine deployment: + +1. The directory containing TDengine logs (`/var/log/taos` by default) +2. The directory containing TDengine configuration files (`/etc/taos` by default) + +In your GitHub issue, provide the version of TDengine and the operating system and environment for your deployment, the operations that you performed when the issue occurred, and the time of occurrence and affected tables. + +To obtain more debugging information, open `taos.cfg` and set the `debugFlag` parameter to `135`. Then restart TDengine Server and reproduce the issue. The debug-level logs generated help the TDengine Team to resolve your issue. If it is not possible to restart TDengine Server, you can run the following command in the TDengine CLI to set the debug flag: + +``` + alter dnode 'debugFlag' '135'; +``` + +You can run the `SHOW DNODES` command to determine the dnode ID. + +When debugging information is no longer needed, set `debugFlag` to 131. ## Frequently Asked Questions -### 1. How to upgrade to TDengine 2.0 from older version? +### 1. What are the best practices for upgrading a previous version of TDengine to version 3.0? -version 2.x is not compatible with version 1.x. With regard to the configuration and data files, please perform the following steps before upgrading. Please follow data integrity, security, backup and other relevant SOPs, best practices before removing/deleting any data. +TDengine 3.0 is not compatible with the configuration and data files from previous versions. Before upgrading, perform the following steps: -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 latest 2.x version -5. If the data needs to be kept and migrated to newer version, please contact professional service at TDengine for assistance. +1. Run `sudo rm -rf /etc/taos/taos.cfg` to delete your configuration file. +2. Run `sudo rm -rf /var/log/taos/` to delete your log files. +3. Run `sudo rm -rf /var/lib/taos/` to delete your data files. +4. Install TDengine 3.0. +5. For assistance in migrating data to TDengine 3.0, contact [TDengine Support](https://tdengine.com/support). -### 2. How to handle "Unable to establish connection"? +### 4. How can I resolve the "Unable to establish connection" error? -When the client is unable to connect to the server, you can try the following ways to troubleshoot and resolve the problem. +This error indicates that the client could not connect to the server. Perform the following troubleshooting steps: -1. Check the network +1. Check the network. - - Check if the hosts where the client and server are running are 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. If possible, disable the firewall for diagnostics, but please ensure that you are following security and other relevant protocols. - - 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. + - For machines deployed in the cloud, verify that your security group can access ports 6030 and 6031 (TCP and UDP). + - For virtual machines deployed locally, verify that the hosts where the client and server are running are accessible to each other. Do not use localhost as the hostname. + - For machines deployed on a corporate network, verify that your NAT configuration allows the server to respond to the client. -2. Make sure the client version and server version are same. +2. Verify that the client and server are running the same version of TDengine. -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. +3. On the server, run `systemctl status taosd` to verify that taosd is running normally. If taosd is stopped, run `systemctl start taosd`. -4. If using connector of Python, Java, Go, Rust, C#, node.JS on Linux to connect to 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`. +4. Verify that the client is configured with the correct FQDN for the server. -5. If using connector on Windows, please make sure `C:\TDengine\driver\taos.dll` is in your system lib search path. We recommend putting `taos.dll` under `C:\Windows\System32`. +5. If the server cannot be reached with the `ping` command, verify that network and DNS or hosts file settings are correct. For a TDengine cluster, the client must be able to ping the FQDN of every node in the cluster. -6. Some advanced network diagnostics tools +6. Verify that your firewall settings allow all hosts in the cluster to communicate on ports 6030 and 6041 (TCP and UDP). You can run `ufw status` (Ubuntu) or `firewall-cmd --list-port` (CentOS) to check the configuration. - - 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}` +7. If you are using the Python, Java, Go, Rust, C#, or Node.js connector on Linux to connect to the server, verify that `libtaos.so` is in the `/usr/local/taos/driver` directory and `/usr/local/taos/driver` is in the `LD_LIBRARY_PATH` environment variable. - - On Windows system `Test-NetConnection -ComputerName {fqdn} -Port {port}` on PowerShell can be used to check whether the port on server side is open for access. +8. If you are using Windows, verify that `C:\TDengine\driver\taos.dll` is in the `PATH` environment variable. If possible, move `taos.dll` to the `C:\Windows\System32` directory. -7. TDengine CLI `taos` can also be used to check network, please refer to [TDengine CLI](/reference/taos-shell). +9. On Linux systems, you can use the `nc` tool to check whether a port is accessible: + - To check whether a UDP port is open, run `nc -vuz {hostIP} {port}`. + - To check whether a TCP port on the server side is open, run `nc -l {port}`. + - To check whether a TCP port on client side is open, run `nc {hostIP} {port}`. -### 3. How to handle "Unexpected generic error in RPC" or "Unable to resolve FQDN" ? +10. On Windows systems, you can run `Test-NetConnection -ComputerName {fqdn} -Port {port}` in PowerShell to check whether a port on the server side is accessible. -This error is caused because the FQDN can't be resolved. Please try following ways: +11. You can also use the TDengine CLI to diagnose network issues. For more information, see [Problem Diagnostics](https://docs.tdengine.com/operation/diagnose/). -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. +### 5. How can I resolve the "Unable to resolve FQDN" error? -### 4. "Invalid SQL" is returned even though the Syntax is correct +Clients and dnodes must be able to resolve the FQDN of each required node. You can confirm your configuration as follows: -"Invalid SQL" is returned when the length of SQL statement exceeds maximum allowed length or the syntax is not correct. +1. Verify that the FQDN is configured properly on the server. +2. If your network has a DNS server, verify that it is operational. +3. If your network does not have a DNS server, verify that the FQDNs in the `hosts` file are correct. +4. On the client, use the `ping` command to test your connection to the server. If you cannot ping an FQDN, TDengine cannot reach it. +5. If TDengine has been previously installed and the `hostname` was modified, open `dnode.json` in the `data` folder and verify that the endpoint configuration is correct. The default location of the dnode file is `/var/lib/taos/dnode`. Ensure that you clean up previous installations before reinstalling TDengine. +6. Confirm whether FQDNs are preconfigured in `/etc/hosts` and `/etc/hostname`. -### 5. Whether validation queries are supported? +### 6. What is the most effective way to write data to TDengine? -It's suggested to use a builtin database named as `log` to monitor. +Writing data in batches provides higher efficiency in most situations. You can insert one or more data records into one or more tables in a single SQL statement. - +### 9. Why are table names not fully displayed? -### 6. Can I delete a record? +The number of columns in the TDengine CLI terminal display is limited. This can cause table names to be cut off, and if you use an incomplete name in a statement, the "Table does not exist" error will occur. You can increase the display size with the `maxBinaryDisplayWidth` parameter or the SQL statement `set max_binary_display_width`. You can also append `\G` to your SQL statement to bypass this limitation. -From version 2.6.0.0 Enterprise version, deleting data can be supported. +### 10. How can I migrate data? -### 7. How to create a table of over 1024 columns? +In TDengine, the `hostname` uniquely identifies a machine. When you move data files to a new machine, you must configure the new machine to have the same `host name` as the original machine. -From version 2.1.7.0, at most 4096 columns can be defined for a table. +:::note -### 8. How to improve the efficiency of inserting data? +The data structure of previous versions of TDengine is not compatible with version 3.0. To migrate from TDengine 1.x or 2.x to 3.0, you must export data from your older deployment and import it back into TDengine 3.0. -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? +### 11. How can I temporary change the log level from the TDengine Client? -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 +To change the log level for debugging purposes, you can use the following command: ```sql -ALTER LOCAL flag_name flag_value; +ALTER LOCAL local_option + +local_option: { + 'resetLog' + | 'rpcDebugFlag' value + | 'tmrDebugFlag' value + | 'cDebugFlag' value + | 'uDebugFlag' value + | 'debugFlag' value +} ``` - - flag_name can be: debugFlag,cDebugFlag,tmrDebugFlag,uDebugFlag,rpcDebugFlag - - flag_value can be: 131 (INFO/WARNING/ERROR), 135 (plus DEBUG), 143 (plus TRACE) - +Use `resetlog` to remove all logs generated on the local client. Use the other parameters to specify a log level for a specific component. -### 13. What to do if go compilation fails? +For each parameter, you can set the value to `131` (error and warning), `135` (error, warning, and debug), or `143` (error, warning, debug, and trace). -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. +### Why do TDengine components written in Go fail to compile? -```sh -go env -w GO111MODULE=on -go env -w GOPROXY=https://goproxy.cn,direct -``` +TDengine includes taosAdapter, an independent component written in Go. This component provides the REST API as well as data access for other products such as Prometheus and Telegraf. +When using the develop branch, you must run `git submodule update --init --recursive` to download the taosAdapter repository and then compile it. + +TDengine Go components require Go version 1.14 or later. + +### 13. How can I query the storage space being used by my data? + +The TDengine data files are stored in `/var/lib/taos` by default. Log files are stored in `/var/log/taos`. + +To see how much space your data files occupy, run `du -sh /var/lib/taos/vnode --exclude='wal'`. This excludes the write-ahead log (WAL) because its size is relatively fixed while writes are occurring, and it is written to disk and cleared when you shut down TDengine. + +If you want to see how much space is occupied by a single database, first determine which vgroup is storing the database by running `show vgroups`. Then check `/var/lib/taos/vnode` for the files associated with the vgroup ID. + +### 15. How is timezone information processed for timestamps? + +TDengine uses the timezone of the client for timestamps. The server timezone does not affect timestamps. The client converts Unix timestamps in SQL statements to UTC before sending them to the server. When you query data on the server, it provides timestamps in UTC to the client, which converts them to its local time. + +Timestamps are processed as follows: + +1. The client uses its system timezone unless it has been configured otherwise. +2. A timezone configured in `taos.cfg` takes precedence over the system timezone. +3. A timezone explicitly specified when establishing a connection to TDengine through a connector takes precedence over `taos.cfg` and the system timezone. For example, the Java connector allows you to specify a timezone in the JDBC URL. +4. If you use an RFC 3339 timestamp (2013-04-12T15:52:01.123+08:00), or an ISO 8601 timestamp (2013-04-12T15:52:01.123+0800), the timezone specified in the timestamp is used instead of the timestamps configured using any other method. + +### 16. Which network ports are required by TDengine? + +See [serverPort](https://docs.tdengine.com/reference/config/#serverport) in Configuration Parameters. + +Note that ports are specified using 6030 as the default first port. If you change this port, all other ports change as well. + +### 17. Why do applications such as Grafana fail to connect to TDengine over the REST API? + +In TDengine, the REST API is provided by taosAdapter. Ensure that taosAdapter is running before you connect an application to TDengine over the REST API. You can run `systemctl start taosadapter` to start the service. + +Note that the log path for taosAdapter must be configured separately. The default path is `/var/log/taos`. You can choose one of eight log levels. The default is `info`. You can set the log level to `panic` to disable log output. You can modify the taosAdapter configuration file to change these settings. The default location is `/etc/taos/taosadapter.toml`. + +For more information, see [taosAdapter](https://docs.tdengine.com/reference/taosadapter/). + +### 18. How can I resolve out-of-memory (OOM) errors? + +OOM errors are thrown by the operating system when its memory, including swap, becomes insufficient and it needs to terminate processes to remain operational. Most OOM errors in TDengine occur for one of the following reasons: free memory is less than the value of `vm.min_free_kbytes` or free memory is less than the size of the request. If TDengine occupies reserved memory, an OOM error can occur even when free memory is sufficient. + +TDengine preallocates memory to each vnode. The number of vnodes per database is determined by the `vgroups` parameter, and the amount of memory per vnode is determined by the `buffer` parameter. To prevent OOM errors from occurring, ensure that you prepare sufficient memory on your hosts to support the number of vnodes that your deployment requires. Configure an appropriately sized swap space. If you continue to receive OOM errors, your SQL statements may be querying too much data for your system. TDengine Enterprise Edition includes optimized memory management that increases stability for enterprise customers. From 4895f1987619000ae6069acc69da187e183639e1 Mon Sep 17 00:00:00 2001 From: Xiaoyu Wang Date: Thu, 25 Aug 2022 18:39:23 +0800 Subject: [PATCH 06/76] fix: plan problem with sorting the first column of the system table --- source/libs/parser/src/parTranslater.c | 2 +- source/libs/planner/src/planOptimizer.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/libs/parser/src/parTranslater.c b/source/libs/parser/src/parTranslater.c index 5a32c87fc3..d938325ef2 100644 --- a/source/libs/parser/src/parTranslater.c +++ b/source/libs/parser/src/parTranslater.c @@ -5001,7 +5001,7 @@ static int32_t checkCreateStream(STranslateContext* pCxt, SCreateStreamStmt* pSt return TSDB_CODE_SUCCESS; } - if (QUERY_NODE_SELECT_STMT != nodeType(pStmt->pQuery) || + if (QUERY_NODE_SELECT_STMT != nodeType(pStmt->pQuery) || NULL == ((SSelectStmt*)pStmt->pQuery)->pFromTable || QUERY_NODE_REAL_TABLE != nodeType(((SSelectStmt*)pStmt->pQuery)->pFromTable)) { return generateSyntaxErrMsgExt(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_STREAM_QUERY, "Unsupported stream query"); } diff --git a/source/libs/planner/src/planOptimizer.c b/source/libs/planner/src/planOptimizer.c index 45ab3903a9..653291d3a4 100644 --- a/source/libs/planner/src/planOptimizer.c +++ b/source/libs/planner/src/planOptimizer.c @@ -1084,7 +1084,7 @@ static int32_t sortPriKeyOptGetSequencingNodesImpl(SLogicNode* pNode, bool* pNot switch (nodeType(pNode)) { case QUERY_NODE_LOGIC_PLAN_SCAN: { SScanLogicNode* pScan = (SScanLogicNode*)pNode; - if (NULL != pScan->pGroupTags) { + if (NULL != pScan->pGroupTags || TSDB_SYSTEM_TABLE == pScan->tableType) { *pNotOptimize = true; return TSDB_CODE_SUCCESS; } From 8df8f90e19d04a891320f60c29b07a03a86b4c72 Mon Sep 17 00:00:00 2001 From: afwerar <1296468573@qq.com> Date: Fri, 26 Aug 2022 16:03:28 +0800 Subject: [PATCH 07/76] os: fix mac run error --- cmake/cmake.define | 5 +- cmake/cmake.install | 16 ++++ cmake/cmake.options | 6 ++ include/os/os.h | 1 + include/os/osSemaphore.h | 5 +- packaging/deb/DEBIAN/prerm | 2 +- packaging/release.bat | 18 +--- source/client/src/clientEnv.c | 2 + source/libs/wal/src/walMeta.c | 1 - source/os/src/osDir.c | 2 + source/os/src/osFile.c | 7 +- source/os/src/osSemaphore.c | 163 ++-------------------------------- source/os/src/osSysinfo.c | 8 +- source/util/src/tlog.c | 3 +- 14 files changed, 55 insertions(+), 184 deletions(-) diff --git a/cmake/cmake.define b/cmake/cmake.define index 989b69a89b..5d64815a9a 100644 --- a/cmake/cmake.define +++ b/cmake/cmake.define @@ -2,8 +2,6 @@ cmake_minimum_required(VERSION 3.0) set(CMAKE_VERBOSE_MAKEFILE OFF) -SET(BUILD_SHARED_LIBS "OFF") - #set output directory SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/build/lib) SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/build/bin) @@ -103,6 +101,9 @@ IF (TD_WINDOWS) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMMON_FLAGS}") ELSE () + IF (${TD_DARWIN}) + set(CMAKE_MACOSX_RPATH 0) + ENDIF () IF (${COVER} MATCHES "true") MESSAGE(STATUS "Test coverage mode, add extra flags") SET(GCC_COVERAGE_COMPILE_FLAGS "-fprofile-arcs -ftest-coverage") diff --git a/cmake/cmake.install b/cmake/cmake.install index 6dc6864975..fd1e080dda 100644 --- a/cmake/cmake.install +++ b/cmake/cmake.install @@ -1,3 +1,19 @@ +SET(PREPARE_ENV_CMD "prepare_env_cmd") +SET(PREPARE_ENV_TARGET "prepare_env_target") +ADD_CUSTOM_COMMAND(OUTPUT ${PREPARE_ENV_CMD} + POST_BUILD + COMMAND echo "make test directory" + DEPENDS taosd + COMMAND ${CMAKE_COMMAND} -E make_directory ${TD_TESTS_OUTPUT_DIR}/cfg/ + COMMAND ${CMAKE_COMMAND} -E make_directory ${TD_TESTS_OUTPUT_DIR}/log/ + COMMAND ${CMAKE_COMMAND} -E make_directory ${TD_TESTS_OUTPUT_DIR}/data/ + COMMAND ${CMAKE_COMMAND} -E echo dataDir ${TD_TESTS_OUTPUT_DIR}/data > ${TD_TESTS_OUTPUT_DIR}/cfg/taos.cfg + COMMAND ${CMAKE_COMMAND} -E echo logDir ${TD_TESTS_OUTPUT_DIR}/log >> ${TD_TESTS_OUTPUT_DIR}/cfg/taos.cfg + COMMAND ${CMAKE_COMMAND} -E echo charset UTF-8 >> ${TD_TESTS_OUTPUT_DIR}/cfg/taos.cfg + COMMAND ${CMAKE_COMMAND} -E echo monitor 0 >> ${TD_TESTS_OUTPUT_DIR}/cfg/taos.cfg + COMMENT "prepare taosd environment") +ADD_CUSTOM_TARGET(${PREPARE_ENV_TARGET} ALL WORKING_DIRECTORY ${TD_EXECUTABLE_OUTPUT_PATH} DEPENDS ${PREPARE_ENV_CMD}) + IF (TD_LINUX) SET(TD_MAKE_INSTALL_SH "${TD_SOURCE_DIR}/packaging/tools/make_install.sh") INSTALL(CODE "MESSAGE(\"make install script: ${TD_MAKE_INSTALL_SH}\")") diff --git a/cmake/cmake.options b/cmake/cmake.options index bec64f7bf0..3baccde4d7 100644 --- a/cmake/cmake.options +++ b/cmake/cmake.options @@ -90,6 +90,12 @@ ELSE () ENDIF () ENDIF () +option( + BUILD_SHARED_LIBS + "" + OFF + ) + option( RUST_BINDINGS "If build with rust-bindings" diff --git a/include/os/os.h b/include/os/os.h index b036002f8a..71966061a1 100644 --- a/include/os/os.h +++ b/include/os/os.h @@ -79,6 +79,7 @@ extern "C" { #include #include +#include "taoserror.h" #include "osAtomic.h" #include "osDef.h" #include "osDir.h" diff --git a/include/os/osSemaphore.h b/include/os/osSemaphore.h index 7fca20d75e..e52da96f01 100644 --- a/include/os/osSemaphore.h +++ b/include/os/osSemaphore.h @@ -23,10 +23,9 @@ extern "C" { #include #if defined(_TD_DARWIN_64) - +#include // typedef struct tsem_s *tsem_t; -typedef struct bosal_sem_t *tsem_t; - +typedef dispatch_semaphore_t tsem_t; int tsem_init(tsem_t *sem, int pshared, unsigned int value); int tsem_wait(tsem_t *sem); diff --git a/packaging/deb/DEBIAN/prerm b/packaging/deb/DEBIAN/prerm index 4953102842..65f261db2c 100644 --- a/packaging/deb/DEBIAN/prerm +++ b/packaging/deb/DEBIAN/prerm @@ -1,6 +1,6 @@ #!/bin/bash -if [ $1 -eq "abort-upgrade" ]; then +if [ "$1"x = "abort-upgrade"x ]; then exit 0 fi diff --git a/packaging/release.bat b/packaging/release.bat index ffd3a68048..14534c8d7e 100644 --- a/packaging/release.bat +++ b/packaging/release.bat @@ -40,10 +40,12 @@ if not exist %work_dir%\debug\ver-%2-x86 ( ) cd %work_dir%\debug\ver-%2-x64 call vcvarsall.bat x64 -cmake ../../ -G "NMake Makefiles JOM" -DCMAKE_MAKE_PROGRAM=jom -DBUILD_TOOLS=true -DBUILD_HTTP=false -DVERNUMBER=%2 -DCPUTYPE=x64 +cmake ../../ -G "NMake Makefiles JOM" -DCMAKE_MAKE_PROGRAM=jom -DBUILD_TOOLS=true -DBUILD_HTTP=false -BUILD_TEST=false -DVERNUMBER=%2 -DCPUTYPE=x64 cmake --build . rd /s /Q C:\TDengine cmake --install . +for /r c:\TDengine %%i in (*.dll) do signtool sign /f D:\\123.pfx /p taosdata %%i +for /r c:\TDengine %%i in (*.exe) do signtool sign /f D:\\123.pfx /p taosdata %%i if not %errorlevel% == 0 ( call :RUNFAILED build x64 failed & exit /b 1) cd %package_dir% iscc /DMyAppInstallName="%packagServerName_x64%" /DMyAppVersion="%2" /DMyAppExcludeSource="" tools\tdengine.iss /O..\release @@ -51,19 +53,7 @@ if not %errorlevel% == 0 ( call :RUNFAILED package %packagServerName_x64% faile iscc /DMyAppInstallName="%packagClientName_x64%" /DMyAppVersion="%2" /DMyAppExcludeSource="taosd.exe" tools\tdengine.iss /O..\release if not %errorlevel% == 0 ( call :RUNFAILED package %packagClientName_x64% failed & exit /b 1) -cd %work_dir%\debug\ver-%2-x86 -call vcvarsall.bat x86 -cmake ../../ -G "NMake Makefiles JOM" -DCMAKE_MAKE_PROGRAM=jom -DBUILD_TOOLS=true -DBUILD_HTTP=false -DVERNUMBER=%2 -DCPUTYPE=x86 -cmake --build . -rd /s /Q C:\TDengine -cmake --install . -if not %errorlevel% == 0 ( call :RUNFAILED build x86 failed & exit /b 1) -cd %package_dir% -@REM iscc /DMyAppInstallName="%packagServerName_x86%" /DMyAppVersion="%2" /DMyAppExcludeSource="" tools\tdengine.iss /O..\release -@REM if not %errorlevel% == 0 ( call :RUNFAILED package %packagServerName_x86% failed & exit /b 1) -iscc /DMyAppInstallName="%packagClientName_x86%" /DMyAppVersion="%2" /DMyAppExcludeSource="taosd.exe" tools\tdengine.iss /O..\release -if not %errorlevel% == 0 ( call :RUNFAILED package %packagClientName_x86% failed & exit /b 1) - +for /r ..\release %%i in (*.exe) do signtool sign /f d:\\123.pfx /p taosdata %%i goto EXIT0 :USAGE diff --git a/source/client/src/clientEnv.c b/source/client/src/clientEnv.c index ff1b9322c9..99ecab9642 100644 --- a/source/client/src/clientEnv.c +++ b/source/client/src/clientEnv.c @@ -393,7 +393,9 @@ void taos_init_imp(void) { schedulerInit(); tscDebug("starting to initialize TAOS driver"); +#ifndef WINDOWS taosSetCoreDump(true); +#endif initTaskQueue(); fmFuncMgtInit(); diff --git a/source/libs/wal/src/walMeta.c b/source/libs/wal/src/walMeta.c index a8da680910..0983d344c1 100644 --- a/source/libs/wal/src/walMeta.c +++ b/source/libs/wal/src/walMeta.c @@ -221,7 +221,6 @@ int walCheckAndRepairMeta(SWal* pWal) { int code = walSaveMeta(pWal); if (code < 0) { - taosArrayDestroy(actualLog); return -1; } } diff --git a/source/os/src/osDir.c b/source/os/src/osDir.c index b755a35815..30aaa01dae 100644 --- a/source/os/src/osDir.c +++ b/source/os/src/osDir.c @@ -133,6 +133,7 @@ int32_t taosMulMkDir(const char *dirname) { code = mkdir(temp, 0755); #endif if (code < 0 && errno != EEXIST) { + terrno = TAOS_SYSTEM_ERROR(errno); return code; } *pos = TD_DIRSEP[0]; @@ -146,6 +147,7 @@ int32_t taosMulMkDir(const char *dirname) { code = mkdir(temp, 0755); #endif if (code < 0 && errno != EEXIST) { + terrno = TAOS_SYSTEM_ERROR(errno); return code; } } diff --git a/source/os/src/osFile.c b/source/os/src/osFile.c index 2d9cfe3246..fab933755a 100644 --- a/source/os/src/osFile.c +++ b/source/os/src/osFile.c @@ -203,10 +203,11 @@ int32_t taosRenameFile(const char *oldName, const char *newName) { } int32_t taosStatFile(const char *path, int64_t *size, int32_t *mtime) { - struct stat fileStat; #ifdef WINDOWS - int32_t code = _stat(path, &fileStat); + struct _stati64 fileStat; + int32_t code = _stati64(path, &fileStat); #else + struct stat fileStat; int32_t code = stat(path, &fileStat); #endif if (code < 0) { @@ -312,6 +313,7 @@ TdFilePtr taosOpenFile(const char *path, int32_t tdFileOptions) { assert(!(tdFileOptions & TD_FILE_EXCL)); fp = fopen(path, mode); if (fp == NULL) { + terrno = TAOS_SYSTEM_ERROR(errno); return NULL; } } else { @@ -334,6 +336,7 @@ TdFilePtr taosOpenFile(const char *path, int32_t tdFileOptions) { fd = open(path, access, S_IRWXU | S_IRWXG | S_IRWXO); #endif if (fd == -1) { + terrno = TAOS_SYSTEM_ERROR(errno); return NULL; } } diff --git a/source/os/src/osSemaphore.c b/source/os/src/osSemaphore.c index a7d2ba8531..8cc6f0ef2e 100644 --- a/source/os/src/osSemaphore.c +++ b/source/os/src/osSemaphore.c @@ -392,179 +392,32 @@ int32_t tsem_timewait(tsem_t* sem, int64_t nanosecs) { // *sem = NULL; // return 0; // } -typedef struct { - pthread_mutex_t count_lock; - pthread_cond_t count_bump; - unsigned int count; -} bosal_sem_t; int tsem_init(tsem_t *psem, int flags, unsigned int count) { - bosal_sem_t *pnewsem; - int result; - - pnewsem = (bosal_sem_t *)malloc(sizeof(bosal_sem_t)); - if (!pnewsem) { - return -1; - } - result = pthread_mutex_init(&pnewsem->count_lock, NULL); - if (result) { - free(pnewsem); - return result; - } - result = pthread_cond_init(&pnewsem->count_bump, NULL); - if (result) { - pthread_mutex_destroy(&pnewsem->count_lock); - free(pnewsem); - return result; - } - pnewsem->count = count; - *psem = (tsem_t)pnewsem; + *psem = dispatch_semaphore_create(count); + if (*psem == NULL) return -1; return 0; } int tsem_destroy(tsem_t *psem) { - bosal_sem_t *poldsem; - - if (!psem) { - return EINVAL; - } - poldsem = (bosal_sem_t *)*psem; - - pthread_mutex_destroy(&poldsem->count_lock); - pthread_cond_destroy(&poldsem->count_bump); - free(poldsem); return 0; } int tsem_post(tsem_t *psem) { - bosal_sem_t *pxsem; - int result, xresult; - - if (!psem) { - return EINVAL; - } - pxsem = (bosal_sem_t *)*psem; - - result = pthread_mutex_lock(&pxsem->count_lock); - if (result) { - return result; - } - pxsem->count = pxsem->count + 1; - - xresult = pthread_cond_signal(&pxsem->count_bump); - - result = pthread_mutex_unlock(&pxsem->count_lock); - if (result) { - return result; - } - if (xresult) { - errno = xresult; - return -1; - } - return 0; -} - -int tsem_trywait(tsem_t *psem) { - bosal_sem_t *pxsem; - int result, xresult; - - if (!psem) { - return EINVAL; - } - pxsem = (bosal_sem_t *)*psem; - - result = pthread_mutex_lock(&pxsem->count_lock); - if (result) { - return result; - } - xresult = 0; - - if (pxsem->count > 0) { - pxsem->count--; - } else { - xresult = EAGAIN; - } - result = pthread_mutex_unlock(&pxsem->count_lock); - if (result) { - return result; - } - if (xresult) { - errno = xresult; - return -1; - } + if (psem == NULL || *psem == NULL) return -1; + dispatch_semaphore_signal(*psem); return 0; } int tsem_wait(tsem_t *psem) { - bosal_sem_t *pxsem; - int result, xresult; - - if (!psem) { - return EINVAL; - } - pxsem = (bosal_sem_t *)*psem; - - result = pthread_mutex_lock(&pxsem->count_lock); - if (result) { - return result; - } - xresult = 0; - - if (pxsem->count == 0) { - xresult = pthread_cond_wait(&pxsem->count_bump, &pxsem->count_lock); - } - if (!xresult) { - if (pxsem->count > 0) { - pxsem->count--; - } - } - result = pthread_mutex_unlock(&pxsem->count_lock); - if (result) { - return result; - } - if (xresult) { - errno = xresult; - return -1; - } + if (psem == NULL || *psem == NULL) return -1; + dispatch_semaphore_wait(*psem, DISPATCH_TIME_FOREVER); return 0; } int tsem_timewait(tsem_t *psem, int64_t nanosecs) { - struct timespec abstim = { - .tv_sec = 0, - .tv_nsec = nanosecs, - }; - - bosal_sem_t *pxsem; - int result, xresult; - - if (!psem) { - return EINVAL; - } - pxsem = (bosal_sem_t *)*psem; - - result = pthread_mutex_lock(&pxsem->count_lock); - if (result) { - return result; - } - xresult = 0; - - if (pxsem->count == 0) { - xresult = pthread_cond_timedwait(&pxsem->count_bump, &pxsem->count_lock, &abstim); - } - if (!xresult) { - if (pxsem->count > 0) { - pxsem->count--; - } - } - result = pthread_mutex_unlock(&pxsem->count_lock); - if (result) { - return result; - } - if (xresult) { - errno = xresult; - return -1; - } + if (psem == NULL || *psem == NULL) return -1; + dispatch_semaphore_wait(*psem, nanosecs); return 0; } diff --git a/source/os/src/osSysinfo.c b/source/os/src/osSysinfo.c index 3a75e18a7f..19e9568bbe 100644 --- a/source/os/src/osSysinfo.c +++ b/source/os/src/osSysinfo.c @@ -595,6 +595,7 @@ int32_t taosGetDiskSize(char *dataDir, SDiskSize *diskSize) { #else struct statvfs info; if (statvfs(dataDir, &info)) { + terrno = TAOS_SYSTEM_ERROR(errno); return -1; } else { diskSize->total = info.f_blocks * info.f_frsize; @@ -851,13 +852,12 @@ char *taosGetCmdlineByPID(int pid) { } void taosSetCoreDump(bool enable) { + if (!enable) return; #ifdef WINDOWS - // SetUnhandledExceptionFilter(exceptionHandler); - // SetUnhandledExceptionFilter(&FlCrashDump); + SetUnhandledExceptionFilter(exceptionHandler); + SetUnhandledExceptionFilter(&FlCrashDump); #elif defined(_TD_DARWIN_64) #else - if (!enable) return; - // 1. set ulimit -c unlimited struct rlimit rlim; struct rlimit rlim_new; diff --git a/source/util/src/tlog.c b/source/util/src/tlog.c index 2e8239c68f..a2d65d6a54 100644 --- a/source/util/src/tlog.c +++ b/source/util/src/tlog.c @@ -429,7 +429,7 @@ static inline int32_t taosBuildLogHead(char *buffer, const char *flags) { } static inline void taosPrintLogImp(ELogLevel level, int32_t dflag, const char *buffer, int32_t len) { - if ((dflag & DEBUG_FILE) && tsLogObj.logHandle && tsLogObj.logHandle->pFile != NULL) { + if ((dflag & DEBUG_FILE) && tsLogObj.logHandle && tsLogObj.logHandle->pFile != NULL && osLogSpaceAvailable()) { taosUpdateLogNums(level); if (tsAsyncLog) { taosPushLogBuffer(tsLogObj.logHandle, buffer, len); @@ -451,7 +451,6 @@ static inline void taosPrintLogImp(ELogLevel level, int32_t dflag, const char *b } void taosPrintLog(const char *flags, ELogLevel level, int32_t dflag, const char *format, ...) { - if (!osLogSpaceAvailable()) return; if (!(dflag & DEBUG_FILE) && !(dflag & DEBUG_SCREEN)) return; char buffer[LOG_MAX_LINE_BUFFER_SIZE]; From d18e7cd739d82221d41610441c8c779fcb3c2630 Mon Sep 17 00:00:00 2001 From: dapan1121 Date: Fri, 26 Aug 2022 16:20:07 +0800 Subject: [PATCH 08/76] enh: remove compare type convertion --- include/util/tcompare.h | 91 ++++ source/libs/scalar/inc/filterInt.h | 1 + source/libs/scalar/inc/sclInt.h | 1 + source/libs/scalar/src/filter.c | 158 +++++++ source/libs/scalar/src/sclvector.c | 38 +- source/util/src/tcompare.c | 722 +++++++++++++++++++++++++++++ 6 files changed, 996 insertions(+), 15 deletions(-) diff --git a/include/util/tcompare.h b/include/util/tcompare.h index cc9e8ae464..c7a3ca20f2 100644 --- a/include/util/tcompare.h +++ b/include/util/tcompare.h @@ -105,6 +105,97 @@ int32_t compareStrPatternNotMatch(const void *pLeft, const void *pRight); int32_t compareWStrPatternMatch(const void *pLeft, const void *pRight); int32_t compareWStrPatternNotMatch(const void *pLeft, const void *pRight); +int32_t compareInt8Int16(const void *pLeft, const void *pRight); +int32_t compareInt8Int32(const void *pLeft, const void *pRight); +int32_t compareInt8Int64(const void *pLeft, const void *pRight); +int32_t compareInt8Float(const void *pLeft, const void *pRight); +int32_t compareInt8Double(const void *pLeft, const void *pRight); +int32_t compareInt8Uint8(const void *pLeft, const void *pRight); +int32_t compareInt8Uint16(const void *pLeft, const void *pRight); +int32_t compareInt8Uint32(const void *pLeft, const void *pRight); +int32_t compareInt8Uint64(const void *pLeft, const void *pRight); +int32_t compareInt16Int8(const void *pLeft, const void *pRight); +int32_t compareInt16Int32(const void *pLeft, const void *pRight); +int32_t compareInt16Int64(const void *pLeft, const void *pRight); +int32_t compareInt16Float(const void *pLeft, const void *pRight); +int32_t compareInt16Double(const void *pLeft, const void *pRight); +int32_t compareInt16Uint8(const void *pLeft, const void *pRight); +int32_t compareInt16Uint16(const void *pLeft, const void *pRight); +int32_t compareInt16Uint32(const void *pLeft, const void *pRight); +int32_t compareInt16Uint64(const void *pLeft, const void *pRight); +int32_t compareInt32Int8(const void *pLeft, const void *pRight); +int32_t compareInt32Int16(const void *pLeft, const void *pRight); +int32_t compareInt32Int64(const void *pLeft, const void *pRight); +int32_t compareInt32Float(const void *pLeft, const void *pRight); +int32_t compareInt32Double(const void *pLeft, const void *pRight); +int32_t compareInt32Uint8(const void *pLeft, const void *pRight); +int32_t compareInt32Uint16(const void *pLeft, const void *pRight); +int32_t compareInt32Uint32(const void *pLeft, const void *pRight); +int32_t compareInt32Uint64(const void *pLeft, const void *pRight); +int32_t compareInt64Int8(const void *pLeft, const void *pRight); +int32_t compareInt64Int16(const void *pLeft, const void *pRight); +int32_t compareInt64Int32(const void *pLeft, const void *pRight); +int32_t compareInt64Float(const void *pLeft, const void *pRight); +int32_t compareInt64Double(const void *pLeft, const void *pRight); +int32_t compareInt64Uint8(const void *pLeft, const void *pRight); +int32_t compareInt64Uint16(const void *pLeft, const void *pRight); +int32_t compareInt64Uint32(const void *pLeft, const void *pRight); +int32_t compareInt64Uint64(const void *pLeft, const void *pRight); +int32_t compareFloatInt8(const void *pLeft, const void *pRight); +int32_t compareFloatInt16(const void *pLeft, const void *pRight); +int32_t compareFloatInt32(const void *pLeft, const void *pRight); +int32_t compareFloatInt64(const void *pLeft, const void *pRight); +int32_t compareFloatDouble(const void *pLeft, const void *pRight); +int32_t compareFloatUint8(const void *pLeft, const void *pRight); +int32_t compareFloatUint16(const void *pLeft, const void *pRight); +int32_t compareFloatUint32(const void *pLeft, const void *pRight); +int32_t compareFloatUint64(const void *pLeft, const void *pRight); +int32_t compareDoubleInt8(const void *pLeft, const void *pRight); +int32_t compareDoubleInt16(const void *pLeft, const void *pRight); +int32_t compareDoubleInt32(const void *pLeft, const void *pRight); +int32_t compareDoubleInt64(const void *pLeft, const void *pRight); +int32_t compareDoubleFloat(const void *pLeft, const void *pRight); +int32_t compareDoubleUint8(const void *pLeft, const void *pRight); +int32_t compareDoubleUint16(const void *pLeft, const void *pRight); +int32_t compareDoubleUint32(const void *pLeft, const void *pRight); +int32_t compareDoubleUint64(const void *pLeft, const void *pRight); +int32_t compareUint8Int8(const void *pLeft, const void *pRight); +int32_t compareUint8Int16(const void *pLeft, const void *pRight); +int32_t compareUint8Int32(const void *pLeft, const void *pRight); +int32_t compareUint8Int64(const void *pLeft, const void *pRight); +int32_t compareUint8Float(const void *pLeft, const void *pRight); +int32_t compareUint8Double(const void *pLeft, const void *pRight); +int32_t compareUint8Uint16(const void *pLeft, const void *pRight); +int32_t compareUint8Uint32(const void *pLeft, const void *pRight); +int32_t compareUint8Uint64(const void *pLeft, const void *pRight); +int32_t compareUint16Int8(const void *pLeft, const void *pRight); +int32_t compareUint16Int16(const void *pLeft, const void *pRight); +int32_t compareUint16Int32(const void *pLeft, const void *pRight); +int32_t compareUint16Int64(const void *pLeft, const void *pRight); +int32_t compareUint16Float(const void *pLeft, const void *pRight); +int32_t compareUint16Double(const void *pLeft, const void *pRight); +int32_t compareUint16Uint8(const void *pLeft, const void *pRight); +int32_t compareUint16Uint32(const void *pLeft, const void *pRight); +int32_t compareUint16Uint64(const void *pLeft, const void *pRight); +int32_t compareUint32Int8(const void *pLeft, const void *pRight); +int32_t compareUint32Int16(const void *pLeft, const void *pRight); +int32_t compareUint32Int32(const void *pLeft, const void *pRight); +int32_t compareUint32Int64(const void *pLeft, const void *pRight); +int32_t compareUint32Float(const void *pLeft, const void *pRight); +int32_t compareUint32Double(const void *pLeft, const void *pRight); +int32_t compareUint32Uint8(const void *pLeft, const void *pRight); +int32_t compareUint32Uint16(const void *pLeft, const void *pRight); +int32_t compareUint32Uint64(const void *pLeft, const void *pRight); +int32_t compareUint64Int8(const void *pLeft, const void *pRight); +int32_t compareUint64Int16(const void *pLeft, const void *pRight); +int32_t compareUint64Int32(const void *pLeft, const void *pRight); +int32_t compareUint64Int64(const void *pLeft, const void *pRight); +int32_t compareUint64Float(const void *pLeft, const void *pRight); +int32_t compareUint64Double(const void *pLeft, const void *pRight); +int32_t compareUint64Uint8(const void *pLeft, const void *pRight); +int32_t compareUint64Uint16(const void *pLeft, const void *pRight); +int32_t compareUint64Uint32(const void *pLeft, const void *pRight); + __compar_fn_t getComparFunc(int32_t type, int32_t optr); __compar_fn_t getKeyComparFunc(int32_t keyType, int32_t order); int32_t doCompare(const char *a, const char *b, int32_t type, size_t size); diff --git a/source/libs/scalar/inc/filterInt.h b/source/libs/scalar/inc/filterInt.h index 23693c785a..87327a3657 100644 --- a/source/libs/scalar/inc/filterInt.h +++ b/source/libs/scalar/inc/filterInt.h @@ -350,6 +350,7 @@ struct SFilterInfo { extern bool filterDoCompare(__compar_fn_t func, uint8_t optr, void *left, void *right); extern __compar_fn_t filterGetCompFunc(int32_t type, int32_t optr); +extern __compar_fn_t filterGetCompFuncEx(int32_t lType, int32_t rType, int32_t optr); #ifdef __cplusplus } diff --git a/source/libs/scalar/inc/sclInt.h b/source/libs/scalar/inc/sclInt.h index 36d2c5a49c..15e9026ddb 100644 --- a/source/libs/scalar/inc/sclInt.h +++ b/source/libs/scalar/inc/sclInt.h @@ -47,6 +47,7 @@ typedef struct SScalarCtx { #define SCL_IS_NULL_VALUE_NODE(_node) ((QUERY_NODE_VALUE == nodeType(_node)) && (TSDB_DATA_TYPE_NULL == ((SValueNode *)_node)->node.resType.type)) #define SCL_IS_COMPARISON_OPERATOR(_opType) ((_opType) >= OP_TYPE_GREATER_THAN && (_opType) < OP_TYPE_IS_NOT_UNKNOWN) #define SCL_DOWNGRADE_DATETYPE(_type) ((_type) == TSDB_DATA_TYPE_BIGINT || TSDB_DATA_TYPE_DOUBLE == (_type) || (_type) == TSDB_DATA_TYPE_UBIGINT) +#define SCL_NO_NEED_CONVERT_COMPARISION(_ltype, _rtype, _optr) (IS_NUMERIC_TYPE(_ltype) && IS_NUMERIC_TYPE(_rtype) && ((_optr) >= OP_TYPE_GREATER_THAN && (_optr) <= OP_TYPE_NOT_EQUAL)) #define sclFatal(...) qFatal(__VA_ARGS__) #define sclError(...) qError(__VA_ARGS__) diff --git a/source/libs/scalar/src/filter.c b/source/libs/scalar/src/filter.c index 4377dbf14e..5555a52c8e 100644 --- a/source/libs/scalar/src/filter.c +++ b/source/libs/scalar/src/filter.c @@ -132,6 +132,77 @@ __compar_fn_t gDataCompare[] = {compareInt32Val, compareInt8Val, compareInt16Val compareChkNotInString, compareStrPatternNotMatch, compareWStrPatternNotMatch }; +__compar_fn_t gInt8SignCompare[] = { + compareInt8Val, compareInt8Int16, compareInt8Int32, compareInt8Int64, compareInt8Float, compareInt8Double +}; +__compar_fn_t gInt8UsignCompare[] = { + compareInt8Uint8, compareInt8Uint16, compareInt8Uint32, compareInt8Uint64 +}; + +__compar_fn_t gInt16SignCompare[] = { + compareInt16Int8, compareInt16Val, compareInt16Int32, compareInt16Int64, compareInt16Float, compareInt16Double +}; +__compar_fn_t gInt16UsignCompare[] = { + compareInt16Uint8, compareInt16Uint16, compareInt16Uint32, compareInt16Uint64 +}; + +__compar_fn_t gInt32SignCompare[] = { + compareInt32Int8, compareInt32Int16, compareInt32Val, compareInt32Int64, compareInt32Float, compareInt32Double +}; +__compar_fn_t gInt32UsignCompare[] = { + compareInt32Uint8, compareInt32Uint16, compareInt32Uint32, compareInt32Uint64 +}; + +__compar_fn_t gInt64SignCompare[] = { + compareInt64Int8, compareInt64Int16, compareInt64Int32, compareInt64Val, compareInt64Float, compareInt64Double +}; +__compar_fn_t gInt64UsignCompare[] = { + compareInt64Uint8, compareInt64Uint16, compareInt64Uint32, compareInt64Uint64 +}; + +__compar_fn_t gFloatSignCompare[] = { + compareFloatInt8, compareFloatInt16, compareFloatInt32, compareFloatInt64, compareFloatVal, compareFloatDouble +}; +__compar_fn_t gFloatUsignCompare[] = { + compareFloatUint8, compareFloatUint16, compareFloatUint32, compareFloatUint64 +}; + +__compar_fn_t gDoubleSignCompare[] = { + compareDoubleInt8, compareDoubleInt16, compareDoubleInt32, compareDoubleInt64, compareDoubleFloat, compareDoubleVal +}; +__compar_fn_t gDoubleUsignCompare[] = { + compareDoubleUint8, compareDoubleUint16, compareDoubleUint32, compareDoubleUint64 +}; + +__compar_fn_t gUint8SignCompare[] = { + compareUint8Int8, compareUint8Int16, compareUint8Int32, compareUint8Int64, compareUint8Float, compareUint8Double +}; +__compar_fn_t gUint8UsignCompare[] = { + compareUint8Val, compareUint8Uint16, compareUint8Uint32, compareUint8Uint64 +}; + +__compar_fn_t gUint16SignCompare[] = { + compareUint16Int8, compareUint16Int16, compareUint16Int32, compareUint16Int64, compareUint16Float, compareUint16Double +}; +__compar_fn_t gUint16UsignCompare[] = { + compareUint16Uint8, compareUint16Val, compareUint16Uint32, compareUint16Uint64 +}; + +__compar_fn_t gUint32SignCompare[] = { + compareUint32Int8, compareUint32Int16, compareUint32Int32, compareUint32Int64, compareUint32Float, compareUint32Double +}; +__compar_fn_t gUint32UsignCompare[] = { + compareUint32Uint8, compareUint32Uint16, compareUint32Val, compareUint32Uint64 +}; + +__compar_fn_t gUint64SignCompare[] = { + compareUint64Int8, compareUint64Int16, compareUint64Int32, compareUint64Int64, compareUint64Float, compareUint64Double +}; +__compar_fn_t gUint64UsignCompare[] = { + compareUint64Uint8, compareUint64Uint16, compareUint64Uint32, compareUint64Val +}; + + int8_t filterGetCompFuncIdx(int32_t type, int32_t optr) { int8_t comparFn = 0; @@ -257,6 +328,93 @@ __compar_fn_t filterGetCompFunc(int32_t type, int32_t optr) { return gDataCompare[filterGetCompFuncIdx(type, optr)]; } +__compar_fn_t filterGetCompFuncEx(int32_t lType, int32_t rType, int32_t optr) { + switch (lType) { + case TSDB_DATA_TYPE_TINYINT: { + if (IS_SIGNED_NUMERIC_TYPE(rType) || IS_FLOAT_TYPE(rType)) { + return gInt8SignCompare[rType - TSDB_DATA_TYPE_TINYINT]; + } else { + return gInt8UsignCompare[rType - TSDB_DATA_TYPE_UTINYINT]; + } + break; + } + case TSDB_DATA_TYPE_SMALLINT: { + if (IS_SIGNED_NUMERIC_TYPE(rType) || IS_FLOAT_TYPE(rType)) { + return gInt16SignCompare[rType - TSDB_DATA_TYPE_TINYINT]; + } else { + return gInt16UsignCompare[rType - TSDB_DATA_TYPE_UTINYINT]; + } + break; + } + case TSDB_DATA_TYPE_INT: { + if (IS_SIGNED_NUMERIC_TYPE(rType) || IS_FLOAT_TYPE(rType)) { + return gInt32SignCompare[rType - TSDB_DATA_TYPE_TINYINT]; + } else { + return gInt32UsignCompare[rType - TSDB_DATA_TYPE_UTINYINT]; + } + break; + } + case TSDB_DATA_TYPE_BIGINT: { + if (IS_SIGNED_NUMERIC_TYPE(rType) || IS_FLOAT_TYPE(rType)) { + return gInt64SignCompare[rType - TSDB_DATA_TYPE_TINYINT]; + } else { + return gInt64UsignCompare[rType - TSDB_DATA_TYPE_UTINYINT]; + } + break; + } + case TSDB_DATA_TYPE_FLOAT: { + if (IS_SIGNED_NUMERIC_TYPE(rType) || IS_FLOAT_TYPE(rType)) { + return gFloatSignCompare[rType - TSDB_DATA_TYPE_TINYINT]; + } else { + return gFloatUsignCompare[rType - TSDB_DATA_TYPE_UTINYINT]; + } + break; + } + case TSDB_DATA_TYPE_DOUBLE: { + if (IS_SIGNED_NUMERIC_TYPE(rType) || IS_FLOAT_TYPE(rType)) { + return gDoubleSignCompare[rType - TSDB_DATA_TYPE_TINYINT]; + } else { + return gDoubleUsignCompare[rType - TSDB_DATA_TYPE_UTINYINT]; + } + break; + } + case TSDB_DATA_TYPE_UTINYINT: { + if (IS_SIGNED_NUMERIC_TYPE(rType) || IS_FLOAT_TYPE(rType)) { + return gUint8SignCompare[rType - TSDB_DATA_TYPE_TINYINT]; + } else { + return gUint8UsignCompare[rType - TSDB_DATA_TYPE_UTINYINT]; + } + break; + } + case TSDB_DATA_TYPE_USMALLINT: { + if (IS_SIGNED_NUMERIC_TYPE(rType) || IS_FLOAT_TYPE(rType)) { + return gUint16SignCompare[rType - TSDB_DATA_TYPE_TINYINT]; + } else { + return gUint16UsignCompare[rType - TSDB_DATA_TYPE_UTINYINT]; + } + break; + } + case TSDB_DATA_TYPE_UINT: { + if (IS_SIGNED_NUMERIC_TYPE(rType) || IS_FLOAT_TYPE(rType)) { + return gUint32SignCompare[rType - TSDB_DATA_TYPE_TINYINT]; + } else { + return gUint32UsignCompare[rType - TSDB_DATA_TYPE_UTINYINT]; + } + break; + } + case TSDB_DATA_TYPE_UBIGINT: { + if (IS_SIGNED_NUMERIC_TYPE(rType) || IS_FLOAT_TYPE(rType)) { + return gUint64SignCompare[rType - TSDB_DATA_TYPE_TINYINT]; + } else { + return gUint64UsignCompare[rType - TSDB_DATA_TYPE_UTINYINT]; + } + break; + } + default: + break; + } + return NULL; +} static FORCE_INLINE int32_t filterCompareGroupCtx(const void *pLeft, const void *pRight) { SFilterGroupCtx *left = *((SFilterGroupCtx**)pLeft), *right = *((SFilterGroupCtx**)pRight); diff --git a/source/libs/scalar/src/sclvector.c b/source/libs/scalar/src/sclvector.c index aaa70ef5ae..caccf4bfac 100644 --- a/source/libs/scalar/src/sclvector.c +++ b/source/libs/scalar/src/sclvector.c @@ -1681,10 +1681,14 @@ void vectorBitOr(SScalarParam* pLeft, SScalarParam* pRight, SScalarParam *pOut, void vectorCompareImpl(SScalarParam* pLeft, SScalarParam* pRight, SScalarParam *pOut, int32_t _ord, int32_t optr) { int32_t i = ((_ord) == TSDB_ORDER_ASC) ? 0 : TMAX(pLeft->numOfRows, pRight->numOfRows) - 1; int32_t step = ((_ord) == TSDB_ORDER_ASC) ? 1 : -1; - - __compar_fn_t fp = filterGetCompFunc(GET_PARAM_TYPE(pLeft), optr); - if(terrno != TSDB_CODE_SUCCESS){ - return; + int32_t lType = GET_PARAM_TYPE(pLeft); + int32_t rType = GET_PARAM_TYPE(pRight); + __compar_fn_t fp = NULL; + + if (lType == rType) { + fp = filterGetCompFunc(lType, optr); + } else { + fp = filterGetCompFuncEx(lType, rType, optr); } pOut->numOfRows = TMAX(pLeft->numOfRows, pRight->numOfRows); @@ -1716,22 +1720,26 @@ void vectorCompareImpl(SScalarParam* pLeft, SScalarParam* pRight, SScalarParam * void vectorCompare(SScalarParam* pLeft, SScalarParam* pRight, SScalarParam *pOut, int32_t _ord, int32_t optr) { SScalarParam pLeftOut = {0}; SScalarParam pRightOut = {0}; - - vectorConvert(pLeft, pRight, &pLeftOut, &pRightOut); - SScalarParam *param1 = NULL; SScalarParam *param2 = NULL; - if (pLeftOut.columnData != NULL) { - param1 = &pLeftOut; - } else { + if (SCL_NO_NEED_CONVERT_COMPARISION(GET_PARAM_TYPE(pLeft), GET_PARAM_TYPE(pRight), optr)) { param1 = pLeft; - } - - if (pRightOut.columnData != NULL) { - param2 = &pRightOut; - } else { param2 = pRight; + } else { + vectorConvert(pLeft, pRight, &pLeftOut, &pRightOut); + + if (pLeftOut.columnData != NULL) { + param1 = &pLeftOut; + } else { + param1 = pLeft; + } + + if (pRightOut.columnData != NULL) { + param2 = &pRightOut; + } else { + param2 = pRight; + } } vectorCompareImpl(param1, param2, pOut, _ord, optr); diff --git a/source/util/src/tcompare.c b/source/util/src/tcompare.c index fe3065b2b7..adebb09912 100644 --- a/source/util/src/tcompare.c +++ b/source/util/src/tcompare.c @@ -246,6 +246,728 @@ int32_t compareJsonVal(const void *pLeft, const void *pRight) { } } +int32_t compareInt8Int16(const void *pLeft, const void *pRight) { + int8_t left = GET_INT32_VAL(pLeft); + int16_t right = GET_INT16_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareInt8Int32(const void *pLeft, const void *pRight) { + int8_t left = GET_INT32_VAL(pLeft); + int32_t right = GET_INT32_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareInt8Int64(const void *pLeft, const void *pRight) { + int8_t left = GET_INT32_VAL(pLeft); + int64_t right = GET_INT64_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareInt8Float(const void *pLeft, const void *pRight) { + int8_t left = GET_INT32_VAL(pLeft); + float right = GET_FLOAT_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareInt8Double(const void *pLeft, const void *pRight) { + int8_t left = GET_INT32_VAL(pLeft); + double right = GET_DOUBLE_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareInt8Uint8(const void *pLeft, const void *pRight) { + int8_t left = GET_INT32_VAL(pLeft); + uint8_t right = GET_UINT8_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareInt8Uint16(const void *pLeft, const void *pRight) { + int8_t left = GET_INT32_VAL(pLeft); + uint16_t right = GET_UINT16_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareInt8Uint32(const void *pLeft, const void *pRight) { + int8_t left = GET_INT32_VAL(pLeft); + uint32_t right = GET_UINT32_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareInt8Uint64(const void *pLeft, const void *pRight) { + int8_t left = GET_INT32_VAL(pLeft); + uint64_t right = GET_UINT64_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareInt16Int8(const void *pLeft, const void *pRight) { + int16_t left = GET_INT32_VAL(pLeft); + int8_t right = GET_INT8_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareInt16Int32(const void *pLeft, const void *pRight) { + int16_t left = GET_INT32_VAL(pLeft); + int32_t right = GET_INT32_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareInt16Int64(const void *pLeft, const void *pRight) { + int16_t left = GET_INT32_VAL(pLeft); + int64_t right = GET_INT64_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareInt16Float(const void *pLeft, const void *pRight) { + int16_t left = GET_INT32_VAL(pLeft); + float right = GET_FLOAT_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareInt16Double(const void *pLeft, const void *pRight) { + int16_t left = GET_INT32_VAL(pLeft); + double right = GET_DOUBLE_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareInt16Uint8(const void *pLeft, const void *pRight) { + int16_t left = GET_INT32_VAL(pLeft); + uint8_t right = GET_UINT8_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareInt16Uint16(const void *pLeft, const void *pRight) { + int16_t left = GET_INT32_VAL(pLeft); + uint16_t right = GET_UINT16_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareInt16Uint32(const void *pLeft, const void *pRight) { + int16_t left = GET_INT32_VAL(pLeft); + uint32_t right = GET_UINT32_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareInt16Uint64(const void *pLeft, const void *pRight) { + int16_t left = GET_INT32_VAL(pLeft); + uint64_t right = GET_UINT64_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + + +int32_t compareInt32Int8(const void *pLeft, const void *pRight) { + int32_t left = GET_INT32_VAL(pLeft); + int8_t right = GET_INT8_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareInt32Int16(const void *pLeft, const void *pRight) { + int32_t left = GET_INT32_VAL(pLeft); + int16_t right = GET_INT16_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareInt32Int64(const void *pLeft, const void *pRight) { + int32_t left = GET_INT32_VAL(pLeft); + int64_t right = GET_INT64_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareInt32Float(const void *pLeft, const void *pRight) { + int32_t left = GET_INT32_VAL(pLeft); + float right = GET_FLOAT_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareInt32Double(const void *pLeft, const void *pRight) { + int32_t left = GET_INT32_VAL(pLeft); + double right = GET_DOUBLE_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareInt32Uint8(const void *pLeft, const void *pRight) { + int32_t left = GET_INT32_VAL(pLeft); + uint8_t right = GET_UINT8_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareInt32Uint16(const void *pLeft, const void *pRight) { + int32_t left = GET_INT32_VAL(pLeft); + uint16_t right = GET_UINT16_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareInt32Uint32(const void *pLeft, const void *pRight) { + int32_t left = GET_INT32_VAL(pLeft); + uint32_t right = GET_UINT32_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareInt32Uint64(const void *pLeft, const void *pRight) { + int32_t left = GET_INT32_VAL(pLeft); + uint64_t right = GET_UINT64_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareInt64Int8(const void *pLeft, const void *pRight) { + int64_t left = GET_INT32_VAL(pLeft); + int8_t right = GET_INT8_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareInt64Int16(const void *pLeft, const void *pRight) { + int64_t left = GET_INT32_VAL(pLeft); + int16_t right = GET_INT16_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareInt64Int32(const void *pLeft, const void *pRight) { + int64_t left = GET_INT32_VAL(pLeft); + int32_t right = GET_INT32_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareInt64Float(const void *pLeft, const void *pRight) { + int64_t left = GET_INT32_VAL(pLeft); + float right = GET_FLOAT_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareInt64Double(const void *pLeft, const void *pRight) { + int64_t left = GET_INT32_VAL(pLeft); + double right = GET_DOUBLE_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareInt64Uint8(const void *pLeft, const void *pRight) { + int64_t left = GET_INT32_VAL(pLeft); + uint8_t right = GET_UINT8_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareInt64Uint16(const void *pLeft, const void *pRight) { + int64_t left = GET_INT32_VAL(pLeft); + uint16_t right = GET_UINT16_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareInt64Uint32(const void *pLeft, const void *pRight) { + int64_t left = GET_INT32_VAL(pLeft); + uint32_t right = GET_UINT32_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareInt64Uint64(const void *pLeft, const void *pRight) { + int64_t left = GET_INT32_VAL(pLeft); + uint64_t right = GET_UINT64_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareFloatInt8(const void *pLeft, const void *pRight) { + float left = GET_FLOAT_VAL(pLeft); + int8_t right = GET_INT8_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareFloatInt16(const void *pLeft, const void *pRight) { + float left = GET_FLOAT_VAL(pLeft); + int16_t right = GET_INT16_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareFloatInt32(const void *pLeft, const void *pRight) { + float left = GET_FLOAT_VAL(pLeft); + int32_t right = GET_INT32_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareFloatInt64(const void *pLeft, const void *pRight) { + float left = GET_FLOAT_VAL(pLeft); + int64_t right = GET_INT64_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareFloatDouble(const void *pLeft, const void *pRight) { + float left = GET_FLOAT_VAL(pLeft); + double right = GET_DOUBLE_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareFloatUint8(const void *pLeft, const void *pRight) { + float left = GET_FLOAT_VAL(pLeft); + uint8_t right = GET_UINT8_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareFloatUint16(const void *pLeft, const void *pRight) { + float left = GET_FLOAT_VAL(pLeft); + uint16_t right = GET_UINT16_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareFloatUint32(const void *pLeft, const void *pRight) { + float left = GET_FLOAT_VAL(pLeft); + uint32_t right = GET_UINT32_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareFloatUint64(const void *pLeft, const void *pRight) { + float left = GET_FLOAT_VAL(pLeft); + uint64_t right = GET_UINT64_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareDoubleInt8(const void *pLeft, const void *pRight) { + double left = GET_DOUBLE_VAL(pLeft); + int8_t right = GET_INT8_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareDoubleInt16(const void *pLeft, const void *pRight) { + double left = GET_DOUBLE_VAL(pLeft); + int16_t right = GET_INT16_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareDoubleInt32(const void *pLeft, const void *pRight) { + double left = GET_DOUBLE_VAL(pLeft); + int32_t right = GET_INT32_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareDoubleInt64(const void *pLeft, const void *pRight) { + double left = GET_DOUBLE_VAL(pLeft); + int64_t right = GET_INT64_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareDoubleFloat(const void *pLeft, const void *pRight) { + double left = GET_DOUBLE_VAL(pLeft); + float right = GET_FLOAT_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareDoubleUint8(const void *pLeft, const void *pRight) { + double left = GET_DOUBLE_VAL(pLeft); + uint8_t right = GET_UINT8_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareDoubleUint16(const void *pLeft, const void *pRight) { + double left = GET_DOUBLE_VAL(pLeft); + uint16_t right = GET_UINT16_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareDoubleUint32(const void *pLeft, const void *pRight) { + double left = GET_DOUBLE_VAL(pLeft); + uint32_t right = GET_UINT32_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareDoubleUint64(const void *pLeft, const void *pRight) { + double left = GET_DOUBLE_VAL(pLeft); + uint64_t right = GET_UINT64_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint8Int8(const void *pLeft, const void *pRight) { + uint8_t left = GET_UINT8_VAL(pLeft); + int8_t right = GET_INT8_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint8Int16(const void *pLeft, const void *pRight) { + uint8_t left = GET_UINT8_VAL(pLeft); + int16_t right = GET_INT16_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint8Int32(const void *pLeft, const void *pRight) { + uint8_t left = GET_UINT8_VAL(pLeft); + int32_t right = GET_INT32_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint8Int64(const void *pLeft, const void *pRight) { + uint8_t left = GET_UINT8_VAL(pLeft); + int64_t right = GET_INT64_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint8Float(const void *pLeft, const void *pRight) { + uint8_t left = GET_UINT8_VAL(pLeft); + float right = GET_FLOAT_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint8Double(const void *pLeft, const void *pRight) { + uint8_t left = GET_UINT8_VAL(pLeft); + double right = GET_DOUBLE_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint8Uint16(const void *pLeft, const void *pRight) { + uint8_t left = GET_UINT8_VAL(pLeft); + uint16_t right = GET_UINT16_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint8Uint32(const void *pLeft, const void *pRight) { + uint8_t left = GET_UINT8_VAL(pLeft); + uint32_t right = GET_UINT32_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint8Uint64(const void *pLeft, const void *pRight) { + uint8_t left = GET_UINT8_VAL(pLeft); + uint64_t right = GET_UINT64_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint16Int8(const void *pLeft, const void *pRight) { + uint16_t left = GET_UINT16_VAL(pLeft); + int8_t right = GET_INT8_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint16Int16(const void *pLeft, const void *pRight) { + uint16_t left = GET_UINT16_VAL(pLeft); + int16_t right = GET_INT16_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint16Int32(const void *pLeft, const void *pRight) { + uint16_t left = GET_UINT16_VAL(pLeft); + int32_t right = GET_INT32_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint16Int64(const void *pLeft, const void *pRight) { + uint16_t left = GET_UINT16_VAL(pLeft); + int64_t right = GET_INT64_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint16Float(const void *pLeft, const void *pRight) { + uint16_t left = GET_UINT16_VAL(pLeft); + float right = GET_FLOAT_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint16Double(const void *pLeft, const void *pRight) { + uint16_t left = GET_UINT16_VAL(pLeft); + double right = GET_DOUBLE_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint16Uint8(const void *pLeft, const void *pRight) { + uint16_t left = GET_UINT16_VAL(pLeft); + uint8_t right = GET_UINT8_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint16Uint32(const void *pLeft, const void *pRight) { + uint16_t left = GET_UINT16_VAL(pLeft); + uint32_t right = GET_UINT32_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint16Uint64(const void *pLeft, const void *pRight) { + uint16_t left = GET_UINT16_VAL(pLeft); + uint64_t right = GET_UINT64_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint32Int8(const void *pLeft, const void *pRight) { + uint32_t left = GET_UINT32_VAL(pLeft); + int8_t right = GET_INT8_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint32Int16(const void *pLeft, const void *pRight) { + uint32_t left = GET_UINT32_VAL(pLeft); + int16_t right = GET_INT16_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint32Int32(const void *pLeft, const void *pRight) { + uint32_t left = GET_UINT32_VAL(pLeft); + int32_t right = GET_INT32_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint32Int64(const void *pLeft, const void *pRight) { + uint32_t left = GET_UINT32_VAL(pLeft); + int64_t right = GET_INT64_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint32Float(const void *pLeft, const void *pRight) { + uint32_t left = GET_UINT32_VAL(pLeft); + float right = GET_FLOAT_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint32Double(const void *pLeft, const void *pRight) { + uint32_t left = GET_UINT32_VAL(pLeft); + double right = GET_DOUBLE_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint32Uint8(const void *pLeft, const void *pRight) { + uint32_t left = GET_UINT32_VAL(pLeft); + uint8_t right = GET_UINT8_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint32Uint16(const void *pLeft, const void *pRight) { + uint32_t left = GET_UINT32_VAL(pLeft); + uint16_t right = GET_UINT16_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint32Uint64(const void *pLeft, const void *pRight) { + uint32_t left = GET_UINT32_VAL(pLeft); + uint64_t right = GET_UINT64_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint64Int8(const void *pLeft, const void *pRight) { + uint64_t left = GET_UINT64_VAL(pLeft); + int8_t right = GET_INT8_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint64Int16(const void *pLeft, const void *pRight) { + uint64_t left = GET_UINT64_VAL(pLeft); + int16_t right = GET_INT16_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint64Int32(const void *pLeft, const void *pRight) { + uint64_t left = GET_UINT64_VAL(pLeft); + int32_t right = GET_INT32_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint64Int64(const void *pLeft, const void *pRight) { + uint64_t left = GET_UINT64_VAL(pLeft); + int64_t right = GET_INT64_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint64Float(const void *pLeft, const void *pRight) { + uint64_t left = GET_UINT64_VAL(pLeft); + float right = GET_FLOAT_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint64Double(const void *pLeft, const void *pRight) { + uint64_t left = GET_UINT64_VAL(pLeft); + double right = GET_DOUBLE_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint64Uint8(const void *pLeft, const void *pRight) { + uint64_t left = GET_UINT64_VAL(pLeft); + uint8_t right = GET_UINT8_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint64Uint16(const void *pLeft, const void *pRight) { + uint64_t left = GET_UINT64_VAL(pLeft); + uint16_t right = GET_UINT16_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + +int32_t compareUint64Uint32(const void *pLeft, const void *pRight) { + uint64_t left = GET_UINT64_VAL(pLeft); + uint32_t right = GET_UINT32_VAL(pRight); + if (left > right) return 1; + if (left < right) return -1; + return 0; +} + + int32_t compareJsonValDesc(const void *pLeft, const void *pRight) { return compareJsonVal(pRight, pLeft); } From e2fd1cb2837876f5df12f7dcbab55eb9376bcdbb Mon Sep 17 00:00:00 2001 From: afwerar <1296468573@qq.com> Date: Fri, 26 Aug 2022 16:21:33 +0800 Subject: [PATCH 09/76] os: fix mac run error --- packaging/tools/make_install.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packaging/tools/make_install.sh b/packaging/tools/make_install.sh index 6a95ace99e..f554942ce3 100755 --- a/packaging/tools/make_install.sh +++ b/packaging/tools/make_install.sh @@ -381,8 +381,7 @@ function install_header() { ${install_main_dir}/include || ${csudo}cp -f ${source_dir}/include/client/taos.h ${source_dir}/include/common/taosdef.h ${source_dir}/include/util/taoserror.h ${source_dir}/include/libs/function/taosudf.h \ ${install_main_2_dir}/include && - ${csudo}chmod 644 ${install_main_dir}/include/* ||: - ${csudo}chmod 644 ${install_main_2_dir}/include/* + ${csudo}chmod 644 ${install_main_dir}/include/* || ${csudo}chmod 644 ${install_main_2_dir}/include/* fi } From f8ab11e2fc67de53545a581a355dc3233f28f261 Mon Sep 17 00:00:00 2001 From: huolibo Date: Fri, 26 Aug 2022 17:26:02 +0800 Subject: [PATCH 10/76] fix(driver): initialize variable when only use tmq --- source/client/src/TMQConnector.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/client/src/TMQConnector.c b/source/client/src/TMQConnector.c index 17d3a212c4..fcf6957df9 100644 --- a/source/client/src/TMQConnector.c +++ b/source/client/src/TMQConnector.c @@ -42,6 +42,7 @@ void commit_cb(tmq_t *tmq, int32_t code, void *param) { JNIEXPORT jlong JNICALL Java_com_taosdata_jdbc_tmq_TMQConnector_tmqConfNewImp(JNIEnv *env, jobject jobj) { tmq_conf_t *conf = tmq_conf_new(); + jniGetGlobalMethod(env); return (jlong)conf; } From 44dcaf2517328d6e3965cd523bb3fa02c3d2d02a Mon Sep 17 00:00:00 2001 From: dapan1121 Date: Mon, 29 Aug 2022 09:28:38 +0800 Subject: [PATCH 11/76] fix: fix type convertion issue --- source/libs/scalar/src/sclvector.c | 4 +-- source/util/src/tcompare.c | 40 +++++++++++++++++++++++++----- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/source/libs/scalar/src/sclvector.c b/source/libs/scalar/src/sclvector.c index caccf4bfac..a003315fca 100644 --- a/source/libs/scalar/src/sclvector.c +++ b/source/libs/scalar/src/sclvector.c @@ -909,11 +909,11 @@ int32_t vectorConvertImpl(const SScalarParam* pIn, SScalarParam* pOut, int32_t* int8_t gConvertTypes[TSDB_DATA_TYPE_BLOB+1][TSDB_DATA_TYPE_BLOB+1] = { /* NULL BOOL TINY SMAL INT BIG FLOA DOUB VARC TIME NCHA UTIN USMA UINT UBIG JSON VARB DECI BLOB */ /*NULL*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -/*BOOL*/ 0, 0, 0, 3, 4, 5, 6, 7, 7, 9, 7, 0, 12, 13, 14, 0, 7, 0, 0, +/*BOOL*/ 0, 0, 2, 3, 4, 5, 6, 7, 7, 9, 7, 11, 12, 13, 14, 0, 7, 0, 0, /*TINY*/ 0, 0, 0, 3, 4, 5, 6, 7, 7, 9, 7, 3, 4, 5, 7, 0, 7, 0, 0, /*SMAL*/ 0, 0, 0, 0, 4, 5, 6, 7, 7, 9, 7, 3, 4, 5, 7, 0, 7, 0, 0, /*INT */ 0, 0, 0, 0, 0, 5, 6, 7, 7, 9, 7, 4, 4, 5, 7, 0, 7, 0, 0, -/*BIGI*/ 0, 0, 0, 0, 0, 0, 6, 7, 7, 0, 7, 5, 5, 5, 7, 0, 7, 0, 0, +/*BIGI*/ 0, 0, 0, 0, 0, 0, 6, 7, 7, 9, 7, 5, 5, 5, 7, 0, 7, 0, 0, /*FLOA*/ 0, 0, 0, 0, 0, 0, 0, 7, 7, 6, 7, 6, 6, 6, 6, 0, 7, 0, 0, /*DOUB*/ 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 7, 7, 7, 7, 0, 7, 0, 0, /*VARC*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 8, 7, 7, 7, 7, 0, 0, 0, 0, diff --git a/source/util/src/tcompare.c b/source/util/src/tcompare.c index adebb09912..a8bab19fb5 100644 --- a/source/util/src/tcompare.c +++ b/source/util/src/tcompare.c @@ -570,9 +570,23 @@ int32_t compareFloatInt64(const void *pLeft, const void *pRight) { int32_t compareFloatDouble(const void *pLeft, const void *pRight) { float left = GET_FLOAT_VAL(pLeft); double right = GET_DOUBLE_VAL(pRight); - if (left > right) return 1; - if (left < right) return -1; - return 0; + + if (isnan(left) && isnan(right)) { + return 0; + } + + if (isnan(left)) { + return -1; + } + + if (isnan(right)) { + return 1; + } + + if (FLT_EQUAL(left, right)) { + return 0; + } + return FLT_GREATER(left, right) ? 1 : -1; } int32_t compareFloatUint8(const void *pLeft, const void *pRight) { @@ -642,9 +656,23 @@ int32_t compareDoubleInt64(const void *pLeft, const void *pRight) { int32_t compareDoubleFloat(const void *pLeft, const void *pRight) { double left = GET_DOUBLE_VAL(pLeft); float right = GET_FLOAT_VAL(pRight); - if (left > right) return 1; - if (left < right) return -1; - return 0; + + if (isnan(left) && isnan(right)) { + return 0; + } + + if (isnan(left)) { + return -1; + } + + if (isnan(right)) { + return 1; + } + + if (FLT_EQUAL(left, right)) { + return 0; + } + return FLT_GREATER(left, right) ? 1 : -1; } int32_t compareDoubleUint8(const void *pLeft, const void *pRight) { From 2bf49b0c8ec12b49dac804cc63cf547f96610a6c Mon Sep 17 00:00:00 2001 From: dapan1121 Date: Mon, 29 Aug 2022 10:34:36 +0800 Subject: [PATCH 12/76] fix: fix uint64_t value issue --- source/util/src/tcompare.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/source/util/src/tcompare.c b/source/util/src/tcompare.c index 386018ffec..7032f39744 100644 --- a/source/util/src/tcompare.c +++ b/source/util/src/tcompare.c @@ -465,7 +465,7 @@ int32_t compareInt32Uint64(const void *pLeft, const void *pRight) { } int32_t compareInt64Int8(const void *pLeft, const void *pRight) { - int64_t left = GET_INT32_VAL(pLeft); + int64_t left = GET_INT64_VAL(pLeft); int8_t right = GET_INT8_VAL(pRight); if (left > right) return 1; if (left < right) return -1; @@ -473,7 +473,7 @@ int32_t compareInt64Int8(const void *pLeft, const void *pRight) { } int32_t compareInt64Int16(const void *pLeft, const void *pRight) { - int64_t left = GET_INT32_VAL(pLeft); + int64_t left = GET_INT64_VAL(pLeft); int16_t right = GET_INT16_VAL(pRight); if (left > right) return 1; if (left < right) return -1; @@ -481,7 +481,7 @@ int32_t compareInt64Int16(const void *pLeft, const void *pRight) { } int32_t compareInt64Int32(const void *pLeft, const void *pRight) { - int64_t left = GET_INT32_VAL(pLeft); + int64_t left = GET_INT64_VAL(pLeft); int32_t right = GET_INT32_VAL(pRight); if (left > right) return 1; if (left < right) return -1; @@ -489,7 +489,7 @@ int32_t compareInt64Int32(const void *pLeft, const void *pRight) { } int32_t compareInt64Float(const void *pLeft, const void *pRight) { - int64_t left = GET_INT32_VAL(pLeft); + int64_t left = GET_INT64_VAL(pLeft); float right = GET_FLOAT_VAL(pRight); if (left > right) return 1; if (left < right) return -1; @@ -497,7 +497,7 @@ int32_t compareInt64Float(const void *pLeft, const void *pRight) { } int32_t compareInt64Double(const void *pLeft, const void *pRight) { - int64_t left = GET_INT32_VAL(pLeft); + int64_t left = GET_INT64_VAL(pLeft); double right = GET_DOUBLE_VAL(pRight); if (left > right) return 1; if (left < right) return -1; @@ -505,7 +505,7 @@ int32_t compareInt64Double(const void *pLeft, const void *pRight) { } int32_t compareInt64Uint8(const void *pLeft, const void *pRight) { - int64_t left = GET_INT32_VAL(pLeft); + int64_t left = GET_INT64_VAL(pLeft); uint8_t right = GET_UINT8_VAL(pRight); if (left > right) return 1; if (left < right) return -1; @@ -513,7 +513,7 @@ int32_t compareInt64Uint8(const void *pLeft, const void *pRight) { } int32_t compareInt64Uint16(const void *pLeft, const void *pRight) { - int64_t left = GET_INT32_VAL(pLeft); + int64_t left = GET_INT64_VAL(pLeft); uint16_t right = GET_UINT16_VAL(pRight); if (left > right) return 1; if (left < right) return -1; @@ -521,7 +521,7 @@ int32_t compareInt64Uint16(const void *pLeft, const void *pRight) { } int32_t compareInt64Uint32(const void *pLeft, const void *pRight) { - int64_t left = GET_INT32_VAL(pLeft); + int64_t left = GET_INT64_VAL(pLeft); uint32_t right = GET_UINT32_VAL(pRight); if (left > right) return 1; if (left < right) return -1; @@ -529,7 +529,7 @@ int32_t compareInt64Uint32(const void *pLeft, const void *pRight) { } int32_t compareInt64Uint64(const void *pLeft, const void *pRight) { - int64_t left = GET_INT32_VAL(pLeft); + int64_t left = GET_INT64_VAL(pLeft); uint64_t right = GET_UINT64_VAL(pRight); if (left > right) return 1; if (left < right) return -1; From 081e844645c76a9e47b3b6c95ba649c432aa515f Mon Sep 17 00:00:00 2001 From: afwerar <1296468573@qq.com> Date: Mon, 29 Aug 2022 10:36:16 +0800 Subject: [PATCH 13/76] build: link-libtaos.so.1 --- source/client/CMakeLists.txt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/source/client/CMakeLists.txt b/source/client/CMakeLists.txt index f52edbe71f..e8e3c87849 100644 --- a/source/client/CMakeLists.txt +++ b/source/client/CMakeLists.txt @@ -27,11 +27,18 @@ else() INCLUDE_DIRECTORIES(jni/linux) endif() +set_target_properties( + taos + PROPERTIES + CLEAN_DIRECT_OUTPUT + 1 +) + set_target_properties( taos PROPERTIES VERSION ${TD_VER_NUMBER} - SOVERSION ${TD_VER_NUMBER} + SOVERSION 1 ) add_library(taos_static STATIC ${CLIENT_SRC}) From 08cc6cb23a7fe2bf757dbf95d6ea1a6bf3e2127d Mon Sep 17 00:00:00 2001 From: wade zhang <95411902+gccgdb1234@users.noreply.github.com> Date: Mon, 29 Aug 2022 13:18:47 +0800 Subject: [PATCH 14/76] Update index.md --- docs/en/02-intro/index.md | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/en/02-intro/index.md b/docs/en/02-intro/index.md index 5d21fbaf90..51df831948 100644 --- a/docs/en/02-intro/index.md +++ b/docs/en/02-intro/index.md @@ -3,7 +3,7 @@ title: Introduction toc_max_heading_level: 2 --- -TDengine is an open source, high-performance, cloud native [time-series database](https://tdengine.com/tsdb/) optimized for Internet of Things (IoT), Connected Cars, and Industrial IoT. 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/stream), [data subscription](/develop/tmq) and other functionalities to reduce the system complexity and cost of development and operation. +TDengine is an open source, high-performance, cloud native [time-series database](https://tdengine.com/tsdb/) optimized for Internet of Things (IoT), Connected Cars, and Industrial IoT. 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/stream), [data subscription](../develop/tmq) and other functionalities to reduce the system complexity and cost of development and operation. This section introduces the major features, competitive advantages, typical use-cases and benchmarks to help you get a high level overview of TDengine. @@ -12,32 +12,32 @@ This section introduces the major features, competitive advantages, typical use- The major features are listed below: 1. Insert data - * supports [using SQL to insert](/develop/insert-data/sql-writing). - * supports [schemaless writing](/reference/schemaless/) just like NoSQL databases. It also supports standard protocols like [InfluxDB LINE](/develop/insert-data/influxdb-line),[OpenTSDB Telnet](/develop/insert-data/opentsdb-telnet), [OpenTSDB JSON ](/develop/insert-data/opentsdb-json) among others. - * supports seamless integration with third-party tools like [Telegraf](/third-party/telegraf/), [Prometheus](/third-party/prometheus/), [collectd](/third-party/collectd/), [StatsD](/third-party/statsd/), [TCollector](/third-party/tcollector/) and [icinga2/](/third-party/icinga2/), they can write data into TDengine with simple configuration and without a single line of code. + * supports [using SQL to insert](../develop/insert-data/sql-writing). + * supports [schemaless writing](../reference/schemaless/) just like NoSQL databases. It also supports standard protocols like [InfluxDB LINE](../develop/insert-data/influxdb-line),[OpenTSDB Telnet](../develop/insert-data/opentsdb-telnet), [OpenTSDB JSON ](../develop/insert-data/opentsdb-json) among others. + * supports seamless integration with third-party tools like [Telegraf](../third-party/telegraf/), [Prometheus](../third-party/prometheus/), [collectd](../third-party/collectd/), [StatsD](../third-party/statsd/), [TCollector](../third-party/tcollector/) and [icinga2/](../third-party/icinga2/), they can write data into TDengine with simple configuration and without a single line of code. 2. Query data - * supports standard [SQL](/taos-sql/), including nested query. - * supports [time series specific functions](/taos-sql/function/#time-series-extensions) and [time series specific queries](/taos-sql/distinguished), like downsampling, interpolation, cumulated sum, time weighted average, state window, session window and many others. - * supports [user defined functions](/taos-sql/udf). -3. [Caching](/develop/cache/): TDengine always saves the last data point in cache, so Redis is not needed for time-series data processing. -4. [Stream Processing](/develop/stream/): not only is the continuous query is supported, but TDengine also supports even driven stream processing, so Flink or spark is not needed for time-series daata processing. -5. [Data Dubscription](/develop/tmq/): application can subscribe a table or a set of tables. API is the same as Kafka, but you can specify filter conditions. + * supports standard [SQL](../taos-sql/), including nested query. + * supports [time series specific functions](../taos-sql/function/#time-series-extensions) and [time series specific queries](../taos-sql/distinguished), like downsampling, interpolation, cumulated sum, time weighted average, state window, session window and many others. + * supports [user defined functions](../taos-sql/udf). +3. [Caching](../develop/cache/): TDengine always saves the last data point in cache, so Redis is not needed for time-series data processing. +4. [Stream Processing](../develop/stream/): not only is the continuous query is supported, but TDengine also supports even driven stream processing, so Flink or spark is not needed for time-series daata processing. +5. [Data Dubscription](../develop/tmq/): application can subscribe a table or a set of tables. API is the same as Kafka, but you can specify filter conditions. 6. Visualization - * supports seamless integration with [Grafana](/third-party/grafana/) for visualization. + * supports seamless integration with [Grafana](../third-party/grafana/) for visualization. * supports seamless integration with Google Data Studio. 7. Cluster - * supports [cluster](/deployment/) with the capability of increasing processing power by adding more nodes. - * supports [deployment on Kubernetes](/deployment/k8s/) + * supports [cluster](../deployment/) with the capability of increasing processing power by adding more nodes. + * supports [deployment on Kubernetes](../deployment/k8s/) * supports high availability via data replication. 8. Administration - * provides [monitoring](/operation/monitor) on running instances of TDengine. - * provides many ways to [import](/operation/import) and [export](/operation/export) data. + * provides [monitoring](../operation/monitor) on running instances of TDengine. + * provides many ways to [import](../operation/import) and [export](../operation/export) data. 9. Tools - * provides an interactive [command-line interface](/reference/taos-shell) for management, maintenance and ad-hoc queries. - * provides a tool [taosBenchmark](/reference/taosbenchmark/) for testing the performance of TDengine. + * provides an interactive [command-line interface](../reference/taos-shell) for management, maintenance and ad-hoc queries. + * provides a tool [taosBenchmark](../reference/taosbenchmark/) for testing the performance of TDengine. 10. Programming - * 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. - * provides a [REST API](/reference/rest-api/). + * 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. + * provides a [REST API](../reference/rest-api/). For more details on features, please read through the entire documentation. From 7caf6bd66d2d97a82a7bcccd819d270625e582ae Mon Sep 17 00:00:00 2001 From: gccgdb1234 Date: Mon, 29 Aug 2022 13:23:47 +0800 Subject: [PATCH 15/76] doc: rewrite introduction of chinese version according to english version change --- docs/zh/02-intro.md | 77 +++++++++++++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 27 deletions(-) diff --git a/docs/zh/02-intro.md b/docs/zh/02-intro.md index f6779b8776..3c788478f0 100644 --- a/docs/zh/02-intro.md +++ b/docs/zh/02-intro.md @@ -4,7 +4,7 @@ description: 简要介绍 TDengine 的主要功能 toc_max_heading_level: 2 --- -TDengine 是一款[开源](https://www.taosdata.com/tdengine/open_source_time-series_database)、[高性能](https://www.taosdata.com/tdengine/fast)、[云原生](https://www.taosdata.com/tdengine/cloud_native_time-series_database)的时序数据库Time Series Database, TSDB)。TDengine 能被广泛运用于物联网、工业互联网、车联网、IT 运维、金融等领域。除核心的时序数据库功能外,TDengine 还提供[缓存](../develop/cache/)、[数据订阅](../develop/tmq)、[流式计算](../develop/stream)等功能,是一极简的时序数据处理平台,最大程度的减小系统设计的复杂度,降低研发和运营成本。 +TDengine 是一款开源、高性能、云原生的[时序数据库](https://tdengine.com/tsdb/),且针对物联网、车联网以及工业互联网进行了优化。TDengine 的代码,包括其集群功能,都在 GNU AGPL v3.0 下开源。除核心的时序数据库功能外,TDengine 还提供[缓存](../develop/cache/)、[数据订阅](../develop/tmq)、[流式计算](../develop/stream)等其它功能以降低系统复杂度及研发和运维成本。 本章节介绍TDengine的主要功能、竞争优势、适用场景、与其他数据库的对比测试等等,让大家对TDengine有个整体的了解。 @@ -12,47 +12,70 @@ TDengine 是一款[开源](https://www.taosdata.com/tdengine/open_source_time-se 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/stream)(Stream Processing) -7. 支持[数据订阅](../develop/tmq),而且可以指定过滤条件 -8. 支持[集群](../deployment/),可以通过多节点进行水平扩展,并通过多副本实现高可靠 -9. 提供[命令行程序](../reference/taos-shell),便于管理集群,检查系统状态,做即席查询 -10. 提供多种数据的[导入](../operation/import)、[导出](../operation/export) -11. 支持对[TDengine 集群本身的监控](../operation/monitor) -12. 提供各种语言的[连接器](../connector): 如 C/C++, Java, Go, Node.JS, Rust, Python, C# 等 -13. 支持 [REST 接口](../connector/rest-api/) -14. 支持与[ Grafana 无缝集成](../third-party/grafana) -15. 支持与 Google Data Studio 无缝集成 -16. 支持 [Kubernetes 部署](../deployment/k8s) +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) + - 与多种第三方工具的无缝集成,它们都可以仅通过配置而无需任何代码即可将数据写入 TDengine + - [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) ; +2. 查询数据,支持 + - [标准SQL](../taos-sql),含嵌套查询 + - [时序数据特色函数](../taos-sql/function/#time-series-extensions) + - [时序顺序特色查询](../taos-sql/distinguished),例如降采样、插值、累加和、时间加权平均、状态窗口、会话窗口等 + - [用户自定义函数](../taos-sql/udf) +3. [缓存](../develop/cache),将每张表的最后一条记录缓存起来,这样无需 Redis 就能对时序数据进行高效处理 +4. [流式计算](../develop/stream)(Stream Processing),TDengine 不仅支持连续查询,还支持事件驱动的流式计算,这样在处理时序数据时就无需 Flink 或 Spark 这样流计算组件 +5. [数据订阅](../develop/tmq),应用程序可以订阅一张表或一组表的数据,API 与 Kafka 相同,而且可以指定过滤条件 +6. 可视化 + - 支持与 [Grafana](../third-party/grafana/) 的无缝集成 + - 支持与 Google Data Studio 的无缝集成 +7. 集群 + - 集群部署(../deployment/),可以通过增加节点进行水平扩展以提升处理能力 + - 可以通过 [Kubernets 部署 TDengine](../deployment/k8s/) + - 通过多副本提供高可用能力 +8. 管理 + - [监控](..//operation/monitor)运行中的 TDengine 实例 + - 多种[数据导入](../operation/import)方式 + - 多种[数据导出](../operation/export)方式 +9. 工具 + - 提供交互式[命令行程序](../reference/taos-shell),便于管理集群,检查系统状态,做即席查询 + - 提供压力测试工具[taosBenchmark](../reference/taosbenchmark),用于测试 TDengine 的性能 +10. 编程 + - 提供各种语言的[连接器](../connector): 如 [C/C++](../connector/cpp), [Java](../connector/java), [Go](../connector/go), [Node.JS](../connector/node), [Rust](../connector/rust), [Python](../connector/python), [C#](../connector/csharp) 等 + - 支持 [REST 接口](../connector/rest-api/) -更多细小的功能,请阅读整个文档。 +更多细节功能,请阅读整个文档。 ## 竞争优势 -由于 TDengine 充分利用了[时序数据特点](https://www.taosdata.com/blog/2019/07/09/105.html),比如结构化、无需事务、很少删除或更新、写多读少等等,设计了全新的针对时序数据的存储引擎和计算引擎,因此与其他时序数据库相比,TDengine 有以下特点: +由于 TDengine 充分利用了[时序数据特点](https://www.taosdata.com/blog/2019/07/09/105.html),比如结构化、无需事务、很少删除或更新、写多读少等等,因此与其他时序数据库相比,TDengine 有以下特点: -- **[高性能](https://www.taosdata.com/tdengine/fast)**:通过创新的存储引擎设计,无论是数据写入还是查询,TDengine 的性能比通用数据库快 10 倍以上,也远超其他时序数据库,存储空间不及通用数据库的1/10。 +- **[高性能](https://www.taosdata.com/tdengine/fast)**:TDengine 是唯一一个解决了时序数据存储的高基数难题的时序数据库,支持上亿数据采集点,并在数据插入、查询和数据压缩上远胜其它时序数据库。 -- **[云原生](https://www.taosdata.com/tdengine/cloud_native_time-series_database)**:通过原生分布式的设计,充分利用云平台的优势,TDengine 提供了水平扩展能力,具备弹性、韧性和可观测性,支持k8s部署,可运行在公有云、私有云和混合云上。 +- **[极简时序数据平台](https://www.taosdata.com/tdengine/simplified_solution_for_time-series_data_processing)**:TDengine 内建缓存、流式计算和数据订阅等功能,为时序数据的处理提供了极简的解决方案,从而大幅降低了业务系统的设计复杂度和运维成本。 -- **[极简时序数据平台](https://www.taosdata.com/tdengine/simplified_solution_for_time-series_data_processing)**:TDengine 内建消息队列、缓存、流式计算等功能,应用无需再集成 Kafka/Redis/HBase/Spark 等软件,大幅降低系统的复杂度,降低应用开发和运营成本。 +- **[云原生](https://www.taosdata.com/tdengine/cloud_native_time-series_database)**:通过原生的分布式设计、数据分片和分区、存算分离、RAFT 协议、Kubernets 部署和完整的可观测性,TDengine 是一款云原生时序数据库并且能够部署在公有云、私有云和混合云上。 -- **[分析能力](https://www.taosdata.com/tdengine/easy_data_analytics)**:支持 SQL,同时为时序数据特有的分析提供SQL扩展。通过超级表、存储计算分离、分区分片、预计算、自定义函数等技术,TDengine 具备强大的分析能力。 +- **[简单易用](https://www.taosdata.com/tdengine/ease_of_use)**:对系统管理员来说,TDengine 大幅降低了管理和维护的代价。对开发者来说, TDengine 提供了简单的接口、极简的解决方案和与第三方工具的无缝集成。对数据分析专家来说,TDengine 提供了便捷的数据访问。 -- **[简单易用](https://www.taosdata.com/tdengine/ease_of_use)**:无任何依赖,安装、集群几秒搞定;提供REST以及各种语言连接器,与众多第三方工具无缝集成;提供命令行程序,便于管理和即席查询;提供各种运维工具。 +- **[分析能力](https://www.taosdata.com/tdengine/easy_data_analytics)**:通过超级表、存储计算分离、分区分片、预计算和其它技术,TDengine 能够高效地浏览、格式化和访问数据。 -- **[核心开源](https://www.taosdata.com/tdengine/open_source_time-series_database)**:TDengine 的核心代码包括集群功能全部开源,截止到2022年8月1日,全球超过 135.9k 个运行实例,GitHub Star 18.7k,Fork 4.4k,社区活跃。 +- **[核心开源](https://www.taosdata.com/tdengine/open_source_time-series_database)**:TDengine 的核心代码包括集群功能全部在开源协议下公开。全球超过 140k 个运行实例,GitHub Star 19k,且拥有一个活跃的开发者社区。 采用 TDengine,可将典型的物联网、车联网、工业互联网大数据平台的总拥有成本大幅降低。表现在几个方面: 1. 由于其超强性能,它能将系统需要的计算资源和存储资源大幅降低 2. 因为支持 SQL,能与众多第三方软件无缝集成,学习迁移成本大幅下降 3. 因为是一极简的时序数据平台,系统复杂度、研发和运营成本大幅降低 -4. 因为维护简单,运营维护成本能大幅降低 ## 技术生态 @@ -67,7 +90,7 @@ TDengine的主要功能如下: 上图中,左侧是各种数据采集或消息队列,包括 OPC-UA、MQTT、Telegraf、也包括 Kafka, 他们的数据将被源源不断的写入到 TDengine。右侧则是可视化、BI 工具、组态软件、应用程序。下侧则是 TDengine 自身提供的命令行程序 (CLI) 以及可视化管理管理。 -## 总体适用场景 +## 典型适用场景 作为一个高性能、分布式、支持 SQL 的时序数据库 (Database),TDengine 的典型适用场景包括但不限于 IoT、工业互联网、车联网、IT 运维、能源、金融证券等领域。需要指出的是,TDengine 是针对时序数据场景设计的专用数据库和专用大数据处理工具,因充分利用了时序大数据的特点,它无法用来处理网络爬虫、微博、微信、电商、ERP、CRM 等通用型数据。本文对适用场景做更多详细的分析。 From cf0dd63b888e632c07a82e2da9b1dbb07057e93f Mon Sep 17 00:00:00 2001 From: gccgdb1234 Date: Mon, 29 Aug 2022 13:46:23 +0800 Subject: [PATCH 16/76] doc: fix broken link in introduction --- docs/zh/02-intro.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/zh/02-intro.md b/docs/zh/02-intro.md index 3c788478f0..152d03d9e3 100644 --- a/docs/zh/02-intro.md +++ b/docs/zh/02-intro.md @@ -43,7 +43,7 @@ TDengine的主要功能如下: - 可以通过 [Kubernets 部署 TDengine](../deployment/k8s/) - 通过多副本提供高可用能力 8. 管理 - - [监控](..//operation/monitor)运行中的 TDengine 实例 + - [监控](../operation/monitor)运行中的 TDengine 实例 - 多种[数据导入](../operation/import)方式 - 多种[数据导出](../operation/export)方式 9. 工具 From cde4621e77630a875e7de2a9658eda30ccbd28de Mon Sep 17 00:00:00 2001 From: Xuefeng Tan <1172915550@qq.com> Date: Mon, 29 Aug 2022 13:53:43 +0800 Subject: [PATCH 17/76] fix(taosAdapter): fix taosAdapter version shows unknown (#16472) --- tools/CMakeLists.txt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index d27fb99ef7..03097e31b9 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -82,6 +82,7 @@ ELSE () COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR}/taosadapter ) EXECUTE_PROCESS( + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/taosadapter COMMAND git rev-parse --short HEAD RESULT_VARIABLE commit_sha1 OUTPUT_VARIABLE taosadapter_commit_sha1 @@ -118,8 +119,8 @@ ELSE () PATCH_COMMAND COMMAND git clean -f -d BUILD_COMMAND - COMMAND CGO_CFLAGS=-I${CMAKE_CURRENT_SOURCE_DIR}/../include/client CGO_LDFLAGS=-L${CMAKE_BINARY_DIR}/build/lib go build -a -ldflags "-s -w -X github.com/taosdata/taosadapter/version.Version=${taos_version} -X github.com/taosdata/taosadapter/version.CommitID=${taosadapter_commit_sha1}" - COMMAND CGO_CFLAGS=-I${CMAKE_CURRENT_SOURCE_DIR}/../include/client CGO_LDFLAGS=-L${CMAKE_BINARY_DIR}/build/lib go build -a -o taosadapter-debug -ldflags "-X github.com/taosdata/taosadapter/version.Version=${taos_version} -X github.com/taosdata/taosadapter/version.CommitID=${taosadapter_commit_sha1}" + COMMAND CGO_CFLAGS=-I${CMAKE_CURRENT_SOURCE_DIR}/../include/client CGO_LDFLAGS=-L${CMAKE_BINARY_DIR}/build/lib go build -a -ldflags "-s -w -X github.com/taosdata/taosadapter/v3/version.Version=${taos_version} -X github.com/taosdata/taosadapter/v3/version.CommitID=${taosadapter_commit_sha1}" + COMMAND CGO_CFLAGS=-I${CMAKE_CURRENT_SOURCE_DIR}/../include/client CGO_LDFLAGS=-L${CMAKE_BINARY_DIR}/build/lib go build -a -o taosadapter-debug -ldflags "-X github.com/taosdata/taosadapter/v3/version.Version=${taos_version} -X github.com/taosdata/taosadapter/v3/version.CommitID=${taosadapter_commit_sha1}" INSTALL_COMMAND COMMAND ${_upx_prefix}/src/upx/upx taosadapter COMMAND cmake -E copy taosadapter ${CMAKE_BINARY_DIR}/build/bin @@ -141,8 +142,8 @@ ELSE () PATCH_COMMAND COMMAND git clean -f -d BUILD_COMMAND - COMMAND CGO_CFLAGS=-I${CMAKE_CURRENT_SOURCE_DIR}/../include/client CGO_LDFLAGS=-L${CMAKE_BINARY_DIR}/build/lib go build -a -ldflags "-s -w -X github.com/taosdata/taosadapter/version.Version=${taos_version} -X github.com/taosdata/taosadapter/version.CommitID=${taosadapter_commit_sha1}" - COMMAND CGO_CFLAGS=-I${CMAKE_CURRENT_SOURCE_DIR}/../include/client CGO_LDFLAGS=-L${CMAKE_BINARY_DIR}/build/lib go build -a -o taosadapter-debug -ldflags "-X github.com/taosdata/taosadapter/version.Version=${taos_version} -X github.com/taosdata/taosadapter/version.CommitID=${taosadapter_commit_sha1}" + COMMAND CGO_CFLAGS=-I${CMAKE_CURRENT_SOURCE_DIR}/../include/client CGO_LDFLAGS=-L${CMAKE_BINARY_DIR}/build/lib go build -a -ldflags "-s -w -X github.com/taosdata/taosadapter/v3/version.Version=${taos_version} -X github.com/taosdata/taosadapter/v3/version.CommitID=${taosadapter_commit_sha1}" + COMMAND CGO_CFLAGS=-I${CMAKE_CURRENT_SOURCE_DIR}/../include/client CGO_LDFLAGS=-L${CMAKE_BINARY_DIR}/build/lib go build -a -o taosadapter-debug -ldflags "-X github.com/taosdata/taosadapter/v3/version.Version=${taos_version} -X github.com/taosdata/taosadapter/v3/version.CommitID=${taosadapter_commit_sha1}" INSTALL_COMMAND COMMAND cmake -E copy taosadapter ${CMAKE_BINARY_DIR}/build/bin COMMAND cmake -E make_directory ${CMAKE_BINARY_DIR}/test/cfg/ @@ -174,8 +175,8 @@ ELSE () BUILD_COMMAND COMMAND set CGO_CFLAGS=-I${CMAKE_CURRENT_SOURCE_DIR}/../include/client COMMAND set CGO_LDFLAGS=-L${CMAKE_BINARY_DIR}/build/lib - COMMAND go build -a -o taosadapter.exe -ldflags "-s -w -X github.com/taosdata/taosadapter/version.Version=${taos_version} -X github.com/taosdata/taosadapter/version.CommitID=${taosadapter_commit_sha1}" - COMMAND go build -a -o taosadapter-debug.exe -ldflags "-X github.com/taosdata/taosadapter/version.Version=${taos_version} -X github.com/taosdata/taosadapter/version.CommitID=${taosadapter_commit_sha1}" + COMMAND go build -a -o taosadapter.exe -ldflags "-s -w -X github.com/taosdata/taosadapter/v3/version.Version=${taos_version} -X github.com/taosdata/taosadapter/v3/version.CommitID=${taosadapter_commit_sha1}" + COMMAND go build -a -o taosadapter-debug.exe -ldflags "-X github.com/taosdata/taosadapter/v3/version.Version=${taos_version} -X github.com/taosdata/taosadapter/v3/version.CommitID=${taosadapter_commit_sha1}" INSTALL_COMMAND COMMAND ${_upx_prefix}/src/upx/upx taosadapter.exe COMMAND cmake -E copy taosadapter.exe ${CMAKE_BINARY_DIR}/build/bin From 3574abcc6ed983610417ec09662adc752ce39862 Mon Sep 17 00:00:00 2001 From: Xiaoyu Wang <59301069+xiao-yu-wang@users.noreply.github.com> Date: Mon, 29 Aug 2022 14:39:12 +0800 Subject: [PATCH 18/76] Update 12-distinguished.md --- docs/zh/12-taos-sql/12-distinguished.md | 35 +++++++++++-------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/docs/zh/12-taos-sql/12-distinguished.md b/docs/zh/12-taos-sql/12-distinguished.md index b9e06033d6..016c1929fe 100644 --- a/docs/zh/12-taos-sql/12-distinguished.md +++ b/docs/zh/12-taos-sql/12-distinguished.md @@ -6,11 +6,11 @@ description: TDengine 提供的时序数据特有的查询功能 TDengine 是专为时序数据而研发的大数据平台,存储和计算都针对时序数据的特定进行了量身定制,在支持标准 SQL 的基础之上,还提供了一系列贴合时序业务场景的特色查询语法,极大的方便时序场景的应用开发。 -TDengine 提供的特色查询包括标签切分查询和窗口切分查询。 +TDengine 提供的特色查询包括数据切分查询和窗口切分查询。 -## 标签切分查询 +## 数据切分查询 -超级表查询中,当需要针对标签进行数据切分然后在切分出的数据空间内再进行一系列的计算时使用标签切分子句,标签切分的语句如下: +当需要按一定的维度对数据进行切分然后在切分出的数据空间内再进行一系列的计算时使用数据切分子句,数据切分语句的语法如下: ```sql PARTITION BY part_list @@ -18,22 +18,23 @@ PARTITION BY part_list part_list 可以是任意的标量表达式,包括列、常量、标量函数和它们的组合。 -当 PARTITION BY 和标签一起使用时,TDengine 按如下方式处理标签切分子句: +TDengine 按如下方式处理数据切分子句: -- 标签切分子句位于 WHERE 子句之后,且不能和 JOIN 子句一起使用。 -- 标签切分子句将超级表数据按指定的标签组合进行切分,每个切分的分片进行指定的计算。计算由之后的子句定义(窗口子句、GROUP BY 子句或 SELECT 子句)。 -- 标签切分子句可以和窗口切分子句(或 GROUP BY 子句)一起使用,此时后面的子句作用在每个切分的分片上。例如,将数据按标签 location 进行分组,并对每个组按 10 分钟进行降采样,取其最大值。 +- 数据切分子句位于 WHERE 子句之后。 +- 数据切分子句将表数据按指定的维度进行切分,每个切分的分片进行指定的计算。计算由之后的子句定义(窗口子句、GROUP BY 子句或 SELECT 子句)。 +- 数据切分子句可以和窗口切分子句(或 GROUP BY 子句)一起使用,此时后面的子句作用在每个切分的分片上。例如,将数据按标签 location 进行分组,并对每个组按 10 分钟进行降采样,取其最大值。 ```sql select max(current) from meters partition by location interval(10m) ``` +数据切分子句最常见的用法就是在超级表查询中,按标签将子表数据进行切分,然后分别进行计算。特别是 PARTITION BY TBNAME 用法,它将每个子表的数据独立出来,形成一条条独立的时间序列,极大的方便了各种时序场景的统计分析。 ## 窗口切分查询 TDengine 支持按时间段窗口切分方式进行聚合结果查询,比如温度传感器每秒采集一次数据,但需查询每隔 10 分钟的温度平均值。这种场景下可以使用窗口子句来获得需要的查询结果。窗口子句用于针对查询的数据集合按照窗口切分成为查询子集并进行聚合,窗口包含时间窗口(time window)、状态窗口(status window)、会话窗口(session window)三种窗口。其中时间窗口又可划分为滑动时间窗口和翻转时间窗口。窗口切分查询语法如下: ```sql -SELECT function_list FROM tb_name +SELECT select_list FROM tb_name [WHERE where_condition] [SESSION(ts_col, tol_val)] [STATE_WINDOW(col)] @@ -43,19 +44,15 @@ SELECT function_list FROM tb_name 在上述语法中的具体限制如下 -### 窗口切分查询中使用函数的限制 - -- 在聚合查询中,function_list 位置允许使用聚合和选择函数,并要求每个函数仅输出单个结果(例如:COUNT、AVG、SUM、STDDEV、LEASTSQUARES、PERCENTILE、MIN、MAX、FIRST、LAST),而不能使用具有多行输出结果的函数(例如:DIFF 以及四则运算)。 -- 此外 LAST_ROW 查询也不能与窗口聚合同时出现。 -- 标量函数(如:CEIL/FLOOR 等)也不能使用在窗口聚合查询中。 - ### 窗口子句的规则 -- 窗口子句位于标签切分子句之后,GROUP BY 子句之前,且不可以和 GROUP BY 子句一起使用。 +- 窗口子句位于数据切分子句之后,GROUP BY 子句之前,且不可以和 GROUP BY 子句一起使用。 - 窗口子句将数据按窗口进行切分,对每个窗口进行 SELECT 列表中的表达式的计算,SELECT 列表中的表达式只能包含: - 常量。 - - 聚集函数。 + - _wstart伪列、_wend伪列和_wduration伪列。 + - 聚集函数(包括选择函数和可以由参数确定输出行数的时序特有函数)。 - 包含上面表达式的表达式。 + - 且至少包含一个聚集函数。 - 窗口子句不可以和 GROUP BY 子句一起使用。 - WHERE 语句可以指定查询的起止时间和其他过滤条件。 @@ -74,7 +71,7 @@ FILL 语句指定某一窗口区间数据缺失的情况下的填充模式。填 1. 使用 FILL 语句的时候可能生成大量的填充输出,务必指定查询的时间区间。针对每次查询,系统可返回不超过 1 千万条具有插值的结果。 2. 在时间维度聚合中,返回的结果中时间序列严格单调递增。 -3. 如果查询对象是超级表,则聚合函数会作用于该超级表下满足值过滤条件的所有表的数据。如果查询中没有使用 GROUP BY 语句,则返回的结果按照时间序列严格单调递增;如果查询中使用了 GROUP BY 语句分组,则返回结果中每个 GROUP 内不按照时间序列严格单调递增。 +3. 如果查询对象是超级表,则聚合函数会作用于该超级表下满足值过滤条件的所有表的数据。如果查询中没有使用 PARTITION BY 语句,则返回的结果按照时间序列严格单调递增;如果查询中使用了 PARTITION BY 语句分组,则返回结果中每个 PARTITION 内不按照时间序列严格单调递增。 ::: @@ -106,7 +103,7 @@ SELECT COUNT(*) FROM temp_tb_1 INTERVAL(1m) SLIDING(2m); ### 状态窗口 -使用整数(布尔值)或字符串来标识产生记录时候设备的状态量。产生的记录如果具有相同的状态量数值则归属于同一个状态窗口,数值改变后该窗口关闭。如下图所示,根据状态量确定的状态窗口分别是[2019-04-28 14:22:07,2019-04-28 14:22:10]和[2019-04-28 14:22:11,2019-04-28 14:22:12]两个。(状态窗口暂不支持对超级表使用) +使用整数(布尔值)或字符串来标识产生记录时候设备的状态量。产生的记录如果具有相同的状态量数值则归属于同一个状态窗口,数值改变后该窗口关闭。如下图所示,根据状态量确定的状态窗口分别是[2019-04-28 14:22:07,2019-04-28 14:22:10]和[2019-04-28 14:22:11,2019-04-28 14:22:12]两个。 ![TDengine Database 时间窗口示意图](./timewindow-3.webp) @@ -122,7 +119,7 @@ SELECT COUNT(*), FIRST(ts), status FROM temp_tb_1 STATE_WINDOW(status); ![TDengine Database 时间窗口示意图](./timewindow-2.webp) -在 tol_value 时间间隔范围内的结果都认为归属于同一个窗口,如果连续的两条记录的时间超过 tol_val,则自动开启下一个窗口。(会话窗口暂不支持对超级表使用) +在 tol_value 时间间隔范围内的结果都认为归属于同一个窗口,如果连续的两条记录的时间超过 tol_val,则自动开启下一个窗口。 ``` From a51cfe837b12032594c42d2dbf86e0a2432b3a6f Mon Sep 17 00:00:00 2001 From: Shuduo Sang Date: Mon, 29 Aug 2022 14:39:55 +0800 Subject: [PATCH 19/76] feat: update taostools aa45ad4 for3.0 (#16469) * feat: update taos-tools for 3.0 [TD-14141] * feat: update taos-tools for 3.0 * feat: update taos-tools for 3.0 * feat: update taos-tools for 3.0 * feat: update taos-tools for 3.0 * feat: update taos-tools for 3.0 * feat: update taos-tools for 3.0 * feat: update taos-tools for 3.0 * feat: update taos-tools for 3.0 * feat: update taos-tools for 3.0 * feat: update taos-tools 8e3b3ee * fix: remove submodules * feat: update taos-tools c529299 * feat: update taos-tools 9dc2fec for 3.0 * fix: optim upx * feat: update taos-tools f4e456a for 3.0 * feat: update taos-tools 2a2def1 for 3.0 * feat: update taos-tools c9cc20f for 3.0 * feat: update taostoosl 8a5e336 for 3.0 * feat: update taostools 3c7dafe for 3.0 * feat: update taos-tools 2d68404 for 3.0 * feat: update taos-tools 57bdfbf for 3.0 * fix: jenkinsfile2 to upgrade pip * feat: update taostoosl 11d23e5 for 3.0 * feat: update taostools 43924b8 for 3.0 * feat: update taostools 53a0103 for 3.0 * feat: update taostoosl d237772 for 3.0 * feat: update taos-tools 6bde102 for 3.0 * feat: upate taos-tools 2af2222 for 3.0 * feat: update taos-tools 833b721 for 3.0 * feat: update taostools e8bfca6 for 3.0 * feat: update taos-tools aa45ad4 for 3.0 --- cmake/taostools_CMakeLists.txt.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/taostools_CMakeLists.txt.in b/cmake/taostools_CMakeLists.txt.in index 9547323acf..c0de75c6dd 100644 --- a/cmake/taostools_CMakeLists.txt.in +++ b/cmake/taostools_CMakeLists.txt.in @@ -2,7 +2,7 @@ # taos-tools ExternalProject_Add(taos-tools GIT_REPOSITORY https://github.com/taosdata/taos-tools.git - GIT_TAG e8bfca6 + GIT_TAG aa45ad4 SOURCE_DIR "${TD_SOURCE_DIR}/tools/taos-tools" BINARY_DIR "" #BUILD_IN_SOURCE TRUE From 167e58578ad176dc427c7b7cccbb597a5c868310 Mon Sep 17 00:00:00 2001 From: Cary Xu Date: Mon, 29 Aug 2022 14:53:13 +0800 Subject: [PATCH 20/76] enh: add check for tpagebuf --- source/libs/executor/src/tsimplehash.c | 6 +----- source/util/src/tpagedbuf.c | 1 + 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/source/libs/executor/src/tsimplehash.c b/source/libs/executor/src/tsimplehash.c index 8cd376e092..84b615af7a 100644 --- a/source/libs/executor/src/tsimplehash.c +++ b/source/libs/executor/src/tsimplehash.c @@ -295,11 +295,7 @@ int32_t tSimpleHashIterateRemove(SSHashObj *pHashObj, const void *key, size_t ke } if (*pIter == (void *)GET_SHASH_NODE_DATA(pNode)) { - if (!pPrev) { - *pIter = NULL; - } else { - *pIter = GET_SHASH_NODE_DATA(pPrev); - } + *pIter = pPrev ? GET_SHASH_NODE_DATA(pPrev) : NULL; } FREE_HASH_NODE(pNode); diff --git a/source/util/src/tpagedbuf.c b/source/util/src/tpagedbuf.c index 4d5532b9a6..0c30cc1003 100644 --- a/source/util/src/tpagedbuf.c +++ b/source/util/src/tpagedbuf.c @@ -465,6 +465,7 @@ void* getBufPage(SDiskbasedBuf* pBuf, int32_t id) { // set the ptr to the new SPageInfo ((void**)((*pi)->pData))[0] = (*pi); + assert(listNEles(pBuf->lruList) < pBuf->inMemPages && pBuf->inMemPages > 0); lruListPushFront(pBuf->lruList, *pi); (*pi)->used = true; From 430086e17bbbd1acf9c7265a7804be8ecbfe33e2 Mon Sep 17 00:00:00 2001 From: Xiaoyu Wang <59301069+xiao-yu-wang@users.noreply.github.com> Date: Mon, 29 Aug 2022 15:19:33 +0800 Subject: [PATCH 21/76] Update 25-grant.md --- docs/zh/12-taos-sql/25-grant.md | 57 +++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/docs/zh/12-taos-sql/25-grant.md b/docs/zh/12-taos-sql/25-grant.md index 6f7024d32e..7fb9447101 100644 --- a/docs/zh/12-taos-sql/25-grant.md +++ b/docs/zh/12-taos-sql/25-grant.md @@ -9,14 +9,51 @@ description: 企业版中才具有的权限管理功能 ## 创建用户 ```sql -CREATE USER use_name PASS 'password'; +CREATE USER use_name PASS 'password' [SYSINFO {1|0}]; ``` 创建用户。 -use_name最长为23字节。 +use_name 最长为 23 字节。 -password最长为128字节,合法字符包括"a-zA-Z0-9!?$%^&*()_–+={[}]:;@~#|<,>.?/",不可以出现单双引号、撇号、反斜杠和空格,且不可以为空。 +password 最长为 128 字节,合法字符包括"a-zA-Z0-9!?$%^&*()_–+={[}]:;@~#|<,>.?/",不可以出现单双引号、撇号、反斜杠和空格,且不可以为空。 + +SYSINFO 表示用户是否可以查看系统信息。1 表示可以查看,0 表示不可以查看。系统信息包括服务端配置信息、服务端各种节点信息(如 DNODE、QNODE等)、存储相关的信息等。默认为可以查看系统信息。 + +例如,创建密码为123456且可以查看系统信息的用户test如下: + +```sql +taos> create user test pass '123456' sysinfo 1; +Query OK, 0 of 0 rows affected (0.001254s) +``` + +## 查看用户 + +```sql +SHOW USERS; +``` + +查看用户信息。 + +```sql +taos> show users; + name | super | enable | sysinfo | create_time | +================================================================================ + test | 0 | 1 | 1 | 2022-08-29 15:10:27.315 | + root | 1 | 1 | 1 | 2022-08-29 15:03:34.710 | +Query OK, 2 rows in database (0.001657s) +``` + +也可以通过查询INFORMATION_SCHEMA.INS_USERS系统表来查看用户信息,例如: + +```sql +taos> select * from information_schema.ins_users; + name | super | enable | sysinfo | create_time | +================================================================================ + test | 0 | 1 | 1 | 2022-08-29 15:10:27.315 | + root | 1 | 1 | 1 | 2022-08-29 15:03:34.710 | +Query OK, 2 rows in database (0.001953s) +``` ## 删除用户 @@ -37,9 +74,15 @@ alter_user_clause: { ``` - PASS:修改用户密码。 -- ENABLE:修改用户是否启用。1表示启用此用户,0表示禁用此用户。 -- SYSINFO:修改用户是否可查看系统信息。1表示可以查看系统信息,0表示不可以查看系统信息。 +- ENABLE:修改用户是否启用。1 表示启用此用户,0 表示禁用此用户。 +- SYSINFO:修改用户是否可查看系统信息。1 表示可以查看系统信息,0 表示不可以查看系统信息。 +例如,禁用 test 用户: + +```sql +taos> alter user test enable 0; +Query OK, 0 of 0 rows affected (0.001160s) +``` ## 授权 @@ -62,7 +105,7 @@ priv_level : { } ``` -对用户授权。 +对用户授权。授权功能只包含在企业版中。 授权级别支持到DATABASE,权限有READ和WRITE两种。 @@ -92,4 +135,4 @@ priv_level : { ``` -收回对用户的授权。 +收回对用户的授权。授权功能只包含在企业版中。 From 95e591efede2d6200254d38df471364e86ed341e Mon Sep 17 00:00:00 2001 From: Minglei Jin Date: Mon, 29 Aug 2022 14:41:56 +0800 Subject: [PATCH 22/76] fix: release pages with drop case --- source/libs/tdb/src/db/tdbBtree.c | 14 ++++++-------- source/libs/tdb/src/db/tdbPCache.c | 1 + source/libs/tdb/src/db/tdbPager.c | 1 + source/libs/tdb/src/inc/tdbInt.h | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/source/libs/tdb/src/db/tdbBtree.c b/source/libs/tdb/src/db/tdbBtree.c index 4701318779..1480920f90 100644 --- a/source/libs/tdb/src/db/tdbBtree.c +++ b/source/libs/tdb/src/db/tdbBtree.c @@ -509,7 +509,7 @@ static int tdbBtreeBalanceDeeper(SBTree *pBt, SPage *pRoot, SPage **ppChild, TXN static int tdbBtreeBalanceNonRoot(SBTree *pBt, SPage *pParent, int idx, TXN *pTxn) { int ret; - int nOlds; + int nOlds, pageIdx; SPage *pOlds[3] = {0}; SCell *pDivCell[3] = {0}; int szDivCell[3]; @@ -849,13 +849,11 @@ static int tdbBtreeBalanceNonRoot(SBTree *pBt, SPage *pParent, int idx, TXN *pTx } } - // TODO: here is not corrent for drop case - for (int i = 0; i < nNews; i++) { - if (i < nOlds) { - tdbPagerReturnPage(pBt->pPager, pOlds[i], pTxn); - } else { - tdbPagerReturnPage(pBt->pPager, pNews[i], pTxn); - } + for (pageIdx = 0; pageIdx < nOlds; ++pageIdx) { + tdbPagerReturnPage(pBt->pPager, pOlds[pageIdx], pTxn); + } + for (; pageIdx < nNews; ++pageIdx) { + tdbPagerReturnPage(pBt->pPager, pNews[pageIdx], pTxn); } return 0; diff --git a/source/libs/tdb/src/db/tdbPCache.c b/source/libs/tdb/src/db/tdbPCache.c index 76d95cbb91..6254158591 100644 --- a/source/libs/tdb/src/db/tdbPCache.c +++ b/source/libs/tdb/src/db/tdbPCache.c @@ -98,6 +98,7 @@ SPage *tdbPCacheFetch(SPCache *pCache, const SPgid *pPgid, TXN *pTxn) { // printf("thread %" PRId64 " fetch page %d pgno %d pPage %p nRef %d\n", taosGetSelfPthreadId(), pPage->id, // TDB_PAGE_PGNO(pPage), pPage, nRef); + tdbDebug("pcache/fetch page %p/%d/%d/%d", pPage, TDB_PAGE_PGNO(pPage), pPage->id, nRef); return pPage; } diff --git a/source/libs/tdb/src/db/tdbPager.c b/source/libs/tdb/src/db/tdbPager.c index 4de99e8b1b..f90c392788 100644 --- a/source/libs/tdb/src/db/tdbPager.c +++ b/source/libs/tdb/src/db/tdbPager.c @@ -166,6 +166,7 @@ int tdbPagerWrite(SPager *pPager, SPage *pPage) { // ref page one more time so the page will not be release tdbRefPage(pPage); + tdbDebug("pcache/mdirty page %p/%d/%d", pPage, TDB_PAGE_PGNO(pPage), pPage->id); // Set page as dirty pPage->isDirty = 1; diff --git a/source/libs/tdb/src/inc/tdbInt.h b/source/libs/tdb/src/inc/tdbInt.h index 49126b80b6..6a694cf8f1 100644 --- a/source/libs/tdb/src/inc/tdbInt.h +++ b/source/libs/tdb/src/inc/tdbInt.h @@ -280,13 +280,13 @@ struct SPage { static inline i32 tdbRefPage(SPage *pPage) { i32 nRef = atomic_add_fetch_32(&((pPage)->nRef), 1); - tdbTrace("ref page %d, nRef %d", pPage->id, nRef); + tdbTrace("ref page %p/%d, nRef %d", pPage, pPage->id, nRef); return nRef; } static inline i32 tdbUnrefPage(SPage *pPage) { i32 nRef = atomic_sub_fetch_32(&((pPage)->nRef), 1); - tdbTrace("unref page %d, nRef %d", pPage->id, nRef); + tdbTrace("unref page %p/%d, nRef %d", pPage, pPage->id, nRef); return nRef; } From c36119bd704f94a3c8a8fa1d30aa8ace6c44620d Mon Sep 17 00:00:00 2001 From: Xiaoyu Wang <59301069+xiao-yu-wang@users.noreply.github.com> Date: Mon, 29 Aug 2022 15:36:59 +0800 Subject: [PATCH 23/76] Update 06-select.md --- docs/zh/12-taos-sql/06-select.md | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/docs/zh/12-taos-sql/06-select.md b/docs/zh/12-taos-sql/06-select.md index 0c305231e0..d8ff3f04ed 100644 --- a/docs/zh/12-taos-sql/06-select.md +++ b/docs/zh/12-taos-sql/06-select.md @@ -355,19 +355,15 @@ SELECT ... FROM (SELECT ... FROM ...) ...; :::info -- 目前仅支持一层嵌套,也即不能在子查询中再嵌入子查询。 -- 内层查询的返回结果将作为“虚拟表”供外层查询使用,此虚拟表可以使用 AS 语法做重命名,以便于外层查询中方便引用。 -- 目前不能在“连续查询”功能中使用子查询。 +- 内层查询的返回结果将作为“虚拟表”供外层查询使用,此虚拟表建议起别名,以便于外层查询中方便引用。 - 在内层和外层查询中,都支持普通的表间/超级表间 JOIN。内层查询的计算结果也可以再参与数据子表的 JOIN 操作。 -- 目前内层查询、外层查询均不支持 UNION 操作。 - 内层查询支持的功能特性与非嵌套的查询语句能力是一致的。 - 内层查询的 ORDER BY 子句一般没有意义,建议避免这样的写法以免无谓的资源消耗。 - 与非嵌套的查询语句相比,外层查询所能支持的功能特性存在如下限制: - 计算函数部分: - - 如果内层查询的结果数据未提供时间戳,那么计算过程依赖时间戳的函数在外层会无法正常工作。例如:TOP, BOTTOM, FIRST, LAST, DIFF。 - - 计算过程需要两遍扫描的函数,在外层查询中无法正常工作。例如:此类函数包括:STDDEV, PERCENTILE。 - - 外层查询中不支持 IN 算子,但在内层中可以使用。 - - 外层查询不支持 GROUP BY。 + - 如果内层查询的结果数据未提供时间戳,那么计算过程隐式依赖时间戳的函数在外层会无法正常工作。例如:INTERP, DERIVATIVE, IRATE, LAST_ROW, FIRST, LAST, TWA, STATEDURATION, TAIL, UNIQUE。 + - 如果内层查询的结果数据不是有效的时间序列,那么计算过程依赖数据为时间序列的函数在外层会无法正常工作。例如:LEASTSQUARES, ELAPSED, INTERP, DERIVATIVE, IRATE, TWA, DIFF, STATECOUNT, STATEDURATION, CSUM, MAVG, TAIL, UNIQUE。 + - 计算过程需要两遍扫描的函数,在外层查询中无法正常工作。例如:此类函数包括:PERCENTILE。 ::: From a861537bc075f8b1dc2ebccd4ad4cdfaa7ceb1a6 Mon Sep 17 00:00:00 2001 From: dapan1121 Date: Mon, 29 Aug 2022 15:44:10 +0800 Subject: [PATCH 24/76] fix: fix filter group merge error --- source/libs/scalar/inc/filterInt.h | 2 +- source/libs/scalar/src/filter.c | 82 ++++++++++++++++++++++++----- tests/script/jenkins/basic.txt | 1 + tests/script/tsim/scalar/filter.sim | 38 +++++++++++++ 4 files changed, 110 insertions(+), 13 deletions(-) create mode 100644 tests/script/tsim/scalar/filter.sim diff --git a/source/libs/scalar/inc/filterInt.h b/source/libs/scalar/inc/filterInt.h index 23693c785a..23d88d1a26 100644 --- a/source/libs/scalar/inc/filterInt.h +++ b/source/libs/scalar/inc/filterInt.h @@ -276,7 +276,7 @@ struct SFilterInfo { #define FILTER_CLR_FLAG(st, f) st &= (~f) #define SIMPLE_COPY_VALUES(dst, src) *((int64_t *)dst) = *((int64_t *)src) -#define FILTER_PACKAGE_UNIT_HASH_KEY(v, optr, idx1, idx2) do { char *_t = (char *)v; _t[0] = optr; *(uint32_t *)(_t + 1) = idx1; *(uint32_t *)(_t + 3) = idx2; } while (0) +#define FLT_PACKAGE_UNIT_HASH_KEY(v, op1, op2, lidx, ridx, ridx2) do { char *_t = (char *)(v); _t[0] = (op1); _t[1] = (op2); *(uint32_t *)(_t + 2) = (lidx); *(uint32_t *)(_t + 2 + sizeof(uint32_t)) = (ridx); } while (0) #define FILTER_GREATER(cr,sflag,eflag) ((cr > 0) || ((cr == 0) && (FILTER_GET_FLAG(sflag,RANGE_FLG_EXCLUDE) || FILTER_GET_FLAG(eflag,RANGE_FLG_EXCLUDE)))) #define FILTER_COPY_RA(dst, src) do { (dst)->sflag = (src)->sflag; (dst)->eflag = (src)->eflag; (dst)->s = (src)->s; (dst)->e = (src)->e; } while (0) diff --git a/source/libs/scalar/src/filter.c b/source/libs/scalar/src/filter.c index 4377dbf14e..b27a61b8bd 100644 --- a/source/libs/scalar/src/filter.c +++ b/source/libs/scalar/src/filter.c @@ -910,14 +910,14 @@ int32_t filterAddFieldFromNode(SFilterInfo *info, SNode *node, SFilterFieldId *f return TSDB_CODE_SUCCESS; } -int32_t filterAddUnit(SFilterInfo *info, uint8_t optr, SFilterFieldId *left, SFilterFieldId *right, uint32_t *uidx) { +int32_t filterAddUnitImpl(SFilterInfo *info, uint8_t optr, SFilterFieldId *left, SFilterFieldId *right, uint8_t optr2, SFilterFieldId *right2, uint32_t *uidx) { if (FILTER_GET_FLAG(info->options, FLT_OPTION_NEED_UNIQE)) { if (info->pctx.unitHash == NULL) { info->pctx.unitHash = taosHashInit(FILTER_DEFAULT_GROUP_SIZE * FILTER_DEFAULT_UNIT_SIZE, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BIGINT), false, false); } else { - int64_t v = 0; - FILTER_PACKAGE_UNIT_HASH_KEY(&v, optr, left->idx, right ? right->idx : -1); - void *hu = taosHashGet(info->pctx.unitHash, &v, sizeof(v)); + char v[14] = {0}; + FLT_PACKAGE_UNIT_HASH_KEY(&v, optr, optr2, left->idx, (right ? right->idx : -1), (right2 ? right2->idx : -1)); + void *hu = taosHashGet(info->pctx.unitHash, v, sizeof(v)); if (hu) { *uidx = *(uint32_t *)hu; return TSDB_CODE_SUCCESS; @@ -939,7 +939,11 @@ int32_t filterAddUnit(SFilterInfo *info, uint8_t optr, SFilterFieldId *left, SFi if (right) { u->right = *right; } - + u->compare.optr2 = optr2; + if (right2) { + u->right2 = *right2; + } + if (u->right.type == FLD_TYPE_VALUE) { SFilterField *val = FILTER_UNIT_RIGHT_FIELD(info, u); assert(FILTER_GET_FLAG(val->flag, FLD_TYPE_VALUE)); @@ -960,9 +964,9 @@ int32_t filterAddUnit(SFilterInfo *info, uint8_t optr, SFilterFieldId *left, SFi *uidx = info->unitNum; if (FILTER_GET_FLAG(info->options, FLT_OPTION_NEED_UNIQE)) { - int64_t v = 0; - FILTER_PACKAGE_UNIT_HASH_KEY(&v, optr, left->idx, right ? right->idx : -1); - taosHashPut(info->pctx.unitHash, &v, sizeof(v), uidx, sizeof(*uidx)); + char v[14] = {0}; + FLT_PACKAGE_UNIT_HASH_KEY(&v, optr, optr2, left->idx, (right ? right->idx : -1), (right2 ? right2->idx : -1)); + taosHashPut(info->pctx.unitHash, v, sizeof(v), uidx, sizeof(*uidx)); } ++info->unitNum; @@ -971,6 +975,9 @@ int32_t filterAddUnit(SFilterInfo *info, uint8_t optr, SFilterFieldId *left, SFi } +int32_t filterAddUnit(SFilterInfo *info, uint8_t optr, SFilterFieldId *left, SFilterFieldId *right, uint32_t *uidx) { + return filterAddUnitImpl(info, optr, left, right, 0, NULL, uidx); +} int32_t filterAddUnitToGroup(SFilterGroup *group, uint32_t unitIdx) { if (group->unitNum >= group->unitSize) { @@ -1147,8 +1154,8 @@ int32_t filterAddGroupUnitFromCtx(SFilterInfo *dst, SFilterInfo *src, SFilterRan SIMPLE_COPY_VALUES(data2, &ra->e); filterAddField(dst, NULL, &data2, FLD_TYPE_VALUE, &right2, tDataTypes[type].bytes, true); - filterAddUnit(dst, FILTER_GET_FLAG(ra->sflag, RANGE_FLG_EXCLUDE) ? OP_TYPE_GREATER_THAN : OP_TYPE_GREATER_EQUAL, &left, &right, &uidx); - filterAddUnitRight(dst, FILTER_GET_FLAG(ra->eflag, RANGE_FLG_EXCLUDE) ? OP_TYPE_LOWER_THAN : OP_TYPE_LOWER_EQUAL, &right2, uidx); + filterAddUnitImpl(dst, FILTER_GET_FLAG(ra->sflag, RANGE_FLG_EXCLUDE) ? OP_TYPE_GREATER_THAN : OP_TYPE_GREATER_EQUAL, &left, &right, + FILTER_GET_FLAG(ra->eflag, RANGE_FLG_EXCLUDE) ? OP_TYPE_LOWER_THAN : OP_TYPE_LOWER_EQUAL, &right2, &uidx); filterAddUnitToGroup(g, uidx); return TSDB_CODE_SUCCESS; } @@ -1222,8 +1229,8 @@ int32_t filterAddGroupUnitFromCtx(SFilterInfo *dst, SFilterInfo *src, SFilterRan SIMPLE_COPY_VALUES(data2, &r->ra.e); filterAddField(dst, NULL, &data2, FLD_TYPE_VALUE, &right2, tDataTypes[type].bytes, true); - filterAddUnit(dst, FILTER_GET_FLAG(r->ra.sflag, RANGE_FLG_EXCLUDE) ? OP_TYPE_GREATER_THAN : OP_TYPE_GREATER_EQUAL, &left, &right, &uidx); - filterAddUnitRight(dst, FILTER_GET_FLAG(r->ra.eflag, RANGE_FLG_EXCLUDE) ? OP_TYPE_LOWER_THAN : OP_TYPE_LOWER_EQUAL, &right2, uidx); + filterAddUnitImpl(dst, FILTER_GET_FLAG(r->ra.sflag, RANGE_FLG_EXCLUDE) ? OP_TYPE_GREATER_THAN : OP_TYPE_GREATER_EQUAL, &left, &right, + FILTER_GET_FLAG(r->ra.eflag, RANGE_FLG_EXCLUDE) ? OP_TYPE_LOWER_THAN : OP_TYPE_LOWER_EQUAL, &right2, &uidx); filterAddUnitToGroup(g, uidx); } @@ -2073,6 +2080,44 @@ int32_t filterMergeGroupUnits(SFilterInfo *info, SFilterGroupCtx** gRes, int32_t return TSDB_CODE_SUCCESS; } +bool filterIsSameUnits(SFilterColInfo* pCol1, SFilterColInfo* pCol2) { + if (pCol1->type != pCol2->type) { + return false; + } + + if (RANGE_TYPE_MR_CTX == pCol1->type) { + SFilterRangeCtx* pCtx1 = (SFilterRangeCtx*)pCol1->info; + SFilterRangeCtx* pCtx2 = (SFilterRangeCtx*)pCol2->info; + + if ((pCtx1->isnull != pCtx2->isnull) || (pCtx1->notnull != pCtx2->notnull) || (pCtx1->isrange != pCtx2->isrange)) { + return false; + } + + + SFilterRangeNode* pNode1 = pCtx1->rs; + SFilterRangeNode* pNode2 = pCtx2->rs; + + while (true) { + if (NULL == pNode1 && NULL == pNode2) { + break; + } + + if (NULL == pNode1 || NULL == pNode2) { + return false; + } + + if (pNode1->ra.s != pNode2->ra.s || pNode1->ra.e != pNode2->ra.e || pNode1->ra.sflag != pNode2->ra.sflag || pNode1->ra.eflag != pNode2->ra.eflag) { + return false; + } + + pNode1 = pNode1->next; + pNode2 = pNode2->next; + } + } + + return true; +} + void filterCheckColConflict(SFilterGroupCtx* gRes1, SFilterGroupCtx* gRes2, bool *conflict) { uint32_t idx1 = 0, idx2 = 0, m = 0, n = 0; bool equal = false; @@ -2098,6 +2143,11 @@ void filterCheckColConflict(SFilterGroupCtx* gRes1, SFilterGroupCtx* gRes2, bool return; } + if (!filterIsSameUnits(&gRes1->colInfo[idx1], &gRes2->colInfo[idx2])) { + *conflict = true; + return; + } + // for long in operation if (gRes1->colInfo[idx1].optr == OP_TYPE_EQUAL && gRes2->colInfo[idx2].optr == OP_TYPE_EQUAL) { SFilterRangeCtx* ctx = gRes1->colInfo[idx1].info; @@ -2711,17 +2761,22 @@ int32_t filterRmUnitByRange(SFilterInfo *info, SColumnDataAgg *pDataStatis, int3 for (uint32_t g = 0; g < info->groupNum; ++g) { SFilterGroup *group = &info->groups[g]; + // first is block unint num for a group, following append unitNum blkUnitIdx for this group *unitNum = group->unitNum; all = 0; empty = 0; + // save group idx start pointer + uint32_t * pGroupIdx = unitIdx; for (uint32_t u = 0; u < group->unitNum; ++u) { uint32_t uidx = group->unitIdxs[u]; if (info->blkUnitRes[uidx] == 1) { + // blkUnitRes == 1 is always true, so need not compare every time, delete this unit from group --(*unitNum); all = 1; continue; } else if (info->blkUnitRes[uidx] == -1) { + // blkUnitRes == -1 is alwary false, so in group is alwary false, need delete this group from blkGroupNum *unitNum = 0; empty = 1; break; @@ -2731,6 +2786,9 @@ int32_t filterRmUnitByRange(SFilterInfo *info, SColumnDataAgg *pDataStatis, int3 } if (*unitNum == 0) { + // if unit num is zero, reset unitIdx to start on this group + unitIdx = pGroupIdx; + --info->blkGroupNum; assert(empty || all); diff --git a/tests/script/jenkins/basic.txt b/tests/script/jenkins/basic.txt index 97295d75e0..46bae734ea 100644 --- a/tests/script/jenkins/basic.txt +++ b/tests/script/jenkins/basic.txt @@ -344,6 +344,7 @@ # --- scalar ---- ./test.sh -f tsim/scalar/in.sim ./test.sh -f tsim/scalar/scalar.sim +./test.sh -f tsim/scalar/filter.sim # ---- alter ---- ./test.sh -f tsim/alter/cached_schema_after_alter.sim diff --git a/tests/script/tsim/scalar/filter.sim b/tests/script/tsim/scalar/filter.sim new file mode 100644 index 0000000000..9231662278 --- /dev/null +++ b/tests/script/tsim/scalar/filter.sim @@ -0,0 +1,38 @@ +system sh/stop_dnodes.sh +system sh/deploy.sh -n dnode1 -i 1 +system sh/exec.sh -n dnode1 -s start +sql connect + +print ======== step1 +sql drop database if exists db1; +sql create database db1 vgroups 3; +sql use db1; +sql create stable st1 (fts timestamp, fbool bool, ftiny tinyint, fsmall smallint, fint int, fbig bigint, futiny tinyint unsigned, fusmall smallint unsigned, fuint int unsigned, fubig bigint unsigned, ffloat float, fdouble double, fbin binary(10), fnchar nchar(10)) tags(tts timestamp, tbool bool, ttiny tinyint, tsmall smallint, tint int, tbig bigint, tutiny tinyint unsigned, tusmall smallint unsigned, tuint int unsigned, tubig bigint unsigned, tfloat float, tdouble double, tbin binary(10), tnchar nchar(10)); +sql create table tb1 using st1 tags('2022-07-10 16:31:00', true, 1, 1, 1, 1, 1, 1, 1, 1, 1.0, 1.0, 'a', 'a'); +sql create table tb2 using st1 tags('2022-07-10 16:32:00', false, 2, 2, 2, 2, 2, 2, 2, 2, 2.0, 2.0, 'b', 'b'); +sql create table tb3 using st1 tags('2022-07-10 16:33:00', true, 3, 3, 3, 3, 3, 3, 3, 3, 3.0, 3.0, 'c', 'c'); + +sql insert into tb1 values ('2022-07-10 16:31:01', false, 1, 1, 1, 1, 1, 1, 1, 1, 1.0, 1.0, 'a', 'a'); +sql insert into tb1 values ('2022-07-10 16:31:02', true, 2, 2, 2, 2, 2, 2, 2, 2, 2.0, 2.0, 'b', 'b'); +sql insert into tb1 values ('2022-07-10 16:31:03', false, 3, 3, 3, 3, 3, 3, 3, 3, 3.0, 3.0, 'c', 'c'); +sql insert into tb1 values ('2022-07-10 16:31:04', true, 4, 4, 4, 4, 4, 4, 4, 4, 4.0, 4.0, 'd', 'd'); +sql insert into tb1 values ('2022-07-10 16:31:05', false, 5, 5, 5, 5, 5, 5, 5, 5, 5.0, 5.0, 'e', 'e'); + +sql insert into tb2 values ('2022-07-10 16:32:01', false, 1, 1, 1, 1, 1, 1, 1, 1, 1.0, 1.0, 'a', 'a'); +sql insert into tb2 values ('2022-07-10 16:32:02', true, 2, 2, 2, 2, 2, 2, 2, 2, 2.0, 2.0, 'b', 'b'); +sql insert into tb2 values ('2022-07-10 16:32:03', false, 3, 3, 3, 3, 3, 3, 3, 3, 3.0, 3.0, 'c', 'c'); +sql insert into tb2 values ('2022-07-10 16:32:04', true, 4, 4, 4, 4, 4, 4, 4, 4, 4.0, 4.0, 'd', 'd'); +sql insert into tb2 values ('2022-07-10 16:32:05', false, 5, 5, 5, 5, 5, 5, 5, 5, 5.0, 5.0, 'e', 'e'); + +sql insert into tb3 values ('2022-07-10 16:33:01', false, 1, 1, 1, 1, 1, 1, 1, 1, 1.0, 1.0, 'a', 'a'); +sql insert into tb3 values ('2022-07-10 16:33:02', true, 2, 2, 2, 2, 2, 2, 2, 2, 2.0, 2.0, 'b', 'b'); +sql insert into tb3 values ('2022-07-10 16:33:03', false, 3, 3, 3, 3, 3, 3, 3, 3, 3.0, 3.0, 'c', 'c'); +sql insert into tb3 values ('2022-07-10 16:33:04', true, 4, 4, 4, 4, 4, 4, 4, 4, 4.0, 4.0, 'd', 'd'); +sql insert into tb3 values ('2022-07-10 16:33:05', false, 5, 5, 5, 5, 5, 5, 5, 5, 5.0, 5.0, 'e', 'e'); + +sql select * from st1 where (ttiny > 2 or ftiny < 5) and ftiny > 2; +if $rows != 7 then + return -1 +endi + +system sh/exec.sh -n dnode1 -s stop -x SIGINT From 8a59420ccc529047a25b88375b2a12e389e7bdf1 Mon Sep 17 00:00:00 2001 From: tomchon Date: Mon, 29 Aug 2022 15:44:57 +0800 Subject: [PATCH 25/76] test: add checkpackages scritps --- packaging/MPtestJenkinsfile | 38 +++++++++++++--------- packaging/checkPackageRuning.py | 4 ++- packaging/testpackage.sh | 57 ++++++++++++++++++++++++++++++--- 3 files changed, 79 insertions(+), 20 deletions(-) diff --git a/packaging/MPtestJenkinsfile b/packaging/MPtestJenkinsfile index 1e2e69a977..0782038c03 100644 --- a/packaging/MPtestJenkinsfile +++ b/packaging/MPtestJenkinsfile @@ -1,6 +1,7 @@ def sync_source(branch_name) { sh ''' hostname + IP env echo ''' + branch_name + ''' ''' @@ -15,6 +16,7 @@ def sync_source(branch_name) { cd ${TDENGINE_ROOT_DIR} git reset --hard git fetch || git fetch + rm -rf examples/rust/ git checkout ''' + branch_name + ''' -f git branch git pull || git pull @@ -53,6 +55,16 @@ pipeline { defaultValue:'3.0.0.1', description: 'This number of baseVerison is generally not modified.Now it is 3.0.0.1' ) + string ( + name:'toolsVersion', + defaultValue:'2.1.2', + description: 'This number of baseVerison is generally not modified.Now it is 3.0.0.1' + ) + string ( + name:'toolsBaseVersion', + defaultValue:'2.1.2', + description: 'This number of baseVerison is generally not modified.Now it is 3.0.0.1' + ) } environment{ WORK_DIR = '/var/lib/jenkins/workspace' @@ -86,26 +98,18 @@ pipeline { TD_CLIENT_EXE = "TDengine-client-${version}-Windows-x64.exe" + TD_TOOLS_TAR = "taosTools-${toolsVersion}-Linux-x64.tar.gz" + } stages { stage ('RUN') { - stage('get check package scritps'){ - agent{label 'ubuntu18'} - steps { - catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') { - script{ - sync_source("${BRANCH_NAME}") - } - - } - } - } parallel { stage('ubuntu16') { agent{label " ubuntu16 "} steps { - timeout(time: 3, unit: 'MINUTES'){ + timeout(time: 10, unit: 'MINUTES'){ + sync_source("${BRANCH_NAME}") sh ''' cd ${TDENGINE_ROOT_DIR}/packaging bash testpackage.sh ${TD_SERVER_TAR} ${version} ${BASE_TD_SERVER_TAR} ${baseVersion} server @@ -128,7 +132,8 @@ pipeline { stage('ubuntu18') { agent{label " ubuntu18 "} steps { - timeout(time: 3, unit: 'MINUTES'){ + timeout(time: 10, unit: 'MINUTES'){ + sync_source("${BRANCH_NAME}") sh ''' cd ${TDENGINE_ROOT_DIR}/packaging bash testpackage.sh ${TD_SERVER_TAR} ${version} ${BASE_TD_SERVER_TAR} ${baseVersion} server @@ -151,7 +156,8 @@ pipeline { stage('centos7') { agent{label " centos7_9 "} steps { - timeout(time: 240, unit: 'MINUTES'){ + timeout(time: 10, unit: 'MINUTES'){ + sync_source("${BRANCH_NAME}") sh ''' cd ${TDENGINE_ROOT_DIR}/packaging bash testpackage.sh ${TD_SERVER_TAR} ${version} ${BASE_TD_SERVER_TAR} ${baseVersion} server @@ -161,6 +167,7 @@ pipeline { sh ''' cd ${TDENGINE_ROOT_DIR}/packaging bash testpackage.sh ${TD_SERVER_LITE_TAR} ${version} ${BASE_TD_SERVER_LITE_TAR} ${baseVersion} server + bash testpackage.sh ${TD_SERVER_LITE_TAR} ${version} ${BASE_TD_SERVER_LITE_TAR} ${baseVersion} server python3 checkPackageRuning.py ''' sh ''' @@ -174,7 +181,8 @@ pipeline { stage('centos8') { agent{label " centos8_3 "} steps { - timeout(time: 240, unit: 'MINUTES'){ + timeout(time: 10, unit: 'MINUTES'){ + sync_source("${BRANCH_NAME}") sh ''' cd ${TDENGINE_ROOT_DIR}/packaging bash testpackage.sh ${TD_SERVER_TAR} ${version} ${BASE_TD_SERVER_TAR} ${baseVersion} server diff --git a/packaging/checkPackageRuning.py b/packaging/checkPackageRuning.py index e53cc3bdbc..c0d1e8b86c 100755 --- a/packaging/checkPackageRuning.py +++ b/packaging/checkPackageRuning.py @@ -22,10 +22,12 @@ import time # install taospy out = subprocess.getoutput("pip3 show taospy|grep Version| awk -F ':' '{print $2}' ") -print(out) +print("taospy version %s "%out) if (out == "" ): os.system("pip install git+https://github.com/taosdata/taos-connector-python.git") print("install taos python connector") +else: + os.system("pip3 install --upgrade taospy ") diff --git a/packaging/testpackage.sh b/packaging/testpackage.sh index 173fa3a3c3..3ca31ea7a4 100755 --- a/packaging/testpackage.sh +++ b/packaging/testpackage.sh @@ -1,8 +1,32 @@ #!/bin/sh -# function installPkgAndCheckFile{ +# # ============================= get input parameters ================================================= -echo "Download package" +# # 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 "Download package" packgeName=$1 version=$2 @@ -25,22 +49,42 @@ elif [ ${testFile} = "tools" ];then installCmd="install-taostools.sh" fi +function cmdInstall { +comd=$1 +if command -v ${comd} ;then + echo "${comd} is already installed" +else + if command -v apt ;then + apt-get install ${comd} + elif command -v yum ;then + yum install ${comd} + else + echo "you should install ${comd} manually" + fi +fi +} + + echo "Uninstall all components of TDeingne" if command -v rmtaos ;then echo "uninstall all components of TDeingne:rmtaos" - echo " " + rmtaos else echo "os doesn't include TDengine " fi if command -v rmtaostools ;then echo "uninstall all components of TDeingne:rmtaostools" - echo " " + rmtaostools else echo "os doesn't include rmtaostools " fi + +cmdInstall tree +cmdInstall wget + echo "new workroom path" installPath="/usr/local/src/packageTest" oriInstallPath="/usr/local/src/packageTest/3.1" @@ -104,6 +148,11 @@ elif [[ ${packgeName} =~ "tar" ]];then else bash ${installCmd} fi + if [[ ${packgeName} =~ "Lite" ]];then + cd ${installPath} + wget https://www.taosdata.com/assets-download/3.0/taosTools-2.1.2-Linux-x64.tar.gz + tar xvf taosTools-2.1.2-Linux-x64.tar.gz + cd taosTools-2.1.2 && bash install-taostools.sh fi # } From 97052fccc586c42ccc95d14fc743029947988ad1 Mon Sep 17 00:00:00 2001 From: Pan YANG Date: Mon, 29 Aug 2022 16:07:12 +0800 Subject: [PATCH 26/76] docs: change TAOS SQL to TDengine SQL --- docs/en/12-taos-sql/index.md | 4 +- .../13-schemaless/13-schemaless.md | 56 +++++++++---------- docs/en/21-tdinternal/01-arch.md | 20 ++++--- docs/zh/07-develop/02-model/index.mdx | 4 +- .../03-insert-data/01-sql-writing.mdx | 4 +- docs/zh/07-develop/04-query-data/index.mdx | 6 +- docs/zh/12-taos-sql/index.md | 8 +-- .../13-schemaless/13-schemaless.md | 28 +++++----- docs/zh/21-tdinternal/01-arch.md | 2 +- 9 files changed, 69 insertions(+), 63 deletions(-) diff --git a/docs/en/12-taos-sql/index.md b/docs/en/12-taos-sql/index.md index e243cd2318..a5ffc9dc8d 100644 --- a/docs/en/12-taos-sql/index.md +++ b/docs/en/12-taos-sql/index.md @@ -1,6 +1,6 @@ --- title: TDengine SQL -description: "The syntax supported by TDengine SQL " +description: 'The syntax supported by TDengine SQL ' --- This section explains the syntax of SQL to perform operations on databases, tables and STables, insert data, select data and use functions. We also provide some tips that can be used in TDengine SQL. If you have previous experience with SQL this section will be fairly easy to understand. If you do not have previous experience with SQL, you'll come to appreciate the simplicity and power of SQL. TDengine SQL has been enhanced in version 3.0, and the query engine has been rearchitected. For information about how TDengine SQL has changed, see [Changes in TDengine 3.0](../taos-sql/changes). @@ -15,7 +15,7 @@ Syntax Specifications used in this chapter: - | 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 data from electric meters. Each meter collects 3 data measurements: current, voltage, phase. The data model is shown below: +To better demonstrate the syntax, usage and rules of TDengine SQL, hereinafter it's assumed that there is a data set of data from electric meters. Each meter collects 3 data measurements: current, voltage, phase. The data model is shown below: ``` taos> DESCRIBE meters; diff --git a/docs/en/14-reference/13-schemaless/13-schemaless.md b/docs/en/14-reference/13-schemaless/13-schemaless.md index 816ebe0047..6de4810212 100644 --- a/docs/en/14-reference/13-schemaless/13-schemaless.md +++ b/docs/en/14-reference/13-schemaless/13-schemaless.md @@ -1,6 +1,6 @@ --- 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." +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, data is collected for many purposes such as intelligent control, business analysis, device monitoring and so on. Due to changes in business or functional requirements or changes in device hardware, the application logic and even the data collected may change. Schemaless writing automatically creates storage structures for your data as it is being written to TDengine, so that you do not need to create supertables in advance. When necessary, schemaless writing @@ -25,7 +25,7 @@ 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 tags, with format like `=,=` Enter a space between `tag_set` and `field_set`. - `field_set`will be used as data columns, with format like `=,=` Enter a space between `field_set` and `timestamp`. -- `timestamp` is the primary key timestamp corresponding to this row of data +- `timestamp` is the primary key timestamp corresponding to this row of data All data in tag_set is automatically converted to the NCHAR data type and does not require double quotes ("). @@ -36,14 +36,14 @@ In the schemaless writing data line protocol, each data item in the field_set ne - 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/u8 | TinyInt/UTinyInt | 1 | -| 4 | i16/u16 | SmallInt/USmallInt | 2 | -| 5 | i32/u32 | Int/UInt | 4 | -| 6 | i64/i/u64/u | BigInt/BigInt/UBigInt/UBigInt | 8 | +| **Serial number** | **Postfix** | **Mapping type** | **Size (bytes)** | +| ----------------- | ----------- | ----------------------------- | ---------------- | +| 1 | None or f64 | double | 8 | +| 2 | f32 | float | 4 | +| 3 | i8/u8 | TinyInt/UTinyInt | 1 | +| 4 | i16/u16 | SmallInt/USmallInt | 2 | +| 5 | i32/u32 | Int/UInt | 4 | +| 6 | i64/i/u64/u | BigInt/BigInt/UBigInt/UBigInt | 8 | - `t`, `T`, `true`, `True`, `TRUE`, `f`, `F`, `false`, and `False` will be handled directly as BOOL types. @@ -61,14 +61,14 @@ Note that if the wrong case is used when describing the data type suffix, or if Schemaless writes process row data according to the following principles. -1. You can use the following rules to generate the subtable names: first, combine the measurement name and the key and value of the label into the next string: +1. You can use the following rules to generate the subtable 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. +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. You can configure smlChildTableName to specify table names, for example, `smlChildTableName=tname`. You can insert `st,tname=cpul,t1=4 c1=3 1626006833639000000` and the cpu1 table will be automatically created. Note that if multiple rows have the same tname but different tag_set values, the tag_set of the first row is used to create the table and the others are ignored. 2. If the super table obtained by parsing the line protocol does not exist, this super table is created. @@ -82,7 +82,7 @@ You can configure smlChildTableName to specify table names, for example, `smlChi :::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 -16KB. See [TAOS SQL Boundary Limits](/taos-sql/limit) for specific constraints in this area. +16KB. See [TDengine SQL Boundary Limits](/taos-sql/limit) for specific constraints in this area. ::: @@ -90,23 +90,23 @@ All processing logic of schemaless will still follow TDengine's underlying restr 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 file protocol | -| 3 | SML_JSON_PROTOCOL | OpenTSDB JSON protocol | +| **Serial** | **Value** | **Description** | +| ---------- | ------------------- | ---------------------- | +| 1 | SML_LINE_PROTOCOL | InfluxDB Line Protocol | +| 2 | SML_TELNET_PROTOCOL | OpenTSDB file protocol | +| 3 | SML_JSON_PROTOCOL | OpenTSDB JSON protocol | In InfluxDB line protocol mode, you must specify the precision of the input timestamp. Valid precisions are described in the following table. -| **No.** | **Precision** | **Description** | -| -------- | --------------------------------- | -------------- | -| 1 | TSDB_SML_TIMESTAMP_NOT_CONFIGURED | Not defined (invalid) | -| 2 | TSDB_SML_TIMESTAMP_HOURS | Hours | -| 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 | +| **No.** | **Precision** | **Description** | +| ------- | --------------------------------- | --------------------- | +| 1 | TSDB_SML_TIMESTAMP_NOT_CONFIGURED | Not defined (invalid) | +| 2 | TSDB_SML_TIMESTAMP_HOURS | Hours | +| 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 OpenTSDB file and JSON protocol modes, the precision of the timestamp is determined from its length in the standard OpenTSDB manner. User input is ignored. diff --git a/docs/en/21-tdinternal/01-arch.md b/docs/en/21-tdinternal/01-arch.md index 44651c0496..2f876adffc 100644 --- a/docs/en/21-tdinternal/01-arch.md +++ b/docs/en/21-tdinternal/01-arch.md @@ -12,6 +12,7 @@ The design of TDengine is based on the assumption that any hardware or software Logical structure diagram of TDengine's distributed architecture is as follows: ![TDengine Database architecture diagram](structure.webp) +
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. @@ -38,15 +39,16 @@ A complete TDengine system runs on one or more physical nodes. Logically, it inc **Cluster external connection**: TDengine cluster can accommodate a 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: +**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; +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 separate execution code. The server-side only has one 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, totally transparently and 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. +**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 user tutorial for detailed steps. In this way, the cluster will be established step by step. @@ -57,6 +59,7 @@ A complete TDengine system runs on one or more physical nodes. Logically, it inc 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 Database](message.webp) +
Figure 2: Typical process of TDengine
1. Application initiates a request to insert data through JDBC, ODBC, or other APIs. @@ -121,16 +124,17 @@ The load balancing process does not require any manual intervention, and it is t If a database has N replicas, a virtual node group has N virtual nodes. But only one is the Leader and all others are slaves. When the application writes a new record to system, only the Leader vnode can accept the writing request. If a follower vnode receives a writing request, the system will notifies TAOSC to redirect. -### Leader vnode Writing Process +### Leader vnode Writing Process Leader Vnode uses a writing process as follows: ![TDengine Database Leader Writing Process](write_master.webp) +
Figure 3: TDengine Leader writing process
1. Leader 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 follower vnodes in the same virtual node group, and the forwarded packet has a version number with data; +3. If there are multiple replicas, vnode will forward data packet to follower 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. Leader vnode returns a confirmation message to the application, indicating a successful write. 6. If any of Step 2, 3 or 4 fails, the error will directly return to the application. @@ -140,6 +144,7 @@ Leader Vnode uses a writing process as follows: For a follower vnode, the write process as follows: ![TDengine Database Follower Writing Process](write_slave.webp) +
Figure 4: TDengine Follower Writing Process
1. Follower vnode receives a data insertion request forwarded by Leader vnode; @@ -212,6 +217,7 @@ When data is written to disk, the system decideswhether to compress the data bas 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 older than a week is stored on local hard disk, and data older than four weeks is stored on network storage device. This reduces storage costs and ensures efficient data access. The movement of data on different storage media is automatically done by the system and is 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] ``` @@ -270,6 +276,7 @@ For the data collected by device D1001, the number of records per hour is counte 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 (super table). 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. There can be multiple tags which can be added, deleted and modified at any time. Applications can aggregate or statistically operate on all or a subset of tables under a STABLE by specifying tag filters. This greatly simplifies the development of applications. The process is shown in the following figure: ![TDengine Database Diagram of multi-table aggregation query](multi_tables.webp) +
Figure 5: Diagram of multi-table aggregation query
1. Application sends a query condition to system; @@ -279,9 +286,8 @@ TDengine creates a separate table for each data collection point, but in practic 5. Each vnode first finds 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 reduces the volume of data to be 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. +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 reduces the volume of data to be 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 TDengine 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 BRIN (Block Range Index) of PostgreSQL. - diff --git a/docs/zh/07-develop/02-model/index.mdx b/docs/zh/07-develop/02-model/index.mdx index 634c8a98d4..d66059c2cd 100644 --- a/docs/zh/07-develop/02-model/index.mdx +++ b/docs/zh/07-develop/02-model/index.mdx @@ -41,7 +41,7 @@ USE power; CREATE STABLE meters (ts timestamp, current float, voltage int, phase float) TAGS (location binary(64), groupId int); ``` -与创建普通表一样,创建超级表时,需要提供表名(示例中为 meters),表结构 Schema,即数据列的定义。第一列必须为时间戳(示例中为 ts),其他列为采集的物理量(示例中为 current, voltage, phase),数据类型可以为整型、浮点型、字符串等。除此之外,还需要提供标签的 schema (示例中为 location, groupId),标签的数据类型可以为整型、浮点型、字符串等。采集点的静态属性往往可以作为标签,比如采集点的地理位置、设备型号、设备组 ID、管理员 ID 等等。标签的 schema 可以事后增加、删除、修改。具体定义以及细节请见 [TAOS SQL 的超级表管理](/taos-sql/stable) 章节。 +与创建普通表一样,创建超级表时,需要提供表名(示例中为 meters),表结构 Schema,即数据列的定义。第一列必须为时间戳(示例中为 ts),其他列为采集的物理量(示例中为 current, voltage, phase),数据类型可以为整型、浮点型、字符串等。除此之外,还需要提供标签的 schema (示例中为 location, groupId),标签的数据类型可以为整型、浮点型、字符串等。采集点的静态属性往往可以作为标签,比如采集点的地理位置、设备型号、设备组 ID、管理员 ID 等等。标签的 schema 可以事后增加、删除、修改。具体定义以及细节请见 [TDengine SQL 的超级表管理](/taos-sql/stable) 章节。 每一种类型的数据采集点需要建立一个超级表,因此一个物联网系统,往往会有多个超级表。对于电网,我们就需要对智能电表、变压器、母线、开关等都建立一个超级表。在物联网中,一个设备就可能有多个数据采集点(比如一台风力发电的风机,有的采集点采集电流、电压等电参数,有的采集点采集温度、湿度、风向等环境参数),这个时候,对这一类型的设备,需要建立多张超级表。 @@ -55,7 +55,7 @@ TDengine 对每个数据采集点需要独立建表。与标准的关系型数 CREATE TABLE d1001 USING meters TAGS ("California.SanFrancisco", 2); ``` -其中 d1001 是表名,meters 是超级表的表名,后面紧跟标签 Location 的具体标签值 "California.SanFrancisco",标签 groupId 的具体标签值 2。虽然在创建表时,需要指定标签值,但可以事后修改。详细细则请见 [TAOS SQL 的表管理](/taos-sql/table) 章节。 +其中 d1001 是表名,meters 是超级表的表名,后面紧跟标签 Location 的具体标签值 "California.SanFrancisco",标签 groupId 的具体标签值 2。虽然在创建表时,需要指定标签值,但可以事后修改。详细细则请见 [TDengine SQL 的表管理](/taos-sql/table) 章节。 TDengine 建议将数据采集点的全局唯一 ID 作为表名(比如设备序列号)。但对于有的场景,并没有唯一的 ID,可以将多个 ID 组合成一个唯一的 ID。不建议将具有唯一性的 ID 作为标签值。 diff --git a/docs/zh/07-develop/03-insert-data/01-sql-writing.mdx b/docs/zh/07-develop/03-insert-data/01-sql-writing.mdx index 2920fa35a4..8818eaae3d 100644 --- a/docs/zh/07-develop/03-insert-data/01-sql-writing.mdx +++ b/docs/zh/07-develop/03-insert-data/01-sql-writing.mdx @@ -26,6 +26,7 @@ import PhpStmt from "./_php_stmt.mdx"; 应用通过连接器执行 INSERT 语句来插入数据,用户还可以通过 TDengine CLI,手动输入 INSERT 语句插入数据。 ### 一次写入一条 + 下面这条 INSERT 就将一条记录写入到表 d1001 中: ```sql @@ -48,7 +49,7 @@ TDengine 也支持一次向多个表写入数据,比如下面这条命令就 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)。 +详细的 SQL INSERT 语法规则参考 [TDengine SQL 的数据写入](/taos-sql/insert)。 :::info @@ -134,4 +135,3 @@ TDengine 也提供了支持参数绑定的 Prepare API,与 MySQL 类似,这
- diff --git a/docs/zh/07-develop/04-query-data/index.mdx b/docs/zh/07-develop/04-query-data/index.mdx index 92cb1906d9..d6156c8a59 100644 --- a/docs/zh/07-develop/04-query-data/index.mdx +++ b/docs/zh/07-develop/04-query-data/index.mdx @@ -44,7 +44,7 @@ Query OK, 2 row(s) in set (0.001100s) 为满足物联网场景的需求,TDengine 支持几个特殊的函数,比如 twa(时间加权平均),spread (最大值与最小值的差),last_row(最后一条记录)等,更多与物联网场景相关的函数将添加进来。 -具体的查询语法请看 [TAOS SQL 的数据查询](../../taos-sql/select) 章节。 +具体的查询语法请看 [TDengine SQL 的数据查询](../../taos-sql/select) 章节。 ## 多表聚合查询 @@ -75,7 +75,7 @@ taos> SELECT count(*), max(current) FROM meters where groupId = 2; Query OK, 1 row(s) in set (0.002136s) ``` -在 [TAOS SQL 的数据查询](../../taos-sql/select) 一章,查询类操作都会注明是否支持超级表。 +在 [TDengine SQL 的数据查询](../../taos-sql/select) 一章,查询类操作都会注明是否支持超级表。 ## 降采样查询、插值 @@ -123,7 +123,7 @@ Query OK, 6 rows in database (0.005515s) 如果一个时间间隔里,没有采集的数据,TDengine 还提供插值计算的功能。 -语法规则细节请见 [TAOS SQL 的按时间窗口切分聚合](../../taos-sql/distinguished) 章节。 +语法规则细节请见 [TDengine SQL 的按时间窗口切分聚合](../../taos-sql/distinguished) 章节。 ## 示例代码 diff --git a/docs/zh/12-taos-sql/index.md b/docs/zh/12-taos-sql/index.md index 821679551c..739d26b224 100644 --- a/docs/zh/12-taos-sql/index.md +++ b/docs/zh/12-taos-sql/index.md @@ -1,11 +1,11 @@ --- -title: TAOS SQL -description: "TAOS SQL 支持的语法规则、主要查询功能、支持的 SQL 查询函数,以及常用技巧等内容" +title: TDengine SQL +description: 'TDengine SQL 支持的语法规则、主要查询功能、支持的 SQL 查询函数,以及常用技巧等内容' --- -本文档说明 TAOS SQL 支持的语法规则、主要查询功能、支持的 SQL 查询函数,以及常用技巧等内容。阅读本文档需要读者具有基本的 SQL 语言的基础。TDengine 3.0 版本相比 2.x 版本做了大量改进和优化,特别是查询引擎进行了彻底的重构,因此 SQL 语法相比 2.x 版本有很多变更。详细的变更内容请见 [3.0 版本语法变更](/taos-sql/changes) 章节 +本文档说明 TDengine SQL 支持的语法规则、主要查询功能、支持的 SQL 查询函数,以及常用技巧等内容。阅读本文档需要读者具有基本的 SQL 语言的基础。TDengine 3.0 版本相比 2.x 版本做了大量改进和优化,特别是查询引擎进行了彻底的重构,因此 SQL 语法相比 2.x 版本有很多变更。详细的变更内容请见 [3.0 版本语法变更](/taos-sql/changes) 章节 -TAOS SQL 是用户对 TDengine 进行数据写入和查询的主要工具。TAOS SQL 提供标准的 SQL 语法,并针对时序数据和业务的特点优化和新增了许多语法和功能。TAOS SQL 语句的最大长度为 1M。TAOS SQL 不支持关键字的缩写,例如 DELETE 不能缩写为 DEL。 +TDengine SQL 是用户对 TDengine 进行数据写入和查询的主要工具。TDengine SQL 提供标准的 SQL 语法,并针对时序数据和业务的特点优化和新增了许多语法和功能。TDengine SQL 语句的最大长度为 1M。TDengine SQL 不支持关键字的缩写,例如 DELETE 不能缩写为 DEL。 本章节 SQL 语法遵循如下约定: diff --git a/docs/zh/14-reference/13-schemaless/13-schemaless.md b/docs/zh/14-reference/13-schemaless/13-schemaless.md index ae4280e26a..6c40a6ecb1 100644 --- a/docs/zh/14-reference/13-schemaless/13-schemaless.md +++ b/docs/zh/14-reference/13-schemaless/13-schemaless.md @@ -3,7 +3,7 @@ title: Schemaless 写入 description: 'Schemaless 写入方式,可以免于预先创建超级表/子表的步骤,随着数据写入接口能够自动创建与数据对应的存储结构' --- -在物联网应用中,常会采集比较多的数据项,用于实现智能控制、业务分析、设备监控等。由于应用逻辑的版本升级,或者设备自身的硬件调整等原因,数据采集项就有可能比较频繁地出现变动。为了在这种情况下方便地完成数据记录工作,TDengine提供调用 Schemaless 写入方式,可以免于预先创建超级表/子表的步骤,随着数据写入接口能够自动创建与数据对应的存储结构。并且在必要时,Schemaless +在物联网应用中,常会采集比较多的数据项,用于实现智能控制、业务分析、设备监控等。由于应用逻辑的版本升级,或者设备自身的硬件调整等原因,数据采集项就有可能比较频繁地出现变动。为了在这种情况下方便地完成数据记录工作,TDengine 提供调用 Schemaless 写入方式,可以免于预先创建超级表/子表的步骤,随着数据写入接口能够自动创建与数据对应的存储结构。并且在必要时,Schemaless 将自动增加必要的数据列,保证用户写入的数据可以被正确存储。 无模式写入方式建立的超级表及其对应的子表与通过 SQL 直接建立的超级表和子表完全没有区别,你也可以通过,SQL 语句直接向其中写入数据。需要注意的是,通过无模式写入方式建立的表,其表名是基于标签值按照固定的映射规则生成,所以无法明确地进行表意,缺乏可读性。 @@ -36,14 +36,14 @@ tag_set 中的所有的数据自动转化为 nchar 数据类型,并不需要 - 对空格、等号(=)、逗号(,)、双引号("),前面需要使用反斜杠(\)进行转义。(都指的是英文半角符号) - 数值类型将通过后缀来区分数据类型: -| **序号** | **后缀** | **映射类型** | **大小(字节)** | -| -------- | -------- | ------------ | -------------- | -| 1 | 无或 f64 | double | 8 | -| 2 | f32 | float | 4 | -| 3 | i8/u8 | TinyInt/UTinyInt | 1 | -| 4 | i16/u16 | SmallInt/USmallInt | 2 | -| 5 | i32/u32 | Int/UInt | 4 | -| 6 | i64/i/u64/u | BigInt/BigInt/UBigInt/UBigInt | 8 | +| **序号** | **后缀** | **映射类型** | **大小(字节)** | +| -------- | ----------- | ----------------------------- | -------------- | +| 1 | 无或 f64 | double | 8 | +| 2 | f32 | float | 4 | +| 3 | i8/u8 | TinyInt/UTinyInt | 1 | +| 4 | i16/u16 | SmallInt/USmallInt | 2 | +| 5 | i32/u32 | Int/UInt | 4 | +| 6 | i64/i/u64/u | BigInt/BigInt/UBigInt/UBigInt | 8 | - t, T, true, True, TRUE, f, F, false, False 将直接作为 BOOL 型来处理。 @@ -67,9 +67,9 @@ st,t1=3,t2=4,t3=t3 c1=3i64,c3="passit",c2=false,c4=4f64 1626006833639000000 "measurement,tag_key1=tag_value1,tag_key2=tag_value2" ``` -需要注意的是,这里的 tag_key1, tag_key2 并不是用户输入的标签的原始顺序,而是使用了标签名称按照字符串升序排列后的结果。所以,tag_key1 并不是在行协议中输入的第一个标签。 -排列完成以后计算该字符串的 MD5 散列值 "md5_val"。然后将计算的结果与字符串组合生成表名:“t_md5_val”。其中的 “t_” 是固定的前缀,每个通过该映射关系自动生成的表都具有该前缀。 -为了让用户可以指定生成的表名,可以通过配置smlChildTableName来指定(比如 配置smlChildTableName=tname 插入数据为st,tname=cpu1,t1=4 c1=3 1626006833639000000 则创建的表名为cpu1,注意如果多行数据tname相同,但是后面的tag_set不同,则使用第一次自动建表时指定的tag_set,其他的会忽略)。 +需要注意的是,这里的 tag*key1, tag_key2 并不是用户输入的标签的原始顺序,而是使用了标签名称按照字符串升序排列后的结果。所以,tag_key1 并不是在行协议中输入的第一个标签。 +排列完成以后计算该字符串的 MD5 散列值 "md5_val"。然后将计算的结果与字符串组合生成表名:“t_md5_val”。其中的 “t*” 是固定的前缀,每个通过该映射关系自动生成的表都具有该前缀。 +为了让用户可以指定生成的表名,可以通过配置 smlChildTableName 来指定(比如 配置 smlChildTableName=tname 插入数据为 st,tname=cpu1,t1=4 c1=3 1626006833639000000 则创建的表名为 cpu1,注意如果多行数据 tname 相同,但是后面的 tag_set 不同,则使用第一次自动建表时指定的 tag_set,其他的会忽略)。 2. 如果解析行协议获得的超级表不存在,则会创建这个超级表(不建议手动创建超级表,不然插入数据可能异常)。 3. 如果解析行协议获得子表不存在,则 Schemaless 会按照步骤 1 或 2 确定的子表名来创建子表。 @@ -78,11 +78,11 @@ st,t1=3,t2=4,t3=t3 c1=3i64,c3="passit",c2=false,c4=4f64 1626006833639000000 NULL。 6. 对 BINARY 或 NCHAR 列,如果数据行中所提供值的长度超出了列类型的限制,自动增加该列允许存储的字符长度上限(只增不减),以保证数据的完整保存。 7. 整个处理过程中遇到的错误会中断写入过程,并返回错误代码。 -8. 为了提高写入的效率,默认假设同一个超级表中field_set的顺序是一样的(第一条数据包含所有的field,后面的数据按照这个顺序),如果顺序不一样,需要配置参数smlDataFormat为false,否则,数据写入按照相同顺序写入,库中数据会异常。 +8. 为了提高写入的效率,默认假设同一个超级表中 field_set 的顺序是一样的(第一条数据包含所有的 field,后面的数据按照这个顺序),如果顺序不一样,需要配置参数 smlDataFormat 为 false,否则,数据写入按照相同顺序写入,库中数据会异常。 :::tip 无模式所有的处理逻辑,仍会遵循 TDengine 对数据结构的底层限制,例如每行数据的总长度不能超过 -16KB。这方面的具体限制约束请参见 [TAOS SQL 边界限制](/taos-sql/limit) +16KB。这方面的具体限制约束请参见 [TDengine SQL 边界限制](/taos-sql/limit) ::: diff --git a/docs/zh/21-tdinternal/01-arch.md b/docs/zh/21-tdinternal/01-arch.md index d74366d129..704524fd21 100644 --- a/docs/zh/21-tdinternal/01-arch.md +++ b/docs/zh/21-tdinternal/01-arch.md @@ -288,7 +288,7 @@ TDengine 对每个数据采集点单独建表,但在实际应用中经常需 7. vnode 返回本节点的查询计算结果; 8. qnode 完成多节点数据聚合后将最终查询结果返回给客户端; -由于 TDengine 在 vnode 内将标签数据与时序数据分离存储,通过在内存里过滤标签数据,先找到需要参与聚合操作的表的集合,将需要扫描的数据集大幅减少,大幅提升聚合计算速度。同时,由于数据分布在多个 vnode/dnode,聚合计算操作在多个 vnode 里并发进行,又进一步提升了聚合的速度。 对普通表的聚合函数以及绝大部分操作都适用于超级表,语法完全一样,细节请看 TAOS SQL。 +由于 TDengine 在 vnode 内将标签数据与时序数据分离存储,通过在内存里过滤标签数据,先找到需要参与聚合操作的表的集合,将需要扫描的数据集大幅减少,大幅提升聚合计算速度。同时,由于数据分布在多个 vnode/dnode,聚合计算操作在多个 vnode 里并发进行,又进一步提升了聚合的速度。 对普通表的聚合函数以及绝大部分操作都适用于超级表,语法完全一样,细节请看 TDengine SQL。 ### 预计算 From 6b46ac5f54f3ddad2f039386a5237492cc13ad1e Mon Sep 17 00:00:00 2001 From: tomchon Date: Mon, 29 Aug 2022 16:11:33 +0800 Subject: [PATCH 27/76] test: add checkpackages scritps --- packaging/MPtestJenkinsfile | 11 +++-------- packaging/testpackage.sh | 5 +++-- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/packaging/MPtestJenkinsfile b/packaging/MPtestJenkinsfile index 0782038c03..5e259d56be 100644 --- a/packaging/MPtestJenkinsfile +++ b/packaging/MPtestJenkinsfile @@ -1,8 +1,7 @@ def sync_source(branch_name) { sh ''' hostname - IP - env + ip addr|grep 192|awk '{print $2}'|sed "s/\\/.*//" echo ''' + branch_name + ''' ''' sh ''' @@ -73,7 +72,7 @@ pipeline { BRANCH_NAME = '3.0' TD_SERVER_TAR = "TDengine-server-${version}-Linux-x64.tar.gz" - BASE_TD_SERVER_TAR = "TDengine-server-${baseVersion}-arm64-x64.tar.gz" + BASE_TD_SERVER_TAR = "TDengine-server-${baseVersion}-Linux-x64.tar.gz" TD_SERVER_ARM_TAR = "TDengine-server-${version}-Linux-arm64.tar.gz" BASE_TD_SERVER_ARM_TAR = "TDengine-server-${baseVersion}-Linux-arm64.tar.gz" @@ -82,7 +81,7 @@ pipeline { BASE_TD_SERVER_LITE_TAR = "TDengine-server-${baseVersion}-Linux-x64-Lite.tar.gz" TD_CLIENT_TAR = "TDengine-client-${version}-Linux-x64.tar.gz" - BASE_TD_CLIENT_TAR = "TDengine-client-${baseVersion}-arm64-x64.tar.gz" + BASE_TD_CLIENT_TAR = "TDengine-client-${baseVersion}-Linux-x64.tar.gz" TD_CLIENT_ARM_TAR = "TDengine-client-${version}-Linux-arm64.tar.gz" BASE_TD_CLIENT_ARM_TAR = "TDengine-client-${baseVersion}-Linux-arm64.tar.gz" @@ -114,7 +113,6 @@ pipeline { cd ${TDENGINE_ROOT_DIR}/packaging bash testpackage.sh ${TD_SERVER_TAR} ${version} ${BASE_TD_SERVER_TAR} ${baseVersion} server python3 checkPackageRuning.py - rmtaos ''' sh ''' cd ${TDENGINE_ROOT_DIR}/packaging @@ -138,7 +136,6 @@ pipeline { cd ${TDENGINE_ROOT_DIR}/packaging bash testpackage.sh ${TD_SERVER_TAR} ${version} ${BASE_TD_SERVER_TAR} ${baseVersion} server python3 checkPackageRuning.py - rmtaos ''' sh ''' cd ${TDENGINE_ROOT_DIR}/packaging @@ -162,12 +159,10 @@ pipeline { cd ${TDENGINE_ROOT_DIR}/packaging bash testpackage.sh ${TD_SERVER_TAR} ${version} ${BASE_TD_SERVER_TAR} ${baseVersion} server python3 checkPackageRuning.py - rmtaos ''' sh ''' cd ${TDENGINE_ROOT_DIR}/packaging bash testpackage.sh ${TD_SERVER_LITE_TAR} ${version} ${BASE_TD_SERVER_LITE_TAR} ${baseVersion} server - bash testpackage.sh ${TD_SERVER_LITE_TAR} ${version} ${BASE_TD_SERVER_LITE_TAR} ${baseVersion} server python3 checkPackageRuning.py ''' sh ''' diff --git a/packaging/testpackage.sh b/packaging/testpackage.sh index 3ca31ea7a4..148aabc091 100755 --- a/packaging/testpackage.sh +++ b/packaging/testpackage.sh @@ -55,9 +55,9 @@ if command -v ${comd} ;then echo "${comd} is already installed" else if command -v apt ;then - apt-get install ${comd} + apt-get install ${comd} -y elif command -v yum ;then - yum install ${comd} + yum install ${comd} - y else echo "you should install ${comd} manually" fi @@ -153,6 +153,7 @@ elif [[ ${packgeName} =~ "tar" ]];then wget https://www.taosdata.com/assets-download/3.0/taosTools-2.1.2-Linux-x64.tar.gz tar xvf taosTools-2.1.2-Linux-x64.tar.gz cd taosTools-2.1.2 && bash install-taostools.sh + fi fi # } From 12a01a88dd2cf189aa702ce40758e9f11dafcede Mon Sep 17 00:00:00 2001 From: Cary Xu Date: Mon, 29 Aug 2022 16:15:12 +0800 Subject: [PATCH 28/76] fix: race condition between fetch all and optr serialize --- source/dnode/vnode/src/sma/smaRollup.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/dnode/vnode/src/sma/smaRollup.c b/source/dnode/vnode/src/sma/smaRollup.c index 426ab521fd..5b2595ad21 100644 --- a/source/dnode/vnode/src/sma/smaRollup.c +++ b/source/dnode/vnode/src/sma/smaRollup.c @@ -1715,7 +1715,8 @@ int32_t tdRSmaProcessExecImpl(SSma *pSma, ERsmaExecType type) { smaDebug("vgId:%d, batchSize:%d, execType:%" PRIi8, SMA_VID(pSma), qallItemSize, type); } - if (type == RSMA_EXEC_OVERFLOW) { + if (((type == RSMA_EXEC_OVERFLOW) && (atomic_load_8(RSMA_COMMIT_STAT(pRSmaStat)) == 0)) || + (type == RSMA_EXEC_COMMIT)) { tdRSmaFetchAllResult(pSma, pInfo, pSubmitArr); } From df7daebf4264318936f160473283e432290565a3 Mon Sep 17 00:00:00 2001 From: tomchon Date: Mon, 29 Aug 2022 16:25:50 +0800 Subject: [PATCH 29/76] test: add checkpackages scritps --- packaging/testpackage.sh | 35 ++--------------------------------- 1 file changed, 2 insertions(+), 33 deletions(-) diff --git a/packaging/testpackage.sh b/packaging/testpackage.sh index 148aabc091..669b0c9e1e 100755 --- a/packaging/testpackage.sh +++ b/packaging/testpackage.sh @@ -1,32 +1,5 @@ #!/bin/sh -# # ============================= 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 "Download package" packgeName=$1 version=$2 @@ -57,8 +30,7 @@ else if command -v apt ;then apt-get install ${comd} -y elif command -v yum ;then - yum install ${comd} - y - else + yum -y install ${comd} echo "you should install ${comd} manually" fi fi @@ -155,8 +127,5 @@ elif [[ ${packgeName} =~ "tar" ]];then cd taosTools-2.1.2 && bash install-taostools.sh fi -fi -# } - -# installPkgAndCheckFile +fi From 8f6e87594fc07228c3ca919cfbc23ee65e3cb829 Mon Sep 17 00:00:00 2001 From: Cary Xu Date: Mon, 29 Aug 2022 16:33:08 +0800 Subject: [PATCH 30/76] enh: not fetch all during commit --- source/dnode/vnode/src/sma/smaRollup.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/dnode/vnode/src/sma/smaRollup.c b/source/dnode/vnode/src/sma/smaRollup.c index 5b2595ad21..3ed8dc2a20 100644 --- a/source/dnode/vnode/src/sma/smaRollup.c +++ b/source/dnode/vnode/src/sma/smaRollup.c @@ -1715,8 +1715,7 @@ int32_t tdRSmaProcessExecImpl(SSma *pSma, ERsmaExecType type) { smaDebug("vgId:%d, batchSize:%d, execType:%" PRIi8, SMA_VID(pSma), qallItemSize, type); } - if (((type == RSMA_EXEC_OVERFLOW) && (atomic_load_8(RSMA_COMMIT_STAT(pRSmaStat)) == 0)) || - (type == RSMA_EXEC_COMMIT)) { + if (atomic_load_8(RSMA_COMMIT_STAT(pRSmaStat)) == 0) { tdRSmaFetchAllResult(pSma, pInfo, pSubmitArr); } From 3b83cf9d9771774d4ac2e873e7cc906a16ac0cda Mon Sep 17 00:00:00 2001 From: Pan YANG Date: Mon, 29 Aug 2022 16:36:17 +0800 Subject: [PATCH 31/76] docs: fix errors by auto formatter of markdown --- docs/en/14-reference/13-schemaless/13-schemaless.md | 4 ++-- docs/zh/14-reference/13-schemaless/13-schemaless.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/en/14-reference/13-schemaless/13-schemaless.md b/docs/en/14-reference/13-schemaless/13-schemaless.md index 6de4810212..4f50c38cbb 100644 --- a/docs/en/14-reference/13-schemaless/13-schemaless.md +++ b/docs/en/14-reference/13-schemaless/13-schemaless.md @@ -67,8 +67,8 @@ Schemaless writes process row data according to the following principles. "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. +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. You can configure smlChildTableName to specify table names, for example, `smlChildTableName=tname`. You can insert `st,tname=cpul,t1=4 c1=3 1626006833639000000` and the cpu1 table will be automatically created. Note that if multiple rows have the same tname but different tag_set values, the tag_set of the first row is used to create the table and the others are ignored. 2. If the super table obtained by parsing the line protocol does not exist, this super table is created. diff --git a/docs/zh/14-reference/13-schemaless/13-schemaless.md b/docs/zh/14-reference/13-schemaless/13-schemaless.md index 6c40a6ecb1..a33abafaf8 100644 --- a/docs/zh/14-reference/13-schemaless/13-schemaless.md +++ b/docs/zh/14-reference/13-schemaless/13-schemaless.md @@ -67,8 +67,8 @@ st,t1=3,t2=4,t3=t3 c1=3i64,c3="passit",c2=false,c4=4f64 1626006833639000000 "measurement,tag_key1=tag_value1,tag_key2=tag_value2" ``` -需要注意的是,这里的 tag*key1, tag_key2 并不是用户输入的标签的原始顺序,而是使用了标签名称按照字符串升序排列后的结果。所以,tag_key1 并不是在行协议中输入的第一个标签。 -排列完成以后计算该字符串的 MD5 散列值 "md5_val"。然后将计算的结果与字符串组合生成表名:“t_md5_val”。其中的 “t*” 是固定的前缀,每个通过该映射关系自动生成的表都具有该前缀。 +需要注意的是,这里的 tag_key1, tag_key2 并不是用户输入的标签的原始顺序,而是使用了标签名称按照字符串升序排列后的结果。所以,tag_key1 并不是在行协议中输入的第一个标签。 +排列完成以后计算该字符串的 MD5 散列值 "md5_val"。然后将计算的结果与字符串组合生成表名:“t_md5_val”。其中的 “t_” 是固定的前缀,每个通过该映射关系自动生成的表都具有该前缀。 为了让用户可以指定生成的表名,可以通过配置 smlChildTableName 来指定(比如 配置 smlChildTableName=tname 插入数据为 st,tname=cpu1,t1=4 c1=3 1626006833639000000 则创建的表名为 cpu1,注意如果多行数据 tname 相同,但是后面的 tag_set 不同,则使用第一次自动建表时指定的 tag_set,其他的会忽略)。 2. 如果解析行协议获得的超级表不存在,则会创建这个超级表(不建议手动创建超级表,不然插入数据可能异常)。 From 70f86af71141f3d2b46827abf51e808b8a59520d Mon Sep 17 00:00:00 2001 From: dapan1121 Date: Mon, 29 Aug 2022 16:57:59 +0800 Subject: [PATCH 32/76] fix: fix heartbeat crash issue --- source/client/src/clientHb.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/source/client/src/clientHb.c b/source/client/src/clientHb.c index 9475d1b51e..56e3527f96 100644 --- a/source/client/src/clientHb.c +++ b/source/client/src/clientHb.c @@ -145,7 +145,7 @@ static int32_t hbProcessStbInfoRsp(void *value, int32_t valueLen, struct SCatalo } static int32_t hbQueryHbRspHandle(SAppHbMgr *pAppHbMgr, SClientHbRsp *pRsp) { - SClientHbReq *pReq = taosHashGet(pAppHbMgr->activeInfo, &pRsp->connKey, sizeof(SClientHbKey)); + SClientHbReq *pReq = taosHashAcquire(pAppHbMgr->activeInfo, &pRsp->connKey, sizeof(SClientHbKey)); if (NULL == pReq) { tscWarn("pReq to get activeInfo, may be dropped, refId:%" PRIx64 ", type:%d", pRsp->connKey.tscRid, pRsp->connKey.connType); @@ -260,6 +260,8 @@ static int32_t hbQueryHbRspHandle(SAppHbMgr *pAppHbMgr, SClientHbRsp *pRsp) { } } + taosHashRelease(pAppHbMgr->activeInfo, pReq); + return TSDB_CODE_SUCCESS; } @@ -914,10 +916,11 @@ 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)); + SClientHbReq *pReq = taosHashAcquire(pAppHbMgr->activeInfo, &connKey, sizeof(SClientHbKey)); if (pReq) { tFreeClientHbReq(pReq); taosHashRemove(pAppHbMgr->activeInfo, &connKey, sizeof(SClientHbKey)); + taosHashRelease(pAppHbMgr->activeInfo, pReq); } if (NULL == pReq) { From bf3231ab9fa89ae567597594c4ffafe77e9e8f78 Mon Sep 17 00:00:00 2001 From: wangmm0220 Date: Mon, 29 Aug 2022 17:30:50 +0800 Subject: [PATCH 33/76] fix: cols num error in tmq for query --- source/dnode/vnode/src/inc/tq.h | 2 +- source/dnode/vnode/src/tq/tq.c | 2 +- source/dnode/vnode/src/tq/tqExec.c | 7 ++++++- source/dnode/vnode/src/tq/tqMeta.c | 2 +- source/libs/executor/src/executor.c | 4 ++-- tests/system-test/7-tmq/stbTagFilter-1ctb.py | 5 ++--- 6 files changed, 13 insertions(+), 9 deletions(-) diff --git a/source/dnode/vnode/src/inc/tq.h b/source/dnode/vnode/src/inc/tq.h index 7c394c4baf..a97c8ff132 100644 --- a/source/dnode/vnode/src/inc/tq.h +++ b/source/dnode/vnode/src/inc/tq.h @@ -88,7 +88,7 @@ typedef struct { STqExecTb execTb; STqExecDb execDb; }; -// int32_t numOfCols; // number of out pout column, temporarily used + int32_t numOfCols; // number of out pout column, temporarily used SSchemaWrapper* pSchemaWrapper; // columns that are involved in query } STqExecHandle; diff --git a/source/dnode/vnode/src/tq/tq.c b/source/dnode/vnode/src/tq/tq.c index 26db68a1d4..54f764c6b3 100644 --- a/source/dnode/vnode/src/tq/tq.c +++ b/source/dnode/vnode/src/tq/tq.c @@ -596,7 +596,7 @@ int32_t tqProcessVgChangeReq(STQ* pTq, int64_t version, char* msg, int32_t msgLe req.qmsg = NULL; pHandle->execHandle.task = - qCreateQueueExecTaskInfo(pHandle->execHandle.execCol.qmsg, &handle, NULL, + qCreateQueueExecTaskInfo(pHandle->execHandle.execCol.qmsg, &handle, &pHandle->execHandle.numOfCols, &pHandle->execHandle.pSchemaWrapper); ASSERT(pHandle->execHandle.task); void* scanner = NULL; diff --git a/source/dnode/vnode/src/tq/tqExec.c b/source/dnode/vnode/src/tq/tqExec.c index e21125f3a4..a0b8141cfb 100644 --- a/source/dnode/vnode/src/tq/tqExec.c +++ b/source/dnode/vnode/src/tq/tqExec.c @@ -110,7 +110,12 @@ int64_t tqScan(STQ* pTq, const STqHandle* pHandle, SMqDataRsp* pRsp, SMqMetaRsp* taosArrayPush(pRsp->blockSchema, &pSW); } } - tqAddBlockDataToRsp(pDataBlock, pRsp, taosArrayGetSize(pDataBlock->pDataBlock)); + + if(pHandle->execHandle.subType == TOPIC_SUB_TYPE__COLUMN){ + tqAddBlockDataToRsp(pDataBlock, pRsp, pExec->numOfCols); + }else{ + tqAddBlockDataToRsp(pDataBlock, pRsp, taosArrayGetSize(pDataBlock->pDataBlock)); + } pRsp->blockNum++; if (pOffset->type == TMQ_OFFSET__LOG) { continue; diff --git a/source/dnode/vnode/src/tq/tqMeta.c b/source/dnode/vnode/src/tq/tqMeta.c index 6b6717ff57..a192d1f863 100644 --- a/source/dnode/vnode/src/tq/tqMeta.c +++ b/source/dnode/vnode/src/tq/tqMeta.c @@ -260,7 +260,7 @@ int32_t tqMetaRestoreHandle(STQ* pTq) { if (handle.execHandle.subType == TOPIC_SUB_TYPE__COLUMN) { handle.execHandle.task = qCreateQueueExecTaskInfo( - handle.execHandle.execCol.qmsg, &reader, NULL, &handle.execHandle.pSchemaWrapper); + handle.execHandle.execCol.qmsg, &reader, &handle.execHandle.numOfCols, &handle.execHandle.pSchemaWrapper); ASSERT(handle.execHandle.task); void* scanner = NULL; qExtractStreamScanner(handle.execHandle.task, &scanner); diff --git a/source/libs/executor/src/executor.c b/source/libs/executor/src/executor.c index 271a65647d..124f4b44b0 100644 --- a/source/libs/executor/src/executor.c +++ b/source/libs/executor/src/executor.c @@ -177,13 +177,13 @@ qTaskInfo_t qCreateQueueExecTaskInfo(void* msg, SReadHandle* readers, int32_t* n // extract the number of output columns SDataBlockDescNode* pDescNode = pPlan->pNode->pOutputDataBlockDesc; - if(numOfCols) *numOfCols = 0; + *numOfCols = 0; SNode* pNode; FOREACH(pNode, pDescNode->pSlots) { SSlotDescNode* pSlotDesc = (SSlotDescNode*)pNode; if (pSlotDesc->output) { - if(numOfCols) ++(*numOfCols); + ++(*numOfCols); } } diff --git a/tests/system-test/7-tmq/stbTagFilter-1ctb.py b/tests/system-test/7-tmq/stbTagFilter-1ctb.py index 526ff7181e..6cb152342b 100644 --- a/tests/system-test/7-tmq/stbTagFilter-1ctb.py +++ b/tests/system-test/7-tmq/stbTagFilter-1ctb.py @@ -250,15 +250,14 @@ class TDTestCase: tdLog.printNoPrefix("=============================================") tdLog.printNoPrefix("======== snapshot is 0: only consume from wal") self.tmqCase1() - # self.tmqCase2() + self.tmqCase2() self.prepareTestEnv() tdLog.printNoPrefix("====================================================================") tdLog.printNoPrefix("======== snapshot is 1: firstly consume from tsbs, and then from wal") self.snapshot = 1 self.tmqCase1() - # self.tmqCase2() - + self.tmqCase2() def stop(self): tdSql.close() From c563948c8eabc2db69ef835af9cd9449afab9fe0 Mon Sep 17 00:00:00 2001 From: gccgdb1234 Date: Mon, 29 Aug 2022 17:34:44 +0800 Subject: [PATCH 34/76] doc: correct taosdata.com to tdengine.com --- docs/en/04-concept/index.md | 2 +- docs/en/07-develop/01-connect/index.md | 2 +- docs/en/10-deployment/01-deploy.md | 16 ++++++++-------- docs/en/14-reference/06-taosdump.md | 1 - docs/en/14-reference/07-tdinsight/index.md | 2 +- docs/en/20-third-party/09-emq-broker.md | 6 ++---- docs/en/25-application/03-immigrate.md | 6 +++--- 7 files changed, 16 insertions(+), 19 deletions(-) diff --git a/docs/en/04-concept/index.md b/docs/en/04-concept/index.md index 5a9c55fdd6..b0a0c25d85 100644 --- a/docs/en/04-concept/index.md +++ b/docs/en/04-concept/index.md @@ -162,7 +162,7 @@ To better understand the data model using metri, tags, super table and subtable, ## 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. The [characteristics of time-series data](https://www.taosdata.com/blog/2019/07/09/86.html) from different data collection points may be different. Characteristics include collection frequency, retention policy and others which determine how you create and configure the database. For e.g. days to keep, number of replicas, data block size, whether data updates are allowed and other configurable parameters would be determined by the characteristics of your data and your business requirements. 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. +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. The [characteristics of time-series data](https://tdengine.com/tsdb/characteristics-of-time-series-data/) from different data collection points may be different. Characteristics include collection frequency, retention policy and others which determine how you create and configure the database. For e.g. days to keep, number of replicas, data block size, whether data updates are allowed and other configurable parameters would be determined by the characteristics of your data and your business requirements. 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. diff --git a/docs/en/07-develop/01-connect/index.md b/docs/en/07-develop/01-connect/index.md index 2053706421..901fe69d24 100644 --- a/docs/en/07-develop/01-connect/index.md +++ b/docs/en/07-develop/01-connect/index.md @@ -279,6 +279,6 @@ Prior to establishing connection, please make sure TDengine is already running a :::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). +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.tdengine.com/train-faq/faq). ::: diff --git a/docs/en/10-deployment/01-deploy.md b/docs/en/10-deployment/01-deploy.md index a445b684dc..5dfcd3108d 100644 --- a/docs/en/10-deployment/01-deploy.md +++ b/docs/en/10-deployment/01-deploy.md @@ -39,18 +39,18 @@ To get the hostname on any host, the command `hostname -f` can be executed. On the physical machine running the application, ping the dnode that is running taosd. If the dnode is not accessible, the application cannot connect to taosd. In this case, verify the DNS and hosts settings on the physical node running the application. -The end point of each dnode is the output hostname and port, such as h1.taosdata.com:6030. +The end point of each dnode is the output hostname and port, such as h1.tdengine.com:6030. ### Step 5 -Modify the TDengine configuration file `/etc/taos/taos.cfg` on each node. Assuming the first dnode of TDengine cluster is "h1.taosdata.com:6030", its `taos.cfg` is configured as following. +Modify the TDengine configuration file `/etc/taos/taos.cfg` on each node. Assuming the first dnode of TDengine cluster is "h1.tdengine.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 +firstEp h1.tdengine.com:6030 // must be configured to the FQDN of the host where the dnode is launched -fqdn h1.taosdata.com +fqdn h1.tdengine.com // the port used by the dnode, default is 6030 serverPort 6030 @@ -76,13 +76,13 @@ The first dnode can be started following the instructions in [Get Started](/get- taos> show dnodes; id | endpoint | vnodes | support_vnodes | status | create_time | note | ============================================================================================================================================ -1 | h1.taosdata.com:6030 | 0 | 1024 | ready | 2022-07-16 10:50:42.673 | | +1 | h1.tdengine.com:6030 | 0 | 1024 | ready | 2022-07-16 10:50:42.673 | | Query OK, 1 rows affected (0.007984s) ``` -From the above output, it is shown that the end point of the started dnode is "h1.taosdata.com:6030", which is the `firstEp` of the cluster. +From the above output, it is shown that the end point of the started dnode is "h1.tdengine.com:6030", which is the `firstEp` of the cluster. ## Add DNODE @@ -90,7 +90,7 @@ There are a few steps necessary to add other dnodes in the cluster. Second, we can start `taosd` as instructed in [Get Started](/get-started/). -Then, on the first dnode i.e. h1.taosdata.com in our example, use TDengine CLI `taos` to execute the following command: +Then, on the first dnode i.e. h1.tdengine.com in our example, use TDengine CLI `taos` to execute the following command: ```sql CREATE DNODE "h2.taos.com:6030"; @@ -98,7 +98,7 @@ CREATE DNODE "h2.taos.com:6030"; This adds the end point of the new dnode (from Step 4) into the end point list of the cluster. In the command "fqdn:port" should be quoted using double quotes. Change `"h2.taos.com:6030"` to the end point of your new dnode. -Then on the first dnode h1.taosdata.com, execute `show dnodes` in `taos` +Then on the first dnode h1.tdengine.com, execute `show dnodes` in `taos` ```sql SHOW DNODES; diff --git a/docs/en/14-reference/06-taosdump.md b/docs/en/14-reference/06-taosdump.md index 2105ba83fa..e73441a96b 100644 --- a/docs/en/14-reference/06-taosdump.md +++ b/docs/en/14-reference/06-taosdump.md @@ -116,5 +116,4 @@ Usage: taosdump [OPTION...] dbname [tbname ...] 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/index.md b/docs/en/14-reference/07-tdinsight/index.md index e74c9de7b2..2e56203525 100644 --- a/docs/en/14-reference/07-tdinsight/index.md +++ b/docs/en/14-reference/07-tdinsight/index.md @@ -263,7 +263,7 @@ Once the import is complete, the full page view of TDinsight is shown below. ## 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. +The TDinsight dashboard is designed to provide the usage and status of TDengine-related resources [dnodes, mnodes, vnodes](../../taos-sql/node/) or databases. Details of the metrics are as follows. diff --git a/docs/en/20-third-party/09-emq-broker.md b/docs/en/20-third-party/09-emq-broker.md index 0900dd3d75..2ead1bbaf4 100644 --- a/docs/en/20-third-party/09-emq-broker.md +++ b/docs/en/20-third-party/09-emq-broker.md @@ -9,7 +9,7 @@ MQTT is a popular IoT data transfer protocol. [EMQX](https://github.com/emqx/emq 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. +- 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 @@ -28,8 +28,6 @@ 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.5 as an example. For other versions, please refer to the corresponding official documentation. @@ -137,5 +135,5 @@ Use the TDengine CLI program to log in and query the appropriate databases and t ![TDengine Database EMQX result in taos](./emqx/check-result-in-taos.webp) -Please refer to the [TDengine official documentation](https://docs.taosdata.com/) for more details on how to use TDengine. +Please refer to the [TDengine official documentation](https://docs.tdengine.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/25-application/03-immigrate.md b/docs/en/25-application/03-immigrate.md index 9614574c71..5c1c92fcb3 100644 --- a/docs/en/25-application/03-immigrate.md +++ b/docs/en/25-application/03-immigrate.md @@ -41,7 +41,7 @@ The agents deployed in the application nodes are responsible for providing opera - **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). +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 [Install TDengine](../../get-started/package) Note that once the installation is complete, do not start the `taosd` service before properly configuring the parameters. @@ -51,7 +51,7 @@ TDengine version 2.4 and later version includes `taosAdapter`. taosAdapter is a Users can flexibly deploy taosAdapter instances, based on their requirements, to improve data writing throughput and provide guarantees for data writes in different application scenarios. -Through taosAdapter, users can directly write the data collected by `collectd` or `StatsD` to TDengine to achieve easy, convenient and seamless migration in application scenarios. taosAdapter also supports Telegraf, Icinga, TCollector, and node_exporter data. For more details, please refer to [taosAdapter](/reference/taosadapter/). +Through taosAdapter, users can directly write the data collected by `collectd` or `StatsD` to TDengine to achieve easy, convenient and seamless migration in application scenarios. 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. @@ -411,7 +411,7 @@ TDengine provides a wealth of help documents to explain many aspects of cluster ### 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. +The first is TDengine installation. Download the latest stable version of TDengine from the official website, and install it. Please refer to [Install TDengine](../../get-started/pacakge) for more details. Note that once the installation is complete, do not immediately start the `taosd` service, but start it after correctly configuring the parameters. From 8b5283ceff97089630827fa3967cfa4e49001ad7 Mon Sep 17 00:00:00 2001 From: gccgdb1234 Date: Mon, 29 Aug 2022 17:45:51 +0800 Subject: [PATCH 35/76] doc: remove improper taosdata.com --- docs/en/10-deployment/05-helm.md | 2 +- docs/en/14-reference/02-rest-api/02-rest-api.mdx | 4 ++-- docs/en/14-reference/03-connector/04-java.mdx | 2 -- docs/en/14-reference/03-connector/09-csharp.mdx | 1 - docs/en/14-reference/03-connector/_preparation.mdx | 2 +- docs/en/14-reference/11-docker/index.md | 6 +++--- 6 files changed, 7 insertions(+), 10 deletions(-) diff --git a/docs/en/10-deployment/05-helm.md b/docs/en/10-deployment/05-helm.md index 302730f1b5..a4fa681000 100644 --- a/docs/en/10-deployment/05-helm.md +++ b/docs/en/10-deployment/05-helm.md @@ -152,7 +152,7 @@ clusterDomainSuffix: "" # converting an upper-snake-cased variable like `TAOS_DEBUG_FLAG`, # to a camelCase taos config variable `debugFlag`. # -# See the variable list at https://www.taosdata.com/cn/documentation/administrator . +# See the [Configuration Variables](../../reference/config) # # Note: # 1. firstEp/secondEp: should not be setted here, it's auto generated at scale-up. 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 index 74ba78b7fc..ce28ee87d9 100644 --- a/docs/en/14-reference/02-rest-api/02-rest-api.mdx +++ b/docs/en/14-reference/02-rest-api/02-rest-api.mdx @@ -18,12 +18,12 @@ If the TDengine server is already installed, it can be verified as follows: The following example is in an Ubuntu environment and uses the `curl` tool to verify that the REST interface is working. Note that the `curl` tool may need to be installed in your environment. -The following example lists all databases on the host h1.taosdata.com. To use it in your environment, replace `h1.taosdata.com` and `6041` (the default port) with the actual running TDengine service FQDN and port number. +The following example lists all databases on the host h1.tdengine.com. To use it in your environment, replace `h1.tdengine.com` and `6041` (the default port) with the actual running TDengine service FQDN and port number. ```bash curl -L -H "Authorization: Basic cm9vdDp0YW9zZGF0YQ==" \ -d "select name, ntables, status from information_schema.ins_databases;" \ - h1.taosdata.com:6041/rest/sql + h1.tdengine.com:6041/rest/sql ``` The following return value results indicate that the verification passed. diff --git a/docs/en/14-reference/03-connector/04-java.mdx b/docs/en/14-reference/03-connector/04-java.mdx index 0f977393f1..129d90ea85 100644 --- a/docs/en/14-reference/03-connector/04-java.mdx +++ b/docs/en/14-reference/03-connector/04-java.mdx @@ -133,8 +133,6 @@ The configuration parameters in the URL are as follows: - batchfetch: true: pulls result sets in batches when executing queries; false: pulls result sets row by row. The default value is true. 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: diff --git a/docs/en/14-reference/03-connector/09-csharp.mdx b/docs/en/14-reference/03-connector/09-csharp.mdx index c745b8dd1a..823e907599 100644 --- a/docs/en/14-reference/03-connector/09-csharp.mdx +++ b/docs/en/14-reference/03-connector/09-csharp.mdx @@ -172,7 +172,6 @@ namespace TDengineExample `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 diff --git a/docs/en/14-reference/03-connector/_preparation.mdx b/docs/en/14-reference/03-connector/_preparation.mdx index 07ebdbca3d..c6e42ce023 100644 --- a/docs/en/14-reference/03-connector/_preparation.mdx +++ b/docs/en/14-reference/03-connector/_preparation.mdx @@ -2,7 +2,7 @@ :::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. +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, please refer to [Install TDengine](../../get-started/package). - 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/11-docker/index.md b/docs/en/14-reference/11-docker/index.md index be1d72ff9c..7cd1e810dc 100644 --- a/docs/en/14-reference/11-docker/index.md +++ b/docs/en/14-reference/11-docker/index.md @@ -116,7 +116,7 @@ If you want to start your application in a container, you need to add the corres FROM ubuntu:20.04 RUN apt-get update && apt-get install -y wget ENV TDENGINE_VERSION=3.0.0.0 -RUN wget -c https://www.taosdata.com/assets-download/TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz \ +RUN wget -c https://www.taosdata.com/assets-download/3.0/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 \ @@ -217,7 +217,7 @@ Here is the full Dockerfile: ```docker FROM golang:1.17.6-buster as builder ENV TDENGINE_VERSION=3.0.0.0 -RUN wget -c https://www.taosdata.com/assets-download/TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz \ +RUN wget -c https://www.taosdata.com/assets-download/3.0/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 \ @@ -233,7 +233,7 @@ RUN go build FROM ubuntu:20.04 RUN apt-get update && apt-get install -y wget ENV TDENGINE_VERSION=3.0.0.0 -RUN wget -c https://www.taosdata.com/assets-download/TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz \ +RUN wget -c https://www.taosdata.com/assets-download/3.0/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 \ From 205299e532a556c2cf0df0db81044351abe32b7e Mon Sep 17 00:00:00 2001 From: dapan1121 Date: Mon, 29 Aug 2022 17:46:34 +0800 Subject: [PATCH 36/76] fix: fix taosd stmt query crash issue --- source/libs/parser/src/parser.c | 3 +-- tests/script/api/batchprepare.c | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/source/libs/parser/src/parser.c b/source/libs/parser/src/parser.c index 7e27132f3c..7ee6a5b223 100644 --- a/source/libs/parser/src/parser.c +++ b/source/libs/parser/src/parser.c @@ -136,8 +136,7 @@ static int32_t setValueByBindParam(SValueNode* pVal, TAOS_MULTI_BIND* pParam) { } static EDealRes rewriteQueryExprAliasImpl(SNode* pNode, void* pContext) { - if (nodesIsExprNode(pNode) && QUERY_NODE_COLUMN != nodeType(pNode) && '\0' == ((SExprNode*)pNode)->userAlias[0]) { - strcpy(((SExprNode*)pNode)->userAlias, ((SExprNode*)pNode)->aliasName); + if (nodesIsExprNode(pNode) && QUERY_NODE_COLUMN != nodeType(pNode)) { sprintf(((SExprNode*)pNode)->aliasName, "#%d", *(int32_t*)pContext); ++(*(int32_t*)pContext); } diff --git a/tests/script/api/batchprepare.c b/tests/script/api/batchprepare.c index ada2039460..f39d5e6528 100644 --- a/tests/script/api/batchprepare.c +++ b/tests/script/api/batchprepare.c @@ -2598,7 +2598,6 @@ void runAll(TAOS *taos) { printf("%s Begin\n", gCaseCtrl.caseCatalog); runCaseList(taos); -#if 0 strcpy(gCaseCtrl.caseCatalog, "Micro DB precision Test"); printf("%s Begin\n", gCaseCtrl.caseCatalog); gCaseCtrl.precision = TIME_PRECISION_MICRO; @@ -2654,7 +2653,6 @@ void runAll(TAOS *taos) { gCaseCtrl.bindColNum = 6; runCaseList(taos); gCaseCtrl.bindColNum = 0; -#endif /* strcpy(gCaseCtrl.caseCatalog, "Bind Col Type Test"); From 798e10d048c299497c996a117f72dfe7a0de0ea6 Mon Sep 17 00:00:00 2001 From: gccgdb1234 Date: Mon, 29 Aug 2022 17:48:43 +0800 Subject: [PATCH 37/76] doc: fix broken links --- docs/en/25-application/03-immigrate.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/25-application/03-immigrate.md b/docs/en/25-application/03-immigrate.md index 5c1c92fcb3..1aabaa43e7 100644 --- a/docs/en/25-application/03-immigrate.md +++ b/docs/en/25-application/03-immigrate.md @@ -411,7 +411,7 @@ TDengine provides a wealth of help documents to explain many aspects of cluster ### Cluster Deployment -The first is TDengine installation. Download the latest stable version of TDengine from the official website, and install it. Please refer to [Install TDengine](../../get-started/pacakge) for more details. +The first is TDengine installation. Download the latest stable version of TDengine from the official website, and install it. Please refer to [Install TDengine](../../get-started/package) for more details. Note that once the installation is complete, do not immediately start the `taosd` service, but start it after correctly configuring the parameters. From c8d6960291937accffc9179ff8ec5ec37a093b9e Mon Sep 17 00:00:00 2001 From: Cary Xu Date: Mon, 29 Aug 2022 17:53:02 +0800 Subject: [PATCH 38/76] other: adjust sma log level --- source/dnode/vnode/src/sma/smaRollup.c | 2 +- source/util/src/tlog.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/dnode/vnode/src/sma/smaRollup.c b/source/dnode/vnode/src/sma/smaRollup.c index 3ed8dc2a20..1b1e304c03 100644 --- a/source/dnode/vnode/src/sma/smaRollup.c +++ b/source/dnode/vnode/src/sma/smaRollup.c @@ -1531,7 +1531,7 @@ static void tdRSmaFetchTrigger(void *param, void *tmrId) { if (atomic_load_8(&pRSmaInfo->assigned) == 0) { tsem_post(&(pStat->notEmpty)); } - smaInfo("vgId:%d, rsma fetch task planned for level:%" PRIi8 " suid:%" PRIi64, SMA_VID(pSma), pItem->level, + smaDebug("vgId:%d, rsma fetch task planned for level:%" PRIi8 " suid:%" PRIi64, SMA_VID(pSma), pItem->level, pRSmaInfo->suid); } break; case TASK_TRIGGER_STAT_PAUSED: { diff --git a/source/util/src/tlog.c b/source/util/src/tlog.c index a2d65d6a54..06ebbf27fb 100644 --- a/source/util/src/tlog.c +++ b/source/util/src/tlog.c @@ -97,7 +97,7 @@ int32_t tqDebugFlag = 135; int32_t fsDebugFlag = 135; int32_t metaDebugFlag = 135; int32_t udfDebugFlag = 135; -int32_t smaDebugFlag = 135; +int32_t smaDebugFlag = 131; int32_t idxDebugFlag = 135; int64_t dbgEmptyW = 0; From 894e71371928a2a5428901778f35e232bc281e12 Mon Sep 17 00:00:00 2001 From: Cary Xu Date: Mon, 29 Aug 2022 17:54:20 +0800 Subject: [PATCH 39/76] other: revert the assert for tpagebuf --- source/util/src/tpagedbuf.c | 1 - 1 file changed, 1 deletion(-) diff --git a/source/util/src/tpagedbuf.c b/source/util/src/tpagedbuf.c index 0c30cc1003..4d5532b9a6 100644 --- a/source/util/src/tpagedbuf.c +++ b/source/util/src/tpagedbuf.c @@ -465,7 +465,6 @@ void* getBufPage(SDiskbasedBuf* pBuf, int32_t id) { // set the ptr to the new SPageInfo ((void**)((*pi)->pData))[0] = (*pi); - assert(listNEles(pBuf->lruList) < pBuf->inMemPages && pBuf->inMemPages > 0); lruListPushFront(pBuf->lruList, *pi); (*pi)->used = true; From f478cb4f782d5ba006bd28ebf62b533c72e9a3dc Mon Sep 17 00:00:00 2001 From: Pan YANG Date: Mon, 29 Aug 2022 18:32:35 +0800 Subject: [PATCH 40/76] docs: read through and fix minor problems --- docs/en/01-index.md | 21 ++++++++++----------- docs/zh/01-index.md | 18 +++++++++--------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/docs/en/01-index.md b/docs/en/01-index.md index 22e62bc5e0..5265be42f8 100644 --- a/docs/en/01-index.md +++ b/docs/en/01-index.md @@ -4,25 +4,24 @@ sidebar_label: Documentation Home slug: / --- - -TDengine is an [open source](https://tdengine.com/tdengine/open-source-time-series-database/), [cloud native](https://tdengine.com/tdengine/cloud-native-time-series-database/) time-series database optimized for Internet of Things (IoT), Connected Cars, and Industrial IoT. It enables efficient, real-time data ingestion, processing, and monitoring of TB and even PB scale data per day, generated by billions of sensors and data collectors. 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. +TDengine is an [open-source](https://tdengine.com/tdengine/open-source-time-series-database/), [cloud-native](https://tdengine.com/tdengine/cloud-native-time-series-database/) time-series database optimized for the Internet of Things (IoT), Connected Cars, and Industrial IoT. It enables efficient, real-time data ingestion, processing, and monitoring of TB and even PB scale data per day, generated by billions of sensors and data collectors. 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. +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. +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, and 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 deployment"](../deployment). +We live in the era of big data, and scale-up is unable to meet the growing needs of the 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 Deployment](../deployment). -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. +TDengine uses ubiquitous 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 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 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. +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. +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. +Together, we make a difference! diff --git a/docs/zh/01-index.md b/docs/zh/01-index.md index 79d5424ac2..f9127121f3 100644 --- a/docs/zh/01-index.md +++ b/docs/zh/01-index.md @@ -4,22 +4,22 @@ sidebar_label: 文档首页 slug: / --- -TDengine是一款[开源](https://www.taosdata.com/tdengine/open_source_time-series_database)、[高性能](https://www.taosdata.com/fast)、[云原生](https://www.taosdata.com/tdengine/cloud_native_time-series_database)的时序数据库Time Series Database, TSDB), 它专为物联网、工业互联网、金融等场景优化设计。同时它还带有内建的缓存、流式计算、数据订阅等系统功能,能大幅减少系统设计的复杂度,降低研发和运营成本,是一极简的时序数据处理平台。本文档是 TDengine 用户手册,主要是介绍 TDengine 的基本概念、安装、使用、功能、开发接口、运营维护、TDengine 内核设计等等,它主要是面向架构师、开发者与系统管理员的。 +TDengine 是一款[开源](https://www.taosdata.com/tdengine/open_source_time-series_database)、[高性能](https://www.taosdata.com/fast)、[云原生](https://www.taosdata.com/tdengine/cloud_native_time-series_database)的时序数据库Time Series Database, TSDB), 它专为物联网、车联网、工业互联网、金融、IT 运维等场景优化设计。同时它还带有内建的缓存、流式计算、数据订阅等系统功能,能大幅减少系统设计的复杂度,降低研发和运营成本,是一款极简的时序数据处理平台。本文档是 TDengine 的用户手册,主要是介绍 TDengine 的基本概念、安装、使用、功能、开发接口、运营维护、TDengine 内核设计等等,它主要是面向架构师、开发工程师与系统管理员的。 -TDengine 充分利用了时序数据的特点,提出了“一个数据采集点一张表”与“超级表”的概念,设计了创新的存储引擎,让数据的写入、查询和存储效率都得到极大的提升。为正确理解并使用TDengine, 无论如何,请您仔细阅读[基本概念](./concept)一章。 +TDengine 充分利用了时序数据的特点,提出了“一个数据采集点一张表”与“超级表”的概念,设计了创新的存储引擎,让数据的写入、查询和存储效率都得到极大的提升。为正确理解并使用 TDengine,无论如何,请您仔细阅读[基本概念](./concept)一章。 -如果你是开发者,请一定仔细阅读[开发指南](./develop)一章,该部分对数据库连接、建模、插入数据、查询、流式计算、缓存、数据订阅、用户自定义函数等功能都做了详细介绍,并配有各种编程语言的示例代码。大部分情况下,你只要把示例代码拷贝粘贴,针对自己的应用稍作改动,就能跑起来。 +如果你是开发工程师,请一定仔细阅读[开发指南](./develop)一章,该部分对数据库连接、建模、插入数据、查询、流式计算、缓存、数据订阅、用户自定义函数等功能都做了详细介绍,并配有各种编程语言的示例代码。大部分情况下,你只要复制粘贴示例代码,针对自己的应用稍作改动,就能跑起来。 -我们已经生活在大数据的时代,纵向扩展已经无法满足日益增长的业务需求,任何系统都必须具有水平扩展的能力,集群成为大数据以及 database 系统的不可缺失功能。TDengine 团队不仅实现了集群功能,而且将这一重要核心功能开源。怎么部署、管理和维护 TDengine 集群,请参考[部署集群](./deployment)一章。 +我们已经生活在大数据时代,纵向扩展已经无法满足日益增长的业务需求,任何系统都必须具有水平扩展的能力,集群成为大数据以及 Database 系统的不可缺失功能。TDengine 团队不仅实现了集群功能,而且将这一重要核心功能开源。怎么部署、管理和维护 TDengine 集群,请仔细参考[部署集群](./deployment)一章。 -TDengine 采用 SQL 作为其查询语言,大大降低学习成本、降低迁移成本,但同时针对时序数据场景,又做了一些扩展,以支持插值、降采样、时间加权平均等操作。[SQL 手册](./taos-sql)一章详细描述了 SQL 语法、详细列出了各种支持的命令和函数。 +TDengine 采用 SQL 作为查询语言,大大降低学习成本、降低迁移成本,但同时针对时序数据场景,又做了一些扩展,以支持插值、降采样、时间加权平均等操作。[SQL 手册](./taos-sql)一章详细描述了 SQL 语法、详细列出了各种支持的命令和函数。 -如果你是系统管理员,关心安装、升级、容错灾备、关心数据导入、导出,配置参数,怎么监测 TDengine 是否健康运行,怎么提升系统运行的性能,那么请仔细参考[运维指南](./operation)一章。 +如果你是系统管理员,关心安装、升级、容错灾备、关心数据导入、导出、配置参数,如何监测 TDengine 是否健康运行,如何提升系统运行的性能,请仔细参考[运维指南](./operation)一章。 -如果你对 TDengine 外围工具,REST API, 各种编程语言的连接器想做更多详细了解,请看[参考指南](./reference)一章。 +如果你对 TDengine 的外围工具、REST API、各种编程语言的连接器(Connector)想做更多详细了解,请看[参考指南](./reference)一章。 -如果你对 TDengine 内部的架构设计很有兴趣,欢迎仔细阅读[技术内幕](./tdinternal)一章,里面对集群的设计、数据分区、分片、写入、读出、查询、聚合查询的流程都做了详细的介绍。如果你想研读 TDengine 代码甚至贡献代码,请一定仔细读完这一章。 +如果你对 TDengine 的内部架构设计很有兴趣,欢迎仔细阅读[技术内幕](./tdinternal)一章,里面对集群的设计、数据分区、分片、写入、读出、查询、聚合查询的流程都做了详细的介绍。如果你想研读 TDengine 代码甚至贡献代码,请一定仔细读完这一章。 -最后,作为一个开源软件,欢迎大家的参与。如果发现文档的任何错误,描述不清晰的地方,都请在每个页面的最下方,点击“编辑本文档“直接进行修改。 +最后,作为一个开源软件,欢迎大家的参与。如果发现文档有任何错误、描述不清晰的地方,请在每个页面的最下方,点击“编辑本文档”直接进行修改。 Together, we make a difference! From 808545ca7fa45bdab421329dcd7804d25da4319c Mon Sep 17 00:00:00 2001 From: gccgdb1234 Date: Mon, 29 Aug 2022 18:39:40 +0800 Subject: [PATCH 41/76] doc: refine the title and label of grant page --- docs/en/12-taos-sql/25-grant.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/en/12-taos-sql/25-grant.md b/docs/en/12-taos-sql/25-grant.md index 37438ee780..b9a3fa2321 100644 --- a/docs/en/12-taos-sql/25-grant.md +++ b/docs/en/12-taos-sql/25-grant.md @@ -1,6 +1,7 @@ --- -sidebar_label: Permissions Management -title: Permissions Management +sidebar_label: Access Control +title: User and Access Control +description: Manage user and user's permission --- This document describes how to manage permissions in TDengine. From d4c91f8a10a24f1628b0cd130af8d40c0a22ab5e Mon Sep 17 00:00:00 2001 From: afwerar <1296468573@qq.com> Date: Mon, 29 Aug 2022 18:39:54 +0800 Subject: [PATCH 42/76] feature: update cfg --- cmake/cmake.platform | 2 +- include/common/tglobal.h | 4 +- source/common/src/tglobal.c | 198 +++++++++++++++++++++++++++++++----- 3 files changed, 177 insertions(+), 27 deletions(-) diff --git a/cmake/cmake.platform b/cmake/cmake.platform index 887fbd86d5..3aa1ffc07e 100644 --- a/cmake/cmake.platform +++ b/cmake/cmake.platform @@ -46,7 +46,7 @@ IF (${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR ${CMAKE_SYSTEM_NAME} MATCHES "Darwin MESSAGE("Current system processor is ${CMAKE_SYSTEM_PROCESSOR}.") IF (${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm64" OR ${CMAKE_SYSTEM_PROCESSOR} MATCHES "x86_64") - MESSAGE("Current system arch is arm64") + MESSAGE("Current system arch is 64") SET(TD_DARWIN_64 TRUE) ADD_DEFINITIONS("-D_TD_DARWIN_64") ENDIF () diff --git a/include/common/tglobal.h b/include/common/tglobal.h index 03e15ed8e7..f72cb3d6d9 100644 --- a/include/common/tglobal.h +++ b/include/common/tglobal.h @@ -144,8 +144,8 @@ void taosCfgDynamicOptions(const char *option, const char *value); struct SConfig *taosGetCfg(); -void taosSetAllDebugFlag(int32_t flag); -void taosSetDebugFlag(int32_t *pFlagPtr, const char *flagName, int32_t flagVal); +void taosSetAllDebugFlag(int32_t flag, bool rewrite); +void taosSetDebugFlag(int32_t *pFlagPtr, const char *flagName, int32_t flagVal, bool rewrite); int32_t taosSetCfg(SConfig *pCfg, char *name); void taosLocalCfgForbiddenToChange(char* name, bool* forbidden); diff --git a/source/common/src/tglobal.c b/source/common/src/tglobal.c index ee9d751555..a45845197d 100644 --- a/source/common/src/tglobal.c +++ b/source/common/src/tglobal.c @@ -427,6 +427,152 @@ static int32_t taosAddServerCfg(SConfig *pCfg) { return 0; } +static int32_t taosUpdateServerCfg(SConfig *pCfg) { + SConfigItem *pItem; + ECfgSrcType stype; + int32_t numOfCores; + int64_t totalMemoryKB; + + pItem = cfgGetItem(tsCfg, "numOfCores"); + if (pItem == NULL) { + return -1; + } else { + stype = pItem->stype; + numOfCores = pItem->i32; + } + + pItem = cfgGetItem(tsCfg, "supportVnodes"); + if (pItem != NULL && pItem->stype == CFG_STYPE_DEFAULT) { + tsNumOfSupportVnodes = numOfCores * 2; + tsNumOfSupportVnodes = TMAX(tsNumOfSupportVnodes, 2); + pItem->i32 = tsNumOfSupportVnodes; + pItem->stype = stype; + } + + pItem = cfgGetItem(tsCfg, "numOfRpcThreads"); + if (pItem != NULL && pItem->stype == CFG_STYPE_DEFAULT) { + tsNumOfRpcThreads = numOfCores / 2; + tsNumOfRpcThreads = TRANGE(tsNumOfRpcThreads, 1, 4); + pItem->i32 = tsNumOfRpcThreads; + pItem->stype = stype; + } + + pItem = cfgGetItem(tsCfg, "numOfCommitThreads"); + if (pItem != NULL && pItem->stype == CFG_STYPE_DEFAULT) { + tsNumOfCommitThreads = numOfCores / 2; + tsNumOfCommitThreads = TRANGE(tsNumOfCommitThreads, 2, 4); + pItem->i32 = tsNumOfCommitThreads; + pItem->stype = stype; + } + + pItem = cfgGetItem(tsCfg, "numOfMnodeReadThreads"); + if (pItem != NULL && pItem->stype == CFG_STYPE_DEFAULT) { + tsNumOfMnodeReadThreads = numOfCores / 8; + tsNumOfMnodeReadThreads = TRANGE(tsNumOfMnodeReadThreads, 1, 4); + pItem->i32 = tsNumOfMnodeReadThreads; + pItem->stype = stype; + } + + pItem = cfgGetItem(tsCfg, "numOfVnodeQueryThreads"); + if (pItem != NULL && pItem->stype == CFG_STYPE_DEFAULT) { + tsNumOfVnodeQueryThreads = numOfCores * 2; + tsNumOfVnodeQueryThreads = TMAX(tsNumOfVnodeQueryThreads, 4); + pItem->i32 = tsNumOfVnodeQueryThreads; + pItem->stype = stype; + } + + pItem = cfgGetItem(tsCfg, "numOfVnodeStreamThreads"); + if (pItem != NULL && pItem->stype == CFG_STYPE_DEFAULT) { + tsNumOfVnodeStreamThreads = numOfCores / 4; + tsNumOfVnodeStreamThreads = TMAX(tsNumOfVnodeStreamThreads, 4); + pItem->i32 = tsNumOfVnodeStreamThreads; + pItem->stype = stype; + } + + pItem = cfgGetItem(tsCfg, "numOfVnodeFetchThreads"); + if (pItem != NULL && pItem->stype == CFG_STYPE_DEFAULT) { + tsNumOfVnodeFetchThreads = numOfCores / 4; + tsNumOfVnodeFetchThreads = TMAX(tsNumOfVnodeFetchThreads, 4); + pItem->i32 = tsNumOfVnodeFetchThreads; + pItem->stype = stype; + } + + pItem = cfgGetItem(tsCfg, "numOfVnodeWriteThreads"); + if (pItem != NULL && pItem->stype == CFG_STYPE_DEFAULT) { + tsNumOfVnodeWriteThreads = numOfCores; + tsNumOfVnodeWriteThreads = TMAX(tsNumOfVnodeWriteThreads, 1); + pItem->i32 = tsNumOfVnodeWriteThreads; + pItem->stype = stype; + } + + pItem = cfgGetItem(tsCfg, "numOfVnodeSyncThreads"); + if (pItem != NULL && pItem->stype == CFG_STYPE_DEFAULT) { + tsNumOfVnodeSyncThreads = numOfCores * 2; + tsNumOfVnodeSyncThreads = TMAX(tsNumOfVnodeSyncThreads, 16); + pItem->i32 = tsNumOfVnodeSyncThreads; + pItem->stype = stype; + } + + pItem = cfgGetItem(tsCfg, "numOfVnodeRsmaThreads"); + if (pItem != NULL && pItem->stype == CFG_STYPE_DEFAULT) { + tsNumOfVnodeRsmaThreads = numOfCores; + tsNumOfVnodeRsmaThreads = TMAX(tsNumOfVnodeRsmaThreads, 4); + pItem->i32 = tsNumOfVnodeRsmaThreads; + pItem->stype = stype; + } + + pItem = cfgGetItem(tsCfg, "numOfQnodeQueryThreads"); + if (pItem != NULL && pItem->stype == CFG_STYPE_DEFAULT) { + tsNumOfQnodeQueryThreads = numOfCores * 2; + tsNumOfQnodeQueryThreads = TMAX(tsNumOfQnodeQueryThreads, 4); + pItem->i32 = tsNumOfQnodeQueryThreads; + pItem->stype = stype; + } + + pItem = cfgGetItem(tsCfg, "numOfQnodeFetchThreads"); + if (pItem != NULL && pItem->stype == CFG_STYPE_DEFAULT) { + tsNumOfQnodeFetchThreads = numOfCores / 2; + tsNumOfQnodeFetchThreads = TMAX(tsNumOfQnodeFetchThreads, 4); + pItem->i32 = tsNumOfQnodeFetchThreads; + pItem->stype = stype; + } + + pItem = cfgGetItem(tsCfg, "numOfSnodeSharedThreads"); + if (pItem != NULL && pItem->stype == CFG_STYPE_DEFAULT) { + tsNumOfSnodeSharedThreads = numOfCores / 4; + tsNumOfSnodeSharedThreads = TRANGE(tsNumOfSnodeSharedThreads, 2, 4); + pItem->i32 = tsNumOfSnodeSharedThreads; + pItem->stype = stype; + } + + pItem = cfgGetItem(tsCfg, "numOfSnodeUniqueThreads"); + if (pItem != NULL && pItem->stype == CFG_STYPE_DEFAULT) { + tsNumOfSnodeUniqueThreads = numOfCores / 4; + tsNumOfSnodeUniqueThreads = TRANGE(tsNumOfSnodeUniqueThreads, 2, 4); + pItem->i32 = tsNumOfSnodeUniqueThreads; + pItem->stype = stype; + } + + pItem = cfgGetItem(tsCfg, "totalMemoryKB"); + if (pItem == NULL) { + return -1; + } else { + stype = pItem->stype; + totalMemoryKB = pItem->i64; + } + + pItem = cfgGetItem(tsCfg, "rpcQueueMemoryAllowed"); + if (pItem != NULL && pItem->stype == CFG_STYPE_DEFAULT) { + tsRpcQueueMemoryAllowed = totalMemoryKB * 1024 * 0.1; + tsRpcQueueMemoryAllowed = TRANGE(tsRpcQueueMemoryAllowed, TSDB_MAX_MSG_SIZE * 10LL, TSDB_MAX_MSG_SIZE * 10000LL); + pItem->i64 = tsRpcQueueMemoryAllowed; + pItem->stype = stype; + } + + return 0; +} + + static void taosSetClientLogCfg(SConfig *pCfg) { SConfigItem *pItem = cfgGetItem(pCfg, "logDir"); tstrncpy(tsLogDir, cfgGetItem(pCfg, "logDir")->str, PATH_MAX); @@ -981,7 +1127,7 @@ int32_t taosCreateLog(const char *logname, int32_t logFileNum, const char *cfgDi taosSetServerLogCfg(pCfg); } - taosSetAllDebugFlag(cfgGetItem(pCfg, "debugFlag")->i32); + taosSetAllDebugFlag(cfgGetItem(pCfg, "debugFlag")->i32, false); if (taosMulMkDir(tsLogDir) != 0) { uError("failed to create dir:%s since %s", tsLogDir, terrstr()); @@ -1048,6 +1194,7 @@ int32_t taosInitCfg(const char *cfgDir, const char **envCmd, const char *envFile if (taosSetClientCfg(tsCfg)) return -1; } else { if (taosSetClientCfg(tsCfg)) return -1; + if (taosUpdateServerCfg(tsCfg)) return -1; if (taosSetServerCfg(tsCfg)) return -1; if (taosSetTfsCfg(tsCfg) != 0) return -1; } @@ -1072,7 +1219,7 @@ void taosCleanupCfg() { void taosCfgDynamicOptions(const char *option, const char *value) { if (strncasecmp(option, "debugFlag", 9) == 0) { int32_t flag = atoi(value); - taosSetAllDebugFlag(flag); + taosSetAllDebugFlag(flag, true); return; } @@ -1097,11 +1244,13 @@ void taosCfgDynamicOptions(const char *option, const char *value) { "dDebugFlag", "vDebugFlag", "mDebugFlag", "wDebugFlag", "sDebugFlag", "tsdbDebugFlag", "tqDebugFlag", "fsDebugFlag", "udfDebugFlag", "smaDebugFlag", "idxDebugFlag", "tdbDebugFlag", "tmrDebugFlag", "uDebugFlag", "smaDebugFlag", "rpcDebugFlag", "qDebugFlag", "metaDebugFlag", + "jniDebugFlag", }; int32_t *optionVars[] = { &dDebugFlag, &vDebugFlag, &mDebugFlag, &wDebugFlag, &sDebugFlag, &tsdbDebugFlag, &tqDebugFlag, &fsDebugFlag, &udfDebugFlag, &smaDebugFlag, &idxDebugFlag, &tdbDebugFlag, &tmrDebugFlag, &uDebugFlag, &smaDebugFlag, &rpcDebugFlag, &qDebugFlag, &metaDebugFlag, + &jniDebugFlag, }; int32_t optionSize = tListLen(options); @@ -1113,41 +1262,42 @@ void taosCfgDynamicOptions(const char *option, const char *value) { int32_t flag = atoi(value); uInfo("%s set from %d to %d", optName, *optionVars[d], flag); *optionVars[d] = flag; - taosSetDebugFlag(optionVars[d], optName, flag); + taosSetDebugFlag(optionVars[d], optName, flag, true); return; } uError("failed to cfg dynamic option:%s value:%s", option, value); } -void taosSetDebugFlag(int32_t *pFlagPtr, const char *flagName, int32_t flagVal) { +void taosSetDebugFlag(int32_t *pFlagPtr, const char *flagName, int32_t flagVal, bool rewrite) { SConfigItem *pItem = cfgGetItem(tsCfg, flagName); - if (pItem != NULL) { + if (pItem != NULL && (rewrite || pItem->i32 == 0)) { pItem->i32 = flagVal; } *pFlagPtr = flagVal; } -void taosSetAllDebugFlag(int32_t flag) { +void taosSetAllDebugFlag(int32_t flag, bool rewrite) { if (flag <= 0) return; - taosSetDebugFlag(&uDebugFlag, "uDebugFlag", flag); - taosSetDebugFlag(&rpcDebugFlag, "rpcDebugFlag", flag); - taosSetDebugFlag(&jniDebugFlag, "jniDebugFlag", flag); - taosSetDebugFlag(&qDebugFlag, "qDebugFlag", flag); - taosSetDebugFlag(&cDebugFlag, "cDebugFlag", flag); - taosSetDebugFlag(&dDebugFlag, "dDebugFlag", flag); - taosSetDebugFlag(&vDebugFlag, "vDebugFlag", flag); - taosSetDebugFlag(&mDebugFlag, "mDebugFlag", flag); - taosSetDebugFlag(&wDebugFlag, "wDebugFlag", flag); - taosSetDebugFlag(&sDebugFlag, "sDebugFlag", flag); - taosSetDebugFlag(&tsdbDebugFlag, "tsdbDebugFlag", flag); - taosSetDebugFlag(&tqDebugFlag, "tqDebugFlag", flag); - taosSetDebugFlag(&fsDebugFlag, "fsDebugFlag", flag); - taosSetDebugFlag(&udfDebugFlag, "udfDebugFlag", flag); - taosSetDebugFlag(&smaDebugFlag, "smaDebugFlag", flag); - taosSetDebugFlag(&idxDebugFlag, "idxDebugFlag", flag); - taosSetDebugFlag(&tdbDebugFlag, "tdbDebugFlag", flag); - taosSetDebugFlag(&metaDebugFlag, "metaDebugFlag", flag); + taosSetDebugFlag(&uDebugFlag, "uDebugFlag", flag, rewrite); + taosSetDebugFlag(&rpcDebugFlag, "rpcDebugFlag", flag, rewrite); + taosSetDebugFlag(&jniDebugFlag, "jniDebugFlag", flag, rewrite); + taosSetDebugFlag(&qDebugFlag, "qDebugFlag", flag, rewrite); + taosSetDebugFlag(&cDebugFlag, "cDebugFlag", flag, rewrite); + taosSetDebugFlag(&dDebugFlag, "dDebugFlag", flag, rewrite); + taosSetDebugFlag(&vDebugFlag, "vDebugFlag", flag, rewrite); + taosSetDebugFlag(&mDebugFlag, "mDebugFlag", flag, rewrite); + taosSetDebugFlag(&wDebugFlag, "wDebugFlag", flag, rewrite); + taosSetDebugFlag(&sDebugFlag, "sDebugFlag", flag, rewrite); + taosSetDebugFlag(&tsdbDebugFlag, "tsdbDebugFlag", flag, rewrite); + taosSetDebugFlag(&tqDebugFlag, "tqDebugFlag", flag, rewrite); + taosSetDebugFlag(&fsDebugFlag, "fsDebugFlag", flag, rewrite); + taosSetDebugFlag(&udfDebugFlag, "udfDebugFlag", flag, rewrite); + taosSetDebugFlag(&smaDebugFlag, "smaDebugFlag", flag, rewrite); + taosSetDebugFlag(&idxDebugFlag, "idxDebugFlag", flag, rewrite); + taosSetDebugFlag(&tdbDebugFlag, "tdbDebugFlag", flag, rewrite); + taosSetDebugFlag(&metaDebugFlag, "metaDebugFlag", flag, rewrite); + taosSetDebugFlag(&metaDebugFlag, "tmrDebugFlag", flag, rewrite); uInfo("all debug flag are set to %d", flag); } From 15cf8f04d97ae7183fa82401e794da6faabdd5ec Mon Sep 17 00:00:00 2001 From: Shuduo Sang Date: Mon, 29 Aug 2022 18:43:43 +0800 Subject: [PATCH 43/76] fix: 3.0 asserts download path (#16490) * fix: docker script permisison * fix: assets-download path --- packaging/docker/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packaging/docker/README.md b/packaging/docker/README.md index cb27d3bca6..763ab73724 100644 --- a/packaging/docker/README.md +++ b/packaging/docker/README.md @@ -158,7 +158,7 @@ When you build your application with docker, you should add the TDengine client 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 \ +RUN wget -c https://www.taosdata.com/assets-download/3.0/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 \ @@ -265,7 +265,7 @@ 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 \ +RUN wget -c https://www.taosdata.com/assets-download/3.0/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 \ @@ -279,7 +279,7 @@ 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 \ +RUN wget -c https://www.taosdata.com/assets-download/3.0/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 \ From ba8b10e27b6e38c939626796a08ad17d64d31c55 Mon Sep 17 00:00:00 2001 From: Haojun Liao Date: Mon, 29 Aug 2022 18:47:46 +0800 Subject: [PATCH 44/76] refactor(query): do some internal refactor. --- include/libs/function/function.h | 10 +- include/util/tpagedbuf.h | 3 +- source/libs/executor/src/executil.c | 6 +- source/libs/executor/src/executorimpl.c | 14 +- source/libs/executor/src/groupoperator.c | 4 +- source/libs/executor/src/projectoperator.c | 10 - source/libs/executor/src/timewindowoperator.c | 2 +- source/libs/executor/src/tlinearhash.c | 4 +- source/libs/executor/src/tsort.c | 4 +- source/libs/function/src/builtinsimpl.c | 276 +++++++++--------- source/libs/function/src/tpercentile.c | 2 +- source/util/src/tpagedbuf.c | 2 +- source/util/test/pageBufferTest.cpp | 34 +-- 13 files changed, 188 insertions(+), 183 deletions(-) diff --git a/include/libs/function/function.h b/include/libs/function/function.h index c8db01625e..3f26eee86a 100644 --- a/include/libs/function/function.h +++ b/include/libs/function/function.h @@ -92,6 +92,8 @@ struct SResultRowEntryInfo; //for selectivity query, the corresponding tag value is assigned if the data is qualified typedef struct SSubsidiaryResInfo { int16_t num; + int32_t rowLen; + char* buf; // serialize data buffer struct SqlFunctionCtx **pCtx; } SSubsidiaryResInfo; @@ -118,6 +120,11 @@ typedef struct SInputColumnInfoData { uint64_t uid; // table uid, used to set the tag value when building the final query result for selectivity functions. } SInputColumnInfoData; +typedef struct SSerializeDataHandle { + struct SDiskbasedBuf* pBuf; + int32_t currentPage; +} SSerializeDataHandle; + // sql function runtime context typedef struct SqlFunctionCtx { SInputColumnInfoData input; @@ -137,10 +144,9 @@ typedef struct SqlFunctionCtx { SFuncExecFuncs fpSet; SScalarFuncExecFuncs sfp; struct SExprInfo *pExpr; - struct SDiskbasedBuf *pBuf; struct SSDataBlock *pSrcBlock; struct SSDataBlock *pDstBlock; // used by indefinite rows function to set selectivity - int32_t curBufPage; + SSerializeDataHandle saveHandle; bool isStream; char udfName[TSDB_FUNC_NAME_LEN]; diff --git a/include/util/tpagedbuf.h b/include/util/tpagedbuf.h index 57a489c0dd..9ab89273e6 100644 --- a/include/util/tpagedbuf.h +++ b/include/util/tpagedbuf.h @@ -58,11 +58,10 @@ int32_t createDiskbasedBuf(SDiskbasedBuf** pBuf, int32_t pagesize, int32_t inMem /** * * @param pBuf - * @param groupId * @param pageId * @return */ -void* getNewBufPage(SDiskbasedBuf* pBuf, int32_t groupId, int32_t* pageId); +void* getNewBufPage(SDiskbasedBuf* pBuf, int32_t* pageId); /** * diff --git a/source/libs/executor/src/executil.c b/source/libs/executor/src/executil.c index 4b018f81ef..c6bc120c62 100644 --- a/source/libs/executor/src/executil.c +++ b/source/libs/executor/src/executil.c @@ -46,8 +46,8 @@ size_t getResultRowSize(SqlFunctionCtx* pCtx, int32_t numOfOutput) { rowSize += pCtx[i].resDataInfo.interBufSize; } - rowSize += - (numOfOutput * sizeof(bool)); // expand rowSize to mark if col is null for top/bottom result(doSaveTupleData) + rowSize += (numOfOutput * sizeof(bool)); + // expand rowSize to mark if col is null for top/bottom result(saveTupleData) return rowSize; } @@ -1175,7 +1175,6 @@ SqlFunctionCtx* createSqlFunctionCtx(SExprInfo* pExprInfo, int32_t numOfOutput, SqlFunctionCtx* pCtx = &pFuncCtx[i]; pCtx->functionId = -1; - pCtx->curBufPage = -1; pCtx->pExpr = pExpr; if (pExpr->pExpr->nodeType == QUERY_NODE_FUNCTION) { @@ -1219,6 +1218,7 @@ SqlFunctionCtx* createSqlFunctionCtx(SExprInfo* pExprInfo, int32_t numOfOutput, pCtx->isStream = false; pCtx->param = pFunct->pParam; + pCtx->saveHandle.currentPage = -1; } for (int32_t i = 1; i < numOfOutput; ++i) { diff --git a/source/libs/executor/src/executorimpl.c b/source/libs/executor/src/executorimpl.c index cf6940c52a..9b102de5bf 100644 --- a/source/libs/executor/src/executorimpl.c +++ b/source/libs/executor/src/executorimpl.c @@ -187,7 +187,7 @@ SResultRow* getNewResultRow(SDiskbasedBuf* pResultBuf, int64_t tableGroupId, int SIDList list = getDataBufPagesIdList(pResultBuf); if (taosArrayGetSize(list) == 0) { - pData = getNewBufPage(pResultBuf, tableGroupId, &pageId); + pData = getNewBufPage(pResultBuf, &pageId); pData->num = sizeof(SFilePage); } else { SPageInfo* pi = getLastPageInfo(list); @@ -198,7 +198,7 @@ SResultRow* getNewResultRow(SDiskbasedBuf* pResultBuf, int64_t tableGroupId, int // release current page first, and prepare the next one releaseBufPageInfo(pResultBuf, pi); - pData = getNewBufPage(pResultBuf, tableGroupId, &pageId); + pData = getNewBufPage(pResultBuf, &pageId); if (pData != NULL) { pData->num = sizeof(SFilePage); } @@ -302,7 +302,7 @@ static int32_t addNewWindowResultBuf(SResultRow* pWindowRes, SDiskbasedBuf* pRes SIDList list = getDataBufPagesIdList(pResultBuf); if (taosArrayGetSize(list) == 0) { - pData = getNewBufPage(pResultBuf, tid, &pageId); + pData = getNewBufPage(pResultBuf, &pageId); pData->num = sizeof(SFilePage); } else { SPageInfo* pi = getLastPageInfo(list); @@ -313,7 +313,7 @@ static int32_t addNewWindowResultBuf(SResultRow* pWindowRes, SDiskbasedBuf* pRes // release current page first, and prepare the next one releaseBufPageInfo(pResultBuf, pi); - pData = getNewBufPage(pResultBuf, tid, &pageId); + pData = getNewBufPage(pResultBuf, &pageId); if (pData != NULL) { pData->num = sizeof(SFilePage); } @@ -3488,7 +3488,7 @@ int32_t initAggInfo(SExprSupp* pSup, SAggSupporter* pAggSup, SExprInfo* pExprInf } for (int32_t i = 0; i < numOfCols; ++i) { - pSup->pCtx[i].pBuf = pAggSup->pResultBuf; + pSup->pCtx[i].saveHandle.pBuf = pAggSup->pResultBuf; } return TSDB_CODE_SUCCESS; @@ -3520,6 +3520,7 @@ void* destroySqlFunctionCtx(SqlFunctionCtx* pCtx, int32_t numOfOutput) { } taosMemoryFreeClear(pCtx[i].subsidiaries.pCtx); + taosMemoryFreeClear(pCtx[i].subsidiaries.buf); taosMemoryFree(pCtx[i].input.pData); taosMemoryFree(pCtx[i].input.pColumnDataAgg); } @@ -4704,7 +4705,8 @@ int32_t initStreamAggSupporter(SStreamAggSupporter* pSup, const char* pKey, SqlF } int32_t code = createDiskbasedBuf(&pSup->pResultBuf, pageSize, bufSize, pKey, tsTempDir); for (int32_t i = 0; i < numOfOutput; ++i) { - pCtx[i].pBuf = pSup->pResultBuf; + pCtx[i].saveHandle.pBuf = pSup->pResultBuf; } + return code; } diff --git a/source/libs/executor/src/groupoperator.c b/source/libs/executor/src/groupoperator.c index 9d7e833b19..5d123f723e 100644 --- a/source/libs/executor/src/groupoperator.c +++ b/source/libs/executor/src/groupoperator.c @@ -547,7 +547,7 @@ void* getCurrentDataGroupInfo(const SPartitionOperatorInfo* pInfo, SDataGroupInf p = taosHashGet(pInfo->pGroupSet, pInfo->keyBuf, len); int32_t pageId = 0; - pPage = getNewBufPage(pInfo->pBuf, 0, &pageId); + pPage = getNewBufPage(pInfo->pBuf, &pageId); taosArrayPush(p->pPageList, &pageId); *(int32_t *) pPage = 0; @@ -562,7 +562,7 @@ void* getCurrentDataGroupInfo(const SPartitionOperatorInfo* pInfo, SDataGroupInf // add a new page for current group int32_t pageId = 0; - pPage = getNewBufPage(pInfo->pBuf, 0, &pageId); + pPage = getNewBufPage(pInfo->pBuf, &pageId); taosArrayPush(p->pPageList, &pageId); memset(pPage, 0, getBufPageSize(pInfo->pBuf)); } diff --git a/source/libs/executor/src/projectoperator.c b/source/libs/executor/src/projectoperator.c index 0661ccd390..2f12a0d19b 100644 --- a/source/libs/executor/src/projectoperator.c +++ b/source/libs/executor/src/projectoperator.c @@ -195,16 +195,6 @@ static int32_t doIngroupLimitOffset(SLimitInfo* pLimitInfo, uint64_t groupId, SS return PROJECT_RETRIEVE_DONE; } -void printDataBlock1(SSDataBlock* pBlock, const char* flag) { - if (!pBlock || pBlock->info.rows == 0) { - qDebug("===stream===printDataBlock: Block is Null or Empty"); - return; - } - char* pBuf = NULL; - qDebug("%s", dumpBlockData(pBlock, flag, &pBuf)); - taosMemoryFreeClear(pBuf); -} - SSDataBlock* doProjectOperation(SOperatorInfo* pOperator) { SProjectOperatorInfo* pProjectInfo = pOperator->info; SOptrBasicInfo* pInfo = &pProjectInfo->binfo; diff --git a/source/libs/executor/src/timewindowoperator.c b/source/libs/executor/src/timewindowoperator.c index 4909c8d387..d87e235ba5 100644 --- a/source/libs/executor/src/timewindowoperator.c +++ b/source/libs/executor/src/timewindowoperator.c @@ -3529,7 +3529,7 @@ int32_t initBasicInfoEx(SOptrBasicInfo* pBasicInfo, SExprSupp* pSup, SExprInfo* initBasicInfo(pBasicInfo, pResultBlock); for (int32_t i = 0; i < numOfCols; ++i) { - pSup->pCtx[i].pBuf = NULL; + pSup->pCtx[i].saveHandle.pBuf = NULL; } ASSERT(numOfCols > 0); diff --git a/source/libs/executor/src/tlinearhash.c b/source/libs/executor/src/tlinearhash.c index e0752840db..cffabcb6ac 100644 --- a/source/libs/executor/src/tlinearhash.c +++ b/source/libs/executor/src/tlinearhash.c @@ -97,7 +97,7 @@ static int32_t doAddToBucket(SLHashObj* pHashObj, SLHashBucket* pBucket, int32_t // allocate the overflow buffer page to hold this k/v. int32_t newPageId = -1; - SFilePage* pNewPage = getNewBufPage(pHashObj->pBuf, 0, &newPageId); + SFilePage* pNewPage = getNewBufPage(pHashObj->pBuf, &newPageId); if (pNewPage == NULL) { return terrno; } @@ -227,7 +227,7 @@ static int32_t doAddNewBucket(SLHashObj* pHashObj) { } int32_t pageId = -1; - SFilePage* p = getNewBufPage(pHashObj->pBuf, 0, &pageId); + SFilePage* p = getNewBufPage(pHashObj->pBuf, &pageId); if (p == NULL) { return terrno; } diff --git a/source/libs/executor/src/tsort.c b/source/libs/executor/src/tsort.c index fc411e850a..168cd21c44 100644 --- a/source/libs/executor/src/tsort.c +++ b/source/libs/executor/src/tsort.c @@ -180,7 +180,7 @@ static int32_t doAddToBuf(SSDataBlock* pDataBlock, SSortHandle* pHandle) { } int32_t pageId = -1; - void* pPage = getNewBufPage(pHandle->pBuf, pHandle->sourceId, &pageId); + void* pPage = getNewBufPage(pHandle->pBuf, &pageId); if (pPage == NULL) { blockDataDestroy(p); return terrno; @@ -512,7 +512,7 @@ static int32_t doInternalMergeSort(SSortHandle* pHandle) { } int32_t pageId = -1; - void* pPage = getNewBufPage(pHandle->pBuf, pHandle->sourceId, &pageId); + void* pPage = getNewBufPage(pHandle->pBuf, &pageId); if (pPage == NULL) { return terrno; } diff --git a/source/libs/function/src/builtinsimpl.c b/source/libs/function/src/builtinsimpl.c index 32d0472a50..417875838a 100644 --- a/source/libs/function/src/builtinsimpl.c +++ b/source/libs/function/src/builtinsimpl.c @@ -1146,8 +1146,9 @@ bool getMinmaxFuncEnv(SFunctionNode* UNUSED_PARAM(pFunc), SFuncExecEnv* pEnv) { return true; } -static void doSaveTupleData(SqlFunctionCtx* pCtx, int32_t rowIndex, const SSDataBlock* pSrcBlock, STuplePos* pPos); -static void doCopyTupleData(SqlFunctionCtx* pCtx, int32_t rowIndex, const SSDataBlock* pSrcBlock, STuplePos* pPos); +static STuplePos saveTupleData(SqlFunctionCtx* pCtx, int32_t rowIndex, const SSDataBlock* pSrcBlock); +static int32_t updateTupleData(SqlFunctionCtx* pCtx, int32_t rowIndex, const SSDataBlock* pSrcBlock, STuplePos* pPos); +static const char* loadTupleData(SqlFunctionCtx* pCtx, const STuplePos* pPos); static int32_t findRowIndex(int32_t start, int32_t num, SColumnInfoData* pCol, const char* tval) { // the data is loaded, not only the block SMA value @@ -1199,7 +1200,7 @@ int32_t doMinMaxHelper(SqlFunctionCtx* pCtx, int32_t isMinFunc) { pBuf->v = *(int64_t*)tval; if (pCtx->subsidiaries.num > 0) { index = findRowIndex(pInput->startRowIndex, pInput->numOfRows, pCol, tval); - doSaveTupleData(pCtx, index, pCtx->pSrcBlock, &pBuf->tuplePos); + pBuf->tuplePos = saveTupleData(pCtx, index, pCtx->pSrcBlock); } } else { if (IS_SIGNED_NUMERIC_TYPE(type)) { @@ -1211,7 +1212,7 @@ int32_t doMinMaxHelper(SqlFunctionCtx* pCtx, int32_t isMinFunc) { *(int64_t*)&pBuf->v = val; if (pCtx->subsidiaries.num > 0) { index = findRowIndex(pInput->startRowIndex, pInput->numOfRows, pCol, tval); - doSaveTupleData(pCtx, index, pCtx->pSrcBlock, &pBuf->tuplePos); + pBuf->tuplePos = saveTupleData(pCtx, index, pCtx->pSrcBlock); } } @@ -1224,7 +1225,7 @@ int32_t doMinMaxHelper(SqlFunctionCtx* pCtx, int32_t isMinFunc) { *(uint64_t*)&pBuf->v = val; if (pCtx->subsidiaries.num > 0) { index = findRowIndex(pInput->startRowIndex, pInput->numOfRows, pCol, tval); - doSaveTupleData(pCtx, index, pCtx->pSrcBlock, &pBuf->tuplePos); + pBuf->tuplePos = saveTupleData(pCtx, index, pCtx->pSrcBlock); } } } else if (type == TSDB_DATA_TYPE_DOUBLE) { @@ -1236,7 +1237,7 @@ int32_t doMinMaxHelper(SqlFunctionCtx* pCtx, int32_t isMinFunc) { *(double*)&pBuf->v = val; if (pCtx->subsidiaries.num > 0) { index = findRowIndex(pInput->startRowIndex, pInput->numOfRows, pCol, tval); - doSaveTupleData(pCtx, index, pCtx->pSrcBlock, &pBuf->tuplePos); + pBuf->tuplePos = saveTupleData(pCtx, index, pCtx->pSrcBlock); } } } else if (type == TSDB_DATA_TYPE_FLOAT) { @@ -1250,7 +1251,7 @@ int32_t doMinMaxHelper(SqlFunctionCtx* pCtx, int32_t isMinFunc) { if (pCtx->subsidiaries.num > 0) { index = findRowIndex(pInput->startRowIndex, pInput->numOfRows, pCol, tval); - doSaveTupleData(pCtx, index, pCtx->pSrcBlock, &pBuf->tuplePos); + pBuf->tuplePos = saveTupleData(pCtx, index, pCtx->pSrcBlock); } } } @@ -1275,7 +1276,7 @@ int32_t doMinMaxHelper(SqlFunctionCtx* pCtx, int32_t isMinFunc) { if (!pBuf->assign) { *val = pData[i]; if (pCtx->subsidiaries.num > 0) { - doSaveTupleData(pCtx, i, pCtx->pSrcBlock, &pBuf->tuplePos); + pBuf->tuplePos = saveTupleData(pCtx, i, pCtx->pSrcBlock); } pBuf->assign = true; } else { @@ -1287,7 +1288,7 @@ int32_t doMinMaxHelper(SqlFunctionCtx* pCtx, int32_t isMinFunc) { if ((*val < pData[i]) ^ isMinFunc) { *val = pData[i]; if (pCtx->subsidiaries.num > 0) { - doCopyTupleData(pCtx, i, pCtx->pSrcBlock, &pBuf->tuplePos); + updateTupleData(pCtx, i, pCtx->pSrcBlock, &pBuf->tuplePos); } } } @@ -1306,7 +1307,7 @@ int32_t doMinMaxHelper(SqlFunctionCtx* pCtx, int32_t isMinFunc) { if (!pBuf->assign) { *val = pData[i]; if (pCtx->subsidiaries.num > 0) { - doSaveTupleData(pCtx, i, pCtx->pSrcBlock, &pBuf->tuplePos); + pBuf->tuplePos = saveTupleData(pCtx, i, pCtx->pSrcBlock); } pBuf->assign = true; } else { @@ -1318,7 +1319,7 @@ int32_t doMinMaxHelper(SqlFunctionCtx* pCtx, int32_t isMinFunc) { if ((*val < pData[i]) ^ isMinFunc) { *val = pData[i]; if (pCtx->subsidiaries.num > 0) { - doCopyTupleData(pCtx, i, pCtx->pSrcBlock, &pBuf->tuplePos); + updateTupleData(pCtx, i, pCtx->pSrcBlock, &pBuf->tuplePos); } } } @@ -1337,7 +1338,7 @@ int32_t doMinMaxHelper(SqlFunctionCtx* pCtx, int32_t isMinFunc) { if (!pBuf->assign) { *val = pData[i]; if (pCtx->subsidiaries.num > 0) { - doSaveTupleData(pCtx, i, pCtx->pSrcBlock, &pBuf->tuplePos); + pBuf->tuplePos = saveTupleData(pCtx, i, pCtx->pSrcBlock); } pBuf->assign = true; } else { @@ -1349,7 +1350,7 @@ int32_t doMinMaxHelper(SqlFunctionCtx* pCtx, int32_t isMinFunc) { if ((*val < pData[i]) ^ isMinFunc) { *val = pData[i]; if (pCtx->subsidiaries.num > 0) { - doCopyTupleData(pCtx, i, pCtx->pSrcBlock, &pBuf->tuplePos); + updateTupleData(pCtx, i, pCtx->pSrcBlock, &pBuf->tuplePos); } } } @@ -1368,7 +1369,7 @@ int32_t doMinMaxHelper(SqlFunctionCtx* pCtx, int32_t isMinFunc) { if (!pBuf->assign) { *val = pData[i]; if (pCtx->subsidiaries.num > 0) { - doSaveTupleData(pCtx, i, pCtx->pSrcBlock, &pBuf->tuplePos); + pBuf->tuplePos = saveTupleData(pCtx, i, pCtx->pSrcBlock); } pBuf->assign = true; } else { @@ -1380,7 +1381,7 @@ int32_t doMinMaxHelper(SqlFunctionCtx* pCtx, int32_t isMinFunc) { if ((*val < pData[i]) ^ isMinFunc) { *val = pData[i]; if (pCtx->subsidiaries.num > 0) { - doCopyTupleData(pCtx, i, pCtx->pSrcBlock, &pBuf->tuplePos); + updateTupleData(pCtx, i, pCtx->pSrcBlock, &pBuf->tuplePos); } } } @@ -1401,7 +1402,7 @@ int32_t doMinMaxHelper(SqlFunctionCtx* pCtx, int32_t isMinFunc) { if (!pBuf->assign) { *val = pData[i]; if (pCtx->subsidiaries.num > 0) { - doSaveTupleData(pCtx, i, pCtx->pSrcBlock, &pBuf->tuplePos); + pBuf->tuplePos = saveTupleData(pCtx, i, pCtx->pSrcBlock); } pBuf->assign = true; } else { @@ -1413,7 +1414,7 @@ int32_t doMinMaxHelper(SqlFunctionCtx* pCtx, int32_t isMinFunc) { if ((*val < pData[i]) ^ isMinFunc) { *val = pData[i]; if (pCtx->subsidiaries.num > 0) { - doCopyTupleData(pCtx, i, pCtx->pSrcBlock, &pBuf->tuplePos); + updateTupleData(pCtx, i, pCtx->pSrcBlock, &pBuf->tuplePos); } } } @@ -1432,7 +1433,7 @@ int32_t doMinMaxHelper(SqlFunctionCtx* pCtx, int32_t isMinFunc) { if (!pBuf->assign) { *val = pData[i]; if (pCtx->subsidiaries.num > 0) { - doSaveTupleData(pCtx, i, pCtx->pSrcBlock, &pBuf->tuplePos); + pBuf->tuplePos = saveTupleData(pCtx, i, pCtx->pSrcBlock); } pBuf->assign = true; } else { @@ -1444,7 +1445,7 @@ int32_t doMinMaxHelper(SqlFunctionCtx* pCtx, int32_t isMinFunc) { if ((*val < pData[i]) ^ isMinFunc) { *val = pData[i]; if (pCtx->subsidiaries.num > 0) { - doCopyTupleData(pCtx, i, pCtx->pSrcBlock, &pBuf->tuplePos); + updateTupleData(pCtx, i, pCtx->pSrcBlock, &pBuf->tuplePos); } } } @@ -1463,7 +1464,7 @@ int32_t doMinMaxHelper(SqlFunctionCtx* pCtx, int32_t isMinFunc) { if (!pBuf->assign) { *val = pData[i]; if (pCtx->subsidiaries.num > 0) { - doSaveTupleData(pCtx, i, pCtx->pSrcBlock, &pBuf->tuplePos); + pBuf->tuplePos = saveTupleData(pCtx, i, pCtx->pSrcBlock); } pBuf->assign = true; } else { @@ -1475,7 +1476,7 @@ int32_t doMinMaxHelper(SqlFunctionCtx* pCtx, int32_t isMinFunc) { if ((*val < pData[i]) ^ isMinFunc) { *val = pData[i]; if (pCtx->subsidiaries.num > 0) { - doCopyTupleData(pCtx, i, pCtx->pSrcBlock, &pBuf->tuplePos); + updateTupleData(pCtx, i, pCtx->pSrcBlock, &pBuf->tuplePos); } } } @@ -1494,7 +1495,7 @@ int32_t doMinMaxHelper(SqlFunctionCtx* pCtx, int32_t isMinFunc) { if (!pBuf->assign) { *val = pData[i]; if (pCtx->subsidiaries.num > 0) { - doSaveTupleData(pCtx, i, pCtx->pSrcBlock, &pBuf->tuplePos); + pBuf->tuplePos = saveTupleData(pCtx, i, pCtx->pSrcBlock); } pBuf->assign = true; } else { @@ -1506,7 +1507,7 @@ int32_t doMinMaxHelper(SqlFunctionCtx* pCtx, int32_t isMinFunc) { if ((*val < pData[i]) ^ isMinFunc) { *val = pData[i]; if (pCtx->subsidiaries.num > 0) { - doCopyTupleData(pCtx, i, pCtx->pSrcBlock, &pBuf->tuplePos); + updateTupleData(pCtx, i, pCtx->pSrcBlock, &pBuf->tuplePos); } } } @@ -1526,7 +1527,7 @@ int32_t doMinMaxHelper(SqlFunctionCtx* pCtx, int32_t isMinFunc) { if (!pBuf->assign) { *val = pData[i]; if (pCtx->subsidiaries.num > 0) { - doSaveTupleData(pCtx, i, pCtx->pSrcBlock, &pBuf->tuplePos); + pBuf->tuplePos = saveTupleData(pCtx, i, pCtx->pSrcBlock); } pBuf->assign = true; } else { @@ -1538,7 +1539,7 @@ int32_t doMinMaxHelper(SqlFunctionCtx* pCtx, int32_t isMinFunc) { if ((*val < pData[i]) ^ isMinFunc) { *val = pData[i]; if (pCtx->subsidiaries.num > 0) { - doCopyTupleData(pCtx, i, pCtx->pSrcBlock, &pBuf->tuplePos); + updateTupleData(pCtx, i, pCtx->pSrcBlock, &pBuf->tuplePos); } } } @@ -1557,7 +1558,7 @@ int32_t doMinMaxHelper(SqlFunctionCtx* pCtx, int32_t isMinFunc) { if (!pBuf->assign) { *val = pData[i]; if (pCtx->subsidiaries.num > 0) { - doSaveTupleData(pCtx, i, pCtx->pSrcBlock, &pBuf->tuplePos); + pBuf->tuplePos = saveTupleData(pCtx, i, pCtx->pSrcBlock); } pBuf->assign = true; } else { @@ -1569,7 +1570,7 @@ int32_t doMinMaxHelper(SqlFunctionCtx* pCtx, int32_t isMinFunc) { if ((*val < pData[i]) ^ isMinFunc) { *val = pData[i]; if (pCtx->subsidiaries.num > 0) { - doCopyTupleData(pCtx, i, pCtx->pSrcBlock, &pBuf->tuplePos); + updateTupleData(pCtx, i, pCtx->pSrcBlock, &pBuf->tuplePos); } } } @@ -1580,7 +1581,7 @@ int32_t doMinMaxHelper(SqlFunctionCtx* pCtx, int32_t isMinFunc) { _min_max_over: if (numOfElems == 0 && pCtx->subsidiaries.num > 0 && !pBuf->nullTupleSaved ) { - doSaveTupleData(pCtx, pInput->startRowIndex, pCtx->pSrcBlock, &pBuf->nullTuplePos); + pBuf->nullTuplePos = saveTupleData(pCtx, pInput->startRowIndex, pCtx->pSrcBlock); pBuf->nullTupleSaved = true; } return numOfElems; @@ -1599,8 +1600,7 @@ int32_t maxFunction(SqlFunctionCtx* pCtx) { } static void setNullSelectivityValue(SqlFunctionCtx* pCtx, SSDataBlock* pBlock, int32_t rowIndex); - -static void setSelectivityValue(SqlFunctionCtx* pCtx, SSDataBlock* pBlock, const STuplePos* pTuplePos, int32_t rIndex); +static void setSelectivityValue(SqlFunctionCtx* pCtx, SSDataBlock* pBlock, const STuplePos* pTuplePos, int32_t rowIndex); int32_t minmaxFunctionFinalize(SqlFunctionCtx* pCtx, SSDataBlock* pBlock) { SResultRowEntryInfo* pEntryInfo = GET_RES_INFO(pCtx); @@ -1648,34 +1648,29 @@ void setSelectivityValue(SqlFunctionCtx* pCtx, SSDataBlock* pBlock, const STuple return; } - int32_t pageId = pTuplePos->pageId; - int32_t offset = pTuplePos->offset; + if (pCtx->saveHandle.pBuf != NULL) { + if (pTuplePos->pageId != -1) { + int32_t numOfCols = pCtx->subsidiaries.num; + const char* p = loadTupleData(pCtx, pTuplePos); - if (pTuplePos->pageId != -1) { - int32_t numOfCols = pCtx->subsidiaries.num; - SFilePage* pPage = getBufPage(pCtx->pBuf, pageId); + bool* nullList = (bool*)p; + char* pStart = (char*)(nullList + numOfCols * sizeof(bool)); - bool* nullList = (bool*)((char*)pPage + offset); - char* pStart = (char*)(nullList + numOfCols * sizeof(bool)); + // todo set the offset value to optimize the performance. + for (int32_t j = 0; j < numOfCols; ++j) { + SqlFunctionCtx* pc = pCtx->subsidiaries.pCtx[j]; + int32_t dstSlotId = pc->pExpr->base.resSchema.slotId; - // todo set the offset value to optimize the performance. - for (int32_t j = 0; j < pCtx->subsidiaries.num; ++j) { - SqlFunctionCtx* pc = pCtx->subsidiaries.pCtx[j]; - - SFunctParam* pFuncParam = &pc->pExpr->base.pParam[0]; - int32_t dstSlotId = pc->pExpr->base.resSchema.slotId; - - SColumnInfoData* pDstCol = taosArrayGet(pBlock->pDataBlock, dstSlotId); - ASSERT(pc->pExpr->base.resSchema.bytes == pDstCol->info.bytes); - if (nullList[j]) { - colDataAppendNULL(pDstCol, rowIndex); - } else { - colDataAppend(pDstCol, rowIndex, pStart, false); + SColumnInfoData* pDstCol = taosArrayGet(pBlock->pDataBlock, dstSlotId); + ASSERT(pc->pExpr->base.resSchema.bytes == pDstCol->info.bytes); + if (nullList[j]) { + colDataAppendNULL(pDstCol, rowIndex); + } else { + colDataAppend(pDstCol, rowIndex, pStart, false); + } + pStart += pDstCol->info.bytes; } - pStart += pDstCol->info.bytes; } - - releaseBufPage(pCtx->pBuf, pPage); } } @@ -2756,15 +2751,15 @@ static FORCE_INLINE TSKEY getRowPTs(SColumnInfoData* pTsColInfo, int32_t rowInde return *(TSKEY*)colDataGetData(pTsColInfo, rowIndex); } -static void saveTupleData(const SSDataBlock* pSrcBlock, int32_t rowIndex, SqlFunctionCtx* pCtx, SFirstLastRes* pInfo) { +static void firstlastSaveTupleData(const SSDataBlock* pSrcBlock, int32_t rowIndex, SqlFunctionCtx* pCtx, SFirstLastRes* pInfo) { if (pCtx->subsidiaries.num <= 0) { return; } if (!pInfo->hasResult) { - doSaveTupleData(pCtx, rowIndex, pSrcBlock, &pInfo->pos); + pInfo->pos = saveTupleData(pCtx, rowIndex, pSrcBlock); } else { - doCopyTupleData(pCtx, rowIndex, pSrcBlock, &pInfo->pos); + updateTupleData(pCtx, rowIndex, pSrcBlock, &pInfo->pos); } } @@ -2778,7 +2773,7 @@ static void doSaveCurrentVal(SqlFunctionCtx* pCtx, int32_t rowIndex, int64_t cur memcpy(pInfo->buf, pData, pInfo->bytes); pInfo->ts = currentTs; - saveTupleData(pCtx->pSrcBlock, rowIndex, pCtx, pInfo); + firstlastSaveTupleData(pCtx->pSrcBlock, rowIndex, pCtx, pInfo); pInfo->hasResult = true; } @@ -2982,7 +2977,7 @@ static void firstLastTransferInfo(SqlFunctionCtx* pCtx, SFirstLastRes* pInput, S pOutput->bytes = pInput->bytes; memcpy(pOutput->buf, pInput->buf, pOutput->bytes); - saveTupleData(pCtx->pSrcBlock, start, pCtx, pOutput); + firstlastSaveTupleData(pCtx->pSrcBlock, start, pCtx, pOutput); pOutput->hasResult = true; } @@ -3087,7 +3082,7 @@ static void doSaveLastrow(SqlFunctionCtx* pCtx, char* pData, int32_t rowIndex, i } pInfo->ts = cts; - saveTupleData(pCtx->pSrcBlock, rowIndex, pCtx, pInfo); + firstlastSaveTupleData(pCtx->pSrcBlock, rowIndex, pCtx, pInfo); pInfo->hasResult = true; } @@ -3420,7 +3415,7 @@ int32_t topFunction(SqlFunctionCtx* pCtx) { } if (numOfElems == 0 && pCtx->subsidiaries.num > 0 && !pRes->nullTupleSaved) { - doSaveTupleData(pCtx, pInput->startRowIndex, pCtx->pSrcBlock, &pRes->nullTuplePos); + pRes->nullTuplePos = saveTupleData(pCtx, pInput->startRowIndex, pCtx->pSrcBlock); pRes->nullTupleSaved = true; } return TSDB_CODE_SUCCESS; @@ -3448,7 +3443,7 @@ int32_t bottomFunction(SqlFunctionCtx* pCtx) { } if (numOfElems == 0 && pCtx->subsidiaries.num > 0 && !pRes->nullTupleSaved) { - doSaveTupleData(pCtx, pInput->startRowIndex, pCtx->pSrcBlock, &pRes->nullTuplePos); + pRes->nullTuplePos = saveTupleData(pCtx, pInput->startRowIndex, pCtx->pSrcBlock); pRes->nullTupleSaved = true; } @@ -3500,7 +3495,7 @@ void doAddIntoResult(SqlFunctionCtx* pCtx, void* pData, int32_t rowIndex, SSData // save the data of this tuple if (pCtx->subsidiaries.num > 0) { - doSaveTupleData(pCtx, rowIndex, pSrcBlock, &pItem->tuplePos); + pItem->tuplePos = saveTupleData(pCtx, rowIndex, pSrcBlock); } #ifdef BUF_PAGE_DEBUG qDebug("page_saveTuple i:%d, item:%p,pageId:%d, offset:%d\n", pEntryInfo->numOfRes, pItem, pItem->tuplePos.pageId, @@ -3524,7 +3519,7 @@ void doAddIntoResult(SqlFunctionCtx* pCtx, void* pData, int32_t rowIndex, SSData // save the data of this tuple by over writing the old data if (pCtx->subsidiaries.num > 0) { - doCopyTupleData(pCtx, rowIndex, pSrcBlock, &pItem->tuplePos); + updateTupleData(pCtx, rowIndex, pSrcBlock, &pItem->tuplePos); } #ifdef BUF_PAGE_DEBUG qDebug("page_copyTuple pageId:%d, offset:%d", pItem->tuplePos.pageId, pItem->tuplePos.offset); @@ -3541,38 +3536,13 @@ void doAddIntoResult(SqlFunctionCtx* pCtx, void* pData, int32_t rowIndex, SSData * |(n columns, one bit for each column)| src column #1| src column #2| * +------------------------------------+--------------+--------------+ */ -void doSaveTupleData(SqlFunctionCtx* pCtx, int32_t rowIndex, const SSDataBlock* pSrcBlock, STuplePos* pPos) { - SFilePage* pPage = NULL; +void* serializeTupleData(const SSDataBlock* pSrcBlock, int32_t rowIndex, SSubsidiaryResInfo* pSubsidiaryies, char* buf) { + char* nullList = buf; + char* pStart = (char*)(nullList + sizeof(bool) * pSubsidiaryies->num); - // todo refactor: move away - int32_t completeRowSize = pCtx->subsidiaries.num * sizeof(bool); - for (int32_t j = 0; j < pCtx->subsidiaries.num; ++j) { - SqlFunctionCtx* pc = pCtx->subsidiaries.pCtx[j]; - completeRowSize += pc->pExpr->base.resSchema.bytes; - } - - if (pCtx->curBufPage == -1) { - pPage = getNewBufPage(pCtx->pBuf, 0, &pCtx->curBufPage); - pPage->num = sizeof(SFilePage); - } else { - pPage = getBufPage(pCtx->pBuf, pCtx->curBufPage); - if (pPage->num + completeRowSize > getBufPageSize(pCtx->pBuf)) { - // current page is all used, let's prepare a new buffer page - releaseBufPage(pCtx->pBuf, pPage); - pPage = getNewBufPage(pCtx->pBuf, 0, &pCtx->curBufPage); - pPage->num = sizeof(SFilePage); - } - } - - pPos->pageId = pCtx->curBufPage; - pPos->offset = pPage->num; - - // keep the current row data, extract method int32_t offset = 0; - bool* nullList = (bool*)((char*)pPage + pPage->num); - char* pStart = (char*)(nullList + sizeof(bool) * pCtx->subsidiaries.num); - for (int32_t i = 0; i < pCtx->subsidiaries.num; ++i) { - SqlFunctionCtx* pc = pCtx->subsidiaries.pCtx[i]; + for (int32_t i = 0; i < pSubsidiaryies->num; ++i) { + SqlFunctionCtx* pc = pSubsidiaryies->pCtx[i]; SFunctParam* pFuncParam = &pc->pExpr->base.pParam[0]; int32_t srcSlotId = pFuncParam->pCol->slotId; @@ -3593,50 +3563,90 @@ void doSaveTupleData(SqlFunctionCtx* pCtx, int32_t rowIndex, const SSDataBlock* offset += pCol->info.bytes; } - pPage->num += completeRowSize; - - setBufPageDirty(pPage, true); - releaseBufPage(pCtx->pBuf, pPage); -#ifdef BUF_PAGE_DEBUG - qDebug("page_saveTuple pos:%p,pageId:%d, offset:%d\n", pPos, pPos->pageId, pPos->offset); -#endif + return buf; } -void doCopyTupleData(SqlFunctionCtx* pCtx, int32_t rowIndex, const SSDataBlock* pSrcBlock, STuplePos* pPos) { - SFilePage* pPage = getBufPage(pCtx->pBuf, pPos->pageId); +static STuplePos doSaveTupleData(SSerializeDataHandle* pHandle, const void* pBuf, size_t length) { + STuplePos p = {0}; + if (pHandle->pBuf != NULL) { + SFilePage* pPage = NULL; - int32_t numOfCols = pCtx->subsidiaries.num; - - bool* nullList = (bool*)((char*)pPage + pPos->offset); - char* pStart = (char*)(nullList + numOfCols * sizeof(bool)); - - int32_t offset = 0; - for (int32_t i = 0; i < numOfCols; ++i) { - SqlFunctionCtx* pc = pCtx->subsidiaries.pCtx[i]; - SFunctParam* pFuncParam = &pc->pExpr->base.pParam[0]; - int32_t srcSlotId = pFuncParam->pCol->slotId; - - SColumnInfoData* pCol = taosArrayGet(pSrcBlock->pDataBlock, srcSlotId); - if ((nullList[i] = colDataIsNull_s(pCol, rowIndex)) == true) { - offset += pCol->info.bytes; - continue; - } - - char* p = colDataGetData(pCol, rowIndex); - if (IS_VAR_DATA_TYPE(pCol->info.type)) { - memcpy(pStart + offset, p, (pCol->info.type == TSDB_DATA_TYPE_JSON) ? getJsonValueLen(p) : varDataTLen(p)); + if (pHandle->currentPage == -1) { + pPage = getNewBufPage(pHandle->pBuf, &pHandle->currentPage); + pPage->num = sizeof(SFilePage); } else { - memcpy(pStart + offset, p, pCol->info.bytes); + pPage = getBufPage(pHandle->pBuf, pHandle->currentPage); + if (pPage->num + length > getBufPageSize(pHandle->pBuf)) { + // current page is all used, let's prepare a new buffer page + releaseBufPage(pHandle->pBuf, pPage); + pPage = getNewBufPage(pHandle->pBuf, &pHandle->currentPage); + pPage->num = sizeof(SFilePage); + } } - offset += pCol->info.bytes; + p = (STuplePos) {.pageId = pHandle->currentPage, .offset = pPage->num}; + memcpy(pPage->data + pPage->num, pBuf, length); + + pPage->num += length; + setBufPageDirty(pPage, true); + releaseBufPage(pHandle->pBuf, pPage); + } else { + // other tuple save policy } - setBufPageDirty(pPage, true); - releaseBufPage(pCtx->pBuf, pPage); -#ifdef BUF_PAGE_DEBUG - qDebug("page_copyTuple pos:%p, pageId:%d, offset:%d", pPos, pPos->pageId, pPos->offset); -#endif + return p; +} + +STuplePos saveTupleData(SqlFunctionCtx* pCtx, int32_t rowIndex, const SSDataBlock* pSrcBlock) { + if (pCtx->subsidiaries.rowLen == 0) { + int32_t rowLen = 0; + for (int32_t j = 0; j < pCtx->subsidiaries.num; ++j) { + SqlFunctionCtx* pc = pCtx->subsidiaries.pCtx[j]; + rowLen += pc->pExpr->base.resSchema.bytes; + } + + pCtx->subsidiaries.rowLen = rowLen + pCtx->subsidiaries.num * sizeof(bool); + pCtx->subsidiaries.buf = taosMemoryMalloc(pCtx->subsidiaries.rowLen); + } + + char* buf = serializeTupleData(pSrcBlock, rowIndex, &pCtx->subsidiaries, pCtx->subsidiaries.buf); + return doSaveTupleData(&pCtx->saveHandle, buf, pCtx->subsidiaries.rowLen); +} + +static int32_t doUpdateTupleData(SSerializeDataHandle* pHandle, const void* pBuf, size_t length, STuplePos* pPos) { + if (pHandle->pBuf != NULL) { + SFilePage* pPage = getBufPage(pHandle->pBuf, pPos->pageId); + memcpy(pPage->data + pPos->offset, pBuf, length); + setBufPageDirty(pPage, true); + releaseBufPage(pHandle->pBuf, pPage); + } else { + + } + + return TSDB_CODE_SUCCESS; +} + +static int32_t updateTupleData(SqlFunctionCtx* pCtx, int32_t rowIndex, const SSDataBlock* pSrcBlock, STuplePos* pPos) { + int32_t rowLen = 0; + int32_t completeRowSize = rowLen + pCtx->subsidiaries.num * sizeof(bool); + char* buf = serializeTupleData(pSrcBlock, rowIndex, &pCtx->subsidiaries, pCtx->subsidiaries.buf); + doUpdateTupleData(&pCtx->saveHandle, buf, completeRowSize, pPos); + return TSDB_CODE_SUCCESS; +} + +static char* doLoadTupleData(SSerializeDataHandle* pHandle, const STuplePos* pPos) { + if (pHandle->pBuf != NULL) { + SFilePage* pPage = getBufPage(pHandle->pBuf, pPos->pageId); + char* p = pPage->data + pPos->offset; + releaseBufPage(pHandle->pBuf, pPage); + return p; + } else { + return NULL; + } +} + +static const char* loadTupleData(SqlFunctionCtx* pCtx, const STuplePos* pPos) { + return doLoadTupleData(&pCtx->saveHandle, pPos); } int32_t topBotFinalize(SqlFunctionCtx* pCtx, SSDataBlock* pBlock) { @@ -3788,8 +3798,6 @@ int32_t spreadFunction(SqlFunctionCtx* pCtx) { SColumnInfoData* pCol = pInput->pData[0]; int32_t start = pInput->startRowIndex; - int32_t numOfRows = pInput->numOfRows; - // check the valid data one by one for (int32_t i = start; i < pInput->numOfRows + start; ++i) { if (colDataIsNull_f(pCol->nullbitmap, i)) { @@ -4964,7 +4972,7 @@ static void doReservoirSample(SqlFunctionCtx* pCtx, SSampleInfo* pInfo, char* da if (pInfo->numSampled < pInfo->samples) { sampleAssignResult(pInfo, data, pInfo->numSampled); if (pCtx->subsidiaries.num > 0) { - doSaveTupleData(pCtx, index, pCtx->pSrcBlock, &pInfo->tuplePos[pInfo->numSampled]); + pInfo->tuplePos[pInfo->numSampled] = saveTupleData(pCtx, index, pCtx->pSrcBlock); } pInfo->numSampled++; } else { @@ -4972,7 +4980,7 @@ static void doReservoirSample(SqlFunctionCtx* pCtx, SSampleInfo* pInfo, char* da if (j < pInfo->samples) { sampleAssignResult(pInfo, data, j); if (pCtx->subsidiaries.num > 0) { - doCopyTupleData(pCtx, index, pCtx->pSrcBlock, &pInfo->tuplePos[j]); + updateTupleData(pCtx, index, pCtx->pSrcBlock, &pInfo->tuplePos[j]); } } } @@ -4995,7 +5003,7 @@ int32_t sampleFunction(SqlFunctionCtx* pCtx) { } if (pInfo->numSampled == 0 && pCtx->subsidiaries.num > 0 && !pInfo->nullTupleSaved) { - doSaveTupleData(pCtx, pInput->startRowIndex, pCtx->pSrcBlock, &pInfo->nullTuplePos); + pInfo->nullTuplePos = saveTupleData(pCtx, pInput->startRowIndex, pCtx->pSrcBlock); pInfo->nullTupleSaved = true; } diff --git a/source/libs/function/src/tpercentile.c b/source/libs/function/src/tpercentile.c index dbe0b6bb3a..4c58c0abe5 100644 --- a/source/libs/function/src/tpercentile.c +++ b/source/libs/function/src/tpercentile.c @@ -372,7 +372,7 @@ int32_t tMemBucketPut(tMemBucket *pBucket, const void *data, size_t size) { pPageIdList = pList; } - pSlot->info.data = getNewBufPage(pBucket->pBuffer, groupId, &pageId); + pSlot->info.data = getNewBufPage(pBucket->pBuffer, &pageId); pSlot->info.pageId = pageId; taosArrayPush(pPageIdList, &pageId); } diff --git a/source/util/src/tpagedbuf.c b/source/util/src/tpagedbuf.c index 4d5532b9a6..2767fed937 100644 --- a/source/util/src/tpagedbuf.c +++ b/source/util/src/tpagedbuf.c @@ -371,7 +371,7 @@ int32_t createDiskbasedBuf(SDiskbasedBuf** pBuf, int32_t pagesize, int32_t inMem return TSDB_CODE_SUCCESS; } -void* getNewBufPage(SDiskbasedBuf* pBuf, int32_t groupId, int32_t* pageId) { +void* getNewBufPage(SDiskbasedBuf* pBuf, int32_t* pageId) { pBuf->statis.getPages += 1; char* availablePage = NULL; diff --git a/source/util/test/pageBufferTest.cpp b/source/util/test/pageBufferTest.cpp index eaf198a483..1a057c5875 100644 --- a/source/util/test/pageBufferTest.cpp +++ b/source/util/test/pageBufferTest.cpp @@ -18,7 +18,7 @@ void simpleTest() { int32_t pageId = 0; int32_t groupId = 0; - SFilePage* pBufPage = static_cast(getNewBufPage(pBuf, groupId, &pageId)); + SFilePage* pBufPage = static_cast(getNewBufPage(pBuf, &pageId)); ASSERT_TRUE(pBufPage != NULL); ASSERT_EQ(getTotalBufSize(pBuf), 1024); @@ -29,26 +29,26 @@ void simpleTest() { releaseBufPage(pBuf, pBufPage); - SFilePage* pBufPage1 = static_cast(getNewBufPage(pBuf, groupId, &pageId)); + SFilePage* pBufPage1 = static_cast(getNewBufPage(pBuf, &pageId)); SFilePage* t = static_cast(getBufPage(pBuf, pageId)); ASSERT_TRUE(t == pBufPage1); - SFilePage* pBufPage2 = static_cast(getNewBufPage(pBuf, groupId, &pageId)); + SFilePage* pBufPage2 = static_cast(getNewBufPage(pBuf, &pageId)); SFilePage* t1 = static_cast(getBufPage(pBuf, pageId)); ASSERT_TRUE(t1 == pBufPage2); - SFilePage* pBufPage3 = static_cast(getNewBufPage(pBuf, groupId, &pageId)); + SFilePage* pBufPage3 = static_cast(getNewBufPage(pBuf, &pageId)); SFilePage* t2 = static_cast(getBufPage(pBuf, pageId)); ASSERT_TRUE(t2 == pBufPage3); - SFilePage* pBufPage4 = static_cast(getNewBufPage(pBuf, groupId, &pageId)); + SFilePage* pBufPage4 = static_cast(getNewBufPage(pBuf, &pageId)); SFilePage* t3 = static_cast(getBufPage(pBuf, pageId)); ASSERT_TRUE(t3 == pBufPage4); releaseBufPage(pBuf, pBufPage2); - SFilePage* pBufPage5 = static_cast(getNewBufPage(pBuf, groupId, &pageId)); + SFilePage* pBufPage5 = static_cast(getNewBufPage(pBuf, &pageId)); SFilePage* t4 = static_cast(getBufPage(pBuf, pageId)); ASSERT_TRUE(t4 == pBufPage5); @@ -64,7 +64,7 @@ void writeDownTest() { int32_t groupId = 0; int32_t nx = 12345; - SFilePage* pBufPage = static_cast(getNewBufPage(pBuf, groupId, &pageId)); + SFilePage* pBufPage = static_cast(getNewBufPage(pBuf, &pageId)); ASSERT_TRUE(pBufPage != NULL); *(int32_t*)(pBufPage->data) = nx; @@ -73,22 +73,22 @@ void writeDownTest() { setBufPageDirty(pBufPage, true); releaseBufPage(pBuf, pBufPage); - SFilePage* pBufPage1 = static_cast(getNewBufPage(pBuf, groupId, &pageId)); + SFilePage* pBufPage1 = static_cast(getNewBufPage(pBuf, &pageId)); SFilePage* t1 = static_cast(getBufPage(pBuf, pageId)); ASSERT_TRUE(t1 == pBufPage1); ASSERT_TRUE(pageId == 1); - SFilePage* pBufPage2 = static_cast(getNewBufPage(pBuf, groupId, &pageId)); + SFilePage* pBufPage2 = static_cast(getNewBufPage(pBuf, &pageId)); SFilePage* t2 = static_cast(getBufPage(pBuf, pageId)); ASSERT_TRUE(t2 == pBufPage2); ASSERT_TRUE(pageId == 2); - SFilePage* pBufPage3 = static_cast(getNewBufPage(pBuf, groupId, &pageId)); + SFilePage* pBufPage3 = static_cast(getNewBufPage(pBuf, &pageId)); SFilePage* t3 = static_cast(getBufPage(pBuf, pageId)); ASSERT_TRUE(t3 == pBufPage3); ASSERT_TRUE(pageId == 3); - SFilePage* pBufPage4 = static_cast(getNewBufPage(pBuf, groupId, &pageId)); + SFilePage* pBufPage4 = static_cast(getNewBufPage(pBuf, &pageId)); SFilePage* t4 = static_cast(getBufPage(pBuf, pageId)); ASSERT_TRUE(t4 == pBufPage4); ASSERT_TRUE(pageId == 4); @@ -113,32 +113,32 @@ void recyclePageTest() { int32_t groupId = 0; int32_t nx = 12345; - SFilePage* pBufPage = static_cast(getNewBufPage(pBuf, groupId, &pageId)); + SFilePage* pBufPage = static_cast(getNewBufPage(pBuf, &pageId)); ASSERT_TRUE(pBufPage != NULL); releaseBufPage(pBuf, pBufPage); - SFilePage* pBufPage1 = static_cast(getNewBufPage(pBuf, groupId, &pageId)); + SFilePage* pBufPage1 = static_cast(getNewBufPage(pBuf, &pageId)); SFilePage* t1 = static_cast(getBufPage(pBuf, pageId)); ASSERT_TRUE(t1 == pBufPage1); ASSERT_TRUE(pageId == 1); - SFilePage* pBufPage2 = static_cast(getNewBufPage(pBuf, groupId, &pageId)); + SFilePage* pBufPage2 = static_cast(getNewBufPage(pBuf, &pageId)); SFilePage* t2 = static_cast(getBufPage(pBuf, pageId)); ASSERT_TRUE(t2 == pBufPage2); ASSERT_TRUE(pageId == 2); - SFilePage* pBufPage3 = static_cast(getNewBufPage(pBuf, groupId, &pageId)); + SFilePage* pBufPage3 = static_cast(getNewBufPage(pBuf, &pageId)); SFilePage* t3 = static_cast(getBufPage(pBuf, pageId)); ASSERT_TRUE(t3 == pBufPage3); ASSERT_TRUE(pageId == 3); - SFilePage* pBufPage4 = static_cast(getNewBufPage(pBuf, groupId, &pageId)); + SFilePage* pBufPage4 = static_cast(getNewBufPage(pBuf, &pageId)); SFilePage* t4 = static_cast(getBufPage(pBuf, pageId)); ASSERT_TRUE(t4 == pBufPage4); ASSERT_TRUE(pageId == 4); releaseBufPage(pBuf, t4); - SFilePage* pBufPage5 = static_cast(getNewBufPage(pBuf, groupId, &pageId)); + SFilePage* pBufPage5 = static_cast(getNewBufPage(pBuf, &pageId)); SFilePage* t5 = static_cast(getBufPage(pBuf, pageId)); ASSERT_TRUE(t5 == pBufPage5); ASSERT_TRUE(pageId == 5); From 2be26326001036347d0a8c8688bc6e13c3703f03 Mon Sep 17 00:00:00 2001 From: tomchon Date: Mon, 29 Aug 2022 19:02:59 +0800 Subject: [PATCH 45/76] test: modify checkpackages scritps --- packaging/MPtestJenkinsfile | 8 ++++---- packaging/testpackage.sh | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packaging/MPtestJenkinsfile b/packaging/MPtestJenkinsfile index 5e259d56be..a003bf354c 100644 --- a/packaging/MPtestJenkinsfile +++ b/packaging/MPtestJenkinsfile @@ -107,7 +107,7 @@ pipeline { stage('ubuntu16') { agent{label " ubuntu16 "} steps { - timeout(time: 10, unit: 'MINUTES'){ + timeout(time: 30, unit: 'MINUTES'){ sync_source("${BRANCH_NAME}") sh ''' cd ${TDENGINE_ROOT_DIR}/packaging @@ -130,7 +130,7 @@ pipeline { stage('ubuntu18') { agent{label " ubuntu18 "} steps { - timeout(time: 10, unit: 'MINUTES'){ + timeout(time: 30, unit: 'MINUTES'){ sync_source("${BRANCH_NAME}") sh ''' cd ${TDENGINE_ROOT_DIR}/packaging @@ -153,7 +153,7 @@ pipeline { stage('centos7') { agent{label " centos7_9 "} steps { - timeout(time: 10, unit: 'MINUTES'){ + timeout(time: 30, unit: 'MINUTES'){ sync_source("${BRANCH_NAME}") sh ''' cd ${TDENGINE_ROOT_DIR}/packaging @@ -176,7 +176,7 @@ pipeline { stage('centos8') { agent{label " centos8_3 "} steps { - timeout(time: 10, unit: 'MINUTES'){ + timeout(time: 30, unit: 'MINUTES'){ sync_source("${BRANCH_NAME}") sh ''' cd ${TDENGINE_ROOT_DIR}/packaging diff --git a/packaging/testpackage.sh b/packaging/testpackage.sh index 669b0c9e1e..054c24eb5d 100755 --- a/packaging/testpackage.sh +++ b/packaging/testpackage.sh @@ -83,8 +83,10 @@ wget https://www.taosdata.com/assets-download/3.0/${originPackageName} if [[ ${packgeName} =~ "deb" ]];then + cd ${installPath} echo "dpkg ${packgeName}" && dpkg -i ${packgeName} elif [[ ${packgeName} =~ "rpm" ]];then + cd ${installPath} echo "rpm ${packgeName}" && rpm -ivh ${packgeName} elif [[ ${packgeName} =~ "tar" ]];then echo "tar ${packgeName}" && tar -xvf ${packgeName} From 1a67ca34b9803f0f7534d0d873666f43d6f5eb00 Mon Sep 17 00:00:00 2001 From: yihaoDeng Date: Mon, 29 Aug 2022 19:14:12 +0800 Subject: [PATCH 46/76] avoid deadlock --- source/libs/transport/src/thttp.c | 2 + source/libs/transport/src/transCli.c | 182 ++++++++++++++++----------- source/libs/transport/src/transSvr.c | 3 +- 3 files changed, 113 insertions(+), 74 deletions(-) diff --git a/source/libs/transport/src/thttp.c b/source/libs/transport/src/thttp.c index 386ea95dd7..e880a5abdb 100644 --- a/source/libs/transport/src/thttp.c +++ b/source/libs/transport/src/thttp.c @@ -155,6 +155,8 @@ static void clientSentCb(uv_write_t* req, int32_t status) { if (status != 0) { terrno = TAOS_SYSTEM_ERROR(status); uError("http-report failed to send data %s", uv_strerror(status)); + uv_close((uv_handle_t*)&cli->tcp, clientCloseCb); + return; } else { uTrace("http-report succ to send data"); } diff --git a/source/libs/transport/src/transCli.c b/source/libs/transport/src/transCli.c index 7052b0b915..41688c7330 100644 --- a/source/libs/transport/src/transCli.c +++ b/source/libs/transport/src/transCli.c @@ -16,7 +16,7 @@ #include "transComm.h" typedef struct SConnList { - queue conn; + queue conns; int32_t size; } SConnList; @@ -107,11 +107,11 @@ static void doCloseIdleConn(void* param); static void cliReadTimeoutCb(uv_timer_t* handle); // register timer in each thread to clear expire conn // static void cliTimeoutCb(uv_timer_t* handle); -// alloc buf for recv +// alloc buffer for recv static void cliAllocRecvBufferCb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf); -// callback after read nbytes from socket +// callback after recv nbytes from socket static void cliRecvCb(uv_stream_t* cli, ssize_t nread, const uv_buf_t* buf); -// callback after write data to socket +// callback after send data to socket static void cliSendCb(uv_write_t* req, int status); // callback after conn to server static void cliConnCb(uv_connect_t* req, int status); @@ -129,19 +129,14 @@ static SCliConn* cliCreateConn(SCliThrd* thrd); static void cliDestroyConn(SCliConn* pConn, bool clear /*clear tcp handle or not*/); static void cliDestroy(uv_handle_t* handle); static void cliSend(SCliConn* pConn); +static void cliDestroyConnMsgs(SCliConn* conn, bool destroy); -static bool cliIsEpsetUpdated(int32_t code, STransConnCtx* pCtx) { - if (code != 0) return false; - if (pCtx->retryCnt == 0) return false; - if (transEpSetIsEqual(&pCtx->epSet, &pCtx->origEpSet)) return false; - return true; -} +// cli util func +static bool cliIsEpsetUpdated(int32_t code, STransConnCtx* pCtx); +static void cliMayCvtFqdnToIp(SEpSet* pEpSet, SCvtAddr* pCvtAddr); + +static int32_t cliBuildExceptResp(SCliMsg* pMsg, STransMsg* resp); -void cliMayCvtFqdnToIp(SEpSet* pEpSet, SCvtAddr* pCvtAddr); -/* - * set TCP connection timeout per-socket level - */ -static int cliCreateSocket(); // process data read from server, add decompress etc later static void cliHandleResp(SCliConn* conn); // handle except about conn @@ -169,15 +164,14 @@ static void destroyThrdObj(SCliThrd* pThrd); static void cliWalkCb(uv_handle_t* handle, void* arg); static void cliReleaseUnfinishedMsg(SCliConn* conn) { - SCliMsg* pMsg = NULL; for (int i = 0; i < transQueueSize(&conn->cliMsgs); i++) { - pMsg = transQueueGet(&conn->cliMsgs, i); - if (pMsg != NULL && pMsg->ctx != NULL) { - if (conn->ctx.freeFunc != NULL) { - conn->ctx.freeFunc(pMsg->ctx->ahandle); + SCliMsg* msg = transQueueGet(&conn->cliMsgs, i); + if (msg != NULL && msg->ctx != NULL) { + if (conn->ctx.freeFunc != NULL && msg->ctx->ahandle != NULL) { + conn->ctx.freeFunc(msg->ctx->ahandle); } } - destroyCmsg(pMsg); + destroyCmsg(msg); } } #define CLI_RELEASE_UV(loop) \ @@ -217,8 +211,10 @@ static void cliReleaseUnfinishedMsg(SCliConn* conn) { } \ if (i == sz) { \ pMsg = NULL; \ + tDebug("msg not found, %" PRIu64 "", ahandle); \ } else { \ pMsg = transQueueRm(&conn->cliMsgs, i); \ + tDebug("msg found, %" PRIu64 "", ahandle); \ } \ } while (0) #define CONN_GET_NEXT_SENDMSG(conn) \ @@ -470,8 +466,8 @@ void* createConnPool(int size) { void* destroyConnPool(void* pool) { SConnList* connList = taosHashIterate((SHashObj*)pool, NULL); while (connList != NULL) { - while (!QUEUE_IS_EMPTY(&connList->conn)) { - queue* h = QUEUE_HEAD(&connList->conn); + while (!QUEUE_IS_EMPTY(&connList->conns)) { + queue* h = QUEUE_HEAD(&connList->conns); SCliConn* c = QUEUE_DATA(h, SCliConn, q); cliDestroyConn(c, true); } @@ -484,21 +480,21 @@ void* destroyConnPool(void* pool) { static SCliConn* getConnFromPool(void* pool, char* ip, uint32_t port) { char key[32] = {0}; CONN_CONSTRUCT_HASH_KEY(key, ip, port); - SHashObj* pPool = pool; - SConnList* plist = taosHashGet(pPool, key, strlen(key)); + + SConnList* plist = taosHashGet((SHashObj*)pool, key, strlen(key)); if (plist == NULL) { SConnList list = {0}; - taosHashPut(pPool, key, strlen(key), (void*)&list, sizeof(list)); - plist = taosHashGet(pPool, key, strlen(key)); - QUEUE_INIT(&plist->conn); + taosHashPut((SHashObj*)pool, key, strlen(key), (void*)&list, sizeof(list)); + plist = taosHashGet((SHashObj*)pool, key, strlen(key)); + QUEUE_INIT(&plist->conns); } - if (QUEUE_IS_EMPTY(&plist->conn)) { + if (QUEUE_IS_EMPTY(&plist->conns)) { return NULL; } plist->size -= 1; - queue* h = QUEUE_HEAD(&plist->conn); + queue* h = QUEUE_HEAD(&plist->conns); SCliConn* conn = QUEUE_DATA(h, SCliConn, q); conn->status = ConnNormal; QUEUE_REMOVE(&conn->q); @@ -514,22 +510,21 @@ static void addConnToPool(void* pool, SCliConn* conn) { if (conn->status == ConnInPool) { return; } - SCliThrd* thrd = conn->hostThrd; - CONN_HANDLE_THREAD_QUIT(thrd); - allocConnRef(conn, true); + SCliThrd* thrd = conn->hostThrd; if (conn->timer != NULL) { uv_timer_stop(conn->timer); taosArrayPush(thrd->timerList, &conn->timer); conn->timer->data = NULL; conn->timer = NULL; } + if (T_REF_VAL_GET(conn) > 1) { + transUnrefCliHandle(conn); + } + + cliDestroyConnMsgs(conn, false); - STrans* pTransInst = thrd->pTransInst; - cliReleaseUnfinishedMsg(conn); - transQueueClear(&conn->cliMsgs); - transCtxCleanup(&conn->ctx); conn->status = ConnInPool; if (conn->list == NULL) { @@ -540,18 +535,15 @@ static void addConnToPool(void* pool, SCliConn* conn) { } else { tTrace("%s conn %p added to conn pool, read buf cap:%d", CONN_GET_INST_LABEL(conn), conn, conn->readBuf.cap); } - assert(conn->list != NULL); - QUEUE_INIT(&conn->q); - QUEUE_PUSH(&conn->list->conn, &conn->q); + QUEUE_PUSH(&conn->list->conns, &conn->q); conn->list->size += 1; - conn->task = NULL; - assert(!QUEUE_IS_EMPTY(&conn->list->conn)); - if (conn->list->size >= 50) { STaskArg* arg = taosMemoryCalloc(1, sizeof(STaskArg)); arg->param1 = conn; arg->param2 = thrd; + + STrans* pTransInst = thrd->pTransInst; conn->task = transDQSched(thrd->timeoutQueue, doCloseIdleConn, arg, CONN_PERSIST_TIME(pTransInst->idleTime)); } } @@ -691,11 +683,10 @@ static void cliDestroy(uv_handle_t* handle) { transRemoveExHandle(transGetRefMgt(), conn->refId); taosMemoryFree(conn->ip); - conn->stream->data = NULL; taosMemoryFree(conn->stream); - transCtxCleanup(&conn->ctx); - cliReleaseUnfinishedMsg(conn); - transQueueDestroy(&conn->cliMsgs); + + cliDestroyConnMsgs(conn, true); + tTrace("%s conn %p destroy successfully", CONN_GET_INST_LABEL(conn), conn); transReqQueueClear(&conn->wreqQueue); transDestroyBuffer(&conn->readBuf); @@ -738,8 +729,6 @@ static void cliSendCb(uv_write_t* req, int status) { } void cliSend(SCliConn* pConn) { - CONN_HANDLE_BROKEN(pConn); - assert(!transQueueEmpty(&pConn->cliMsgs)); SCliMsg* pCliMsg = NULL; @@ -756,8 +745,8 @@ void cliSend(SCliConn* pConn) { pMsg->pCont = (void*)rpcMallocCont(0); pMsg->contLen = 0; } - int msgLen = transMsgLenFromCont(pMsg->contLen); + int msgLen = transMsgLenFromCont(pMsg->contLen); STransMsgHead* pHead = transHeadFromCont(pMsg->pCont); pHead->ahandle = pCtx != NULL ? (uint64_t)pCtx->ahandle : 0; pHead->noResp = REQUEST_NO_RESP(pMsg) ? 1 : 0; @@ -769,8 +758,6 @@ void cliSend(SCliConn* pConn) { pHead->traceId = pMsg->info.traceId; pHead->magicNum = htonl(TRANS_MAGIC_NUM); - uv_buf_t wb = uv_buf_init((char*)pHead, msgLen); - STraceId* trace = &pMsg->info.traceId; tGDebug("%s conn %p %s is sent to %s, local info %s, len:%d", CONN_GET_INST_LABEL(pConn), pConn, TMSG_INFO(pHead->msgType), pConn->dst, pConn->src, pMsg->contLen); @@ -792,6 +779,8 @@ void cliSend(SCliConn* pConn) { tGTrace("%s conn %p start timer for msg:%s", CONN_GET_INST_LABEL(pConn), pConn, TMSG_INFO(pMsg->msgType)); uv_timer_start((uv_timer_t*)pConn->timer, cliReadTimeoutCb, TRANS_READ_TIMEOUT, 0); } + + uv_buf_t wb = uv_buf_init((char*)pHead, msgLen); uv_write_t* req = transReqQueuePush(&pConn->wreqQueue); uv_write(req, (uv_stream_t*)pConn->stream, &wb, 1, cliSendCb); return; @@ -807,7 +796,6 @@ void cliConnCb(uv_connect_t* req, int status) { cliHandleExcept(pConn); return; } - // int addrlen = sizeof(pConn->addr); struct sockaddr peername, sockname; int addrlen = sizeof(peername); @@ -840,7 +828,7 @@ static void cliHandleRelease(SCliMsg* pMsg, SCliThrd* pThrd) { int64_t refId = (int64_t)(pMsg->msg.info.handle); SExHandle* exh = transAcquireExHandle(transGetRefMgt(), refId); if (exh == NULL) { - tDebug("%" PRId64 " already release", refId); + tDebug("%" PRId64 " already released", refId); destroyCmsg(pMsg); return; } @@ -856,6 +844,9 @@ static void cliHandleRelease(SCliMsg* pMsg, SCliThrd* pThrd) { return; } cliSend(conn); + } else { + tError("%s conn %p already released", CONN_GET_INST_LABEL(conn), conn); + destroyCmsg(pMsg); } } static void cliHandleUpdate(SCliMsg* pMsg, SCliThrd* pThrd) { @@ -905,6 +896,27 @@ void cliMayCvtFqdnToIp(SEpSet* pEpSet, SCvtAddr* pCvtAddr) { } } } + +bool cliIsEpsetUpdated(int32_t code, STransConnCtx* pCtx) { + if (code != 0) return false; + if (pCtx->retryCnt == 0) return false; + if (transEpSetIsEqual(&pCtx->epSet, &pCtx->origEpSet)) return false; + return true; +} + +int32_t cliBuildExceptResp(SCliMsg* pMsg, STransMsg* pResp) { + if (pMsg == NULL) return -1; + + memset(pResp, 0, sizeof(STransMsg)); + + pResp->code = TSDB_CODE_RPC_BROKEN_LINK; + pResp->msgType = pMsg->msg.msgType + 1; + pResp->info.ahandle = pMsg->ctx ? pMsg->ctx->ahandle : NULL; + pResp->info.traceId = pMsg->msg.info.traceId; + + return 0; +} + void cliHandleReq(SCliMsg* pMsg, SCliThrd* pThrd) { STrans* pTransInst = pThrd->pTransInst; STransConnCtx* pCtx = pMsg->ctx; @@ -920,13 +932,8 @@ void cliHandleReq(SCliMsg* pMsg, SCliThrd* pThrd) { SCliConn* conn = cliGetConn(pMsg, pThrd, &ignore); if (ignore == true) { // persist conn already release by server - STransMsg resp = {0}; - resp.code = TSDB_CODE_RPC_BROKEN_LINK; - resp.msgType = pMsg->msg.msgType + 1; - - resp.info.ahandle = pMsg && pMsg->ctx ? pMsg->ctx->ahandle : NULL; - resp.info.traceId = pMsg->msg.info.traceId; - + STransMsg resp; + cliBuildExceptResp(pMsg, &resp); pTransInst->cfp(pTransInst->parent, &resp, NULL); destroyCmsg(pMsg); return; @@ -991,9 +998,6 @@ static void cliAsyncCb(uv_async_t* handle) { QUEUE_REMOVE(h); SCliMsg* pMsg = QUEUE_DATA(h, SCliMsg, q); - if (pMsg == NULL) { - continue; - } (*cliAsyncHandle[pMsg->type])(pMsg, pThrd); count++; } @@ -1035,24 +1039,58 @@ static void cliPrepareCb(uv_prepare_t* handle) { if (thrd->stopMsg != NULL) cliHandleQuit(thrd->stopMsg, thrd); } +void cliDestroyConnMsgs(SCliConn* conn, bool destroy) { + transCtxCleanup(&conn->ctx); + cliReleaseUnfinishedMsg(conn); + if (destroy == 1) { + transQueueDestroy(&conn->cliMsgs); + } else { + transQueueClear(&conn->cliMsgs); + } +} + +void cliIteraConnMsgs(SCliConn* conn) { + SCliThrd* pThrd = conn->hostThrd; + STrans* pTransInst = pThrd->pTransInst; + + for (int i = 0; i < transQueueSize(&conn->cliMsgs); i++) { + SCliMsg* cmsg = transQueueGet(&conn->cliMsgs, i); + if (cmsg->type == Release || REQUEST_NO_RESP(&cmsg->msg) || cmsg->msg.msgType == TDMT_SCH_DROP_TASK) { + continue; + } + + STransMsg resp = {0}; + if (-1 == cliBuildExceptResp(cmsg, &resp)) { + continue; + } + pTransInst->cfp(pTransInst->parent, &resp, NULL); + + cmsg->ctx->ahandle = NULL; + } +} bool cliRecvReleaseReq(SCliConn* conn, STransMsgHead* pHead) { if (pHead->release == 1 && (pHead->msgLen) == sizeof(*pHead)) { uint64_t ahandle = pHead->ahandle; + tDebug("ahandle = %" PRIu64 "", ahandle); SCliMsg* pMsg = NULL; CONN_GET_MSGCTX_BY_AHANDLE(conn, ahandle); + transClearBuffer(&conn->readBuf); transFreeMsg(transContFromHead((char*)pHead)); - if (transQueueSize(&conn->cliMsgs) > 0 && ahandle == 0) { - SCliMsg* cliMsg = transQueueGet(&conn->cliMsgs, 0); - if (cliMsg->type == Release) return true; + + for (int i = 0; ahandle == 0 && i < transQueueSize(&conn->cliMsgs); i++) { + SCliMsg* cliMsg = transQueueGet(&conn->cliMsgs, i); + if (cliMsg->type == Release) { + assert(pMsg == NULL); + return true; + } } + + cliIteraConnMsgs(conn); + tDebug("%s conn %p receive release request, refId:%" PRId64 "", CONN_GET_INST_LABEL(conn), conn, conn->refId); - if (T_REF_VAL_GET(conn) > 1) { - transUnrefCliHandle(conn); - } destroyCmsg(pMsg); - cliReleaseUnfinishedMsg(conn); - transQueueClear(&conn->cliMsgs); + addConnToPool(((SCliThrd*)conn->hostThrd)->pool, conn); return true; } diff --git a/source/libs/transport/src/transSvr.c b/source/libs/transport/src/transSvr.c index 207b967923..46046b2a95 100644 --- a/source/libs/transport/src/transSvr.c +++ b/source/libs/transport/src/transSvr.c @@ -492,7 +492,6 @@ void uvWorkerAsyncCb(uv_async_t* handle) { // release handle to rpc init if (msg->type == Quit) { (*transAsyncHandle[msg->type])(msg, pThrd); - continue; } else { STransMsg transMsg = msg->msg; @@ -771,7 +770,7 @@ static bool addHandleToWorkloop(SWorkThrd* pThrd, char* pipeName) { // conn set QUEUE_INIT(&pThrd->conn); - pThrd->asyncPool = transAsyncPoolCreate(pThrd->loop, 1, pThrd, uvWorkerAsyncCb); + pThrd->asyncPool = transAsyncPoolCreate(pThrd->loop, 5, pThrd, uvWorkerAsyncCb); uv_pipe_connect(&pThrd->connect_req, pThrd->pipe, pipeName, uvOnPipeConnectionCb); // uv_read_start((uv_stream_t*)pThrd->pipe, uvAllocConnBufferCb, uvOnConnectionCb); return true; From d2ed6ff0e8964d3bbb05bf09a1e95053a18f0bde Mon Sep 17 00:00:00 2001 From: Haojun Liao Date: Mon, 29 Aug 2022 22:42:15 +0800 Subject: [PATCH 47/76] fix(query): set correct page buffer id. --- source/libs/executor/inc/executorimpl.h | 12 ++++++---- source/libs/executor/src/executorimpl.c | 24 ++++++++----------- source/libs/executor/src/timewindowoperator.c | 2 +- 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/source/libs/executor/inc/executorimpl.h b/source/libs/executor/inc/executorimpl.h index 05bdc39701..594955d469 100644 --- a/source/libs/executor/inc/executorimpl.h +++ b/source/libs/executor/inc/executorimpl.h @@ -297,10 +297,11 @@ enum { }; typedef struct SAggSupporter { - SHashObj* pResultRowHashTable; // quick locate the window object for each result - char* keyBuf; // window key buffer - SDiskbasedBuf* pResultBuf; // query result buffer based on blocked-wised disk file - int32_t resultRowSize; // the result buffer size for each result row, with the meta data size for each row + SHashObj* pResultRowHashTable; // quick locate the window object for each result + char* keyBuf; // window key buffer + SDiskbasedBuf* pResultBuf; // query result buffer based on blocked-wised disk file + int32_t resultRowSize; // the result buffer size for each result row, with the meta data size for each row + int32_t currentPageId; // current write page id } SAggSupporter; typedef struct { @@ -429,6 +430,7 @@ typedef struct SStreamAggSupporter { char* pKeyBuf; // window key buffer SDiskbasedBuf* pResultBuf; // query result buffer based on blocked-wised disk file int32_t resultRowSize; // the result buffer size for each result row, with the meta data size for each row + int32_t currentPageId; // buffer page that is active SSDataBlock* pScanBlock; } SStreamAggSupporter; @@ -991,7 +993,7 @@ int32_t getNumOfRowsInTimeWindow(SDataBlockInfo* pDataBlockInfo, TSKEY* pPrimary int32_t binarySearchForKey(char* pValue, int num, TSKEY key, int order); int32_t initStreamAggSupporter(SStreamAggSupporter* pSup, const char* pKey, SqlFunctionCtx* pCtx, int32_t numOfOutput, int32_t size); -SResultRow* getNewResultRow(SDiskbasedBuf* pResultBuf, int64_t tableGroupId, int32_t interBufSize); +SResultRow* getNewResultRow(SDiskbasedBuf* pResultBuf, int32_t* currentPageId, int32_t interBufSize); SResultWindowInfo* getSessionTimeWindow(SStreamAggSupporter* pAggSup, TSKEY startTs, TSKEY endTs, uint64_t groupId, int64_t gap, int32_t* pIndex); SResultWindowInfo* getCurSessionWindow(SStreamAggSupporter* pAggSup, TSKEY startTs, diff --git a/source/libs/executor/src/executorimpl.c b/source/libs/executor/src/executorimpl.c index 9b102de5bf..30d7283264 100644 --- a/source/libs/executor/src/executorimpl.c +++ b/source/libs/executor/src/executorimpl.c @@ -179,24 +179,21 @@ static bool chkResultRowFromKey(STaskRuntimeEnv* pRuntimeEnv, SResultRowInfo* pR } #endif -SResultRow* getNewResultRow(SDiskbasedBuf* pResultBuf, int64_t tableGroupId, int32_t interBufSize) { +SResultRow* getNewResultRow(SDiskbasedBuf* pResultBuf, int32_t* currentPageId, int32_t interBufSize) { SFilePage* pData = NULL; // in the first scan, new space needed for results int32_t pageId = -1; - SIDList list = getDataBufPagesIdList(pResultBuf); - - if (taosArrayGetSize(list) == 0) { + if (*currentPageId == -1) { pData = getNewBufPage(pResultBuf, &pageId); pData->num = sizeof(SFilePage); } else { - SPageInfo* pi = getLastPageInfo(list); - pData = getBufPage(pResultBuf, getPageId(pi)); - pageId = getPageId(pi); + pData = getBufPage(pResultBuf, *currentPageId); + pageId = *currentPageId; if (pData->num + interBufSize > getBufPageSize(pResultBuf)) { // release current page first, and prepare the next one - releaseBufPageInfo(pResultBuf, pi); + releaseBufPage(pResultBuf, pData); pData = getNewBufPage(pResultBuf, &pageId); if (pData != NULL) { @@ -215,9 +212,9 @@ SResultRow* getNewResultRow(SDiskbasedBuf* pResultBuf, int64_t tableGroupId, int SResultRow* pResultRow = (SResultRow*)((char*)pData + pData->num); pResultRow->pageId = pageId; pResultRow->offset = (int32_t)pData->num; + *currentPageId = pageId; pData->num += interBufSize; - return pResultRow; } @@ -263,11 +260,8 @@ SResultRow* doSetResultOutBufByKey(SDiskbasedBuf* pResultBuf, SResultRowInfo* pR // allocate a new buffer page if (pResult == NULL) { -#ifdef BUF_PAGE_DEBUG - qDebug("page_2"); -#endif ASSERT(pSup->resultRowSize > 0); - pResult = getNewResultRow(pResultBuf, groupId, pSup->resultRowSize); + pResult = getNewResultRow(pResultBuf, &pSup->currentPageId, pSup->resultRowSize); initResultRow(pResult); @@ -3093,7 +3087,7 @@ int32_t aggDecodeResultRow(SOperatorInfo* pOperator, char* result) { offset += sizeof(int32_t); uint64_t tableGroupId = *(uint64_t*)(result + offset); - SResultRow* resultRow = getNewResultRow(pSup->pResultBuf, tableGroupId, pSup->resultRowSize); + SResultRow* resultRow = getNewResultRow(pSup->pResultBuf, &pSup->currentPageId, pSup->resultRowSize); if (!resultRow) { return TSDB_CODE_TSC_INVALID_INPUT; } @@ -3442,6 +3436,7 @@ int32_t doInitAggInfoSup(SAggSupporter* pAggSup, SqlFunctionCtx* pCtx, int32_t n const char* pKey) { _hash_fn_t hashFn = taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY); + pAggSup->currentPageId = -1; pAggSup->resultRowSize = getResultRowSize(pCtx, numOfOutput); pAggSup->keyBuf = taosMemoryCalloc(1, keyBufSize + POINTER_BYTES + sizeof(int64_t)); pAggSup->pResultRowHashTable = taosHashInit(10, hashFn, true, HASH_NO_LOCK); @@ -4678,6 +4673,7 @@ int32_t getOperatorExplainExecInfo(SOperatorInfo* operatorInfo, SArray* pExecInf int32_t initStreamAggSupporter(SStreamAggSupporter* pSup, const char* pKey, SqlFunctionCtx* pCtx, int32_t numOfOutput, int32_t size) { + pSup->currentPageId = -1; pSup->resultRowSize = getResultRowSize(pCtx, numOfOutput); pSup->keySize = sizeof(int64_t) + sizeof(TSKEY); pSup->pKeyBuf = taosMemoryCalloc(1, pSup->keySize); diff --git a/source/libs/executor/src/timewindowoperator.c b/source/libs/executor/src/timewindowoperator.c index d87e235ba5..f4ebf17646 100644 --- a/source/libs/executor/src/timewindowoperator.c +++ b/source/libs/executor/src/timewindowoperator.c @@ -3790,7 +3790,7 @@ static int32_t setWindowOutputBuf(SResultWindowInfo* pWinInfo, SResultRow** pRes } if (pWinInfo->pos.pageId == -1) { - *pResult = getNewResultRow(pAggSup->pResultBuf, groupId, pAggSup->resultRowSize); + *pResult = getNewResultRow(pAggSup->pResultBuf, &pAggSup->currentPageId, pAggSup->resultRowSize); if (*pResult == NULL) { return TSDB_CODE_OUT_OF_MEMORY; } From d53400085e02d737b8233c6b4844878724410f67 Mon Sep 17 00:00:00 2001 From: Pan YANG Date: Mon, 29 Aug 2022 23:28:21 +0800 Subject: [PATCH 48/76] docs: read through and fix minor problems 02 --- docs/en/02-intro/index.md | 93 +++++++++++++++++----------------- docs/zh/02-intro.md | 102 +++++++++++++++++++------------------- 2 files changed, 98 insertions(+), 97 deletions(-) diff --git a/docs/en/02-intro/index.md b/docs/en/02-intro/index.md index 51df831948..03ecf253a4 100644 --- a/docs/en/02-intro/index.md +++ b/docs/en/02-intro/index.md @@ -12,34 +12,34 @@ This section introduces the major features, competitive advantages, typical use- The major features are listed below: 1. Insert data - * supports [using SQL to insert](../develop/insert-data/sql-writing). - * supports [schemaless writing](../reference/schemaless/) just like NoSQL databases. It also supports standard protocols like [InfluxDB LINE](../develop/insert-data/influxdb-line),[OpenTSDB Telnet](../develop/insert-data/opentsdb-telnet), [OpenTSDB JSON ](../develop/insert-data/opentsdb-json) among others. - * supports seamless integration with third-party tools like [Telegraf](../third-party/telegraf/), [Prometheus](../third-party/prometheus/), [collectd](../third-party/collectd/), [StatsD](../third-party/statsd/), [TCollector](../third-party/tcollector/) and [icinga2/](../third-party/icinga2/), they can write data into TDengine with simple configuration and without a single line of code. + - supports [using SQL to insert](../develop/insert-data/sql-writing). + - supports [schemaless writing](../reference/schemaless/) just like NoSQL databases. It also supports standard protocols like [InfluxDB LINE](../develop/insert-data/influxdb-line),[OpenTSDB Telnet](../develop/insert-data/opentsdb-telnet), [OpenTSDB JSON ](../develop/insert-data/opentsdb-json) among others. + - supports seamless integration with third-party tools like [Telegraf](../third-party/telegraf/), [Prometheus](../third-party/prometheus/), [collectd](../third-party/collectd/), [StatsD](../third-party/statsd/), [TCollector](../third-party/tcollector/) and [icinga2/](../third-party/icinga2/), they can write data into TDengine with simple configuration and without a single line of code. 2. Query data - * supports standard [SQL](../taos-sql/), including nested query. - * supports [time series specific functions](../taos-sql/function/#time-series-extensions) and [time series specific queries](../taos-sql/distinguished), like downsampling, interpolation, cumulated sum, time weighted average, state window, session window and many others. - * supports [user defined functions](../taos-sql/udf). + - supports standard [SQL](../taos-sql/), including nested query. + - supports [time series specific functions](../taos-sql/function/#time-series-extensions) and [time series specific queries](../taos-sql/distinguished), like downsampling, interpolation, cumulated sum, time weighted average, state window, session window and many others. + - supports [user defined functions](../taos-sql/udf). 3. [Caching](../develop/cache/): TDengine always saves the last data point in cache, so Redis is not needed for time-series data processing. -4. [Stream Processing](../develop/stream/): not only is the continuous query is supported, but TDengine also supports even driven stream processing, so Flink or spark is not needed for time-series daata processing. -5. [Data Dubscription](../develop/tmq/): application can subscribe a table or a set of tables. API is the same as Kafka, but you can specify filter conditions. +4. [Stream Processing](../develop/stream/): not only is the continuous query is supported, but TDengine also supports even driven stream processing, so Flink or spark is not needed for time-series data processing. +5. [Data Subscription](../develop/tmq/): application can subscribe a table or a set of tables. API is the same as Kafka, but you can specify filter conditions. 6. Visualization - * supports seamless integration with [Grafana](../third-party/grafana/) for visualization. - * supports seamless integration with Google Data Studio. + - supports seamless integration with [Grafana](../third-party/grafana/) for visualization. + - supports seamless integration with Google Data Studio. 7. Cluster - * supports [cluster](../deployment/) with the capability of increasing processing power by adding more nodes. - * supports [deployment on Kubernetes](../deployment/k8s/) - * supports high availability via data replication. + - supports [cluster](../deployment/) with the capability of increasing processing power by adding more nodes. + - supports [deployment on Kubernetes](../deployment/k8s/) + - supports high availability via data replication. 8. Administration - * provides [monitoring](../operation/monitor) on running instances of TDengine. - * provides many ways to [import](../operation/import) and [export](../operation/export) data. + - provides [monitoring](../operation/monitor) on running instances of TDengine. + - provides many ways to [import](../operation/import) and [export](../operation/export) data. 9. Tools - * provides an interactive [command-line interface](../reference/taos-shell) for management, maintenance and ad-hoc queries. - * provides a tool [taosBenchmark](../reference/taosbenchmark/) for testing the performance of TDengine. + - provides an interactive [command-line interface](../reference/taos-shell) for management, maintenance and ad-hoc queries. + - provides a tool [taosBenchmark](../reference/taosbenchmark/) for testing the performance of TDengine. 10. Programming - * 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. - * provides a [REST API](../reference/rest-api/). + - 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. + - provides a [REST API](../reference/rest-api/). -For more details on features, please read through the entire documentation. +For more details on features, please read through the entire documentation. ## Competitive Advantages @@ -52,15 +52,16 @@ By making full use of [characteristics of time series data](https://tdengine.com - **[Cloud Native](https://tdengine.com/tdengine/cloud-native-time-series-database/)**: Through native distributed design, sharding and partitioning, separation of compute and storage, RAFT, support for kubernetes deployment and full observability, TDengine is a cloud native Time-Series Database and can be deployed on public, private or hybrid clouds. - **[Ease of Use](https://tdengine.com/tdengine/easy-time-series-data-platform/)**: For administrators, TDengine significantly reduces the effort to[ -](https://tdengine.com/tdengine/easy-time-series-data-platform/) deploy and maintain. For developers, it provides a simple interface, simplified solution and seamless integrations for third party tools. For data users, it gives easy data access. + ](https://tdengine.com/tdengine/easy-time-series-data-platform/) deploy and maintain. For developers, it provides a simple interface, simplified solution and seamless integrations for third party tools. For data users, it gives easy data access. -- **[Easy Data Analytics](https://tdengine.com/tdengine/time-series-data-analytics-made-easy/)**: Through super tables, storage and compute separation, data partitioning by time interval, pre-computation and other means, TDengine makes it easy to explore, format, and get access to data in a highly efficient way. +- **[Easy Data Analytics](https://tdengine.com/tdengine/time-series-data-analytics-made-easy/)**: Through super tables, storage and compute separation, data partitioning by time interval, pre-computation and other means, TDengine makes it easy to explore, format, and get access to data in a highly efficient way. - **[Open Source](https://tdengine.com/tdengine/open-source-time-series-database/)**: TDengine’s core modules, including cluster feature, are all available under open source licenses. It has gathered over 19k stars on GitHub. There is an active developer community, and over 140k running instances worldwide. -With TDengine, the total cost of ownership of your time-series data platform can be greatly reduced. 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 simplified solution and nearly zero management, the operation and maintenance costs are reduced significantly. +With TDengine, the total cost of ownership of your time-series data platform can be greatly reduced. 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 simplified solution and nearly zero management, the operation and maintenance costs are reduced significantly. ## Technical Ecosystem + This is how TDengine would be situated, in a typical time-series data processing platform: ![TDengine Database Technical Ecosystem ](eco_system.webp) @@ -75,42 +76,42 @@ As a high-performance, scalable and SQL supported time-series database, TDengine ### 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. | +| **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** | -| ------------------------------------------------- | ------------------ | ----------------------- | ------------------- | ------------------------------------------------------------ | +| **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. | +| 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 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 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 TDengine CLI 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.| +| **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 TDengine CLI 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 diff --git a/docs/zh/02-intro.md b/docs/zh/02-intro.md index 152d03d9e3..c78eb17640 100644 --- a/docs/zh/02-intro.md +++ b/docs/zh/02-intro.md @@ -4,53 +4,53 @@ description: 简要介绍 TDengine 的主要功能 toc_max_heading_level: 2 --- -TDengine 是一款开源、高性能、云原生的[时序数据库](https://tdengine.com/tsdb/),且针对物联网、车联网以及工业互联网进行了优化。TDengine 的代码,包括其集群功能,都在 GNU AGPL v3.0 下开源。除核心的时序数据库功能外,TDengine 还提供[缓存](../develop/cache/)、[数据订阅](../develop/tmq)、[流式计算](../develop/stream)等其它功能以降低系统复杂度及研发和运维成本。 +TDengine 是一款开源、高性能、云原生的[时序数据库](https://tdengine.com/tsdb/),且针对物联网、车联网、工业互联网、金融、IT 运维等场景进行了优化。TDengine 的代码,包括集群功能,都在 GNU AGPL v3.0 下开源。除核心的时序数据库功能外,TDengine 还提供[缓存](../develop/cache/)、[数据订阅](../develop/tmq)、[流式计算](../develop/stream)等其它功能以降低系统复杂度及研发和运维成本。 -本章节介绍TDengine的主要功能、竞争优势、适用场景、与其他数据库的对比测试等等,让大家对TDengine有个整体的了解。 +本章节介绍 TDengine 的主要功能、竞争优势、适用场景、与其他数据库的对比测试等等,让大家对 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) - - 与多种第三方工具的无缝集成,它们都可以仅通过配置而无需任何代码即可将数据写入 TDengine - - [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) ; + - [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) + - 与多种第三方工具的无缝集成,它们都可以仅通过配置而无需任何代码即可将数据写入 TDengine + - [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) ; 2. 查询数据,支持 - - [标准SQL](../taos-sql),含嵌套查询 - - [时序数据特色函数](../taos-sql/function/#time-series-extensions) - - [时序顺序特色查询](../taos-sql/distinguished),例如降采样、插值、累加和、时间加权平均、状态窗口、会话窗口等 - - [用户自定义函数](../taos-sql/udf) + - [标准 SQL](../taos-sql),含嵌套查询 + - [时序数据特色函数](../taos-sql/function/#time-series-extensions) + - [时序数据特色查询](../taos-sql/distinguished),例如降采样、插值、累加和、时间加权平均、状态窗口、会话窗口等 + - [用户自定义函数(UDF)](../taos-sql/udf) 3. [缓存](../develop/cache),将每张表的最后一条记录缓存起来,这样无需 Redis 就能对时序数据进行高效处理 -4. [流式计算](../develop/stream)(Stream Processing),TDengine 不仅支持连续查询,还支持事件驱动的流式计算,这样在处理时序数据时就无需 Flink 或 Spark 这样流计算组件 -5. [数据订阅](../develop/tmq),应用程序可以订阅一张表或一组表的数据,API 与 Kafka 相同,而且可以指定过滤条件 +4. [流式计算(Stream Processing)](../develop/stream),TDengine 不仅支持连续查询,还支持事件驱动的流式计算,这样在处理时序数据时就无需 Flink 或 Spark 这样流式计算组件 +5. [数据订阅](../develop/tmq),应用程序可以订阅一张表或一组表的数据,提供与 Kafka 相同的 API,而且可以指定过滤条件 6. 可视化 - - 支持与 [Grafana](../third-party/grafana/) 的无缝集成 - - 支持与 Google Data Studio 的无缝集成 + - 支持与 [Grafana](../third-party/grafana/) 的无缝集成 + - 支持与 Google Data Studio 的无缝集成 7. 集群 - - 集群部署(../deployment/),可以通过增加节点进行水平扩展以提升处理能力 - - 可以通过 [Kubernets 部署 TDengine](../deployment/k8s/) - - 通过多副本提供高可用能力 + - [集群部署](../deployment/),可以通过增加节点进行水平扩展以提升处理能力 + - 可以通过 [Kubernetes 部署 TDengine](../deployment/k8s/) + - 通过多副本提供高可用能力 8. 管理 - - [监控](../operation/monitor)运行中的 TDengine 实例 - - 多种[数据导入](../operation/import)方式 - - 多种[数据导出](../operation/export)方式 + - [监控](../operation/monitor)运行中的 TDengine 实例 + - 多种[数据导入](../operation/import)方式 + - 多种[数据导出](../operation/export)方式 9. 工具 - - 提供交互式[命令行程序](../reference/taos-shell),便于管理集群,检查系统状态,做即席查询 - - 提供压力测试工具[taosBenchmark](../reference/taosbenchmark),用于测试 TDengine 的性能 + - 提供[交互式命令行程序(CLI)](../reference/taos-shell),便于管理集群,检查系统状态,做即席查询 + - 提供压力测试工具[taosBenchmark](../reference/taosbenchmark),用于测试 TDengine 的性能 10. 编程 - - 提供各种语言的[连接器](../connector): 如 [C/C++](../connector/cpp), [Java](../connector/java), [Go](../connector/go), [Node.JS](../connector/node), [Rust](../connector/rust), [Python](../connector/python), [C#](../connector/csharp) 等 + - 提供各种语言的[连接器(Connector)](../connector): 如 [C/C++](../connector/cpp)、[Java](../connector/java)、[Go](../connector/go)、[Node.js](../connector/node)、[Rust](../connector/rust)、[Python](../connector/python)、[C#](../connector/csharp) 等 - 支持 [REST 接口](../connector/rest-api/) 更多细节功能,请阅读整个文档。 @@ -63,23 +63,23 @@ TDengine的主要功能如下: - **[极简时序数据平台](https://www.taosdata.com/tdengine/simplified_solution_for_time-series_data_processing)**:TDengine 内建缓存、流式计算和数据订阅等功能,为时序数据的处理提供了极简的解决方案,从而大幅降低了业务系统的设计复杂度和运维成本。 -- **[云原生](https://www.taosdata.com/tdengine/cloud_native_time-series_database)**:通过原生的分布式设计、数据分片和分区、存算分离、RAFT 协议、Kubernets 部署和完整的可观测性,TDengine 是一款云原生时序数据库并且能够部署在公有云、私有云和混合云上。 +- **[云原生](https://www.taosdata.com/tdengine/cloud_native_time-series_database)**:通过原生的分布式设计、数据分片和分区、存算分离、RAFT 协议、Kubernetes 部署和完整的可观测性,TDengine 是一款云原生时序数据库并且能够部署在公有云、私有云和混合云上。 -- **[简单易用](https://www.taosdata.com/tdengine/ease_of_use)**:对系统管理员来说,TDengine 大幅降低了管理和维护的代价。对开发者来说, TDengine 提供了简单的接口、极简的解决方案和与第三方工具的无缝集成。对数据分析专家来说,TDengine 提供了便捷的数据访问。 +- **[简单易用](https://www.taosdata.com/tdengine/ease_of_use)**:对系统管理员来说,TDengine 大幅降低了管理和维护的代价。对开发者来说, TDengine 提供了简单的接口、极简的解决方案和与第三方工具的无缝集成。对数据分析专家来说,TDengine 提供了便捷的数据访问能力。 - **[分析能力](https://www.taosdata.com/tdengine/easy_data_analytics)**:通过超级表、存储计算分离、分区分片、预计算和其它技术,TDengine 能够高效地浏览、格式化和访问数据。 -- **[核心开源](https://www.taosdata.com/tdengine/open_source_time-series_database)**:TDengine 的核心代码包括集群功能全部在开源协议下公开。全球超过 140k 个运行实例,GitHub Star 19k,且拥有一个活跃的开发者社区。 +- **[核心开源](https://www.taosdata.com/tdengine/open_source_time-series_database)**:TDengine 的核心代码包括集群功能全部在开源协议下公开。全球超过 140k 个运行实例,GitHub Star 19k,且拥有一个活跃的开发者社区。 采用 TDengine,可将典型的物联网、车联网、工业互联网大数据平台的总拥有成本大幅降低。表现在几个方面: -1. 由于其超强性能,它能将系统需要的计算资源和存储资源大幅降低 +1. 由于其超强性能,它能将系统所需的计算资源和存储资源大幅降低 2. 因为支持 SQL,能与众多第三方软件无缝集成,学习迁移成本大幅下降 -3. 因为是一极简的时序数据平台,系统复杂度、研发和运营成本大幅降低 +3. 因为是一款极简的时序数据平台,系统复杂度、研发和运营成本大幅降低 ## 技术生态 -在整个时序大数据平台中,TDengine 在其中扮演的角色如下: +在整个时序大数据平台中,TDengine 扮演的角色如下:
@@ -88,11 +88,11 @@ TDengine的主要功能如下:
图 1. TDengine技术生态图
-上图中,左侧是各种数据采集或消息队列,包括 OPC-UA、MQTT、Telegraf、也包括 Kafka, 他们的数据将被源源不断的写入到 TDengine。右侧则是可视化、BI 工具、组态软件、应用程序。下侧则是 TDengine 自身提供的命令行程序 (CLI) 以及可视化管理管理。 +上图中,左侧是各种数据采集或消息队列,包括 OPC-UA、MQTT、Telegraf、也包括 Kafka,他们的数据将被源源不断的写入到 TDengine。右侧则是可视化、BI 工具、组态软件、应用程序。下侧则是 TDengine 自身提供的命令行程序(CLI)以及可视化管理工具。 ## 典型适用场景 -作为一个高性能、分布式、支持 SQL 的时序数据库 (Database),TDengine 的典型适用场景包括但不限于 IoT、工业互联网、车联网、IT 运维、能源、金融证券等领域。需要指出的是,TDengine 是针对时序数据场景设计的专用数据库和专用大数据处理工具,因充分利用了时序大数据的特点,它无法用来处理网络爬虫、微博、微信、电商、ERP、CRM 等通用型数据。本文对适用场景做更多详细的分析。 +作为一个高性能、分布式、支持 SQL 的时序数据库(Database),TDengine 的典型适用场景包括但不限于 IoT、工业互联网、车联网、IT 运维、能源、金融证券等领域。需要指出的是,TDengine 是针对时序数据场景设计的专用数据库和专用大数据处理工具,因其充分利用了时序大数据的特点,它无法用来处理网络爬虫、微博、微信、电商、ERP、CRM 等通用型数据。下面本文将对适用场景做更多详细的分析。 ### 数据源特点和需求 @@ -114,18 +114,18 @@ TDengine的主要功能如下: ### 系统功能需求 -| 系统功能需求 | 不适用 | 可能适用 | 非常适用 | 简单说明 | -| -------------------------- | ------ | -------- | -------- | --------------------------------------------------------------------------------------------------------------------- | -| 要求完整的内置数据处理算法 | | √ | | TDengine 的实现了通用的数据处理算法,但是还没有做到妥善处理各行各业的所有要求,因此特殊类型的处理还需要应用层面处理。 | -| 需要大量的交叉查询处理 | | √ | | 这种类型的处理更多应该用关系型数据系统处理,或者应该考虑 TDengine 和关系型数据系统配合实现系统功能。 | +| 系统功能需求 | 不适用 | 可能适用 | 非常适用 | 简单说明 | +| -------------------------- | ------ | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------- | +| 要求完整的内置数据处理算法 | | √ | | TDengine 实现了通用的数据处理算法,但是还没有做到妥善处理各行各业的所有需求,因此特殊类型的处理需求还需要在应用层面解决。 | +| 需要大量的交叉查询处理 | | √ | | 这种类型的处理更多应该用关系型数据库处理,或者应该考虑 TDengine 和关系型数据库配合实现系统功能。 | ### 系统性能需求 -| 系统性能需求 | 不适用 | 可能适用 | 非常适用 | 简单说明 | -| ---------------------- | ------ | -------- | -------- | ------------------------------------------------------------------------------------------------------ | -| 要求较大的总体处理能力 | | | √ | TDengine 的集群功能可以轻松地让多服务器配合达成处理能力的提升。 | -| 要求高速处理数据 | | | √ | TDengine 的专门为 IoT 优化的存储和数据处理的设计,一般可以让系统得到超出同类产品多倍数的处理速度提升。 | -| 要求快速处理小粒度数据 | | | √ | 这方面 TDengine 性能可以完全对标关系型和 NoSQL 型数据处理系统。 | +| 系统性能需求 | 不适用 | 可能适用 | 非常适用 | 简单说明 | +| ---------------------- | ------ | -------- | -------- | -------------------------------------------------------------------------------------------------- | +| 要求较大的总体处理能力 | | | √ | TDengine 的集群功能可以轻松地让多服务器配合达成处理能力的提升。 | +| 要求高速处理数据 | | | √ | TDengine 专门为 IoT 优化的存储和数据处理设计,一般可以让系统得到超出同类产品多倍数的处理速度提升。 | +| 要求快速处理小粒度数据 | | | √ | 这方面 TDengine 性能可以完全对标关系型和 NoSQL 型数据处理系统。 | ### 系统维护需求 From 9600840aad86ad17bfdef206b2ff22d956e78de1 Mon Sep 17 00:00:00 2001 From: Cary Xu Date: Tue, 30 Aug 2022 08:14:32 +0800 Subject: [PATCH 49/76] fix: race condition between fetch all and commit --- source/dnode/vnode/src/sma/smaCommit.c | 11 +++++++++-- source/dnode/vnode/src/sma/smaRollup.c | 25 +++++++++++++++++-------- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/source/dnode/vnode/src/sma/smaCommit.c b/source/dnode/vnode/src/sma/smaCommit.c index ca5367f397..0e644be288 100644 --- a/source/dnode/vnode/src/sma/smaCommit.c +++ b/source/dnode/vnode/src/sma/smaCommit.c @@ -312,15 +312,22 @@ static int32_t tdProcessRSmaAsyncPreCommitImpl(SSma *pSma) { SSmaStat *pStat = SMA_ENV_STAT(pEnv); SRSmaStat *pRSmaStat = SMA_STAT_RSMA(pStat); + int32_t nLoops = 0; // step 1: set rsma stat atomic_store_8(RSMA_TRIGGER_STAT(pRSmaStat), TASK_TRIGGER_STAT_PAUSED); - atomic_store_8(RSMA_COMMIT_STAT(pRSmaStat), 1); + while (atomic_val_compare_exchange_8(RSMA_COMMIT_STAT(pRSmaStat), 0, 1) != 0) { + ++nLoops; + if (nLoops > 1000) { + sched_yield(); + nLoops = 0; + } + } pRSmaStat->commitAppliedVer = pSma->pVnode->state.applied; ASSERT(pRSmaStat->commitAppliedVer > 0); // step 2: wait for all triggered fetch tasks to finish - int32_t nLoops = 0; + while (1) { if (T_REF_VAL_GET(pStat) == 0) { smaDebug("vgId:%d, rsma commit, fetch tasks are all finished", SMA_VID(pSma)); diff --git a/source/dnode/vnode/src/sma/smaRollup.c b/source/dnode/vnode/src/sma/smaRollup.c index 1b1e304c03..52b08d131c 100644 --- a/source/dnode/vnode/src/sma/smaRollup.c +++ b/source/dnode/vnode/src/sma/smaRollup.c @@ -21,6 +21,7 @@ #define RSMA_SUBMIT_BATCH_SIZE (1024) // cnt #define RSMA_FETCH_DELAY_MAX (900000) // ms #define RSMA_FETCH_ACTIVE_MAX (1800) // ms +#define RSMA_FETCH_INTERVAL (5000) // ms SSmaMgmt smaMgmt = { .inited = 0, @@ -1501,13 +1502,13 @@ static void tdRSmaFetchTrigger(void *param, void *tmrId) { switch (rsmaTriggerStat) { case TASK_TRIGGER_STAT_PAUSED: case TASK_TRIGGER_STAT_CANCELLED: { - tdReleaseSmaRef(smaMgmt.rsetId, pRSmaInfo->refId); smaDebug("vgId:%d, rsma fetch task not start for level %" PRIi8 " since stat is %" PRIi8 ", rsetId rsetId:%" PRIi64 " refId:%d", SMA_VID(pSma), pItem->level, rsmaTriggerStat, smaMgmt.rsetId, pRSmaInfo->refId); if (rsmaTriggerStat == TASK_TRIGGER_STAT_PAUSED) { - taosTmrReset(tdRSmaFetchTrigger, 5000, pItem, smaMgmt.tmrHandle, &pItem->tmrId); + taosTmrReset(tdRSmaFetchTrigger, RSMA_FETCH_INTERVAL, pItem, smaMgmt.tmrHandle, &pItem->tmrId); } + tdReleaseSmaRef(smaMgmt.rsetId, pRSmaInfo->refId); return; } default: @@ -1518,7 +1519,7 @@ static void tdRSmaFetchTrigger(void *param, void *tmrId) { atomic_val_compare_exchange_8(&pItem->triggerStat, TASK_TRIGGER_STAT_ACTIVE, TASK_TRIGGER_STAT_INACTIVE); switch (fetchTriggerStat) { case TASK_TRIGGER_STAT_ACTIVE: { - smaDebug("vgId:%d, rsma fetch task started for level:%" PRIi8 " suid:%" PRIi64 " since stat is active", + smaDebug("vgId:%d, rsma fetch task planned for level:%" PRIi8 " suid:%" PRIi64 " since stat is active", SMA_VID(pSma), pItem->level, pRSmaInfo->suid); // async process pItem->fetchLevel = pItem->level; @@ -1531,8 +1532,6 @@ static void tdRSmaFetchTrigger(void *param, void *tmrId) { if (atomic_load_8(&pRSmaInfo->assigned) == 0) { tsem_post(&(pStat->notEmpty)); } - smaDebug("vgId:%d, rsma fetch task planned for level:%" PRIi8 " suid:%" PRIi64, SMA_VID(pSma), pItem->level, - pRSmaInfo->suid); } break; case TASK_TRIGGER_STAT_PAUSED: { smaDebug("vgId:%d, rsma fetch task not start for level:%" PRIi8 " suid:%" PRIi64 " since stat is paused", @@ -1715,15 +1714,25 @@ int32_t tdRSmaProcessExecImpl(SSma *pSma, ERsmaExecType type) { smaDebug("vgId:%d, batchSize:%d, execType:%" PRIi8, SMA_VID(pSma), qallItemSize, type); } - if (atomic_load_8(RSMA_COMMIT_STAT(pRSmaStat)) == 0) { + if (atomic_val_compare_exchange_8(RSMA_COMMIT_STAT(pRSmaStat), 0, 2) == 0) { tdRSmaFetchAllResult(pSma, pInfo, pSubmitArr); + atomic_store_8(RSMA_COMMIT_STAT(pRSmaStat), 0); } if (qallItemSize > 0) { atomic_fetch_sub_64(&pRSmaStat->nBufItems, qallItemSize); continue; } else if (RSMA_INFO_ITEM(pInfo, 0)->fetchLevel || RSMA_INFO_ITEM(pInfo, 1)->fetchLevel) { - continue; + if (atomic_load_8(RSMA_COMMIT_STAT(pRSmaStat)) == 0) { + continue; + } + for (int32_t j = 0; j < TSDB_RETENTION_L2; ++j) { + SRSmaInfoItem *pItem = RSMA_INFO_ITEM(pInfo, j); + if (pItem->fetchLevel) { + pItem->fetchLevel = 0; + taosTmrReset(tdRSmaFetchTrigger, RSMA_FETCH_INTERVAL, pItem, smaMgmt.tmrHandle, &pItem->tmrId); + } + } } break; @@ -1775,7 +1784,7 @@ int32_t tdRSmaProcessExecImpl(SSma *pSma, ERsmaExecType type) { if (pEnv->flag & SMA_ENV_FLG_CLOSE) { break; } - + tsem_wait(&pRSmaStat->notEmpty); if ((pEnv->flag & SMA_ENV_FLG_CLOSE) && (atomic_load_64(&pRSmaStat->nBufItems) <= 0)) { From 0ebd3b0ff89ae160b182f77f9f760fad301e2cf5 Mon Sep 17 00:00:00 2001 From: gccgdb1234 Date: Tue, 30 Aug 2022 09:05:01 +0800 Subject: [PATCH 50/76] doc: correct high performance writing to be adapted to 3.0 --- docs/en/07-develop/03-insert-data/05-high-volume.md | 7 ++----- docs/zh/07-develop/03-insert-data/05-high-volume.md | 8 ++------ 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/docs/en/07-develop/03-insert-data/05-high-volume.md b/docs/en/07-develop/03-insert-data/05-high-volume.md index 8163ae03b2..4c677e23fc 100644 --- a/docs/en/07-develop/03-insert-data/05-high-volume.md +++ b/docs/en/07-develop/03-insert-data/05-high-volume.md @@ -46,12 +46,9 @@ If the data source is Kafka, then the appication program is a consumer of Kafka, ### Tune TDengine -TDengine is a distributed and high performance time series database, there are also some ways to tune TDengine to get better writing performance. +On the server side, database configuration parameter `vgroups` needs to be set carefully to maximize the system performance. If it's set too low, the system capability can't be utilized fully; if it's set too big, unnecessary resource competition may be produced. A normal recommendation for `vgroups` parameter is 2 times of the number of CPU cores. However, depending on the actual system resources, it may still need to tuned. -1. Set proper number of `vgroups` according to available CPU cores. Normally, we recommend 2 \* number_of_cores as a starting point. If the verification result shows this is not enough to utilize CPU resources, you can use a higher value. -2. Set proper `minTablesPerVnode`, `tableIncStepPerVnode`, and `maxVgroupsPerDb` according to the number of tables so that tables are distributed even across vgroups. The purpose is to balance the workload among all vnodes so that system resources can be utilized better to get higher performance. - -For more performance tuning parameters, please refer to [Configuration Parameters](../../../reference/config). +For more configuration parameters, please refer to [Database Configuration](../../../taos-sql/database) and [Server Configuration](../../../reference/config)。 ## Sample Programs diff --git a/docs/zh/07-develop/03-insert-data/05-high-volume.md b/docs/zh/07-develop/03-insert-data/05-high-volume.md index 32be8cb890..a47da322a8 100644 --- a/docs/zh/07-develop/03-insert-data/05-high-volume.md +++ b/docs/zh/07-develop/03-insert-data/05-high-volume.md @@ -38,13 +38,9 @@ import TabItem from "@theme/TabItem"; ### 服务器配置的角度 {#setting-view} -从服务器配置的角度来说,也有很多优化写入性能的方法。 +从服务端配置的角度,要根据系统中磁盘的数量,磁盘的 I/O 能力,以及处理器能力在创建数据库时设置适当的 vgroups 数量以充分发挥系统性能。如果 vgroups 过少,则系统性能无法发挥;如果 vgroups 过多,会造成无谓的资源竞争。常规推荐 vgroups 数量为 CPU 核数的 2 倍,但仍然要结合具体的系统资源配置进行调优。 -如果总表数不多(远小于核数乘以1000), 且无论怎么调节客户端程序,taosd 进程的 CPU 使用率都很低,那么很可能是因为表在各个 vgroup 分布不均。比如:数据库总表数是 1000 且 minTablesPerVnode 设置的也是 1000,那么所有的表都会分布在 1 个 vgroup 上。此时如果将 minTablesPerVnode 和 tablelncStepPerVnode 都设置成 100, 则可将表分布至 10 个 vgroup。(假设 maxVgroupsPerDb 大于等于 10)。 - -如果总表数比较大(比如大于500万),适当增加 maxVgroupsPerDb 也能显著提高建表的速度。maxVgroupsPerDb 默认值为 0, 自动配置为 CPU 的核数。 如果表的数量巨大,也建议调节 maxTablesPerVnode 参数,以免超过单个 vnode 建表的上限。 - -更多调优参数,请参考 [配置参考](../../../reference/config)部分。 +更多调优参数,请参考 [数据库管理](../../../taos-sql/database) 和 [服务端配置](../../../reference/config)。 ## 高效写入示例 {#sample-code} From 00afe94fc38bbe2074ca350cee40a939eab2f811 Mon Sep 17 00:00:00 2001 From: gccgdb1234 Date: Tue, 30 Aug 2022 09:41:25 +0800 Subject: [PATCH 51/76] doc: correct wrong wget address --- docs/zh/14-reference/11-docker/index.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/zh/14-reference/11-docker/index.md b/docs/zh/14-reference/11-docker/index.md index d712e9aba8..58bbe1e117 100644 --- a/docs/zh/14-reference/11-docker/index.md +++ b/docs/zh/14-reference/11-docker/index.md @@ -119,7 +119,7 @@ taos -h tdengine -P 6030 FROM ubuntu:20.04 RUN apt-get update && apt-get install -y wget ENV TDENGINE_VERSION=3.0.0.0 -RUN wget -c https://www.taosdata.com/assets-download/TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz \ +RUN wget -c https://www.tdengine.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 \ @@ -234,7 +234,7 @@ go mod tidy ```dockerfile FROM golang:1.19.0-buster as builder ENV TDENGINE_VERSION=3.0.0.0 -RUN wget -c https://www.taosdata.com/assets-download/3.0/TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz \ +RUN wget -c https://www.tdengine.com/assets-download/3.0/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 \ @@ -250,7 +250,7 @@ RUN go build FROM ubuntu:20.04 RUN apt-get update && apt-get install -y wget ENV TDENGINE_VERSION=3.0.0.0 -RUN wget -c https://www.taosdata.com/assets-download/3.0/TDengine-client-${TDENGINE_VERSION}-Linux-x64.tar.gz \ +RUN wget -c https://www.tdengine.com/assets-download/3.0/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 \ From 45616c6548163f9dc31fd541587e57f5b5b1be60 Mon Sep 17 00:00:00 2001 From: Haojun Liao Date: Tue, 30 Aug 2022 09:47:42 +0800 Subject: [PATCH 52/76] fix(query):fix syntax error. --- source/libs/executor/inc/executorimpl.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/libs/executor/inc/executorimpl.h b/source/libs/executor/inc/executorimpl.h index e1c9c6a114..f0518a72ab 100644 --- a/source/libs/executor/inc/executorimpl.h +++ b/source/libs/executor/inc/executorimpl.h @@ -299,7 +299,7 @@ enum { }; typedef struct SAggSupporter { - SHashObj* pResultRowHashTable; // quick locate the window object for each result + SSHashObj* pResultRowHashTable; // quick locate the window object for each result char* keyBuf; // window key buffer SDiskbasedBuf* pResultBuf; // query result buffer based on blocked-wised disk file int32_t resultRowSize; // the result buffer size for each result row, with the meta data size for each row @@ -328,7 +328,6 @@ typedef struct STableScanInfo { SQueryTableDataCond cond; int32_t scanFlag; // table scan flag to denote if it is a repeat/reverse/main scan int32_t dataBlockLoadFlag; -// SInterval interval; // if the upstream is an interval operator, the interval info is also kept here to get the time window to check if current data block needs to be loaded. SSampleExecInfo sample; // sample execution info int32_t currentGroupId; int32_t currentTable; From 2a45862946d7b47063e5cc03b752aa352c03b6b4 Mon Sep 17 00:00:00 2001 From: Cary Xu Date: Tue, 30 Aug 2022 09:55:47 +0800 Subject: [PATCH 53/76] other: code format --- source/dnode/vnode/src/sma/smaCommit.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/source/dnode/vnode/src/sma/smaCommit.c b/source/dnode/vnode/src/sma/smaCommit.c index 0e644be288..4bd3cfa5ac 100644 --- a/source/dnode/vnode/src/sma/smaCommit.c +++ b/source/dnode/vnode/src/sma/smaCommit.c @@ -312,7 +312,7 @@ static int32_t tdProcessRSmaAsyncPreCommitImpl(SSma *pSma) { SSmaStat *pStat = SMA_ENV_STAT(pEnv); SRSmaStat *pRSmaStat = SMA_STAT_RSMA(pStat); - int32_t nLoops = 0; + int32_t nLoops = 0; // step 1: set rsma stat atomic_store_8(RSMA_TRIGGER_STAT(pRSmaStat), TASK_TRIGGER_STAT_PAUSED); @@ -351,7 +351,8 @@ static int32_t tdProcessRSmaAsyncPreCommitImpl(SSma *pSma) { return TSDB_CODE_FAILED; } - smaInfo("vgId:%d, rsma commit, wait for all items to be consumed, TID:%p", SMA_VID(pSma), (void*)taosGetSelfPthreadId()); + smaInfo("vgId:%d, rsma commit, wait for all items to be consumed, TID:%p", SMA_VID(pSma), + (void *)taosGetSelfPthreadId()); nLoops = 0; while (atomic_load_64(&pRSmaStat->nBufItems) > 0) { ++nLoops; @@ -366,7 +367,7 @@ static int32_t tdProcessRSmaAsyncPreCommitImpl(SSma *pSma) { } smaInfo("vgId:%d, rsma commit, operator state commited, TID:%p", SMA_VID(pSma), (void *)taosGetSelfPthreadId()); -#if 0 // consuming task of qTaskInfo clone +#if 0 // consuming task of qTaskInfo clone // step 4: swap queue/qall and iQueue/iQall // lock // taosWLockLatch(SMA_ENV_LOCK(pEnv)); From f0544677635f22c51524733fbb72cd5cd3b240ce Mon Sep 17 00:00:00 2001 From: Pan YANG Date: Tue, 30 Aug 2022 10:02:51 +0800 Subject: [PATCH 54/76] docs: read through and fix minor problems 02 --- docs/en/02-intro/index.md | 51 ++++++++++++++++++++++----------------- docs/zh/02-intro.md | 2 +- 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/docs/en/02-intro/index.md b/docs/en/02-intro/index.md index 03ecf253a4..d385845d7c 100644 --- a/docs/en/02-intro/index.md +++ b/docs/en/02-intro/index.md @@ -12,32 +12,32 @@ This section introduces the major features, competitive advantages, typical use- The major features are listed below: 1. Insert data - - supports [using SQL to insert](../develop/insert-data/sql-writing). - - supports [schemaless writing](../reference/schemaless/) just like NoSQL databases. It also supports standard protocols like [InfluxDB LINE](../develop/insert-data/influxdb-line),[OpenTSDB Telnet](../develop/insert-data/opentsdb-telnet), [OpenTSDB JSON ](../develop/insert-data/opentsdb-json) among others. - - supports seamless integration with third-party tools like [Telegraf](../third-party/telegraf/), [Prometheus](../third-party/prometheus/), [collectd](../third-party/collectd/), [StatsD](../third-party/statsd/), [TCollector](../third-party/tcollector/) and [icinga2/](../third-party/icinga2/), they can write data into TDengine with simple configuration and without a single line of code. + - Supports [using SQL to insert](../develop/insert-data/sql-writing). + - Supports [schemaless writing](../reference/schemaless/) just like NoSQL databases. It also supports standard protocols like [InfluxDB Line](../develop/insert-data/influxdb-line), [OpenTSDB Telnet](../develop/insert-data/opentsdb-telnet), [OpenTSDB JSON ](../develop/insert-data/opentsdb-json) among others. + - Supports seamless integration with third-party tools like [Telegraf](../third-party/telegraf/), [Prometheus](../third-party/prometheus/), [collectd](../third-party/collectd/), [StatsD](../third-party/statsd/), [TCollector](../third-party/tcollector/), [EMQX](../third-party/emq-broker), [HiveMQ](../third-party/hive-mq-broker), and [Icinga2](../third-party/icinga2/), they can write data into TDengine with simple configuration and without a single line of code. 2. Query data - - supports standard [SQL](../taos-sql/), including nested query. - - supports [time series specific functions](../taos-sql/function/#time-series-extensions) and [time series specific queries](../taos-sql/distinguished), like downsampling, interpolation, cumulated sum, time weighted average, state window, session window and many others. - - supports [user defined functions](../taos-sql/udf). + - Supports standard [SQL](../taos-sql/), including nested query. + - Supports [time series specific functions](../taos-sql/function/#time-series-extensions) and [time series specific queries](../taos-sql/distinguished), like downsampling, interpolation, cumulated sum, time weighted average, state window, session window and many others. + - Supports [User Defined Functions (UDF)](../taos-sql/udf). 3. [Caching](../develop/cache/): TDengine always saves the last data point in cache, so Redis is not needed for time-series data processing. -4. [Stream Processing](../develop/stream/): not only is the continuous query is supported, but TDengine also supports even driven stream processing, so Flink or spark is not needed for time-series data processing. -5. [Data Subscription](../develop/tmq/): application can subscribe a table or a set of tables. API is the same as Kafka, but you can specify filter conditions. +4. [Stream Processing](../develop/stream/): Not only is the continuous query is supported, but TDengine also supports event driven stream processing, so Flink or Spark is not needed for time-series data processing. +5. [Data Subscription](../develop/tmq/): Application can subscribe a table or a set of tables. API is the same as Kafka, but you can specify filter conditions. 6. Visualization - - supports seamless integration with [Grafana](../third-party/grafana/) for visualization. - - supports seamless integration with Google Data Studio. + - Supports seamless integration with [Grafana](../third-party/grafana/) for visualization. + - Supports seamless integration with Google Data Studio. 7. Cluster - - supports [cluster](../deployment/) with the capability of increasing processing power by adding more nodes. - - supports [deployment on Kubernetes](../deployment/k8s/) - - supports high availability via data replication. + - Supports [cluster](../deployment/) with the capability of increasing processing power by adding more nodes. + - Supports [deployment on Kubernetes](../deployment/k8s/). + - Supports high availability via data replication. 8. Administration - - provides [monitoring](../operation/monitor) on running instances of TDengine. - - provides many ways to [import](../operation/import) and [export](../operation/export) data. + - Provides [monitoring](../operation/monitor) on running instances of TDengine. + - Provides many ways to [import](../operation/import) and [export](../operation/export) data. 9. Tools - - provides an interactive [command-line interface](../reference/taos-shell) for management, maintenance and ad-hoc queries. - - provides a tool [taosBenchmark](../reference/taosbenchmark/) for testing the performance of TDengine. + - Provides an interactive [Command-line Interface (CLI)](../reference/taos-shell) for management, maintenance and ad-hoc queries. + - Provides a tool [taosBenchmark](../reference/taosbenchmark/) for testing the performance of TDengine. 10. Programming - - 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. - - provides a [REST API](../reference/rest-api/). + - 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. + - Provides a [REST API](../reference/rest-api/). For more details on features, please read through the entire documentation. @@ -49,7 +49,7 @@ By making full use of [characteristics of time series data](https://tdengine.com - **[Simplified Solution](https://tdengine.com/tdengine/simplified-time-series-data-solution/)**: Through built-in caching, stream processing and data subscription features, TDengine provides a simplified solution for time-series data processing. It reduces system design complexity and operation costs significantly. -- **[Cloud Native](https://tdengine.com/tdengine/cloud-native-time-series-database/)**: Through native distributed design, sharding and partitioning, separation of compute and storage, RAFT, support for kubernetes deployment and full observability, TDengine is a cloud native Time-Series Database and can be deployed on public, private or hybrid clouds. +- **[Cloud Native](https://tdengine.com/tdengine/cloud-native-time-series-database/)**: Through native distributed design, sharding and partitioning, separation of compute and storage, RAFT, support for Kubernetes deployment and full observability, TDengine is a cloud native Time-series Database and can be deployed on public, private or hybrid clouds. - **[Ease of Use](https://tdengine.com/tdengine/easy-time-series-data-platform/)**: For administrators, TDengine significantly reduces the effort to[ ](https://tdengine.com/tdengine/easy-time-series-data-platform/) deploy and maintain. For developers, it provides a simple interface, simplified solution and seamless integrations for third party tools. For data users, it gives easy data access. @@ -58,15 +58,22 @@ By making full use of [characteristics of time series data](https://tdengine.com - **[Open Source](https://tdengine.com/tdengine/open-source-time-series-database/)**: TDengine’s core modules, including cluster feature, are all available under open source licenses. It has gathered over 19k stars on GitHub. There is an active developer community, and over 140k running instances worldwide. -With TDengine, the total cost of ownership of your time-series data platform can be greatly reduced. 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 simplified solution and nearly zero management, the operation and maintenance costs are reduced significantly. +With TDengine, the total cost of ownership of your time-series data platform can be greatly reduced. + +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 simplified solution and nearly zero management, the operation and maintenance costs are reduced significantly. ## Technical Ecosystem This is how TDengine would be situated, in a typical time-series data processing platform: +
+ ![TDengine Database Technical Ecosystem ](eco_system.webp) -
Figure 1. TDengine Technical Ecosystem
+
Figure 1. TDengine Technical Ecosystem
+
On the left-hand side, there are data collection agents like OPC-UA, MQTT, Telegraf and Kafka. On the right-hand side, visualization/BI tools, HMI, Python/R, and IoT Apps can be connected. TDengine itself provides an interactive command-line interface and a web interface for management and maintenance. diff --git a/docs/zh/02-intro.md b/docs/zh/02-intro.md index c78eb17640..012c49d2c3 100644 --- a/docs/zh/02-intro.md +++ b/docs/zh/02-intro.md @@ -85,8 +85,8 @@ TDengine 的主要功能如下: ![TDengine Database 技术生态图](eco_system.webp) +
图 1. TDengine技术生态图
-
图 1. TDengine技术生态图
上图中,左侧是各种数据采集或消息队列,包括 OPC-UA、MQTT、Telegraf、也包括 Kafka,他们的数据将被源源不断的写入到 TDengine。右侧则是可视化、BI 工具、组态软件、应用程序。下侧则是 TDengine 自身提供的命令行程序(CLI)以及可视化管理工具。 From 01aca9ffc7fb7bd5bf6769ad1a751c9bce13fc2f Mon Sep 17 00:00:00 2001 From: Minglei Jin Date: Tue, 30 Aug 2022 10:07:33 +0800 Subject: [PATCH 55/76] fix: stat timeseries every minute to avoid dnode offline caused by massive tables --- source/dnode/vnode/inc/vnode.h | 28 +++++++++++++------------ source/dnode/vnode/src/meta/metaQuery.c | 20 +++++++++++------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/source/dnode/vnode/inc/vnode.h b/source/dnode/vnode/inc/vnode.h index 3a3cbe72ba..5d4285b7c2 100644 --- a/source/dnode/vnode/inc/vnode.h +++ b/source/dnode/vnode/inc/vnode.h @@ -157,17 +157,17 @@ void tsdbCacheSetCapacity(SVnode *pVnode, size_t capacity); size_t tsdbCacheGetCapacity(SVnode *pVnode); // tq -typedef struct SMetaTableInfo{ +typedef struct SMetaTableInfo { int64_t suid; int64_t uid; SSchemaWrapper *schema; char tbName[TSDB_TABLE_NAME_LEN]; -}SMetaTableInfo; +} SMetaTableInfo; -typedef struct SIdInfo{ - int64_t version; - int32_t index; -}SIdInfo; +typedef struct SIdInfo { + int64_t version; + int32_t index; +} SIdInfo; typedef struct SSnapContext { SMeta *pMeta; @@ -180,8 +180,8 @@ typedef struct SSnapContext { SArray *idList; int32_t index; bool withMeta; - bool queryMetaOrData; // true-get meta, false-get data -}SSnapContext; + bool queryMetaOrData; // true-get meta, false-get data +} SSnapContext; typedef struct STqReader { int64_t ver; @@ -232,11 +232,12 @@ int32_t vnodeSnapWriterOpen(SVnode *pVnode, int64_t sver, int64_t ever, SVSnapWr int32_t vnodeSnapWriterClose(SVSnapWriter *pWriter, int8_t rollback, SSnapshot *pSnapshot); int32_t vnodeSnapWrite(SVSnapWriter *pWriter, uint8_t *pData, uint32_t nData); -int32_t buildSnapContext(SMeta* pMeta, int64_t snapVersion, int64_t suid, int8_t subType, bool withMeta, SSnapContext** ctxRet); -int32_t getMetafromSnapShot(SSnapContext* ctx, void **pBuf, int32_t *contLen, int16_t *type, int64_t *uid); -SMetaTableInfo getUidfromSnapShot(SSnapContext* ctx); -int32_t setForSnapShot(SSnapContext* ctx, int64_t uid); -int32_t destroySnapContext(SSnapContext* ctx); +int32_t buildSnapContext(SMeta *pMeta, int64_t snapVersion, int64_t suid, int8_t subType, bool withMeta, + SSnapContext **ctxRet); +int32_t getMetafromSnapShot(SSnapContext *ctx, void **pBuf, int32_t *contLen, int16_t *type, int64_t *uid); +SMetaTableInfo getUidfromSnapShot(SSnapContext *ctx); +int32_t setForSnapShot(SSnapContext *ctx, int64_t uid); +int32_t destroySnapContext(SSnapContext *ctx); // structs struct STsdbCfg { @@ -259,6 +260,7 @@ typedef struct { int64_t numOfNTables; int64_t numOfNTimeSeries; int64_t numOfTimeSeries; + int64_t itvTimeSeries; int64_t pointsWritten; int64_t totalStorage; int64_t compStorage; diff --git a/source/dnode/vnode/src/meta/metaQuery.c b/source/dnode/vnode/src/meta/metaQuery.c index 9d3b4d82eb..7df355a59b 100644 --- a/source/dnode/vnode/src/meta/metaQuery.c +++ b/source/dnode/vnode/src/meta/metaQuery.c @@ -615,9 +615,13 @@ int64_t metaGetTbNum(SMeta *pMeta) { // N.B. Called by statusReq per second int64_t metaGetTimeSeriesNum(SMeta *pMeta) { // sum of (number of columns of stable - 1) * number of ctables (excluding timestamp column) - int64_t num = 0; - vnodeGetTimeSeriesNum(pMeta->pVnode, &num); - pMeta->pVnode->config.vndStats.numOfTimeSeries = num; + if (pMeta->pVnode->config.vndStats.numOfTimeSeries <= 0 || ++pMeta->pVnode->config.vndStats.itvTimeSeries % 60 == 0) { + int64_t num = 0; + vnodeGetTimeSeriesNum(pMeta->pVnode, &num); + pMeta->pVnode->config.vndStats.numOfTimeSeries = num; + + pMeta->pVnode->config.vndStats.itvTimeSeries = 0; + } return pMeta->pVnode->config.vndStats.numOfTimeSeries + pMeta->pVnode->config.vndStats.numOfNTimeSeries; } @@ -890,7 +894,7 @@ const void *metaGetTableTagVal(void *pTag, int16_t type, STagVal *val) { #ifdef TAG_FILTER_DEBUG if (IS_VAR_DATA_TYPE(val->type)) { - char* buf = taosMemoryCalloc(val->nData + 1, 1); + char *buf = taosMemoryCalloc(val->nData + 1, 1); memcpy(buf, val->pData, val->nData); metaDebug("metaTag table val varchar index:%d cid:%d type:%d value:%s", 1, val->cid, val->type, buf); taosMemoryFree(buf); @@ -900,13 +904,13 @@ const void *metaGetTableTagVal(void *pTag, int16_t type, STagVal *val) { metaDebug("metaTag table val number index:%d cid:%d type:%d value:%f", 1, val->cid, val->type, dval); } - SArray* pTagVals = NULL; - tTagToValArray((STag*)pTag, &pTagVals); + SArray *pTagVals = NULL; + tTagToValArray((STag *)pTag, &pTagVals); for (int i = 0; i < taosArrayGetSize(pTagVals); i++) { - STagVal* pTagVal = (STagVal*)taosArrayGet(pTagVals, i); + STagVal *pTagVal = (STagVal *)taosArrayGet(pTagVals, i); if (IS_VAR_DATA_TYPE(pTagVal->type)) { - char* buf = taosMemoryCalloc(pTagVal->nData + 1, 1); + char *buf = taosMemoryCalloc(pTagVal->nData + 1, 1); memcpy(buf, pTagVal->pData, pTagVal->nData); metaDebug("metaTag table varchar index:%d cid:%d type:%d value:%s", i, pTagVal->cid, pTagVal->type, buf); taosMemoryFree(buf); From 24f96e1ebb7f83c4d4ca24182f55d79482090326 Mon Sep 17 00:00:00 2001 From: gccgdb1234 Date: Tue, 30 Aug 2022 10:37:42 +0800 Subject: [PATCH 56/76] doc: correct wrong description about maximum SQL length --- docs/en/07-develop/03-insert-data/05-high-volume.md | 4 ++-- docs/zh/07-develop/03-insert-data/05-high-volume.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/en/07-develop/03-insert-data/05-high-volume.md b/docs/en/07-develop/03-insert-data/05-high-volume.md index 4c677e23fc..9ea0c88447 100644 --- a/docs/en/07-develop/03-insert-data/05-high-volume.md +++ b/docs/en/07-develop/03-insert-data/05-high-volume.md @@ -16,7 +16,7 @@ To achieve high performance writing, there are a few aspects to consider. In the From the perspective of application program, you need to consider: -1. The data size of each single write, also known as batch size. Generally speaking, higher batch size generates better writing performance. However, once the batch size is over a specific value, you will not get any additional benefit anymore. When using SQL to write into TDengine, it's better to put as much as possible data in single SQL. The maximum SQL length supported by TDengine is 1,048,576 bytes, i.e. 1 MB. It can be configured by parameter `maxSQLLength` on client side, and the default value is 65,480. +1. The data size of each single write, also known as batch size. Generally speaking, higher batch size generates better writing performance. However, once the batch size is over a specific value, you will not get any additional benefit anymore. When using SQL to write into TDengine, it's better to put as much as possible data in single SQL. The maximum SQL length supported by TDengine is 1,048,576 bytes, i.e. 1 MB. 2. The number of concurrent connections. Normally more connections can get better result. However, once the number of connections exceeds the processing ability of the server side, the performance may downgrade. @@ -356,7 +356,7 @@ Writing process tries to read as much as possible data from message queue and wr
-SQLWriter class encapsulates the logic of composing SQL and writing data. Please be noted that the tables have not been created before writing, but are created automatically when catching the exception of table doesn't exist. For other exceptions caught, the SQL which caused the exception are logged for you to debug. This class also checks the SQL length, if the SQL length is closed to `maxSQLLength` the SQL will be executed immediately. To improve writing efficiency, it's better to increase `maxSQLLength` properly. +SQLWriter class encapsulates the logic of composing SQL and writing data. Please be noted that the tables have not been created before writing, but are created automatically when catching the exception of table doesn't exist. For other exceptions caught, the SQL which caused the exception are logged for you to debug. This class also checks the SQL length, and passes the maximum SQL length by parameter maxSQLLength according to actual TDengine limit. SQLWriter diff --git a/docs/zh/07-develop/03-insert-data/05-high-volume.md b/docs/zh/07-develop/03-insert-data/05-high-volume.md index a47da322a8..d7581467ae 100644 --- a/docs/zh/07-develop/03-insert-data/05-high-volume.md +++ b/docs/zh/07-develop/03-insert-data/05-high-volume.md @@ -11,7 +11,7 @@ import TabItem from "@theme/TabItem"; 从客户端程序的角度来说,高效写入数据要考虑以下几个因素: -1. 单次写入的数据量。一般来讲,每批次写入的数据量越大越高效(但超过一定阈值其优势会消失)。使用 SQL 写入 TDengine 时,尽量在一条 SQL 中拼接更多数据。目前,TDengine 支持的一条 SQL 的最大长度为 1,048,576(1M)个字符。可通过配置客户端参数 maxSQLLength(默认值为 65480)进行修改。 +1. 单次写入的数据量。一般来讲,每批次写入的数据量越大越高效(但超过一定阈值其优势会消失)。使用 SQL 写入 TDengine 时,尽量在一条 SQL 中拼接更多数据。目前,TDengine 支持的一条 SQL 的最大长度为 1,048,576(1M)个字符。 2. 并发连接数。一般来讲,同时写入数据的并发连接数越多写入越高效(但超过一定阈值反而会下降,取决于服务端处理能力)。 3. 数据在不同表(或子表)之间的分布,即要写入数据的相邻性。一般来说,每批次只向同一张表(或子表)写入数据比向多张表(或子表)写入数据要更高效; 4. 写入方式。一般来讲: @@ -348,7 +348,7 @@ main 函数可以接收 5 个启动参数,依次是:
-SQLWriter 类封装了拼 SQL 和写数据的逻辑。所有的表都没有提前创建,而是在发生表不存在错误的时候,再以超级表为模板批量建表,然后重新执行 INSERT 语句。对于其它错误会记录当时执行的 SQL, 以便排查错误和故障恢复。这个类也对 SQL 是否超过最大长度限制做了检查,如果接近 SQL 最大长度限制(maxSQLLength),将会立即执行 SQL。为了减少 SQL 此时,建议将 maxSQLLength 适当调大。 +SQLWriter 类封装了拼 SQL 和写数据的逻辑。所有的表都没有提前创建,而是在发生表不存在错误的时候,再以超级表为模板批量建表,然后重新执行 INSERT 语句。对于其它错误会记录当时执行的 SQL, 以便排查错误和故障恢复。这个类也对 SQL 是否超过最大长度限制做了检查,根据 TDengine 3.0 的限制由输入参数 maxSQLLength 传入了支持的最大 SQL 长度,即 1048576 。 SQLWriter From d338e4dc60a9834d2cec4f2d9f0a5b041ee70b04 Mon Sep 17 00:00:00 2001 From: Haojun Liao Date: Tue, 30 Aug 2022 10:38:32 +0800 Subject: [PATCH 57/76] fix(query): set correct length value. --- source/libs/function/src/builtinsimpl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/libs/function/src/builtinsimpl.c b/source/libs/function/src/builtinsimpl.c index 417875838a..1633065ea9 100644 --- a/source/libs/function/src/builtinsimpl.c +++ b/source/libs/function/src/builtinsimpl.c @@ -3627,8 +3627,7 @@ static int32_t doUpdateTupleData(SSerializeDataHandle* pHandle, const void* pBuf } static int32_t updateTupleData(SqlFunctionCtx* pCtx, int32_t rowIndex, const SSDataBlock* pSrcBlock, STuplePos* pPos) { - int32_t rowLen = 0; - int32_t completeRowSize = rowLen + pCtx->subsidiaries.num * sizeof(bool); + int32_t completeRowSize = pCtx->subsidiaries.rowLen + pCtx->subsidiaries.num * sizeof(bool); char* buf = serializeTupleData(pSrcBlock, rowIndex, &pCtx->subsidiaries, pCtx->subsidiaries.buf); doUpdateTupleData(&pCtx->saveHandle, buf, completeRowSize, pPos); return TSDB_CODE_SUCCESS; From cfb62d5e07ff02cada64c7df4d45bbbdc04addb3 Mon Sep 17 00:00:00 2001 From: afwerar <1296468573@qq.com> Date: Tue, 30 Aug 2022 10:59:16 +0800 Subject: [PATCH 58/76] feature: update cfg --- source/common/src/tglobal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/common/src/tglobal.c b/source/common/src/tglobal.c index a45845197d..0bab6a8611 100644 --- a/source/common/src/tglobal.c +++ b/source/common/src/tglobal.c @@ -438,7 +438,7 @@ static int32_t taosUpdateServerCfg(SConfig *pCfg) { return -1; } else { stype = pItem->stype; - numOfCores = pItem->i32; + numOfCores = pItem->fval; } pItem = cfgGetItem(tsCfg, "supportVnodes"); From 8b3489760c1447e9789589cf29625fa5ed5fc227 Mon Sep 17 00:00:00 2001 From: Liu Jicong Date: Tue, 30 Aug 2022 10:57:24 +0800 Subject: [PATCH 59/76] feat(tmq): support taosx --- include/client/taos.h | 1 + include/common/tcommon.h | 2 +- include/common/tmsg.h | 44 +++++--- include/util/tencode.h | 28 +++-- source/client/inc/clientInt.h | 23 +++- source/client/src/clientMain.c | 13 +++ source/client/src/tmq.c | 67 +++++++++-- source/common/src/tmsg.c | 122 +++++++++++++++++---- source/dnode/mnode/impl/src/mndSubscribe.c | 1 + 9 files changed, 243 insertions(+), 58 deletions(-) diff --git a/include/client/taos.h b/include/client/taos.h index f260b84f4a..49cfbb52b8 100644 --- a/include/client/taos.h +++ b/include/client/taos.h @@ -254,6 +254,7 @@ enum tmq_res_t { TMQ_RES_INVALID = -1, TMQ_RES_DATA = 1, TMQ_RES_TABLE_META = 2, + TMQ_RES_TAOSX = 3, }; typedef struct tmq_raw_data { diff --git a/include/common/tcommon.h b/include/common/tcommon.h index 03672f96f3..891c9ab040 100644 --- a/include/common/tcommon.h +++ b/include/common/tcommon.h @@ -73,6 +73,7 @@ enum { TMQ_MSG_TYPE__POLL_RSP, TMQ_MSG_TYPE__POLL_META_RSP, TMQ_MSG_TYPE__EP_RSP, + TMQ_MSG_TYPE__TAOSX_RSP, TMQ_MSG_TYPE__END_RSP, }; @@ -129,7 +130,6 @@ typedef struct SDataBlockInfo { uint32_t capacity; // TODO: optimize and remove following int64_t version; // used for stream, and need serialization - int64_t ts; // used for stream, and need serialization int32_t childId; // used for stream, do not serialize EStreamType type; // used for stream, do not serialize STimeWindow calWin; // used for stream, do not serialize diff --git a/include/common/tmsg.h b/include/common/tmsg.h index 681094471a..d503592361 100644 --- a/include/common/tmsg.h +++ b/include/common/tmsg.h @@ -276,7 +276,6 @@ struct SSchema { char name[TSDB_COL_NAME_LEN]; }; - typedef struct { char tbName[TSDB_TABLE_NAME_LEN]; char stbName[TSDB_TABLE_NAME_LEN]; @@ -295,17 +294,15 @@ typedef struct { SSchema* pSchemas; } STableMetaRsp; - - typedef struct { - int32_t code; - int8_t hashMeta; - int64_t uid; - char* tblFName; - int32_t numOfRows; - int32_t affectedRows; - int64_t sver; - STableMetaRsp* pMeta; + int32_t code; + int8_t hashMeta; + int64_t uid; + char* tblFName; + int32_t numOfRows; + int32_t affectedRows; + int64_t sver; + STableMetaRsp* pMeta; } SSubmitBlkRsp; typedef struct { @@ -320,7 +317,7 @@ typedef struct { int32_t tEncodeSSubmitRsp(SEncoder* pEncoder, const SSubmitRsp* pRsp); int32_t tDecodeSSubmitRsp(SDecoder* pDecoder, SSubmitRsp* pRsp); -void tFreeSSubmitBlkRsp(void* param); +void tFreeSSubmitBlkRsp(void* param); void tFreeSSubmitRsp(SSubmitRsp* pRsp); #define COL_SMA_ON ((int8_t)0x1) @@ -2049,8 +2046,8 @@ typedef struct { STableMetaRsp* pMeta; } SVCreateTbRsp, SVUpdateTbRsp; -int tEncodeSVCreateTbRsp(SEncoder* pCoder, const SVCreateTbRsp* pRsp); -int tDecodeSVCreateTbRsp(SDecoder* pCoder, SVCreateTbRsp* pRsp); +int tEncodeSVCreateTbRsp(SEncoder* pCoder, const SVCreateTbRsp* pRsp); +int tDecodeSVCreateTbRsp(SDecoder* pCoder, SVCreateTbRsp* pRsp); void tFreeSVCreateTbRsp(void* param); int32_t tSerializeSVCreateTbReq(void** buf, SVCreateTbReq* pReq); @@ -2961,6 +2958,25 @@ typedef struct { int32_t tEncodeSMqDataRsp(SEncoder* pEncoder, const SMqDataRsp* pRsp); int32_t tDecodeSMqDataRsp(SDecoder* pDecoder, SMqDataRsp* pRsp); +typedef struct { + SMqRspHead head; + STqOffsetVal reqOffset; + STqOffsetVal rspOffset; + int32_t blockNum; + int8_t withTbName; + int8_t withSchema; + SArray* blockDataLen; + SArray* blockData; + SArray* blockTbName; + SArray* blockSchema; + int32_t createTableNum; + SArray* createTableLen; + SArray* createTableReq; +} STaosxRsp; + +int32_t tEncodeSTaosxRsp(SEncoder* pEncoder, const STaosxRsp* pRsp); +int32_t tDecodeSTaosxRsp(SDecoder* pDecoder, STaosxRsp* pRsp); + typedef struct { SMqRspHead head; char cgroup[TSDB_CGROUP_LEN]; diff --git a/include/util/tencode.h b/include/util/tencode.h index ad642cd612..a6dd58297e 100644 --- a/include/util/tencode.h +++ b/include/util/tencode.h @@ -264,12 +264,14 @@ static FORCE_INLINE int32_t tEncodeDouble(SEncoder* pCoder, double val) { static FORCE_INLINE int32_t tEncodeBinary(SEncoder* pCoder, const uint8_t* val, uint32_t len) { if (tEncodeU32v(pCoder, len) < 0) return -1; - if (pCoder->data) { - if (TD_CODER_CHECK_CAPACITY_FAILED(pCoder, len)) return -1; - memcpy(TD_CODER_CURRENT(pCoder), val, len); - } + if (len) { + if (pCoder->data) { + if (TD_CODER_CHECK_CAPACITY_FAILED(pCoder, len)) return -1; + memcpy(TD_CODER_CURRENT(pCoder), val, len); + } - TD_CODER_MOVE_POS(pCoder, len); + TD_CODER_MOVE_POS(pCoder, len); + } return 0; } @@ -414,14 +416,18 @@ static int32_t tDecodeCStrTo(SDecoder* pCoder, char* val) { static FORCE_INLINE int32_t tDecodeBinaryAlloc(SDecoder* pCoder, void** val, uint64_t* len) { uint64_t length = 0; if (tDecodeU64v(pCoder, &length) < 0) return -1; - if (len) *len = length; + if (length) { + if (len) *len = length; - if (TD_CODER_CHECK_CAPACITY_FAILED(pCoder, length)) return -1; - *val = taosMemoryMalloc(length); - if (*val == NULL) return -1; - memcpy(*val, TD_CODER_CURRENT(pCoder), length); + if (TD_CODER_CHECK_CAPACITY_FAILED(pCoder, length)) return -1; + *val = taosMemoryMalloc(length); + if (*val == NULL) return -1; + memcpy(*val, TD_CODER_CURRENT(pCoder), length); - TD_CODER_MOVE_POS(pCoder, length); + TD_CODER_MOVE_POS(pCoder, length); + } else { + *val = NULL; + } return 0; } diff --git a/source/client/inc/clientInt.h b/source/client/inc/clientInt.h index 07e5f75e87..c26208b9b9 100644 --- a/source/client/inc/clientInt.h +++ b/source/client/inc/clientInt.h @@ -52,15 +52,17 @@ enum { RES_TYPE__QUERY = 1, RES_TYPE__TMQ, RES_TYPE__TMQ_META, + RES_TYPE__TAOSX, }; #define SHOW_VARIABLES_RESULT_COLS 2 #define SHOW_VARIABLES_RESULT_FIELD1_LEN (TSDB_CONFIG_OPTION_LEN + VARSTR_HEADER_SIZE) #define SHOW_VARIABLES_RESULT_FIELD2_LEN (TSDB_CONFIG_VALUE_LEN + VARSTR_HEADER_SIZE) -#define TD_RES_QUERY(res) (*(int8_t*)res == RES_TYPE__QUERY) -#define TD_RES_TMQ(res) (*(int8_t*)res == RES_TYPE__TMQ) -#define TD_RES_TMQ_META(res) (*(int8_t*)res == RES_TYPE__TMQ_META) +#define TD_RES_QUERY(res) (*(int8_t*)res == RES_TYPE__QUERY) +#define TD_RES_TMQ(res) (*(int8_t*)res == RES_TYPE__TMQ || *(int8_t*)res == RES_TYPE__TAOSX) +#define TD_RES_TMQ_META(res) (*(int8_t*)res == RES_TYPE__TMQ_META) +#define TD_RES_TMQ_TAOSX(res) (*(int8_t*)res == RES_TYPE__TAOSX) typedef struct SAppInstInfo SAppInstInfo; @@ -198,8 +200,8 @@ typedef struct { int32_t vgId; SSchemaWrapper schema; int32_t resIter; - SMqDataRsp rsp; SReqResultInfo resInfo; + SMqDataRsp rsp; } SMqRspObj; typedef struct { @@ -210,6 +212,17 @@ typedef struct { SMqMetaRsp metaRsp; } SMqMetaRspObj; +typedef struct { + int8_t resType; + char topic[TSDB_TOPIC_FNAME_LEN]; + char db[TSDB_DB_FNAME_LEN]; + int32_t vgId; + SSchemaWrapper schema; + int32_t resIter; + SReqResultInfo resInfo; + STaosxRsp rsp; +} SMqTaosxRspObj; + typedef struct SRequestObj { int8_t resType; // query or tmq uint64_t requestId; @@ -369,7 +382,7 @@ void launchAsyncQuery(SRequestObj* pRequest, SQuery* pQuery, SMetaData* int32_t refreshMeta(STscObj* pTscObj, SRequestObj* pRequest); int32_t updateQnodeList(SAppInstInfo* pInfo, SArray* pNodeList); void doAsyncQuery(SRequestObj* pRequest, bool forceUpdateMeta); -int32_t removeMeta(STscObj* pTscObj, SArray* tbList); +int32_t removeMeta(STscObj* pTscObj, SArray* tbList); int32_t handleAlterTbExecRes(void* res, struct SCatalog* pCatalog); int32_t handleCreateTbExecRes(void* res, SCatalog* pCatalog); bool qnodeRequired(SRequestObj* pRequest); diff --git a/source/client/src/clientMain.c b/source/client/src/clientMain.c index 9ceb6e0683..3086078080 100644 --- a/source/client/src/clientMain.c +++ b/source/client/src/clientMain.c @@ -184,6 +184,19 @@ void taos_free_result(TAOS_RES *res) { SRequestObj *pRequest = (SRequestObj *)res; tscDebug("0x%" PRIx64 " taos_free_result start to free query", pRequest->requestId); destroyRequest(pRequest); + } else if (TD_RES_TMQ_TAOSX(res)) { + SMqTaosxRspObj *pRsp = (SMqTaosxRspObj *)res; + if (pRsp->rsp.blockData) taosArrayDestroyP(pRsp->rsp.blockData, taosMemoryFree); + if (pRsp->rsp.blockDataLen) taosArrayDestroy(pRsp->rsp.blockDataLen); + if (pRsp->rsp.withTbName) taosArrayDestroyP(pRsp->rsp.blockTbName, taosMemoryFree); + if (pRsp->rsp.withSchema) taosArrayDestroyP(pRsp->rsp.blockSchema, (FDelete)tDeleteSSchemaWrapper); + // taosx + taosArrayDestroy(pRsp->rsp.createTableLen); + taosArrayDestroyP(pRsp->rsp.createTableReq, taosMemoryFree); + + pRsp->resInfo.pRspMsg = NULL; + doFreeReqResultInfo(&pRsp->resInfo); + taosMemoryFree(pRsp); } else if (TD_RES_TMQ(res)) { SMqRspObj *pRsp = (SMqRspObj *)res; if (pRsp->rsp.blockData) taosArrayDestroyP(pRsp->rsp.blockData, taosMemoryFree); diff --git a/source/client/src/tmq.c b/source/client/src/tmq.c index fa657fcb10..29d509c27c 100644 --- a/source/client/src/tmq.c +++ b/source/client/src/tmq.c @@ -164,6 +164,7 @@ typedef struct { union { SMqDataRsp dataRsp; SMqMetaRsp metaRsp; + STaosxRsp taosxRsp; }; } SMqPollRspWrapper; @@ -1130,21 +1131,29 @@ int32_t tmqPollCb(void* param, SDataBuf* pMsg, int32_t code) { tDecodeSMqDataRsp(&decoder, &pRspWrapper->dataRsp); tDecoderClear(&decoder); memcpy(&pRspWrapper->dataRsp, pMsg->pData, sizeof(SMqRspHead)); - } else { - ASSERT(rspType == TMQ_MSG_TYPE__POLL_META_RSP); + + tscDebug("consumer:%" PRId64 ", recv poll: vgId:%d, req offset %" PRId64 ", rsp offset %" PRId64 " type %d", + tmq->consumerId, pVg->vgId, pRspWrapper->dataRsp.reqOffset.version, pRspWrapper->dataRsp.rspOffset.version, + rspType); + + } else if (rspType == TMQ_MSG_TYPE__POLL_META_RSP) { SDecoder decoder; tDecoderInit(&decoder, POINTER_SHIFT(pMsg->pData, sizeof(SMqRspHead)), pMsg->len - sizeof(SMqRspHead)); tDecodeSMqMetaRsp(&decoder, &pRspWrapper->metaRsp); tDecoderClear(&decoder); memcpy(&pRspWrapper->metaRsp, pMsg->pData, sizeof(SMqRspHead)); + } else if (rspType == TMQ_MSG_TYPE__TAOSX_RSP) { + SDecoder decoder; + tDecoderInit(&decoder, POINTER_SHIFT(pMsg->pData, sizeof(SMqRspHead)), pMsg->len - sizeof(SMqRspHead)); + tDecodeSTaosxRsp(&decoder, &pRspWrapper->taosxRsp); + tDecoderClear(&decoder); + memcpy(&pRspWrapper->taosxRsp, pMsg->pData, sizeof(SMqRspHead)); + } else { + ASSERT(0); } taosMemoryFree(pMsg->pData); - tscDebug("consumer:%" PRId64 ", recv poll: vgId:%d, req offset %" PRId64 ", rsp offset %" PRId64 " type %d", - tmq->consumerId, pVg->vgId, pRspWrapper->dataRsp.reqOffset.version, pRspWrapper->dataRsp.rspOffset.version, - rspType); - taosWriteQitem(tmq->mqueue, pRspWrapper); tsem_post(&tmq->rspSem); @@ -1443,6 +1452,24 @@ SMqRspObj* tmqBuildRspFromWrapper(SMqPollRspWrapper* pWrapper) { return pRspObj; } +SMqTaosxRspObj* tmqBuildTaosxRspFromWrapper(SMqPollRspWrapper* pWrapper) { + SMqTaosxRspObj* pRspObj = taosMemoryCalloc(1, sizeof(SMqTaosxRspObj)); + pRspObj->resType = RES_TYPE__TAOSX; + tstrncpy(pRspObj->topic, pWrapper->topicHandle->topicName, TSDB_TOPIC_FNAME_LEN); + tstrncpy(pRspObj->db, pWrapper->topicHandle->db, TSDB_DB_FNAME_LEN); + pRspObj->vgId = pWrapper->vgHandle->vgId; + pRspObj->resIter = -1; + memcpy(&pRspObj->rsp, &pWrapper->dataRsp, sizeof(SMqTaosxRspObj)); + + pRspObj->resInfo.totalRows = 0; + pRspObj->resInfo.precision = TSDB_TIME_PRECISION_MILLI; + if (!pWrapper->dataRsp.withSchema) { + setResSchemaInfo(&pRspObj->resInfo, pWrapper->topicHandle->schema.pSchema, pWrapper->topicHandle->schema.nCols); + } + + return pRspObj; +} + int32_t tmqPollImpl(tmq_t* tmq, int64_t timeout) { /*tscDebug("call poll");*/ for (int i = 0; i < taosArrayGetSize(tmq->clientTopics); i++) { @@ -1595,6 +1622,30 @@ void* tmqHandleAllRsp(tmq_t* tmq, int64_t timeout, bool pollIfReset) { pollRspWrapper->metaRsp.head.epoch, consumerEpoch); taosFreeQitem(pollRspWrapper); } + } else if (rspWrapper->tmqRspType == TMQ_MSG_TYPE__TAOSX_RSP) { + SMqPollRspWrapper* pollRspWrapper = (SMqPollRspWrapper*)rspWrapper; + /*atomic_sub_fetch_32(&tmq->readyRequest, 1);*/ + int32_t consumerEpoch = atomic_load_32(&tmq->epoch); + if (pollRspWrapper->taosxRsp.head.epoch == consumerEpoch) { + SMqClientVg* pVg = pollRspWrapper->vgHandle; + /*printf("vgId:%d, offset %" PRId64 " up to %" PRId64 "\n", pVg->vgId, pVg->currentOffset, + * rspMsg->msg.rspOffset);*/ + pVg->currentOffset = pollRspWrapper->taosxRsp.rspOffset; + atomic_store_32(&pVg->vgStatus, TMQ_VG_STATUS__IDLE); + if (pollRspWrapper->taosxRsp.blockNum == 0) { + taosFreeQitem(pollRspWrapper); + rspWrapper = NULL; + continue; + } + // build rsp + SMqRspObj* pRsp = tmqBuildRspFromWrapper(pollRspWrapper); + taosFreeQitem(pollRspWrapper); + return pRsp; + } else { + tscDebug("msg discard since epoch mismatch: msg epoch %d, consumer epoch %d\n", + pollRspWrapper->taosxRsp.head.epoch, consumerEpoch); + taosFreeQitem(pollRspWrapper); + } } else { /*printf("handle ep rsp %d\n", rspMsg->head.mqMsgType);*/ bool reset = false; @@ -1707,9 +1758,11 @@ tmq_res_t tmq_get_res_type(TAOS_RES* res) { } else if (TD_RES_TMQ_META(res)) { SMqMetaRspObj* pMetaRspObj = (SMqMetaRspObj*)res; if (pMetaRspObj->metaRsp.resMsgType == TDMT_VND_DELETE) { - return TMQ_RES_DATA; + return TMQ_RES_TAOSX; } return TMQ_RES_TABLE_META; + } else if (TD_RES_TMQ_TAOSX(res)) { + return TMQ_RES_TAOSX; } else { return TMQ_RES_INVALID; } diff --git a/source/common/src/tmsg.c b/source/common/src/tmsg.c index 8dc4931573..2fc93cc9b5 100644 --- a/source/common/src/tmsg.c +++ b/source/common/src/tmsg.c @@ -3330,7 +3330,7 @@ int32_t tDeserializeSSTbHbRsp(void *buf, int32_t bufLen, SSTbHbRsp *pRsp) { return 0; } -void tFreeSTableMetaRsp(void *pRsp) { taosMemoryFreeClear(((STableMetaRsp*)pRsp)->pSchemas); } +void tFreeSTableMetaRsp(void *pRsp) { taosMemoryFreeClear(((STableMetaRsp *)pRsp)->pSchemas); } void tFreeSTableIndexRsp(void *info) { if (NULL == info) { @@ -5119,17 +5119,17 @@ int tDecodeSVCreateTbRsp(SDecoder *pCoder, SVCreateTbRsp *pRsp) { } else { pRsp->pMeta = NULL; } - + tEndDecode(pCoder); return 0; } -void tFreeSVCreateTbRsp(void* param) { +void tFreeSVCreateTbRsp(void *param) { if (NULL == param) { return; } - - SVCreateTbRsp* pRsp = (SVCreateTbRsp*)param; + + SVCreateTbRsp *pRsp = (SVCreateTbRsp *)param; if (pRsp->pMeta) { taosMemoryFree(pRsp->pMeta->pSchemas); taosMemoryFree(pRsp->pMeta); @@ -5345,7 +5345,7 @@ static int32_t tDecodeSSubmitBlkRsp(SDecoder *pDecoder, SSubmitBlkRsp *pBlock) { if (tDecodeI32v(pDecoder, &pBlock->numOfRows) < 0) return -1; if (tDecodeI32v(pDecoder, &pBlock->affectedRows) < 0) return -1; if (tDecodeI64v(pDecoder, &pBlock->sver) < 0) return -1; - + int32_t meta = 0; if (tDecodeI32(pDecoder, &meta) < 0) return -1; if (meta) { @@ -5393,12 +5393,12 @@ int32_t tDecodeSSubmitRsp(SDecoder *pDecoder, SSubmitRsp *pRsp) { return 0; } -void tFreeSSubmitBlkRsp(void* param) { +void tFreeSSubmitBlkRsp(void *param) { if (NULL == param) { return; } - - SSubmitBlkRsp* pRsp = (SSubmitBlkRsp*)param; + + SSubmitBlkRsp *pRsp = (SSubmitBlkRsp *)param; taosMemoryFree(pRsp->tblFName); if (pRsp->pMeta) { @@ -5407,7 +5407,6 @@ void tFreeSSubmitBlkRsp(void* param) { } } - void tFreeSSubmitRsp(SSubmitRsp *pRsp) { if (NULL == pRsp) return; @@ -5619,7 +5618,6 @@ void tFreeSMAlterStbRsp(SMAlterStbRsp *pRsp) { } } - int32_t tEncodeSMCreateStbRsp(SEncoder *pEncoder, const SMCreateStbRsp *pRsp) { if (tStartEncode(pEncoder) < 0) return -1; if (tEncodeI32(pEncoder, pRsp->pMeta->pSchemas ? 1 : 0) < 0) return -1; @@ -5671,8 +5669,6 @@ void tFreeSMCreateStbRsp(SMCreateStbRsp *pRsp) { } } - - int32_t tEncodeSTqOffsetVal(SEncoder *pEncoder, const STqOffsetVal *pOffsetVal) { if (tEncodeI8(pEncoder, pOffsetVal->type) < 0) return -1; if (pOffsetVal->type == TMQ_OFFSET__SNAPSHOT_DATA || pOffsetVal->type == TMQ_OFFSET__SNAPSHOT_META) { @@ -5690,7 +5686,7 @@ int32_t tEncodeSTqOffsetVal(SEncoder *pEncoder, const STqOffsetVal *pOffsetVal) int32_t tDecodeSTqOffsetVal(SDecoder *pDecoder, STqOffsetVal *pOffsetVal) { if (tDecodeI8(pDecoder, &pOffsetVal->type) < 0) return -1; - if (pOffsetVal->type == TMQ_OFFSET__SNAPSHOT_DATA || pOffsetVal->type == TMQ_OFFSET__SNAPSHOT_META) { + if (pOffsetVal->type == TMQ_OFFSET__SNAPSHOT_DATA || pOffsetVal->type == TMQ_OFFSET__SNAPSHOT_META) { if (tDecodeI64(pDecoder, &pOffsetVal->uid) < 0) return -1; if (tDecodeI64(pDecoder, &pOffsetVal->ts) < 0) return -1; } else if (pOffsetVal->type == TMQ_OFFSET__LOG) { @@ -5712,7 +5708,7 @@ int32_t tFormatOffset(char *buf, int32_t maxLen, const STqOffsetVal *pVal) { snprintf(buf, maxLen, "offset(reset to latest)"); } else if (pVal->type == TMQ_OFFSET__LOG) { snprintf(buf, maxLen, "offset(log) ver:%" PRId64, pVal->version); - } else if (pVal->type == TMQ_OFFSET__SNAPSHOT_DATA || pVal->type == TMQ_OFFSET__SNAPSHOT_META) { + } else if (pVal->type == TMQ_OFFSET__SNAPSHOT_DATA || pVal->type == TMQ_OFFSET__SNAPSHOT_META) { snprintf(buf, maxLen, "offset(ss data) uid:%" PRId64 ", ts:%" PRId64, pVal->uid, pVal->ts); } else { ASSERT(0); @@ -5813,17 +5809,17 @@ int32_t tDecodeDeleteRes(SDecoder *pCoder, SDeleteRes *pRes) { return 0; } -int32_t tEncodeSMqMetaRsp(SEncoder* pEncoder, const SMqMetaRsp* pRsp) { +int32_t tEncodeSMqMetaRsp(SEncoder *pEncoder, const SMqMetaRsp *pRsp) { if (tEncodeSTqOffsetVal(pEncoder, &pRsp->rspOffset) < 0) return -1; - if(tEncodeI16(pEncoder, pRsp->resMsgType)) return -1; - if(tEncodeBinary(pEncoder, pRsp->metaRsp, pRsp->metaRspLen)) return -1; + if (tEncodeI16(pEncoder, pRsp->resMsgType)) return -1; + if (tEncodeBinary(pEncoder, pRsp->metaRsp, pRsp->metaRspLen)) return -1; return 0; } -int32_t tDecodeSMqMetaRsp(SDecoder* pDecoder, SMqMetaRsp* pRsp) { +int32_t tDecodeSMqMetaRsp(SDecoder *pDecoder, SMqMetaRsp *pRsp) { if (tDecodeSTqOffsetVal(pDecoder, &pRsp->rspOffset) < 0) return -1; if (tDecodeI16(pDecoder, &pRsp->resMsgType) < 0) return -1; - if (tDecodeBinaryAlloc(pDecoder, &pRsp->metaRsp, (uint64_t*)&pRsp->metaRspLen) < 0) return -1; + if (tDecodeBinaryAlloc(pDecoder, &pRsp->metaRsp, (uint64_t *)&pRsp->metaRspLen) < 0) return -1; return 0; } @@ -5893,6 +5889,92 @@ int32_t tDecodeSMqDataRsp(SDecoder *pDecoder, SMqDataRsp *pRsp) { return 0; } +int32_t tEncodeSTaosxRsp(SEncoder *pEncoder, const STaosxRsp *pRsp) { + if (tEncodeSTqOffsetVal(pEncoder, &pRsp->reqOffset) < 0) return -1; + if (tEncodeSTqOffsetVal(pEncoder, &pRsp->rspOffset) < 0) return -1; + if (tEncodeI32(pEncoder, pRsp->blockNum) < 0) return -1; + if (pRsp->blockNum != 0) { + if (tEncodeI8(pEncoder, pRsp->withTbName) < 0) return -1; + if (tEncodeI8(pEncoder, pRsp->withSchema) < 0) return -1; + + for (int32_t i = 0; i < pRsp->blockNum; i++) { + int32_t bLen = *(int32_t *)taosArrayGet(pRsp->blockDataLen, i); + void *data = taosArrayGetP(pRsp->blockData, i); + if (tEncodeBinary(pEncoder, (const uint8_t *)data, bLen) < 0) return -1; + if (pRsp->withSchema) { + SSchemaWrapper *pSW = (SSchemaWrapper *)taosArrayGetP(pRsp->blockSchema, i); + if (tEncodeSSchemaWrapper(pEncoder, pSW) < 0) return -1; + } + if (pRsp->withTbName) { + char *tbName = (char *)taosArrayGetP(pRsp->blockTbName, i); + if (tEncodeCStr(pEncoder, tbName) < 0) return -1; + } + } + } + if (tEncodeI32(pEncoder, pRsp->createTableNum) < 0) return -1; + if (pRsp->createTableNum) { + for (int32_t i = 0; i < pRsp->createTableNum; i++) { + void *createTableReq = taosArrayGetP(pRsp->createTableReq, i); + int32_t createTableLen = *(int32_t *)taosArrayGet(pRsp->createTableLen, i); + if (tEncodeBinary(pEncoder, createTableReq, createTableLen) < 0) return -1; + } + } + return 0; +} + +int32_t tDecodeSTaosxRsp(SDecoder *pDecoder, STaosxRsp *pRsp) { + if (tDecodeSTqOffsetVal(pDecoder, &pRsp->reqOffset) < 0) return -1; + if (tDecodeSTqOffsetVal(pDecoder, &pRsp->rspOffset) < 0) return -1; + if (tDecodeI32(pDecoder, &pRsp->blockNum) < 0) return -1; + if (pRsp->blockNum != 0) { + pRsp->blockData = taosArrayInit(pRsp->blockNum, sizeof(void *)); + pRsp->blockDataLen = taosArrayInit(pRsp->blockNum, sizeof(int32_t)); + if (tDecodeI8(pDecoder, &pRsp->withTbName) < 0) return -1; + if (tDecodeI8(pDecoder, &pRsp->withSchema) < 0) return -1; + if (pRsp->withTbName) { + pRsp->blockTbName = taosArrayInit(pRsp->blockNum, sizeof(void *)); + } + if (pRsp->withSchema) { + pRsp->blockSchema = taosArrayInit(pRsp->blockNum, sizeof(void *)); + } + + for (int32_t i = 0; i < pRsp->blockNum; i++) { + void *data; + uint64_t bLen; + if (tDecodeBinaryAlloc(pDecoder, &data, &bLen) < 0) return -1; + taosArrayPush(pRsp->blockData, &data); + int32_t len = bLen; + taosArrayPush(pRsp->blockDataLen, &len); + + if (pRsp->withSchema) { + SSchemaWrapper *pSW = (SSchemaWrapper *)taosMemoryCalloc(1, sizeof(SSchemaWrapper)); + if (pSW == NULL) return -1; + if (tDecodeSSchemaWrapper(pDecoder, pSW) < 0) return -1; + taosArrayPush(pRsp->blockSchema, &pSW); + } + + if (pRsp->withTbName) { + char *tbName; + if (tDecodeCStrAlloc(pDecoder, &tbName) < 0) return -1; + taosArrayPush(pRsp->blockTbName, &tbName); + } + } + } + if (tDecodeI32(pDecoder, &pRsp->createTableNum) < 0) return -1; + if (pRsp->createTableNum) { + pRsp->createTableLen = taosArrayInit(pRsp->createTableNum, sizeof(int32_t)); + pRsp->createTableReq = taosArrayInit(pRsp->createTableNum, sizeof(void *)); + for (int32_t i = 0; i < pRsp->createTableNum; i++) { + void *pCreate = NULL; + uint64_t len; + if (tDecodeBinaryAlloc(pDecoder, &pCreate, &len) < 0) return -1; + int32_t l = (int32_t)len; + taosArrayPush(pRsp->createTableLen, &l); + taosArrayPush(pRsp->createTableReq, &pCreate); + } + } + return 0; +} int32_t tEncodeSSingleDeleteReq(SEncoder *pEncoder, const SSingleDeleteReq *pReq) { if (tEncodeI64(pEncoder, pReq->uid) < 0) return -1; if (tEncodeI64(pEncoder, pReq->ts) < 0) return -1; diff --git a/source/dnode/mnode/impl/src/mndSubscribe.c b/source/dnode/mnode/impl/src/mndSubscribe.c index 10e520d9ec..1452c5ae2f 100644 --- a/source/dnode/mnode/impl/src/mndSubscribe.c +++ b/source/dnode/mnode/impl/src/mndSubscribe.c @@ -287,6 +287,7 @@ static int32_t mndDoRebalance(SMnode *pMnode, const SMqRebInputObj *pInput, SMqR if (consumerVgNum > minVgCnt) { if (imbCnt < imbConsumerNum) { if (consumerVgNum == minVgCnt + 1) { + imbCnt++; continue; } else { // pop until equal minVg + 1 From ad35a67f2fba26ca02c7440d4ab5cca070e55641 Mon Sep 17 00:00:00 2001 From: dapan1121 Date: Tue, 30 Aug 2022 11:30:43 +0800 Subject: [PATCH 60/76] fix: add table comment in show create table result --- source/libs/command/src/command.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/libs/command/src/command.c b/source/libs/command/src/command.c index 7d259fe06c..18d839e109 100644 --- a/source/libs/command/src/command.c +++ b/source/libs/command/src/command.c @@ -471,6 +471,7 @@ static int32_t setCreateTBResultIntoDataBlock(SSDataBlock* pBlock, SDbCfgInfo* p len += sprintf(buf2 + VARSTR_HEADER_SIZE, "CREATE TABLE `%s` (", tbName); appendColumnFields(buf2, &len, pCfg); len += sprintf(buf2 + VARSTR_HEADER_SIZE + len, ")"); + appendTableOptions(buf2, &len, pDbCfg, pCfg); } varDataLen(buf2) = len; From 5531a5a121155cf2b28bbadda404a7e0e5d62e31 Mon Sep 17 00:00:00 2001 From: Cary Xu Date: Tue, 30 Aug 2022 11:33:34 +0800 Subject: [PATCH 61/76] enh: rsam fetch all logic optimization --- source/dnode/vnode/src/inc/sma.h | 1 + source/dnode/vnode/src/sma/smaRollup.c | 9 +++++++-- source/dnode/vnode/src/sma/smaSnapshot.c | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/source/dnode/vnode/src/inc/sma.h b/source/dnode/vnode/src/inc/sma.h index abfffc045f..c29c4cb6c4 100644 --- a/source/dnode/vnode/src/inc/sma.h +++ b/source/dnode/vnode/src/inc/sma.h @@ -95,6 +95,7 @@ struct SRSmaStat { int64_t refId; // shared by fetch tasks volatile int64_t nBufItems; // number of items in queue buffer SRWLatch lock; // r/w lock for rsma fs(e.g. qtaskinfo) + volatile int32_t nFetchAll; // active number of fetch all int8_t triggerStat; // shared by fetch tasks int8_t commitStat; // 0 not in committing, 1 in committing SArray *aTaskFile; // qTaskFiles committed recently(for recovery/snapshot r/w) diff --git a/source/dnode/vnode/src/sma/smaRollup.c b/source/dnode/vnode/src/sma/smaRollup.c index 52b08d131c..89cdd58c4e 100644 --- a/source/dnode/vnode/src/sma/smaRollup.c +++ b/source/dnode/vnode/src/sma/smaRollup.c @@ -1714,9 +1714,14 @@ int32_t tdRSmaProcessExecImpl(SSma *pSma, ERsmaExecType type) { smaDebug("vgId:%d, batchSize:%d, execType:%" PRIi8, SMA_VID(pSma), qallItemSize, type); } - if (atomic_val_compare_exchange_8(RSMA_COMMIT_STAT(pRSmaStat), 0, 2) == 0) { + int8_t oldStat = atomic_val_compare_exchange_8(RSMA_COMMIT_STAT(pRSmaStat), 0, 2); + if (oldStat == 0 || + ((oldStat == 2) && atomic_load_8(RSMA_TRIGGER_STAT(pRSmaStat)) < TASK_TRIGGER_STAT_PAUSED)) { + atomic_fetch_add_32(&pRSmaStat->nFetchAll, 1); tdRSmaFetchAllResult(pSma, pInfo, pSubmitArr); - atomic_store_8(RSMA_COMMIT_STAT(pRSmaStat), 0); + if (0 == atomic_sub_fetch_32(&pRSmaStat->nFetchAll, 1)) { + atomic_store_8(RSMA_COMMIT_STAT(pRSmaStat), 0); + } } if (qallItemSize > 0) { diff --git a/source/dnode/vnode/src/sma/smaSnapshot.c b/source/dnode/vnode/src/sma/smaSnapshot.c index 335c15a539..28d7218bc8 100644 --- a/source/dnode/vnode/src/sma/smaSnapshot.c +++ b/source/dnode/vnode/src/sma/smaSnapshot.c @@ -80,7 +80,7 @@ int32_t rsmaSnapReaderOpen(SSma* pSma, int64_t sver, int64_t ever, SRsmaSnapRead goto _err; } pReader->pQTaskFReader->pReadH = qTaskF; -#if 0 +#if 1 SQTaskFile* pQTaskF = &pReader->pQTaskFReader->fTask; pQTaskF->nRef = 1; #endif From 7a67a9be4b424e1ff84ac22a6b123c8a67c9fb43 Mon Sep 17 00:00:00 2001 From: Cary Xu Date: Tue, 30 Aug 2022 11:44:28 +0800 Subject: [PATCH 62/76] enh: rsma fetch all logic optimization --- source/dnode/vnode/src/inc/sma.h | 1 + source/dnode/vnode/src/sma/smaRollup.c | 9 +++++++-- source/dnode/vnode/src/sma/smaSnapshot.c | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/source/dnode/vnode/src/inc/sma.h b/source/dnode/vnode/src/inc/sma.h index abfffc045f..c29c4cb6c4 100644 --- a/source/dnode/vnode/src/inc/sma.h +++ b/source/dnode/vnode/src/inc/sma.h @@ -95,6 +95,7 @@ struct SRSmaStat { int64_t refId; // shared by fetch tasks volatile int64_t nBufItems; // number of items in queue buffer SRWLatch lock; // r/w lock for rsma fs(e.g. qtaskinfo) + volatile int32_t nFetchAll; // active number of fetch all int8_t triggerStat; // shared by fetch tasks int8_t commitStat; // 0 not in committing, 1 in committing SArray *aTaskFile; // qTaskFiles committed recently(for recovery/snapshot r/w) diff --git a/source/dnode/vnode/src/sma/smaRollup.c b/source/dnode/vnode/src/sma/smaRollup.c index 52b08d131c..89cdd58c4e 100644 --- a/source/dnode/vnode/src/sma/smaRollup.c +++ b/source/dnode/vnode/src/sma/smaRollup.c @@ -1714,9 +1714,14 @@ int32_t tdRSmaProcessExecImpl(SSma *pSma, ERsmaExecType type) { smaDebug("vgId:%d, batchSize:%d, execType:%" PRIi8, SMA_VID(pSma), qallItemSize, type); } - if (atomic_val_compare_exchange_8(RSMA_COMMIT_STAT(pRSmaStat), 0, 2) == 0) { + int8_t oldStat = atomic_val_compare_exchange_8(RSMA_COMMIT_STAT(pRSmaStat), 0, 2); + if (oldStat == 0 || + ((oldStat == 2) && atomic_load_8(RSMA_TRIGGER_STAT(pRSmaStat)) < TASK_TRIGGER_STAT_PAUSED)) { + atomic_fetch_add_32(&pRSmaStat->nFetchAll, 1); tdRSmaFetchAllResult(pSma, pInfo, pSubmitArr); - atomic_store_8(RSMA_COMMIT_STAT(pRSmaStat), 0); + if (0 == atomic_sub_fetch_32(&pRSmaStat->nFetchAll, 1)) { + atomic_store_8(RSMA_COMMIT_STAT(pRSmaStat), 0); + } } if (qallItemSize > 0) { diff --git a/source/dnode/vnode/src/sma/smaSnapshot.c b/source/dnode/vnode/src/sma/smaSnapshot.c index 335c15a539..28d7218bc8 100644 --- a/source/dnode/vnode/src/sma/smaSnapshot.c +++ b/source/dnode/vnode/src/sma/smaSnapshot.c @@ -80,7 +80,7 @@ int32_t rsmaSnapReaderOpen(SSma* pSma, int64_t sver, int64_t ever, SRsmaSnapRead goto _err; } pReader->pQTaskFReader->pReadH = qTaskF; -#if 0 +#if 1 SQTaskFile* pQTaskF = &pReader->pQTaskFReader->fTask; pQTaskF->nRef = 1; #endif From 3b3abbc293e85721f13edbb78a1401f1a927bcb3 Mon Sep 17 00:00:00 2001 From: Cary Xu Date: Tue, 30 Aug 2022 11:47:23 +0800 Subject: [PATCH 63/76] other: revert the code --- source/dnode/vnode/src/sma/smaSnapshot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/dnode/vnode/src/sma/smaSnapshot.c b/source/dnode/vnode/src/sma/smaSnapshot.c index 28d7218bc8..335c15a539 100644 --- a/source/dnode/vnode/src/sma/smaSnapshot.c +++ b/source/dnode/vnode/src/sma/smaSnapshot.c @@ -80,7 +80,7 @@ int32_t rsmaSnapReaderOpen(SSma* pSma, int64_t sver, int64_t ever, SRsmaSnapRead goto _err; } pReader->pQTaskFReader->pReadH = qTaskF; -#if 1 +#if 0 SQTaskFile* pQTaskF = &pReader->pQTaskFReader->fTask; pQTaskF->nRef = 1; #endif From e257bd3986cacc260eebc30aeae9f56c559a765b Mon Sep 17 00:00:00 2001 From: Cary Xu Date: Tue, 30 Aug 2022 11:50:50 +0800 Subject: [PATCH 64/76] other: code optimization --- source/dnode/vnode/src/sma/smaCommit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/dnode/vnode/src/sma/smaCommit.c b/source/dnode/vnode/src/sma/smaCommit.c index 4bd3cfa5ac..4d1dcd6909 100644 --- a/source/dnode/vnode/src/sma/smaCommit.c +++ b/source/dnode/vnode/src/sma/smaCommit.c @@ -327,7 +327,7 @@ static int32_t tdProcessRSmaAsyncPreCommitImpl(SSma *pSma) { ASSERT(pRSmaStat->commitAppliedVer > 0); // step 2: wait for all triggered fetch tasks to finish - + nLoops = 0; while (1) { if (T_REF_VAL_GET(pStat) == 0) { smaDebug("vgId:%d, rsma commit, fetch tasks are all finished", SMA_VID(pSma)); From e5f5d3710dc86ffae0baa930a6f85b1f51078e5f Mon Sep 17 00:00:00 2001 From: Haojun Liao Date: Tue, 30 Aug 2022 12:48:06 +0800 Subject: [PATCH 65/76] fix(query): set correct value length --- source/libs/function/src/builtinsimpl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/libs/function/src/builtinsimpl.c b/source/libs/function/src/builtinsimpl.c index 1633065ea9..b71d06231e 100644 --- a/source/libs/function/src/builtinsimpl.c +++ b/source/libs/function/src/builtinsimpl.c @@ -3627,9 +3627,8 @@ static int32_t doUpdateTupleData(SSerializeDataHandle* pHandle, const void* pBuf } static int32_t updateTupleData(SqlFunctionCtx* pCtx, int32_t rowIndex, const SSDataBlock* pSrcBlock, STuplePos* pPos) { - int32_t completeRowSize = pCtx->subsidiaries.rowLen + pCtx->subsidiaries.num * sizeof(bool); char* buf = serializeTupleData(pSrcBlock, rowIndex, &pCtx->subsidiaries, pCtx->subsidiaries.buf); - doUpdateTupleData(&pCtx->saveHandle, buf, completeRowSize, pPos); + doUpdateTupleData(&pCtx->saveHandle, buf, pCtx->subsidiaries.rowLen, pPos); return TSDB_CODE_SUCCESS; } From 55292eebe600ef98e05809edc477f65b001529d6 Mon Sep 17 00:00:00 2001 From: huolibo Date: Tue, 30 Aug 2022 13:23:36 +0800 Subject: [PATCH 66/76] docs: add taosKeeper document (#16384) --- docs/en/14-reference/14-taosKeeper.md | 27 +++++++++++++++++--------- docs/zh/14-reference/14-taosKeeper.md | 28 ++++++++++++++++++--------- 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/docs/en/14-reference/14-taosKeeper.md b/docs/en/14-reference/14-taosKeeper.md index 476b5a1fd2..665bc75380 100644 --- a/docs/en/14-reference/14-taosKeeper.md +++ b/docs/en/14-reference/14-taosKeeper.md @@ -1,7 +1,7 @@ --- sidebar_label: taosKeeper title: taosKeeper -description: Instructions and tips for using taosKeeper +description: exports TDengine monitoring metrics. --- ## Introduction @@ -22,26 +22,35 @@ You can compile taosKeeper separately and install it. Please refer to the [taosK ### Configuration and running methods - -taosKeeper needs to be executed on the terminal of the operating system. To run taosKeeper, see [configuration file](#configuration-file-parameters-in-detail). +taosKeeper needs to be executed on the terminal of the operating system, it supports three configuration methods: [Command-line arguments](#command-line-arguments-in-detail), [environment variable](#environment-variable-in-detail) and [configuration file](#configuration-file-parameters-in-detail). The precedence of those is Command-line, environment variable and configuration file. **Make sure that the TDengine cluster is running correctly before running taosKeeper. ** Ensure that the monitoring service in TDengine has been started. For more information, see [TDengine Monitoring Configuration](../config/#monitoring). - +### Environment variable + +You can use Environment variable to run taosKeeper and control its behavior: + +```shell +$ export TAOS_KEEPER_TDENGINE_HOST=192.168.64.3 + +$ taoskeeper +``` + +you can run `taoskeeper -h` for more detail. + ### Configuration File You can quickly launch taosKeeper with the following commands. If you do not specify a configuration file, `/etc/taos/keeper.toml` is used by default. If this file does not specify configurations, the default values are used. ```shell -taoskeeper -c +$ taoskeeper -c ``` **Sample configuration files** @@ -110,7 +119,7 @@ Query OK, 1 rows in database (0.036162s) #### Export Monitoring Metrics ```shell -curl http://127.0.0.1:6043/metrics +$ curl http://127.0.0.1:6043/metrics ``` Sample result set (excerpt): diff --git a/docs/zh/14-reference/14-taosKeeper.md b/docs/zh/14-reference/14-taosKeeper.md index f1165c9d0f..ae0a496f03 100644 --- a/docs/zh/14-reference/14-taosKeeper.md +++ b/docs/zh/14-reference/14-taosKeeper.md @@ -1,7 +1,7 @@ --- sidebar_label: taosKeeper title: taosKeeper -description: TDengine taosKeeper 使用说明 +description: TDengine 3.0 版本监控指标的导出工具 --- ## 简介 @@ -22,26 +22,36 @@ taosKeeper 安装方式: ### 配置和运行方式 - -taosKeeper 需要在操作系统终端执行,该工具支持 [配置文件启动](#配置文件启动)。 +taosKeeper 需要在操作系统终端执行,该工具支持三种配置方式:[命令行参数](#命令行参数启动)、[环境变量](#环境变量启动) 和 [配置文件](#配置文件启动)。优先级为:命令行参数、环境变量、配置文件参数。 **在运行 taosKeeper 之前要确保 TDengine 集群与 taosAdapter 已经在正确运行。** 并且 TDengine 已经开启监控服务,具体请参考:[TDengine 监控配置](../config/#监控相关)。 - + +### 环境变量启动 + +通过设置环境变量达到控制启动参数的目的,通常在容器中运行时使用。 + +```shell +$ export TAOS_KEEPER_TDENGINE_HOST=192.168.64.3 + +$ taoskeeper +``` + +具体参数列表请参照 `taoskeeper -h` 输入结果。 + ### 配置文件启动 执行以下命令即可快速体验 taosKeeper。当不指定 taosKeeper 配置文件时,优先使用 `/etc/taos/keeper.toml` 配置,否则将使用默认配置。 ```shell -taoskeeper -c +$ taoskeeper -c ``` **下面是配置文件的示例:** @@ -110,7 +120,7 @@ Query OK, 1 rows in database (0.036162s) #### 导出监控指标 ```shell -curl http://127.0.0.1:6043/metrics +$ curl http://127.0.0.1:6043/metrics ``` 部分结果集: From ed137b36d5d66736fa7b695972ac5e04f57bb095 Mon Sep 17 00:00:00 2001 From: wangmm0220 Date: Tue, 30 Aug 2022 14:11:44 +0800 Subject: [PATCH 67/76] opti: test casese for tmq snapshot for taosX --- examples/c/CMakeLists.txt | 15 - examples/c/tmq_taosx.c | 489 ------------------------- tests/system-test/7-tmq/tmq_taosx.py | 203 +++++------ tests/test/c/CMakeLists.txt | 8 - tests/test/c/tmq_taosx_ci.c | 268 +++++++------- tests/test/c/tmq_taosx_snapshot_ci.c | 512 --------------------------- 6 files changed, 244 insertions(+), 1251 deletions(-) delete mode 100644 examples/c/tmq_taosx.c delete mode 100644 tests/test/c/tmq_taosx_snapshot_ci.c diff --git a/examples/c/CMakeLists.txt b/examples/c/CMakeLists.txt index 9d06dbac6d..4a9007acec 100644 --- a/examples/c/CMakeLists.txt +++ b/examples/c/CMakeLists.txt @@ -13,15 +13,9 @@ IF (TD_LINUX) #TARGET_LINK_LIBRARIES(epoll taos_static trpc tutil pthread lua) add_executable(tmq "") - add_executable(tmq_taosx "") add_executable(stream_demo "") add_executable(demoapi "") - target_sources(tmq_taosx - PRIVATE - "tmq_taosx.c" - ) - target_sources(tmq PRIVATE "tmq.c" @@ -41,10 +35,6 @@ IF (TD_LINUX) taos_static ) - target_link_libraries(tmq_taosx - taos_static - ) - target_link_libraries(stream_demo taos_static ) @@ -57,10 +47,6 @@ IF (TD_LINUX) PUBLIC "${TD_SOURCE_DIR}/include/os" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/inc" ) - target_include_directories(tmq_taosx - PUBLIC "${TD_SOURCE_DIR}/include/os" - PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/inc" - ) target_include_directories(stream_demo PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/inc" @@ -73,7 +59,6 @@ IF (TD_LINUX) ) SET_TARGET_PROPERTIES(tmq PROPERTIES OUTPUT_NAME tmq) - SET_TARGET_PROPERTIES(tmq_taosx PROPERTIES OUTPUT_NAME tmq_taosx) SET_TARGET_PROPERTIES(stream_demo PROPERTIES OUTPUT_NAME stream_demo) SET_TARGET_PROPERTIES(demoapi PROPERTIES OUTPUT_NAME demoapi) ENDIF () diff --git a/examples/c/tmq_taosx.c b/examples/c/tmq_taosx.c deleted file mode 100644 index 491eda1ddb..0000000000 --- a/examples/c/tmq_taosx.c +++ /dev/null @@ -1,489 +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 -#include -#include -#include -#include -#include "taos.h" - -static int running = 1; - -static TAOS* use_db(){ - TAOS* pConn = taos_connect("localhost", "root", "taosdata", NULL, 0); - if (pConn == NULL) { - return NULL; - } - - TAOS_RES* pRes = taos_query(pConn, "use db_taosx"); - if (taos_errno(pRes) != 0) { - printf("error in use db_taosx, reason:%s\n", taos_errstr(pRes)); - return NULL; - } - taos_free_result(pRes); - return pConn; -} - -static void msg_process(TAOS_RES* msg) { - /*memset(buf, 0, 1024);*/ - printf("-----------topic-------------: %s\n", tmq_get_topic_name(msg)); - printf("db: %s\n", tmq_get_db_name(msg)); - printf("vg: %d\n", tmq_get_vgroup_id(msg)); - TAOS *pConn = use_db(); - if (tmq_get_res_type(msg) == TMQ_RES_TABLE_META) { - char* result = tmq_get_json_meta(msg); - if (result) { - printf("meta result: %s\n", result); - } - tmq_free_json_meta(result); - } - - tmq_raw_data raw = {0}; - tmq_get_raw(msg, &raw); - int32_t ret = tmq_write_raw(pConn, raw); - printf("write raw data: %s\n", tmq_err2str(ret)); - -// else{ -// while(1){ -// int numOfRows = 0; -// void *pData = NULL; -// taos_fetch_raw_block(msg, &numOfRows, &pData); -// if(numOfRows == 0) break; -// printf("write data: tbname:%s, numOfRows:%d\n", tmq_get_table_name(msg), numOfRows); -// int ret = taos_write_raw_block(pConn, numOfRows, pData, tmq_get_table_name(msg)); -// printf("write raw data: %s\n", tmq_err2str(ret)); -// } -// } - - taos_close(pConn); -} - -int32_t init_env() { - TAOS* pConn = taos_connect("localhost", "root", "taosdata", NULL, 0); - if (pConn == NULL) { - return -1; - } - - TAOS_RES* pRes = taos_query(pConn, "drop database if exists db_taosx"); - if (taos_errno(pRes) != 0) { - printf("error in drop db_taosx, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "create database if not exists db_taosx vgroups 4"); - if (taos_errno(pRes) != 0) { - printf("error in create db_taosx, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "drop database if exists abc1"); - if (taos_errno(pRes) != 0) { - printf("error in drop db, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "create database if not exists abc1 vgroups 3"); - if (taos_errno(pRes) != 0) { - printf("error in create db, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "use abc1"); - if (taos_errno(pRes) != 0) { - printf("error in use db, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, - "create stable if not exists st1 (ts timestamp, c1 int, c2 float, c3 binary(16)) tags(t1 int, t3 " - "nchar(8), t4 bool)"); - if (taos_errno(pRes) != 0) { - printf("failed to create super table st1, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "create table if not exists ct0 using st1 tags(1000, \"ttt\", true)"); - if (taos_errno(pRes) != 0) { - printf("failed to create child table tu1, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "insert into ct0 values(1626006833600, 1, 2, 'a')"); - if (taos_errno(pRes) != 0) { - printf("failed to insert into ct0, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "create table if not exists ct1 using st1(t1) tags(2000)"); - if (taos_errno(pRes) != 0) { - printf("failed to create child table ct1, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "create table if not exists ct2 using st1(t1) tags(NULL)"); - if (taos_errno(pRes) != 0) { - printf("failed to create child table ct2, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "insert into ct1 values(1626006833600, 3, 4, 'b')"); - if (taos_errno(pRes) != 0) { - printf("failed to insert into ct1, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "create table if not exists ct3 using st1(t1) tags(3000)"); - if (taos_errno(pRes) != 0) { - printf("failed to create child table ct3, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "create table if not exists ct4 using st1(t3) tags('ct4')"); - if (taos_errno(pRes) != 0) { - printf("failed to create child table ct4, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "insert into ct3 values(1626006833600, 5, 6, 'c') ct1 values(1626006833601, 2, 3, 'sds') (1626006833602, 4, 5, 'ddd') ct0 values(1626006833602, 4, 3, 'hwj') ct1 values(now+5s, 23, 32, 's21ds')"); - if (taos_errno(pRes) != 0) { - printf("failed to insert into ct3, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "alter table st1 add column c4 bigint"); - if (taos_errno(pRes) != 0) { - printf("failed to alter super table st1, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "alter table st1 modify column c3 binary(64)"); - if (taos_errno(pRes) != 0) { - printf("failed to alter super table st1, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "insert into ct3 values(1626006833605, 53, 63, 'cffffffffffffffffffffffffffff', 8989898899999) (1626006833609, 51, 62, 'c333', 940)"); - if (taos_errno(pRes) != 0) { - printf("failed to insert into ct3, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "insert into ct3 select * from ct1"); - if (taos_errno(pRes) != 0) { - printf("failed to insert into ct3, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "alter table st1 add tag t2 binary(64)"); - if (taos_errno(pRes) != 0) { - printf("failed to alter super table st1, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "alter table ct3 set tag t1=5000"); - if (taos_errno(pRes) != 0) { - printf("failed to slter child table ct3, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "delete from abc1 .ct3 where ts < 1626006833606"); - if (taos_errno(pRes) != 0) { - printf("failed to insert into ct3, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "drop table ct3 ct1"); - if (taos_errno(pRes) != 0) { - printf("failed to drop child table ct3, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "drop table st1"); - if (taos_errno(pRes) != 0) { - printf("failed to drop super table st1, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "create table if not exists n1(ts timestamp, c1 int, c2 nchar(4))"); - if (taos_errno(pRes) != 0) { - printf("failed to create normal table n1, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "alter table n1 add column c3 bigint"); - if (taos_errno(pRes) != 0) { - printf("failed to alter normal table n1, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "alter table n1 modify column c2 nchar(8)"); - if (taos_errno(pRes) != 0) { - printf("failed to alter normal table n1, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "alter table n1 rename column c3 cc3"); - if (taos_errno(pRes) != 0) { - printf("failed to alter normal table n1, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "alter table n1 comment 'hello'"); - if (taos_errno(pRes) != 0) { - printf("failed to alter normal table n1, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "alter table n1 drop column c1"); - if (taos_errno(pRes) != 0) { - printf("failed to alter normal table n1, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "insert into n1 values(now, 'eeee', 8989898899999) (now+9s, 'c333', 940)"); - if (taos_errno(pRes) != 0) { - printf("failed to insert into n1, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "drop table n1"); - if (taos_errno(pRes) != 0) { - printf("failed to drop normal table n1, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "create table jt(ts timestamp, i int) tags(t json)"); - if (taos_errno(pRes) != 0) { - printf("failed to create super table jt, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "create table jt1 using jt tags('{\"k1\":1, \"k2\":\"hello\"}')"); - if (taos_errno(pRes) != 0) { - printf("failed to create super table jt, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "create table jt2 using jt tags('')"); - if (taos_errno(pRes) != 0) { - printf("failed to create super table jt2, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, - "create stable if not exists st1 (ts timestamp, c1 int, c2 float, c3 binary(16)) tags(t1 int, t3 " - "nchar(8), t4 bool)"); - if (taos_errno(pRes) != 0) { - printf("failed to create super table st1, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "drop table st1"); - if (taos_errno(pRes) != 0) { - printf("failed to drop super table st1, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - taos_close(pConn); - return 0; -} - -int32_t create_topic() { - printf("create topic\n"); - TAOS_RES* pRes; - TAOS* pConn = taos_connect("localhost", "root", "taosdata", NULL, 0); - if (pConn == NULL) { - return -1; - } - - pRes = taos_query(pConn, "use abc1"); - if (taos_errno(pRes) != 0) { - printf("error in use db, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "create topic topic_ctb_column with meta as database abc1"); - if (taos_errno(pRes) != 0) { - printf("failed to create topic topic_ctb_column, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - taos_close(pConn); - return 0; -} - -void tmq_commit_cb_print(tmq_t* tmq, int32_t code, void* param) { - printf("commit %d tmq %p param %p\n", code, tmq, param); -} - -tmq_t* build_consumer() { -#if 0 - TAOS* pConn = taos_connect("localhost", "root", "taosdata", NULL, 0); - assert(pConn != NULL); - - TAOS_RES* pRes = taos_query(pConn, "use abc1"); - if (taos_errno(pRes) != 0) { - printf("error in use db, reason:%s\n", taos_errstr(pRes)); - } - taos_free_result(pRes); -#endif - - tmq_conf_t* conf = tmq_conf_new(); - tmq_conf_set(conf, "group.id", "tg2"); - tmq_conf_set(conf, "client.id", "my app 1"); - tmq_conf_set(conf, "td.connect.user", "root"); - tmq_conf_set(conf, "td.connect.pass", "taosdata"); - tmq_conf_set(conf, "msg.with.table.name", "true"); - tmq_conf_set(conf, "enable.auto.commit", "true"); - tmq_conf_set(conf, "experimental.snapshot.enable", "true"); - - - /*tmq_conf_set(conf, "experimental.snapshot.enable", "true");*/ - - 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; -} - -tmq_list_t* build_topic_list() { - tmq_list_t* topic_list = tmq_list_new(); - tmq_list_append(topic_list, "topic_ctb_column"); - /*tmq_list_append(topic_list, "tmq_test_db_multi_insert_topic");*/ - return topic_list; -} - -void basic_consume_loop(tmq_t* tmq, tmq_list_t* topics) { - int32_t code; - - if ((code = tmq_subscribe(tmq, topics))) { - fprintf(stderr, "%% Failed to start consuming topics: %s\n", tmq_err2str(code)); - printf("subscribe err\n"); - return; - } - int32_t cnt = 0; - while (running) { - TAOS_RES* tmqmessage = tmq_consumer_poll(tmq, 1000); - if (tmqmessage) { - cnt++; - msg_process(tmqmessage); - /*if (cnt >= 2) break;*/ - /*printf("get data\n");*/ - taos_free_result(tmqmessage); - /*} else {*/ - /*break;*/ - /*tmq_commit_sync(tmq, NULL);*/ - } - } - - code = tmq_consumer_close(tmq); - if (code) - fprintf(stderr, "%% Failed to close consumer: %s\n", tmq_err2str(code)); - else - fprintf(stderr, "%% Consumer closed\n"); -} - -void sync_consume_loop(tmq_t* tmq, tmq_list_t* topics) { - static const int MIN_COMMIT_COUNT = 1; - - int msg_count = 0; - int32_t code; - - if ((code = tmq_subscribe(tmq, topics))) { - fprintf(stderr, "%% Failed to start consuming topics: %s\n", tmq_err2str(code)); - return; - } - - tmq_list_t* subList = NULL; - tmq_subscription(tmq, &subList); - char** subTopics = tmq_list_to_c_array(subList); - int32_t sz = tmq_list_get_size(subList); - printf("subscribed topics: "); - for (int32_t i = 0; i < sz; i++) { - printf("%s, ", subTopics[i]); - } - printf("\n"); - tmq_list_destroy(subList); - - while (running) { - TAOS_RES* tmqmessage = tmq_consumer_poll(tmq, 1000); - if (tmqmessage) { - msg_process(tmqmessage); - taos_free_result(tmqmessage); - - /*tmq_commit_sync(tmq, NULL);*/ - /*if ((++msg_count % MIN_COMMIT_COUNT) == 0) tmq_commit(tmq, NULL, 0);*/ - } - } - - code = tmq_consumer_close(tmq); - if (code) - fprintf(stderr, "%% Failed to close consumer: %s\n", tmq_err2str(code)); - else - fprintf(stderr, "%% Consumer closed\n"); -} - -int main(int argc, char* argv[]) { - printf("env init\n"); - if (init_env() < 0) { - return -1; - } - create_topic(); - - tmq_t* tmq = build_consumer(); - tmq_list_t* topic_list = build_topic_list(); - basic_consume_loop(tmq, topic_list); - /*sync_consume_loop(tmq, topic_list);*/ -} diff --git a/tests/system-test/7-tmq/tmq_taosx.py b/tests/system-test/7-tmq/tmq_taosx.py index d38e509d26..07602ec29f 100644 --- a/tests/system-test/7-tmq/tmq_taosx.py +++ b/tests/system-test/7-tmq/tmq_taosx.py @@ -20,15 +20,9 @@ class TDTestCase: tdSql.init(conn.cursor()) #tdSql.init(conn.cursor(), logSql) # output sql.txt file - def checkFileContent(self): - buildPath = tdCom.getBuildPath() - cfgPath = tdCom.getClientCfgPath() - cmdStr = '%s/build/bin/tmq_taosx_ci -c %s'%(buildPath, cfgPath) - tdLog.info(cmdStr) - os.system(cmdStr) - - srcFile = '%s/../log/tmq_taosx_tmp.source'%(cfgPath) - dstFile = '%s/../log/tmq_taosx_tmp.result'%(cfgPath) + def checkJson(self, cfgPath, name): + srcFile = '%s/../log/%s.source'%(cfgPath, name) + dstFile = '%s/../log/%s.result'%(cfgPath, name) tdLog.info("compare file: %s, %s"%(srcFile, dstFile)) consumeFile = open(srcFile, mode='r') @@ -43,107 +37,31 @@ class TDTestCase: tdLog.exit("compare error: %s != %s"%src, dst) else: break - - tdSql.execute('use db_taosx') - tdSql.query("select * from ct3 order by c1 desc") - tdSql.checkRows(2) - tdSql.checkData(0, 1, 51) - tdSql.checkData(0, 4, 940) - tdSql.checkData(1, 1, 23) - tdSql.checkData(1, 4, None) - - tdSql.query("select * from st1 order by ts") - tdSql.checkRows(8) - tdSql.checkData(0, 1, 1) - tdSql.checkData(1, 1, 3) - tdSql.checkData(4, 1, 4) - tdSql.checkData(6, 1, 23) - - tdSql.checkData(0, 2, 2) - tdSql.checkData(1, 2, 4) - tdSql.checkData(4, 2, 3) - tdSql.checkData(6, 2, 32) - - tdSql.checkData(0, 3, 'a') - tdSql.checkData(1, 3, 'b') - tdSql.checkData(4, 3, 'hwj') - tdSql.checkData(6, 3, 's21ds') - - tdSql.checkData(0, 4, None) - tdSql.checkData(1, 4, None) - tdSql.checkData(5, 4, 940) - tdSql.checkData(6, 4, None) - - tdSql.checkData(0, 5, 1000) - tdSql.checkData(1, 5, 2000) - tdSql.checkData(4, 5, 1000) - tdSql.checkData(6, 5, 5000) - - tdSql.checkData(0, 6, 'ttt') - tdSql.checkData(1, 6, None) - tdSql.checkData(4, 6, 'ttt') - tdSql.checkData(6, 6, None) - - tdSql.checkData(0, 7, True) - tdSql.checkData(1, 7, None) - tdSql.checkData(4, 7, True) - tdSql.checkData(6, 7, None) - - tdSql.checkData(0, 8, None) - tdSql.checkData(1, 8, None) - tdSql.checkData(4, 8, None) - tdSql.checkData(6, 8, None) - - tdSql.query("select * from ct1") - tdSql.checkRows(4) - - tdSql.query("select * from ct2") - tdSql.checkRows(0) - - tdSql.query("select * from ct0 order by c1") - tdSql.checkRows(2) - tdSql.checkData(0, 3, "a") - tdSql.checkData(1, 4, None) - - tdSql.query("select * from n1 order by cc3 desc") - tdSql.checkRows(2) - tdSql.checkData(0, 1, "eeee") - tdSql.checkData(1, 2, 940) - - tdSql.query("select * from jt order by i desc") - tdSql.checkRows(2) - tdSql.checkData(0, 1, 11) - tdSql.checkData(0, 2, None) - tdSql.checkData(1, 1, 1) - tdSql.checkData(1, 2, '{"k1":1,"k2":"hello"}') - - tdSql.execute('drop topic if exists topic_ctb_column') return - def checkFileContentSnapshot(self): - buildPath = tdCom.getBuildPath() - cfgPath = tdCom.getClientCfgPath() - cmdStr = '%s/build/bin/tmq_taosx_snapshot_ci -c %s'%(buildPath, cfgPath) - tdLog.info(cmdStr) - os.system(cmdStr) + def checkDropData(self): + tdSql.execute('use db_taosx') + tdSql.query("show tables") + tdSql.checkRows(2) + tdSql.query("select * from jt order by i") + tdSql.checkRows(2) + tdSql.checkData(0, 1, 1) + tdSql.checkData(1, 1, 11) + tdSql.checkData(0, 2, '{"k1":1,"k2":"hello"}') + tdSql.checkData(1, 2, None) - srcFile = '%s/../log/tmq_taosx_tmp_snapshot.source'%(cfgPath) - dstFile = '%s/../log/tmq_taosx_tmp_snapshot.result'%(cfgPath) - tdLog.info("compare file: %s, %s"%(srcFile, dstFile)) - - consumeFile = open(srcFile, mode='r') - queryFile = open(dstFile, mode='r') - - while True: - dst = queryFile.readline() - src = consumeFile.readline() - - if dst: - if dst != src: - tdLog.exit("compare error: %s != %s"%src, dst) - else: - break + tdSql.execute('use abc1') + tdSql.query("show tables") + tdSql.checkRows(2) + tdSql.query("select * from jt order by i") + tdSql.checkRows(2) + tdSql.checkData(0, 1, 1) + tdSql.checkData(1, 1, 11) + tdSql.checkData(0, 2, '{"k1":1,"k2":"hello"}') + tdSql.checkData(1, 2, None) + return + def checkData(self): tdSql.execute('use db_taosx') tdSql.query("select * from ct3 order by c1 desc") tdSql.checkRows(2) @@ -216,13 +134,82 @@ class TDTestCase: tdSql.checkData(0, 2, None) tdSql.checkData(1, 1, 1) tdSql.checkData(1, 2, '{"k1":1,"k2":"hello"}') + return + + def checkWal1Vgroup(self): + buildPath = tdCom.getBuildPath() + cfgPath = tdCom.getClientCfgPath() + cmdStr = '%s/build/bin/tmq_taosx_ci -c %s -sv 1 -dv 1'%(buildPath, cfgPath) + tdLog.info(cmdStr) + os.system(cmdStr) + + self.checkJson(cfgPath, "tmq_taosx_tmp") + self.checkData() + + return + + def checkWalMultiVgroups(self): + buildPath = tdCom.getBuildPath() + cmdStr = '%s/build/bin/tmq_taosx_ci -sv 3 -dv 5'%(buildPath) + tdLog.info(cmdStr) + os.system(cmdStr) + + self.checkData() + + return + + def checkWalMultiVgroupsWithDropTable(self): + buildPath = tdCom.getBuildPath() + cmdStr = '%s/build/bin/tmq_taosx_ci -sv 3 -dv 5 -d'%(buildPath) + tdLog.info(cmdStr) + os.system(cmdStr) + + self.checkDropData() + + return + + def checkSnapshot1Vgroup(self): + buildPath = tdCom.getBuildPath() + cfgPath = tdCom.getClientCfgPath() + cmdStr = '%s/build/bin/tmq_taosx_ci -c %s -sv 1 -dv 1 -s'%(buildPath, cfgPath) + tdLog.info(cmdStr) + os.system(cmdStr) + + self.checkJson(cfgPath, "tmq_taosx_tmp_snapshot") + self.checkData() + + return + + def checkSnapshotMultiVgroups(self): + buildPath = tdCom.getBuildPath() + cmdStr = '%s/build/bin/tmq_taosx_ci -sv 2 -dv 4 -s'%(buildPath) + tdLog.info(cmdStr) + os.system(cmdStr) + + self.checkData() + + return + + def checkSnapshotMultiVgroupsWithDropTable(self): + buildPath = tdCom.getBuildPath() + cmdStr = '%s/build/bin/tmq_taosx_ci -sv 2 -dv 4 -s -d'%(buildPath) + tdLog.info(cmdStr) + os.system(cmdStr) + + self.checkDropData() return def run(self): tdSql.prepare() - self.checkFileContent() - self.checkFileContentSnapshot() + self.checkWal1Vgroup() + self.checkSnapshot1Vgroup() + + self.checkWalMultiVgroups() + self.checkSnapshotMultiVgroups() + + self.checkWalMultiVgroupsWithDropTable() + self.checkSnapshotMultiVgroupsWithDropTable() def stop(self): tdSql.close() diff --git a/tests/test/c/CMakeLists.txt b/tests/test/c/CMakeLists.txt index 0fb80e69c2..31331b5265 100644 --- a/tests/test/c/CMakeLists.txt +++ b/tests/test/c/CMakeLists.txt @@ -2,7 +2,6 @@ add_executable(tmq_demo tmqDemo.c) add_executable(tmq_sim tmqSim.c) add_executable(create_table createTable.c) add_executable(tmq_taosx_ci tmq_taosx_ci.c) -add_executable(tmq_taosx_snapshot_ci tmq_taosx_snapshot_ci.c) add_executable(sml_test sml_test.c) target_link_libraries( create_table @@ -32,13 +31,6 @@ target_link_libraries( PUBLIC common PUBLIC os ) -target_link_libraries( - tmq_taosx_snapshot_ci - PUBLIC taos_static - PUBLIC util - PUBLIC common - PUBLIC os -) target_link_libraries( sml_test diff --git a/tests/test/c/tmq_taosx_ci.c b/tests/test/c/tmq_taosx_ci.c index ee5af03f05..2afa05b012 100644 --- a/tests/test/c/tmq_taosx_ci.c +++ b/tests/test/c/tmq_taosx_ci.c @@ -22,8 +22,16 @@ #include "types.h" static int running = 1; -TdFilePtr g_fp = NULL; -char dir[64]={0}; +TdFilePtr g_fp = NULL; +typedef struct{ + bool snapShot; + bool dropTable; + int srcVgroups; + int dstVgroups; + char dir[64]; +}Config; + +Config g_conf = {0}; static TAOS* use_db(){ TAOS* pConn = taos_connect("localhost", "root", "taosdata", NULL, 0); @@ -41,7 +49,6 @@ static TAOS* use_db(){ } static void msg_process(TAOS_RES* msg) { - /*memset(buf, 0, 1024);*/ printf("-----------topic-------------: %s\n", tmq_get_topic_name(msg)); printf("db: %s\n", tmq_get_db_name(msg)); printf("vg: %d\n", tmq_get_vgroup_id(msg)); @@ -51,8 +58,11 @@ static void msg_process(TAOS_RES* msg) { if (result) { printf("meta result: %s\n", result); } - taosFprintfFile(g_fp, result); - taosFprintfFile(g_fp, "\n"); + if(g_fp){ + taosFprintfFile(g_fp, result); + taosFprintfFile(g_fp, "\n"); + } + tmq_free_json_meta(result); } @@ -61,22 +71,10 @@ static void msg_process(TAOS_RES* msg) { int32_t ret = tmq_write_raw(pConn, raw); printf("write raw data: %s\n", tmq_err2str(ret)); -// else{ -// while(1){ -// int numOfRows = 0; -// void *pData = NULL; -// taos_fetch_raw_block(msg, &numOfRows, &pData); -// if(numOfRows == 0) break; -// printf("write data: tbname:%s, numOfRows:%d\n", tmq_get_table_name(msg), numOfRows); -// int ret = taos_write_raw_block(pConn, numOfRows, pData, tmq_get_table_name(msg)); -// printf("write raw data: %s\n", tmq_err2str(ret)); -// } -// } - taos_close(pConn); } -int32_t init_env() { +int32_t init_env(Config *conf) { TAOS* pConn = taos_connect("localhost", "root", "taosdata", NULL, 0); if (pConn == NULL) { return -1; @@ -89,13 +87,22 @@ int32_t init_env() { } taos_free_result(pRes); - pRes = taos_query(pConn, "create database if not exists db_taosx vgroups 1"); + char sql[128] = {0}; + snprintf(sql, 128, "create database if not exists db_taosx vgroups %d", conf->dstVgroups); + pRes = taos_query(pConn, sql); if (taos_errno(pRes) != 0) { printf("error in create db_taosx, reason:%s\n", taos_errstr(pRes)); return -1; } taos_free_result(pRes); + pRes = taos_query(pConn, "drop topic if exists topic_db"); + if (taos_errno(pRes) != 0) { + printf("error in drop topic, reason:%s\n", taos_errstr(pRes)); + return -1; + } + taos_free_result(pRes); + pRes = taos_query(pConn, "drop database if exists abc1"); if (taos_errno(pRes) != 0) { printf("error in drop db, reason:%s\n", taos_errstr(pRes)); @@ -103,7 +110,8 @@ int32_t init_env() { } taos_free_result(pRes); - pRes = taos_query(pConn, "create database if not exists abc1 vgroups 1"); + snprintf(sql, 128, "create database if not exists abc1 vgroups %d", conf->srcVgroups); + pRes = taos_query(pConn, sql); if (taos_errno(pRes) != 0) { printf("error in create db, reason:%s\n", taos_errstr(pRes)); return -1; @@ -133,7 +141,7 @@ int32_t init_env() { } taos_free_result(pRes); - pRes = taos_query(pConn, "insert into ct0 values(1626006833600, 1, 2, 'a')"); + pRes = taos_query(pConn, "insert into ct0 values(1626006833400, 1, 2, 'a')"); if (taos_errno(pRes) != 0) { printf("failed to insert into ct0, reason:%s\n", taos_errstr(pRes)); return -1; @@ -168,7 +176,7 @@ int32_t init_env() { } taos_free_result(pRes); - pRes = taos_query(pConn, "insert into ct3 values(1626006833600, 5, 6, 'c') ct1 values(1626006833601, 2, 3, 'sds') (1626006833602, 4, 5, 'ddd') ct0 values(1626006833602, 4, 3, 'hwj') ct1 values(now+5s, 23, 32, 's21ds')"); + pRes = taos_query(pConn, "insert into ct3 values(1626006833600, 5, 6, 'c') ct1 values(1626006833601, 2, 3, 'sds') (1626006833602, 4, 5, 'ddd') ct0 values(1626006833603, 4, 3, 'hwj') ct1 values(now+5s, 23, 32, 's21ds')"); if (taos_errno(pRes) != 0) { printf("failed to insert into ct3, reason:%s\n", taos_errstr(pRes)); return -1; @@ -224,6 +232,22 @@ int32_t init_env() { } taos_free_result(pRes); + if(conf->dropTable){ + pRes = taos_query(pConn, "drop table ct3 ct1"); + if (taos_errno(pRes) != 0) { + printf("failed to drop child table ct3, reason:%s\n", taos_errstr(pRes)); + return -1; + } + taos_free_result(pRes); + + pRes = taos_query(pConn, "drop table st1"); + if (taos_errno(pRes) != 0) { + printf("failed to drop super table st1, reason:%s\n", taos_errstr(pRes)); + return -1; + } + taos_free_result(pRes); + } + pRes = taos_query(pConn, "create table if not exists n1(ts timestamp, c1 int, c2 nchar(4))"); if (taos_errno(pRes) != 0) { printf("failed to create normal table n1, reason:%s\n", taos_errstr(pRes)); @@ -273,6 +297,15 @@ int32_t init_env() { } taos_free_result(pRes); + if(conf->dropTable){ + pRes = taos_query(pConn, "drop table n1"); + if (taos_errno(pRes) != 0) { + printf("failed to drop normal table n1, reason:%s\n", taos_errstr(pRes)); + return -1; + } + taos_free_result(pRes); + } + pRes = taos_query(pConn, "create table jt(ts timestamp, i int) tags(t json)"); if (taos_errno(pRes) != 0) { printf("failed to create super table jt, reason:%s\n", taos_errstr(pRes)); @@ -308,6 +341,23 @@ int32_t init_env() { } taos_free_result(pRes); + if(conf->dropTable){ + pRes = taos_query(pConn, + "create stable if not exists st1 (ts timestamp, c1 int, c2 float, c3 binary(16)) tags(t1 int, t3 " + "nchar(8), t4 bool)"); + if (taos_errno(pRes) != 0) { + printf("failed to create super table st1, reason:%s\n", taos_errstr(pRes)); + return -1; + } + taos_free_result(pRes); + + pRes = taos_query(pConn, "drop table st1"); + if (taos_errno(pRes) != 0) { + printf("failed to drop super table st1, reason:%s\n", taos_errstr(pRes)); + return -1; + } + taos_free_result(pRes); + } taos_close(pConn); return 0; } @@ -327,9 +377,9 @@ int32_t create_topic() { } taos_free_result(pRes); - pRes = taos_query(pConn, "create topic topic_ctb_column with meta as database abc1"); + pRes = taos_query(pConn, "create topic topic_db with meta as database abc1"); if (taos_errno(pRes) != 0) { - printf("failed to create topic topic_ctb_column, reason:%s\n", taos_errstr(pRes)); + printf("failed to create topic topic_db, reason:%s\n", taos_errstr(pRes)); return -1; } taos_free_result(pRes); @@ -342,18 +392,7 @@ void tmq_commit_cb_print(tmq_t* tmq, int32_t code, void* param) { printf("commit %d tmq %p param %p\n", code, tmq, param); } -tmq_t* build_consumer() { -#if 0 - TAOS* pConn = taos_connect("localhost", "root", "taosdata", NULL, 0); - assert(pConn != NULL); - - TAOS_RES* pRes = taos_query(pConn, "use abc1"); - if (taos_errno(pRes) != 0) { - printf("error in use db, reason:%s\n", taos_errstr(pRes)); - } - taos_free_result(pRes); -#endif - +tmq_t* build_consumer(Config *config) { tmq_conf_t* conf = tmq_conf_new(); tmq_conf_set(conf, "group.id", "tg2"); tmq_conf_set(conf, "client.id", "my app 1"); @@ -363,7 +402,9 @@ tmq_t* build_consumer() { tmq_conf_set(conf, "enable.auto.commit", "true"); tmq_conf_set(conf, "enable.heartbeat.background", "true"); - /*tmq_conf_set(conf, "experimental.snapshot.enable", "true");*/ + if(config->snapShot){ + tmq_conf_set(conf, "experimental.snapshot.enable", "true"); + } tmq_conf_set_auto_commit_cb(conf, tmq_commit_cb_print, NULL); tmq_t* tmq = tmq_consumer_new(conf, NULL, 0); @@ -374,8 +415,7 @@ tmq_t* build_consumer() { tmq_list_t* build_topic_list() { tmq_list_t* topic_list = tmq_list_new(); - tmq_list_append(topic_list, "topic_ctb_column"); - /*tmq_list_append(topic_list, "tmq_test_db_multi_insert_topic");*/ + tmq_list_append(topic_list, "topic_db"); return topic_list; } @@ -393,12 +433,7 @@ void basic_consume_loop(tmq_t* tmq, tmq_list_t* topics) { if (tmqmessage) { cnt++; msg_process(tmqmessage); - /*if (cnt >= 2) break;*/ - /*printf("get data\n");*/ taos_free_result(tmqmessage); - /*} else {*/ - /*break;*/ - /*tmq_commit_sync(tmq, NULL);*/ }else{ break; } @@ -411,52 +446,18 @@ void basic_consume_loop(tmq_t* tmq, tmq_list_t* topics) { fprintf(stderr, "%% Consumer closed\n"); } -void sync_consume_loop(tmq_t* tmq, tmq_list_t* topics) { - static const int MIN_COMMIT_COUNT = 1; - - int msg_count = 0; - int32_t code; - - if ((code = tmq_subscribe(tmq, topics))) { - fprintf(stderr, "%% Failed to start consuming topics: %s\n", tmq_err2str(code)); - return; - } - - tmq_list_t* subList = NULL; - tmq_subscription(tmq, &subList); - char** subTopics = tmq_list_to_c_array(subList); - int32_t sz = tmq_list_get_size(subList); - printf("subscribed topics: "); - for (int32_t i = 0; i < sz; i++) { - printf("%s, ", subTopics[i]); - } - printf("\n"); - tmq_list_destroy(subList); - - while (running) { - TAOS_RES* tmqmessage = tmq_consumer_poll(tmq, 1000); - if (tmqmessage) { - msg_process(tmqmessage); - taos_free_result(tmqmessage); - - /*tmq_commit_sync(tmq, NULL);*/ - /*if ((++msg_count % MIN_COMMIT_COUNT) == 0) tmq_commit(tmq, NULL, 0);*/ - } - } - - code = tmq_consumer_close(tmq); - if (code) - fprintf(stderr, "%% Failed to close consumer: %s\n", tmq_err2str(code)); - else - fprintf(stderr, "%% Consumer closed\n"); -} - -void initLogFile() { +void initLogFile(Config *conf) { char f1[256] = {0}; char f2[256] = {0}; - sprintf(f1, "%s/../log/tmq_taosx_tmp.source", dir); - sprintf(f2, "%s/../log/tmq_taosx_tmp.result", dir); + if(conf->snapShot){ + sprintf(f1, "%s/../log/tmq_taosx_tmp_snapshot.source", conf->dir); + sprintf(f2, "%s/../log/tmq_taosx_tmp_snapshot.result", conf->dir); + }else{ + sprintf(f1, "%s/../log/tmq_taosx_tmp.source", conf->dir); + sprintf(f2, "%s/../log/tmq_taosx_tmp.result", conf->dir); + } + TdFilePtr pFile = taosOpenFile(f1, TD_FILE_TEXT | TD_FILE_TRUNC | TD_FILE_STREAM); if (NULL == pFile) { fprintf(stderr, "Failed to open %s for save result\n", f1); @@ -469,53 +470,82 @@ void initLogFile() { fprintf(stderr, "Failed to open %s for save result\n", f2); exit(-1); } - char *result[] = { - "{\"type\":\"create\",\"tableName\":\"st1\",\"tableType\":\"super\",\"columns\":[{\"name\":\"ts\",\"type\":9},{\"name\":\"c1\",\"type\":4},{\"name\":\"c2\",\"type\":6},{\"name\":\"c3\",\"type\":8,\"length\":16}],\"tags\":[{\"name\":\"t1\",\"type\":4},{\"name\":\"t3\",\"type\":10,\"length\":8},{\"name\":\"t4\",\"type\":1}]}", - "{\"type\":\"create\",\"tableName\":\"ct0\",\"tableType\":\"child\",\"using\":\"st1\",\"tagNum\":3,\"tags\":[{\"name\":\"t1\",\"type\":4,\"value\":1000},{\"name\":\"t3\",\"type\":10,\"value\":\"\\\"ttt\\\"\"},{\"name\":\"t4\",\"type\":1,\"value\":1}]}", - "{\"type\":\"create\",\"tableName\":\"ct1\",\"tableType\":\"child\",\"using\":\"st1\",\"tagNum\":3,\"tags\":[{\"name\":\"t1\",\"type\":4,\"value\":2000}]}", - "{\"type\":\"create\",\"tableName\":\"ct2\",\"tableType\":\"child\",\"using\":\"st1\",\"tagNum\":3,\"tags\":[]}", - "{\"type\":\"create\",\"tableName\":\"ct3\",\"tableType\":\"child\",\"using\":\"st1\",\"tagNum\":3,\"tags\":[{\"name\":\"t1\",\"type\":4,\"value\":3000}]}", - "{\"type\":\"alter\",\"tableName\":\"st1\",\"tableType\":\"super\",\"alterType\":5,\"colName\":\"c4\",\"colType\":5}", - "{\"type\":\"alter\",\"tableName\":\"st1\",\"tableType\":\"super\",\"alterType\":7,\"colName\":\"c3\",\"colType\":8,\"colLength\":64}", - "{\"type\":\"alter\",\"tableName\":\"st1\",\"tableType\":\"super\",\"alterType\":1,\"colName\":\"t2\",\"colType\":8,\"colLength\":64}", - "{\"type\":\"alter\",\"tableName\":\"ct3\",\"tableType\":\"child\",\"alterType\":4,\"colName\":\"t1\",\"colValue\":\"5000\",\"colValueNull\":false}", - "{\"type\":\"create\",\"tableName\":\"n1\",\"tableType\":\"normal\",\"columns\":[{\"name\":\"ts\",\"type\":9},{\"name\":\"c1\",\"type\":4},{\"name\":\"c2\",\"type\":10,\"length\":4}],\"tags\":[]}", - "{\"type\":\"alter\",\"tableName\":\"n1\",\"tableType\":\"normal\",\"alterType\":5,\"colName\":\"c3\",\"colType\":5}", - "{\"type\":\"alter\",\"tableName\":\"n1\",\"tableType\":\"normal\",\"alterType\":7,\"colName\":\"c2\",\"colType\":10,\"colLength\":8}", - "{\"type\":\"alter\",\"tableName\":\"n1\",\"tableType\":\"normal\",\"alterType\":10,\"colName\":\"c3\",\"colNewName\":\"cc3\"}", - "{\"type\":\"alter\",\"tableName\":\"n1\",\"tableType\":\"normal\",\"alterType\":9}", - "{\"type\":\"alter\",\"tableName\":\"n1\",\"tableType\":\"normal\",\"alterType\":6,\"colName\":\"c1\"}", - "{\"type\":\"create\",\"tableName\":\"jt\",\"tableType\":\"super\",\"columns\":[{\"name\":\"ts\",\"type\":9},{\"name\":\"i\",\"type\":4}],\"tags\":[{\"name\":\"t\",\"type\":15}]}", - "{\"type\":\"create\",\"tableName\":\"jt1\",\"tableType\":\"child\",\"using\":\"jt\",\"tagNum\":1,\"tags\":[{\"name\":\"t\",\"type\":15,\"value\":\"{\\\"k1\\\":1,\\\"k2\\\":\\\"hello\\\"}\"}]}", - "{\"type\":\"create\",\"tableName\":\"jt2\",\"tableType\":\"child\",\"using\":\"jt\",\"tagNum\":1,\"tags\":[]}" - }; - for(int i = 0; i < sizeof(result)/sizeof(result[0]); i++){ - taosFprintfFile(pFile2, result[i]); - taosFprintfFile(pFile2, "\n"); + if(conf->snapShot){ + char *result[] = { + "{\"type\":\"create\",\"tableName\":\"st1\",\"tableType\":\"super\",\"columns\":[{\"name\":\"ts\",\"type\":9},{\"name\":\"c1\",\"type\":4},{\"name\":\"c2\",\"type\":6},{\"name\":\"c3\",\"type\":8,\"length\":64},{\"name\":\"c4\",\"type\":5}],\"tags\":[{\"name\":\"t1\",\"type\":4},{\"name\":\"t3\",\"type\":10,\"length\":8},{\"name\":\"t4\",\"type\":1},{\"name\":\"t2\",\"type\":8,\"length\":64}]}", + "{\"type\":\"create\",\"tableName\":\"ct0\",\"tableType\":\"child\",\"using\":\"st1\",\"tagNum\":4,\"tags\":[{\"name\":\"t1\",\"type\":4,\"value\":1000},{\"name\":\"t3\",\"type\":10,\"value\":\"\\\"ttt\\\"\"},{\"name\":\"t4\",\"type\":1,\"value\":1}]}", + "{\"type\":\"create\",\"tableName\":\"ct1\",\"tableType\":\"child\",\"using\":\"st1\",\"tagNum\":4,\"tags\":[{\"name\":\"t1\",\"type\":4,\"value\":2000}]}", + "{\"type\":\"create\",\"tableName\":\"ct2\",\"tableType\":\"child\",\"using\":\"st1\",\"tagNum\":4,\"tags\":[]}", + "{\"type\":\"create\",\"tableName\":\"ct3\",\"tableType\":\"child\",\"using\":\"st1\",\"tagNum\":4,\"tags\":[{\"name\":\"t1\",\"type\":4,\"value\":5000}]}", + "{\"type\":\"create\",\"tableName\":\"n1\",\"tableType\":\"normal\",\"columns\":[{\"name\":\"ts\",\"type\":9},{\"name\":\"c2\",\"type\":10,\"length\":8},{\"name\":\"cc3\",\"type\":5}],\"tags\":[]}", + "{\"type\":\"create\",\"tableName\":\"jt\",\"tableType\":\"super\",\"columns\":[{\"name\":\"ts\",\"type\":9},{\"name\":\"i\",\"type\":4}],\"tags\":[{\"name\":\"t\",\"type\":15}]}", + "{\"type\":\"create\",\"tableName\":\"jt1\",\"tableType\":\"child\",\"using\":\"jt\",\"tagNum\":1,\"tags\":[{\"name\":\"t\",\"type\":15,\"value\":\"{\\\"k1\\\":1,\\\"k2\\\":\\\"hello\\\"}\"}]}", + "{\"type\":\"create\",\"tableName\":\"jt2\",\"tableType\":\"child\",\"using\":\"jt\",\"tagNum\":1,\"tags\":[]}", + }; + + for(int i = 0; i < sizeof(result)/sizeof(result[0]); i++){ + taosFprintfFile(pFile2, result[i]); + taosFprintfFile(pFile2, "\n"); + } + }else{ + char *result[] = { + "{\"type\":\"create\",\"tableName\":\"st1\",\"tableType\":\"super\",\"columns\":[{\"name\":\"ts\",\"type\":9},{\"name\":\"c1\",\"type\":4},{\"name\":\"c2\",\"type\":6},{\"name\":\"c3\",\"type\":8,\"length\":16}],\"tags\":[{\"name\":\"t1\",\"type\":4},{\"name\":\"t3\",\"type\":10,\"length\":8},{\"name\":\"t4\",\"type\":1}]}", + "{\"type\":\"create\",\"tableName\":\"ct0\",\"tableType\":\"child\",\"using\":\"st1\",\"tagNum\":3,\"tags\":[{\"name\":\"t1\",\"type\":4,\"value\":1000},{\"name\":\"t3\",\"type\":10,\"value\":\"\\\"ttt\\\"\"},{\"name\":\"t4\",\"type\":1,\"value\":1}]}", + "{\"type\":\"create\",\"tableName\":\"ct1\",\"tableType\":\"child\",\"using\":\"st1\",\"tagNum\":3,\"tags\":[{\"name\":\"t1\",\"type\":4,\"value\":2000}]}", + "{\"type\":\"create\",\"tableName\":\"ct2\",\"tableType\":\"child\",\"using\":\"st1\",\"tagNum\":3,\"tags\":[]}", + "{\"type\":\"create\",\"tableName\":\"ct3\",\"tableType\":\"child\",\"using\":\"st1\",\"tagNum\":3,\"tags\":[{\"name\":\"t1\",\"type\":4,\"value\":3000}]}", + "{\"type\":\"alter\",\"tableName\":\"st1\",\"tableType\":\"super\",\"alterType\":5,\"colName\":\"c4\",\"colType\":5}", + "{\"type\":\"alter\",\"tableName\":\"st1\",\"tableType\":\"super\",\"alterType\":7,\"colName\":\"c3\",\"colType\":8,\"colLength\":64}", + "{\"type\":\"alter\",\"tableName\":\"st1\",\"tableType\":\"super\",\"alterType\":1,\"colName\":\"t2\",\"colType\":8,\"colLength\":64}", + "{\"type\":\"alter\",\"tableName\":\"ct3\",\"tableType\":\"child\",\"alterType\":4,\"colName\":\"t1\",\"colValue\":\"5000\",\"colValueNull\":false}", + "{\"type\":\"create\",\"tableName\":\"n1\",\"tableType\":\"normal\",\"columns\":[{\"name\":\"ts\",\"type\":9},{\"name\":\"c1\",\"type\":4},{\"name\":\"c2\",\"type\":10,\"length\":4}],\"tags\":[]}", + "{\"type\":\"alter\",\"tableName\":\"n1\",\"tableType\":\"normal\",\"alterType\":5,\"colName\":\"c3\",\"colType\":5}", + "{\"type\":\"alter\",\"tableName\":\"n1\",\"tableType\":\"normal\",\"alterType\":7,\"colName\":\"c2\",\"colType\":10,\"colLength\":8}", + "{\"type\":\"alter\",\"tableName\":\"n1\",\"tableType\":\"normal\",\"alterType\":10,\"colName\":\"c3\",\"colNewName\":\"cc3\"}", + "{\"type\":\"alter\",\"tableName\":\"n1\",\"tableType\":\"normal\",\"alterType\":9}", + "{\"type\":\"alter\",\"tableName\":\"n1\",\"tableType\":\"normal\",\"alterType\":6,\"colName\":\"c1\"}", + "{\"type\":\"create\",\"tableName\":\"jt\",\"tableType\":\"super\",\"columns\":[{\"name\":\"ts\",\"type\":9},{\"name\":\"i\",\"type\":4}],\"tags\":[{\"name\":\"t\",\"type\":15}]}", + "{\"type\":\"create\",\"tableName\":\"jt1\",\"tableType\":\"child\",\"using\":\"jt\",\"tagNum\":1,\"tags\":[{\"name\":\"t\",\"type\":15,\"value\":\"{\\\"k1\\\":1,\\\"k2\\\":\\\"hello\\\"}\"}]}", + "{\"type\":\"create\",\"tableName\":\"jt2\",\"tableType\":\"child\",\"using\":\"jt\",\"tagNum\":1,\"tags\":[]}" + }; + + for(int i = 0; i < sizeof(result)/sizeof(result[0]); i++){ + taosFprintfFile(pFile2, result[i]); + taosFprintfFile(pFile2, "\n"); + } } + taosCloseFile(&pFile2); } int main(int argc, char* argv[]) { - if(argc == 3 && strcmp(argv[1], "-c") == 0) { - strcpy(dir, argv[2]); - }else{ -// strcpy(dir, "../../../sim/psim/cfg"); - strcpy(dir, "/var/log"); + for (int32_t i = 1; i < argc; i++) { + if(strcmp(argv[i], "-c") == 0){ + strcpy(g_conf.dir, argv[++i]); + }else if(strcmp(argv[i], "-s") == 0){ + g_conf.snapShot = true; + }else if(strcmp(argv[i], "-d") == 0){ + g_conf.dropTable = true; + }else if(strcmp(argv[i], "-sv") == 0){ + g_conf.srcVgroups = atol(argv[++i]); + }else if(strcmp(argv[i], "-dv") == 0){ + g_conf.dstVgroups = atol(argv[++i]); + } } printf("env init\n"); - initLogFile(); + if(strlen(g_conf.dir) != 0){ + initLogFile(&g_conf); + } - if (init_env() < 0) { + if (init_env(&g_conf) < 0) { return -1; } create_topic(); - tmq_t* tmq = build_consumer(); + tmq_t* tmq = build_consumer(&g_conf); tmq_list_t* topic_list = build_topic_list(); basic_consume_loop(tmq, topic_list); - /*sync_consume_loop(tmq, topic_list);*/ taosCloseFile(&g_fp); } diff --git a/tests/test/c/tmq_taosx_snapshot_ci.c b/tests/test/c/tmq_taosx_snapshot_ci.c deleted file mode 100644 index e3a52f7cad..0000000000 --- a/tests/test/c/tmq_taosx_snapshot_ci.c +++ /dev/null @@ -1,512 +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 -#include -#include -#include -#include -#include "taos.h" -#include "types.h" - -static int running = 1; -TdFilePtr g_fp = NULL; -char dir[64]={0}; - -static TAOS* use_db(){ - TAOS* pConn = taos_connect("localhost", "root", "taosdata", NULL, 0); - if (pConn == NULL) { - return NULL; - } - - TAOS_RES* pRes = taos_query(pConn, "use db_taosx"); - if (taos_errno(pRes) != 0) { - printf("error in use db_taosx, reason:%s\n", taos_errstr(pRes)); - return NULL; - } - taos_free_result(pRes); - return pConn; -} - -static void msg_process(TAOS_RES* msg) { - /*memset(buf, 0, 1024);*/ - printf("-----------topic-------------: %s\n", tmq_get_topic_name(msg)); - printf("db: %s\n", tmq_get_db_name(msg)); - printf("vg: %d\n", tmq_get_vgroup_id(msg)); - TAOS *pConn = use_db(); - if (tmq_get_res_type(msg) == TMQ_RES_TABLE_META) { - char* result = tmq_get_json_meta(msg); - if (result) { - printf("meta result: %s\n", result); - } - taosFprintfFile(g_fp, result); - taosFprintfFile(g_fp, "\n"); - tmq_free_json_meta(result); - } - - tmq_raw_data raw = {0}; - tmq_get_raw(msg, &raw); - int32_t ret = tmq_write_raw(pConn, raw); - printf("write raw data: %s\n", tmq_err2str(ret)); - -// else{ -// while(1){ -// int numOfRows = 0; -// void *pData = NULL; -// taos_fetch_raw_block(msg, &numOfRows, &pData); -// if(numOfRows == 0) break; -// printf("write data: tbname:%s, numOfRows:%d\n", tmq_get_table_name(msg), numOfRows); -// int ret = taos_write_raw_block(pConn, numOfRows, pData, tmq_get_table_name(msg)); -// printf("write raw data: %s\n", tmq_err2str(ret)); -// } -// } - - taos_close(pConn); -} - -int32_t init_env() { - TAOS* pConn = taos_connect("localhost", "root", "taosdata", NULL, 0); - if (pConn == NULL) { - return -1; - } - - TAOS_RES* pRes = taos_query(pConn, "drop database if exists db_taosx"); - if (taos_errno(pRes) != 0) { - printf("error in drop db_taosx, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "create database if not exists db_taosx vgroups 1"); - if (taos_errno(pRes) != 0) { - printf("error in create db_taosx, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "drop database if exists abc1"); - if (taos_errno(pRes) != 0) { - printf("error in drop db, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "create database if not exists abc1 vgroups 1"); - if (taos_errno(pRes) != 0) { - printf("error in create db, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "use abc1"); - if (taos_errno(pRes) != 0) { - printf("error in use db, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, - "create stable if not exists st1 (ts timestamp, c1 int, c2 float, c3 binary(16)) tags(t1 int, t3 " - "nchar(8), t4 bool)"); - if (taos_errno(pRes) != 0) { - printf("failed to create super table st1, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "create table if not exists ct0 using st1 tags(1000, \"ttt\", true)"); - if (taos_errno(pRes) != 0) { - printf("failed to create child table tu1, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "insert into ct0 values(1626006833600, 1, 2, 'a')"); - if (taos_errno(pRes) != 0) { - printf("failed to insert into ct0, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "create table if not exists ct1 using st1(t1) tags(2000)"); - if (taos_errno(pRes) != 0) { - printf("failed to create child table ct1, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "create table if not exists ct2 using st1(t1) tags(NULL)"); - if (taos_errno(pRes) != 0) { - printf("failed to create child table ct2, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "insert into ct1 values(1626006833600, 3, 4, 'b')"); - if (taos_errno(pRes) != 0) { - printf("failed to insert into ct1, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "create table if not exists ct3 using st1(t1) tags(3000)"); - if (taos_errno(pRes) != 0) { - printf("failed to create child table ct3, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "insert into ct3 values(1626006833600, 5, 6, 'c') ct1 values(1626006833601, 2, 3, 'sds') (1626006833602, 4, 5, 'ddd') ct0 values(1626006833602, 4, 3, 'hwj') ct1 values(now+5s, 23, 32, 's21ds')"); - if (taos_errno(pRes) != 0) { - printf("failed to insert into ct3, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "alter table st1 add column c4 bigint"); - if (taos_errno(pRes) != 0) { - printf("failed to alter super table st1, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "alter table st1 modify column c3 binary(64)"); - if (taos_errno(pRes) != 0) { - printf("failed to alter super table st1, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "insert into ct3 values(1626006833605, 53, 63, 'cffffffffffffffffffffffffffff', 8989898899999) (1626006833609, 51, 62, 'c333', 940)"); - if (taos_errno(pRes) != 0) { - printf("failed to insert into ct3, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "insert into ct3 select * from ct1"); - if (taos_errno(pRes) != 0) { - printf("failed to insert into ct3, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "alter table st1 add tag t2 binary(64)"); - if (taos_errno(pRes) != 0) { - printf("failed to alter super table st1, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "alter table ct3 set tag t1=5000"); - if (taos_errno(pRes) != 0) { - printf("failed to slter child table ct3, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "delete from abc1 .ct3 where ts < 1626006833606"); - if (taos_errno(pRes) != 0) { - printf("failed to insert into ct3, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "create table if not exists n1(ts timestamp, c1 int, c2 nchar(4))"); - if (taos_errno(pRes) != 0) { - printf("failed to create normal table n1, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "alter table n1 add column c3 bigint"); - if (taos_errno(pRes) != 0) { - printf("failed to alter normal table n1, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "alter table n1 modify column c2 nchar(8)"); - if (taos_errno(pRes) != 0) { - printf("failed to alter normal table n1, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "alter table n1 rename column c3 cc3"); - if (taos_errno(pRes) != 0) { - printf("failed to alter normal table n1, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "alter table n1 comment 'hello'"); - if (taos_errno(pRes) != 0) { - printf("failed to alter normal table n1, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "alter table n1 drop column c1"); - if (taos_errno(pRes) != 0) { - printf("failed to alter normal table n1, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "insert into n1 values(now, 'eeee', 8989898899999) (now+9s, 'c333', 940)"); - if (taos_errno(pRes) != 0) { - printf("failed to insert into n1, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "create table jt(ts timestamp, i int) tags(t json)"); - if (taos_errno(pRes) != 0) { - printf("failed to create super table jt, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "create table jt1 using jt tags('{\"k1\":1, \"k2\":\"hello\"}')"); - if (taos_errno(pRes) != 0) { - printf("failed to create super table jt, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "create table jt2 using jt tags('')"); - if (taos_errno(pRes) != 0) { - printf("failed to create super table jt2, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "insert into jt1 values(now, 1)"); - if (taos_errno(pRes) != 0) { - printf("failed to create super table jt1, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "insert into jt2 values(now, 11)"); - if (taos_errno(pRes) != 0) { - printf("failed to create super table jt2, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - taos_close(pConn); - return 0; -} - -int32_t create_topic() { - printf("create topic\n"); - TAOS_RES* pRes; - TAOS* pConn = taos_connect("localhost", "root", "taosdata", NULL, 0); - if (pConn == NULL) { - return -1; - } - - pRes = taos_query(pConn, "use abc1"); - if (taos_errno(pRes) != 0) { - printf("error in use db, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - pRes = taos_query(pConn, "create topic topic_ctb_column with meta as database abc1"); - if (taos_errno(pRes) != 0) { - printf("failed to create topic topic_ctb_column, reason:%s\n", taos_errstr(pRes)); - return -1; - } - taos_free_result(pRes); - - taos_close(pConn); - return 0; -} - -void tmq_commit_cb_print(tmq_t* tmq, int32_t code, void* param) { - printf("commit %d tmq %p param %p\n", code, tmq, param); -} - -tmq_t* build_consumer() { -#if 0 - TAOS* pConn = taos_connect("localhost", "root", "taosdata", NULL, 0); - assert(pConn != NULL); - - TAOS_RES* pRes = taos_query(pConn, "use abc1"); - if (taos_errno(pRes) != 0) { - printf("error in use db, reason:%s\n", taos_errstr(pRes)); - } - taos_free_result(pRes); -#endif - - tmq_conf_t* conf = tmq_conf_new(); - tmq_conf_set(conf, "group.id", "tg2"); - tmq_conf_set(conf, "client.id", "my app 1"); - tmq_conf_set(conf, "td.connect.user", "root"); - tmq_conf_set(conf, "td.connect.pass", "taosdata"); - tmq_conf_set(conf, "msg.with.table.name", "true"); - tmq_conf_set(conf, "enable.auto.commit", "true"); - tmq_conf_set(conf, "enable.heartbeat.background", "true"); - tmq_conf_set(conf, "experimental.snapshot.enable", "true"); - /*tmq_conf_set(conf, "experimental.snapshot.enable", "true");*/ - - 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; -} - -tmq_list_t* build_topic_list() { - tmq_list_t* topic_list = tmq_list_new(); - tmq_list_append(topic_list, "topic_ctb_column"); - /*tmq_list_append(topic_list, "tmq_test_db_multi_insert_topic");*/ - return topic_list; -} - -void basic_consume_loop(tmq_t* tmq, tmq_list_t* topics) { - int32_t code; - - if ((code = tmq_subscribe(tmq, topics))) { - fprintf(stderr, "%% Failed to start consuming topics: %s\n", tmq_err2str(code)); - printf("subscribe err\n"); - return; - } - int32_t cnt = 0; - while (running) { - TAOS_RES* tmqmessage = tmq_consumer_poll(tmq, 1000); - if (tmqmessage) { - cnt++; - msg_process(tmqmessage); - /*if (cnt >= 2) break;*/ - /*printf("get data\n");*/ - taos_free_result(tmqmessage); - /*} else {*/ - /*break;*/ - /*tmq_commit_sync(tmq, NULL);*/ - }else{ - break; - } - } - - code = tmq_consumer_close(tmq); - if (code) - fprintf(stderr, "%% Failed to close consumer: %s\n", tmq_err2str(code)); - else - fprintf(stderr, "%% Consumer closed\n"); -} - -void sync_consume_loop(tmq_t* tmq, tmq_list_t* topics) { - static const int MIN_COMMIT_COUNT = 1; - - int msg_count = 0; - int32_t code; - - if ((code = tmq_subscribe(tmq, topics))) { - fprintf(stderr, "%% Failed to start consuming topics: %s\n", tmq_err2str(code)); - return; - } - - tmq_list_t* subList = NULL; - tmq_subscription(tmq, &subList); - char** subTopics = tmq_list_to_c_array(subList); - int32_t sz = tmq_list_get_size(subList); - printf("subscribed topics: "); - for (int32_t i = 0; i < sz; i++) { - printf("%s, ", subTopics[i]); - } - printf("\n"); - tmq_list_destroy(subList); - - while (running) { - TAOS_RES* tmqmessage = tmq_consumer_poll(tmq, 1000); - if (tmqmessage) { - msg_process(tmqmessage); - taos_free_result(tmqmessage); - - /*tmq_commit_sync(tmq, NULL);*/ - /*if ((++msg_count % MIN_COMMIT_COUNT) == 0) tmq_commit(tmq, NULL, 0);*/ - } - } - - code = tmq_consumer_close(tmq); - if (code) - fprintf(stderr, "%% Failed to close consumer: %s\n", tmq_err2str(code)); - else - fprintf(stderr, "%% Consumer closed\n"); -} - -void initLogFile() { - char f1[256] = {0}; - char f2[256] = {0}; - - sprintf(f1, "%s/../log/tmq_taosx_tmp_snapshot.source", dir); - sprintf(f2, "%s/../log/tmq_taosx_tmp_snapshot.result", dir); - TdFilePtr pFile = taosOpenFile(f1, TD_FILE_TEXT | TD_FILE_TRUNC | TD_FILE_STREAM); - if (NULL == pFile) { - fprintf(stderr, "Failed to open %s for save result\n", f1); - exit(-1); - } - g_fp = pFile; - - TdFilePtr pFile2 = taosOpenFile(f2, TD_FILE_TEXT | TD_FILE_TRUNC | TD_FILE_STREAM); - if (NULL == pFile2) { - fprintf(stderr, "Failed to open %s for save result\n", f2); - exit(-1); - } - char *result[] = { - "{\"type\":\"create\",\"tableName\":\"st1\",\"tableType\":\"super\",\"columns\":[{\"name\":\"ts\",\"type\":9},{\"name\":\"c1\",\"type\":4},{\"name\":\"c2\",\"type\":6},{\"name\":\"c3\",\"type\":8,\"length\":64},{\"name\":\"c4\",\"type\":5}],\"tags\":[{\"name\":\"t1\",\"type\":4},{\"name\":\"t3\",\"type\":10,\"length\":8},{\"name\":\"t4\",\"type\":1},{\"name\":\"t2\",\"type\":8,\"length\":64}]}", - "{\"type\":\"create\",\"tableName\":\"ct0\",\"tableType\":\"child\",\"using\":\"st1\",\"tagNum\":4,\"tags\":[{\"name\":\"t1\",\"type\":4,\"value\":1000},{\"name\":\"t3\",\"type\":10,\"value\":\"\\\"ttt\\\"\"},{\"name\":\"t4\",\"type\":1,\"value\":1}]}", - "{\"type\":\"create\",\"tableName\":\"ct1\",\"tableType\":\"child\",\"using\":\"st1\",\"tagNum\":4,\"tags\":[{\"name\":\"t1\",\"type\":4,\"value\":2000}]}", - "{\"type\":\"create\",\"tableName\":\"ct2\",\"tableType\":\"child\",\"using\":\"st1\",\"tagNum\":4,\"tags\":[]}", - "{\"type\":\"create\",\"tableName\":\"ct3\",\"tableType\":\"child\",\"using\":\"st1\",\"tagNum\":4,\"tags\":[{\"name\":\"t1\",\"type\":4,\"value\":5000}]}", - "{\"type\":\"create\",\"tableName\":\"n1\",\"tableType\":\"normal\",\"columns\":[{\"name\":\"ts\",\"type\":9},{\"name\":\"c2\",\"type\":10,\"length\":8},{\"name\":\"cc3\",\"type\":5}],\"tags\":[]}", - "{\"type\":\"create\",\"tableName\":\"jt\",\"tableType\":\"super\",\"columns\":[{\"name\":\"ts\",\"type\":9},{\"name\":\"i\",\"type\":4}],\"tags\":[{\"name\":\"t\",\"type\":15}]}", - "{\"type\":\"create\",\"tableName\":\"jt1\",\"tableType\":\"child\",\"using\":\"jt\",\"tagNum\":1,\"tags\":[{\"name\":\"t\",\"type\":15,\"value\":\"{\\\"k1\\\":1,\\\"k2\\\":\\\"hello\\\"}\"}]}", - "{\"type\":\"create\",\"tableName\":\"jt2\",\"tableType\":\"child\",\"using\":\"jt\",\"tagNum\":1,\"tags\":[]}", - }; - - for(int i = 0; i < sizeof(result)/sizeof(result[0]); i++){ - taosFprintfFile(pFile2, result[i]); - taosFprintfFile(pFile2, "\n"); - } - taosCloseFile(&pFile2); -} - -int main(int argc, char* argv[]) { - if(argc == 3 && strcmp(argv[1], "-c") == 0) { - strcpy(dir, argv[2]); - }else{ -// strcpy(dir, "../../../sim/psim/cfg"); - strcpy(dir, "/var/log"); - } - - printf("env init\n"); - initLogFile(); - - if (init_env() < 0) { - return -1; - } - create_topic(); - - tmq_t* tmq = build_consumer(); - tmq_list_t* topic_list = build_topic_list(); - basic_consume_loop(tmq, topic_list); - /*sync_consume_loop(tmq, topic_list);*/ - taosCloseFile(&g_fp); -} From 2061922794cabcfc3d915a908020a448a710c666 Mon Sep 17 00:00:00 2001 From: Shuduo Sang Date: Tue, 30 Aug 2022 14:14:08 +0800 Subject: [PATCH 68/76] docs: add gds doc for3.0 (#16507) * docs: add google data studio for 3.0 * docs: fix picture links in markdown --- .../20-third-party/12-google-data-studio.md | 36 ++++++++++++++++ docs/en/20-third-party/gds/gds-01.webp | Bin 0 -> 17116 bytes docs/en/20-third-party/gds/gds-02.png.webp | Bin 0 -> 17500 bytes docs/en/20-third-party/gds/gds-03.png.webp | Bin 0 -> 14868 bytes docs/en/20-third-party/gds/gds-04.png.webp | Bin 0 -> 15904 bytes docs/en/20-third-party/gds/gds-05.png.webp | Bin 0 -> 4678 bytes docs/en/20-third-party/gds/gds-06.png.webp | Bin 0 -> 6038 bytes docs/en/20-third-party/gds/gds-07.png.webp | Bin 0 -> 14274 bytes docs/en/20-third-party/gds/gds-08.png.webp | Bin 0 -> 13944 bytes docs/en/20-third-party/gds/gds-09.png.webp | Bin 0 -> 13868 bytes docs/en/20-third-party/gds/gds-10.png.webp | Bin 0 -> 14516 bytes docs/en/20-third-party/gds/gds-11.png.webp | Bin 0 -> 12822 bytes .../20-third-party/12-google-data-studio.md | 39 ++++++++++++++++++ docs/zh/20-third-party/gds/gds-01.webp | Bin 0 -> 17116 bytes docs/zh/20-third-party/gds/gds-02.png.webp | Bin 0 -> 17500 bytes docs/zh/20-third-party/gds/gds-03.png.webp | Bin 0 -> 14868 bytes docs/zh/20-third-party/gds/gds-04.png.webp | Bin 0 -> 15904 bytes docs/zh/20-third-party/gds/gds-05.png.webp | Bin 0 -> 4678 bytes docs/zh/20-third-party/gds/gds-06.png.webp | Bin 0 -> 6038 bytes docs/zh/20-third-party/gds/gds-07.png.webp | Bin 0 -> 14274 bytes docs/zh/20-third-party/gds/gds-08.png.webp | Bin 0 -> 13944 bytes docs/zh/20-third-party/gds/gds-09.png.webp | Bin 0 -> 13868 bytes docs/zh/20-third-party/gds/gds-10.png.webp | Bin 0 -> 14516 bytes docs/zh/20-third-party/gds/gds-11.png.webp | Bin 0 -> 12822 bytes 24 files changed, 75 insertions(+) create mode 100644 docs/en/20-third-party/12-google-data-studio.md create mode 100644 docs/en/20-third-party/gds/gds-01.webp create mode 100644 docs/en/20-third-party/gds/gds-02.png.webp create mode 100644 docs/en/20-third-party/gds/gds-03.png.webp create mode 100644 docs/en/20-third-party/gds/gds-04.png.webp create mode 100644 docs/en/20-third-party/gds/gds-05.png.webp create mode 100644 docs/en/20-third-party/gds/gds-06.png.webp create mode 100644 docs/en/20-third-party/gds/gds-07.png.webp create mode 100644 docs/en/20-third-party/gds/gds-08.png.webp create mode 100644 docs/en/20-third-party/gds/gds-09.png.webp create mode 100644 docs/en/20-third-party/gds/gds-10.png.webp create mode 100644 docs/en/20-third-party/gds/gds-11.png.webp create mode 100644 docs/zh/20-third-party/12-google-data-studio.md create mode 100644 docs/zh/20-third-party/gds/gds-01.webp create mode 100644 docs/zh/20-third-party/gds/gds-02.png.webp create mode 100644 docs/zh/20-third-party/gds/gds-03.png.webp create mode 100644 docs/zh/20-third-party/gds/gds-04.png.webp create mode 100644 docs/zh/20-third-party/gds/gds-05.png.webp create mode 100644 docs/zh/20-third-party/gds/gds-06.png.webp create mode 100644 docs/zh/20-third-party/gds/gds-07.png.webp create mode 100644 docs/zh/20-third-party/gds/gds-08.png.webp create mode 100644 docs/zh/20-third-party/gds/gds-09.png.webp create mode 100644 docs/zh/20-third-party/gds/gds-10.png.webp create mode 100644 docs/zh/20-third-party/gds/gds-11.png.webp diff --git a/docs/en/20-third-party/12-google-data-studio.md b/docs/en/20-third-party/12-google-data-studio.md new file mode 100644 index 0000000000..fc94f98056 --- /dev/null +++ b/docs/en/20-third-party/12-google-data-studio.md @@ -0,0 +1,36 @@ +--- +sidebar_label: Google Data Studio +title: Use Google Data Studio to access TDengine +--- + +Data Studio is a powerful tool for reporting and visualization, offering a wide variety of charts and connectors and making it easy to generate reports based on predefined templates. Its ease of use and robust ecosystem have made it one of the first choices for people working in data analysis. + +TDengine is a high-performance, scalable time-series database that supports SQL. Many businesses and developers in fields spanning from IoT and Industry Internet to IT and finance are using TDengine as their time-series database management solution. + +The TDengine team immediately saw the benefits of using TDengine to process time-series data with Data Studio to analyze it, and they got to work to create a connector for Data Studio. + +With the release of the TDengine connector in Data Studio, you can now get even more out of your data. To obtain the connector, first go to the Data Studio Connector Gallery, click Connect to Data, and search for “TDengine”. + +![02](gds/gds-02.png.webp) + +Select the TDengine connector and click Authorize. + +![03](gds/gds-03.png.webp) + +Then sign in to your Google Account and click Allow to enable the connection to TDengine. + +![04](gds/gds-04.png.webp) + +In the Enter URL field, type the hostname and port of the server running the TDengine REST service. In the following fields, type your username, password, database name, table name, and the start and end times of your query range. Then, click Connect. + +![05](gds/gds-05.png.webp) + +After the connection is established, you can use Data Studio to process your data and create reports. + +![06](gds/gds-06.png.webp) + +In Data Studio, TDengine timestamps and tags are considered dimensions, and all other items are considered metrics. You can create all kinds of custom charts with your data – some examples are shown below. + +![07](gds/gds-07.png.webp) + +With the ability to process petabytes of data per day and provide monitoring and alerting in real time, TDengine is a great solution for time-series data management. Now, with the Data Studio connector, we’re sure you’ll be able to gain new insights and obtain even more value from your data. diff --git a/docs/en/20-third-party/gds/gds-01.webp b/docs/en/20-third-party/gds/gds-01.webp new file mode 100644 index 0000000000000000000000000000000000000000..2e5f9e4ff5db1e37718e2397c9a13a9f0e05602d GIT binary patch literal 17116 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 literal 0 HcmV?d00001 diff --git a/docs/en/20-third-party/gds/gds-02.png.webp b/docs/en/20-third-party/gds/gds-02.png.webp new file mode 100644 index 0000000000000000000000000000000000000000..3b3537f5a488019482f94452e70bd1bd79867ab5 GIT binary patch literal 17500 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 literal 0 HcmV?d00001 diff --git a/docs/en/20-third-party/gds/gds-03.png.webp b/docs/en/20-third-party/gds/gds-03.png.webp new file mode 100644 index 0000000000000000000000000000000000000000..5719436d5b2f21aa861067b966511e4b34d17dce GIT binary patch literal 14868 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
6o7l@PnTsS}|GnXyzD&oBu#_ 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 z zJA0{M=k26GIjenoi($blLm zaF(^QFOuTLz(v=(E(3!d&1Ui%y$#Q$A08LszY{9xuwnHTbADjPisdF^Vy1~FIs0b* z#yTug- zy=0PZ`Iu-?wOn4!y?l~tTgBO#O6#6oNf7_VF{{CJ>t5=iz*3EM8q1X2L)!UxTljWw mRZZiM*SWikq6g3ZXUmM$%N$DecH_lIBE4GjUz_NE9{&%_h5HQv literal 0 HcmV?d00001 diff --git a/docs/en/20-third-party/gds/gds-04.png.webp b/docs/en/20-third-party/gds/gds-04.png.webp new file mode 100644 index 0000000000000000000000000000000000000000..ddaae5c1a63b6b4db692e12491df55b88dcaadee GIT binary patch literal 15904 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 CS=1Q- literal 0 HcmV?d00001 diff --git a/docs/en/20-third-party/gds/gds-05.png.webp b/docs/en/20-third-party/gds/gds-05.png.webp new file mode 100644 index 0000000000000000000000000000000000000000..9a917678fc7e60f0a739fa1e2b0f4fa010d12708 GIT binary patch literal 4678 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 X33vA}NYF8rW)1+7<4G@Mx>)}MCDH(# literal 0 HcmV?d00001 diff --git a/docs/en/20-third-party/gds/gds-06.png.webp b/docs/en/20-third-party/gds/gds-06.png.webp new file mode 100644 index 0000000000000000000000000000000000000000..c76b68d32b5907bd5ba4e4010456f2ca5303448f GIT binary patch literal 6038 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 UaBu7BfucbEyU~K*zW-Jw~ty5jy)wOHy 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 literal 0 HcmV?d00001 diff --git a/docs/en/20-third-party/gds/gds-08.png.webp b/docs/en/20-third-party/gds/gds-08.png.webp new file mode 100644 index 0000000000000000000000000000000000000000..59dcf8b31df8bde8d4073ee0c7b1c7bdd7bd439d GIT binary patch literal 13944 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 literal 0 HcmV?d00001 diff --git a/docs/en/20-third-party/gds/gds-09.png.webp b/docs/en/20-third-party/gds/gds-09.png.webp new file mode 100644 index 0000000000000000000000000000000000000000..b94439f211a814f66d41231c9386c57f3ffe8322 GIT binary patch literal 13868 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

oyT;BdY`J;|dt_bIPg^v=OVVZvusv6V zCc2q-1w8sRnk?Y)O92`UF1pPzv<)RiO0uxxw`^?J!mPxc@-O5h2CtS&HybGE~ z;-X_KFXf1r{wS_@h-2K0s2Xh7NR~&xh?iJ0`?m4eL}64k>nfU$WtDTaeGnxByT+gC zzk~vFJ5wA1o65^Wx0pYx2b5C_l8XMsoT74axlFRj;T|hpc@wLBC{imcImWH6)%J#{ z2jo*I_gg*o3c8}0y4qeUmt9#h9>&FtME5Z5gaGA}C zxjbqhi3ntu+rg48M`gbc1;ysFqX`y!1_A&Dl;IWg2kTgfpBqQe{#Yx^9dPxefdhF-vLu1@T~DU z(D#z9-w7R=y_Ph;eWn1!c8I=2@`kMuU`jCT3I!B4q#uMqH<&$7g;3$$ts@PtAoclw zBURDqS)Bs;`t$JxP|v&?mSaWPZW$5h5|)u}SNBU~?cH=3mYx?_8&~Rh5jPbx_Wr^K zku9Jv{1cb3PoQ<(27&H8puVs+?7dE(Ju>%Yu(zNF>y=;jZKsX>?G$YZph2l+y<_1C zgdBP)I)YDYG%?+2SZwwbwC7!Zcte`J&d@NgtvUerXEvW)6n^ z+pv({5-BqW zsk5D3rUI{Rh{JE6)Ws{Dow##hYFE$*a~ifE<@=@S#lOZ9`|FZmuiO%0}hTMb-*QmTsnD|E)qgVGEa(zoe=0adeef^pyn`sB79lZa;^g zu@|;ZgJQ5eDs#`Wk)P#-lm0UVEjX*sLd{Gme|DgUT-KDX?>^BY7V;j#N43lHG?BH18!2%4{oMc^8nc`Yd zP3Qwa6@kjS?#QhPZ<0??thvt3`uR-gnGtd+Y_M0WE0zCy+-v|Ia%YT|atLN$vtV7t zK|D4X#KQ-gq4w9!t54=Gr7f5c09+v+ah$t`_KVYeW=rwF{e9=!Mj(TM!ZmXCAWl9t z#*M-+`%x`XUaz<)OCGUo=hQTb$d{;70=~7gl$2(n0`tkq6e_ifaI26~c^^12B@RtB z4^H<2SiYGV(KT+vxCpOk^@wRAor%R~z^BPRNxb??rvaYa`ech$_T!~3&wVHNFwT&Q z>2?aHv9J;Z8#&`zfs&%fk@=_-5ILli8oZB&t*(_W$)k61tBquN_VKdAoYhR9nfx(H zIUEaA_oM8s^OqVf%*&=Tlsn+xgH@pCIIR^mKeZ8`k6Rd&911*5aF!Tw-|Zbs<9^$n zk@*{k1$EnYX!FCl@(;dLL={?UlzJGbO%N2XAd^~@d<1g3f=ZkvN)8ewE4E%_5HfwcU z#hQAE<#Q}7dpsT?Jr7Ut75XN5+BTx#`kbH|W9)aBg1@(D2Cl2BlhrovdMr4a8&(>I zR5Ilh<6o$ln5q%}dv2HZ#_o@gub>#rGx{F-%Zhn5iQNlKq-F=&fd*{{ldQ%BP`)jw z5c$P)H?!CKAv5O1-ES{7!gPDX!Pn^;wvS-F3rN~TfSnI?7tYK=722?Qx?b^KNV zG3d}g>g_snY+dgzByqVU2T>cYn@jWaG?XO_FxOQxs~M$)|BGkhYn*VcePWSm2IaB& zXrI`w^jr0Ot`A7Q3{i|x^2Fi2!}{wpG4;VzOY?$KRLTw6h5u``YGf|YnE3j+I04Wc#lBNjZ}5k&K~F{bL)+MS`3-WNr`z*WKEJr*qV|I zjzQX|OqMV>y6&2z!a5Sh`iSH}Ozg~*GnDU`AU~_jOo@039kTiSFI;>vf^wzGKl-`S z{(Ag$QOzT!&|%{(7~>8Qf!2fu;!!7}@kI4+oTG>rB6(;eV9THtYQ^wXM*iJD7WV9p zLW=8mlE|Rima1T}cXAL;f`8t2W~K&#;d(men14!e1F;asq-LM;ckLXG4(EJx(h@F! zT{14N7+fqQL7s6H2}oM zkDCLVf+a(;^vHL)*R$ZZLuh{*@~oVIgLus@e=U}(-r(?xMEHYTM>;-Q!`AxevHrss zm7D#jYJ9hdNA4O!`IO_^FcKK_0ElWF8L|ppDL!-IsbY0Cl;}grb2waxXx<&xQ+Lg! z;V~E%JQ;G{vHCA&ia{aI8Y6Y!5-iC8!ZiAW@-kD>8#1`Ik``=W28$ko=>l*at&E_i zL2zX>*7-uLFn_azwMFzLD6m66{(fJ)poCSj`MO_JTrcbgy?-yPy){X_3d0D7650ly zm6}^%)bl@rzvroIL#*PpB-gW2PJgv7jdF_T$S{1>5pVToDF~)`lW1V2K(onuN=~ZA znUr)Mp9kaLyS{J<;jES;cj-fqmB3H7^{BlK#ZmC@P8ENgXO3k5W}52nuX_xZLeHd6 zrgr%9Us!TClHBt|#uekEQou|T3S-`eXY6k&936JOh;|u_iYp1{({{Cs@o0Ft#Qb7c zhI?x2j^f28it{jb{Ee(gNo6wpExQ&CpI(8wp%i&cz<#shbaF$T2pULVK5aUjo|srN z50A_^9@)xCkJk&4E0xiaW6AQv5r*@W_nBm*`rEFMX37lm-X%lNk`fd^f(`s(TFP0q zvUW(bP0K9zqCBlj{&tLA;syKcZ7wQi9YOt*?hMo~)&wf9#)>zxxo z9*^u%m7+Bh(Ss+5$W@ysxiaqAxG^|wVj~+U+_b6h7Kden?_DdoVw_r!Zb2Sxa@vda z4|JI=KamJ1QPiN*#DFbI>mB?G-hTYFR$+cI*arlyu*_Kvr5uR8dGoK3QP>zH}i5JWG=xF!c*^ZA1UK- zz8hKT$D0G9Or!(J6WS{Q!MY2YY)$EW+ItKA9~Q8u=Yqm3H(&F;)RB=}MJ?na;)o#W z*QAB9Pz>}HAJ&tb#Z(LsFrp3>$Q`syoP3nzK<}I1I=-q zvIT%hY6L!Ezgq2Gq^_)%t)|N)p}ovzP6g8$0z*yevV*l9Dxu(DwKCE~aD`gkWQUa~ zFhpfi)GHYXxBJL07YB#9B*7f;^#TaJfB%MeR@L|GvG^8pk!nxmylJ=LqjoC zQs#R#tmh9T-O3NNeN^m_a=JhxrqA4TW&%f~QDw*{;9!oGlj*T=pl+?dQk`z&5YoCZ z-z>pqIx~MaMbbN*(aF6AbEOUtWFNVwDqikNx=+HgL->N}(LDG1Og-OD-Xgf@Vv-#D zaAJ7Q-^LL3)OPh*8YW4Zh1&Lh`NJVPlF!{bkV;X#r%2&Q{`3ISw7@Ys1Nq2{S=FL6 zkNX(JF9g@)@dX~Llz#PO_H$+9L0bMN-+!MSC+VM$sKZ z10j3>A(pV_DUx}G08aXnHJ1t_>hSWcJ?IOf(4LZ{{V#xunEPd{-Z}YD=%oo$InzrF zP%Ph2wihN8-67k^4sY@_`FEJrcM-wKQnTN$JG!5_wlH*s&d~UNig9|IrnG2F2xQ~P zY?}u5HWTo0z(s~ZGA`D1&FGlF)gszPKeDRZz%_UY(h}_h7x_6=yi^oSRo#oUk6_<`qqS$YtceQX%Q&K$YGUoo+ug~mD8ndX>$fM57b11e`$aSwYnl)Q_x^` zBO3Vg&3%H`O>ud`q^+-zI-IDpgIqq+_?|g%!d;sE{1}{`@}p1-@;ZXp6B1w1M)RS> zvf1O2hZ7Q+6M)>h%$i&>xbh$vY}TTKeaS-qFgL1{zHxw@S4e&3>{r-31E57YTXNom_WNH~L?rI>e zV==T~$#eEs-b+UhaAT87QmMcwU#%AY*SFD_H>nD;aqeG3D;xynRgr1Dq!@aea9S); zL%zg@?0m81z^KXry9J5k!UBaKYq)t%OnOAVz2Bv%mgezlxB``$bYK(35l6`I0qg8s zMId~dKk?=NMZ7Dhvn5t+Ne{dQ6HDE3LJ_I0of@(#0ssBU&q?H3M4yJ=Ezb*VnrbBo z`nx$7J00dk`ZIDn7}Lufk8DnJB<4srw|Xv`@GA^JUUyj);_!l4nW;zK$UN z1uD(Cftuo+jc+@huRv6bi^No|n#b2?L^9V(iw{#bb9ekWelCBeB%q((BVRe12 z|3aGlg>Ero7MzxN_4$ZmR1gbhvi|qPW9OT-LOy&v1>{=O?tHBr7Kd9FYXE(BR0X~~uf2Iz zRDHOy+SFUo_lIKW<|eu%{6X>F0p$(BR4L2aHSK%=FiO=? zTeNv6b)S1#e+MSLyo5jna4DMlI`4b$sV^Quc?f~eeu+lID=pupwUuW2lqwIL z2f!E1%%%ENY%mm5>Y?&_0C9Xg;EF5Z0X2kS)>Ql*_Ul6xv7k2N-u|!Jt)r` zP&KT`D#(74z-iNUC%pWa^*1oPBKsVXq%pN7H>jE-?!8upkn%DWk;6h_1C)+54a6;< za7vI0sd=USWJuv5W8s-`Cm!xlAv&`wz@Q@P_yUA~n-A7wUQ_gMWVTpdHu~(B8mzzW$jyrSZA6mH7bS)L;MlZqW{Qh=gN@l#k(Y z^$ALcjOf*V-(G2DXFiRq0e*-NcZjvhCIkS;<^8{2rZzr#B;3fo7>R;;q6iYlxa{b@ZA<@61AX%Co^G+AQY^V2Y}72SmbFnWGn&B4 z=OIewb^z78M;xma@PO0(@lPS5!1@x{3}6yX8NLsX$xR_j007=|r5cxT zevt#xgYC0WL~|Dcgx!T+hdwT6G9z2?%zye<*Y>ZZji6Hu0ng+L?)%EM<^Hw2ejG;g zbzRzbGmFADh=Ouo0PvbUTa~PfAermSbXb^lxH0HB^yjrHRYI>3L{a70YT{2Qhyfsi zK<}N+tG2Gx!wXy?vQ`0{o?M-7I>t%;P3ZY`ZRKNc;w5+hJL+b z^xAkIzt{Mfng@F;e&S2<-QWfNT=d!VZ2nq&$oe>%mwMHEjn(>6<-gMXy*2Fh+L`m! z`L*^j^w_f?aliHAdy(DK6#1bgdub@n#=X=lK2`E~2-`DPOs=CuMzJ@R^&7Rvr;$%L^EwWg0#A?y(wJo1r3 z-Mk>ihy=pYzt*ZL)uizo=sLzCN55hkjzpJPpk*Q>e*buYw8iHS_G8C$UIS5=r%-Nq zOh5+O(Jep)y?#ZQ%mj2hd9{L4xIU{NUnne9SH~)4EV-iA#6Lk0?U7<<#E27Uh(twH zd`Ry5Y3712Q)z7ER?P8_q^IO7vk#jmJ_NFy=RLJ#GgQ8-D{TsK6Ar5G5DKO z<#R#-WEwt(aUN0kFu5Cli1sqZ;}=XpX5dW+txJ@9OXpD}$~z^GL2E}qh3zrU2a%7O zn#O77B^#wLLBVf(+p|UGc=s}MNJ$;vUi^=l7U~+xyl#}fyKo-zoc}L4q^u~vBpszI zD5rOi-=6cEUw{0L;7Z^)c;togLAVUA)G`+IE~{Bb9Z(&1l6J}Wdh)lie@9u|@;)k; z(+gH}&^0ceW@kh|{;=EJU$mi^NcZ!B0xIZgr9#DflQ5gh>bx>M#4A2=S?TPXzrbe~ za$0uot@tbRHMOb=&ACO17%2l5_4UoNg8R^R6{R%n=06zJf)Y5{*tu$d51p~aa6n2q z0d5KLH%4dWFoEyH3I1{FxjV{Zdn>|#xco#^8z+(txfCqXheQxUz5|QDm{&5~nHI2f z%r#P0T^#4t(c9nqcug}092pNrf`mB2BIvJ<{5HNW6z*YQ1H&{R-b6&q<0RAnt>B!2 z@4u1six1%vlLZ;o1;adBr?$NQ4{=NW~2m?d-hE(oYKizQhr7ap`{$gsw`C$U8V}^ z(z~V!`IF}{S_TtmFHgHkIwg-@kVp3$KFrlN%LiW2GlbNz)eTI|XAzkl+)#{t$HBS& zX&zT%-ro?5T|*7hmxTyOa zf`s>KSa)}drYjhhoj>Zh61FxEk{u48?B=t`INMe+N`83GH!E7X%OD}RW$(@65SR?8 z@31>m)|dlaqe|2568ysRYH(2bGqawg4Z!;S<*ozv`t$B`4`IIUcSkcL@{=35j|p^y zq+x-wq29=u?9vZYsh_55QQ$CHY%RvF<7D;OITij7d4_33;H@AYM!aiVRC*brEeso4 z9luv-7;3K2;HMrrZVVVuv{F~186Pj@7lXIn*CF1yI=g>%VZ!M@buRmHh&34Bz(!Il zB^36o#!gAB{8Px#o|iNC6WL2%=M)|e;hZ^m{y)G;FA)zFVVenS_BY2tlLiae&qw+n zm8iH%(Gtl<$%RNbM9Awcpu9g;NsVjCLPvm=WD8XgF{UOK^ta_>PJkm>{S{XtO6AlRP2gS>8zD6fsdF9I=@0;Fj5d zkmz@u(=ajgl9{>IZdSIjulHVO>df$^9+i-FX}TpJj7a0c!S+R+x(U=jySM7e5J$5_ zDfGP2=ThXBQ zbAWZ#*Vw!VI~(oE1%E4yi3x3A{W59ihN2XQ-kHt4ykU2c^oelBknYcyc&q1e_ajeN z{|HE#44E%bF^RMQ82b3fn30;^qdcg5k;fz&IsS6; zOy}tA-?X6Nb5hQ|J578gw-Y(UpTPODyP>lN_^xE3&w2kyP)Qr?baGJW7EvN1FF$P6 z!`wn7cxcl+C@g}ezw!FKo#0=2@59(v4cb0~ZS~+%ptbE(23S*}=0Z-t_MiSUcV^(B z^9*!(_p&-)Q6u%X#6J*oCt3euzhWAJg!iMT9$t7-S>jLFm_8A%l6*u>UYR23n2^X> z6l%IW<61$R;Y5%nZ!U^Pz+>)E5<|0JSPd6Kj^+BCV$Rtw^82^-?^W(Y*K~OZ9+l|f z)>-A+cO%0}o%gNtI}@>4yk80M^fLco*62Z@1&nfieD@hGRMXKBGDBW8Hz;tgzEZYnE&8n%886 zV@ITE;h%2Xzs$V6utB_F=(H5c_S+_Om9@RU_~= z2KBl@Xv>Pha^_G4LJwV7RREdJJTT%Cro41&q}j0{#oF??-pBwgf`}6mFlwP?f)9@! zN}dU4B+dI5^j;5SubV>V?h`8*N6s~c5~Q6oLT+zWI8OG~55Q3eKkRQlZPQ)gJwP%q zBTM$Z-<1$&tLf=0!2$yV7vSTe@J{7-A&p0kw1`t{b2xN?Ego3mjv`si(zvMNdjAt( zZv1gR)It!MSuXM4+(op%^GB z@(YPe@b#rvvqp1*<2tjU-=LOp{oNdVJW9NcPrI>dI zC8A__8;!%XtGXV3br?YRG>@K8u?RkM;plPp|7L~r8;Nq6IO$PYJlyG8_C~*Z3!MKn z#_?ZB+mmNwWtqRV5MpHwzzUVr3*TNu>!jplkFJE&8D}9j(j=?>It1A!zfb|%{?o;w z|3Xr)3Wn?sQ&`eTxaYZV?X;Witd(uTZThbYJAgEle)^B9V24&J zfc#rUQVK5oea%kk1D1#Er8j4RQ0S~!M57UZQ^5mLzAw44m+xSUkYxa~Q<9h{w=0eR z+xK4yL;bJPPh(;H2*g}e6Z=9DluI60A~&Wd@f94W{vN}+fFt1DvL4E%*v=vAzehvoK1RgYw6A` zC(5_0oT1_1CwBj+*@ zrw^ygSp#znRwS#r&-j*JK+Tx%)5II@WHB}OeI4#E4{5G)C46Kt2I=4i?l9T%wLWt9GS(3m9v1S87RaGcfD^u)w z$t%vc$NQ2xxqX1fkTBuo@CGjL9rl-5ZMNVxcH=r1 znmID=iY#4I`Wk$U)TcaJ7nx|}QnHF2;FOIB871nl&^$S?tFAVVmXNn!R-Mf|&0h0*2~;uw;ZlGvUo`j2vmS|8r4xtpkZ@2pHTwr#KY+(3&tTL~`^CiQ5QT~y&#reKP zpdA+q786=mAvXI3Cd7iTgp(Jm`}K>oTJ1ebbSqiZcL%lrc`5?xsvpH_GHAPT@!Ob% z2#7PWJyb;D)~Y&_Om|jUu5m>bQE`V3fFFu-Q-t`va6#4q^u+iQ+Sfr6NUokSG&H-7lW&GSjMt5Pzz8?Xo%9P~kCNaMaS#)H|?)`pbFcl}&D zTu!P;U`y+MdS_RYaVnV22()+^qVA+C=_40QzPr2W9*p7TGF8FPM22PK`O>FD#Wfs- z^bv_sT6{Qql}rnd#<%%33S<)53DLXdRsz5^6jqj;qu-(C(2NTeK^=MGpY^W2^HkKW zNSGEvjE!w8)=X^}^xos}q%?(f#txo^*XY5a9h1K{R6=cA!ZyPiV_+TS5(4No75wf>FP6V;?Ue7dYPhjnB0xMRzk^cqf^B1>J z&P(cV`|cZSYJT@k8l>h4cNoUdFnE4EEw7}*5l4#|u3bM|^Gkv)3*d3K-I~^{j5N@s zhY)Hxs${zk0#@__Nkd!bNtK?6NU526%&debhm!i0AKW*vhW4hfL9_IC{aBw`F?3t* zLVk@z3#ungiUGy?xp>~`4b<#CSVl{sKx}Hb!56o-F`BMK?XYWgwtnlgk@=UwvaUZI zbWpPW?p1C=MN&U4Vhqw4oB>i>I_*(292h?v!#5suH&xm$IGhQJTS=|^?_OcRL-&Pv z9h!TjQ$t+PUqv=;7)f=u(3eAXXtcbWZ5n#Q;@{NyGpYrTWX;fpgSLZ+~R zDk6CpNAwsecEY0mWFU={eQnuJ)jiEm+rX?ExiK=XUGjt#K6ugvO8&fxg>NR-7fswW zNcjCb=)3tYABv%%r7yKjM-#%x6WhkmCvInAY;f;gf-gHe0i6drFNtTw7HC7+!EC+p z?LVW8VdfC2!sRK~9^&b+z9nP73r{R|l1*NXF;e>aCLZP94O|`cH}0JS0058%W_tj5 zY7<9()kvip7>(&cw9tWul8=5J#t1L(-L#ReMZ&N{lB)A2*q!)Ql_;2x)TD0=*>^ch z^0#==%%Zn2u@3Ydh7*hNIckbU=XJbpbz?~=AYG3BLS zf1(xI>Z)U&x^U%gaquU$NRR};zZ*uY^b4Jo{x~wB zqZca(GUI>YI)Ip%Fp9xoGfmLSVNwr(Iqfg!r>82Nk|L;E4;n~_vRA;n8%uD$YT$Uq z(6=FjfZGXeXwA&NU1%rzfu33Iht5l$yQ12kPvo=v6;wcCJ= zI}F}F2XAtDBjDHVh{_wZY+zhI=#YY70JOLYF34-3ZWn^g$tXE+0Jo}4ycqs|ES=Xu zvX>a=cl3kX7}~6Oe3u)we*rYjwVy0sthP>w?{RbX<3O9Y25-(i4Oq$xJ~6STRmdK0 zm@4LaZQ8)WE4T9;K8>_X{fJESGK~i7{j|?G!mNCR64hrCd&7%A8uE$H>AA*Rb%$tw*j3#ZfXfC=WKE74$3Yil0(F%-NRzniiAbT2e{a=qjUsWKuZ zC-?_rm>!(Ff+GF|%%IC;F_YovnLA7EYndXF1y%2Mq#M+g)hv;uye!j4h5nc1uq!(d zc}Zbhj%-?JpZzS+J-6J=7hB@KR+I9!y&;AC!!|{y{FM_Mq-_HBC(s$&6fg|;dDS>a zV{c013g+CeW3&M2CSL^OEeFQp06zG*IS-AL>sV5GDZd>lhN{`V2)j@Zs*f-Up3`l5 zK$(a1qornY^U2{+ztG{n*l^BVWpOez|7 zarmmZ?2ZUD%lcAYF}1j|m$qI>)LS?Gy*AMea?U(FL}~;AyCct^C-LZ0h*hLcUkh(qiZNVYkrLDT;yKvZ8_$$*=xmq(!$NsEhdB-Z4fKG30M2_ zrK&=Pp>O%es{Qg`sz&5WBvj5iQ>xCf zU6PW|&jqMb4ZDR&amJ&?PTM&Y1D6NKLfxfe+r6mmY9_q$I@0In@`3oi!4q7Sn;*;M z>!=to8u}K>R=*L~g%OLASh%sJcilpU)SkDKy~}?%GX&rwST9!)-#R;3d`os`{C*2# z4D>erpwQ?zkAFdosd+j}19#IB`n~82&=j(-g0C-RZICe=VQDJ|I+obDLRpoos9=;t z1>yHkJ3TX|79l-oK+Q!fmmsd>+Briomq z(Hun%g)%a>JS0sn-eJdE`}Wl_a;Rp&O>#yZ%b0*gJAi9y*Es{p*@JsZHY$pB>IrP$ zX>?A>p;w%*EB2OZYYsbKDv2IJMMXPDt>?0?ZA1LdyK>mturg2f&Wp#tYO&u5p+zR# z;Pfr-t*eXe=J!pUrH8N2$nJlQsH1J0(-DK&Go?-19vah-Pkt8f=Ol`(Lfk(;512)6 zW;ewYoiGPtn@K*@bql9~9(!fXRoLN{v@j_sd$Aa_@aA?_xoR)8fpk8JWO=bhlS!E# ze&!fg-a=fvG5YZ&{XkuN0qc7QwQKt1NZZEo^jZ{05-TQIr2WmLAGT{CtM9VkvnQ93 zXk^=&0r$i7V6M*t%&y{075vQ&Jg?2xr#bODH)sAvt85fJN8^BR-OZc;uP=DKww8;Cht``{ej3trasTe9=}`4d zt`y`^FtM8TLWV(R8A}6irc))q=9XwrdlJ|08Q%rEb8^pf1)!yL9p2-nQnb^KF-Eh( z@{6$m8>~4${=y$JhkWMoVX4*r`M?oybL-h{dTSTbykW$gH!E{6luLkJg4Lm=0v4cv zKSas?TxhnKhDgfM-S!=JwnNXVqoA8u7um<59}ys;*y z8%@aCT&p3~(=8@KC=%LnG~)v(;aL`j=L|1`NnuM&TJ&;c$K}oxwev%CwgGUTo~4!o znK2=|bi?r8wRQv()Ub|Ii{2}7HCM8E)4d25I1>$CYEhTFntLS!Ca~;u6Rb(u&I)Es zo{pJ*d>c7&&yiC08R>~us@jY@@=(J7Y3xR^A;OwbCL5pIknW&7amh6(sT{e9x@=3< zZ?ehE(bqyeIk5v-k0h8vop8~H`1~3El_@BJ;GVFRk@hwsUEzy((J4hQB33l~V#P%e zX5kLsqMzL!uKhg^K3}hJF{tx(KNNO{^HSmWyPN&@j8Q zxu{&N%~rUo8L_wTge##drYy>!^;V-N!*GMBwHUOoV?DaF1+qjD^uui2^2u_UGNG=EYvb4+W9P&JWOOA7(X`G`v zKP(c87A!nM!@;tlT4KsW8`{0$w>*#O_5E~qVfV1vozC3hX@v&Af4o!8vHM6AX}rJ{ z*bYE|6kowvO7hmFjygE{3Q0DQpL)gqso<9;TvgT>U)1KJV(A_8HyhO7?=6~zSR6zZ zWsmN5e>#hleynBU30RuyoZMp+^P1nYxF=VB&ej7q(#Sf0W_|KF zYrgBMOB)lzZGdm-P+hL+@;#dv;-ISQCFDavM;x49iLyFD7aNO|n|lv-KphNU>k_yw`mXzZ4}%aod4Ts%{liFY*U6w7Mu%@3&t5ub*t^?zYuTV3{s|M^Xw!5!^%D5y z4w>~fzNEF|RQ->Kpf->`T9?hhOwnaZOf4j5eQ_%&D-bPe_!A3DO8rZDnX$z)a!6CHhV%+r-F;VMTK7r{6lWR$wpJt@PLfLar=#q5AaibV6=f#?@8K~7jK8tTf;t~z+snU)KY|8x5ef&TQRET{1_>%H&133tgtQ|L z#gbFPARtdt4?Lkq=YkFTUFklyyF+PZcn3Rg$z!HH(A-|<&nQgBtb+qGSFg}wt@B~u z2d-cFD38y0Keb8NhKluos@?9kcba#U7Bv27Jxo`hcG`;1LbL3|+o)nc^GPZHeg#B2 zxUpTQXBi+0_Ij&|VAbVC|7v8z(z(o2A|K^0&e9DLwnfpgH%@w$u&?@z?!>7zii(SG zxsrcS@gmBJst*55K98PIfAdHLFDIFoD_S&;6%d zAjFoBBc**UotK9-WMp4gUaAmPH(2%;Xypq$E^)q>>VUN5#Q6i|sG_TtO>gZ&5MuST z6h|el0vWCkqy}9&>neP{zZKkwm{RGFTnqBNw1q=b0Qok?c=7a=69&{{d+k9tQZI$M}i*mm{I?cIIT{cTxj!9#B`qzDNdsSvIoJ*G8YObbmy} zt~j{Q@rJ)5(u9_%`L~WUKa`3N3F71L%5fMq@=aNK(99BuX{-R+(74|0-}~mzGJ0Xx zT70{aZTp$vt`+3~N=UUNF3fh4TzdEST*!9AU!qBqb^E!__LfwV1-34B%Tva{n8818 zL2^Sh`|V4|c37)O9sYof?)zpwbxVJ#&DpB zK4Uo}yPy?hfB8G;Mesd=6rMXHZ8Pwjfkjzj%;crVpc?H^g;j+|&|f7&K=<`BCM;7y zO{{BEQgjDR%UFR1yY=x<&?&ce;Cq$LQ+IK#iZofqENCGNbWDvD^}u~i>7}7~MpvY* zyGH6%Y%%v1)1|)db8-R-ImDH7yFy#@`YTh1;>4XFC40O{fBzZt8VT3a2dWsG9}@ZC zvoFB(WXP-bf|2La4w3q*ZL*b*Cf7~**0R`1iHkqkHX4<6DY5Q8P?2{GgiW;N>7kBG z`bADUW_Q|9%8d(`=k7Bgdy%=T^4CHw%kVUao&^0wH`WX9&jW&%1|~ZZ8$ngfgF_qW z$O96^F2VzKy+m0O$%>%3JTRm*yW&?ka~Tzworjv1-YoZvC+!x0q~9VlSX{%TG_M1S z^Vx3A>zzc8dTv2Q{i!LrbW9-FQS`lNV+_>oKzqJL5`K4Z958Y~fWI@;hct1TKJ1?F zi7$*}Jmer&cz-Bd&ooYBs_rW~ZuxM%yPVec<;NIN205jogPr9lM8>ejnQUq#+8a9OI$zJpayCJRQ-tnk3U_zgk04+e+dB_rg=u0_g#dwA`9L=cHoiqMS z-h5{fHjPuV_i0=^IF-IL=R+ij=R0F*$89uIr`JXD;M9r2KV5`ZCtnt`@Z>k9i5PPF zX1OV7HOymh)QlpG%A-Ck+&iT`)6*$Im)UBoF-Z*;qtr zzc?h(VAxlv+e_1}!S&W63-ps*47SL@KcX@OLe0sILKwTq=jr*u3hX&LZo!YqyWk>H8=;E}O-YJ(VL0Imy9!oI^9MEU1Oe~<>RKB-UOKKtNSL*Usdy2F_Q0kFyb>ch(T*9q+rs3uz^)63HUk7rOkf95?=x}m_G*OVnwI^;g;DEq1S~m zy7q^f8c^tQ+L!s}#>`E&FDuAHn_8{>p--hiBJN4z>W~varEXUeM^@1sa-L*4Tw5FE zg+$|rvIvu=l>l-p`ItihR{1FGphnN8f<@nq6bJA1=2A)&P!P{B`S}8LH!uDC_Wdc?RmZ)*{8KW! zvB$m3qlR6nVeUEPDh-V*s&MO^Rey_DE(m2HvoI_@OHO7Gs$*dgDI)HYR_oV9EeM}t z1E|>WK9}@oeyK?2PPII5MNM8N2VlpQ{$uepdAE*Ok-Tb&H{ zJeWPic9d7z_fUZVI$rKDR9hs9$yQYB3B?W{Rm#exg&63%1%E9*Q)6Oj6^eCqOtzQ>vxjZ2NZzyv<45WYBg)WkWl(az&x(YSq7UdYII+_Mqu9Oq9$=ZMT~Yk z#Lp>0jNU+9T?=MZI#Q34BM*vm%t)Y+N~EVZs!!mmTlBB%_Ou@q@kh#Y+T544Sr^%Q zZ*zw3FBq|TSCe4dIrg98|7!N+T_{uB2qmPVAO@2{;seL3zTzmzf5=|=ur%ReO zDafa8!zIcQ4)VMQfI?>qTe2;kX#-P#dGC54eo;T1jSFaV z6liaLKJs5Jbo?Qg;p$4RE4$T)p@(YiAz2~znybRKTONo^(6;NN0V^*om@55reflg` z7)-vx>Oe_3FpcRERswJ!^&0##ZOf+X-gzPSkUsDY3)Ee3J|Cji@PEp< z!jR&Ps3r({h9vQ`B6Qy)*T<|B5AK-APrj1>Iaif)z8BRFokZ?^Hr{RhN*Ng##y zgXae4Nzw(FoqayK<5)|aYHja4_xC-Ja-%lp0?IS)I!Zt%{HJ+k%a)BXh8({coq`sl zS5$Si`Jw)(DbmR!{`#*Nq4vc0-JdtY!|(|+QG-7OBmg$GyQL_Ili@NngD$Kkf~Ovv z#nwDKN0vzW#3HIZtqrhak=;drLId{^j zlUo-8RlFBvoMH{tL~~RG9%ph-m2Pbk%fsSvIY!n@l<)5 zS3jVyUoGq9ct0F$ix_S1^du)AC@iK-^P=@gX7^2)xc7*5@GLX7Yc5RU$YE$A9G^ykgDz9FW3z`ObK6SD;3)%zQEoUkQ zv0ZB@q0K@V?LgQ~klCwI8C}6~M}J+b@!>>=P)9FN`5QKEEf zNC>Swu23$Guy?j6-D04lE%i*YrDe_<%H(jL>p^^t+#YuqKt{%nHl34DunOKkDwR(` z8culOxFOO|sb#p5WU(m@$+NgV9|cQ{Fh==CZsSEbJADCcUUnznOQ&#PMX+d7jyoS& zMDpXbH@zz?%Z4@k(@4_o*#2QP-yiwJ8Fu0jhA0fwHJ`SLCA{+FI zuitly{*;B5ZwB$m6MygO0}>@-K{gsz#e!vc#!fuj?dh*m@9^noo0piJ1Wbx=69uH9 zJmhee-KUv7fR9_0^Pq%s%sUYY!(Qh8k^M*=f@QDedTxzu<_!F%rbO+MXE45lzr$`6~B!JD?C zDUdg4bjBbIpBO-r?=W33G*-WhSI&OU9eGF^HrNt6cHWWawCtX^h_+McctaYqyGF@p z7><;y^9<~wN=DgU5661%lXC!#40dqv@BCZ)Xy|Q6l~m|12F8>o*wdK{s|jLYDGzZR zf4@kTmH5EhNj>ECTDR0z9ZN0`Z)f zJ~X6|INe-THoP@|6@e>4Qun}rtkLOY_Cx$6M6};m3+W!Np;Oh(3D32 z_!^TV(uLiwR_pKyPQ?Z`CC44ab9so?y}f^pOzXgqSRXelrQ#*CC~gSI%EYiTfF5)TBA5C&OQW-yU_K!2X~wpZfc;|c(67s zoVlk#`r?>_^^+=mmYt_k?ZvOEZ!r3MU~2#!4DxOMY6jUv;^$AWw3vgN;WF*o&IWM>4J@irtex zMUtttc{kRA+M|;(`T}an^)w3Gy}6GRu?%wC8s#cr?t!o3A`8-RLJAH1yt=5M+l=tO zw1!fBeCNm+aPoZKOu|;$d89(8bt+hv7N8Ut3K8M&Nmds}xmfqAjBD0BuN~m{IVWfd zfA5+gE}PcP`?YMFGC1GHt01s&_@(!PEivvn$E!eMI{Ao`tN6icCe-2%$_`7oFz;XpE6?w~}MpwRTj6>!f%)yH!4e zZ{TS~7HJzKk{OMi;d&jD5M@m}{I+)P^)L+nMgtn_Ue3=2H(R<@%lkxJJT?s7GP{rR z0%6PASoxAUQ{T}feXE2?=5YAS}4&6zCaO&*Q>TfYIaki2Z`x3(1bp4WaASo z1Vl9S=SOyK98Deu+taO57)_Sl?1kRlC$k?2)%2QF+ z2e0lPN$2z323JQv2yYm`uu;cBX!%Kz1l>ctrT6I})$fR~snFWxlXQxrl|FK7)i9y`>Gs2_&n=4~2a?UHp z?GWbUyg+1{+H7LK7Ds!d^X}`^KCu*OI(~kWs@Gzm4 z?Q^{G-`=y9stn2J{)@tc)P*;C9(|1FM7+>G09LER!#&>jJuDPJ$C{ z2gR3|!HQizTSGr00hHfAfbO-Hx&r$nzgS|_1@s!qxQ^+KSioN6kz|{Yj@D3+(a1ngKq9BA8(U)x z$STrZvkHprYvaRRJ2EuJheLeE+b_>824_@Yv?mg7A6cWoeV>dav@{0%p#f{{h5T5* z&I3XMtdt$2s6@!G`~{E|L>VK6Av)^9Nt<|k?n)zf?z!448p^hL1f0RTV|PgG}?#K?od5#m}G4yejN z+)8jIa)2evA+OKDz1F-aKlUc@G3ir?ZqijyXQa<9v`5@cYHKJI^!psw(EG4#ZYya|ze@T(_1)wQHt@7L+bw;2 znW>r3BS)Zm3eM`5F2@#5-I?eRoDB_|Uk(OALV-)k(l;QVkl-`;)ZZQO)ida(F_DcU zkb`ZeAs0Q_IfLZTGsOeU2B|~nc#v)>-Zg$iNY;b-0EKh|JkK8zrbP=2w!#vw+c%KU=1UwAz_qkohDN zq#No=ab!VjKZKpbjPK-yF1ZoHX>*aOsB6)>KJRd-R&L9ZnIu%m2S4@hID#^vB>st%rglFw2z5?18oB(&T*PrFuIuS+l!7R171{+%KtNxA%g6M50~Dl1dO}x{lp>d z4N$0JdqJOJ3HljqMV=H*)8%O-b?Jz{t=Cu(gQ{oPXGM?UM>}LO2@@Zp%$#FWuiIH& z`4eAI*_TAG>lnGI%+=>Sddd&$z+m64aHNeKF^Pct;O|@f4Tk*OTbp|S|>+COV{M*lgFr&buDoe?stTy#8D0F95 zH!bO$3JMFP5=tb9Rx*+@P5=dOJ$^E_aFkteNqTK&lqCJF1*eU+1YO!<8NLBfVoAru5w#KlA-H$Ng| zhh8UCB|q^+?6Sm4>CCFpJe?2#Hm!u%V9?6kk9r^`P7bvlXLRb51iTe}ebF#hOdK^M^Mn)~I_N>H&OEN=LGz~!@`oVSL#gv+ffx2o~-&^jkn z?SPWTKw&g$P-h0;zX`OzQqB=I?Gr42Ja~e!*bmPEg^%tkb=m(3R)0nl<@HYpP)e=( z;pT4eblQ7l7>zKXWDmUpr7ddSSl+?Ad!ETpfO*vO(bGXq9Sg%&PYunjFL})X`O=IU zIHqsSd_4HZt{!@b@(qp!n)VfK& z`|_%`+*v_d8s%=nfhk=+%QD<)3Kq;H>&^yBX6xXii+-(O$&%1m>@H8_oA_F&*u=)0 he^9{W;VRDfZD?=EC4S7l?5_{^ziza5e*FCV_%BF;FLVF^ literal 0 HcmV?d00001 diff --git a/docs/en/20-third-party/gds/gds-11.png.webp b/docs/en/20-third-party/gds/gds-11.png.webp new file mode 100644 index 0000000000000000000000000000000000000000..fc38cd9a29c00afa48238741c33b439f737a7b8f GIT binary patch literal 12822 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 YFqF#Q#{dA*c^$sGY?<3A`WNSa0m(bvfB*mh literal 0 HcmV?d00001 diff --git a/docs/zh/20-third-party/12-google-data-studio.md b/docs/zh/20-third-party/12-google-data-studio.md new file mode 100644 index 0000000000..bc06f0ea32 --- /dev/null +++ b/docs/zh/20-third-party/12-google-data-studio.md @@ -0,0 +1,39 @@ +--- +sidebar_label: Google Data Studio +title: TDengine Google Data Studio Connector +description: 使用 Google Data Studio 存取 TDengine 数据的详细指南 +--- + +Google Data Studio 是一个强大的报表可视化工具,它提供了丰富的数据图表和数据连接,可以非常方便地按照既定模板生成报表。因其简便易用和生态丰富而在数据分析领域得到一众数据科学家的青睐。 + +Data Studio 可以支持多种数据来源,除了诸如 Google Analytics、Google AdWords、Search Console、BigQuery 等 Google 自己的服务之外,用户也可以直接将离线文件上传至 Google Cloud Storage,或是通过连接器来接入其它数据源。 + +![01](gds/gds-01.webp) + +目前 TDengine 连接器已经发布到 Google Data Studio 应用商店,你可以在 “Connect to Data” 页面下直接搜索 TDengine,将其选作数据源。 + +![02](gds/gds-02.png.webp) + +接下来选择 AUTHORIZE 按钮。 + +![03](gds/gds-03.png.webp) + +设置允许连接自己的账号到外部服务。 + +![04](gds/gds-04.png.webp) + +在接下来的页面选择运行 TDengine REST 服务的 URL,并输入用户名、密码、数据库名称、表名称以及查询时间范围,并点击右上角的 CONNECT 按钮。 + +![05](gds/gds-05.png.webp) + +连接成功后,就可以使用 GDS 方便地进行数据处理并创建报表了。 + +![06](gds/gds-06.png.webp) + +目前的维度和指标规则是:timestamp 类型的字段和 tag 字段会被连接器定义为维度,而其他类型的字段是指标。用户还可以根据自己的需求创建不同的表。 + +![07](gds/gds-07.png.webp) +![08](gds/gds-08.png.webp) +![09](gds/gds-09.png.webp) +![10](gds/gds-10.png.webp) +![11](gds/gds-11.png.webp) diff --git a/docs/zh/20-third-party/gds/gds-01.webp b/docs/zh/20-third-party/gds/gds-01.webp new file mode 100644 index 0000000000000000000000000000000000000000..2e5f9e4ff5db1e37718e2397c9a13a9f0e05602d GIT binary patch literal 17116 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 literal 0 HcmV?d00001 diff --git a/docs/zh/20-third-party/gds/gds-02.png.webp b/docs/zh/20-third-party/gds/gds-02.png.webp new file mode 100644 index 0000000000000000000000000000000000000000..3b3537f5a488019482f94452e70bd1bd79867ab5 GIT binary patch literal 17500 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 literal 0 HcmV?d00001 diff --git a/docs/zh/20-third-party/gds/gds-03.png.webp b/docs/zh/20-third-party/gds/gds-03.png.webp new file mode 100644 index 0000000000000000000000000000000000000000..5719436d5b2f21aa861067b966511e4b34d17dce GIT binary patch literal 14868 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

6o7l@PnTsS}|GnXyzD&oBu#_ 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 z zJA0{M=k26GIjenoi($blLm zaF(^QFOuTLz(v=(E(3!d&1Ui%y$#Q$A08LszY{9xuwnHTbADjPisdF^Vy1~FIs0b* z#yTug- zy=0PZ`Iu-?wOn4!y?l~tTgBO#O6#6oNf7_VF{{CJ>t5=iz*3EM8q1X2L)!UxTljWw mRZZiM*SWikq6g3ZXUmM$%N$DecH_lIBE4GjUz_NE9{&%_h5HQv literal 0 HcmV?d00001 diff --git a/docs/zh/20-third-party/gds/gds-04.png.webp b/docs/zh/20-third-party/gds/gds-04.png.webp new file mode 100644 index 0000000000000000000000000000000000000000..ddaae5c1a63b6b4db692e12491df55b88dcaadee GIT binary patch literal 15904 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 CS=1Q- literal 0 HcmV?d00001 diff --git a/docs/zh/20-third-party/gds/gds-05.png.webp b/docs/zh/20-third-party/gds/gds-05.png.webp new file mode 100644 index 0000000000000000000000000000000000000000..9a917678fc7e60f0a739fa1e2b0f4fa010d12708 GIT binary patch literal 4678 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 X33vA}NYF8rW)1+7<4G@Mx>)}MCDH(# literal 0 HcmV?d00001 diff --git a/docs/zh/20-third-party/gds/gds-06.png.webp b/docs/zh/20-third-party/gds/gds-06.png.webp new file mode 100644 index 0000000000000000000000000000000000000000..c76b68d32b5907bd5ba4e4010456f2ca5303448f GIT binary patch literal 6038 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 UaBu7BfucbEyU~K*zW-Jw~ty5jy)wOHy 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 literal 0 HcmV?d00001 diff --git a/docs/zh/20-third-party/gds/gds-08.png.webp b/docs/zh/20-third-party/gds/gds-08.png.webp new file mode 100644 index 0000000000000000000000000000000000000000..59dcf8b31df8bde8d4073ee0c7b1c7bdd7bd439d GIT binary patch literal 13944 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 literal 0 HcmV?d00001 diff --git a/docs/zh/20-third-party/gds/gds-09.png.webp b/docs/zh/20-third-party/gds/gds-09.png.webp new file mode 100644 index 0000000000000000000000000000000000000000..b94439f211a814f66d41231c9386c57f3ffe8322 GIT binary patch literal 13868 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

oyT;BdY`J;|dt_bIPg^v=OVVZvusv6V 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 literal 0 HcmV?d00001 diff --git a/docs/zh/20-third-party/gds/gds-11.png.webp b/docs/zh/20-third-party/gds/gds-11.png.webp new file mode 100644 index 0000000000000000000000000000000000000000..fc38cd9a29c00afa48238741c33b439f737a7b8f GIT binary patch literal 12822 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 YFqF#Q#{dA*c^$sGY?<3A`WNSa0m(bvfB*mh literal 0 HcmV?d00001 From 254d16667c673e79df0c3353a8fcd13461957f01 Mon Sep 17 00:00:00 2001 From: dapan1121 Date: Tue, 30 Aug 2022 14:19:02 +0800 Subject: [PATCH 69/76] fix: no memory free in parser for stmt --- source/libs/parser/src/parInsert.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/libs/parser/src/parInsert.c b/source/libs/parser/src/parInsert.c index 56fbafe76d..049d1ef545 100644 --- a/source/libs/parser/src/parInsert.c +++ b/source/libs/parser/src/parInsert.c @@ -1669,6 +1669,10 @@ int32_t parseInsertSql(SParseContext* pContext, SQuery** pQuery, SParseMetaCache pDb = taosHashIterate(context.pDbFNameHashObj, pDb); } } + if (pContext->pStmtCb) { + context.pVgroupsHashObj = NULL; + context.pTableBlockHashObj = NULL; + } destroyInsertParseContext(&context); return code; } From 417a29af0a885c048c2da7f4d263b8d91e17f012 Mon Sep 17 00:00:00 2001 From: Xiaoyu Wang Date: Tue, 30 Aug 2022 14:32:07 +0800 Subject: [PATCH 70/76] fix: a plan problem of system table complex query --- source/libs/planner/src/planLogicCreater.c | 50 ++++++++++++---------- source/libs/planner/test/planSysTbTest.cpp | 6 +++ 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/source/libs/planner/src/planLogicCreater.c b/source/libs/planner/src/planLogicCreater.c index 0667c5f5b9..bf72f52105 100644 --- a/source/libs/planner/src/planLogicCreater.c +++ b/source/libs/planner/src/planLogicCreater.c @@ -197,28 +197,21 @@ static EScanType getScanType(SLogicPlanContext* pCxt, SNodeList* pScanPseudoCols return SCAN_TYPE_TABLE; } -static SNode* createPrimaryKeyCol(uint64_t tableId) { +static SNode* createFirstCol(uint64_t tableId, const SSchema* pSchema) { SColumnNode* pCol = (SColumnNode*)nodesMakeNode(QUERY_NODE_COLUMN); if (NULL == pCol) { return NULL; } - pCol->node.resType.type = TSDB_DATA_TYPE_TIMESTAMP; - pCol->node.resType.bytes = tDataTypes[TSDB_DATA_TYPE_TIMESTAMP].bytes; + pCol->node.resType.type = pSchema->type; + pCol->node.resType.bytes = pSchema->bytes; pCol->tableId = tableId; - pCol->colId = PRIMARYKEY_TIMESTAMP_COL_ID; + pCol->colId = pSchema->colId; pCol->colType = COLUMN_TYPE_COLUMN; - strcpy(pCol->colName, "#primarykey"); + strcpy(pCol->colName, pSchema->name); return (SNode*)pCol; } -static int32_t addPrimaryKeyCol(uint64_t tableId, SNodeList** pCols) { - if (NULL == *pCols) { - *pCols = nodesMakeList(); - if (NULL == *pCols) { - return TSDB_CODE_OUT_OF_MEMORY; - } - } - +static int32_t addPrimaryKeyCol(uint64_t tableId, const SSchema* pSchema, SNodeList** pCols) { bool found = false; SNode* pCol = NULL; FOREACH(pCol, *pCols) { @@ -229,13 +222,25 @@ static int32_t addPrimaryKeyCol(uint64_t tableId, SNodeList** pCols) { } if (!found) { - if (TSDB_CODE_SUCCESS != nodesListStrictAppend(*pCols, createPrimaryKeyCol(tableId))) { - return TSDB_CODE_OUT_OF_MEMORY; - } + return nodesListMakeStrictAppend(pCols, createFirstCol(tableId, pSchema)); } return TSDB_CODE_SUCCESS; } +static int32_t addSystableFirstCol(uint64_t tableId, const SSchema* pSchema, SNodeList** pCols) { + if (LIST_LENGTH(*pCols) > 0) { + return TSDB_CODE_SUCCESS; + } + return nodesListMakeStrictAppend(pCols, createFirstCol(tableId, pSchema)); +} + +static int32_t addDefaultScanCol(const STableMeta* pMeta, SNodeList** pCols) { + if (TSDB_SYSTEM_TABLE == pMeta->tableType) { + return addSystableFirstCol(pMeta->uid, pMeta->schema, pCols); + } + return addPrimaryKeyCol(pMeta->uid, pMeta->schema, pCols); +} + static int32_t makeScanLogicNode(SLogicPlanContext* pCxt, SRealTableNode* pRealTable, bool hasRepeatScanFuncs, SLogicNode** pLogicNode) { SScanLogicNode* pScan = (SScanLogicNode*)nodesMakeNode(QUERY_NODE_LOGIC_PLAN_SCAN); @@ -299,8 +304,8 @@ static int32_t createScanLogicNode(SLogicPlanContext* pCxt, SSelectStmt* pSelect pScan->hasNormalCols = true; } - if (TSDB_CODE_SUCCESS == code && SCAN_TYPE_SYSTEM_TABLE != pScan->scanType) { - code = addPrimaryKeyCol(pScan->tableId, &pScan->pScanCols); + if (TSDB_CODE_SUCCESS == code) { + code = addDefaultScanCol(pRealTable->pMeta, &pScan->pScanCols); } // set output @@ -787,10 +792,8 @@ static int32_t createWindowLogicNode(SLogicPlanContext* pCxt, SSelectStmt* pSele static EDealRes needFillValueImpl(SNode* pNode, void* pContext) { if (QUERY_NODE_COLUMN == nodeType(pNode)) { SColumnNode* pCol = (SColumnNode*)pNode; - if (COLUMN_TYPE_WINDOW_START != pCol->colType && - COLUMN_TYPE_WINDOW_END != pCol->colType && - COLUMN_TYPE_WINDOW_DURATION != pCol->colType && - COLUMN_TYPE_GROUP_KEY != pCol->colType) { + if (COLUMN_TYPE_WINDOW_START != pCol->colType && COLUMN_TYPE_WINDOW_END != pCol->colType && + COLUMN_TYPE_WINDOW_DURATION != pCol->colType && COLUMN_TYPE_GROUP_KEY != pCol->colType) { *(bool*)pContext = true; return DEAL_RES_END; } @@ -1008,7 +1011,8 @@ static int32_t createPartitionLogicNode(SLogicPlanContext* pCxt, SSelectStmt* pS int32_t code = nodesCollectColumns(pSelect, SQL_CLAUSE_PARTITION_BY, NULL, COLLECT_COL_TYPE_ALL, &pPartition->node.pTargets); if (TSDB_CODE_SUCCESS == code && NULL == pPartition->node.pTargets) { - code = nodesListMakeStrictAppend(&pPartition->node.pTargets, nodesCloneNode(nodesListGetNode(pCxt->pCurrRoot->pTargets, 0))); + code = nodesListMakeStrictAppend(&pPartition->node.pTargets, + nodesCloneNode(nodesListGetNode(pCxt->pCurrRoot->pTargets, 0))); } if (TSDB_CODE_SUCCESS == code) { diff --git a/source/libs/planner/test/planSysTbTest.cpp b/source/libs/planner/test/planSysTbTest.cpp index 921f86f09a..6b40e381cc 100644 --- a/source/libs/planner/test/planSysTbTest.cpp +++ b/source/libs/planner/test/planSysTbTest.cpp @@ -32,3 +32,9 @@ TEST_F(PlanSysTableTest, informationSchema) { run("SELECT * FROM information_schema.ins_databases WHERE name = 'information_schema'"); } + +TEST_F(PlanSysTableTest, withAgg) { + useDb("root", "information_schema"); + + run("SELECT COUNT(1) FROM ins_users"); +} From dba4188f8a1ce06b7bb762cdc7d242f78b6e4cba Mon Sep 17 00:00:00 2001 From: Haojun Liao Date: Tue, 30 Aug 2022 14:33:03 +0800 Subject: [PATCH 71/76] fix(query): reset the page id. --- source/libs/executor/src/executorimpl.c | 19 ++++++++++--------- source/libs/executor/src/timewindowoperator.c | 11 ++--------- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/source/libs/executor/src/executorimpl.c b/source/libs/executor/src/executorimpl.c index 19daa7e96a..4ffa80d468 100644 --- a/source/libs/executor/src/executorimpl.c +++ b/source/libs/executor/src/executorimpl.c @@ -3434,6 +3434,7 @@ int32_t getBufferPgSize(int32_t rowSize, uint32_t* defaultPgsz, uint32_t* defaul int32_t doInitAggInfoSup(SAggSupporter* pAggSup, SqlFunctionCtx* pCtx, int32_t numOfOutput, size_t keyBufSize, const char* pKey) { + int32_t code = 0; _hash_fn_t hashFn = taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY); pAggSup->currentPageId = -1; @@ -3450,18 +3451,18 @@ int32_t doInitAggInfoSup(SAggSupporter* pAggSup, SqlFunctionCtx* pCtx, int32_t n getBufferPgSize(pAggSup->resultRowSize, &defaultPgsz, &defaultBufsz); if (!osTempSpaceAvailable()) { - terrno = TSDB_CODE_NO_AVAIL_DISK; - qError("Init stream agg supporter failed since %s", terrstr(terrno)); - return terrno; - } - - int32_t code = createDiskbasedBuf(&pAggSup->pResultBuf, defaultPgsz, defaultBufsz, pKey, tsTempDir); - if (code != TSDB_CODE_SUCCESS) { - qError("Create agg result buf failed since %s", tstrerror(code)); + code = TSDB_CODE_NO_AVAIL_DISK; + qError("Init stream agg supporter failed since %s, %s", terrstr(code), pKey); return code; } - return TSDB_CODE_SUCCESS; + code = createDiskbasedBuf(&pAggSup->pResultBuf, defaultPgsz, defaultBufsz, pKey, tsTempDir); + if (code != TSDB_CODE_SUCCESS) { + qError("Create agg result buf failed since %s, %s", tstrerror(code), pKey); + return code; + } + + return code; } void cleanupAggSup(SAggSupporter* pAggSup) { diff --git a/source/libs/executor/src/timewindowoperator.c b/source/libs/executor/src/timewindowoperator.c index 9ea6b4c42f..c28bc7e9e8 100644 --- a/source/libs/executor/src/timewindowoperator.c +++ b/source/libs/executor/src/timewindowoperator.c @@ -1828,12 +1828,6 @@ static bool timeWindowinterpNeeded(SqlFunctionCtx* pCtx, int32_t numOfCols, SInt return needed; } -void increaseTs(SqlFunctionCtx* pCtx) { - if (pCtx[0].pExpr->pExpr->_function.pFunctNode->funcType == FUNCTION_TYPE_WSTART) { -// pCtx[0].increase = true; - } -} - void initIntervalDownStream(SOperatorInfo* downstream, uint16_t type, SAggSupporter* pSup) { if (downstream->operatorType != QUERY_NODE_PHYSICAL_PLAN_STREAM_SCAN) { // Todo(liuyao) support partition by column @@ -1895,7 +1889,6 @@ SOperatorInfo* createIntervalOperatorInfo(SOperatorInfo* downstream, SExprInfo* if (isStream) { ASSERT(numOfCols > 0); - increaseTs(pSup->pCtx); initStreamFunciton(pSup->pCtx, pSup->numOfExprs); } @@ -3050,6 +3043,7 @@ static void clearStreamIntervalOperator(SStreamFinalIntervalOperatorInfo* pInfo) tSimpleHashClear(pInfo->aggSup.pResultRowHashTable); clearDiskbasedBuf(pInfo->aggSup.pResultBuf); initResultRowInfo(&pInfo->binfo.resultRowInfo); + pInfo->aggSup.currentPageId = -1; } static void clearSpecialDataBlock(SSDataBlock* pBlock) { @@ -3420,7 +3414,6 @@ SOperatorInfo* createStreamFinalIntervalOperatorInfo(SOperatorInfo* downstream, initBasicInfo(&pInfo->binfo, pResBlock); ASSERT(numOfCols > 0); - increaseTs(pOperator->exprSupp.pCtx); initExecTimeWindowInfo(&pInfo->twAggSup.timeWindowData, &pTaskInfo->window); initResultRowInfo(&pInfo->binfo.resultRowInfo); @@ -3451,6 +3444,7 @@ SOperatorInfo* createStreamFinalIntervalOperatorInfo(SOperatorInfo* downstream, // semi interval operator does not catch result pInfo->isFinal = false; pOperator->name = "StreamSemiIntervalOperator"; + ASSERT(pInfo->aggSup.currentPageId == -1); } if (!IS_FINAL_OP(pInfo) || numOfChild == 0) { @@ -3563,7 +3557,6 @@ int32_t initBasicInfoEx(SOptrBasicInfo* pBasicInfo, SExprSupp* pSup, SExprInfo* } ASSERT(numOfCols > 0); - increaseTs(pSup->pCtx); return TSDB_CODE_SUCCESS; } From 825858b52ed8cccef2514f5961aa5b41b9de6429 Mon Sep 17 00:00:00 2001 From: Haojun Liao Date: Tue, 30 Aug 2022 17:04:09 +0800 Subject: [PATCH 72/76] fix(query): reset the default page id. --- source/libs/executor/src/timewindowoperator.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/libs/executor/src/timewindowoperator.c b/source/libs/executor/src/timewindowoperator.c index c28bc7e9e8..152bd5939d 100644 --- a/source/libs/executor/src/timewindowoperator.c +++ b/source/libs/executor/src/timewindowoperator.c @@ -4330,6 +4330,7 @@ static void clearStreamSessionOperator(SStreamSessionAggOperatorInfo* pInfo) { } } clearDiskbasedBuf(pInfo->streamAggSup.pResultBuf); + pInfo->streamAggSup.currentPageId = -1; } static void removeSessionResults(SHashObj* pHashMap, SArray* pWins) { From 753b7253005ad1e9f05faef9448be8c7ef0d00ad Mon Sep 17 00:00:00 2001 From: Cary Xu Date: Tue, 30 Aug 2022 17:29:02 +0800 Subject: [PATCH 73/76] other: simplify qtaskinfo file naming --- source/dnode/vnode/src/sma/smaCommit.c | 2 +- source/dnode/vnode/src/sma/smaRollup.c | 10 ++-------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/source/dnode/vnode/src/sma/smaCommit.c b/source/dnode/vnode/src/sma/smaCommit.c index 4d1dcd6909..3cf50a035a 100644 --- a/source/dnode/vnode/src/sma/smaCommit.c +++ b/source/dnode/vnode/src/sma/smaCommit.c @@ -172,7 +172,7 @@ static int32_t tdCleanupQTaskInfoFiles(SSma *pSma, SRSmaStat *pRSmaStat) { TdDirPtr pDir = NULL; TdDirEntryPtr pDirEntry = NULL; char dir[TSDB_FILENAME_LEN]; - const char *pattern = "v[0-9]+qtaskinfo\\.ver([0-9]+)?$"; + const char *pattern = "v[0-9]+qinf\\.v([0-9]+)?$"; regex_t regex; int code = 0; diff --git a/source/dnode/vnode/src/sma/smaRollup.c b/source/dnode/vnode/src/sma/smaRollup.c index 89cdd58c4e..af41c53956 100644 --- a/source/dnode/vnode/src/sma/smaRollup.c +++ b/source/dnode/vnode/src/sma/smaRollup.c @@ -28,11 +28,10 @@ SSmaMgmt smaMgmt = { .rsetId = -1, }; -#define TD_QTASKINFO_FNAME_PREFIX "qtaskinfo.ver" -#define TD_RSMAINFO_DEL_FILE "rsmainfo.del" +#define TD_QTASKINFO_FNAME_PREFIX "qinf.v" + typedef struct SRSmaQTaskInfoItem SRSmaQTaskInfoItem; typedef struct SRSmaQTaskInfoIter SRSmaQTaskInfoIter; -typedef struct SRSmaExecQItem SRSmaExecQItem; static int32_t tdUidStorePut(STbUidStore *pStore, tb_uid_t suid, tb_uid_t *uid); static int32_t tdUpdateTbUidListImpl(SSma *pSma, tb_uid_t *suid, SArray *tbUids); @@ -83,11 +82,6 @@ struct SRSmaQTaskInfoIter { int32_t nBufPos; }; -struct SRSmaExecQItem { - void *pRSmaInfo; - void *qall; -}; - void tdRSmaQTaskInfoGetFileName(int32_t vgId, int64_t version, char *outputName) { tdGetVndFileName(vgId, NULL, VNODE_RSMA_DIR, TD_QTASKINFO_FNAME_PREFIX, version, outputName); } From 651d7d16aee31c0fa5966e28e720f305ee23ec8d Mon Sep 17 00:00:00 2001 From: Shuduo Sang Date: Tue, 30 Aug 2022 17:41:54 +0800 Subject: [PATCH 74/76] feat: update taostools 9cb965f for3.0 (#16511) * feat: update taos-tools for 3.0 [TD-14141] * feat: update taos-tools for 3.0 * feat: update taos-tools for 3.0 * feat: update taos-tools for 3.0 * feat: update taos-tools for 3.0 * feat: update taos-tools for 3.0 * feat: update taos-tools for 3.0 * feat: update taos-tools for 3.0 * feat: update taos-tools for 3.0 * feat: update taos-tools for 3.0 * feat: update taos-tools 8e3b3ee * fix: remove submodules * feat: update taos-tools c529299 * feat: update taos-tools 9dc2fec for 3.0 * fix: optim upx * feat: update taos-tools f4e456a for 3.0 * feat: update taos-tools 2a2def1 for 3.0 * feat: update taos-tools c9cc20f for 3.0 * feat: update taostoosl 8a5e336 for 3.0 * feat: update taostools 3c7dafe for 3.0 * feat: update taos-tools 2d68404 for 3.0 * feat: update taos-tools 57bdfbf for 3.0 * fix: jenkinsfile2 to upgrade pip * feat: update taostoosl 11d23e5 for 3.0 * feat: update taostools 43924b8 for 3.0 * feat: update taostools 53a0103 for 3.0 * feat: update taostoosl d237772 for 3.0 * feat: update taos-tools 6bde102 for 3.0 * feat: upate taos-tools 2af2222 for 3.0 * feat: update taos-tools 833b721 for 3.0 * feat: update taostools e8bfca6 for 3.0 * feat: update taos-tools aa45ad4 for 3.0 * feat: update taos tools 9cb965f for 3.0 --- cmake/taostools_CMakeLists.txt.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/taostools_CMakeLists.txt.in b/cmake/taostools_CMakeLists.txt.in index c0de75c6dd..68caf9a9ac 100644 --- a/cmake/taostools_CMakeLists.txt.in +++ b/cmake/taostools_CMakeLists.txt.in @@ -2,7 +2,7 @@ # taos-tools ExternalProject_Add(taos-tools GIT_REPOSITORY https://github.com/taosdata/taos-tools.git - GIT_TAG aa45ad4 + GIT_TAG 9cb965f SOURCE_DIR "${TD_SOURCE_DIR}/tools/taos-tools" BINARY_DIR "" #BUILD_IN_SOURCE TRUE From f34287faaa533362daeb19c74509cc0d7612033b Mon Sep 17 00:00:00 2001 From: tomchon Date: Tue, 30 Aug 2022 19:13:04 +0800 Subject: [PATCH 75/76] test: modify checkpackages scritps --- packaging/testpackage.sh | 64 +++++++++++++++++++++++++++++++--------- 1 file changed, 50 insertions(+), 14 deletions(-) diff --git a/packaging/testpackage.sh b/packaging/testpackage.sh index 054c24eb5d..4b6264db2b 100755 --- a/packaging/testpackage.sh +++ b/packaging/testpackage.sh @@ -7,6 +7,7 @@ originPackageName=$3 originversion=$4 testFile=$5 subFile="taos.tar.gz" +password=$6 if [ ${testFile} = "server" ];then tdPath="TDengine-server-${version}" @@ -56,6 +57,7 @@ fi cmdInstall tree cmdInstall wget +cmdInstall sshpass echo "new workroom path" installPath="/usr/local/src/packageTest" @@ -74,24 +76,49 @@ else echo "${oriInstallPath} already exists" fi -echo "decompress installPackage" + + + +echo "download installPackage" +# cd ${installPath} +# wget https://www.taosdata.com/assets-download/3.0/${packgeName} +# cd ${oriInstallPath} +# wget https://www.taosdata.com/assets-download/3.0/${originPackageName} cd ${installPath} -wget https://www.taosdata.com/assets-download/3.0/${packgeName} -cd ${oriInstallPath} -wget https://www.taosdata.com/assets-download/3.0/${originPackageName} - +if [ ! -f {packgeName} ];then + sshpass -p ${password} scp 192.168.1.131:/nas/TDengine3/v${version}/community/${packgeName} . +fi +if [ ! -f debAuto.sh ];then + echo '#!/usr/bin/expect ' > debAuto.sh + echo 'set timeout 3 ' >> debAuto.sh + echo 'pset packgeName [lindex $argv 0]' >> debAuto.sh + echo 'spawn dpkg -i ${packgeName}' >> debAuto.sh + echo 'expect "*one:"' >> debAuto.sh + echo 'send "\r"' >> debAuto.sh + echo 'expect "*skip:"' >> debAuto.sh + echo 'send "\r" ' >> debAuto.sh +fi if [[ ${packgeName} =~ "deb" ]];then cd ${installPath} - echo "dpkg ${packgeName}" && dpkg -i ${packgeName} + dpkg -r taostools + dpkg -r tdengine + if [[ ${packgeName} =~ "TDengine" ]];then + echo "./debAuto.sh ${packgeName}" && chmod 755 debAuto.sh && ./debAuto.sh ${packgeName} + else + echo "dpkg -i ${packgeName}" && dpkg -i ${packgeName} + elif [[ ${packgeName} =~ "rpm" ]];then cd ${installPath} - echo "rpm ${packgeName}" && rpm -ivh ${packgeName} + echo "rpm ${packgeName}" && rpm -ivh ${packgeName} --quiet elif [[ ${packgeName} =~ "tar" ]];then - echo "tar ${packgeName}" && tar -xvf ${packgeName} - cd ${oriInstallPath} + cd ${oriInstallPath} + if [ ! -f {originPackageName} ];then + sshpass -p ${password} scp 192.168.1.131:/nas/TDengine3/v${originversion}/community${originPackageName} . + fi echo "tar -xvf ${originPackageName}" && tar -xvf ${originPackageName} + cd ${installPath} echo "tar -xvf ${packgeName}" && tar -xvf ${packgeName} @@ -105,10 +132,10 @@ elif [[ ${packgeName} =~ "tar" ]];then cd ${installPath} - tree ${oriInstallPath}/${originTdpPath} > ${originPackageName}_checkfile - tree ${installPath}/${tdPath} > ${packgeName}_checkfile + tree ${oriInstallPath}/${originTdpPath} > ${oriInstallPath}/${originPackageName}_checkfile + tree ${installPath}/${tdPath} > ${installPath}/${packgeName}_checkfile - diff ${packgeName}_checkfile ${originPackageName}_checkfile > ${installPath}/diffFile.log + diff ${installPath}/${packgeName}_checkfile ${oriInstallPath}/${originPackageName}_checkfile > ${installPath}/diffFile.log diffNumbers=`cat ${installPath}/diffFile.log |wc -l ` if [ ${diffNumbers} != 0 ];then echo "The number and names of files have changed from the previous installation package" @@ -122,11 +149,20 @@ elif [[ ${packgeName} =~ "tar" ]];then else bash ${installCmd} fi - if [[ ${packgeName} =~ "Lite" ]];then + if [[ ${packgeName} =~ "Lite" ]] && [[ ${packgeName} =~ "tar" ]] ;then cd ${installPath} - wget https://www.taosdata.com/assets-download/3.0/taosTools-2.1.2-Linux-x64.tar.gz + sshpass -p ${password} scp 192.168.1.131:/nas/TDengine3/v${version}/community/taosTools-2.1.2-Linux-x64.tar.gz . + # wget https://www.taosdata.com/assets-download/3.0/taosTools-2.1.2-Linux-x64.tar.gz tar xvf taosTools-2.1.2-Linux-x64.tar.gz cd taosTools-2.1.2 && bash install-taostools.sh + elif [[ ${packgeName} =~ "Lite" ]] && [[ ${packgeName} =~ "deb" ]] ;then + cd ${installPath} + sshpass -p ${password} scp 192.168.1.131:/nas/TDengine3/v${version}/community/taosTools-2.1.2-Linux-x64.deb . + dpkg -i taosTools-2.1.2-Linux-x64.deb + elif [[ ${packgeName} =~ "Lite" ]] && [[ ${packgeName} =~ "rpm" ]] ;then + cd ${installPath} + sshpass -p ${password} scp 192.168.1.131:/nas/TDengine3/v${version}/community/taosTools-2.1.2-Linux-x64.rpm . + rpm -ivh taosTools-2.1.2-Linux-x64.rpm --quiet fi fi From 6478a2d600a7beb0eead4330ef6bc39e58a121b7 Mon Sep 17 00:00:00 2001 From: tomchon Date: Tue, 30 Aug 2022 19:13:26 +0800 Subject: [PATCH 76/76] test: modify checkpackages scritps --- packaging/MPtestJenkinsfile | 1 - 1 file changed, 1 deletion(-) diff --git a/packaging/MPtestJenkinsfile b/packaging/MPtestJenkinsfile index a003bf354c..45c8d8abf2 100644 --- a/packaging/MPtestJenkinsfile +++ b/packaging/MPtestJenkinsfile @@ -182,7 +182,6 @@ pipeline { cd ${TDENGINE_ROOT_DIR}/packaging bash testpackage.sh ${TD_SERVER_TAR} ${version} ${BASE_TD_SERVER_TAR} ${baseVersion} server python3 checkPackageRuning.py - rmtaos ''' sh ''' cd ${TDENGINE_ROOT_DIR}/packaging