diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml index 654f0ae4..1605256b 100644 --- a/.github/workflows/push.yaml +++ b/.github/workflows/push.yaml @@ -32,7 +32,14 @@ jobs: path: 'TDengine' ref: 'main' submodules: 'recursive' - + + - name: Install system dependencies + run: | + sudo apt update -y + sudo apt install -y build-essential cmake \ + libgeos-dev libjansson-dev libsnappy-dev liblzma-dev libz-dev \ + zlib1g pkg-config libssl-dev gawk + - name: install TDengine run: | cd TDengine @@ -75,4 +82,4 @@ jobs: files: coverage/lcov.info verbose: true env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} \ No newline at end of file + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/README-CN.md b/README-CN.md new file mode 100644 index 00000000..2cbdcf76 --- /dev/null +++ b/README-CN.md @@ -0,0 +1,118 @@ + +# TDengine Node.js Connector + + +| Github Action Tests | CodeCov | +|--------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------| +| ![actions](https://github.com/taosdata/taos-connector-node/actions/workflows/push.yaml/badge.svg) | [![codecov](https://codecov.io/gh/taosdata/taos-connector-node/graph/badge.svg?token=5379a80b-063f-48c2-ab56-09564e7ca777)](https://codecov.io/gh/taosdata/taos-connector-node) | + +[English](README.md) | 简体中文 + + +## 目录 + + +- [1. 简介](#1-简介) + - [1.1 Node.js 版本兼容性](#11-nodejs-版本兼容性) + - [1.2 支持的平台](#12-支持的平台) +- [2. 获取驱动](#2-获取驱动) +- [3. 文档](#3-文档) +- [4. 前置条件](#4-前置条件) +- [5. 构建](#5-构建) +- [6. 测试](#6-测试) + - [6.1 运行测试](#61-运行测试) + - [6.2 添加用例](#62-添加用例) + - [6.3 性能测试](#63-性能测试) +- [7. 提交 Issue](#7-提交-issue) +- [8. 提交 PR](#8-提交-pr) +- [9. 引用](#9-引用) +- [10. 许可证](#10-许可证) + + +## 1. 简介 + +@tdengine/websocket 是 TDengine 官方专为 Node.js 开发人员精心设计的一款高效连接器,它借助 taosAdapter 组件提供的 WebSocket API 与 TDengine 建立连接,摆脱了对 TDengine 客户端驱动的依赖 ,为开发者开辟了一条便捷的开发路径。凭借这一强大工具,开发人员能够轻松构建面向 TDengine 集群的应用程序。不管是执行复杂的 SQL 写入与查询任务,还是实现灵活的无模式写入操作,亦或是达成对实时性要求极高的订阅功能,这款连接器都能轻松胜任、完美实现,全方位满足多样化的数据交互需求。 + +### 1.1 Node.js 版本兼容性 + +支持 Node.js 14 及以上版本。 + +### 1.2 支持的平台 + +支持所有能运行 Node.js 的平台。 + +## 2. 获取驱动 + +使用 npm 安装 Node.js 连接器 + +```shell +npm install @tdengine/websocket +``` + +## 3. 文档 + +- 开发示例请见 [开发指南](https://docs.taosdata.com/develop/)。 +- 版本历史、TDengine 对应版本以及 API 说明请见 [参考手册](https://docs.taosdata.com/reference/connector/node/)。 + +## 4. 前置条件 + +- 安装 Node.js 开发环境, 使用14以上版本,[下载 Node.js](https://nodejs.org/en/download/)。 +- 使用 npm 安装 Node.js 连接器。 +- 使用 npm 安装 TypeScript 5.3.3 以上版本。 +- 本地已经部署 TDengine,具体步骤请参考 部署服务端,且已经启动 `taosd` 与 `taosAdapter`。 + +## 5. 构建 + +在项目 `nodejs` 目录下执行 `tsc` 构建项目。 + +## 6. 测试 + +### 6.1 运行测试 + +在项目的 `nodejs` 目录下,执行 `npm run test` 命令,即可运行 `test/bulkPulling` 目录中的所有测试用例。这些测试用例将连接本地的 `TDengine` 服务器与 `taosAdapter` 进行相关测试。在终端内将实时输出各测试用例的执行结果。待整个测试流程结束,如果所有用例通过,没有 Failures 和 Errors, 并且还会给出相应的覆盖率数据。 + +```text +Test Suites: 8 passed, 8 total +Tests: 1 skipped, 44 passed, 45 total +Snapshots: 0 total +Time: 20.373 s +Ran all test suites. +``` + +### 6.2 添加用例 + +所有测试在项目的 `nodejs/test/bulkPulling` 目录下,可以新增加测试文件或者在已有的测试文件中添加用例。用例使用 jest 框架,一般在 `beforeAll` 方法中建立连接和创建数据库,在 `afterAll` 方法中删除数据库和释放连接。 + +### 6.3 性能测试 + +性能测试还在开发中。 + +## 7. 提交 Issue + +我们欢迎提交 [GitHub Issue](https://github.com/taosdata/taos-connector-node/issues/new?template=Blank+issue)。 提交时请说明下面信息: + +- 问题描述,是否必现。 +- Nodejs 版本。 +- @tdengine/websocket 版本。 +- 连接参数(不需要用户名密码)。 +- TDengine 服务端版本。 + +## 8. 提交 PR + +我们欢迎开发者一起开发本项目,提交 PR 时请参考下面步骤: + +1. Fork 本项目,请参考 ([how to fork a repo](https://docs.github.com/en/get-started/quickstart/fork-a-repo))。 +1. 从 main 分支创建一个新分支,请使用有意义的分支名称 (`git checkout -b my_branch`)。注意不要直接在 main 分支上修改。 +1. 修改代码,保证所有单元测试通过,并增加新的单元测试验证修改。 +1. 提交修改到远端分支 (`git push origin my_branch`)。 +1. 在 GitHub 上创建一个 Pull Request ([how to create a pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request))。 +1. 提交 PR 后,如果 CI 通过,可以在 [codecov](https://app.codecov.io/gh/taosdata/taos-connector-node/pulls) 页面找到自己 PR,看单测覆盖率。 + +## 9. 引用 + +- [TDengine 官网](https://www.taosdata.com/) +- [TDengine GitHub](https://github.com/taosdata/TDengine) + +## 10. 许可证 + +[MIT License](./LICENSE) diff --git a/README.md b/README.md index 07cac95e..bd8f7a50 100644 --- a/README.md +++ b/README.md @@ -1,552 +1,120 @@ -# TDengine Connectors for Node.js + +# TDengine Node.js Connector + -This repository includes two Node connector for TDengine. One is `@tdengine/client`, a Node.js connector using TDengine's native connection. Another is `@tdengine/rest`, a TypeScript connector using TDengine's rest connection. +| Github Action Tests | CodeCov | +|--------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------| +| ![actions](https://github.com/taosdata/taos-connector-node/actions/workflows/push.yaml/badge.svg) | [![codecov](https://codecov.io/gh/taosdata/taos-connector-node/graph/badge.svg?token=5379a80b-063f-48c2-ab56-09564e7ca777)](https://codecov.io/gh/taosdata/taos-connector-node) | -This readme file introduce basic installation and work with our connectors. +English | [简体中文](README-CN.md) -## `@tdengine/client` + +## Table of Contents + -This is the Node.js library that lets you connect to [TDengine](https://www.github.com/taosdata/tdengine) 3.0 version. It is built so that you can use as much of it as you want or as little of it as you want through providing an extensive API. If you want the raw data in the form of an array of arrays for the row data retrieved from a table, you can do that. If you want to wrap that data with objects that allow you easily manipulate and display data such as using a prettifier function, you can do that! +- [1. Introduction](#1-introduction) + - [1.1 Node.js Version Compatibility](#11-nodejs-version-compatibility) + - [1.2 Supported Platforms](#12-supported-platforms) +- [2. Get the Driver](#2-get-the-driver) +- [3. Documentation](#3-documentation) +- [4. Prerequisites](#4-prerequisites) +- [5. Build](#5-build) +- [6. Testing](#6-testing) + - [6.1 Test Execution](#61-test-execution) + - [6.2 Test Case Addition](#62-test-case-addition) + - [6.3 Performance Testing](#63-performance-testing) +- [7. Submitting Issues](#7-submitting-issues) +- [8. Submitting PRs](#8-submitting-prs) +- [9. References](#9-references) +- [10. License](#10-license) -### Installation +## 1. Introduction -To get started, just type in the following to install the connector through [npm](https://www.npmjs.com/) +@tdengine/websocket is an efficient connector specially designed by TDengine for Node.js developers. It uses the WebSocket API provided by the taosAdapter component to establish a connection with TDengine, eliminating the dependence on TDengine client drivers and opening up a convenient development path for developers. With this powerful tool, developers can easily build applications for TDengine clusters. Whether it is performing complex SQL write and query tasks, implementing flexible schemaless write operations, or achieving highly real-time subscription functionality, this connector can easily and perfectly meet diverse data interaction needs in all aspects. -```cmd -npm install @tdengine/client -``` - -To interact with TDengine, we make use of the [node-gyp](https://github.com/nodejs/node-gyp) library. To install, you will need to install the following depending on platform (the following instructions are quoted from node-gyp) - -#### On Linux - -- `python` -- `make` -- A proper C/C++ compiler toolchain, like [GCC](https://gcc.gnu.org) -- `node` (either `v10.x` or `v12.x`, other version has some dependency compatibility problems) - - - -#### On Windows - -##### Option 1 - -Install all the required tools and configurations using Microsoft's [windows-build-tools](https://github.com/felixrieseberg/windows-build-tools) using `npm install --global --production windows-build-tools` from an elevated PowerShell or CMD.exe (run as Administrator). - -##### Option 2 - -Install tools and configuration manually: - -- Install Visual C++ Build Environment: [Visual Studio Build Tools](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools) (using "Visual C++ build tools" workload) or [Visual Studio 2017 Community](https://visualstudio.microsoft.com/pl/thank-you-downloading-visual-studio/?sku=Community) (using the "Desktop development with C++" workload) -- Install [Python 2.7](https://www.python.org/downloads/) (`v3.x.x` is not supported), and run `npm config set python python2.7` (or see below for further instructions on specifying the proper Python version and path.) -- Launch cmd, `npm config set msvs_version 2017` - -If the above steps didn't work for you, please visit [Microsoft's Node.js Guidelines for Windows](https://github.com/Microsoft/nodejs-guidelines/blob/master/windows-environment.md#compiling-native-addon-modules) for additional tips. - -To target native ARM64 Node.js on Windows 10 on ARM, add the components "Visual C++ compilers and libraries for ARM64" and "Visual C++ ATL for ARM64". - -### Usage - -The following is a short summary of the basic usage of the connector, the full api and documentation can be found [here](https://www.taosdata.com/docs/cn/v2.0/connector#nodejs) - -#### Connection - -To use the connector, first require the library ```@tdengine/client```. Running the function ```taos.connect``` with the connection options passed in as an object will return a TDengine connection object. The required connection option is ```host```, other options if not set, will be the default values as shown below. - -A cursor also needs to be initialized in order to interact with TDengine from Node.js. +### 1.1 Node.js Version Compatibility -```javascript -const taos = require('@tdengine/client'); -var conn = taos.connect({host:"127.0.0.1", user:"root", password:"taosdata", config:"/etc/taos",port:0}) -var cursor = conn.cursor(); // Initializing a new cursor -``` - -Close a connection - -```javascript -conn.close(); -``` - -#### Queries - -We can now start executing simple queries through the ```cursor.query``` function, which returns a TaosQuery object. - -```javascript -var query = cursor.query('show databases;') -``` - -We can get the results of the queries through the ```query.execute()``` function, which returns a promise that resolves with a TaosResult object, which contains the raw data and additional functionalities such as pretty printing the results. +Supports Node.js 14 and above. -```javascript -var promise = query.execute(); -promise.then(function(result) { - result.pretty(); //logs the results to the console as if you were in the taos shell -}); -``` +### 1.2 Supported Platforms -You can also query by binding parameters to a query by filling in the question marks in a string as so. The query will automatically parse what was binded and convert it to the proper format for use with TDengine +Support all platforms that can run Node.js. -```javascript -var query = cursor.query('select * from meterinfo.meters where ts <= ? and areaid = ?;').bind(new Date(), 5); -query.execute().then(function(result) { - result.pretty(); -}) -``` +## 2. Get the Driver -The TaosQuery object can also be immediately executed upon creation by passing true as the second argument, returning a promise instead of a TaosQuery. +Install the Node.js connector using npm. -```javascript -var promise = cursor.query('select * from meterinfo.meters where v1 = 30;', true) -promise.then(function(result) { - result.pretty(); -}) +```shell +npm install @tdengine/websocket ``` -If you want to execute queries without objects being wrapped around the data, use `cursor.execute()` directly and `cursor.fetchall()` to retrieve data if there is any. +## 3. Documentation -```javascript -cursor.execute('select count(*), avg(v1), min(v2) from meterinfo.meters where ts >= \"2019-07-20 00:00:00.000\";'); -var data = cursor.fetchall(); -console.log(cursor.fields); // Latest query's Field metadata is stored in cursor.fields -console.log(cursor.data); // Latest query's result data is stored in cursor.data, also returned by fetchall. -``` - -#### Async functionality +- For development examples, see [Developer Guide](https://docs.tdengine.com/developer-guide/), which includes examples of data writing, querying, schemaless writing, parameter binding, and data subscription. +- For other reference information, see [Reference Manual](https://docs.tdengine.com/tdengine-reference/client-libraries/node/), which includes version history, data types, example programs, API descriptions, and FAQs. -Async queries can be performed using the same functions such as `cursor.execute`, `TaosQuery.query`, but now with `_a` appended to them. -Say you want to execute an two async query on two separate tables, using `cursor.query`, you can do that and get a TaosQuery object, which upon executing with the `execute_a` function, returns a promise that resolves with a TaosResult object. +## 4. Prerequisites -```javascript -var promise1 = cursor.query('select count(*), avg(v1), avg(v2) from meter1;').execute_a() -var promise2 = cursor.query('select count(*), avg(v1), avg(v2) from meter2;').execute_a(); -promise1.then(function(result) { - result.pretty(); -}) -promise2.then(function(result) { - result.pretty(); -}) -``` +- Install the Node.js development environment, using version 14 or above. Download link: [https://nodejs.org/en/download/](https://nodejs.org/en/download/) +- Install the Node.js connector using npm. +- Install TypeScript 5.3.3 and above using npm. +- TDengine has been deployed locally. For specific steps, please refer to Deploy TDengine, and `taosd` and `taosAdapter` have been started. -## `@tdengine/rest` +## 5. Build -This is a TDengine's RESTful connector in TypeScript. It's depend on [node-fetch v2](https://github.com/node-fetch/node-fetch/tree/2.x). Using `fetch(url,options)` to send sql statement and receive response. +Execute `tsc` to build the project in the 'nodejs' directory. -### Installation +## 6. Testing -```bash -npm i @tdengine/rest -``` +### 6.1 Test Execution -### Usage - -```TypeScript -import { options, connect } from '@tdengine/rest' -options.path='/rest/sql'; -// set host -options.host='localhost'; -// set other options like user/passwd - -let conn = connect(options); -let cursor = conn.cursor(); -(async()=>{ - let result = await cursor.query('show databases'); - - // Get Result object, return Result object. - console.log(result.getResult()); - // Get status, return 'succ'|'error'. - console.log(result.getStatus()); - // Get head,return response head (Array|undefined,when execute failed this is undefined). - console.log(result.getHead()); - // Get Meta data, return Meta[]|undefined(when execute failed this is undefined). - console.log(result.getMeta()); - // Get data,return Array>|undefined(when execute failed this is undefined). - console.log(result.getData()); - // Get affect rows,return number|undefined(when execute failed this is undefined). - console.log(result.getAffectRows()); - // Get command,return SQL send to server(need to `query(sql,false)`,set 'pure=false',default true). - console.log(result.getCommand()); - // Get error code ,return number|undefined(when execute failed this is undefined). - console.log(result.getErrCode()); - // Get error string,return string|undefined(when execute failed this is undefined). - console.log(result.getErrStr()); -})() +Execute `npm run test` in the project directory to run the tests. The test cases will connect to the local TDengine server and taosAdapter for testing. +After running the tests, the result similar to the following will be printed eventually. If all test cases pass, without any failures or errors. +```text +Test Suites: 8 passed, 8 total +Tests: 1 skipped, 44 passed, 45 total +Snapshots: 0 total +Time: 20.373 s +Ran all test suites. ``` -## `@tdengine/websocket` -This is a TDengine's WEBSOCKET connector in TypeScript. -### Installation +### 6.2 Test Case Addition -```bash -npm i @tdengine/websocket -``` -### Usage +All tests are located in the `nodejs/test/bulkPulling` directory of the project. You can add new test files or add test cases in existing test files. +The test cases use the jest framework. Generally, a connection is established and a database is created in the `beforeAll` method, and the database is droped and the connection is released in the `afterAll` method. -Create a connection using DSN +### 6.3 Performance Testing -### DSN +Performance testing is in progress. -User can connect to the TDengine by passing DSN to WebSocket client. The description about the DSN like before. +## 7. Submitting Issues -```text -[+]://[[:@]:][/][?=[&=]] -|------------|---|-----------|-----------|------|------|------------|-----------------------| -| protocol | | username | password | host | port | database | params | -``` - -- **protocol**: Display using websocket protocol to establish connection. eg. `ws://localhost:6041` -- **username/password**: Database's username and password. -- **host/port**: Declare host and port. eg. `localhost:6041` -- **database**: Optional, use to specify database name. -- **params**: Other parameters. Like cloud Token. - -A complete DSN string example: -```TypeScript -import { WSConfig } from '../src/common/config'; -import { sqlConnect } from '../index' -let dsn = 'ws://root:taosdata@127.0.0.1:6041'; -(async () => { - let wsSql = null; - try { - let conf :WSConfig = new WSConfig(dsn) - wsSql = await sqlConnect(conf) - } catch (err:any) { - console.error(err); - } finally { - if (wsSql) { - wsSql.Close(); - } - } -})(); -``` +We welcome the submission of [GitHub Issue](https://github.com/taosdata/taos-connector-node/issues/new?template=Blank+issue). When submitting, please provide the following information: -Create a connection using config - -```TypeScript -import { WSConfig } from '../src/common/config'; -import { sqlConnect } from '../index' - -let dns = 'ws://127.0.0.1:6041/ws' -let conf :WSConfig = new WSConfig(dns) -conf.SetUser('root') -conf.SetPwd('taosdata') -(async () => { - let wsSql = null; - try { - wsSql = await sqlConnect(conf) - } catch (err:any) { - console.error(err); - - } finally { - if (wsSql) { - wsSql.Close(); - } - } -})(); -``` -Sql usage examples -```TypeScript -import { WSConfig } from '../src/common/config'; -import { sqlConnect } from '../index' - -let dns = 'ws://127.0.0.1:6041/ws' -let conf :WSConfig = new WSConfig(dns) -conf.SetUser('root') -conf.SetPwd('taosdata') -(async () => { - let wsSql = null; - let wsRows = null; - let reqId = 0; - try { - wsSql = await sqlConnect(conf) - - let version = await wsSql.Version(); - console.log(version); - - let taosResult = await wsSql.Exec('show databases', reqId++) - console.log(taosResult); - - taosResult = await wsSql.Exec('create database if not exists power KEEP 3650 DURATION 10 BUFFER 16 WAL_LEVEL 1;',reqId++); - console.log(taosResult); - - taosResult = await wsSql.Exec('use power',reqId++) - console.log(taosResult); - - taosResult = await wsSql.Exec('CREATE STABLE if not exists meters (ts timestamp, current float, voltage int, phase float) TAGS (location binary(64), groupId int);', reqId++); - console.log(taosResult); - - taosResult = await wsSql.Exec('describe meters', reqId++) - console.log(taosResult); - - taosResult = await wsSql.Exec('INSERT INTO d1001 USING meters TAGS ("California.SanFrancisco", 3) VALUES (NOW, 10.2, 219, 0.32)', reqId++) - console.log(taosResult); - - wsRows = await wsSql.Query('select * from meters', reqId++); - let meta = wsRows.GetMeta() - console.log("wsRow:meta:=>", meta); - - while (await wsRows.Next()) { - let result = await wsRows.GetData(); - console.log('queryRes.Scan().then=>', result); - } - await wsRows.Close() - - } catch (e) { - let err:any = e - console.error(err); - - } finally { - if (wsRows) { - await wsRows.Close(); - } - if (wsSql) { - wsSql.Close(); - } - } -})(); -``` +- Problem description, is it necessary to present it. +- Nodejs version. +- @tdengine/websocket version. +- Connection parameters (no username or password required). +- TDengine Server Version. -Writing data via parameter binding - -TDengine's node.js connection implementation has significantly improved its support for data writing (INSERT) scenarios via bind interface. Writing data in this way avoids the resource consumption of SQL syntax parsing, resulting in significant write performance improvements in many cases. - -usage examples - -```TypeScript -import { WSConfig } from '../src/common/config'; -import { sqlConnect } from '../index'; - -let db = 'power' -let stable = 'meters' -let tags = ['California.SanFrancisco', 3]; -let multi = [ - [1706786044994, 1706786044995, 1706786044996], - [10.2, 10.3, 10.4], - [292, 293, 294], - [0.32, 0.33, 0.34], -]; - -(async () => { - let stmt = null; - let connector = null; - try { - await Prepare(); - let dsn = 'ws://root:taosdata@127.0.0.1:6041'; - let wsConf = new WSConfig(dsn); - wsConf.SetDb(db) - connector = await sqlConnect(wsConf); - stmt = await connector.StmtInit() - await stmt.Prepare(`INSERT INTO ? USING ${db}.${stable} TAGS (?, ?) VALUES (?, ?, ?, ?)`); - await stmt.SetTableName('d1001'); - - let tagParams = stmt.NewStmtParam() - tagParams.SetVarcharColumn([tags[0]]) - tagParams.SetIntColumn([tags[1]]) - await stmt.SetBinaryTags(tagParams); - - let bindParams = stmt.NewStmtParam() - bindParams.SetTimestampColumn(multi[0]); - bindParams.SetFloatColumn(multi[1]) - bindParams.SetIntColumn(multi[2]) - bindParams.SetFloatColumn(multi[3]) - await stmt.BinaryBind(bindParams); - await stmt.Batch(); - await stmt.Exec(); - } catch (e) { - console.error(e); - }finally { - if (stmt) { - stmt.Close(); - } - if (connector) { - connector.Close(); - } - } -})(); -``` -Schemaless Writing - -TDengine has added the ability to schemaless writing. It is compatible with InfluxDB's Line Protocol, OpenTSDB's telnet line protocol, and OpenTSDB's JSON format protocol. See schemaless writing for details. - -usage examples -```TypeScript -import { WSConfig } from '../src/common/config'; -import { Precision, SchemalessProto } from '../src/sql/wsProto'; -import { sqlConnect } from '../index'; - -let db = 'power' -let dsn = 'ws://root:taosdata@127.0.0.1:6041'; -let influxdbData = "st,t1=3i64,t2=4f64,t3=\"t3\" c1=3i64,c3=L\"passit\",c2=false,c4=4f64 1626006833639000000"; -let telnetData = "stb0_0 1626006833 4 host=host0 interface=eth0"; -let jsonData = "{\"metric\": \"meter_current\",\"timestamp\": 1626846400,\"value\": 10.3, \"tags\": {\"groupid\": 2, \"location\": \"California.SanFrancisco\", \"id\": \"d1001\"}}"; - -async function Prepare() { - let conf :WSConfig = new WSConfig(dsn) - let wsSql = await sqlConnect(conf) - await wsSql.Exec(`create database if not exists ${db} KEEP 3650 DURATION 10 BUFFER 16 WAL_LEVEL 1;`) - wsSql.Close() -} - -(async () => { - let wsSchemaless = null - try { - await Prepare() - let conf = new WSConfig(dsn); - conf.SetDb(db) - wsSchemaless = await sqlConnect(conf) - await wsSchemaless.SchemalessInsert([influxdbData], SchemalessProto.InfluxDBLineProtocol, Precision.NANO_SECONDS, 0); - await wsSchemaless.SchemalessInsert([telnetData], SchemalessProto.OpenTSDBTelnetLineProtocol, Precision.SECONDS, 0); - await wsSchemaless.SchemalessInsert([jsonData], SchemalessProto.OpenTSDBJsonFormatProtocol, Precision.SECONDS, 0); - } catch (e) { - console.error(e); - }finally { - if (wsSchemaless) { - wsSchemaless.Close(); - } - } -})(); -``` +## 8. Submitting PRs -Subscriptions +We welcome developers to contribute to this project. When submitting PRs, please follow these steps: -The TDengine node.js Connector supports subscription functionality with the following application API. +1. Fork this project, refer to ([how to fork a repo](https://docs.github.com/en/get-started/quickstart/fork-a-repo)). +1. Create a new branch from the main branch with a meaningful branch name (`git checkout -b my_branch`). Do not modify the main branch directly. +1. Modify the code, ensure all unit tests pass, and add new unit tests to verify the changes. +1. Push the changes to the remote branch (`git push origin my_branch`). +1. Create a Pull Request on GitHub ([how to create a pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request)). +1. After submitting the PR, if CI passes, you can find your PR on the [codecov](https://app.codecov.io/gh/taosdata/taos-connector-node/pulls) page to check the test coverage. -create subscriptions +## 9. References -```TypeScript - let createTopic = `create topic if not exists pwer_meters_topic as select * from power.meters` - let dsn = 'ws://root:taosdata@127.0.0.1:6041'; - let conf :WSConfig = new WSConfig(dsn) - let ws = await sqlConnect(conf); - await ws.Exec(createTopic); - ws.Close() -``` +- [TDengine Official Website](https://www.tdengine.com/) +- [TDengine GitHub](https://github.com/taosdata/TDengine) -The two parameters of the subscribe() method have the following meanings. - -pwer_meters_topic: the subscribed topic (i.e., name). This parameter is the unique identifier of the subscription. - -sql: the query statement of the subscription, this statement can only be select statement, only the original data should be queried, and you can query only the data in the positive time order -The above example will use the SQL command select ts, speed from speed_table to create a subscription named topic_speed. If the subscription exists. - -Create Consumer and Subscribe topic -```TypeScript - let configMap = new Map([ - [TMQConstants.GROUP_ID, "gId"], - [TMQConstants.CONNECT_USER, "root"], - [TMQConstants.CONNECT_PASS, "taosdata"], - [TMQConstants.AUTO_OFFSET_RESET, "earliest"], - [TMQConstants.CLIENT_ID, 'test_tmq_client'], - [TMQConstants.WS_URL, 'ws://127.0.0.1:6041'], - [TMQConstants.ENABLE_AUTO_COMMIT, 'true'], - [TMQConstants.AUTO_COMMIT_INTERVAL_MS, '1000'] - ]); - consumer = await tmqConnect(configMap); - await consumer.Subscribe(topics); -``` - enable.auto.commit: whether to allow auto commit. - group.id: group id of consumer - client.id: client id, maximum length: 192 - auto.offset.reset:earliest: subscribe from the earliest data; latest: subscribe from the latest data - auto.commit.interval.ms:Interval for automatic commits, in milliseconds - -usage examples - -```TypeScript -import { WSConfig } from "../src/common/config"; -import { TMQConstants } from "../src/tmq/constant"; -import { sqlConnect, tmqConnect } from "../index"; - -const stable = 'meters'; -const db = 'power' -const topics:string[] = ['pwer_meters_topic'] -let configMap = new Map([ - [TMQConstants.GROUP_ID, "gId"], - [TMQConstants.CONNECT_USER, "root"], - [TMQConstants.CONNECT_PASS, "taosdata"], - [TMQConstants.AUTO_OFFSET_RESET, "earliest"], - [TMQConstants.CLIENT_ID, 'test_tmq_client'], - [TMQConstants.WS_URL, 'ws://127.0.0.1:6041'], - [TMQConstants.ENABLE_AUTO_COMMIT, 'true'], - [TMQConstants.AUTO_COMMIT_INTERVAL_MS, '1000'] -]); - -async function Prepare() { - let dsn = 'ws://root:taosdata@localhost:6041'; - let conf :WSConfig = new WSConfig(dsn) - const createDB = `create database if not exists ${db} KEEP 3650 DURATION 10 BUFFER 16 WAL_LEVEL 1;` - const createStable = `CREATE STABLE if not exists ${db}.${stable} (ts timestamp, current float, voltage int, phase float) TAGS (location binary(64), groupId int);` - let createTopic = `create topic if not exists ${topics[0]} as select * from ${db}.${stable}` - const useDB = `use ${db}` - - let ws = await sqlConnect(conf); - await ws.Exec(createDB); - await ws.Exec(useDB); - await ws.Exec(createStable); - await ws.Exec(createTopic); - for (let i = 0; i < 10; i++) { - await ws.Exec(`INSERT INTO d1001 USING ${stable} TAGS ("California.SanFrancisco", 3) VALUES (NOW, ${10+i}, ${200+i}, ${0.32 + i})`) - } - ws.Close() -} - -(async () => { - let consumer = null - try { - await Prepare() - consumer = await tmqConnect(configMap); - await consumer.Subscribe(topics); - for (let i = 0; i < 5; i++) { - let res = await consumer.Poll(500); - for (let [key, value] of res) { - console.log(key, value); - } - if (res.size == 0) { - break; - } - await consumer.Commit(); - } - - let assignment = await consumer.Assignment() - console.log(assignment) - await consumer.SeekToBeginning(assignment) - await consumer.Unsubscribe() - } catch (e:any) { - console.error(e); - } finally { - if (consumer) { - consumer.Close(); - } - } -})(); +## 10. License -``` +[MIT License](./LICENSE) diff --git a/nodejs/src/client/wsConnectorPool.ts b/nodejs/src/client/wsConnectorPool.ts index 29a57a7d..4d6650c3 100644 --- a/nodejs/src/client/wsConnectorPool.ts +++ b/nodejs/src/client/wsConnectorPool.ts @@ -23,6 +23,7 @@ export class WebSocketConnectionPool { async getConnection(url:URL, timeout: number | undefined | null): Promise { let connectAddr = url.origin.concat(url.pathname).concat(url.search) + logger.info("url:" + url) let connector:WebSocketConnector | undefined; const unlock = await mutex.acquire() try { @@ -36,7 +37,7 @@ export class WebSocketConnectionPool { } if (connector) { - logger.debug("get connection success:", this._connectionCount) + logger.debug("get connection success:" + this._connectionCount) return connector; } if (this._maxConnections != -1 && this._connectionCount > this._maxConnections) {