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.
- PostgreSQL wire protocol - Standard PG wire protocol via psycopg 3 (port 8812)
- REST API CSV import - High-throughput bulk data loading via
/impendpoint (port 9000) - Full TPC-H support - All 22 queries with row count validation
- PostgreSQL dialect - SQL translation via SQLGlot
postgresdialect - 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
.tblfiles with trailing delimiter removal - Chunked parquet loading - Large parquet inputs are streamed through
/impas CSV chunks, withparquet_chunk_rowscontrolling the row budget per request - TLS support - Optional HTTPS for REST API endpoints via the adapter
use_tlsparameter
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.
uv add benchbox --extra questdb
export QUESTDB_HOST=localhost
benchbox run --platform questdb --benchmark tpch --scale 0.01The commands start QuestDB with Docker (all ports exposed), verify PG wire protocol connectivity, verify the REST API, and run the benchmark.
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| Port | Protocol | Purpose |
|---|---|---|
| 9000 | HTTP | REST API for CSV import (/imp) and queries (/exec) |
| 8812 | TCP | PostgreSQL wire protocol for SQL queries |
| 9009 | TCP | InfluxDB Line Protocol (ILP) for high-throughput ingestion |
| 9003 | HTTP | Health check and metrics endpoint |
CLI options are passed as --platform-option KEY=VALUE.
| Option | CLI | Environment Variable | Default | Description |
|---|---|---|---|---|
host |
yes | QUESTDB_HOST |
localhost |
QuestDB server hostname |
pg_port |
yes | QUESTDB_PG_PORT |
8812 |
PostgreSQL wire protocol port |
http_port |
yes | QUESTDB_HTTP_PORT |
9000 |
REST API HTTP port |
ilp_port |
yes | - | 9009 |
InfluxDB Line Protocol port |
username |
yes | - | admin |
QuestDB username |
password |
yes | - | quest |
QuestDB password |
database |
yes | - | qdb |
QuestDB database name |
loading_method |
yes | - | rest |
Loading method: rest (CSV import) or ilp |
use_tls |
no | - | false |
Use HTTPS for REST API endpoints |
connect_timeout |
no | - | 10 |
Connection timeout in seconds |
parquet_chunk_rows |
no | - | 200000 |
Rows per parquet batch when streaming wide tables through /imp |
Options marked "no" are adapter constructor parameters; the benchbox run CLI
does not accept them.
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.
- Schema creation - Tables are created via PG wire protocol with QuestDB-compatible DDL
- Foreign key removal - FK constraints are stripped (QuestDB does not support foreign keys)
- DROP TABLE adaptation -
IF EXISTSis added to DROP TABLE statements for idempotent schema creation - TPC file normalization - Pipe-delimited
.tblfiles are streamed through a normalizer that removes trailing delimiters - REST API import - CSV data is uploaded to the
/impendpoint with table name, delimiter, durability settings, andforceHeader=truefor chunked parquet uploads - Chunk sizing for wide parquet tables - the adapter
parquet_chunk_rowsparameter lowers the number of rows per/imprequest when a denormalized parquet file would otherwise exceed QuestDB's HTTP receive budget
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,durableandforceHeader=truefor chunked parquet uploads - Format: CSV with configurable delimiter (auto-detected for TPC
.tblfiles) - Throughput: Significantly higher than row-by-row INSERT for bulk datasets
- Wide parquet tuning: lower
parquet_chunk_rowsto 50000 or less when a very wide table would make each CSV chunk too large for a single HTTP request
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
The commands run TPC-H at scale factor 0.01 (a quick test), at scale factor 1, and for specific queries only.
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,Q17export QUESTDB_HOST=questdb.example.com
export QUESTDB_PG_PORT=8812
export QUESTDB_HTTP_PORT=9000
benchbox run --platform questdb --benchmark tpch --scale 1.0benchbox 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=questbenchbox run --platform questdb --benchmark tpch --scale 1.0 \
--platform-option host=questdb.example.com \
--platform-option loading_method=ilpThis previews the execution plan without running it.
benchbox run --platform questdb --benchmark tpch --scale 1.0 --dry-run ./previewThe QuestDB adapter is a single-module adapter:
| Module | Class | Responsibility |
|---|---|---|
questdb.py |
QuestDBAdapter |
Connection, schema, loading, query execution |
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
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"
}QuestDB uses a single database per instance. The adapter sets skip_database_management = True because:
- There is no
CREATE DATABASEorDROP DATABASEsupport - The
databaseparameter (default:qdb) identifies the instance, not a user-created database check_database_exists()tests connectivity rather than database presence
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.
QuestDB supports automatic time-based partitioning:
PARTITION BY NONE- No partitioning (default)PARTITION BY HOUR- Hourly partitionsPARTITION BY DAY- Daily partitionsPARTITION BY MONTH- Monthly partitionsPARTITION BY YEAR- Yearly partitions
Partitioning is specified at table creation time and cannot be changed after the fact.
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.
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
Error: Failed to connect to QuestDB
Solutions:
- Verify QuestDB is running and accessible on the configured host
- Check that port 8812 (PG wire protocol) is open and not blocked by a firewall
- For Docker deployments, ensure port mapping is correct (
-p 8812:8812) - Test connectivity directly:
psql -h <host> -p 8812 -U admin -d qdb -c "SELECT 1"
Error: Missing dependencies for questdb platform: psycopg
Solutions:
- Install the QuestDB extra:
uv add benchbox --extra questdb - Or install the driver directly:
uv add "psycopg[binary]"
Error: REST API import failed
Solutions:
- Verify the REST API is accessible on the HTTP port (default: 9000)
- Check that
requestsis installed:uv add requests - Test the REST API directly:
curl http://<host>:9000/exec?query=SELECT%201 - Lower the adapter
parquet_chunk_rowsparameter for very wide parquet tables - If using TLS, ensure the adapter
use_tlsparameter is set and certificates are valid
Error: critical CREATE TABLE statement(s) failed
Solutions:
- Check QuestDB server logs for detailed error messages
- Verify the QuestDB user has sufficient privileges
- Ensure sufficient disk space on the QuestDB server
- Review the translated DDL for QuestDB-incompatible syntax (e.g., unsupported column types)
QuestDB requires autocommit mode on the PG wire protocol. The adapter sets conn.autocommit = True automatically. If you encounter transaction-related errors:
- Ensure you are not wrapping queries in explicit
BEGIN/COMMITblocks - QuestDB does not support multi-statement transactions via PG wire protocol
Solutions:
- Ensure the REST API port (9000) is accessible for bulk CSV import
- If falling back to COPY, check network latency between BenchBox host and QuestDB
- For large datasets, consider increasing the QuestDB server's memory allocation
- Check disk I/O on the QuestDB server during loading
- Platform Comparison Matrix - Compare all platforms
- Platform Selection Guide - Choosing the right platform
- TPC-H Benchmark - TPC-H benchmark guide
- TPC-DS Benchmark - TPC-DS benchmark guide
- Deployment Modes Guide - Platform deployment architecture