Skip to content

QuestDB Platform

QuestDB is a high-performance open-source time-series database optimized for fast ingestion and SQL queries. It supports the PostgreSQL wire protocol (port 8812) for query execution and a REST API (port 9000) for efficient bulk data import. BenchBox connects via psycopg 3 over the PG wire protocol and uses the REST API /imp endpoint for high-throughput CSV ingestion. SQL translation targets the postgres dialect via SQLGlot.

QuestDB is designed for time-series and event-driven workloads, with features like designated timestamps, automatic partitioning by time, and the SYMBOL type for low-cardinality string columns. It delivers competitive ingestion throughput against InfluxDB, TimescaleDB, and ClickHouse on time-series workloads.

Features

  • PostgreSQL wire protocol - Standard PG wire protocol via psycopg 3 (port 8812)
  • REST API CSV import - High-throughput bulk data loading via /imp endpoint (port 9000)
  • Full TPC-H support - All 22 queries with row count validation
  • PostgreSQL dialect - SQL translation via SQLGlot postgres dialect
  • Time-series optimized - Designated timestamps, partitioning by time, SYMBOL type
  • Autocommit mode - PG wire protocol operates in autocommit mode (required by QuestDB)
  • TPC file normalization - Automatic handling of pipe-delimited .tbl files with trailing delimiter removal
  • Chunked parquet loading - Large parquet inputs are streamed through /imp as CSV chunks, with parquet_chunk_rows controlling the row budget per request
  • TLS support - Optional HTTPS for REST API endpoints via the adapter use_tls parameter

Quick Start

The commands install the QuestDB extra (which provides the psycopg 3 driver), configure the connection (QuestDB must be running), and run the TPC-H benchmark.

Terminal window
uv add benchbox --extra questdb
export QUESTDB_HOST=localhost
benchbox run --platform questdb --benchmark tpch --scale 0.01

Docker Quick Start

The commands start QuestDB with Docker (all ports exposed), verify PG wire protocol connectivity, verify the REST API, and run the benchmark.

Terminal window
docker run -p 9000:9000 -p 8812:8812 -p 9009:9009 -p 9003:9003 \
questdb/questdb:latest
psql -h 127.0.0.1 -p 8812 -U admin -d qdb -c "SELECT 1"
curl http://localhost:9000/exec?query=SELECT%201
benchbox run --platform questdb --benchmark tpch --scale 0.01

QuestDB Ports

PortProtocolPurpose
9000HTTPREST API for CSV import (/imp) and queries (/exec)
8812TCPPostgreSQL wire protocol for SQL queries
9009TCPInfluxDB Line Protocol (ILP) for high-throughput ingestion
9003HTTPHealth check and metrics endpoint

Configuration Options

CLI options are passed as --platform-option KEY=VALUE.

OptionCLIEnvironment VariableDefaultDescription
hostyesQUESTDB_HOSTlocalhostQuestDB server hostname
pg_portyesQUESTDB_PG_PORT8812PostgreSQL wire protocol port
http_portyesQUESTDB_HTTP_PORT9000REST API HTTP port
ilp_portyes-9009InfluxDB Line Protocol port
usernameyes-adminQuestDB username
passwordyes-questQuestDB password
databaseyes-qdbQuestDB database name
loading_methodyes-restLoading method: rest (CSV import) or ilp
use_tlsno-falseUse HTTPS for REST API endpoints
connect_timeoutno-10Connection timeout in seconds
parquet_chunk_rowsno-200000Rows per parquet batch when streaming wide tables through /imp

Options marked “no” are adapter constructor parameters; the benchbox run CLI does not accept them.

Data Loading

BenchBox loads data into QuestDB using the REST API /imp endpoint for bulk CSV import. For parquet inputs, the adapter converts each pyarrow batch into CSV and streams it through /imp; there is no COPY FROM STDIN fallback in the current adapter.

