From d5de058db511b36c5d3ede8042308ce584983f4d Mon Sep 17 00:00:00 2001 From: KazukiKanda Date: Sun, 27 Sep 2026 20:16:17 +0900 Subject: [PATCH 1/2] [verified] Add Impala Kudu benchmark --- impala-kudu/README.md | 36 +++ impala-kudu/benchmark.sh | 17 ++ impala-kudu/check | 38 +++ impala-kudu/create.sql | 156 ++++++++++++ impala-kudu/create_kudu.sql | 143 +++++++++++ impala-kudu/data-size | 17 ++ impala-kudu/docker-compose.yml | 189 ++++++++++++++ impala-kudu/hive-site.xml | 17 ++ impala-kudu/hms-hive-site.xml | 55 ++++ impala-kudu/install | 47 ++++ impala-kudu/load | 27 ++ impala-kudu/load_kudu.sh | 79 ++++++ impala-kudu/queries.sql | 43 ++++ impala-kudu/query | 29 +++ impala-kudu/results/20260927/c6a.2xlarge.json | 236 ++++++++++++++++++ impala-kudu/start | 59 +++++ impala-kudu/stop | 14 ++ impala-kudu/template.json | 12 + impala-kudu/upsert_batch_template.sql | 26 ++ 19 files changed, 1240 insertions(+) create mode 100644 impala-kudu/README.md create mode 100755 impala-kudu/benchmark.sh create mode 100755 impala-kudu/check create mode 100644 impala-kudu/create.sql create mode 100644 impala-kudu/create_kudu.sql create mode 100755 impala-kudu/data-size create mode 100644 impala-kudu/docker-compose.yml create mode 100644 impala-kudu/hive-site.xml create mode 100644 impala-kudu/hms-hive-site.xml create mode 100755 impala-kudu/install create mode 100755 impala-kudu/load create mode 100755 impala-kudu/load_kudu.sh create mode 100644 impala-kudu/queries.sql create mode 100755 impala-kudu/query create mode 100644 impala-kudu/results/20260927/c6a.2xlarge.json create mode 100755 impala-kudu/start create mode 100755 impala-kudu/stop create mode 100644 impala-kudu/template.json create mode 100644 impala-kudu/upsert_batch_template.sql diff --git a/impala-kudu/README.md b/impala-kudu/README.md new file mode 100644 index 0000000000..0ea42efd7a --- /dev/null +++ b/impala-kudu/README.md @@ -0,0 +1,36 @@ +# Impala with Apache Kudu + +This configuration uses Apache Impala as the SQL execution layer and Apache Kudu as the persistent mutable storage layer. It is separate from `impala/`, which queries the source Parquet file directly. + +## What runs + +The single-node stack contains one Kudu master, one Kudu tablet server, Hive Metastore, Impala statestored, catalogd, impalad, and an Impala shell client. The Kudu table uses replication factor 1 and a composite `(WatchID, EventTime)` primary key so all 99,997,497 source rows are preserved. A view restores ClickBench's canonical 105-column order. + +`benchmark.sh` invokes the shared ClickBench driver. `load` first registers the downloaded Parquet file as the source table, creates the Kudu table, and loads it through 10 sequential hash buckets. The batches are sequential rather than parallel; a single full-table `UPSERT ... SELECT` exceeded the Impala sort-node memory limit on the target machine. + +## Caching and restart behavior + +The result is tagged `no-cold`. The shared driver drops the host page cache before the first try, but it does not restart the stack between cold tries. Restarting the existing Impala quickstart stack loses the in-memory catalog state needed by the benchmark; the existing Parquet `impala/` configuration uses the same exception. The two later tries are warm-cache measurements. + +## Storage measurement + +`data-size` reports allocated filesystem blocks for all persistent Kudu master, Kudu tablet server, and Hive Metastore paths. This avoids counting unallocated extents in Kudu's sparse WAL files. Input Parquet is excluded because it is only the load source and is not part of the Kudu-backed database after loading. + +## Reproduction + +The dated result was measured on an AWS `c6a.2xlarge` instance with Ubuntu 24.04, 8 vCPUs, 16 GiB RAM, and a 150 GB gp3 root volume. From a fresh ClickBench checkout: + +```bash +cd impala-kudu +./benchmark.sh +``` + +The benchmark requires x86-64 with AVX support because the Apache Impala images do not start on ARM hosts. `install` installs Docker only when absent, preserves an already-working Compose plugin, downloads the HMS dependency, and pulls the container images before the measured run. + +The exact pre-run source manifest and raw logs were retained as external review evidence; they are not part of this upstream result package. The formal query sweep remains the unmodified 43-query ClickBench workload in `queries.sql`; any deterministic diagnostic queries used to investigate tied or floating-point outputs are kept outside this upstream system directory. + +## Result qualification + +The retained formal run preserved all `99,997,497` rows and produced all 129 timing cells. Its validator accepted Q1–Q41 and Q43; the original Q42 selected a different row at a `PageViews = 1` tie boundary because its `ORDER BY PageViews DESC LIMIT 10 OFFSET 10000` does not define an order among tied groups. A validation-only variant ordering additionally by `WindowClientWidth, WindowClientHeight` matched exactly. The formal timed SQL was not changed. + +Q4's observed relative difference was `8.1792e-14`, inside the predeclared `1e-12` tolerance. Independent exact diagnostics also matched `COUNT(*)`, `COUNT(UserID)`, and `SUM(CAST(UserID AS DECIMAL(38,0)))` between the Parquet and Kudu tables. diff --git a/impala-kudu/benchmark.sh b/impala-kudu/benchmark.sh new file mode 100755 index 0000000000..6cb17bd6b8 --- /dev/null +++ b/impala-kudu/benchmark.sh @@ -0,0 +1,17 @@ +#!/bin/bash +set -euo pipefail + +# Apache Kudu is a durable shared server, but restarting catalogd between +# queries loses the in-memory Impala catalog used by this single-node setup. +# We therefore flush only the OS page cache and mark the result no-cold. +export BENCH_DOWNLOAD_SCRIPT="download-hits-parquet-single" +export BENCH_RESTARTABLE=no +export BENCH_DURABLE=yes +export BENCH_TRIES=3 +export BENCH_QUERIES_FILE="queries.sql" +export BENCH_CHECK_TIMEOUT=900 +export BENCH_CONCURRENT_CONNECTIONS=10 +export BENCH_CONCURRENT_DURATION=600 +export KUDU_LOAD_BUCKETS="${KUDU_LOAD_BUCKETS:-10}" + +exec ../lib/benchmark-common.sh diff --git a/impala-kudu/check b/impala-kudu/check new file mode 100755 index 0000000000..03330dd8fc --- /dev/null +++ b/impala-kudu/check @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +set -euo pipefail + +if docker info >/dev/null 2>&1; then + DOCKER=(docker) +else + DOCKER=(sudo docker) +fi + +dump_diagnostics() { + "${DOCKER[@]}" ps -a --format ' {{.Names}}: {{.Status}}' \ + --filter 'name=impala-' --filter 'name=kudu-' >&2 || true + local container + while IFS= read -r container; do + [ -n "$container" ] || continue + printf '%s\n' "--- $container (last 50 log lines) ---" >&2 + "${DOCKER[@]}" logs --tail 50 "$container" 2>&1 | sed 's/^/ /' >&2 || true + done < <("${DOCKER[@]}" ps -a --format '{{.Names}}' \ + --filter 'name=impala-' --filter 'name=kudu-') +} + +out=$(curl -sSf -m 5 http://localhost:25000/healthz 2>&1) || { + printf 'impala-kudu/check: /healthz unreachable: %s\n' "$out" >&2 + dump_diagnostics + exit 1 +} +if [ "$out" != "OK" ]; then + printf 'impala-kudu/check: /healthz returned: %s\n' "$out" >&2 + dump_diagnostics + exit 1 +fi + +kudu_out=$("${DOCKER[@]}" exec kudu-master \ + kudu cluster ksck kudu-master:7051 2>&1) || { + printf 'impala-kudu/check: kudu cluster ksck failed:\n%s\n' "$kudu_out" >&2 + dump_diagnostics + exit 1 +} diff --git a/impala-kudu/create.sql b/impala-kudu/create.sql new file mode 100644 index 0000000000..324de3309e --- /dev/null +++ b/impala-kudu/create.sql @@ -0,0 +1,156 @@ +CREATE DATABASE IF NOT EXISTS clickbench; +USE clickbench; + +DROP VIEW IF EXISTS hits; +DROP TABLE IF EXISTS hits_raw; + +CREATE EXTERNAL TABLE hits_raw ( + WatchID bigint, + JavaEnable smallint, + Title string, + GoodEvent smallint, + EventTime bigint, + EventDate int, + CounterID int, + ClientIP int, + RegionID int, + UserID bigint, + CounterClass smallint, + OS smallint, + UserAgent smallint, + URL string, + Referer string, + IsRefresh smallint, + RefererCategoryID smallint, + RefererRegionID int, + URLCategoryID smallint, + URLRegionID int, + ResolutionWidth smallint, + ResolutionHeight smallint, + ResolutionDepth smallint, + FlashMajor smallint, + FlashMinor smallint, + FlashMinor2 string, + NetMajor smallint, + NetMinor smallint, + UserAgentMajor smallint, + UserAgentMinor string, + CookieEnable smallint, + JavascriptEnable smallint, + IsMobile smallint, + MobilePhone smallint, + MobilePhoneModel string, + Params string, + IPNetworkID int, + TraficSourceID smallint, + SearchEngineID smallint, + SearchPhrase string, + AdvEngineID smallint, + IsArtifical smallint, + WindowClientWidth smallint, + WindowClientHeight smallint, + ClientTimeZone smallint, + ClientEventTime bigint, + SilverlightVersion1 smallint, + SilverlightVersion2 smallint, + SilverlightVersion3 int, + SilverlightVersion4 smallint, + PageCharset string, + CodeVersion int, + IsLink smallint, + IsDownload smallint, + IsNotBounce smallint, + FUniqID bigint, + OriginalURL string, + HID int, + IsOldCounter smallint, + IsEvent smallint, + IsParameter smallint, + DontCountHits smallint, + WithHash smallint, + HitColor string, + LocalEventTime bigint, + Age smallint, + Sex smallint, + Income smallint, + Interests smallint, + Robotness smallint, + RemoteIP int, + WindowName int, + OpenerName int, + HistoryLength smallint, + BrowserLanguage string, + BrowserCountry string, + SocialNetwork string, + SocialAction string, + HTTPError smallint, + SendTiming int, + DNSTiming int, + ConnectTiming int, + ResponseStartTiming int, + ResponseEndTiming int, + FetchTiming int, + SocialSourceNetworkID smallint, + SocialSourcePage string, + ParamPrice bigint, + ParamOrderID string, + ParamCurrency string, + ParamCurrencyID smallint, + OpenstatServiceName string, + OpenstatCampaignID string, + OpenstatAdID string, + OpenstatSourceID string, + UTMSource string, + UTMMedium string, + UTMCampaign string, + UTMContent string, + UTMTerm string, + FromTag string, + HasGCLID smallint, + RefererHash bigint, + URLHash bigint, + CLID int +) +STORED AS PARQUET +LOCATION '/clickbench/hits'; + +REFRESH hits_raw; + +-- The Parquet file stores EventTime/ClientEventTime/LocalEventTime as Unix epoch seconds (BIGINT) +-- and EventDate as days since 1970-01-01 (INT). Wrap the raw table in a view that exposes the +-- standard ClickBench types so queries.sql matches the canonical text. CAST(from_unixtime(...) AS +-- TIMESTAMP) is the idiomatic Impala BIGINT-epoch -> TIMESTAMP conversion, and days_add() yields a +-- DATE from an int day count. +CREATE VIEW hits AS +SELECT + WatchID, JavaEnable, Title, GoodEvent, + CAST(from_unixtime(EventTime) AS TIMESTAMP) AS EventTime, + days_add(DATE '1970-01-01', EventDate) AS EventDate, + CounterID, ClientIP, RegionID, UserID, CounterClass, OS, UserAgent, + URL, Referer, IsRefresh, RefererCategoryID, RefererRegionID, + URLCategoryID, URLRegionID, ResolutionWidth, ResolutionHeight, + ResolutionDepth, FlashMajor, FlashMinor, FlashMinor2, NetMajor, NetMinor, + UserAgentMajor, UserAgentMinor, CookieEnable, JavascriptEnable, + IsMobile, MobilePhone, MobilePhoneModel, Params, IPNetworkID, + TraficSourceID, SearchEngineID, SearchPhrase, AdvEngineID, IsArtifical, + WindowClientWidth, WindowClientHeight, ClientTimeZone, + CAST(from_unixtime(ClientEventTime) AS TIMESTAMP) AS ClientEventTime, + SilverlightVersion1, SilverlightVersion2, SilverlightVersion3, + SilverlightVersion4, PageCharset, CodeVersion, IsLink, IsDownload, + IsNotBounce, FUniqID, OriginalURL, HID, IsOldCounter, IsEvent, + IsParameter, DontCountHits, WithHash, HitColor, + CAST(from_unixtime(LocalEventTime) AS TIMESTAMP) AS LocalEventTime, + Age, Sex, Income, Interests, Robotness, RemoteIP, WindowName, + OpenerName, HistoryLength, BrowserLanguage, BrowserCountry, + SocialNetwork, SocialAction, HTTPError, SendTiming, DNSTiming, + ConnectTiming, ResponseStartTiming, ResponseEndTiming, FetchTiming, + SocialSourceNetworkID, SocialSourcePage, ParamPrice, ParamOrderID, + ParamCurrency, ParamCurrencyID, OpenstatServiceName, OpenstatCampaignID, + OpenstatAdID, OpenstatSourceID, UTMSource, UTMMedium, UTMCampaign, + UTMContent, UTMTerm, FromTag, HasGCLID, RefererHash, URLHash, CLID +FROM hits_raw; + +-- Make sure Impala's planner has up-to-date stats; without this the +-- first query pays the metadata-collection wall-clock as if it were +-- query work, and pure scan queries plan as full broadcasts. +COMPUTE STATS hits_raw; diff --git a/impala-kudu/create_kudu.sql b/impala-kudu/create_kudu.sql new file mode 100644 index 0000000000..5bea2bb330 --- /dev/null +++ b/impala-kudu/create_kudu.sql @@ -0,0 +1,143 @@ +USE clickbench; + +DROP VIEW IF EXISTS hits_kudu; +DROP TABLE IF EXISTS hits_kudu_raw; + +CREATE TABLE hits_kudu_raw ( + WatchID bigint, + EventTime timestamp, + JavaEnable smallint, + Title string, + GoodEvent smallint, + EventDate date, + CounterID int, + ClientIP int, + RegionID int, + UserID bigint, + CounterClass smallint, + OS smallint, + UserAgent smallint, + URL string, + Referer string, + IsRefresh smallint, + RefererCategoryID smallint, + RefererRegionID int, + URLCategoryID smallint, + URLRegionID int, + ResolutionWidth smallint, + ResolutionHeight smallint, + ResolutionDepth smallint, + FlashMajor smallint, + FlashMinor smallint, + FlashMinor2 string, + NetMajor smallint, + NetMinor smallint, + UserAgentMajor smallint, + UserAgentMinor string, + CookieEnable smallint, + JavascriptEnable smallint, + IsMobile smallint, + MobilePhone smallint, + MobilePhoneModel string, + Params string, + IPNetworkID int, + TraficSourceID smallint, + SearchEngineID smallint, + SearchPhrase string, + AdvEngineID smallint, + IsArtifical smallint, + WindowClientWidth smallint, + WindowClientHeight smallint, + ClientTimeZone smallint, + ClientEventTime timestamp, + SilverlightVersion1 smallint, + SilverlightVersion2 smallint, + SilverlightVersion3 int, + SilverlightVersion4 smallint, + PageCharset string, + CodeVersion int, + IsLink smallint, + IsDownload smallint, + IsNotBounce smallint, + FUniqID bigint, + OriginalURL string, + HID int, + IsOldCounter smallint, + IsEvent smallint, + IsParameter smallint, + DontCountHits smallint, + WithHash smallint, + HitColor string, + LocalEventTime timestamp, + Age smallint, + Sex smallint, + Income smallint, + Interests smallint, + Robotness smallint, + RemoteIP int, + WindowName int, + OpenerName int, + HistoryLength smallint, + BrowserLanguage string, + BrowserCountry string, + SocialNetwork string, + SocialAction string, + HTTPError smallint, + SendTiming int, + DNSTiming int, + ConnectTiming int, + ResponseStartTiming int, + ResponseEndTiming int, + FetchTiming int, + SocialSourceNetworkID smallint, + SocialSourcePage string, + ParamPrice bigint, + ParamOrderID string, + ParamCurrency string, + ParamCurrencyID smallint, + OpenstatServiceName string, + OpenstatCampaignID string, + OpenstatAdID string, + OpenstatSourceID string, + UTMSource string, + UTMMedium string, + UTMCampaign string, + UTMContent string, + UTMTerm string, + FromTag string, + HasGCLID smallint, + RefererHash bigint, + URLHash bigint, + CLID int, + PRIMARY KEY (WatchID, EventTime) +) +PARTITION BY HASH (WatchID) PARTITIONS 2 +STORED AS KUDU +TBLPROPERTIES ( + 'kudu.master_addresses' = 'kudu-master:7051', + 'kudu.num_tablet_replicas' = '1' +); + +-- Kudu requires primary-key columns to lead the physical schema. Expose a +-- projection in canonical ClickBench order so SELECT * remains comparable. +CREATE VIEW hits_kudu AS +SELECT + WatchID, JavaEnable, Title, GoodEvent, EventTime, EventDate, + CounterID, ClientIP, RegionID, UserID, CounterClass, OS, + UserAgent, URL, Referer, IsRefresh, RefererCategoryID, RefererRegionID, + URLCategoryID, URLRegionID, ResolutionWidth, ResolutionHeight, ResolutionDepth, FlashMajor, + FlashMinor, FlashMinor2, NetMajor, NetMinor, UserAgentMajor, UserAgentMinor, + CookieEnable, JavascriptEnable, IsMobile, MobilePhone, MobilePhoneModel, Params, + IPNetworkID, TraficSourceID, SearchEngineID, SearchPhrase, AdvEngineID, IsArtifical, + WindowClientWidth, WindowClientHeight, ClientTimeZone, ClientEventTime, SilverlightVersion1, SilverlightVersion2, + SilverlightVersion3, SilverlightVersion4, PageCharset, CodeVersion, IsLink, IsDownload, + IsNotBounce, FUniqID, OriginalURL, HID, IsOldCounter, IsEvent, + IsParameter, DontCountHits, WithHash, HitColor, LocalEventTime, Age, + Sex, Income, Interests, Robotness, RemoteIP, WindowName, + OpenerName, HistoryLength, BrowserLanguage, BrowserCountry, SocialNetwork, SocialAction, + HTTPError, SendTiming, DNSTiming, ConnectTiming, ResponseStartTiming, ResponseEndTiming, + FetchTiming, SocialSourceNetworkID, SocialSourcePage, ParamPrice, ParamOrderID, ParamCurrency, + ParamCurrencyID, OpenstatServiceName, OpenstatCampaignID, OpenstatAdID, OpenstatSourceID, UTMSource, + UTMMedium, UTMCampaign, UTMContent, UTMTerm, FromTag, HasGCLID, + RefererHash, URLHash, CLID +FROM hits_kudu_raw; diff --git a/impala-kudu/data-size b/impala-kudu/data-size new file mode 100755 index 0000000000..82486437d5 --- /dev/null +++ b/impala-kudu/data-size @@ -0,0 +1,17 @@ +#!/bin/bash +set -euo pipefail + +cd "$(dirname "$0")" + +# Count allocated blocks for every persistent service path required by this +# Kudu-backed configuration. Input Parquet under data/hits is excluded. +if docker info >/dev/null 2>&1; then + DOCKER=(docker) +else + DOCKER=(sudo docker) +fi + +read -r master_bytes _ < <("${DOCKER[@]}" exec kudu-master du -s -B1 /var/lib/kudu/master) +read -r tserver_bytes _ < <("${DOCKER[@]}" exec kudu-tserver du -s -B1 /var/lib/kudu/tserver) +read -r hms_bytes _ < <("${DOCKER[@]}" exec impala-hms du -s -B1 /var/lib/hive/metastore) +printf '%s\n' "$((master_bytes + tserver_bytes + hms_bytes))" diff --git a/impala-kudu/docker-compose.yml b/impala-kudu/docker-compose.yml new file mode 100644 index 0000000000..dd9f2f49cd --- /dev/null +++ b/impala-kudu/docker-compose.yml @@ -0,0 +1,189 @@ +# Apache Impala 4 quickstart cluster, in four containers on one +# user-defined bridge network. Mirrors the upstream +# docker/quickstart-compose layout from the Apache Impala repo, minus +# HDFS — the table data is bind-mounted into every container at +# /clickbench so Impala reads it through its local-filesystem driver +# rather than through HDFS or S3. +# +# Pinned to 4.5.0; 4.4.1 has the same jdo-api gap so the pin alone +# doesn't matter, but 4.5.0 is the newest tagged release. The +# missing jdo-api jar is supplied via a bind-mount under +# /opt/impala/lib/ on the two daemons (./install fetches it from +# Maven Central); without that, both impalad_coord_exec and catalogd +# crash at startup with NoClassDefFoundError: javax/jdo/JDOException → +# SIGSEGV in the C++ "Check failure" abort path, before /healthz ever +# opens. + +services: + kudu-master: + image: apache/kudu@sha256:c24e448f996326803277de7629704a673459d05c608d57b055144006cbfe4173 + container_name: kudu-master + command: ["master"] + environment: + KUDU_MASTERS: kudu-master:7051 + MASTER_ARGS: >- + --fs_wal_dir=/var/lib/kudu/master + --rpc_bind_addresses=0.0.0.0:7051 + --rpc_advertised_addresses=kudu-master:7051 + --webserver_port=8051 + --webserver_advertised_addresses=kudu-master:8051 + --webserver_doc_root=/opt/kudu/www + --stderrthreshold=0 + --unlock_unsafe_flags=true + volumes: + - ./data/kudu/master:/var/lib/kudu/master + networks: + - impala-net + hostname: kudu-master + + kudu-tserver: + image: apache/kudu@sha256:c24e448f996326803277de7629704a673459d05c608d57b055144006cbfe4173 + container_name: kudu-tserver + command: ["tserver"] + depends_on: + - kudu-master + environment: + KUDU_MASTERS: kudu-master:7051 + TSERVER_ARGS: >- + --fs_wal_dir=/var/lib/kudu/tserver + --rpc_bind_addresses=0.0.0.0:7050 + --rpc_advertised_addresses=kudu-tserver:7050 + --webserver_port=8050 + --webserver_advertised_addresses=kudu-tserver:8050 + --webserver_doc_root=/opt/kudu/www + --stderrthreshold=0 + --unlock_unsafe_flags=true + volumes: + - ./data/kudu/tserver:/var/lib/kudu/tserver + networks: + - impala-net + hostname: kudu-tserver + + hms-init: + image: apache/impala:4.5.0-impala_quickstart_hms + user: "0:0" + entrypoint: ["sh", "-c"] + command: ["mkdir -p /var/lib/hive/metastore && chown -R 1000:1000 /var/lib/hive/metastore"] + volumes: + - hms_warehouse:/var/lib/hive/metastore + + hms: + image: apache/impala:4.5.0-impala_quickstart_hms + container_name: impala-hms + command: ["hms"] + depends_on: + hms-init: + condition: service_completed_successfully + volumes: + - hms_warehouse:/var/lib/hive/metastore + - ./data:/clickbench:rw + # Register DbNotificationListener server-side so client RPCs to + # get_current_notificationEventId succeed; without it, DDL like + # CREATE DATABASE in ./load fails with + # TApplicationException: Internal error processing + # get_current_notificationEventId + - ./hms-hive-site.xml:/opt/hive/conf/hive-site.xml:ro + networks: + - impala-net + hostname: hms + + statestored: + image: apache/impala:4.5.0-statestored + container_name: impala-statestored + command: ["-redirect_stdout_stderr=false", "-logtostderr", "-v=1"] + networks: + - impala-net + hostname: statestored + + catalogd: + image: apache/impala:4.5.0-catalogd + container_name: impala-catalogd + depends_on: + - statestored + - hms + command: + - "-redirect_stdout_stderr=false" + - "-logtostderr" + - "-v=1" + # 0 disables HMS event polling. The entrypoint defaults to 1 + # (poll every second), which causes catalogd to subscribe to the + # metastore's notification log on startup via + # get_current_notificationEventId. The quickstart hms image + # ships without DbNotificationListener configured, so that RPC + # throws "Internal error processing get_current_notificationEventId" + # and the FATAL aborts catalogd before /healthz on impalad ever + # reaches OK. We don't need event-driven cache invalidation in a + # single-process benchmark — `invalidate metadata` after ./load + # is enough. + - "-hms_event_polling_interval_s=0" + - "-invalidate_tables_timeout_s=999999" + # Override entrypoint default. With it on, the daemon expands + # `hms` to its reverse-DNS FQDN. Even with the network renamed, + # docker still appends the network as a hostname suffix, and the + # resulting hostname can pick up illegal URI characters; keep + # the configured `hms` literal so the URI parses cleanly. + - "-use_resolved_hostname=false" + volumes: + - ./data:/clickbench:rw + - ./data/extra-lib/jdo-api-3.0.1.jar:/opt/impala/lib/jdo-api-3.0.1.jar:ro + - ./hive-site.xml:/opt/impala/conf/hive-site.xml:ro + networks: + - impala-net + hostname: catalogd + + impalad-1: + image: apache/impala:4.5.0-impalad_coord_exec + container_name: impala-impalad-1 + depends_on: + - statestored + - catalogd + command: + - "-v=1" + - "-redirect_stdout_stderr=false" + - "-logtostderr" + - "-kudu_master_hosts=" + - "-mt_dop_auto_fallback=true" + - "-mem_limit=14gb" + - "--default_query_options=default_file_format=parquet" + # See catalogd above — same FQDN-underscore problem applies here. + - "-use_resolved_hostname=false" + ports: + - "21000:21000" + - "21050:21050" + - "25000:25000" + volumes: + - ./data:/clickbench:rw + - ./data/extra-lib/jdo-api-3.0.1.jar:/opt/impala/lib/jdo-api-3.0.1.jar:ro + - ./hive-site.xml:/opt/impala/conf/hive-site.xml:ro + networks: + - impala-net + hostname: impalad-1 + + # impala-shell is not bundled in the coordinator image — it lives in + # the separate quickstart_client image. Run that as a long-lived + # sidecar (sleep infinity) and `docker exec` impala-shell into it for + # ./load and ./query. The image's default entrypoint runs a one-shot + # data-load script; override it so the container just stays alive. + client: + image: apache/impala:4.5.0-impala_quickstart_client + container_name: impala-client + depends_on: + - impalad-1 + entrypoint: ["sleep", "infinity"] + networks: + - impala-net + +networks: + impala-net: + # Pin the network name; without `name:` compose auto-prefixes the + # current project (= cwd basename), producing `impala_impala-net`. + # Catalogd then reverse-resolves the hms IP and gets back a + # hostname containing an underscore (`impala-hms.impala_impala-net`), + # which java.net.URI rejects with + # URISyntaxException: Illegal character in hostname at index 26 + # before HiveMetaStoreClient can connect. + name: impala-net + driver: bridge + +volumes: + hms_warehouse: diff --git a/impala-kudu/hive-site.xml b/impala-kudu/hive-site.xml new file mode 100644 index 0000000000..797c2bf577 --- /dev/null +++ b/impala-kudu/hive-site.xml @@ -0,0 +1,17 @@ + + + + + + hive.metastore.uris + thrift://hms:9083 + + diff --git a/impala-kudu/hms-hive-site.xml b/impala-kudu/hms-hive-site.xml new file mode 100644 index 0000000000..b90fe3721b --- /dev/null +++ b/impala-kudu/hms-hive-site.xml @@ -0,0 +1,55 @@ + + + + + + + javax.jdo.option.ConnectionURL + jdbc:derby:;databaseName=/var/lib/hive/metastore/metastore_db;create=true + + + + + hive.metastore.transactional.event.listeners + org.apache.hive.hcatalog.listener.DbNotificationListener + + + + + hive.metastore.event.db.notification.api.auth + false + + + + + hive.metastore.notifications.add.thrift.objects + true + + diff --git a/impala-kudu/install b/impala-kudu/install new file mode 100755 index 0000000000..51b59bd66a --- /dev/null +++ b/impala-kudu/install @@ -0,0 +1,47 @@ +#!/bin/bash +set -e + +# Install Docker only when absent. Do not replace a working Compose plugin: +# Docker's official docker-compose-plugin and Ubuntu's docker-compose-v2 +# own the same path and conflict if both are installed. +if ! command -v docker >/dev/null 2>&1; then + sudo apt-get update -y + sudo apt-get install -y docker.io +fi +if ! docker compose version >/dev/null 2>&1; then + sudo apt-get update -y + sudo apt-get install -y docker-compose-v2 +fi +if ! command -v curl >/dev/null 2>&1 || ! command -v wget >/dev/null 2>&1; then + sudo apt-get update -y + sudo apt-get install -y curl wget +fi + +# Pull all four service images up-front so the first ./start doesn't +# include image-pull wall-clock — it stays a pure container-start +# operation and ./check's readiness timeout is enough. +sudo docker compose -f docker-compose.yml pull + +# Catalogd and impalad both bind-mount this dir as /clickbench. Create +# it before the first ./start so the mount succeeds even when the +# compose stack is brought up before the first ./load. +mkdir -p data/hits data/kudu/master data/kudu/tserver + +# Apache Impala's published 4.4.x and 4.5.0 quickstart images ship the +# embedded HMS client library but NOT the jdo-api jar it loads through +# reflection, so impalad_coord_exec and catalogd both crash at startup +# with NoClassDefFoundError: javax.jdo.JDOException → SIGSEGV in the +# C++ "Check failure" abort path, before /healthz ever opens. +# Pull jdo-api from Maven Central into a host-side dir; the compose +# bind-mounts it under /opt/impala/lib/ in both daemon containers, +# which is already on their Java classpath. +JDO_API_VERSION=3.0.1 +mkdir -p data/extra-lib +if [ ! -f "data/extra-lib/jdo-api-${JDO_API_VERSION}.jar" ]; then + curl -fsSL -o "data/extra-lib/jdo-api-${JDO_API_VERSION}.jar" \ + "https://repo1.maven.org/maven2/javax/jdo/jdo-api/${JDO_API_VERSION}/jdo-api-${JDO_API_VERSION}.jar" +fi + +# The Impala containers run their daemons as uid 1000 ("impala"); make +# the bind-mount writable by that uid even when cloud-init runs as root. +sudo chown -R 1000:1000 data diff --git a/impala-kudu/load b/impala-kudu/load new file mode 100755 index 0000000000..1fa834f254 --- /dev/null +++ b/impala-kudu/load @@ -0,0 +1,27 @@ +#!/bin/bash +set -euo pipefail + +cd "$(dirname "$0")" + +if docker info >/dev/null 2>&1; then + DOCKER=(docker) +else + DOCKER=(sudo docker) +fi + +# Place the benchmark input inside the external Parquet table location. Kudu +# is loaded from this table, and the same table remains available for the +# post-benchmark result validator. +ln -f hits.parquet data/hits/hits.parquet +if [ "$(uname -s)" != "Darwin" ]; then + sudo chown -R 1000:1000 data +fi + +"${DOCKER[@]}" cp create.sql impala-client:/tmp/create.sql +"${DOCKER[@]}" exec -i impala-client impala-shell \ + -i impalad-1:21050 -B --quiet -f /tmp/create.sql < /dev/null + +./load_kudu.sh + +rm -f hits.parquet +sync diff --git a/impala-kudu/load_kudu.sh b/impala-kudu/load_kudu.sh new file mode 100755 index 0000000000..60b0791012 --- /dev/null +++ b/impala-kudu/load_kudu.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd "$(dirname "$0")" + +BUCKETS="${KUDU_LOAD_BUCKETS:-10}" +LOG_DIR="${KUDU_LOAD_LOG_DIR:-load-logs}" +mkdir -p "$LOG_DIR" + +if docker info >/dev/null 2>&1; then + DOCKER=(docker) +else + DOCKER=(sudo docker) +fi + +if ! [[ "$BUCKETS" =~ ^[1-9][0-9]*$ ]]; then + echo "KUDU_LOAD_BUCKETS must be a positive integer" >&2 + exit 2 +fi + +run_sql_file() { + local path="$1" + "${DOCKER[@]}" cp "$path" "impala-client:/tmp/$(basename "$path")" + "${DOCKER[@]}" exec -i impala-client impala-shell \ + -i impalad-1:21050 -B --quiet -f "/tmp/$(basename "$path")" < /dev/null +} + +query_scalar() { + local sql="$1" + "${DOCKER[@]}" exec -i impala-client impala-shell \ + -i impalad-1:21050 -d clickbench -B --quiet -q "$sql" < /dev/null +} + +# Clean both catalog metadata and any physical Kudu table left behind by a +# failed HMS registration. The latter is possible because Kudu CREATE and HMS +# registration are separate operations. +query_scalar 'DROP VIEW IF EXISTS hits_kudu;' >/dev/null 2>&1 || true +query_scalar 'DROP TABLE IF EXISTS hits_kudu;' >/dev/null 2>&1 || true +query_scalar 'DROP TABLE IF EXISTS hits_kudu_raw;' >/dev/null 2>&1 || true +"${DOCKER[@]}" exec kudu-master kudu table delete kudu-master:7051 \ + impala::clickbench.hits_kudu_raw \ + -nomodify_external_catalogs -reserve_seconds=0 >/dev/null 2>&1 || true + +# Retain the allocated-byte baseline after logical cleanup and before creating +# the measured table. A final-run bundle can therefore show whether it began +# from a newly initialized stack or from storage carrying prior table data. +read -r master_bytes _ < <("${DOCKER[@]}" exec kudu-master du -s -B1 /var/lib/kudu/master) +read -r tserver_bytes _ < <("${DOCKER[@]}" exec kudu-tserver du -s -B1 /var/lib/kudu/tserver) +read -r hms_bytes _ < <("${DOCKER[@]}" exec impala-hms du -s -B1 /var/lib/hive/metastore) +printf 'path\tallocated_bytes\nmaster\t%s\ntserver\t%s\nhms\t%s\ntotal\t%s\n' \ + "$master_bytes" "$tserver_bytes" "$hms_bytes" \ + "$((master_bytes + tserver_bytes + hms_bytes))" \ + | tee "$LOG_DIR/pre-load-storage.tsv" + +run_sql_file create_kudu.sql + +SOURCE_ROWS="$(query_scalar 'SELECT COUNT(*) FROM hits;')" +template="$(< upsert_batch_template.sql)" + +for ((bucket = 0; bucket < BUCKETS; bucket++)); do + sql="${template//__BUCKETS__/$BUCKETS}" + sql="${sql//__BUCKET__/$bucket}" + log="$LOG_DIR/bucket-$(printf '%02d' "$bucket").log" + { + echo "bucket=$bucket start=$(date -u +%Y-%m-%dT%H:%M:%SZ)" + printf '%s\n' "$sql" | "${DOCKER[@]}" exec -i impala-client impala-shell \ + -i impalad-1:21050 -B --quiet + echo "bucket=$bucket exit=0 end=$(date -u +%Y-%m-%dT%H:%M:%SZ)" + } 2>&1 | tee "$log" +done + +TARGET_ROWS="$(query_scalar 'SELECT COUNT(*) FROM hits_kudu_raw;')" +if [[ "$TARGET_ROWS" != "$SOURCE_ROWS" ]]; then + echo "row count mismatch: source=$SOURCE_ROWS target=$TARGET_ROWS" >&2 + exit 1 +fi + +query_scalar 'COMPUTE STATS hits_kudu_raw;' +printf 'SOURCE_ROWS=%s\nTARGET_ROWS=%s\n' "$SOURCE_ROWS" "$TARGET_ROWS" | tee "$LOG_DIR/row-counts.log" diff --git a/impala-kudu/queries.sql b/impala-kudu/queries.sql new file mode 100644 index 0000000000..0d4200011b --- /dev/null +++ b/impala-kudu/queries.sql @@ -0,0 +1,43 @@ +SELECT COUNT(*) FROM hits_kudu; +SELECT COUNT(*) FROM hits_kudu WHERE AdvEngineID <> 0; +SELECT SUM(AdvEngineID), COUNT(*), AVG(ResolutionWidth) FROM hits_kudu; +SELECT AVG(UserID) FROM hits_kudu; +SELECT COUNT(DISTINCT UserID) FROM hits_kudu; +SELECT COUNT(DISTINCT SearchPhrase) FROM hits_kudu; +SELECT MIN(EventDate), MAX(EventDate) FROM hits_kudu; +SELECT AdvEngineID, COUNT(*) FROM hits_kudu WHERE AdvEngineID <> 0 GROUP BY AdvEngineID ORDER BY COUNT(*) DESC; +SELECT RegionID, COUNT(DISTINCT UserID) AS u FROM hits_kudu GROUP BY RegionID ORDER BY u DESC LIMIT 10; +SELECT RegionID, SUM(AdvEngineID), COUNT(*) AS c, AVG(ResolutionWidth), COUNT(DISTINCT UserID) FROM hits_kudu GROUP BY RegionID ORDER BY c DESC LIMIT 10; +SELECT MobilePhoneModel, COUNT(DISTINCT UserID) AS u FROM hits_kudu WHERE MobilePhoneModel <> '' GROUP BY MobilePhoneModel ORDER BY u DESC LIMIT 10; +SELECT MobilePhone, MobilePhoneModel, COUNT(DISTINCT UserID) AS u FROM hits_kudu WHERE MobilePhoneModel <> '' GROUP BY MobilePhone, MobilePhoneModel ORDER BY u DESC LIMIT 10; +SELECT SearchPhrase, COUNT(*) AS c FROM hits_kudu WHERE SearchPhrase <> '' GROUP BY SearchPhrase ORDER BY c DESC LIMIT 10; +SELECT SearchPhrase, COUNT(DISTINCT UserID) AS u FROM hits_kudu WHERE SearchPhrase <> '' GROUP BY SearchPhrase ORDER BY u DESC LIMIT 10; +SELECT SearchEngineID, SearchPhrase, COUNT(*) AS c FROM hits_kudu WHERE SearchPhrase <> '' GROUP BY SearchEngineID, SearchPhrase ORDER BY c DESC LIMIT 10; +SELECT UserID, COUNT(*) FROM hits_kudu GROUP BY UserID ORDER BY COUNT(*) DESC LIMIT 10; +SELECT UserID, SearchPhrase, COUNT(*) FROM hits_kudu GROUP BY UserID, SearchPhrase ORDER BY COUNT(*) DESC LIMIT 10; +SELECT UserID, SearchPhrase, COUNT(*) FROM hits_kudu GROUP BY UserID, SearchPhrase LIMIT 10; +SELECT UserID, EXTRACT(MINUTE FROM EventTime) AS m, SearchPhrase, COUNT(*) FROM hits_kudu GROUP BY UserID, EXTRACT(MINUTE FROM EventTime), SearchPhrase ORDER BY COUNT(*) DESC LIMIT 10; +SELECT UserID FROM hits_kudu WHERE UserID = 435090932899640449; +SELECT COUNT(*) FROM hits_kudu WHERE URL LIKE '%google%'; +SELECT SearchPhrase, MIN(URL), COUNT(*) AS c FROM hits_kudu WHERE URL LIKE '%google%' AND SearchPhrase <> '' GROUP BY SearchPhrase ORDER BY c DESC LIMIT 10; +SELECT SearchPhrase, MIN(URL), MIN(Title), COUNT(*) AS c, COUNT(DISTINCT UserID) FROM hits_kudu WHERE Title LIKE '%Google%' AND URL NOT LIKE '%.google.%' AND SearchPhrase <> '' GROUP BY SearchPhrase ORDER BY c DESC LIMIT 10; +SELECT * FROM hits_kudu WHERE URL LIKE '%google%' ORDER BY EventTime LIMIT 10; +SELECT SearchPhrase FROM hits_kudu WHERE SearchPhrase <> '' ORDER BY EventTime LIMIT 10; +SELECT SearchPhrase FROM hits_kudu WHERE SearchPhrase <> '' ORDER BY SearchPhrase LIMIT 10; +SELECT SearchPhrase FROM hits_kudu WHERE SearchPhrase <> '' ORDER BY EventTime, SearchPhrase LIMIT 10; +SELECT CounterID, AVG(length(URL)) AS l, COUNT(*) AS c FROM hits_kudu WHERE URL <> '' GROUP BY CounterID HAVING COUNT(*) > 100000 ORDER BY l DESC LIMIT 25; +SELECT REGEXP_REPLACE(Referer, '^https?://(?:www\\.)?([^/]+)/.*$', '\\1') AS k, AVG(length(Referer)) AS l, COUNT(*) AS c, MIN(Referer) FROM hits_kudu WHERE Referer <> '' GROUP BY REGEXP_REPLACE(Referer, '^https?://(?:www\\.)?([^/]+)/.*$', '\\1') HAVING COUNT(*) > 100000 ORDER BY l DESC LIMIT 25; +SELECT SUM(ResolutionWidth), SUM(ResolutionWidth + 1), SUM(ResolutionWidth + 2), SUM(ResolutionWidth + 3), SUM(ResolutionWidth + 4), SUM(ResolutionWidth + 5), SUM(ResolutionWidth + 6), SUM(ResolutionWidth + 7), SUM(ResolutionWidth + 8), SUM(ResolutionWidth + 9), SUM(ResolutionWidth + 10), SUM(ResolutionWidth + 11), SUM(ResolutionWidth + 12), SUM(ResolutionWidth + 13), SUM(ResolutionWidth + 14), SUM(ResolutionWidth + 15), SUM(ResolutionWidth + 16), SUM(ResolutionWidth + 17), SUM(ResolutionWidth + 18), SUM(ResolutionWidth + 19), SUM(ResolutionWidth + 20), SUM(ResolutionWidth + 21), SUM(ResolutionWidth + 22), SUM(ResolutionWidth + 23), SUM(ResolutionWidth + 24), SUM(ResolutionWidth + 25), SUM(ResolutionWidth + 26), SUM(ResolutionWidth + 27), SUM(ResolutionWidth + 28), SUM(ResolutionWidth + 29), SUM(ResolutionWidth + 30), SUM(ResolutionWidth + 31), SUM(ResolutionWidth + 32), SUM(ResolutionWidth + 33), SUM(ResolutionWidth + 34), SUM(ResolutionWidth + 35), SUM(ResolutionWidth + 36), SUM(ResolutionWidth + 37), SUM(ResolutionWidth + 38), SUM(ResolutionWidth + 39), SUM(ResolutionWidth + 40), SUM(ResolutionWidth + 41), SUM(ResolutionWidth + 42), SUM(ResolutionWidth + 43), SUM(ResolutionWidth + 44), SUM(ResolutionWidth + 45), SUM(ResolutionWidth + 46), SUM(ResolutionWidth + 47), SUM(ResolutionWidth + 48), SUM(ResolutionWidth + 49), SUM(ResolutionWidth + 50), SUM(ResolutionWidth + 51), SUM(ResolutionWidth + 52), SUM(ResolutionWidth + 53), SUM(ResolutionWidth + 54), SUM(ResolutionWidth + 55), SUM(ResolutionWidth + 56), SUM(ResolutionWidth + 57), SUM(ResolutionWidth + 58), SUM(ResolutionWidth + 59), SUM(ResolutionWidth + 60), SUM(ResolutionWidth + 61), SUM(ResolutionWidth + 62), SUM(ResolutionWidth + 63), SUM(ResolutionWidth + 64), SUM(ResolutionWidth + 65), SUM(ResolutionWidth + 66), SUM(ResolutionWidth + 67), SUM(ResolutionWidth + 68), SUM(ResolutionWidth + 69), SUM(ResolutionWidth + 70), SUM(ResolutionWidth + 71), SUM(ResolutionWidth + 72), SUM(ResolutionWidth + 73), SUM(ResolutionWidth + 74), SUM(ResolutionWidth + 75), SUM(ResolutionWidth + 76), SUM(ResolutionWidth + 77), SUM(ResolutionWidth + 78), SUM(ResolutionWidth + 79), SUM(ResolutionWidth + 80), SUM(ResolutionWidth + 81), SUM(ResolutionWidth + 82), SUM(ResolutionWidth + 83), SUM(ResolutionWidth + 84), SUM(ResolutionWidth + 85), SUM(ResolutionWidth + 86), SUM(ResolutionWidth + 87), SUM(ResolutionWidth + 88), SUM(ResolutionWidth + 89) FROM hits_kudu; +SELECT SearchEngineID, ClientIP, COUNT(*) AS c, SUM(IsRefresh), AVG(ResolutionWidth) FROM hits_kudu WHERE SearchPhrase <> '' GROUP BY SearchEngineID, ClientIP ORDER BY c DESC LIMIT 10; +SELECT WatchID, ClientIP, COUNT(*) AS c, SUM(IsRefresh), AVG(ResolutionWidth) FROM hits_kudu WHERE SearchPhrase <> '' GROUP BY WatchID, ClientIP ORDER BY c DESC LIMIT 10; +SELECT WatchID, ClientIP, COUNT(*) AS c, SUM(IsRefresh), AVG(ResolutionWidth) FROM hits_kudu GROUP BY WatchID, ClientIP ORDER BY c DESC LIMIT 10; +SELECT URL, COUNT(*) AS c FROM hits_kudu GROUP BY URL ORDER BY c DESC LIMIT 10; +SELECT 1, URL, COUNT(*) AS c FROM hits_kudu GROUP BY 1, URL ORDER BY c DESC LIMIT 10; +SELECT ClientIP, ClientIP - 1, ClientIP - 2, ClientIP - 3, COUNT(*) AS c FROM hits_kudu GROUP BY ClientIP, ClientIP - 1, ClientIP - 2, ClientIP - 3 ORDER BY c DESC LIMIT 10; +SELECT URL, COUNT(*) AS PageViews FROM hits_kudu WHERE CounterID = 62 AND EventDate >= DATE '2013-07-01' AND EventDate <= DATE '2013-07-31' AND DontCountHits = 0 AND IsRefresh = 0 AND URL <> '' GROUP BY URL ORDER BY PageViews DESC LIMIT 10; +SELECT Title, COUNT(*) AS PageViews FROM hits_kudu WHERE CounterID = 62 AND EventDate >= DATE '2013-07-01' AND EventDate <= DATE '2013-07-31' AND DontCountHits = 0 AND IsRefresh = 0 AND Title <> '' GROUP BY Title ORDER BY PageViews DESC LIMIT 10; +SELECT URL, COUNT(*) AS PageViews FROM hits_kudu WHERE CounterID = 62 AND EventDate >= DATE '2013-07-01' AND EventDate <= DATE '2013-07-31' AND IsRefresh = 0 AND IsLink <> 0 AND IsDownload = 0 GROUP BY URL ORDER BY PageViews DESC LIMIT 10 OFFSET 1000; +SELECT TraficSourceID, SearchEngineID, AdvEngineID, CASE WHEN (SearchEngineID = 0 AND AdvEngineID = 0) THEN Referer ELSE '' END AS Src, URL AS Dst, COUNT(*) AS PageViews FROM hits_kudu WHERE CounterID = 62 AND EventDate >= DATE '2013-07-01' AND EventDate <= DATE '2013-07-31' AND IsRefresh = 0 GROUP BY TraficSourceID, SearchEngineID, AdvEngineID, CASE WHEN (SearchEngineID = 0 AND AdvEngineID = 0) THEN Referer ELSE '' END, URL ORDER BY PageViews DESC LIMIT 10 OFFSET 1000; +SELECT URLHash, EventDate, COUNT(*) AS PageViews FROM hits_kudu WHERE CounterID = 62 AND EventDate >= DATE '2013-07-01' AND EventDate <= DATE '2013-07-31' AND IsRefresh = 0 AND TraficSourceID IN (-1, 6) AND RefererHash = 3594120000172545465 GROUP BY URLHash, EventDate ORDER BY PageViews DESC LIMIT 10 OFFSET 100; +SELECT WindowClientWidth, WindowClientHeight, COUNT(*) AS PageViews FROM hits_kudu WHERE CounterID = 62 AND EventDate >= DATE '2013-07-01' AND EventDate <= DATE '2013-07-31' AND IsRefresh = 0 AND DontCountHits = 0 AND URLHash = 2868770270353813622 GROUP BY WindowClientWidth, WindowClientHeight ORDER BY PageViews DESC LIMIT 10 OFFSET 10000; +SELECT DATE_TRUNC('MINUTE', EventTime) AS M, COUNT(*) AS PageViews FROM hits_kudu WHERE CounterID = 62 AND EventDate >= DATE '2013-07-14' AND EventDate <= DATE '2013-07-15' AND IsRefresh = 0 AND DontCountHits = 0 GROUP BY DATE_TRUNC('MINUTE', EventTime) ORDER BY DATE_TRUNC('MINUTE', EventTime) LIMIT 10 OFFSET 1000; diff --git a/impala-kudu/query b/impala-kudu/query new file mode 100755 index 0000000000..434500fe32 --- /dev/null +++ b/impala-kudu/query @@ -0,0 +1,29 @@ +#!/bin/bash +set -euo pipefail + +# ClickBench's shared driver supplies SQL on stdin. Keeping a single argument +# supported makes the script convenient for direct probes and preserves the +# local prototype interface. +if [ -n "${1:-}" ]; then + query_sql="$1" +else + query_sql=$(cat) +fi +[ -n "$query_sql" ] || { + echo "query SQL is required on stdin or as one argument" >&2 + exit 2 +} + +if docker info >/dev/null 2>&1; then + DOCKER=(docker) +else + DOCKER=(sudo docker) +fi + +start=$(date +%s.%N) +"${DOCKER[@]}" exec -i impala-client impala-shell \ + -i impalad-1:21050 -d clickbench -B --quiet \ + -q "$query_sql" < /dev/null +end=$(date +%s.%N) + +awk -v s="$start" -v e="$end" 'BEGIN { printf "%.3f\n", e - s }' >&2 diff --git a/impala-kudu/results/20260927/c6a.2xlarge.json b/impala-kudu/results/20260927/c6a.2xlarge.json new file mode 100644 index 0000000000..b13cc94d8e --- /dev/null +++ b/impala-kudu/results/20260927/c6a.2xlarge.json @@ -0,0 +1,236 @@ +{ + "system": "Impala (Kudu, single)", + "proprietary": "no", + "hardware": "cpu", + "tuned": "no", + "tags": [ + "C++", + "MPP", + "column-oriented", + "no-cold" + ], + "date": "2026-09-27", + "machine": "c6a.2xlarge", + "cluster_size": 1, + "load_time": 3800.123, + "data_size": 47958401024, + "concurrent_qps": 0.1, + "concurrent_error_ratio": 0.016, + "result": [ + [ + 1.439, + 0.436, + 0.426 + ], + [ + 2.963, + 1.45, + 1.438 + ], + [ + 4.25, + 2.348, + 2.345 + ], + [ + 9.336, + 2.543, + 2.642 + ], + [ + 16.939, + 13.78, + 13.786 + ], + [ + 15.786, + 6.654, + 7.06 + ], + [ + 4.005, + 2.033, + 1.932 + ], + [ + 2.825, + 1.445, + 1.439 + ], + [ + 20.753, + 15.788, + 15.777 + ], + [ + 25.293, + 18.793, + 18.28 + ], + [ + 13.238, + 5.64, + 5.759 + ], + [ + 15.42, + 6.164, + 6.36 + ], + [ + 15.243, + 6.258, + 6.351 + ], + [ + 28.795, + 12.282, + 12.283 + ], + [ + 17.864, + 7.162, + 7.157 + ], + [ + 17.752, + 14.273, + 14.274 + ], + [ + 28.263, + 17.783, + 17.776 + ], + [ + 26.868, + 17.916, + 17.786 + ], + [ + 42.807, + 32.3, + 31.298 + ], + [ + 9.56, + 0.624, + 0.621 + ], + [ + 94.057, + 17.789, + 18.275 + ], + [ + 104.894, + 22.814, + 34.8 + ], + [ + 200.898, + 175.507, + 168.5 + ], + [ + 337.031, + 321.63, + 320.641 + ], + [ + 12.64, + 8.153, + 7.353 + ], + [ + 8.112, + 4.646, + 4.739 + ], + [ + 13.226, + 8.06, + 7.547 + ], + [ + 73.821, + 16.785, + 16.77 + ], + [ + 88.739, + 86.374, + 86.376 + ], + [ + 15.289, + 14.283, + 14.29 + ], + [ + 15.752, + 8.957, + 8.459 + ], + [ + 18.748, + 10.767, + 11.263 + ], + [ + 37.27, + 35.797, + 36.81 + ], + [ + 77.835, + 57.834, + 59.338 + ], + [ + 78.741, + 58.852, + 57.83 + ], + [ + 15.16, + 14.278, + 14.774 + ], + [ + 74.817, + 2.138, + 2.13 + ], + [ + 80.777, + 2.039, + 2.031 + ], + [ + 74.753, + 2.146, + 2.136 + ], + [ + 128.83, + 105.403, + 103.419 + ], + [ + 15.18, + 1.224, + 1.221 + ], + [ + 11.129, + 1.225, + 1.221 + ], + [ + 7.339, + 1.128, + 1.127 + ] + ] +} diff --git a/impala-kudu/start b/impala-kudu/start new file mode 100755 index 0000000000..99fec99a23 --- /dev/null +++ b/impala-kudu/start @@ -0,0 +1,59 @@ +#!/bin/bash +set -euo pipefail + +cd "$(dirname "$0")" + +if docker info >/dev/null 2>&1; then + DOCKER=(docker) +else + DOCKER=(sudo docker) +fi + +# Start storage and metadata dependencies first. Compose's depends_on only +# waits for process creation, not for HMS to accept Thrift connections. +"${DOCKER[@]}" compose -f docker-compose.yml up -d hms statestored kudu-master kudu-tserver + +# The quickstart HMS image does not create the local warehouse path with +# writable Hive ownership. Without this, Kudu CREATE TABLE fails during HMS +# registration even when Kudu itself is healthy. +"${DOCKER[@]}" exec -u root impala-hms sh -c \ + 'mkdir -p /user/hive/warehouse && chown -R hive:hive /user' + +prerequisites_ready=0 +for _ in $(seq 1 60); do + hms_ready=0 + kudu_ready=0 + "${DOCKER[@]}" exec impala-hms bash -c \ + 'exec 3<>/dev/tcp/127.0.0.1/9083' >/dev/null 2>&1 && hms_ready=1 + "${DOCKER[@]}" exec kudu-master \ + kudu cluster ksck kudu-master:7051 >/dev/null 2>&1 && kudu_ready=1 + if [ "$hms_ready" -eq 1 ] && [ "$kudu_ready" -eq 1 ]; then + prerequisites_ready=1 + break + fi + sleep 2 +done + +if [ "$prerequisites_ready" -ne 1 ]; then + "${DOCKER[@]}" ps >&2 + "${DOCKER[@]}" logs --tail 100 impala-hms >&2 || true + "${DOCKER[@]}" logs --tail 100 kudu-master >&2 || true + "${DOCKER[@]}" logs --tail 100 kudu-tserver >&2 || true + echo "HMS or Kudu did not become healthy within 120 seconds" >&2 + exit 1 +fi + +"${DOCKER[@]}" compose -f docker-compose.yml up -d catalogd impalad-1 client + +for _ in $(seq 1 90); do + if curl -fsS http://localhost:25000/healthz 2>/dev/null | grep -q '^OK$'; then + exit 0 + fi + sleep 2 +done + +"${DOCKER[@]}" ps >&2 +"${DOCKER[@]}" logs --tail 100 impala-catalogd >&2 || true +"${DOCKER[@]}" logs --tail 100 impala-impalad-1 >&2 || true +echo "Impala did not become healthy within 180 seconds" >&2 +exit 1 diff --git a/impala-kudu/stop b/impala-kudu/stop new file mode 100755 index 0000000000..e0bb0e13e3 --- /dev/null +++ b/impala-kudu/stop @@ -0,0 +1,14 @@ +#!/bin/bash +set -euo pipefail + +cd "$(dirname "$0")" + +if docker info >/dev/null 2>&1; then + DOCKER=(docker) +else + DOCKER=(sudo docker) +fi + +# Preserve persistent Kudu/HMS data so the benchmark driver can restart the +# services without changing the measured dataset. +"${DOCKER[@]}" compose -f docker-compose.yml stop diff --git a/impala-kudu/template.json b/impala-kudu/template.json new file mode 100644 index 0000000000..040ef29cdb --- /dev/null +++ b/impala-kudu/template.json @@ -0,0 +1,12 @@ +{ + "system": "Impala (Kudu, single)", + "proprietary": "no", + "hardware": "cpu", + "tuned": "no", + "tags": [ + "C++", + "MPP", + "column-oriented", + "no-cold" + ] +} diff --git a/impala-kudu/upsert_batch_template.sql b/impala-kudu/upsert_batch_template.sql new file mode 100644 index 0000000000..4edf19720f --- /dev/null +++ b/impala-kudu/upsert_batch_template.sql @@ -0,0 +1,26 @@ +-- Load one deterministic hash bucket. The double modulo maps signed +-- fnv_hash() output into [0, bucket_count), avoiding the -9..-1 trap. +USE clickbench; + +UPSERT INTO hits_kudu_raw +SELECT + WatchID, EventTime, JavaEnable, Title, GoodEvent, EventDate, + CounterID, ClientIP, RegionID, UserID, CounterClass, OS, + UserAgent, URL, Referer, IsRefresh, RefererCategoryID, RefererRegionID, + URLCategoryID, URLRegionID, ResolutionWidth, ResolutionHeight, ResolutionDepth, FlashMajor, + FlashMinor, FlashMinor2, NetMajor, NetMinor, UserAgentMajor, UserAgentMinor, + CookieEnable, JavascriptEnable, IsMobile, MobilePhone, MobilePhoneModel, Params, + IPNetworkID, TraficSourceID, SearchEngineID, SearchPhrase, AdvEngineID, IsArtifical, + WindowClientWidth, WindowClientHeight, ClientTimeZone, ClientEventTime, SilverlightVersion1, SilverlightVersion2, + SilverlightVersion3, SilverlightVersion4, PageCharset, CodeVersion, IsLink, IsDownload, + IsNotBounce, FUniqID, OriginalURL, HID, IsOldCounter, IsEvent, + IsParameter, DontCountHits, WithHash, HitColor, LocalEventTime, Age, + Sex, Income, Interests, Robotness, RemoteIP, WindowName, + OpenerName, HistoryLength, BrowserLanguage, BrowserCountry, SocialNetwork, SocialAction, + HTTPError, SendTiming, DNSTiming, ConnectTiming, ResponseStartTiming, ResponseEndTiming, + FetchTiming, SocialSourceNetworkID, SocialSourcePage, ParamPrice, ParamOrderID, ParamCurrency, + ParamCurrencyID, OpenstatServiceName, OpenstatCampaignID, OpenstatAdID, OpenstatSourceID, UTMSource, + UTMMedium, UTMCampaign, UTMContent, UTMTerm, FromTag, HasGCLID, + RefererHash, URLHash, CLID +FROM hits +WHERE ((fnv_hash(WatchID) % __BUCKETS__) + __BUCKETS__) % __BUCKETS__ = __BUCKET__; From a6d958e77b7f44b319ce6085bc624d9042e1a451 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 28 Sep 2026 01:04:12 +0000 Subject: [PATCH 2/2] Add benchmark results for impala-kudu (c6a.4xlarge) --- impala-kudu/results/20260927/c6a.2xlarge.json | 236 ------------------ impala-kudu/results/20260928/c6a.4xlarge.json | 60 +++++ 2 files changed, 60 insertions(+), 236 deletions(-) delete mode 100644 impala-kudu/results/20260927/c6a.2xlarge.json create mode 100644 impala-kudu/results/20260928/c6a.4xlarge.json diff --git a/impala-kudu/results/20260927/c6a.2xlarge.json b/impala-kudu/results/20260927/c6a.2xlarge.json deleted file mode 100644 index b13cc94d8e..0000000000 --- a/impala-kudu/results/20260927/c6a.2xlarge.json +++ /dev/null @@ -1,236 +0,0 @@ -{ - "system": "Impala (Kudu, single)", - "proprietary": "no", - "hardware": "cpu", - "tuned": "no", - "tags": [ - "C++", - "MPP", - "column-oriented", - "no-cold" - ], - "date": "2026-09-27", - "machine": "c6a.2xlarge", - "cluster_size": 1, - "load_time": 3800.123, - "data_size": 47958401024, - "concurrent_qps": 0.1, - "concurrent_error_ratio": 0.016, - "result": [ - [ - 1.439, - 0.436, - 0.426 - ], - [ - 2.963, - 1.45, - 1.438 - ], - [ - 4.25, - 2.348, - 2.345 - ], - [ - 9.336, - 2.543, - 2.642 - ], - [ - 16.939, - 13.78, - 13.786 - ], - [ - 15.786, - 6.654, - 7.06 - ], - [ - 4.005, - 2.033, - 1.932 - ], - [ - 2.825, - 1.445, - 1.439 - ], - [ - 20.753, - 15.788, - 15.777 - ], - [ - 25.293, - 18.793, - 18.28 - ], - [ - 13.238, - 5.64, - 5.759 - ], - [ - 15.42, - 6.164, - 6.36 - ], - [ - 15.243, - 6.258, - 6.351 - ], - [ - 28.795, - 12.282, - 12.283 - ], - [ - 17.864, - 7.162, - 7.157 - ], - [ - 17.752, - 14.273, - 14.274 - ], - [ - 28.263, - 17.783, - 17.776 - ], - [ - 26.868, - 17.916, - 17.786 - ], - [ - 42.807, - 32.3, - 31.298 - ], - [ - 9.56, - 0.624, - 0.621 - ], - [ - 94.057, - 17.789, - 18.275 - ], - [ - 104.894, - 22.814, - 34.8 - ], - [ - 200.898, - 175.507, - 168.5 - ], - [ - 337.031, - 321.63, - 320.641 - ], - [ - 12.64, - 8.153, - 7.353 - ], - [ - 8.112, - 4.646, - 4.739 - ], - [ - 13.226, - 8.06, - 7.547 - ], - [ - 73.821, - 16.785, - 16.77 - ], - [ - 88.739, - 86.374, - 86.376 - ], - [ - 15.289, - 14.283, - 14.29 - ], - [ - 15.752, - 8.957, - 8.459 - ], - [ - 18.748, - 10.767, - 11.263 - ], - [ - 37.27, - 35.797, - 36.81 - ], - [ - 77.835, - 57.834, - 59.338 - ], - [ - 78.741, - 58.852, - 57.83 - ], - [ - 15.16, - 14.278, - 14.774 - ], - [ - 74.817, - 2.138, - 2.13 - ], - [ - 80.777, - 2.039, - 2.031 - ], - [ - 74.753, - 2.146, - 2.136 - ], - [ - 128.83, - 105.403, - 103.419 - ], - [ - 15.18, - 1.224, - 1.221 - ], - [ - 11.129, - 1.225, - 1.221 - ], - [ - 7.339, - 1.128, - 1.127 - ] - ] -} diff --git a/impala-kudu/results/20260928/c6a.4xlarge.json b/impala-kudu/results/20260928/c6a.4xlarge.json new file mode 100644 index 0000000000..d4091c179b --- /dev/null +++ b/impala-kudu/results/20260928/c6a.4xlarge.json @@ -0,0 +1,60 @@ +{ + "system": "Impala (Kudu, single)", + "date": "2026-09-28", + "machine": "c6a.4xlarge", + "cluster_size": 1, + "proprietary": "no", + "hardware": "cpu", + "tuned": "no", + "tags": ["C++","MPP","column-oriented","no-cold"], + "load_time": 2817, + "data_size": 48030982144, + "concurrent_qps": 0.163, + "concurrent_error_ratio": 0.03, + "result": [ + [1.228, 0.424, 0.416], + [2.512, 1.332, 1.327], + [3.576, 2.032, 2.032], + [4.973, 2.434, 2.329], + [13.212, 12.763, 12.769], + [9.605, 6.04, 6.045], + [3.672, 1.725, 1.827], + [2.579, 1.329, 1.428], + [15.731, 15.271, 15.268], + [17.718, 17.789, 17.777], + [8.829, 4.959, 4.947], + [9.899, 5.443, 5.443], + [9.305, 5.743, 5.749], + [16.727, 11.776, 11.277], + [11.217, 6.438, 6.545], + [14.203, 13.761, 13.758], + [17.75, 16.779, 16.765], + [16.917, 16.456, 16.615], + [28.257, 26.783, 26.784], + [3.902, 0.611, 0.614], + [48.259, 16.759, 17.272], + [54.281, 20.28, 20.272], + [104.848, 36.823, 36.8], + [300.056, 285.635, 287.626], + [12.72, 6.839, 6.938], + [7.943, 4.332, 4.332], + [12.726, 7.141, 7.033], + [36.725, 15.262, 15.26], + [84.775, 84.368, 84.371], + [15.256, 14.266, 14.766], + [15.203, 8.151, 7.942], + [18.23, 9.753, 9.951], + [35.764, 34.793, 35.297], + [40.272, 19.767, 19.766], + [40.18, 19.764, 19.765], + [14.205, 13.759, 13.764], + [37.293, 2.126, 2.128], + [40.189, 2.026, 2.024], + [37.188, 2.125, 2.125], + [64.779, 3.128, 3.233], + [8.546, 1.221, 1.231], + [9.344, 1.224, 1.223], + [7.897, 1.111, 1.116] +] + } + \ No newline at end of file