Skip to content

feat(log-db): support ClickHouse LOG_SQL_DSN - #29

Merged
james-6-23 merged 1 commit into
james-6-23:mainfrom
782042369:feat/clickhouse-log-db
Jul 24, 2026
Merged

james-6-23 merged 1 commit into
james-6-23:mainfrom
782042369:feat/clickhouse-log-db

Conversation

@782042369

@782042369 782042369 commented Jul 23, 2026 •

Copy link
Copy Markdown
Contributor

背景

部分 NewAPI 部署会把 logs 写入 ClickHouse。现有日志分库连接只支持 MySQL/PostgreSQL,导致仪表盘、风控和 IP 分析无法读取这类实例的实时日志。

Related to #23.

改动

  • 仅对 LOG_SQL_DSN 识别 clickhouse://,主库仍限定为 MySQL/PostgreSQL
  • 引入 ClickHouse Go 驱动,并为日志查询补充占位符、标识符、去重聚合等方言适配
  • 兼容 ClickHouse 返回的无符号整数类型
  • ClickHouse 日志缺少 channel_name 时,从主库批量补齐渠道名称
  • 最近日志按 created_at, request_id 排序,避免兼容 id 恒为 0 时顺序失真
  • deploy.sh 与 setup-log-db.sh 支持解析、改写和写入 ClickHouse 日志库 DSN
  • 保持未配置 LOG_SQL_DSN 时回落主库的原有行为,并补充配置与方言单元测试

验证

  • cd backend && go test ./...
  • bash -n deploy.sh setup-log-db.sh
  • 完整 Docker 镜像构建通过,重建后容器健康检查为 healthy
  • PostgreSQL 主库 + ClickHouse 日志库实测连接成功
  • /api/analytics/sync-status 返回的日志总数与 ClickHouse SELECT count() 一致
  • /api/dashboard/usage、/api/risk/leaderboards、/api/risk/users/1/analysis 均成功读取 ClickHouse 日志数据

Summary by CodeRabbit

  • New Features

    • Added ClickHouse support for the separate log database.
    • Added ClickHouse-compatible log queries, analytics, index monitoring, and SQL handling.
    • Deployment and setup scripts now recognize ClickHouse DSNs and apply the appropriate defaults.
    • Improved numeric data handling across dashboard and user-management views.
  • Documentation

    • Expanded LOG_SQL_DSN configuration guidance with supported database types, fallback behavior, generation recommendations, and examples.
  • Tests

    • Added coverage for ClickHouse detection and database-specific SQL behavior.

@coderabbitai

coderabbitai Bot commented Jul 23, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

ClickHouse log database support

Layer / File(s) Summary
Engine detection and DSN setup
README.md, backend/go.mod, backend/internal/config/*, deploy.sh, setup-log-db.sh
ClickHouse is added to log-database detection, driver selection, DSN generation, default-port handling, dependency requirements, tests, and configuration documentation.
Database manager dialect support
backend/internal/database/*
The database manager tracks ClickHouse connections, preserves ClickHouse placeholders, and generates dialect-specific identifier, aggregation, and distinct-count SQL.
ClickHouse service query integration
backend/internal/service/abuse_broadcast.go, backend/internal/service/ai_auto_ban.go, backend/internal/service/ip_monitoring.go, backend/internal/service/log_analytics.go, backend/internal/service/risk_monitoring.go
Service queries use shared dialect helpers and ClickHouse-specific index, count, aggregation, ordering, and channel-name enrichment logic.
Portable service consumers and conversions
backend/internal/service/token.go, backend/internal/service/user_management.go, backend/internal/service/dashboard.go
Reserved identifier quoting is centralized, and numeric conversion helpers support additional integer types.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SetupScript
  participant ConfigLoader
  participant DatabaseManager
  participant LogServices
  SetupScript->>SetupScript: Detect ClickHouse DSN and default port
  SetupScript->>ConfigLoader: Provide LOG_SQL_DSN
  ConfigLoader->>DatabaseManager: Select ClickHouse log engine
  DatabaseManager->>LogServices: Expose ClickHouse dialect helpers
  LogServices->>DatabaseManager: Build ClickHouse-compatible queries
  DatabaseManager-->>LogServices: Execute queries and return results
Loading

Suggested reviewers: james-6-23

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding ClickHouse support for LOG_SQL_DSN.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/internal/config/config.go`:
- Around line 237-243: Update detectLogEngine in
backend/internal/config/config.go and the ClickHouse DSN handling at deploy.sh
lines 93-94, 566-577, and 611-613, plus setup-log-db.sh lines 116-125, 300-304,
and 339-342, to recognize http:// and https:// schemes. Preserve the original
scheme and query parameters, including secure, skip_verify, and tls_server_name,
when rewriting LOG_SQL_DSN, and use port 8123 for HTTP and 8443 for HTTPS
instead of native protocol defaults.

In `@backend/internal/service/user_management.go`:
- Around line 731-732: Update the uint64 conversion case in the relevant
value-conversion function to handle values greater than math.MaxInt64 before
converting to int64. Apply an explicit overflow policy, such as returning an
error or another established safe fallback, so oversized ClickHouse unsigned
values are never silently converted into negative results.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: da69dc7c-d0a7-4e41-98a3-536299fcfc1c

📥 Commits

Reviewing files that changed from the base of the PR and between b8ad8e3 and 493cb09.

⛔ Files ignored due to path filters (1)
  • backend/go.sum is excluded by !**/*.sum
