clickhouse-rs

repository·main·Indexed 19 days ago

https://github.com/clickhouse/clickhouse-rs

Official pure Rust typed client for ClickHouse DB. It leverages serde for high-performance row encoding/decoding and supports various compression and TLS configurations. Features include an Inserter for continuous data streams, Apache Arrow support via the clickhouse-ext-arrow crate, and integration with OpenTelemetry.

Tokens
25.7K
Snippets
77
Records
103
Agent score
66%

What's inside clickhouse-rs

  1. Overview of ClickHouse Rust client usage scenarios

    main

    The clickhouse-rs repository provides examples covering several categories of usage:

    General Usage

    • Basic Operations: Creating tables (DDL), inserting data, and selecting rows (usage.rs).
    • Testing: Writing tests using the mock feature (mock.rs).
    • Batching:
      • Client-side batching via the inserter feature (inserter.rs).
      • Server-side batching using ClickHouse's asynchronous inserts (async_insert.rs).
    • Cloud & Settings:
      • Connecting to ClickHouse Cloud with specific settings like wait_end_of_query and select_sequential_consistency (clickhouse_cloud.rs).
      • Applying query-level ClickHouse settings (clickhouse_settings.rs).
    • Parameters: Using parametrized queries with server-side parameters (server_side_params.rs).

    Data Types

    • Deriving Types: Using macros to derive ClickHouse data types in structs, including simple types (data_types_derive_simple.rs), container types like Array, Tuple, Map, Nested, and Geo (data_types_derive_containers.rs), and the Variant type (data_types_variant.rs).
    • JSON: Working with the new ClickHouse JSON data type as a String (data_types_new_json.rs).

    Special Cases

    • Apache Arrow: Reading and writing RecordBatches via the clickhouse-ext-arrow crate (arrow.rs).
    • HTTP Customization: Using a custom Hyper client with tuned connection pools (custom_http_client.rs) or setting/overriding HTTP headers (custom_http_headers.rs).
    • Observability: Integrating with tracing and the OpenTelemetry Rust SDK (opentelemetry.rs).
    • Query Control: Setting a specific query_id (query_id.rs) or using session contexts with temporary tables (session_id.rs).
    • Streaming: Streaming query results as raw bytes to a file (stream_into_file.rs) or streaming rows in an arbitrary format (stream_arbitrary_format_rows.rs).
  2. Overview of ClickHouse Rust client capabilities

    main

    The clickhouse crate is the official Rust client for ClickHouse. Key characteristics include:

    • Data Encoding/Decoding: Uses serde for row serialization and deserialization. It supports standard serde attributes like skip_serializing, skip_deserializing, and rename.
    • Transport: Uses the RowBinary format over HTTP. (Future plans include Native over TCP).
    • Compression: Supports LZ4 compression/decompression.
    • Security: Supports TLS via native-tls or rustls-tls.
    • Core APIs: Provides methods for selecting rows, inserting data (including client-side batching via inserter), executing DDLs, and watching data.
  3. Disable schema validation for performance

    main

    By default, clickhouse-rs uses the RowBinaryWithNamesAndTypes format, which validates your Rust structs against the ClickHouse schema. This provides clear error messages but adds a small performance penalty.

    To maximize performance, you can disable validation using Client::with_validation(false). This switches the client to the RowBinary format.

    Warning: When validation is disabled, schema mismatches will result in a generic NotEnoughData error instead of descriptive error messages. It is highly recommended to write smoke tests to ensure your row types match the database schema if you disable validation.

  4. Verify ClickHouse server compatibility

    main
    The supported versions of the ClickHouse database server coincide with the versions currently receiving security updates from the official ClickHouse project. Check the official ClickHouse security changelog for the list of supported versions.
  5. How infinite inserting works with the Inserter

    main

    The Inserter (requires the inserter feature) provides a way to manage continuous data streams by automatically committing batches when certain thresholds are met. This prevents memory bloat and manages network load.

    Thresholds:

    • max_bytes: Maximum bytes accumulated before a commit.
    • max_rows: Maximum rows accumulated before a commit.
    • period: Maximum time elapsed before a commit.

    Key Operations:

    • inserter.write(&row): Adds a row to the current buffer.
    • inserter.commit(): Manually triggers a commit and returns statistics (rows, bytes, transactions).
    • inserter.end(): Flushes and terminates the inserter. Always call this to ensure all data is sent.
    • inserter.time_left(): Useful for detecting when the current period is nearing its end.
    use serde::Serialize;
    use clickhouse::Row;
    use clickhouse::inserter::Inserter;
    use std::time::Duration;
    
    #[derive(Row, Serialize)]
    struct MyRow {
        no: u32,
        name: String,
    }
    
    async fn example(client: clickhouse::Client) -> clickhouse::error::Result<()> {
        let mut inserter = client.inserter::<MyRow>("some")
            .with_timeouts(Some(Duration::from_secs(5)), Some(Duration::from_secs(20)))
            .with_max_bytes(50_000_000)
            .with_max_rows(750_000)
            .with_period(Some(Duration::from_secs(15)));
        
        inserter.write(&MyRow { no: 0, name: "foo".into() }).await?;
        inserter.write(&MyRow { no: 1, name: "bar".into() }).await?;
        let stats = inserter.commit().await?;
        if stats.rows > 0 {
            println!(
                "{} bytes, {} rows, {} transactions have been inserted",
                stats.bytes, stats.rows, stats.transactions,
            );
        }
        Ok(())
    }
  6. Use test-util for mocking ClickHouse

    main

    The crate provides utilities to mock the ClickHouse server for testing DDL, SELECT, INSERT, and WATCH queries.

    Important: The test-util feature should be enabled only as a dev-dependency in your Cargo.toml to avoid bloating production binaries.

  7. ClickHouse version compatibility

    main

    The client is compatible with ClickHouse Cloud and ClickHouse LTS or newer versions.

    Legacy Server Support: ClickHouse servers older than v22.6 may incorrectly handle RowBinary in rare cases. To mitigate this, you can use clickhouse version 0.11+ and enable the wa-37420 feature.

    <Warning> Do NOT use the wa-37420 feature with newer ClickHouse versions. </Warning>

  8. Use the Variant data type

    main

    The Variant data type is supported as a Rust enum.

    CRITICAL: Because ClickHouse sorts inner Variant types alphabetically, your Rust enum variants must be defined in the exact same order as the types in the ClickHouse column definition. The names of the variants do not matter, but their order is strictly enforced.

    use clickhouse::Row;
    use serde::{Serialize, Deserialize};
    use time::Date;
    
    // Column: Variant(Array(UInt16), Bool, Date, String, UInt32)
    #[derive(Serialize, Deserialize)]
    enum MyRowVariant {
        Array(Vec<i16>),      // 1. Array(UInt16)
        Boolean(bool),        // 2. Bool
        #[serde(with = "clickhouse::serde::time::date")]
        Date(time::Date),     // 3. Date
        String(String),       // 4. String
        UInt32(u32),          // 5. UInt32
    }
    
    #[derive(Row, Serialize, Deserialize)]
    struct MyRow {
        id: u64,
        var: MyRowVariant,
    }
  9. Run benchmarks with a mocked server

    main

    To measure the overhead of the clickhouse-rs client itself without network or database latency, you can run benchmarks against a mocked server. The mocked server is a simple HTTP server that returns fixed responses.

    Scenarios:

    • mocked_select: Measures throughput of Client::query().
    • mocked_insert: Measures throughput of Client::insert() and Client::inserter() (note: Client::inserter() requires the inserter feature to be enabled).

    Run the benchmarks using:

    cargo bench --bench <case>
    cargo bench --bench mocked_select
  10. Create a ClickHouse client instance

    main

    To interact with ClickHouse, initialize a Client using the builder pattern. You should provide the URL (including protocol and port), user, password, and database.

    Tip: Reuse or clone Client instances to benefit from the underlying hyper connection pool.

    For HTTPS or ClickHouse Cloud connections, ensure you have enabled either the rustls-tls or native-tls cargo features.

    use clickhouse::Client;
    
    let client = Client::default()
        // should include both protocol and port
        .with_url("http://localhost:8123")
        .with_user("name")
        .with_password("123")
        .with_database("test");
  11. Mock the ClickHouse server for testing

    main

    The crate provides utilities to mock the ClickHouse server, allowing you to test DDL, SELECT, and INSERT queries without a live database.

    To use this functionality, you must enable the test-util feature. Warning: This feature is intended for use only in dev-dependencies to avoid bloating production binaries.

    [dev-dependencies]
    clickhouse = { version = "0.15.1", features = ["test-util"] }
  12. Install clickhouse-rs

    main

    To use the clickhouse crate in your Rust project, add it to your Cargo.toml. For development and testing, you may also want to include the test-util feature.

    [dependencies]
    clickhouse = "0.14.2"
    
    [dev-dependencies]
    clickhouse = { version = "0.14.2", features = ["test-util"] }