Loading Process

  1. Schema creation - Tables are created via PG wire protocol with QuestDB-compatible DDL
  2. Foreign key removal - FK constraints are stripped (QuestDB does not support foreign keys)
  3. DROP TABLE adaptation - IF EXISTS is added to DROP TABLE statements for idempotent schema creation
  4. TPC file normalization - Pipe-delimited .tbl files are streamed through a normalizer that removes trailing delimiters
  5. REST API import - CSV data is uploaded to the /imp endpoint with table name, delimiter, durability settings, and forceHeader=true for chunked parquet uploads
  6. Chunk sizing for wide parquet tables - the adapter parquet_chunk_rows parameter lowers the number of rows per /imp request when a denormalized parquet file would otherwise exceed QuestDB’s HTTP receive budget

REST API Import (/imp)

The primary data loading mechanism uses QuestDB’s REST API:

  • Endpoint: http://<host>:<http_port>/imp
  • Method: HTTP POST with multipart file upload
  • Parameters: name (table), delimiter, overwrite, durable and forceHeader=true for chunked parquet uploads
  • Format: CSV with configurable delimiter (auto-detected for TPC .tbl files)
  • Throughput: Significantly higher than row-by-row INSERT for bulk datasets
  • Wide parquet tuning: lower parquet_chunk_rows to 50000 or less when a very wide table would make each CSV chunk too large for a single HTTP request

InfluxDB Line Protocol (ILP)

QuestDB also supports the InfluxDB Line Protocol on port 9009 for high-throughput time-series ingestion. While BenchBox uses the REST API for benchmark data loading, ILP is available for custom ingestion workflows:

  • Port: 9009 (TCP)
  • Protocol: InfluxDB Line Protocol
  • Throughput: Millions of rows per second for time-series data
  • Use case: Real-time streaming ingestion

Usage Examples

Basic Benchmarks

The commands run TPC-H at scale factor 0.01 (a quick test), at scale factor 1, and for specific queries only.

Terminal window
benchbox run --platform questdb --benchmark tpch --scale 0.01
benchbox run --platform questdb --benchmark tpch --scale 1.0
benchbox run --platform questdb --benchmark tpch --queries Q1,Q6,Q17

Environment Variable Configuration

Terminal window
export QUESTDB_HOST=questdb.example.com
export QUESTDB_PG_PORT=8812
export QUESTDB_HTTP_PORT=9000
benchbox run --platform questdb --benchmark tpch --scale 1.0

CLI Option Configuration

Terminal window
benchbox run --platform questdb --benchmark tpch --scale 1.0 \
--platform-option host=questdb.example.com \
--platform-option pg_port=8812 \
--platform-option http_port=9000 \
--platform-option username=admin \
--platform-option password=quest

Line Protocol Loading

Terminal window
benchbox run --platform questdb --benchmark tpch --scale 1.0 \
--platform-option host=questdb.example.com \
--platform-option loading_method=ilp

Dry Run (Preview)

This previews the execution plan without running it.

Terminal window
benchbox run --platform questdb --benchmark tpch --scale 1.0 --dry-run ./preview

Architecture

Adapter Structure

The QuestDB adapter is a single-module adapter:

ModuleClassResponsibility
questdb.pyQuestDBAdapterConnection, schema, loading, query execution

Connection Model

BenchBox CLI
|
v
QuestDBAdapter
|
+-- PG Wire Protocol (psycopg 3) --> QuestDB (port 8812)
| - Schema DDL (CREATE TABLE, DROP TABLE)
| - Query execution (SELECT, EXPLAIN)
| - Autocommit mode required
|
+-- REST API (requests) --> QuestDB (port 9000)
- /imp endpoint for bulk CSV import
- Chunked parquet → CSV uploads via /imp
- /exec endpoint for row count verification
- Optional TLS (HTTPS) support

Platform Information

At runtime, BenchBox captures platform metadata:

{
"platform_type": "questdb",
"platform_name": "QuestDB",
"host": "localhost",
"pg_port": 8812,
"http_port": 9000,
"dialect": "postgres",
"configuration": {
"database": "qdb"
},
"version": "7.x.x"
}

Single Database Model

QuestDB uses a single database per instance. The adapter sets skip_database_management = True because:

  • There is no CREATE DATABASE or DROP DATABASE support
  • The database parameter (default: qdb) identifies the instance, not a user-created database
  • check_database_exists() tests connectivity rather than database presence

Time-Series Considerations

Designated Timestamps

QuestDB tables can have a designated timestamp column that enables time-based partitioning and optimized time-range queries:

CREATE TABLE sensor_data (
timestamp TIMESTAMP,
device_id SYMBOL,
temperature DOUBLE,
humidity DOUBLE
) TIMESTAMP(timestamp) PARTITION BY DAY;

While TPC-H tables do not use designated timestamps, the adapter supports them for custom benchmarks and time-series workloads.

Partitioning

QuestDB supports automatic time-based partitioning:

  • PARTITION BY NONE - No partitioning (default)
  • PARTITION BY HOUR - Hourly partitions
  • PARTITION BY DAY - Daily partitions
  • PARTITION BY MONTH - Monthly partitions
  • PARTITION BY YEAR - Yearly partitions

Partitioning is specified at table creation time and cannot be changed after the fact.

SYMBOL Type

QuestDB’s SYMBOL type is optimized for low-cardinality string columns (e.g., status codes, country codes, device IDs). SYMBOLs are stored as integers internally with a symbol table for deduplication, providing significant storage and query performance benefits over VARCHAR for repeated string values.

Constraints

QuestDB has a minimal constraint model:

  • No foreign keys - The adapter automatically strips FK constraints from DDL
  • No traditional primary keys - Uniqueness is not enforced at the database level
  • No ALTER TABLE ADD CONSTRAINT - Constraints must be defined at table creation time

Troubleshooting

Connection Refused

Error: Failed to connect to QuestDB

Solutions:

  1. Verify QuestDB is running and accessible on the configured host
  2. Check that port 8812 (PG wire protocol) is open and not blocked by a firewall
  3. For Docker deployments, ensure port mapping is correct (-p 8812:8812)
  4. Test connectivity directly: psql -h <host> -p 8812 -U admin -d qdb -c "SELECT 1"

Missing psycopg Dependency

Error: Missing dependencies for questdb platform: psycopg

Solutions:

  1. Install the QuestDB extra: uv add benchbox --extra questdb
  2. Or install the driver directly: uv add "psycopg[binary]"

REST API Import Failures

Error: REST API import failed

Solutions:

  1. Verify the REST API is accessible on the HTTP port (default: 9000)
  2. Check that requests is installed: uv add requests
  3. Test the REST API directly: curl http://<host>:9000/exec?query=SELECT%201
  4. Lower the adapter parquet_chunk_rows parameter for very wide parquet tables
  5. If using TLS, ensure the adapter use_tls parameter is set and certificates are valid

Schema Creation Failures

Error: critical CREATE TABLE statement(s) failed

Solutions:

  1. Check QuestDB server logs for detailed error messages
  2. Verify the QuestDB user has sufficient privileges
  3. Ensure sufficient disk space on the QuestDB server
  4. Review the translated DDL for QuestDB-incompatible syntax (e.g., unsupported column types)

Autocommit Mode Errors

QuestDB requires autocommit mode on the PG wire protocol. The adapter sets conn.autocommit = True automatically. If you encounter transaction-related errors:

  1. Ensure you are not wrapping queries in explicit BEGIN/COMMIT blocks
  2. QuestDB does not support multi-statement transactions via PG wire protocol

Slow Data Loading

Solutions:

  1. Ensure the REST API port (9000) is accessible for bulk CSV import
  2. If falling back to COPY, check network latency between BenchBox host and QuestDB
  3. For large datasets, consider increasing the QuestDB server’s memory allocation
  4. Check disk I/O on the QuestDB server during loading

See Also