Files
AstralRinth/apps/labrinth/src/clickhouse/mod.rs
Alejandro González a945e9b005 tweak(labrinth): drop last remaining dependency on system OpenSSL (#3982)
We standarized on using `rustls` as a TLS implementation across the
monorepo, which is written in Rust and has better ergonomics,
integration with the Rust ecosystem, and consistent behavior among
platforms. However, the Labrinth Clickhouse client was the last
remaining exception to this, using the native, OS-provided TLS
implementation, which on Linux is OpenSSL and requires developers and
Docker images to install OpenSSL development packages to build Labrinth,
in addition to introducing an additional runtime dependency to Labrinth.

Let's make the process of building Labrinth slightly simpler by
switching such client to `rustls` as well, which results in finally
using the same TLS implementation for everything, a simplified build and
distribution process, less transitive dependencies, and potentially
smaller binaries (since `rustls` was already being pulled in for, e.g.,
the SMTP client).
2025-07-12 13:39:41 +00:00

129 lines
3.5 KiB
Rust

use hyper_rustls::HttpsConnectorBuilder;
use hyper_util::rt::TokioExecutor;
mod fetch;
pub use fetch::*;
pub async fn init_client() -> clickhouse::error::Result<clickhouse::Client> {
init_client_with_database(&dotenvy::var("CLICKHOUSE_DATABASE").unwrap())
.await
}
pub async fn init_client_with_database(
database: &str,
) -> clickhouse::error::Result<clickhouse::Client> {
let client = {
let https_connector = HttpsConnectorBuilder::new()
.with_native_roots()?
.https_or_http()
.enable_all_versions()
.build();
let hyper_client =
hyper_util::client::legacy::Client::builder(TokioExecutor::new())
.build(https_connector);
clickhouse::Client::with_http_client(hyper_client)
.with_url(dotenvy::var("CLICKHOUSE_URL").unwrap())
.with_user(dotenvy::var("CLICKHOUSE_USER").unwrap())
.with_password(dotenvy::var("CLICKHOUSE_PASSWORD").unwrap())
};
client
.query(&format!("CREATE DATABASE IF NOT EXISTS {database}"))
.execute()
.await?;
let clickhouse_replicated =
dotenvy::var("CLICKHOUSE_REPLICATED").unwrap() == "true";
let cluster_line = if clickhouse_replicated {
"ON cluster '{cluster}'"
} else {
""
};
let engine = if clickhouse_replicated {
"ReplicatedMergeTree('/clickhouse/{installation}/{cluster}/tables/{shard}/{database}/{table}', '{replica}')"
} else {
"MergeTree()"
};
client
.query(&format!(
"
CREATE TABLE IF NOT EXISTS {database}.views {cluster_line}
(
recorded DateTime64(4),
domain String,
site_path String,
user_id UInt64,
project_id UInt64,
monetized Bool DEFAULT True,
ip IPv6,
country String,
user_agent String,
headers Array(Tuple(String, String))
)
ENGINE = {engine}
PRIMARY KEY (project_id, recorded, ip)
SETTINGS index_granularity = 8192
"
))
.execute()
.await?;
client
.query(&format!(
"
CREATE TABLE IF NOT EXISTS {database}.downloads {cluster_line}
(
recorded DateTime64(4),
domain String,
site_path String,
user_id UInt64,
project_id UInt64,
version_id UInt64,
ip IPv6,
country String,
user_agent String,
headers Array(Tuple(String, String))
)
ENGINE = {engine}
PRIMARY KEY (project_id, recorded, ip)
SETTINGS index_granularity = 8192
"
))
.execute()
.await?;
client
.query(&format!(
"
CREATE TABLE IF NOT EXISTS {database}.playtime {cluster_line}
(
recorded DateTime64(4),
seconds UInt64,
user_id UInt64,
project_id UInt64,
version_id UInt64,
loader String,
game_version String,
parent UInt64
)
ENGINE = {engine}
PRIMARY KEY (project_id, recorded, user_id)
SETTINGS index_granularity = 8192
"
))
.execute()
.await?;
Ok(client.with_database(database))
}