Files
ResourcePool.Docs/SMARTPOOL_DATABASE_SEPARATION.md
T

8.6 KiB

SmartPool Database Separation

Tổng quan

SmartPool sử dụng 2 database riêng biệt trong ClickHouse để tối ưu hóa hiệu suất và quản lý:

  1. smart_pool_meta - Metadata database
  2. smart_pool_logs - Logs & Analytics database

Lý do tách riêng

1. Performance Optimization

  • Metadata database: Ít thay đổi, truy vấn nhanh, không cần TTL
  • Logs database: Ghi liên tục, volume lớn, có TTL để tự động xóa dữ liệu cũ

2. Scalability

  • Có thể scale từng database độc lập
  • Logs database có thể dùng cluster riêng với nhiều replica
  • Metadata database có thể dùng SSD nhanh hơn

3. Backup & Maintenance

  • Backup metadata thường xuyên hơn (quan trọng)
  • Backup logs ít hơn hoặc không cần (có thể tái tạo)
  • Maintenance window riêng biệt

4. Access Control

  • Có thể phân quyền riêng cho từng database
  • Service chỉ đọc metadata không cần quyền ghi logs

Cấu trúc Database

smart_pool_meta

smart_pool_meta/
├── proxy_metadata           (ReplacingMergeTree)
│   ├── id, host, port
│   ├── protocol, ip_version, country
│   ├── auth_username, auth_password
│   └── status, cluster, source
│
└── proxy_access_mapping     (ReplacingMergeTree)
    ├── access_token
    ├── cluster              (← Maps to cluster, not proxy_id)
    └── priority

Đặc điểm:

  • Dữ liệu ít thay đổi
  • Không có TTL
  • Sử dụng ReplacingMergeTree để update
  • Truy vấn nhanh với FINAL

smart_pool_logs

smart_pool_logs/
├── proxy_usage_logs         (MergeTree)
│   ├── access_token, target_domain
│   ├── proxy_id, status_code
│   ├── response_time_ms, is_success
│   ├── timestamp
│   └── TTL: 90 days
│
├── proxy_stats_hourly       (AggregatingMergeTree)
│   ├── hour, access_token
│   ├── target_domain, proxy_id
│   └── Aggregate states
│
└── mv_proxy_stats_hourly    (Materialized View)
    └── Auto-aggregate from usage_logs

Đặc điểm:

  • Ghi liên tục, volume lớn
  • TTL 90 ngày tự động xóa
  • Partition by date
  • Materialized View tự động aggregate

Configuration

appsettings.json

{
  "ClickHouse": {
    "Host": "localhost",
    "Port": 8123,
    "MetaDatabase": "smart_pool_meta",
    "LogsDatabase": "smart_pool_logs",
    "Username": "default",
    "Password": "",
    "UseCompression": true,
    "CommandTimeout": 30
  }
}

Production Recommendations

{
  "ClickHouse": {
    "Host": "clickhouse-cluster.internal",
    "Port": 8123,
    "MetaDatabase": "smart_pool_meta",
    "LogsDatabase": "smart_pool_logs",
    "Username": "smartpool_user",
    "Password": "secure_password",
    "UseCompression": true,
    "CommandTimeout": 30
  }
}

Code Implementation

IClickHouseContext Interface

public interface IClickHouseContext
{
    /// <summary>
    /// Create connection to metadata database
    /// </summary>
    ClickHouseConnection CreateMetaConnection();

    /// <summary>
    /// Create connection to logs database
    /// </summary>
    ClickHouseConnection CreateLogsConnection();
}

Repository Usage

ProxyMetadataRepository - Sử dụng Meta Database:

using var connection = _clickHouseContext.CreateMetaConnection();
var proxies = await connection.QueryAsync<SmartProxyServer>(
    "SELECT * FROM smart_pool_meta.proxy_metadata FINAL"
);

ProxyLogRepository - Sử dụng Logs Database:

using var connection = _clickHouseContext.CreateLogsConnection();
await connection.ExecuteAsync(
    "INSERT INTO smart_pool_logs.proxy_usage_logs (...) VALUES (...)"
);

Cross-Database Query - Khi cần enrich data:

// 1. Query metadata từ meta database
using var metaConn = _clickHouseContext.CreateMetaConnection();
var proxyMeta = await metaConn.QueryFirstOrDefaultAsync(
    "SELECT host, protocol FROM smart_pool_meta.proxy_metadata WHERE id = @id"
);

// 2. Insert vào logs database với enriched data
using var logsConn = _clickHouseContext.CreateLogsConnection();
await logsConn.ExecuteAsync(
    "INSERT INTO smart_pool_logs.proxy_usage_logs (...) VALUES (...)"
);

Query Examples

Metadata Queries

