blob: ab6cd1daf4bd7ba863369ede07fe1824432d7b34 [file] [view]
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
Apache IoTDB Node.js client library providing Session, SessionPool, and TableSessionPool for time-series database operations. Uses Apache Thrift for RPC communication.
## Build & Development Commands
```bash
npm install # Install dependencies
npm run build # Full build (esbuild + tsc declarations + copy:thrift)
npm run lint # Run ESLint
npm run lint:fix # Fix lint issues
npm run format # Format with Prettier
```
## Testing Commands
```bash
npm test # All tests (runs sequentially)
npm run test:unit # Unit tests only
npm run test:e2e # E2E tests (requires IoTDB instance)
```
**E2E tests require IoTDB**:
```bash
# Start single node
docker-compose -f docker-compose-1c1d.yml up -d
# Or 1 ConfigNode + 3 DataNodes
docker-compose -f docker-compose-1c3d.yml up -d
# Or 3-node cluster
docker-compose -f docker-compose-3c3d.yml up -d
# Environment variables
export IOTDB_HOST=localhost IOTDB_PORT=6667 IOTDB_USER=root IOTDB_PASSWORD=root
```
## Architecture
Three-layer design: **Connection** **Session** **Pool**
```
┌─────────────────────────────────────────────────────┐
│ Pool Layer (SessionPool / TableSessionPool) │
│ - Round-robin load balancing across nodeUrls │
│ - Connection pooling (min/max size, idle cleanup) │
└─────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ Session Layer │
│ - executeQueryStatement() → SessionDataSet │
│ - executeNonQueryStatement() │
│ - insertTablet() (TreeTablet or TableTablet) │
└─────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ Connection Layer (Thrift) │
│ - TFramedTransport + TBinaryProtocol │
│ - TCP/SSL transport │
└─────────────────────────────────────────────────────┘
```
**Key files**:
- `src/client/Session.ts` - Single connection session (tree model)
- `src/client/TableSession.ts` - Single connection session (table model)
- `src/client/BaseSessionPool.ts` - Abstract pool with common logic
- `src/client/SessionPool.ts` - Tree model pool (sql_dialect='tree')
- `src/client/TableSessionPool.ts` - Table model pool (sql_dialect='table')
- `src/connection/Connection.ts` - Low-level Thrift connection
## Critical Patterns
### Constructor Overloading (Maintain Both Signatures)
```typescript
// New format (recommended):
new SessionPool({ nodeUrls: ["host1:6667", "host2:6667"], maxPoolSize: 10 });
// Old format (backward compatible):
new SessionPool(["host1", "host2"], 6667, { maxPoolSize: 10 });
```
### Data Types - Use Official TSFile Codes
| Code | Type | JavaScript Type |
| ---- | --------- | --------------- |
| 0 | BOOLEAN | boolean |
| 1 | INT32 | number |
| 2 | INT64 | number/string |
| 3 | FLOAT | number |
| 4 | DOUBLE | number |
| 5 | TEXT | string |
| 8 | TIMESTAMP | number/Date |
| 9 | DATE | number/Date |
| 10 | BLOB | Buffer |
| 11 | STRING | string |
### TreeTablet vs TableTablet
**TreeTablet** (timeseries model - Session/SessionPool):
```typescript
{ deviceId: "root.sg.device1", measurements: ["temp"], dataTypes: [3], timestamps: [...], values: [...] }
```
**TableTablet** (relational model - TableSession/TableSessionPool):
```typescript
{ tableName: "sensor_data", columnNames: ["device_id", "temp"], columnTypes: [11, 3],
columnCategories: [ColumnCategory.TAG, ColumnCategory.FIELD], timestamps: [...], values: [...] }
```
**CRITICAL**: Never include `ColumnCategory.TIME` in columnCategories - timestamps are handled separately.
### SessionDataSet (Lazy Loading)
```typescript
const dataSet = await session.executeQueryStatement("SELECT * FROM root.test");
while (await dataSet.hasNext()) {
// async - fetches batches
const row = dataSet.next(); // sync - returns cached row
}
await dataSet.close(); // REQUIRED - releases server resources
```
### Thrift Integration
- Generated code in `src/thrift/generated/` - **DO NOT modify directly**
- Use `require()` not `import` for Thrift files (CommonJS)
- Regenerate with: `npm run generate:thrift`
## E2E Test Patterns
```typescript
beforeAll(async () => {
session = new Session({
host: process.env.IOTDB_HOST || "localhost",
port: 6667,
});
await session.open();
}, 60000); // 60s timeout - IoTDB startup is slow
test("example", async () => {
if (!session.isOpen()) return; // Skip gracefully if no IoTDB
// Cleanup with error tolerance
try {
await session.executeNonQueryStatement("DROP DATABASE root.test");
} catch (e: any) {
if (!e.message?.includes("not exist")) throw e;
}
});
```
Tests run sequentially (`maxWorkers: 1`) to avoid database conflicts.
## Common Pitfalls
1. **Build order**: `copy:thrift` must run after compilation to copy JS files to dist
2. **Pool vs Session**: Session uses first node; Pool does round-robin across all nodes
3. **SessionDataSet**: Always call `close()` or resources leak on server
4. **Test isolation**: Tests share database names (`root.test`), run sequentially