Rust LogoDruid

Druid is an open-source, high-performance distributed data store designed for real-time analytical queries on large datasets. It's often categorized as a time-series database, OLAP database, or a data warehouse for analytics. Key characteristics include:

* Real-time Ingestion: Druid can ingest millions of events per second from various sources like Kafka, Kinesis, HDFS, and local files, making data available for querying almost instantly.
* Fast Query Performance: It uses a column-oriented storage format, bitmap indexes, and query-time parallelization to achieve sub-second query latency even on petabytes of data. It's particularly optimized for aggregations (count, sum, min, max) and filtering on time and categorical dimensions.
* Scalability: Druid is designed to scale horizontally by adding more nodes. Its shared-nothing architecture ensures high availability and fault tolerance.
* High Availability: Data is replicated, and the system can recover from node failures without service interruption.
* Flexibility: Supports various data models, including event-level data, aggregated data, and time-series data. It offers both native JSON-based queries and SQL queries.

Druid is widely used for:
* Business Intelligence (BI) and operational analytics dashboards.
* IoT analytics.
* Network performance monitoring.
* Clickstream analytics.
* Ad-tech analytics.

While Druid itself is primarily implemented in Java and Scala, interacting with it from other programming languages like Rust is typically done via its well-defined HTTP API. Rust applications can leverage HTTP client libraries (like `reqwest`) to send native JSON queries or SQL queries to a Druid broker and parse the JSON responses. There isn't a dedicated "Druid client library" written in Rust in the same way there might be for a relational database, but integrating via its HTTP/SQL API is straightforward and robust.

Example Code

use reqwest::Client;
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use tokio; // For async runtime

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let druid_broker_url = "http://localhost:8082/druid/v2/"; // Replace with your Druid broker URL

    // 1. Define the Druid native JSON query
    // This example performs a simple timeseries aggregation on a hypothetical "wikipedia" datasource
    // for a specific time range, counting events.
    let query = json!({
        "queryType": "timeseries",
        "dataSource": "wikipedia", // Replace with an existing datasource in your Druid instance
        "intervals": ["2015-09-12T00:00:00.000Z/2015-09-13T00:00:00.000Z"],
        "granularity": "all",
        "aggregations": [
            { "type": "count", "name": "count" }
        ]
    });

    println!("Sending query to Druid:\n{}", serde_json::to_string_pretty(&query)?);

    // 2. Create an HTTP client
    let client = Client::new();

    // 3. Send the POST request to the Druid broker
    let response = client
        .post(druid_broker_url)
        .header("Content-Type", "application/json")
        .json(&query)
        .send()
        .await?;

    // 4. Check if the request was successful
    if response.status().is_success() {
        let response_body: Vec<Value> = response.json().await?;
        println!("\nDruid Query Results:");
        for item in response_body {
            println!("{}", serde_json::to_string_pretty(&item)?);
        }
    } else {
        eprintln!("\nError querying Druid: Status code {}", response.status());
        eprintln!("Response body: {}", response.text().await?);
    }

    Ok(())
}

// To run this example, add the following to your Cargo.toml:
// [dependencies]
// tokio = { version = "1", features = ["full"] }
// reqwest = { version = "0.11", features = ["json"] }
// serde = { version = "1", features = ["derive"] }
// serde_json = "1"