-- Get all active proxies
SELECT * FROM smart_pool_meta.proxy_metadata FINAL
WHERE status = 1;

-- Get proxy access mapping
SELECT * FROM smart_pool_meta.proxy_access_mapping FINAL
WHERE access_token = 'your-token';

-- Get proxy by ID
SELECT * FROM smart_pool_meta.proxy_metadata FINAL
WHERE id = 123;

Logs Queries

-- Get recent logs
SELECT * FROM smart_pool_logs.proxy_usage_logs
WHERE timestamp >= now() - INTERVAL 1 HOUR
ORDER BY timestamp DESC
LIMIT 100;

-- Get proxy stats
SELECT 
    proxy_id,
    avgMerge(avg_response_time_state) as avg_time,
    sumIfMerge(success_count_state) / countMerge(request_count_state) as success_rate
FROM smart_pool_logs.proxy_stats_hourly
WHERE hour >= now() - INTERVAL 24 HOUR
GROUP BY proxy_id;

-- Get success rate by domain
SELECT 
    target_domain,
    proxy_id,
    sumIfMerge(success_count_state) / countMerge(request_count_state) as success_rate
FROM smart_pool_logs.proxy_stats_hourly
WHERE hour >= now() - INTERVAL 7 DAY
GROUP BY target_domain, proxy_id
ORDER BY success_rate DESC;

Migration Guide

Nếu bạn đang migrate từ single database:

1. Backup dữ liệu cũ

clickhouse-client --query "SELECT * FROM smart_pool.proxy_metadata FORMAT Native" > proxy_metadata.native
clickhouse-client --query "SELECT * FROM smart_pool.proxy_access_mapping FORMAT Native" > proxy_access_mapping.native

2. Tạo databases mới

clickhouse-client < clickhouse_init.sql

3. Import dữ liệu

cat proxy_metadata.native | clickhouse-client --query "INSERT INTO smart_pool_meta.proxy_metadata FORMAT Native"
cat proxy_access_mapping.native | clickhouse-client --query "INSERT INTO smart_pool_meta.proxy_access_mapping FORMAT Native"

4. Cập nhật configuration

Thay đổi Database thành MetaDatabaseLogsDatabase trong appsettings.json

5. Deploy code mới

Deploy API với code đã cập nhật

Monitoring

Disk Usage

-- Check meta database size
SELECT 
    database,
    table,
    formatReadableSize(sum(bytes)) as size
FROM system.parts
WHERE database = 'smart_pool_meta'
GROUP BY database, table;

-- Check logs database size
SELECT 
    database,
    table,
    formatReadableSize(sum(bytes)) as size
FROM system.parts
WHERE database = 'smart_pool_logs'
GROUP BY database, table;

Query Performance

-- Slow queries on meta database
SELECT 
    query,
    query_duration_ms,
    read_rows,
    read_bytes
FROM system.query_log
WHERE database = 'smart_pool_meta'
    AND type = 'QueryFinish'
    AND query_duration_ms > 1000
ORDER BY query_duration_ms DESC
LIMIT 10;

Best Practices

  1. Metadata Database:

    • Sử dụng FINAL trong queries
    • Cache kết quả ở application layer
    • Update ít, read nhiều
  2. Logs Database:

    • Async insert để không block
    • Sử dụng batch insert khi có thể
    • Query từ aggregated tables (proxy_stats_hourly) thay vì raw logs
  3. Connection Management:

    • Reuse connections khi có thể
    • Set appropriate timeout
    • Handle connection errors gracefully
  4. Security:

    • Tạo user riêng cho mỗi database
    • Phân quyền READ/WRITE phù hợp
    • Sử dụng SSL trong production

Troubleshooting

Connection Issues

// Test meta connection
try {
    using var conn = _clickHouseContext.CreateMetaConnection();
    await conn.ExecuteAsync("SELECT 1");
    Console.WriteLine("Meta DB: OK");
} catch (Exception ex) {
    Console.WriteLine($"Meta DB Error: {ex.Message}");
}

// Test logs connection
try {
    using var conn = _clickHouseContext.CreateLogsConnection();
    await conn.ExecuteAsync("SELECT 1");
    Console.WriteLine("Logs DB: OK");
} catch (Exception ex) {
    Console.WriteLine($"Logs DB Error: {ex.Message}");
}

Performance Issues

  • Check if using correct database
  • Verify indexes and partitions
  • Monitor query execution time
  • Check disk I/O and memory usage

Summary

Việc tách metadata và logs thành 2 database riêng mang lại:

Performance: Tối ưu cho từng loại workload
Scalability: Scale độc lập
Maintenance: Quản lý dễ dàng hơn
Security: Phân quyền chi tiết
Cost: Tối ưu storage và compute

Thiết kế này phù hợp cho production với high-volume logging và real-time analytics.