📒 Files selected for processing (17)
  • README.md
  • backend/go.mod
  • backend/internal/config/config.go
  • backend/internal/config/config_test.go
  • backend/internal/database/database.go
  • backend/internal/database/database_test.go
  • backend/internal/database/indexes.go
  • backend/internal/service/abuse_broadcast.go
  • backend/internal/service/ai_auto_ban.go
  • backend/internal/service/dashboard.go
  • backend/internal/service/ip_monitoring.go
  • backend/internal/service/log_analytics.go
  • backend/internal/service/risk_monitoring.go
  • backend/internal/service/token.go
  • backend/internal/service/user_management.go
  • deploy.sh
  • setup-log-db.sh

Comment on lines +237 to +243
// detectLogEngine extends main database detection with ClickHouse log stores.
func detectLogEngine(dsn string) DatabaseEngine {
if strings.HasPrefix(strings.ToLower(dsn), "clickhouse://") {
return ClickHouse
}
return detectEngine(dsn)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

For github.com/ClickHouse/clickhouse-go/v2 v2.47.0, which DSN schemes are supported by the database/sql driver, and which options must be retained for HTTP/HTTPS and TLS connections?

💡 Result:

For github.com/ClickHouse/clickhouse-go/v2 (version 2.47.0), the supported Data Source Name (DSN) schemes for the database/sql driver are clickhouse:// (which uses the Native TCP protocol) and http:// (or https://) for HTTP-based communication [1][2][3]. When configuring connections for HTTP/HTTPS or TLS, the following options must be considered: 1. Protocol Selection: - Use http:// or https:// in your DSN string to trigger HTTP transport [4][2][3]. - Alternatively, you can specify Protocol: clickhouse.HTTP in an Options struct if using the non-sql.Open connection methods [5][2]. 2. TLS Connections (HTTPS/Native+TLS): - For secure connections, you can use the https:// scheme in your DSN or set secure=true as a query parameter [4][2]. - When secure=true is used, a minimal TLS configuration is created [4]. - To manage TLS verification, use the skip_verify=true DSN parameter to set InsecureSkipVerify to true (strongly discouraged for production) [1][5][2]. - For advanced TLS configurations (e.g., custom CA, client certificates, or specific cipher suites), you must pass a fully configured tls.Config object via the Options struct rather than relying solely on DSN string parameters [5][4]. - Use the tls_server_name parameter in the DSN to set the TLS SNI (Server Name Indication) or verification hostname [6][7]. 3. HTTP-Specific Settings: - http_proxy: Used to specify an HTTP proxy address [6][1]. - http_path: Used for custom URL paths (e.g., for proxies or load balancers requiring specific endpoints) [6][1]. Note that when using an https:// scheme, the driver automatically handles the secure connection, making the explicit secure=true parameter redundant but often used interchangeably [4]. Always ensure you are using the correct ports: 9000/9440 for Native (TCP) and 8123/8443 for HTTP [2][8].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files =="
git ls-files | rg '(^|/)(config\.go|deploy\.sh|setup-log-db\.sh)$' || true

echo "== config.go relevant =="
sed -n '200,260p' backend/internal/config/config.go 2>/dev/null || true

echo "== deploy.sh relevant =="
sed -n '80,105p' deploy.sh 2>/dev/null || true
sed -n '550,585p' deploy.sh 2>/dev/null || true
sed -n '600,620p' deploy.sh 2>/dev/null || true

echo "== setup-log-db.sh relevant =="
sed -n '105,130p' setup-log-db.sh 2>/dev/null || true
sed -n '290,310p' setup-log-db.sh 2>/dev/null || true
sed -n '330,350p' setup-log-db.sh 2>/dev/null || true

echo "== search ClickHouse/log/db engine detection =="
rg -n 'detectLogEngine|LOG_SQL_DSN|clickhouse|LOG_SQL_DSN_FINAL|dsn_engine|final_dsn|detectEngine' backend deploy.sh setup-log-db.sh 2>/dev/null || true

Repository: james-6-23/new_api_tools

Length of output: 13325


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== clickhouse-go DSN handling =="
go mod download github.com/ClickHouse/clickhouse-go/v2@v2.47.0 >/dev/null
CACHE="$(go env GOCACHE)"
FILE="$(go env GOPATH)/pkg/mod/github.com/!click!house/clickhouse-go/v2@v2.47.0/conn.go"
if [ -f "$FILE" ]; then
  rg -n 'http://|https://|clickhouse://|ParseDSN|Protocol|InsecureSkipVerify|secure|skip_verify|tls_server_name|Port' "$FILE" -C 2
else
  echo "conn.go not found"
fi

echo "== deploy/sh build_clickhouse_dsn =="
sed -n '130,200p' setup-log-db.sh 2>/dev/null || true

echo "== static parser for DSN rewriting sites =="
python3 - <<'PY'
import re
from pathlib import Path

files = {
    'config.go': Path('backend/internal/config/config.go'),
    'deploy.sh': Path('deploy.sh'),
    'setup-log-db.sh': Path('setup-log-db.sh'),
}
cases = [
    ("http://user:pass@clickhouse:8123/db?secure=true", "http"),
    ("https://user:pass@clickhouse:8443/db?skip_verify=true", "https"),
    ("clickhouse://user:pass@clickhouse:9000/db?secure=true", "clickhouse"),
    ("clickhouse://user:pass@clickhouse:9440/db?secure=true", "clickhouse"),
]
for path in files.values():
    text = path.read_text()
    print(f"-- {path} --")
    for dsn, expected in cases:
        detected = None
        if "detectLogEngine" in text or "dsn_engine" in text:
            detected = "clickhouse" if re.search(r'(?:^[^#]*?\b(?:detectLogEngine|dsn_engine)\b[^#]*?)?elif\s+(?:\[\[|"|\'|`)?\$\{?dsn\}?~\s*\^\s*(http[s]?://|https://|clickhouse:)//', text) \
                  or re.search(r'elif\s+\[\[\s*"?'\$$dsn\??[^\]]*~\^\s*(http[s]?://|https://|clickhouse:)//', text) \
                  or 'http://user:pass@clicklass:8123/db?secure=true"'.startswith(expected) else None
# just no-op placeholder
PY

PY

Repository: james-6-23/new_api_tools

Length of output: 3849


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== clickhouse-go DSN parsing =="
CACHE="$(go env GOCACHE)"
find "$CACHE" -path 'github.com/!click!house/clickhouse-go/v2@v2.47.0/*' \( -name '*.go' -o -path '*dsn*' -o -path '*conn*' \) -maxdepth 4 | head -50 || true

PKG="$(go env GOPATH)/pkg/mod/github.com/!click!house/clickhouse-go/v2@v2.47.0"
echo "pkg=$PKG"
find "$PKG" -maxdepth 3 -type f | sed "s#$PKG/##" | head -100

for f in "$PKG/conn.go" "$PKG/dsn.go" "$PKG/options.go" "$PKG/go.mod"; do
  if [ -f "$f" ]; then
    echo "-- $f --"
    rg -n 'http(s)?://|clickhouse://|ParseDSN|InsecureSkipVerify|secure|skip_verify|tls_server_name|http_path|http_proxy|Port|NewTLS|Dial|Open' "$f" -C 2 || true
  fi
done

echo "== all dsn_engine and clickhouse builders =="
rg -n 'dsn_engine|build_clickhouse_dsn|extract_dsn_engine|build_clickhouse_dsn|9000|9440|8123|8443|LOG_SQL_DSN_FINAL|LOG_SQL_DSN|detectLogEngine|detectEngine|host=|dbname' backend/internal/config/config.go deploy.sh setup-log-db.sh backend/internal/config/config_test.go 2>/dev/null || true

Repository: james-6-23/new_api_tools

Length of output: 3219


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== clickhouse options files =="
for f in backend/internal/config/config.go backend/internal/config/config_test.go deploy.sh setup-log-db.sh \
  backend/go.mod backend/go.sum; do
  echo "-- $f --"
  rg -n 'http(s)?://|clickhouse://|detectLogEngine|dsn_engine|build_clickhouse_dsn|extract_dsn_engine|LOG_SQL_DSN|LOG_SQL_DSN_FINAL|8123|8443|9000|9440|secure|skip_verify|tls_server_name|http_path|Protocol|Driver_name' "$f" -C 2 || true
done

echo "== clickhouse-go option parser strings in installed module =="
PKG="$(go env GOPATH)/pkg/mod/github.com/!click!house/clickhouse-go/v2@v2.47.0"
for f in "$PKG/clickhouse_options.go" "$PKG/clickhouse_std.go" "$PKG/dsn.go" "$PKG/conn.go"; do
  if [ -f "$f" ]; then
    echo "-- $f --"
    rg -n 'http(s)?://|clickhouse://|parseDSN|Scheme|Protocol|clickhouse.HTTP|InsecureSkipVerify|secure|skip_verify|tls_server_name|http_path|http_proxy|Port|NewTLS|Dial' "$f" -C 2
  fi
done

Repository: james-6-23/new_api_tools

Length of output: 14840


Handle ClickHouse HTTP(S) log DSNs consistently.

ClickHouse DSNs can use http:// or https:// for HTTP transport, but these scripts only recognize native clickhouse:// and rewire them into clickhouse://${user}:${pass}@${host}:${port}/${db}, dropping the original scheme and query parameters. secure defaults to true even for HTTP, so HTTPS/TLS options are not preserved.

  • Keep HTTP 8123/HTTPS 8443 defaults instead of 9000/9440.
  • Preserve the original scheme and ClickHouse DSN parameters such as secure, skip_verify, and tls_server_name when rewriting LOG_SQL_DSN.
📍 Affects 3 files
  • backend/internal/config/config.go#L237-L243 (this comment)
  • deploy.sh#L93-L94
  • deploy.sh#L566-L577
  • deploy.sh#L611-L613
  • setup-log-db.sh#L116-L125
  • setup-log-db.sh#L300-L304
  • setup-log-db.sh#L339-L342
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/internal/config/config.go` around lines 237 - 243, Update
detectLogEngine in backend/internal/config/config.go and the ClickHouse DSN
handling at deploy.sh lines 93-94, 566-577, and 611-613, plus setup-log-db.sh
lines 116-125, 300-304, and 339-342, to recognize http:// and https:// schemes.
Preserve the original scheme and query parameters, including secure,
skip_verify, and tls_server_name, when rewriting LOG_SQL_DSN, and use port 8123
for HTTP and 8443 for HTTPS instead of native protocol defaults.

Comment on lines +731 to +732
case uint64:
return int64(val)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Prevent uint64 overflow from becoming negative.

Line 732 wraps values above MaxInt64 into negative int64s. Define an overflow policy before conversion so ClickHouse unsigned values cannot silently corrupt IDs or metrics.

Proposed fix
 case uint64:
+    if val > uint64(^uint64(0)>>1) {
+        return int64(^uint64(0) >> 1)
+    }
     return int64(val)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
case uint64:
return int64(val)
case uint64:
if val > uint64(^uint64(0)>>1) {
return int64(^uint64(0) >> 1)
}
return int64(val)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/internal/service/user_management.go` around lines 731 - 732, Update
the uint64 conversion case in the relevant value-conversion function to handle
values greater than math.MaxInt64 before converting to int64. Apply an explicit
overflow policy, such as returning an error or another established safe
fallback, so oversized ClickHouse unsigned values are never silently converted
into negative results.

@james-6-23
james-6-23 merged commit b31b207 into james-6-23:main Jul 24, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants