create-dwy 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -2
- package/src/prompts/grouped-checkbox.js +223 -0
- package/src/sync.js +61 -39
- package/templates/claude-global/hooks/pre-git-commit-sensitive-check.sh +0 -0
- package/templates/claude-global/rules/{dwy-python-backend.md → Python/dwy-python-backend.md} +43 -3
- package/templates/claude-global/rules/{dwy-python-core.md → Python/dwy-python-core.md} +35 -2
- package/templates/claude-global/skills//345/237/272/347/241/200/345/272/223/dwy-eapi/SKILL.md +226 -0
- package/templates/claude-global/skills//346/225/260/346/215/256/345/272/223/dwy-dolphindb/SKILL.md +144 -0
- package/templates/claude-global/skills//346/225/260/346/215/256/345/272/223/dwy-dolphindb/references/create-database-table.md +219 -0
- package/templates/claude-global/skills//346/225/260/346/215/256/345/272/223/dwy-dolphindb/references/crud-operations.md +281 -0
- package/templates/claude-global/skills//346/225/260/346/215/256/345/272/223/dwy-dolphindb/references/data-import.md +271 -0
- package/templates/claude-global/skills//346/225/260/346/215/256/345/272/223/dwy-dolphindb/references/docker-deployment.md +63 -0
- package/templates/claude-global/skills//346/225/260/346/215/256/345/272/223/dwy-dolphindb/references/performance-tuning.md +223 -0
- package/templates/claude-global/skills//346/225/260/346/215/256/345/272/223/dwy-dolphindb/references/pitfalls-and-best-practices.md +352 -0
- package/templates/claude-global/skills//346/225/260/346/215/256/345/272/223/dwy-dolphindb/references/python-api.md +306 -0
- package/templates/claude-global/skills//346/225/260/346/215/256/345/272/223/dwy-dolphindb/references/setup-and-connection.md +143 -0
- package/templates/claude-global/skills//346/225/260/346/215/256/345/272/223/dwy-dolphindb/references/sources.md +46 -0
- package/templates/claude-global/skills//346/225/260/346/215/256/345/272/223/dwy-dolphindb/references/sql-batch-compute.md +206 -0
- package/templates/claude-global/skills//350/277/220/347/273/264/345/217/221/345/270/203/dwy-cibuildwheel/SKILL.md +354 -0
- package/templates/claude-global/skills//350/277/220/347/273/264/345/217/221/345/270/203/dwy-cibuildwheel/references/release-workflow-template.yml +99 -0
- package/templates/claude-global/skills/{dwy-deploy-audit → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-audit}/scripts/check_base.sh +0 -0
- package/templates/claude-global/skills/{dwy-deploy-audit → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-audit}/scripts/check_capacity.sh +0 -0
- package/templates/claude-global/skills/{dwy-deploy-audit → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-audit}/scripts/check_db.sh +0 -0
- package/templates/claude-global/skills/{dwy-deploy-audit → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-audit}/scripts/check_docker.sh +0 -0
- package/templates/claude-global/skills/{dwy-deploy-audit → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-audit}/scripts/check_env.sh +0 -0
- package/templates/claude-global/skills/{dwy-deploy-audit → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-audit}/scripts/check_https.sh +0 -0
- package/templates/claude-global/skills/{dwy-deploy-audit → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-audit}/scripts/check_logs.sh +0 -0
- package/templates/claude-global/skills/{dwy-deploy-audit → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-audit}/scripts/check_nginx.sh +0 -0
- package/templates/claude-global/skills/{dwy-deploy-audit → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-audit}/scripts/check_resilience.sh +0 -0
- package/templates/claude-global/skills/{dwy-deploy-audit → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-audit}/scripts/check_secrets.sh +0 -0
- package/templates/claude-global/skills/{dwy-deploy-audit → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-audit}/scripts/check_services.sh +0 -0
- package/templates/claude-global/skills/{dwy-deploy-audit → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-audit}/scripts/check_ssh.sh +0 -0
- package/templates/claude-global/skills/{dwy-deploy-audit → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-audit}/scripts/run_all.sh +0 -0
- package/templates/claude-global/skills/{dwy-docker → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-docker}/scripts/query_dockerhub.py +0 -0
- package/templates/claude-global/skills/{dwy-docker → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-docker}/scripts/scan_images.sh +0 -0
- package/templates/claude-global/skills/{dwy-mirror-source → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-mirror-source}/scripts/apply_mirrors.py +0 -0
- package/templates/claude-global/skills/{dwy-mirror-source → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-mirror-source}/scripts/check_mirrors.py +0 -0
- package/templates/claude-global/skills/dwy-dolphindb/SKILL.md +0 -98
- package/templates/claude-global/skills/dwy-dolphindb/evals/evals.json +0 -67
- package/templates/claude-global/skills/dwy-dolphindb/references/dolphindb-rules.md +0 -441
- package/templates/claude-global/skills/dwy-eapi/SKILL.md +0 -495
- package/templates/claude-global/skills/dwy-eapi/references/health.md +0 -47
- package/templates/claude-global/skills/dwy-eapi/references/logger.md +0 -60
- /package/templates/claude-global/rules/{dwy-android-core.md → Android/dwy-android-core.md} +0 -0
- /package/templates/claude-global/rules/{dwy-flutter-core.md → Flutter/dwy-flutter-core.md} +0 -0
- /package/templates/claude-global/rules/{dwy-flutter-network.md → Flutter/dwy-flutter-network.md} +0 -0
- /package/templates/claude-global/rules/{dwy-flutter-router.md → Flutter/dwy-flutter-router.md} +0 -0
- /package/templates/claude-global/rules/{dwy-flutter-testing.md → Flutter/dwy-flutter-testing.md} +0 -0
- /package/templates/claude-global/rules/{dwy-python-testing.md → Python/dwy-python-testing.md} +0 -0
- /package/templates/claude-global/rules/{dwy-vue-core.md → Vue/dwy-vue-core.md} +0 -0
- /package/templates/claude-global/rules/{dwy-vue-pinia.md → Vue/dwy-vue-pinia.md} +0 -0
- /package/templates/claude-global/rules/{dwy-vue-testing.md → Vue/dwy-vue-testing.md} +0 -0
- /package/templates/claude-global/rules/{dwy-tdd-development.md → /345/274/200/345/217/221/346/265/201/347/250/213/dwy-tdd-development.md"} +0 -0
- /package/templates/claude-global/rules/{dwy-db-migration.md → /346/225/260/346/215/256/345/272/223/dwy-db-migration.md"} +0 -0
- /package/templates/claude-global/rules/{dwy-postgres.md → /346/225/260/346/215/256/345/272/223/dwy-postgres.md"} +0 -0
- /package/templates/claude-global/rules/{dwy-redis.md → /346/225/260/346/215/256/345/272/223/dwy-redis.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-eapi/references/tasks-integration-guide.md → /345/237/272/347/241/200/345/272/223/dwy-eapi/references/tasks-integration-guide.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-ekit/SKILL.md → /345/237/272/347/241/200/345/272/223/dwy-ekit/SKILL.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-eui/SKILL.md → /345/237/272/347/241/200/345/272/223/dwy-eui/SKILL.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-eui/references/eui-design-guide.md → /345/237/272/347/241/200/345/272/223/dwy-eui/references/eui-design-guide.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-eui/references/eui-integration-guide.md → /345/237/272/347/241/200/345/272/223/dwy-eui/references/eui-integration-guide.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-eui/references/eui-landing-design-guide.md → /345/237/272/347/241/200/345/272/223/dwy-eui/references/eui-landing-design-guide.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/SKILL.md → /345/256/211/345/205/250/dwy-pentest/SKILL.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/.python-version → /345/256/211/345/205/250/dwy-pentest/scripts/.python-version"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/lib/__init__.py → /345/256/211/345/205/250/dwy-pentest/scripts/lib/__init__.py"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/lib/__pycache__/__init__.cpython-311.pyc → /345/256/211/345/205/250/dwy-pentest/scripts/lib/__pycache__/__init__.cpython-311.pyc"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/lib/__pycache__/config.cpython-311.pyc → /345/256/211/345/205/250/dwy-pentest/scripts/lib/__pycache__/config.cpython-311.pyc"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/lib/__pycache__/executor.cpython-311.pyc → /345/256/211/345/205/250/dwy-pentest/scripts/lib/__pycache__/executor.cpython-311.pyc"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/lib/__pycache__/severity.cpython-311.pyc → /345/256/211/345/205/250/dwy-pentest/scripts/lib/__pycache__/severity.cpython-311.pyc"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/lib/__pycache__/validator.cpython-311.pyc → /345/256/211/345/205/250/dwy-pentest/scripts/lib/__pycache__/validator.cpython-311.pyc"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/lib/config.py → /345/256/211/345/205/250/dwy-pentest/scripts/lib/config.py"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/lib/executor.py → /345/256/211/345/205/250/dwy-pentest/scripts/lib/executor.py"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/lib/severity.py → /345/256/211/345/205/250/dwy-pentest/scripts/lib/severity.py"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/lib/validator.py → /345/256/211/345/205/250/dwy-pentest/scripts/lib/validator.py"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/pentest_auth.py → /345/256/211/345/205/250/dwy-pentest/scripts/pentest_auth.py"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/pentest_recon.py → /345/256/211/345/205/250/dwy-pentest/scripts/pentest_recon.py"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/pentest_report.py → /345/256/211/345/205/250/dwy-pentest/scripts/pentest_report.py"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/pentest_scan.py → /345/256/211/345/205/250/dwy-pentest/scripts/pentest_scan.py"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/pentest_ssl.py → /345/256/211/345/205/250/dwy-pentest/scripts/pentest_ssl.py"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/pentest_vuln.py → /345/256/211/345/205/250/dwy-pentest/scripts/pentest_vuln.py"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/pentest_web.py → /345/256/211/345/205/250/dwy-pentest/scripts/pentest_web.py"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/pyproject.toml → /345/256/211/345/205/250/dwy-pentest/scripts/pyproject.toml"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/setup_tools.sh → /345/256/211/345/205/250/dwy-pentest/scripts/setup_tools.sh"} +0 -0
- /package/templates/claude-global/skills/{dwy-pentest/scripts/uv.lock → /345/256/211/345/205/250/dwy-pentest/scripts/uv.lock"} +0 -0
- /package/templates/claude-global/skills/{dwy-dev-qa/SKILL.md → /345/274/200/345/217/221/346/265/201/347/250/213/dwy-dev-qa/SKILL.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-git-commit/SKILL.md → /345/274/200/345/217/221/346/265/201/347/250/213/dwy-git-commit/SKILL.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-fullstack-scaffold/SKILL.md → /350/204/232/346/211/213/346/236/266/dwy-fullstack-scaffold/SKILL.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-tech-stack/SKILL.md → /350/204/232/346/211/213/346/236/266/dwy-tech-stack/SKILL.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-deploy-audit/SKILL.md → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-audit/SKILL.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-deploy-audit/references/checks-data.md → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-audit/references/checks-data.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-deploy-audit/references/checks-network.md → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-audit/references/checks-network.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-deploy-audit/references/checks-runtime.md → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-audit/references/checks-runtime.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-docker/SKILL.md → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-docker/SKILL.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-docker/references/ask-templates.md → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-docker/references/ask-templates.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-docker/references/templates.md → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-docker/references/templates.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-docker/references/version-rules.md → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-docker/references/version-rules.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-mirror-source/SKILL.md → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-mirror-source/SKILL.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-mirror-source/references/dockerfile-snippets.md → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-mirror-source/references/dockerfile-snippets.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-mirror-source/references/mirror-providers.md → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-mirror-source/references/mirror-providers.md"} +0 -0
- /package/templates/claude-global/skills/{dwy-mirror-source/scripts/__pycache__/check_mirrors.cpython-313.pyc → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-mirror-source/scripts/__pycache__/check_mirrors.cpython-313.pyc"} +0 -0
- /package/templates/claude-global/skills/{dwy-publish/SKILL.md → /350/277/220/347/273/264/345/217/221/345/270/203/dwy-publish/SKILL.md"} +0 -0
|
@@ -1,441 +0,0 @@
|
|
|
1
|
-
# DolphinDB 开发规则
|
|
2
|
-
|
|
3
|
-
所有涉及 DolphinDB 的代码必须遵循以下规则。AI 生成或修改代码时,必须逐条检查。
|
|
4
|
-
|
|
5
|
-
## 一、社区版硬约束
|
|
6
|
-
|
|
7
|
-
| 约束 | 值 | 违反后果 |
|
|
8
|
-
|---|---|---|
|
|
9
|
-
| 最大内存 | 8GB(建议 `maxMemSize=5`,留 3GB 给 OS) | 进程 OOM 崩溃 |
|
|
10
|
-
| 最大集群节点数 | 2(允许 1 controller + 1 datanode 的最小集群) | 超出被许可证拒绝;无法扩展到 3 节点以上 |
|
|
11
|
-
| 最大并发客户端连接 | 512(`maxConnections=512`) | 新连接直接被拒 |
|
|
12
|
-
| TSDB 单次 append | ≤ 204MB(约 800K 行 × 30 列) | 报错 `exceeds max limit` |
|
|
13
|
-
| 高可用 / Raft / 异步复制 | 不支持 | 多副本能力需企业版 |
|
|
14
|
-
|
|
15
|
-
### 本项目部署侧配置(非社区版硬限,是 VM/机器约束)
|
|
16
|
-
|
|
17
|
-
- VM 规格:**2 核 8 GB**(与社区版 8 GB 内存硬限一致)
|
|
18
|
-
- 因 CPU 只有 2 核,`workerNum` 和客户端 `dolphindb_pool_size` 按经验值 `2`,避免大量并行争抢 CPU;**这不是社区版硬限,而是 2 核机器上的合理取值**
|
|
19
|
-
- 512 并发连接是协议层上限,实际可持有的连接远少于这个数,不要把它当作"应该开 512 条"的指令
|
|
20
|
-
|
|
21
|
-
### 违规检测
|
|
22
|
-
如果代码中出现以下情况,必须提示:
|
|
23
|
-
- 单次 `append!` 数据超过 500K 行(安全上限)
|
|
24
|
-
- `maxMemSize` > 8
|
|
25
|
-
- 集群配置超过 2 个节点
|
|
26
|
-
- 客户端 `dolphindb_pool_size` 显著高于 CPU 核数(在 2 核机器上 > 4 需说明理由)
|
|
27
|
-
|
|
28
|
-
## 二、连接与会话管理
|
|
29
|
-
|
|
30
|
-
### 强制规则
|
|
31
|
-
|
|
32
|
-
| 场景 | 使用方式 | 原因 |
|
|
33
|
-
|---|---|---|
|
|
34
|
-
| 只读查询 | `run_ddb(script)` — 连接池 | 池自动管理连接复用 |
|
|
35
|
-
| 单次 upload + run | `run_ddb_with_data(script, data)` — 独立 Session | 确保 upload 和 run 在同一连接 |
|
|
36
|
-
| 批量 upload(多次循环) | `DDBSession` — 复用 Session | 避免每批新建/关闭连接的开销 |
|
|
37
|
-
|
|
38
|
-
```python
|
|
39
|
-
# ❌ 用连接池做 upload + run(变量跨连接不可见)
|
|
40
|
-
await run_ddb("upload_var = ...") # 连接 A
|
|
41
|
-
await run_ddb("select * from upload_var") # 可能路由到连接 B → 变量不存在
|
|
42
|
-
|
|
43
|
-
# ✅ 独立 Session 保证同一连接
|
|
44
|
-
await run_ddb_with_data(script, {"batch_data": df})
|
|
45
|
-
|
|
46
|
-
# ✅ 批量操作复用 Session
|
|
47
|
-
session = DDBSession()
|
|
48
|
-
try:
|
|
49
|
-
for batch in batches:
|
|
50
|
-
await session.upload_and_run(script, {"batch_data": batch})
|
|
51
|
-
finally:
|
|
52
|
-
session.close()
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
### SDK v3 异步 API
|
|
56
|
-
- `DBConnectionPool.run()` 在 SDK v3 中是 **原生 async**,直接 `await pool.run(script)`
|
|
57
|
-
- **禁止** `asyncio.to_thread(pool.run, script)`(会产生 "coroutine never awaited" 警告)
|
|
58
|
-
- `Session.run()` 仍然是 **同步**,需要 `asyncio.to_thread()` 包装
|
|
59
|
-
|
|
60
|
-
## 三、分区裁剪(性能关键)
|
|
61
|
-
|
|
62
|
-
### 强制规则
|
|
63
|
-
**WHERE 条件必须直接命中分区列,禁止对分区列使用函数。**
|
|
64
|
-
|
|
65
|
-
```python
|
|
66
|
-
# ❌ 函数包裹分区列 → 全表扫描 → 8GB OOM
|
|
67
|
-
WHERE date(datetime) = 2020.01.01
|
|
68
|
-
WHERE month(trade_date) = 2020.01M
|
|
69
|
-
WHERE string(order_book_id) = "000001.XSHE"
|
|
70
|
-
|
|
71
|
-
# ✅ 直接范围过滤 → 分区裁剪生效
|
|
72
|
-
WHERE trade_date = 2020.01.01
|
|
73
|
-
WHERE trade_date >= 2020.01.01, trade_date < 2020.02.01
|
|
74
|
-
WHERE order_book_id in [`000001.XSHE`000002.XSHE]
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
### 范围查询禁止链式过滤
|
|
78
|
-
|
|
79
|
-
DolphinDB 不识别链式比较的两端边界,无法做分区剪枝。必须用 `between ... and ...` 或拆成两个独立条件。
|
|
80
|
-
|
|
81
|
-
```sql
|
|
82
|
-
-- ❌ 链式过滤 → 全表扫描(实测扫描 10091 个分区)
|
|
83
|
-
select * from loadTable(dbName, tbName)
|
|
84
|
-
where 2022.12.01 <= TradeDate <= 2022.12.03
|
|
85
|
-
|
|
86
|
-
-- ✅ between ... and ... → 分区剪枝(实测扫描 90 个分区)
|
|
87
|
-
select * from loadTable(dbName, tbName)
|
|
88
|
-
where TradeDate between 2022.12.01 and 2022.12.03
|
|
89
|
-
|
|
90
|
-
-- ✅ 拆成两个独立条件 → 分区剪枝
|
|
91
|
-
select * from loadTable(dbName, tbName)
|
|
92
|
-
where TradeDate >= 2022.12.01 and TradeDate <= 2022.12.03
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
可用 `[HINT_EXPLAIN]` 查看实际扫描的分区数验证:`select [HINT_EXPLAIN] * from ...` 输出中的 `partitions.local` 数量应等于范围内的分区数,而不是全库分区数。
|
|
96
|
-
|
|
97
|
-
### tickDB 分区列
|
|
98
|
-
- 第一级:`trade_date`(VALUE 分区,按日)
|
|
99
|
-
- 第二级:`order_book_id`(HASH 分区,20 桶)
|
|
100
|
-
|
|
101
|
-
### factorDB 分区列
|
|
102
|
-
- 第一级:`trade_date`(VALUE 分区,按月)
|
|
103
|
-
- 第二级:`factor_name`(VALUE 分区)
|
|
104
|
-
|
|
105
|
-
### 违规检测
|
|
106
|
-
如果 DolphinDB 查询脚本中出现以下模式,必须提示:
|
|
107
|
-
- `date(...)` / `month(...)` / `year(...)` 包裹分区列
|
|
108
|
-
- `WHERE 1=1` 无分区条件(全表扫描)
|
|
109
|
-
- `select * from loadTable(...)` 无 WHERE 子句
|
|
110
|
-
- 链式比较 `a <= col <= b`(必须改为 `between a and b` 或拆成两个独立条件)
|
|
111
|
-
|
|
112
|
-
## 四、批量写入
|
|
113
|
-
|
|
114
|
-
### 强制规则
|
|
115
|
-
|
|
116
|
-
```python
|
|
117
|
-
BATCH_SIZE = 500_000 # TSDB 上限 204MB,500K 行 ≈ 120MB(安全余量)
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
| 规则 | 说明 |
|
|
121
|
-
|---|---|
|
|
122
|
-
| 每批 ≤ 500K 行 | TSDB 单次 append 内存上限 204MB |
|
|
123
|
-
| 每批后 `gc.collect()` | 释放 Python 侧内存 |
|
|
124
|
-
| 每批后 `del batch_df` | 显式释放 DataFrame 引用 |
|
|
125
|
-
| 大文件用 pyarrow 分片读取 | `pf.read_table()` + `table.slice()` 避免全量加载 |
|
|
126
|
-
| 复用 Session | 批量循环中不要每批新建连接 |
|
|
127
|
-
|
|
128
|
-
```python
|
|
129
|
-
# ✅ 标准批量上传模式
|
|
130
|
-
table = pf.read_table(filepath) # memory-mapped,不全量加载
|
|
131
|
-
session = DDBSession()
|
|
132
|
-
try:
|
|
133
|
-
for i in range(num_batches):
|
|
134
|
-
arrow_slice = table.slice(i * BATCH_SIZE, BATCH_SIZE)
|
|
135
|
-
batch_df = prepare_batch(arrow_slice.to_pandas())
|
|
136
|
-
del arrow_slice
|
|
137
|
-
await session.upload_and_run(script, {"batch_data": batch_df})
|
|
138
|
-
del batch_df
|
|
139
|
-
gc.collect()
|
|
140
|
-
finally:
|
|
141
|
-
session.close()
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
### 禁止的写法
|
|
145
|
-
```python
|
|
146
|
-
# ❌ 一次性读取大文件到 pandas(OOM)
|
|
147
|
-
df = pd.read_feather("10M_rows.ftr") # 2.6GB 内存
|
|
148
|
-
|
|
149
|
-
# ❌ 单次 append 超过 500K 行
|
|
150
|
-
await run_ddb_with_data(script, {"data": df_1million_rows})
|
|
151
|
-
|
|
152
|
-
# ❌ 不释放内存
|
|
153
|
-
for batch in batches:
|
|
154
|
-
await session.upload_and_run(script, {"data": batch})
|
|
155
|
-
# 忘记 del + gc.collect()
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
## 五、DolphinDB 脚本语法
|
|
159
|
-
|
|
160
|
-
### 列名定义必须单行
|
|
161
|
-
```python
|
|
162
|
-
# ❌ 多行 backtick 列名 → 语法错误
|
|
163
|
-
schema = table(1:0,
|
|
164
|
-
`col1`col2
|
|
165
|
-
`col3`col4,
|
|
166
|
-
[INT, INT, INT, INT])
|
|
167
|
-
|
|
168
|
-
# ✅ 单行定义
|
|
169
|
-
colNames = `col1`col2`col3`col4
|
|
170
|
-
colTypes = [INT, INT, INT, INT]
|
|
171
|
-
schema = table(1:0, colNames, colTypes)
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
### 多值过滤的 backtick 拼接
|
|
175
|
-
```python
|
|
176
|
-
# ✅ 正确的 Symbol 列表拼接
|
|
177
|
-
symbols = ["000001.XSHE", "000002.XSHE"]
|
|
178
|
-
symbols_str = "`" + "`".join(symbols)
|
|
179
|
-
script = f"... order_book_id in [{symbols_str}]"
|
|
180
|
-
# 生成: order_book_id in [`000001.XSHE`000002.XSHE]
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
### 脚本拼接用分号 + 换行
|
|
184
|
-
```python
|
|
185
|
-
# ✅ 多语句用 ;\n 分隔
|
|
186
|
-
script = (
|
|
187
|
-
f'tickData = select * from loadTable("dfs://tickDB","snapshots") '
|
|
188
|
-
f'where {ts_filter};\n'
|
|
189
|
-
f'{user_script};\n'
|
|
190
|
-
f'exec count(*) from result'
|
|
191
|
-
)
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
## 六、输入校验(安全关键)
|
|
195
|
-
|
|
196
|
-
### 强制规则
|
|
197
|
-
**所有外部输入在拼接到 DolphinDB 脚本前,必须经过 `validators.py` 校验。**
|
|
198
|
-
|
|
199
|
-
| 类型 | 正则 | 示例 |
|
|
200
|
-
|---|---|---|
|
|
201
|
-
| 因子名 | `^[a-zA-Z0-9_]{1,50}$` | `alpha_vwap_001` |
|
|
202
|
-
| 股票代码 (ukey) | `^\d{6}\.[A-Z]{2}$` | `000001.SZ` |
|
|
203
|
-
| 日期 | `^\d{4}\.\d{2}\.\d{2}$` | `2024.01.02` |
|
|
204
|
-
|
|
205
|
-
```python
|
|
206
|
-
# ✅ 先校验再拼接
|
|
207
|
-
validate_factor_name(factor_name) # 不通过则抛 BusinessError
|
|
208
|
-
script = f'... factor_name = "{factor_name}"'
|
|
209
|
-
|
|
210
|
-
# ❌ 未校验直接拼接 → 脚本注入风险
|
|
211
|
-
script = f'... factor_name = "{user_input}"'
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
### 违规检测
|
|
215
|
-
如果代码中出现以下情况,必须提示:
|
|
216
|
-
- f-string 拼接 DolphinDB 脚本时使用了未经 `validate_*` 校验的变量
|
|
217
|
-
- 用户输入直接出现在 `run_ddb()` 的 script 参数中
|
|
218
|
-
|
|
219
|
-
## 七、分区操作
|
|
220
|
-
|
|
221
|
-
### 删除分区用 `dropPartition`,不用 `DELETE`
|
|
222
|
-
```python
|
|
223
|
-
# ✅ dropPartition — 原子操作,无 tombstone
|
|
224
|
-
dropPartition(db, partitionPath, tableName)
|
|
225
|
-
|
|
226
|
-
# ❌ DELETE — 标记删除,产生 tombstone,需后续 GC
|
|
227
|
-
delete from loadTable("dfs://tickDB", "snapshots") where trade_date = 2020.01.01
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
### 新增分区值
|
|
231
|
-
配置 `newValuePartitionPolicy=add`,VALUE 分区自动扩展,无需预创建。
|
|
232
|
-
|
|
233
|
-
## 八、查询优化
|
|
234
|
-
|
|
235
|
-
### 聚合查询合并
|
|
236
|
-
```python
|
|
237
|
-
# ❌ 3 次查询
|
|
238
|
-
min_date = await run_ddb("select min(trade_date) ...")
|
|
239
|
-
max_date = await run_ddb("select max(trade_date) ...")
|
|
240
|
-
count = await run_ddb("select count(*) ...")
|
|
241
|
-
|
|
242
|
-
# ✅ 1 次查询
|
|
243
|
-
result = await run_ddb(
|
|
244
|
-
'select min(trade_date) as earliest, max(trade_date) as latest, count(*) as total '
|
|
245
|
-
'from loadTable("dfs://tickDB","snapshots")'
|
|
246
|
-
)
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
### 多因子查询用 PIVOT BY
|
|
250
|
-
```python
|
|
251
|
-
# ✅ DolphinDB 服务端完成列转换,无需 Python 端 merge
|
|
252
|
-
script = (
|
|
253
|
-
'select value from loadTable("dfs://factorDB","factors") '
|
|
254
|
-
f'where {where_clause} '
|
|
255
|
-
'pivot by trade_date, ukey, factor_name'
|
|
256
|
-
)
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
### 分页查询用 `limit offset, count`
|
|
260
|
-
```python
|
|
261
|
-
# ❌ SQL 标准语法 — DolphinDB 不支持 OFFSET 关键字
|
|
262
|
-
f"select * from loadTable(...) limit {page_size} offset {offset}"
|
|
263
|
-
|
|
264
|
-
# ✅ DolphinDB 分页语法:limit 跳过行数, 返回行数
|
|
265
|
-
offset = (page - 1) * page_size
|
|
266
|
-
f"select * from loadTable(...) limit {offset}, {page_size}"
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
### 只查需要的列
|
|
270
|
-
```python
|
|
271
|
-
# ❌ select *(传输全部 30 列)
|
|
272
|
-
"select * from loadTable(...)"
|
|
273
|
-
|
|
274
|
-
# ✅ 只取需要的列
|
|
275
|
-
"select trade_date, time, order_book_id, last, volume from loadTable(...)"
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
## 九、Docker 环境注意事项
|
|
279
|
-
|
|
280
|
-
### DolphinDB v3 Alpine 镜像
|
|
281
|
-
| 问题 | 解决方案 |
|
|
282
|
-
|---|---|
|
|
283
|
-
| 无 `curl` | 用 `wget` 替代 |
|
|
284
|
-
| `localhost` 解析到 IPv6 | healthcheck 用 `127.0.0.1` |
|
|
285
|
-
| macOS 无 `/etc/hostname` `/etc/machine-id` | 不要挂载这两个文件 |
|
|
286
|
-
| 自定义 cfg 中 `logFile`/`volumes` 路径与 Docker volume 冲突 | 只覆盖必要参数,其余用默认值 |
|
|
287
|
-
| `dataSync + CacheEngine` 必须同时启用 | 开发环境建议都不配置,用默认值 |
|
|
288
|
-
|
|
289
|
-
### healthcheck 配置
|
|
290
|
-
```yaml
|
|
291
|
-
healthcheck:
|
|
292
|
-
test: ["CMD-SHELL", "wget -qO- --timeout=3 http://127.0.0.1:8848/ || exit 1"]
|
|
293
|
-
start_period: 30s # DolphinDB 启动较慢,给足初始化时间
|
|
294
|
-
interval: 5s
|
|
295
|
-
timeout: 5s
|
|
296
|
-
retries: 10
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
### 后端连接配置
|
|
300
|
-
```
|
|
301
|
-
DOLPHINDB_HOST=dolphindb # Docker 服务名(不是 localhost)
|
|
302
|
-
DOLPHINDB_PORT=8848
|
|
303
|
-
DOLPHINDB_POOL_SIZE=2 # 2 核机器的经验值;社区版协议上限是 512 并发,不是 2
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
## 十、数据类型映射
|
|
307
|
-
|
|
308
|
-
### Feather/Pandas → DolphinDB
|
|
309
|
-
|
|
310
|
-
| Python/Pandas 类型 | DolphinDB 类型 | 转换方式 |
|
|
311
|
-
|---|---|---|
|
|
312
|
-
| `uint32` (日期如 20191205) | `DATE` | `pd.to_datetime(str, format='%Y%m%d')` |
|
|
313
|
-
| `uint32` (时间如 91509000) | `INT` | `.astype('int64')` |
|
|
314
|
-
| `float64` | `DOUBLE` | 无需转换 |
|
|
315
|
-
| `uint64` | `LONG` | `.astype('int64')` |
|
|
316
|
-
| `uint32` | `INT` | `.astype('int64')` |
|
|
317
|
-
| `float64` 含 NaN → 整数 | `INT` | `.fillna(0).astype('int64')` |
|
|
318
|
-
| `object` (字符串) | `SYMBOL` | `.astype(str)` |
|
|
319
|
-
|
|
320
|
-
### 注意事项
|
|
321
|
-
- DolphinDB 不接受 `uint` 类型,必须先转 `int64`
|
|
322
|
-
- NaN 不能直接转整数,必须先 `fillna(0)`
|
|
323
|
-
- `SYMBOL` 类型是 DolphinDB 的字典编码字符串,适用于低基数列(如股票代码)
|
|
324
|
-
|
|
325
|
-
## 十一、测试 Mock 规范
|
|
326
|
-
|
|
327
|
-
### DolphinDB 测试不连真实服务
|
|
328
|
-
```python
|
|
329
|
-
# conftest.py 中使用 autouse fixture mock 所有 DolphinDB 调用
|
|
330
|
-
@pytest.fixture(autouse=True)
|
|
331
|
-
def mock_dolphindb():
|
|
332
|
-
async def mock_run_ddb(script):
|
|
333
|
-
if "snapshots" in script and "count" in script.lower():
|
|
334
|
-
return pd.DataFrame({"count": [1000]})
|
|
335
|
-
# ... 基于脚本内容的模式匹配
|
|
336
|
-
return pd.DataFrame()
|
|
337
|
-
|
|
338
|
-
with patch("app.services.tick_service.run_ddb", side_effect=mock_run_ddb):
|
|
339
|
-
yield
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
### Mock 必须覆盖所有 import 路径
|
|
343
|
-
```python
|
|
344
|
-
# 每个 service 文件都有独立的 import,必须分别 patch
|
|
345
|
-
patch("app.services.tick_service.run_ddb", ...)
|
|
346
|
-
patch("app.services.tick_archive.run_ddb", ...)
|
|
347
|
-
patch("app.services.tick_compute.run_ddb", ...)
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
## 十二、分区设计原则(建表时)
|
|
351
|
-
|
|
352
|
-
### 单分区合理大小(压缩前)
|
|
353
|
-
|
|
354
|
-
| 引擎 | 推荐范围 | 说明 |
|
|
355
|
-
|---|---|---|
|
|
356
|
-
| OLAP | 100MB ~ 300MB | 列存按分区粒度加载,过大易 OOM |
|
|
357
|
-
| TSDB / PKEY | 400MB ~ 1GB | 内部还有 LevelFile 二级组织,单分区可大一些 |
|
|
358
|
-
|
|
359
|
-
分区过小:元数据膨胀、查询时分区数爆炸、命中 `maxPartitionNumPerQuery` 限制(默认 65536)。
|
|
360
|
-
分区过大:单分区加载内存压力大、并行度下降、删除/重写代价高。
|
|
361
|
-
|
|
362
|
-
### 分区层级与类型
|
|
363
|
-
|
|
364
|
-
| 维度 | 规则 |
|
|
365
|
-
|---|---|
|
|
366
|
-
| 层级 | 最少 1 级,最多 3 级;分区方案一经确定无法调整,库下所有表共用 |
|
|
367
|
-
| 范围分区 (RANGE) | **只能向后扩展且无法自动扩展**,需手动 `addRangePartitions` |
|
|
368
|
-
| 范围分区越界数据 | `allowMissingPartitions=true`(默认)会**静默丢弃**范围外数据;需要报错改为 `false` |
|
|
369
|
-
| 值分区 (VALUE) | 可自动扩展(需 `newValuePartitionPolicy=add`);**不宜初始化过多空分区** |
|
|
370
|
-
| 哈希分区 (HASH) | 桶数固定,建库后不可改 |
|
|
371
|
-
|
|
372
|
-
### 分区方案设计反例
|
|
373
|
-
|
|
374
|
-
> 股票行情数据(每天 5000 万条记录),先按 `TradeDate` 值分区,再按 `SecurityID` 二级值分区。
|
|
375
|
-
> 假设全是国内股票(约 5000 只),一天分区数就达 5000+。每天数据 3GB,**每个分区只有约 0.6MB**。
|
|
376
|
-
|
|
377
|
-
后果:
|
|
378
|
-
- 默认 `maxPartitionNumPerQuery=65536`,每天 5000 个分区,**查 14 天就报错**:`The number of partitions relevant to the query is too large`
|
|
379
|
-
- 单分区 0.6MB 远低于 OLAP 100MB / TSDB 400MB 推荐下限,元数据开销远大于数据本身
|
|
380
|
-
- 修复方向:二级分区改用 `HASH(SecurityID, 20)`(按经验固定 20 桶),或合并到日粒度单级分区
|
|
381
|
-
|
|
382
|
-
### 设计检查项
|
|
383
|
-
|
|
384
|
-
- [ ] 单分区估算大小是否落在引擎推荐区间?
|
|
385
|
-
- [ ] 总分区数是否远低于单次查询 65536 上限?
|
|
386
|
-
- [ ] 二级是否用了高基数列做 VALUE 分区?(高基数列优先 HASH)
|
|
387
|
-
- [ ] RANGE 分区是否预留了 `addRangePartitions` 的扩展机制?是否需要 `allowMissingPartitions=false`?
|
|
388
|
-
|
|
389
|
-
## 十三、字符串/BLOB/SYMBOL 长度限制
|
|
390
|
-
|
|
391
|
-
自 **1.30.23 / 2.00.11** 版本起,分布式表写入对字符串类数据增加了硬性大小限制:
|
|
392
|
-
|
|
393
|
-
| 类型 | 上限 | 超限行为 |
|
|
394
|
-
|---|---|---|
|
|
395
|
-
| `STRING` | < 64 KB | 静默截断到 65,535 字节(64 KB - 1) |
|
|
396
|
-
| `BLOB` | < 64 MB | 静默截断到 67,108,863 字节(64 MB - 1) |
|
|
397
|
-
| `SYMBOL` | ≤ 255 字节 | **直接抛异常**,写入失败 |
|
|
398
|
-
|
|
399
|
-
### 强制规则
|
|
400
|
-
|
|
401
|
-
写入前必须做长度校验,**特别是 SYMBOL 列会让整批写入失败**:
|
|
402
|
-
|
|
403
|
-
```python
|
|
404
|
-
# ✅ 写入前校验 SYMBOL 列长度
|
|
405
|
-
MAX_SYMBOL_BYTES = 255
|
|
406
|
-
oversize_mask = batch_df["order_book_id"].str.encode("utf-8").str.len() > MAX_SYMBOL_BYTES
|
|
407
|
-
if oversize_mask.any():
|
|
408
|
-
raise BusinessError(
|
|
409
|
-
f"SYMBOL 列 order_book_id 有 {oversize_mask.sum()} 行超过 255 字节"
|
|
410
|
-
)
|
|
411
|
-
```
|
|
412
|
-
|
|
413
|
-
### 类型选择建议
|
|
414
|
-
|
|
415
|
-
- 短编码字段(股票代码、因子名)→ `SYMBOL`,但必须保证 ≤ 255 字节
|
|
416
|
-
- 用户可输入的中等长度文本(备注、描述)→ `STRING`,注意 64 KB 截断风险,超长字段做应用层校验
|
|
417
|
-
- 大文本/二进制(JSON、文件)→ `BLOB`,超长会被截断而不是报错,**截断不可逆**,必须在应用层校验长度
|
|
418
|
-
- 中文按 UTF-8 编码,1 字符 ≈ 3 字节,预估时按字节而非字符
|
|
419
|
-
|
|
420
|
-
### 违规检测
|
|
421
|
-
|
|
422
|
-
- 写入路径上对 `SYMBOL` 列没有长度校验 → 整批写入可能失败
|
|
423
|
-
- 写入 `STRING`/`BLOB` 时没有长度上限断言 → 数据被静默截断,事后无法恢复
|
|
424
|
-
|
|
425
|
-
## 十四、检查清单
|
|
426
|
-
|
|
427
|
-
每次涉及 DolphinDB 的代码变更,必须逐条检查:
|
|
428
|
-
|
|
429
|
-
- [ ] WHERE 条件是否命中分区列?是否有函数包裹分区列?
|
|
430
|
-
- [ ] 范围条件是否使用 `between ... and ...` 或两个独立条件?是否存在链式比较 `a <= col <= b`?
|
|
431
|
-
- [ ] 批量写入是否 ≤ 500K 行?是否有 `gc.collect()`?
|
|
432
|
-
- [ ] 外部输入是否经过 `validators.py` 校验?
|
|
433
|
-
- [ ] 使用 `run_ddb` 还是 `run_ddb_with_data`?场景是否匹配?
|
|
434
|
-
- [ ] 连接池大小是否与 CPU 核数匹配(2 核机器建议 `pool_size=2`)?
|
|
435
|
-
- [ ] 查询是否只 select 了需要的列?
|
|
436
|
-
- [ ] 多次聚合查询能否合并为一次?
|
|
437
|
-
- [ ] 脚本中 backtick 列名是否在单行内?
|
|
438
|
-
- [ ] 新增 service 文件的 `run_ddb` 导入是否在 conftest.py 中 mock?
|
|
439
|
-
- [ ] 分页查询是否使用 `limit offset, count` 语法?(禁止 `LIMIT x OFFSET y`)
|
|
440
|
-
- [ ] 新建库表时单分区估算大小是否在引擎推荐区间内?总分区数是否远低于 65536?
|
|
441
|
-
- [ ] 写入路径对 `SYMBOL` 列是否有 ≤ 255 字节长度校验?`STRING` / `BLOB` 是否有上限断言以避免静默截断?
|